340 lines
13 KiB
Python
340 lines
13 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
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 import get_user_model
|
|
from django.conf import settings
|
|
from oxdjango.fields import JSONField
|
|
|
|
import ox
|
|
|
|
from archive import extract
|
|
from user.utils import update_groups
|
|
from user.models import Group
|
|
|
|
from . import managers
|
|
|
|
User = get_user_model()
|
|
|
|
|
|
def get_path(f, x):
|
|
return f.path(x)
|
|
|
|
def get_icon_path(f, x):
|
|
return get_path(f, 'icon.jpg')
|
|
|
|
def get_collectionview():
|
|
return settings.CONFIG['user']['ui']['collectionView']
|
|
|
|
def get_collectionsort():
|
|
return tuple(settings.CONFIG['user']['ui']['collectionSort'])
|
|
|
|
class Collection(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='collections', on_delete=models.CASCADE)
|
|
groups = models.ManyToManyField(Group, blank=True, related_name='collections')
|
|
name = models.CharField(max_length=255)
|
|
status = models.CharField(max_length=20, default='private')
|
|
_status = ['private', 'public', 'featured']
|
|
query = JSONField(default=lambda: {"static": True}, editable=False)
|
|
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_collectionview)
|
|
sort = JSONField(default=get_collectionsort, editable=False)
|
|
|
|
poster_frames = JSONField(default=list, editable=False)
|
|
|
|
#is through table still required?
|
|
documents = models.ManyToManyField('document.Document', related_name='collections',
|
|
through='CollectionDocument')
|
|
|
|
numberofdocuments = models.IntegerField(default=0)
|
|
subscribed_users = models.ManyToManyField(User, related_name='subscribed_collections')
|
|
|
|
objects = managers.CollectionManager()
|
|
|
|
def save(self, *args, **kwargs):
|
|
if self.query.get('static', False):
|
|
self.type = 'static'
|
|
else:
|
|
self.type = 'smart'
|
|
if self.id:
|
|
self.numberofdocuments = self.get_numberofdocuments(self.user)
|
|
super(Collection, self).save(*args, **kwargs)
|
|
|
|
@classmethod
|
|
def get(cls, id):
|
|
id = id.split(':')
|
|
username = id[0]
|
|
collectionname = ":".join(id[1:])
|
|
return cls.objects.get(user__username=username, name=collectionname)
|
|
|
|
def get_documents(self, user=None):
|
|
if self.query.get('static', False):
|
|
return self.documents
|
|
from document.models import Document
|
|
return Document.objects.find({'query': self.query}, user)
|
|
|
|
def get_numberofdocuments(self, user=None):
|
|
return self.get_documents(user).count()
|
|
|
|
def add(self, document):
|
|
q = self.documents.filter(id=document.id)
|
|
if q.count() == 0:
|
|
l = CollectionDocument()
|
|
l.collection = self
|
|
l.document = document
|
|
l.index = CollectionDocument.objects.filter(collection=self).aggregate(Max('index'))['index__max']
|
|
if l.index is None:
|
|
l.index = 0
|
|
else:
|
|
l.index += 1
|
|
l.save()
|
|
|
|
def remove(self, document=None, documents=None):
|
|
if document:
|
|
CollectionDocument.objects.all().filter(document=document, collection=self).delete()
|
|
if documents:
|
|
CollectionDocument.objects.all().filter(document__id__in=documents, collection=self).delete()
|
|
|
|
def __str__(self):
|
|
return self.get_id()
|
|
|
|
def get_id(self):
|
|
return '%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('canEditFeaturedCollections'):
|
|
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, collection=self)
|
|
if qs.count() > 1:
|
|
pos = qs[0]
|
|
pos.section = 'personal'
|
|
pos.save()
|
|
elif value == 'featured':
|
|
if user.profile.capability('canEditFeaturedCollections'):
|
|
pos, created = Position.objects.get_or_create(collection=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(collection=self).exclude(id=pos.id).delete()
|
|
else:
|
|
value = self.status
|
|
elif self.status == 'featured' and value == 'public':
|
|
Position.objects.filter(collection=self).delete()
|
|
pos, created = Position.objects.get_or_create(collection=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(collection=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 Collection.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(collection=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 in ('items', 'documents'):
|
|
response[key] = self.get_numberofdocuments(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('collections', h[:2], h[2:4], h[4:6], h[6:], name)
|
|
|
|
def update_icon(self):
|
|
frames = []
|
|
if not self.poster_frames:
|
|
documents = self.get_documents(self.user).all()
|
|
if documents.count():
|
|
poster_frames = []
|
|
for i in range(0, documents.count(), max(1, int(documents.count()/4))):
|
|
poster_frames.append({
|
|
'document': documents[int(i)].get_id(),
|
|
})
|
|
self.poster_frames = tuple(poster_frames)
|
|
self.save()
|
|
for i in self.poster_frames:
|
|
from document.models import Document
|
|
if 'document' in i:
|
|
qs = Document.objects.filter(id=ox.fromAZ(i['document']))
|
|
if qs.count() > 0:
|
|
frame = qs[0].thumbnail(size=1024, page=i.get('page'))
|
|
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.COLLECTION_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 CollectionDocument(models.Model):
|
|
created = models.DateTimeField(auto_now_add=True)
|
|
modified = models.DateTimeField(auto_now=True)
|
|
collection = models.ForeignKey(Collection, on_delete=models.CASCADE)
|
|
index = models.IntegerField(default=0)
|
|
document = models.ForeignKey('document.Document', on_delete=models.CASCADE)
|
|
|
|
def __str__(self):
|
|
return '%s in %s' % (self.document, self.collection)
|
|
|
|
class Position(models.Model):
|
|
|
|
class Meta:
|
|
unique_together = ("user", "collection", "section")
|
|
|
|
collection = models.ForeignKey(Collection, related_name='position', on_delete=models.CASCADE)
|
|
user = models.ForeignKey(User, related_name='collection_positions', on_delete=models.CASCADE)
|
|
section = models.CharField(max_length=255)
|
|
position = models.IntegerField(default=0)
|
|
|
|
def __str__(self):
|
|
return '%s/%s/%s' % (self.section, self.position, self.collection)
|
|
|