diff --git a/pandora/0xdb.jsonc b/pandora/0xdb.jsonc index 78f2e0c6e..90d6d7587 100644 --- a/pandora/0xdb.jsonc +++ b/pandora/0xdb.jsonc @@ -20,8 +20,10 @@ "canDeleteItems": {"admin": true}, "canDownloadVideo": {"guest": 0, "member": 0, "friend": 4, "staff": 4, "admin": 4}, "canEditAnnotations": {"staff": true, "admin": true}, + "canEditEvents": {"staff": true, "admin": true}, "canEditFeaturedLists": {"staff": true, "admin": true}, "canEditMetadata": {"staff": true, "admin": true}, + "canEditPlaces": {"staff": true, "admin": true}, "canEditSitePages": {"staff": true, "admin": true}, "canEditUsers": {"admin": true}, "canPlayClips": {"guest": 2, "member": 2, "friend": 4, "staff": 4, "admin": 4}, diff --git a/pandora/annotation/models.py b/pandora/annotation/models.py index 53c2de4aa..a9534fe0a 100644 --- a/pandora/annotation/models.py +++ b/pandora/annotation/models.py @@ -108,12 +108,12 @@ class Annotation(models.Model): settings.CONFIG['layers']): update_matching_events.delay(self.id) - def json(self, layer=False, keys=None): + def json(self, layer=False, keys=None, user=None): j = { 'user': self.user.username, } - for field in ('id', 'in', 'out', 'value', 'created', 'modified'): - j[field] = getattr(self, { + for key in ('id', 'in', 'out', 'value', 'created', 'modified'): + j[key] = getattr(self, { 'duration': 'clip__duration', 'hue': 'clip__hue', 'id': 'public_id', @@ -122,7 +122,18 @@ class Annotation(models.Model): 'out': 'end', 'saturation': 'clip__saturation', 'volume': 'clip__volume', - }.get(field, field)) + }.get(key, key)) + + l = self.get_layer() + if l['type'] == 'place': + qs = self.places.all() + if qs.count() > 0: + j['place'] = qs[0].json(user=user) + elif l['type'] == 'event': + qs = self.events.all() + if qs.count() > 0: + j['event'] = qs[0].json(user=user) + if layer or (keys and 'layer' in keys): j['layer'] = self.layer if keys: diff --git a/pandora/event/models.py b/pandora/event/models.py index 348ff7d70..9fb8e96be 100644 --- a/pandora/event/models.py +++ b/pandora/event/models.py @@ -64,8 +64,9 @@ class Event(models.Model): return self.name def editable(self, user): - if self.user == user or user.is_staff: - return True + if user and not user.is_anonymous() \ + and (self.user == user or user.get_profile().capability('canEditEvents')): + return True return False def get_matches(self): @@ -137,6 +138,7 @@ class Event(models.Model): def json(self, user=None): j = { 'id': self.get_id(), + 'editable': self.editable(user) } if self.user: j['user'] = self.user.username diff --git a/pandora/item/models.py b/pandora/item/models.py index 577020ebe..04ee7b680 100644 --- a/pandora/item/models.py +++ b/pandora/item/models.py @@ -435,7 +435,7 @@ class Item(models.Model): user = None qs = qs.filter(user=user) for a in qs.order_by('start'): - ll.append(a.json()) + ll.append(a.json(user=user)) return layers def get_json(self, keys=None): @@ -545,7 +545,12 @@ class Item(models.Model): save(i, '\n'.join([f.path for f in self.files.all()])) elif key['type'] == 'layer': - qs = Annotation.objects.filter(layer=i, item=self).order_by('start') + qs = Annotation.objects.filter(item=self) + if i == 'annotations': + qs = qs.filter(layer__in=Annotation.public_layers()) + else: + qs = qs.filter(layer=i) + qs = qs.order_by('start') save(i, u'\n'.join([l.findvalue for l in qs])) elif i != '*' and i not in self.facet_keys: value = self.get(i) diff --git a/pandora/padma.jsonc b/pandora/padma.jsonc index a27dd1206..ce905008c 100644 --- a/pandora/padma.jsonc +++ b/pandora/padma.jsonc @@ -20,8 +20,10 @@ "canDeleteItems": {"admin": true}, "canDownloadVideo": {"guest": 1, "member": 1, "staff": 4, "admin": 4}, "canEditAnnotations": {"staff": true, "admin": true}, + "canEditEvents": {"staff": true, "admin": true}, "canEditFeaturedLists": {"staff": true, "admin": true}, "canEditMetadata": {"staff": true, "admin": true}, + "canEditPlaces": {"staff": true, "admin": true}, "canEditSitePages": {"staff": true, "admin": true}, "canEditUsers": {"admin": true}, "canPlayClips": {"guest": 1, "member": 1, "staff": 4, "admin": 4}, @@ -98,42 +100,12 @@ "sort": "title" }, { - "id": "director", - "title": "Director", - "type": ["string"], - "autocomplete": true, - "columnRequired": true, - "columnWidth": 180, - "filter": true, - "find": true, - "sort": "person" - }, - { - "id": "cinematographer", - "title": "Cinematographer", - "type": ["string"], + "id": "source", + "title": "Source", + "type": "string", "autocomplete": true, "columnWidth": 180, "filter": true, - "find": true, - "sort": "person" - }, - { - "id": "features", - "title": "Features", - "type": ["string"], - "autocomplete": true, - "columnRequired": true, - "columnWidth": 180, - "filter": true, - "find": true, - "sort": "person" - }, - { - "id": "name", - "title": "Name", - "type": ["string"], - "autocomplete": true, "find": true }, { @@ -145,14 +117,70 @@ "filter": true, "find": true }, + { + "id": "topic", + "title": "Topic", + "type": ["string"], + "autocomplete": true, + "columnWidth": 180, + "filter": true, + "find": true + }, + { + "id": "name", + "title": "People", + "type": ["string"], + "autocomplete": true, + "find": true + }, + { + "id": "annotations", + "title": "Annotation", + "type": "string", + "find": true + }, + { + "id": "keywords", + "title": "Keywords", + "type": "layer" + }, + { + "id": "director", + "title": "Director", + "type": ["string"], + "autocomplete": true, + "columnRequired": true, + "columnWidth": 180, + "filter": true, + "sort": "person" + }, + { + "id": "cinematographer", + "title": "Cinematographer", + "type": ["string"], + "autocomplete": true, + "columnWidth": 180, + "filter": true, + "sort": "person" + }, + { + "id": "features", + "title": "Features", + "type": ["string"], + "autocomplete": true, + "columnRequired": true, + "columnWidth": 180, + "filter": true, + "sort": "person" + }, { "id": "year", "title": "Year", "type": "year", "autocomplete": true, "columnWidth": 60, - "filter": true, - "find": true + "filter": true + //"find": true }, { "id": "language", @@ -170,24 +198,6 @@ "columnWidth": 60, "format": {"type": "duration", "args": [0, "medium"]} }, - { - "id": "source", - "title": "Source", - "type": "string", - "autocomplete": true, - "columnWidth": 180, - "filter": true, - "find": true - }, - { - "id": "topic", - "title": "Topic", - "type": ["string"], - "autocomplete": true, - "columnWidth": 180, - "filter": true, - "find": true - }, { "id": "location", "title": "Location", @@ -226,26 +236,22 @@ { "id": "places", "title": "Places", - "type": "layer", - "find": true + "type": "layer" }, { - "id": "keywords", - "title": "Keywords", - "type": "layer", - "find": true + "id": "events", + "title": "Events", + "type": "layer" }, { "id": "descriptions", "title": "Descriptions", - "type": "layer", - "find": true + "type": "layer" }, { "id": "transcripts", "title": "Transcripts", - "type": "layer", - "find": true + "type": "layer" }, { "id": "duration", diff --git a/pandora/place/models.py b/pandora/place/models.py index 19a329a5b..882492ce7 100644 --- a/pandora/place/models.py +++ b/pandora/place/models.py @@ -57,12 +57,9 @@ class Place(models.Model): return self.name def editable(self, user): - if not user or user.is_anonymous(): - level = 'guest' - else: - level = user.get_profile().get_level() - if self.user == user or level in ('admin', 'staff'): - return True + if user and not user.is_anonymous() \ + and (self.user == user or user.get_profile().capability('canEditPlaces')): + return True return False def get_id(self): @@ -83,8 +80,6 @@ class Place(models.Model): return j def get_matches(self): - layers = [l['id'] for l in filter(lambda l: l['type'] == 'place' or l.get('hasPlaces'), - settings.CONFIG['layers'])] super_matches = [] q = Q(name_find__contains=" " + self.name)|Q(name_find__contains="|%s"%self.name) for name in self.alternativeNames: @@ -94,11 +89,30 @@ class Place(models.Model): for name in [self.name] + list(self.alternativeNames): if name in othername: super_matches.append(othername) - q = Q(value__icontains=" " + self.name)|Q(value__istartswith=self.name) - for name in self.alternativeNames: - q = q|Q(value__icontains=" " + name)|Q(value__istartswith=name) + + + exact = [l['id'] for l in filter(lambda l: l['type'] == 'place', settings.CONFIG['layers'])] + if exact: + q = Q(value__iexact=self.name) + for name in self.alternativeNames: + q = q|Q(value__iexact=name) + f = q&Q(layer__in=exact) + else: + f = None + + contains = [l['id'] for l in filter(lambda l: l.get('hasPlaces'), settings.CONFIG['layers'])] + if contains: + q = Q(value__icontains=" " + self.name)|Q(value__istartswith=self.name) + for name in self.alternativeNames: + q = q|Q(value__icontains=" " + name)|Q(value__istartswith=name) + contains_matches = q&Q(layer__in=contains) + if f: + f = contains_matches | f + else: + f = contains_matches + matches = [] - for a in Annotation.objects.filter(layer__in=layers).filter(q): + for a in Annotation.objects.filter(f): value = a.value.lower() for name in super_matches: value = value.replace(name.lower(), '') @@ -125,7 +139,11 @@ class Place(models.Model): for i in Item.objects.filter(id__in=ids).exclude(id__in=self.items.all()): self.items.add(i) if self.matches != numberofmatches: - Place.objects.filter(id=self.id).update(matches=numberofmatches) + if numberofmatches: + Place.objects.filter(id=self.id).update(matches=numberofmatches) + else: + self.matches = numberofmatches + self.save() def save(self, *args, **kwargs): if not self.name_sort: diff --git a/pandora/place/views.py b/pandora/place/views.py index 711c7cf8c..dd278424f 100644 --- a/pandora/place/views.py +++ b/pandora/place/views.py @@ -90,7 +90,11 @@ def editPlace(request): conflict = False conflict_names = [] conflict_geoname = '' - for name in names + data.get('alternativeNames', []): + alternative_names = data.get('alternativeNames', []) + if alternative_names: + alternative_names = filter(lambda n: n.strip(), alternative_names) + data['alternativeNames'] = alternative_names + for name in names + alternative_names: if models.Place.objects.filter(name_find__icontains=u'|%s|'%name).exclude(id=place.id).count() != 0: conflict = True conflict_names.append(name) diff --git a/static/js/pandora/clipList.js b/static/js/pandora/clipList.js index 6e7817549..10a9b21a1 100644 --- a/static/js/pandora/clipList.js +++ b/static/js/pandora/clipList.js @@ -64,7 +64,8 @@ pandora.ui.clipList = function(videoRatio) { // if the item query contains a layer condition, // then this condition is added to the clip query itemsQuery.conditions.forEach(function(condition) { - if (Ox.getIndexById(pandora.site.layers, condition.key) > -1) { + if (condition.key == 'annotations' + || Ox.getIndexById(pandora.site.layers, condition.key) > -1) { query.conditions.push(condition); } }); diff --git a/static/js/pandora/utils.js b/static/js/pandora/utils.js index f70b04f02..8d2a07d45 100644 --- a/static/js/pandora/utils.js +++ b/static/js/pandora/utils.js @@ -839,7 +839,8 @@ pandora.isClipView = function(view, item) { pandora.isItemFind = function(find) { return find.conditions.length == 1 - && Ox.getIndexById(pandora.site.layers, find.conditions[0].key) > -1 + && (find.conditions[0].key == 'annotations' + ||Ox.getIndexById(pandora.site.layers, find.conditions[0].key) > -1) && find.conditions[0].operator == '='; };