diff --git a/pandora/annotaion/models.py b/pandora/annotaion/models.py index fa0af1ed..bf62525b 100644 --- a/pandora/annotaion/models.py +++ b/pandora/annotaion/models.py @@ -2,49 +2,32 @@ # vi:si:et:sw=4:sts=4:ts=4 from __future__ import division, with_statement -from datetime import datetime -import os.path -import math -import random -import re -import subprocess -import unicodedata -from glob import glob - from django.db import models -from django.db.models import Q from django.contrib.auth.models import User -from django.core.files.base import ContentFile -from django.conf import settings - -from ox.django import fields -import ox -from ox.utils import json -from ox import stripTags -from ox.normalize import canonicalTitle, canonicalName import utils class Layer(models.Model): + class Meta: ordering = ('position', ) name = models.CharField(null=True, max_length=255, unique=True) title = models.CharField(null=True, max_length=255) - #text, string, string from list(fixme), event, place, person + #text, string, string from list(fixme), event, place, person type = models.CharField(null=True, max_length=255) position = models.IntegerField(default=0) - overlapping = models.BooleanField(default=True) - enabled = models.BooleanField(default=True) + overlapping = models.BooleanField(default=True) + enabled = models.BooleanField(default=True) - enabled = models.BooleanField(default=True) - public = models.BooleanField(default=True) #false=users only see there own bins - subtitle = models.BooleanField(default=True) #bis can be displayed as subtitle, only one bin + enabled = models.BooleanField(default=True) + public = models.BooleanField(default=True) #false=users only see there own bins + subtitle = models.BooleanField(default=True) #bis can be displayed as subtitle, only one bin - find = models.BooleanField(default=True) - #words / item duration(wpm), total words, cuts per minute, cuts, number of annotations, number of annotations/duration + find = models.BooleanField(default=True) + #words / item duration(wpm), total words, cuts per minute, cuts, number of annotations, number of annotations/duration sort = models.CharField(null=True, max_length=255) def properties(self): @@ -61,7 +44,9 @@ class Layer(models.Model): def __unicode__(self): return self.title + class Annotation(models.Model): + #FIXME: here having a item,start index would be good created = models.DateTimeField(auto_now_add=True) modified = models.DateTimeField(auto_now=True) @@ -77,9 +62,9 @@ class Annotation(models.Model): def editable(self, user): if user.is_authenticated(): - if obj.user == user.id or user.has_perm('0x.admin'): + if self.user == user or user.has_perm('0x.admin'): return True - if user.groups.filter(id__in=obj.groups.all()).count() > 0: + if user.groups.filter(id__in=self.groups.all()).count() > 0: return True return False @@ -102,4 +87,3 @@ class Annotation(models.Model): def __unicode__(self): return "%s/%s-%s" %(self.item, self.start, self.stop) - diff --git a/pandora/annotaion/tests.py b/pandora/annotaion/tests.py index 2247054b..927cadf0 100644 --- a/pandora/annotaion/tests.py +++ b/pandora/annotaion/tests.py @@ -7,7 +7,9 @@ Replace these with more appropriate tests for your application. from django.test import TestCase + class SimpleTest(TestCase): + def test_basic_addition(self): """ Tests that 1 + 1 always equals 2. @@ -20,4 +22,3 @@ Another way to test that 1 + 1 is equal to 2. >>> 1 + 1 == 2 True """} - diff --git a/pandora/annotaion/utils.py b/pandora/annotaion/utils.py index 133d874e..79ace73d 100644 --- a/pandora/annotaion/utils.py +++ b/pandora/annotaion/utils.py @@ -1,19 +1,22 @@ # -*- coding: utf-8 -*- # ci:si:et:sw=4:sts=4:ts=4 +import re +import ox + def html_parser(text, nofollow=True): text = text.replace('', '__i__').replace('', '__/i__') text = text.replace('', '__b__').replace('', '__/b__') #truns links into wiki links, make sure to only take http links text = re.sub('(.*?)', '[\\1 \\2]', text) - text = escape(text) + text = ox.escape(text) text = text.replace('__i__', '').replace('__/i__', '') text = text.replace('__b__', '').replace('__/b__', '') if nofollow: nofollow_rel = ' rel="nofollow"' else: nofollow_rel = '' - + links = re.compile('(\[(http.*?) (.*?)\])').findall(text) for t, link, txt in links: link = link.replace('http', '__LINK__').replace('.', '__DOT__') @@ -24,8 +27,8 @@ def html_parser(text, nofollow=True): link = link.replace('http', '__LINK__').replace('.', '__DOT__') ll = '%s' % (link, nofollow_rel, link) text = text.replace(t, ll) - - text = urlize(text, nofollow=nofollow) + + text = ox.urlize(text, nofollow=nofollow) #inpage links text = re.sub('\[(/.+?) (.+?)\]', '\\2', text) @@ -33,4 +36,3 @@ def html_parser(text, nofollow=True): text = text.replace('__LINK__', 'http').replace('__DOT__', '.') text = text.replace("\n", '
') return text - diff --git a/pandora/annotaion/views.py b/pandora/annotaion/views.py index ac7e1228..1f2c4770 100644 --- a/pandora/annotaion/views.py +++ b/pandora/annotaion/views.py @@ -1,27 +1,10 @@ # -*- coding: utf-8 -*- # vi:si:et:sw=4:sts=4:ts=4 from __future__ import division -import os.path -import re -from datetime import datetime -from urllib2 import unquote -import mimetypes - -from django import forms -from django.core.paginator import Paginator -from django.contrib.auth.decorators import login_required -from django.contrib.auth.models import User -from django.db.models import Q, Avg, Count, Sum -from django.http import HttpResponse, Http404 -from django.shortcuts import render_to_response, get_object_or_404, get_list_or_404, redirect -from django.template import RequestContext -from django.conf import settings 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 -from ox.django.http import HttpFileResponse -import ox import models from api.actions import actions @@ -48,6 +31,7 @@ def findAnnotation(request): return render_to_json_response(response) actions.register(findAnnotation) + @login_required_json def addAnnotation(request): ''' @@ -67,18 +51,18 @@ def addAnnotation(request): data = json.loads(request.POST['data']) for key in ('item', 'layer', 'start', 'end', 'value'): if key not in data: - return render_to_json_response(json_response(status=400, text='invalid data')) + return render_to_json_response(json_response(status=400, + text='invalid data')) item = get_object_or_404_json(models.Item, itemId=data['item']) layer = get_object_or_404_json(models.Layer, layerId=data['layer']) - + annotation = models.Annotation( item=item, layer=layer, user=request.user, start=float(data['start']), end=float(data['end']), - value=data['value'] - ) + value=data['value']) annotation.save() response = json_response() response['data']['annotation'] = annotation.json() @@ -88,6 +72,7 @@ def addAnnotation(request): return render_to_json_response(response) actions.register(addAnnotation) + @login_required_json def removeAnnotation(request): ''' @@ -103,6 +88,7 @@ def removeAnnotation(request): return render_to_json_response(response) actions.register(removeAnnotation) + @login_required_json def editAnnotation(request): ''' @@ -129,4 +115,3 @@ def editAnnotation(request): response = json_response(status=501, text='not implemented') return render_to_json_response(response) actions.register(editAnnotation) - diff --git a/pandora/api/actions.py b/pandora/api/actions.py index 6f5ad528..4fca44c5 100644 --- a/pandora/api/actions.py +++ b/pandora/api/actions.py @@ -18,7 +18,7 @@ def autodiscover(): import_module('%s.views'%app) except: if module_has_submodule(mod, 'views'): - raise + raise def trim(docstring): @@ -46,8 +46,11 @@ def trim(docstring): # Return a single string: return '\n'.join(trimmed) + class ApiActions(dict): + def __init__(self): + def api(request): ''' returns list of all known api action @@ -56,7 +59,8 @@ class ApiActions(dict): ''' actions = self.keys() actions.sort() - return render_to_json_response(json_response({'actions': actions})) + response = json_response({'actions': actions}) + return render_to_json_response(response) self['api'] = api def apidoc(request): diff --git a/pandora/api/views.py b/pandora/api/views.py index 028ff1b9..4f0c6c76 100644 --- a/pandora/api/views.py +++ b/pandora/api/views.py @@ -1,54 +1,32 @@ # -*- coding: utf-8 -*- # vi:si:et:sw=4:sts=4:ts=4 from __future__ import division -import os.path -import re -from datetime import datetime -from urllib2 import unquote -import mimetypes -from django import forms -from django.core.paginator import Paginator -from django.contrib.auth.decorators import login_required -from django.contrib.auth.models import User -from django.db.models import Q, Avg, Count, Sum -from django.http import HttpResponse, Http404 -from django.shortcuts import render_to_response, get_object_or_404, get_list_or_404, redirect +from django.shortcuts import render_to_response from django.template import RequestContext from django.conf import settings -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 -from ox.django.http import HttpFileResponse -import ox - -import models +from ox.django.shortcuts import render_to_json_response, json_response from pandora.user.models import get_user_json -from pandora.archive.models import File -from pandora.archive import extract - - from actions import actions + def api(request): if request.META['REQUEST_METHOD'] == "OPTIONS": - response = HttpResponse('') - response = render_to_json_response({'status': {'code': 200, 'text': 'use POST'}}) + response = render_to_json_response({'status': {'code': 200, + 'text': 'use POST'}}) response['Access-Control-Allow-Origin'] = '*' return response if not 'action' in request.POST: methods = actions.keys() api = [] for f in sorted(methods): - api.append({ - 'name': f, - 'doc': actions.doc(f).replace('\n', '
\n') - }) + api.append({'name': f, + 'doc': actions.doc(f).replace('\n', '
\n')}) context = RequestContext(request, {'api': api, - 'sitename': settings.SITENAME,}) + 'sitename': settings.SITENAME}) return render_to_response('api.html', context) function = request.POST['action'] #FIXME: possible to do this in f @@ -63,6 +41,7 @@ def api(request): response['Access-Control-Allow-Origin'] = '*' return response + def hello(request): ''' return {'status': {'code': int, 'text': string}, @@ -73,15 +52,17 @@ def hello(request): if request.user.is_authenticated(): response['data']['user'] = get_user_json(request.user) else: - response['data']['user'] = {'name': 'Guest', 'group': 'guest', 'preferences': {}} + response['data']['user'] = {'name': 'Guest', + 'group': 'guest', + 'preferences': {}} return render_to_json_response(response) actions.register(hello) + def error(request): ''' - this action is used to test api error codes, it should return a 503 error + this action is used to test api error codes, it should return a 503 error ''' success = error_is_success return render_to_json_response({}) actions.register(error) - diff --git a/pandora/app/admin.py b/pandora/app/admin.py index 428d4a3d..90cbd79f 100644 --- a/pandora/app/admin.py +++ b/pandora/app/admin.py @@ -5,6 +5,7 @@ from django.contrib import admin import models + class PageAdmin(admin.ModelAdmin): search_fields = ['name', 'body'] @@ -15,4 +16,3 @@ class SiteSettingsAdmin(admin.ModelAdmin): search_fields = ['key', 'value'] admin.site.register(models.SiteSettings, SiteSettingsAdmin) - diff --git a/pandora/app/models.py b/pandora/app/models.py index b57bfd1a..16bc4289 100644 --- a/pandora/app/models.py +++ b/pandora/app/models.py @@ -2,6 +2,7 @@ # vi:si:et:sw=4:sts=4:ts=4 from django.db import models + class Page(models.Model): created = models.DateTimeField(auto_now_add=True) modified = models.DateTimeField(auto_now=True) @@ -11,10 +12,10 @@ class Page(models.Model): def __unicode__(self): return self.name + class SiteSettings(models.Model): key = models.CharField(max_length=1024, unique=True) value = models.TextField(blank=True) def __unicode__(self): return self.key - diff --git a/pandora/app/tests.py b/pandora/app/tests.py index 2247054b..927cadf0 100644 --- a/pandora/app/tests.py +++ b/pandora/app/tests.py @@ -7,7 +7,9 @@ Replace these with more appropriate tests for your application. from django.test import TestCase + class SimpleTest(TestCase): + def test_basic_addition(self): """ Tests that 1 + 1 always equals 2. @@ -20,4 +22,3 @@ Another way to test that 1 + 1 is equal to 2. >>> 1 + 1 == 2 True """} - diff --git a/pandora/app/views.py b/pandora/app/views.py index 82965bdb..c2e0b90c 100644 --- a/pandora/app/views.py +++ b/pandora/app/views.py @@ -1,31 +1,32 @@ # -*- coding: utf-8 -*- # vi:si:et:sw=4:sts=4:ts=4 -from django.shortcuts import render_to_response, get_object_or_404, get_list_or_404 +from django.shortcuts import render_to_response from django.template import RequestContext from django.conf import settings from ox.django.shortcuts import json_response, render_to_json_response, get_object_or_404_json +from ox.utils import json import models -from item.models import siteJson - from api.actions import actions + def intro(request): - context = RequestContext(request, {'settings':settings}) + context = RequestContext(request, {'settings': settings}) return render_to_response('intro.html', context) + def index(request): - context = RequestContext(request, {'settings':settings}) - if request.GET.get('_escaped_fragment_', None): - return html_snapshot(request) + context = RequestContext(request, {'settings': settings}) return render_to_response('index.html', context) + def timeline(request): - context = RequestContext(request, {'settings':settings}) + context = RequestContext(request, {'settings': settings}) return render_to_response('timeline.html', context) + def getPage(request): data = json.loads(request.POST['data']) name = data['page'] @@ -34,6 +35,7 @@ def getPage(request): return render_to_json_response(response) actions.register(getPage) + def site_json(request): ''' return render_to_json_response(siteJson()) @@ -41,6 +43,7 @@ def site_json(request): siteSettings = {} for s in models.SiteSettings.objects.all(): siteSettings[s.key] = s.value - context = RequestContext(request, {'settings':settings, 'siteSettings': siteSettings}) - return render_to_response('site.json', context, mimetype="application/javascript") - + context = RequestContext(request, {'settings': settings, + 'siteSettings': siteSettings}) + return render_to_response('site.json', context, + mimetype="application/javascript") diff --git a/pandora/archive/admin.py b/pandora/archive/admin.py index 48c82a1c..a8057b7b 100644 --- a/pandora/archive/admin.py +++ b/pandora/archive/admin.py @@ -6,6 +6,7 @@ from django.contrib import admin from forms import FileAdminForm, InstanceAdminForm import models + class FileAdmin(admin.ModelAdmin): search_fields = ['name', 'video_codec'] list_display = ['available', 'is_main', '__unicode__', 'itemId'] @@ -18,9 +19,9 @@ class FileAdmin(admin.ModelAdmin): admin.site.register(models.File, FileAdmin) + class InstanceAdmin(admin.ModelAdmin): search_fields = ['name', 'folder', 'volume__name'] form = InstanceAdminForm admin.site.register(models.Instance, InstanceAdmin) - diff --git a/pandora/archive/extract.py b/pandora/archive/extract.py index 95a64128..87241eaa 100644 --- a/pandora/archive/extract.py +++ b/pandora/archive/extract.py @@ -3,33 +3,31 @@ from __future__ import division, with_statement import os -from os.path import abspath, join, dirname, exists +from os.path import exists import fractions import subprocess -import sys -import shutil import tempfile import time -import re import math from glob import glob import numpy as np import Image -import ox -from ox.utils import json img_extension='jpg' FFMPEG2THEORA = 'ffmpeg2theora' + class AspectRatio(fractions.Fraction): + def __new__(cls, numerator, denominator=None): if not denominator: ratio = map(int, numerator.split(':')) - if len(ratio) == 1: ratio.append(1) + if len(ratio) == 1: + ratio.append(1) numerator = ratio[0] denominator = ratio[1] #if its close enough to the common aspect ratios rather use that @@ -45,6 +43,7 @@ class AspectRatio(fractions.Fraction): def ratio(self): return "%d:%d" % (self.numerator, self.denominator) + def stream(video, target, profile, info): if not os.path.exists(target): fdir = os.path.dirname(target) @@ -118,8 +117,8 @@ def stream(video, target, profile, info): bpp = 0.17 fps = AspectRatio(info['video'][0]['framerate']) - width = int(dar * height) - width += width % 2 + width = int(dar * height) + width += width % 2 bitrate = height*width*fps*bpp/1000 aspect = dar.ratio @@ -184,7 +183,7 @@ def stream(video, target, profile, info): '-me_range', '16', '-g', '250', #FIXME: should this be related to fps? '-keyint_min', '25', #FIXME: should this be related to fps? - '-sc_threshold','40', + '-sc_threshold', '40', '-i_qfactor', '0.71', '-qmin', '10', '-qmax', '51', '-qdiff', '4' @@ -200,19 +199,20 @@ def stream(video, target, profile, info): if format == 'mp4': cmd += ["%s.mp4"%target] else: - cmd += ['-f','webm', target] + cmd += ['-f', 'webm', target] print cmd p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=open('/dev/null', 'w'), stderr=subprocess.STDOUT) p.communicate() if format == 'mp4': - cmd = ['qt-faststart', "%s.mp4"%target, target] + cmd = ['qt-faststart', "%s.mp4"%target, target] print cmd p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=open('/dev/null', 'w'), stderr=subprocess.STDOUT) p.communicate() os.unlink("%s.mp4"%target) return True + def run_command(cmd, timeout=10): #print cmd p = subprocess.Popen(cmd, stdout=open('/dev/null', 'w'), stderr=subprocess.STDOUT) @@ -226,6 +226,7 @@ def run_command(cmd, timeout=10): killedpid, stat = os.waitpid(p.pid, os.WNOHANG) return p.returncode + def frame(videoFile, frame, position, width=128, redo=False): ''' params: @@ -243,6 +244,7 @@ def frame(videoFile, frame, position, width=128, redo=False): cmd = ['oxframe', '-i', videoFile, '-o', frame, '-p', str(position), '-x', str(width)] run_command(cmd) + def resize_image(image_source, image_output, width=None, size=None): if exists(image_source): source = Image.open(image_source).convert('RGB') @@ -257,7 +259,7 @@ def resize_image(image_source, image_output, width=None, size=None): height = size width = int(height * (float(source_width) / source_height)) width = width - width % 2 - + else: height = int(width / (float(source_width) / source_height)) height = height - height % 2 @@ -269,12 +271,13 @@ def resize_image(image_source, image_output, width=None, size=None): output = source.resize((width, height), resize_method) output.save(image_output) + def timeline(video, prefix): cmd = ['oxtimeline', '-i', video, '-o', prefix] p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) p.wait() -#stats based on timeline images + def average_color(prefix): height = 64 width = 1500 @@ -294,10 +297,12 @@ def average_color(prefix): color += p return list(map(float, color)) + def get_distance(rgb0, rgb1): dst = math.sqrt(pow(rgb0[0] - rgb1[0], 2) + pow(rgb0[0] - rgb1[0], 2) + pow(rgb0[0] - rgb1[0], 2)) return dst / math.sqrt(3 * pow(255, 2)) + def cuts(prefix): cuts = [] fps = 25 @@ -324,6 +329,7 @@ def cuts(prefix): cuts.append(frame / fps) return cuts + def divide(num, by): # >>> divide(100, 3) # [33, 33, 34] @@ -334,9 +340,10 @@ def divide(num, by): arr.append(div + (i > by - 1 - mod)) return arr + def timeline_strip(item, cuts, info, prefix): _debug = False - duration = info['duration'] + duration = info['duration'] video_height = info['video'][0]['height'] video_width = info['video'][0]['width'] video_ratio = video_width / video_height @@ -392,6 +399,7 @@ def timeline_strip(item, cuts, info, prefix): print 'writing', timeline_file timeline_image.save(timeline_file) + def chop(video, start, end): t = end - start tmp = tempfile.mkdtemp() @@ -401,7 +409,7 @@ def chop(video, start, end): '-y', '-i', video, '-ss', '%.3f'%start, - '-t','%.3f'%t, + '-t', '%.3f'%t, '-vcodec', 'copy', '-acodec', 'copy', '-f', 'webm', diff --git a/pandora/archive/forms.py b/pandora/archive/forms.py index 59758ad5..291d77c5 100644 --- a/pandora/archive/forms.py +++ b/pandora/archive/forms.py @@ -1,4 +1,4 @@ -from ajax_filtered_fields.forms import AjaxManyToManyField, ForeignKeyByLetter +from ajax_filtered_fields.forms import ForeignKeyByLetter from django.conf import settings from django import forms @@ -10,6 +10,8 @@ ajax_filtered_js = ( settings.STATIC_URL + 'js/jquery/jquery.js', settings.STATIC_URL + 'js/ajax_filtered_fields.js', ) + + class FileAdminForm(forms.ModelForm): item = ForeignKeyByLetter(models.Item, field_name='itemId') @@ -28,4 +30,3 @@ class InstanceAdminForm(forms.ModelForm): class Media: js = ajax_filtered_js - diff --git a/pandora/archive/models.py b/pandora/archive/models.py index eb9a788a..cd5793a0 100644 --- a/pandora/archive/models.py +++ b/pandora/archive/models.py @@ -1,31 +1,23 @@ # -*- coding: utf-8 -*- # vi:si:et:sw=4:sts=4:ts=4 from __future__ import division -from datetime import datetime + import os.path -import random import re import time from django.db import models -from django.db.models import Q from django.contrib.auth.models import User -from django.core.files.base import ContentFile -from django.utils import simplejson as json from django.conf import settings from ox.django import fields import ox -from ox import stripTags -from ox.normalize import canonicalTitle, canonicalName -from firefogg import Firefogg +from ox.normalize import canonicalTitle import chardet from item import utils from item.models import Item -import extract - class File(models.Model): created = models.DateTimeField(auto_now_add=True) @@ -37,7 +29,7 @@ class File(models.Model): item = models.ForeignKey(Item, related_name='files') name = models.CharField(max_length=2048, default="") # canoncial path/file - sort_name = models.CharField(max_length=2048, default="") # sort path/file name + sort_name = models.CharField(max_length=2048, default="") # sort name part = models.CharField(default="", max_length=255) version = models.CharField(default="", max_length=255) # sort path/file name @@ -146,13 +138,14 @@ class File(models.Model): return None def srt(self): + def _detectEncoding(fp): - bomDict={ # bytepattern : name - (0x00, 0x00, 0xFE, 0xFF) : "utf_32_be", - (0xFF, 0xFE, 0x00, 0x00) : "utf_32_le", - (0xFE, 0xFF, None, None) : "utf_16_be", - (0xFF, 0xFE, None, None) : "utf_16_le", - (0xEF, 0xBB, 0xBF, None) : "utf_8", + bomDict={ # bytepattern : name + (0x00, 0x00, 0xFE, 0xFF): "utf_32_be", + (0xFF, 0xFE, 0x00, 0x00): "utf_32_le", + (0xFE, 0xFF, None, None): "utf_16_be", + (0xFF, 0xFE, None, None): "utf_16_le", + (0xEF, 0xBB, 0xBF, None): "utf_8", } # go to beginning of file and get the first 4 bytes @@ -162,15 +155,15 @@ class File(models.Model): # try bom detection using 4 bytes, 3 bytes, or 2 bytes bomDetection = bomDict.get((byte1, byte2, byte3, byte4)) - if not bomDetection : + if not bomDetection: bomDetection = bomDict.get((byte1, byte2, byte3, None)) - if not bomDetection : + if not bomDetection: bomDetection = bomDict.get((byte1, byte2, None, None)) ## if BOM detected, we're done :-) fp.seek(oldFP) - if bomDetection : - return bomDetection + if bomDetection: + return bomDetection encoding = 'latin-1' #more character detecting magick using http://chardet.feedparser.org/ @@ -221,7 +214,9 @@ class File(models.Model): return True return False + class Volume(models.Model): + class Meta: unique_together = ("user", "name") @@ -234,7 +229,9 @@ class Volume(models.Model): def __unicode__(self): return u"%s's %s"% (self.user, self.name) + class Instance(models.Model): + class Meta: unique_together = ("name", "folder", "volume") @@ -258,12 +255,15 @@ class Instance(models.Model): def itemId(self): return File.objects.get(oshash=self.oshash).itemId + def frame_path(frame, name): ext = os.path.splitext(name)[-1] name = "%s%s" % (frame.position, ext) return frame.file.path(name) + class Frame(models.Model): + class Meta: unique_together = ("file", "position") created = models.DateTimeField(auto_now_add=True) @@ -282,5 +282,3 @@ class Frame(models.Model): def __unicode__(self): return u'%s at %s' % (self.file, self.position) - - diff --git a/pandora/archive/tasks.py b/pandora/archive/tasks.py index c6a9f6e1..f96c9764 100644 --- a/pandora/archive/tasks.py +++ b/pandora/archive/tasks.py @@ -1,17 +1,15 @@ # -*- coding: utf-8 -*- # vi:si:et:sw=4:sts=4:ts=4 -from datetime import timedelta import os -import re -from celery.decorators import task, periodic_task +from celery.decorators import task -from item.utils import oxid, parse_path +from item.utils import parse_path from item.models import get_item -import item.tasks import models + @task(ignore_resulsts=True, queue='default') def update_files(user, volume, files): user = models.User.objects.get(username=user) @@ -33,9 +31,9 @@ def update_files(user, volume, files): same_folder = models.Instance.objects.filter(folder=folder, volume=volume) if same_folder.count() > 0: - item = same_folder[0].file.item + i = same_folder[0].file.item else: - item = None + i = None path = os.path.join(folder, name) @@ -48,7 +46,7 @@ def update_files(user, volume, files): setattr(instance, key, f[key]) updated=True if updated: - instance.save() + instance.save() else: #look if oshash is known file_objects = models.File.objects.filter(oshash=oshash) @@ -56,13 +54,13 @@ def update_files(user, volume, files): file_object = file_objects[0] #new oshash, add to database else: - if not item: + if not i: item_info = parse_path(folder) - item = get_item(item_info) + i = get_item(item_info) file_object = models.File() file_object.oshash = oshash file_object.name = name - file_object.item = item + file_object.item = i file_object.save() instance = models.Instance() instance.volume = volume @@ -74,4 +72,3 @@ def update_files(user, volume, files): #remove deleted files #FIXME: can this have any bad consequences? i.e. on the selction of used item files. models.Instance.objects.filter(volume=volume).exclude(file__oshash__in=all_files).delete() - diff --git a/pandora/archive/tests.py b/pandora/archive/tests.py index 2247054b..927cadf0 100644 --- a/pandora/archive/tests.py +++ b/pandora/archive/tests.py @@ -7,7 +7,9 @@ Replace these with more appropriate tests for your application. from django.test import TestCase + class SimpleTest(TestCase): + def test_basic_addition(self): """ Tests that 1 + 1 always equals 2. @@ -20,4 +22,3 @@ Another way to test that 1 + 1 is equal to 2. >>> 1 + 1 == 2 True """} - diff --git a/pandora/archive/views.py b/pandora/archive/views.py index cc7dd12b..3f2cf5ed 100644 --- a/pandora/archive/views.py +++ b/pandora/archive/views.py @@ -2,29 +2,18 @@ # vi:si:et:sw=4:sts=4:ts=4 from __future__ import division import os.path -import re from datetime import datetime -from urllib2 import unquote -import mimetypes from django import forms -from django.core.paginator import Paginator -from django.contrib.auth.decorators import login_required -from django.contrib.auth.models import User -from django.db.models import Q, Avg, Count, Sum -from django.http import HttpResponse, Http404 -from django.shortcuts import render_to_response, get_object_or_404, get_list_or_404, redirect -from django.template import RequestContext +from django.shortcuts import get_object_or_404, redirect from django.conf import settings 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 -from ox.django.http import HttpFileResponse from ox.django.views import task_status -import ox -from item.utils import oxid, parse_path +from item.utils import parse_path from item.models import get_item import item.tasks from api.actions import actions @@ -47,6 +36,7 @@ def removeVolume(request): return render_to_json_response(response) actions.register(removeVolume) + @login_required_json def update(request): ''' @@ -111,12 +101,14 @@ def update(request): return render_to_json_response(response) actions.register(update) + @login_required_json def encodingProfile(request): response = json_response({'profile': settings.VIDEO_PROFILE}) return render_to_json_response(response) actions.register(encodingProfile) + @login_required_json def upload(request): ''' @@ -157,11 +149,13 @@ def upload(request): return render_to_json_response(response) actions.register(upload) + class VideoChunkForm(forms.Form): chunk = forms.FileField() chunkId = forms.IntegerField(required=False) done = forms.IntegerField(required=False) + @login_required_json def firefogg_upload(request): profile = request.GET['profile'] @@ -189,7 +183,7 @@ def firefogg_upload(request): #FIXME: this fails badly if rabbitmq goes down try: t = item.tasks.update_streams.delay((f.item.itemId)) - data['resultUrl'] = t.task_id + response['resultUrl'] = t.task_id except: pass response['result'] = 1 @@ -213,6 +207,7 @@ def firefogg_upload(request): response = json_response(status=400, text='this request requires POST') return render_to_json_response(response) + @login_required_json def taskStatus(request): #FIXME: should check if user has permissions to get status @@ -223,6 +218,7 @@ def taskStatus(request): return render_to_json_response(response) actions.register(taskStatus) + @login_required_json def editFile(request): ''' @@ -257,6 +253,7 @@ def editFile(request): return render_to_json_response(response) actions.register(editFile) + def lookup_file(request, oshash): f = get_object_or_404(models.File, oshash=oshash) return redirect(f.item.get_absolute_url()) @@ -270,34 +267,34 @@ def fileInfo(request): 'data': {imdbId:string }} ''' if 'data' in request.POST: - oshash = json.loads(request.POST['data']) - elif 'oshash' in request.GET: - oshash = request.GET['oshash'] + oshash = json.loads(request.POST['data']) + elif 'oshash' in request.GET: + oshash = request.GET['oshash'] f = models.ItemFile.objects.get(oshash=oshash) response = {'data': f.json()} return render_to_json_response(response) actions.register(fileInfo) def subtitles(request): - ''' - param data - oshash string - language string - subtitle string - return - if no language is provided: - {data: {languages: array}} - if language is set: - {data: {subtitle: string}} - if subtitle is set: - saves subtitle for given language - ''' + ''' + param data + oshash string + language string + subtitle string + return + if no language is provided: + {data: {languages: array}} + if language is set: + {data: {subtitle: string}} + if subtitle is set: + saves subtitle for given language + ''' if 'data' in request.POST: - data = json.loads(request.POST['data']) - oshash = data['oshash'] - language = data.get('language', None) - srt = data.get('subtitle', None) - if srt: + data = json.loads(request.POST['data']) + oshash = data['oshash'] + language = data.get('language', None) + srt = data.get('subtitle', None) + if srt: user = request.user sub = models.Subtitles.objects.get_or_create(user, oshash, language) sub.srt = srt @@ -307,8 +304,8 @@ def subtitles(request): if language: q = models.Subtitles.objects.filter(item_file__oshash=oshash, language=language) if q.count() > 0: - response['data']['subtitle'] = q[0].srt - return render_to_json_response(response) + response['data']['subtitle'] = q[0].srt + return render_to_json_response(response) l = models.Subtitles.objects.filter(item_file__oshash=oshash).values('language') response['data']['languages'] = [f['language'] for f in l] return render_to_json_response(response) diff --git a/pandora/date/admin.py b/pandora/date/admin.py index b1141f0a..a02f4185 100644 --- a/pandora/date/admin.py +++ b/pandora/date/admin.py @@ -9,4 +9,3 @@ import models class DateAdmin(admin.ModelAdmin): search_fields = ['name'] admin.site.register(models.Date, DateAdmin) - diff --git a/pandora/date/managers.py b/pandora/date/managers.py index 9a7e4e99..78333b5c 100644 --- a/pandora/date/managers.py +++ b/pandora/date/managers.py @@ -1,15 +1,11 @@ # -*- coding: utf-8 -*- # vi:si:et:sw=4:sts=4:ts=4 -import re -from ox.utils import json -from django.contrib.auth.models import User -from django.core.exceptions import ObjectDoesNotExist from django.db.models import Q, Manager - -import models + class DateManager(Manager): + def get_query_set(self): return super(DateManager, self).get_query_set() diff --git a/pandora/date/models.py b/pandora/date/models.py index ac44ed09..fc4057d8 100644 --- a/pandora/date/models.py +++ b/pandora/date/models.py @@ -3,13 +3,12 @@ from __future__ import division, with_statement from django.db import models -from django.db.models import Q -from django.conf import settings from ox.django import fields import managers + class Date(models.Model): ''' Dates are dates in time that can be once or recurring, @@ -41,4 +40,3 @@ class Date(models.Model): self.name_sort = self.name self.name_find = self.name + '||'.join(self.aliases) super(Date, self).save(*args, **kwargs) - diff --git a/pandora/date/tests.py b/pandora/date/tests.py index 2247054b..927cadf0 100644 --- a/pandora/date/tests.py +++ b/pandora/date/tests.py @@ -7,7 +7,9 @@ Replace these with more appropriate tests for your application. from django.test import TestCase + class SimpleTest(TestCase): + def test_basic_addition(self): """ Tests that 1 + 1 always equals 2. @@ -20,4 +22,3 @@ Another way to test that 1 + 1 is equal to 2. >>> 1 + 1 == 2 True """} - diff --git a/pandora/date/views.py b/pandora/date/views.py index 348f56c6..7c68ac17 100644 --- a/pandora/date/views.py +++ b/pandora/date/views.py @@ -1,32 +1,15 @@ # -*- coding: utf-8 -*- # vi:si:et:sw=4:sts=4:ts=4 from __future__ import division -import os.path -import re -from datetime import datetime -from urllib2 import unquote -import mimetypes - -from django import forms -from django.core.paginator import Paginator -from django.contrib.auth.decorators import login_required -from django.contrib.auth.models import User -from django.db.models import Q, Avg, Count, Sum -from django.http import HttpResponse, Http404 -from django.shortcuts import render_to_response, get_object_or_404, get_list_or_404, redirect -from django.template import RequestContext -from django.conf import settings 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 -from ox.django.http import HttpFileResponse -import ox import models from api.actions import actions + @login_required_json def addDate(request): data = json.loads(request.POST['data']) @@ -39,6 +22,7 @@ def addDate(request): return render_to_json_response(response) actions.register(addDate) + @login_required_json def editDate(request): ''' @@ -69,17 +53,19 @@ def editDate(request): return render_to_json_response(response) actions.register(editDate) + @login_required_json def removeDate(request): response = json_response(status=501, text='not implemented') return render_to_json_response(response) actions.register(removeDate) + def findDate(request): ''' param data {'query': query, 'sort': array, 'range': array} - + query: query object, more on query syntax at https://wiki.0x2620.org/wiki/pandora/QuerySyntax sort: array of key, operator dics @@ -102,7 +88,7 @@ def findDate(request): Positions param data {'query': query, 'ids': []} - + query: query object, more on query syntax at https://wiki.0x2620.org/wiki/pandora/QuerySyntax ids: ids of dates for which positions are required @@ -111,8 +97,7 @@ Positions response = json_response(status=200, text='ok') response['data']['places'] = [] #FIXME: add coordinates to limit search - for p in Dates.objects.find(data['query']): + for p in models.Date.objects.find(data['query']): response['data']['dates'].append(p.json()) return render_to_json_response(response) actions.register(findDate) - diff --git a/pandora/item/admin.py b/pandora/item/admin.py index 09daa8f3..0561b70a 100644 --- a/pandora/item/admin.py +++ b/pandora/item/admin.py @@ -5,6 +5,7 @@ from django.contrib import admin import models + class ItemAdmin(admin.ModelAdmin): search_fields = ['itemId', 'data', 'external_data'] list_display = ['available', 'itemId', '__unicode__'] @@ -12,8 +13,7 @@ class ItemAdmin(admin.ModelAdmin): admin.site.register(models.Item, ItemAdmin) + class PropertyAdmin(admin.ModelAdmin): search_fields = ['name', 'title'] admin.site.register(models.Property, PropertyAdmin) - - diff --git a/pandora/item/forms.py b/pandora/item/forms.py index dc56ffea..664b728b 100644 --- a/pandora/item/forms.py +++ b/pandora/item/forms.py @@ -1,8 +1,5 @@ from ajax_filtered_fields.forms import AjaxManyToManyField, ForeignKeyByLetter from django.conf import settings -from django import forms - -import models ajax_filtered_js = ( settings.ADMIN_MEDIA_PREFIX + "js/SelectBox.js", diff --git a/pandora/item/managers.py b/pandora/item/managers.py index 167bc251..06f0d3ba 100644 --- a/pandora/item/managers.py +++ b/pandora/item/managers.py @@ -1,14 +1,9 @@ # -*- coding: utf-8 -*- # vi:si:et:sw=4:sts=4:ts=4 -import re from datetime import datetime -from urllib2 import unquote -from ox.utils import json -from django.contrib.auth.models import User -from django.core.exceptions import ObjectDoesNotExist from django.db.models import Q, Manager - + import models @@ -21,6 +16,7 @@ def keyType(key): return "float" return "string" + def parseCondition(condition): ''' condition: { @@ -33,13 +29,15 @@ def parseCondition(condition): operator: "!=" } ... - ''' + ''' k = condition.get('key', 'all') k = {'id': 'itemId'}.get(k, k) - if not k: k = 'all' + if not k: + k = 'all' v = condition['value'] op = condition.get('operator', None) - if not op: op = '~' + if not op: + op = '~' if op.startswith('!'): op = op[1:] exclude = True @@ -66,16 +64,17 @@ def parseCondition(condition): k = str(k) if exclude: if in_find and not k.startswith('itemId'): - q = ~Q(**{'find__key':k, value_key:v}) + q = ~Q(**{'find__key': k, value_key: v}) else: - q = ~Q(**{k:v}) + q = ~Q(**{k: v}) else: if in_find and not k.startswith('itemId'): - q = Q(**{'find__key':k, value_key:v}) + q = Q(**{'find__key': k, value_key: v}) else: - q = Q(**{k:v}) + q = Q(**{k: v}) return q else: #number or date + def parseDate(d): while len(d) < 3: d.append(1) @@ -90,11 +89,11 @@ def parseCondition(condition): if exclude: #!1960-1970 k1 = 'value__lt' k2 = 'value__gte' - return Q(**{'find__key': k, k1:v1})|Q(**{'find__key': k, k2:v2}) + return Q(**{'find__key': k, k1: v1})|Q(**{'find__key': k, k2: v2}) else: #1960-1970 k1 = 'value__gte' k2 = 'value__lt' - return Q(**{'find__key': k, k1:v1})&Q(**{'find__key': k, k2:v2}) + return Q(**{'find__key': k, k1: v1})&Q(**{'find__key': k, k2: v2}) else: if keyType(k) == "date": v = parseDate(v.split('.')) @@ -113,9 +112,10 @@ def parseCondition(condition): vk = str(vk) if exclude: #!1960 - return ~Q(**{'find__key': k, vk:v}) + return ~Q(**{'find__key': k, vk: v}) else: #1960 - return Q(**{'find__key': k, vk:v}) + return Q(**{'find__key': k, vk: v}) + def parseConditions(conditions, operator): ''' @@ -135,16 +135,18 @@ def parseConditions(conditions, operator): } ], operator: "&" - ''' + ''' conn = [] for condition in conditions: if 'conditions' in condition: q = parseConditions(condition['conditions'], condition.get('operator', '&')) - if q: conn.append(q) + if q: + conn.append(q) pass else: - if condition.get('value', '') != '' or condition.get('operator', '') == '=': + if condition.get('value', '') != '' or \ + condition.get('operator', '') == '=': conn.append(parseCondition(condition)) if conn: q = conn[0] @@ -156,7 +158,9 @@ def parseConditions(conditions, operator): return q return None + class ItemManager(Manager): + def get_query_set(self): return super(ItemManager, self).get_query_set() @@ -165,13 +169,14 @@ class ItemManager(Manager): l = l.split(":") only_public = True if not user.is_anonymous(): - if len(l) == 1: l = [request.user.username] + l - if request.user.username == l[0]: + if len(l) == 1: + l = [user.username] + l + if user.username == l[0]: only_public = False if len(l) == 2: lqs = models.List.objects.filter(name=l[1], user__username=l[0]) if only_public: - lqs = qls.filter(public=True) + lqs = lqs.filter(public=True) if lqs.count() == 1: qs = qs.filter(listitem__list__id=lqs[0].id) return qs @@ -196,7 +201,7 @@ class ItemManager(Manager): ], operator: "&" } - ''' + ''' #join query with operator qs = self.get_query_set() @@ -212,4 +217,3 @@ class ItemManager(Manager): l = data.get('list', 'all') qs = self.filter_list(qs, l, user) return qs - diff --git a/pandora/item/models.py b/pandora/item/models.py index 1f8cad83..6dda1951 100644 --- a/pandora/item/models.py +++ b/pandora/item/models.py @@ -4,26 +4,18 @@ from __future__ import division, with_statement from datetime import datetime import os.path -import math -import random -import re import subprocess -import unicodedata from glob import glob from django.db import models -from django.db.models import Q -from django.contrib.auth.models import User from django.core.files.base import ContentFile from django.utils import simplejson as json from django.conf import settings -from ox.django import fields import ox -from ox import stripTags +from ox.django import fields +from ox.normalize import canonicalTitle import ox.web.imdb -from ox.normalize import canonicalTitle, canonicalName -from firefogg import Firefogg import managers import utils @@ -31,23 +23,23 @@ import tasks from archive import extract from annotaion.models import Annotation, Layer -from person.models import get_name_sort, Person +from person.models import get_name_sort def siteJson(): - r = {} - r['findKeys'] = [{"id": "all", "title": "All"}] - for p in Property.objects.all(): - if p.find: - title = p.title - if not title: - title = p.name.capitalize() - f = {"id": p.name, "title": title} - f['autocomplete'] = p.autocomplete - r['findKeys'].append(f) - - r['groups'] = [p.name for p in Property.objects.filter(group=True)] - r['layers'] = [l.json() for l in Layer.objects.all()] + r = {} + r['findKeys'] = [{"id": "all", "title": "All"}] + for p in Property.objects.all(): + if p.find: + title = p.title + if not title: + title = p.name.capitalize() + f = {"id": p.name, "title": title} + f['autocomplete'] = p.autocomplete + r['findKeys'].append(f) + + r['groups'] = [p.name for p in Property.objects.filter(group=True)] + r['layers'] = [l.json() for l in Layer.objects.all()] r['itemViews'] = [ {"id": "info", "title": "Info"}, @@ -81,28 +73,28 @@ def siteJson(): {"id": "public", "title": "Public Lists"}, {"id": "featured", "title": "Featured Lists"} ] - r['sortKeys'] = [] - for p in Property.objects.exclude(sort=''): - title = p.title - if not title: - title = p.name.capitalize() + r['sortKeys'] = [] + for p in Property.objects.exclude(sort=''): + title = p.title + if not title: + title = p.name.capitalize() - f = { - "id": p.name, - "title": title, - "operator": p.operator, - "align": p.align, - "width": p.width, - } - if not p.removable: - f['removable'] = False - r['sortKeys'].append(f) - r['sortKeys'].append([{"id": "id", "title": "ID", "operator": "", "align": "left", "width": 90}]) + f = { + "id": p.name, + "title": title, + "operator": p.operator, + "align": p.align, + "width": p.width, + } + if not p.removable: + f['removable'] = False + r['sortKeys'].append(f) + r['sortKeys'].append([{"id": "id", "title": "ID", "operator": "", "align": "left", "width": 90}]) - r['totals'] = [{"id": "items"}] - for p in Property.objects.filter(totals=True): - f = {'id': p.name, 'admin': p.admin} - r['totals'].append(f) + r['totals'] = [{"id": "items"}] + for p in Property.objects.filter(totals=True): + f = {'id': p.name, 'admin': p.admin} + r['totals'].append(f) #FIXME: defaults should also be populated from properties r["user"] = { @@ -127,7 +119,8 @@ def siteJson(): }, "username": "" } - return r + return r + def get_item(info): ''' @@ -176,28 +169,30 @@ def get_item(info): item.save() return item + class Property(models.Model): + class Meta: ordering = ('position', ) verbose_name_plural = "Properties" - + name = models.CharField(null=True, max_length=255, unique=True) title = models.CharField(null=True, max_length=255, blank=True) - #text, string, string from list(fixme), event, place, person + #text, string, string from list(fixme), event, place, person type = models.CharField(null=True, max_length=255) array = models.BooleanField(default=False) position = models.IntegerField(default=0) width = models.IntegerField(default=180) align = models.CharField(null=True, max_length=255, default='left') operator = models.CharField(null=True, max_length=5, default='', blank=True) - default = models.BooleanField('Enabled by default', default=False) - removable = models.BooleanField(default=True) + default = models.BooleanField('Enabled by default', default=False) + removable = models.BooleanField(default=True) - #sort values: title, string, integer, float, date + #sort values: title, string, integer, float, date sort = models.CharField(null=True, max_length=255, blank=True) - find = models.BooleanField(default=False) - autocomplete = models.BooleanField(default=False) - group = models.BooleanField(default=False) + find = models.BooleanField(default=False) + autocomplete = models.BooleanField(default=False) + group = models.BooleanField(default=False) totals = models.BooleanField(default=False) admin = models.BooleanField(default=False) @@ -207,19 +202,20 @@ class Property(models.Model): return self.title return self.name - def json(self): - j = {} - for key in ('type', 'sort', 'title', 'array', 'totals', 'admin'): - value = getattr(self, key) - if value: - j[key] = value - return j + def json(self): + j = {} + for key in ('type', 'sort', 'title', 'array', 'totals', 'admin'): + value = getattr(self, key) + if value: + j[key] = value + return j def save(self, *args, **kwargs): if not self.title: self.title = self.name.capitalize() super(Property, self).save(*args, **kwargs) - + + class Item(models.Model): person_keys = ('director', 'writer', 'producer', 'editor', 'cinematographer', 'actor', 'character') facet_keys = person_keys + ('country', 'language', 'genre', 'keyword') @@ -228,7 +224,7 @@ class Item(models.Model): published = models.DateTimeField(default=datetime.now, editable=False) #only items that have data from files are available, - #this is indicated by setting available to True + #this is indicated by setting available to True available = models.BooleanField(default=False, db_index=True) itemId = models.CharField(max_length=128, unique=True, blank=True) oxdbId = models.CharField(max_length=42, unique=True, blank=True) @@ -258,9 +254,9 @@ class Item(models.Model): def edit(self, data): #FIXME: how to map the keys to the right place to write them to? - for key in data: - if key != 'id': - setattr(self.data, key, data[key]) + for key in data: + if key != 'id': + setattr(self.data, key, data[key]) self.oxdb.save() self.save() @@ -339,11 +335,11 @@ class Item(models.Model): #FIXME: this should not be used _public_fields = { 'itemId': 'id', - 'title': 'title', - 'year': 'year', + 'title': 'title', + 'year': 'year', - 'runtime': 'runtime', - 'release_date': 'release_date', + 'runtime': 'runtime', + 'release_date': 'release_date', 'countries': 'country', 'directors': 'director', @@ -402,7 +398,7 @@ class Item(models.Model): s = self.streams.all()[0] if s.video and s.info: stream['duration'] = s.info['duration'] - if 'video' in s.info and s.info['video']: + if 'video' in s.info and s.info['video']: stream['aspectRatio'] = s.info['video'][0]['width'] / s.info['video'][0]['height'] if settings.XSENDFILE or settings.XACCELREDIRECT: stream['baseUrl'] = '/%s' % self.itemId @@ -414,7 +410,7 @@ class Item(models.Model): def get_layers(self): layers = {} layers['cuts'] = self.data.get('cuts', {}) - + layers['subtitles'] = {} #FIXME: subtitles should be stored in Annotation qs = self.files.filter(is_subtitle=True, is_main=True, available=True) @@ -468,7 +464,9 @@ class Item(models.Model): ''' Search related functions ''' + def update_find(self): + def save(key, value): f, created = ItemFind.objects.get_or_create(item=self, key=key) if value not in ('', '||'): @@ -478,7 +476,7 @@ class Item(models.Model): f.delete() save('title', '\n'.join([self.get('title'), self.get('original_title', '')])) - + #FIXME: filter us/int title #f.title += ' '.join([t.title for t in self.alternative_titles()]) @@ -537,17 +535,17 @@ class Item(models.Model): for key in ('keywords', 'genres', 'cast', 'summary', 'trivia', 'connections'): setattr(s, key, len(self.get(key, ''))) - + s.itemId = self.itemId.replace('0x', 'xx') s.rating = self.get('rating', -1) s.votes = self.get('votes', -1) # data from related subtitles s.scenes = 0 #FIXME - s.dialog = 0 #FIXME - s.words = 0 #FIXME - s.wpm = 0 #FIXME - s.risk = 0 #FIXME + s.dialog = 0 #FIXME + s.words = 0 #FIXME + s.wpm = 0 #FIXME + s.risk = 0 #FIXME # data from related files videos = self.main_videos() if len(videos) > 0: @@ -574,7 +572,7 @@ class Item(models.Model): s.color = int(sum(self.data.get('color', []))) s.saturation = 0 #FIXME s.brightness = 0 #FIXME - + s.cuts = len(self.data.get('cuts', [])) if s.duration: s.cutsperminute = s.cuts / (s.duration/60) @@ -585,13 +583,13 @@ class Item(models.Model): if not getattr(s, key): setattr(s, key, u'zzzzzzzzzzzzzzzzzzzzzzzzz') if not s.year: - s.year_desc = ''; - s.year = '9999'; + s.year_desc = '' + s.year = '9999' #FIXME: also deal with number based rows like genre, keywords etc s.save() def update_facets(self): - #FIXME: what to do with Unkown Director, Year, Country etc. + #FIXME: what to do with Unkown Director, Year, Country etc. for key in self.facet_keys: if key == 'actor': current_values = [i[0] for i in self.get('actor', [])] @@ -616,7 +614,7 @@ class Item(models.Model): f, created = Facet.objects.get_or_create(key='year', value=year, value_sort=year, item=self) else: Facet.objects.filter(item=self, key='year').delete() - + def path(self, name=''): h = self.itemId return os.path.join('items', h[:2], h[2:4], h[4:6], h[6:], name) @@ -624,6 +622,7 @@ class Item(models.Model): ''' Video related functions ''' + def frame(self, position, width=128): stream = self.streams.filter(profile=settings.VIDEO_PROFILE+'.webm')[0] path = os.path.join(settings.MEDIA_ROOT, self.path(), 'frames', "%d"%width, "%s.jpg"%position) @@ -647,14 +646,14 @@ class Item(models.Model): first.width == v.width and first.height == v.height: return True return False - videos = filter(check, videos) + videos = filter(check, videos) return videos def update_streams(self): files = {} for f in self.main_videos(): files[utils.sort_title(f.name)] = f.video.path - + #FIXME: how to detect if something changed? if files: stream, created = Stream.objects.get_or_create(item=self, profile='%s.webm' % settings.VIDEO_PROFILE) @@ -693,6 +692,7 @@ class Item(models.Model): ''' Poster related functions ''' + def update_poster_urls(self): _current = {} for s in settings.POSTER_SERVICES: @@ -781,12 +781,14 @@ class Item(models.Model): p.wait() return posters.keys() + class ItemFind(models.Model): """ used to find items, item.update_find populates this table its used in manager.ItemManager """ + class Meta: unique_together = ("item", "key") @@ -794,8 +796,9 @@ class ItemFind(models.Model): key = models.CharField(max_length=200, db_index=True) value = models.TextField(blank=True) -#FIXME: make sort based on site.json + class ItemSort(models.Model): + #FIXME: make sort based on site.json """ used to sort items, all sort values are in here """ @@ -872,6 +875,7 @@ class ItemSort(models.Model): return tuple(fields) fields = classmethod(fields) + class Facet(models.Model): item = models.ForeignKey('Item', related_name='facets') key = models.CharField(max_length=200, db_index=True) @@ -883,7 +887,9 @@ class Facet(models.Model): self.value_sort = self.value super(Facet, self).save(*args, **kwargs) + class Stream(models.Model): + class Meta: unique_together = ("item", "profile") @@ -899,7 +905,7 @@ class Stream(models.Model): def path(self): return self.item.path(self.profile) - + def extract_derivatives(self): if settings.VIDEO_H264: profile = self.profile.replace('.webm', '.mp4') @@ -945,7 +951,9 @@ class Stream(models.Model): self.info = ox.avinfo(self.video.path) super(Stream, self).save(*args, **kwargs) + class PosterUrl(models.Model): + class Meta: unique_together = ("item", "service", "url") ordering = ('-height', ) @@ -958,4 +966,3 @@ class PosterUrl(models.Model): def __unicode__(self): return u'%s %s %dx%d' % (unicode(self.item), self.service, self.width, self.height) - diff --git a/pandora/item/tasks.py b/pandora/item/tasks.py index 2a90a681..f1d12091 100644 --- a/pandora/item/tasks.py +++ b/pandora/item/tasks.py @@ -11,16 +11,19 @@ import models def cronjob(**kwargs): print "do some cleanup stuff once a day" + @task(ignore_resulsts=True, queue='default') def update_poster(itemId): item = models.Item.objects.get(itemId=itemId) item.make_poster(True) + @task(ignore_resulsts=True, queue='default') def update_imdb(itemId): item = models.Item.objects.get(itemId=itemId) item.update_imdb() + @task(queue="encoding") def update_streams(itemId): ''' diff --git a/pandora/item/tests.py b/pandora/item/tests.py index 2247054b..927cadf0 100644 --- a/pandora/item/tests.py +++ b/pandora/item/tests.py @@ -7,7 +7,9 @@ Replace these with more appropriate tests for your application. from django.test import TestCase + class SimpleTest(TestCase): + def test_basic_addition(self): """ Tests that 1 + 1 always equals 2. @@ -20,4 +22,3 @@ Another way to test that 1 + 1 is equal to 2. >>> 1 + 1 == 2 True """} - diff --git a/pandora/item/urls.py b/pandora/item/urls.py index fee31d54..458d29e0 100644 --- a/pandora/item/urls.py +++ b/pandora/item/urls.py @@ -13,4 +13,3 @@ urlpatterns = patterns("item.views", (r'^(?P[A-Z0-9].*)/poster\.jpg$', 'poster'), (r'^(?P[A-Z0-9].*)/timelines/(?P.+)\.(?P\d+)\.(?P\d+)\.png$', 'timeline'), ) - diff --git a/pandora/item/utils.py b/pandora/item/utils.py index 017baa1c..d6d13050 100644 --- a/pandora/item/utils.py +++ b/pandora/item/utils.py @@ -2,17 +2,15 @@ # -*- coding: utf-8 -*- # vi:si:et:sw=4:sts=4:ts=4 # -import errno from decimal import Decimal import os -import sys import re import hashlib import unicodedata import ox import ox.iso -from ox.normalize import normalizeName, normalizeTitle, canonicalTitle +from ox.normalize import normalizeName, normalizeTitle def parse_decimal(string): @@ -22,11 +20,13 @@ def parse_decimal(string): d = string.split('/') return Decimal(d[0]) / Decimal(d[1]) + def plural_key(term): return { 'country': 'countries', }.get(term, term + 's') + def oxid(title, directors, year='', seriesTitle='', episodeTitle='', season=0, episode=0): director = ', '.join(directors) oxid_value = u"\n".join([title, director, year]) @@ -38,6 +38,7 @@ def oxid(title, directors, year='', seriesTitle='', episodeTitle='', season=0, e oxid += hashlib.sha1(oxid_value.encode('utf-8')).hexdigest()[:20] return u"0x" + oxid + def oxdb_id(title, directors=[], year='', season='', episode='', episode_title='', episode_directors=[], episode_year=''): # new id function, will replace oxid() def get_hash(string): @@ -51,11 +52,13 @@ def oxdb_id(title, directors=[], year='', season='', episode='', episode_title=' get_hash('\n'.join([str(episode), episode_director, episode_title, str(episode_year)]))[:8] return u'0x' + oxdb_id + def oxdb_directors(director): director = os.path.basename(os.path.dirname(director)) if director.endswith('_'): director = "%s." % director[:-1] directors = [normalizeName(d) for d in director.split('; ')] + def cleanup(director): director = director.strip() director = director.replace('Series', '') @@ -65,6 +68,7 @@ def oxdb_directors(director): directors = filter(None, [cleanup(d) for d in directors]) return directors + def oxdb_title(_title, searchTitle = False): ''' normalize filename to get item title @@ -83,7 +87,7 @@ def oxdb_title(_title, searchTitle = False): else: stitle = _title.split('.')[-2] if stitle.startswith('Episode '): - stitle = '' + stitle = '' if searchTitle: title = '"%s" %s' % (title, stitle) else: @@ -98,9 +102,11 @@ def oxdb_title(_title, searchTitle = False): title = normalizeTitle(title) return title + def oxdb_year(data): return ox.findRe(data, '\.(\d{4})\.') + def oxdb_series_title(path): seriesTitle = u'' if path.startswith('Series'): @@ -111,6 +117,7 @@ def oxdb_series_title(path): seriesTitle = t.split(" (S")[0] return seriesTitle + def oxdb_episode_title(path): episodeTitle = u'' ep = re.compile('.Episode \d+?\.(.*?)\.[a-zA-Z]').findall(path) @@ -118,6 +125,7 @@ def oxdb_episode_title(path): episodeTitle = ep[0] return episodeTitle + def oxdb_season_episode(path): season = 0 episode = 0 @@ -137,6 +145,7 @@ def oxdb_season_episode(path): episode = int(se[0][1]) return (season, episode) + def oxdb_part(path): part = 1 path = path.lower() @@ -149,6 +158,7 @@ def oxdb_part(path): part = p[0] return part + def parse_path(path): ''' expects path in the form @@ -178,6 +188,7 @@ def parse_path(path): episode_year='') return r + def sort_title(title): #title title = re.sub(u'[\'!¿¡,\.;\-"\:\*\[\]]', '', title) @@ -185,9 +196,8 @@ def sort_title(title): #title = title.replace(u'Æ', 'Ae') if isinstance(title, str): title = unicode(title) - title = unicodedata.normalize('NFKD',title) + title = unicodedata.normalize('NFKD', title) #pad numbered titles title = re.sub('(\d+)', lambda x: '%010d' % int(x.group(0)), title) return title.strip() - diff --git a/pandora/item/views.py b/pandora/item/views.py index 33882587..40def7d3 100644 --- a/pandora/item/views.py +++ b/pandora/item/views.py @@ -2,19 +2,10 @@ # vi:si:et:sw=4:sts=4:ts=4 from __future__ import division import os.path -import re -from datetime import datetime -from urllib2 import unquote -import mimetypes -from django import forms -from django.core.paginator import Paginator -from django.contrib.auth.decorators import login_required -from django.contrib.auth.models import User -from django.db.models import Q, Avg, Count, Sum +from django.db.models import Count, Sum from django.http import HttpResponse, Http404 -from django.shortcuts import render_to_response, get_object_or_404, get_list_or_404, redirect -from django.template import RequestContext +from django.shortcuts import get_object_or_404, redirect from django.conf import settings from ox.utils import json @@ -28,13 +19,13 @@ import models import utils import tasks -from user.models import get_user_json from archive.models import File from archive import extract from api.actions import actions + def _order_query(qs, sort, prefix='sort__'): order_by = [] if len(sort) == 1: @@ -44,7 +35,8 @@ def _order_query(qs, sort, prefix='sort__'): sort.append({'operator': '+', 'key': 'director'}) for e in sort: operator = e['operator'] - if operator != '-': operator = '' + if operator != '-': + operator = '' key = {'id': 'itemId'}.get(e['key'], e['key']) #FIXME: this should be a property of models.ItemSort!!! if operator=='-' and key in ('title', 'director', 'writer', 'producer', 'editor', 'cinematographer', 'language', 'country', 'year'): @@ -82,7 +74,7 @@ def find(request): 'sort': array, 'range': array } - + query: query object, more on query syntax at https://wiki.0x2620.org/wiki/pandora/QuerySyntax sort: array of key, operator dics @@ -111,7 +103,7 @@ Groups 'group': string, 'range': array } - + query: query object, more on query syntax at https://wiki.0x2620.org/wiki/pandora/QuerySyntax range: result range, array [from, to] @@ -134,7 +126,7 @@ Positions 'query': query, 'ids': [] } - + query: query object, more on query syntax at https://wiki.0x2620.org/wiki/pandora/QuerySyntax ids: ids of items for which positions are required @@ -143,7 +135,7 @@ Positions if settings.JSON_DEBUG: print json.dumps(data, indent=2) query = _parse_query(data, request.user) - + response = json_response({}) if 'group' in query: if 'sort' in query: @@ -183,7 +175,7 @@ Positions elif 'ids' in query: #FIXME: this does not scale for larger results qs = _order_query(query['qs'], query['sort']) - + response['data']['positions'] = {} ids = [j['itemId'] for j in qs.values('itemId')] response['data']['positions'] = _get_positions(ids, query['ids']) @@ -371,7 +363,8 @@ def getImdbId(request): } } ''' - imdbId = ox.web.imdb.guess(search_title, r['director'], timeout=-1) + data = json.loads(request.POST['data']) + imdbId = ox.web.imdb.getImdbId(data['title'], data['director'], timeout=-1) if imdbId: response = json_response({'imdbId': imdbId}) else: @@ -406,7 +399,8 @@ def poster(request, id, size=None): else: poster_path = item.poster.path else: - if not size: size='large' + if not size: + size='large' return redirect('http://0xdb.org/%s/poster.%s.jpg' % (item.itemId, size)) poster_path = os.path.join(settings.STATIC_ROOT, 'png/posterDark.48.png') return HttpFileResponse(poster_path, content_type='image/jpeg') diff --git a/pandora/itemlist/models.py b/pandora/itemlist/models.py index e116efb6..0320c578 100644 --- a/pandora/itemlist/models.py +++ b/pandora/itemlist/models.py @@ -2,29 +2,12 @@ # vi:si:et:sw=4:sts=4:ts=4 from __future__ import division, with_statement -from datetime import datetime -import os.path -import math -import random -import re -import subprocess -import unicodedata -from glob import glob - from django.db import models -from django.db.models import Q from django.contrib.auth.models import User -from django.core.files.base import ContentFile -from django.utils import simplejson as json -from django.conf import settings - -from ox.django import fields -import ox -from ox import stripTags -from ox.normalize import canonicalTitle, canonicalName class List(models.Model): + class Meta: unique_together = ("user", "name") @@ -33,7 +16,8 @@ class List(models.Model): user = models.ForeignKey(User) name = models.CharField(max_length=255) public = models.BooleanField(default=False) - items = models.ManyToManyField('item.Item', related_name='lists', through='ListItem') + items = models.ManyToManyField('item.Item', related_name='lists', + through='ListItem') def add(self, item): q = self.items.filter(id=item.id) @@ -55,6 +39,7 @@ class List(models.Model): return True return False + class ListItem(models.Model): created = models.DateTimeField(auto_now_add=True) modified = models.DateTimeField(auto_now=True) @@ -63,4 +48,3 @@ class ListItem(models.Model): def __unicode__(self): return u'%s in %s' % (self.item, self.list) - diff --git a/pandora/itemlist/tests.py b/pandora/itemlist/tests.py index 2247054b..927cadf0 100644 --- a/pandora/itemlist/tests.py +++ b/pandora/itemlist/tests.py @@ -7,7 +7,9 @@ Replace these with more appropriate tests for your application. from django.test import TestCase + class SimpleTest(TestCase): + def test_basic_addition(self): """ Tests that 1 + 1 always equals 2. @@ -20,4 +22,3 @@ Another way to test that 1 + 1 is equal to 2. >>> 1 + 1 == 2 True """} - diff --git a/pandora/itemlist/views.py b/pandora/itemlist/views.py index b2d534ef..84f03ee2 100644 --- a/pandora/itemlist/views.py +++ b/pandora/itemlist/views.py @@ -1,28 +1,12 @@ # -*- coding: utf-8 -*- # vi:si:et:sw=4:sts=4:ts=4 from __future__ import division -import os.path -import re -from datetime import datetime -from urllib2 import unquote -import mimetypes - -from django import forms -from django.core.paginator import Paginator -from django.contrib.auth.decorators import login_required -from django.contrib.auth.models import User -from django.db.models import Q, Avg, Count, Sum -from django.http import HttpResponse, Http404 -from django.shortcuts import render_to_response, get_object_or_404, get_list_or_404, redirect -from django.template import RequestContext -from django.conf import settings 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 -from ox.django.http import HttpFileResponse -import ox +import models from api.actions import actions @@ -48,12 +32,13 @@ def addListItem(request): response = json_response(status=403, text='not allowed') elif 'query' in data: response = json_response(status=501, text='not implemented') - + else: response = json_response(status=501, text='not implemented') return render_to_json_response(response) actions.register(addListItem) + @login_required_json def removeListItem(request): ''' @@ -76,12 +61,13 @@ def removeListItem(request): response = json_response(status=403, text='not allowed') elif 'query' in data: response = json_response(status=501, text='not implemented') - + else: response = json_response(status=501, text='not implemented') return render_to_json_response(response) actions.register(removeListItem) + @login_required_json def addList(request): ''' @@ -97,12 +83,11 @@ def addList(request): response = json_response(status=200, text='created') else: response = json_response(status=200, text='list already exists') - response['data']['errors'] = { - 'name': 'List already exists' - } + response['data']['errors'] = {'name': 'List already exists'} return render_to_json_response(response) actions.register(addList) + @login_required_json def editList(request): ''' @@ -124,6 +109,8 @@ def editList(request): return render_to_json_response(response) actions.register(editList) + +@login_required_json def removeList(request): ''' param data @@ -139,4 +126,3 @@ def removeList(request): response = json_response(status=403, text='not allowed') return render_to_json_response(response) actions.register(removeList) - diff --git a/pandora/person/models.py b/pandora/person/models.py index fa37de2d..41f78303 100644 --- a/pandora/person/models.py +++ b/pandora/person/models.py @@ -2,26 +2,12 @@ # vi:si:et:sw=4:sts=4:ts=4 from __future__ import division, with_statement -from datetime import datetime -import os.path -import math -import random -import re -import subprocess import unicodedata -from glob import glob from django.db import models -from django.db.models import Q -from django.contrib.auth.models import User -from django.core.files.base import ContentFile -from django.utils import simplejson as json -from django.conf import settings from ox.django import fields import ox -from ox import stripTags -from ox.normalize import canonicalTitle, canonicalName def get_name_sort(name): @@ -29,6 +15,7 @@ def get_name_sort(name): name_sort = unicodedata.normalize('NFKD', person.name_sort) return name_sort + class Person(models.Model): name = models.CharField(max_length=200) name_sort = models.CharField(max_length=200) @@ -67,4 +54,3 @@ class Person(models.Model): def json(self): return self.name - diff --git a/pandora/person/tests.py b/pandora/person/tests.py index 2247054b..927cadf0 100644 --- a/pandora/person/tests.py +++ b/pandora/person/tests.py @@ -7,7 +7,9 @@ Replace these with more appropriate tests for your application. from django.test import TestCase + class SimpleTest(TestCase): + def test_basic_addition(self): """ Tests that 1 + 1 always equals 2. @@ -20,4 +22,3 @@ Another way to test that 1 + 1 is equal to 2. >>> 1 + 1 == 2 True """} - diff --git a/pandora/place/admin.py b/pandora/place/admin.py index 8044bab1..f7196a9e 100644 --- a/pandora/place/admin.py +++ b/pandora/place/admin.py @@ -9,5 +9,3 @@ import models class PlaceAdmin(admin.ModelAdmin): search_fields = ['name'] admin.site.register(models.Place, PlaceAdmin) - - diff --git a/pandora/place/managers.py b/pandora/place/managers.py index 2e276537..bef67bb7 100644 --- a/pandora/place/managers.py +++ b/pandora/place/managers.py @@ -1,28 +1,23 @@ # -*- coding: utf-8 -*- # vi:si:et:sw=4:sts=4:ts=4 -import re - -from ox.utils import json -from django.contrib.auth.models import User -from django.core.exceptions import ObjectDoesNotExist from django.db.models import Q, Manager - -import models + class PlaceManager(Manager): + def get_query_set(self): return super(PlaceManager, self).get_query_set() def find(self, q='', f="globe", sw_lat=-180.0, sw_lng=-180.0, ne_lat=180.0, ne_lng=180.0): qs = self.get_query_set() qs = qs.filter(Q( - Q(Q(sw_lat__gt=sw_lat)|Q(sw_lat__lt=ne_lat)|Q(sw_lng__gt=sw_lng)|Q(sw_lng__lt=ne_lng)) & + Q(Q(sw_lat__gt=sw_lat)|Q(sw_lat__lt=ne_lat)|Q(sw_lng__gt=sw_lng)|Q(sw_lng__lt=ne_lng)) & Q(Q(sw_lat__gt=sw_lat)|Q(sw_lat__lt=ne_lat)|Q(sw_lng__lt=ne_lng)|Q(ne_lng__gt=ne_lng)) & Q(Q(ne_lat__gt=sw_lat)|Q(ne_lat__lt=ne_lat)|Q(sw_lng__gt=sw_lng)|Q(sw_lng__lt=ne_lng)) & Q(Q(ne_lat__gt=sw_lat)|Q(ne_lat__lt=ne_lat)|Q(ne_lng__gt=sw_lng)|Q(ne_lng__lt=ne_lng)) )) if q: - qs = qs.filter(name_find__icontains, q) + qs = qs.filter(name_find__icontains=q) return qs ''' #only return locations that have layers of videos visible to current user diff --git a/pandora/place/models.py b/pandora/place/models.py index fa7f8016..c0780502 100644 --- a/pandora/place/models.py +++ b/pandora/place/models.py @@ -3,13 +3,13 @@ from __future__ import division, with_statement from django.db import models -from django.db.models import Q -from django.conf import settings +import ox from ox.django import fields import managers + class Place(models.Model): ''' Places are named locations, they should have geographical information attached to them. @@ -60,7 +60,6 @@ class Place(models.Model): self.lng_center = ox.location.center(self.lng_sw, self.lng_ne) #update area - self.area = location.area(self.lat_sw, self.lng_sw, self.lat_ne, self.lng_ne) + self.area = ox.location.area(self.lat_sw, self.lng_sw, self.lat_ne, self.lng_ne) super(Place, self).save(*args, **kwargs) - diff --git a/pandora/place/tests.py b/pandora/place/tests.py index 2247054b..927cadf0 100644 --- a/pandora/place/tests.py +++ b/pandora/place/tests.py @@ -7,7 +7,9 @@ Replace these with more appropriate tests for your application. from django.test import TestCase + class SimpleTest(TestCase): + def test_basic_addition(self): """ Tests that 1 + 1 always equals 2. @@ -20,4 +22,3 @@ Another way to test that 1 + 1 is equal to 2. >>> 1 + 1 == 2 True """} - diff --git a/pandora/place/views.py b/pandora/place/views.py index 433b613c..32a25d40 100644 --- a/pandora/place/views.py +++ b/pandora/place/views.py @@ -1,37 +1,19 @@ # -*- coding: utf-8 -*- # vi:si:et:sw=4:sts=4:ts=4 from __future__ import division -import os.path -import re -from datetime import datetime -from urllib2 import unquote -import mimetypes - -from django import forms -from django.core.paginator import Paginator -from django.contrib.auth.decorators import login_required -from django.contrib.auth.models import User -from django.db.models import Q, Avg, Count, Sum -from django.http import HttpResponse, Http404 -from django.shortcuts import render_to_response, get_object_or_404, get_list_or_404, redirect -from django.template import RequestContext -from django.conf import settings 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 -from ox.django.http import HttpFileResponse -import ox import models from api.actions import actions -''' -fixme, require admin -''' + @login_required_json def addPlace(request): + #FIXME: require admin ''' param data { @@ -56,6 +38,7 @@ def addPlace(request): return render_to_json_response(response) actions.register(addPlace) + @login_required_json def editPlace(request): ''' @@ -86,17 +69,19 @@ def editPlace(request): return render_to_json_response(response) actions.register(editPlace) + @login_required_json def removePlace(request): response = json_response(status=501, text='not implemented') return render_to_json_response(response) actions.register(removePlace) + def findPlace(request): ''' param data {'query': query, 'sort': array, 'range': array, 'area': array} - + query: query object, more on query syntax at https://wiki.0x2620.org/wiki/pandora/QuerySyntax sort: array of key, operator dics @@ -120,7 +105,7 @@ def findPlace(request): Positions param data {'query': query, 'ids': []} - + query: query object, more on query syntax at https://wiki.0x2620.org/wiki/pandora/QuerySyntax ids: ids of places for which positions are required @@ -129,8 +114,7 @@ Positions response = json_response(status=200, text='ok') response['data']['places'] = [] #FIXME: add coordinates to limit search - for p in Places.objects.find(data['query']): + for p in models.Place.objects.find(data['query']): response['data']['places'].append(p.json()) return render_to_json_response(response) actions.register(findPlace) - diff --git a/pandora/text/models.py b/pandora/text/models.py index 6eacc624..e4ff094c 100644 --- a/pandora/text/models.py +++ b/pandora/text/models.py @@ -5,11 +5,6 @@ from datetime import datetime from django.db import models from django.contrib.auth.models import User -from django.conf import settings - -import ox -from ox.django import fields -from ox.utils import json class News(models.Model): @@ -29,6 +24,7 @@ class News(models.Model): def get_absolute_url(self): return '/text/%s' % self.slug + class Text(models.Model): created = models.DateTimeField(auto_now_add=True) modified = models.DateTimeField(auto_now=True) @@ -46,6 +42,7 @@ class Text(models.Model): def get_absolute_url(self): return '/text/%s' % self.slug + class Image(models.Model): image = models.ImageField(upload_to='text/image') caption = models.CharField(max_length=255, default="") @@ -53,10 +50,10 @@ class Image(models.Model): def get_absolute_url(self): return self.image.url + class Attachment(models.Model): file = models.FileField(upload_to='text/attachment') caption = models.CharField(max_length=255, default="") def get_absolute_url(self): return self.file.url - diff --git a/pandora/text/views.py b/pandora/text/views.py index b86393fb..ff894f92 100644 --- a/pandora/text/views.py +++ b/pandora/text/views.py @@ -2,23 +2,15 @@ # vi:si:et:sw=4:sts=4:ts=4 from __future__ import division -from django.contrib.auth.decorators import login_required -from django.contrib.auth.models import User -from django.db.models import Q, Avg, Count, Sum -from django.http import HttpResponse, Http404 -from django.shortcuts import render_to_response, get_object_or_404, get_list_or_404, redirect -from django.template import RequestContext -from django.conf import settings 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 -from ox.django.http import HttpFileResponse -import ox import models from api.actions import actions + def getNews(request): ''' param data @@ -33,6 +25,23 @@ def getNews(request): return render_to_json_response(response) actions.register(getNews) + +@login_required_json +def editNews(request): + ''' + param data + string id + + return page + ''' + response = json_response({}) + itemId = json.loads(request.POST['data']) + item = get_object_or_404_json(models.Text, pk=itemId) + response['data']['page'] = item.html() + return render_to_json_response(response) +actions.register(editNews) + + def findNews(request): ''' ''' @@ -40,6 +49,7 @@ def findNews(request): return render_to_json_response(response) actions.register(findNews) + def getText(request): ''' param data @@ -54,10 +64,26 @@ def getText(request): return render_to_json_response(response) actions.register(getText) + +@login_required_json +def editText(request): + ''' + param data + string id + + return page + ''' + response = json_response({}) + itemId = json.loads(request.POST['data']) + item = get_object_or_404_json(models.Text, pk=itemId) + response['data']['page'] = item.html() + return render_to_json_response(response) +actions.register(editText) + + def findText(request): ''' ''' response = json_response({}) return render_to_json_response(response) actions.register(findText) - diff --git a/pandora/torrent/transmission.py b/pandora/torrent/transmission.py index 75bed7e1..af5b339e 100644 --- a/pandora/torrent/transmission.py +++ b/pandora/torrent/transmission.py @@ -10,12 +10,14 @@ from django.conf import settings import ox.torrent import transmissionrpc + def connect(): return transmissionrpc.Client(settings.TRANSMISSON_HOST, port=settings.TRANSMISSON_PORT, user=settings.TRANSMISSON_USER, password=settings.TRANSMISSON_PASSWORD) + def remove(info_hash): if settings.DEBUG: print 'remove', info_hash @@ -24,10 +26,11 @@ def remove(info_hash): tc = connect() tc.remove(info_hash.lower()) except: - if DEBUG: + if settings.DEBUG: import traceback traceback.print_exc() - + + def add(torrent_file): download_dir = os.path.dirname(torrent_file) with open(torrent_file) as f: @@ -42,6 +45,7 @@ def add(torrent_file): import traceback traceback.print_exc() + def is_seeding(info_hash): info_hash = info_hash.lower() try: @@ -56,6 +60,7 @@ def is_seeding(info_hash): return True return False + def start_daemon(): try: tc = connect() @@ -71,4 +76,3 @@ def start_daemon(): '-w', settings.MEDIA_ROOT, ]) time.sleep(1) - diff --git a/pandora/user/models.py b/pandora/user/models.py index 7a3d58e2..a4e5744b 100644 --- a/pandora/user/models.py +++ b/pandora/user/models.py @@ -5,24 +5,25 @@ from datetime import datetime from django.contrib.auth.models import User from django.db import models -from django.db.models import signals -from django.dispatch import dispatcher from django.conf import settings from ox.utils import json + class UserProfile(models.Model): reset_token = models.TextField(blank=True, null=True, unique=True) user = models.ForeignKey(User, unique=True) - + files_updated = models.DateTimeField(default=datetime.now) newsletter = models.BooleanField(default=True) + def user_post_save(sender, instance, **kwargs): profile, new = UserProfile.objects.get_or_create(user=instance) models.signals.post_save.connect(user_post_save, sender=User) - + + class Preference(models.Model): user = models.ForeignKey(User, related_name='preferences') created = models.DateTimeField(auto_now_add=True) @@ -33,18 +34,20 @@ class Preference(models.Model): def __unicode__(self): return u"%s/%s=%s" % (self.user, self.key, self.value) + def get_user_json(user): - json = {} + result = {} for key in ('username', ): - json[key] = getattr(user, key) - json['group'] = 'user' + result[key] = getattr(user, key) + result['group'] = 'user' if user.is_staff: - json['group'] = 'admin' + result['group'] = 'admin' elif user.has_perm('0x.vip'): #FIXME: permissions - json['group'] = 'vip' - json['preferences'] = get_preferences(user) - json['ui'] = get_ui(user) - return json + result['group'] = 'vip' + result['preferences'] = get_preferences(user) + result['ui'] = get_ui(user) + return result + def get_ui(user): with open(os.path.join(settings.PROJECT_ROOT, 'templates', 'site.json')) as f: @@ -70,6 +73,7 @@ def get_ui(user): } ''' + def get_preferences(user): prefs = {} for p in Preference.objects.filter(user=user): @@ -77,6 +81,7 @@ def get_preferences(user): prefs['email'] = user.email return prefs + def get_preference(user, key, value=None): if key in ('email', ): value = getattr(user, key) @@ -86,6 +91,7 @@ def get_preference(user, key, value=None): value = json.loads(q[0].value) return value + def set_preference(user, key, value): if key in ('email', ): setattr(user, key, value) diff --git a/pandora/user/views.py b/pandora/user/views.py index dbb9df38..bf48ca6c 100644 --- a/pandora/user/views.py +++ b/pandora/user/views.py @@ -7,8 +7,7 @@ random.seed() from django import forms from django.contrib.auth.models import User from django.contrib.auth import authenticate, login, logout -from django.shortcuts import render_to_response, get_object_or_404, get_list_or_404, redirect -from django.template import RequestContext, loader, Context +from django.template import RequestContext, loader from django.utils import simplejson as json from django.conf import settings from django.core.mail import send_mail, BadHeaderError @@ -26,13 +25,14 @@ class LoginForm(forms.Form): username = forms.TextInput() password = forms.TextInput() + def api_login(request): ''' param data { username: 'username', password: 'password' } - + return { status: {'code': 200, 'text': 'ok'} data: { @@ -81,11 +81,12 @@ def api_login(request): return render_to_json_response(response) actions.register(api_login, 'login') + def api_logout(request): ''' param data { } - + return { status: {'code': int, 'text': string} data: { @@ -105,11 +106,13 @@ def api_logout(request): return render_to_json_response(response) actions.register(api_logout, 'logout') + class RegisterForm(forms.Form): username = forms.TextInput() password = forms.TextInput() email = forms.TextInput() + def register(request): ''' param data { @@ -117,7 +120,7 @@ def register(request): password: 'password', email: 'emailaddress' } - + return { status: {'code': int, 'text': string} data: { @@ -172,13 +175,14 @@ def register(request): return render_to_json_response(response) actions.register(register) + def resetPassword(request): ''' param data { token: reset token password: new password } - + return { status: {'code': int, 'text': string} data: { @@ -226,13 +230,14 @@ def resetPassword(request): return render_to_json_response(response) actions.register(resetPassword) + def requestToken(request): ''' param data { username: username, email: email } - + return { status: {'code': int, 'text': string} data: { @@ -291,13 +296,14 @@ def requestToken(request): return render_to_json_response(response) actions.register(requestToken) + def findUser(request): ''' param data { key: "username", value: "foo", operator: "=" } - + return { 'status': {'code': int, 'text': string} 'data': { @@ -315,18 +321,20 @@ def findUser(request): return render_to_json_response(response) actions.register(findUser) + class ContactForm(forms.Form): email = forms.EmailField() subject = forms.TextInput() message = forms.TextInput() + def contact(request): ''' param data { 'email': string, 'message': string } - + return { 'status': {'code': int, 'text': string} } @@ -353,6 +361,7 @@ def contact(request): return render_to_json_response(response) actions.register(contact) + @login_required_json def preferences(request): ''' @@ -391,4 +400,3 @@ def preferences(request): models.set_preference(request.user, key, data[key]) return render_to_json_response(response) actions.register(preferences) -