pandora/pandora/itemlist/models.py

340 lines
12 KiB
Python

# -*- coding: utf-8 -*-
from __future__ import division, print_function, absolute_import
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, Group
from django.conf import settings
from django.utils.encoding import python_2_unicode_compatible
import ox
from oxdjango.fields import DictField, TupleField
from archive import extract
from user.utils import update_groups
from . 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'])
@python_2_unicode_compatible
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')
groups = models.ManyToManyField(Group, blank=True, 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 __str__(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 not user or user.is_anonymous():
return False
if self.user == user or \
self.groups.filter(id__in=user.groups.all()).count() > 0 or \
user.is_staff or \
user.profile.capability('canEditFeaturedLists'):
return True
return False
def edit(self, data, user):
if 'groups' in data:
groups = data.pop('groups')
update_groups(self, groups)
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 = [
'description',
'editable',
'groups',
'id',
'name',
'posterFrames',
'query',
'status',
'subscribed',
'type',
'user',
'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 == 'groups':
response[key] = [g.name for g in self.groups.all()]
elif key == 'editable':
response[key] = self.editable(user)
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:
if i.get('position'):
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
@python_2_unicode_compatible
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 __str__(self):
return u'%s in %s' % (self.item, self.list)
@python_2_unicode_compatible
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 __str__(self):
return u'%s/%s/%s' % (self.section, self.position, self.list)