src/ldt/ldt/api/ldt/handlers.py
branchnew_rest_api
changeset 871 f52567ae138c
parent 870 834ceff06966
child 872 cd56949a3510
equal deleted inserted replaced
870:834ceff06966 871:f52567ae138c
     1 from ldt.ldt_utils.models import Project, Content, Segment
       
     2 from django.db.models import F, Q
       
     3 from piston.handler import BaseHandler
       
     4 from piston.utils import rc, require_extended
       
     5 from ldt.ldt_utils.utils import LdtAnnotation
       
     6 from ldt.ldt_utils.stat import add_annotation_to_stat
       
     7 from ldt.security import protect_models, unprotect_models
       
     8 from ldt.ldt_utils.segmentserializer import SegmentSerializer
       
     9 import logging #@UnresolvedImport
       
    10 
       
    11 
       
    12 class ProjectHandler(BaseHandler):
       
    13     allowed_methods = ('GET', 'PUT',)
       
    14     model = Project   
       
    15 
       
    16     def read(self, request, project_id):
       
    17         """
       
    18         returns a single project
       
    19         """
       
    20         return Project.objects.get(ldt_id=project_id)
       
    21     
       
    22     @require_extended
       
    23     def update(self, request, project_id):
       
    24         #return rc.ALL_OK
       
    25         logging.debug("request " + repr(request))
       
    26         data = request.data
       
    27         ldt_str = data["ldt"]
       
    28         
       
    29         logging.debug("request data" + repr(ldt_str))
       
    30         
       
    31         if not ldt_str:
       
    32             return rc.BAD_REQUEST
       
    33         
       
    34         project = Project.objects.get(ldt_id=project_id)
       
    35         
       
    36         project.ldt = ldt_str
       
    37         
       
    38         unprotect_models()
       
    39         project.save()
       
    40         protect_models()
       
    41     
       
    42         return rc.ALL_OK
       
    43 
       
    44 
       
    45 class AnnotationHandler(BaseHandler):
       
    46     allowed_methods = ('PUT',)
       
    47     
       
    48     @require_extended
       
    49     def update(self, request, project_id):
       
    50         """
       
    51         This method is called when a PUT request is sent to http://<plateform_location>/api/ldt/projects/<project_id>.<format>. 
       
    52             <project_id> is the ldt_id field of the project. If <projet_id> does not match any project on the platform, a 410 ("Gone")
       
    53             error will be returned.
       
    54             <format> is the format of the data sent back by the server. It can be 'json', 'yaml', 'xml' or 'pickle'.
       
    55             
       
    56         If the request contains a content-type header whose value is set to "application/json" and a valid utf-8 encoded JSON file,
       
    57         the following conditions will be checked before the annotations are added : 
       
    58             If the submitted file is not valid or refers to a media that is not contained in the project, a 500  ("Bad Request")
       
    59             error will be returned. If the "type" field of an annotation matches an already existing cutting, it will be added to that 
       
    60             cutting. Otherwise, a new cutting will be added (as well as a new ensemble if needed). New cuttings are added to the view 
       
    61             "View at the last recording" if it exists, or to the view "Init view" else. If none of those views exist, the server will 
       
    62             not add the cutting to a view. Several annotations can be added at the same time if the submitted file contains multiple 
       
    63             annotations. The server returns the file submitted if all annotations have been added successfully, and adds to this file
       
    64             IDs of created annotations to the file with a 200("OK") error code.
       
    65         
       
    66         If no content-type header is set, the file submitted must be a valid XML file and will replace entirely the ldt field
       
    67         of the project without any verifications.  
       
    68         
       
    69         Example :
       
    70         
       
    71         Remark : The file below contain the minimum necessary fields and attributes for the handler to work. If one field or attribute is
       
    72         missing (e.g. author, or date) during submission, an error will occur.
       
    73         
       
    74         A platform is reachable at http://localhost/. It contains a project with ID a0593b58-f258-11df-80e1-00145ea4a2be. This project has
       
    75         a content milosforman_amadeus, which has a cutting Salieri inside the view "View at the last recording". The following JSON file exists in the current directory :
       
    76     
       
    77         Example of ajax call with 2 differents annotations :
       
    78         $('#mon_ajax').click(function(e) {
       
    79             var url = "{% url project_api project_id='c8448f21-272d-11e1-876b-c8bcc896c290' emitter_format='.json' %}"; // Don't forget the "." before "json" !
       
    80             
       
    81             var monjson = '{\
       
    82                     "annotations": [\
       
    83                         {\
       
    84                             "type": "c_07BA1284-5F24-71A8-1EE2-423EED999B8A",\
       
    85                             "type_title": "New cutting name if necessary",\
       
    86                             "media": "briandepalma_scarfacedepalma",\
       
    87                             "begin": 1600000,\
       
    88                             "end": 2100000,\
       
    89                             "content": {\
       
    90                                 "data": "new scar annot"\
       
    91                                 "audio": {\
       
    92                                     "mimetype": "audio/mp3",\
       
    93                                     "src": "mic",\
       
    94                                     "href": "rtmp://media.iri.centrepompidou.fr/ddc_micro_record/r_20120606190143793"\
       
    95                                 }\
       
    96                             },\
       
    97                             "tags": [ "json","dude" ]\
       
    98                         }\
       
    99                     ],\
       
   100                     "meta": {\
       
   101                         "creator": "John Doe",\
       
   102                         "created": "2011-09-10T09:12:58"\
       
   103                     }\
       
   104                 }';
       
   105             var monjson2 = '{\
       
   106                     "annotations": [\
       
   107                         {\
       
   108                             "type": "c_07BA1284-5F24-71A8-1EE2-423EED999B8A",\
       
   109                             "type_title": "New cutting name if necessary",\
       
   110                             "media": "briandepalma_scarfacedepalma",\
       
   111                             "begin": 2400000,\
       
   112                             "end": 3000000,\
       
   113                             "content": {\
       
   114                                 "data": "ntm iam 2"\
       
   115                             },\
       
   116                             "tags": [ "jak" ]\
       
   117                         }\
       
   118                     ],\
       
   119                     "meta": {\
       
   120                         "creator": "John Doe",\
       
   121                         "created": "2011-09-10T09:12:58"\
       
   122                     }\
       
   123                 }';
       
   124             
       
   125             $.ajax({
       
   126                 url: url,
       
   127                 type: 'PUT',
       
   128                 contentType: 'application/json',
       
   129                 data: monjson,
       
   130                 // bug with jquery >= 1.5, "json" adds a callback so we don't specify dataType
       
   131                 //dataType: 'json',
       
   132                 success: function(json, textStatus, XMLHttpRequest) {
       
   133                     alert("success = " + json);
       
   134                 },
       
   135                 error: function(jqXHR, textStatus, errorThrown) {
       
   136                     alert("ERROR = " + jqXHR.responseText + ", " + errorThrown);
       
   137                 }
       
   138             });
       
   139         });
       
   140         
       
   141         If we send a PUT request with curl :    
       
   142         $curl -X PUT http://localhost/api/ldt/projects/a0593b58-f258-11df-80e1-00145ea4a2be.json -d @example.JSON -H  "content-type:application/json"
       
   143         A new cutting titled "New cutting name" will be created with the first annotation inside, and the annotation "Annotation about Salieri"
       
   144         will be added to the Salieri cutting. The returned file is :
       
   145         
       
   146         {
       
   147             "annotations": [
       
   148                 {
       
   149                     "id": "6d8baf01-ffb1-11e0-810c-001485352c9a",
       
   150                     "type": "id_annot_type",
       
   151                     "type_title": "New cutting name",
       
   152                     "media": "milosforman_amadeus",
       
   153                     "begin": 50000,
       
   154                     "end": 900000,
       
   155                     "content": {
       
   156                         "data": "new annotation"
       
   157                     },
       
   158                     "tags": [ "json" ]
       
   159                 },
       
   160                 {
       
   161                     "id": "6d8baf00-ffb1-11e0-8097-001485352c9b",
       
   162                     "type": "another_id_annot_type",
       
   163                     "type_title": "Salieri",
       
   164                     "media": "milosforman_amadeus",
       
   165                     "begin": 700000,
       
   166                     "end": 1200000,
       
   167                     "content": {
       
   168                         "data": "Annotation about Salieri"
       
   169                     },
       
   170                     "tags": [ "xml", "test", "blop" ]
       
   171                 }
       
   172             ],
       
   173             
       
   174             "meta": {
       
   175                 "creator": "John Doe",
       
   176                 "created": "2011-09-10T09:12:58"
       
   177             }
       
   178         }
       
   179         
       
   180         """
       
   181         #return rc.ALL_OK
       
   182         try:
       
   183             project = Project.objects.get(ldt_id=project_id)
       
   184         except Project.DoesNotExist:
       
   185             return rc.NOT_HERE
       
   186     
       
   187         adder = LdtAnnotation(project)
       
   188         logging.debug("request json " + repr(request.data))
       
   189         
       
   190         unprotect_models() # Allows anonymous user to modify models in this request only 
       
   191 
       
   192         meta = request.data['meta']
       
   193         author = meta['creator']
       
   194         date = meta['created']
       
   195         new_annotations = request.data['annotations']
       
   196 
       
   197         for a in new_annotations:
       
   198             dur = str(a['end'] - a['begin'])
       
   199             begin = str(a['begin'])
       
   200             # We test if the annotation has audio node
       
   201             audio_src = ""
       
   202             audio_href = ""
       
   203             if a['content'].has_key('audio') :
       
   204                 if a['content']['audio'].has_key('src') :
       
   205                     audio_src = a['content']['audio']['src']
       
   206                 if a['content']['audio'].has_key('href') :
       
   207                     audio_href = a['content']['audio']['href']
       
   208             type_id, new_id = adder.add(a['media'], a['type'], a['type_title'], a['content']['data'], '', a['tags'], begin, dur, author, date, None, "2194379", audio_src, audio_href)
       
   209             if not new_id:
       
   210                 protect_models()
       
   211                 return rc.BAD_REQUEST
       
   212             
       
   213                             
       
   214             content = project.contents.get(iri_id=a['media'])
       
   215             add_annotation_to_stat(content, a['begin'], a['end'])
       
   216             
       
   217             # We update the ids
       
   218             a['type'] = type_id
       
   219             a['id'] = new_id
       
   220             if not a['content'].has_key('audio') :
       
   221                 a['content']['audio'] = {'src':audio_src, 'href':audio_href}
       
   222         
       
   223         # We save if there were added annotation
       
   224         if len(new_annotations)>0 :
       
   225             adder.save()
       
   226         
       
   227         protect_models()
       
   228         
       
   229         return request.data
       
   230             
       
   231 
       
   232         
       
   233 class ContentHandler(BaseHandler):
       
   234     allowed_methods = ('GET', 'PUT')
       
   235     model = Content
       
   236     exclude = (
       
   237                ("media_obj"),
       
   238                )   
       
   239 
       
   240     def read(self, request, iri_id):
       
   241         """
       
   242         returns a single content
       
   243         """
       
   244         return Content.objects.get(iri_id=iri_id)   
       
   245 
       
   246     @require_extended
       
   247     def update(self, request, iri_id):
       
   248         """
       
   249         Receives a json exactly like AnnotationHandler, but without any project indicated.
       
   250         We get or set the current content front project, and add the annotation
       
   251         """
       
   252         try:
       
   253             content = Content.objects.get(iri_id=iri_id)
       
   254         except Content.DoesNotExist:
       
   255             return rc.NOT_HERE
       
   256         proj = content.get_or_create_front_project()
       
   257         ah = AnnotationHandler()
       
   258         updated_data = ah.update(request, proj.ldt_id)
       
   259         
       
   260         return updated_data
       
   261     
       
   262 
       
   263 class SegmentHandler(BaseHandler):
       
   264     allowed_methods = ('GET', )
       
   265     model = Segment
       
   266     exclude = (
       
   267                ("project_obj"),
       
   268                ("content"),
       
   269                )
       
   270     
       
   271     def read(self, request, iri_id, begin, end):
       
   272         """
       
   273         returns segments about content iri_id between timecodes begin and end
       
   274         """
       
   275         begin = int(begin)
       
   276         end = int(end)
       
   277         
       
   278         content = Content.objects.filter(iri_id=iri_id)
       
   279         if not content:
       
   280             return rc.NOT_FOUND
       
   281         content = content[0]
       
   282         
       
   283         segments = Segment.objects.filter(content=content).filter(
       
   284                     Q(start_ts__gte=begin, start_ts__lte=end) |                            # segment starts between begin and end
       
   285                     Q(start_ts__gte=begin-F('duration'), start_ts__lte=end-F('duration')) |# segment ends between begin and end
       
   286                     Q(start_ts__lte=begin, start_ts__gte=end-F('duration'))                # period [begin:end] is included in the segment
       
   287                     )
       
   288         
       
   289         a = SegmentSerializer(content, segments)
       
   290         return a.serialize_to_cinelab()
       
   291