Compare commits
No commits in common. "769f0b7fd2dda5cdf0331a5f415c38ee329fd643" and "2d25c60606004a02297e521211f3d08e823cd8df" have entirely different histories.
769f0b7fd2
...
2d25c60606
160 changed files with 783 additions and 812 deletions
|
|
@ -1,13 +1,13 @@
|
||||||
# pan.do/ra - open media archive
|
# 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
|
## 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
|
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.
|
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:
|
Use the following commands as root to install pan.do/ra and all dependencies:
|
||||||
|
|
@ -16,7 +16,6 @@
|
||||||
cd /root
|
cd /root
|
||||||
curl -sL https://pan.do/ra-install > pandora_install.sh
|
curl -sL https://pan.do/ra-install > pandora_install.sh
|
||||||
chmod +x 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
|
./pandora_install.sh 2>&1 | tee pandora_install.log
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
40
ctl
40
ctl
|
|
@ -1,18 +1,13 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
SERVICES="pandora pandora-tasks pandora-encoding pandora-cron pandora-websocketd"
|
SERVICES="pandora pandora-tasks pandora-encoding pandora-cron pandora-websocketd"
|
||||||
if [ -z "$1" ]; then
|
if [ -z "$1" ]; then
|
||||||
echo "Usage: $0 (start|stop|restart|reload|status)"
|
echo "Usage: $0 (start|stop|restart|reload)"
|
||||||
exit 1
|
exit 1
|
||||||
else
|
else
|
||||||
action="$1"
|
action="$1"
|
||||||
fi
|
fi
|
||||||
self=`readlink "$0"`
|
|
||||||
if [ -z $self ]; then
|
|
||||||
self="$0"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$action" = "init" ]; then
|
if [ "$action" = "init" ]; then
|
||||||
cd "`dirname "$self"`"
|
cd "`dirname "$0"`"
|
||||||
BASE=`pwd`
|
BASE=`pwd`
|
||||||
SUDO=""
|
SUDO=""
|
||||||
PANDORA_USER=`ls -l update.py | cut -f3 -d" "`
|
PANDORA_USER=`ls -l update.py | cut -f3 -d" "`
|
||||||
|
|
@ -48,27 +43,6 @@ if [ "$action" = "init" ]; then
|
||||||
fi
|
fi
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
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
|
if [ `whoami` != 'root' ]; then
|
||||||
echo you have to be root or run $0 with sudo
|
echo you have to be root or run $0 with sudo
|
||||||
exit 1
|
exit 1
|
||||||
|
|
@ -93,7 +67,6 @@ if [ "$action" = "install" ]; then
|
||||||
systemctl enable ${service}.service
|
systemctl enable ${service}.service
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
test -e /usr/local/bin/pandoractl || ln -s /srv/pandora/ctl /usr/local/bin/pandoractl
|
|
||||||
else
|
else
|
||||||
if [ -d /etc/init ]; then
|
if [ -d /etc/init ]; then
|
||||||
cp $BASE/etc/init/* /etc/init/
|
cp $BASE/etc/init/* /etc/init/
|
||||||
|
|
@ -101,13 +74,6 @@ if [ "$action" = "install" ]; then
|
||||||
fi
|
fi
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
if [ "status" = "$action" ]; then
|
|
||||||
export SYSTEMD_PAGER=
|
|
||||||
fi
|
|
||||||
for service in $SERVICES; do
|
for service in $SERVICES; do
|
||||||
if [ -x /bin/systemctl ]; then
|
service $service $action
|
||||||
/bin/systemctl $action $service
|
|
||||||
else
|
|
||||||
service $service $action
|
|
||||||
fi
|
|
||||||
done
|
done
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, print_function, absolute_import
|
||||||
|
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import unicodedata
|
import unicodedata
|
||||||
|
|
||||||
|
from six import string_types
|
||||||
from django.db.models import Q, Manager
|
from django.db.models import Q, Manager
|
||||||
from oxdjango.query import QuerySet
|
from oxdjango.query import QuerySet
|
||||||
|
|
||||||
|
|
@ -67,7 +68,7 @@ def parseCondition(condition, user):
|
||||||
else:
|
else:
|
||||||
key = k + get_operator(op, 'istr' if k in case_insensitive_keys else 'str')
|
key = k + get_operator(op, 'istr' if k in case_insensitive_keys else 'str')
|
||||||
key = str(key)
|
key = str(key)
|
||||||
if isinstance(v, str):
|
if isinstance(v, string_types):
|
||||||
v = unicodedata.normalize('NFKD', v)
|
v = unicodedata.normalize('NFKD', v)
|
||||||
if k not in case_sensitive_keys:
|
if k not in case_sensitive_keys:
|
||||||
v = v.lower()
|
v = v.lower()
|
||||||
|
|
@ -157,7 +158,7 @@ class AnnotationManager(Manager):
|
||||||
#anonymous can only see public items
|
#anonymous can only see public items
|
||||||
public_layers = self.model.public_layers()
|
public_layers = self.model.public_layers()
|
||||||
|
|
||||||
if user.is_anonymous:
|
if user.is_anonymous():
|
||||||
qs = qs.filter(layer__in=public_layers)
|
qs = qs.filter(layer__in=public_layers)
|
||||||
#users can see public and own
|
#users can see public and own
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, print_function, absolute_import
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import unicodedata
|
import unicodedata
|
||||||
|
|
||||||
|
from django.utils.encoding import python_2_unicode_compatible
|
||||||
from django.db import models, transaction
|
from django.db import models, transaction
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
|
|
@ -81,15 +83,16 @@ def get_matches(obj, model, layer_type, qs=None):
|
||||||
matches = [-1]
|
matches = [-1]
|
||||||
return Annotation.objects.filter(id__in=matches)
|
return Annotation.objects.filter(id__in=matches)
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class Annotation(models.Model):
|
class Annotation(models.Model):
|
||||||
objects = managers.AnnotationManager()
|
objects = managers.AnnotationManager()
|
||||||
|
|
||||||
#FIXME: here having a item,start index would be good
|
#FIXME: here having a item,start index would be good
|
||||||
created = models.DateTimeField(auto_now_add=True)
|
created = models.DateTimeField(auto_now_add=True)
|
||||||
modified = models.DateTimeField(auto_now=True)
|
modified = models.DateTimeField(auto_now=True)
|
||||||
user = models.ForeignKey(User, related_name='annotations', on_delete=models.CASCADE)
|
user = models.ForeignKey(User, related_name='annotations')
|
||||||
item = models.ForeignKey('item.Item', related_name='annotations', on_delete=models.CASCADE)
|
item = models.ForeignKey('item.Item', related_name='annotations')
|
||||||
clip = models.ForeignKey('clip.Clip', null=True, related_name='annotations', on_delete=models.CASCADE)
|
clip = models.ForeignKey('clip.Clip', null=True, related_name='annotations')
|
||||||
|
|
||||||
public_id = models.CharField(max_length=128, unique=True)
|
public_id = models.CharField(max_length=128, unique=True)
|
||||||
#seconds
|
#seconds
|
||||||
|
|
@ -104,7 +107,7 @@ class Annotation(models.Model):
|
||||||
languages = models.CharField(max_length=255, null=True, blank=True)
|
languages = models.CharField(max_length=255, null=True, blank=True)
|
||||||
|
|
||||||
def editable(self, user):
|
def editable(self, user):
|
||||||
if user.is_authenticated:
|
if user.is_authenticated():
|
||||||
if user.profile.capability('canEditAnnotations') or \
|
if user.profile.capability('canEditAnnotations') or \
|
||||||
self.user == user or \
|
self.user == user or \
|
||||||
user.groups.filter(id__in=self.item.groups.all()).count() > 0:
|
user.groups.filter(id__in=self.item.groups.all()).count() > 0:
|
||||||
|
|
@ -397,7 +400,7 @@ class Annotation(models.Model):
|
||||||
return j
|
return j
|
||||||
|
|
||||||
def __str__(self):
|
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):
|
def cleanup_related(sender, **kwargs):
|
||||||
kwargs['instance'].cleanup_undefined_relations()
|
kwargs['instance'].cleanup_undefined_relations()
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, print_function, absolute_import
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, print_function, absolute_import
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db.models import Count, Sum, F, Value
|
from django.db.models import Count, Sum, F, Value
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, print_function, absolute_import
|
||||||
|
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, print_function, absolute_import
|
||||||
|
|
||||||
import codecs
|
import codecs
|
||||||
import os
|
import os
|
||||||
|
|
@ -9,8 +10,8 @@ import sys
|
||||||
import time
|
import time
|
||||||
from os.path import dirname, exists, join
|
from os.path import dirname, exists, join
|
||||||
from glob import glob
|
from glob import glob
|
||||||
import _thread as thread
|
|
||||||
|
|
||||||
|
from six.moves import _thread as thread
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
|
|
||||||
|
|
@ -28,16 +29,20 @@ RUN_RELOADER = True
|
||||||
NOTIFIER = None
|
NOTIFIER = None
|
||||||
|
|
||||||
def get_version():
|
def get_version():
|
||||||
|
info = join(dirname(dirname(dirname(__file__))), '.bzr', 'branch', 'last-revision')
|
||||||
git_dir = join(dirname(dirname(dirname(__file__))), '.git')
|
git_dir = join(dirname(dirname(dirname(__file__))), '.git')
|
||||||
if exists(git_dir):
|
if exists(git_dir):
|
||||||
env = {'GIT_DIR': git_dir}
|
env = {'GIT_DIR': git_dir}
|
||||||
cmd = ['git', 'rev-list', 'HEAD', '--count']
|
cmd = ['git', 'rev-list', 'HEAD', '--count']
|
||||||
version = subprocess.check_output(cmd, env=env).strip().decode('utf-8')
|
return subprocess.check_output(cmd, env=env).strip().decode('utf-8')
|
||||||
if settings.VERSION_EPOCH:
|
elif exists(info):
|
||||||
version = settings.VERSION_EPOCH + version
|
f = open(info)
|
||||||
return version
|
rev = int(f.read().split()[0])
|
||||||
|
f.close()
|
||||||
|
if rev:
|
||||||
|
return u'%s' % rev
|
||||||
else:
|
else:
|
||||||
return 'unknown'
|
return u'unknown'
|
||||||
|
|
||||||
def load_config(init=False):
|
def load_config(init=False):
|
||||||
with open(settings.SITE_CONFIG) as f:
|
with open(settings.SITE_CONFIG) as f:
|
||||||
|
|
@ -394,7 +399,8 @@ def update_static():
|
||||||
def update_geoip(force=False):
|
def update_geoip(force=False):
|
||||||
path = os.path.join(settings.GEOIP_PATH, 'GeoLite2-City.mmdb')
|
path = os.path.join(settings.GEOIP_PATH, 'GeoLite2-City.mmdb')
|
||||||
if not os.path.exists(path) or force:
|
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)
|
match = re.compile('href=[\'"](http.*.mmdb.gz)').findall(index)
|
||||||
if match:
|
if match:
|
||||||
url = match[0]
|
url = match[0]
|
||||||
|
|
@ -404,7 +410,7 @@ def update_geoip(force=False):
|
||||||
os.unlink(path)
|
os.unlink(path)
|
||||||
os.system('gunzip "%s.gz"' % path)
|
os.system('gunzip "%s.gz"' % path)
|
||||||
else:
|
else:
|
||||||
print('failed to download GeoLite2-City.mmdb')
|
print('failed to download dbip-country-lite-2020-03.mmdb.gz')
|
||||||
|
|
||||||
def init():
|
def init():
|
||||||
if not settings.RELOADER_RUNNING:
|
if not settings.RELOADER_RUNNING:
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import ox.jsonc
|
import ox.jsonc
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
from django.db import connection
|
from django.db import connection
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import print_function
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from ... import documentation
|
from ... import documentation
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,16 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, print_function, absolute_import
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.utils.encoding import python_2_unicode_compatible
|
||||||
|
|
||||||
from . import monkey_patch
|
from . import monkey_patch
|
||||||
from . import tasks
|
from . import tasks
|
||||||
|
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class Page(models.Model):
|
class Page(models.Model):
|
||||||
created = models.DateTimeField(auto_now_add=True)
|
created = models.DateTimeField(auto_now_add=True)
|
||||||
modified = models.DateTimeField(auto_now=True)
|
modified = models.DateTimeField(auto_now=True)
|
||||||
|
|
@ -17,6 +20,7 @@ class Page(models.Model):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class Settings(models.Model):
|
class Settings(models.Model):
|
||||||
|
|
||||||
created = models.DateTimeField(auto_now_add=True)
|
created = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, print_function, absolute_import
|
||||||
|
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django.contrib.auth.models import Group
|
from django.contrib.auth.models import Group
|
||||||
|
|
@ -13,17 +14,10 @@ config.init()
|
||||||
|
|
||||||
NEW_LENGTH = {
|
NEW_LENGTH = {
|
||||||
'username': 255,
|
'username': 255,
|
||||||
'email': 254,
|
'email': 255,
|
||||||
'password': 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():
|
def monkey_patch_username():
|
||||||
for field in NEW_LENGTH:
|
for field in NEW_LENGTH:
|
||||||
f = User._meta.get_field(field)
|
f = User._meta.get_field(field)
|
||||||
|
|
@ -31,18 +25,22 @@ def monkey_patch_username():
|
||||||
for v in f.validators:
|
for v in f.validators:
|
||||||
if isinstance(v, MaxLengthValidator):
|
if isinstance(v, MaxLengthValidator):
|
||||||
v.limit_value = NEW_LENGTH[field]
|
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():
|
def apply_patch():
|
||||||
from django.db import connection, transaction
|
from django.db import connection, transaction
|
||||||
cursor = connection.cursor()
|
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 = []
|
sql = []
|
||||||
for row in table:
|
for row in table:
|
||||||
if row.name == 'name' and row.internal_size != 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)' % (
|
sql.append('ALTER TABLE "%s" ALTER "%s" TYPE varchar(%d)' % (User._meta.db_table, row.name, NEW_LENGTH[row.name]))
|
||||||
Group._meta.db_table, row.name, 255)
|
|
||||||
)
|
|
||||||
for q in sql:
|
for q in sql:
|
||||||
cursor.execute(q)
|
cursor.execute(q)
|
||||||
if sql:
|
if sql:
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, print_function, absolute_import
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, with_statement
|
||||||
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from .models import Settings
|
from .models import Settings
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, print_function, absolute_import
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
from six import string_types
|
||||||
from django.shortcuts import render, redirect
|
from django.shortcuts import render, redirect
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
|
|
@ -112,7 +114,7 @@ def getPage(request, data):
|
||||||
}
|
}
|
||||||
see: editPage
|
see: editPage
|
||||||
'''
|
'''
|
||||||
if isinstance(data, str):
|
if isinstance(data, string_types):
|
||||||
name = data
|
name = data
|
||||||
else:
|
else:
|
||||||
name = data['name']
|
name = data['name']
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, print_function, absolute_import
|
||||||
|
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, with_statement
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import ox
|
import ox
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, print_function, absolute_import
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, print_function, absolute_import
|
||||||
|
|
||||||
import os
|
import os
|
||||||
from os.path import exists
|
from os.path import exists
|
||||||
|
|
@ -12,6 +13,7 @@ import shutil
|
||||||
from distutils.spawn import find_executable
|
from distutils.spawn import find_executable
|
||||||
from glob import glob
|
from glob import glob
|
||||||
|
|
||||||
|
from six import string_types
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import ox
|
import ox
|
||||||
import ox.image
|
import ox.image
|
||||||
|
|
@ -462,7 +464,7 @@ def timeline(video, prefix, modes=None, size=None):
|
||||||
modes = ['antialias', 'slitscan', 'keyframes', 'audio', 'data']
|
modes = ['antialias', 'slitscan', 'keyframes', 'audio', 'data']
|
||||||
if size is None:
|
if size is None:
|
||||||
size = [64, 16]
|
size = [64, 16]
|
||||||
if isinstance(video, str):
|
if isinstance(video, string_types):
|
||||||
video = [video]
|
video = [video]
|
||||||
cmd = ['../bin/oxtimelines',
|
cmd = ['../bin/oxtimelines',
|
||||||
'-s', ','.join(map(str, reversed(sorted(size)))),
|
'-s', ','.join(map(str, reversed(sorted(size)))),
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, print_function, absolute_import
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import os.path
|
import os.path
|
||||||
|
|
@ -6,10 +7,12 @@ import shutil
|
||||||
import tempfile
|
import tempfile
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
from six import string_types, PY2
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models.signals import pre_delete
|
from django.db.models.signals import pre_delete
|
||||||
|
from django.utils.encoding import python_2_unicode_compatible
|
||||||
from oxdjango.fields import JSONField
|
from oxdjango.fields import JSONField
|
||||||
|
|
||||||
from oxdjango import fields
|
from oxdjango import fields
|
||||||
|
|
@ -27,9 +30,13 @@ from . import managers
|
||||||
|
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
|
|
||||||
|
if not PY2:
|
||||||
|
unicode = str
|
||||||
|
|
||||||
def data_path(f, x):
|
def data_path(f, x):
|
||||||
return f.get_path('data.bin')
|
return f.get_path('data.bin')
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class File(models.Model):
|
class File(models.Model):
|
||||||
AV_INFO = (
|
AV_INFO = (
|
||||||
'duration', 'video', 'audio', 'oshash', 'size',
|
'duration', 'video', 'audio', 'oshash', 'size',
|
||||||
|
|
@ -48,7 +55,7 @@ class File(models.Model):
|
||||||
modified = models.DateTimeField(auto_now=True)
|
modified = models.DateTimeField(auto_now=True)
|
||||||
|
|
||||||
oshash = models.CharField(max_length=16, unique=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
|
path = models.CharField(max_length=2048, default="") # canoncial path/file
|
||||||
sort_path = models.CharField(max_length=2048, default="") # sort name
|
sort_path = models.CharField(max_length=2048, default="") # sort name
|
||||||
|
|
@ -161,7 +168,7 @@ class File(models.Model):
|
||||||
if self.item:
|
if self.item:
|
||||||
for key in self.ITEM_INFO:
|
for key in self.ITEM_INFO:
|
||||||
data[key] = self.item.get(key)
|
data[key] = self.item.get(key)
|
||||||
if isinstance(data[key], str):
|
if isinstance(data[key], string_types):
|
||||||
data[key] = ox.decode_html(data[key])
|
data[key] = ox.decode_html(data[key])
|
||||||
elif isinstance(data[key], list):
|
elif isinstance(data[key], list):
|
||||||
data[key] = [ox.decode_html(e) for e in data[key]]
|
data[key] = [ox.decode_html(e) for e in data[key]]
|
||||||
|
|
@ -255,8 +262,8 @@ class File(models.Model):
|
||||||
data = self.get_path_info()
|
data = self.get_path_info()
|
||||||
self.extension = data.get('extension')
|
self.extension = data.get('extension')
|
||||||
self.language = data.get('language')
|
self.language = data.get('language')
|
||||||
self.part = ox.sort_string(str(data.get('part') or ''))
|
self.part = ox.sort_string(unicode(data.get('part') or ''))
|
||||||
self.part_title = ox.sort_string(str(data.get('partTitle')) or '')
|
self.part_title = ox.sort_string(unicode(data.get('partTitle')) or '')
|
||||||
self.type = data.get('type') or 'unknown'
|
self.type = data.get('type') or 'unknown'
|
||||||
self.version = data.get('version')
|
self.version = data.get('version')
|
||||||
|
|
||||||
|
|
@ -476,7 +483,7 @@ class File(models.Model):
|
||||||
if k not in keys:
|
if k not in keys:
|
||||||
del data[k]
|
del data[k]
|
||||||
can_see_media = False
|
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 \
|
can_see_media = user.profile.capability('canSeeMedia') or \
|
||||||
user.is_staff or \
|
user.is_staff or \
|
||||||
self.item.user == user or \
|
self.item.user == user or \
|
||||||
|
|
@ -591,15 +598,15 @@ class File(models.Model):
|
||||||
status = {}
|
status = {}
|
||||||
if self.encoding:
|
if self.encoding:
|
||||||
for s in self.streams.all():
|
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']
|
config = settings.CONFIG['video']
|
||||||
max_resolution = self.streams.get(source=None).resolution
|
max_resolution = self.streams.get(source=None).resolution
|
||||||
for resolution in sorted(config['resolutions'], reverse=True):
|
for resolution in sorted(config['resolutions'], reverse=True):
|
||||||
if resolution <= max_resolution:
|
if resolution <= max_resolution:
|
||||||
for f in config['formats']:
|
for f in config['formats']:
|
||||||
name = '%sp.%s' % (resolution, f)
|
name = u'%sp.%s' % (resolution, f)
|
||||||
if name not in status:
|
if name not in status:
|
||||||
status[name] = 'queued'
|
status[name] = u'queued'
|
||||||
return status
|
return status
|
||||||
|
|
||||||
def delete_frames(self):
|
def delete_frames(self):
|
||||||
|
|
@ -620,6 +627,7 @@ def delete_file(sender, **kwargs):
|
||||||
f.delete_files()
|
f.delete_files()
|
||||||
pre_delete.connect(delete_file, sender=File)
|
pre_delete.connect(delete_file, sender=File)
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class Volume(models.Model):
|
class Volume(models.Model):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
@ -628,11 +636,11 @@ class Volume(models.Model):
|
||||||
created = models.DateTimeField(auto_now_add=True)
|
created = models.DateTimeField(auto_now_add=True)
|
||||||
modified = models.DateTimeField(auto_now=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)
|
name = models.CharField(max_length=1024)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "%s's %s" % (self.user, self.name)
|
return u"%s's %s" % (self.user, self.name)
|
||||||
|
|
||||||
def json(self):
|
def json(self):
|
||||||
return {
|
return {
|
||||||
|
|
@ -644,6 +652,7 @@ class Volume(models.Model):
|
||||||
def inttime():
|
def inttime():
|
||||||
return int(time.time())
|
return int(time.time())
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class Instance(models.Model):
|
class Instance(models.Model):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
@ -659,11 +668,11 @@ class Instance(models.Model):
|
||||||
path = models.CharField(max_length=2048)
|
path = models.CharField(max_length=2048)
|
||||||
ignore = models.BooleanField(default=False)
|
ignore = models.BooleanField(default=False)
|
||||||
|
|
||||||
file = models.ForeignKey(File, related_name='instances', on_delete=models.CASCADE)
|
file = models.ForeignKey(File, related_name='instances')
|
||||||
volume = models.ForeignKey(Volume, related_name='files', on_delete=models.CASCADE)
|
volume = models.ForeignKey(Volume, related_name='files')
|
||||||
|
|
||||||
def __str__(self):
|
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
|
@property
|
||||||
def public_id(self):
|
def public_id(self):
|
||||||
|
|
@ -682,13 +691,14 @@ def frame_path(frame, name):
|
||||||
name = "%s%s" % (frame.position, ext)
|
name = "%s%s" % (frame.position, ext)
|
||||||
return frame.file.get_path(name)
|
return frame.file.get_path(name)
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class Frame(models.Model):
|
class Frame(models.Model):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ("file", "position")
|
unique_together = ("file", "position")
|
||||||
created = models.DateTimeField(auto_now_add=True)
|
created = models.DateTimeField(auto_now_add=True)
|
||||||
modified = models.DateTimeField(auto_now=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()
|
position = models.FloatField()
|
||||||
frame = models.ImageField(default=None, null=True, upload_to=frame_path)
|
frame = models.ImageField(default=None, null=True, upload_to=frame_path)
|
||||||
width = models.IntegerField(default=0)
|
width = models.IntegerField(default=0)
|
||||||
|
|
@ -701,7 +711,7 @@ class Frame(models.Model):
|
||||||
super(Frame, self).save(*args, **kwargs)
|
super(Frame, self).save(*args, **kwargs)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return '%s/%s' % (self.file, self.position)
|
return u'%s/%s' % (self.file, self.position)
|
||||||
|
|
||||||
def delete_frame(sender, **kwargs):
|
def delete_frame(sender, **kwargs):
|
||||||
f = kwargs['instance']
|
f = kwargs['instance']
|
||||||
|
|
@ -712,17 +722,18 @@ pre_delete.connect(delete_frame, sender=Frame)
|
||||||
def stream_path(f, x):
|
def stream_path(f, x):
|
||||||
return f.path(x)
|
return f.path(x)
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class Stream(models.Model):
|
class Stream(models.Model):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ("file", "resolution", "format")
|
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)
|
resolution = models.IntegerField(default=96)
|
||||||
format = models.CharField(max_length=255, default='webm')
|
format = models.CharField(max_length=255, default='webm')
|
||||||
|
|
||||||
media = models.FileField(default=None, blank=True, upload_to=stream_path)
|
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)
|
available = models.BooleanField(default=False)
|
||||||
oshash = models.CharField(max_length=16, null=True, db_index=True)
|
oshash = models.CharField(max_length=16, null=True, db_index=True)
|
||||||
info = JSONField(default=dict, editable=False)
|
info = JSONField(default=dict, editable=False)
|
||||||
|
|
@ -742,10 +753,10 @@ class Stream(models.Model):
|
||||||
return os.path.join(settings.MEDIA_ROOT, self.path(), 'timeline')
|
return os.path.join(settings.MEDIA_ROOT, self.path(), 'timeline')
|
||||||
|
|
||||||
def name(self):
|
def name(self):
|
||||||
return "%sp.%s" % (self.resolution, self.format)
|
return u"%sp.%s" % (self.resolution, self.format)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "%s/%s" % (self.file, self.name())
|
return u"%s/%s" % (self.file, self.name())
|
||||||
|
|
||||||
def get(self, resolution, format):
|
def get(self, resolution, format):
|
||||||
streams = []
|
streams = []
|
||||||
|
|
@ -803,12 +814,8 @@ class Stream(models.Model):
|
||||||
|
|
||||||
# file could have been moved while encoding
|
# file could have been moved while encoding
|
||||||
# get current version from db and update
|
# get current version from db and update
|
||||||
try:
|
self.refresh_from_db()
|
||||||
self.refresh_from_db()
|
self.update_status(ok, error)
|
||||||
except archive.models.DoesNotExist:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
self.update_status(ok, error)
|
|
||||||
|
|
||||||
def get_index(self):
|
def get_index(self):
|
||||||
index = 1
|
index = 1
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, print_function
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from time import time
|
from time import time
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, print_function, absolute_import
|
||||||
|
|
||||||
from glob import glob
|
from glob import glob
|
||||||
|
|
||||||
|
from six import string_types
|
||||||
from celery.task import task
|
from celery.task import task
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
|
|
@ -218,7 +220,7 @@ def move_media(data, user):
|
||||||
data['public_id'] = data.pop('item').strip()
|
data['public_id'] = data.pop('item').strip()
|
||||||
if not is_imdb_id(data['public_id']):
|
if not is_imdb_id(data['public_id']):
|
||||||
del 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'] == '':
|
if data['director'] == '':
|
||||||
data['director'] = []
|
data['director'] = []
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, print_function, absolute_import
|
||||||
|
|
||||||
import os.path
|
import os.path
|
||||||
from datetime import datetime
|
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.conf import settings
|
||||||
from django.db.models import Count, Q
|
from django.db.models import Count, Q
|
||||||
|
|
||||||
|
from six import string_types
|
||||||
from celery.utils import get_full_cls_name
|
from celery.utils import get_full_cls_name
|
||||||
from celery._state import current_app
|
from celery._state import current_app
|
||||||
import ox
|
import ox
|
||||||
|
|
@ -554,7 +556,7 @@ def getPath(request, data):
|
||||||
'''
|
'''
|
||||||
response = json_response()
|
response = json_response()
|
||||||
ids = data['id']
|
ids = data['id']
|
||||||
if isinstance(ids, str):
|
if isinstance(ids, string_types):
|
||||||
ids = [ids]
|
ids = [ids]
|
||||||
for f in models.File.objects.filter(oshash__in=ids).values('path', 'oshash').order_by('sort_path'):
|
for f in models.File.objects.filter(oshash__in=ids).values('path', 'oshash').order_by('sort_path'):
|
||||||
response['data'][f['oshash']] = f['path']
|
response['data'][f['oshash']] = f['path']
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, print_function, absolute_import
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.utils.encoding import python_2_unicode_compatible
|
||||||
from oxdjango.fields import JSONField
|
from oxdjango.fields import JSONField
|
||||||
|
|
||||||
import ox
|
import ox
|
||||||
|
|
@ -17,13 +19,14 @@ User = get_user_model()
|
||||||
'''
|
'''
|
||||||
FIXME: remove this table more migrate to new ChangeLog
|
FIXME: remove this table more migrate to new ChangeLog
|
||||||
'''
|
'''
|
||||||
|
@python_2_unicode_compatible
|
||||||
class Changelog(models.Model):
|
class Changelog(models.Model):
|
||||||
created = models.DateTimeField(auto_now_add=True)
|
created = models.DateTimeField(auto_now_add=True)
|
||||||
type = models.CharField(max_length=255, db_index=True)
|
type = models.CharField(max_length=255, db_index=True)
|
||||||
value = JSONField(default=dict, editable=False)
|
value = JSONField(default=dict, editable=False)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return '%s %s' % (self.type, self.created)
|
return u'%s %s' % (self.type, self.created)
|
||||||
|
|
||||||
def json(self):
|
def json(self):
|
||||||
return self.value
|
return self.value
|
||||||
|
|
@ -47,18 +50,19 @@ def add_changelog(request, data, id=None):
|
||||||
'user': c.user.username,
|
'user': c.user.username,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class Log(models.Model):
|
class Log(models.Model):
|
||||||
|
|
||||||
action = models.CharField(max_length=255, db_index=True)
|
action = models.CharField(max_length=255, db_index=True)
|
||||||
data = JSONField(default=dict, editable=False)
|
data = JSONField(default=dict, editable=False)
|
||||||
created = models.DateTimeField(db_index=True)
|
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()
|
changeid = models.TextField()
|
||||||
|
|
||||||
objects = managers.LogManager()
|
objects = managers.LogManager()
|
||||||
|
|
||||||
def __str__(self):
|
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):
|
def get_id(self):
|
||||||
return ox.toAZ(self.id)
|
return ox.toAZ(self.id)
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
import models
|
import models
|
||||||
import item.models
|
import item.models
|
||||||
|
|
@ -17,33 +18,33 @@ def recover_item(id):
|
||||||
created = old.value['created']
|
created = old.value['created']
|
||||||
i.user = user.models.User.objects.get(username=i.data['user'])
|
i.user = user.models.User.objects.get(username=i.data['user'])
|
||||||
for key in [
|
for key in [
|
||||||
'rendered',
|
u'rendered',
|
||||||
'random',
|
u'random',
|
||||||
'cuts',
|
u'cuts',
|
||||||
'duration',
|
u'duration',
|
||||||
'id',
|
u'id',
|
||||||
'size',
|
u'size',
|
||||||
'posterFrame',
|
u'posterFrame',
|
||||||
'parts',
|
u'parts',
|
||||||
'cutsperminute',
|
u'cutsperminute',
|
||||||
'hue',
|
u'hue',
|
||||||
'numberofcuts',
|
u'numberofcuts',
|
||||||
'durations',
|
u'durations',
|
||||||
'volume',
|
u'volume',
|
||||||
'user',
|
u'user',
|
||||||
'words',
|
u'words',
|
||||||
'videoRatio',
|
u'videoRatio',
|
||||||
'aspectratio',
|
u'aspectratio',
|
||||||
'bitrate',
|
u'bitrate',
|
||||||
'pixels',
|
u'pixels',
|
||||||
'created',
|
u'created',
|
||||||
'numberoffiles',
|
u'numberoffiles',
|
||||||
'modified',
|
u'modified',
|
||||||
'timesaccessed',
|
u'timesaccessed',
|
||||||
'accessed',
|
u'accessed',
|
||||||
'resolution',
|
u'resolution',
|
||||||
'wordsperminute',
|
u'wordsperminute',
|
||||||
'posterRatio'
|
u'posterRatio'
|
||||||
]:
|
]:
|
||||||
if key in i.data:
|
if key in i.data:
|
||||||
del i.data[key]
|
del i.data[key]
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, print_function, absolute_import
|
||||||
|
|
||||||
import ox
|
import ox
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import unicodedata
|
import unicodedata
|
||||||
|
|
||||||
|
from six import string_types
|
||||||
from django.db.models import Q, Manager
|
from django.db.models import Q, Manager
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
|
|
@ -77,7 +78,7 @@ def parseCondition(condition, user):
|
||||||
else:
|
else:
|
||||||
key = k + get_operator(op, 'istr' if k in case_insensitive_keys else 'str')
|
key = k + get_operator(op, 'istr' if k in case_insensitive_keys else 'str')
|
||||||
key = str(key)
|
key = str(key)
|
||||||
if isinstance(v, str) and op != '===':
|
if isinstance(v, string_types) and op != '===':
|
||||||
v = unicodedata.normalize('NFKD', v).lower()
|
v = unicodedata.normalize('NFKD', v).lower()
|
||||||
if exclude:
|
if exclude:
|
||||||
q = ~Q(**{key: v})
|
q = ~Q(**{key: v})
|
||||||
|
|
@ -154,7 +155,7 @@ class ClipManager(Manager):
|
||||||
def parse(condition):
|
def parse(condition):
|
||||||
key = 'findvalue' + get_operator(condition.get('operator', ''))
|
key = 'findvalue' + get_operator(condition.get('operator', ''))
|
||||||
v = condition['value']
|
v = condition['value']
|
||||||
if isinstance(v, str):
|
if isinstance(v, string_types):
|
||||||
v = unicodedata.normalize('NFKD', v).lower()
|
v = unicodedata.normalize('NFKD', v).lower()
|
||||||
q = Q(**{key: v})
|
q = Q(**{key: v})
|
||||||
if condition['key'] in layer_ids:
|
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'])):
|
for l in list(filter(lambda k: k in layer_ids, data['keys'])):
|
||||||
qs = qs.filter(**{l: True})
|
qs = qs.filter(**{l: True})
|
||||||
#anonymous can only see public clips
|
#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']
|
allowed_level = settings.CONFIG['capabilities']['canSeeItem']['guest']
|
||||||
qs = qs.filter(sort__rightslevel__lte=allowed_level)
|
qs = qs.filter(sort__rightslevel__lte=allowed_level)
|
||||||
#users can see public clips, there own clips and clips of there groups
|
#users can see public clips, there own clips and clips of there groups
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, print_function, absolute_import
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.utils.encoding import python_2_unicode_compatible
|
||||||
|
|
||||||
import ox
|
import ox
|
||||||
|
|
||||||
|
|
@ -13,7 +15,7 @@ from . import managers
|
||||||
def get_layers(item, interval=None, user=None):
|
def get_layers(item, interval=None, user=None):
|
||||||
from annotation.models import Annotation
|
from annotation.models import Annotation
|
||||||
|
|
||||||
if user and user.is_anonymous:
|
if user and user.is_anonymous():
|
||||||
user = None
|
user = None
|
||||||
|
|
||||||
layers = {}
|
layers = {}
|
||||||
|
|
@ -43,6 +45,7 @@ def get_layers(item, interval=None, user=None):
|
||||||
return layers
|
return layers
|
||||||
|
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class MetaClip(object):
|
class MetaClip(object):
|
||||||
def update_calculated_values(self):
|
def update_calculated_values(self):
|
||||||
start = self.start
|
start = self.start
|
||||||
|
|
@ -181,7 +184,7 @@ class MetaClip(object):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def public_id(self):
|
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):
|
def __str__(self):
|
||||||
return self.public_id
|
return self.public_id
|
||||||
|
|
@ -197,8 +200,8 @@ attrs = {
|
||||||
'modified': models.DateTimeField(auto_now=True),
|
'modified': models.DateTimeField(auto_now=True),
|
||||||
'aspect_ratio': models.FloatField(default=0),
|
'aspect_ratio': models.FloatField(default=0),
|
||||||
|
|
||||||
'item': models.ForeignKey('item.Item', related_name='clips', on_delete=models.CASCADE),
|
'item': models.ForeignKey('item.Item', related_name='clips'),
|
||||||
'sort': models.ForeignKey('item.ItemSort', related_name='matching_clips', on_delete=models.CASCADE),
|
'sort': models.ForeignKey('item.ItemSort', related_name='matching_clips'),
|
||||||
'user': models.IntegerField(db_index=True, null=True),
|
'user': models.IntegerField(db_index=True, null=True),
|
||||||
|
|
||||||
#seconds
|
#seconds
|
||||||
|
|
@ -223,4 +226,4 @@ Clip = type('Clip', (MetaClip, models.Model), attrs)
|
||||||
|
|
||||||
class ClipRandom(models.Model):
|
class ClipRandom(models.Model):
|
||||||
id = models.BigIntegerField(primary_key=True)
|
id = models.BigIntegerField(primary_key=True)
|
||||||
clip = models.OneToOneField(Clip, on_delete=models.CASCADE)
|
clip = models.OneToOneField(Clip)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, print_function, absolute_import
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
import ox
|
import ox
|
||||||
|
|
@ -153,7 +154,7 @@ def findClips(request, data):
|
||||||
add_annotations(layer, aqs)
|
add_annotations(layer, aqs)
|
||||||
elif 'position' in query:
|
elif 'position' in query:
|
||||||
qs = order_query(qs, query['sort'])
|
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')]
|
for c in qs.values('item__public_id', 'start', 'end')]
|
||||||
data['conditions'] = data['conditions'] + {
|
data['conditions'] = data['conditions'] + {
|
||||||
'value': data['position'],
|
'value': data['position'],
|
||||||
|
|
@ -166,7 +167,7 @@ def findClips(request, data):
|
||||||
response['data']['position'] = utils.get_positions(ids, [qs[0].public_id])[0]
|
response['data']['position'] = utils.get_positions(ids, [qs[0].public_id])[0]
|
||||||
elif 'positions' in data:
|
elif 'positions' in data:
|
||||||
qs = order_query(qs, query['sort'])
|
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')]
|
for c in qs.values('item__public_id', 'start', 'end')]
|
||||||
response['data']['positions'] = utils.get_positions(ids, data['positions'])
|
response['data']['positions'] = utils.get_positions(ids, data['positions'])
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
from django.db import connection, transaction
|
from django.db import connection, transaction
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
from django.db import connection, transaction
|
from django.db import connection, transaction
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import unicodedata
|
import unicodedata
|
||||||
|
|
||||||
|
from six import string_types
|
||||||
from django.db.models import Q, Manager
|
from django.db.models import Q, Manager
|
||||||
from django.conf import settings
|
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())
|
q = Q(id__in=l.documents.all())
|
||||||
else:
|
else:
|
||||||
q = Q(id=0)
|
q = Q(id=0)
|
||||||
if exclude:
|
|
||||||
q = ~q
|
|
||||||
return q
|
return q
|
||||||
elif key_config.get('fulltext'):
|
elif key_config.get('fulltext'):
|
||||||
qs = models.Document.find_fulltext_ids(v)
|
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'
|
value_key = 'find__value'
|
||||||
else:
|
else:
|
||||||
value_key = k
|
value_key = k
|
||||||
if isinstance(v, str):
|
if isinstance(v, string_types):
|
||||||
v = unicodedata.normalize('NFKD', v).lower()
|
v = unicodedata.normalize('NFKD', v).lower()
|
||||||
if k in facet_keys:
|
if k in facet_keys:
|
||||||
in_find = False
|
in_find = False
|
||||||
|
|
@ -282,7 +281,7 @@ class DocumentManager(Manager):
|
||||||
qs = qs.distinct()
|
qs = qs.distinct()
|
||||||
|
|
||||||
#anonymous can only see public items
|
#anonymous can only see public items
|
||||||
if not user or user.is_anonymous:
|
if not user or user.is_anonymous():
|
||||||
level = 'guest'
|
level = 'guest'
|
||||||
allowed_level = settings.CONFIG['capabilities']['canSeeDocument'][level]
|
allowed_level = settings.CONFIG['capabilities']['canSeeDocument'][level]
|
||||||
qs = qs.filter(rightslevel__lte=allowed_level)
|
qs = qs.filter(rightslevel__lte=allowed_level)
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,19 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, print_function, absolute_import
|
||||||
|
|
||||||
from glob import glob
|
|
||||||
from urllib.parse import quote, unquote
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
from glob import glob
|
||||||
import unicodedata
|
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 import models, transaction
|
||||||
from django.db.models import Q, Sum, Max
|
from django.db.models import Q, Sum, Max
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django.db.models.signals import pre_delete
|
from django.db.models.signals import pre_delete
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.utils.encoding import python_2_unicode_compatible
|
||||||
from oxdjango.fields import JSONField
|
from oxdjango.fields import JSONField
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
@ -24,7 +27,6 @@ from annotation.models import Annotation
|
||||||
from archive.extract import resize_image
|
from archive.extract import resize_image
|
||||||
from archive.chunk import save_chunk
|
from archive.chunk import save_chunk
|
||||||
from user.models import Group
|
from user.models import Group
|
||||||
from user.utils import update_groups
|
|
||||||
|
|
||||||
from . import managers
|
from . import managers
|
||||||
from . import utils
|
from . import utils
|
||||||
|
|
@ -33,15 +35,19 @@ from .fulltext import FulltextMixin
|
||||||
|
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
|
|
||||||
|
if not PY2:
|
||||||
|
unicode = str
|
||||||
|
|
||||||
def get_path(f, x):
|
def get_path(f, x):
|
||||||
return f.path(x)
|
return f.path(x)
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class Document(models.Model, FulltextMixin):
|
class Document(models.Model, FulltextMixin):
|
||||||
|
|
||||||
created = models.DateTimeField(auto_now_add=True)
|
created = models.DateTimeField(auto_now_add=True)
|
||||||
modified = models.DateTimeField(auto_now=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')
|
groups = models.ManyToManyField(Group, blank=True, related_name='documents')
|
||||||
|
|
||||||
extension = models.CharField(max_length=255)
|
extension = models.CharField(max_length=255)
|
||||||
|
|
@ -67,7 +73,7 @@ class Document(models.Model, FulltextMixin):
|
||||||
data = JSONField(default=dict, editable=False)
|
data = JSONField(default=dict, editable=False)
|
||||||
|
|
||||||
def update_access(self, user):
|
def update_access(self, user):
|
||||||
if not user.is_authenticated:
|
if not user.is_authenticated():
|
||||||
user = None
|
user = None
|
||||||
access, created = Access.objects.get_or_create(document=self, user=user)
|
access, created = Access.objects.get_or_create(document=self, user=user)
|
||||||
if not created:
|
if not created:
|
||||||
|
|
@ -84,7 +90,7 @@ class Document(models.Model, FulltextMixin):
|
||||||
if not current_values:
|
if not current_values:
|
||||||
current_values = []
|
current_values = []
|
||||||
else:
|
else:
|
||||||
current_values = [str(current_values)]
|
current_values = [unicode(current_values)]
|
||||||
|
|
||||||
filter_map = utils.get_by_id(settings.CONFIG['documentKeys'], key).get('filterMap')
|
filter_map = utils.get_by_id(settings.CONFIG['documentKeys'], key).get('filterMap')
|
||||||
if filter_map:
|
if filter_map:
|
||||||
|
|
@ -135,7 +141,7 @@ class Document(models.Model, FulltextMixin):
|
||||||
f, created = Find.objects.get_or_create(document=self, key=key)
|
f, created = Find.objects.get_or_create(document=self, key=key)
|
||||||
if isinstance(value, bool):
|
if isinstance(value, bool):
|
||||||
value = value and 'true' or 'false'
|
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 = ox.decode_html(ox.strip_tags(value.strip()))
|
||||||
value = unicodedata.normalize('NFKD', value).lower()
|
value = unicodedata.normalize('NFKD', value).lower()
|
||||||
f.value = value
|
f.value = value
|
||||||
|
|
@ -154,7 +160,7 @@ class Document(models.Model, FulltextMixin):
|
||||||
elif i not in ('*', 'dimensions') and i not in self.facet_keys:
|
elif i not in ('*', 'dimensions') and i not in self.facet_keys:
|
||||||
value = data.get(i)
|
value = data.get(i)
|
||||||
if isinstance(value, list):
|
if isinstance(value, list):
|
||||||
value = '\n'.join(value)
|
value = u'\n'.join(value)
|
||||||
save(i, value)
|
save(i, value)
|
||||||
|
|
||||||
base_keys = ('id', 'size', 'dimensions', 'extension', 'matches')
|
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)
|
s.dimensions = ox.sort_string('%d' % prefix) + ox.sort_string('%d' % value)
|
||||||
|
|
||||||
def sortNames(values):
|
def sortNames(values):
|
||||||
sort_value = ''
|
sort_value = u''
|
||||||
if values:
|
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:
|
if not sort_value:
|
||||||
sort_value = ''
|
sort_value = u''
|
||||||
return sort_value.lower()
|
return sort_value.lower()
|
||||||
|
|
||||||
def set_value(s, name, value):
|
def set_value(s, name, value):
|
||||||
if isinstance(value, str):
|
if isinstance(value, string_types):
|
||||||
value = ox.decode_html(value.lower())
|
value = ox.decode_html(value.lower())
|
||||||
if not value:
|
if not value:
|
||||||
value = None
|
value = None
|
||||||
|
|
@ -222,7 +228,7 @@ class Document(models.Model, FulltextMixin):
|
||||||
if isinstance(sort_type, list):
|
if isinstance(sort_type, list):
|
||||||
sort_type = sort_type[0]
|
sort_type = sort_type[0]
|
||||||
if sort_type == 'title':
|
if sort_type == 'title':
|
||||||
value = self.get_value(source, 'Untitled')
|
value = self.get_value(source, u'Untitled')
|
||||||
value = utils.sort_title(value)[:955]
|
value = utils.sort_title(value)[:955]
|
||||||
set_value(s, name, value)
|
set_value(s, name, value)
|
||||||
elif sort_type == 'person':
|
elif sort_type == 'person':
|
||||||
|
|
@ -230,9 +236,9 @@ class Document(models.Model, FulltextMixin):
|
||||||
value = utils.sort_string(value)[:955]
|
value = utils.sort_string(value)[:955]
|
||||||
set_value(s, name, value)
|
set_value(s, name, value)
|
||||||
elif sort_type == 'string':
|
elif sort_type == 'string':
|
||||||
value = self.get_value(source, '')
|
value = self.get_value(source, u'')
|
||||||
if isinstance(value, list):
|
if isinstance(value, list):
|
||||||
value = ','.join(value)
|
value = u','.join(value)
|
||||||
if not isinstance(value, str):
|
if not isinstance(value, str):
|
||||||
value = str(value)
|
value = str(value)
|
||||||
value = utils.sort_string(value)[:955]
|
value = utils.sort_string(value)[:955]
|
||||||
|
|
@ -255,7 +261,7 @@ class Document(models.Model, FulltextMixin):
|
||||||
set_value(s, name, value)
|
set_value(s, name, value)
|
||||||
elif sort_type == 'date':
|
elif sort_type == 'date':
|
||||||
value = self.get_value(source)
|
value = self.get_value(source)
|
||||||
if isinstance(value, str):
|
if isinstance(value, string_types):
|
||||||
value = datetime_safe.datetime.strptime(value, '%Y-%m-%d')
|
value = datetime_safe.datetime.strptime(value, '%Y-%m-%d')
|
||||||
set_value(s, name, value)
|
set_value(s, name, value)
|
||||||
s.save()
|
s.save()
|
||||||
|
|
@ -312,7 +318,7 @@ class Document(models.Model, FulltextMixin):
|
||||||
return ox.toAZ(self.id)
|
return ox.toAZ(self.id)
|
||||||
|
|
||||||
def access(self, user):
|
def access(self, user):
|
||||||
if user.is_anonymous:
|
if user.is_anonymous():
|
||||||
level = 'guest'
|
level = 'guest'
|
||||||
else:
|
else:
|
||||||
level = user.profile.get_level()
|
level = user.profile.get_level()
|
||||||
|
|
@ -325,10 +331,9 @@ class Document(models.Model, FulltextMixin):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def editable(self, user, item=None):
|
def editable(self, user, item=None):
|
||||||
if not user or user.is_anonymous:
|
if not user or user.is_anonymous():
|
||||||
return False
|
return False
|
||||||
if self.user == user or \
|
if self.user == user or \
|
||||||
self.groups.filter(id__in=user.groups.all()).count() > 0 or \
|
|
||||||
user.is_staff or \
|
user.is_staff or \
|
||||||
user.profile.capability('canEditDocuments') is True or \
|
user.profile.capability('canEditDocuments') is True or \
|
||||||
(item and item.editable(user)):
|
(item and item.editable(user)):
|
||||||
|
|
@ -342,9 +347,6 @@ class Document(models.Model, FulltextMixin):
|
||||||
p.description = ox.sanitize_html(data['description'])
|
p.description = ox.sanitize_html(data['description'])
|
||||||
p.save()
|
p.save()
|
||||||
else:
|
else:
|
||||||
if 'groups' in data:
|
|
||||||
groups = data.pop('groups')
|
|
||||||
update_groups(self, groups)
|
|
||||||
for key in data:
|
for key in data:
|
||||||
k = list(filter(lambda i: i['id'] == key, settings.CONFIG['documentKeys']))
|
k = list(filter(lambda i: i['id'] == key, settings.CONFIG['documentKeys']))
|
||||||
ktype = k and k[0].get('type') or ''
|
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]]
|
self.data[key] = [ox.sanitize_html(t) for t in data[key]]
|
||||||
elif ktype == '[string]':
|
elif ktype == '[string]':
|
||||||
self.data[key] = [ox.escape_html(t) for t in data[key]]
|
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])
|
self.data[key] = ox.escape_html(data[key])
|
||||||
elif isinstance(data[key], list):
|
elif isinstance(data[key], list):
|
||||||
def cleanup(i):
|
def cleanup(i):
|
||||||
if isinstance(i, str):
|
if isinstance(i, string_types):
|
||||||
i = ox.escape_html(i)
|
i = ox.escape_html(i)
|
||||||
return i
|
return i
|
||||||
self.data[key] = [cleanup(i) for i in data[key]]
|
self.data[key] = [cleanup(i) for i in data[key]]
|
||||||
|
|
@ -434,7 +436,6 @@ class Document(models.Model, FulltextMixin):
|
||||||
'matches',
|
'matches',
|
||||||
'size',
|
'size',
|
||||||
'user',
|
'user',
|
||||||
'groups',
|
|
||||||
'referenced',
|
'referenced',
|
||||||
]
|
]
|
||||||
if self.extension in ('html', 'txt'):
|
if self.extension in ('html', 'txt'):
|
||||||
|
|
@ -454,8 +455,6 @@ class Document(models.Model, FulltextMixin):
|
||||||
response[key] = self.editable(user)
|
response[key] = self.editable(user)
|
||||||
elif key == 'user':
|
elif key == 'user':
|
||||||
response[key] = self.user.username
|
response[key] = self.user.username
|
||||||
elif key == 'groups':
|
|
||||||
response[key] = [g.name for g in self.groups.all()]
|
|
||||||
elif key == 'accessed':
|
elif key == 'accessed':
|
||||||
response[key] = self.accessed.aggregate(Max('access'))['access__max']
|
response[key] = self.accessed.aggregate(Max('access'))['access__max']
|
||||||
elif key == 'timesaccessed':
|
elif key == 'timesaccessed':
|
||||||
|
|
@ -476,7 +475,7 @@ class Document(models.Model, FulltextMixin):
|
||||||
if self.extension == 'html':
|
if self.extension == 'html':
|
||||||
response['text'] = self.data.get('text', '')
|
response['text'] = self.data.get('text', '')
|
||||||
if item:
|
if item:
|
||||||
if isinstance(item, str):
|
if isinstance(item, string_types):
|
||||||
item = Item.objects.get(public_id=item)
|
item = Item.objects.get(public_id=item)
|
||||||
d = self.descriptions.filter(item=item)
|
d = self.descriptions.filter(item=item)
|
||||||
if d.exists():
|
if d.exists():
|
||||||
|
|
@ -686,8 +685,8 @@ class ItemProperties(models.Model):
|
||||||
created = models.DateTimeField(auto_now_add=True)
|
created = models.DateTimeField(auto_now_add=True)
|
||||||
modified = models.DateTimeField(auto_now=True)
|
modified = models.DateTimeField(auto_now=True)
|
||||||
|
|
||||||
item = models.ForeignKey(Item, on_delete=models.CASCADE)
|
item = models.ForeignKey(Item)
|
||||||
document = models.ForeignKey(Document, related_name='descriptions', on_delete=models.CASCADE)
|
document = models.ForeignKey(Document, related_name='descriptions')
|
||||||
description = models.TextField(default="")
|
description = models.TextField(default="")
|
||||||
index = models.IntegerField(default=0)
|
index = models.IntegerField(default=0)
|
||||||
|
|
||||||
|
|
@ -702,13 +701,14 @@ class ItemProperties(models.Model):
|
||||||
super(ItemProperties, self).save(*args, **kwargs)
|
super(ItemProperties, self).save(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class Access(models.Model):
|
class Access(models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ("document", "user")
|
unique_together = ("document", "user")
|
||||||
|
|
||||||
access = models.DateTimeField(auto_now=True)
|
access = models.DateTimeField(auto_now=True)
|
||||||
document = models.ForeignKey(Document, related_name='accessed', on_delete=models.CASCADE)
|
document = models.ForeignKey(Document, related_name='accessed')
|
||||||
user = models.ForeignKey(User, null=True, related_name='accessed_documents', on_delete=models.CASCADE)
|
user = models.ForeignKey(User, null=True, related_name='accessed_documents')
|
||||||
accessed = models.IntegerField(default=0)
|
accessed = models.IntegerField(default=0)
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
|
|
@ -721,9 +721,10 @@ class Access(models.Model):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
if self.user:
|
if self.user:
|
||||||
return "%s/%s/%s" % (self.user, self.document, self.access)
|
return u"%s/%s/%s" % (self.user, self.document, self.access)
|
||||||
return "%s/%s" % (self.item, self.access)
|
return u"%s/%s" % (self.item, self.access)
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class Facet(models.Model):
|
class Facet(models.Model):
|
||||||
'''
|
'''
|
||||||
used for keys that can have multiple values like people, languages etc.
|
used for keys that can have multiple values like people, languages etc.
|
||||||
|
|
@ -734,13 +735,13 @@ class Facet(models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ("document", "key", "value")
|
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)
|
key = models.CharField(max_length=200, db_index=True)
|
||||||
value = models.CharField(max_length=1000, db_index=True)
|
value = models.CharField(max_length=1000, db_index=True)
|
||||||
sortvalue = models.CharField(max_length=1000, db_index=True)
|
sortvalue = models.CharField(max_length=1000, db_index=True)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "%s=%s" % (self.key, self.value)
|
return u"%s=%s" % (self.key, self.value)
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
if not self.sortvalue:
|
if not self.sortvalue:
|
||||||
|
|
@ -759,17 +760,18 @@ for key in settings.CONFIG['itemKeys']:
|
||||||
if key.get('sortType') == 'person':
|
if key.get('sortType') == 'person':
|
||||||
Document.person_keys.append(key['id'])
|
Document.person_keys.append(key['id'])
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class Find(models.Model):
|
class Find(models.Model):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ('document', 'key')
|
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)
|
key = models.CharField(max_length=200, db_index=True)
|
||||||
value = models.TextField(blank=True, db_index=settings.DB_GIN_TRGM)
|
value = models.TextField(blank=True, db_index=settings.DB_GIN_TRGM)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return '%s=%s' % (self.key, self.value)
|
return u'%s=%s' % (self.key, self.value)
|
||||||
|
|
||||||
'''
|
'''
|
||||||
Sort
|
Sort
|
||||||
|
|
@ -777,7 +779,7 @@ table constructed based on info in settings.CONFIG['documentKeys']
|
||||||
'''
|
'''
|
||||||
attrs = {
|
attrs = {
|
||||||
'__module__': 'document.models',
|
'__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),
|
'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'])):
|
for key in list(filter(lambda k: k.get('sort', False) or k['type'] in ('integer', 'time', 'float', 'date', 'enum'), settings.CONFIG['documentKeys'])):
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
from django.db import connection, transaction
|
from django.db import connection, transaction
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, print_function, absolute_import
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
from glob import glob
|
from glob import glob
|
||||||
import unicodedata
|
import unicodedata
|
||||||
|
|
||||||
|
from six import string_types
|
||||||
import ox
|
import ox
|
||||||
from ox.utils import json
|
from ox.utils import json
|
||||||
from oxdjango.api import actions
|
from oxdjango.api import actions
|
||||||
|
|
@ -69,7 +71,7 @@ def addDocument(request, data):
|
||||||
else:
|
else:
|
||||||
ids = [data['id']]
|
ids = [data['id']]
|
||||||
if 'item' in data:
|
if 'item' in data:
|
||||||
if isinstance(data['item'], str):
|
if isinstance(data['item'], string_types):
|
||||||
item = Item.objects.get(public_id=data['item'])
|
item = Item.objects.get(public_id=data['item'])
|
||||||
if item.editable(request.user):
|
if item.editable(request.user):
|
||||||
for id in ids:
|
for id in ids:
|
||||||
|
|
@ -86,7 +88,7 @@ def addDocument(request, data):
|
||||||
document.add(item)
|
document.add(item)
|
||||||
add_changelog(request, data, data['item'])
|
add_changelog(request, data, data['item'])
|
||||||
elif 'entity' in data:
|
elif 'entity' in data:
|
||||||
if isinstance(data['entity'], str):
|
if isinstance(data['entity'], string_types):
|
||||||
entity = Entity.get(data['entity'])
|
entity = Entity.get(data['entity'])
|
||||||
if entity.editable(request.user):
|
if entity.editable(request.user):
|
||||||
for id in ids:
|
for id in ids:
|
||||||
|
|
|
||||||
|
|
@ -124,7 +124,7 @@ class CollectionManager(Manager):
|
||||||
if conditions:
|
if conditions:
|
||||||
qs = qs.filter(conditions)
|
qs = qs.filter(conditions)
|
||||||
|
|
||||||
if user.is_anonymous:
|
if user.is_anonymous():
|
||||||
qs = qs.filter(Q(status='public') | Q(status='featured'))
|
qs = qs.filter(Q(status='public') | Q(status='featured'))
|
||||||
else:
|
else:
|
||||||
qs = qs.filter(Q(status='public') | Q(status='featured') | Q(user=user))
|
qs = qs.filter(Q(status='public') | Q(status='featured') | Q(user=user))
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, print_function, absolute_import
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
|
@ -9,6 +10,7 @@ from django.db import models
|
||||||
from django.db.models import Max
|
from django.db.models import Max
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.utils.encoding import python_2_unicode_compatible
|
||||||
from oxdjango.fields import JSONField
|
from oxdjango.fields import JSONField
|
||||||
|
|
||||||
import ox
|
import ox
|
||||||
|
|
@ -34,6 +36,7 @@ def get_collectionview():
|
||||||
def get_collectionsort():
|
def get_collectionsort():
|
||||||
return tuple(settings.CONFIG['user']['ui']['collectionSort'])
|
return tuple(settings.CONFIG['user']['ui']['collectionSort'])
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class Collection(models.Model):
|
class Collection(models.Model):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
@ -41,7 +44,7 @@ class Collection(models.Model):
|
||||||
|
|
||||||
created = models.DateTimeField(auto_now_add=True)
|
created = models.DateTimeField(auto_now_add=True)
|
||||||
modified = models.DateTimeField(auto_now=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')
|
groups = models.ManyToManyField(Group, blank=True, related_name='collections')
|
||||||
name = models.CharField(max_length=255)
|
name = models.CharField(max_length=255)
|
||||||
status = models.CharField(max_length=20, default='private')
|
status = models.CharField(max_length=20, default='private')
|
||||||
|
|
@ -55,7 +58,7 @@ class Collection(models.Model):
|
||||||
view = models.TextField(default=get_collectionview)
|
view = models.TextField(default=get_collectionview)
|
||||||
sort = JSONField(default=get_collectionsort, editable=False)
|
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?
|
#is through table still required?
|
||||||
documents = models.ManyToManyField('document.Document', related_name='collections',
|
documents = models.ManyToManyField('document.Document', related_name='collections',
|
||||||
|
|
@ -114,13 +117,13 @@ class Collection(models.Model):
|
||||||
return self.get_id()
|
return self.get_id()
|
||||||
|
|
||||||
def get_id(self):
|
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):
|
def accessible(self, user):
|
||||||
return self.user == user or self.status in ('public', 'featured')
|
return self.user == user or self.status in ('public', 'featured')
|
||||||
|
|
||||||
def editable(self, user):
|
def editable(self, user):
|
||||||
if not user or user.is_anonymous:
|
if not user or user.is_anonymous():
|
||||||
return False
|
return False
|
||||||
if self.user == user or \
|
if self.user == user or \
|
||||||
self.groups.filter(id__in=user.groups.all()).count() > 0 or \
|
self.groups.filter(id__in=user.groups.all()).count() > 0 or \
|
||||||
|
|
@ -248,7 +251,7 @@ class Collection(models.Model):
|
||||||
elif key == 'subscribers':
|
elif key == 'subscribers':
|
||||||
response[key] = self.subscribed_users.all().count()
|
response[key] = self.subscribed_users.all().count()
|
||||||
elif key == 'subscribed':
|
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()
|
response[key] = self.subscribed_users.filter(id=user.id).exists()
|
||||||
else:
|
else:
|
||||||
response[key] = getattr(self, {
|
response[key] = getattr(self, {
|
||||||
|
|
@ -315,26 +318,28 @@ class Collection(models.Model):
|
||||||
path = source
|
path = source
|
||||||
return path
|
return path
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class CollectionDocument(models.Model):
|
class CollectionDocument(models.Model):
|
||||||
created = models.DateTimeField(auto_now_add=True)
|
created = models.DateTimeField(auto_now_add=True)
|
||||||
modified = models.DateTimeField(auto_now=True)
|
modified = models.DateTimeField(auto_now=True)
|
||||||
collection = models.ForeignKey(Collection, on_delete=models.CASCADE)
|
collection = models.ForeignKey(Collection)
|
||||||
index = models.IntegerField(default=0)
|
index = models.IntegerField(default=0)
|
||||||
document = models.ForeignKey('document.Document', on_delete=models.CASCADE)
|
document = models.ForeignKey('document.Document')
|
||||||
|
|
||||||
def __str__(self):
|
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 Position(models.Model):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ("user", "collection", "section")
|
unique_together = ("user", "collection", "section")
|
||||||
|
|
||||||
collection = models.ForeignKey(Collection, related_name='position', on_delete=models.CASCADE)
|
collection = models.ForeignKey(Collection, related_name='position')
|
||||||
user = models.ForeignKey(User, related_name='collection_positions', on_delete=models.CASCADE)
|
user = models.ForeignKey(User, related_name='collection_positions')
|
||||||
section = models.CharField(max_length=255)
|
section = models.CharField(max_length=255)
|
||||||
position = models.IntegerField(default=0)
|
position = models.IntegerField(default=0)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return '%s/%s/%s' % (self.section, self.position, self.collection)
|
return u'%s/%s/%s' % (self.section, self.position, self.collection)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, print_function, absolute_import
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
|
@ -74,7 +75,7 @@ def findCollections(request, data):
|
||||||
query = parse_query(data, request.user)
|
query = parse_query(data, request.user)
|
||||||
|
|
||||||
#order
|
#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):
|
def is_featured_condition(x):
|
||||||
return x['key'] == 'status' and \
|
return x['key'] == 'status' and \
|
||||||
|
|
@ -88,7 +89,7 @@ def findCollections(request, data):
|
||||||
|
|
||||||
if is_section_request:
|
if is_section_request:
|
||||||
qs = query['qs']
|
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.filter(position__in=models.Position.objects.filter(user=request.user))
|
||||||
qs = qs.order_by('position__position')
|
qs = qs.order_by('position__position')
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -124,7 +124,7 @@ class EditManager(Manager):
|
||||||
if conditions:
|
if conditions:
|
||||||
qs = qs.filter(conditions)
|
qs = qs.filter(conditions)
|
||||||
|
|
||||||
if user.is_anonymous:
|
if user.is_anonymous():
|
||||||
qs = qs.filter(Q(status='public') | Q(status='featured'))
|
qs = qs.filter(Q(status='public') | Q(status='featured'))
|
||||||
else:
|
else:
|
||||||
qs = qs.filter(Q(status='public') | Q(status='featured') | Q(user=user))
|
qs = qs.filter(Q(status='public') | Q(status='featured') | Q(user=user))
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,21 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- 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 re
|
||||||
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
|
from glob import glob
|
||||||
import subprocess
|
import subprocess
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
|
from six.moves.urllib.parse import quote
|
||||||
import ox
|
import ox
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import models, transaction
|
from django.db import models, transaction
|
||||||
from django.db.models import Max
|
from django.db.models import Max
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
|
|
||||||
|
from django.utils.encoding import python_2_unicode_compatible
|
||||||
from oxdjango.fields import JSONField
|
from oxdjango.fields import JSONField
|
||||||
|
|
||||||
from annotation.models import Annotation
|
from annotation.models import Annotation
|
||||||
|
|
@ -33,6 +35,7 @@ User = get_user_model()
|
||||||
def get_path(f, x): return f.path(x)
|
def get_path(f, x): return f.path(x)
|
||||||
def get_icon_path(f, x): return get_path(f, 'icon.jpg')
|
def get_icon_path(f, x): return get_path(f, 'icon.jpg')
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class Edit(models.Model):
|
class Edit(models.Model):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
@ -42,7 +45,7 @@ class Edit(models.Model):
|
||||||
|
|
||||||
created = models.DateTimeField(auto_now_add=True)
|
created = models.DateTimeField(auto_now_add=True)
|
||||||
modified = models.DateTimeField(auto_now=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')
|
groups = models.ManyToManyField(Group, blank=True, related_name='edits')
|
||||||
name = models.CharField(max_length=255)
|
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)
|
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')
|
subscribed_users = models.ManyToManyField(User, related_name='subscribed_edits')
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return '%s (%s)' % (self.name, self.user)
|
return u'%s (%s)' % (self.name, self.user)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get(cls, id):
|
def get(cls, id):
|
||||||
|
|
@ -70,7 +73,7 @@ class Edit(models.Model):
|
||||||
return cls.objects.get(user__username=username, name=name)
|
return cls.objects.get(user__username=username, name=name)
|
||||||
|
|
||||||
def get_id(self):
|
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):
|
def get_absolute_url(self):
|
||||||
return ('/edits/%s' % quote(self.get_id())).replace('%3A', ':')
|
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')
|
return self.user == user or self.status in ('public', 'featured')
|
||||||
|
|
||||||
def editable(self, user):
|
def editable(self, user):
|
||||||
if not user or user.is_anonymous:
|
if not user or user.is_anonymous():
|
||||||
return False
|
return False
|
||||||
if self.user == user or \
|
if self.user == user or \
|
||||||
self.groups.filter(id__in=user.groups.all()).count() > 0 or \
|
self.groups.filter(id__in=user.groups.all()).count() > 0 or \
|
||||||
|
|
@ -400,7 +403,7 @@ class Edit(models.Model):
|
||||||
elif key == 'subscribers':
|
elif key == 'subscribers':
|
||||||
response[key] = self.subscribed_users.all().count()
|
response[key] = self.subscribed_users.all().count()
|
||||||
elif key == 'subscribed':
|
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()
|
response[key] = self.subscribed_users.filter(id=user.id).exists()
|
||||||
elif hasattr(self, _map.get(key, key)):
|
elif hasattr(self, _map.get(key, key)):
|
||||||
response[key] = getattr(self, _map.get(key, key))
|
response[key] = getattr(self, _map.get(key, key))
|
||||||
|
|
@ -427,14 +430,15 @@ class Edit(models.Model):
|
||||||
#p.wait()
|
#p.wait()
|
||||||
shutil.rmtree(tmp)
|
shutil.rmtree(tmp)
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class Clip(models.Model):
|
class Clip(models.Model):
|
||||||
created = models.DateTimeField(auto_now_add=True)
|
created = models.DateTimeField(auto_now_add=True)
|
||||||
modified = models.DateTimeField(auto_now=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)
|
index = models.IntegerField(default=0)
|
||||||
item = models.ForeignKey(Item, 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', on_delete=models.CASCADE)
|
annotation = models.ForeignKey(Annotation, null=True, default=None, related_name='editclip')
|
||||||
start = models.FloatField(default=0)
|
start = models.FloatField(default=0)
|
||||||
end = models.FloatField(default=0)
|
end = models.FloatField(default=0)
|
||||||
duration = models.FloatField(default=0)
|
duration = models.FloatField(default=0)
|
||||||
|
|
@ -450,8 +454,8 @@ class Clip(models.Model):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
if self.annotation:
|
if self.annotation:
|
||||||
return '%s' % self.annotation.public_id
|
return u'%s' % self.annotation.public_id
|
||||||
return '%s/%0.3f-%0.3f' % (self.item.public_id, self.start, self.end)
|
return u'%s/%0.3f-%0.3f' % (self.item.public_id, self.start, self.end)
|
||||||
|
|
||||||
def get_id(self):
|
def get_id(self):
|
||||||
return ox.toAZ(self.id)
|
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)
|
return clip.models.get_layers(item=item, interval=(start, end), user=user)
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class Position(models.Model):
|
class Position(models.Model):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ("user", "edit", "section")
|
unique_together = ("user", "edit", "section")
|
||||||
|
|
||||||
edit = models.ForeignKey(Edit, related_name='position', on_delete=models.CASCADE)
|
edit = models.ForeignKey(Edit, related_name='position')
|
||||||
user = models.ForeignKey(User, related_name='edit_position', on_delete=models.CASCADE)
|
user = models.ForeignKey(User, related_name='edit_position')
|
||||||
section = models.CharField(max_length=255)
|
section = models.CharField(max_length=255)
|
||||||
position = models.IntegerField(default=0)
|
position = models.IntegerField(default=0)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return '%s/%s/%s' % (self.section, self.position, self.edit)
|
return u'%s/%s/%s' % (self.section, self.position, self.edit)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, print_function, absolute_import
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
|
@ -403,8 +404,7 @@ def findEdits(request, data):
|
||||||
query = parse_query(data, request.user)
|
query = parse_query(data, request.user)
|
||||||
|
|
||||||
#order
|
#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):
|
def is_featured_condition(x):
|
||||||
return x['key'] == 'status' and \
|
return x['key'] == 'status' and \
|
||||||
x['value'] == 'featured' and \
|
x['value'] == 'featured' and \
|
||||||
|
|
@ -414,7 +414,7 @@ def findEdits(request, data):
|
||||||
|
|
||||||
if is_section_request:
|
if is_section_request:
|
||||||
qs = query['qs']
|
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.filter(position__in=models.Position.objects.filter(user=request.user))
|
||||||
qs = qs.order_by('position__position')
|
qs = qs.order_by('position__position')
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,20 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, print_function, absolute_import
|
||||||
|
|
||||||
from glob import glob
|
|
||||||
from urllib.parse import quote, unquote
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
from glob import glob
|
||||||
import unicodedata
|
import unicodedata
|
||||||
|
|
||||||
|
from six import string_types
|
||||||
|
from six.moves.urllib.parse import quote, unquote
|
||||||
from django.db import models, transaction
|
from django.db import models, transaction
|
||||||
from django.db.models import Max
|
from django.db.models import Max
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
|
|
||||||
from django.db.models.signals import pre_delete, post_init
|
from django.db.models.signals import pre_delete, post_init
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.utils.encoding import python_2_unicode_compatible
|
||||||
from oxdjango.fields import JSONField
|
from oxdjango.fields import JSONField
|
||||||
|
|
||||||
import ox
|
import ox
|
||||||
|
|
@ -25,6 +28,7 @@ from . import managers
|
||||||
|
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class Entity(models.Model):
|
class Entity(models.Model):
|
||||||
class ValueError(ValueError):
|
class ValueError(ValueError):
|
||||||
'''Raised if a field name or value is invalid (based on the "entities"
|
'''Raised if a field name or value is invalid (based on the "entities"
|
||||||
|
|
@ -34,7 +38,7 @@ class Entity(models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ("type", "name")
|
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)
|
created = models.DateTimeField(auto_now_add=True)
|
||||||
modified = models.DateTimeField(auto_now=True)
|
modified = models.DateTimeField(auto_now=True)
|
||||||
|
|
@ -60,7 +64,7 @@ class Entity(models.Model):
|
||||||
self.name = self.name.decode('utf-8')
|
self.name = self.name.decode('utf-8')
|
||||||
self.name_sort = get_name_sort(self.name)[:255].lower()
|
self.name_sort = get_name_sort(self.name)[:255].lower()
|
||||||
else:
|
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 = '||' + '||'.join((self.name,) + self.alternativeNames) + '||'
|
||||||
self.name_find = self.name_find.lower()
|
self.name_find = self.name_find.lower()
|
||||||
super(Entity, self).save(*args, **kwargs)
|
super(Entity, self).save(*args, **kwargs)
|
||||||
|
|
@ -84,11 +88,11 @@ class Entity(models.Model):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_by_name(cls, name, type):
|
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
|
@classmethod
|
||||||
def get_or_create(model, name):
|
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:
|
if qs.count() == 0:
|
||||||
instance = model(name=name)
|
instance = model(name=name)
|
||||||
instance.save()
|
instance.save()
|
||||||
|
|
@ -113,7 +117,7 @@ class Entity(models.Model):
|
||||||
DocumentProperties.objects.filter(document=document, entity=self).delete()
|
DocumentProperties.objects.filter(document=document, entity=self).delete()
|
||||||
|
|
||||||
def editable(self, user, item=None):
|
def editable(self, user, item=None):
|
||||||
if not user or user.is_anonymous:
|
if not user or user.is_anonymous():
|
||||||
return False
|
return False
|
||||||
if user.is_staff or \
|
if user.is_staff or \
|
||||||
user.profile.capability('canEditEntities') == True or \
|
user.profile.capability('canEditEntities') == True or \
|
||||||
|
|
@ -136,7 +140,7 @@ class Entity(models.Model):
|
||||||
data['name'] = "Unnamed"
|
data['name'] = "Unnamed"
|
||||||
name = data['name']
|
name = data['name']
|
||||||
n = 1
|
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
|
n += 1
|
||||||
name = data['name'] + ' [%d]' % n
|
name = data['name'] + ' [%d]' % n
|
||||||
self.name = name
|
self.name = name
|
||||||
|
|
@ -151,7 +155,7 @@ class Entity(models.Model):
|
||||||
name_ = name
|
name_ = name
|
||||||
n = 1
|
n = 1
|
||||||
while name in used_names or \
|
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
|
n += 1
|
||||||
name = name_ + ' [%d]' % n
|
name = name_ + ' [%d]' % n
|
||||||
names.append(name)
|
names.append(name)
|
||||||
|
|
@ -187,7 +191,7 @@ class Entity(models.Model):
|
||||||
.delete()
|
.delete()
|
||||||
else:
|
else:
|
||||||
#FIXME: more data validation
|
#FIXME: more data validation
|
||||||
if isinstance(data[key], str):
|
if isinstance(data[key], string_types):
|
||||||
self.data[key] = ox.sanitize_html(data[key])
|
self.data[key] = ox.sanitize_html(data[key])
|
||||||
else:
|
else:
|
||||||
self.data[key] = data[key]
|
self.data[key] = data[key]
|
||||||
|
|
@ -264,7 +268,7 @@ class Entity(models.Model):
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def annotation_value(self):
|
def annotation_value(self):
|
||||||
#return '<a href="/entities/%s">%s</a>' % (self.get_id(), ox.escape_html(self.name))
|
#return u'<a href="/entities/%s">%s</a>' % (self.get_id(), ox.escape_html(self.name))
|
||||||
return ox.escape_html(self.name)
|
return ox.escape_html(self.name)
|
||||||
|
|
||||||
def update_find(self):
|
def update_find(self):
|
||||||
|
|
@ -274,7 +278,7 @@ class Entity(models.Model):
|
||||||
f, created = Find.objects.get_or_create(entity=self, key=key)
|
f, created = Find.objects.get_or_create(entity=self, key=key)
|
||||||
if isinstance(value, bool):
|
if isinstance(value, bool):
|
||||||
value = value and 'true' or 'false'
|
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 = ox.decode_html(ox.strip_tags(value.strip()))
|
||||||
value = unicodedata.normalize('NFKD', value).lower()
|
value = unicodedata.normalize('NFKD', value).lower()
|
||||||
f.value = value
|
f.value = value
|
||||||
|
|
@ -288,10 +292,10 @@ class Entity(models.Model):
|
||||||
for key in entity['keys']:
|
for key in entity['keys']:
|
||||||
value = self.data.get(key['id'])
|
value = self.data.get(key['id'])
|
||||||
if isinstance(value, list):
|
if isinstance(value, list):
|
||||||
value = '\n'.join(value)
|
value = u'\n'.join(value)
|
||||||
save(key['id'], value)
|
save(key['id'], value)
|
||||||
ids.append(key['id'])
|
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()
|
self.find.exclude(key__in=ids).delete()
|
||||||
|
|
||||||
def update_matches(self):
|
def update_matches(self):
|
||||||
|
|
@ -340,6 +344,7 @@ post_init.connect(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class DocumentProperties(models.Model):
|
class DocumentProperties(models.Model):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
@ -348,40 +353,42 @@ class DocumentProperties(models.Model):
|
||||||
created = models.DateTimeField(auto_now_add=True)
|
created = models.DateTimeField(auto_now_add=True)
|
||||||
modified = models.DateTimeField(auto_now=True)
|
modified = models.DateTimeField(auto_now=True)
|
||||||
|
|
||||||
document = models.ForeignKey(Document, related_name='documentproperties', on_delete=models.CASCADE)
|
document = models.ForeignKey(Document, related_name='documentproperties')
|
||||||
entity = models.ForeignKey(Entity, related_name='documentproperties', on_delete=models.CASCADE)
|
entity = models.ForeignKey(Entity, related_name='documentproperties')
|
||||||
index = models.IntegerField(default=0)
|
index = models.IntegerField(default=0)
|
||||||
data = JSONField(default=dict, editable=False)
|
data = JSONField(default=dict, editable=False)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "%r-%r" % (self.document, self.entity)
|
return u"%r-%r" % (self.document, self.entity)
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
|
|
||||||
super(DocumentProperties, self).save(*args, **kwargs)
|
super(DocumentProperties, self).save(*args, **kwargs)
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class Find(models.Model):
|
class Find(models.Model):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ("entity", "key")
|
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)
|
key = models.CharField(max_length=200, db_index=True)
|
||||||
value = models.TextField(blank=True, db_index=settings.DB_GIN_TRGM)
|
value = models.TextField(blank=True, db_index=settings.DB_GIN_TRGM)
|
||||||
|
|
||||||
def __str__(self):
|
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):
|
class Link(models.Model):
|
||||||
'''Models entity fields of type "entity".'''
|
'''Models entity fields of type "entity".'''
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ("source", "key", "target")
|
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)
|
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):
|
def __str__(self):
|
||||||
return "%s-[%s]->%s" % (self.source, self.key, self.target)
|
return u"%s-[%s]->%s" % (self.source, self.key, self.target)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, print_function, absolute_import
|
||||||
|
|
||||||
|
from six import string_types
|
||||||
import ox
|
import ox
|
||||||
from ox.utils import json
|
from ox.utils import json
|
||||||
from oxdjango.api import actions
|
from oxdjango.api import actions
|
||||||
|
|
@ -58,7 +60,7 @@ def addEntity(request, data):
|
||||||
for name in names:
|
for name in names:
|
||||||
name = ox.decode_html(name)
|
name = ox.decode_html(name)
|
||||||
if models.Entity.objects.filter(type=data['type'],
|
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
|
exists = True
|
||||||
existing_names.append(name)
|
existing_names.append(name)
|
||||||
if not exists:
|
if not exists:
|
||||||
|
|
@ -68,7 +70,7 @@ def addEntity(request, data):
|
||||||
for key in ('type', 'alternativeNames'):
|
for key in ('type', 'alternativeNames'):
|
||||||
if key in data and data[key]:
|
if key in data and data[key]:
|
||||||
value = data[key]
|
value = data[key]
|
||||||
if isinstance(value, str):
|
if isinstance(value, string_types):
|
||||||
value = ox.escape_html(value)
|
value = ox.escape_html(value)
|
||||||
if key == 'alternativeNames':
|
if key == 'alternativeNames':
|
||||||
value = tuple([ox.escape_html(v) for v in value])
|
value = tuple([ox.escape_html(v) for v in value])
|
||||||
|
|
@ -85,7 +87,7 @@ def addEntity(request, data):
|
||||||
type = data['type']
|
type = data['type']
|
||||||
name = 'Unnamed'
|
name = 'Unnamed'
|
||||||
num = 1
|
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
|
num += 1
|
||||||
name = 'Unnamed [%d]' % num
|
name = 'Unnamed [%d]' % num
|
||||||
entity = models.Entity(name=name, type=type)
|
entity = models.Entity(name=name, type=type)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, print_function, absolute_import
|
||||||
|
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import unicodedata
|
import unicodedata
|
||||||
|
|
||||||
|
from six import string_types
|
||||||
from django.db.models import Q, Manager
|
from django.db.models import Q, Manager
|
||||||
|
|
||||||
from oxdjango.query import QuerySet
|
from oxdjango.query import QuerySet
|
||||||
|
|
@ -29,7 +30,7 @@ def parseCondition(condition, user):
|
||||||
|
|
||||||
key = k + get_operator(op, 'istr')
|
key = k + get_operator(op, 'istr')
|
||||||
key = str(key)
|
key = str(key)
|
||||||
if isinstance(v, str):
|
if isinstance(v, string_types):
|
||||||
v = unicodedata.normalize('NFKD', v).lower()
|
v = unicodedata.normalize('NFKD', v).lower()
|
||||||
if exclude:
|
if exclude:
|
||||||
q = ~Q(**{k: v})
|
q = ~Q(**{k: v})
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, print_function, absolute_import
|
||||||
|
|
||||||
from django.db import models, transaction
|
from django.db import models, transaction
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
|
|
||||||
|
from django.utils.encoding import python_2_unicode_compatible
|
||||||
|
|
||||||
import ox
|
import ox
|
||||||
from oxdjango import fields
|
from oxdjango import fields
|
||||||
|
|
@ -17,6 +19,7 @@ from . import managers
|
||||||
|
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class Event(models.Model):
|
class Event(models.Model):
|
||||||
'''
|
'''
|
||||||
Events are events in time that can be once or recurring,
|
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)
|
modified = models.DateTimeField(auto_now=True)
|
||||||
defined = models.BooleanField(default=False)
|
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 = models.CharField(null=True, max_length=255, unique=True)
|
||||||
name_sort = models.CharField(null=True, max_length=255, db_index=True)
|
name_sort = models.CharField(null=True, max_length=255, db_index=True)
|
||||||
|
|
@ -63,7 +66,7 @@ class Event(models.Model):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_or_create(model, name):
|
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:
|
if qs.count() == 0:
|
||||||
instance = model(name=name)
|
instance = model(name=name)
|
||||||
instance.save()
|
instance.save()
|
||||||
|
|
@ -72,7 +75,7 @@ class Event(models.Model):
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
def editable(self, user):
|
def editable(self, user):
|
||||||
if user and not user.is_anonymous \
|
if user and not user.is_anonymous() \
|
||||||
and (not self.user or \
|
and (not self.user or \
|
||||||
self.user == user or \
|
self.user == user or \
|
||||||
user.profile.capability('canEditEvents')):
|
user.profile.capability('canEditEvents')):
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, print_function, absolute_import
|
||||||
|
|
||||||
from celery.task import task
|
from celery.task import task
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, print_function, absolute_import
|
||||||
|
|
||||||
from django.db.models import Count
|
from django.db.models import Count
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
|
from six import string_types
|
||||||
import ox
|
import ox
|
||||||
from ox.utils import json
|
from ox.utils import json
|
||||||
|
|
||||||
|
|
@ -35,7 +37,7 @@ def addEvent(request, data):
|
||||||
for name in names:
|
for name in names:
|
||||||
name = ox.decode_html(name)
|
name = ox.decode_html(name)
|
||||||
if models.Event.objects.filter(defined=True,
|
if models.Event.objects.filter(defined=True,
|
||||||
name_find__icontains='|%s|'%name).count() != 0:
|
name_find__icontains=u'|%s|'%name).count() != 0:
|
||||||
exists = True
|
exists = True
|
||||||
existing_names.append(name)
|
existing_names.append(name)
|
||||||
if not exists:
|
if not exists:
|
||||||
|
|
@ -46,7 +48,7 @@ def addEvent(request, data):
|
||||||
'type', 'alternativeNames'):
|
'type', 'alternativeNames'):
|
||||||
if key in data and data[key]:
|
if key in data and data[key]:
|
||||||
value = data[key]
|
value = data[key]
|
||||||
if isinstance(value, str):
|
if isinstance(value, string_types):
|
||||||
value = ox.escape_html(value)
|
value = ox.escape_html(value)
|
||||||
if key == 'alternativeNames':
|
if key == 'alternativeNames':
|
||||||
value = tuple([ox.escape_html(v) for v in value])
|
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', [])
|
names = [data.get('name', event.name)] + data.get('alternativeNames', [])
|
||||||
for name in names:
|
for name in names:
|
||||||
if models.Event.objects.filter(defined=True,
|
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 = True
|
||||||
conflict_names.append(name)
|
conflict_names.append(name)
|
||||||
if not conflict:
|
if not conflict:
|
||||||
|
|
@ -100,7 +102,7 @@ def editEvent(request, data):
|
||||||
'type', 'alternativeNames'):
|
'type', 'alternativeNames'):
|
||||||
if key in data:
|
if key in data:
|
||||||
value = data[key]
|
value = data[key]
|
||||||
if isinstance(value, str):
|
if isinstance(value, string_types):
|
||||||
value = ox.escape_html(value)
|
value = ox.escape_html(value)
|
||||||
if key == 'alternativeNames':
|
if key == 'alternativeNames':
|
||||||
value = tuple([ox.escape_html(v) for v in value])
|
value = tuple([ox.escape_html(v) for v in value])
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.apps import AppConfig
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,13 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- 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 import models
|
||||||
from django.db.models import Max
|
from django.db.models import Max
|
||||||
from django.db.models.signals import pre_delete
|
from django.db.models.signals import pre_delete
|
||||||
|
from django.utils.encoding import python_2_unicode_compatible
|
||||||
from oxdjango.fields import JSONField
|
from oxdjango.fields import JSONField
|
||||||
|
|
||||||
import ox
|
import ox
|
||||||
|
|
@ -14,6 +17,7 @@ from edit.models import Edit
|
||||||
from documentcollection.models import Collection
|
from documentcollection.models import Collection
|
||||||
|
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class Item(models.Model):
|
class Item(models.Model):
|
||||||
created = models.DateTimeField(auto_now_add=True)
|
created = models.DateTimeField(auto_now_add=True)
|
||||||
modified = models.DateTimeField(auto_now=True)
|
modified = models.DateTimeField(auto_now=True)
|
||||||
|
|
@ -23,7 +27,7 @@ class Item(models.Model):
|
||||||
data = JSONField(default=dict, editable=False)
|
data = JSONField(default=dict, editable=False)
|
||||||
|
|
||||||
def editable(self, user):
|
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):
|
def edit(self, data):
|
||||||
changed = False
|
changed = False
|
||||||
|
|
@ -42,7 +46,7 @@ class Item(models.Model):
|
||||||
len([d for d in data[key] if isinstance(d, int)]) == 4):
|
len([d for d in data[key] if isinstance(d, int)]) == 4):
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
if not isinstance(data[key], str):
|
if not isinstance(data[key], string_types):
|
||||||
return False
|
return False
|
||||||
self.data[key] = data[key]
|
self.data[key] = data[key]
|
||||||
if key == 'contentid' and self.data[key]:
|
if key == 'contentid' and self.data[key]:
|
||||||
|
|
@ -149,7 +153,7 @@ class Item(models.Model):
|
||||||
return j
|
return j
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "%s" % (self.get_id())
|
return u"%s" % (self.get_id())
|
||||||
|
|
||||||
def delete_item(type, contentid):
|
def delete_item(type, contentid):
|
||||||
for home in Item.objects.all():
|
for home in Item.objects.all():
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, print_function, absolute_import
|
||||||
|
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, with_statement
|
||||||
|
|
||||||
import ox
|
import ox
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
|
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
from django.db import connection, transaction
|
from django.db import connection, transaction
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
from django.db import connection, transaction
|
from django.db import connection, transaction
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
|
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
import os
|
import os
|
||||||
from glob import glob
|
from glob import glob
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
|
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
|
|
@ -43,12 +44,7 @@ class Command(BaseCommand):
|
||||||
(document.models.Find._meta.db_table, 'value'), # Document Find
|
(document.models.Find._meta.db_table, 'value'), # Document Find
|
||||||
):
|
):
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
contraints = connection.introspection.get_constraints(cursor, table)
|
indexes = connection.introspection.get_indexes(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']
|
|
||||||
}
|
|
||||||
drop = []
|
drop = []
|
||||||
if column in indexes:
|
if column in indexes:
|
||||||
for sql in (
|
for sql in (
|
||||||
|
|
@ -67,12 +63,7 @@ class Command(BaseCommand):
|
||||||
if options['debug']:
|
if options['debug']:
|
||||||
print(sql)
|
print(sql)
|
||||||
cursor.execute(sql)
|
cursor.execute(sql)
|
||||||
contraints = connection.introspection.get_constraints(cursor, table)
|
indexes = connection.introspection.get_indexes(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']
|
|
||||||
}
|
|
||||||
if column not in indexes:
|
if column not in indexes:
|
||||||
create_index("%s_%s_idx" % (table, column), table, column)
|
create_index("%s_%s_idx" % (table, column), table, column)
|
||||||
transaction.commit()
|
transaction.commit()
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
from django.db import connection, transaction
|
from django.db import connection, transaction
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
|
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
|
@ -37,10 +38,10 @@ class Command(BaseCommand):
|
||||||
versions = os.path.join(prefix, os.path.dirname(path), 'Versions')
|
versions = os.path.join(prefix, os.path.dirname(path), 'Versions')
|
||||||
s = f.streams.filter(source=None)[0]
|
s = f.streams.filter(source=None)[0]
|
||||||
basename = os.path.basename(path).rsplit('.', 1)[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)
|
link(s.media.path, target)
|
||||||
else:
|
else:
|
||||||
s = f.streams.filter(source=None)[0]
|
s = f.streams.filter(source=None)[0]
|
||||||
basename = path.rsplit('.', 1)[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)
|
link(s.media.path, target)
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, print_function, absolute_import
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import unicodedata
|
import unicodedata
|
||||||
|
|
||||||
|
from six import string_types
|
||||||
from django.db.models import Q, Manager
|
from django.db.models import Q, Manager
|
||||||
from django.conf import settings
|
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 == '':
|
if (not exclude and op == '=' or op in ('$', '^')) and v == '':
|
||||||
return Q()
|
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)
|
return Q(id=0)
|
||||||
elif k == 'oshash':
|
elif k == 'oshash':
|
||||||
return Q(files__oshash=v)
|
return Q(files__oshash=v)
|
||||||
|
|
@ -98,7 +100,7 @@ def parseCondition(condition, user, owner=None):
|
||||||
q = ~q
|
q = ~q
|
||||||
return q
|
return q
|
||||||
elif k in ('canplayvideo', 'canplayclips'):
|
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'][{
|
allowed_level = settings.CONFIG['capabilities'][{
|
||||||
'canplayvideo': 'canPlayVideo',
|
'canplayvideo': 'canPlayVideo',
|
||||||
'canplayclips': 'canPlayClips'
|
'canplayclips': 'canPlayClips'
|
||||||
|
|
@ -122,7 +124,7 @@ def parseCondition(condition, user, owner=None):
|
||||||
else:
|
else:
|
||||||
value_key = k
|
value_key = k
|
||||||
if not k.startswith('public_id'):
|
if not k.startswith('public_id'):
|
||||||
if isinstance(v, str):
|
if isinstance(v, string_types):
|
||||||
v = unicodedata.normalize('NFKD', v).lower()
|
v = unicodedata.normalize('NFKD', v).lower()
|
||||||
if k in facet_keys:
|
if k in facet_keys:
|
||||||
in_find = False
|
in_find = False
|
||||||
|
|
@ -247,7 +249,7 @@ class ItemManager(Manager):
|
||||||
if l != "*":
|
if l != "*":
|
||||||
l = l.split(":")
|
l = l.split(":")
|
||||||
only_public = True
|
only_public = True
|
||||||
if not user.is_anonymous:
|
if not user.is_anonymous():
|
||||||
if len(l) == 1:
|
if len(l) == 1:
|
||||||
l = [user.username] + l
|
l = [user.username] + l
|
||||||
if user.username == l[0]:
|
if user.username == l[0]:
|
||||||
|
|
@ -303,7 +305,7 @@ class ItemManager(Manager):
|
||||||
qs = qs.distinct()
|
qs = qs.distinct()
|
||||||
|
|
||||||
#anonymous can only see public items
|
#anonymous can only see public items
|
||||||
if not user or user.is_anonymous:
|
if not user or user.is_anonymous():
|
||||||
level = 'guest'
|
level = 'guest'
|
||||||
allowed_level = settings.CONFIG['capabilities']['canSeeItem'][level]
|
allowed_level = settings.CONFIG['capabilities']['canSeeItem'][level]
|
||||||
qs = qs.filter(level__lte=allowed_level)
|
qs = qs.filter(level__lte=allowed_level)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, print_function, absolute_import
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
|
@ -10,7 +11,9 @@ import unicodedata
|
||||||
import uuid
|
import uuid
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from glob import glob
|
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.conf import settings
|
||||||
from django.contrib.auth import get_user_model
|
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 import models, transaction, connection
|
||||||
from django.db.models import Q, Sum, Max
|
from django.db.models import Q, Sum, Max
|
||||||
from django.db.models.signals import pre_delete
|
from django.db.models.signals import pre_delete
|
||||||
|
from django.utils.encoding import python_2_unicode_compatible
|
||||||
from django.utils import datetime_safe
|
from django.utils import datetime_safe
|
||||||
|
|
||||||
import ox
|
import ox
|
||||||
|
|
@ -44,6 +48,8 @@ import archive.models
|
||||||
|
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
|
|
||||||
|
if not PY2:
|
||||||
|
unicode = str
|
||||||
|
|
||||||
def get_id(info):
|
def get_id(info):
|
||||||
q = Item.objects.all()
|
q = Item.objects.all()
|
||||||
|
|
@ -158,11 +164,12 @@ def get_poster_path(f, x):
|
||||||
def get_torrent_path(f, x):
|
def get_torrent_path(f, x):
|
||||||
return get_path(f, 'torrent.torrent')
|
return get_path(f, 'torrent.torrent')
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class Item(models.Model):
|
class Item(models.Model):
|
||||||
created = models.DateTimeField(auto_now_add=True)
|
created = models.DateTimeField(auto_now_add=True)
|
||||||
modified = models.DateTimeField(auto_now=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')
|
groups = models.ManyToManyField(Group, blank=True, related_name='items')
|
||||||
|
|
||||||
# while metadata is updated, files are set to rendered=False
|
# while metadata is updated, files are set to rendered=False
|
||||||
|
|
@ -214,7 +221,7 @@ class Item(models.Model):
|
||||||
return default
|
return default
|
||||||
|
|
||||||
def access(self, user):
|
def access(self, user):
|
||||||
if user.is_anonymous:
|
if user.is_anonymous():
|
||||||
level = 'guest'
|
level = 'guest'
|
||||||
else:
|
else:
|
||||||
level = user.profile.get_level()
|
level = user.profile.get_level()
|
||||||
|
|
@ -229,7 +236,7 @@ class Item(models.Model):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def editable(self, user):
|
def editable(self, user):
|
||||||
if user.is_anonymous:
|
if user.is_anonymous():
|
||||||
return False
|
return False
|
||||||
if user.profile.capability('canEditMetadata') or \
|
if user.profile.capability('canEditMetadata') or \
|
||||||
user.is_staff 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]]
|
self.data[key] = [ox.escape_html(t) for t in data[key]]
|
||||||
elif key in ('episodeTitle', 'seriesTitle', 'episodeDirector', 'seriesYear'):
|
elif key in ('episodeTitle', 'seriesTitle', 'episodeDirector', 'seriesYear'):
|
||||||
self.data[key] = ox.escape_html(data[key])
|
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])
|
self.data[key] = ox.escape_html(data[key])
|
||||||
elif isinstance(data[key], list):
|
elif isinstance(data[key], list):
|
||||||
def cleanup(i):
|
def cleanup(i):
|
||||||
if isinstance(i, str):
|
if isinstance(i, string_types):
|
||||||
i = ox.escape_html(i)
|
i = ox.escape_html(i)
|
||||||
return i
|
return i
|
||||||
self.data[key] = [cleanup(i) for i in data[key]]
|
self.data[key] = [cleanup(i) for i in data[key]]
|
||||||
|
|
@ -325,7 +332,7 @@ class Item(models.Model):
|
||||||
if c:
|
if c:
|
||||||
for t in list(c):
|
for t in list(c):
|
||||||
if c[t]:
|
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]]
|
c[t] = [{'id': i, 'title': None} for i in c[t]]
|
||||||
ids = [i['id'] for i in c[t]]
|
ids = [i['id'] for i in c[t]]
|
||||||
known = {}
|
known = {}
|
||||||
|
|
@ -343,10 +350,10 @@ class Item(models.Model):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
year = self.get('year')
|
year = self.get('year')
|
||||||
if 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:
|
else:
|
||||||
string = self.get('title', 'Untitled')
|
string = self.get('title', u'Untitled')
|
||||||
return '[%s] %s' % (self.public_id, string)
|
return u'[%s] %s' % (self.public_id, string)
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return '/%s' % self.public_id
|
return '/%s' % self.public_id
|
||||||
|
|
@ -393,7 +400,7 @@ class Item(models.Model):
|
||||||
title = self.get(key, 'Untitled')
|
title = self.get(key, 'Untitled')
|
||||||
while q.count() != 0:
|
while q.count() != 0:
|
||||||
n += 1
|
n += 1
|
||||||
self.data[key] = '%s [%d]' % (title, n)
|
self.data[key] = u'%s [%d]' % (title, n)
|
||||||
oxdbId = self.oxdb_id()
|
oxdbId = self.oxdb_id()
|
||||||
q = Item.objects.filter(oxdbId=oxdbId).exclude(id=self.id)
|
q = Item.objects.filter(oxdbId=oxdbId).exclude(id=self.id)
|
||||||
self.oxdbId = oxdbId
|
self.oxdbId = oxdbId
|
||||||
|
|
@ -622,7 +629,7 @@ class Item(models.Model):
|
||||||
if value:
|
if value:
|
||||||
i[key] = 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']]
|
i['cast'] = [i['cast']]
|
||||||
if 'cast' in i and isinstance(i['cast'][0], list):
|
if 'cast' in i and isinstance(i['cast'][0], list):
|
||||||
i['cast'] = [{'actor': x[0], 'character': x[1]} for x in i['cast']]
|
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)
|
f, created = ItemFind.objects.get_or_create(item=self, key=key)
|
||||||
if isinstance(value, bool):
|
if isinstance(value, bool):
|
||||||
value = value and 'true' or 'false'
|
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 = ox.decode_html(ox.strip_tags(value.strip()))
|
||||||
value = unicodedata.normalize('NFKD', value).lower()
|
value = unicodedata.normalize('NFKD', value).lower()
|
||||||
f.value = value
|
f.value = value
|
||||||
|
|
@ -814,7 +821,7 @@ class Item(models.Model):
|
||||||
for key in settings.CONFIG['itemKeys']:
|
for key in settings.CONFIG['itemKeys']:
|
||||||
i = key['id']
|
i = key['id']
|
||||||
if i == 'title':
|
if i == 'title':
|
||||||
save(i, '\n'.join(get_titles()))
|
save(i, u'\n'.join(get_titles()))
|
||||||
elif i == 'rightslevel':
|
elif i == 'rightslevel':
|
||||||
save(i, self.level)
|
save(i, self.level)
|
||||||
elif i == 'filename':
|
elif i == 'filename':
|
||||||
|
|
@ -823,17 +830,17 @@ class Item(models.Model):
|
||||||
qs = Annotation.objects.filter(item=self)
|
qs = Annotation.objects.filter(item=self)
|
||||||
qs = qs.filter(layer__in=Annotation.public_layers()).exclude(findvalue=None)
|
qs = qs.filter(layer__in=Annotation.public_layers()).exclude(findvalue=None)
|
||||||
qs = qs.order_by('start')
|
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':
|
elif key['type'] == 'layer':
|
||||||
qs = Annotation.objects.filter(item=self).exclude(findvalue=None)
|
qs = Annotation.objects.filter(item=self).exclude(findvalue=None)
|
||||||
qs = qs.filter(layer=i)
|
qs = qs.filter(layer=i)
|
||||||
qs = qs.order_by('start')
|
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)
|
layer_keys.append(i)
|
||||||
elif i != '*' and i not in self.facet_keys:
|
elif i != '*' and i not in self.facet_keys:
|
||||||
value = self.get(i)
|
value = self.get(i)
|
||||||
if isinstance(value, list):
|
if isinstance(value, list):
|
||||||
value = '\n'.join(value)
|
value = u'\n'.join(value)
|
||||||
save(i, value)
|
save(i, value)
|
||||||
|
|
||||||
for key in self.facet_keys:
|
for key in self.facet_keys:
|
||||||
|
|
@ -904,15 +911,15 @@ class Item(models.Model):
|
||||||
s = ItemSort(item=self)
|
s = ItemSort(item=self)
|
||||||
|
|
||||||
def sortNames(values):
|
def sortNames(values):
|
||||||
sort_value = ''
|
sort_value = u''
|
||||||
if values:
|
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:
|
if not sort_value:
|
||||||
sort_value = ''
|
sort_value = u''
|
||||||
return sort_value.lower()
|
return sort_value.lower()
|
||||||
|
|
||||||
def set_value(s, name, value):
|
def set_value(s, name, value):
|
||||||
if isinstance(value, str):
|
if isinstance(value, string_types):
|
||||||
value = ox.decode_html(value.lower())
|
value = ox.decode_html(value.lower())
|
||||||
if not value:
|
if not value:
|
||||||
value = None
|
value = None
|
||||||
|
|
@ -1012,7 +1019,7 @@ class Item(models.Model):
|
||||||
sort_type = sort_type[0]
|
sort_type = sort_type[0]
|
||||||
if name not in self.base_keys:
|
if name not in self.base_keys:
|
||||||
if sort_type == 'title':
|
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]
|
value = utils.sort_title(value)[:955]
|
||||||
set_value(s, name, value)
|
set_value(s, name, value)
|
||||||
elif sort_type == 'person':
|
elif sort_type == 'person':
|
||||||
|
|
@ -1020,9 +1027,9 @@ class Item(models.Model):
|
||||||
value = utils.sort_string(value)[:955]
|
value = utils.sort_string(value)[:955]
|
||||||
set_value(s, name, value)
|
set_value(s, name, value)
|
||||||
elif sort_type == 'string':
|
elif sort_type == 'string':
|
||||||
value = self.get(source, '')
|
value = self.get(source, u'')
|
||||||
if isinstance(value, list):
|
if isinstance(value, list):
|
||||||
value = ','.join(value)
|
value = u','.join(value)
|
||||||
value = utils.sort_string(value)[:955]
|
value = utils.sort_string(value)[:955]
|
||||||
set_value(s, name, value)
|
set_value(s, name, value)
|
||||||
elif sort_type == 'words':
|
elif sort_type == 'words':
|
||||||
|
|
@ -1048,7 +1055,7 @@ class Item(models.Model):
|
||||||
set_value(s, name, value)
|
set_value(s, name, value)
|
||||||
elif sort_type == 'date':
|
elif sort_type == 'date':
|
||||||
value = value_ = self.get(source)
|
value = value_ = self.get(source)
|
||||||
if isinstance(value, str):
|
if isinstance(value, string_types):
|
||||||
value_ = None
|
value_ = None
|
||||||
for fmt in ('%Y-%m-%d', '%Y-%m', '%Y'):
|
for fmt in ('%Y-%m-%d', '%Y-%m', '%Y'):
|
||||||
try:
|
try:
|
||||||
|
|
@ -1087,7 +1094,7 @@ class Item(models.Model):
|
||||||
if not current_values:
|
if not current_values:
|
||||||
current_values = []
|
current_values = []
|
||||||
else:
|
else:
|
||||||
current_values = [str(current_values)]
|
current_values = [unicode(current_values)]
|
||||||
|
|
||||||
filter_map = utils.get_by_id(settings.CONFIG['itemKeys'], key).get('filterMap')
|
filter_map = utils.get_by_id(settings.CONFIG['itemKeys'], key).get('filterMap')
|
||||||
if filter_map:
|
if filter_map:
|
||||||
|
|
@ -1777,6 +1784,7 @@ for key in settings.CONFIG['itemKeys']:
|
||||||
if key.get('sortType') == 'person':
|
if key.get('sortType') == 'person':
|
||||||
Item.person_keys.append(key['id'])
|
Item.person_keys.append(key['id'])
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class ItemFind(models.Model):
|
class ItemFind(models.Model):
|
||||||
"""
|
"""
|
||||||
used to find items,
|
used to find items,
|
||||||
|
|
@ -1787,19 +1795,19 @@ class ItemFind(models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ("item", "key")
|
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)
|
key = models.CharField(max_length=200, db_index=True)
|
||||||
value = models.TextField(blank=True, db_index=settings.DB_GIN_TRGM)
|
value = models.TextField(blank=True, db_index=settings.DB_GIN_TRGM)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "%s=%s" % (self.key, self.value)
|
return u"%s=%s" % (self.key, self.value)
|
||||||
'''
|
'''
|
||||||
ItemSort
|
ItemSort
|
||||||
table constructed based on info in settings.CONFIG['itemKeys']
|
table constructed based on info in settings.CONFIG['itemKeys']
|
||||||
'''
|
'''
|
||||||
attrs = {
|
attrs = {
|
||||||
'__module__': 'item.models',
|
'__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),
|
'duration': models.FloatField(null=True, blank=True, db_index=True),
|
||||||
'width': models.BigIntegerField(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),
|
'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 = type('ItemSort', (models.Model,), attrs)
|
||||||
ItemSort.fields = [f.name for f in ItemSort._meta.fields]
|
ItemSort.fields = [f.name for f in ItemSort._meta.fields]
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class Access(models.Model):
|
class Access(models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ("item", "user")
|
unique_together = ("item", "user")
|
||||||
|
|
||||||
access = models.DateTimeField(auto_now=True)
|
access = models.DateTimeField(auto_now=True)
|
||||||
item = models.ForeignKey(Item, related_name='accessed', on_delete=models.CASCADE)
|
item = models.ForeignKey(Item, related_name='accessed')
|
||||||
user = models.ForeignKey(User, null=True, related_name='accessed_items', on_delete=models.CASCADE)
|
user = models.ForeignKey(User, null=True, related_name='accessed_items')
|
||||||
accessed = models.IntegerField(default=0)
|
accessed = models.IntegerField(default=0)
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
|
|
@ -1837,9 +1846,10 @@ class Access(models.Model):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
if self.user:
|
if self.user:
|
||||||
return "%s/%s/%s" % (self.user, self.item, self.access)
|
return u"%s/%s/%s" % (self.user, self.item, self.access)
|
||||||
return "%s/%s" % (self.item, self.access)
|
return u"%s/%s" % (self.item, self.access)
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class Facet(models.Model):
|
class Facet(models.Model):
|
||||||
'''
|
'''
|
||||||
used for keys that can have multiple values like people, languages etc.
|
used for keys that can have multiple values like people, languages etc.
|
||||||
|
|
@ -1850,13 +1860,13 @@ class Facet(models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ("item", "key", "value")
|
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)
|
key = models.CharField(max_length=200, db_index=True)
|
||||||
value = models.CharField(max_length=1000, db_index=True)
|
value = models.CharField(max_length=1000, db_index=True)
|
||||||
sortvalue = models.CharField(max_length=1000, db_index=True)
|
sortvalue = models.CharField(max_length=1000, db_index=True)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "%s=%s" % (self.key, self.value)
|
return u"%s=%s" % (self.key, self.value)
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
if not self.sortvalue:
|
if not self.sortvalue:
|
||||||
|
|
@ -1876,7 +1886,7 @@ class Description(models.Model):
|
||||||
|
|
||||||
|
|
||||||
class AnnotationSequence(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)
|
value = models.BigIntegerField(default=1)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
from django.urls import path, re_path
|
|
||||||
|
|
||||||
from . import views
|
|
||||||
|
|
||||||
urls = [
|
|
||||||
[
|
|
||||||
#frames
|
|
||||||
re_path(r'^(?P<id>[A-Z0-9].*)/(?P<size>\d+)p(?P<position>[\d\.]*)\.jpg$', views.frame),
|
|
||||||
|
|
||||||
#timelines
|
|
||||||
re_path(r'^(?P<id>[A-Z0-9].*)/timeline(?P<mode>[a-z]*)(?P<size>\d+)p(?P<position>\d+)\.(?P<format>png|jpg)$', views.timeline),
|
|
||||||
re_path(r'^(?P<id>[A-Z0-9].*)/timeline(?P<mode>[a-z]*)(?P<size>\d+)p\.(?P<format>png|jpg)$', views.timeline),
|
|
||||||
|
|
||||||
#download
|
|
||||||
re_path(r'^(?P<id>[A-Z0-9].*)/download$', views.download),
|
|
||||||
re_path(r'^(?P<id>[A-Z0-9].*)/download/$', views.download),
|
|
||||||
re_path(r'^(?P<id>[A-Z0-9].*)/download/source/(?P<part>\d+)?$', views.download_source),
|
|
||||||
re_path(r'^(?P<id>[A-Z0-9].*)/download/(?P<resolution>\d+)p(?P<part>\d+)\.(?P<format>webm|ogv|mp4)$', views.download),
|
|
||||||
re_path(r'^(?P<id>[A-Z0-9].*)/download/(?P<resolution>\d+)p\.(?P<format>webm|ogv|mp4)$', views.download),
|
|
||||||
|
|
||||||
#video
|
|
||||||
re_path(r'^(?P<id>[A-Z0-9].*)/(?P<resolution>\d+)p(?P<index>\d*)\.(?P<format>webm|ogv|mp4)$', views.video),
|
|
||||||
re_path(r'^(?P<id>[A-Z0-9].*)/(?P<resolution>\d+)p(?P<index>\d*)\.(?P<track>.+)\.(?P<format>webm|ogv|mp4)$', views.video),
|
|
||||||
|
|
||||||
#torrent
|
|
||||||
re_path(r'^(?P<id>[A-Z0-9].*)/torrent$', views.torrent),
|
|
||||||
re_path(r'^(?P<id>[A-Z0-9].*)/torrent/(?P<filename>.*?)$', views.torrent),
|
|
||||||
|
|
||||||
#export
|
|
||||||
re_path(r'^(?P<id>[A-Z0-9].*)/json$', views.item_json),
|
|
||||||
re_path(r'^(?P<id>[A-Z0-9].*)/xml$', views.item_xml),
|
|
||||||
|
|
||||||
#srt export
|
|
||||||
re_path(r'^(?P<id>[A-Z0-9].*)/(?P<layer>.+)\.(?:(?P<language>.{2})\.)?(?P<ext>srt|vtt)$', views.srt),
|
|
||||||
|
|
||||||
#icon
|
|
||||||
re_path(r'^(?P<id>[A-Z0-9].*)/icon(?P<size>\d*)\.jpg$', views.icon),
|
|
||||||
|
|
||||||
#poster
|
|
||||||
re_path(r'^(?P<id>[A-Z0-9].*)/posterframe(?P<position>\d+).jpg$', views.poster_frame),
|
|
||||||
re_path(r'^(?P<id>[A-Z0-9].*)/poster(?P<size>\d+)\.jpg$', views.poster),
|
|
||||||
re_path(r'^(?P<id>[A-Z0-9].*)/siteposter(?P<size>\d*)\.jpg$', views.siteposter),
|
|
||||||
re_path(r'^(?P<id>[A-Z0-9].*)/poster\.jpg$', views.siteposter),
|
|
||||||
|
|
||||||
re_path(r'^random$', views.random_annotation),
|
|
||||||
],
|
|
||||||
'item',
|
|
||||||
'item',
|
|
||||||
]
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- 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
|
import os
|
||||||
|
from datetime import timedelta, datetime
|
||||||
|
import gzip
|
||||||
import random
|
import random
|
||||||
|
|
||||||
|
from six.moves.urllib.parse import quote
|
||||||
from celery.task import task, periodic_task
|
from celery.task import task, periodic_task
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import connection, transaction
|
from django.db import connection, transaction
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, with_statement, print_function
|
||||||
|
|
||||||
import math
|
import math
|
||||||
import os
|
import os
|
||||||
|
|
|
||||||
47
pandora/item/urls.py
Normal file
47
pandora/item/urls.py
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from django.conf.urls import url
|
||||||
|
|
||||||
|
from . import views
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
#frames
|
||||||
|
url(r'^(?P<id>[A-Z0-9].*)/(?P<size>\d+)p(?P<position>[\d\.]*)\.jpg$', views.frame),
|
||||||
|
|
||||||
|
#timelines
|
||||||
|
url(r'^(?P<id>[A-Z0-9].*)/timeline(?P<mode>[a-z]*)(?P<size>\d+)p(?P<position>\d+)\.(?P<format>png|jpg)$', views.timeline),
|
||||||
|
url(r'^(?P<id>[A-Z0-9].*)/timeline(?P<mode>[a-z]*)(?P<size>\d+)p\.(?P<format>png|jpg)$', views.timeline),
|
||||||
|
|
||||||
|
#download
|
||||||
|
url(r'^(?P<id>[A-Z0-9].*)/download$', views.download),
|
||||||
|
url(r'^(?P<id>[A-Z0-9].*)/download/$', views.download),
|
||||||
|
url(r'^(?P<id>[A-Z0-9].*)/download/source/(?P<part>\d+)?$', views.download_source),
|
||||||
|
url(r'^(?P<id>[A-Z0-9].*)/download/(?P<resolution>\d+)p(?P<part>\d+)\.(?P<format>webm|ogv|mp4)$', views.download),
|
||||||
|
url(r'^(?P<id>[A-Z0-9].*)/download/(?P<resolution>\d+)p\.(?P<format>webm|ogv|mp4)$', views.download),
|
||||||
|
|
||||||
|
#video
|
||||||
|
url(r'^(?P<id>[A-Z0-9].*)/(?P<resolution>\d+)p(?P<index>\d*)\.(?P<format>webm|ogv|mp4)$', views.video),
|
||||||
|
url(r'^(?P<id>[A-Z0-9].*)/(?P<resolution>\d+)p(?P<index>\d*)\.(?P<track>.+)\.(?P<format>webm|ogv|mp4)$', views.video),
|
||||||
|
|
||||||
|
#torrent
|
||||||
|
url(r'^(?P<id>[A-Z0-9].*)/torrent$', views.torrent),
|
||||||
|
url(r'^(?P<id>[A-Z0-9].*)/torrent/(?P<filename>.*?)$', views.torrent),
|
||||||
|
|
||||||
|
#export
|
||||||
|
url(r'^(?P<id>[A-Z0-9].*)/json$', views.item_json),
|
||||||
|
url(r'^(?P<id>[A-Z0-9].*)/xml$', views.item_xml),
|
||||||
|
|
||||||
|
#srt export
|
||||||
|
url(r'^(?P<id>[A-Z0-9].*)/(?P<layer>.+)\.(?:(?P<language>.{2})\.)?(?P<ext>srt|vtt)$', views.srt),
|
||||||
|
|
||||||
|
#icon
|
||||||
|
url(r'^(?P<id>[A-Z0-9].*)/icon(?P<size>\d*)\.jpg$', views.icon),
|
||||||
|
|
||||||
|
#poster
|
||||||
|
url(r'^(?P<id>[A-Z0-9].*)/posterframe(?P<position>\d+).jpg$', views.poster_frame),
|
||||||
|
url(r'^(?P<id>[A-Z0-9].*)/poster(?P<size>\d+)\.jpg$', views.poster),
|
||||||
|
url(r'^(?P<id>[A-Z0-9].*)/siteposter(?P<size>\d*)\.jpg$', views.siteposter),
|
||||||
|
url(r'^(?P<id>[A-Z0-9].*)/poster\.jpg$', views.siteposter),
|
||||||
|
|
||||||
|
url(r'^random$', views.random_annotation),
|
||||||
|
]
|
||||||
|
|
@ -6,6 +6,7 @@ import unicodedata
|
||||||
|
|
||||||
import ox
|
import ox
|
||||||
from ox import sort_string
|
from ox import sort_string
|
||||||
|
from six import PY2
|
||||||
|
|
||||||
|
|
||||||
def safe_filename(filename):
|
def safe_filename(filename):
|
||||||
|
|
@ -54,14 +55,13 @@ def plural_key(term):
|
||||||
|
|
||||||
def sort_title(title):
|
def sort_title(title):
|
||||||
|
|
||||||
title = title.replace('Æ', 'Ae')
|
title = title.replace(u'Æ', 'Ae')
|
||||||
if isinstance(title, bytes):
|
if isinstance(title, bytes):
|
||||||
title = title.decode('utf-8')
|
title = title.decode('utf-8')
|
||||||
title = ox.decode_html(title)
|
|
||||||
title = sort_string(title)
|
title = sort_string(title)
|
||||||
|
|
||||||
#title
|
#title
|
||||||
title = re.sub('[\'!¿¡,\.;\-"\:\*\[\]]', '', title)
|
title = re.sub(u'[\'!¿¡,\.;\-"\:\*\[\]]', '', title)
|
||||||
return title.strip()
|
return title.strip()
|
||||||
|
|
||||||
def get_positions(ids, pos, decode_id=False):
|
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)
|
return get_by_key(objects, 'id', id)
|
||||||
|
|
||||||
def normalize_dict(encoding, data):
|
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)
|
data = unicodedata.normalize(encoding, data)
|
||||||
elif isinstance(data, dict):
|
elif isinstance(data, dict):
|
||||||
for key in data:
|
for key in data:
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,14 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- 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 os.path
|
||||||
|
import mimetypes
|
||||||
import random
|
import random
|
||||||
import time
|
import time
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
from six import PY2
|
||||||
|
from six.moves.urllib.parse import quote, urlparse
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from django.db.models import Count, Sum
|
from django.db.models import Count, Sum
|
||||||
from django.http import HttpResponse, HttpResponseForbidden, Http404
|
from django.http import HttpResponse, HttpResponseForbidden, Http404
|
||||||
|
|
@ -34,6 +36,8 @@ from changelog.models import add_changelog
|
||||||
|
|
||||||
from oxdjango.api import actions
|
from oxdjango.api import actions
|
||||||
|
|
||||||
|
if not PY2:
|
||||||
|
unicode = str
|
||||||
|
|
||||||
def _order_query(qs, sort, prefix='sort__'):
|
def _order_query(qs, sort, prefix='sort__'):
|
||||||
order_by = []
|
order_by = []
|
||||||
|
|
@ -140,7 +144,7 @@ def get_positions(request, query):
|
||||||
return utils.get_positions(ids, query['positions'])
|
return utils.get_positions(ids, query['positions'])
|
||||||
|
|
||||||
def is_editable(request, item):
|
def is_editable(request, item):
|
||||||
if request.user.is_anonymous:
|
if request.user.is_anonymous():
|
||||||
return False
|
return False
|
||||||
if not hasattr(request, 'user_group_names'):
|
if not hasattr(request, 'user_group_names'):
|
||||||
request.user_group_names = {g.name for g in request.user.groups.all()}
|
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
|
ext = '.%s' % format
|
||||||
duration = stream.info['duration']
|
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'),
|
item.get('title'),
|
||||||
ox.format_duration(t[0] * 1000).replace(':', '.')[:-4],
|
ox.format_duration(t[0] * 1000).replace(':', '.')[:-4],
|
||||||
ox.format_duration(t[1] * 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]
|
content_type, encoder = _subtitle_formats[ext]
|
||||||
response = HttpResponse()
|
response = HttpResponse()
|
||||||
if language:
|
if language:
|
||||||
filename = "%s.%s.%s" % (item.get('title'), language, ext)
|
filename = u"%s.%s.%s" % (item.get('title'), language, ext)
|
||||||
else:
|
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-Disposition'] = "attachment; filename*=UTF-8''%s" % quote(filename.encode('utf-8'))
|
||||||
response['Content-Type'] = content_type
|
response['Content-Type'] = content_type
|
||||||
response.write(item.srt(layer, language, encoder=encoder))
|
response.write(item.srt(layer, language, encoder=encoder))
|
||||||
|
|
@ -1202,7 +1206,7 @@ def atom_xml(request):
|
||||||
el.text = atom_link
|
el.text = atom_link
|
||||||
|
|
||||||
level = settings.CONFIG['capabilities']['canSeeItem']['guest']
|
level = settings.CONFIG['capabilities']['canSeeItem']['guest']
|
||||||
if not request.user.is_anonymous:
|
if not request.user.is_anonymous():
|
||||||
level = request.user.profile.level
|
level = request.user.profile.level
|
||||||
for item in models.Item.objects.filter(level__lte=level, rendered=True).order_by('-created')[:7]:
|
for item in models.Item.objects.filter(level__lte=level, rendered=True).order_by('-created')[:7]:
|
||||||
if add_updated:
|
if add_updated:
|
||||||
|
|
@ -1228,7 +1232,7 @@ def atom_xml(request):
|
||||||
if item.get('director'):
|
if item.get('director'):
|
||||||
el = ET.SubElement(entry, "author")
|
el = ET.SubElement(entry, "author")
|
||||||
name = ET.SubElement(el, "name")
|
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:
|
elif item.user:
|
||||||
el = ET.SubElement(entry, "author")
|
el = ET.SubElement(entry, "author")
|
||||||
name = ET.SubElement(el, "name")
|
name = ET.SubElement(el, "name")
|
||||||
|
|
@ -1277,9 +1281,9 @@ def atom_xml(request):
|
||||||
}.get(key, key))
|
}.get(key, key))
|
||||||
if value and value != -1:
|
if value and value != -1:
|
||||||
el = ET.SubElement(format, key)
|
el = ET.SubElement(format, key)
|
||||||
el.text = str(value)
|
el.text = unicode(value)
|
||||||
el = ET.SubElement(format, 'pixel_aspect_ratio')
|
el = ET.SubElement(format, 'pixel_aspect_ratio')
|
||||||
el.text = "1:1"
|
el.text = u"1:1"
|
||||||
|
|
||||||
if has_capability(request.user, 'canDownloadVideo'):
|
if has_capability(request.user, 'canDownloadVideo'):
|
||||||
if item.torrent:
|
if item.torrent:
|
||||||
|
|
@ -1354,7 +1358,7 @@ def oembed(request):
|
||||||
oxml = ET.Element('oembed')
|
oxml = ET.Element('oembed')
|
||||||
for key in oembed:
|
for key in oembed:
|
||||||
e = ET.SubElement(oxml, key)
|
e = ET.SubElement(oxml, key)
|
||||||
e.text = str(oembed[key])
|
e.text = unicode(oembed[key])
|
||||||
return HttpResponse(
|
return HttpResponse(
|
||||||
'<?xml version="1.0" encoding="utf-8" standalone="yes"?>\n' + ET.tostring(oxml).decode(),
|
'<?xml version="1.0" encoding="utf-8" standalone="yes"?>\n' + ET.tostring(oxml).decode(),
|
||||||
'application/xml'
|
'application/xml'
|
||||||
|
|
@ -1382,7 +1386,7 @@ def sitemap_part_xml(request, part):
|
||||||
|
|
||||||
def item_json(request, id):
|
def item_json(request, id):
|
||||||
level = settings.CONFIG['capabilities']['canSeeItem']['guest']
|
level = settings.CONFIG['capabilities']['canSeeItem']['guest']
|
||||||
if not request.user.is_anonymous:
|
if not request.user.is_anonymous():
|
||||||
level = request.user.profile.level
|
level = request.user.profile.level
|
||||||
qs = models.Item.objects.filter(public_id=id, level__lte=level)
|
qs = models.Item.objects.filter(public_id=id, level__lte=level)
|
||||||
if qs.count() == 0:
|
if qs.count() == 0:
|
||||||
|
|
@ -1395,7 +1399,7 @@ def item_json(request, id):
|
||||||
|
|
||||||
def item_xml(request, id):
|
def item_xml(request, id):
|
||||||
level = settings.CONFIG['capabilities']['canSeeItem']['guest']
|
level = settings.CONFIG['capabilities']['canSeeItem']['guest']
|
||||||
if not request.user.is_anonymous:
|
if not request.user.is_anonymous():
|
||||||
level = request.user.profile.level
|
level = request.user.profile.level
|
||||||
qs = models.Item.objects.filter(public_id=id, level__lte=level)
|
qs = models.Item.objects.filter(public_id=id, level__lte=level)
|
||||||
if qs.count() == 0:
|
if qs.count() == 0:
|
||||||
|
|
@ -1420,7 +1424,7 @@ def item_xml(request, id):
|
||||||
xmltree(root, k, data[k])
|
xmltree(root, k, data[k])
|
||||||
else:
|
else:
|
||||||
e = ET.SubElement(root, key)
|
e = ET.SubElement(root, key)
|
||||||
e.text = str(data)
|
e.text = unicode(data)
|
||||||
|
|
||||||
oxml = ET.Element('item')
|
oxml = ET.Element('item')
|
||||||
xmltree(oxml, 'item', j)
|
xmltree(oxml, 'item', j)
|
||||||
|
|
@ -1435,7 +1439,7 @@ def item(request, id):
|
||||||
view = None
|
view = None
|
||||||
template = 'index.html'
|
template = 'index.html'
|
||||||
level = settings.CONFIG['capabilities']['canSeeItem']['guest']
|
level = settings.CONFIG['capabilities']['canSeeItem']['guest']
|
||||||
if not request.user.is_anonymous:
|
if not request.user.is_anonymous():
|
||||||
level = request.user.profile.level
|
level = request.user.profile.level
|
||||||
qs = models.Item.objects.filter(public_id=id, level__lte=level)
|
qs = models.Item.objects.filter(public_id=id, level__lte=level)
|
||||||
if qs.count() == 0:
|
if qs.count() == 0:
|
||||||
|
|
@ -1480,7 +1484,7 @@ def item(request, id):
|
||||||
else:
|
else:
|
||||||
title = key['title'] if key else k.capitalize()
|
title = key['title'] if key else k.capitalize()
|
||||||
if isinstance(value, list):
|
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':
|
elif key and key.get('type') == 'float':
|
||||||
value = '%0.3f' % value
|
value = '%0.3f' % value
|
||||||
elif key and key.get('type') == 'time':
|
elif key and key.get('type') == 'time':
|
||||||
|
|
@ -1489,7 +1493,7 @@ def item(request, id):
|
||||||
clips = []
|
clips = []
|
||||||
clip = {'in': 0, 'annotations': []}
|
clip = {'in': 0, 'annotations': []}
|
||||||
# logged in users should have javascript. not adding annotations makes load faster
|
# 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(
|
for a in item.annotations.exclude(
|
||||||
layer='subtitles'
|
layer='subtitles'
|
||||||
).exclude(
|
).exclude(
|
||||||
|
|
@ -1509,13 +1513,13 @@ def item(request, id):
|
||||||
head_title = item.get('title', '')
|
head_title = item.get('title', '')
|
||||||
title = item.get('title', '')
|
title = item.get('title', '')
|
||||||
if item.get('director'):
|
if item.get('director'):
|
||||||
head_title += ' (%s)' % ', '.join(item.get('director', []))
|
head_title += u' (%s)' % u', '.join(item.get('director', []))
|
||||||
if item.get('year'):
|
if item.get('year'):
|
||||||
head_title += ' %s' % item.get('year')
|
head_title += u' %s' % item.get('year')
|
||||||
title += ' (%s)' % item.get('year')
|
title += u' (%s)' % item.get('year')
|
||||||
if view:
|
if view:
|
||||||
head_title += ' – %s' % view
|
head_title += u' – %s' % view
|
||||||
head_title += ' – %s' % settings.SITENAME
|
head_title += u' – %s' % settings.SITENAME
|
||||||
head_title = ox.decode_html(head_title)
|
head_title = ox.decode_html(head_title)
|
||||||
title = ox.decode_html(title)
|
title = ox.decode_html(title)
|
||||||
ctx = {
|
ctx = {
|
||||||
|
|
|
||||||
|
|
@ -124,7 +124,7 @@ class ListManager(Manager):
|
||||||
if conditions:
|
if conditions:
|
||||||
qs = qs.filter(conditions)
|
qs = qs.filter(conditions)
|
||||||
|
|
||||||
if user.is_anonymous:
|
if user.is_anonymous():
|
||||||
qs = qs.filter(Q(status='public') | Q(status='featured'))
|
qs = qs.filter(Q(status='public') | Q(status='featured'))
|
||||||
else:
|
else:
|
||||||
qs = qs.filter(Q(status='public') | Q(status='featured') | Q(user=user))
|
qs = qs.filter(Q(status='public') | Q(status='featured') | Q(user=user))
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, print_function, absolute_import
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
|
@ -9,6 +10,7 @@ from django.db import models
|
||||||
from django.db.models import Max
|
from django.db.models import Max
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.utils.encoding import python_2_unicode_compatible
|
||||||
from oxdjango.fields import JSONField
|
from oxdjango.fields import JSONField
|
||||||
|
|
||||||
import ox
|
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_listview(): return settings.CONFIG['user']['ui']['listView']
|
||||||
def get_listsort(): return tuple(settings.CONFIG['user']['ui']['listSort'])
|
def get_listsort(): return tuple(settings.CONFIG['user']['ui']['listSort'])
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class List(models.Model):
|
class List(models.Model):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
@ -33,7 +36,7 @@ class List(models.Model):
|
||||||
|
|
||||||
created = models.DateTimeField(auto_now_add=True)
|
created = models.DateTimeField(auto_now_add=True)
|
||||||
modified = models.DateTimeField(auto_now=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')
|
groups = models.ManyToManyField(Group, blank=True, related_name='lists')
|
||||||
name = models.CharField(max_length=255)
|
name = models.CharField(max_length=255)
|
||||||
status = models.CharField(max_length=20, default='private')
|
status = models.CharField(max_length=20, default='private')
|
||||||
|
|
@ -47,7 +50,7 @@ class List(models.Model):
|
||||||
view = models.TextField(default=get_listview)
|
view = models.TextField(default=get_listview)
|
||||||
sort = JSONField(default=get_listsort, editable=False)
|
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?
|
#is through table still required?
|
||||||
items = models.ManyToManyField('item.Item', related_name='lists',
|
items = models.ManyToManyField('item.Item', related_name='lists',
|
||||||
|
|
@ -107,13 +110,13 @@ class List(models.Model):
|
||||||
return self.get_id()
|
return self.get_id()
|
||||||
|
|
||||||
def get_id(self):
|
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):
|
def accessible(self, user):
|
||||||
return self.user == user or self.status in ('public', 'featured')
|
return self.user == user or self.status in ('public', 'featured')
|
||||||
|
|
||||||
def editable(self, user):
|
def editable(self, user):
|
||||||
if not user or user.is_anonymous:
|
if not user or user.is_anonymous():
|
||||||
return False
|
return False
|
||||||
if self.user == user or \
|
if self.user == user or \
|
||||||
self.groups.filter(id__in=user.groups.all()).count() > 0 or \
|
self.groups.filter(id__in=user.groups.all()).count() > 0 or \
|
||||||
|
|
@ -241,7 +244,7 @@ class List(models.Model):
|
||||||
elif key == 'subscribers':
|
elif key == 'subscribers':
|
||||||
response[key] = self.subscribed_users.all().count()
|
response[key] = self.subscribed_users.all().count()
|
||||||
elif key == 'subscribed':
|
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()
|
response[key] = self.subscribed_users.filter(id=user.id).exists()
|
||||||
else:
|
else:
|
||||||
response[key] = getattr(self, {
|
response[key] = getattr(self, {
|
||||||
|
|
@ -311,27 +314,29 @@ class List(models.Model):
|
||||||
path = source
|
path = source
|
||||||
return path
|
return path
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class ListItem(models.Model):
|
class ListItem(models.Model):
|
||||||
created = models.DateTimeField(auto_now_add=True)
|
created = models.DateTimeField(auto_now_add=True)
|
||||||
modified = models.DateTimeField(auto_now=True)
|
modified = models.DateTimeField(auto_now=True)
|
||||||
list = models.ForeignKey(List, on_delete=models.CASCADE)
|
list = models.ForeignKey(List)
|
||||||
index = models.IntegerField(default=0)
|
index = models.IntegerField(default=0)
|
||||||
item = models.ForeignKey('item.Item', on_delete=models.CASCADE)
|
item = models.ForeignKey('item.Item')
|
||||||
|
|
||||||
def __str__(self):
|
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 Position(models.Model):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ("user", "list", "section")
|
unique_together = ("user", "list", "section")
|
||||||
|
|
||||||
list = models.ForeignKey(List, related_name='position', on_delete=models.CASCADE)
|
list = models.ForeignKey(List, related_name='position')
|
||||||
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
user = models.ForeignKey(User)
|
||||||
section = models.CharField(max_length=255)
|
section = models.CharField(max_length=255)
|
||||||
position = models.IntegerField(default=0)
|
position = models.IntegerField(default=0)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return '%s/%s/%s' % (self.section, self.position, self.list)
|
return u'%s/%s/%s' % (self.section, self.position, self.list)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, print_function, absolute_import
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
|
@ -74,7 +75,7 @@ def findLists(request, data):
|
||||||
query = parse_query(data, request.user)
|
query = parse_query(data, request.user)
|
||||||
|
|
||||||
#order
|
#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):
|
def is_featured_condition(x):
|
||||||
return x['key'] == 'status' and \
|
return x['key'] == 'status' and \
|
||||||
x['value'] == 'featured' and \
|
x['value'] == 'featured' and \
|
||||||
|
|
@ -86,7 +87,7 @@ def findLists(request, data):
|
||||||
|
|
||||||
if is_section_request:
|
if is_section_request:
|
||||||
qs = query['qs']
|
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.filter(position__in=models.Position.objects.filter(user=request.user))
|
||||||
qs = qs.order_by('position__position')
|
qs = qs.order_by('position__position')
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, print_function, absolute_import
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.utils.encoding import python_2_unicode_compatible
|
||||||
|
|
||||||
import ox
|
import ox
|
||||||
|
|
||||||
|
|
@ -10,10 +12,11 @@ from . import managers
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
|
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class Log(models.Model):
|
class Log(models.Model):
|
||||||
created = models.DateTimeField(auto_now_add=True, db_index=True)
|
created = models.DateTimeField(auto_now_add=True, db_index=True)
|
||||||
modified = models.DateTimeField(auto_now=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='')
|
url = models.CharField(max_length=1000, default='')
|
||||||
line = models.IntegerField(default=0)
|
line = models.IntegerField(default=0)
|
||||||
text = models.TextField(blank=True)
|
text = models.TextField(blank=True)
|
||||||
|
|
@ -21,7 +24,7 @@ class Log(models.Model):
|
||||||
objects = managers.LogManager()
|
objects = managers.LogManager()
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "%s" % self.id
|
return u"%s" % self.id
|
||||||
|
|
||||||
def json(self, keys=None):
|
def json(self, keys=None):
|
||||||
j = {
|
j = {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, print_function, absolute_import
|
||||||
|
|
||||||
from datetime import timedelta, datetime
|
from datetime import timedelta, datetime
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, print_function, absolute_import
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
|
|
@ -33,7 +34,7 @@ class ErrorHandler(logging.Handler):
|
||||||
request = record.request
|
request = record.request
|
||||||
|
|
||||||
request_repr = repr(request)
|
request_repr = repr(request)
|
||||||
if request.user.is_authenticated:
|
if request.user.is_authenticated():
|
||||||
user = request.user
|
user = request.user
|
||||||
url = request.META.get('PATH_INFO', '')
|
url = request.META.get('PATH_INFO', '')
|
||||||
except:
|
except:
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, print_function, absolute_import
|
||||||
|
|
||||||
import ox
|
import ox
|
||||||
from ox.utils import json
|
from ox.utils import json
|
||||||
|
|
@ -24,7 +25,7 @@ def logError(request, data):
|
||||||
returns {}
|
returns {}
|
||||||
see: findErrorLogs, removeErrorLogs
|
see: findErrorLogs, removeErrorLogs
|
||||||
'''
|
'''
|
||||||
if request.user.is_authenticated:
|
if request.user.is_authenticated():
|
||||||
user = request.user
|
user = request.user
|
||||||
else:
|
else:
|
||||||
user = None
|
user = None
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, print_function, absolute_import
|
||||||
|
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,14 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, print_function, absolute_import
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.utils.encoding import python_2_unicode_compatible
|
||||||
import ox
|
import ox
|
||||||
|
|
||||||
from . import managers
|
from . import managers
|
||||||
|
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class News(models.Model):
|
class News(models.Model):
|
||||||
objects = managers.NewsManager()
|
objects = managers.NewsManager()
|
||||||
|
|
||||||
|
|
@ -17,7 +20,7 @@ class News(models.Model):
|
||||||
text = models.TextField()
|
text = models.TextField()
|
||||||
|
|
||||||
def editable(self, user):
|
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):
|
def save(self, *args, **kwargs):
|
||||||
super(News, self).save(*args, **kwargs)
|
super(News, self).save(*args, **kwargs)
|
||||||
|
|
@ -39,5 +42,5 @@ class News(models.Model):
|
||||||
return j
|
return j
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "%s/%s" % (self.date, self.title)
|
return u"%s/%s" % (self.date, self.title)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, print_function, absolute_import
|
||||||
|
|
||||||
import ox
|
import ox
|
||||||
from ox.utils import json
|
from ox.utils import json
|
||||||
|
|
|
||||||
|
|
@ -1 +1,3 @@
|
||||||
|
from __future__ import absolute_import
|
||||||
|
|
||||||
from .actions import actions
|
from .actions import actions
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import
|
||||||
|
|
||||||
import inspect
|
import inspect
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
from six import PY2
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from ..shortcuts import render_to_json_response, json_response
|
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:
|
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))
|
fc = list(filter(lambda c: hasattr(c.cell_contents, '__call__'), f.func_closure))
|
||||||
f = fc[len(fc)-1].cell_contents
|
f = fc[len(fc)-1].cell_contents
|
||||||
info = f.__code__.co_filename[len(settings.PROJECT_ROOT)+1:]
|
if PY2:
|
||||||
info = '%s:%s' % (info, f.__code__.co_firstlineno)
|
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))
|
return info, trim(inspect.getsource(f))
|
||||||
|
|
||||||
def register(self, method, action=None, cache=True, version=None):
|
def register(self, method, action=None, cache=True, version=None):
|
||||||
|
|
|
||||||
|
|
@ -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'
|
|
||||||
]
|
|
||||||
13
pandora/oxdjango/api/urls.py
Normal file
13
pandora/oxdjango/api/urls.py
Normal file
|
|
@ -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),
|
||||||
|
]
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render_to_response
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from ..shortcuts import render_to_json_response, json_response, HttpErrorJson
|
from ..shortcuts import render_to_json_response, json_response, HttpErrorJson
|
||||||
|
|
@ -29,7 +31,7 @@ def api(request):
|
||||||
'settings': settings,
|
'settings': settings,
|
||||||
'sitename': settings.SITENAME
|
'sitename': settings.SITENAME
|
||||||
}
|
}
|
||||||
response = render(request, 'api.html', context)
|
response = render_to_response('api.html', context)
|
||||||
response['Access-Control-Allow-Origin'] = '*'
|
response['Access-Control-Allow-Origin'] = '*'
|
||||||
return response
|
return response
|
||||||
if request.META.get('CONTENT_TYPE') == 'application/json':
|
if request.META.get('CONTENT_TYPE') == 'application/json':
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import absolute_import
|
||||||
|
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
|
||||||
from .shortcuts import render_to_json_response
|
from .shortcuts import render_to_json_response
|
||||||
|
|
@ -10,7 +12,7 @@ def login_required_json(function=None):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def _wrapped_view(request, *args, **kwargs):
|
def _wrapped_view(request, *args, **kwargs):
|
||||||
if request.user.is_authenticated:
|
if request.user.is_authenticated():
|
||||||
return function(request, *args, **kwargs)
|
return function(request, *args, **kwargs)
|
||||||
return render_to_json_response({'status': {'code': 401, 'text': 'login required'}})
|
return render_to_json_response({'status': {'code': 401, 'text': 'login required'}})
|
||||||
return wraps(function)(_wrapped_view)
|
return wraps(function)(_wrapped_view)
|
||||||
|
|
@ -22,7 +24,7 @@ def admin_required_json(function=None):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def _wrapped_view(request, *args, **kwargs):
|
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 function(request, *args, **kwargs)
|
||||||
return render_to_json_response({'status': {'code': 403, 'text': 'permission denied'}})
|
return render_to_json_response({'status': {'code': 403, 'text': 'permission denied'}})
|
||||||
return wraps(function)(_wrapped_view)
|
return wraps(function)(_wrapped_view)
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,8 @@ from django.utils import datetime_safe
|
||||||
import django.contrib.postgres.fields
|
import django.contrib.postgres.fields
|
||||||
from django.core.serializers.json import DjangoJSONEncoder
|
from django.core.serializers.json import DjangoJSONEncoder
|
||||||
|
|
||||||
|
from six import string_types
|
||||||
|
|
||||||
from ox.utils import json
|
from ox.utils import json
|
||||||
|
|
||||||
class JSONField(django.contrib.postgres.fields.JSONField):
|
class JSONField(django.contrib.postgres.fields.JSONField):
|
||||||
|
|
@ -62,7 +64,7 @@ class DictField(models.TextField):
|
||||||
def dumps(cls, obj):
|
def dumps(cls, obj):
|
||||||
return json.dumps(obj, default=to_json, ensure_ascii=False)
|
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:
|
if value is None:
|
||||||
return value
|
return value
|
||||||
if isinstance(value, self._type):
|
if isinstance(value, self._type):
|
||||||
|
|
@ -72,7 +74,7 @@ class DictField(models.TextField):
|
||||||
except:
|
except:
|
||||||
raise Exception('failed to parse value: %s' % value)
|
raise Exception('failed to parse value: %s' % value)
|
||||||
if value is not None:
|
if value is not None:
|
||||||
if isinstance(value, str):
|
if isinstance(value, string_types):
|
||||||
value = json.loads(value)
|
value = json.loads(value)
|
||||||
assert isinstance(value, self._type)
|
assert isinstance(value, self._type)
|
||||||
return value
|
return value
|
||||||
|
|
@ -81,7 +83,7 @@ class DictField(models.TextField):
|
||||||
if isinstance(value, self._type):
|
if isinstance(value, self._type):
|
||||||
value = self.dumps(value)
|
value = self.dumps(value)
|
||||||
if value is not None:
|
if value is not None:
|
||||||
assert isinstance(value, str)
|
assert isinstance(value, string_types)
|
||||||
value = models.TextField.get_prep_value(self, value)
|
value = models.TextField.get_prep_value(self, value)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from datetime import datetime, timedelta
|
|
||||||
from urllib.parse import quote
|
|
||||||
import mimetypes
|
|
||||||
import os
|
import os
|
||||||
|
import mimetypes
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from six.moves.urllib.parse import quote
|
||||||
|
|
||||||
from django.http import HttpResponse, Http404
|
from django.http import HttpResponse, Http404
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,14 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from django.utils.deprecation import MiddlewareMixin
|
|
||||||
|
|
||||||
from .shortcuts import HttpErrorJson, render_to_json_response
|
from .shortcuts import HttpErrorJson, render_to_json_response
|
||||||
|
|
||||||
class ExceptionMiddleware(MiddlewareMixin):
|
class ExceptionMiddleware(object):
|
||||||
|
|
||||||
def process_exception(self, request, exception):
|
def process_exception(self, request, exception):
|
||||||
if isinstance(exception, HttpErrorJson):
|
if isinstance(exception, HttpErrorJson):
|
||||||
return render_to_json_response(exception.response)
|
return render_to_json_response(exception.response)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
class ChromeFrameMiddleware(MiddlewareMixin):
|
class ChromeFrameMiddleware(object):
|
||||||
|
|
||||||
def process_response(self, request, response):
|
def process_response(self, request, response):
|
||||||
response['X-UA-Compatible'] = 'chrome=1'
|
response['X-UA-Compatible'] = 'chrome=1'
|
||||||
return response
|
return response
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import print_function
|
||||||
import datetime
|
import datetime
|
||||||
from django.utils import datetime_safe
|
from django.utils import datetime_safe
|
||||||
from django.http import HttpResponse, Http404
|
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')
|
return python_object.strftime('%Y-%m-%dT%H:%M:%SZ')
|
||||||
if isinstance(python_object, datetime_safe.datetime):
|
if isinstance(python_object, datetime_safe.datetime):
|
||||||
return python_object.strftime('%Y-%m-%dT%H:%M:%SZ')
|
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):
|
def json_dump(data, fp, indent=4):
|
||||||
return json.dump(data, fp, indent=indent, default=_to_json, ensure_ascii=False)
|
return json.dump(data, fp, indent=indent, default=_to_json, ensure_ascii=False)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import unicodedata
|
import unicodedata
|
||||||
|
|
||||||
|
from six import string_types
|
||||||
from django.db.models import Q, Manager
|
from django.db.models import Q, Manager
|
||||||
|
|
||||||
from item.utils import decode_id
|
from item.utils import decode_id
|
||||||
|
|
@ -55,7 +56,7 @@ def parseCondition(condition, user):
|
||||||
else:
|
else:
|
||||||
key = k + get_operator(op, 'istr')
|
key = k + get_operator(op, 'istr')
|
||||||
key = str(key)
|
key = str(key)
|
||||||
if isinstance(v, str):
|
if isinstance(v, string_types):
|
||||||
v = unicodedata.normalize('NFKD', v).lower()
|
v = unicodedata.normalize('NFKD', v).lower()
|
||||||
if exclude:
|
if exclude:
|
||||||
q = ~Q(**{key: v})
|
q = ~Q(**{key: v})
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, print_function, absolute_import
|
||||||
|
|
||||||
import unicodedata
|
import unicodedata
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.utils.encoding import python_2_unicode_compatible
|
||||||
|
|
||||||
from oxdjango import fields
|
from oxdjango import fields
|
||||||
import ox
|
import ox
|
||||||
|
|
@ -22,9 +24,10 @@ def get_name_sort(name, sortname=None):
|
||||||
person.save()
|
person.save()
|
||||||
sortname = unicodedata.normalize('NFKD', person.sortname)
|
sortname = unicodedata.normalize('NFKD', person.sortname)
|
||||||
else:
|
else:
|
||||||
sortname = ''
|
sortname = u''
|
||||||
return sortname
|
return sortname
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class Person(models.Model):
|
class Person(models.Model):
|
||||||
name = models.CharField(max_length=200, unique=True)
|
name = models.CharField(max_length=200, unique=True)
|
||||||
sortname = models.CharField(max_length=200)
|
sortname = models.CharField(max_length=200)
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue