440 lines
15 KiB
Python
440 lines
15 KiB
Python
# -*- coding: utf-8 -*-
|
|
# vi:si:et:sw=4:sts=4:ts=4
|
|
from __future__ import division
|
|
|
|
import os.path
|
|
from datetime import datetime, timedelta
|
|
import mimetypes
|
|
import re
|
|
|
|
from django.db.models import Count, Sum, Max
|
|
from django.http import HttpResponse, Http404
|
|
from django.shortcuts import get_object_or_404, redirect
|
|
from django.conf import settings
|
|
|
|
from ox.utils import json
|
|
|
|
from ox.django.decorators import login_required_json
|
|
from ox.django.shortcuts import render_to_json_response, get_object_or_404_json, json_response
|
|
from ox.django.http import HttpFileResponse
|
|
import ox
|
|
|
|
import models
|
|
import utils
|
|
|
|
from api.actions import actions
|
|
|
|
from pymongo import ASCENDING, DESCENDING
|
|
|
|
|
|
def _order_query(qs, sort, prefix='sort__'):
|
|
order_by = []
|
|
if len(sort) == 1:
|
|
if sort[0]['key'] == 'title':
|
|
sort.append({'operator': '-', 'key': 'year'})
|
|
sort.append({'operator': '+', 'key': 'director'})
|
|
elif sort[0]['key'] == 'director':
|
|
sort.append({'operator': '-', 'key': 'year'})
|
|
sort.append({'operator': '+', 'key': 'title'})
|
|
elif sort[0]['key'] == 'year':
|
|
sort.append({'operator': '+', 'key': 'director'})
|
|
sort.append({'operator': '+', 'key': 'title'})
|
|
elif not sort[0]['key'] in ('value', 'value_sort'):
|
|
sort.append({'operator': '+', 'key': 'director'})
|
|
sort.append({'operator': '-', 'key': 'year'})
|
|
sort.append({'operator': '+', 'key': 'title'})
|
|
|
|
for e in sort:
|
|
operator = e['operator']
|
|
if operator != '-':
|
|
operator = ''
|
|
key = {
|
|
'id': 'itemId',
|
|
'accessed': 'accessed__access',
|
|
'viewed': 'accessed__access',
|
|
}.get(e['key'], e['key'])
|
|
if key not in ('accessed__access', 'accessed__accessed'):
|
|
key = "%s%s" % (prefix, key)
|
|
order = '%s%s' % (operator, key)
|
|
order_by.append(order)
|
|
if order_by:
|
|
qs = qs.order_by(*order_by, nulls_last=True)
|
|
return qs
|
|
|
|
def _order_by_group(query):
|
|
if 'sort' in query:
|
|
if len(query['sort']) == 1 and query['sort'][0]['key'] == 'items':
|
|
if query['group'] == "year":
|
|
order_by = query['sort'][0]['operator'] == '-' and 'items' or '-items'
|
|
else:
|
|
order_by = query['sort'][0]['operator'] == '-' and '-items' or 'items'
|
|
if query['group'] != "keyword":
|
|
order_by = (order_by, 'value_sort')
|
|
else:
|
|
order_by = (order_by,)
|
|
else:
|
|
order_by = query['sort'][0]['operator'] == '-' and '-value_sort' or 'value_sort'
|
|
order_by = (order_by, 'items')
|
|
else:
|
|
order_by = ('-value_sort', 'items')
|
|
return order_by
|
|
|
|
def parseQuery(q):
|
|
'''
|
|
query: {
|
|
conditions: [
|
|
{
|
|
value: "war"
|
|
}
|
|
{
|
|
key: "year",
|
|
value: "1970-1980,
|
|
operator: "!="
|
|
},
|
|
{
|
|
key: "country",
|
|
value: "f",
|
|
operator: "^"
|
|
}
|
|
],
|
|
operator: "&"
|
|
}
|
|
'''
|
|
#FIXME: support or operator
|
|
#FIXME: support sub conditions with $or/$and
|
|
#http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-ConditionalOperators
|
|
conditions = q.get('conditions', [])
|
|
operator = q.get('operator', '&')
|
|
|
|
query = {}
|
|
for c in conditions:
|
|
key = c.get('key', 'all')
|
|
op = c.get('operator')
|
|
value = c['value']
|
|
if op == '>':
|
|
mop = '$gt'
|
|
elif op == '>=':
|
|
mop = '$gte'
|
|
elif op == '<':
|
|
mop = '$lt'
|
|
elif op == '<=':
|
|
mop = '$lte'
|
|
elif op == '!=':
|
|
mop = '$ne'
|
|
elif op == '^':
|
|
mop = None
|
|
value = re.compile(u'^%s'%value, re.IGNORECASE)
|
|
elif op == '$':
|
|
mop = None
|
|
value = re.compile(u'%s$'%value, re.IGNORECASE)
|
|
elif op == '=':
|
|
mop = None
|
|
else:
|
|
mop = None
|
|
value = re.compile(u'%s'%value, re.IGNORECASE)
|
|
|
|
if mop:
|
|
query[key]= {mop: value}
|
|
else:
|
|
query[key] = value
|
|
|
|
return query
|
|
|
|
def parse_query(data, user):
|
|
query = {}
|
|
query['range'] = [0, 100]
|
|
query['sort'] = [{'key':'title', 'operator':'+'}]
|
|
for key in ('sort', 'keys', 'group', 'range', 'position', 'positions'):
|
|
if key in data:
|
|
query[key] = data[key]
|
|
query['q'] = parseQuery(data.get('query', {}))
|
|
#query['qs'] = models.Item.objects.find(data, user)
|
|
|
|
#group by only allows sorting by name or number of itmes
|
|
return query
|
|
|
|
def find(request):
|
|
'''
|
|
param data {
|
|
'query': query,
|
|
'sort': array,
|
|
'range': array
|
|
}
|
|
|
|
query: query object, more on query syntax at
|
|
https://wiki.0x2620.org/wiki/pandora/QuerySyntax
|
|
sort: array of key, operator dics
|
|
[
|
|
{
|
|
key: "year",
|
|
operator: "-"
|
|
},
|
|
{
|
|
key: "director",
|
|
operator: ""
|
|
}
|
|
]
|
|
range: result range, array [from, to]
|
|
keys: array of keys to return
|
|
group: group elements by, country, genre, director...
|
|
|
|
with keys, items is list of dicts with requested properties:
|
|
return {'status': {'code': int, 'text': string},
|
|
'data': {items: array}}
|
|
|
|
Groups
|
|
param data {
|
|
'query': query,
|
|
'key': string,
|
|
'group': string,
|
|
'range': array
|
|
}
|
|
|
|
query: query object, more on query syntax at
|
|
https://wiki.0x2620.org/wiki/pandora/QuerySyntax
|
|
range: result range, array [from, to]
|
|
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}}
|
|
|
|
without keys: return number of items in given query
|
|
return {'status': {'code': int, 'text': string},
|
|
'data': {items: int}}
|
|
|
|
Positions
|
|
param data {
|
|
'query': query,
|
|
'positions': [],
|
|
'sort': array
|
|
}
|
|
|
|
query: query object, more on query syntax at
|
|
https://wiki.0x2620.org/wiki/pandora/QuerySyntax
|
|
positions: ids of items for which positions are required
|
|
return {
|
|
status: {...},
|
|
data: {
|
|
positions: {
|
|
id: position
|
|
}
|
|
}
|
|
}
|
|
'''
|
|
data = json.loads(request.POST['data'])
|
|
if settings.JSON_DEBUG:
|
|
print json.dumps(data, indent=2)
|
|
query = parse_query(data, request.user)
|
|
|
|
response = json_response({})
|
|
if 'group' in query:
|
|
'''
|
|
response['data']['items'] = []
|
|
items = 'items'
|
|
item_qs = query['qs']
|
|
order_by = _order_by_group(query)
|
|
qs = models.Facet.objects.filter(key=query['group']).filter(item__id__in=item_qs)
|
|
qs = qs.values('value').annotate(items=Count('id')).order_by(*order_by)
|
|
|
|
if 'positions' in query:
|
|
response['data']['positions'] = {}
|
|
ids = [j['value'] for j in qs]
|
|
response['data']['positions'] = utils.get_positions(ids, query['positions'])
|
|
elif 'range' in data:
|
|
qs = qs[query['range'][0]:query['range'][1]]
|
|
response['data']['items'] = [{'name': i['value'], 'items': i[items]} for i in qs]
|
|
else:
|
|
response['data']['items'] = qs.count()
|
|
'''
|
|
if 'positions' in query:
|
|
response['data']['positions'] = {}
|
|
elif 'range' in data:
|
|
response['data']['items'] = []
|
|
else:
|
|
response['data']['items'] = 0
|
|
elif 'position' in query:
|
|
'''
|
|
qs = _order_query(query['qs'], query['sort'])
|
|
ids = [j['itemId'] for j in qs.values('itemId')]
|
|
data['conditions'] = data['conditions'] + {
|
|
'value': query['position'],
|
|
'key': query['sort'][0]['key'],
|
|
'operator': '^'
|
|
}
|
|
query = parse_query(data, request.user)
|
|
qs = _order_query(query['qs'], query['sort'])
|
|
if qs.count() > 0:
|
|
response['data']['position'] = utils.get_positions(ids, [qs[0].itemId])[0]
|
|
'''
|
|
response['data']['position'] = -1
|
|
elif 'positions' in query:
|
|
'''
|
|
qs = _order_query(query['qs'], query['sort'])
|
|
ids = [j['itemId'] for j in qs.values('itemId')]
|
|
response['data']['positions'] = utils.get_positions(ids, query['positions'])
|
|
'''
|
|
response['data']['position'] = {}
|
|
elif 'keys' in query:
|
|
response['data']['items'] = []
|
|
'''
|
|
qs = _order_query(query['qs'], query['sort'])
|
|
_p = query['keys']
|
|
def only_p_sums(m):
|
|
r = {}
|
|
for p in _p:
|
|
if p == 'viewed' and request.user.is_authenticated():
|
|
value = m.accessed.filter(user=request.user).annotate(v=Max('access'))
|
|
r[p] = value.exists() and value[0].v or None
|
|
elif p == 'accessed':
|
|
r[p] = m.a
|
|
elif p == 'popularity':
|
|
r[p] = m.sort.popularity
|
|
else:
|
|
r[p] = m.json.get(p, '')
|
|
if 'annotations' in query:
|
|
n = query['annotations']
|
|
r['annotations'] = [a.json(layer=True)
|
|
for a in query['aqs'].filter(itemID=m.id)[:n]]
|
|
return r
|
|
def only_p(m):
|
|
r = {}
|
|
if m:
|
|
m = json.loads(m, object_hook=ox.django.fields.from_json)
|
|
for p in _p:
|
|
r[p] = m.get(p, '')
|
|
if 'annotations' in query:
|
|
n = query['annotations']
|
|
r['annotations'] = [a.json(layer=True)
|
|
for a in query['aqs'].filter(item__itemId=m['id'])[:n]]
|
|
return r
|
|
|
|
qs = qs[query['range'][0]:query['range'][1]]
|
|
#response['data']['items'] = [m.get_json(_p) for m in qs]
|
|
if 'popularity' in _p:
|
|
qs = qs.annotate(popularity=Sum('accessed__accessed'))
|
|
if 'accessed' in _p:
|
|
qs = qs.annotate(a=Max('accessed__access'))
|
|
if 'viewed' in _p or 'popularity' in _p or 'accessed' in _p:
|
|
response['data']['items'] = [only_p_sums(m) for m in qs]
|
|
else:
|
|
response['data']['items'] = [only_p(m['json']) for m in qs.values('json')]
|
|
'''
|
|
def only_p(i):
|
|
r = {}
|
|
for key in query['keys']:
|
|
r[key] = i.get(key, '')
|
|
return r
|
|
print query
|
|
skey = query['sort'][0]['key']
|
|
if query['sort'][0]['operator'] == '+':
|
|
sdir = ASCENDING
|
|
else:
|
|
sdir = DESCENDING
|
|
qs = models.items.find(query['q']).sort(skey, sdir)
|
|
if query['range'][0] < query['range'][1]:
|
|
qs = qs[query['range'][0]:query['range'][1]]
|
|
response['data']['items'] = [only_p(i) for i in qs]
|
|
|
|
else: # otherwise stats
|
|
'''
|
|
items = query['qs']
|
|
files = File.objects.filter(item__in=items).filter(size__gt=0)
|
|
r = files.aggregate(
|
|
Sum('duration'),
|
|
Sum('pixels'),
|
|
Sum('size')
|
|
)
|
|
response['data']['duration'] = r['duration__sum']
|
|
response['data']['files'] = files.count()
|
|
response['data']['items'] = items.count()
|
|
response['data']['pixels'] = r['pixels__sum']
|
|
response['data']['runtime'] = items.aggregate(Sum('sort__runtime'))['sort__runtime__sum']
|
|
response['data']['size'] = r['size__sum']
|
|
for key in ('runtime', 'duration', 'pixels', 'size'):
|
|
if response['data'][key] == None:
|
|
response['data'][key] = 0
|
|
'''
|
|
response['data']['items'] = models.items.find(query['q']).count()
|
|
|
|
return render_to_json_response(response)
|
|
actions.register(find)
|
|
|
|
|
|
def autocomplete(request):
|
|
'''
|
|
param data
|
|
key
|
|
value
|
|
operator '', '^', '$'
|
|
range
|
|
return
|
|
'''
|
|
data = json.loads(request.POST['data'])
|
|
if not 'range' in data:
|
|
data['range'] = [0, 10]
|
|
op = data.get('operator', '')
|
|
|
|
site_config = models.site_config()
|
|
key = site_config['keys'][data['key']]
|
|
order_by = key.get('autocompleteSortKey', False)
|
|
if order_by:
|
|
order_by = '-sort__%s' % order_by
|
|
else:
|
|
order_by = '-items'
|
|
sort_type = key.get('sort', key.get('type', 'string'))
|
|
if sort_type == 'title':
|
|
qs = parse_query({'query': data.get('query', {})}, request.user)['qs']
|
|
if data['value']:
|
|
if op == '':
|
|
qs = qs.filter(find__key=data['key'], find__value__icontains=data['value'])
|
|
elif op == '^':
|
|
qs = qs.filter(find__key=data['key'], find__value__istartswith=data['value'])
|
|
elif op == '$':
|
|
qs = qs.filter(find__key=data['key'], find__value__iendswith=data['value'])
|
|
qs = qs.order_by(order_by, nulls_last=True)
|
|
qs = qs[data['range'][0]:data['range'][1]]
|
|
response = json_response({})
|
|
response['data']['items'] = [i.get(data['key']) for i in qs]
|
|
else:
|
|
qs = models.Facet.objects.filter(key=data['key'])
|
|
if data['value']:
|
|
if op == '':
|
|
qs = qs.filter(value__icontains=data['value'])
|
|
elif op == '^':
|
|
qs = qs.filter(value__istartswith=data['value'])
|
|
elif op == '$':
|
|
qs = qs.filter(value__iendswith=data['value'])
|
|
qs = qs.values('value').annotate(items=Count('id'))
|
|
qs = qs.order_by(order_by)
|
|
qs = qs[data['range'][0]:data['range'][1]]
|
|
response = json_response({})
|
|
response['data']['items'] = [i['value'] for i in qs]
|
|
return render_to_json_response(response)
|
|
actions.register(autocomplete)
|
|
|
|
|
|
def get(request):
|
|
'''
|
|
param data {
|
|
id: string
|
|
keys: array
|
|
}
|
|
return item array
|
|
'''
|
|
response = json_response({})
|
|
data = json.loads(request.POST['data'])
|
|
item = models.items.find_one({'refid': data['id']})
|
|
if item:
|
|
del item['_id']
|
|
response['data'] = item
|
|
else:
|
|
response = json_response(status=404, text='not found')
|
|
return render_to_json_response(response)
|
|
actions.register(get)
|
|
|