pandora/pandora/text/models.py

313 lines
12 KiB
Python
Raw Normal View History

2010-11-23 11:30:03 +01:00
# -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4
from __future__ import division, with_statement
2013-02-16 01:20:40 +00:00
import os
2013-02-20 11:29:29 +00:00
import re
2013-02-16 01:20:40 +00:00
import subprocess
from glob import glob
2013-02-21 14:09:23 +00:00
from urllib import quote
2010-11-23 11:30:03 +01:00
from django.db import models
from django.db.models import Max
2010-11-23 11:30:03 +01:00
from django.contrib.auth.models import User
2013-02-16 01:20:40 +00:00
from django.conf import settings
from django.db.models.signals import pre_delete
2013-02-16 01:20:40 +00:00
import ox
2016-02-20 09:06:41 +00:00
from oxdjango.fields import TupleField
2013-02-16 01:20:40 +00:00
from archive import extract
from archive.chunk import save_chunk
2013-02-16 01:20:40 +00:00
import managers
2010-11-23 11:30:03 +01:00
def get_path(i, x): return i.path(x)
def get_icon_path(i, x): return get_path(i, 'icon.jpg')
2010-11-23 11:30:03 +01:00
class Text(models.Model):
2013-02-16 01:20:40 +00:00
class Meta:
unique_together = ("user", "name")
2010-11-23 11:30:03 +01:00
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
2013-02-16 01:20:40 +00:00
user = models.ForeignKey(User, related_name='texts')
name = models.CharField(max_length=255)
status = models.CharField(max_length=20, default='private')
_status = ['private', 'public', 'featured']
type = models.CharField(max_length=255, default='html')
description = models.TextField(default='')
2013-02-28 12:16:46 +00:00
rightslevel = models.IntegerField(db_index=True, default=0)
icon = models.ImageField(default=None, blank=True, upload_to=get_icon_path)
2013-02-16 01:20:40 +00:00
text = models.TextField(default="")
2013-02-21 14:51:46 +00:00
embeds = TupleField(default=[], editable=True)
2013-02-16 01:20:40 +00:00
poster_frames = TupleField(default=[], editable=False)
subscribed_users = models.ManyToManyField(User, related_name='subscribed_texts')
2010-11-23 11:30:03 +01:00
2013-02-16 01:20:40 +00:00
objects = managers.TextManager()
2013-02-19 16:39:50 +00:00
uploading = models.BooleanField(default = False)
file = models.FileField(default=None, blank=True,null=True, upload_to=get_path)
2013-02-16 01:20:40 +00:00
def save(self, *args, **kwargs):
2013-02-28 12:16:46 +00:00
self.rightslevel = min(self.rightslevel, len(settings.CONFIG['textRightsLevels']) - 1)
2013-02-16 01:20:40 +00:00
super(Text, self).save(*args, **kwargs)
2010-11-23 11:30:03 +01:00
def __unicode__(self):
2013-02-16 01:20:40 +00:00
return self.get_id()
@classmethod
def get(cls, id):
2014-01-17 18:24:34 +00:00
if id == '':
return cls.objects.get(name='')
else:
id = id.split(':')
username = id[0]
name = ":".join(id[1:])
return cls.objects.get(user__username=username, name=name)
2013-02-19 16:39:50 +00:00
def get_absolute_url(self):
2014-11-04 15:04:07 +02:00
return '/texts/%s' % quote(self.get_id().replace('_', '\t').replace(' ', '_').encode('utf-8')).replace('/', '%2F')
2013-02-21 14:09:23 +00:00
def get_absolute_pdf_url(self):
return '%s/text.pdf' % self.get_absolute_url()
2013-02-16 01:20:40 +00:00
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):
2013-02-21 12:15:20 +00:00
if not user or user.is_anonymous():
2013-02-16 01:20:40 +00:00
return False
if self.user == user or \
user.is_staff or \
2016-02-19 22:04:15 +05:30
user.profile.capability('canEditFeaturedTexts') == True:
2013-02-16 01:20:40 +00:00
return True
return False
2010-11-23 11:30:03 +01:00
2013-02-20 11:29:29 +00:00
def edit(self, data, user):
for key in data:
if 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,
section='section', text=self)
if qs.count() > 1:
pos = qs[0]
pos.section = 'personal'
pos.save()
elif value == 'featured':
2016-02-19 22:04:15 +05:30
if user.profile.capability('canEditFeaturedTexts'):
2013-02-20 11:29:29 +00:00
pos, created = Position.objects.get_or_create(text=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(text=self).exclude(id=pos.id).delete()
else:
value = self.status
elif self.status == 'featured' and value == 'public':
Position.objects.filter(text=self).delete()
pos, created = Position.objects.get_or_create(text=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(text=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"
2013-02-20 11:29:29 +00:00
name = data['name']
num = 1
while Text.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'], global_attributes=['lang'])
2013-02-20 11:29:29 +00:00
elif key == 'text':
self.text = ox.sanitize_html(data['text'], global_attributes=[
'data-name',
'data-type',
'data-value',
'lang'
])
2013-02-28 12:16:46 +00:00
elif key == 'rightslevel':
self.rightslevel = int(data['rightslevel'])
2013-02-20 11:29:29 +00:00
if 'position' in data:
pos, created = Position.objects.get_or_create(text=self, user=user)
2013-02-20 11:29:29 +00:00
pos.position = data['position']
pos.section = 'featured'
if self.status == 'private':
pos.section = 'personal'
pos.save()
if 'type' in data:
self.type = data['type'] == 'pdf' and 'pdf' or 'html'
if 'posterFrames' in data:
self.poster_frames = tuple(data['posterFrames'])
2013-02-21 14:51:46 +00:00
if 'embeds' in data:
self.embeds = tuple(data['embeds'])
2013-02-20 11:29:29 +00:00
self.save()
2013-02-20 11:44:55 +00:00
if 'posterFrames' in data:
self.update_icon()
2013-02-20 11:29:29 +00:00
2013-02-16 01:20:40 +00:00
def json(self, keys=None, user=None):
default_keys = ['id']
2013-02-16 01:20:40 +00:00
if not keys:
2013-02-21 12:15:20 +00:00
keys=[
'description',
'editable',
'rightslevel',
2013-02-21 12:15:20 +00:00
'id',
'links',
'name',
'posterFrames',
'status',
'subscribed',
'text',
'type',
'user',
'uploaded',
'embeds',
'names',
2013-02-21 12:15:20 +00:00
]
2013-02-16 01:20:40 +00:00
response = {}
_map = {
'posterFrames': 'poster_frames'
}
for key in keys:
if key == 'id':
response[key] = self.get_id()
2013-02-21 12:15:20 +00:00
elif key == 'editable':
response[key] = self.editable(user)
2013-02-16 01:20:40 +00:00
elif key == 'user':
response[key] = self.user.username
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()
elif hasattr(self, _map.get(key, key)):
response[key] = getattr(self, _map.get(key,key))
2013-02-19 16:39:50 +00:00
if self.type == 'pdf':
2013-02-21 10:25:39 +00:00
response['uploaded'] = True if self.file and not self.uploading else False
2013-02-21 14:51:46 +00:00
response['embeds'] = self.embeds
2013-11-08 18:57:24 +00:00
response['names'] = []
else:
response['names'] = re.compile('<[^<>]*?data-name="(.+?)"').findall(self.text)
for key in response.keys():
if key not in keys + default_keys:
del response[key]
2013-02-16 01:20:40 +00:00
return response
2010-11-23 11:30:03 +01:00
2013-02-16 01:20:40 +00:00
def path(self, name=''):
h = "%07d" % self.id
return os.path.join('texts', h[:2], h[2:4], h[4:6], h[6:], name)
2011-01-01 17:14:42 +05:30
2013-02-16 01:20:40 +00:00
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({
2014-09-19 12:26:46 +00:00
'item': items[int(i)].public_id,
2013-02-16 01:20:40 +00:00
'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
2014-09-19 12:26:46 +00:00
qs = Item.objects.filter(public_id=i['item'])
2013-02-16 01:20:40 +00:00
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)
2013-02-16 01:20:40 +00:00
p.wait()
self.save()
2010-11-23 11:30:03 +01:00
2013-02-16 01:20:40 +00:00
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
2010-11-23 11:30:03 +01:00
def save_chunk(self, chunk, offset=None, done=False):
2013-02-19 16:39:50 +00:00
if self.uploading:
name = self.path('data.pdf')
def done_cb():
if done:
self.uploading = False
self.save()
return True, self.file.size
return save_chunk(self, self.file, chunk, offset, name, done_cb)
return False, 0
2013-02-19 16:39:50 +00:00
def delete_file(sender, **kwargs):
t = kwargs['instance']
if t.file:
t.file.delete(save=False)
pre_delete.connect(delete_file, sender=Text)
2013-02-16 01:20:40 +00:00
class Position(models.Model):
2011-01-01 17:14:42 +05:30
2013-02-16 01:20:40 +00:00
class Meta:
unique_together = ("user", "text", "section")
text = models.ForeignKey(Text, related_name='position')
user = models.ForeignKey(User, related_name='text_position')
2016-02-19 22:04:15 +05:30
section = models.CharField(max_length=255)
2013-02-16 01:20:40 +00:00
position = models.IntegerField(default=0)
def __unicode__(self):
return u'%s/%s/%s' % (self.section, self.position, self.text)
2010-11-23 11:30:03 +01:00