# -*- coding: utf-8 -*- # vi:si:et:sw=4:sts=4:ts=4 from __future__ import division, with_statement import os import re import subprocess from glob import glob from django.db import models from django.db.models import Max from django.contrib.auth.models import User from django.conf import settings import ox from oxdjango.fields import DictField, TupleField from archive import extract import managers def get_path(f, x): return f.path(x) def get_icon_path(f, x): return get_path(f, 'icon.jpg') def get_listview(): return settings.CONFIG['user']['ui']['listView'] def get_listsort(): return tuple(settings.CONFIG['user']['ui']['listSort']) class List(models.Model): class Meta: unique_together = ("user", "name") created = models.DateTimeField(auto_now_add=True) modified = models.DateTimeField(auto_now=True) user = models.ForeignKey(User, related_name='lists') name = models.CharField(max_length=255) status = models.CharField(max_length=20, default='private') _status = ['private', 'public', 'featured'] query = DictField(default={"static": True}) type = models.CharField(max_length=255, default='static') description = models.TextField(default='') icon = models.ImageField(default=None, blank=True, upload_to=get_icon_path) view = models.TextField(default=get_listview) sort = TupleField(default=get_listsort, editable=False) poster_frames = TupleField(default=[], editable=False) #is through table still required? items = models.ManyToManyField('item.Item', related_name='lists', through='ListItem') numberofitems = models.IntegerField(default=0) subscribed_users = models.ManyToManyField(User, related_name='subscribed_lists') objects = managers.ListManager() def save(self, *args, **kwargs): if self.query.get('static', False): self.type = 'static' else: self.type = 'smart' if self.id: self.numberofitems = self.get_numberofitems(self.user) super(List, self).save(*args, **kwargs) @classmethod def get(cls, id): id = id.split(':') username = id[0] listname = ":".join(id[1:]) return cls.objects.get(user__username=username, name=listname) def get_items(self, user=None): if self.query.get('static', False): return self.items from item.models import Item return Item.objects.find({'query': self.query}, user) def get_numberofitems(self, user=None): return self.get_items(user).count() def add(self, item): q = self.items.filter(id=item.id) if q.count() == 0: l = ListItem() l.list = self l.item = item l.index = ListItem.objects.filter(list=self).aggregate(Max('index'))['index__max'] if l.index == None: l.index = 0 else: l.index += 1 l.save() def remove(self, item=None, items=None): if item: ListItem.objects.all().filter(item=item, list=self).delete() if items: ListItem.objects.all().filter(item__public_id__in=items, list=self).delete() def __unicode__(self): return self.get_id() def get_id(self): return u'%s:%s' % (self.user.username, self.name) def accessible(self, user): return self.user == user or self.status in ('public', 'featured') def editable(self, user): if user.is_anonymous(): return False if self.user == user or \ user.is_staff or \ user.profile.capability('canEditFeaturedLists') == True: return True return False def edit(self, data, user): for key in data: if key == 'query' and not data['query']: setattr(self, key, {"static":True}) elif key == 'query' and isinstance(data[key], dict): setattr(self, key, data[key]) elif key == 'type': if data[key] == 'static': self.query = {"static":True} self.type = 'static' else: self.type = 'smart' if self.query.get('static', False): self.query = {} elif key == 'status': value = data[key] if value not in self._status: value = self._status[0] if value == 'private': for user in self.subscribed_users.all(): self.subscribed_users.remove(user) qs = Position.objects.filter(user=user, list=self) if qs.count() > 1: pos = qs[0] pos.section = 'personal' pos.save() elif value == 'featured': if user.profile.capability('canEditFeaturedLists'): pos, created = Position.objects.get_or_create(list=self, user=user, section='featured') if created: qs = Position.objects.filter(user=user, section='featured') pos.position = qs.aggregate(Max('position'))['position__max'] + 1 pos.save() Position.objects.filter(list=self).exclude(id=pos.id).delete() else: value = self.status elif self.status == 'featured' and value == 'public': Position.objects.filter(list=self).delete() pos, created = Position.objects.get_or_create(list=self, user=self.user, section='personal') qs = Position.objects.filter(user=self.user, section='personal') pos.position = qs.aggregate(Max('position'))['position__max'] + 1 pos.save() for u in self.subscribed_users.all(): pos, created = Position.objects.get_or_create(list=self, user=u, section='public') qs = Position.objects.filter(user=u, section='public') pos.position = qs.aggregate(Max('position'))['position__max'] + 1 pos.save() self.status = value elif key == 'name': data['name'] = re.sub(' \[\d+\]$', '', data['name']).strip() if not data['name']: data['name'] = "Untitled" name = data['name'] num = 1 while List.objects.filter(name=name, user=self.user).exclude(id=self.id).count()>0: num += 1 name = data['name'] + ' [%d]' % num self.name = name elif key == 'description': self.description = ox.sanitize_html(data['description']) if 'position' in data: pos, created = Position.objects.get_or_create(list=self, user=user) pos.position = data['position'] pos.section = 'featured' if self.status == 'private': pos.section = 'personal' pos.save() if 'posterFrames' in data: self.poster_frames = tuple(data['posterFrames']) if 'view' in data: self.view = data['view'] if 'sort' in data: self.sort= tuple(data['sort']) self.save() if 'posterFrames' in data: self.update_icon() def json(self, keys=None, user=None): if not keys: keys=['id', 'name', 'user', 'type', 'query', 'status', 'subscribed', 'posterFrames', 'description', 'view'] response = {} for key in keys: if key == 'items': response[key] = self.get_numberofitems(user) elif key == 'id': response[key] = self.get_id() elif key == 'user': response[key] = self.user.username elif key == 'query': if not self.query.get('static', False): response[key] = self.query elif key == 'subscribers': response[key] = self.subscribed_users.all().count() elif key == 'subscribed': if user and not user.is_anonymous(): response[key] = self.subscribed_users.filter(id=user.id).exists() else: response[key] = getattr(self, { 'posterFrames': 'poster_frames' }.get(key, key)) return response def path(self, name=''): h = "%07d" % self.id return os.path.join('lists', h[:2], h[2:4], h[4:6], h[6:], name) def update_icon(self): frames = [] if not self.poster_frames: items = self.get_items(self.user).filter(rendered=True) if items.count(): poster_frames = [] for i in range(0, items.count(), max(1, int(items.count()/4))): poster_frames.append({ 'item': items[int(i)].public_id, 'position': items[int(i)].poster_frame }) self.poster_frames = tuple(poster_frames) self.save() for i in self.poster_frames: from item.models import Item qs = Item.objects.filter(public_id=i['item']) if qs.count() > 0: frame = qs[0].frame(i['position']) if frame: frames.append(frame) self.icon.name = self.path('icon.jpg') icon = self.icon.path if frames: while len(frames) < 4: frames += frames folder = os.path.dirname(icon) ox.makedirs(folder) for f in glob("%s/icon*.jpg" % folder): os.unlink(f) cmd = [ settings.LIST_ICON, '-f', ','.join(frames), '-o', icon ] p = subprocess.Popen(cmd, close_fds=True) p.wait() self.save() def get_icon(self, size=16): path = self.path('icon%d.jpg' % size) path = os.path.join(settings.MEDIA_ROOT, path) if not os.path.exists(path): folder = os.path.dirname(path) ox.makedirs(folder) if self.icon and os.path.exists(self.icon.path): source = self.icon.path max_size = min(self.icon.width, self.icon.height) else: source = os.path.join(settings.STATIC_ROOT, 'jpg/list256.jpg') max_size = 256 if size < max_size: extract.resize_image(source, path, size=size) else: path = source return path class ListItem(models.Model): created = models.DateTimeField(auto_now_add=True) modified = models.DateTimeField(auto_now=True) list = models.ForeignKey(List) index = models.IntegerField(default=0) item = models.ForeignKey('item.Item') def __unicode__(self): return u'%s in %s' % (self.item, self.list) class Position(models.Model): class Meta: unique_together = ("user", "list", "section") list = models.ForeignKey(List, related_name='position') user = models.ForeignKey(User) section = models.CharField(max_length=255) position = models.IntegerField(default=0) def __unicode__(self): return u'%s/%s/%s' % (self.section, self.position, self.list)