2009-08-01 14:14:54 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
# vi:si:et:sw=4:sts=4:ts=4
|
2010-02-16 12:41:57 +00:00
|
|
|
from __future__ import division
|
2009-08-01 14:14:54 +00:00
|
|
|
import os.path
|
2009-08-16 12:23:29 +00:00
|
|
|
import re
|
|
|
|
from datetime import datetime
|
|
|
|
from urllib2 import unquote
|
2010-02-16 12:41:57 +00:00
|
|
|
import mimetypes
|
2009-08-16 12:23:29 +00:00
|
|
|
|
2010-02-03 12:05:38 +00:00
|
|
|
from django import forms
|
|
|
|
from django.core.paginator import Paginator
|
|
|
|
from django.contrib.auth.decorators import login_required
|
2009-08-01 14:14:54 +00:00
|
|
|
from django.contrib.auth.models import User
|
2010-07-01 08:49:08 +00:00
|
|
|
from django.db.models import Q, Avg, Count, Sum
|
2010-02-16 12:41:57 +00:00
|
|
|
from django.http import HttpResponse, Http404
|
2009-08-01 14:14:54 +00:00
|
|
|
from django.shortcuts import render_to_response, get_object_or_404, get_list_or_404
|
|
|
|
from django.template import RequestContext
|
2010-02-08 10:26:25 +00:00
|
|
|
from django.conf import settings
|
2010-02-03 12:05:38 +00:00
|
|
|
|
2009-12-31 15:04:32 +00:00
|
|
|
try:
|
|
|
|
import simplejson as json
|
|
|
|
except ImportError:
|
|
|
|
from django.utils import simplejson as json
|
2010-02-03 12:05:38 +00:00
|
|
|
|
2009-12-31 15:04:32 +00:00
|
|
|
from oxdjango.decorators import login_required_json
|
2010-02-10 13:10:28 +00:00
|
|
|
from oxdjango.shortcuts import render_to_json_response, get_object_or_404_json, json_response
|
2010-02-16 12:41:57 +00:00
|
|
|
from oxdjango.http import HttpFileResponse
|
|
|
|
import oxlib
|
2009-12-31 15:04:32 +00:00
|
|
|
|
2009-08-01 14:14:54 +00:00
|
|
|
import models
|
2009-10-04 22:00:08 +00:00
|
|
|
import utils
|
2010-06-25 11:53:57 +00:00
|
|
|
import tasks
|
2010-01-25 09:20:21 +00:00
|
|
|
|
2010-01-26 13:11:57 +00:00
|
|
|
from oxuser.models import getUserJSON
|
2010-02-16 10:10:53 +00:00
|
|
|
from oxuser.views import api_login, api_logout, api_register, api_contact, api_recover, api_preferences
|
2010-01-25 09:20:21 +00:00
|
|
|
|
2010-01-26 13:11:57 +00:00
|
|
|
|
2010-01-25 09:20:21 +00:00
|
|
|
def api(request):
|
2010-01-29 11:03:45 +00:00
|
|
|
if request.META['REQUEST_METHOD'] == "OPTIONS":
|
|
|
|
response = HttpResponse('')
|
2010-07-01 08:49:08 +00:00
|
|
|
response = render_to_json_response({'status': {'code': 200, 'text': 'use POST'}})
|
2010-01-31 10:14:17 +00:00
|
|
|
response['Access-Control-Allow-Origin'] = '*'
|
2010-01-29 11:03:45 +00:00
|
|
|
return response
|
2010-01-30 06:56:10 +00:00
|
|
|
if not 'action' in request.POST:
|
2010-01-26 13:11:57 +00:00
|
|
|
return apidoc(request)
|
2010-01-30 06:56:10 +00:00
|
|
|
function = request.POST['action']
|
2010-01-25 09:20:21 +00:00
|
|
|
#FIXME: possible to do this in f
|
|
|
|
#data = json.loads(request.POST['data'])
|
|
|
|
|
|
|
|
#FIXME: security considerations, web facing api should not call anything in globals!!!
|
|
|
|
f = globals().get('api_'+function, None)
|
|
|
|
if f:
|
|
|
|
response = f(request)
|
|
|
|
else:
|
2010-02-10 13:10:28 +00:00
|
|
|
response = render_to_json_response(json_response(status=400,
|
|
|
|
text='Unknown function %s' % function))
|
2010-06-26 14:32:08 +00:00
|
|
|
response['Access-Control-Allow-Origin'] = '*'
|
2010-01-25 14:57:10 +00:00
|
|
|
return response
|
2010-01-25 09:20:21 +00:00
|
|
|
|
|
|
|
def api_hello(request):
|
|
|
|
'''
|
2010-01-26 13:11:57 +00:00
|
|
|
return {'status': {'code': int, 'text': string},
|
2010-01-29 11:03:45 +00:00
|
|
|
'data': {user: object}}
|
2010-01-25 09:20:21 +00:00
|
|
|
'''
|
2010-02-10 13:10:28 +00:00
|
|
|
response = json_response({})
|
2010-01-26 13:11:57 +00:00
|
|
|
if request.user.is_authenticated():
|
|
|
|
response['data']['user'] = getUserJSON(request.user)
|
|
|
|
else:
|
2010-02-08 10:26:25 +00:00
|
|
|
response['data']['user'] = {'name': 'Guest', 'group': 'guest', 'preferences': {}}
|
2010-01-25 09:20:21 +00:00
|
|
|
return render_to_json_response(response)
|
|
|
|
|
2010-01-31 08:56:44 +00:00
|
|
|
def api_error(request):
|
|
|
|
'''
|
|
|
|
trows 503 error
|
|
|
|
'''
|
2010-01-31 10:14:17 +00:00
|
|
|
success = error_is_success
|
2010-01-31 08:56:44 +00:00
|
|
|
return render_to_json_response({})
|
2010-01-25 09:20:21 +00:00
|
|
|
|
2010-02-22 09:25:29 +00:00
|
|
|
def _order_query(qs, sort, prefix='sort__'):
|
2009-08-16 12:23:29 +00:00
|
|
|
order_by = []
|
2010-02-22 09:25:29 +00:00
|
|
|
for e in sort:
|
2010-06-30 08:40:48 +00:00
|
|
|
operator = e['operator']
|
|
|
|
if operator != '-': operator = ''
|
2010-06-27 12:41:38 +00:00
|
|
|
key = {'id': 'movieId'}.get(e['key'], e['key'])
|
2010-06-30 08:40:48 +00:00
|
|
|
order = '%s%s%s' % (operator, prefix, key)
|
2010-02-22 09:25:29 +00:00
|
|
|
order_by.append(order)
|
|
|
|
if order_by:
|
|
|
|
qs = qs.order_by(*order_by)
|
|
|
|
return qs
|
|
|
|
|
|
|
|
def _parse_query(data, user):
|
|
|
|
query = {}
|
|
|
|
query['range'] = [0, 100]
|
2010-07-01 08:49:08 +00:00
|
|
|
query['sort'] = [{'key':'title', 'operator':'+'}]
|
2010-06-30 11:18:04 +00:00
|
|
|
for key in ('sort', 'keys', 'group', 'list', 'range', 'ids'):
|
2010-02-22 09:25:29 +00:00
|
|
|
if key in data:
|
|
|
|
query[key] = data[key]
|
|
|
|
query['qs'] = models.Movie.objects.find(data, user)
|
|
|
|
#group by only allows sorting by name or number of itmes
|
|
|
|
return query
|
2009-08-01 14:14:54 +00:00
|
|
|
|
2010-07-01 08:49:08 +00:00
|
|
|
def _get_positions(ids, get_ids):
|
|
|
|
positions = {}
|
|
|
|
for i in get_ids:
|
|
|
|
try:
|
|
|
|
positions[i] = ids.index(i)
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
return positions
|
|
|
|
|
2010-01-25 09:20:21 +00:00
|
|
|
def api_find(request):
|
|
|
|
'''
|
2010-01-26 13:11:57 +00:00
|
|
|
param data
|
2010-06-27 12:41:38 +00:00
|
|
|
{'query': query, 'sort': array, 'range': array}
|
2010-01-25 09:20:21 +00:00
|
|
|
|
2010-02-22 09:25:29 +00:00
|
|
|
query: query object, more on query syntax at
|
2010-06-30 14:21:04 +00:00
|
|
|
https://wiki.0x2620.org/wiki/pandora/QuerySyntax
|
2010-06-27 12:41:38 +00:00
|
|
|
sort: array of key, operator dics
|
|
|
|
[
|
|
|
|
{
|
|
|
|
key: "year",
|
|
|
|
operator: "-"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
key: "director",
|
|
|
|
operator: ""
|
|
|
|
}
|
|
|
|
]
|
2010-02-22 09:25:29 +00:00
|
|
|
range: result range, array [from, to]
|
|
|
|
keys: array of keys to return
|
|
|
|
group: group elements by, country, genre, director...
|
2010-01-26 13:11:57 +00:00
|
|
|
|
2010-02-22 09:25:29 +00:00
|
|
|
with keys, items is list of dicts with requested properties:
|
2010-01-26 13:11:57 +00:00
|
|
|
return {'status': {'code': int, 'text': string},
|
2010-01-29 11:03:45 +00:00
|
|
|
'data': {items: array}}
|
2010-01-26 13:11:57 +00:00
|
|
|
|
2010-06-30 11:18:04 +00:00
|
|
|
Groups
|
|
|
|
param data
|
|
|
|
{'query': query, 'key': string, 'group': string, 'range': array}
|
|
|
|
|
|
|
|
query: query object, more on query syntax at
|
2010-06-30 14:21:04 +00:00
|
|
|
https://wiki.0x2620.org/wiki/pandora/QuerySyntax
|
2010-06-30 11:18:04 +00:00
|
|
|
range: result range, array [from, to]
|
|
|
|
keys: array of keys to return
|
|
|
|
group: group elements by, country, genre, director...
|
|
|
|
|
|
|
|
possible values for keys: name, items
|
|
|
|
|
|
|
|
with keys
|
|
|
|
items contains list of {'name': string, 'items': int}:
|
|
|
|
return {'status': {'code': int, 'text': string},
|
|
|
|
'data': {items: array}}
|
2010-01-26 13:11:57 +00:00
|
|
|
|
2010-06-30 11:18:04 +00:00
|
|
|
without keys: return number of items in given query
|
2010-01-26 13:11:57 +00:00
|
|
|
return {'status': {'code': int, 'text': string},
|
|
|
|
'data': {items: int}}
|
|
|
|
|
2010-06-30 11:18:04 +00:00
|
|
|
Positions
|
|
|
|
param data
|
|
|
|
{'query': query, 'ids': []}
|
|
|
|
|
|
|
|
query: query object, more on query syntax at
|
2010-06-30 14:21:04 +00:00
|
|
|
https://wiki.0x2620.org/wiki/pandora/QuerySyntax
|
2010-06-30 11:18:04 +00:00
|
|
|
ids: ids of items for which positions are required
|
2010-01-25 09:20:21 +00:00
|
|
|
'''
|
2010-02-22 09:25:29 +00:00
|
|
|
data = json.loads(request.POST['data'])
|
2010-06-26 14:32:08 +00:00
|
|
|
if settings.JSON_DEBUG:
|
|
|
|
print json.dumps(data, indent=2)
|
2010-02-22 09:25:29 +00:00
|
|
|
query = _parse_query(data, request.user)
|
2010-06-26 14:32:08 +00:00
|
|
|
|
2010-02-10 13:10:28 +00:00
|
|
|
response = json_response({})
|
2010-07-01 08:49:08 +00:00
|
|
|
if 'group' in query:
|
|
|
|
if 'sort' in query:
|
|
|
|
if len(query['sort']) == 1 and query['sort'][0]['key'] == 'items':
|
|
|
|
if query['group'] == "year":
|
|
|
|
query['sort'].append({'key': 'name', 'operator':'-'})
|
|
|
|
else:
|
|
|
|
query['sort'].append({'key': 'name', 'operator':'+'})
|
|
|
|
else:
|
|
|
|
query['sort'] = [{'key': 'name', 'operator':'+'}]
|
2010-06-30 11:18:04 +00:00
|
|
|
#FIXME: also filter lists here
|
|
|
|
response['data']['items'] = []
|
|
|
|
name = 'name'
|
|
|
|
items = 'movies'
|
|
|
|
movie_qs = query['qs']
|
2010-07-01 12:44:18 +00:00
|
|
|
if query['group'] == "director":
|
2010-07-02 13:44:10 +00:00
|
|
|
qs = models.Cast.objects.filter(role='directors').filter(movie__id__in=movie_qs).values('person__name').annotate(movies=Count('person__id')).order_by()
|
2010-07-01 12:44:18 +00:00
|
|
|
name = 'person__name'
|
2010-07-02 13:44:10 +00:00
|
|
|
elif query['group'] == "country":
|
|
|
|
qs = models.Country.objects.filter(movies__id__in=movie_qs).values('name').annotate(movies=Count('id'))
|
|
|
|
elif query['group'] == "genre":
|
|
|
|
qs = models.Genre.objects.filter(movies__id__in=movie_qs).values('name').annotate(movies=Count('id'))
|
|
|
|
elif query['group'] == "language":
|
|
|
|
qs = models.Language.objects.filter(movies__id__in=movie_qs).values('name').annotate(movies=Count('id'))
|
2010-06-30 11:18:04 +00:00
|
|
|
elif query['group'] == "year":
|
|
|
|
qs = models.MovieSort.objects.filter(movie__id__in=movie_qs).values('year').annotate(movies=Count('year'))
|
|
|
|
name='year'
|
2010-07-01 08:49:08 +00:00
|
|
|
#replace normalized items/name sort with actual db value
|
|
|
|
for i in range(0, len(query['sort'])):
|
|
|
|
if query['sort'][i]['key'] == 'name':
|
|
|
|
if query['group'] in ('director', ):
|
2010-07-01 12:44:18 +00:00
|
|
|
query['sort'][i]['key'] = 'person__name_sort'
|
2010-07-01 08:49:08 +00:00
|
|
|
else:
|
|
|
|
query['sort'][i]['key'] = name
|
|
|
|
if query['sort'][i]['key'] == 'items':
|
|
|
|
query['sort'][i]['key'] = items
|
|
|
|
qs = _order_query(qs, query['sort'], prefix='')
|
|
|
|
if 'ids' in query:
|
|
|
|
#FIXME: this does not scale for larger results
|
|
|
|
response['data']['positions'] = {}
|
|
|
|
ids = [j[name] for j in qs]
|
|
|
|
response['data']['positions'] = _get_positions(ids, query['ids'])
|
2010-06-30 11:18:04 +00:00
|
|
|
|
2010-07-01 08:49:08 +00:00
|
|
|
elif 'range' in data:
|
2010-06-30 11:18:04 +00:00
|
|
|
qs = qs[query['range'][0]:query['range'][1]]
|
2010-07-01 08:49:08 +00:00
|
|
|
response['data']['items'] = [{'name': i[name], 'items': i[items]} for i in qs]
|
2010-06-30 11:18:04 +00:00
|
|
|
else:
|
|
|
|
response['data']['items'] = qs.count()
|
|
|
|
elif 'ids' in query:
|
2010-07-01 08:49:08 +00:00
|
|
|
#FIXME: this does not scale for larger results
|
2010-06-30 11:18:04 +00:00
|
|
|
qs = _order_query(query['qs'], query['sort'])
|
|
|
|
|
|
|
|
response['data']['positions'] = {}
|
|
|
|
ids = [j['movieId'] for j in qs.values('movieId')]
|
2010-07-01 08:49:08 +00:00
|
|
|
response['data']['positions'] = _get_positions(ids, query['ids'])
|
|
|
|
|
|
|
|
elif 'keys' in query:
|
|
|
|
response['data']['items'] = []
|
|
|
|
qs = _order_query(query['qs'], query['sort'])
|
|
|
|
_p = query['keys']
|
|
|
|
def only_p(m):
|
|
|
|
r = {}
|
|
|
|
if m:
|
|
|
|
m = json.loads(m)
|
|
|
|
for p in _p:
|
|
|
|
r[p] = m.get(p, '')
|
|
|
|
return r
|
|
|
|
|
|
|
|
qs = qs[query['range'][0]:query['range'][1]]
|
|
|
|
response['data']['items'] = [only_p(m['json']) for m in qs.values('json')]
|
|
|
|
else: # otherwise stats
|
|
|
|
#movies = models.Movie.objects.filter(available=True)
|
2010-06-26 14:32:08 +00:00
|
|
|
movies = query['qs']
|
|
|
|
files = models.File.objects.all().filter(movie__in=movies)
|
2010-07-01 08:49:08 +00:00
|
|
|
r = files.aggregate(
|
|
|
|
Count('duration'),
|
|
|
|
Count('pixels'),
|
|
|
|
Count('size')
|
|
|
|
)
|
|
|
|
response['data']['duration'] = r['duration__count']
|
2010-01-26 13:11:57 +00:00
|
|
|
response['data']['files'] = files.count()
|
2010-07-01 08:49:08 +00:00
|
|
|
response['data']['items'] = movies.count()
|
2010-01-26 13:11:57 +00:00
|
|
|
response['data']['pixels'] = r['pixels__count']
|
2010-07-01 08:49:08 +00:00
|
|
|
response['data']['runtime'] = movies.aggregate(Sum('sort__runtime'))['sort__runtime__sum']
|
2010-07-06 18:28:42 +00:00
|
|
|
if response['data']['runtime'] == None:
|
|
|
|
response['data']['runtime'] = 1337
|
2010-01-26 13:11:57 +00:00
|
|
|
response['data']['size'] = r['size__count']
|
2009-08-01 14:14:54 +00:00
|
|
|
return render_to_json_response(response)
|
2009-06-08 16:08:59 +00:00
|
|
|
|
2010-01-25 09:20:21 +00:00
|
|
|
def api_getItem(request):
|
|
|
|
'''
|
2010-01-27 06:43:17 +00:00
|
|
|
param data
|
|
|
|
string id
|
|
|
|
|
2010-01-29 11:03:45 +00:00
|
|
|
return item array
|
2010-01-25 09:20:21 +00:00
|
|
|
'''
|
2010-02-10 13:10:28 +00:00
|
|
|
response = json_response({})
|
2010-01-25 09:20:21 +00:00
|
|
|
itemId = json.loads(request.POST['data'])
|
2010-01-27 06:43:17 +00:00
|
|
|
item = get_object_or_404_json(models.Movie, movieId=itemId)
|
|
|
|
#FIXME: check permissions
|
|
|
|
response['data'] = {'item': item.json}
|
2010-01-25 09:20:21 +00:00
|
|
|
return render_to_json_response(response)
|
|
|
|
|
2010-01-26 13:11:57 +00:00
|
|
|
@login_required_json
|
2010-01-25 09:20:21 +00:00
|
|
|
def api_editItem(request):
|
2010-01-26 02:24:05 +00:00
|
|
|
'''
|
2010-01-26 13:11:57 +00:00
|
|
|
param data
|
2010-01-27 06:43:17 +00:00
|
|
|
{id: string, key: value,..}
|
2010-01-26 13:11:57 +00:00
|
|
|
return {'status': {'code': int, 'text': string},
|
|
|
|
'data': {}}
|
2010-01-26 02:24:05 +00:00
|
|
|
'''
|
2010-01-27 06:43:17 +00:00
|
|
|
data = json.loads(request.POST['data'])
|
|
|
|
item = get_object_or_404_json(models.Movie, movieId=data['id'])
|
|
|
|
if item.editable(request.user):
|
2010-02-10 13:10:28 +00:00
|
|
|
response = json_response(status=501, text='not implemented')
|
2010-01-27 06:43:17 +00:00
|
|
|
item.edit(data)
|
|
|
|
else:
|
2010-02-10 13:10:28 +00:00
|
|
|
response = json_response(status=403, text='permissino denied')
|
2010-01-25 09:20:21 +00:00
|
|
|
return render_to_json_response(response)
|
2010-01-26 02:24:05 +00:00
|
|
|
|
2010-01-26 13:11:57 +00:00
|
|
|
@login_required_json
|
2010-01-25 09:20:21 +00:00
|
|
|
def api_removeItem(request):
|
2010-01-26 02:24:05 +00:00
|
|
|
'''
|
2010-01-26 13:11:57 +00:00
|
|
|
param data
|
2010-01-27 06:43:17 +00:00
|
|
|
string id
|
|
|
|
|
|
|
|
return {'status': {'code': int, 'text': string}}
|
2010-01-26 02:24:05 +00:00
|
|
|
'''
|
2010-02-10 13:10:28 +00:00
|
|
|
response = json_response({})
|
2010-01-27 06:43:17 +00:00
|
|
|
itemId = json.loads(request.POST['data'])
|
|
|
|
item = get_object_or_404_json(models.Movie, movieId=itemId)
|
|
|
|
if item.editable(request.user):
|
2010-02-10 13:10:28 +00:00
|
|
|
response = json_response(status=501, text='not implemented')
|
2010-01-27 06:43:17 +00:00
|
|
|
else:
|
2010-02-10 13:10:28 +00:00
|
|
|
response = json_response(status=403, text='permissino denied')
|
2010-01-25 09:20:21 +00:00
|
|
|
return render_to_json_response(response)
|
2010-01-26 02:24:05 +00:00
|
|
|
|
2010-01-26 13:11:57 +00:00
|
|
|
@login_required_json
|
2010-01-25 09:20:21 +00:00
|
|
|
def api_addLayer(request):
|
2010-01-26 02:24:05 +00:00
|
|
|
'''
|
2010-01-26 13:11:57 +00:00
|
|
|
param data
|
|
|
|
{key: value}
|
|
|
|
return {'status': {'code': int, 'text': string},
|
|
|
|
'data': {}}
|
2010-01-26 02:24:05 +00:00
|
|
|
'''
|
2010-01-29 11:03:45 +00:00
|
|
|
response = {'status': {'code': 501, 'text': 'not implemented'}}
|
2010-01-25 09:20:21 +00:00
|
|
|
return render_to_json_response(response)
|
2010-01-26 02:24:05 +00:00
|
|
|
|
2010-01-26 13:11:57 +00:00
|
|
|
@login_required_json
|
2010-01-25 09:20:21 +00:00
|
|
|
def api_removeLayer(request):
|
2010-01-26 02:24:05 +00:00
|
|
|
'''
|
2010-01-26 13:11:57 +00:00
|
|
|
param data
|
|
|
|
{key: value}
|
|
|
|
return {'status': {'code': int, 'text': string},
|
|
|
|
'data': {}}
|
2010-01-26 02:24:05 +00:00
|
|
|
'''
|
2010-01-29 11:03:45 +00:00
|
|
|
response = {'status': {'code': 501, 'text': 'not implemented'}}
|
2010-01-25 09:20:21 +00:00
|
|
|
return render_to_json_response(response)
|
2010-01-26 02:24:05 +00:00
|
|
|
|
2010-01-26 13:11:57 +00:00
|
|
|
@login_required_json
|
2010-01-25 09:20:21 +00:00
|
|
|
def api_editLayer(request):
|
2010-01-26 02:24:05 +00:00
|
|
|
'''
|
2010-01-26 13:11:57 +00:00
|
|
|
param data
|
|
|
|
{key: value}
|
|
|
|
return {'status': {'code': int, 'text': string},
|
|
|
|
'data': {}}
|
2010-01-26 02:24:05 +00:00
|
|
|
'''
|
2010-02-10 13:10:28 +00:00
|
|
|
response = json_response({})
|
2010-01-27 06:43:17 +00:00
|
|
|
data = json.loads(request.POST['data'])
|
|
|
|
layer = get_object_or_404_json(models.Layer, pk=data['id'])
|
|
|
|
if layer.editable(request.user):
|
2010-02-10 13:10:28 +00:00
|
|
|
response = json_response(status=501, text='not implemented')
|
2010-01-27 06:43:17 +00:00
|
|
|
else:
|
2010-02-10 13:10:28 +00:00
|
|
|
response = json_response(status=403, text='permission denied')
|
2010-01-27 06:43:17 +00:00
|
|
|
return render_to_json_response(response)
|
|
|
|
|
2010-02-10 13:10:28 +00:00
|
|
|
response = json_response(status=501, text='not implemented')
|
2010-01-25 09:20:21 +00:00
|
|
|
return render_to_json_response(response)
|
2010-01-26 02:24:05 +00:00
|
|
|
|
2010-01-26 13:11:57 +00:00
|
|
|
@login_required_json
|
2010-01-25 09:20:21 +00:00
|
|
|
def api_addListItem(request):
|
2010-01-26 02:24:05 +00:00
|
|
|
'''
|
2010-01-26 13:11:57 +00:00
|
|
|
param data
|
|
|
|
{key: value}
|
|
|
|
return {'status': {'code': int, 'text': string},
|
|
|
|
'data': {}}
|
2010-01-26 02:24:05 +00:00
|
|
|
'''
|
2010-02-10 13:10:28 +00:00
|
|
|
response = json_response(status=501, text='not implemented')
|
2010-01-25 09:20:21 +00:00
|
|
|
return render_to_json_response(response)
|
2010-01-26 02:24:05 +00:00
|
|
|
|
2010-01-26 13:11:57 +00:00
|
|
|
@login_required_json
|
2010-01-25 09:20:21 +00:00
|
|
|
def api_removeListItem(request):
|
2010-01-26 02:24:05 +00:00
|
|
|
'''
|
2010-01-26 13:11:57 +00:00
|
|
|
param data
|
|
|
|
{key: value}
|
|
|
|
return {'status': {'code': int, 'text': string},
|
|
|
|
'data': {}}
|
2010-01-26 02:24:05 +00:00
|
|
|
'''
|
2010-02-10 13:10:28 +00:00
|
|
|
response = json_response(status=501, text='not implemented')
|
2010-01-25 09:20:21 +00:00
|
|
|
return render_to_json_response(response)
|
2010-01-26 02:24:05 +00:00
|
|
|
|
2010-01-26 13:11:57 +00:00
|
|
|
@login_required_json
|
2010-01-25 09:20:21 +00:00
|
|
|
def api_addList(request):
|
2010-01-26 02:24:05 +00:00
|
|
|
'''
|
2010-01-26 13:11:57 +00:00
|
|
|
param data
|
|
|
|
{key: value}
|
|
|
|
return {'status': {'code': int, 'text': string},
|
|
|
|
'data': {}}
|
2010-01-26 02:24:05 +00:00
|
|
|
'''
|
2010-02-10 13:10:28 +00:00
|
|
|
response = json_response(status=501, text='not implemented')
|
2010-01-25 09:20:21 +00:00
|
|
|
return render_to_json_response(response)
|
2010-01-26 02:24:05 +00:00
|
|
|
|
2010-01-26 13:11:57 +00:00
|
|
|
@login_required_json
|
2010-01-25 09:20:21 +00:00
|
|
|
def api_editList(request):
|
2010-01-26 02:24:05 +00:00
|
|
|
'''
|
2010-01-26 13:11:57 +00:00
|
|
|
param data
|
|
|
|
{key: value}
|
|
|
|
return {'status': {'code': int, 'text': string},
|
|
|
|
'data': {}}
|
2010-01-26 02:24:05 +00:00
|
|
|
'''
|
2010-02-10 13:10:28 +00:00
|
|
|
response = json_response(status=501, text='not implemented')
|
2010-01-25 09:20:21 +00:00
|
|
|
return render_to_json_response(response)
|
2010-01-26 02:24:05 +00:00
|
|
|
|
2010-01-25 09:20:21 +00:00
|
|
|
def api_removeList(request):
|
2010-01-26 02:24:05 +00:00
|
|
|
'''
|
2010-01-26 13:11:57 +00:00
|
|
|
param data
|
|
|
|
{key: value}
|
|
|
|
return {'status': {'code': int, 'text': string},
|
|
|
|
'data': {}}
|
2010-01-26 02:24:05 +00:00
|
|
|
'''
|
2010-02-10 13:10:28 +00:00
|
|
|
response = json_response(status=501, text='not implemented')
|
2010-01-25 09:20:21 +00:00
|
|
|
return render_to_json_response(response)
|
|
|
|
|
2010-01-30 06:56:10 +00:00
|
|
|
@login_required_json
|
|
|
|
def api_addArchive(request):
|
|
|
|
'''
|
2010-02-03 11:59:11 +00:00
|
|
|
ARCHIVE API NEEDS CLEANUP
|
2010-01-30 06:56:10 +00:00
|
|
|
param data
|
|
|
|
{name: string}
|
|
|
|
return {'status': {'code': int, 'text': string},
|
|
|
|
'data': {}}
|
|
|
|
'''
|
|
|
|
data = json.loads(request.POST['data'])
|
|
|
|
try:
|
|
|
|
archive = models.Archive.objects.get(name=data['name'])
|
2010-01-31 08:56:44 +00:00
|
|
|
response = {'status': {'code': 401, 'text': 'archive with this name exists'}}
|
2010-01-30 06:56:10 +00:00
|
|
|
except models.Archive.DoesNotExist:
|
|
|
|
archive = models.Archive(name=data['name'])
|
|
|
|
archive.save()
|
|
|
|
archive.users.add(request.user)
|
2010-02-10 13:10:28 +00:00
|
|
|
response = json_response({})
|
|
|
|
response['status']['text'] = 'archive created'
|
2010-01-30 06:56:10 +00:00
|
|
|
return render_to_json_response(response)
|
|
|
|
|
|
|
|
@login_required_json
|
|
|
|
def api_editArchive(request):
|
|
|
|
'''
|
2010-02-03 11:59:11 +00:00
|
|
|
ARCHIVE API NEEDS CLEANUP
|
2010-01-30 06:56:10 +00:00
|
|
|
param data
|
|
|
|
{id: string, key: value,..}
|
|
|
|
return {'status': {'code': int, 'text': string},
|
|
|
|
'data': {}}
|
|
|
|
'''
|
|
|
|
data = json.loads(request.POST['data'])
|
|
|
|
item = get_object_or_404_json(models.Archive, name=data['name'])
|
|
|
|
if item.editable(request.user):
|
2010-02-10 13:10:28 +00:00
|
|
|
response = json_response(status=501, text='not implemented')
|
2010-01-30 06:56:10 +00:00
|
|
|
item.edit(data)
|
|
|
|
else:
|
2010-02-10 13:10:28 +00:00
|
|
|
response = json_response(status=403, text='permission denied')
|
2010-01-30 06:56:10 +00:00
|
|
|
return render_to_json_response(response)
|
|
|
|
|
|
|
|
@login_required_json
|
|
|
|
def api_removeArchive(request):
|
|
|
|
'''
|
2010-02-03 11:59:11 +00:00
|
|
|
ARCHIVE API NEEDS CLEANUP
|
2010-01-30 06:56:10 +00:00
|
|
|
param data
|
|
|
|
string id
|
|
|
|
|
|
|
|
return {'status': {'code': int, 'text': string}}
|
|
|
|
'''
|
2010-02-10 13:10:28 +00:00
|
|
|
response = json_response({})
|
2010-01-30 06:56:10 +00:00
|
|
|
itemId = json.loads(request.POST['data'])
|
|
|
|
item = get_object_or_404_json(models.Archive, movieId=itemId)
|
|
|
|
if item.editable(request.user):
|
2010-02-10 13:10:28 +00:00
|
|
|
response = json_response(status=501, text='not implemented')
|
2010-01-30 06:56:10 +00:00
|
|
|
else:
|
2010-02-10 13:10:28 +00:00
|
|
|
response = json_response(status=403, text='permission denied')
|
2010-01-30 06:56:10 +00:00
|
|
|
return render_to_json_response(response)
|
|
|
|
|
|
|
|
|
|
|
|
|
2010-01-25 09:20:21 +00:00
|
|
|
#@login_required_json
|
2010-01-25 14:57:10 +00:00
|
|
|
def api_update(request):
|
2010-01-26 13:11:57 +00:00
|
|
|
'''
|
|
|
|
param data
|
|
|
|
{archive: string, files: json}
|
|
|
|
return {'status': {'code': int, 'text': string},
|
2010-01-29 11:03:45 +00:00
|
|
|
'data': {info: object, rename: object}}
|
2010-01-26 13:11:57 +00:00
|
|
|
'''
|
2010-01-25 09:20:21 +00:00
|
|
|
data = json.loads(request.POST['data'])
|
|
|
|
archive = data['archive']
|
|
|
|
files = data['files']
|
2010-01-29 11:03:45 +00:00
|
|
|
archive = get_object_or_404_json(models.Archive, name=archive)
|
|
|
|
if archive.editable(request.user):
|
|
|
|
needs_data = []
|
|
|
|
rename = {}
|
|
|
|
for oshash in files:
|
|
|
|
data = files[oshash]
|
|
|
|
q = models.ArchiveFile.objects.filter(archive=archive, file__oshash=oshash)
|
|
|
|
if q.count() == 0:
|
2010-01-30 06:56:10 +00:00
|
|
|
#print "adding file", oshash, data['path']
|
2010-01-29 11:03:45 +00:00
|
|
|
f = models.ArchiveFile.get_or_create(archive, oshash)
|
|
|
|
f.update(data)
|
|
|
|
if not f.file.movie:
|
2010-06-25 11:53:57 +00:00
|
|
|
task.findMovie(f.file.id)
|
2010-01-29 11:03:45 +00:00
|
|
|
#FIXME: only add if it was not in File
|
|
|
|
else:
|
|
|
|
f = q[0]
|
|
|
|
if data['path'] != f.path:
|
|
|
|
f.path = data['path']
|
|
|
|
f.save()
|
|
|
|
if f.file.needs_data:
|
|
|
|
needs_data.append(oshash)
|
|
|
|
if f.path != f.file.path:
|
|
|
|
rename[oshash] = f.file.path
|
2010-01-30 06:56:10 +00:00
|
|
|
#print "processed files for", archive.name
|
2010-01-29 11:03:45 +00:00
|
|
|
#remove all files not in files.keys() from ArchiveFile
|
2010-02-10 13:10:28 +00:00
|
|
|
response = json_response({'info': needs_data, 'rename': rename})
|
2010-01-29 11:03:45 +00:00
|
|
|
else:
|
2010-02-10 13:10:28 +00:00
|
|
|
response = json_response(status=403, text='permission denied')
|
2010-01-25 09:20:21 +00:00
|
|
|
return render_to_json_response(response)
|
|
|
|
|
2010-02-03 11:59:11 +00:00
|
|
|
def api_encodingSettings(request):
|
|
|
|
'''
|
|
|
|
returns Firefogg encoding settings as specified by site
|
|
|
|
return {'status': {'code': int, 'text': string},
|
|
|
|
'data': {'options': {'videoQuality':...}}}
|
|
|
|
'''
|
2010-02-10 13:10:28 +00:00
|
|
|
response = json_response({'options': settings.VIDEO_ENCODING[settings.VIDEO_PROFILE]})
|
2010-02-03 11:59:11 +00:00
|
|
|
return render_to_json_response(response)
|
|
|
|
|
2010-01-31 10:14:17 +00:00
|
|
|
class UploadForm(forms.Form):
|
|
|
|
data = forms.TextInput()
|
|
|
|
file = forms.FileField()
|
|
|
|
|
2010-02-03 11:59:11 +00:00
|
|
|
class VideoChunkForm(forms.Form):
|
|
|
|
chunk = forms.FileField()
|
|
|
|
done = forms.IntegerField(required=False)
|
|
|
|
|
2010-02-07 12:15:10 +00:00
|
|
|
@login_required_json
|
2010-01-25 09:20:21 +00:00
|
|
|
def api_upload(request): #video, timeline, frame
|
2010-01-26 13:11:57 +00:00
|
|
|
'''
|
|
|
|
upload video, timeline or frame
|
|
|
|
param data
|
|
|
|
param file
|
|
|
|
return {'status': {'code': int, 'text': string},
|
|
|
|
'data': {}}
|
|
|
|
'''
|
2010-02-03 11:59:11 +00:00
|
|
|
form = UploadForm(request.POST, request.FILES)
|
2010-01-31 10:14:17 +00:00
|
|
|
if form.is_valid():
|
2010-02-06 11:34:20 +00:00
|
|
|
data = json.loads(request.POST['data'])
|
2010-02-06 08:24:39 +00:00
|
|
|
oshash = data['oshash']
|
|
|
|
f = get_object_or_404(models.File, oshash=oshash)
|
|
|
|
if data['item'] == 'frame':
|
|
|
|
ff = form.cleaned_data['file']
|
|
|
|
position = data['position']
|
2010-02-06 11:34:20 +00:00
|
|
|
frame, created = models.Frame.objects.get_or_create(file=f, position=position)
|
|
|
|
if not created and frame.frame:
|
|
|
|
frame.frame.delete()
|
2010-02-06 08:24:39 +00:00
|
|
|
frame.frame.save(ff.name, ff)
|
|
|
|
frame.save()
|
2010-02-10 13:10:28 +00:00
|
|
|
response = json_response({'url': frame.frame.url})
|
2010-02-06 08:24:39 +00:00
|
|
|
return render_to_json_response(response)
|
2010-01-31 10:14:17 +00:00
|
|
|
if data['item'] == 'timeline':
|
|
|
|
pass
|
|
|
|
#print "not implemented"
|
2010-02-03 11:59:11 +00:00
|
|
|
|
2010-02-10 13:10:28 +00:00
|
|
|
response = json_response(status=501, text='not implemented')
|
2010-01-25 09:20:21 +00:00
|
|
|
return render_to_json_response(response)
|
|
|
|
|
2010-02-03 11:59:11 +00:00
|
|
|
@login_required_json
|
|
|
|
def firefogg_upload(request):
|
|
|
|
#handle video upload
|
|
|
|
if request.method == 'POST':
|
|
|
|
#init upload
|
|
|
|
if 'oshash' in request.POST:
|
|
|
|
#FIXME: what to do if requested oshash is not in db?
|
|
|
|
#FIXME: should existing data be reset here? or better, should this fail if an upload was there
|
|
|
|
f = get_object_or_404(models.File, oshash=request.POST['oshash'])
|
2010-02-08 10:26:25 +00:00
|
|
|
stream = getattr(f, 'stream_%s'%settings.VIDEO_UPLOAD)
|
|
|
|
if stream:
|
|
|
|
stream.delete()
|
2010-02-07 12:15:10 +00:00
|
|
|
f.available = False
|
|
|
|
f.save()
|
2010-02-03 11:59:11 +00:00
|
|
|
response = {
|
|
|
|
'uploadUrl': request.build_absolute_uri('/api/upload/?oshash=%s' % f.oshash),
|
|
|
|
'result': 1
|
|
|
|
}
|
|
|
|
return render_to_json_response(response)
|
|
|
|
#post next chunk
|
|
|
|
if 'chunk' in request.FILES and 'oshash' in request.GET:
|
|
|
|
print "all chunk now"
|
|
|
|
f = get_object_or_404(models.File, oshash=request.GET['oshash'])
|
|
|
|
form = VideoChunkForm(request.POST, request.FILES)
|
|
|
|
#FIXME:
|
|
|
|
if form.is_valid() and f.editable(request.user):
|
|
|
|
c = form.cleaned_data['chunk']
|
|
|
|
response = {
|
|
|
|
'result': 1,
|
|
|
|
'resultUrl': request.build_absolute_uri('/')
|
|
|
|
}
|
|
|
|
if not f.save_chunk(c, c.name):
|
|
|
|
response['result'] = -1
|
|
|
|
elif form.cleaned_data['done']:
|
|
|
|
#FIXME: send message to encode deamon to create derivates instead
|
|
|
|
f.available = True
|
|
|
|
f.save()
|
|
|
|
response['result'] = 1
|
|
|
|
response['done'] = 1
|
|
|
|
return render_to_json_response(response)
|
|
|
|
print request.GET, request.POST
|
2010-02-10 13:10:28 +00:00
|
|
|
response = json_response(status=400, text='this request requires POST')
|
2010-02-03 11:59:11 +00:00
|
|
|
return render_to_json_response(response)
|
|
|
|
|
2010-01-26 13:11:57 +00:00
|
|
|
@login_required_json
|
2010-01-26 02:24:05 +00:00
|
|
|
def api_editFile(request): #FIXME: should this be file.files. or part of update
|
2010-01-25 09:20:21 +00:00
|
|
|
'''
|
|
|
|
change file / imdb link
|
|
|
|
'''
|
2010-02-10 13:10:28 +00:00
|
|
|
response = json_response(status=501, text='not implemented')
|
2010-01-25 09:20:21 +00:00
|
|
|
return render_to_json_response(response)
|
|
|
|
|
|
|
|
def api_parse(request): #parse path and return info
|
2010-01-26 13:11:57 +00:00
|
|
|
'''
|
|
|
|
param data
|
|
|
|
{path: string}
|
|
|
|
return {'status': {'code': int, 'text': string},
|
|
|
|
data: {imdb: string}}
|
|
|
|
'''
|
|
|
|
path = json.loads(request.POST['data'])['path']
|
2010-02-10 13:10:28 +00:00
|
|
|
response = json_response(utils.parsePath(path))
|
2010-01-25 09:20:21 +00:00
|
|
|
return render_to_json_response(response)
|
|
|
|
|
2010-01-26 13:11:57 +00:00
|
|
|
def api_getImdbId(request):
|
|
|
|
'''
|
|
|
|
param data
|
|
|
|
{title: string, director: string, year: string}
|
|
|
|
return {'status': {'code': int, 'text': string},
|
|
|
|
'data': {imdbId:string }}
|
|
|
|
'''
|
|
|
|
imdbId = oxweb.imdb.guess(search_title, r['director'], timeout=-1)
|
|
|
|
if imdbId:
|
2010-02-10 13:10:28 +00:00
|
|
|
response = json_response({'imdbId': imdbId})
|
2010-01-26 13:11:57 +00:00
|
|
|
else:
|
2010-02-10 13:10:28 +00:00
|
|
|
response = json_response(status=404, text='not found')
|
2010-01-25 09:20:21 +00:00
|
|
|
return render_to_json_response(response)
|
|
|
|
|
2010-01-29 11:03:45 +00:00
|
|
|
def api_fileInfo(request):
|
2010-01-27 06:43:17 +00:00
|
|
|
'''
|
|
|
|
param data
|
|
|
|
oshash string
|
|
|
|
return {'status': {'code': int, 'text': string},
|
|
|
|
'data': {imdbId:string }}
|
|
|
|
'''
|
|
|
|
if 'data' in request.POST:
|
|
|
|
oshash = json.loads(request.POST['data'])
|
|
|
|
elif 'oshash' in request.GET:
|
|
|
|
oshash = request.GET['oshash']
|
2009-08-16 12:23:29 +00:00
|
|
|
f = models.MovieFile.objects.get(oshash=oshash)
|
2010-01-27 06:43:17 +00:00
|
|
|
response = {'data': f.json()}
|
2009-08-16 12:23:29 +00:00
|
|
|
return render_to_json_response(response)
|
2009-06-08 16:08:59 +00:00
|
|
|
|
2010-01-29 11:03:45 +00:00
|
|
|
def api_subtitles(request):
|
|
|
|
'''
|
|
|
|
param data
|
|
|
|
oshash string
|
|
|
|
language string
|
|
|
|
subtitle string
|
|
|
|
return
|
|
|
|
if no language is provided:
|
|
|
|
{data: {languages: array}}
|
|
|
|
if language is set:
|
|
|
|
{data: {subtitle: string}}
|
|
|
|
if subtitle is set:
|
|
|
|
saves subtitle for given language
|
|
|
|
'''
|
|
|
|
if 'data' in request.POST:
|
|
|
|
data = json.loads(request.POST['data'])
|
|
|
|
oshash = data['oshash']
|
|
|
|
language = data.get('language', None)
|
|
|
|
srt = data.get('subtitle', None)
|
|
|
|
if srt:
|
2009-08-16 12:23:29 +00:00
|
|
|
user = request.user
|
2010-02-03 12:43:41 +00:00
|
|
|
sub = models.Subtitles.objects.get_or_create(user, oshash, language)
|
2010-01-29 11:03:45 +00:00
|
|
|
sub.srt = srt
|
2009-08-16 12:23:29 +00:00
|
|
|
sub.save()
|
|
|
|
else:
|
2010-02-10 13:10:28 +00:00
|
|
|
response = json_response({})
|
2009-08-16 12:23:29 +00:00
|
|
|
if language:
|
|
|
|
q = models.Subtitles.objects.filter(movie_file__oshash=oshash, language=language)
|
|
|
|
if q.count() > 0:
|
2010-01-29 11:03:45 +00:00
|
|
|
response['data']['subtitle'] = q[0].srt
|
|
|
|
return render_to_json_response(response)
|
2009-08-16 12:23:29 +00:00
|
|
|
l = models.Subtitles.objects.filter(movie_file__oshash=oshash).values('language')
|
2010-01-29 11:03:45 +00:00
|
|
|
response['data']['languages'] = [f['language'] for f in l]
|
2009-08-16 12:23:29 +00:00
|
|
|
return render_to_json_response(response)
|
2010-02-16 12:41:57 +00:00
|
|
|
|
|
|
|
def video(request, id, quality):
|
|
|
|
movie = get_object_or_404(models.Movie, movieId=id)
|
|
|
|
if quality not in settings.VIDEO_ENCODING:
|
|
|
|
raise Http404
|
|
|
|
stream = getattr(movie, 'stream_'+quality)
|
|
|
|
response = HttpFileResponse(stream.path, content_type='video/ogg')
|
|
|
|
#FIXME: movie needs duration field
|
|
|
|
#response['Content-Duration'] = movie.duration
|
|
|
|
return response
|
|
|
|
|
|
|
|
def frame(request, id, position, size):
|
|
|
|
movie = get_object_or_404(models.Movie, movieId=id)
|
|
|
|
position = oxlib.time2ms(position)/1000
|
|
|
|
frame = movie.frame(position, int(size))
|
|
|
|
if not frame:
|
|
|
|
raise Http404
|
|
|
|
return HttpFileResponse(frame, content_type='image/jpeg')
|
2009-06-08 16:08:59 +00:00
|
|
|
|
|
|
|
'''
|
|
|
|
GET list
|
|
|
|
> {
|
|
|
|
"files": {
|
|
|
|
"a41cde31c581e11d": {"path": "E/Example, The/An Example.avi", "size":1646274},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
'''
|
2009-10-04 22:00:08 +00:00
|
|
|
@login_required_json
|
2009-06-08 16:08:59 +00:00
|
|
|
def list_files(request):
|
2009-12-31 15:04:32 +00:00
|
|
|
response = {}
|
2009-08-16 12:23:29 +00:00
|
|
|
response['files'] = {}
|
|
|
|
qs = models.UserFile.filter(user=request.user)
|
|
|
|
p = Paginator(qs, 1000)
|
|
|
|
for i in p.page_range:
|
|
|
|
page = p.page(i)
|
|
|
|
for f in page.object_list:
|
|
|
|
response['files'][f.movie_file.oshash] = {'path': f.path, 'size': f.movie_file.size}
|
|
|
|
return render_to_json_response(response)
|
|
|
|
|
|
|
|
def find_files(request):
|
2009-12-31 15:04:32 +00:00
|
|
|
response = {}
|
2010-01-25 09:20:21 +00:00
|
|
|
query = _parse_query(request)
|
2009-08-16 12:23:29 +00:00
|
|
|
response['files'] = {}
|
2009-12-31 15:04:32 +00:00
|
|
|
qs = models.UserFile.filter(user=request.user).filter(movie_file__movie__id__in=query['q'])
|
2009-08-16 12:23:29 +00:00
|
|
|
p = Paginator(qs, 1000)
|
|
|
|
for i in p.page_range:
|
|
|
|
page = p.page(i)
|
|
|
|
for f in page.object_list:
|
|
|
|
response['files'][f.movie_file.oshash] = {'path': f.path, 'size': f.movie_file.size}
|
|
|
|
return render_to_json_response(response)
|
2009-06-08 16:08:59 +00:00
|
|
|
|
2009-12-31 15:04:32 +00:00
|
|
|
|
2010-01-26 02:24:05 +00:00
|
|
|
def apidoc(request):
|
2010-01-26 13:11:57 +00:00
|
|
|
'''
|
|
|
|
this is used for online documentation at http://127.0.0.1:8000/api/
|
|
|
|
'''
|
|
|
|
import sys
|
|
|
|
def trim(docstring):
|
|
|
|
if not docstring:
|
|
|
|
return ''
|
|
|
|
# Convert tabs to spaces (following the normal Python rules)
|
|
|
|
# and split into a list of lines:
|
|
|
|
lines = docstring.expandtabs().splitlines()
|
|
|
|
# Determine minimum indentation (first line doesn't count):
|
|
|
|
indent = sys.maxint
|
|
|
|
for line in lines[1:]:
|
|
|
|
stripped = line.lstrip()
|
|
|
|
if stripped:
|
|
|
|
indent = min(indent, len(line) - len(stripped))
|
|
|
|
# Remove indentation (first line is special):
|
|
|
|
trimmed = [lines[0].strip()]
|
|
|
|
if indent < sys.maxint:
|
|
|
|
for line in lines[1:]:
|
|
|
|
trimmed.append(line[indent:].rstrip())
|
|
|
|
# Strip off trailing and leading blank lines:
|
|
|
|
while trimmed and not trimmed[-1]:
|
|
|
|
trimmed.pop()
|
|
|
|
while trimmed and not trimmed[0]:
|
|
|
|
trimmed.pop(0)
|
|
|
|
# Return a single string:
|
|
|
|
return '\n'.join(trimmed)
|
2010-01-26 02:24:05 +00:00
|
|
|
|
|
|
|
functions = filter(lambda x: x.startswith('api_'), globals().keys())
|
2010-01-26 13:11:57 +00:00
|
|
|
api = []
|
|
|
|
for f in sorted(functions):
|
|
|
|
api.append({
|
|
|
|
'name': f[4:],
|
|
|
|
'doc': trim(globals()[f].__doc__).replace('\n', '<br>\n')
|
|
|
|
})
|
2010-02-16 10:22:34 +00:00
|
|
|
context = RequestContext(request, {'api': api,
|
|
|
|
'sitename': settings.SITENAME,})
|
2010-01-26 02:24:05 +00:00
|
|
|
return render_to_response('api.html', context)
|