diff --git a/.gitignore b/.gitignore index a7c6b10..db3f8c8 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ build *.pyc *~ *.swp +local diff --git a/nginx/oxdata.conf b/nginx/oxdata.conf index 2d58b7e..f186581 100644 --- a/nginx/oxdata.conf +++ b/nginx/oxdata.conf @@ -20,7 +20,8 @@ end script exec start-stop-daemon \ --pidfile /var/run/oxdata/oxdata.pid \ --start -c $USER -d $VENV/oxdata \ - --exec $VENV/bin/gunicorn_django -- \ + --exec $VENV/bin/gunicorn -- \ + wsgi:application \ --bind 127.0.0.1:8087 \ --workers 5 \ --max-requests 1000 \ diff --git a/oxdata/api/management/__init__.py b/oxdata/api/management/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/oxdata/api/management/commands/__init__.py b/oxdata/api/management/commands/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/oxdata/api/models.py b/oxdata/api/models.py deleted file mode 100644 index 04d0a3c..0000000 --- a/oxdata/api/models.py +++ /dev/null @@ -1,3 +0,0 @@ -# -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 - diff --git a/oxdata/api/templates/api.html b/oxdata/api/templates/api.html deleted file mode 100644 index cabd746..0000000 --- a/oxdata/api/templates/api.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - {{sitename}} API - - - - - - diff --git a/oxdata/api/urls.py b/oxdata/api/urls.py deleted file mode 100644 index fb0b40c..0000000 --- a/oxdata/api/urls.py +++ /dev/null @@ -1,10 +0,0 @@ -# -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 - -from django.conf.urls.defaults import * - - -urlpatterns = patterns("api.views", - (r'^$', 'api'), -) - diff --git a/oxdata/cover/models.py b/oxdata/cover/models.py index 63625b5..c246aee 100644 --- a/oxdata/cover/models.py +++ b/oxdata/cover/models.py @@ -5,10 +5,7 @@ from __future__ import division import os.path import hashlib -from django.conf import settings from django.db import models -from django.db.models import Q -from django.contrib.auth.models import User from django.core.files.base import ContentFile from lookup.models import MovieId @@ -43,6 +40,9 @@ def cover_path(url, filename): name = "%s.%s" % (h, ext) return os.path.join('covers', h[:2], h[2:4], h[4:6], name) +def covercache_image_path(i, f): + return cover_path(i.url.encode('utf-8'), f) + class CoverCache(models.Model): class Meta: unique_together = ("isbn", "url") @@ -54,7 +54,7 @@ class CoverCache(models.Model): url = models.CharField(max_length=1024) site = models.CharField(max_length=255) site_id = models.CharField(max_length=42) - image = models.ImageField(max_length=255, upload_to=lambda i, f: cover_path(i.url.encode('utf-8'), f)) + image = models.ImageField(max_length=255, upload_to=covercache_image_path) status = models.CharField(max_length=1024, default='200') failed = models.BooleanField(default=False) @@ -89,10 +89,13 @@ class CoverCache(models.Model): self.image.delete() return self.image +def cover_image_path(i, f): + return cover_path('upload/%s' % i.id, f) + class Cover(models.Model): created = models.DateTimeField(auto_now_add=True) modified = models.DateTimeField(auto_now=True) isbn = models.ForeignKey(MovieId, related_name='cover') - cover = models.ImageField(max_length=255, upload_to=lambda i, f: cover_path('upload/%s' % i.id, f)) + cover = models.ImageField(max_length=255, upload_to=cover_image_path) diff --git a/oxdata/cover/urls.py b/oxdata/cover/urls.py index 629466f..29395cc 100644 --- a/oxdata/cover/urls.py +++ b/oxdata/cover/urls.py @@ -1,6 +1,8 @@ -from django.conf.urls.defaults import * +from django.conf.urls import url -urlpatterns = patterns('oxdata.cover.views', - (r'^$', 'cover'), -) +import views + +urlpatterns = [ + url(r'^$', views.cover), +] diff --git a/oxdata/cover/views.py b/oxdata/cover/views.py index aa8ecf5..76012b1 100644 --- a/oxdata/cover/views.py +++ b/oxdata/cover/views.py @@ -1,13 +1,7 @@ # -*- coding: utf-8 -*- # vi:si:et:sw=4:sts=4:ts=4 -import os.path -from django.db import models -from django.db.models import Q -from django.contrib.auth.models import User -from django.shortcuts import render_to_response, get_object_or_404, get_list_or_404 -from django.template import RequestContext -from ox.django.shortcuts import render_to_json_response +from oxdjango.shortcuts import render_to_json_response import models diff --git a/oxdata/lookup/cache.py b/oxdata/lookup/cache.py index f98e1b1..1a7fbf3 100644 --- a/oxdata/lookup/cache.py +++ b/oxdata/lookup/cache.py @@ -1,15 +1,12 @@ # -*- coding: UTF-8 -*- # vi:si:et:sw=4:sts=4:ts=4 -import os -from django.conf import settings -from ox import find_re import ox.web.criterion import ox.web.imdb import ox.web.impawards import models -from oxdata.poster.models import PosterCache +from poster.models import PosterCache import modules def addPoster(m, url, site, site_id): diff --git a/oxdata/lookup/migrations/0001_initial.py b/oxdata/lookup/migrations/0001_initial.py index 42a1df6..cb748a1 100644 --- a/oxdata/lookup/migrations/0001_initial.py +++ b/oxdata/lookup/migrations/0001_initial.py @@ -1,78 +1,38 @@ # -*- coding: utf-8 -*- +# Generated by Django 1.9.4 on 2016-03-06 10:11 +from __future__ import unicode_literals -from south.db import db -from django.db import models -from oxdata.lookup.models import * +from django.db import migrations, models -class Migration: - - def forwards(self, orm): - - # Adding model 'MovieId' - db.create_table('lookup_movieid', ( - ('id', orm['lookup.MovieId:id']), - ('created', orm['lookup.MovieId:created']), - ('modified', orm['lookup.MovieId:modified']), - ('title', orm['lookup.MovieId:title']), - ('year', orm['lookup.MovieId:year']), - ('director', orm['lookup.MovieId:director']), - ('series_title', orm['lookup.MovieId:series_title']), - ('episode_title', orm['lookup.MovieId:episode_title']), - ('season', orm['lookup.MovieId:season']), - ('episode', orm['lookup.MovieId:episode']), - ('oxdb_id', orm['lookup.MovieId:oxdb_id']), - ('imdb_id', orm['lookup.MovieId:imdb_id']), - ('amg_id', orm['lookup.MovieId:amg_id']), - ('wikipedia_id', orm['lookup.MovieId:wikipedia_id']), - ('criterion_id', orm['lookup.MovieId:criterion_id']), - ('impawards_id', orm['lookup.MovieId:impawards_id']), - )) - db.send_create_signal('lookup', ['MovieId']) - - # Adding model 'Karagarga' - db.create_table('lookup_karagarga', ( - ('id', orm['lookup.Karagarga:id']), - ('movie_id', orm['lookup.Karagarga:movie_id']), - ('karagarga_id', orm['lookup.Karagarga:karagarga_id']), - )) - db.send_create_signal('lookup', ['Karagarga']) - - - - def backwards(self, orm): - - # Deleting model 'MovieId' - db.delete_table('lookup_movieid') - - # Deleting model 'Karagarga' - db.delete_table('lookup_karagarga') - - - - models = { - 'lookup.karagarga': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'karagarga_id': ('django.db.models.fields.IntegerField', [], {'unique': 'True'}), - 'movie_id': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'karagarga_ids'", 'to': "orm['lookup.MovieId']"}) - }, - 'lookup.movieid': { - 'amg_id': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'unique': 'True', 'null': 'True', 'blank': 'True'}), - 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'criterion_id': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'unique': 'True', 'null': 'True', 'blank': 'True'}), - 'director': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1000', 'blank': 'True'}), - 'episode': ('django.db.models.fields.IntegerField', [], {'default': '-1'}), - 'episode_title': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1000', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'imdb_id': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '7', 'unique': 'True', 'null': 'True', 'blank': 'True'}), - 'impawards_id': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'unique': 'True', 'null': 'True', 'blank': 'True'}), - 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), - 'oxdb_id': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '42', 'unique': 'True', 'null': 'True', 'blank': 'True'}), - 'season': ('django.db.models.fields.IntegerField', [], {'default': '-1'}), - 'series_title': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1000', 'blank': 'True'}), - 'title': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1000', 'blank': 'True'}), - 'wikipedia_id': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'unique': 'True', 'null': 'True', 'blank': 'True'}), - 'year': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '4', 'blank': 'True'}) - } - } - - complete_apps = ['lookup'] + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='MovieId', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created', models.DateTimeField(auto_now_add=True)), + ('modified', models.DateTimeField(auto_now=True)), + ('title', models.CharField(blank=True, default=b'', max_length=1000)), + ('year', models.CharField(blank=True, default=b'', max_length=4)), + ('director', models.CharField(blank=True, default=b'', max_length=1000)), + ('series_title', models.CharField(blank=True, default=b'', max_length=1000)), + ('episode_title', models.CharField(blank=True, default=b'', max_length=1000)), + ('season', models.IntegerField(default=-1)), + ('episode', models.IntegerField(default=-1)), + ('oxdb_id', models.CharField(blank=True, default=None, max_length=42, null=True, unique=True)), + ('imdb_id', models.CharField(blank=True, default=None, max_length=7, null=True, unique=True)), + ('amg_id', models.IntegerField(blank=True, default=None, null=True, unique=True)), + ('archiveorg_id', models.CharField(blank=True, default=None, max_length=255, null=True, unique=True)), + ('wikipedia_id', models.CharField(blank=True, default=None, max_length=255, null=True, unique=True)), + ('criterion_id', models.IntegerField(blank=True, default=None, null=True, unique=True)), + ('impawards_id', models.CharField(blank=True, default=None, max_length=255, null=True, unique=True)), + ], + ), + ] diff --git a/oxdata/lookup/migrations/0002_lookdown.py b/oxdata/lookup/migrations/0002_lookdown.py deleted file mode 100644 index be66839..0000000 --- a/oxdata/lookup/migrations/0002_lookdown.py +++ /dev/null @@ -1,47 +0,0 @@ -# encoding: utf-8 -import datetime -from south.db import db -from south.v2 import SchemaMigration -from django.db import models - -class Migration(SchemaMigration): - - def forwards(self, orm): - - # Deleting model 'karagarga' - db.delete_table('lookup_karagarga') - - def backwards(self, orm): - - # Adding model 'karagarga' - db.create_table('lookup_karagarga', ( - ('movie_id', self.gf('django.db.models.fields.related.ForeignKey')(default=None, related_name='karagarga_ids', to=orm['lookup.MovieId'])), - ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), - ('karagarga_id', self.gf('django.db.models.fields.IntegerField')(unique=True)), - )) - db.send_create_signal('lookup', ['karagarga']) - - - models = { - 'lookup.movieid': { - 'Meta': {'object_name': 'MovieId'}, - 'amg_id': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'unique': 'True', 'null': 'True', 'blank': 'True'}), - 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'criterion_id': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'unique': 'True', 'null': 'True', 'blank': 'True'}), - 'director': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1000', 'blank': 'True'}), - 'episode': ('django.db.models.fields.IntegerField', [], {'default': '-1'}), - 'episode_title': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1000', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'imdb_id': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '7', 'unique': 'True', 'null': 'True', 'blank': 'True'}), - 'impawards_id': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'unique': 'True', 'null': 'True', 'blank': 'True'}), - 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), - 'oxdb_id': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '42', 'unique': 'True', 'null': 'True', 'blank': 'True'}), - 'season': ('django.db.models.fields.IntegerField', [], {'default': '-1'}), - 'series_title': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1000', 'blank': 'True'}), - 'title': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1000', 'blank': 'True'}), - 'wikipedia_id': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'unique': 'True', 'null': 'True', 'blank': 'True'}), - 'year': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '4', 'blank': 'True'}) - } - } - - complete_apps = ['lookup'] diff --git a/oxdata/lookup/models.py b/oxdata/lookup/models.py index 14535d2..c6511f0 100644 --- a/oxdata/lookup/models.py +++ b/oxdata/lookup/models.py @@ -1,16 +1,12 @@ # -*- coding: utf-8 -*- # vi:si:et:sw=4:sts=4:ts=4 import os -import hashlib from urllib import quote from django.db import models -from django.db.models import Q, Max -from django.contrib.auth.models import User import ox -from ox.normalize import canonical_name, normalize_path, strip_accents -from ox import strip_tags +from ox.normalize import canonical_name import ox.web.archive import ox.web.imdb import ox.web.wikipedia diff --git a/oxdata/lookup/urls.py b/oxdata/lookup/urls.py index a1990e2..a297376 100644 --- a/oxdata/lookup/urls.py +++ b/oxdata/lookup/urls.py @@ -1,7 +1,8 @@ -from django.conf.urls.defaults import * +from django.conf.urls import url -urlpatterns = patterns('oxdata.lookup.views', - (r'^$', 'ids'), - (r'^urls$', 'urls'), -) +import views +urlpatterns = [ + url(r'^$', views.ids), + url(r'^urls$', views.urls), +] diff --git a/oxdata/lookup/views.py b/oxdata/lookup/views.py index 6b3b23e..7db074a 100644 --- a/oxdata/lookup/views.py +++ b/oxdata/lookup/views.py @@ -1,13 +1,8 @@ # -*- coding: utf-8 -*- # vi:si:et:sw=4:sts=4:ts=4 -import os.path -from django.db import models -from django.db.models import Q -from django.contrib.auth.models import User +from oxdjango.shortcuts import render_to_json_response, json_response +from oxdjango.api import actions -from ox.django.shortcuts import render_to_json_response, json_response -from ox.utils import json -from api.actions import actions import models diff --git a/oxdata/manage.py b/oxdata/manage.py index b9fbfbf..2b2827f 100755 --- a/oxdata/manage.py +++ b/oxdata/manage.py @@ -1,5 +1,6 @@ #!/usr/bin/env python import os +import sys root_dir = os.path.normpath(os.path.abspath(os.path.dirname(__file__))) os.chdir(root_dir) @@ -8,13 +9,8 @@ os.chdir(root_dir) activate_this = os.path.join(root_dir, '..', 'bin', 'activate_this.py') execfile(activate_this, dict(__file__=activate_this)) -from django.core.management import execute_manager -try: - import settings # Assumed to be in the same directory. -except ImportError: - import sys - sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__) - sys.exit(1) if __name__ == "__main__": - execute_manager(settings) + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings") + from django.core.management import execute_from_command_line + execute_from_command_line(sys.argv) diff --git a/oxdata/movie/models.py b/oxdata/movie/models.py index de28a57..9dafe7b 100644 --- a/oxdata/movie/models.py +++ b/oxdata/movie/models.py @@ -11,7 +11,7 @@ import base64 from django.db import models from django.conf import settings import ox -from ox.django.fields import DictField +from oxdjango.fields import DictField from lookup.models import get_movie_id from poster.models import getPosters @@ -238,10 +238,11 @@ def get_new_ids(timeout=-1): ids = re.compile('http://www.imdb.com/title/tt(\d{7})/combined').findall(s) added = 0 for i in frozenset(ids) - known_ids: - m = Imdb(imdb=i) + m, created = Imdb.objects.get_or_create(imdb=i) m.update() print m - added += 1 + if created: + added += 1 if added: print url, added diff --git a/oxdata/movie/views.py b/oxdata/movie/views.py index b0740d9..b6b83cc 100644 --- a/oxdata/movie/views.py +++ b/oxdata/movie/views.py @@ -2,12 +2,11 @@ # vi:si:et:sw=4:sts=4:ts=4 from __future__ import division -import re -from ox.django.shortcuts import render_to_json_response, json_response +from oxdjango.shortcuts import render_to_json_response, json_response import ox.web.imdb -from api.actions import actions +from oxdjango.api import actions import models def getId(request, data): diff --git a/oxdata/api/__init__.py b/oxdata/oxdjango/__init__.py similarity index 100% rename from oxdata/api/__init__.py rename to oxdata/oxdjango/__init__.py diff --git a/oxdata/oxdjango/api/__init__.py b/oxdata/oxdjango/api/__init__.py new file mode 100644 index 0000000..cf410f8 --- /dev/null +++ b/oxdata/oxdjango/api/__init__.py @@ -0,0 +1 @@ +from actions import actions diff --git a/oxdata/api/actions.py b/oxdata/oxdjango/api/actions.py similarity index 52% rename from oxdata/api/actions.py rename to oxdata/oxdjango/api/actions.py index efca680..850ece4 100644 --- a/oxdata/api/actions.py +++ b/oxdata/oxdjango/api/actions.py @@ -1,17 +1,16 @@ # -*- coding: utf-8 -*- # vi:si:et:sw=4:sts=4:ts=4 -import sys +from __future__ import division, with_statement import inspect +import sys from django.conf import settings -from ox.django.shortcuts import render_to_json_response, json_response -from ox.utils import json - +from ..shortcuts import render_to_json_response, json_response def autodiscover(): - #register api actions from all installed apps - from django.utils.importlib import import_module + # Register api actions from all installed apps + from importlib import import_module from django.utils.module_loading import module_has_submodule for app in settings.INSTALLED_APPS: if app != 'api': @@ -50,62 +49,75 @@ def trim(docstring): class ApiActions(dict): properties = {} + versions = {} def __init__(self): def api(request, data): ''' - returns list of all known api actions - param data { - docs: bool - } - if docs is true, action properties contain docstrings - return { - status: {'code': int, 'text': string}, - data: { - actions: { - 'api': { - cache: true, - doc: 'recursion' - }, - 'hello': { - cache: true, - .. - } - ... - } - } + Returns a list of all api actions + takes { + code: boolean, // if true, return source code (optional) + docs: boolean // if true, return doc strings (optional) + } + returns { + actions: { + name: { + cache: boolean, // if false, don't cache results + code: string, // source code + doc: string // doc strings + }, + ... // more actions } + } ''' docs = data.get('docs', False) code = data.get('code', False) - _actions = self.keys() + version = getattr(request, 'version', None) + if version: + _actions = self.versions.get(version, {}).keys() + _actions = list(set(_actions + self.keys())) + else: + _actions = self.keys() _actions.sort() actions = {} for a in _actions: actions[a] = self.properties[a] if docs: - actions[a]['doc'] = self.doc(a) + actions[a]['doc'] = self.doc(a, version) if code: - actions[a]['code'] = self.code(a) + actions[a]['code'] = self.code(a, version) response = json_response({'actions': actions}) return render_to_json_response(response) self.register(api) - def doc(self, f): - return trim(self[f].__doc__) + def doc(self, name, version=None): + if version: + f = self.versions[version].get(name, self.get(name)) + else: + f = self[name] + return trim(f.__doc__) - def code(self, name): - f = self[name] + def code(self, name, version=None): + if version: + f = self.versions[version].get(name, self.get(name)) + else: + f = self[name] if name != 'api' and hasattr(f, 'func_closure') and f.func_closure: - f = f.func_closure[0].cell_contents + fc = filter(lambda c: hasattr(c.cell_contents, '__call__'), f.func_closure) + f = fc[len(fc)-1].cell_contents info = f.func_code.co_filename[len(settings.PROJECT_ROOT)+1:] info = u'%s:%s' % (info, f.func_code.co_firstlineno) return info, trim(inspect.getsource(f)) - def register(self, method, action=None, cache=True): + def register(self, method, action=None, cache=True, version=None): if not action: action = method.func_name - self[action] = method + if version: + if not version in self.versions: + self.versions[version] = {} + self.versions[version][action] = method + else: + self[action] = method self.properties[action] = {'cache': cache} def unregister(self, action): @@ -114,3 +126,10 @@ class ApiActions(dict): actions = ApiActions() +def error(request, data): + ''' + This action is used to test API error codes. It should return a 503 error. + ''' + success = error_is_success + return render_to_json_response({}) +actions.register(error) diff --git a/oxdata/oxdjango/api/urls.py b/oxdata/oxdjango/api/urls.py new file mode 100644 index 0000000..5346a98 --- /dev/null +++ b/oxdata/oxdjango/api/urls.py @@ -0,0 +1,13 @@ +# -*- coding: utf-8 -*- +# vi:si:et:sw=4:sts=4:ts=4 + +from django.conf.urls import url + +import views + +import actions +actions.autodiscover() + +urlpatterns = [ + url(r'^$', views.api), +] diff --git a/oxdata/api/views.py b/oxdata/oxdjango/api/views.py similarity index 70% rename from oxdata/api/views.py rename to oxdata/oxdjango/api/views.py index 16deb7b..43591ed 100644 --- a/oxdata/api/views.py +++ b/oxdata/oxdjango/api/views.py @@ -2,20 +2,16 @@ # vi:si:et:sw=4:sts=4:ts=4 from __future__ import division, with_statement -import os -import copy +import json from django.shortcuts import render_to_response from django.template import RequestContext from django.conf import settings -from django.db.models import Max, Sum -from ox.django.shortcuts import render_to_json_response, json_response -from ox.utils import json +from ..shortcuts import render_to_json_response, json_response from actions import actions - def api(request): if request.META['REQUEST_METHOD'] == "OPTIONS": response = render_to_json_response({'status': {'code': 200, @@ -30,8 +26,11 @@ def api(request): for f in sorted(methods): api.append({'name': f, 'doc': actions.doc(f).replace('\n', '
\n')}) - context = RequestContext(request, {'api': api, - 'sitename': settings.SITENAME}) + context = RequestContext(request, { + 'api': api, + 'settings': settings, + 'sitename': settings.SITENAME + }) return render_to_response('api.html', context) if request.META.get('CONTENT_TYPE') == 'application/json': r = json.loads(request.body) @@ -40,8 +39,11 @@ def api(request): else: action = request.POST['action'] data = json.loads(request.POST.get('data', '{}')) - - f = actions.get(action) + version = getattr(request, 'version', None) + if version: + f = actions.versions.get(version, {}).get(action, actions.get(action)) + else: + f = actions.get(action) if f: response = f(request, data) else: @@ -50,14 +52,3 @@ def api(request): response['Access-Control-Allow-Origin'] = '*' return response -def init(request, data): - return render_to_json_response(json_response()) -actions.register(init) - -def error(request, data): - ''' - this action is used to test api error codes, it should return a 503 error - ''' - success = error_is_success - return render_to_json_response({}) -actions.register(error) diff --git a/oxdata/oxdjango/decorators.py b/oxdata/oxdjango/decorators.py new file mode 100644 index 0000000..98aa9dd --- /dev/null +++ b/oxdata/oxdjango/decorators.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +# vi:si:et:sw=4:sts=4:ts=4 + +try: + from django.contrib.auth.decorators import wraps +except: + from django.utils.functional import wraps +from shortcuts import render_to_json_response + +def login_required_json(function=None): + """ + Decorator for views that checks that the user is logged in + return json error if not logged in. + """ + + def _wrapped_view(request, *args, **kwargs): + if request.user.is_authenticated(): + return function(request, *args, **kwargs) + return render_to_json_response({'status': {'code': 401, 'text': 'login required'}}) + return wraps(function)(_wrapped_view) + +def admin_required_json(function=None): + """ + Decorator for views that checks that the user is logged in + return json error if not logged in. + """ + + def _wrapped_view(request, *args, **kwargs): + if request.user.is_authenticated() and request.user.profile.get_level() == 'admin': + return function(request, *args, **kwargs) + return render_to_json_response({'status': {'code': 403, 'text': 'permission denied'}}) + return wraps(function)(_wrapped_view) diff --git a/oxdata/oxdjango/fields.py b/oxdata/oxdjango/fields.py new file mode 100644 index 0000000..31f957e --- /dev/null +++ b/oxdata/oxdjango/fields.py @@ -0,0 +1,99 @@ +# -*- coding: utf-8 -*- +# vi:si:et:sw=4:sts=4:ts=4 +import time +import datetime +import copy + +from django.db import models +from django.utils import datetime_safe +from six import string_types + +from ox.utils import json + + +def to_json(python_object): + if isinstance(python_object, datetime.datetime): + if python_object.year < 1900: + tt = python_object.timetuple() + value = '%d-%02d-%02dT%02d:%02d%02dZ' % tuple(list(tt)[:6]) + else: + value = python_object.strftime('%Y-%m-%dT%H:%M:%SZ') + return {'__class__': 'datetime.datetime', + '__value__': value} + if isinstance(python_object, datetime_safe.datetime): + return {'__class__': 'datetime.datetime', + '__value__': python_object.strftime('%Y-%m-%dT%H:%M:%SZ')} + if isinstance(python_object, time.struct_time): + return {'__class__': 'time.asctime', + '__value__': time.asctime(python_object)} + try: + if isinstance(python_object, bytes): + return {'__class__': 'bytes', + '__value__': list(python_object)} + except: + pass + raise TypeError(repr(python_object) + ' is not JSON serializable') + +def from_json(json_object): + if '__class__' in json_object: + if json_object['__class__'] == 'bytes': + return bytes(json_object['__value__']) + if json_object['__class__'] == 'datetime_safe.datetime' \ + or json_object['__class__'] == 'datetime.datetime': + return datetime_safe.datetime.strptime(json_object['__value__'], '%Y-%m-%dT%H:%M:%SZ') + if json_object['__class__'] == 'time.asctime': + return time.strptime(json_object['__value__']) + return json_object + +class DictField(models.TextField): + _type = dict + + def loads(self, value): + return json.loads(value, object_hook=from_json) + + def dumps(self, obj): + return json.dumps(obj, default=to_json, ensure_ascii=False) + + def from_db_value(self, value, expression, connection, context): + if value is None: + return value + if isinstance(value, self._type): + return value + try: + value = self.loads(value) + except: + raise Exception('failed to parse value: %s' % value) + if value is not None: + assert isinstance(value, self._type) + return value + + def get_prep_value(self, value): + if isinstance(value, self._type): + value = self.dumps(value) + if value is not None: + assert isinstance(value, string_types) + value = models.TextField.get_prep_value(self, value) + return value + + def get_default(self): + if self.has_default(): + if callable(self.default): + return self.default() + return copy.deepcopy(self.default) + return super(DictField, self).get_default() + +class TupleField(DictField): + _type = (tuple, list) + + def loads(self, value): + value = DictField.loads(self, value) + if isinstance(value, list): + value = tuple(value) + return value + +try: + from south.modelsinspector import add_introspection_rules + add_introspection_rules([], ["^oxdjango\.fields\.DictField"]) + add_introspection_rules([], ["^oxdjango\.fields\.TupleField"]) +except: + pass diff --git a/oxdata/oxdjango/http.py b/oxdata/oxdjango/http.py new file mode 100644 index 0000000..aa76a65 --- /dev/null +++ b/oxdata/oxdjango/http.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- +# vi:si:et:sw=4:sts=4:ts=4 +import os +import mimetypes +from datetime import datetime, timedelta +from six.moves.urllib.parse import quote + +from django.http import HttpResponse, Http404 +from django.conf import settings + + +def HttpFileResponse(path, content_type=None, filename=None): + if not os.path.exists(path): + raise Http404 + if not content_type: + content_type = mimetypes.guess_type(path)[0] + if not content_type: + content_type = 'application/octet-stream' + + if getattr(settings, 'XACCELREDIRECT', False): + response = HttpResponse() + response['Content-Length'] = os.stat(path).st_size + + for PREFIX in ('STATIC', 'MEDIA'): + root = getattr(settings, PREFIX+'_ROOT', '') + url = getattr(settings, PREFIX+'_URL', '') + if root and path.startswith(root): + path = url + path[len(root)+1:] + if not isinstance(path, bytes): + path = path.encode('utf-8') + response['X-Accel-Redirect'] = path + if content_type: + response['Content-Type'] = content_type + elif getattr(settings, 'XSENDFILE', False): + response = HttpResponse() + if not isinstance(path, bytes): + path = path.encode('utf-8') + response['X-Sendfile'] = path + if content_type: + response['Content-Type'] = content_type + response['Content-Length'] = os.stat(path).st_size + else: + response = HttpResponse(open(path), content_type=content_type) + if filename: + if not isinstance(filename, bytes): + filename = filename.encode('utf-8') + response['Content-Disposition'] = "attachment; filename*=UTF=8''%s" % quote(filename) + + response['Expires'] = datetime.strftime(datetime.utcnow() + timedelta(days=1), "%a, %d-%b-%Y %H:%M:%S GMT") + + def allow_access(): + for key in ('X-Accel-Redirect', 'X-Sendfile'): + if key in response: + del response[key] + response['Access-Control-Allow-Origin'] = '*' + response.allow_access = allow_access + return response + diff --git a/oxdata/oxdjango/middleware.py b/oxdata/oxdjango/middleware.py new file mode 100644 index 0000000..0eccf47 --- /dev/null +++ b/oxdata/oxdjango/middleware.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +# vi:si:et:sw=4:sts=4:ts=4 + +from .shortcuts import HttpErrorJson, render_to_json_response + +class ExceptionMiddleware(object): + def process_exception(self, request, exception): + if isinstance(exception, HttpErrorJson): + return render_to_json_response(exception.response) + return None + +class ChromeFrameMiddleware(object): + def process_response(self, request, response): + response['X-UA-Compatible'] = 'chrome=1' + return response diff --git a/oxdata/oxdjango/query.py b/oxdata/oxdjango/query.py new file mode 100644 index 0000000..5a7e43d --- /dev/null +++ b/oxdata/oxdjango/query.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- +# vi:si:et:sw=4:sts=4:ts=4 + +from django.db.models.sql import Query +from django.db.models.sql.compiler import SQLCompiler +from django.db import connections +import django.db.models.query + +''' +models.py: +----------------------------------- +from oxdjango.query import QuerySet + +class Manager(models.Manager): + def get_query_set(self): + return QuerySet(self.model) + +class Model(models.Model): + ... + objects = Manager() +''' + +class NullLastSQLCompiler(SQLCompiler): + + def get_order_by(self): + result = super(NullLastSQLCompiler, self).get_order_by() + if self.query.nulls_last and result \ + and self.connection.vendor == 'postgresql': + return [(expr, (sql + ' NULLS LAST', params, is_ref)) + for (expr, (sql, params, is_ref)) in result] + return result + +class NullsLastQuery(Query): + nulls_last = False + + def clone(self, *args, **kwargs): + obj = super(NullsLastQuery, self).clone(*args, **kwargs) + obj.nulls_last = self.nulls_last + return obj + + def get_compiler(self, using=None, connection=None): + if using is None and connection is None: + raise ValueError("Need either using or connection") + if using: + connection = connections[using] + return NullLastSQLCompiler(self, connection, using) + +class QuerySet(django.db.models.query.QuerySet): + + def __init__(self, model=None, query=None, using=None, **kwargs): + super(QuerySet, self).__init__(model=model, query=query, using=None, **kwargs) + self.query = query or NullsLastQuery(self.model) + + def order_by(self, *args, **kwargs): + nulls_last = kwargs.pop('nulls_last', False) + obj = super(QuerySet, self).order_by(*args, **kwargs) + obj.query.nulls_last = nulls_last + return obj diff --git a/oxdata/oxdjango/shortcuts.py b/oxdata/oxdjango/shortcuts.py new file mode 100644 index 0000000..3f01ed1 --- /dev/null +++ b/oxdata/oxdjango/shortcuts.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- +# vi:si:et:sw=4:sts=4:ts=4 +from __future__ import print_function +import datetime +from django.utils import datetime_safe +from django.http import HttpResponse, Http404 +import json +from django.conf import settings + +class HttpErrorJson(Http404): + def __init__(self, response): + self.response = response + +def json_response(data=None, status=200, text='ok'): + if not data: + data = {} + return {'status': {'code': status, 'text': text}, 'data': data} + +def _to_json(python_object): + if isinstance(python_object, datetime.datetime): + if python_object.year < 1900: + tt = python_object.timetuple() + return '%d-%02d-%02dT%02d:%02d%02dZ' % tuple(list(tt)[:6]) + return python_object.strftime('%Y-%m-%dT%H:%M:%SZ') + if isinstance(python_object, datetime_safe.datetime): + return python_object.strftime('%Y-%m-%dT%H:%M:%SZ') + raise TypeError(u'%s %s is not JSON serializable' % (repr(python_object), type(python_object))) + +def render_to_json_response(dictionary, content_type="text/json", status=200): + indent=None + if settings.DEBUG: + content_type = "text/javascript" + indent = 2 + if getattr(settings, 'JSON_DEBUG', False): + print(json.dumps(dictionary, indent=2, default=_to_json, ensure_ascii=False).encode('utf-8')) + + return HttpResponse(json.dumps(dictionary, indent=indent, default=_to_json, + ensure_ascii=False).encode('utf-8'), content_type=content_type, status=status) + +def get_object_or_404_json(klass, *args, **kwargs): + from django.shortcuts import _get_queryset + queryset = _get_queryset(klass) + try: + return queryset.get(*args, **kwargs) + except queryset.model.DoesNotExist: + response = {'status': {'code': 404, + 'text': '%s not found' % queryset.model._meta.object_name}} + raise HttpErrorJson(response) + diff --git a/oxdata/oxdjango/utils.py b/oxdata/oxdjango/utils.py new file mode 100644 index 0000000..60eb896 --- /dev/null +++ b/oxdata/oxdjango/utils.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +# vi:si:et:sw=4:sts=4:ts=4 +from django.http import HttpResponse,Http404 +from django.core.servers.basehttp import FileWrapper +from django.conf import settings + +import mimetypes +import os + +def basic_sendfile(fname,download_name=None): + if not os.path.exists(fname): + raise Http404 + + wrapper = FileWrapper(open(fname,"r")) + + content_type = mimetypes.guess_type(fname)[0] + response = HttpResponse(wrapper, content_type=content_type) + response['Content-Length'] = os.path.getsize(fname) + + if download_name: + response['Content-Disposition'] = "attachment; filename=%s"%download_name + + return response + +def x_sendfile(fname,download_name=None): + if not os.path.exists(fname): + raise Http404 + + content_type = mimetypes.guess_type(fname)[0] + response = HttpResponse('', content_type=content_type) + response['Content-Length'] = os.path.getsize(fname) + response['X-Sendfile'] = fname + + if download_name: + response['Content-Disposition'] = "attachment; filename=%s"%download_name + + return response + +try: + __sendfile = getattr(settings,'SENDFILE',False) == 'x_sendfile' +except: + __sendfile = False +if __sendfile == 'x_sendfile': + sendfile = x_sendfile +else: + sendfile = basic_sendfile + diff --git a/oxdata/poster/migrations/0001_initial.py b/oxdata/poster/migrations/0001_initial.py index 55414f2..9d8ea93 100644 --- a/oxdata/poster/migrations/0001_initial.py +++ b/oxdata/poster/migrations/0001_initial.py @@ -1,90 +1,38 @@ # -*- coding: utf-8 -*- +# Generated by Django 1.9.4 on 2016-03-06 10:11 +from __future__ import unicode_literals -from south.db import db -from django.db import models -from oxdata.poster.models import * +from django.db import migrations, models +import django.db.models.deletion +import poster.models -class Migration: - - def forwards(self, orm): - - # Adding model 'PosterCache' - db.create_table('poster_postercache', ( - ('id', orm['poster.PosterCache:id']), - ('created', orm['poster.PosterCache:created']), - ('modified', orm['poster.PosterCache:modified']), - ('movie_id', orm['poster.PosterCache:movie_id']), - ('url', orm['poster.PosterCache:url']), - ('site', orm['poster.PosterCache:site']), - ('site_id', orm['poster.PosterCache:site_id']), - ('image', orm['poster.PosterCache:image']), - ('failed', orm['poster.PosterCache:failed']), - )) - db.send_create_signal('poster', ['PosterCache']) - - # Adding model 'Poster' - db.create_table('poster_poster', ( - ('id', orm['poster.Poster:id']), - ('created', orm['poster.Poster:created']), - ('modified', orm['poster.Poster:modified']), - ('movie_id', orm['poster.Poster:movie_id']), - ('poster', orm['poster.Poster:poster']), - ('upload', orm['poster.Poster:upload']), - ('oxdb', orm['poster.Poster:oxdb']), - )) - db.send_create_signal('poster', ['Poster']) - - - - def backwards(self, orm): - - # Deleting model 'PosterCache' - db.delete_table('poster_postercache') - - # Deleting model 'Poster' - db.delete_table('poster_poster') - - - - models = { - 'lookup.movieid': { - 'amg_id': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'unique': 'True', 'null': 'True', 'blank': 'True'}), - 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'criterion_id': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'unique': 'True', 'null': 'True', 'blank': 'True'}), - 'director': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1000', 'blank': 'True'}), - 'episode': ('django.db.models.fields.IntegerField', [], {'default': '-1'}), - 'episode_title': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1000', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'imdb_id': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '7', 'unique': 'True', 'null': 'True', 'blank': 'True'}), - 'impawards_id': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'unique': 'True', 'null': 'True', 'blank': 'True'}), - 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), - 'oxdb_id': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '42', 'unique': 'True', 'null': 'True', 'blank': 'True'}), - 'season': ('django.db.models.fields.IntegerField', [], {'default': '-1'}), - 'series_title': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1000', 'blank': 'True'}), - 'title': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1000', 'blank': 'True'}), - 'wikipedia_id': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'unique': 'True', 'null': 'True', 'blank': 'True'}), - 'year': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '4', 'blank': 'True'}) - }, - 'poster.poster': { - 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), - 'movie_id': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'poster'", 'to': "orm['lookup.MovieId']"}), - 'oxdb': ('django.db.models.fields.files.ImageField', [], {'max_length': '255'}), - 'poster': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['poster.PosterCache']", 'blank': 'True'}), - 'upload': ('django.db.models.fields.files.ImageField', [], {'max_length': '255'}) - }, - 'poster.postercache': { - 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'failed': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '255'}), - 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), - 'movie_id': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'postercache'", 'to': "orm['lookup.MovieId']"}), - 'site': ('django.db.models.fields.CharField', [], {'max_length': '255'}), - 'site_id': ('django.db.models.fields.CharField', [], {'max_length': '42'}), - 'url': ('django.db.models.fields.CharField', [], {'max_length': '1024'}) - } - } - - complete_apps = ['poster'] + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('lookup', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='PosterCache', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created', models.DateTimeField(auto_now_add=True)), + ('modified', models.DateTimeField(auto_now=True)), + ('url', models.CharField(max_length=1024)), + ('site', models.CharField(max_length=255)), + ('site_id', models.CharField(max_length=1024)), + ('image', models.ImageField(max_length=255, upload_to=poster.models.postercache_path)), + ('status', models.CharField(default=b'200', max_length=1024)), + ('failed', models.BooleanField(default=False)), + ('movie_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='postercache', to='lookup.MovieId')), + ], + ), + migrations.AlterUniqueTogether( + name='postercache', + unique_together=set([('movie_id', 'url')]), + ), + ] diff --git a/oxdata/poster/migrations/0002_reposter.py b/oxdata/poster/migrations/0002_reposter.py deleted file mode 100644 index 0236f63..0000000 --- a/oxdata/poster/migrations/0002_reposter.py +++ /dev/null @@ -1,92 +0,0 @@ -# encoding: utf-8 -import datetime -from south.db import db -from south.v2 import SchemaMigration -from django.db import models - -class Migration(SchemaMigration): - - def forwards(self, orm): - - # Adding field 'PosterCache.status' - db.add_column('poster_postercache', 'status', self.gf('django.db.models.fields.CharField')(default='200', max_length=1024), keep_default=False) - - # Deleting field 'poster.upload' - db.delete_column('poster_poster', 'upload') - - # Deleting field 'poster.oxdb' - db.delete_column('poster_poster', 'oxdb') - - # Renaming column for 'Poster.poster' to match new field type. - db.rename_column('poster_poster', 'poster_id', 'poster') - # Changing field 'Poster.poster' - db.add_column('poster_poster', 'poster', self.gf('django.db.models.fields.files.ImageField')(max_length=255)) - - # Removing index on 'poster', fields ['poster'] - db.delete_index('poster_poster', ['poster_id']) - - - def backwards(self, orm): - - # Deleting field 'PosterCache.status' - db.delete_column('poster_postercache', 'status') - - # Adding field 'poster.upload' - db.add_column('poster_poster', 'upload', self.gf('django.db.models.fields.files.ImageField')(default=None, max_length=255), keep_default=False) - - # Adding field 'poster.oxdb' - db.add_column('poster_poster', 'oxdb', self.gf('django.db.models.fields.files.ImageField')(default=None, max_length=255), keep_default=False) - - # Renaming column for 'Poster.poster' to match new field type. - db.rename_column('poster_poster', 'poster', 'poster_id') - # Changing field 'Poster.poster' - db.alter_column('poster_poster', 'poster_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['poster.PosterCache'], blank=True)) - - # Adding index on 'poster', fields ['poster'] - db.create_index('poster_poster', ['poster_id']) - - - models = { - 'lookup.movieid': { - 'Meta': {'object_name': 'MovieId'}, - 'amg_id': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'unique': 'True', 'null': 'True', 'blank': 'True'}), - 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'criterion_id': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'unique': 'True', 'null': 'True', 'blank': 'True'}), - 'director': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1000', 'blank': 'True'}), - 'episode': ('django.db.models.fields.IntegerField', [], {'default': '-1'}), - 'episode_title': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1000', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'imdb_id': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '7', 'unique': 'True', 'null': 'True', 'blank': 'True'}), - 'impawards_id': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'unique': 'True', 'null': 'True', 'blank': 'True'}), - 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), - 'oxdb_id': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '42', 'unique': 'True', 'null': 'True', 'blank': 'True'}), - 'season': ('django.db.models.fields.IntegerField', [], {'default': '-1'}), - 'series_title': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1000', 'blank': 'True'}), - 'title': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1000', 'blank': 'True'}), - 'wikipedia_id': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'unique': 'True', 'null': 'True', 'blank': 'True'}), - 'year': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '4', 'blank': 'True'}) - }, - 'poster.poster': { - 'Meta': {'object_name': 'Poster'}, - 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), - 'movie_id': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'poster'", 'to': "orm['lookup.MovieId']"}), - 'poster': ('django.db.models.fields.files.ImageField', [], {'max_length': '255'}) - }, - 'poster.postercache': { - 'Meta': {'object_name': 'PosterCache'}, - 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'failed': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '255'}), - 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), - 'movie_id': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'postercache'", 'to': "orm['lookup.MovieId']"}), - 'site': ('django.db.models.fields.CharField', [], {'max_length': '255'}), - 'site_id': ('django.db.models.fields.CharField', [], {'max_length': '42'}), - 'status': ('django.db.models.fields.CharField', [], {'default': "'200'", 'max_length': '1024'}), - 'url': ('django.db.models.fields.CharField', [], {'max_length': '1024'}) - } - } - - complete_apps = ['poster'] diff --git a/oxdata/poster/migrations/0003_poster_url_unique.py b/oxdata/poster/migrations/0003_poster_url_unique.py deleted file mode 100644 index c087fbb..0000000 --- a/oxdata/poster/migrations/0003_poster_url_unique.py +++ /dev/null @@ -1,64 +0,0 @@ -# encoding: utf-8 -import datetime -from south.db import db -from south.v2 import SchemaMigration -from django.db import models - -class Migration(SchemaMigration): - - def forwards(self, orm): - - # Adding unique constraint on 'PosterCache', fields ['url'] - db.create_unique('poster_postercache', ['url']) - - - def backwards(self, orm): - - # Removing unique constraint on 'PosterCache', fields ['url'] - db.delete_unique('poster_postercache', ['url']) - - - models = { - 'lookup.movieid': { - 'Meta': {'object_name': 'MovieId'}, - 'amg_id': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'unique': 'True', 'null': 'True', 'blank': 'True'}), - 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'criterion_id': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'unique': 'True', 'null': 'True', 'blank': 'True'}), - 'director': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1000', 'blank': 'True'}), - 'episode': ('django.db.models.fields.IntegerField', [], {'default': '-1'}), - 'episode_title': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1000', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'imdb_id': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '7', 'unique': 'True', 'null': 'True', 'blank': 'True'}), - 'impawards_id': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'unique': 'True', 'null': 'True', 'blank': 'True'}), - 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), - 'oxdb_id': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '42', 'unique': 'True', 'null': 'True', 'blank': 'True'}), - 'season': ('django.db.models.fields.IntegerField', [], {'default': '-1'}), - 'series_title': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1000', 'blank': 'True'}), - 'title': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1000', 'blank': 'True'}), - 'wikipedia_id': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'unique': 'True', 'null': 'True', 'blank': 'True'}), - 'year': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '4', 'blank': 'True'}) - }, - 'poster.poster': { - 'Meta': {'object_name': 'Poster'}, - 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), - 'movie_id': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'poster'", 'to': "orm['lookup.MovieId']"}), - 'poster': ('django.db.models.fields.files.ImageField', [], {'max_length': '255'}) - }, - 'poster.postercache': { - 'Meta': {'object_name': 'PosterCache'}, - 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'failed': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '255'}), - 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), - 'movie_id': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'postercache'", 'to': "orm['lookup.MovieId']"}), - 'site': ('django.db.models.fields.CharField', [], {'max_length': '255'}), - 'site_id': ('django.db.models.fields.CharField', [], {'max_length': '42'}), - 'status': ('django.db.models.fields.CharField', [], {'default': "'200'", 'max_length': '1024'}), - 'url': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '1024'}) - } - } - - complete_apps = ['poster'] diff --git a/oxdata/poster/migrations/0004_unique_urls.py b/oxdata/poster/migrations/0004_unique_urls.py deleted file mode 100644 index 13df6b1..0000000 --- a/oxdata/poster/migrations/0004_unique_urls.py +++ /dev/null @@ -1,70 +0,0 @@ -# encoding: utf-8 -import datetime -from south.db import db -from south.v2 import SchemaMigration -from django.db import models - -class Migration(SchemaMigration): - - def forwards(self, orm): - - # Removing unique constraint on 'PosterCache', fields ['url'] - db.delete_unique('poster_postercache', ['url']) - - # Adding unique constraint on 'PosterCache', fields ['url', 'movie_id'] - db.create_unique('poster_postercache', ['url', 'movie_id_id']) - - - def backwards(self, orm): - - # Adding unique constraint on 'PosterCache', fields ['url'] - db.create_unique('poster_postercache', ['url']) - - # Removing unique constraint on 'PosterCache', fields ['url', 'movie_id'] - db.delete_unique('poster_postercache', ['url', 'movie_id_id']) - - - models = { - 'lookup.movieid': { - 'Meta': {'object_name': 'MovieId'}, - 'amg_id': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'unique': 'True', 'null': 'True', 'blank': 'True'}), - 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'criterion_id': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'unique': 'True', 'null': 'True', 'blank': 'True'}), - 'director': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1000', 'blank': 'True'}), - 'episode': ('django.db.models.fields.IntegerField', [], {'default': '-1'}), - 'episode_title': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1000', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'imdb_id': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '7', 'unique': 'True', 'null': 'True', 'blank': 'True'}), - 'impawards_id': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'unique': 'True', 'null': 'True', 'blank': 'True'}), - 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), - 'oxdb_id': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '42', 'unique': 'True', 'null': 'True', 'blank': 'True'}), - 'season': ('django.db.models.fields.IntegerField', [], {'default': '-1'}), - 'series_title': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1000', 'blank': 'True'}), - 'title': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1000', 'blank': 'True'}), - 'wikipedia_id': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'unique': 'True', 'null': 'True', 'blank': 'True'}), - 'year': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '4', 'blank': 'True'}) - }, - 'poster.poster': { - 'Meta': {'object_name': 'Poster'}, - 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), - 'movie_id': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'poster'", 'to': "orm['lookup.MovieId']"}), - 'poster': ('django.db.models.fields.files.ImageField', [], {'max_length': '255'}) - }, - 'poster.postercache': { - 'Meta': {'unique_together': "(('movie_id', 'url'),)", 'object_name': 'PosterCache'}, - 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'failed': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '255'}), - 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), - 'movie_id': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'postercache'", 'to': "orm['lookup.MovieId']"}), - 'site': ('django.db.models.fields.CharField', [], {'max_length': '255'}), - 'site_id': ('django.db.models.fields.CharField', [], {'max_length': '42'}), - 'status': ('django.db.models.fields.CharField', [], {'default': "'200'", 'max_length': '1024'}), - 'url': ('django.db.models.fields.CharField', [], {'max_length': '1024'}) - } - } - - complete_apps = ['poster'] diff --git a/oxdata/poster/models.py b/oxdata/poster/models.py index 65d3a4f..6ac39d6 100644 --- a/oxdata/poster/models.py +++ b/oxdata/poster/models.py @@ -7,11 +7,7 @@ import hashlib import socket import urllib2 -from django.conf import settings from django.db import models -from django.db.models import Q -from django.contrib.auth.models import User -from django.core.files.base import ContentFile import ox import ox.web.criterion @@ -21,7 +17,7 @@ import ox.web.impawards import ox.web.apple import ox.web.piratecinema -from oxdata.lookup.models import MovieId +from lookup.models import MovieId def getPosters(movie_id, url_prefix='', limit=lambda x, y: 0.3 < x/y < 1): if not movie_id: @@ -55,6 +51,9 @@ def poster_path(url, filename): name = "%s.%s" % (h, ext) return os.path.join('posters', h[:2], h[2:4], h[4:6], name) +def postercache_path(i, f): + return poster_path(i.url.encode('utf-8'), f) + class PosterCache(models.Model): class Meta: unique_together = ("movie_id", "url") @@ -66,7 +65,7 @@ class PosterCache(models.Model): url = models.CharField(max_length=1024) site = models.CharField(max_length=255) site_id = models.CharField(max_length=1024) - image = models.ImageField(max_length=255, upload_to=lambda i, f: poster_path(i.url.encode('utf-8'), f)) + image = models.ImageField(max_length=255, upload_to=postercache_path) status = models.CharField(max_length=1024, default='200') failed = models.BooleanField(default=False) diff --git a/oxdata/poster/urls.py b/oxdata/poster/urls.py index b9d0d22..b6d11e8 100644 --- a/oxdata/poster/urls.py +++ b/oxdata/poster/urls.py @@ -1,6 +1,8 @@ -from django.conf.urls.defaults import * +from django.conf.urls import url -urlpatterns = patterns('oxdata.poster.views', - (r'^$', 'poster'), -) +import views + +urlpatterns = [ + url(r'^$', views.poster), +] diff --git a/oxdata/poster/views.py b/oxdata/poster/views.py index 50cbcf8..feaf961 100644 --- a/oxdata/poster/views.py +++ b/oxdata/poster/views.py @@ -1,19 +1,12 @@ # -*- coding: utf-8 -*- # vi:si:et:sw=4:sts=4:ts=4 -import os.path -from django.db import models -from django.db.models import Q -from django.contrib.auth.models import User -from django.shortcuts import render_to_response, get_object_or_404, get_list_or_404 -from django.template import RequestContext +from oxdjango.shortcuts import render_to_json_response -from ox.django.shortcuts import render_to_json_response -from oxdata.lookup.models import MovieId - -from oxdata.lookup.views import get_movie_id +from lookup.views import get_movie_id import models + def poster(request): movie_id = get_movie_id(request) json = models.getPosters(movie_id, request.build_absolute_uri('/')) diff --git a/oxdata/settings.py b/oxdata/settings.py index 6806823..a8c9776 100644 --- a/oxdata/settings.py +++ b/oxdata/settings.py @@ -2,11 +2,13 @@ # vi:si:et:sw=4:sts=4:ts=4 # Django settings for oxdata project. import os -from os.path import join +from os.path import join, normpath, dirname +import djcelery +djcelery.setup_loader() SITENAME = 'oxdata' -PROJECT_ROOT = os.path.normpath(os.path.dirname(__file__)) +BASE_DIR = PROJECT_ROOT = normpath(dirname(__file__)) DEBUG = False TEMPLATE_DEBUG = DEBUG @@ -30,7 +32,7 @@ DATABASES = { # although not all choices may be available on all operating systems. # If running in a Windows environment this must be set to the same as your # system time zone. -TIME_ZONE = 'Europe/Berlin' +TIME_ZONE = 'UTC' # Language code for this installation. All choices can be found here: # http://www.i18nguy.com/unicode/language-identifiers.html @@ -58,6 +60,23 @@ STATIC_ROOT = join(PROJECT_ROOT, 'static') DATA_ROOT = join(PROJECT_ROOT, 'data') CACHE_ROOT = join(PROJECT_ROOT, 'cache') +STATIC_URL = '/static/' + +# Additional locations of static files +STATICFILES_DIRS = ( + # Put strings here, like "/home/html/static" or "C:/www/django/static". + # Always use forward slashes, even on Windows. + # Don't forget to use absolute paths, not relative paths. +) + +# List of finder classes that know how to find static files in +# various locations. +STATICFILES_FINDERS = ( + 'django.contrib.staticfiles.finders.FileSystemFinder', + 'django.contrib.staticfiles.finders.AppDirectoriesFinder', + #'django.contrib.staticfiles.finders.DefaultStorageFinder', +) + os.environ['oxCACHE'] = 'fs:' + CACHE_ROOT @@ -71,38 +90,45 @@ MEDIA_URL = '/media/' # Examples: "http://foo.com/media/", "/media/". ADMIN_MEDIA_PREFIX = '/admin/media/' -# List of callables that know how to import templates from various sources. -TEMPLATE_LOADERS = ( - 'django.template.loaders.filesystem.Loader', - 'django.template.loaders.app_directories.Loader', - 'django.template.loaders.eggs.Loader', -) - +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [ + join(PROJECT_ROOT, 'templates'), + ], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] MIDDLEWARE_CLASSES = ( 'django.middleware.common.CommonMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', ) -ROOT_URLCONF = 'oxdata.urls' - -TEMPLATE_DIRS = ( - join(PROJECT_ROOT, 'templates'), -) +ROOT_URLCONF = 'urls' INSTALLED_APPS = ( 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.sites', + 'django.contrib.messages', + 'django.contrib.staticfiles', 'django.contrib.admin', 'django.contrib.humanize', - 'south', 'django_extensions', 'djcelery', - 'api', 'lookup', 'movie', 'poster', @@ -112,12 +138,12 @@ INSTALLED_APPS = ( LOGIN_REDIRECT_URL='/' -CELERY_RESULT_BACKEND = "database" -BROKER_HOST = "127.0.0.1" -BROKER_PORT = 5672 -BROKER_USER = "oxdata" -BROKER_PASSWORD = "ox" -BROKER_VHOST = "/oxdata" +CELERY_RESULT_BACKEND = 'database' +CELERY_TASK_SERIALIZER = 'json' +CELERY_RESULT_SERIALIZER = 'json' +CELERY_ACCEPT_CONTENT = ['json'] + +BROKER_URL = 'amqp://oxdata:ox@localhost:5672//odata' #Movie related settings REVIEW_WHITELIST = { diff --git a/oxdata/static/js/jquery.js b/oxdata/static/js/jquery.js deleted file mode 100644 index b1ae21d..0000000 --- a/oxdata/static/js/jquery.js +++ /dev/null @@ -1,19 +0,0 @@ -/* - * jQuery JavaScript Library v1.3.2 - * http://jquery.com/ - * - * Copyright (c) 2009 John Resig - * Dual licensed under the MIT and GPL licenses. - * http://docs.jquery.com/License - * - * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009) - * Revision: 6246 - */ -(function(){var l=this,g,y=l.jQuery,p=l.$,o=l.jQuery=l.$=function(E,F){return new o.fn.init(E,F)},D=/^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,f=/^.[^:#\[\.,]*$/;o.fn=o.prototype={init:function(E,H){E=E||document;if(E.nodeType){this[0]=E;this.length=1;this.context=E;return this}if(typeof E==="string"){var G=D.exec(E);if(G&&(G[1]||!H)){if(G[1]){E=o.clean([G[1]],H)}else{var I=document.getElementById(G[3]);if(I&&I.id!=G[3]){return o().find(E)}var F=o(I||[]);F.context=document;F.selector=E;return F}}else{return o(H).find(E)}}else{if(o.isFunction(E)){return o(document).ready(E)}}if(E.selector&&E.context){this.selector=E.selector;this.context=E.context}return this.setArray(o.isArray(E)?E:o.makeArray(E))},selector:"",jquery:"1.3.2",size:function(){return this.length},get:function(E){return E===g?Array.prototype.slice.call(this):this[E]},pushStack:function(F,H,E){var G=o(F);G.prevObject=this;G.context=this.context;if(H==="find"){G.selector=this.selector+(this.selector?" ":"")+E}else{if(H){G.selector=this.selector+"."+H+"("+E+")"}}return G},setArray:function(E){this.length=0;Array.prototype.push.apply(this,E);return this},each:function(F,E){return o.each(this,F,E)},index:function(E){return o.inArray(E&&E.jquery?E[0]:E,this)},attr:function(F,H,G){var E=F;if(typeof F==="string"){if(H===g){return this[0]&&o[G||"attr"](this[0],F)}else{E={};E[F]=H}}return this.each(function(I){for(F in E){o.attr(G?this.style:this,F,o.prop(this,E[F],G,I,F))}})},css:function(E,F){if((E=="width"||E=="height")&&parseFloat(F)<0){F=g}return this.attr(E,F,"curCSS")},text:function(F){if(typeof F!=="object"&&F!=null){return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(F))}var E="";o.each(F||this,function(){o.each(this.childNodes,function(){if(this.nodeType!=8){E+=this.nodeType!=1?this.nodeValue:o.fn.text([this])}})});return E},wrapAll:function(E){if(this[0]){var F=o(E,this[0].ownerDocument).clone();if(this[0].parentNode){F.insertBefore(this[0])}F.map(function(){var G=this;while(G.firstChild){G=G.firstChild}return G}).append(this)}return this},wrapInner:function(E){return this.each(function(){o(this).contents().wrapAll(E)})},wrap:function(E){return this.each(function(){o(this).wrapAll(E)})},append:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.appendChild(E)}})},prepend:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.insertBefore(E,this.firstChild)}})},before:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this)})},after:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this.nextSibling)})},end:function(){return this.prevObject||o([])},push:[].push,sort:[].sort,splice:[].splice,find:function(E){if(this.length===1){var F=this.pushStack([],"find",E);F.length=0;o.find(E,this[0],F);return F}else{return this.pushStack(o.unique(o.map(this,function(G){return o.find(E,G)})),"find",E)}},clone:function(G){var E=this.map(function(){if(!o.support.noCloneEvent&&!o.isXMLDoc(this)){var I=this.outerHTML;if(!I){var J=this.ownerDocument.createElement("div");J.appendChild(this.cloneNode(true));I=J.innerHTML}return o.clean([I.replace(/ jQuery\d+="(?:\d+|null)"/g,"").replace(/^\s*/,"")])[0]}else{return this.cloneNode(true)}});if(G===true){var H=this.find("*").andSelf(),F=0;E.find("*").andSelf().each(function(){if(this.nodeName!==H[F].nodeName){return}var I=o.data(H[F],"events");for(var K in I){for(var J in I[K]){o.event.add(this,K,I[K][J],I[K][J].data)}}F++})}return E},filter:function(E){return this.pushStack(o.isFunction(E)&&o.grep(this,function(G,F){return E.call(G,F)})||o.multiFilter(E,o.grep(this,function(F){return F.nodeType===1})),"filter",E)},closest:function(E){var G=o.expr.match.POS.test(E)?o(E):null,F=0;return this.map(function(){var H=this;while(H&&H.ownerDocument){if(G?G.index(H)>-1:o(H).is(E)){o.data(H,"closest",F);return H}H=H.parentNode;F++}})},not:function(E){if(typeof E==="string"){if(f.test(E)){return this.pushStack(o.multiFilter(E,this,true),"not",E)}else{E=o.multiFilter(E,this)}}var F=E.length&&E[E.length-1]!==g&&!E.nodeType;return this.filter(function(){return F?o.inArray(this,E)<0:this!=E})},add:function(E){return this.pushStack(o.unique(o.merge(this.get(),typeof E==="string"?o(E):o.makeArray(E))))},is:function(E){return !!E&&o.multiFilter(E,this).length>0},hasClass:function(E){return !!E&&this.is("."+E)},val:function(K){if(K===g){var E=this[0];if(E){if(o.nodeName(E,"option")){return(E.attributes.value||{}).specified?E.value:E.text}if(o.nodeName(E,"select")){var I=E.selectedIndex,L=[],M=E.options,H=E.type=="select-one";if(I<0){return null}for(var F=H?I:0,J=H?I+1:M.length;F=0||o.inArray(this.name,K)>=0)}else{if(o.nodeName(this,"select")){var N=o.makeArray(K);o("option",this).each(function(){this.selected=(o.inArray(this.value,N)>=0||o.inArray(this.text,N)>=0)});if(!N.length){this.selectedIndex=-1}}else{this.value=K}}})},html:function(E){return E===g?(this[0]?this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g,""):null):this.empty().append(E)},replaceWith:function(E){return this.after(E).remove()},eq:function(E){return this.slice(E,+E+1)},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments),"slice",Array.prototype.slice.call(arguments).join(","))},map:function(E){return this.pushStack(o.map(this,function(G,F){return E.call(G,F,G)}))},andSelf:function(){return this.add(this.prevObject)},domManip:function(J,M,L){if(this[0]){var I=(this[0].ownerDocument||this[0]).createDocumentFragment(),F=o.clean(J,(this[0].ownerDocument||this[0]),I),H=I.firstChild;if(H){for(var G=0,E=this.length;G1||G>0?I.cloneNode(true):I)}}if(F){o.each(F,z)}}return this;function K(N,O){return M&&o.nodeName(N,"table")&&o.nodeName(O,"tr")?(N.getElementsByTagName("tbody")[0]||N.appendChild(N.ownerDocument.createElement("tbody"))):N}}};o.fn.init.prototype=o.fn;function z(E,F){if(F.src){o.ajax({url:F.src,async:false,dataType:"script"})}else{o.globalEval(F.text||F.textContent||F.innerHTML||"")}if(F.parentNode){F.parentNode.removeChild(F)}}function e(){return +new Date}o.extend=o.fn.extend=function(){var J=arguments[0]||{},H=1,I=arguments.length,E=false,G;if(typeof J==="boolean"){E=J;J=arguments[1]||{};H=2}if(typeof J!=="object"&&!o.isFunction(J)){J={}}if(I==H){J=this;--H}for(;H-1}},swap:function(H,G,I){var E={};for(var F in G){E[F]=H.style[F];H.style[F]=G[F]}I.call(H);for(var F in G){H.style[F]=E[F]}},css:function(H,F,J,E){if(F=="width"||F=="height"){var L,G={position:"absolute",visibility:"hidden",display:"block"},K=F=="width"?["Left","Right"]:["Top","Bottom"];function I(){L=F=="width"?H.offsetWidth:H.offsetHeight;if(E==="border"){return}o.each(K,function(){if(!E){L-=parseFloat(o.curCSS(H,"padding"+this,true))||0}if(E==="margin"){L+=parseFloat(o.curCSS(H,"margin"+this,true))||0}else{L-=parseFloat(o.curCSS(H,"border"+this+"Width",true))||0}})}if(H.offsetWidth!==0){I()}else{o.swap(H,G,I)}return Math.max(0,Math.round(L))}return o.curCSS(H,F,J)},curCSS:function(I,F,G){var L,E=I.style;if(F=="opacity"&&!o.support.opacity){L=o.attr(E,"opacity");return L==""?"1":L}if(F.match(/float/i)){F=w}if(!G&&E&&E[F]){L=E[F]}else{if(q.getComputedStyle){if(F.match(/float/i)){F="float"}F=F.replace(/([A-Z])/g,"-$1").toLowerCase();var M=q.getComputedStyle(I,null);if(M){L=M.getPropertyValue(F)}if(F=="opacity"&&L==""){L="1"}}else{if(I.currentStyle){var J=F.replace(/\-(\w)/g,function(N,O){return O.toUpperCase()});L=I.currentStyle[F]||I.currentStyle[J];if(!/^\d+(px)?$/i.test(L)&&/^\d/.test(L)){var H=E.left,K=I.runtimeStyle.left;I.runtimeStyle.left=I.currentStyle.left;E.left=L||0;L=E.pixelLeft+"px";E.left=H;I.runtimeStyle.left=K}}}}return L},clean:function(F,K,I){K=K||document;if(typeof K.createElement==="undefined"){K=K.ownerDocument||K[0]&&K[0].ownerDocument||document}if(!I&&F.length===1&&typeof F[0]==="string"){var H=/^<(\w+)\s*\/?>$/.exec(F[0]);if(H){return[K.createElement(H[1])]}}var G=[],E=[],L=K.createElement("div");o.each(F,function(P,S){if(typeof S==="number"){S+=""}if(!S){return}if(typeof S==="string"){S=S.replace(/(<(\w+)[^>]*?)\/>/g,function(U,V,T){return T.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?U:V+">"});var O=S.replace(/^\s+/,"").substring(0,10).toLowerCase();var Q=!O.indexOf("",""]||!O.indexOf("",""]||O.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"","
"]||!O.indexOf("",""]||(!O.indexOf("",""]||!O.indexOf("",""]||!o.support.htmlSerialize&&[1,"div
","
"]||[0,"",""];L.innerHTML=Q[1]+S+Q[2];while(Q[0]--){L=L.lastChild}if(!o.support.tbody){var R=/"&&!R?L.childNodes:[];for(var M=N.length-1;M>=0;--M){if(o.nodeName(N[M],"tbody")&&!N[M].childNodes.length){N[M].parentNode.removeChild(N[M])}}}if(!o.support.leadingWhitespace&&/^\s/.test(S)){L.insertBefore(K.createTextNode(S.match(/^\s*/)[0]),L.firstChild)}S=o.makeArray(L.childNodes)}if(S.nodeType){G.push(S)}else{G=o.merge(G,S)}});if(I){for(var J=0;G[J];J++){if(o.nodeName(G[J],"script")&&(!G[J].type||G[J].type.toLowerCase()==="text/javascript")){E.push(G[J].parentNode?G[J].parentNode.removeChild(G[J]):G[J])}else{if(G[J].nodeType===1){G.splice.apply(G,[J+1,0].concat(o.makeArray(G[J].getElementsByTagName("script"))))}I.appendChild(G[J])}}return E}return G},attr:function(J,G,K){if(!J||J.nodeType==3||J.nodeType==8){return g}var H=!o.isXMLDoc(J),L=K!==g;G=H&&o.props[G]||G;if(J.tagName){var F=/href|src|style/.test(G);if(G=="selected"&&J.parentNode){J.parentNode.selectedIndex}if(G in J&&H&&!F){if(L){if(G=="type"&&o.nodeName(J,"input")&&J.parentNode){throw"type property can't be changed"}J[G]=K}if(o.nodeName(J,"form")&&J.getAttributeNode(G)){return J.getAttributeNode(G).nodeValue}if(G=="tabIndex"){var I=J.getAttributeNode("tabIndex");return I&&I.specified?I.value:J.nodeName.match(/(button|input|object|select|textarea)/i)?0:J.nodeName.match(/^(a|area)$/i)&&J.href?0:g}return J[G]}if(!o.support.style&&H&&G=="style"){return o.attr(J.style,"cssText",K)}if(L){J.setAttribute(G,""+K)}var E=!o.support.hrefNormalized&&H&&F?J.getAttribute(G,2):J.getAttribute(G);return E===null?g:E}if(!o.support.opacity&&G=="opacity"){if(L){J.zoom=1;J.filter=(J.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(K)+""=="NaN"?"":"alpha(opacity="+K*100+")")}return J.filter&&J.filter.indexOf("opacity=")>=0?(parseFloat(J.filter.match(/opacity=([^)]*)/)[1])/100)+"":""}G=G.replace(/-([a-z])/ig,function(M,N){return N.toUpperCase()});if(L){J[G]=K}return J[G]},trim:function(E){return(E||"").replace(/^\s+|\s+$/g,"")},makeArray:function(G){var E=[];if(G!=null){var F=G.length;if(F==null||typeof G==="string"||o.isFunction(G)||G.setInterval){E[0]=G}else{while(F){E[--F]=G[F]}}}return E},inArray:function(G,H){for(var E=0,F=H.length;E0?this.clone(true):this).get();o.fn[F].apply(o(L[K]),I);J=J.concat(I)}return this.pushStack(J,E,G)}});o.each({removeAttr:function(E){o.attr(this,E,"");if(this.nodeType==1){this.removeAttribute(E)}},addClass:function(E){o.className.add(this,E)},removeClass:function(E){o.className.remove(this,E)},toggleClass:function(F,E){if(typeof E!=="boolean"){E=!o.className.has(this,F)}o.className[E?"add":"remove"](this,F)},remove:function(E){if(!E||o.filter(E,[this]).length){o("*",this).add([this]).each(function(){o.event.remove(this);o.removeData(this)});if(this.parentNode){this.parentNode.removeChild(this)}}},empty:function(){o(this).children().remove();while(this.firstChild){this.removeChild(this.firstChild)}}},function(E,F){o.fn[E]=function(){return this.each(F,arguments)}});function j(E,F){return E[0]&&parseInt(o.curCSS(E[0],F,true),10)||0}var h="jQuery"+e(),v=0,A={};o.extend({cache:{},data:function(F,E,G){F=F==l?A:F;var H=F[h];if(!H){H=F[h]=++v}if(E&&!o.cache[H]){o.cache[H]={}}if(G!==g){o.cache[H][E]=G}return E?o.cache[H][E]:H},removeData:function(F,E){F=F==l?A:F;var H=F[h];if(E){if(o.cache[H]){delete o.cache[H][E];E="";for(E in o.cache[H]){break}if(!E){o.removeData(F)}}}else{try{delete F[h]}catch(G){if(F.removeAttribute){F.removeAttribute(h)}}delete o.cache[H]}},queue:function(F,E,H){if(F){E=(E||"fx")+"queue";var G=o.data(F,E);if(!G||o.isArray(H)){G=o.data(F,E,o.makeArray(H))}else{if(H){G.push(H)}}}return G},dequeue:function(H,G){var E=o.queue(H,G),F=E.shift();if(!G||G==="fx"){F=E[0]}if(F!==g){F.call(H)}}});o.fn.extend({data:function(E,G){var H=E.split(".");H[1]=H[1]?"."+H[1]:"";if(G===g){var F=this.triggerHandler("getData"+H[1]+"!",[H[0]]);if(F===g&&this.length){F=o.data(this[0],E)}return F===g&&H[1]?this.data(H[0]):F}else{return this.trigger("setData"+H[1]+"!",[H[0],G]).each(function(){o.data(this,E,G)})}},removeData:function(E){return this.each(function(){o.removeData(this,E)})},queue:function(E,F){if(typeof E!=="string"){F=E;E="fx"}if(F===g){return o.queue(this[0],E)}return this.each(function(){var G=o.queue(this,E,F);if(E=="fx"&&G.length==1){G[0].call(this)}})},dequeue:function(E){return this.each(function(){o.dequeue(this,E)})}}); -/* - * Sizzle CSS Selector Engine - v0.9.3 - * Copyright 2009, The Dojo Foundation - * Released under the MIT, BSD, and GPL Licenses. - * More information: http://sizzlejs.com/ - */ -(function(){var R=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,L=0,H=Object.prototype.toString;var F=function(Y,U,ab,ac){ab=ab||[];U=U||document;if(U.nodeType!==1&&U.nodeType!==9){return[]}if(!Y||typeof Y!=="string"){return ab}var Z=[],W,af,ai,T,ad,V,X=true;R.lastIndex=0;while((W=R.exec(Y))!==null){Z.push(W[1]);if(W[2]){V=RegExp.rightContext;break}}if(Z.length>1&&M.exec(Y)){if(Z.length===2&&I.relative[Z[0]]){af=J(Z[0]+Z[1],U)}else{af=I.relative[Z[0]]?[U]:F(Z.shift(),U);while(Z.length){Y=Z.shift();if(I.relative[Y]){Y+=Z.shift()}af=J(Y,af)}}}else{var ae=ac?{expr:Z.pop(),set:E(ac)}:F.find(Z.pop(),Z.length===1&&U.parentNode?U.parentNode:U,Q(U));af=F.filter(ae.expr,ae.set);if(Z.length>0){ai=E(af)}else{X=false}while(Z.length){var ah=Z.pop(),ag=ah;if(!I.relative[ah]){ah=""}else{ag=Z.pop()}if(ag==null){ag=U}I.relative[ah](ai,ag,Q(U))}}if(!ai){ai=af}if(!ai){throw"Syntax error, unrecognized expression: "+(ah||Y)}if(H.call(ai)==="[object Array]"){if(!X){ab.push.apply(ab,ai)}else{if(U.nodeType===1){for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&(ai[aa]===true||ai[aa].nodeType===1&&K(U,ai[aa]))){ab.push(af[aa])}}}else{for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&ai[aa].nodeType===1){ab.push(af[aa])}}}}}else{E(ai,ab)}if(V){F(V,U,ab,ac);if(G){hasDuplicate=false;ab.sort(G);if(hasDuplicate){for(var aa=1;aa":function(Z,U,aa){var X=typeof U==="string";if(X&&!/\W/.test(U)){U=aa?U:U.toUpperCase();for(var V=0,T=Z.length;V=0)){if(!V){T.push(Y)}}else{if(V){U[X]=false}}}}return false},ID:function(T){return T[1].replace(/\\/g,"")},TAG:function(U,T){for(var V=0;T[V]===false;V++){}return T[V]&&Q(T[V])?U[1]:U[1].toUpperCase()},CHILD:function(T){if(T[1]=="nth"){var U=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(T[2]=="even"&&"2n"||T[2]=="odd"&&"2n+1"||!/\D/.test(T[2])&&"0n+"+T[2]||T[2]);T[2]=(U[1]+(U[2]||1))-0;T[3]=U[3]-0}T[0]=L++;return T},ATTR:function(X,U,V,T,Y,Z){var W=X[1].replace(/\\/g,"");if(!Z&&I.attrMap[W]){X[1]=I.attrMap[W]}if(X[2]==="~="){X[4]=" "+X[4]+" "}return X},PSEUDO:function(X,U,V,T,Y){if(X[1]==="not"){if(X[3].match(R).length>1||/^\w/.test(X[3])){X[3]=F(X[3],null,null,U)}else{var W=F.filter(X[3],U,V,true^Y);if(!V){T.push.apply(T,W)}return false}}else{if(I.match.POS.test(X[0])||I.match.CHILD.test(X[0])){return true}}return X},POS:function(T){T.unshift(true);return T}},filters:{enabled:function(T){return T.disabled===false&&T.type!=="hidden"},disabled:function(T){return T.disabled===true},checked:function(T){return T.checked===true},selected:function(T){T.parentNode.selectedIndex;return T.selected===true},parent:function(T){return !!T.firstChild},empty:function(T){return !T.firstChild},has:function(V,U,T){return !!F(T[3],V).length},header:function(T){return/h\d/i.test(T.nodeName)},text:function(T){return"text"===T.type},radio:function(T){return"radio"===T.type},checkbox:function(T){return"checkbox"===T.type},file:function(T){return"file"===T.type},password:function(T){return"password"===T.type},submit:function(T){return"submit"===T.type},image:function(T){return"image"===T.type},reset:function(T){return"reset"===T.type},button:function(T){return"button"===T.type||T.nodeName.toUpperCase()==="BUTTON"},input:function(T){return/input|select|textarea|button/i.test(T.nodeName)}},setFilters:{first:function(U,T){return T===0},last:function(V,U,T,W){return U===W.length-1},even:function(U,T){return T%2===0},odd:function(U,T){return T%2===1},lt:function(V,U,T){return UT[3]-0},nth:function(V,U,T){return T[3]-0==U},eq:function(V,U,T){return T[3]-0==U}},filter:{PSEUDO:function(Z,V,W,aa){var U=V[1],X=I.filters[U];if(X){return X(Z,W,V,aa)}else{if(U==="contains"){return(Z.textContent||Z.innerText||"").indexOf(V[3])>=0}else{if(U==="not"){var Y=V[3];for(var W=0,T=Y.length;W=0)}}},ID:function(U,T){return U.nodeType===1&&U.getAttribute("id")===T},TAG:function(U,T){return(T==="*"&&U.nodeType===1)||U.nodeName===T},CLASS:function(U,T){return(" "+(U.className||U.getAttribute("class"))+" ").indexOf(T)>-1},ATTR:function(Y,W){var V=W[1],T=I.attrHandle[V]?I.attrHandle[V](Y):Y[V]!=null?Y[V]:Y.getAttribute(V),Z=T+"",X=W[2],U=W[4];return T==null?X==="!=":X==="="?Z===U:X==="*="?Z.indexOf(U)>=0:X==="~="?(" "+Z+" ").indexOf(U)>=0:!U?Z&&T!==false:X==="!="?Z!=U:X==="^="?Z.indexOf(U)===0:X==="$="?Z.substr(Z.length-U.length)===U:X==="|="?Z===U||Z.substr(0,U.length+1)===U+"-":false},POS:function(X,U,V,Y){var T=U[2],W=I.setFilters[T];if(W){return W(X,V,U,Y)}}}};var M=I.match.POS;for(var O in I.match){I.match[O]=RegExp(I.match[O].source+/(?![^\[]*\])(?![^\(]*\))/.source)}var E=function(U,T){U=Array.prototype.slice.call(U);if(T){T.push.apply(T,U);return T}return U};try{Array.prototype.slice.call(document.documentElement.childNodes)}catch(N){E=function(X,W){var U=W||[];if(H.call(X)==="[object Array]"){Array.prototype.push.apply(U,X)}else{if(typeof X.length==="number"){for(var V=0,T=X.length;V";var T=document.documentElement;T.insertBefore(U,T.firstChild);if(!!document.getElementById(V)){I.find.ID=function(X,Y,Z){if(typeof Y.getElementById!=="undefined"&&!Z){var W=Y.getElementById(X[1]);return W?W.id===X[1]||typeof W.getAttributeNode!=="undefined"&&W.getAttributeNode("id").nodeValue===X[1]?[W]:g:[]}};I.filter.ID=function(Y,W){var X=typeof Y.getAttributeNode!=="undefined"&&Y.getAttributeNode("id");return Y.nodeType===1&&X&&X.nodeValue===W}}T.removeChild(U)})();(function(){var T=document.createElement("div");T.appendChild(document.createComment(""));if(T.getElementsByTagName("*").length>0){I.find.TAG=function(U,Y){var X=Y.getElementsByTagName(U[1]);if(U[1]==="*"){var W=[];for(var V=0;X[V];V++){if(X[V].nodeType===1){W.push(X[V])}}X=W}return X}}T.innerHTML="";if(T.firstChild&&typeof T.firstChild.getAttribute!=="undefined"&&T.firstChild.getAttribute("href")!=="#"){I.attrHandle.href=function(U){return U.getAttribute("href",2)}}})();if(document.querySelectorAll){(function(){var T=F,U=document.createElement("div");U.innerHTML="

";if(U.querySelectorAll&&U.querySelectorAll(".TEST").length===0){return}F=function(Y,X,V,W){X=X||document;if(!W&&X.nodeType===9&&!Q(X)){try{return E(X.querySelectorAll(Y),V)}catch(Z){}}return T(Y,X,V,W)};F.find=T.find;F.filter=T.filter;F.selectors=T.selectors;F.matches=T.matches})()}if(document.getElementsByClassName&&document.documentElement.getElementsByClassName){(function(){var T=document.createElement("div");T.innerHTML="
";if(T.getElementsByClassName("e").length===0){return}T.lastChild.className="e";if(T.getElementsByClassName("e").length===1){return}I.order.splice(1,0,"CLASS");I.find.CLASS=function(U,V,W){if(typeof V.getElementsByClassName!=="undefined"&&!W){return V.getElementsByClassName(U[1])}}})()}function P(U,Z,Y,ad,aa,ac){var ab=U=="previousSibling"&&!ac;for(var W=0,V=ad.length;W0){X=T;break}}}T=T[U]}ad[W]=X}}}var K=document.compareDocumentPosition?function(U,T){return U.compareDocumentPosition(T)&16}:function(U,T){return U!==T&&(U.contains?U.contains(T):true)};var Q=function(T){return T.nodeType===9&&T.documentElement.nodeName!=="HTML"||!!T.ownerDocument&&Q(T.ownerDocument)};var J=function(T,aa){var W=[],X="",Y,V=aa.nodeType?[aa]:aa;while((Y=I.match.PSEUDO.exec(T))){X+=Y[0];T=T.replace(I.match.PSEUDO,"")}T=I.relative[T]?T+"*":T;for(var Z=0,U=V.length;Z0||T.offsetHeight>0};F.selectors.filters.animated=function(T){return o.grep(o.timers,function(U){return T===U.elem}).length};o.multiFilter=function(V,T,U){if(U){V=":not("+V+")"}return F.matches(V,T)};o.dir=function(V,U){var T=[],W=V[U];while(W&&W!=document){if(W.nodeType==1){T.push(W)}W=W[U]}return T};o.nth=function(X,T,V,W){T=T||1;var U=0;for(;X;X=X[V]){if(X.nodeType==1&&++U==T){break}}return X};o.sibling=function(V,U){var T=[];for(;V;V=V.nextSibling){if(V.nodeType==1&&V!=U){T.push(V)}}return T};return;l.Sizzle=F})();o.event={add:function(I,F,H,K){if(I.nodeType==3||I.nodeType==8){return}if(I.setInterval&&I!=l){I=l}if(!H.guid){H.guid=this.guid++}if(K!==g){var G=H;H=this.proxy(G);H.data=K}var E=o.data(I,"events")||o.data(I,"events",{}),J=o.data(I,"handle")||o.data(I,"handle",function(){return typeof o!=="undefined"&&!o.event.triggered?o.event.handle.apply(arguments.callee.elem,arguments):g});J.elem=I;o.each(F.split(/\s+/),function(M,N){var O=N.split(".");N=O.shift();H.type=O.slice().sort().join(".");var L=E[N];if(o.event.specialAll[N]){o.event.specialAll[N].setup.call(I,K,O)}if(!L){L=E[N]={};if(!o.event.special[N]||o.event.special[N].setup.call(I,K,O)===false){if(I.addEventListener){I.addEventListener(N,J,false)}else{if(I.attachEvent){I.attachEvent("on"+N,J)}}}}L[H.guid]=H;o.event.global[N]=true});I=null},guid:1,global:{},remove:function(K,H,J){if(K.nodeType==3||K.nodeType==8){return}var G=o.data(K,"events"),F,E;if(G){if(H===g||(typeof H==="string"&&H.charAt(0)==".")){for(var I in G){this.remove(K,I+(H||""))}}else{if(H.type){J=H.handler;H=H.type}o.each(H.split(/\s+/),function(M,O){var Q=O.split(".");O=Q.shift();var N=RegExp("(^|\\.)"+Q.slice().sort().join(".*\\.")+"(\\.|$)");if(G[O]){if(J){delete G[O][J.guid]}else{for(var P in G[O]){if(N.test(G[O][P].type)){delete G[O][P]}}}if(o.event.specialAll[O]){o.event.specialAll[O].teardown.call(K,Q)}for(F in G[O]){break}if(!F){if(!o.event.special[O]||o.event.special[O].teardown.call(K,Q)===false){if(K.removeEventListener){K.removeEventListener(O,o.data(K,"handle"),false)}else{if(K.detachEvent){K.detachEvent("on"+O,o.data(K,"handle"))}}}F=null;delete G[O]}}})}for(F in G){break}if(!F){var L=o.data(K,"handle");if(L){L.elem=null}o.removeData(K,"events");o.removeData(K,"handle")}}},trigger:function(I,K,H,E){var G=I.type||I;if(!E){I=typeof I==="object"?I[h]?I:o.extend(o.Event(G),I):o.Event(G);if(G.indexOf("!")>=0){I.type=G=G.slice(0,-1);I.exclusive=true}if(!H){I.stopPropagation();if(this.global[G]){o.each(o.cache,function(){if(this.events&&this.events[G]){o.event.trigger(I,K,this.handle.elem)}})}}if(!H||H.nodeType==3||H.nodeType==8){return g}I.result=g;I.target=H;K=o.makeArray(K);K.unshift(I)}I.currentTarget=H;var J=o.data(H,"handle");if(J){J.apply(H,K)}if((!H[G]||(o.nodeName(H,"a")&&G=="click"))&&H["on"+G]&&H["on"+G].apply(H,K)===false){I.result=false}if(!E&&H[G]&&!I.isDefaultPrevented()&&!(o.nodeName(H,"a")&&G=="click")){this.triggered=true;try{H[G]()}catch(L){}}this.triggered=false;if(!I.isPropagationStopped()){var F=H.parentNode||H.ownerDocument;if(F){o.event.trigger(I,K,F,true)}}},handle:function(K){var J,E;K=arguments[0]=o.event.fix(K||l.event);K.currentTarget=this;var L=K.type.split(".");K.type=L.shift();J=!L.length&&!K.exclusive;var I=RegExp("(^|\\.)"+L.slice().sort().join(".*\\.")+"(\\.|$)");E=(o.data(this,"events")||{})[K.type];for(var G in E){var H=E[G];if(J||I.test(H.type)){K.handler=H;K.data=H.data;var F=H.apply(this,arguments);if(F!==g){K.result=F;if(F===false){K.preventDefault();K.stopPropagation()}}if(K.isImmediatePropagationStopped()){break}}}},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(H){if(H[h]){return H}var F=H;H=o.Event(F);for(var G=this.props.length,J;G;){J=this.props[--G];H[J]=F[J]}if(!H.target){H.target=H.srcElement||document}if(H.target.nodeType==3){H.target=H.target.parentNode}if(!H.relatedTarget&&H.fromElement){H.relatedTarget=H.fromElement==H.target?H.toElement:H.fromElement}if(H.pageX==null&&H.clientX!=null){var I=document.documentElement,E=document.body;H.pageX=H.clientX+(I&&I.scrollLeft||E&&E.scrollLeft||0)-(I.clientLeft||0);H.pageY=H.clientY+(I&&I.scrollTop||E&&E.scrollTop||0)-(I.clientTop||0)}if(!H.which&&((H.charCode||H.charCode===0)?H.charCode:H.keyCode)){H.which=H.charCode||H.keyCode}if(!H.metaKey&&H.ctrlKey){H.metaKey=H.ctrlKey}if(!H.which&&H.button){H.which=(H.button&1?1:(H.button&2?3:(H.button&4?2:0)))}return H},proxy:function(F,E){E=E||function(){return F.apply(this,arguments)};E.guid=F.guid=F.guid||E.guid||this.guid++;return E},special:{ready:{setup:B,teardown:function(){}}},specialAll:{live:{setup:function(E,F){o.event.add(this,F[0],c)},teardown:function(G){if(G.length){var E=0,F=RegExp("(^|\\.)"+G[0]+"(\\.|$)");o.each((o.data(this,"events").live||{}),function(){if(F.test(this.type)){E++}});if(E<1){o.event.remove(this,G[0],c)}}}}}};o.Event=function(E){if(!this.preventDefault){return new o.Event(E)}if(E&&E.type){this.originalEvent=E;this.type=E.type}else{this.type=E}this.timeStamp=e();this[h]=true};function k(){return false}function u(){return true}o.Event.prototype={preventDefault:function(){this.isDefaultPrevented=u;var E=this.originalEvent;if(!E){return}if(E.preventDefault){E.preventDefault()}E.returnValue=false},stopPropagation:function(){this.isPropagationStopped=u;var E=this.originalEvent;if(!E){return}if(E.stopPropagation){E.stopPropagation()}E.cancelBubble=true},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=u;this.stopPropagation()},isDefaultPrevented:k,isPropagationStopped:k,isImmediatePropagationStopped:k};var a=function(F){var E=F.relatedTarget;while(E&&E!=this){try{E=E.parentNode}catch(G){E=this}}if(E!=this){F.type=F.data;o.event.handle.apply(this,arguments)}};o.each({mouseover:"mouseenter",mouseout:"mouseleave"},function(F,E){o.event.special[E]={setup:function(){o.event.add(this,F,a,E)},teardown:function(){o.event.remove(this,F,a)}}});o.fn.extend({bind:function(F,G,E){return F=="unload"?this.one(F,G,E):this.each(function(){o.event.add(this,F,E||G,E&&G)})},one:function(G,H,F){var E=o.event.proxy(F||H,function(I){o(this).unbind(I,E);return(F||H).apply(this,arguments)});return this.each(function(){o.event.add(this,G,E,F&&H)})},unbind:function(F,E){return this.each(function(){o.event.remove(this,F,E)})},trigger:function(E,F){return this.each(function(){o.event.trigger(E,F,this)})},triggerHandler:function(E,G){if(this[0]){var F=o.Event(E);F.preventDefault();F.stopPropagation();o.event.trigger(F,G,this[0]);return F.result}},toggle:function(G){var E=arguments,F=1;while(F=0){var E=G.slice(I,G.length);G=G.slice(0,I)}var H="GET";if(J){if(o.isFunction(J)){K=J;J=null}else{if(typeof J==="object"){J=o.param(J);H="POST"}}}var F=this;o.ajax({url:G,type:H,dataType:"html",data:J,complete:function(M,L){if(L=="success"||L=="notmodified"){F.html(E?o("
").append(M.responseText.replace(//g,"")).find(E):M.responseText)}if(K){F.each(K,[M.responseText,L,M])}}});return this},serialize:function(){return o.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?o.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password|search/i.test(this.type))}).map(function(E,F){var G=o(this).val();return G==null?null:o.isArray(G)?o.map(G,function(I,H){return{name:F.name,value:I}}):{name:F.name,value:G}}).get()}});o.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(E,F){o.fn[F]=function(G){return this.bind(F,G)}});var r=e();o.extend({get:function(E,G,H,F){if(o.isFunction(G)){H=G;G=null}return o.ajax({type:"GET",url:E,data:G,success:H,dataType:F})},getScript:function(E,F){return o.get(E,null,F,"script")},getJSON:function(E,F,G){return o.get(E,F,G,"json")},post:function(E,G,H,F){if(o.isFunction(G)){H=G;G={}}return o.ajax({type:"POST",url:E,data:G,success:H,dataType:F})},ajaxSetup:function(E){o.extend(o.ajaxSettings,E)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return l.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest()},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(M){M=o.extend(true,M,o.extend(true,{},o.ajaxSettings,M));var W,F=/=\?(&|$)/g,R,V,G=M.type.toUpperCase();if(M.data&&M.processData&&typeof M.data!=="string"){M.data=o.param(M.data)}if(M.dataType=="jsonp"){if(G=="GET"){if(!M.url.match(F)){M.url+=(M.url.match(/\?/)?"&":"?")+(M.jsonp||"callback")+"=?"}}else{if(!M.data||!M.data.match(F)){M.data=(M.data?M.data+"&":"")+(M.jsonp||"callback")+"=?"}}M.dataType="json"}if(M.dataType=="json"&&(M.data&&M.data.match(F)||M.url.match(F))){W="jsonp"+r++;if(M.data){M.data=(M.data+"").replace(F,"="+W+"$1")}M.url=M.url.replace(F,"="+W+"$1");M.dataType="script";l[W]=function(X){V=X;I();L();l[W]=g;try{delete l[W]}catch(Y){}if(H){H.removeChild(T)}}}if(M.dataType=="script"&&M.cache==null){M.cache=false}if(M.cache===false&&G=="GET"){var E=e();var U=M.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+E+"$2");M.url=U+((U==M.url)?(M.url.match(/\?/)?"&":"?")+"_="+E:"")}if(M.data&&G=="GET"){M.url+=(M.url.match(/\?/)?"&":"?")+M.data;M.data=null}if(M.global&&!o.active++){o.event.trigger("ajaxStart")}var Q=/^(\w+:)?\/\/([^\/?#]+)/.exec(M.url);if(M.dataType=="script"&&G=="GET"&&Q&&(Q[1]&&Q[1]!=location.protocol||Q[2]!=location.host)){var H=document.getElementsByTagName("head")[0];var T=document.createElement("script");T.src=M.url;if(M.scriptCharset){T.charset=M.scriptCharset}if(!W){var O=false;T.onload=T.onreadystatechange=function(){if(!O&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){O=true;I();L();T.onload=T.onreadystatechange=null;H.removeChild(T)}}}H.appendChild(T);return g}var K=false;var J=M.xhr();if(M.username){J.open(G,M.url,M.async,M.username,M.password)}else{J.open(G,M.url,M.async)}try{if(M.data){J.setRequestHeader("Content-Type",M.contentType)}if(M.ifModified){J.setRequestHeader("If-Modified-Since",o.lastModified[M.url]||"Thu, 01 Jan 1970 00:00:00 GMT")}J.setRequestHeader("X-Requested-With","XMLHttpRequest");J.setRequestHeader("Accept",M.dataType&&M.accepts[M.dataType]?M.accepts[M.dataType]+", */*":M.accepts._default)}catch(S){}if(M.beforeSend&&M.beforeSend(J,M)===false){if(M.global&&!--o.active){o.event.trigger("ajaxStop")}J.abort();return false}if(M.global){o.event.trigger("ajaxSend",[J,M])}var N=function(X){if(J.readyState==0){if(P){clearInterval(P);P=null;if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}}else{if(!K&&J&&(J.readyState==4||X=="timeout")){K=true;if(P){clearInterval(P);P=null}R=X=="timeout"?"timeout":!o.httpSuccess(J)?"error":M.ifModified&&o.httpNotModified(J,M.url)?"notmodified":"success";if(R=="success"){try{V=o.httpData(J,M.dataType,M)}catch(Z){R="parsererror"}}if(R=="success"){var Y;try{Y=J.getResponseHeader("Last-Modified")}catch(Z){}if(M.ifModified&&Y){o.lastModified[M.url]=Y}if(!W){I()}}else{o.handleError(M,J,R)}L();if(X){J.abort()}if(M.async){J=null}}}};if(M.async){var P=setInterval(N,13);if(M.timeout>0){setTimeout(function(){if(J&&!K){N("timeout")}},M.timeout)}}try{J.send(M.data)}catch(S){o.handleError(M,J,null,S)}if(!M.async){N()}function I(){if(M.success){M.success(V,R)}if(M.global){o.event.trigger("ajaxSuccess",[J,M])}}function L(){if(M.complete){M.complete(J,R)}if(M.global){o.event.trigger("ajaxComplete",[J,M])}if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}return J},handleError:function(F,H,E,G){if(F.error){F.error(H,E,G)}if(F.global){o.event.trigger("ajaxError",[H,F,G])}},active:0,httpSuccess:function(F){try{return !F.status&&location.protocol=="file:"||(F.status>=200&&F.status<300)||F.status==304||F.status==1223}catch(E){}return false},httpNotModified:function(G,E){try{var H=G.getResponseHeader("Last-Modified");return G.status==304||H==o.lastModified[E]}catch(F){}return false},httpData:function(J,H,G){var F=J.getResponseHeader("content-type"),E=H=="xml"||!H&&F&&F.indexOf("xml")>=0,I=E?J.responseXML:J.responseText;if(E&&I.documentElement.tagName=="parsererror"){throw"parsererror"}if(G&&G.dataFilter){I=G.dataFilter(I,H)}if(typeof I==="string"){if(H=="script"){o.globalEval(I)}if(H=="json"){I=l["eval"]("("+I+")")}}return I},param:function(E){var G=[];function H(I,J){G[G.length]=encodeURIComponent(I)+"="+encodeURIComponent(J)}if(o.isArray(E)||E.jquery){o.each(E,function(){H(this.name,this.value)})}else{for(var F in E){if(o.isArray(E[F])){o.each(E[F],function(){H(F,this)})}else{H(F,o.isFunction(E[F])?E[F]():E[F])}}}return G.join("&").replace(/%20/g,"+")}});var m={},n,d=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];function t(F,E){var G={};o.each(d.concat.apply([],d.slice(0,E)),function(){G[this]=F});return G}o.fn.extend({show:function(J,L){if(J){return this.animate(t("show",3),J,L)}else{for(var H=0,F=this.length;H").appendTo("body");K=I.css("display");if(K==="none"){K="block"}I.remove();m[G]=K}o.data(this[H],"olddisplay",K)}}for(var H=0,F=this.length;H=0;H--){if(G[H].elem==this){if(E){G[H](true)}G.splice(H,1)}}});if(!E){this.dequeue()}return this}});o.each({slideDown:t("show",1),slideUp:t("hide",1),slideToggle:t("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(E,F){o.fn[E]=function(G,H){return this.animate(F,G,H)}});o.extend({speed:function(G,H,F){var E=typeof G==="object"?G:{complete:F||!F&&H||o.isFunction(G)&&G,duration:G,easing:F&&H||H&&!o.isFunction(H)&&H};E.duration=o.fx.off?0:typeof E.duration==="number"?E.duration:o.fx.speeds[E.duration]||o.fx.speeds._default;E.old=E.complete;E.complete=function(){if(E.queue!==false){o(this).dequeue()}if(o.isFunction(E.old)){E.old.call(this)}};return E},easing:{linear:function(G,H,E,F){return E+F*G},swing:function(G,H,E,F){return((-Math.cos(G*Math.PI)/2)+0.5)*F+E}},timers:[],fx:function(F,E,G){this.options=E;this.elem=F;this.prop=G;if(!E.orig){E.orig={}}}});o.fx.prototype={update:function(){if(this.options.step){this.options.step.call(this.elem,this.now,this)}(o.fx.step[this.prop]||o.fx.step._default)(this);if((this.prop=="height"||this.prop=="width")&&this.elem.style){this.elem.style.display="block"}},cur:function(F){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null)){return this.elem[this.prop]}var E=parseFloat(o.css(this.elem,this.prop,F));return E&&E>-10000?E:parseFloat(o.curCSS(this.elem,this.prop))||0},custom:function(I,H,G){this.startTime=e();this.start=I;this.end=H;this.unit=G||this.unit||"px";this.now=this.start;this.pos=this.state=0;var E=this;function F(J){return E.step(J)}F.elem=this.elem;if(F()&&o.timers.push(F)&&!n){n=setInterval(function(){var K=o.timers;for(var J=0;J=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var E=true;for(var F in this.options.curAnim){if(this.options.curAnim[F]!==true){E=false}}if(E){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(o.css(this.elem,"display")=="none"){this.elem.style.display="block"}}if(this.options.hide){o(this.elem).hide()}if(this.options.hide||this.options.show){for(var I in this.options.curAnim){o.attr(this.elem.style,I,this.options.orig[I])}}this.options.complete.call(this.elem)}return false}else{var J=G-this.startTime;this.state=J/this.options.duration;this.pos=o.easing[this.options.easing||(o.easing.swing?"swing":"linear")](this.state,J,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update()}return true}};o.extend(o.fx,{speeds:{slow:600,fast:200,_default:400},step:{opacity:function(E){o.attr(E.elem.style,"opacity",E.now)},_default:function(E){if(E.elem.style&&E.elem.style[E.prop]!=null){E.elem.style[E.prop]=E.now+E.unit}else{E.elem[E.prop]=E.now}}}});if(document.documentElement.getBoundingClientRect){o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}var G=this[0].getBoundingClientRect(),J=this[0].ownerDocument,F=J.body,E=J.documentElement,L=E.clientTop||F.clientTop||0,K=E.clientLeft||F.clientLeft||0,I=G.top+(self.pageYOffset||o.boxModel&&E.scrollTop||F.scrollTop)-L,H=G.left+(self.pageXOffset||o.boxModel&&E.scrollLeft||F.scrollLeft)-K;return{top:I,left:H}}}else{o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}o.offset.initialized||o.offset.initialize();var J=this[0],G=J.offsetParent,F=J,O=J.ownerDocument,M,H=O.documentElement,K=O.body,L=O.defaultView,E=L.getComputedStyle(J,null),N=J.offsetTop,I=J.offsetLeft;while((J=J.parentNode)&&J!==K&&J!==H){M=L.getComputedStyle(J,null);N-=J.scrollTop,I-=J.scrollLeft;if(J===G){N+=J.offsetTop,I+=J.offsetLeft;if(o.offset.doesNotAddBorder&&!(o.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(J.tagName))){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}F=G,G=J.offsetParent}if(o.offset.subtractsBorderForOverflowNotVisible&&M.overflow!=="visible"){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}E=M}if(E.position==="relative"||E.position==="static"){N+=K.offsetTop,I+=K.offsetLeft}if(E.position==="fixed"){N+=Math.max(H.scrollTop,K.scrollTop),I+=Math.max(H.scrollLeft,K.scrollLeft)}return{top:N,left:I}}}o.offset={initialize:function(){if(this.initialized){return}var L=document.body,F=document.createElement("div"),H,G,N,I,M,E,J=L.style.marginTop,K='
';M={position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"};for(E in M){F.style[E]=M[E]}F.innerHTML=K;L.insertBefore(F,L.firstChild);H=F.firstChild,G=H.firstChild,I=H.nextSibling.firstChild.firstChild;this.doesNotAddBorder=(G.offsetTop!==5);this.doesAddBorderForTableAndCells=(I.offsetTop===5);H.style.overflow="hidden",H.style.position="relative";this.subtractsBorderForOverflowNotVisible=(G.offsetTop===-5);L.style.marginTop="1px";this.doesNotIncludeMarginInBodyOffset=(L.offsetTop===0);L.style.marginTop=J;L.removeChild(F);this.initialized=true},bodyOffset:function(E){o.offset.initialized||o.offset.initialize();var G=E.offsetTop,F=E.offsetLeft;if(o.offset.doesNotIncludeMarginInBodyOffset){G+=parseInt(o.curCSS(E,"marginTop",true),10)||0,F+=parseInt(o.curCSS(E,"marginLeft",true),10)||0}return{top:G,left:F}}};o.fn.extend({position:function(){var I=0,H=0,F;if(this[0]){var G=this.offsetParent(),J=this.offset(),E=/^body|html$/i.test(G[0].tagName)?{top:0,left:0}:G.offset();J.top-=j(this,"marginTop");J.left-=j(this,"marginLeft");E.top+=j(G,"borderTopWidth");E.left+=j(G,"borderLeftWidth");F={top:J.top-E.top,left:J.left-E.left}}return F},offsetParent:function(){var E=this[0].offsetParent||document.body;while(E&&(!/^body|html$/i.test(E.tagName)&&o.css(E,"position")=="static")){E=E.offsetParent}return o(E)}});o.each(["Left","Top"],function(F,E){var G="scroll"+E;o.fn[G]=function(H){if(!this[0]){return null}return H!==g?this.each(function(){this==l||this==document?l.scrollTo(!F?H:o(l).scrollLeft(),F?H:o(l).scrollTop()):this[G]=H}):this[0]==l||this[0]==document?self[F?"pageYOffset":"pageXOffset"]||o.boxModel&&document.documentElement[G]||document.body[G]:this[0][G]}});o.each(["Height","Width"],function(I,G){var E=I?"Left":"Top",H=I?"Right":"Bottom",F=G.toLowerCase();o.fn["inner"+G]=function(){return this[0]?o.css(this[0],F,false,"padding"):null};o.fn["outer"+G]=function(K){return this[0]?o.css(this[0],F,false,K?"margin":"border"):null};var J=G.toLowerCase();o.fn[J]=function(K){return this[0]==l?document.compatMode=="CSS1Compat"&&document.documentElement["client"+G]||document.body["client"+G]:this[0]==document?Math.max(document.documentElement["client"+G],document.body["scroll"+G],document.documentElement["scroll"+G],document.body["offset"+G],document.documentElement["offset"+G]):K===g?(this.length?o.css(this[0],J):null):this.css(J,typeof K==="string"?K:K+"px")}})})(); \ No newline at end of file diff --git a/oxdata/static/js/pandora.api.js b/oxdata/static/js/pandora.api.js deleted file mode 100755 index 31b774b..0000000 --- a/oxdata/static/js/pandora.api.js +++ /dev/null @@ -1,157 +0,0 @@ -/*** - Pandora API -***/ -Ox.load('UI', { - hideScreen: false, - showScreen: true, - theme: 'classic' -}, function() { - -var app = new Ox.App({ - apiURL: '/api/', -}).bindEvent('load', function(data) { - app.default_info = '

Pan.do/ra API Overview

use this api in the browser with Ox.app or use pandora_client it in python. Further description of the api can be found on the wiki
'; - app.$body = $('body'); - app.$document = $(document); - app.$window = $(window); - //app.$body.html(''); - Ox.UI.hideLoadingScreen(); - - app.$ui = {}; - app.$ui.actionList = constructList(); - app.$ui.actionInfo = Ox.Container().css({padding: '16px'}).html(app.default_info); - - app.api.api({docs: true, code: true}, function(results) { - app.actions = results.data.actions; - - if(document.location.hash) { - app.$ui.actionList.triggerEvent('select', {ids: document.location.hash.substring(1).split(',')}); - } - }); - - var $left = new Ox.SplitPanel({ - elements: [ - { - element: new Ox.Element().append(new Ox.Element() - .html('API').css({ - 'padding': '4px', - })).css({ - 'background-color': '#ddd', - 'font-weight': 'bold', - }), - size: 24 - }, - { - element: app.$ui.actionList - } - ], - orientation: 'vertical' - }); - var $main = new Ox.SplitPanel({ - elements: [ - { - element: $left, - size: 160 - }, - { - element: app.$ui.actionInfo, - } - ], - orientation: 'horizontal' - }); - - $main.appendTo(app.$body); -}); - -function constructList() { - return new Ox.TextList({ - columns: [ - { - align: "left", - id: "name", - operator: "+", - title: "Name", - unique: true, - visible: true, - width: 140 - }, - ], - columnsMovable: false, - columnsRemovable: false, - id: 'actionList', - items: function(data, callback) { - function _sort(a, b) { - if(a.name > b.name) - return 1; - else if(a.name == b.name) - return 0; - return -1; - } - if (!data.keys) { - app.api.api(function(results) { - var items = []; - Ox.forEach(results.data.actions, function(v, k) { - items.push({'name': k}) - }); - items.sort(_sort); - var result = {'data': {'items': items.length}}; - callback(result); - }); - } else { - app.api.api(function(results) { - var items = []; - Ox.forEach(results.data.actions, function(v, k) { - items.push({'name': k}) - }); - items.sort(_sort); - var result = {'data': {'items': items}}; - callback(result); - }); - } - }, - scrollbarVisible: true, - sort: [ - { - key: "name", - operator: "+" - } - ] - }).bindEvent({ - select: function(data) { - var info = $('
').addClass('OxSelectable'), - hash = '#'; - if(data.ids.length) - data.ids.forEach(function(id) { - info.append($("

").html(id)); - var $doc =$('
')
-                           .html(app.actions[id].doc.replace('/\n/
\n/g')) - .appendTo(info); - var $code = $('') - .html(app.actions[id].code[1].replace('/\n/
\n/g')) - .hide(); - var $button = new Ox.Button({ - title: [ - {id: "one", title: "right"}, - {id: "two", title: "down"}, - ], - type: "image" - }) - .addClass("margin") - .click(function() { $code.toggle()}) - .appendTo(info) - var f = app.actions[id].code[0]; - $('').html(' View Source ('+f+')').appendTo(info) - $('
').append($code).appendTo(info) 
-
-                hash += id + ','
-              });
-            else
-              info.html(app.default_info);
-
-            document.location.hash = hash.substring(0, hash.length-1);
-            app.$ui.actionInfo.html(info);
-       }
-    });
-}
-});
-
diff --git a/oxdata/templates/404.html b/oxdata/templates/404.html
new file mode 100644
index 0000000..24aa850
--- /dev/null
+++ b/oxdata/templates/404.html
@@ -0,0 +1 @@
+404 not found
diff --git a/oxdata/templates/api.html b/oxdata/templates/api.html
new file mode 100644
index 0000000..eedd89b
--- /dev/null
+++ b/oxdata/templates/api.html
@@ -0,0 +1 @@
+api
diff --git a/oxdata/urls.py b/oxdata/urls.py
index 4754e40..83c30ee 100644
--- a/oxdata/urls.py
+++ b/oxdata/urls.py
@@ -2,49 +2,50 @@
 # vi:si:et:sw=4:sts=4:ts=4
 import os
 
-from django.conf.urls.defaults import *
-from ox.django.http import HttpFileResponse
+from django.conf.urls import url, include
+from oxdjango.http import HttpFileResponse
 from django.conf import settings
+import django.views.static
 
 # Uncomment the next two lines to enable the admin:
 from django.contrib import admin
 admin.autodiscover()
 
-from api import actions
-actions.autodiscover()
+import oxdjango.api.urls
 
+import views
+import poster.urls
+import lookup.urls
 
 def serve_static_file(path, location, content_type):
     return HttpFileResponse(location, content_type=content_type)
 
-urlpatterns = patterns('',
-    (r'^$', 'oxdata.views.index'),
-    (r'^api/$', include('api.urls')),
-    (r'^poster/', include('oxdata.poster.urls')),
-    (r'^still/$', 'oxdata.poster.views.still'),
-    (r'^id/', include('oxdata.lookup.urls')),
-    (r'^get/', include('oxdata.lookup.urls')),
 
-    (r'^robots.txt$', serve_static_file, {
+urlpatterns = [
+    url(r'^$', views.index),
+    url(r'^api/?', include(oxdjango.api.urls)),
+    url(r'^poster/', include(poster.urls)),
+    url(r'^still/$', poster.views.still),
+    url(r'^id/', include(lookup.urls)),
+    url(r'^get/', include(lookup.urls)),
+
+    url(r'^robots.txt$', serve_static_file, {
             'location': os.path.join(settings.STATIC_ROOT, 'robots.txt'),
             'content_type': 'text/plain'
     }),
-    (r'^favicon.ico$', serve_static_file, {
+    url(r'^favicon.ico$', serve_static_file, {
             'location': os.path.join(settings.STATIC_ROOT, 'favicon.ico'),
             'content_type': 'image/x-icon'}),
-    # Uncomment the next line to enable the admin:
-    (r'^admin/', include(admin.site.urls)),
-)
+    url(r'^admin/', include(admin.site.urls)),
+]
 
 if settings.DEBUG:
-    urlpatterns += patterns('',
-        (r'^media/(?P.*)$', 'django.views.static.serve',
+    urlpatterns += [
+        url(r'^media/(?P.*)$', django.views.static.serve,
             {'document_root': settings.MEDIA_ROOT}),
-        (r'^static/(?P.*)$', 'django.views.static.serve',
+        url(r'^static/(?P.*)$', django.views.static.serve,
             {'document_root': settings.STATIC_ROOT}),
-    )
-
-
+    ]
 
 #load local urls if present
 try:
diff --git a/oxdata/views.py b/oxdata/views.py
index fa2777a..c5f9327 100644
--- a/oxdata/views.py
+++ b/oxdata/views.py
@@ -1,10 +1,7 @@
 # -*- coding: utf-8 -*-
 # vi:si:et:sw=4:sts=4:ts=4
-from django.shortcuts import render_to_response, get_object_or_404, get_list_or_404
-from django.template import RequestContext
-
+from django.shortcuts import render
 
 def index(request):
-    context = RequestContext(request, {})
-    return render_to_response('index.html', context)
+    return render(request, 'index.html', {})
 
diff --git a/oxdata/wsgi.py b/oxdata/wsgi.py
new file mode 100644
index 0000000..3ce60c3
--- /dev/null
+++ b/oxdata/wsgi.py
@@ -0,0 +1,16 @@
+"""
+WSGI config for fixme project.
+
+It exposes the WSGI callable as a module-level variable named ``application``.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/1.9/howto/deployment/wsgi/
+"""
+
+import os
+
+from django.core.wsgi import get_wsgi_application
+
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings")
+
+application = get_wsgi_application()
diff --git a/requirements.txt b/requirements.txt
index 6c7b2da..b520f6b 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,7 +1,6 @@
-Django>=1.3.0,<1.4
-South
--e bzr+http://code.0x2620.org/python-ox/#egg=python-ox
-chardet
-django-celery
-gunicorn
--e git://github.com/bit/django-extensions.git#egg=django_extensions
+Django==1.9.4
+celery==3.1.20
+django-celery==3.1.17
+django-extensions==1.6.1
+gunicorn==19.4.5
+-e git+http://git.0x2620.org/python-ox.git#egg=python-ox
diff --git a/wsgi/django.wsgi b/wsgi/django.wsgi
deleted file mode 100644
index 992eb24..0000000
--- a/wsgi/django.wsgi
+++ /dev/null
@@ -1,28 +0,0 @@
-# django.wsgi
-import os
-import sys
-import site
- 
-project_module = 'oxdata'
-
-root_dir = os.path.normpath(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
-
-#using virtualenv's activate_this.py to reorder sys.path
-activate_this = os.path.join(root_dir, 'bin', 'activate_this.py')
-execfile(activate_this, dict(__file__=activate_this))
-
-sys.path.append(root_dir)
-sys.path.append(os.path.join(root_dir, project_module))
-
-#reload if this django.wsgi gets touched
-from oxdjango import monitor
-monitor.start(interval=1.0)
-
-monitor.track(os.path.abspath(os.path.dirname(__file__)))
-
-os.environ['DJANGO_SETTINGS_MODULE'] = project_module + '.settings'
- 
-import django.core.handlers.wsgi
- 
-application = django.core.handlers.wsgi.WSGIHandler()
-