--- a/src/ldt/ldt/api/ldt/resources/annotation.py Sun Aug 23 22:37:27 2015 +0200
+++ b/src/ldt/ldt/api/ldt/resources/annotation.py Fri Aug 21 17:16:18 2015 +0200
@@ -1,4 +1,4 @@
-from ldt.ldt_utils.contentindexer import add_segment
+from ldt.ldt_utils.contentindexer import add_segment, edit_segment
from ldt.ldt_utils.models import Project, Content
from ldt.ldt_utils.utils import LdtAnnotation
from ldt.security import protect_models, unprotect_models
@@ -36,7 +36,7 @@
meta = fields.DictField(attribute = 'meta')
class Meta:
- allowed_methods = ['post']
+ allowed_methods = ['post', 'put']
resource_name = 'annotations'
object_class = AnnotationObject
authorization = Authorization()
@@ -44,15 +44,18 @@
always_return_data = True
include_resource_uri = False
- def obj_delete_list(self, bundle, **kwargs):
- return True
-
- def obj_create(self, bundle, **kwargs):
- # Here the a has the datas for only one annotation. Tastypie's post allows only one resource addition
- a = bundle.data
+ def extract_annotation_data(self, data):
+ # This method extracts and validates the data we receive from the user for adding or editing an annotation
+
project_id = ""
- if a.has_key('project') :
- project_id = a["project"]
+ if data.has_key('project') :
+ project_id = data["project"]
+
+ if data.has_key('media') :
+ iri_id = data["media"]
+ else :
+ raise BadRequest
+
if project_id and project_id != "" :
try:
project = Project.objects.get(ldt_id=project_id)
@@ -60,69 +63,205 @@
raise NotFound("Project not found. project_id = " + project_id)
else :
# If the project's is not defined, we get or create the content's front project.
- iri_id = a["media"]
try:
content = Content.objects.get(iri_id=iri_id)
except Content.DoesNotExist:
raise NotFound("Content not found. iri_id = " + iri_id)
project = content.get_or_create_front_project()
- a[u"project"] = project.ldt_id
+ project_id = project.ldt_id
+
+ ann_duration = str(data['end'] - data['begin'])
+ ann_begin = str(data['begin'])
+ ann_end = str(data['end'])
+ # We test if the annotation has audio node
+ ann_audio_src = ""
+ ann_audio_href = ""
+ if data['content'].has_key('audio') :
+ if data['content']['audio'].has_key('src') :
+ ann_audio_src = data['content']['audio']['src']
+ if data['content']['audio'].has_key('href') :
+ ann_audio_href = data['content']['audio']['href']
+
+ ann_author = data['meta']['creator']
+ ann_date = data['meta']['created']
+
+ ann_type_id = data.get('type', '')
+ ann_type_title = data.get('type_title', '')
+ ann_id = data.get("id", "")
+ ann_title = data.get('content', {}).get('title', "")
+ ann_description = data.get('content', {}).get('description')
+ ann_tags = data.get('tags', [])
+
+ content = project.contents.get(iri_id=iri_id)
- adder = LdtAnnotation(project)
+ return {
+ "project_obj" : project,
+ "project_id" : project_id,
+ "content_obj" : content,
+ "iri_id" : iri_id,
+ "ann_type_id" : ann_type_id,
+ "ann_type_title" : ann_type_title,
+ "ann_id" : ann_id,
+ "ann_content" : {
+ "title" : ann_title,
+ "description" : ann_description,
+ "audio" : {
+ "src" : ann_audio_src,
+ "href" : ann_audio_href,
+ },
+ },
+ "ann_meta" : {
+ "creator" : ann_author,
+ "created" : ann_date,
+ },
+ "ann_tags" : ann_tags,
+ "ann_begin" : ann_begin,
+ "ann_end" : ann_end,
+ "ann_duration" : ann_duration,
+ }
+
+ def obj_delete_list(self, bundle, **kwargs):
+ return True
+
+ def obj_create(self, bundle, **kwargs):
+ # Here the a has the datas for only one annotation. Tastypie's post allows only one resource addition
+ data_dict = self.extract_annotation_data(bundle.data)
+ adder = LdtAnnotation(data_dict["project_obj"])
unprotect_models() # Allows anonymous user to modify models in this request only
- dur = str(a['end'] - a['begin'])
- begin = str(a['begin'])
- # We test if the annotation has audio node
- audio_src = ""
- audio_href = ""
- if a['content'].has_key('audio') :
- if a['content']['audio'].has_key('src') :
- audio_src = a['content']['audio']['src']
- if a['content']['audio'].has_key('href') :
- audio_href = a['content']['audio']['href']
- meta = a['meta']
- author = meta['creator']
- date = meta['created']
- # add(media, cutting_id, cutting_title, title, text, tags_list, begin, dur, author, date
- type_id, new_id, ensemble_id = adder.add(a['media'], a['type'], a['type_title'], a['content']['title'], a['content']['description'], a['tags'], begin, dur, author, date, None, "2194379", audio_src, audio_href)
+ type_id, new_id, ensemble_id = adder.add(
+ data_dict['iri_id'], # media
+ data_dict['ann_type_id'], # cutting_id
+ data_dict['ann_type_title'], # cutting_title
+ data_dict['ann_content']['title'], # title
+ data_dict['ann_content']['description'], # text
+ data_dict['ann_tags'], # tags_list
+ data_dict['ann_begin'], # begin
+ data_dict['ann_duration'], # dur
+ data_dict['ann_meta']['creator'], # author
+ data_dict['ann_meta']['created'], # date
+ None,
+ "2194379",
+ data_dict['ann_content']['audio']['src'],
+ data_dict['ann_content']['audio']['href']
+ )
if not new_id:
protect_models()
raise BadRequest
- content = project.contents.get(iri_id=a['media'])
-
# We update the ids
- a['type'] = type_id
- a['ensemble'] = ensemble_id
- a['id'] = new_id
- if not a['content'].has_key('audio') :
- a['content']['audio'] = {'src':audio_src, 'href':audio_href}
-
+ data_dict['ann_type_id'] = type_id
+ data_dict['ensemble_id'] = ensemble_id
+ data_dict['ann_id'] = new_id
+
#add segment
add_segment({
- "project" : project,
- "content" : content,
- "ensemble_id" : ensemble_id,
- "cutting_id" : a['type'],
- "element_id" : new_id,
- "title" : a['content']['title'],
- "abstract" : a['content']['description'],
- "tags" : ",".join(a['tags']),
- "start_ts" : begin,
- "duration" : dur,
- "author" : author,
- "date" : date,
- "audio_src" : audio_src,
- "audio_href" : audio_href,
- "polemics": adder.get_polemic_syntax(a['content']['title'])
+ "project" : data_dict["project_obj"],
+ "content" : data_dict["content_obj"],
+ "ensemble_id" : data_dict["ensemble_id"],
+ "cutting_id" : data_dict['ann_type_id'],
+ "element_id" : data_dict['ann_id'],
+ "title" : data_dict['ann_content']['title'],
+ "abstract" : data_dict['ann_content']['description'],
+ "tags" : ",".join(data_dict['ann_tags']),
+ "start_ts" : data_dict['ann_begin'],
+ "duration" : data_dict['ann_duration'],
+ "author" : data_dict['ann_meta']['creator'],
+ "date" : data_dict['ann_meta']['created'],
+ "audio_src" : data_dict['ann_content']['audio']['src'],
+ "audio_href" : data_dict['ann_content']['audio']['href'],
+ "polemics": adder.get_polemic_syntax(data_dict['ann_content']['title'])
})
# We save the added annotation and reprotect the contents and projects
adder.save(must_reindex=False)
protect_models()
# We update the AnnotationObject for the returned datas to be correct.
- bundle.obj = AnnotationObject(id = a["id"], project = a["project"], type = a["type"], type_title = a["type_title"], ensemble=a['ensemble'], media = a["media"], begin = a["begin"], end = a["end"], content = a['content'], tags = a['tags'], meta = a['meta'])
+ bundle.obj = AnnotationObject(
+ id = data_dict["ann_id"],
+ project = data_dict["project_id"],
+ type = data_dict["ann_type_id"],
+ type_title = data_dict["ann_type_title"],
+ ensemble = data_dict['ensemble_id'],
+ media = data_dict["iri_id"],
+ begin = data_dict["ann_begin"],
+ end = data_dict["ann_end"],
+ content = data_dict['ann_content'],
+ tags = data_dict['ann_tags'],
+ meta = data_dict['ann_meta']
+ )
+ return bundle
+
+ def obj_update(self, bundle, **kwargs):
+ data_dict = self.extract_annotation_data(bundle.data)
+ adder = LdtAnnotation(data_dict["project_obj"])
+ unprotect_models() # Allows anonymous user to modify models in this request only
+
+ ensemble_id = adder.edit(
+ data_dict['ann_id'], # annotation_id
+ data_dict['iri_id'], # media
+ data_dict['ann_type_id'], # cutting_id
+ data_dict['ann_type_title'], # cutting_title
+ data_dict['ann_content']['title'], # title
+ data_dict['ann_content']['description'], # text
+ data_dict['ann_tags'], # tags_list
+ data_dict['ann_begin'], # begin
+ data_dict['ann_duration'], # dur
+ data_dict['ann_meta']['creator'], # author
+ data_dict['ann_meta']['created'], # date
+ None,
+ "2194379",
+ data_dict['ann_content']['audio']['src'],
+ data_dict['ann_content']['audio']['href']
+ )
+ if not ensemble_id:
+ protect_models()
+ raise BadRequest
+
+ # We update the id
+ data_dict['ensemble_id'] = ensemble_id
+
+ #add segment
+ edit_segment(
+ data_dict["project_id"],
+ data_dict["iri_id"],
+ data_dict["ensemble_id"],
+ data_dict['ann_type_id'],
+ data_dict["ann_id"],
+ params = {
+ "project" : data_dict["project_obj"],
+ "content" : data_dict["content_obj"],
+ "ensemble_id" : data_dict["ensemble_id"],
+ "cutting_id" : data_dict['ann_type_id'],
+ "element_id" : data_dict['ann_id'],
+ "title" : data_dict['ann_content']['title'],
+ "abstract" : data_dict['ann_content']['description'],
+ "tags" : ",".join(data_dict['ann_tags']),
+ "start_ts" : data_dict['ann_begin'],
+ "duration" : data_dict['ann_duration'],
+ "author" : data_dict['ann_meta']['creator'],
+ "date" : data_dict['ann_meta']['created'],
+ "audio_src" : data_dict['ann_content']['audio']['src'],
+ "audio_href" : data_dict['ann_content']['audio']['href'],
+ "polemics": adder.get_polemic_syntax(data_dict['ann_content']['title'])
+ })
+ # We save the added annotation and reprotect the contents and projects
+ adder.save(must_reindex=False)
+ protect_models()
+ # We update the AnnotationObject for the returned datas to be correct.
+ bundle.obj = AnnotationObject(
+ id = data_dict["ann_id"],
+ project = data_dict["project_id"],
+ type = data_dict["ann_type_id"],
+ type_title = data_dict["ann_type_title"],
+ ensemble = data_dict['ensemble_id'],
+ media = data_dict["iri_id"],
+ begin = data_dict["ann_begin"],
+ end = data_dict["ann_end"],
+ content = data_dict['ann_content'],
+ tags = data_dict['ann_tags'],
+ meta = data_dict['ann_meta']
+ )
return bundle
def get_resource_uri(self, bundle_or_obj=None, url_name='api_dispatch_list'):
--- a/src/ldt/ldt/ldt_utils/contentindexer.py Sun Aug 23 22:37:27 2015 +0200
+++ b/src/ldt/ldt/ldt_utils/contentindexer.py Fri Aug 21 17:16:18 2015 +0200
@@ -258,48 +258,82 @@
projectIndexer.index_all()
update_stat_project(instance)
-
-
-def add_segment(params):
-
- project = params.get("project",None)
- content = params.get("content",None)
- ensemble_id = params.get("ensemble_id", "")
- cutting_id = params.get("cutting_id", "")
- element_id = params.get("element_id", "")
- title = params.get("title", "")
- abstract = params.get("abstract", "")
- tags_str = params.get("tags", "")
- start_ts = params.get("start_ts", 0)
- duration = params.get("duration", 0)
- author = params.get("author", "")
- date_str = params.get("date", "")
- audio_src = params.get("audio_src", "")
- audio_href = params.get("audio_href", "")
+def update_or_create_segment(params):
+ project = params.get("project", None)
+ content = params.get("content", None)
+
+ seg_data = {}
+
+ if params.has_key("content"):
+ seg_data["content"] = params["content"]
+ seg_data["iri_id"] = params["content"].iri_id
+ if params.has_key("project"):
+ seg_data["project_obj"] = params["project"]
+ seg_data["project_id"] = params["project"].ldt_id
+
+ if params.has_key("ensemble_id"):
+ seg_data["ensemble_id"] = params["ensemble_id"]
+
+ if params.has_key("cutting_id"):
+ seg_data["cutting_id"] = params["cutting_id"]
+
+ if params.has_key("element_id"):
+ seg_data["element_id"] = params["element_id"]
+
+ if params.has_key("title"):
+ seg_data["title"] = params["title"]
+
+ if params.has_key("abstract"):
+ seg_data["abstract"] = params["abstract"]
+
+ if params.has_key("start_ts"):
+ seg_data["start_ts"] = params["start_ts"]
+
+ if params.has_key("duration"):
+ seg_data["duration"] = params["duration"]
+
+ if params.has_key("date"):
+ seg_data["date"] = params["date"]
+
+ if params.has_key("author"):
+ seg_data["author"] = params["author"]
+
+ if params.has_key("audio_src"):
+ seg_data["audio_src"] = params["audio_src"]
+
+ if params.has_key("audio_href"):
+ seg_data["audio_href"] = params["audio_href"]
+
+ seg, created = Segment.objects.update_or_create(
+ project_id=project.ldt_id if project is not None else "",
+ iri_id=content.iri_id if content is not None else "",
+ ensemble_id=params.get("ensemble_id", ""),
+ cutting_id=params.get("cutting_id", ""),
+ element_id=params.get("element_id", ""),
+ defaults = seg_data
+ )
+
polemics = params.get("polemics", "")
-
- seg = Segment.create(content=content,
- iri_id=content.iri_id if content is not None else "",
- ensemble_id=ensemble_id,
- cutting_id=cutting_id,
- element_id=element_id,
- title=title,
- abstract=abstract,
- duration=duration,
- author=author,
- start_ts=start_ts,
- date=date_str,
- project_obj=project,
- project_id=project.ldt_id if project is not None else "",
- audio_src=audio_src,
- audio_href=audio_href)
seg.polemics = seg.get_polemic(polemics)
seg.save()
+
+ tags_str = params.get("tags", "")
for t in parse_tags(tags_str):
seg.tags.add(t)
seg.save()
add_annotation_to_stat(seg.content, seg.start_ts, seg.start_ts+seg.duration)
+
+ return created
+def add_segment(params):
+ return update_or_create_segment(params)
+
+def edit_segment(project_id, iri_id, ensemble_id, cutting_id, element_id, params):
+ seg = Segment.objects.filter(project_id=project_id, iri_id=iri_id, ensemble_id=ensemble_id, cutting_id=cutting_id, element_id=element_id)
+ if seg.count() <= 0:
+ return False
+ created = update_or_create_segment(params)
+ return not(created)
def delete_segment(project, project_id, iri_id, ensemble_id, cutting_id, element_id):
--- a/src/ldt/ldt/ldt_utils/utils.py Sun Aug 23 22:37:27 2015 +0200
+++ b/src/ldt/ldt/ldt_utils/utils.py Fri Aug 21 17:16:18 2015 +0200
@@ -169,13 +169,80 @@
self.parser = lxml.etree.XMLParser(remove_blank_text=True)
self.ldtdoc = lxml.etree.parse(StringIO(project.ldt_encoded), self.parser)
self.to_add = True
+
+ def update_annotation_element(self, element, title, text, begin, dur, author, date, color, audio_src, audio_href, tags_list):
+ element.set('begin', begin)
+ element.set('dur', dur)
+ element.set('author', author)
+ element.set('date', date)
+ element.set('color', color)
+ element.set('src', "")
- # add( a['media'], a['type'], a['type_title, a[data], '', a['tags'], begin, dur, author, date)
- def add(self, media, cutting_id, cutting_title, title, text, tags_list, begin, dur, author, date, view_id="0", color="2194379", audio_scr="", audio_href=""):
+ abstract_nodes = element.xpath('abstract')
+ if len(abstract_nodes) <= 0:
+ abstract_node = lxml.etree.SubElement(element, 'abstract')
+ else:
+ for node in abstract_nodes:
+ if node != abstract_nodes[0]:
+ element.remove(node)
+ abstract_node = abstract_nodes[0]
+ abstract_node.text = text
+
+ title_nodes = element.xpath('title')
+ if len(title_nodes) <= 0:
+ title_node = lxml.etree.SubElement(element, 'title')
+ else:
+ for node in title_nodes:
+ if node != title_nodes[0]:
+ element.remove(node)
+ title_node = title_nodes[0]
+ title_node.text = title
+
+ audio_nodes = element.xpath('audio')
+ if len(audio_nodes) <= 0:
+ audio_node = lxml.etree.SubElement(element, 'audio')
+ else:
+ for node in audio_nodes:
+ if node != audio_nodes[0]:
+ element.remove(node)
+ audio_node = audio_nodes[0]
+ audio_node.set('source', audio_src)
+ audio_node.text = audio_href
+
+ polemics = self.get_polemic_syntax(title)
+
+ if polemics:
+ meta_nodes = element.xpath('meta')
+ if len(meta_nodes) <= 0:
+ meta_node = lxml.etree.SubElement(element, 'meta')
+ else:
+ for node in meta_nodes:
+ if node != meta_nodes[0]:
+ element.remove(node)
+ meta_node = meta_nodes[0]
+
+ for polemics_node in meta_node.xpath('polemics'):
+ meta_node.remove(polemics_node)
+
+ polemics_node = lxml.etree.SubElement(meta_node, 'polemics')
+
+ for polemic in polemics:
+ polemic_node = lxml.etree.SubElement(polemics_node, 'polemic')
+ polemic_node.text = polemic
+
+ for tag_node in element.xpath('tags'):
+ element.remove(tag_node)
+
+ tags = lxml.etree.SubElement(element, 'tags')
+ for tag in tags_list:
+ tag_node = lxml.etree.SubElement(tags, 'tag')
+ tag_node.text = tag
+
+ # add(a['media'], a['type'], a['type_title, a[data], '', a['tags'], begin, dur, author, date)
+ def add(self, media, cutting_id, cutting_title, title, text, tags_list, begin, dur, author, date, view_id="0", color="2194379", audio_src="", audio_href=""):
"""
Add an annotation to a project. begin and dur must be strings. Default color is yellow.
"""
-
if dur < 0:
self.to_add = False
return False
@@ -248,39 +315,70 @@
dec.set('tagsSelect', '')
# We add the annotation/element node
- element = lxml.etree.SubElement(decoupage_elements[0], 'element')
+
id_annotation = 's_' + generate_uuid()
+ element = lxml.etree.SubElement(decoupage_elements[0], 'element')
element.set('id', id_annotation)
- element.set('begin', begin)
- element.set('dur', dur)
- element.set('author', author)
- element.set('date', date)
- element.set('color', color)
- element.set('src', "")
- abstract = lxml.etree.SubElement(element, 'abstract')
- abstract.text = text
- title_node = lxml.etree.SubElement(element, 'title')
- title_node.text = title
- audio = lxml.etree.SubElement(element, 'audio')
- audio.set('source', audio_scr)
- audio.text = audio_href
- tags = lxml.etree.SubElement(element, 'tags')
-
- polemics = self.get_polemic_syntax(title)
+
+ self.update_annotation_element(element, title, text, begin, dur, author, date, color, audio_src, audio_href, tags_list)
+
+ return cutting_id, id_annotation, ensemble_id
+
+
+ # edit( a["id"], a['media'], a['type'], a['type_title'], a[data], '', a['tags'], begin, dur, author, date)
+ def edit(self, ann_id, media, cutting_id, cutting_title, title, text, tags_list, begin, dur, author, date, view_id="0", color="2194379", audio_src="", audio_href=""):
+ """
+ Edit an annotation in a project. begin and dur must be strings. Default color is yellow.
+ """
+
+ if dur < 0:
+ self.to_add = False
+ return False
+
+ # We check if the project references the media.
+ path_media = self.ldtdoc.xpath('/iri/medias/media[@id="%s"]' % media)
+ if len(path_media) == 0:
+ self.to_add = False
+ return False
+
+ # We get the content node
+ path_annotations = self.ldtdoc.xpath('/iri/annotations')[0]
+ path_content = path_annotations.xpath('content[@id="%s"]' % media)
+ if len(path_content) == 0:
+ # If the content node does not exist, we abort as this should be an edition operation
+ return False
- if polemics:
- meta = lxml.etree.SubElement(element, 'meta')
- polemics_node = lxml.etree.SubElement(meta, 'polemics')
+ # We check if cutting_id is provided, if it isn't we abort as this is an edition operation
+ if cutting_id is None or cutting_id=="" :
+ return False
+ # We get the ensemble node
+ path_ensemble = path_content[0].xpath('ensemble[decoupage[@id="%s"]]' % cutting_id)
+
+ if len(path_ensemble) == 0:
+ # If the ensemble node does not exist, we abort as this should be an edition operation
+ return False
+ #else:
+ # path_ensemble = path_content[0].xpath('ensemble')
- for polemic in polemics:
- polemic_node = lxml.etree.SubElement(polemics_node, 'polemic')
- polemic_node.text = polemic
+ # We get the elements node in the good decoupage node
+ ensemble_id = path_ensemble[0].get('id')
+ decoupage_elements = path_ensemble[0].xpath('decoupage[@id="%s"]/elements' % cutting_id)
+ if len(decoupage_elements) == 0:
+ # If the decoupage node does not exist, we abort as this should be an edition operation
+ return False
+ #else:
+ # cutting_id = path_ensemble[0].xpath('decoupage[title="%s"]' % cutting_title)[0].get('id')
- for tag in tags_list:
- tag_node = lxml.etree.SubElement(tags, 'tag')
- tag_node.text = tag
+ # We get the annotation/element node
+ elements = decoupage_elements[0].xpath('element[@id="%s"]' % ann_id)
+ if len(elements) == 0:
+ # If the element doesn't exist there was a mistake calling edit method so we abort
+ return False
- return cutting_id, id_annotation, ensemble_id
+ element = elements[0]
+ self.update_annotation_element(element, title, text, begin, dur, author, date, color, audio_src, audio_href, tags_list)
+
+ return ensemble_id
def save(self, must_reindex=True):
if self.to_add: