initial round of text section

This commit is contained in:
j 2013-02-16 01:20:40 +00:00
commit c8af45e7cf
25 changed files with 2243 additions and 984 deletions

View file

@ -31,6 +31,8 @@
"canEditAnnotations": {"staff": true, "admin": true},
"canEditEvents": {"staff": true, "admin": true},
"canEditFeaturedLists": {"staff": true, "admin": true},
"canEditFeaturedTexts": {"staff": true, "admin": true},
"canEditFeaturedEdits": {"staff": true, "admin": true},
"canEditMetadata": {"staff": true, "admin": true},
"canEditPlaces": {"staff": true, "admin": true},
"canEditSitePages": {"staff": true, "admin": true},
@ -712,6 +714,8 @@
"icons": "posters",
"infoIconSize": 256,
"item": "",
"text": "",
"edit": "",
"itemFind": "",
"itemSort": [{"key": "position", "operator": "+"}],
"itemView": "info",
@ -749,6 +753,11 @@
"featured": true,
"volumes": true
}
"texts": {
"personal": true,
"favorite": true,
"featured": true
}
},
"showSidebar": true,
"showSitePosters": false,

View file

@ -29,6 +29,8 @@
"canEditAnnotations": {"staff": true, "admin": true},
"canEditEvents": {"staff": true, "admin": true},
"canEditFeaturedLists": {"staff": true, "admin": true},
"canEditFeaturedTexts": {"staff": true, "admin": true},
"canEditFeaturedEdits": {"staff": true, "admin": true},
"canEditMetadata": {"staff": true, "admin": true},
"canEditPlaces": {"staff": true, "admin": true},
"canEditSitePages": {"staff": true, "admin": true},
@ -630,6 +632,8 @@
"icons": "frames",
"infoIconSize": 256,
"item": "",
"text": "",
"edit": "",
"itemFind": "",
"itemSort": [{"key": "position", "operator": "+"}],
"itemView": "info",
@ -670,6 +674,11 @@
"favorite": true,
"featured": true,
"volumes": true
},
"texts": {
"personal": true,
"favorite": true,
"featured": true
}
},
"showSidebar": true,

View file

@ -29,6 +29,8 @@
"canEditAnnotations": {"staff": true, "admin": true},
"canEditEvents": {"staff": true, "admin": true},
"canEditFeaturedLists": {"staff": true, "admin": true},
"canEditFeaturedTexts": {"staff": true, "admin": true},
"canEditFeaturedEdits": {"staff": true, "admin": true},
"canEditMetadata": {"staff": true, "admin": true},
"canEditPlaces": {"staff": true, "admin": true},
"canEditSitePages": {"staff": true, "admin": true},
@ -549,6 +551,8 @@
"icons": "posters",
"infoIconSize": 256,
"item": "",
"text": "",
"edit": "",
"itemFind": "",
"itemSort": [{"key": "position", "operator": "+"}],
"itemView": "info",
@ -589,6 +593,11 @@
"featured": true,
"volumes": true
}
"texts": {
"personal": true,
"favorite": true,
"featured": true
}
},
"showSidebar": true,
"showSitePosters": false,

View file

@ -207,7 +207,9 @@ def addList(request):
return {
status: {'code': int, 'text': string},
data: {
list:
id:
name:
...
}
}
'''

136
pandora/text/managers.py Normal file
View file

@ -0,0 +1,136 @@
# -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4
from datetime import datetime
from django.db.models import Q, Manager
import models
def parseCondition(condition, user):
'''
'''
k = condition.get('key', 'name')
k = {
'user': 'user__username',
'position': 'position__position',
'posterFrames': 'poster_frames',
}.get(k, k)
if not k:
k = 'name'
v = condition['value']
op = condition.get('operator')
if not op:
op = '='
if op.startswith('!'):
op = op[1:]
exclude = True
else:
exclude = False
if k == 'id':
v = v.split(":")
if len(v) >= 2:
v = (v[0], ":".join(v[1:]))
q = Q(user__username=v[0], name=v[1])
else:
q = Q(id__in=[])
return q
if k == 'subscribed':
key = 'subscribed_users__username'
v = user.username
elif isinstance(v, bool): #featured and public flag
key = k
else:
key = "%s%s" % (k, {
'==': '__iexact',
'^': '__istartswith',
'$': '__iendswith',
}.get(op, '__icontains'))
key = str(key)
if exclude:
q = ~Q(**{key: v})
else:
q = Q(**{key: v})
return q
def parseConditions(conditions, operator, user):
'''
conditions: [
{
value: "war"
}
{
key: "year",
value: "1970-1980,
operator: "!="
},
{
key: "country",
value: "f",
operator: "^"
}
],
operator: "&"
'''
conn = []
for condition in conditions:
if 'conditions' in condition:
q = parseConditions(condition['conditions'],
condition.get('operator', '&'), user)
if q:
conn.append(q)
pass
else:
conn.append(parseCondition(condition, user))
if conn:
q = conn[0]
for c in conn[1:]:
if operator == '|':
q = q | c
else:
q = q & c
return q
return None
class TextManager(Manager):
def get_query_set(self):
return super(TextManager, self).get_query_set()
def find(self, data, user):
'''
query: {
conditions: [
{
value: "war"
}
{
key: "year",
value: "1970-1980,
operator: "!="
},
{
key: "country",
value: "f",
operator: "^"
}
],
operator: "&"
}
'''
#join query with operator
qs = self.get_query_set()
conditions = parseConditions(data['query'].get('conditions', []),
data['query'].get('operator', '&'),
user)
if conditions:
qs = qs.filter(conditions)
if user.is_anonymous():
qs = qs.filter(Q(status='public') | Q(status='featured'))
else:
qs = qs.filter(Q(status='public') | Q(status='featured') | Q(user=user))
return qs

View file

@ -0,0 +1,205 @@
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Deleting model 'Image'
db.delete_table('text_image')
# Deleting model 'Attachment'
db.delete_table('text_attachment')
# Adding model 'Position'
db.create_table('text_position', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('text', self.gf('django.db.models.fields.related.ForeignKey')(related_name='position', to=orm['text.Text'])),
('user', self.gf('django.db.models.fields.related.ForeignKey')(related_name='text_position', to=orm['auth.User'])),
('section', self.gf('django.db.models.fields.CharField')(max_length='255')),
('position', self.gf('django.db.models.fields.IntegerField')(default=0)),
))
db.send_create_signal('text', ['Position'])
# Adding unique constraint on 'Position', fields ['user', 'text', 'section']
db.create_unique('text_position', ['user_id', 'text_id', 'section'])
# Deleting field 'Text.public'
db.delete_column('text_text', 'public')
# Deleting field 'Text.slug'
db.delete_column('text_text', 'slug')
# Deleting field 'Text.title'
db.delete_column('text_text', 'title')
# Deleting field 'Text.published'
db.delete_column('text_text', 'published')
# Adding field 'Text.name'
db.add_column('text_text', 'name',
self.gf('django.db.models.fields.CharField')(default=datetime.datetime(2013, 2, 15, 0, 0), max_length=255),
keep_default=False)
# Adding field 'Text.status'
db.add_column('text_text', 'status',
self.gf('django.db.models.fields.CharField')(default='private', max_length=20),
keep_default=False)
# Adding field 'Text.description'
db.add_column('text_text', 'description',
self.gf('django.db.models.fields.TextField')(default=''),
keep_default=False)
# Adding field 'Text.icon'
db.add_column('text_text', 'icon',
self.gf('django.db.models.fields.files.ImageField')(default='', max_length=100, blank=True),
keep_default=False)
# Adding field 'Text.poster_frames'
db.add_column('text_text', 'poster_frames',
self.gf('ox.django.fields.TupleField')(default=[]),
keep_default=False)
# Adding M2M table for field subscribed_users on 'Text'
db.create_table('text_text_subscribed_users', (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('text', models.ForeignKey(orm['text.text'], null=False)),
('user', models.ForeignKey(orm['auth.user'], null=False))
))
db.create_unique('text_text_subscribed_users', ['text_id', 'user_id'])
# Adding unique constraint on 'Text', fields ['user', 'name']
db.create_unique('text_text', ['user_id', 'name'])
def backwards(self, orm):
# Removing unique constraint on 'Text', fields ['user', 'name']
db.delete_unique('text_text', ['user_id', 'name'])
# Removing unique constraint on 'Position', fields ['user', 'text', 'section']
db.delete_unique('text_position', ['user_id', 'text_id', 'section'])
# Adding model 'Image'
db.create_table('text_image', (
('caption', self.gf('django.db.models.fields.CharField')(default='', max_length=255)),
('image', self.gf('django.db.models.fields.files.ImageField')(max_length=100)),
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
))
db.send_create_signal('text', ['Image'])
# Adding model 'Attachment'
db.create_table('text_attachment', (
('caption', self.gf('django.db.models.fields.CharField')(default='', max_length=255)),
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('file', self.gf('django.db.models.fields.files.FileField')(max_length=100)),
))
db.send_create_signal('text', ['Attachment'])
# Deleting model 'Position'
db.delete_table('text_position')
# Adding field 'Text.public'
db.add_column('text_text', 'public',
self.gf('django.db.models.fields.BooleanField')(default=False),
keep_default=False)
# Adding field 'Text.slug'
db.add_column('text_text', 'slug',
self.gf('django.db.models.fields.SlugField')(default='', max_length=50),
keep_default=False)
# Adding field 'Text.title'
db.add_column('text_text', 'title',
self.gf('django.db.models.fields.CharField')(max_length=1000, null=True),
keep_default=False)
# Adding field 'Text.published'
db.add_column('text_text', 'published',
self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now),
keep_default=False)
# Deleting field 'Text.name'
db.delete_column('text_text', 'name')
# Deleting field 'Text.status'
db.delete_column('text_text', 'status')
# Deleting field 'Text.description'
db.delete_column('text_text', 'description')
# Deleting field 'Text.icon'
db.delete_column('text_text', 'icon')
# Deleting field 'Text.poster_frames'
db.delete_column('text_text', 'poster_frames')
# Removing M2M table for field subscribed_users on 'Text'
db.delete_table('text_text_subscribed_users')
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '255', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'text.position': {
'Meta': {'unique_together': "(('user', 'text', 'section'),)", 'object_name': 'Position'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'section': ('django.db.models.fields.CharField', [], {'max_length': "'255'"}),
'text': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'position'", 'to': "orm['text.Text']"}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'text_position'", 'to': "orm['auth.User']"})
},
'text.text': {
'Meta': {'unique_together': "(('user', 'name'),)", 'object_name': 'Text'},
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'default': "''"}),
'icon': ('django.db.models.fields.files.ImageField', [], {'default': 'None', 'max_length': '100', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'poster_frames': ('ox.django.fields.TupleField', [], {'default': '[]'}),
'status': ('django.db.models.fields.CharField', [], {'default': "'private'", 'max_length': '20'}),
'subscribed_users': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'subscribed_texts'", 'symmetrical': 'False', 'to': "orm['auth.User']"}),
'text': ('django.db.models.fields.TextField', [], {'default': "''"}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'texts'", 'to': "orm['auth.User']"})
}
}
complete_apps = ['text']

View file

@ -0,0 +1,93 @@
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding field 'Text.type'
db.add_column('text_text', 'type',
self.gf('django.db.models.fields.CharField')(default='html', max_length=255),
keep_default=False)
# Adding field 'Text.links'
db.add_column('text_text', 'links',
self.gf('ox.django.fields.DictField')(default={}),
keep_default=False)
def backwards(self, orm):
# Deleting field 'Text.type'
db.delete_column('text_text', 'type')
# Deleting field 'Text.links'
db.delete_column('text_text', 'links')
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '255', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'text.position': {
'Meta': {'unique_together': "(('user', 'text', 'section'),)", 'object_name': 'Position'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'section': ('django.db.models.fields.CharField', [], {'max_length': "'255'"}),
'text': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'position'", 'to': "orm['text.Text']"}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'text_position'", 'to': "orm['auth.User']"})
},
'text.text': {
'Meta': {'unique_together': "(('user', 'name'),)", 'object_name': 'Text'},
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'default': "''"}),
'icon': ('django.db.models.fields.files.ImageField', [], {'default': 'None', 'max_length': '100', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'links': ('ox.django.fields.DictField', [], {'default': '{}'}),
'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'poster_frames': ('ox.django.fields.TupleField', [], {'default': '[]'}),
'status': ('django.db.models.fields.CharField', [], {'default': "'private'", 'max_length': '20'}),
'subscribed_users': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'subscribed_texts'", 'symmetrical': 'False', 'to': "orm['auth.User']"}),
'text': ('django.db.models.fields.TextField', [], {'default': "''"}),
'type': ('django.db.models.fields.CharField', [], {'default': "'html'", 'max_length': '255'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'texts'", 'to': "orm['auth.User']"})
}
}
complete_apps = ['text']

View file

@ -2,40 +2,158 @@
# vi:si:et:sw=4:sts=4:ts=4
from __future__ import division, with_statement
from datetime import datetime
import os
import subprocess
from glob import glob
from django.db import models
from django.contrib.auth.models import User
from django.conf import settings
import ox
from ox.django.fields import DictField, TupleField
from archive import extract
import managers
class Text(models.Model):
class Meta:
unique_together = ("user", "name")
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
published = models.DateTimeField(default=datetime.now, editable=False)
public = models.BooleanField(default=False)
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='')
user = models.ForeignKey(User)
slug = models.SlugField()
title = models.CharField(null=True, max_length=1000)
text = models.TextField(default='')
icon = models.ImageField(default=None, blank=True,
upload_to=lambda i, x: i.path("icon.jpg"))
text = models.TextField(default="")
links = DictField(default={}, editable=True)
poster_frames = TupleField(default=[], editable=False)
subscribed_users = models.ManyToManyField(User, related_name='subscribed_texts')
objects = managers.TextManager()
def save(self, *args, **kwargs):
super(Text, self).save(*args, **kwargs)
def __unicode__(self):
return u"%s <%s>" % (self.title, self.slug)
return self.get_id()
def get_absolute_url(self):
return '/text/%s' % self.slug
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')
class Image(models.Model):
image = models.ImageField(upload_to='text/image')
caption = models.CharField(max_length=255, default="")
def editable(self, user):
if user.is_anonymous():
return False
if self.user == user or \
user.is_staff or \
user.get_profile().capability('canEditFeaturedTexts') == True:
return True
return False
def get_absolute_url(self):
return self.image.url
def json(self, keys=None, user=None):
if not keys:
keys=['id', 'name', 'user', 'status', 'subscribed', 'posterFrames', 'description', 'text', 'type', 'links']
response = {}
_map = {
'posterFrames': 'poster_frames'
}
for key in keys:
if key == 'id':
response[key] = self.get_id()
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))
return response
def path(self, name=''):
h = "%07d" % self.id
return os.path.join('texts', h[:2], h[2:4], h[4:6], h[6:], name)
class Attachment(models.Model):
file = models.FileField(upload_to='text/attachment')
caption = models.CharField(max_length=255, default="")
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)].itemId,
'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(itemId=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)
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 Position(models.Model):
class Meta:
unique_together = ("user", "text", "section")
text = models.ForeignKey(Text, related_name='position')
user = models.ForeignKey(User, related_name='text_position')
section = models.CharField(max_length='255')
position = models.IntegerField(default=0)
def __unicode__(self):
return u'%s/%s/%s' % (self.section, self.position, self.text)
def get_absolute_url(self):
return self.file.url

View file

@ -1,27 +1,96 @@
# -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4
from __future__ import division
import os
import re
import ox
from ox.utils import json
from ox.django.decorators import login_required_json
from ox.django.shortcuts import render_to_json_response, get_object_or_404_json, json_response
import models
from ox.django.api import actions
from ox.django.decorators import login_required_json
from ox.django.http import HttpFileResponse
from ox.django.shortcuts import render_to_json_response, get_object_or_404_json, json_response
from django.db.models import Count, Q, Sum, Max
from item import utils
import models
def get_text_or_404_json(id):
id = id.split(':')
username = id[0]
textname = ":".join(id[1:])
return get_object_or_404_json(models.Text, user__username=username, name=textname)
@login_required_json
def addText(request):
'''
param data {
name: value,
}
return {
status: {'code': int, 'text': string},
data: {
id:
name:
...
}
}
'''
data = json.loads(request.POST['data'])
data['name'] = re.sub(' \[\d+\]$', '', data['name']).strip()
name = data['name']
if not name:
name = "Untitled"
num = 1
created = False
while not created:
text, created = models.Text.objects.get_or_create(name=name, user=request.user)
num += 1
name = data['name'] + ' [%d]' % num
text.save()
if text.status == 'featured':
pos, created = models.Position.objects.get_or_create(text=text,
user=request.user, section='featured')
qs = models.Position.objects.filter(section='featured')
else:
pos, created = models.Position.objects.get_or_create(text=text,
user=request.user, section='personal')
qs = models.Position.objects.filter(user=request.user, section='personal')
pos.position = qs.aggregate(Max('position'))['position__max'] + 1
pos.save()
response = json_response(status=200, text='created')
response['data'] = text.json()
return render_to_json_response(response)
actions.register(addText, cache=False)
def getText(request):
'''
param data
string id
return page
param data {
id: textid
}
return {
id:
text:
...
}
'''
response = json_response({})
itemId = json.loads(request.POST['data'])
item = get_object_or_404_json(models.Text, pk=itemId)
response['data']['page'] = item.html()
response = json_response()
data = json.loads(request.POST['data'])
public_id = data['id']
if public_id == '':
qs = models.Text.objects.filter(name='')
if qs.count() == 0:
text = models.Text()
text.name = ''
text.text = 'Please put something here'
text.user = request.user
text.save()
else:
text = qs[0]
else:
text = get_text_or_404_json(data['id'])
response['data'] = text.json()
return render_to_json_response(response)
actions.register(getText)
@ -29,22 +98,355 @@ actions.register(getText)
@login_required_json
def editText(request):
'''
param data
string id
return page
param data {
id:
text:
public: boolean
}
return {
id:
text:
...
}
'''
response = json_response({})
itemId = json.loads(request.POST['data'])
item = get_object_or_404_json(models.Text, pk=itemId)
response['data']['page'] = item.html()
response = json_response()
data = json.loads(request.POST['data'])
if data['id']:
public_id = data['id'].split(':')
username = public_id[0]
name = ":".join(public_id[1:])
text, created = models.Text.objects.get_or_create(name=name, user=models.User.objects.get(username=username))
if created:
text.user = request.user
else:
qs = models.Text.objects.filter(name='')
if qs.count() == 0:
if request.user.get_profile().capability('canEditFeaturedTexts'):
text = models.Text(name='', user=request.user)
text.save()
else:
response = json_response(status=403, text='permission denied')
return render_to_json_response(response)
else:
text = qs[0]
if text.editable(request.user):
for key in data:
if key == 'status':
value = data[key]
if value not in text._status:
value = text._status[0]
if value == 'private':
for user in text.subscribed_users.all():
text.subscribed_users.remove(user)
qs = models.Position.objects.filter(user=request.user,
section='section', text=text)
if qs.count() > 1:
pos = qs[0]
pos.section = 'personal'
pos.save()
elif value == 'featured':
if request.user.get_profile().capability('canEditFeaturedTexts'):
pos, created = models.Position.objects.get_or_create(text=text, user=request.user,
section='featured')
if created:
qs = models.Position.objects.filter(user=request.user, section='featured')
pos.position = qs.aggregate(Max('position'))['position__max'] + 1
pos.save()
models.Position.objects.filter(text=text).exclude(id=pos.id).delete()
else:
value = text.status
elif text.status == 'featured' and value == 'public':
models.Position.objects.filter(text=text).delete()
pos, created = models.Position.objects.get_or_create(text=text,
user=text.user,section='personal')
qs = models.Position.objects.filter(user=text.user,
section='personal')
pos.position = qs.aggregate(Max('position'))['position__max'] + 1
pos.save()
for u in text.subscribed_users.all():
pos, created = models.Position.objects.get_or_create(text=text, user=u,
section='public')
qs = models.Position.objects.filter(user=u, section='public')
pos.position = qs.aggregate(Max('position'))['position__max'] + 1
pos.save()
text.status = value
elif key == 'name':
data['name'] = re.sub(' \[\d+\]$', '', data['name']).strip()
name = data['name']
if not name:
name = "Untitled"
num = 1
while models.Text.objects.filter(name=name, user=text.user).exclude(id=text.id).count()>0:
num += 1
name = data['name'] + ' [%d]' % num
text.name = name
elif key == 'description':
text.description = ox.sanitize_html(data['description'])
elif key == 'text':
text.text = ox.sanitize_html(data['text'])
if 'position' in data:
pos, created = models.Position.objects.get_or_create(text=text, user=request.user)
pos.position = data['position']
pos.section = 'featured'
if text.status == 'private':
pos.section = 'personal'
pos.save()
if 'type' in data:
if data['type'] == 'pdf':
text.type = 'pdf'
else:
text.type = 'html'
if 'posterFrames' in data:
text.poster_frames = tuple(data['posterFrames'])
text.update_icon()
text.save()
response['data'] = text.json(user=request.user)
else:
response = json_response(status=403, text='permission denied')
return render_to_json_response(response)
actions.register(editText, cache=False)
def findText(request):
def _order_query(qs, sort):
order_by = []
for e in sort:
operator = e['operator']
if operator != '-':
operator = ''
key = {
'subscribed': 'subscribed_users',
'items': 'numberofitems'
}.get(e['key'], e['key'])
order = '%s%s' % (operator, key)
order_by.append(order)
if key == 'subscribers':
qs = qs.annotate(subscribers=Sum('subscribed_users'))
if order_by:
qs = qs.order_by(*order_by)
qs = qs.distinct()
return qs
def parse_query(data, user):
query = {}
query['range'] = [0, 100]
query['sort'] = [{'key':'user', 'operator':'+'}, {'key':'name', 'operator':'+'}]
for key in ('keys', 'group', 'text', 'range', 'position', 'positions', 'sort'):
if key in data:
query[key] = data[key]
print data
query['qs'] = models.Text.objects.find(data, user).exclude(name='')
print query
return query
def findTexts(request):
'''
param data {
query: {
conditions: [
{
key: 'user',
value: 'something',
operator: '='
}
]
operator: ","
},
sort: [{key: 'name', operator: '+'}],
range: [0, 100]
keys: []
}
possible query keys:
name, user, featured, subscribed
possible keys:
name, user, featured, subscribed, query
}
return {status: {code: int, text: string},
data: {
items: [
{name:, user:, featured:, public...}
]
}
}
'''
response = json_response({})
data = json.loads(request.POST['data'])
query = parse_query(data, request.user)
#order
is_section_request = query['sort'] == [{u'operator': u'+', u'key': u'position'}]
def is_featured_condition(x):
return x['key'] == 'status' and \
x['value'] == 'featured' and \
x['operator'] in ('=', '==')
is_featured = len(filter(is_featured_condition, data['query'].get('conditions', []))) > 0
if is_section_request:
qs = query['qs']
if not is_featured and not request.user.is_anonymous():
qs = qs.filter(position__in=models.Position.objects.filter(user=request.user))
qs = qs.order_by('position__position')
else:
qs = _order_query(query['qs'], query['sort'])
response = json_response()
if 'keys' in data:
qs = qs[query['range'][0]:query['range'][1]]
response['data']['items'] = [l.json(data['keys'], request.user) for l in qs]
elif 'position' in data:
#FIXME: actually implement position requests
response['data']['position'] = 0
elif 'positions' in data:
ids = [i.get_id() for i in qs]
response['data']['positions'] = utils.get_positions(ids, query['positions'])
else:
response['data']['items'] = qs.count()
return render_to_json_response(response)
actions.register(findText)
actions.register(findTexts)
@login_required_json
def removeText(request):
'''
param data {
id: testId,
}
return {
status: {'code': int, 'text': string},
data: {
}
}
'''
data = json.loads(request.POST['data'])
text = get_text_or_404_json(data['id'])
response = json_response()
if text.editable(request.user):
text.delete()
else:
response = json_response(status=403, text='not allowed')
return render_to_json_response(response)
actions.register(removeText, cache=False)
@login_required_json
def subscribeToText(request):
'''
param data {
id: testId,
}
return {
status: {'code': int, 'text': string},
data: {
}
}
'''
data = json.loads(request.POST['data'])
text = get_text_or_404_json(data['id'])
user = request.user
if text.status == 'public' and \
text.subscribed_users.filter(username=user.username).count() == 0:
text.subscribed_users.add(user)
pos, created = models.Position.objects.get_or_create(text=text, user=user, section='public')
if created:
qs = models.Position.objects.filter(user=user, section='public')
pos.position = qs.aggregate(Max('position'))['position__max'] + 1
pos.save()
response = json_response()
return render_to_json_response(response)
actions.register(subscribeToText, cache=False)
@login_required_json
def unsubscribeFromText(request):
'''
param data {
id: testId,
user: username(only admins)
}
return {
status: {'code': int, 'text': string},
data: {
}
}
'''
data = json.loads(request.POST['data'])
text = get_text_or_404_json(data['id'])
user = request.user
text.subscribed_users.remove(user)
models.Position.objects.filter(text=text, user=user, section='public').delete()
response = json_response()
return render_to_json_response(response)
actions.register(unsubscribeFromText, cache=False)
@login_required_json
def sortTexts(request):
'''
param data {
section: 'personal',
ids: [1,2,4,3]
}
known sections: 'personal', 'public', 'featured'
featured can only be edited by admins
return {
status: {'code': int, 'text': string},
data: {
}
}
'''
data = json.loads(request.POST['data'])
position = 0
section = data['section']
#ids = list(set(data['ids']))
ids = data['ids']
if section == 'featured' and not request.user.get_profile().capability('canEditFeaturedTexts'):
response = json_response(status=403, text='not allowed')
else:
user = request.user
if section == 'featured':
for i in ids:
l = get_text_or_404_json(i)
qs = models.Position.objects.filter(section=section, text=l)
if qs.count() > 0:
pos = qs[0]
else:
pos = models.Position(text=l, user=user, section=section)
if pos.position != position:
pos.position = position
pos.save()
position += 1
models.Position.objects.filter(section=section, text=l).exclude(id=pos.id).delete()
else:
for i in ids:
l = get_text_or_404_json(i)
pos, created = models.Position.objects.get_or_create(text=l,
user=request.user, section=section)
if pos.position != position:
pos.position = position
pos.save()
position += 1
response = json_response()
return render_to_json_response(response)
actions.register(sortTexts, cache=False)
def icon(request, id, size=16):
if not size:
size = 16
id = id.split(':')
username = id[0]
textname = ":".join(id[1:])
qs = models.Text.objects.filter(user__username=username, name=textname)
if qs.count() == 1 and qs[0].accessible(request.user):
text = qs[0]
icon = text.get_icon(int(size))
else:
icon = os.path.join(settings.STATIC_ROOT, 'jpg/list256.jpg')
return HttpFileResponse(icon, content_type='image/jpeg')

View file

@ -28,6 +28,7 @@ urlpatterns = patterns('',
(r'^api/?$', include(ox.django.api.urls)),
(r'^resetUI$', 'user.views.reset_ui'),
(r'^list/(?P<id>.*?)/icon(?P<size>\d*).jpg$', 'itemlist.views.icon'),
(r'^text/(?P<id>.*?)/icon(?P<size>\d*).jpg$', 'text.views.icon'),
(r'^robots.txt$', serve_static_file, {'location': os.path.join(settings.STATIC_ROOT, 'robots.txt'), 'content_type': 'text/plain'}),
(r'^favicon.ico$', serve_static_file, {'location': os.path.join(settings.STATIC_ROOT, 'png/icon.16.png'), 'content_type': 'image/x-icon'}),
(r'^opensearch.xml$', 'app.views.opensearch_xml'),

View file

@ -14,6 +14,7 @@ from ox.django.fields import DictField
from ox.utils import json
from itemlist.models import List, Position
import text
import managers
import tasks
@ -261,6 +262,29 @@ def get_ui(user_ui, user=None):
'''
ids.append(id)
return ids
def add_texts(texts, section):
P = text.models.Position
ids = []
for t in texts:
qs = P.objects.filter(section=section)
if section == 'featured':
try:
pos = P.objects.get(text=t, section=section)
created = False
except P.DoesNotExist:
pos = P(text=t, section=section, user=l.user)
pos.save()
created = True
else:
pos, created = P.objects.get_or_create(text=t, user=user, section=section)
qs = qs.filter(user=user)
if created:
pos.position = qs.aggregate(Max('position'))['position__max'] + 1
pos.save()
ids.append(t.get_id())
return ids
ids = ['']
if user:
@ -270,6 +294,11 @@ def get_ui(user_ui, user=None):
for i in ui['lists'].keys():
if i not in ids:
del ui['lists'][i]
tids = ['']
if user:
tids += add_texts(user.texts.exclude(status="featured"), 'personal')
tids += add_texts(user.subscribed_texts.filter(status='public'), 'public')
tids += add_texts(text.models.Text.objects.filter(status='featured'), 'featured')
return ui
def init_user(user, request=None):