diff --git a/pandora/place/managers.py b/pandora/place/managers.py index 4cdf181d..2ee07679 100644 --- a/pandora/place/managers.py +++ b/pandora/place/managers.py @@ -1,35 +1,151 @@ # -*- coding: utf-8 -*- # vi:si:et:sw=4:sts=4:ts=4 from django.db.models import Q, Manager +from ox.django.query import QuerySet + +def parseCondition(condition, user): + ''' + condition: { + value: "war" + } + or + condition: { + key: "year", + value: "1970-1980, + operator: "!=" + } + ... + ''' + k = condition.get('key', 'name') + k = { + 'user': 'user__username', + }.get(k, k) + if not k: + k = 'name' + v = condition['value'] + op = condition.get('operator', None) + if not op: + op = '' + if op.startswith('!'): + op = op[1:] + exclude = True + else: + exclude = False + if k == 'id': + v = v.split('/') + if len(v) == 2: + q = Q(user__username=v[0], name=v[1]) + else: + q = Q(id__in=[]) + return q + if isinstance(v, bool): #featured and public flag + key = k + else: + if op == '=': + key = '%s__iexact'%k + elif op == '^': + v = v[1:] + key = '%s__istartswith'%k + elif op == '$': + v = v[:-1] + key = '%s__iendswith'%k + else: # default + key = '%s__icontains'%k + + key = str(key) + if exclude: + q = ~Q(**{key: v}) + else: + q = Q(**{key: v}) + return q + +def parseConditions(conditions, operator, user): + ''' + conditions: [ + { + value: "war" + } + { + key: "year", + value: "1970-1980, + operator: "!=" + }, + { + key: "country", + value: "f", + operator: "^" + } + ], + operator: "&" + ''' + conn = [] + for condition in conditions: + if 'conditions' in condition: + q = parseConditions(condition['conditions'], + condition.get('operator', '&'), user) + if q: + conn.append(q) + pass + else: + if condition.get('value', '') != '' or \ + condition.get('operator', '') == '=': + conn.append(parseCondition(condition, user)) + if conn: + q = conn[0] + for c in conn[1:]: + if operator == '|': + q = q | c + else: + q = q & c + return q + return None + class PlaceManager(Manager): def get_query_set(self): - return super(PlaceManager, self).get_query_set() + return QuerySet(self.model) - def find(self, q='', south=-180.0, west=-180.0, north=180.0, east=180.0): + def find(self, data, user): + ''' + query: { + conditions: [ + { + value: "war" + } + { + key: "year", + value: "1970-1980, + operator: "!=" + }, + { + key: "country", + value: "f", + operator: "^" + } + ], + operator: "&" + } + ''' + + #join query with operator qs = self.get_query_set() + + south=data['area']['south'] + west=data['area']['west'] + north=data['area']['north'] + east=data['area']['east'] qs = qs.filter(Q( Q(Q(south__gt=south)|Q(south__lt=north)|Q(west__gt=west)|Q(west__lt=east)) & Q(Q(south__gt=south)|Q(south__lt=north)|Q(west__lt=east)|Q(east__gt=east)) & Q(Q(north__gt=south)|Q(north__lt=north)|Q(west__gt=west)|Q(west__lt=east)) & Q(Q(north__gt=south)|Q(north__lt=north)|Q(east__gt=west)|Q(east__lt=east)) )) - if q: - qs = qs.filter(name_find__icontains=q) + + conditions = parseConditions(data['query'].get('conditions', []), + data['query'].get('operator', '&'), + user) + if conditions: + qs = qs.filter(conditions) return qs - ''' - #only return locations that have layers of videos visible to current user - if not identity.current.anonymous: - user = identity.current.user - if not user.in_group('admin'): - query = AND(query, - id == Layer.q.locationID, Layer.q.videoID == Video.q.id, - OR(Video.q.public == True, Video.q.creatorID == user.id) - ) - else: - query = AND(query, - id == Layer.q.locationID, Layer.q.videoID == Video.q.id, - Video.q.public == True) - ''' diff --git a/pandora/place/models.py b/pandora/place/models.py index ca686120..8fe1182d 100644 --- a/pandora/place/models.py +++ b/pandora/place/models.py @@ -25,6 +25,7 @@ class Place(models.Model): geoname = models.CharField(max_length=1024, unique=True) geoname_sort = models.CharField(max_length=1024, unique=True) + countryCode = models.CharField(max_length=16, default='') wikipediaId = models.CharField(max_length=1000, blank=True) @@ -58,7 +59,7 @@ class Place(models.Model): 'user': self.user.username, } for key in ('created', 'modified', - 'name', 'geoname', + 'name', 'geoname', 'countryCode', 'south', 'west', 'north', 'east', 'lat', 'lng', 'size'): j[key] = getattr(self, key) diff --git a/pandora/place/views.py b/pandora/place/views.py index e25e5b21..4c629dd5 100644 --- a/pandora/place/views.py +++ b/pandora/place/views.py @@ -21,6 +21,7 @@ def addPlace(request): param data { name: "", geoname: "", + countryCode: '', south: float, west: float, north: float, @@ -45,7 +46,7 @@ def addPlace(request): for key in data: setattr(place, key, data[key]) place.save() - response = json_response(status=200, text='created') + response = json_response(place.json()) else: response = json_response(status=403, text='place name exists') return render_to_json_response(response) @@ -72,14 +73,17 @@ def editPlace(request): for name in names: if models.Place.objects.filter(name_find__icontains=u'|%s|'%name).exclude(id=place.id).count() != 0: conflict = True + if 'geoname' in data: + if models.Place.objects.filter(geoname=data['geoname']).exclude(id=place.id).count() != 0: + conflict = True if not conflict: for key in data: if key != 'id': setattr(place, key, data[key]) place.save() - response = json_response(status=200, text='updated') + response = json_response(place.json()) else: - response = json_response(status=403, text='place name/alias conflict') + response = json_response(status=403, text='place name/geoname conflict') else: response = json_response(status=403, text='permission denied') return render_to_json_response(response) @@ -108,13 +112,30 @@ actions.register(removePlace, cache=False) def parse_query(data, user): query = {} query['range'] = [0, 100] - query['sort'] = [{'key':'user', 'operator':'+'}, {'key':'name', 'operator':'+'}] - for key in ('keys', 'group', 'list', 'range', 'ids', 'sort'): + query['sort'] = [{'key':'name', 'operator':'+'}] + query['area'] = {'south': -180.0, 'west': -180.0, 'north': 180.0, 'east': 180.0} + for key in ('area', 'keys', 'group', 'list', 'range', 'ids', 'sort', 'query'): if key in data: query[key] = data[key] - query['qs'] = models.Place.objects.all() + query['qs'] = models.Place.objects.find(query, user) return query +def order_query(qs, sort): + order_by = [] + for e in sort: + operator = e['operator'] + if operator != '-': + operator = '' + key = { + 'name': 'name_sort', + 'geoname': 'geoname_sort', + }.get(e['key'], e['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 findPlaces(request): ''' param data { @@ -185,7 +206,7 @@ Positions response = json_response() query = parse_query(data, request.user) - qs = query['qs'] + qs = order_query(query['qs'], query['sort']) if 'keys' in data: qs = qs[query['range'][0]:query['range'][1]] response['data']['items'] = [p.json(request.user) for p in qs] diff --git a/pandora/templates/index.html b/pandora/templates/index.html index 2abe4513..3882d037 100644 --- a/pandora/templates/index.html +++ b/pandora/templates/index.html @@ -17,6 +17,7 @@ if(typeof(console)=='undefined') { + diff --git a/static/js/pandora.api.js b/static/js/pandora.api.js index c7f36dff..5a7593e1 100755 --- a/static/js/pandora.api.js +++ b/static/js/pandora.api.js @@ -79,7 +79,7 @@ function constructList() { columnsMovable: false, columnsRemovable: false, id: 'actionList', - request: function(data, callback) { + items: function(data, callback) { function _sort(a, b) { if(a.name > b.name) return 1; diff --git a/static/js/pandora.js b/static/js/pandora.js index 2ccf8e1d..c940fb47 100755 --- a/static/js/pandora.js +++ b/static/js/pandora.js @@ -571,16 +571,16 @@ width: ratio >= 1 ? size : size * ratio }; }, - keys: ['director', 'id', 'poster', 'title', 'year'], - max: 1, - min: 1, - orientation: 'horizontal', - request: function(data, callback) { + items: function(data, callback) { //Ox.print('data, Query.toObject', data, Query.toObject()) pandora.api.find($.extend(data, { query: Query.toObject() }), callback); }, + keys: ['director', 'id', 'poster', 'title', 'year'], + max: 1, + min: 1, + orientation: 'horizontal', selected: [app.user.ui.item], size: 64, sort: app.user.ui.lists[app.user.ui.list].sort, @@ -962,8 +962,7 @@ }, ], columnsVisible: true, - pageLength: 1000, - request: function(data, callback) { + items: function(data, callback) { var query = id == 'favorite' ? {conditions: [ {key: 'user', value: app.user.username, operator: '!'}, {key: 'status', value: 'public', operator: '='} @@ -975,6 +974,7 @@ query: query }), callback); }, + pageLength: 1000, // fixme: select if previously selected // selected: app.user.ui.list ? [app.user.ui.list] : [], sort: [ @@ -1062,9 +1062,7 @@ width: app.user.ui.sidebarSize - 16 } ], - max: 1, - min: 1, - request: function(data, callback) { + items: function(data, callback) { var result = {data: {}}; if (!data.range) { result.data.items = Ox.getObjectById(app.ui.sectionFolders.site, id).items.length; @@ -1073,6 +1071,8 @@ } callback(result); }, + max: 1, + min: 1, sort: [{key: '', operator: ''}] }) .bindEvent({ @@ -1165,10 +1165,7 @@ width: 16 } ], - max: 1, - min: 0, - pageLength: 1000, - request: function(data, callback) { + items: function(data, callback) { var query; if (id == 'personal') { query = {conditions: [ @@ -1187,6 +1184,9 @@ query: query }), callback); }, + max: 1, + min: 0, + pageLength: 1000, sort: [ {key: 'position', operator: '+'} ], @@ -1535,7 +1535,7 @@ ], columnsVisible: true, id: 'group_' + id, - request: function(data, callback) { + items: function(data, callback) { //Ox.print('sending request', data) delete data.keys; //alert(id + " Query.toObject " + JSON.stringify(Query.toObject(id)) + ' ' + JSON.stringify(data)) @@ -1958,7 +1958,7 @@ columnsResizable: true, columnsVisible: true, id: 'list', - request: function(data, callback) { + items: function(data, callback) { //Ox.print('data, Query.toObject', data, Query.toObject()) pandora.api.find($.extend(data, { query: Query.toObject() @@ -2005,13 +2005,13 @@ width: ratio >= 1 ? size : size * ratio }; }, - keys: ['director', 'id', 'poster', 'title', 'year'], - request: function(data, callback) { + items: function(data, callback) { //Ox.print('data, Query.toObject', data, Query.toObject()) pandora.api.find($.extend(data, { query: Query.toObject() }), callback); }, + keys: ['director', 'id', 'poster', 'title', 'year'], size: 128, sort: app.user.ui.lists[app.user.ui.list].sort, unique: 'id' @@ -2113,7 +2113,7 @@ orientation: 'horizontal' }) .bindEvent('resize', function() { - app.$ui.map.triggerResize(); + app.$ui.map.resize(); }); } else { $list = new Ox.Element('