diff --git a/README.md b/README.md index 044a3cb8..902b3f46 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@ # pan.do/ra - open media archive - for more information about pan.do/ra visit our website at https://pan.do/ra + for more information on pan.do/ra visit our website at https://pan.do/ra ## Installing pan.do/ra - We recommend to run pan.do/ra inside of LXD or LXC or dedicated VM or server. + we recommend to run pan.do/ra inside of LXD or LXC or dedicated VM or server. You will need at least 2GB of free disk space - pan.do/ra is known to work with Ubuntu 18.04, 20.04 and Debian/10 (buster), + pan.do/ra is known to work with Ubuntu 18.04 and Debian/10 (buster), other distributions might also work, let us know if it works for you. Use the following commands as root to install pan.do/ra and all dependencies: @@ -16,7 +16,6 @@ cd /root curl -sL https://pan.do/ra-install > pandora_install.sh chmod +x pandora_install.sh -export BRANCH=stable # change to 'master' to get current developement version ./pandora_install.sh 2>&1 | tee pandora_install.log ``` diff --git a/ctl b/ctl index 28c74e12..1b93c214 100755 --- a/ctl +++ b/ctl @@ -1,18 +1,13 @@ #!/bin/sh SERVICES="pandora pandora-tasks pandora-encoding pandora-cron pandora-websocketd" if [ -z "$1" ]; then - echo "Usage: $0 (start|stop|restart|reload|status)" + echo "Usage: $0 (start|stop|restart|reload)" exit 1 else action="$1" fi -self=`readlink "$0"` -if [ -z $self ]; then - self="$0" -fi - if [ "$action" = "init" ]; then - cd "`dirname "$self"`" + cd "`dirname "$0"`" BASE=`pwd` SUDO="" PANDORA_USER=`ls -l update.py | cut -f3 -d" "` @@ -48,27 +43,6 @@ if [ "$action" = "init" ]; then fi exit 0 fi - -if [ "$action" = "manage" ]; then - cmd="pandora/manage.py" -fi -if [ "$action" = "update" ]; then - cmd="update.py" -fi - -if [ ! -z $cmd ]; then - cd "`dirname "$self"`" - BASE=`pwd` - SUDO="" - PANDORA_USER=`ls -l update.py | cut -f3 -d" "` - if [ `whoami` != $PANDORA_USER ]; then - SUDO="sudo -H -u $PANDORA_USER" - fi - shift - $SUDO "$BASE/$cmd" $@ - exit $? -fi - if [ `whoami` != 'root' ]; then echo you have to be root or run $0 with sudo exit 1 @@ -93,7 +67,6 @@ if [ "$action" = "install" ]; then systemctl enable ${service}.service done fi - test -e /usr/local/bin/pandoractl || ln -s /srv/pandora/ctl /usr/local/bin/pandoractl else if [ -d /etc/init ]; then cp $BASE/etc/init/* /etc/init/ @@ -101,13 +74,6 @@ if [ "$action" = "install" ]; then fi exit 0 fi -if [ "status" = "$action" ]; then - export SYSTEMD_PAGER= -fi for service in $SERVICES; do - if [ -x /bin/systemctl ]; then - /bin/systemctl $action $service - else - service $service $action - fi + service $service $action done diff --git a/pandora/annotation/admin.py b/pandora/annotation/admin.py index baf4f500..663a1396 100644 --- a/pandora/annotation/admin.py +++ b/pandora/annotation/admin.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import from django.contrib import admin diff --git a/pandora/annotation/management/commands/import_srt.py b/pandora/annotation/management/commands/import_srt.py index 881aa186..7c86ee17 100644 --- a/pandora/annotation/management/commands/import_srt.py +++ b/pandora/annotation/management/commands/import_srt.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import print_function from django.conf import settings from django.contrib.auth import get_user_model diff --git a/pandora/annotation/managers.py b/pandora/annotation/managers.py index e243cd16..a0c32c04 100644 --- a/pandora/annotation/managers.py +++ b/pandora/annotation/managers.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import unicodedata +from six import string_types from django.db.models import Q, Manager from oxdjango.query import QuerySet @@ -67,7 +68,7 @@ def parseCondition(condition, user): else: key = k + get_operator(op, 'istr' if k in case_insensitive_keys else 'str') key = str(key) - if isinstance(v, str): + if isinstance(v, string_types): v = unicodedata.normalize('NFKD', v) if k not in case_sensitive_keys: v = v.lower() @@ -157,7 +158,7 @@ class AnnotationManager(Manager): #anonymous can only see public items public_layers = self.model.public_layers() - if user.is_anonymous: + if user.is_anonymous(): qs = qs.filter(layer__in=public_layers) #users can see public and own else: diff --git a/pandora/annotation/models.py b/pandora/annotation/models.py index e1e9d553..0326e37d 100644 --- a/pandora/annotation/models.py +++ b/pandora/annotation/models.py @@ -1,8 +1,10 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import import re import unicodedata +from django.utils.encoding import python_2_unicode_compatible from django.db import models, transaction from django.db.models import Q from django.contrib.auth import get_user_model @@ -81,15 +83,16 @@ def get_matches(obj, model, layer_type, qs=None): matches = [-1] return Annotation.objects.filter(id__in=matches) +@python_2_unicode_compatible class Annotation(models.Model): objects = managers.AnnotationManager() #FIXME: here having a item,start index would be good created = models.DateTimeField(auto_now_add=True) modified = models.DateTimeField(auto_now=True) - user = models.ForeignKey(User, related_name='annotations', on_delete=models.CASCADE) - item = models.ForeignKey('item.Item', related_name='annotations', on_delete=models.CASCADE) - clip = models.ForeignKey('clip.Clip', null=True, related_name='annotations', on_delete=models.CASCADE) + user = models.ForeignKey(User, related_name='annotations') + item = models.ForeignKey('item.Item', related_name='annotations') + clip = models.ForeignKey('clip.Clip', null=True, related_name='annotations') public_id = models.CharField(max_length=128, unique=True) #seconds @@ -104,7 +107,7 @@ class Annotation(models.Model): languages = models.CharField(max_length=255, null=True, blank=True) def editable(self, user): - if user.is_authenticated: + if user.is_authenticated(): if user.profile.capability('canEditAnnotations') or \ self.user == user or \ user.groups.filter(id__in=self.item.groups.all()).count() > 0: @@ -397,7 +400,7 @@ class Annotation(models.Model): return j def __str__(self): - return "%s %s-%s" % (self.public_id, self.start, self.end) + return u"%s %s-%s" % (self.public_id, self.start, self.end) def cleanup_related(sender, **kwargs): kwargs['instance'].cleanup_undefined_relations() diff --git a/pandora/annotation/tasks.py b/pandora/annotation/tasks.py index 8054c6e3..88087b0f 100644 --- a/pandora/annotation/tasks.py +++ b/pandora/annotation/tasks.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import from django.conf import settings from django.contrib.auth import get_user_model diff --git a/pandora/annotation/views.py b/pandora/annotation/views.py index 8bfe7006..d090de56 100644 --- a/pandora/annotation/views.py +++ b/pandora/annotation/views.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import from django.conf import settings from django.db.models import Count, Sum, F, Value diff --git a/pandora/app/admin.py b/pandora/app/admin.py index e4a5bc96..0394dbc9 100644 --- a/pandora/app/admin.py +++ b/pandora/app/admin.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import from django.contrib import admin diff --git a/pandora/app/config.py b/pandora/app/config.py index e950e5e9..36dfdfe3 100644 --- a/pandora/app/config.py +++ b/pandora/app/config.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import import codecs import os @@ -9,8 +10,8 @@ import sys import time from os.path import dirname, exists, join from glob import glob -import _thread as thread +from six.moves import _thread as thread from django.conf import settings from django.contrib.auth import get_user_model @@ -28,16 +29,20 @@ RUN_RELOADER = True NOTIFIER = None def get_version(): + info = join(dirname(dirname(dirname(__file__))), '.bzr', 'branch', 'last-revision') git_dir = join(dirname(dirname(dirname(__file__))), '.git') if exists(git_dir): env = {'GIT_DIR': git_dir} cmd = ['git', 'rev-list', 'HEAD', '--count'] - version = subprocess.check_output(cmd, env=env).strip().decode('utf-8') - if settings.VERSION_EPOCH: - version = settings.VERSION_EPOCH + version - return version + return subprocess.check_output(cmd, env=env).strip().decode('utf-8') + elif exists(info): + f = open(info) + rev = int(f.read().split()[0]) + f.close() + if rev: + return u'%s' % rev else: - return 'unknown' + return u'unknown' def load_config(init=False): with open(settings.SITE_CONFIG) as f: @@ -394,7 +399,8 @@ def update_static(): def update_geoip(force=False): path = os.path.join(settings.GEOIP_PATH, 'GeoLite2-City.mmdb') if not os.path.exists(path) or force: - index = ox.net.read_url('https://db-ip.com/db/download/ip-to-city-lite').decode() + url = 'http://geolite.maxmind.com/download/geoip/database/GeoLite2-City.mmdb.gz' + index = ox.net.read_url('https://db-ip.com/db/download/ip-to-country-lite').decode() match = re.compile('href=[\'"](http.*.mmdb.gz)').findall(index) if match: url = match[0] @@ -404,7 +410,7 @@ def update_geoip(force=False): os.unlink(path) os.system('gunzip "%s.gz"' % path) else: - print('failed to download GeoLite2-City.mmdb') + print('failed to download dbip-country-lite-2020-03.mmdb.gz') def init(): if not settings.RELOADER_RUNNING: diff --git a/pandora/app/documentation.py b/pandora/app/documentation.py index 89abf100..2cba4e64 100644 --- a/pandora/app/documentation.py +++ b/pandora/app/documentation.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import print_function import re import ox.jsonc diff --git a/pandora/app/management/commands/init_db.py b/pandora/app/management/commands/init_db.py index 92ac750f..e4378cf3 100644 --- a/pandora/app/management/commands/init_db.py +++ b/pandora/app/management/commands/init_db.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import print_function from django.core.management.base import BaseCommand from django.conf import settings diff --git a/pandora/app/management/commands/south_installed.py b/pandora/app/management/commands/south_installed.py index d3957807..e753dbdf 100644 --- a/pandora/app/management/commands/south_installed.py +++ b/pandora/app/management/commands/south_installed.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import print_function from django.core.management.base import BaseCommand from django.db import connection diff --git a/pandora/app/management/commands/update_config_documentation.py b/pandora/app/management/commands/update_config_documentation.py index 665f7528..de7c7332 100644 --- a/pandora/app/management/commands/update_config_documentation.py +++ b/pandora/app/management/commands/update_config_documentation.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import print_function from django.core.management.base import BaseCommand from django.conf import settings from ... import documentation diff --git a/pandora/app/models.py b/pandora/app/models.py index 3798eea7..c7232863 100644 --- a/pandora/app/models.py +++ b/pandora/app/models.py @@ -1,13 +1,16 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import import json from django.db import models +from django.utils.encoding import python_2_unicode_compatible from . import monkey_patch from . import tasks +@python_2_unicode_compatible class Page(models.Model): created = models.DateTimeField(auto_now_add=True) modified = models.DateTimeField(auto_now=True) @@ -17,6 +20,7 @@ class Page(models.Model): def __str__(self): return self.name +@python_2_unicode_compatible class Settings(models.Model): created = models.DateTimeField(auto_now_add=True) diff --git a/pandora/app/monkey_patch.py b/pandora/app/monkey_patch.py index f4579d56..8f48af00 100644 --- a/pandora/app/monkey_patch.py +++ b/pandora/app/monkey_patch.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import from django.contrib.auth import get_user_model from django.contrib.auth.models import Group @@ -13,17 +14,10 @@ config.init() NEW_LENGTH = { 'username': 255, - 'email': 254, + 'email': 255, 'password': 255, } -def monkey_patch_groupname(): - f = Group._meta.get_field('name') - f.max_length = 255 - for v in f.validators: - if isinstance(v, MaxLengthValidator): - v.limit_value = 255 - def monkey_patch_username(): for field in NEW_LENGTH: f = User._meta.get_field(field) @@ -31,18 +25,22 @@ def monkey_patch_username(): for v in f.validators: if isinstance(v, MaxLengthValidator): v.limit_value = NEW_LENGTH[field] - monkey_patch_groupname() + + f = Group._meta.get_field('name') + f.max_length = 255 + for v in f.validators: + if isinstance(v, MaxLengthValidator): + v.limit_value = 255 def apply_patch(): from django.db import connection, transaction cursor = connection.cursor() - table = connection.introspection.get_table_description(cursor, Group._meta.db_table) + table = connection.introspection.get_table_description(cursor, User._meta.db_table) sql = [] for row in table: - if row.name == 'name' and row.internal_size != 255: - sql.append('ALTER TABLE "%s" ALTER "%s" TYPE varchar(%d)' % ( - Group._meta.db_table, row.name, 255) - ) + if row.name in NEW_LENGTH and row.internal_size != NEW_LENGTH[row.name]: + sql.append('ALTER TABLE "%s" ALTER "%s" TYPE varchar(%d)' % (User._meta.db_table, row.name, NEW_LENGTH[row.name])) + for q in sql: cursor.execute(q) if sql: diff --git a/pandora/app/tasks.py b/pandora/app/tasks.py index 03b26778..c380f1e8 100644 --- a/pandora/app/tasks.py +++ b/pandora/app/tasks.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import import datetime diff --git a/pandora/app/utils.py b/pandora/app/utils.py index ee2e9d08..f272e6c8 100644 --- a/pandora/app/utils.py +++ b/pandora/app/utils.py @@ -1,4 +1,6 @@ # -*- coding: utf-8 -*- +from __future__ import division, with_statement + import time from .models import Settings diff --git a/pandora/app/views.py b/pandora/app/views.py index 5c76b8ec..8bd0fbfa 100644 --- a/pandora/app/views.py +++ b/pandora/app/views.py @@ -1,8 +1,10 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import import copy from datetime import datetime +from six import string_types from django.shortcuts import render, redirect from django.conf import settings from django.http import HttpResponse @@ -112,7 +114,7 @@ def getPage(request, data): } see: editPage ''' - if isinstance(data, str): + if isinstance(data, string_types): name = data else: name = data['name'] diff --git a/pandora/archive/admin.py b/pandora/archive/admin.py index 2261bdf5..bf10ba42 100644 --- a/pandora/archive/admin.py +++ b/pandora/archive/admin.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import from django.contrib import admin diff --git a/pandora/archive/chunk.py b/pandora/archive/chunk.py index 8422051e..148ba24f 100644 --- a/pandora/archive/chunk.py +++ b/pandora/archive/chunk.py @@ -1,4 +1,6 @@ # -*- coding: utf-8 -*- +from __future__ import division, with_statement + import os import ox diff --git a/pandora/archive/external.py b/pandora/archive/external.py index 510d3f70..69179e4f 100644 --- a/pandora/archive/external.py +++ b/pandora/archive/external.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import import json import subprocess diff --git a/pandora/archive/extract.py b/pandora/archive/extract.py index dcb9bdcf..90422d47 100644 --- a/pandora/archive/extract.py +++ b/pandora/archive/extract.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import import os from os.path import exists @@ -12,6 +13,7 @@ import shutil from distutils.spawn import find_executable from glob import glob +from six import string_types import numpy as np import ox import ox.image @@ -462,7 +464,7 @@ def timeline(video, prefix, modes=None, size=None): modes = ['antialias', 'slitscan', 'keyframes', 'audio', 'data'] if size is None: size = [64, 16] - if isinstance(video, str): + if isinstance(video, string_types): video = [video] cmd = ['../bin/oxtimelines', '-s', ','.join(map(str, reversed(sorted(size)))), diff --git a/pandora/archive/management/commands/import_streams.py b/pandora/archive/management/commands/import_streams.py index 6b23af51..4d0d18f3 100644 --- a/pandora/archive/management/commands/import_streams.py +++ b/pandora/archive/management/commands/import_streams.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import print_function from django.core.management.base import BaseCommand from django.conf import settings diff --git a/pandora/archive/management/commands/migrate_timelines.py b/pandora/archive/management/commands/migrate_timelines.py index d4ce903d..9d9e5e59 100644 --- a/pandora/archive/management/commands/migrate_timelines.py +++ b/pandora/archive/management/commands/migrate_timelines.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import print_function import os import re diff --git a/pandora/archive/management/commands/orphaned_media.py b/pandora/archive/management/commands/orphaned_media.py index 756602a0..a6e831ab 100644 --- a/pandora/archive/management/commands/orphaned_media.py +++ b/pandora/archive/management/commands/orphaned_media.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import print_function from django.core.management.base import BaseCommand from django.conf import settings diff --git a/pandora/archive/models.py b/pandora/archive/models.py index 05882c40..75d25de7 100644 --- a/pandora/archive/models.py +++ b/pandora/archive/models.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import import json import os.path @@ -6,10 +7,12 @@ import shutil import tempfile import time +from six import string_types, PY2 from django.conf import settings from django.contrib.auth import get_user_model from django.db import models from django.db.models.signals import pre_delete +from django.utils.encoding import python_2_unicode_compatible from oxdjango.fields import JSONField from oxdjango import fields @@ -27,9 +30,13 @@ from . import managers User = get_user_model() +if not PY2: + unicode = str + def data_path(f, x): return f.get_path('data.bin') +@python_2_unicode_compatible class File(models.Model): AV_INFO = ( 'duration', 'video', 'audio', 'oshash', 'size', @@ -48,7 +55,7 @@ class File(models.Model): modified = models.DateTimeField(auto_now=True) oshash = models.CharField(max_length=16, unique=True) - item = models.ForeignKey("item.Item", related_name='files', null=True, on_delete=models.CASCADE) + item = models.ForeignKey("item.Item", related_name='files', null=True) path = models.CharField(max_length=2048, default="") # canoncial path/file sort_path = models.CharField(max_length=2048, default="") # sort name @@ -161,7 +168,7 @@ class File(models.Model): if self.item: for key in self.ITEM_INFO: data[key] = self.item.get(key) - if isinstance(data[key], str): + if isinstance(data[key], string_types): data[key] = ox.decode_html(data[key]) elif isinstance(data[key], list): data[key] = [ox.decode_html(e) for e in data[key]] @@ -255,8 +262,8 @@ class File(models.Model): data = self.get_path_info() self.extension = data.get('extension') self.language = data.get('language') - self.part = ox.sort_string(str(data.get('part') or '')) - self.part_title = ox.sort_string(str(data.get('partTitle')) or '') + self.part = ox.sort_string(unicode(data.get('part') or '')) + self.part_title = ox.sort_string(unicode(data.get('partTitle')) or '') self.type = data.get('type') or 'unknown' self.version = data.get('version') @@ -476,7 +483,7 @@ class File(models.Model): if k not in keys: del data[k] can_see_media = False - if user and not user.is_anonymous: + if user and not user.is_anonymous(): can_see_media = user.profile.capability('canSeeMedia') or \ user.is_staff or \ self.item.user == user or \ @@ -591,15 +598,15 @@ class File(models.Model): status = {} if self.encoding: for s in self.streams.all(): - status[s.name()] = 'done' if s.available else 'encoding' + status[s.name()] = u'done' if s.available else u'encoding' config = settings.CONFIG['video'] max_resolution = self.streams.get(source=None).resolution for resolution in sorted(config['resolutions'], reverse=True): if resolution <= max_resolution: for f in config['formats']: - name = '%sp.%s' % (resolution, f) + name = u'%sp.%s' % (resolution, f) if name not in status: - status[name] = 'queued' + status[name] = u'queued' return status def delete_frames(self): @@ -620,6 +627,7 @@ def delete_file(sender, **kwargs): f.delete_files() pre_delete.connect(delete_file, sender=File) +@python_2_unicode_compatible class Volume(models.Model): class Meta: @@ -628,11 +636,11 @@ class Volume(models.Model): created = models.DateTimeField(auto_now_add=True) modified = models.DateTimeField(auto_now=True) - user = models.ForeignKey(User, related_name='volumes', on_delete=models.CASCADE) + user = models.ForeignKey(User, related_name='volumes') name = models.CharField(max_length=1024) def __str__(self): - return "%s's %s" % (self.user, self.name) + return u"%s's %s" % (self.user, self.name) def json(self): return { @@ -644,6 +652,7 @@ class Volume(models.Model): def inttime(): return int(time.time()) +@python_2_unicode_compatible class Instance(models.Model): class Meta: @@ -659,11 +668,11 @@ class Instance(models.Model): path = models.CharField(max_length=2048) ignore = models.BooleanField(default=False) - file = models.ForeignKey(File, related_name='instances', on_delete=models.CASCADE) - volume = models.ForeignKey(Volume, related_name='files', on_delete=models.CASCADE) + file = models.ForeignKey(File, related_name='instances') + volume = models.ForeignKey(Volume, related_name='files') def __str__(self): - return "%s's %s <%s>" % (self.volume.user, self.path, self.file.oshash) + return u"%s's %s <%s>" % (self.volume.user, self.path, self.file.oshash) @property def public_id(self): @@ -682,13 +691,14 @@ def frame_path(frame, name): name = "%s%s" % (frame.position, ext) return frame.file.get_path(name) +@python_2_unicode_compatible class Frame(models.Model): class Meta: unique_together = ("file", "position") created = models.DateTimeField(auto_now_add=True) modified = models.DateTimeField(auto_now=True) - file = models.ForeignKey(File, related_name="frames", on_delete=models.CASCADE) + file = models.ForeignKey(File, related_name="frames") position = models.FloatField() frame = models.ImageField(default=None, null=True, upload_to=frame_path) width = models.IntegerField(default=0) @@ -701,7 +711,7 @@ class Frame(models.Model): super(Frame, self).save(*args, **kwargs) def __str__(self): - return '%s/%s' % (self.file, self.position) + return u'%s/%s' % (self.file, self.position) def delete_frame(sender, **kwargs): f = kwargs['instance'] @@ -712,17 +722,18 @@ pre_delete.connect(delete_frame, sender=Frame) def stream_path(f, x): return f.path(x) +@python_2_unicode_compatible class Stream(models.Model): class Meta: unique_together = ("file", "resolution", "format") - file = models.ForeignKey(File, related_name='streams', on_delete=models.CASCADE) + file = models.ForeignKey(File, related_name='streams') resolution = models.IntegerField(default=96) format = models.CharField(max_length=255, default='webm') media = models.FileField(default=None, blank=True, upload_to=stream_path) - source = models.ForeignKey('Stream', related_name='derivatives', default=None, null=True, on_delete=models.CASCADE) + source = models.ForeignKey('Stream', related_name='derivatives', default=None, null=True) available = models.BooleanField(default=False) oshash = models.CharField(max_length=16, null=True, db_index=True) info = JSONField(default=dict, editable=False) @@ -742,10 +753,10 @@ class Stream(models.Model): return os.path.join(settings.MEDIA_ROOT, self.path(), 'timeline') def name(self): - return "%sp.%s" % (self.resolution, self.format) + return u"%sp.%s" % (self.resolution, self.format) def __str__(self): - return "%s/%s" % (self.file, self.name()) + return u"%s/%s" % (self.file, self.name()) def get(self, resolution, format): streams = [] @@ -803,12 +814,8 @@ class Stream(models.Model): # file could have been moved while encoding # get current version from db and update - try: - self.refresh_from_db() - except archive.models.DoesNotExist: - pass - else: - self.update_status(ok, error) + self.refresh_from_db() + self.update_status(ok, error) def get_index(self): index = 1 diff --git a/pandora/archive/queue.py b/pandora/archive/queue.py index 8a8ff4ce..6b917c20 100644 --- a/pandora/archive/queue.py +++ b/pandora/archive/queue.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function from datetime import datetime from time import time diff --git a/pandora/archive/tasks.py b/pandora/archive/tasks.py index 022cd377..92446d58 100644 --- a/pandora/archive/tasks.py +++ b/pandora/archive/tasks.py @@ -1,7 +1,9 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import from glob import glob +from six import string_types from celery.task import task from django.conf import settings from django.db.models import Q @@ -218,7 +220,7 @@ def move_media(data, user): data['public_id'] = data.pop('item').strip() if not is_imdb_id(data['public_id']): del data['public_id'] - if 'director' in data and isinstance(data['director'], str): + if 'director' in data and isinstance(data['director'], string_types): if data['director'] == '': data['director'] = [] else: diff --git a/pandora/archive/views.py b/pandora/archive/views.py index 848bfc7e..0b8abddd 100644 --- a/pandora/archive/views.py +++ b/pandora/archive/views.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import import os.path from datetime import datetime @@ -7,6 +8,7 @@ from django.shortcuts import get_object_or_404, redirect, render from django.conf import settings from django.db.models import Count, Q +from six import string_types from celery.utils import get_full_cls_name from celery._state import current_app import ox @@ -554,7 +556,7 @@ def getPath(request, data): ''' response = json_response() ids = data['id'] - if isinstance(ids, str): + if isinstance(ids, string_types): ids = [ids] for f in models.File.objects.filter(oshash__in=ids).values('path', 'oshash').order_by('sort_path'): response['data'][f['oshash']] = f['path'] diff --git a/pandora/changelog/models.py b/pandora/changelog/models.py index 67d1cb0c..bad1a858 100644 --- a/pandora/changelog/models.py +++ b/pandora/changelog/models.py @@ -1,9 +1,11 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import from datetime import datetime from django.contrib.auth import get_user_model from django.db import models +from django.utils.encoding import python_2_unicode_compatible from oxdjango.fields import JSONField import ox @@ -17,13 +19,14 @@ User = get_user_model() ''' FIXME: remove this table more migrate to new ChangeLog ''' +@python_2_unicode_compatible class Changelog(models.Model): created = models.DateTimeField(auto_now_add=True) type = models.CharField(max_length=255, db_index=True) value = JSONField(default=dict, editable=False) def __str__(self): - return '%s %s' % (self.type, self.created) + return u'%s %s' % (self.type, self.created) def json(self): return self.value @@ -47,18 +50,19 @@ def add_changelog(request, data, id=None): 'user': c.user.username, }) +@python_2_unicode_compatible class Log(models.Model): action = models.CharField(max_length=255, db_index=True) data = JSONField(default=dict, editable=False) created = models.DateTimeField(db_index=True) - user = models.ForeignKey(User, null=True, related_name='changelog', on_delete=models.CASCADE) + user = models.ForeignKey(User, null=True, related_name='changelog') changeid = models.TextField() objects = managers.LogManager() def __str__(self): - return '%s %s %s' % (self.created, self.action, self.changeid) + return u'%s %s %s' % (self.created, self.action, self.changeid) def get_id(self): return ox.toAZ(self.id) diff --git a/pandora/changelog/recover.py b/pandora/changelog/recover.py index 53be924c..425af0f9 100644 --- a/pandora/changelog/recover.py +++ b/pandora/changelog/recover.py @@ -1,3 +1,4 @@ +from __future__ import print_function import models import item.models @@ -17,33 +18,33 @@ def recover_item(id): created = old.value['created'] i.user = user.models.User.objects.get(username=i.data['user']) for key in [ - 'rendered', - 'random', - 'cuts', - 'duration', - 'id', - 'size', - 'posterFrame', - 'parts', - 'cutsperminute', - 'hue', - 'numberofcuts', - 'durations', - 'volume', - 'user', - 'words', - 'videoRatio', - 'aspectratio', - 'bitrate', - 'pixels', - 'created', - 'numberoffiles', - 'modified', - 'timesaccessed', - 'accessed', - 'resolution', - 'wordsperminute', - 'posterRatio' + u'rendered', + u'random', + u'cuts', + u'duration', + u'id', + u'size', + u'posterFrame', + u'parts', + u'cutsperminute', + u'hue', + u'numberofcuts', + u'durations', + u'volume', + u'user', + u'words', + u'videoRatio', + u'aspectratio', + u'bitrate', + u'pixels', + u'created', + u'numberoffiles', + u'modified', + u'timesaccessed', + u'accessed', + u'resolution', + u'wordsperminute', + u'posterRatio' ]: if key in i.data: del i.data[key] diff --git a/pandora/changelog/views.py b/pandora/changelog/views.py index f32c3626..677653b4 100644 --- a/pandora/changelog/views.py +++ b/pandora/changelog/views.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import import ox diff --git a/pandora/clip/managers.py b/pandora/clip/managers.py index eb7c6ca9..729624d5 100644 --- a/pandora/clip/managers.py +++ b/pandora/clip/managers.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import unicodedata +from six import string_types from django.db.models import Q, Manager from django.conf import settings @@ -77,7 +78,7 @@ def parseCondition(condition, user): else: key = k + get_operator(op, 'istr' if k in case_insensitive_keys else 'str') key = str(key) - if isinstance(v, str) and op != '===': + if isinstance(v, string_types) and op != '===': v = unicodedata.normalize('NFKD', v).lower() if exclude: q = ~Q(**{key: v}) @@ -154,7 +155,7 @@ class ClipManager(Manager): def parse(condition): key = 'findvalue' + get_operator(condition.get('operator', '')) v = condition['value'] - if isinstance(v, str): + if isinstance(v, string_types): v = unicodedata.normalize('NFKD', v).lower() q = Q(**{key: v}) if condition['key'] in layer_ids: @@ -214,7 +215,7 @@ class ClipManager(Manager): for l in list(filter(lambda k: k in layer_ids, data['keys'])): qs = qs.filter(**{l: True}) #anonymous can only see public clips - if not user or user.is_anonymous: + if not user or user.is_anonymous(): allowed_level = settings.CONFIG['capabilities']['canSeeItem']['guest'] qs = qs.filter(sort__rightslevel__lte=allowed_level) #users can see public clips, there own clips and clips of there groups diff --git a/pandora/clip/models.py b/pandora/clip/models.py index 9736adb1..37641c15 100644 --- a/pandora/clip/models.py +++ b/pandora/clip/models.py @@ -1,7 +1,9 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import from django.db import models from django.conf import settings +from django.utils.encoding import python_2_unicode_compatible import ox @@ -13,7 +15,7 @@ from . import managers def get_layers(item, interval=None, user=None): from annotation.models import Annotation - if user and user.is_anonymous: + if user and user.is_anonymous(): user = None layers = {} @@ -43,6 +45,7 @@ def get_layers(item, interval=None, user=None): return layers +@python_2_unicode_compatible class MetaClip(object): def update_calculated_values(self): start = self.start @@ -181,7 +184,7 @@ class MetaClip(object): @property def public_id(self): - return "%s/%0.03f-%0.03f" % (self.item.public_id, float(self.start), float(self.end)) + return u"%s/%0.03f-%0.03f" % (self.item.public_id, float(self.start), float(self.end)) def __str__(self): return self.public_id @@ -197,8 +200,8 @@ attrs = { 'modified': models.DateTimeField(auto_now=True), 'aspect_ratio': models.FloatField(default=0), - 'item': models.ForeignKey('item.Item', related_name='clips', on_delete=models.CASCADE), - 'sort': models.ForeignKey('item.ItemSort', related_name='matching_clips', on_delete=models.CASCADE), + 'item': models.ForeignKey('item.Item', related_name='clips'), + 'sort': models.ForeignKey('item.ItemSort', related_name='matching_clips'), 'user': models.IntegerField(db_index=True, null=True), #seconds @@ -223,4 +226,4 @@ Clip = type('Clip', (MetaClip, models.Model), attrs) class ClipRandom(models.Model): id = models.BigIntegerField(primary_key=True) - clip = models.OneToOneField(Clip, on_delete=models.CASCADE) + clip = models.OneToOneField(Clip) diff --git a/pandora/clip/views.py b/pandora/clip/views.py index 416e4a10..7c2fcd23 100644 --- a/pandora/clip/views.py +++ b/pandora/clip/views.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import from django.conf import settings import ox @@ -153,7 +154,7 @@ def findClips(request, data): add_annotations(layer, aqs) elif 'position' in query: qs = order_query(qs, query['sort']) - ids = ['%s/%0.03f-%0.03f' % (c['item__public_id'], c['start'], c['end']) + ids = [u'%s/%0.03f-%0.03f' % (c['item__public_id'], c['start'], c['end']) for c in qs.values('item__public_id', 'start', 'end')] data['conditions'] = data['conditions'] + { 'value': data['position'], @@ -166,7 +167,7 @@ def findClips(request, data): response['data']['position'] = utils.get_positions(ids, [qs[0].public_id])[0] elif 'positions' in data: qs = order_query(qs, query['sort']) - ids = ['%s/%0.03f-%0.03f' % (c['item__public_id'], c['start'], c['end']) + ids = [u'%s/%0.03f-%0.03f' % (c['item__public_id'], c['start'], c['end']) for c in qs.values('item__public_id', 'start', 'end')] response['data']['positions'] = utils.get_positions(ids, data['positions']) else: diff --git a/pandora/document/management/commands/rebuild_documentfind.py b/pandora/document/management/commands/rebuild_documentfind.py index af56b748..d374ffaf 100644 --- a/pandora/document/management/commands/rebuild_documentfind.py +++ b/pandora/document/management/commands/rebuild_documentfind.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import print_function from django.core.management.base import BaseCommand from django.db import connection, transaction diff --git a/pandora/document/management/commands/sync_documentsort.py b/pandora/document/management/commands/sync_documentsort.py index fa6cb5d8..0bdb9909 100644 --- a/pandora/document/management/commands/sync_documentsort.py +++ b/pandora/document/management/commands/sync_documentsort.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import print_function from django.core.management.base import BaseCommand from django.db import connection, transaction diff --git a/pandora/document/managers.py b/pandora/document/managers.py index 4210cd62..fa0cd64a 100644 --- a/pandora/document/managers.py +++ b/pandora/document/managers.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import unicodedata +from six import string_types from django.db.models import Q, Manager from django.conf import settings @@ -131,8 +132,6 @@ def buildCondition(k, op, v, user, exclude=False, owner=None): q = Q(id__in=l.documents.all()) else: q = Q(id=0) - if exclude: - q = ~q return q elif key_config.get('fulltext'): qs = models.Document.find_fulltext_ids(v) @@ -151,7 +150,7 @@ def buildCondition(k, op, v, user, exclude=False, owner=None): value_key = 'find__value' else: value_key = k - if isinstance(v, str): + if isinstance(v, string_types): v = unicodedata.normalize('NFKD', v).lower() if k in facet_keys: in_find = False @@ -282,7 +281,7 @@ class DocumentManager(Manager): qs = qs.distinct() #anonymous can only see public items - if not user or user.is_anonymous: + if not user or user.is_anonymous(): level = 'guest' allowed_level = settings.CONFIG['capabilities']['canSeeDocument'][level] qs = qs.filter(rightslevel__lte=allowed_level) diff --git a/pandora/document/models.py b/pandora/document/models.py index e2501244..4d85f785 100644 --- a/pandora/document/models.py +++ b/pandora/document/models.py @@ -1,16 +1,19 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import -from glob import glob -from urllib.parse import quote, unquote import os import re +from glob import glob import unicodedata +from six import PY2, string_types +from six.moves.urllib.parse import quote, unquote from django.db import models, transaction from django.db.models import Q, Sum, Max from django.contrib.auth import get_user_model from django.db.models.signals import pre_delete from django.conf import settings +from django.utils.encoding import python_2_unicode_compatible from oxdjango.fields import JSONField from PIL import Image @@ -24,7 +27,6 @@ from annotation.models import Annotation from archive.extract import resize_image from archive.chunk import save_chunk from user.models import Group -from user.utils import update_groups from . import managers from . import utils @@ -33,15 +35,19 @@ from .fulltext import FulltextMixin User = get_user_model() +if not PY2: + unicode = str + def get_path(f, x): return f.path(x) +@python_2_unicode_compatible class Document(models.Model, FulltextMixin): created = models.DateTimeField(auto_now_add=True) modified = models.DateTimeField(auto_now=True) - user = models.ForeignKey(User, related_name='documents', on_delete=models.CASCADE) + user = models.ForeignKey(User, related_name='documents') groups = models.ManyToManyField(Group, blank=True, related_name='documents') extension = models.CharField(max_length=255) @@ -67,7 +73,7 @@ class Document(models.Model, FulltextMixin): data = JSONField(default=dict, editable=False) def update_access(self, user): - if not user.is_authenticated: + if not user.is_authenticated(): user = None access, created = Access.objects.get_or_create(document=self, user=user) if not created: @@ -84,7 +90,7 @@ class Document(models.Model, FulltextMixin): if not current_values: current_values = [] else: - current_values = [str(current_values)] + current_values = [unicode(current_values)] filter_map = utils.get_by_id(settings.CONFIG['documentKeys'], key).get('filterMap') if filter_map: @@ -135,7 +141,7 @@ class Document(models.Model, FulltextMixin): f, created = Find.objects.get_or_create(document=self, key=key) if isinstance(value, bool): value = value and 'true' or 'false' - if isinstance(value, str): + if isinstance(value, string_types): value = ox.decode_html(ox.strip_tags(value.strip())) value = unicodedata.normalize('NFKD', value).lower() f.value = value @@ -154,7 +160,7 @@ class Document(models.Model, FulltextMixin): elif i not in ('*', 'dimensions') and i not in self.facet_keys: value = data.get(i) if isinstance(value, list): - value = '\n'.join(value) + value = u'\n'.join(value) save(i, value) base_keys = ('id', 'size', 'dimensions', 'extension', 'matches') @@ -185,15 +191,15 @@ class Document(models.Model, FulltextMixin): s.dimensions = ox.sort_string('%d' % prefix) + ox.sort_string('%d' % value) def sortNames(values): - sort_value = '' + sort_value = u'' if values: - sort_value = '; '.join([get_name_sort(name) for name in values]) + sort_value = u'; '.join([get_name_sort(name) for name in values]) if not sort_value: - sort_value = '' + sort_value = u'' return sort_value.lower() def set_value(s, name, value): - if isinstance(value, str): + if isinstance(value, string_types): value = ox.decode_html(value.lower()) if not value: value = None @@ -222,7 +228,7 @@ class Document(models.Model, FulltextMixin): if isinstance(sort_type, list): sort_type = sort_type[0] if sort_type == 'title': - value = self.get_value(source, 'Untitled') + value = self.get_value(source, u'Untitled') value = utils.sort_title(value)[:955] set_value(s, name, value) elif sort_type == 'person': @@ -230,9 +236,9 @@ class Document(models.Model, FulltextMixin): value = utils.sort_string(value)[:955] set_value(s, name, value) elif sort_type == 'string': - value = self.get_value(source, '') + value = self.get_value(source, u'') if isinstance(value, list): - value = ','.join(value) + value = u','.join(value) if not isinstance(value, str): value = str(value) value = utils.sort_string(value)[:955] @@ -255,7 +261,7 @@ class Document(models.Model, FulltextMixin): set_value(s, name, value) elif sort_type == 'date': value = self.get_value(source) - if isinstance(value, str): + if isinstance(value, string_types): value = datetime_safe.datetime.strptime(value, '%Y-%m-%d') set_value(s, name, value) s.save() @@ -312,7 +318,7 @@ class Document(models.Model, FulltextMixin): return ox.toAZ(self.id) def access(self, user): - if user.is_anonymous: + if user.is_anonymous(): level = 'guest' else: level = user.profile.get_level() @@ -325,10 +331,9 @@ class Document(models.Model, FulltextMixin): return False def editable(self, user, item=None): - if not user or user.is_anonymous: + if not user or user.is_anonymous(): return False if self.user == user or \ - self.groups.filter(id__in=user.groups.all()).count() > 0 or \ user.is_staff or \ user.profile.capability('canEditDocuments') is True or \ (item and item.editable(user)): @@ -342,9 +347,6 @@ class Document(models.Model, FulltextMixin): p.description = ox.sanitize_html(data['description']) p.save() else: - if 'groups' in data: - groups = data.pop('groups') - update_groups(self, groups) for key in data: k = list(filter(lambda i: i['id'] == key, settings.CONFIG['documentKeys'])) ktype = k and k[0].get('type') or '' @@ -366,11 +368,11 @@ class Document(models.Model, FulltextMixin): self.data[key] = [ox.sanitize_html(t) for t in data[key]] elif ktype == '[string]': self.data[key] = [ox.escape_html(t) for t in data[key]] - elif isinstance(data[key], str): + elif isinstance(data[key], string_types): self.data[key] = ox.escape_html(data[key]) elif isinstance(data[key], list): def cleanup(i): - if isinstance(i, str): + if isinstance(i, string_types): i = ox.escape_html(i) return i self.data[key] = [cleanup(i) for i in data[key]] @@ -434,7 +436,6 @@ class Document(models.Model, FulltextMixin): 'matches', 'size', 'user', - 'groups', 'referenced', ] if self.extension in ('html', 'txt'): @@ -454,8 +455,6 @@ class Document(models.Model, FulltextMixin): response[key] = self.editable(user) elif key == 'user': response[key] = self.user.username - elif key == 'groups': - response[key] = [g.name for g in self.groups.all()] elif key == 'accessed': response[key] = self.accessed.aggregate(Max('access'))['access__max'] elif key == 'timesaccessed': @@ -476,7 +475,7 @@ class Document(models.Model, FulltextMixin): if self.extension == 'html': response['text'] = self.data.get('text', '') if item: - if isinstance(item, str): + if isinstance(item, string_types): item = Item.objects.get(public_id=item) d = self.descriptions.filter(item=item) if d.exists(): @@ -686,8 +685,8 @@ class ItemProperties(models.Model): created = models.DateTimeField(auto_now_add=True) modified = models.DateTimeField(auto_now=True) - item = models.ForeignKey(Item, on_delete=models.CASCADE) - document = models.ForeignKey(Document, related_name='descriptions', on_delete=models.CASCADE) + item = models.ForeignKey(Item) + document = models.ForeignKey(Document, related_name='descriptions') description = models.TextField(default="") index = models.IntegerField(default=0) @@ -702,13 +701,14 @@ class ItemProperties(models.Model): super(ItemProperties, self).save(*args, **kwargs) +@python_2_unicode_compatible class Access(models.Model): class Meta: unique_together = ("document", "user") access = models.DateTimeField(auto_now=True) - document = models.ForeignKey(Document, related_name='accessed', on_delete=models.CASCADE) - user = models.ForeignKey(User, null=True, related_name='accessed_documents', on_delete=models.CASCADE) + document = models.ForeignKey(Document, related_name='accessed') + user = models.ForeignKey(User, null=True, related_name='accessed_documents') accessed = models.IntegerField(default=0) def save(self, *args, **kwargs): @@ -721,9 +721,10 @@ class Access(models.Model): def __str__(self): if self.user: - return "%s/%s/%s" % (self.user, self.document, self.access) - return "%s/%s" % (self.item, self.access) + return u"%s/%s/%s" % (self.user, self.document, self.access) + return u"%s/%s" % (self.item, self.access) +@python_2_unicode_compatible class Facet(models.Model): ''' used for keys that can have multiple values like people, languages etc. @@ -734,13 +735,13 @@ class Facet(models.Model): class Meta: unique_together = ("document", "key", "value") - document = models.ForeignKey('Document', related_name='facets', on_delete=models.CASCADE) + document = models.ForeignKey('Document', related_name='facets') key = models.CharField(max_length=200, db_index=True) value = models.CharField(max_length=1000, db_index=True) sortvalue = models.CharField(max_length=1000, db_index=True) def __str__(self): - return "%s=%s" % (self.key, self.value) + return u"%s=%s" % (self.key, self.value) def save(self, *args, **kwargs): if not self.sortvalue: @@ -759,17 +760,18 @@ for key in settings.CONFIG['itemKeys']: if key.get('sortType') == 'person': Document.person_keys.append(key['id']) +@python_2_unicode_compatible class Find(models.Model): class Meta: unique_together = ('document', 'key') - document = models.ForeignKey('Document', related_name='find', db_index=True, on_delete=models.CASCADE) + document = models.ForeignKey('Document', related_name='find', db_index=True) key = models.CharField(max_length=200, db_index=True) value = models.TextField(blank=True, db_index=settings.DB_GIN_TRGM) def __str__(self): - return '%s=%s' % (self.key, self.value) + return u'%s=%s' % (self.key, self.value) ''' Sort @@ -777,7 +779,7 @@ table constructed based on info in settings.CONFIG['documentKeys'] ''' attrs = { '__module__': 'document.models', - 'document': models.OneToOneField('Document', related_name='sort', primary_key=True, on_delete=models.CASCADE), + 'document': models.OneToOneField('Document', related_name='sort', primary_key=True), 'created': models.DateTimeField(null=True, blank=True, db_index=True), } for key in list(filter(lambda k: k.get('sort', False) or k['type'] in ('integer', 'time', 'float', 'date', 'enum'), settings.CONFIG['documentKeys'])): diff --git a/pandora/document/sync_sort.py b/pandora/document/sync_sort.py index 4a0dff2d..4fe44881 100644 --- a/pandora/document/sync_sort.py +++ b/pandora/document/sync_sort.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import print_function from django.core.management.base import BaseCommand from django.db import connection, transaction diff --git a/pandora/document/views.py b/pandora/document/views.py index 84279ac9..2cc080e6 100644 --- a/pandora/document/views.py +++ b/pandora/document/views.py @@ -1,10 +1,12 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import import os import re from glob import glob import unicodedata +from six import string_types import ox from ox.utils import json from oxdjango.api import actions @@ -69,7 +71,7 @@ def addDocument(request, data): else: ids = [data['id']] if 'item' in data: - if isinstance(data['item'], str): + if isinstance(data['item'], string_types): item = Item.objects.get(public_id=data['item']) if item.editable(request.user): for id in ids: @@ -86,7 +88,7 @@ def addDocument(request, data): document.add(item) add_changelog(request, data, data['item']) elif 'entity' in data: - if isinstance(data['entity'], str): + if isinstance(data['entity'], string_types): entity = Entity.get(data['entity']) if entity.editable(request.user): for id in ids: diff --git a/pandora/documentcollection/managers.py b/pandora/documentcollection/managers.py index 45d5cbaa..30ead9f0 100644 --- a/pandora/documentcollection/managers.py +++ b/pandora/documentcollection/managers.py @@ -124,7 +124,7 @@ class CollectionManager(Manager): if conditions: qs = qs.filter(conditions) - if user.is_anonymous: + 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)) diff --git a/pandora/documentcollection/models.py b/pandora/documentcollection/models.py index 8504ae47..e0295c47 100644 --- a/pandora/documentcollection/models.py +++ b/pandora/documentcollection/models.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import import os import re @@ -9,6 +10,7 @@ from django.db import models from django.db.models import Max from django.contrib.auth import get_user_model from django.conf import settings +from django.utils.encoding import python_2_unicode_compatible from oxdjango.fields import JSONField import ox @@ -34,6 +36,7 @@ def get_collectionview(): def get_collectionsort(): return tuple(settings.CONFIG['user']['ui']['collectionSort']) +@python_2_unicode_compatible class Collection(models.Model): class Meta: @@ -41,7 +44,7 @@ class Collection(models.Model): created = models.DateTimeField(auto_now_add=True) modified = models.DateTimeField(auto_now=True) - user = models.ForeignKey(User, related_name='collections', on_delete=models.CASCADE) + user = models.ForeignKey(User, related_name='collections') groups = models.ManyToManyField(Group, blank=True, related_name='collections') name = models.CharField(max_length=255) status = models.CharField(max_length=20, default='private') @@ -55,7 +58,7 @@ class Collection(models.Model): view = models.TextField(default=get_collectionview) sort = JSONField(default=get_collectionsort, editable=False) - poster_frames = JSONField(default=list, editable=False) + poster_frames = JSONField(default=[], editable=False) #is through table still required? documents = models.ManyToManyField('document.Document', related_name='collections', @@ -114,13 +117,13 @@ class Collection(models.Model): return self.get_id() def get_id(self): - return '%s:%s' % (self.user.username, self.name) + return u'%s:%s' % (self.user.username, self.name) def accessible(self, user): return self.user == user or self.status in ('public', 'featured') def editable(self, user): - if not user or user.is_anonymous: + if not user or user.is_anonymous(): return False if self.user == user or \ self.groups.filter(id__in=user.groups.all()).count() > 0 or \ @@ -248,7 +251,7 @@ class Collection(models.Model): elif key == 'subscribers': response[key] = self.subscribed_users.all().count() elif key == 'subscribed': - if user and not user.is_anonymous: + if user and not user.is_anonymous(): response[key] = self.subscribed_users.filter(id=user.id).exists() else: response[key] = getattr(self, { @@ -315,26 +318,28 @@ class Collection(models.Model): path = source return path +@python_2_unicode_compatible class CollectionDocument(models.Model): created = models.DateTimeField(auto_now_add=True) modified = models.DateTimeField(auto_now=True) - collection = models.ForeignKey(Collection, on_delete=models.CASCADE) + collection = models.ForeignKey(Collection) index = models.IntegerField(default=0) - document = models.ForeignKey('document.Document', on_delete=models.CASCADE) + document = models.ForeignKey('document.Document') def __str__(self): - return '%s in %s' % (self.document, self.collection) + return u'%s in %s' % (self.document, self.collection) +@python_2_unicode_compatible class Position(models.Model): class Meta: unique_together = ("user", "collection", "section") - collection = models.ForeignKey(Collection, related_name='position', on_delete=models.CASCADE) - user = models.ForeignKey(User, related_name='collection_positions', on_delete=models.CASCADE) + collection = models.ForeignKey(Collection, related_name='position') + user = models.ForeignKey(User, related_name='collection_positions') section = models.CharField(max_length=255) position = models.IntegerField(default=0) def __str__(self): - return '%s/%s/%s' % (self.section, self.position, self.collection) + return u'%s/%s/%s' % (self.section, self.position, self.collection) diff --git a/pandora/documentcollection/views.py b/pandora/documentcollection/views.py index 66fa746d..170168de 100644 --- a/pandora/documentcollection/views.py +++ b/pandora/documentcollection/views.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import import os import re @@ -74,7 +75,7 @@ def findCollections(request, data): query = parse_query(data, request.user) #order - is_section_request = query['sort'] == [{'operator': '+', 'key': 'position'}] + is_section_request = query['sort'] == [{u'operator': u'+', u'key': u'position'}] def is_featured_condition(x): return x['key'] == 'status' and \ @@ -88,7 +89,7 @@ def findCollections(request, data): if is_section_request: qs = query['qs'] - if not is_featured and not request.user.is_anonymous: + 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: diff --git a/pandora/edit/managers.py b/pandora/edit/managers.py index 37909ad4..1bfaea00 100644 --- a/pandora/edit/managers.py +++ b/pandora/edit/managers.py @@ -124,7 +124,7 @@ class EditManager(Manager): if conditions: qs = qs.filter(conditions) - if user.is_anonymous: + 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)) diff --git a/pandora/edit/models.py b/pandora/edit/models.py index d71525a0..4ce6ab29 100644 --- a/pandora/edit/models.py +++ b/pandora/edit/models.py @@ -1,19 +1,21 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import -from glob import glob -from urllib.parse import quote -import os import re +import os import shutil +from glob import glob import subprocess import tempfile +from six.moves.urllib.parse import quote import ox from django.conf import settings from django.db import models, transaction from django.db.models import Max from django.contrib.auth import get_user_model +from django.utils.encoding import python_2_unicode_compatible from oxdjango.fields import JSONField from annotation.models import Annotation @@ -33,6 +35,7 @@ User = get_user_model() def get_path(f, x): return f.path(x) def get_icon_path(f, x): return get_path(f, 'icon.jpg') +@python_2_unicode_compatible class Edit(models.Model): class Meta: @@ -42,7 +45,7 @@ class Edit(models.Model): created = models.DateTimeField(auto_now_add=True) modified = models.DateTimeField(auto_now=True) - user = models.ForeignKey(User, related_name='edits', on_delete=models.CASCADE) + user = models.ForeignKey(User, related_name='edits') groups = models.ManyToManyField(Group, blank=True, related_name='edits') name = models.CharField(max_length=255) @@ -56,11 +59,11 @@ class Edit(models.Model): icon = models.ImageField(default=None, blank=True, null=True, upload_to=get_icon_path) - poster_frames = JSONField(default=list, editable=False) + poster_frames = JSONField(default=[], editable=False) subscribed_users = models.ManyToManyField(User, related_name='subscribed_edits') def __str__(self): - return '%s (%s)' % (self.name, self.user) + return u'%s (%s)' % (self.name, self.user) @classmethod def get(cls, id): @@ -70,7 +73,7 @@ class Edit(models.Model): return cls.objects.get(user__username=username, name=name) def get_id(self): - return '%s:%s' % (self.user.username, self.name) + return u'%s:%s' % (self.user.username, self.name) def get_absolute_url(self): return ('/edits/%s' % quote(self.get_id())).replace('%3A', ':') @@ -134,7 +137,7 @@ class Edit(models.Model): return self.user == user or self.status in ('public', 'featured') def editable(self, user): - if not user or user.is_anonymous: + if not user or user.is_anonymous(): return False if self.user == user or \ self.groups.filter(id__in=user.groups.all()).count() > 0 or \ @@ -400,7 +403,7 @@ class Edit(models.Model): elif key == 'subscribers': response[key] = self.subscribed_users.all().count() elif key == 'subscribed': - if user and not user.is_anonymous: + 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)) @@ -427,14 +430,15 @@ class Edit(models.Model): #p.wait() shutil.rmtree(tmp) +@python_2_unicode_compatible class Clip(models.Model): created = models.DateTimeField(auto_now_add=True) modified = models.DateTimeField(auto_now=True) - edit = models.ForeignKey(Edit, related_name='clips', on_delete=models.CASCADE) + edit = models.ForeignKey(Edit, related_name='clips') index = models.IntegerField(default=0) - item = models.ForeignKey(Item, null=True, default=None, related_name='editclip', on_delete=models.CASCADE) - annotation = models.ForeignKey(Annotation, null=True, default=None, related_name='editclip', on_delete=models.CASCADE) + item = models.ForeignKey(Item, null=True, default=None, related_name='editclip') + annotation = models.ForeignKey(Annotation, null=True, default=None, related_name='editclip') start = models.FloatField(default=0) end = models.FloatField(default=0) duration = models.FloatField(default=0) @@ -450,8 +454,8 @@ class Clip(models.Model): def __str__(self): if self.annotation: - return '%s' % self.annotation.public_id - return '%s/%0.3f-%0.3f' % (self.item.public_id, self.start, self.end) + return u'%s' % self.annotation.public_id + return u'%s/%0.3f-%0.3f' % (self.item.public_id, self.start, self.end) def get_id(self): return ox.toAZ(self.id) @@ -537,16 +541,17 @@ class Clip(models.Model): return clip.models.get_layers(item=item, interval=(start, end), user=user) +@python_2_unicode_compatible class Position(models.Model): class Meta: unique_together = ("user", "edit", "section") - edit = models.ForeignKey(Edit, related_name='position', on_delete=models.CASCADE) - user = models.ForeignKey(User, related_name='edit_position', on_delete=models.CASCADE) + edit = models.ForeignKey(Edit, related_name='position') + user = models.ForeignKey(User, related_name='edit_position') section = models.CharField(max_length=255) position = models.IntegerField(default=0) def __str__(self): - return '%s/%s/%s' % (self.section, self.position, self.edit) + return u'%s/%s/%s' % (self.section, self.position, self.edit) diff --git a/pandora/edit/views.py b/pandora/edit/views.py index 09261310..57abed30 100644 --- a/pandora/edit/views.py +++ b/pandora/edit/views.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import import os import re @@ -403,8 +404,7 @@ def findEdits(request, data): query = parse_query(data, request.user) #order - is_section_request = query['sort'] == [{'operator': '+', 'key': 'position'}] - + 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 \ @@ -414,7 +414,7 @@ def findEdits(request, data): if is_section_request: qs = query['qs'] - if not is_featured and not request.user.is_anonymous: + 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: diff --git a/pandora/entity/models.py b/pandora/entity/models.py index e97a6963..bb58cc53 100644 --- a/pandora/entity/models.py +++ b/pandora/entity/models.py @@ -1,17 +1,20 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import -from glob import glob -from urllib.parse import quote, unquote import os import re +from glob import glob import unicodedata +from six import string_types +from six.moves.urllib.parse import quote, unquote from django.db import models, transaction from django.db.models import Max from django.contrib.auth import get_user_model from django.db.models.signals import pre_delete, post_init from django.conf import settings +from django.utils.encoding import python_2_unicode_compatible from oxdjango.fields import JSONField import ox @@ -25,6 +28,7 @@ from . import managers User = get_user_model() +@python_2_unicode_compatible class Entity(models.Model): class ValueError(ValueError): '''Raised if a field name or value is invalid (based on the "entities" @@ -34,7 +38,7 @@ class Entity(models.Model): class Meta: unique_together = ("type", "name") - user = models.ForeignKey(User, related_name='entities', null=True, default=None, on_delete=models.CASCADE) + user = models.ForeignKey(User, related_name='entities', null=True, default=None) created = models.DateTimeField(auto_now_add=True) modified = models.DateTimeField(auto_now=True) @@ -60,7 +64,7 @@ class Entity(models.Model): self.name = self.name.decode('utf-8') self.name_sort = get_name_sort(self.name)[:255].lower() else: - self.name_sort = ox.sort_string(self.name or '')[:255].lower() or None + self.name_sort = ox.sort_string(self.name or u'')[:255].lower() or None self.name_find = '||' + '||'.join((self.name,) + self.alternativeNames) + '||' self.name_find = self.name_find.lower() super(Entity, self).save(*args, **kwargs) @@ -84,11 +88,11 @@ class Entity(models.Model): @classmethod def get_by_name(cls, name, type): - return cls.objects.get(name_find__contains='|%s|' % name.lower(), type=type) + return cls.objects.get(name_find__contains=u'|%s|' % name.lower(), type=type) @classmethod def get_or_create(model, name): - qs = model.objects.filter(name_find__contains='|%s|' % name.lower()) + qs = model.objects.filter(name_find__contains=u'|%s|' % name.lower()) if qs.count() == 0: instance = model(name=name) instance.save() @@ -113,7 +117,7 @@ class Entity(models.Model): DocumentProperties.objects.filter(document=document, entity=self).delete() def editable(self, user, item=None): - if not user or user.is_anonymous: + if not user or user.is_anonymous(): return False if user.is_staff or \ user.profile.capability('canEditEntities') == True or \ @@ -136,7 +140,7 @@ class Entity(models.Model): data['name'] = "Unnamed" name = data['name'] n = 1 - while Entity.objects.filter(name_find__contains='|%s|' % name.lower()).exclude(id=self.id).count() > 0: + while Entity.objects.filter(name_find__contains=u'|%s|' % name.lower()).exclude(id=self.id).count() > 0: n += 1 name = data['name'] + ' [%d]' % n self.name = name @@ -151,7 +155,7 @@ class Entity(models.Model): name_ = name n = 1 while name in used_names or \ - Entity.objects.filter(name_find__contains='|%s|' % name.lower()).exclude(id=self.id).count() > 0: + Entity.objects.filter(name_find__contains=u'|%s|' % name.lower()).exclude(id=self.id).count() > 0: n += 1 name = name_ + ' [%d]' % n names.append(name) @@ -187,7 +191,7 @@ class Entity(models.Model): .delete() else: #FIXME: more data validation - if isinstance(data[key], str): + if isinstance(data[key], string_types): self.data[key] = ox.sanitize_html(data[key]) else: self.data[key] = data[key] @@ -264,7 +268,7 @@ class Entity(models.Model): return response def annotation_value(self): - #return '%s' % (self.get_id(), ox.escape_html(self.name)) + #return u'%s' % (self.get_id(), ox.escape_html(self.name)) return ox.escape_html(self.name) def update_find(self): @@ -274,7 +278,7 @@ class Entity(models.Model): f, created = Find.objects.get_or_create(entity=self, key=key) if isinstance(value, bool): value = value and 'true' or 'false' - if isinstance(value, str): + if isinstance(value, string_types): value = ox.decode_html(ox.strip_tags(value.strip())) value = unicodedata.normalize('NFKD', value).lower() f.value = value @@ -288,10 +292,10 @@ class Entity(models.Model): for key in entity['keys']: value = self.data.get(key['id']) if isinstance(value, list): - value = '\n'.join(value) + value = u'\n'.join(value) save(key['id'], value) ids.append(key['id']) - save('name', '\n'.join([self.name] + list(self.alternativeNames))) + save('name', u'\n'.join([self.name] + list(self.alternativeNames))) self.find.exclude(key__in=ids).delete() def update_matches(self): @@ -340,6 +344,7 @@ post_init.connect( ) +@python_2_unicode_compatible class DocumentProperties(models.Model): class Meta: @@ -348,40 +353,42 @@ class DocumentProperties(models.Model): created = models.DateTimeField(auto_now_add=True) modified = models.DateTimeField(auto_now=True) - document = models.ForeignKey(Document, related_name='documentproperties', on_delete=models.CASCADE) - entity = models.ForeignKey(Entity, related_name='documentproperties', on_delete=models.CASCADE) + document = models.ForeignKey(Document, related_name='documentproperties') + entity = models.ForeignKey(Entity, related_name='documentproperties') index = models.IntegerField(default=0) data = JSONField(default=dict, editable=False) def __str__(self): - return "%r-%r" % (self.document, self.entity) + return u"%r-%r" % (self.document, self.entity) def save(self, *args, **kwargs): super(DocumentProperties, self).save(*args, **kwargs) +@python_2_unicode_compatible class Find(models.Model): class Meta: unique_together = ("entity", "key") - entity = models.ForeignKey('Entity', related_name='find', db_index=True, on_delete=models.CASCADE) + entity = models.ForeignKey('Entity', related_name='find', db_index=True) key = models.CharField(max_length=200, db_index=True) value = models.TextField(blank=True, db_index=settings.DB_GIN_TRGM) def __str__(self): - return "%s=%s" % (self.key, self.value) + return u"%s=%s" % (self.key, self.value) +@python_2_unicode_compatible class Link(models.Model): '''Models entity fields of type "entity".''' class Meta: unique_together = ("source", "key", "target") - source = models.ForeignKey(Entity, related_name='links', on_delete=models.CASCADE) + source = models.ForeignKey(Entity, related_name='links') key = models.CharField(max_length=200) - target = models.ForeignKey(Entity, related_name='backlinks', on_delete=models.CASCADE) + target = models.ForeignKey(Entity, related_name='backlinks') def __str__(self): - return "%s-[%s]->%s" % (self.source, self.key, self.target) + return u"%s-[%s]->%s" % (self.source, self.key, self.target) diff --git a/pandora/entity/views.py b/pandora/entity/views.py index b1e2d7e2..a42ec38d 100644 --- a/pandora/entity/views.py +++ b/pandora/entity/views.py @@ -1,5 +1,7 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import +from six import string_types import ox from ox.utils import json from oxdjango.api import actions @@ -58,7 +60,7 @@ def addEntity(request, data): for name in names: name = ox.decode_html(name) if models.Entity.objects.filter(type=data['type'], - name_find__icontains='|%s|'%name).count() != 0: + name_find__icontains=u'|%s|'%name).count() != 0: exists = True existing_names.append(name) if not exists: @@ -68,7 +70,7 @@ def addEntity(request, data): for key in ('type', 'alternativeNames'): if key in data and data[key]: value = data[key] - if isinstance(value, str): + if isinstance(value, string_types): value = ox.escape_html(value) if key == 'alternativeNames': value = tuple([ox.escape_html(v) for v in value]) @@ -85,7 +87,7 @@ def addEntity(request, data): type = data['type'] name = 'Unnamed' num = 1 - while models.Entity.objects.filter(name_find__icontains='|%s|'%name).count() > 0: + while models.Entity.objects.filter(name_find__icontains=u'|%s|'%name).count() > 0: num += 1 name = 'Unnamed [%d]' % num entity = models.Entity(name=name, type=type) diff --git a/pandora/event/admin.py b/pandora/event/admin.py index adb737ea..51597291 100644 --- a/pandora/event/admin.py +++ b/pandora/event/admin.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import from django.contrib import admin diff --git a/pandora/event/managers.py b/pandora/event/managers.py index aed5533c..8dcec385 100644 --- a/pandora/event/managers.py +++ b/pandora/event/managers.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import unicodedata +from six import string_types from django.db.models import Q, Manager from oxdjango.query import QuerySet @@ -29,7 +30,7 @@ def parseCondition(condition, user): key = k + get_operator(op, 'istr') key = str(key) - if isinstance(v, str): + if isinstance(v, string_types): v = unicodedata.normalize('NFKD', v).lower() if exclude: q = ~Q(**{k: v}) diff --git a/pandora/event/models.py b/pandora/event/models.py index f35c599a..e66c86fe 100644 --- a/pandora/event/models.py +++ b/pandora/event/models.py @@ -1,8 +1,10 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import from django.db import models, transaction from django.contrib.auth import get_user_model +from django.utils.encoding import python_2_unicode_compatible import ox from oxdjango import fields @@ -17,6 +19,7 @@ from . import managers User = get_user_model() +@python_2_unicode_compatible class Event(models.Model): ''' Events are events in time that can be once or recurring, @@ -30,7 +33,7 @@ class Event(models.Model): modified = models.DateTimeField(auto_now=True) defined = models.BooleanField(default=False) - user = models.ForeignKey(User, null=True, related_name='events', on_delete=models.CASCADE) + user = models.ForeignKey(User, null=True, related_name='events') name = models.CharField(null=True, max_length=255, unique=True) name_sort = models.CharField(null=True, max_length=255, db_index=True) @@ -63,7 +66,7 @@ class Event(models.Model): @classmethod def get_or_create(model, name): - qs = model.objects.filter(name_find__contains='|%s|' % name.lower()) + qs = model.objects.filter(name_find__contains=u'|%s|' % name.lower()) if qs.count() == 0: instance = model(name=name) instance.save() @@ -72,7 +75,7 @@ class Event(models.Model): return instance def editable(self, user): - if user and not user.is_anonymous \ + if user and not user.is_anonymous() \ and (not self.user or \ self.user == user or \ user.profile.capability('canEditEvents')): diff --git a/pandora/event/tasks.py b/pandora/event/tasks.py index 234dd5a7..85f30dff 100644 --- a/pandora/event/tasks.py +++ b/pandora/event/tasks.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import from celery.task import task diff --git a/pandora/event/views.py b/pandora/event/views.py index a535619f..47744fa5 100644 --- a/pandora/event/views.py +++ b/pandora/event/views.py @@ -1,8 +1,10 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import from django.db.models import Count from django.conf import settings +from six import string_types import ox from ox.utils import json @@ -35,7 +37,7 @@ def addEvent(request, data): for name in names: name = ox.decode_html(name) if models.Event.objects.filter(defined=True, - name_find__icontains='|%s|'%name).count() != 0: + name_find__icontains=u'|%s|'%name).count() != 0: exists = True existing_names.append(name) if not exists: @@ -46,7 +48,7 @@ def addEvent(request, data): 'type', 'alternativeNames'): if key in data and data[key]: value = data[key] - if isinstance(value, str): + if isinstance(value, string_types): value = ox.escape_html(value) if key == 'alternativeNames': value = tuple([ox.escape_html(v) for v in value]) @@ -91,7 +93,7 @@ def editEvent(request, data): names = [data.get('name', event.name)] + data.get('alternativeNames', []) for name in names: if models.Event.objects.filter(defined=True, - name_find__icontains='|%s|'%name).exclude(id=event.id).count() != 0: + name_find__icontains=u'|%s|'%name).exclude(id=event.id).count() != 0: conflict = True conflict_names.append(name) if not conflict: @@ -100,7 +102,7 @@ def editEvent(request, data): 'type', 'alternativeNames'): if key in data: value = data[key] - if isinstance(value, str): + if isinstance(value, string_types): value = ox.escape_html(value) if key == 'alternativeNames': value = tuple([ox.escape_html(v) for v in value]) diff --git a/pandora/home/apps.py b/pandora/home/apps.py index 90dc7137..1d6cbbdf 100644 --- a/pandora/home/apps.py +++ b/pandora/home/apps.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + from django.apps import AppConfig diff --git a/pandora/home/models.py b/pandora/home/models.py index 72a06c03..775e3714 100644 --- a/pandora/home/models.py +++ b/pandora/home/models.py @@ -1,10 +1,13 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import -from urllib.parse import quote +from six import string_types +from six.moves.urllib.parse import quote from django.db import models from django.db.models import Max from django.db.models.signals import pre_delete +from django.utils.encoding import python_2_unicode_compatible from oxdjango.fields import JSONField import ox @@ -14,6 +17,7 @@ from edit.models import Edit from documentcollection.models import Collection +@python_2_unicode_compatible class Item(models.Model): created = models.DateTimeField(auto_now_add=True) modified = models.DateTimeField(auto_now=True) @@ -23,7 +27,7 @@ class Item(models.Model): data = JSONField(default=dict, editable=False) def editable(self, user): - return user.is_authenticated and user.profile.capability("canManageHome") + return user.is_authenticated() and user.profile.capability("canManageHome") def edit(self, data): changed = False @@ -42,7 +46,7 @@ class Item(models.Model): len([d for d in data[key] if isinstance(d, int)]) == 4): return False else: - if not isinstance(data[key], str): + if not isinstance(data[key], string_types): return False self.data[key] = data[key] if key == 'contentid' and self.data[key]: @@ -149,7 +153,7 @@ class Item(models.Model): return j def __str__(self): - return "%s" % (self.get_id()) + return u"%s" % (self.get_id()) def delete_item(type, contentid): for home in Item.objects.all(): diff --git a/pandora/item/admin.py b/pandora/item/admin.py index a94cac7f..5cfebaf2 100644 --- a/pandora/item/admin.py +++ b/pandora/item/admin.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import from django.contrib import admin diff --git a/pandora/item/data_api.py b/pandora/item/data_api.py index 0c06015f..81d9d72c 100644 --- a/pandora/item/data_api.py +++ b/pandora/item/data_api.py @@ -1,4 +1,6 @@ # -*- coding: utf-8 -*- +from __future__ import division, with_statement + import ox from django.conf import settings diff --git a/pandora/item/management/commands/get_frame.py b/pandora/item/management/commands/get_frame.py index 1aaba624..69e78e4e 100644 --- a/pandora/item/management/commands/get_frame.py +++ b/pandora/item/management/commands/get_frame.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import print_function from django.core.management.base import BaseCommand from django.conf import settings diff --git a/pandora/item/management/commands/rebuild_filter.py b/pandora/item/management/commands/rebuild_filter.py index eb52b13b..2ff7e2df 100644 --- a/pandora/item/management/commands/rebuild_filter.py +++ b/pandora/item/management/commands/rebuild_filter.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import print_function from django.core.management.base import BaseCommand diff --git a/pandora/item/management/commands/rebuild_find.py b/pandora/item/management/commands/rebuild_find.py index 8418fc2f..e74e1afe 100644 --- a/pandora/item/management/commands/rebuild_find.py +++ b/pandora/item/management/commands/rebuild_find.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import print_function from django.core.management.base import BaseCommand from django.db import connection, transaction diff --git a/pandora/item/management/commands/rebuild_indexes.py b/pandora/item/management/commands/rebuild_indexes.py index 5ad61971..be885e07 100644 --- a/pandora/item/management/commands/rebuild_indexes.py +++ b/pandora/item/management/commands/rebuild_indexes.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import print_function from django.core.management.base import BaseCommand from django.db import connection, transaction diff --git a/pandora/item/management/commands/rebuild_sort.py b/pandora/item/management/commands/rebuild_sort.py index 5e29ade8..71a69986 100644 --- a/pandora/item/management/commands/rebuild_sort.py +++ b/pandora/item/management/commands/rebuild_sort.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import print_function from django.core.management.base import BaseCommand diff --git a/pandora/item/management/commands/rebuild_timelines.py b/pandora/item/management/commands/rebuild_timelines.py index f45559c4..d7c03355 100644 --- a/pandora/item/management/commands/rebuild_timelines.py +++ b/pandora/item/management/commands/rebuild_timelines.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import print_function import os from glob import glob diff --git a/pandora/item/management/commands/rebuildcache.py b/pandora/item/management/commands/rebuildcache.py index 50c3f20e..a15c67d5 100644 --- a/pandora/item/management/commands/rebuildcache.py +++ b/pandora/item/management/commands/rebuildcache.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import print_function import time diff --git a/pandora/item/management/commands/sqlfindindex.py b/pandora/item/management/commands/sqlfindindex.py index 81c39488..3ecba6a8 100644 --- a/pandora/item/management/commands/sqlfindindex.py +++ b/pandora/item/management/commands/sqlfindindex.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import print_function from django.core.management.base import BaseCommand @@ -43,12 +44,7 @@ class Command(BaseCommand): (document.models.Find._meta.db_table, 'value'), # Document Find ): cursor = connection.cursor() - contraints = connection.introspection.get_constraints(cursor, table) - indexes = { - ','.join(c['columns']): {'primary_key': c['primary_key'], 'unique': c['unique']} - for k, c in contraints.items() - if c['index'] or c['primary_key'] or c['unique'] - } + indexes = connection.introspection.get_indexes(cursor, table) drop = [] if column in indexes: for sql in ( @@ -67,12 +63,7 @@ class Command(BaseCommand): if options['debug']: print(sql) cursor.execute(sql) - contraints = connection.introspection.get_constraints(cursor, table) - indexes = { - ','.join(c['columns']): {'primary_key': c['primary_key'], 'unique': c['unique']} - for k, c in contraints.items() - if c['index'] or c['primary_key'] or c['unique'] - } + indexes = connection.introspection.get_indexes(cursor, table) if column not in indexes: create_index("%s_%s_idx" % (table, column), table, column) transaction.commit() diff --git a/pandora/item/management/commands/sync_itemsort.py b/pandora/item/management/commands/sync_itemsort.py index 4640229d..8af5e14a 100644 --- a/pandora/item/management/commands/sync_itemsort.py +++ b/pandora/item/management/commands/sync_itemsort.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import print_function from django.core.management.base import BaseCommand from django.db import connection, transaction diff --git a/pandora/item/management/commands/update_external.py b/pandora/item/management/commands/update_external.py index 49f3d842..4dbd1cbd 100644 --- a/pandora/item/management/commands/update_external.py +++ b/pandora/item/management/commands/update_external.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import print_function from django.core.management.base import BaseCommand diff --git a/pandora/item/management/commands/update_itemsfolder.py b/pandora/item/management/commands/update_itemsfolder.py index 95862428..38f0ea8d 100644 --- a/pandora/item/management/commands/update_itemsfolder.py +++ b/pandora/item/management/commands/update_itemsfolder.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import print_function import os @@ -37,10 +38,10 @@ class Command(BaseCommand): versions = os.path.join(prefix, os.path.dirname(path), 'Versions') s = f.streams.filter(source=None)[0] basename = os.path.basename(path).rsplit('.', 1)[0] - target = os.path.join(versions, '%s.%s' % (basename, s.format)) + target = os.path.join(versions, u'%s.%s' % (basename, s.format)) link(s.media.path, target) else: s = f.streams.filter(source=None)[0] basename = path.rsplit('.', 1)[0] - target = os.path.join(prefix, '%s.%s' % (basename, s.format)) + target = os.path.join(prefix, u'%s.%s' % (basename, s.format)) link(s.media.path, target) diff --git a/pandora/item/managers.py b/pandora/item/managers.py index 654f1dfe..be409e5f 100644 --- a/pandora/item/managers.py +++ b/pandora/item/managers.py @@ -1,8 +1,10 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import from datetime import datetime import unicodedata +from six import string_types from django.db.models import Q, Manager from django.conf import settings @@ -79,7 +81,7 @@ def parseCondition(condition, user, owner=None): if (not exclude and op == '=' or op in ('$', '^')) and v == '': return Q() - elif k == 'filename' and (user.is_anonymous or not user.profile.capability('canSeeMedia')): + elif k == 'filename' and (user.is_anonymous() or not user.profile.capability('canSeeMedia')): return Q(id=0) elif k == 'oshash': return Q(files__oshash=v) @@ -98,7 +100,7 @@ def parseCondition(condition, user, owner=None): q = ~q return q elif k in ('canplayvideo', 'canplayclips'): - level = user.is_anonymous and 'guest' or user.profile.get_level() + level = user.is_anonymous() and 'guest' or user.profile.get_level() allowed_level = settings.CONFIG['capabilities'][{ 'canplayvideo': 'canPlayVideo', 'canplayclips': 'canPlayClips' @@ -122,7 +124,7 @@ def parseCondition(condition, user, owner=None): else: value_key = k if not k.startswith('public_id'): - if isinstance(v, str): + if isinstance(v, string_types): v = unicodedata.normalize('NFKD', v).lower() if k in facet_keys: in_find = False @@ -247,7 +249,7 @@ class ItemManager(Manager): if l != "*": l = l.split(":") only_public = True - if not user.is_anonymous: + if not user.is_anonymous(): if len(l) == 1: l = [user.username] + l if user.username == l[0]: @@ -303,7 +305,7 @@ class ItemManager(Manager): qs = qs.distinct() #anonymous can only see public items - if not user or user.is_anonymous: + if not user or user.is_anonymous(): level = 'guest' allowed_level = settings.CONFIG['capabilities']['canSeeItem'][level] qs = qs.filter(level__lte=allowed_level) diff --git a/pandora/item/models.py b/pandora/item/models.py index 1733e896..b9dd78ee 100644 --- a/pandora/item/models.py +++ b/pandora/item/models.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import import json import os @@ -10,7 +11,9 @@ import unicodedata import uuid from datetime import datetime from glob import glob -from urllib.parse import quote + +from six import PY2, string_types +from six.moves.urllib.parse import quote from django.conf import settings from django.contrib.auth import get_user_model @@ -18,6 +21,7 @@ from django.core.files.temp import NamedTemporaryFile from django.db import models, transaction, connection from django.db.models import Q, Sum, Max from django.db.models.signals import pre_delete +from django.utils.encoding import python_2_unicode_compatible from django.utils import datetime_safe import ox @@ -44,6 +48,8 @@ import archive.models User = get_user_model() +if not PY2: + unicode = str def get_id(info): q = Item.objects.all() @@ -158,11 +164,12 @@ def get_poster_path(f, x): def get_torrent_path(f, x): return get_path(f, 'torrent.torrent') +@python_2_unicode_compatible class Item(models.Model): created = models.DateTimeField(auto_now_add=True) modified = models.DateTimeField(auto_now=True) - user = models.ForeignKey(User, null=True, related_name='items', on_delete=models.CASCADE) + user = models.ForeignKey(User, null=True, related_name='items') groups = models.ManyToManyField(Group, blank=True, related_name='items') # while metadata is updated, files are set to rendered=False @@ -214,7 +221,7 @@ class Item(models.Model): return default def access(self, user): - if user.is_anonymous: + if user.is_anonymous(): level = 'guest' else: level = user.profile.get_level() @@ -229,7 +236,7 @@ class Item(models.Model): return False def editable(self, user): - if user.is_anonymous: + if user.is_anonymous(): return False if user.profile.capability('canEditMetadata') or \ user.is_staff or \ @@ -280,11 +287,11 @@ class Item(models.Model): self.data[key] = [ox.escape_html(t) for t in data[key]] elif key in ('episodeTitle', 'seriesTitle', 'episodeDirector', 'seriesYear'): self.data[key] = ox.escape_html(data[key]) - elif isinstance(data[key], str): + elif isinstance(data[key], string_types): self.data[key] = ox.escape_html(data[key]) elif isinstance(data[key], list): def cleanup(i): - if isinstance(i, str): + if isinstance(i, string_types): i = ox.escape_html(i) return i self.data[key] = [cleanup(i) for i in data[key]] @@ -325,7 +332,7 @@ class Item(models.Model): if c: for t in list(c): if c[t]: - if isinstance(c[t][0], str): + if isinstance(c[t][0], string_types): c[t] = [{'id': i, 'title': None} for i in c[t]] ids = [i['id'] for i in c[t]] known = {} @@ -343,10 +350,10 @@ class Item(models.Model): def __str__(self): year = self.get('year') if year: - string = '%s (%s)' % (ox.decode_html(self.get('title', 'Untitled')), self.get('year')) + string = u'%s (%s)' % (ox.decode_html(self.get('title', 'Untitled')), self.get('year')) else: - string = self.get('title', 'Untitled') - return '[%s] %s' % (self.public_id, string) + string = self.get('title', u'Untitled') + return u'[%s] %s' % (self.public_id, string) def get_absolute_url(self): return '/%s' % self.public_id @@ -393,7 +400,7 @@ class Item(models.Model): title = self.get(key, 'Untitled') while q.count() != 0: n += 1 - self.data[key] = '%s [%d]' % (title, n) + self.data[key] = u'%s [%d]' % (title, n) oxdbId = self.oxdb_id() q = Item.objects.filter(oxdbId=oxdbId).exclude(id=self.id) self.oxdbId = oxdbId @@ -622,7 +629,7 @@ class Item(models.Model): if value: i[key] = value - if 'cast' in i and isinstance(i['cast'][0], str): + if 'cast' in i and isinstance(i['cast'][0], string_types): i['cast'] = [i['cast']] if 'cast' in i and isinstance(i['cast'][0], list): i['cast'] = [{'actor': x[0], 'character': x[1]} for x in i['cast']] @@ -793,7 +800,7 @@ class Item(models.Model): f, created = ItemFind.objects.get_or_create(item=self, key=key) if isinstance(value, bool): value = value and 'true' or 'false' - if isinstance(value, str): + if isinstance(value, string_types): value = ox.decode_html(ox.strip_tags(value.strip())) value = unicodedata.normalize('NFKD', value).lower() f.value = value @@ -814,7 +821,7 @@ class Item(models.Model): for key in settings.CONFIG['itemKeys']: i = key['id'] if i == 'title': - save(i, '\n'.join(get_titles())) + save(i, u'\n'.join(get_titles())) elif i == 'rightslevel': save(i, self.level) elif i == 'filename': @@ -823,17 +830,17 @@ class Item(models.Model): qs = Annotation.objects.filter(item=self) qs = qs.filter(layer__in=Annotation.public_layers()).exclude(findvalue=None) qs = qs.order_by('start') - save(i, '\n'.join([l.findvalue for l in qs])) + save(i, u'\n'.join([l.findvalue for l in qs])) elif key['type'] == 'layer': qs = Annotation.objects.filter(item=self).exclude(findvalue=None) qs = qs.filter(layer=i) qs = qs.order_by('start') - save(i, '\n'.join(list(filter(None, [l.findvalue for l in qs])))) + save(i, u'\n'.join(list(filter(None, [l.findvalue for l in qs])))) layer_keys.append(i) elif i != '*' and i not in self.facet_keys: value = self.get(i) if isinstance(value, list): - value = '\n'.join(value) + value = u'\n'.join(value) save(i, value) for key in self.facet_keys: @@ -904,15 +911,15 @@ class Item(models.Model): s = ItemSort(item=self) def sortNames(values): - sort_value = '' + sort_value = u'' if values: - sort_value = '; '.join([get_name_sort(name) for name in values]) + sort_value = u'; '.join([get_name_sort(name) for name in values]) if not sort_value: - sort_value = '' + sort_value = u'' return sort_value.lower() def set_value(s, name, value): - if isinstance(value, str): + if isinstance(value, string_types): value = ox.decode_html(value.lower()) if not value: value = None @@ -1012,7 +1019,7 @@ class Item(models.Model): sort_type = sort_type[0] if name not in self.base_keys: if sort_type == 'title': - value = get_title_sort(self.get(source, 'Untitled')) + value = get_title_sort(self.get(source, u'Untitled')) value = utils.sort_title(value)[:955] set_value(s, name, value) elif sort_type == 'person': @@ -1020,9 +1027,9 @@ class Item(models.Model): value = utils.sort_string(value)[:955] set_value(s, name, value) elif sort_type == 'string': - value = self.get(source, '') + value = self.get(source, u'') if isinstance(value, list): - value = ','.join(value) + value = u','.join(value) value = utils.sort_string(value)[:955] set_value(s, name, value) elif sort_type == 'words': @@ -1048,7 +1055,7 @@ class Item(models.Model): set_value(s, name, value) elif sort_type == 'date': value = value_ = self.get(source) - if isinstance(value, str): + if isinstance(value, string_types): value_ = None for fmt in ('%Y-%m-%d', '%Y-%m', '%Y'): try: @@ -1087,7 +1094,7 @@ class Item(models.Model): if not current_values: current_values = [] else: - current_values = [str(current_values)] + current_values = [unicode(current_values)] filter_map = utils.get_by_id(settings.CONFIG['itemKeys'], key).get('filterMap') if filter_map: @@ -1777,6 +1784,7 @@ for key in settings.CONFIG['itemKeys']: if key.get('sortType') == 'person': Item.person_keys.append(key['id']) +@python_2_unicode_compatible class ItemFind(models.Model): """ used to find items, @@ -1787,19 +1795,19 @@ class ItemFind(models.Model): class Meta: unique_together = ("item", "key") - item = models.ForeignKey('Item', related_name='find', db_index=True, on_delete=models.CASCADE) + item = models.ForeignKey('Item', related_name='find', db_index=True) key = models.CharField(max_length=200, db_index=True) value = models.TextField(blank=True, db_index=settings.DB_GIN_TRGM) def __str__(self): - return "%s=%s" % (self.key, self.value) + return u"%s=%s" % (self.key, self.value) ''' ItemSort table constructed based on info in settings.CONFIG['itemKeys'] ''' attrs = { '__module__': 'item.models', - 'item': models.OneToOneField('Item', related_name='sort', primary_key=True, on_delete=models.CASCADE), + 'item': models.OneToOneField('Item', related_name='sort', primary_key=True), 'duration': models.FloatField(null=True, blank=True, db_index=True), 'width': models.BigIntegerField(null=True, blank=True, db_index=True), 'height': models.BigIntegerField(null=True, blank=True, db_index=True), @@ -1818,13 +1826,14 @@ for key in list(filter(lambda k: k.get('sort', False) or k['type'] in ('integer' ItemSort = type('ItemSort', (models.Model,), attrs) ItemSort.fields = [f.name for f in ItemSort._meta.fields] +@python_2_unicode_compatible class Access(models.Model): class Meta: unique_together = ("item", "user") access = models.DateTimeField(auto_now=True) - item = models.ForeignKey(Item, related_name='accessed', on_delete=models.CASCADE) - user = models.ForeignKey(User, null=True, related_name='accessed_items', on_delete=models.CASCADE) + item = models.ForeignKey(Item, related_name='accessed') + user = models.ForeignKey(User, null=True, related_name='accessed_items') accessed = models.IntegerField(default=0) def save(self, *args, **kwargs): @@ -1837,9 +1846,10 @@ class Access(models.Model): def __str__(self): if self.user: - return "%s/%s/%s" % (self.user, self.item, self.access) - return "%s/%s" % (self.item, self.access) + return u"%s/%s/%s" % (self.user, self.item, self.access) + return u"%s/%s" % (self.item, self.access) +@python_2_unicode_compatible class Facet(models.Model): ''' used for keys that can have multiple values like people, languages etc. @@ -1850,13 +1860,13 @@ class Facet(models.Model): class Meta: unique_together = ("item", "key", "value") - item = models.ForeignKey('Item', related_name='facets', on_delete=models.CASCADE) + item = models.ForeignKey('Item', related_name='facets') key = models.CharField(max_length=200, db_index=True) value = models.CharField(max_length=1000, db_index=True) sortvalue = models.CharField(max_length=1000, db_index=True) def __str__(self): - return "%s=%s" % (self.key, self.value) + return u"%s=%s" % (self.key, self.value) def save(self, *args, **kwargs): if not self.sortvalue: @@ -1876,7 +1886,7 @@ class Description(models.Model): class AnnotationSequence(models.Model): - item = models.OneToOneField('Item', related_name='_annotation_sequence', on_delete=models.CASCADE) + item = models.OneToOneField('Item', related_name='_annotation_sequence') value = models.BigIntegerField(default=1) @classmethod diff --git a/pandora/item/site.py b/pandora/item/site.py deleted file mode 100644 index cb186eab..00000000 --- a/pandora/item/site.py +++ /dev/null @@ -1,51 +0,0 @@ -# -*- coding: utf-8 -*- - -from django.urls import path, re_path - -from . import views - -urls = [ - [ - #frames - re_path(r'^(?P[A-Z0-9].*)/(?P\d+)p(?P[\d\.]*)\.jpg$', views.frame), - - #timelines - re_path(r'^(?P[A-Z0-9].*)/timeline(?P[a-z]*)(?P\d+)p(?P\d+)\.(?Ppng|jpg)$', views.timeline), - re_path(r'^(?P[A-Z0-9].*)/timeline(?P[a-z]*)(?P\d+)p\.(?Ppng|jpg)$', views.timeline), - - #download - re_path(r'^(?P[A-Z0-9].*)/download$', views.download), - re_path(r'^(?P[A-Z0-9].*)/download/$', views.download), - re_path(r'^(?P[A-Z0-9].*)/download/source/(?P\d+)?$', views.download_source), - re_path(r'^(?P[A-Z0-9].*)/download/(?P\d+)p(?P\d+)\.(?Pwebm|ogv|mp4)$', views.download), - re_path(r'^(?P[A-Z0-9].*)/download/(?P\d+)p\.(?Pwebm|ogv|mp4)$', views.download), - - #video - re_path(r'^(?P[A-Z0-9].*)/(?P\d+)p(?P\d*)\.(?Pwebm|ogv|mp4)$', views.video), - re_path(r'^(?P[A-Z0-9].*)/(?P\d+)p(?P\d*)\.(?P.+)\.(?Pwebm|ogv|mp4)$', views.video), - - #torrent - re_path(r'^(?P[A-Z0-9].*)/torrent$', views.torrent), - re_path(r'^(?P[A-Z0-9].*)/torrent/(?P.*?)$', views.torrent), - - #export - re_path(r'^(?P[A-Z0-9].*)/json$', views.item_json), - re_path(r'^(?P[A-Z0-9].*)/xml$', views.item_xml), - - #srt export - re_path(r'^(?P[A-Z0-9].*)/(?P.+)\.(?:(?P.{2})\.)?(?Psrt|vtt)$', views.srt), - - #icon - re_path(r'^(?P[A-Z0-9].*)/icon(?P\d*)\.jpg$', views.icon), - - #poster - re_path(r'^(?P[A-Z0-9].*)/posterframe(?P\d+).jpg$', views.poster_frame), - re_path(r'^(?P[A-Z0-9].*)/poster(?P\d+)\.jpg$', views.poster), - re_path(r'^(?P[A-Z0-9].*)/siteposter(?P\d*)\.jpg$', views.siteposter), - re_path(r'^(?P[A-Z0-9].*)/poster\.jpg$', views.siteposter), - - re_path(r'^random$', views.random_annotation), - ], - 'item', - 'item', -] diff --git a/pandora/item/tasks.py b/pandora/item/tasks.py index 27511acc..eca80083 100644 --- a/pandora/item/tasks.py +++ b/pandora/item/tasks.py @@ -1,11 +1,12 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import -from datetime import timedelta, datetime -from urllib.parse import quote -import gzip import os +from datetime import timedelta, datetime +import gzip import random +from six.moves.urllib.parse import quote from celery.task import task, periodic_task from django.conf import settings from django.db import connection, transaction diff --git a/pandora/item/timelines.py b/pandora/item/timelines.py index f6ff9f72..87fe741c 100644 --- a/pandora/item/timelines.py +++ b/pandora/item/timelines.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import division, with_statement, print_function import math import os diff --git a/pandora/item/urls.py b/pandora/item/urls.py new file mode 100644 index 00000000..332da86d --- /dev/null +++ b/pandora/item/urls.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- + +from django.conf.urls import url + +from . import views + +urlpatterns = [ + #frames + url(r'^(?P[A-Z0-9].*)/(?P\d+)p(?P[\d\.]*)\.jpg$', views.frame), + + #timelines + url(r'^(?P[A-Z0-9].*)/timeline(?P[a-z]*)(?P\d+)p(?P\d+)\.(?Ppng|jpg)$', views.timeline), + url(r'^(?P[A-Z0-9].*)/timeline(?P[a-z]*)(?P\d+)p\.(?Ppng|jpg)$', views.timeline), + + #download + url(r'^(?P[A-Z0-9].*)/download$', views.download), + url(r'^(?P[A-Z0-9].*)/download/$', views.download), + url(r'^(?P[A-Z0-9].*)/download/source/(?P\d+)?$', views.download_source), + url(r'^(?P[A-Z0-9].*)/download/(?P\d+)p(?P\d+)\.(?Pwebm|ogv|mp4)$', views.download), + url(r'^(?P[A-Z0-9].*)/download/(?P\d+)p\.(?Pwebm|ogv|mp4)$', views.download), + + #video + url(r'^(?P[A-Z0-9].*)/(?P\d+)p(?P\d*)\.(?Pwebm|ogv|mp4)$', views.video), + url(r'^(?P[A-Z0-9].*)/(?P\d+)p(?P\d*)\.(?P.+)\.(?Pwebm|ogv|mp4)$', views.video), + + #torrent + url(r'^(?P[A-Z0-9].*)/torrent$', views.torrent), + url(r'^(?P[A-Z0-9].*)/torrent/(?P.*?)$', views.torrent), + + #export + url(r'^(?P[A-Z0-9].*)/json$', views.item_json), + url(r'^(?P[A-Z0-9].*)/xml$', views.item_xml), + + #srt export + url(r'^(?P[A-Z0-9].*)/(?P.+)\.(?:(?P.{2})\.)?(?Psrt|vtt)$', views.srt), + + #icon + url(r'^(?P[A-Z0-9].*)/icon(?P\d*)\.jpg$', views.icon), + + #poster + url(r'^(?P[A-Z0-9].*)/posterframe(?P\d+).jpg$', views.poster_frame), + url(r'^(?P[A-Z0-9].*)/poster(?P\d+)\.jpg$', views.poster), + url(r'^(?P[A-Z0-9].*)/siteposter(?P\d*)\.jpg$', views.siteposter), + url(r'^(?P[A-Z0-9].*)/poster\.jpg$', views.siteposter), + + url(r'^random$', views.random_annotation), +] diff --git a/pandora/item/utils.py b/pandora/item/utils.py index fce7d724..9d0beb66 100644 --- a/pandora/item/utils.py +++ b/pandora/item/utils.py @@ -6,6 +6,7 @@ import unicodedata import ox from ox import sort_string +from six import PY2 def safe_filename(filename): @@ -54,14 +55,13 @@ def plural_key(term): def sort_title(title): - title = title.replace('Æ', 'Ae') + title = title.replace(u'Æ', 'Ae') if isinstance(title, bytes): title = title.decode('utf-8') - title = ox.decode_html(title) title = sort_string(title) #title - title = re.sub('[\'!¿¡,\.;\-"\:\*\[\]]', '', title) + title = re.sub(u'[\'!¿¡,\.;\-"\:\*\[\]]', '', title) return title.strip() def get_positions(ids, pos, decode_id=False): @@ -91,7 +91,11 @@ def get_by_id(objects, id): return get_by_key(objects, 'id', id) def normalize_dict(encoding, data): - if isinstance(data, str): + if PY2: + string_type = unicode + else: + string_type = str + if isinstance(data, string_type): data = unicodedata.normalize(encoding, data) elif isinstance(data, dict): for key in data: diff --git a/pandora/item/views.py b/pandora/item/views.py index 23fadc29..97f8aa23 100644 --- a/pandora/item/views.py +++ b/pandora/item/views.py @@ -1,12 +1,14 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import -from datetime import datetime, timedelta -from urllib.parse import quote, urlparse -import mimetypes import os.path +import mimetypes import random import time +from datetime import datetime, timedelta +from six import PY2 +from six.moves.urllib.parse import quote, urlparse from PIL import Image from django.db.models import Count, Sum from django.http import HttpResponse, HttpResponseForbidden, Http404 @@ -34,6 +36,8 @@ from changelog.models import add_changelog from oxdjango.api import actions +if not PY2: + unicode = str def _order_query(qs, sort, prefix='sort__'): order_by = [] @@ -140,7 +144,7 @@ def get_positions(request, query): return utils.get_positions(ids, query['positions']) def is_editable(request, item): - if request.user.is_anonymous: + if request.user.is_anonymous(): return False if not hasattr(request, 'user_group_names'): request.user_group_names = {g.name for g in request.user.groups.all()} @@ -1093,7 +1097,7 @@ def video(request, id, resolution, format, index=None, track=None): ext = '.%s' % format duration = stream.info['duration'] - filename = "Clip of %s - %s-%s - %s %s%s" % ( + filename = u"Clip of %s - %s-%s - %s %s%s" % ( item.get('title'), ox.format_duration(t[0] * 1000).replace(':', '.')[:-4], ox.format_duration(t[1] * 1000).replace(':', '.')[:-4], @@ -1162,9 +1166,9 @@ def srt(request, id, layer, language=None, index=None, ext='srt'): content_type, encoder = _subtitle_formats[ext] response = HttpResponse() if language: - filename = "%s.%s.%s" % (item.get('title'), language, ext) + filename = u"%s.%s.%s" % (item.get('title'), language, ext) else: - filename = "%s.%s" % (item.get('title'), ext) + filename = u"%s.%s" % (item.get('title'), ext) response['Content-Disposition'] = "attachment; filename*=UTF-8''%s" % quote(filename.encode('utf-8')) response['Content-Type'] = content_type response.write(item.srt(layer, language, encoder=encoder)) @@ -1202,7 +1206,7 @@ def atom_xml(request): el.text = atom_link level = settings.CONFIG['capabilities']['canSeeItem']['guest'] - if not request.user.is_anonymous: + if not request.user.is_anonymous(): level = request.user.profile.level for item in models.Item.objects.filter(level__lte=level, rendered=True).order_by('-created')[:7]: if add_updated: @@ -1228,7 +1232,7 @@ def atom_xml(request): if item.get('director'): el = ET.SubElement(entry, "author") name = ET.SubElement(el, "name") - name.text = ox.decode_html(', '.join(item.get('director'))) + name.text = ox.decode_html(u', '.join(item.get('director'))) elif item.user: el = ET.SubElement(entry, "author") name = ET.SubElement(el, "name") @@ -1277,9 +1281,9 @@ def atom_xml(request): }.get(key, key)) if value and value != -1: el = ET.SubElement(format, key) - el.text = str(value) + el.text = unicode(value) el = ET.SubElement(format, 'pixel_aspect_ratio') - el.text = "1:1" + el.text = u"1:1" if has_capability(request.user, 'canDownloadVideo'): if item.torrent: @@ -1354,7 +1358,7 @@ def oembed(request): oxml = ET.Element('oembed') for key in oembed: e = ET.SubElement(oxml, key) - e.text = str(oembed[key]) + e.text = unicode(oembed[key]) return HttpResponse( '\n' + ET.tostring(oxml).decode(), 'application/xml' @@ -1382,7 +1386,7 @@ def sitemap_part_xml(request, part): def item_json(request, id): level = settings.CONFIG['capabilities']['canSeeItem']['guest'] - if not request.user.is_anonymous: + if not request.user.is_anonymous(): level = request.user.profile.level qs = models.Item.objects.filter(public_id=id, level__lte=level) if qs.count() == 0: @@ -1395,7 +1399,7 @@ def item_json(request, id): def item_xml(request, id): level = settings.CONFIG['capabilities']['canSeeItem']['guest'] - if not request.user.is_anonymous: + if not request.user.is_anonymous(): level = request.user.profile.level qs = models.Item.objects.filter(public_id=id, level__lte=level) if qs.count() == 0: @@ -1420,7 +1424,7 @@ def item_xml(request, id): xmltree(root, k, data[k]) else: e = ET.SubElement(root, key) - e.text = str(data) + e.text = unicode(data) oxml = ET.Element('item') xmltree(oxml, 'item', j) @@ -1435,7 +1439,7 @@ def item(request, id): view = None template = 'index.html' level = settings.CONFIG['capabilities']['canSeeItem']['guest'] - if not request.user.is_anonymous: + if not request.user.is_anonymous(): level = request.user.profile.level qs = models.Item.objects.filter(public_id=id, level__lte=level) if qs.count() == 0: @@ -1480,7 +1484,7 @@ def item(request, id): else: title = key['title'] if key else k.capitalize() if isinstance(value, list): - value = value = ', '.join([str(v) for v in value]) + value = value = u', '.join([unicode(v) for v in value]) elif key and key.get('type') == 'float': value = '%0.3f' % value elif key and key.get('type') == 'time': @@ -1489,7 +1493,7 @@ def item(request, id): clips = [] clip = {'in': 0, 'annotations': []} # logged in users should have javascript. not adding annotations makes load faster - if not settings.USE_IMDB and request.user.is_anonymous: + if not settings.USE_IMDB and request.user.is_anonymous(): for a in item.annotations.exclude( layer='subtitles' ).exclude( @@ -1509,13 +1513,13 @@ def item(request, id): head_title = item.get('title', '') title = item.get('title', '') if item.get('director'): - head_title += ' (%s)' % ', '.join(item.get('director', [])) + head_title += u' (%s)' % u', '.join(item.get('director', [])) if item.get('year'): - head_title += ' %s' % item.get('year') - title += ' (%s)' % item.get('year') + head_title += u' %s' % item.get('year') + title += u' (%s)' % item.get('year') if view: - head_title += ' – %s' % view - head_title += ' – %s' % settings.SITENAME + head_title += u' – %s' % view + head_title += u' – %s' % settings.SITENAME head_title = ox.decode_html(head_title) title = ox.decode_html(title) ctx = { diff --git a/pandora/itemlist/managers.py b/pandora/itemlist/managers.py index e00c3a14..989c8704 100644 --- a/pandora/itemlist/managers.py +++ b/pandora/itemlist/managers.py @@ -124,7 +124,7 @@ class ListManager(Manager): if conditions: qs = qs.filter(conditions) - if user.is_anonymous: + 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)) diff --git a/pandora/itemlist/models.py b/pandora/itemlist/models.py index ad2cfb14..7d696381 100644 --- a/pandora/itemlist/models.py +++ b/pandora/itemlist/models.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import import os import re @@ -9,6 +10,7 @@ from django.db import models from django.db.models import Max from django.contrib.auth import get_user_model from django.conf import settings +from django.utils.encoding import python_2_unicode_compatible from oxdjango.fields import JSONField import ox @@ -26,6 +28,7 @@ def get_icon_path(f, x): return get_path(f, 'icon.jpg') def get_listview(): return settings.CONFIG['user']['ui']['listView'] def get_listsort(): return tuple(settings.CONFIG['user']['ui']['listSort']) +@python_2_unicode_compatible class List(models.Model): class Meta: @@ -33,7 +36,7 @@ class List(models.Model): created = models.DateTimeField(auto_now_add=True) modified = models.DateTimeField(auto_now=True) - user = models.ForeignKey(User, related_name='lists', on_delete=models.CASCADE) + user = models.ForeignKey(User, related_name='lists') groups = models.ManyToManyField(Group, blank=True, related_name='lists') name = models.CharField(max_length=255) status = models.CharField(max_length=20, default='private') @@ -47,7 +50,7 @@ class List(models.Model): view = models.TextField(default=get_listview) sort = JSONField(default=get_listsort, editable=False) - poster_frames = JSONField(default=list, editable=False) + poster_frames = JSONField(default=[], editable=False) #is through table still required? items = models.ManyToManyField('item.Item', related_name='lists', @@ -107,13 +110,13 @@ class List(models.Model): return self.get_id() def get_id(self): - return '%s:%s' % (self.user.username, self.name) + return u'%s:%s' % (self.user.username, self.name) def accessible(self, user): return self.user == user or self.status in ('public', 'featured') def editable(self, user): - if not user or user.is_anonymous: + if not user or user.is_anonymous(): return False if self.user == user or \ self.groups.filter(id__in=user.groups.all()).count() > 0 or \ @@ -241,7 +244,7 @@ class List(models.Model): elif key == 'subscribers': response[key] = self.subscribed_users.all().count() elif key == 'subscribed': - if user and not user.is_anonymous: + if user and not user.is_anonymous(): response[key] = self.subscribed_users.filter(id=user.id).exists() else: response[key] = getattr(self, { @@ -311,27 +314,29 @@ class List(models.Model): path = source return path +@python_2_unicode_compatible class ListItem(models.Model): created = models.DateTimeField(auto_now_add=True) modified = models.DateTimeField(auto_now=True) - list = models.ForeignKey(List, on_delete=models.CASCADE) + list = models.ForeignKey(List) index = models.IntegerField(default=0) - item = models.ForeignKey('item.Item', on_delete=models.CASCADE) + item = models.ForeignKey('item.Item') def __str__(self): - return '%s in %s' % (self.item, self.list) + return u'%s in %s' % (self.item, self.list) +@python_2_unicode_compatible class Position(models.Model): class Meta: unique_together = ("user", "list", "section") - list = models.ForeignKey(List, related_name='position', on_delete=models.CASCADE) - user = models.ForeignKey(User, on_delete=models.CASCADE) + list = models.ForeignKey(List, related_name='position') + user = models.ForeignKey(User) section = models.CharField(max_length=255) position = models.IntegerField(default=0) def __str__(self): - return '%s/%s/%s' % (self.section, self.position, self.list) + return u'%s/%s/%s' % (self.section, self.position, self.list) diff --git a/pandora/itemlist/views.py b/pandora/itemlist/views.py index 04edcc9d..067b7d51 100644 --- a/pandora/itemlist/views.py +++ b/pandora/itemlist/views.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import import os import re @@ -74,7 +75,7 @@ def findLists(request, data): query = parse_query(data, request.user) #order - is_section_request = query['sort'] == [{'operator': '+', 'key': 'position'}] + 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 \ @@ -86,7 +87,7 @@ def findLists(request, data): if is_section_request: qs = query['qs'] - if not is_featured and not request.user.is_anonymous: + 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: diff --git a/pandora/log/models.py b/pandora/log/models.py index 25e850c7..a4cde6c0 100644 --- a/pandora/log/models.py +++ b/pandora/log/models.py @@ -1,7 +1,9 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import from django.db import models from django.contrib.auth import get_user_model +from django.utils.encoding import python_2_unicode_compatible import ox @@ -10,10 +12,11 @@ from . import managers User = get_user_model() +@python_2_unicode_compatible class Log(models.Model): created = models.DateTimeField(auto_now_add=True, db_index=True) modified = models.DateTimeField(auto_now=True) - user = models.ForeignKey(User, default=None, blank=True, null=True, on_delete=models.CASCADE) + user = models.ForeignKey(User, default=None, blank=True, null=True) url = models.CharField(max_length=1000, default='') line = models.IntegerField(default=0) text = models.TextField(blank=True) @@ -21,7 +24,7 @@ class Log(models.Model): objects = managers.LogManager() def __str__(self): - return "%s" % self.id + return u"%s" % self.id def json(self, keys=None): j = { diff --git a/pandora/log/tasks.py b/pandora/log/tasks.py index 768fd731..ffb9f567 100644 --- a/pandora/log/tasks.py +++ b/pandora/log/tasks.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import from datetime import timedelta, datetime diff --git a/pandora/log/utils.py b/pandora/log/utils.py index f2074567..df115303 100644 --- a/pandora/log/utils.py +++ b/pandora/log/utils.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import import logging import sys @@ -33,7 +34,7 @@ class ErrorHandler(logging.Handler): request = record.request request_repr = repr(request) - if request.user.is_authenticated: + if request.user.is_authenticated(): user = request.user url = request.META.get('PATH_INFO', '') except: diff --git a/pandora/log/views.py b/pandora/log/views.py index d8dfa4f4..5016c406 100644 --- a/pandora/log/views.py +++ b/pandora/log/views.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import import ox from ox.utils import json @@ -24,7 +25,7 @@ def logError(request, data): returns {} see: findErrorLogs, removeErrorLogs ''' - if request.user.is_authenticated: + if request.user.is_authenticated(): user = request.user else: user = None diff --git a/pandora/news/admin.py b/pandora/news/admin.py index 4778b44b..b7f636cb 100644 --- a/pandora/news/admin.py +++ b/pandora/news/admin.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import from django.contrib import admin diff --git a/pandora/news/models.py b/pandora/news/models.py index 98624ab6..194a5ce7 100644 --- a/pandora/news/models.py +++ b/pandora/news/models.py @@ -1,11 +1,14 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import from django.db import models +from django.utils.encoding import python_2_unicode_compatible import ox from . import managers +@python_2_unicode_compatible class News(models.Model): objects = managers.NewsManager() @@ -17,7 +20,7 @@ class News(models.Model): text = models.TextField() def editable(self, user): - return user.is_authenticated and user.profile.capability("canEditSitePages") + return user.is_authenticated() and user.profile.capability("canEditSitePages") def save(self, *args, **kwargs): super(News, self).save(*args, **kwargs) @@ -39,5 +42,5 @@ class News(models.Model): return j def __str__(self): - return "%s/%s" % (self.date, self.title) + return u"%s/%s" % (self.date, self.title) diff --git a/pandora/news/views.py b/pandora/news/views.py index 36e14590..2a7fcf32 100644 --- a/pandora/news/views.py +++ b/pandora/news/views.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import import ox from ox.utils import json diff --git a/pandora/oxdjango/api/__init__.py b/pandora/oxdjango/api/__init__.py index 72294dc6..4ecadf1d 100644 --- a/pandora/oxdjango/api/__init__.py +++ b/pandora/oxdjango/api/__init__.py @@ -1 +1,3 @@ +from __future__ import absolute_import + from .actions import actions diff --git a/pandora/oxdjango/api/actions.py b/pandora/oxdjango/api/actions.py index 70249914..f813e75a 100644 --- a/pandora/oxdjango/api/actions.py +++ b/pandora/oxdjango/api/actions.py @@ -1,7 +1,10 @@ # -*- coding: utf-8 -*- +from __future__ import division, absolute_import + import inspect import sys +from six import PY2 from django.conf import settings from ..shortcuts import render_to_json_response, json_response @@ -106,8 +109,12 @@ class ApiActions(dict): if name != 'api' and hasattr(f, 'func_closure') and f.func_closure: fc = list(filter(lambda c: hasattr(c.cell_contents, '__call__'), f.func_closure)) f = fc[len(fc)-1].cell_contents - info = f.__code__.co_filename[len(settings.PROJECT_ROOT)+1:] - info = '%s:%s' % (info, f.__code__.co_firstlineno) + if PY2: + info = f.func_code.co_filename[len(settings.PROJECT_ROOT)+1:] + info = u'%s:%s' % (info, f.func_code.co_firstlineno) + else: + info = f.__code__.co_filename[len(settings.PROJECT_ROOT)+1:] + info = u'%s:%s' % (info, f.__code__.co_firstlineno) return info, trim(inspect.getsource(f)) def register(self, method, action=None, cache=True, version=None): diff --git a/pandora/oxdjango/api/site.py b/pandora/oxdjango/api/site.py deleted file mode 100644 index cac1e021..00000000 --- a/pandora/oxdjango/api/site.py +++ /dev/null @@ -1,15 +0,0 @@ -# -*- coding: utf-8 -*- -from django.urls import path - -from . import views - -from . import actions -actions.autodiscover() - -urls = [ - [ - path(r'', views.api), - ], - 'api', - 'api' -] diff --git a/pandora/oxdjango/api/urls.py b/pandora/oxdjango/api/urls.py new file mode 100644 index 00000000..159c4ff6 --- /dev/null +++ b/pandora/oxdjango/api/urls.py @@ -0,0 +1,13 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import + +from django.conf.urls import url + +from . import views + +from . import actions +actions.autodiscover() + +urlpatterns = [ + url(r'^$', views.api), +] diff --git a/pandora/oxdjango/api/views.py b/pandora/oxdjango/api/views.py index 3a4a2e89..9e962346 100644 --- a/pandora/oxdjango/api/views.py +++ b/pandora/oxdjango/api/views.py @@ -1,7 +1,9 @@ # -*- coding: utf-8 -*- +from __future__ import division, absolute_import + import json -from django.shortcuts import render +from django.shortcuts import render_to_response from django.conf import settings from ..shortcuts import render_to_json_response, json_response, HttpErrorJson @@ -29,7 +31,7 @@ def api(request): 'settings': settings, 'sitename': settings.SITENAME } - response = render(request, 'api.html', context) + response = render_to_response('api.html', context) response['Access-Control-Allow-Origin'] = '*' return response if request.META.get('CONTENT_TYPE') == 'application/json': diff --git a/pandora/oxdjango/decorators.py b/pandora/oxdjango/decorators.py index e631daef..ab4fbf2d 100644 --- a/pandora/oxdjango/decorators.py +++ b/pandora/oxdjango/decorators.py @@ -1,4 +1,6 @@ # -*- coding: utf-8 -*- +from __future__ import absolute_import + from functools import wraps from .shortcuts import render_to_json_response @@ -10,7 +12,7 @@ def login_required_json(function=None): """ def _wrapped_view(request, *args, **kwargs): - if request.user.is_authenticated: + if request.user.is_authenticated(): return function(request, *args, **kwargs) return render_to_json_response({'status': {'code': 401, 'text': 'login required'}}) return wraps(function)(_wrapped_view) @@ -22,7 +24,7 @@ def admin_required_json(function=None): """ def _wrapped_view(request, *args, **kwargs): - if request.user.is_authenticated and request.user.profile.get_level() == 'admin': + if request.user.is_authenticated() and request.user.profile.get_level() == 'admin': return function(request, *args, **kwargs) return render_to_json_response({'status': {'code': 403, 'text': 'permission denied'}}) return wraps(function)(_wrapped_view) diff --git a/pandora/oxdjango/fields.py b/pandora/oxdjango/fields.py index daebd27c..b9b1e58b 100644 --- a/pandora/oxdjango/fields.py +++ b/pandora/oxdjango/fields.py @@ -8,6 +8,8 @@ from django.utils import datetime_safe import django.contrib.postgres.fields from django.core.serializers.json import DjangoJSONEncoder +from six import string_types + from ox.utils import json class JSONField(django.contrib.postgres.fields.JSONField): @@ -62,7 +64,7 @@ class DictField(models.TextField): def dumps(cls, obj): return json.dumps(obj, default=to_json, ensure_ascii=False) - def from_db_value(self, value, expression, connection, context=None): + def from_db_value(self, value, expression, connection, context): if value is None: return value if isinstance(value, self._type): @@ -72,7 +74,7 @@ class DictField(models.TextField): except: raise Exception('failed to parse value: %s' % value) if value is not None: - if isinstance(value, str): + if isinstance(value, string_types): value = json.loads(value) assert isinstance(value, self._type) return value @@ -81,7 +83,7 @@ class DictField(models.TextField): if isinstance(value, self._type): value = self.dumps(value) if value is not None: - assert isinstance(value, str) + assert isinstance(value, string_types) value = models.TextField.get_prep_value(self, value) return value diff --git a/pandora/oxdjango/http.py b/pandora/oxdjango/http.py index 38db62ac..9bd252d2 100644 --- a/pandora/oxdjango/http.py +++ b/pandora/oxdjango/http.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- -from datetime import datetime, timedelta -from urllib.parse import quote -import mimetypes import os +import mimetypes +from datetime import datetime, timedelta +from six.moves.urllib.parse import quote from django.http import HttpResponse, Http404 from django.conf import settings diff --git a/pandora/oxdjango/middleware.py b/pandora/oxdjango/middleware.py index 5b2ccaab..4883f23a 100644 --- a/pandora/oxdjango/middleware.py +++ b/pandora/oxdjango/middleware.py @@ -1,17 +1,14 @@ # -*- coding: utf-8 -*- -from django.utils.deprecation import MiddlewareMixin from .shortcuts import HttpErrorJson, render_to_json_response -class ExceptionMiddleware(MiddlewareMixin): - +class ExceptionMiddleware(object): def process_exception(self, request, exception): if isinstance(exception, HttpErrorJson): return render_to_json_response(exception.response) return None -class ChromeFrameMiddleware(MiddlewareMixin): - +class ChromeFrameMiddleware(object): def process_response(self, request, response): response['X-UA-Compatible'] = 'chrome=1' return response diff --git a/pandora/oxdjango/shortcuts.py b/pandora/oxdjango/shortcuts.py index 3baa1315..8246a98a 100644 --- a/pandora/oxdjango/shortcuts.py +++ b/pandora/oxdjango/shortcuts.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import print_function import datetime from django.utils import datetime_safe from django.http import HttpResponse, Http404 @@ -22,7 +23,7 @@ def _to_json(python_object): return python_object.strftime('%Y-%m-%dT%H:%M:%SZ') if isinstance(python_object, datetime_safe.datetime): return python_object.strftime('%Y-%m-%dT%H:%M:%SZ') - raise TypeError('%s %s is not JSON serializable' % (repr(python_object), type(python_object))) + raise TypeError(u'%s %s is not JSON serializable' % (repr(python_object), type(python_object))) def json_dump(data, fp, indent=4): return json.dump(data, fp, indent=indent, default=_to_json, ensure_ascii=False) diff --git a/pandora/person/managers.py b/pandora/person/managers.py index 90418746..4c718e6f 100644 --- a/pandora/person/managers.py +++ b/pandora/person/managers.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import unicodedata +from six import string_types from django.db.models import Q, Manager from item.utils import decode_id @@ -55,7 +56,7 @@ def parseCondition(condition, user): else: key = k + get_operator(op, 'istr') key = str(key) - if isinstance(v, str): + if isinstance(v, string_types): v = unicodedata.normalize('NFKD', v).lower() if exclude: q = ~Q(**{key: v}) diff --git a/pandora/person/models.py b/pandora/person/models.py index 54fae287..84fe86d6 100644 --- a/pandora/person/models.py +++ b/pandora/person/models.py @@ -1,8 +1,10 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import import unicodedata from django.db import models +from django.utils.encoding import python_2_unicode_compatible from oxdjango import fields import ox @@ -22,9 +24,10 @@ def get_name_sort(name, sortname=None): person.save() sortname = unicodedata.normalize('NFKD', person.sortname) else: - sortname = '' + sortname = u'' return sortname +@python_2_unicode_compatible class Person(models.Model): name = models.CharField(max_length=200, unique=True) sortname = models.CharField(max_length=200) diff --git a/pandora/person/tasks.py b/pandora/person/tasks.py index a94ed273..2f360dae 100644 --- a/pandora/person/tasks.py +++ b/pandora/person/tasks.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import from celery.task import task diff --git a/pandora/person/views.py b/pandora/person/views.py index fd9258a1..aa6f6570 100644 --- a/pandora/person/views.py +++ b/pandora/person/views.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import import ox from ox.utils import json diff --git a/pandora/place/admin.py b/pandora/place/admin.py index 9ee1aeae..209244d3 100644 --- a/pandora/place/admin.py +++ b/pandora/place/admin.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import from django.contrib import admin diff --git a/pandora/place/managers.py b/pandora/place/managers.py index cea3777c..75d59b1d 100644 --- a/pandora/place/managers.py +++ b/pandora/place/managers.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import unicodedata +from six import string_types from django.db.models import Q, Manager from item.utils import decode_id @@ -54,7 +55,7 @@ def parseCondition(condition, user): key = k + get_operator(op, 'istr') key = str(key) - if isinstance(v, str): + if isinstance(v, string_types): v = unicodedata.normalize('NFKD', v).lower() if exclude: diff --git a/pandora/place/models.py b/pandora/place/models.py index 3c1acc6d..318a3d15 100644 --- a/pandora/place/models.py +++ b/pandora/place/models.py @@ -1,8 +1,10 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import from django.db import models, transaction from django.contrib.auth import get_user_model +from django.utils.encoding import python_2_unicode_compatible import ox from oxdjango import fields @@ -13,6 +15,7 @@ from . import managers User = get_user_model() +@python_2_unicode_compatible class Place(models.Model): ''' Places are named locations, they should have geographical information attached to them. @@ -21,7 +24,7 @@ class Place(models.Model): modified = models.DateTimeField(auto_now=True) defined = models.BooleanField(default=True) - user = models.ForeignKey(User, null=True, related_name='places', on_delete=models.CASCADE) + user = models.ForeignKey(User, null=True, related_name='places') name = models.CharField(max_length=1024) alternativeNames = fields.TupleField(default=()) @@ -57,7 +60,7 @@ class Place(models.Model): @classmethod def get_or_create(model, name): - qs = model.objects.filter(name_find__contains='|%s|' % name.lower()) + qs = model.objects.filter(name_find__contains=u'|%s|' % name.lower()) if qs.count() == 0: instance = model(name=name) instance.save() @@ -66,7 +69,7 @@ class Place(models.Model): return instance def editable(self, user): - if user and not user.is_anonymous \ + if user and not user.is_anonymous() \ and (not self.user or \ self.user == user or \ user.profile.capability('canEditPlaces')): diff --git a/pandora/place/tasks.py b/pandora/place/tasks.py index 3feb88dd..ba6b8375 100644 --- a/pandora/place/tasks.py +++ b/pandora/place/tasks.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import from celery.task import task diff --git a/pandora/place/views.py b/pandora/place/views.py index 0c305d53..01533b52 100644 --- a/pandora/place/views.py +++ b/pandora/place/views.py @@ -1,8 +1,10 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import from django.db.models import Max, Min, Count from django.conf import settings +from six import string_types import ox from ox.utils import json @@ -48,7 +50,7 @@ def addPlace(request, data): n = 0 while _exists: _exists = models.Place.objects.filter(defined=True, - name_find__contains='|%s|' % name.lower()).count() > 0 + name_find__contains=u'|%s|' % name.lower()).count() > 0 if _exists: name = 'Untitled [%s]' %n n += 1 @@ -59,7 +61,7 @@ def addPlace(request, data): for n in names: n = ox.decode_html(name) if models.Place.objects.filter(defined=True, - name_find__contains='|%s|' % n.lower()).count() != 0: + name_find__contains=u'|%s|' % n.lower()).count() != 0: exists = True existing_names.append(n) ''' @@ -114,7 +116,7 @@ def editPlace(request, data): ''' place = get_object_or_404_json(models.Place, pk=ox.fromAZ(data['id'])) names = data.get('name', []) - if isinstance(names, str): + if isinstance(names, string_types): names = [names] names = [ox.escape_html(n) for n in names] alternative_names = [ox.escape_html(n) for n in data.get('alternativeNames', [])] @@ -128,7 +130,7 @@ def editPlace(request, data): for name in names + alternative_names: name = ox.decode_html(name) if models.Place.objects.filter(defined=True, - name_find__contains='|%s|' % name.lower()).exclude(id=place.id).count() != 0: + name_find__contains=u'|%s|' % name.lower()).exclude(id=place.id).count() != 0: conflict = True conflict_names.append(name) ''' @@ -143,7 +145,7 @@ def editPlace(request, data): for key in data: if key != 'id': value = data[key] - if isinstance(value, str): + if isinstance(value, string_types): value = ox.escape_html(value) if isinstance(value, list): value = tuple(value) diff --git a/pandora/sequence/extract.py b/pandora/sequence/extract.py index 90db3b33..5cbcda50 100644 --- a/pandora/sequence/extract.py +++ b/pandora/sequence/extract.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import division import os from PIL import Image @@ -96,9 +97,7 @@ class DataTimeline(): def get_frame(self, pos): frame = int(pos * self.fps) tile = int(frame * 8 / self.timeline_width) - if len(self.file_names) <= tile: - tile = len(self.file_names) - 1 - if self.current_tile != tile and len(self.file_names): + if self.current_tile != tile: self.timeline_image = Image.open(self.file_names[tile]) self.current_tile = tile x = frame * 8 - tile * self.timeline_width diff --git a/pandora/sequence/managers.py b/pandora/sequence/managers.py index 8f4c5d8d..63bf693b 100644 --- a/pandora/sequence/managers.py +++ b/pandora/sequence/managers.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import from django.db.models import Q, Manager diff --git a/pandora/sequence/models.py b/pandora/sequence/models.py index b17c9277..2970c3f2 100644 --- a/pandora/sequence/models.py +++ b/pandora/sequence/models.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import from django.db import models +from django.utils.encoding import python_2_unicode_compatible from item.models import ItemSort @@ -13,6 +15,7 @@ def parse_hash(value): def format_hash(value): return hex(value + 9223372036854775808)[2:-1].upper() +@python_2_unicode_compatible class Sequence(models.Model): class Meta: unique_together = ("sort", "start", "end", "mode") @@ -22,7 +25,7 @@ class Sequence(models.Model): 'color': 1 } mode = models.IntegerField(choices=sorted(zip(MODE.values(), list(MODE)), key=lambda k: k[0]), default=0) - sort = models.ForeignKey(ItemSort, null=True, related_name='sequences', on_delete=models.CASCADE) + sort = models.ForeignKey(ItemSort, null=True, related_name='sequences') hash = models.BigIntegerField(db_index=True, default=-9223372036854775808) start = models.FloatField(default=-1) @@ -37,7 +40,7 @@ class Sequence(models.Model): @property def public_id(self): - return "%s/%0.03f-%0.03f" % (self.sort.item.public_id, float(self.start), float(self.end)) + return u"%s/%0.03f-%0.03f" % (self.sort.item.public_id, float(self.start), float(self.end)) def __str__(self): return self.public_id diff --git a/pandora/sequence/tasks.py b/pandora/sequence/tasks.py index b90c0d5a..1282a66e 100644 --- a/pandora/sequence/tasks.py +++ b/pandora/sequence/tasks.py @@ -1,5 +1,7 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import +from six import string_types from django.db import connection, transaction from celery.task import task @@ -29,7 +31,7 @@ def get_sequences(public_id): sequence['duration'] = sequence['end'] - sequence['start'] if not keys: keys = ', '.join(['"%s"'%k for k in sequence.keys()]) - v = ', '.join([isinstance(v, str) and "'%s'"%v or str(v) + v = ', '.join([isinstance(v, string_types) and "'%s'"%v or str(v) for v in sequence.values()]) values.append('(%s)'%v) if values: diff --git a/pandora/sequence/views.py b/pandora/sequence/views.py index 08ee2543..89986129 100644 --- a/pandora/sequence/views.py +++ b/pandora/sequence/views.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import from ox.utils import json from oxdjango.shortcuts import render_to_json_response, json_response diff --git a/pandora/settings.py b/pandora/settings.py index 99eda0ba..83dfb602 100644 --- a/pandora/settings.py +++ b/pandora/settings.py @@ -2,6 +2,7 @@ # Django settings for pan.do/ra project defaults, # create local_settings.py to overwrite # check pan.do/ra section below for relevant settings +from __future__ import absolute_import import os from os.path import join, normpath, dirname @@ -35,8 +36,6 @@ LANGUAGE_CODE = 'en-us' SITE_ID = 1 -VERSION_EPOCH = None - # If you set this to False, Django will make some optimizations so as not # to load the internationalization machinery. USE_I18N = True @@ -95,7 +94,7 @@ TEMPLATES = [ }, ] -MIDDLEWARE = ( +MIDDLEWARE_CLASSES = ( 'django.middleware.common.CommonMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', @@ -119,7 +118,6 @@ INSTALLED_APPS = ( # Uncomment the next line to enable admin documentation: # 'django.contrib.admindocs', 'django.contrib.humanize', - 'system', 'django_extensions', 'django_celery_results', @@ -151,8 +149,6 @@ INSTALLED_APPS = ( 'home', ) -AUTH_USER_MODEL = 'system.User' - # Log errors into db LOGGING = { 'version': 1, diff --git a/pandora/system/__init__.py b/pandora/system/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/pandora/system/admin.py b/pandora/system/admin.py deleted file mode 100644 index 6d53b53a..00000000 --- a/pandora/system/admin.py +++ /dev/null @@ -1,6 +0,0 @@ -from django.contrib import admin -from django.contrib.auth.admin import UserAdmin - -from .models import User - -admin.site.register(User, UserAdmin) diff --git a/pandora/system/apps.py b/pandora/system/apps.py deleted file mode 100644 index 5dc4d64b..00000000 --- a/pandora/system/apps.py +++ /dev/null @@ -1,5 +0,0 @@ -from django.apps import AppConfig - - -class SystemConfig(AppConfig): - name = 'system' diff --git a/pandora/system/migrations/0001_initial.py b/pandora/system/migrations/0001_initial.py deleted file mode 100644 index b8f4f6d5..00000000 --- a/pandora/system/migrations/0001_initial.py +++ /dev/null @@ -1,42 +0,0 @@ -# Generated by Django 3.0.6 on 2020-05-29 21:41 - -import django.contrib.auth.models -import django.contrib.auth.validators -from django.db import migrations, models -import django.utils.timezone - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ('auth', '0008_alter_user_username_max_length'), - ] - - operations = [ - migrations.CreateModel( - name='User', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('password', models.CharField(max_length=128, verbose_name='password')), - ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), - ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), - ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')), - ('first_name', models.CharField(blank=True, max_length=30, verbose_name='first name')), - ('last_name', models.CharField(blank=True, max_length=30, verbose_name='last name')), - ('email', models.EmailField(blank=True, max_length=255, verbose_name='email address')), - ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), - ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), - ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), - ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')), - ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')), - ], - options={ - 'db_table': 'auth_user', - }, - managers=[ - ('objects', django.contrib.auth.models.UserManager()), - ], - ), - ] diff --git a/pandora/system/migrations/0002_rename_user_table.py b/pandora/system/migrations/0002_rename_user_table.py deleted file mode 100644 index 098c9b67..00000000 --- a/pandora/system/migrations/0002_rename_user_table.py +++ /dev/null @@ -1,21 +0,0 @@ -# Generated by Django 3.0.6 on 2020-05-29 21:54 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('system', '0001_initial'), - ] - - operations = [ - migrations.AlterModelOptions( - name='user', - options={'verbose_name': 'user', 'verbose_name_plural': 'users'}, - ), - migrations.AlterModelTable( - name='user', - table=None, - ), - ] diff --git a/pandora/system/migrations/0003_field_length.py b/pandora/system/migrations/0003_field_length.py deleted file mode 100644 index 6cb2938b..00000000 --- a/pandora/system/migrations/0003_field_length.py +++ /dev/null @@ -1,34 +0,0 @@ -# Generated by Django 3.0.6 on 2020-05-30 10:10 - -import django.contrib.auth.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('system', '0002_rename_user_table'), - ] - - operations = [ - migrations.AlterField( - model_name='user', - name='password', - field=models.CharField(max_length=255, verbose_name='password'), - ), - migrations.AlterField( - model_name='user', - name='last_name', - field=models.CharField(blank=True, max_length=150, verbose_name='last name'), - ), - migrations.AlterField( - model_name='user', - name='email', - field=models.EmailField(blank=True, max_length=254, verbose_name='email address'), - ), - migrations.AlterField( - model_name='user', - name='username', - field=models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 255 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=255, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username'), - ), - ] diff --git a/pandora/system/migrations/__init__.py b/pandora/system/migrations/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/pandora/system/models.py b/pandora/system/models.py deleted file mode 100644 index 2f59135c..00000000 --- a/pandora/system/models.py +++ /dev/null @@ -1,19 +0,0 @@ -from django.db import models -from django.contrib.auth.models import AbstractUser -from django.utils.translation import ugettext_lazy as _ - - -class User(AbstractUser): - - password = models.CharField(_('password'), max_length=255) - username = models.CharField( - _('username'), - max_length=255, - unique=True, - help_text=_('Required. 255 characters or fewer. Letters, digits and @/./+/-/_ only.'), - validators=[AbstractUser.username_validator], - error_messages={ - 'unique': _("A user with that username already exists."), - }, - ) - last_name = models.CharField(_('last name'), max_length=150, blank=True) diff --git a/pandora/system/tests.py b/pandora/system/tests.py deleted file mode 100644 index 7ce503c2..00000000 --- a/pandora/system/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/pandora/system/views.py b/pandora/system/views.py deleted file mode 100644 index 91ea44a2..00000000 --- a/pandora/system/views.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.shortcuts import render - -# Create your views here. diff --git a/pandora/taskqueue/models.py b/pandora/taskqueue/models.py index 214f9a41..9dda9da1 100644 --- a/pandora/taskqueue/models.py +++ b/pandora/taskqueue/models.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import from datetime import datetime, timedelta from time import time @@ -8,6 +9,7 @@ from django.contrib.auth import get_user_model from django.conf import settings from django.db import models from django.db.models import Q +from django.utils.encoding import python_2_unicode_compatible import celery.task.control import kombu.five import ox @@ -38,6 +40,7 @@ def get_tasks(username): tasks.append(task.json()) return tasks +@python_2_unicode_compatible class Task(models.Model): DONE = ['finished', 'failed', 'canceled'] @@ -48,8 +51,8 @@ class Task(models.Model): status = models.CharField(default='unknown', max_length=32) started = models.DateTimeField(null=True) ended = models.DateTimeField(null=True) - item = models.ForeignKey("item.Item", related_name='tasks', on_delete=models.CASCADE) - user = models.ForeignKey(User, related_name='tasks', null=True, on_delete=models.CASCADE) + item = models.ForeignKey("item.Item", related_name='tasks') + user = models.ForeignKey(User, related_name='tasks', null=True) def __str__(self): return "%s [%s]" % (self.item.public_id, self.status) diff --git a/pandora/taskqueue/views.py b/pandora/taskqueue/views.py index aef64c99..e4791768 100644 --- a/pandora/taskqueue/views.py +++ b/pandora/taskqueue/views.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import import ox from oxdjango.decorators import login_required_json diff --git a/pandora/text/managers.py b/pandora/text/managers.py index e0eadc5a..9de4ce65 100644 --- a/pandora/text/managers.py +++ b/pandora/text/managers.py @@ -126,7 +126,7 @@ class TextManager(Manager): if conditions: qs = qs.filter(conditions) - if user.is_anonymous: + 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)) diff --git a/pandora/text/models.py b/pandora/text/models.py index 2973ba6d..fc9e7291 100644 --- a/pandora/text/models.py +++ b/pandora/text/models.py @@ -1,16 +1,18 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import -from glob import glob -from urllib.parse import quote import os import re import subprocess +from glob import glob +from six.moves.urllib.parse import quote from django.db import models from django.db.models import Max from django.contrib.auth import get_user_model from django.conf import settings from django.db.models.signals import pre_delete +from django.utils.encoding import python_2_unicode_compatible import ox from oxdjango.fields import TupleField @@ -24,6 +26,7 @@ User = get_user_model() def get_path(i, x): return i.path(x) def get_icon_path(i, x): return get_path(i, 'icon.jpg') +@python_2_unicode_compatible class Text(models.Model): class Meta: @@ -31,7 +34,7 @@ class Text(models.Model): created = models.DateTimeField(auto_now_add=True) modified = models.DateTimeField(auto_now=True) - user = models.ForeignKey(User, related_name='texts', on_delete=models.CASCADE) + 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'] @@ -74,13 +77,13 @@ class Text(models.Model): return '%s/text.pdf' % self.get_absolute_url() def get_id(self): - return '%s:%s' % (self.user.username, self.name) + return u'%s:%s' % (self.user.username, self.name) def accessible(self, user): return self.user == user or self.status in ('public', 'featured') def editable(self, user): - if not user or user.is_anonymous: + if not user or user.is_anonymous(): return False if self.user == user or \ user.is_staff or \ @@ -206,7 +209,7 @@ class Text(models.Model): elif key == 'subscribers': response[key] = self.subscribed_users.all().count() elif key == 'subscribed': - if user and not user.is_anonymous: + 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)) @@ -301,16 +304,17 @@ def delete_file(sender, **kwargs): t.file.delete(save=False) pre_delete.connect(delete_file, sender=Text) +@python_2_unicode_compatible class Position(models.Model): class Meta: unique_together = ("user", "text", "section") - text = models.ForeignKey(Text, related_name='position', on_delete=models.CASCADE) - user = models.ForeignKey(User, related_name='text_position', on_delete=models.CASCADE) + 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 __str__(self): - return '%s/%s/%s' % (self.section, self.position, self.text) + return u'%s/%s/%s' % (self.section, self.position, self.text) diff --git a/pandora/text/views.py b/pandora/text/views.py index 33338675..ecea30c8 100644 --- a/pandora/text/views.py +++ b/pandora/text/views.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import import os import re @@ -97,7 +98,7 @@ def getText(request, data): 'name': '', 'text': '', 'type': 'html', - 'editable': not request.user.is_anonymous and request.user.profile.capability('canEditFeaturedTexts') + 'editable': not request.user.is_anonymous() and request.user.profile.capability('canEditFeaturedTexts') } else: text = qs[0] @@ -205,8 +206,7 @@ def findTexts(request, data): query = parse_query(data, request.user) #order - is_section_request = query['sort'] == [{'operator': '+', 'key': 'position'}] - + 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 \ @@ -218,7 +218,7 @@ def findTexts(request, data): if is_section_request: qs = query['qs'] - if not is_featured and not request.user.is_anonymous: + 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: diff --git a/pandora/title/managers.py b/pandora/title/managers.py index 2c6ce6f7..b70703a6 100644 --- a/pandora/title/managers.py +++ b/pandora/title/managers.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import unicodedata +from six import string_types from django.db.models import Q, Manager from item.utils import decode_id @@ -46,7 +47,7 @@ def parseCondition(condition, user): return q if k == 'id': v = decode_id(v) - elif isinstance(v, str): + elif isinstance(v, string_types): v = unicodedata.normalize('NFKD', v).lower() if isinstance(v, bool): key = k diff --git a/pandora/title/models.py b/pandora/title/models.py index 7e522c1c..fceb00c6 100644 --- a/pandora/title/models.py +++ b/pandora/title/models.py @@ -1,8 +1,10 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import import unicodedata from django.db import models +from django.utils.encoding import python_2_unicode_compatible import ox @@ -20,9 +22,10 @@ def get_title_sort(title): title.save() sorttitle = unicodedata.normalize('NFKD', title.sorttitle) else: - sorttitle = '' + sorttitle = u'' return sorttitle +@python_2_unicode_compatible class Title(models.Model): title = models.CharField(max_length=1000, unique=True) sorttitle = models.CharField(max_length=1000) diff --git a/pandora/title/views.py b/pandora/title/views.py index aa48940c..178cb74d 100644 --- a/pandora/title/views.py +++ b/pandora/title/views.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import import unicodedata diff --git a/pandora/translation/tasks.py b/pandora/translation/tasks.py index 8b0be30b..09645963 100644 --- a/pandora/translation/tasks.py +++ b/pandora/translation/tasks.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import from datetime import timedelta, datetime diff --git a/pandora/tv/models.py b/pandora/tv/models.py index e9778d70..6a68d214 100644 --- a/pandora/tv/models.py +++ b/pandora/tv/models.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import from datetime import datetime, timedelta from random import randint @@ -6,20 +7,22 @@ from random import randint from django.db import models from django.db.models import Max from django.conf import settings +from django.utils.encoding import python_2_unicode_compatible from item.models import Item +@python_2_unicode_compatible class Channel(models.Model): created = models.DateTimeField(auto_now_add=True) modified = models.DateTimeField(auto_now=True) run = models.IntegerField(default=0) - list = models.OneToOneField('itemlist.List', related_name='channel', null=True, blank=True, on_delete=models.CASCADE) - #list = models.ForeignKey('itemlist.List', related_name='channel', null=True, unique=True, blank=True, on_delete=models.CASCADE) + list = models.OneToOneField('itemlist.List', related_name='channel', null=True, blank=True) + #list = models.ForeignKey('itemlist.List', related_name='channel', null=True, unique=True, blank=True) def __str__(self): - return "%s %s" % (self.list or 'All', self.run) + return u"%s %s" % (self.list or 'All', self.run) def update_program(self, now=None): if not now: @@ -70,17 +73,18 @@ class Channel(models.Model): else: return program.json(user, now) +@python_2_unicode_compatible class Program(models.Model): created = models.DateTimeField(auto_now_add=True) modified = models.DateTimeField(auto_now=True) run = models.IntegerField(default=0) start = models.DateTimeField() end = models.DateTimeField() - item = models.ForeignKey('item.Item', related_name='program', on_delete=models.CASCADE) - channel = models.ForeignKey(Channel, related_name='program', on_delete=models.CASCADE) + item = models.ForeignKey('item.Item', related_name='program') + channel = models.ForeignKey(Channel, related_name='program') def __str__(self): - return "%s %s" % (self.item, self.start) + return u"%s %s" % (self.item, self.start) def json(self, user, current=False): item_json = self.item.json() diff --git a/pandora/tv/tasks.py b/pandora/tv/tasks.py index 20a9df94..80d36fbc 100644 --- a/pandora/tv/tasks.py +++ b/pandora/tv/tasks.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import from datetime import datetime, timedelta diff --git a/pandora/tv/views.py b/pandora/tv/views.py index 89c0cae4..fedf8c7b 100644 --- a/pandora/tv/views.py +++ b/pandora/tv/views.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import from ox.utils import json from oxdjango.shortcuts import render_to_json_response, json_response diff --git a/pandora/urlalias/models.py b/pandora/urlalias/models.py index 13fa2568..c42fa2e1 100644 --- a/pandora/urlalias/models.py +++ b/pandora/urlalias/models.py @@ -1,32 +1,37 @@ from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class IDAlias(models.Model): old = models.CharField(max_length=255, unique=True) new = models.CharField(max_length=255) def __str__(self): - return "%s=%s" % (self.old, self.new) + return u"%s=%s" % (self.old, self.new) +@python_2_unicode_compatible class LayerAlias(models.Model): old = models.CharField(max_length=255, unique=True) new = models.CharField(max_length=255) def __str__(self): - return "%s=%s" % (self.old, self.new) + return u"%s=%s" % (self.old, self.new) +@python_2_unicode_compatible class ListAlias(models.Model): old = models.CharField(max_length=255, unique=True) new = models.CharField(max_length=255) def __str__(self): - return "%s=%s" % (self.old, self.new) + return u"%s=%s" % (self.old, self.new) +@python_2_unicode_compatible class Alias(models.Model): url = models.CharField(max_length=255, unique=True) target = models.CharField(max_length=255) def __str__(self): - return "%s=%s" % (self.url, self.target) + return u"%s=%s" % (self.url, self.target) diff --git a/pandora/urlalias/views.py b/pandora/urlalias/views.py index 9ed1a36c..8994837d 100644 --- a/pandora/urlalias/views.py +++ b/pandora/urlalias/views.py @@ -1,8 +1,9 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import -from urllib.parse import quote import re +from six.moves.urllib.parse import quote from django.shortcuts import get_object_or_404, redirect import app.views diff --git a/pandora/urls.py b/pandora/urls.py index cd241d66..12328f5d 100644 --- a/pandora/urls.py +++ b/pandora/urls.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- import os -from django.urls import path, re_path +from django.conf.urls import url, include from oxdjango.http import HttpFileResponse from django.conf import settings @@ -13,7 +13,7 @@ admin.autodiscover() import app.monkey_patch -import oxdjango.api.site +import oxdjango.api.urls import app.views import archive.views @@ -24,7 +24,7 @@ import user.views import edit.views import itemlist.views import item.views -import item.site +import item.urls import translation.views import urlalias.views @@ -32,59 +32,60 @@ def serve_static_file(path, location, content_type): return HttpFileResponse(location, content_type=content_type) urlpatterns = [ - #path('admin/', admin.site.urls), - - re_path(r'^api/locale.(?P.*).json$', translation.views.locale_json), - re_path(r'^api/upload/text/?$', text.views.upload), - re_path(r'^api/upload/document/?$', document.views.upload), - re_path(r'^api/upload/direct/?$', archive.views.direct_upload), - re_path(r'^api/upload/?$', archive.views.firefogg_upload), - re_path(r'^url=(?P.*)$', app.views.redirect_url), - re_path(r'^file/(?P.*)$', archive.views.lookup_file), - re_path(r'^api/?', oxdjango.api.site.urls), - re_path(r'^resetUI$', user.views.reset_ui), - re_path(r'^collection/(?P.*?)/icon(?P\d*).jpg$', documentcollection.views.icon), - re_path(r'^documents/(?P[A-Z0-9]+)/(?P\d*)p(?P[\d,]*).jpg$', document.views.thumbnail), - re_path(r'^documents/(?P[A-Z0-9]+)/(?P.*?\.[^\d]{3})$', document.views.file), - re_path(r'^edit/(?P.*?)/icon(?P\d*).jpg$', edit.views.icon), - re_path(r'^list/(?P.*?)/icon(?P\d*).jpg$', itemlist.views.icon), - re_path(r'^text/(?P.*?)/icon(?P\d*).jpg$', text.views.icon), - re_path(r'^texts/(?P.*?)/text.pdf$', text.views.pdf), - re_path(r'^texts/(?P.*?)/text.pdf.html$', text.views.pdf_viewer), - re_path(r'^texts/$', text.views.text), - re_path(r'^texts/(?P.*?)/\d+$', text.views.text), - re_path(r'^texts/(?P.*?)$', text.views.text), - re_path(r'^favicon.ico$', serve_static_file, { + # Uncomment the admin/doc line below to enable admin documentation: + # urlurl(r'^admin/doc/', include('django.contrib.admindocs.urls')), + url(r'^admin/', include(admin.site.urls)), + url(r'^api/locale.(?P.*).json$', translation.views.locale_json), + url(r'^api/upload/text/?$', text.views.upload), + url(r'^api/upload/document/?$', document.views.upload), + url(r'^api/upload/direct/?$', archive.views.direct_upload), + url(r'^api/upload/?$', archive.views.firefogg_upload), + url(r'^url=(?P.*)$', app.views.redirect_url), + url(r'^file/(?P.*)$', archive.views.lookup_file), + url(r'^api/?', include(oxdjango.api.urls)), + url(r'^resetUI$', user.views.reset_ui), + url(r'^collection/(?P.*?)/icon(?P\d*).jpg$', documentcollection.views.icon), + url(r'^documents/(?P[A-Z0-9]+)/(?P\d*)p(?P[\d,]*).jpg$', document.views.thumbnail), + url(r'^documents/(?P[A-Z0-9]+)/(?P.*?\.[^\d]{3})$', document.views.file), + url(r'^edit/(?P.*?)/icon(?P\d*).jpg$', edit.views.icon), + url(r'^list/(?P.*?)/icon(?P\d*).jpg$', itemlist.views.icon), + url(r'^text/(?P.*?)/icon(?P\d*).jpg$', text.views.icon), + url(r'^texts/(?P.*?)/text.pdf$', text.views.pdf), + url(r'^texts/(?P.*?)/text.pdf.html$', text.views.pdf_viewer), + url(r'^texts/$', text.views.text), + url(r'^texts/(?P.*?)/\d+$', text.views.text), + url(r'^texts/(?P.*?)$', text.views.text), + url(r'^favicon.ico$', serve_static_file, { 'location': os.path.join(settings.STATIC_ROOT, 'png/icon.16.png'), 'content_type': 'image/x-icon' }), - re_path(r'^opensearch.xml$', app.views.opensearch_xml), - re_path(r'^oembed$', item.views.oembed), - re_path(r'^atom.xml$', item.views.atom_xml), - re_path(r'^robots.txt$', app.views.robots_txt), - re_path(r'^sitemap.xml$', item.views.sitemap_xml), - re_path(r'^sitemap(?P\d+).xml$', item.views.sitemap_part_xml), - path(r'', item.site.urls), + url(r'^opensearch.xml$', app.views.opensearch_xml), + url(r'^oembed$', item.views.oembed), + url(r'^atom.xml$', item.views.atom_xml), + url(r'^robots.txt$', app.views.robots_txt), + url(r'^sitemap.xml$', item.views.sitemap_xml), + url(r'^sitemap(?P\d+).xml$', item.views.sitemap_part_xml), + url(r'', include(item.urls)), ] #sould this not be enabled by default? nginx should handle those if settings.DEBUG: urlpatterns += [ - re_path(r'^data/(?P.*)$', django.views.static.serve, + url(r'^data/(?P.*)$', django.views.static.serve, {'document_root': settings.MEDIA_ROOT}), - re_path(r'^static/(?P.*)$', django.views.static.serve, + url(r'^static/(?P.*)$', django.views.static.serve, {'document_root': settings.STATIC_ROOT}), ] urlpatterns += [ - re_path(r'^(V[a-z0-9]+)$', urlalias.views.padma_video), - re_path(r'^(V[a-z0-9]+/.*)$', urlalias.views.padma_video), - re_path(r'^find$', urlalias.views.padma_find), + url(r'^(V[a-z0-9]+)$', urlalias.views.padma_video), + url(r'^(V[a-z0-9]+/.*)$', urlalias.views.padma_video), + url(r'^find$', urlalias.views.padma_find), ] urlpatterns += [ - re_path(r'^(?P[A-Z0-9x]+)/embed', app.views.embed), - re_path(r'^(?P[A-Z0-9x]+).*', item.views.item), - re_path(r'^[a-z0-9].+$', app.views.index), - re_path(r'^.*$', app.views.index), - path(r'', app.views.index), + url(r'^(?P[A-Z0-9x]+)/embed', app.views.embed), + url(r'^(?P[A-Z0-9x]+).*', item.views.item), + url(r'^[a-z0-9].+$', app.views.index), + url(r'^$', app.views.index), + url(r'^.*$', app.views.index), ] diff --git a/pandora/user/decorators.py b/pandora/user/decorators.py index 1d816bce..f53a7b1f 100644 --- a/pandora/user/decorators.py +++ b/pandora/user/decorators.py @@ -8,7 +8,7 @@ from oxdjango.shortcuts import render_to_json_response, json_response def capability_required_json(capability): def capability_required(function=None): def _wrapped_view(request, *args, **kwargs): - if request.user.is_authenticated and \ + if request.user.is_authenticated() and \ request.user.profile.capability(capability): return function(request, *args, **kwargs) return render_to_json_response(json_response(status=403, text='permission denied')) diff --git a/pandora/user/middleware.py b/pandora/user/middleware.py index 36291497..8c1c35d6 100644 --- a/pandora/user/middleware.py +++ b/pandora/user/middleware.py @@ -1,12 +1,10 @@ # -*- coding: utf-8 -*- from django.conf import settings from django.contrib.sessions.models import Session -from django.utils.deprecation import MiddlewareMixin - -class UpdateSession(MiddlewareMixin): +class UpdateSession(object): def process_request(self, request): - if request.user.is_authenticated: + if request.user.is_authenticated(): expire_date = Session.objects.get(session_key=request.session.session_key).expire_date if (request.session.get_expiry_date() - expire_date).total_seconds() > settings.SESSION_UPDATE: request.session.modified = True diff --git a/pandora/user/models.py b/pandora/user/models.py index fba06561..0c94beb7 100644 --- a/pandora/user/models.py +++ b/pandora/user/models.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import import copy from datetime import datetime @@ -8,6 +9,7 @@ from django.contrib.auth import get_user_model from django.db import models from django.db.models import Max from django.conf import settings +from django.utils.encoding import python_2_unicode_compatible from oxdjango.fields import JSONField import ox @@ -19,9 +21,10 @@ from .utils import get_ip, get_location User = get_user_model() +@python_2_unicode_compatible class SessionData(models.Model): session_key = models.CharField(max_length=40, primary_key=True) - user = models.OneToOneField(User, null=True, blank=True, related_name='data', on_delete=models.CASCADE) + user = models.OneToOneField(User, null=True, blank=True, related_name='data') firstseen = models.DateTimeField(auto_now_add=True, db_index=True) lastseen = models.DateTimeField(default=datetime.now, db_index=True) username = models.CharField(max_length=255, null=True, db_index=True) @@ -47,7 +50,7 @@ class SessionData(models.Model): groupssort = models.CharField(default=None, blank=True, null=True, max_length=255) def __str__(self): - return "%s" % self.session_key + return u"%s" % self.session_key def parse_useragent(self): if self.useragent: @@ -66,8 +69,8 @@ class SessionData(models.Model): if self.ip: city, country = get_location(self.ip) if city: - location = '%s, %s' % (city, country) - location_sort = '%s, %s' % (country, city) + location = u'%s, %s' % (city, country) + location_sort = u'%s, %s' % (country, city) else: location = location_sort = country self.location = location @@ -95,10 +98,10 @@ class SessionData(models.Model): request.session.modified = True session_key = request.session.session_key assert session_key - if request.user.is_authenticated: + if request.user.is_authenticated(): cls.objects.filter(user=request.user).update(session_key=session_key) data, created = cls.objects.get_or_create(session_key=session_key) - if request.user.is_authenticated: + if request.user.is_authenticated(): data.user = request.user data.ip = get_ip(request) data.useragent = request.META.get('HTTP_USER_AGENT', '')[:4096] @@ -112,10 +115,10 @@ class SessionData(models.Model): ] screen = data.info.get('screen', {}) if screen and 'height' in screen and 'width' in screen: - data.screensize = '%s\xd7%s' % (screen['width'], screen['height']) + data.screensize = u'%s\xd7%s' % (screen['width'], screen['height']) window = data.info.get('window', {}) if window and 'outerHeight' in window and 'outerWidth' in window: - data.windowsize = '%s\xd7%s' % (window['outerWidth'], window['outerHeight']) + data.windowsize = u'%s\xd7%s' % (window['outerWidth'], window['outerHeight']) if not data.timesseen: data.timesseen = 0 data.timesseen += 1 @@ -169,9 +172,10 @@ class SessionData(models.Model): del j[key] return j +@python_2_unicode_compatible class UserProfile(models.Model): reset_code = models.CharField(max_length=255, blank=True, null=True, unique=True) - user = models.OneToOneField(User, related_name='profile', on_delete=models.CASCADE) + user = models.OneToOneField(User, related_name='profile') level = models.IntegerField(default=1) files_updated = models.DateTimeField(default=datetime.now) @@ -391,7 +395,7 @@ def get_ui(user_ui, user=None): def init_user(user, request=None): if request: SessionData.get_or_create(request) - if user.is_anonymous: + if user.is_anonymous(): result = settings.CONFIG['user'].copy() result['ui'] = get_ui(json.loads(request.session.get('ui', '{}'))) else: @@ -430,7 +434,7 @@ def user_json(user, keys=None): return j def has_capability(user, capability): - if user.is_anonymous: + if user.is_anonymous(): level = 'guest' else: level = user.profile.get_level() diff --git a/pandora/user/tasks.py b/pandora/user/tasks.py index c1da304c..f54eb190 100644 --- a/pandora/user/tasks.py +++ b/pandora/user/tasks.py @@ -1,9 +1,10 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import -from datetime import timedelta -from itertools import zip_longest import json +from datetime import timedelta +from six.moves import zip_longest from celery.task import task, periodic_task from app.utils import limit_rate diff --git a/pandora/user/utils.py b/pandora/user/utils.py index 49fa7884..2cc82f1c 100644 --- a/pandora/user/utils.py +++ b/pandora/user/utils.py @@ -1,4 +1,4 @@ -from django.contrib.gis.geoip2 import GeoIP2, GeoIP2Exception +from django.contrib.gis.geoip2 import GeoIP2 import ox @@ -17,22 +17,15 @@ def get_location(ip): country = city = None try: g = GeoIP2() - except: - country = city = None - else: - try: - location = g.city(ip) - except GeoIP2Exception: - try: - location = g.country(s.ip) - except: - location = None + location = g.city(ip) if location: country = ox.get_country_name(location['country_code']) - if location.get('city'): + if location['city']: city = location['city'] if isinstance(city, bytes): city = city.decode('latin-1') + except: + country = city = None return city, country diff --git a/pandora/user/views.py b/pandora/user/views.py index aa452cec..88dc2276 100644 --- a/pandora/user/views.py +++ b/pandora/user/views.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, absolute_import import random random.seed() @@ -117,7 +118,7 @@ def signout(request, data): see: signin, signup ''' response = json_response(text='ok') - if request.user.is_authenticated: + if request.user.is_authenticated(): uid = request.user.id profile = request.user.profile if profile.ui.get('page') == 'signout': @@ -607,7 +608,7 @@ def mail(request, data): 'url': request.build_absolute_uri('/'), } message = template.render(context, request) - subject = 'Fwd: %s' % subject + subject = u'Fwd: %s' % subject email_to = '"%s" <%s>' % (request.user.username, request.user.email) receipt = EmailMessage(subject, message, @@ -633,7 +634,7 @@ def contact(request, data): ''' name = data.get('name', '') email = data.get('email', '') - if request.user.is_authenticated: + if request.user.is_authenticated(): if not name: name = request.user.username if not email: @@ -656,10 +657,10 @@ def contact(request, data): message = ox.decode_html(template.render(context, request)) response = json_response(text='message sent') try: - send_mail('%s Contact - %s' % (settings.SITENAME, subject), message, email_from, email_to) + send_mail(u'%s Contact - %s' % (settings.SITENAME, subject), message, email_from, email_to) except BadHeaderError: response = json_response(status=400, text='invalid data') - if request.user.is_authenticated \ + if request.user.is_authenticated() \ and 'receipt' in data \ and data['receipt']: template = loader.get_template('contact_receipt.txt') @@ -734,7 +735,7 @@ actions.register(editPreferences, cache=False) def reset_ui(request): - if request.user.is_authenticated: + if request.user.is_authenticated(): profile = request.user.profile profile.ui = {} profile.save() @@ -751,7 +752,7 @@ def resetUI(request, data): see: setUI ''' response = json_response() - if request.user.is_authenticated: + if request.user.is_authenticated(): profile = request.user.profile profile.ui = {} profile.save() @@ -772,7 +773,7 @@ def setUI(request, data): notes: To set nested keys, use {'foo.bar.baz': value} see: resetUI ''' - if request.user.is_authenticated: + if request.user.is_authenticated(): profile = request.user.profile ui = profile.ui else: @@ -793,7 +794,7 @@ def setUI(request, data): del p[keys[0]] else: p[keys[0]] = value - if request.user.is_authenticated: + if request.user.is_authenticated(): profile.save() else: request.session['ui'] = json.dumps(ui) @@ -801,7 +802,7 @@ def setUI(request, data): if data.get('item'): item = get_object_or_404_json(Item, public_id=data['item']) with transaction.atomic(): - if request.user.is_authenticated: + if request.user.is_authenticated(): access, created = Access.objects.get_or_create(item=item, user=request.user) else: access, created = Access.objects.get_or_create(item=item, user=None) @@ -918,7 +919,7 @@ def addGroup(request, data): while not created: g, created = Group.objects.get_or_create(name=name) n += 1 - name = '%s [%d]' % (_name, n) + name = u'%s [%d]' % (_name, n) response['data'] = group_json(g) add_changelog(request, data, g.name) return render_to_json_response(response) @@ -949,7 +950,7 @@ def editGroup(request, data): _name = re.sub(' \[\d+\]$', '', name).strip() while Group.objects.filter(name=name).count(): n += 1 - name = '%s [%d]' % (_name, n) + name = u'%s [%d]' % (_name, n) g.name = name g.save() add_changelog(request, data, g.name) diff --git a/pandora/websocket/management/commands/websocketd.py b/pandora/websocket/management/commands/websocketd.py index f93bc9d6..0eb9ed25 100644 --- a/pandora/websocket/management/commands/websocketd.py +++ b/pandora/websocket/management/commands/websocketd.py @@ -1,4 +1,6 @@ # -*- coding: utf-8 -*- +from __future__ import absolute_import + import os from django.core.management.base import BaseCommand diff --git a/pandora/websocket/worker.py b/pandora/websocket/worker.py index 1a0e633d..72c35e5a 100644 --- a/pandora/websocket/worker.py +++ b/pandora/websocket/worker.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -import logging +from __future__ import absolute_import from django.conf import settings @@ -8,7 +8,6 @@ from kombu.mixins import ConsumerMixin from . import daemon, key -logger = logging.getLogger('pandora.websocket') queue = Queue('websocket', Exchange(key, type='direct'), routing_key=key) @@ -23,11 +22,8 @@ class Worker(ConsumerMixin): callbacks=[self.process_task])] def process_task(self, body, message): - try: - if body['task'] == 'trigger_event': - daemon.trigger_event(*body['args']) - except: - logger.error('faild to trigger event %s', body, exc_info=True) + if body['task'] == 'trigger_event': + daemon.trigger_event(*body['args']) message.ack() def run(): diff --git a/requirements.txt b/requirements.txt index 436f7030..426726e2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,14 +1,14 @@ -Django==3.0.6 +Django==1.11.28 simplejson chardet -celery>4.3.0 +celery>4 django-celery-results -django-extensions==2.2.9 -gunicorn==20.0.4 +django-extensions==2.0.7 +gunicorn==19.8.1 html5lib -requests==2.23.0 +requests==2.22.0 tornado<5 -geoip2==3.0.0 -youtube-dl>=2020.5.8 +geoip2==2.9.0 +youtube-dl>=2019.4.30 python-memcached elasticsearch diff --git a/scripts/poster.0xdb.py b/scripts/poster.0xdb.py index 9b9f2470..928d5e0d 100755 --- a/scripts/poster.0xdb.py +++ b/scripts/poster.0xdb.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- +from __future__ import division import os from PIL import Image @@ -29,8 +30,8 @@ def get_frame(id, height, position): def render_poster(data, poster): title = ox.decode_html(data.get('title', '')).upper() - director = ox.decode_html(', '.join(data.get('director', []))).upper() - for key, value in {'\u03a0': 'PI', 'ß': 'SS'}.items(): + director = ox.decode_html(u', '.join(data.get('director', []))).upper() + for key, value in {u'\u03a0': 'PI', u'ß': u'SS'}.items(): title = title.replace(key, value) director = director.replace(key, value) year = str(data.get('year', '')) diff --git a/scripts/poster.indiancinema.py b/scripts/poster.indiancinema.py index bc37fca5..392fb50b 100755 --- a/scripts/poster.indiancinema.py +++ b/scripts/poster.indiancinema.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- +from __future__ import division import os from PIL import Image @@ -16,7 +17,7 @@ static_root = os.path.join(os.path.dirname(__file__), 'data') def render_poster(data, poster): title = ox.decode_html(data.get('title', '')) - director = ox.decode_html(', '.join(data.get('director', []))) + director = ox.decode_html(u', '.join(data.get('director', []))) year = str(data.get('year', '')) frame = data.get('frame') timeline = data.get('timeline') diff --git a/scripts/poster.padma.py b/scripts/poster.padma.py index 015aaa9b..81ec3dfe 100755 --- a/scripts/poster.padma.py +++ b/scripts/poster.padma.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- +from __future__ import division import os from PIL import Image diff --git a/scripts/poster.pandora.py b/scripts/poster.pandora.py index 5f196562..fa34e09e 100755 --- a/scripts/poster.pandora.py +++ b/scripts/poster.pandora.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- +from __future__ import division import os from PIL import Image @@ -16,7 +17,7 @@ static_root = os.path.join(os.path.dirname(__file__), 'data') def render_poster(data, poster): title = ox.decode_html(data.get('title', '')) - director = ox.decode_html(', '.join(data.get('director', []))) + director = ox.decode_html(u', '.join(data.get('director', []))) year = str(data.get('year', '')) series = data.get('isSeries', False) oxdb_id = data['oxdbId'] diff --git a/static/js/documentFilterForm.js b/static/js/documentFilterForm.js index a7353eb0..a1fb25c9 100644 --- a/static/js/documentFilterForm.js +++ b/static/js/documentFilterForm.js @@ -72,7 +72,7 @@ pandora.ui.documentFilterForm = function(options) { }); function autocompleteFunction(key) { return key.autocomplete ? function(value, callback) { - pandora.api.autocompleteDocuments({ + pandora.api.autocomplete({ key: key.id, query: { conditions: [], diff --git a/static/js/embedDialog.js b/static/js/embedDialog.js index 60dba564..1142cddb 100644 --- a/static/js/embedDialog.js +++ b/static/js/embedDialog.js @@ -40,7 +40,7 @@ pandora.ui.embedDialog = function(/*[url, ]callback*/) { description: Ox._('Embed a Clip or a Full Video'), inputs: [ 'item', 'position', 'in', 'out', 'annotation', 'title', - 'showTimeline', 'showAnnotations', 'showLayers', 'matchRatio', 'timeline' + 'showTimeline', 'showAnnotations', 'matchRatio' ] }, { @@ -85,7 +85,7 @@ pandora.ui.embedDialog = function(/*[url, ]callback*/) { description: Ox._('Embed an Edited Video'), inputs: [ 'edit', 'editMode', 'position', - 'showTimeline', 'showAnnotations', 'showLayers', 'matchRatio', 'timeline' + 'showTimeline', 'showAnnotations', 'matchRatio' ] } ].map(function(item, index) { @@ -233,7 +233,8 @@ pandora.ui.embedDialog = function(/*[url, ]callback*/) { data = getData(); if (view == 'document') { var prefix = type == 'iframe' - ? (document.location.protocol + '//' + document.location.host + '/') + ? (pandora.site.site.https ? 'https' : 'http') + + '://' + pandora.site.site.url + '/' : '/', page = type == 'iframe' ? 'documents' : 'document', resolution = 480; @@ -259,12 +260,9 @@ pandora.ui.embedDialog = function(/*[url, ]callback*/) { options = Ox.serialize({ title: data.title || void 0, showTimeline: data.showTimeline || void 0, - timeline: data.showTimeline && data.timeline ? data.timeline : void 0, + timeline: data.timeline || void 0, showAnnotations: data.showAnnotations || void 0, - showLayers: data.showAnnotations && data.showLayers && - data.showLayers.length < pandora.site.layers.length - ? data.showLayers.join(',') - : void 0, + showLayers: data.showAnnotations && data.showLayers ? data.showLayers : void 0, matchRatio: Ox.contains(['video', 'edit'], view) ? data.matchRatio || void 0 : void 0, showInfo: Ox.contains(['list', 'text'], view) || (view == 'edit' && !!$input.editMode.value()) || void 0 }, true), @@ -277,7 +275,8 @@ pandora.ui.embedDialog = function(/*[url, ]callback*/) { return Ox.encodeHTMLEntities( ( type == 'iframe' - ? (document.location.protocol + '//' + document.location.host + '/') + ? (pandora.site.site.https ? 'https' : 'http') + + '://' + pandora.site.site.url + '/' : '/' ) + ( diff --git a/static/js/groupsDialog.js b/static/js/groupsDialog.js index cf3973a9..6065a519 100644 --- a/static/js/groupsDialog.js +++ b/static/js/groupsDialog.js @@ -23,7 +23,6 @@ pandora.ui.groupsDialog = function(options) { canManageGroups = pandora.hasCapability('canManageUsers'), isItem = options.type == 'item', - isDocument = options.type == 'document', $content, $label, @@ -41,11 +40,7 @@ pandora.ui.groupsDialog = function(options) { selectedGroups = options.groups; renderGroups(); } else { - pandora.api[ - isItem ? 'get' - : isDocument ? 'getDocument' - : 'getUser' - ]({ + pandora.api[isItem ? 'get' : 'getUser']({ id: options.id, keys: ['groups'] }, function(result) { @@ -75,16 +70,8 @@ pandora.ui.groupsDialog = function(options) { return group.name; }); // disableElements(); - Ox.Request.clearCache( - isItem ? 'get' - : isDocument ? 'getDocument' - : 'getUser' - ); - pandora.api[ - isItem ? 'edit' - : isDocument ? 'editDocument' - : 'editUser' - ]({ + Ox.Request.clearCache(isItem ? 'get' : 'getUser'); + pandora.api[isItem ? 'edit' : 'editUser']({ id: options.id, groups: selectedGroups }, function(result) { @@ -211,9 +198,7 @@ pandora.ui.groupsDialog = function(options) { $label = Ox.Label({ textAlign: 'center', title: Ox._( - isItem ? pandora.site.itemName.singular - : isDocument ? 'Document' - : 'User' + isItem ? pandora.site.itemName.singular : 'User' ) + ': ' + options.name, width: 552 }) diff --git a/static/js/pandora.js b/static/js/pandora.js index 8770be61..92f34709 100644 --- a/static/js/pandora.js +++ b/static/js/pandora.js @@ -456,9 +456,6 @@ appPanel videoFormat: Ox.getVideoFormat(pandora.site.video.formats) }); - pandora.site.site.url = document.location.host - pandora.site.site.https = document.location.protocol == 'https:' - Ox.Map.GoogleApiKey = pandora.site.site.googleapikey; // set locale and initialize url controller diff --git a/static/js/utils.js b/static/js/utils.js index d4d0f610..a08f49a0 100644 --- a/static/js/utils.js +++ b/static/js/utils.js @@ -425,13 +425,6 @@ pandora.uploadDroppedFiles = function(files) { Ox.Request.clearCache('findDocuments'); if (pandora.user.ui.document || pandora.user.ui.section != 'documents') { pandora.UI.set({section: 'documents', document: ''}); - } else if (pandora.user.ui._collection && pandora.getListData().editable) { - pandora.api.addCollectionItems({ - items: files.ids, - collection: pandora.user.ui._collection - }, function(results) { - pandora.$ui.list && pandora.$ui.list.reloadList(); - }) } else { pandora.$ui.list && pandora.$ui.list.reloadList(); } diff --git a/update.py b/update.py index 3a2c7a40..6b32fc67 100755 --- a/update.py +++ b/update.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +from __future__ import print_function import json import os import shutil @@ -116,12 +117,6 @@ def run_git(path, *args): return subprocess.check_output(cmd, env=env).decode().strip() -def run_sql(sql): - cmd = [join(base, 'pandora/manage.py'), 'dbshell'] - p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) - stdout, error = p.communicate(input=sql.encode()) - return stdout.decode() - def get_version(path): return run_git(path, 'rev-list', 'HEAD', '--count') @@ -280,30 +275,13 @@ if __name__ == "__main__": ] with open('pandora/local_settings.py', 'w') as f: f.write('\n'.join(local_settings)) - if old <= 6313: - if sys.version_info[0:2] < (3, 6): - print('Python 3.6 or late is required now, upgrade your system and run:') - print('') - print('./update.py postupdate %s %s' % (6313, new)) - print('') - sys.exit(1) - run('./bin/pip', 'uninstall', 'django-celery', '-y') - run('./bin/pip', 'install', '-r', 'requirements.txt') - if old <= 6315: - for sql in [ - "INSERT INTO django_migrations (app, name, applied) VALUES ('system', '0001_initial', CURRENT_TIMESTAMP)", - "UPDATE django_content_type SET app_label = 'system' WHERE app_label = 'auth' and model = 'user'", - ]: - run_sql(sql) - run(join(base, 'pandora/manage.py'), 'migrate', 'system') - run(join(base, 'pandora/manage.py'), 'update_geoip') else: if len(sys.argv) == 1: branch = get_branch() development = branch == 'master' elif len(sys.argv) == 3 and sys.argv[1] == 'switch': branch = sys.argv[2] - development = branch == 'master' + development = False else: branch = 'master' development = True @@ -315,14 +293,14 @@ if __name__ == "__main__": if exists(path): os.chdir(path) current_branch = get_branch(path) + if current_branch != branch: + run('git', 'remote', 'set-branches', 'origin', '*') + run('git', 'fetch', 'origin') + run('git', 'checkout', branch) revno = get_version(path) if repo == 'pandora': pandora_old_revno = revno current += revno - if current_branch != branch: - run('git', 'remote', 'set-branches', 'origin', '*') - run('git', 'fetch', 'origin') - run('git', 'checkout', branch) url = repos[repo]['url'] print('Checking', repo) run('git', 'pull') diff --git a/vm/LXC_README.md b/vm/LXC_README.md index 440d412a..92cd6262 100644 --- a/vm/LXC_README.md +++ b/vm/LXC_README.md @@ -4,7 +4,7 @@ # Installing pan.do/ra inside LXC -1) Install lxc on the host (Ubuntu 18.04): +1) Install lxc on the host (Ubuntu 18.04 or later): sudo apt-get install lxc @@ -15,7 +15,7 @@ 2) Create a new container, use different names if installing multiple instances: - sudo lxc-create -n pandora -t ubuntu-cloud -- -r focal + sudo lxc-create -n pandora -t ubuntu-cloud -- -r bionic or @@ -41,6 +41,5 @@ cd /root curl -sL https://pan.do/ra-install > pandora_install.sh chmod +x pandora_install.sh - export BRANCH=stable # or master ./pandora_install.sh 2>&1 | tee pandora_install.log diff --git a/vm/LXD_README.md b/vm/LXD_README.md index 9aa78a25..3b003deb 100644 --- a/vm/LXD_README.md +++ b/vm/LXD_README.md @@ -4,13 +4,15 @@ # Installing pan.do/ra inside LXD -1) Install lxd on the host (Ubuntu 18.04 or later, Debian/10): +1) Install lxd on the host (Ubuntu 16.04 or later): - sudo snap install lxd + sudo apt-get install lxd + +[on debian you can use snap install lxd] 2) Create a new container, use different names if installing multiple instances: - sudo lxc launch ubuntu:20.04 pandora + sudo lxc launch ubuntu:18.04 pandora or diff --git a/vm/pandora_install.sh b/vm/pandora_install.sh index ec77d3b0..5f957bb2 100755 --- a/vm/pandora_install.sh +++ b/vm/pandora_install.sh @@ -12,7 +12,7 @@ NGINX=${NGINX-local} BRANCH=${BRANCH-stable} # add a pandora user -echo Installing pandora with user: $PANDORA branch: $BRANCH +echo Installing pandora with user: $PANDORA getent passwd $PANDORA > /dev/null 2>&1 || adduser --disabled-password --gecos "" $PANDORA # @@ -196,11 +196,7 @@ fi # configure nginx if [ "$NGINX" == "local" ]; then -cp "/srv/pandora/etc/nginx/pandora" "/etc/nginx/sites-available/pandora" -rm -f /etc/nginx/sites-enabled/default /etc/nginx/sites-enabled/pandora -ln -s ../sites-available/pandora /etc/nginx/sites-enabled/pandora -ln -s /srv/pandora/ctl /usr/local/bin/pandoractl - +cp "/srv/pandora/etc/nginx/pandora" "/etc/nginx/sites-available/default" read -r -d '' GZIP <