chapters from annotations
authorcavaliet
Fri, 21 Mar 2014 11:22:42 +0100
changeset 23 1d61b91b0b46
parent 22 68746521df51
child 24 dda5c321379a
chapters from annotations
src/spel/static/spel/css/spel.css
src/spel/static/spel/js/spectacle.js
src/spel/templates/partial/spel_annotations.html
src/spel/templates/partial/spel_chapters.html
src/spel/templates/spel_spectacle.html
src/spel/views.py
--- a/src/spel/static/spel/css/spel.css	Wed Mar 19 15:55:34 2014 +0100
+++ b/src/spel/static/spel/css/spel.css	Fri Mar 21 11:22:42 2014 +0100
@@ -23,6 +23,7 @@
 .chapter-results, .annotation-results{
 	border-top: 2px solid #BBBBBB;
     margin-top: 10px;
+    min-height: 50px;
     padding-top: 10px;
 }
 
@@ -63,4 +64,11 @@
 .searched-tag .glyphicon-remove:hover{
 	color: #FFF;
 	cursor: pointer;
+}
+.multiline{
+	white-space: normal;
+}
+.info{
+    padding-left: 5px;
+    padding-right: 5px;
 }
\ No newline at end of file
--- a/src/spel/static/spel/js/spectacle.js	Wed Mar 19 15:55:34 2014 +0100
+++ b/src/spel/static/spel/js/spectacle.js	Fri Mar 21 11:22:42 2014 +0100
@@ -100,7 +100,24 @@
 			$("#btn-search-annotations").attr('disabled','disabled');
 		}
 	});
-
+	
+	function buildChapterTable(html){
+		$(".chapter-results").removeClass("loader");
+		$(".chapter-results").html(html);
+		$('.popinfo').popover({ html: true });
+		$(".tablesorter").tablesorter({
+		    theme : "bootstrap",
+		    widthFixed: true,
+		    headerTemplate : '{content} {icon}',
+		    widgets : [ "uitheme"],
+		    widgetOptions : {
+		        zebra : ["even", "odd"],
+		        filter_reset : ".reset"
+		    },
+		    headers: { 3: { sorter: false } }
+		});
+	}
+	
 	// Search chapters management
 	$("#btn-filter-chapters").click(function(e){
 		if($("#mulsel1").val()==null && $("#mulsel2").val()==null && $("#mulsel3").val()==null){
@@ -120,20 +137,7 @@
 			}
 		})
 		.done(function( html ) {
-			$(".chapter-results").removeClass("loader");
-			$(".chapter-results").html(html);
-			$('.popinfo').popover({ html: true });
-			$(".tablesorter").tablesorter({
-			    theme : "bootstrap",
-			    widthFixed: true,
-			    headerTemplate : '{content} {icon}',
-			    widgets : [ "uitheme"],
-			    widgetOptions : {
-			        zebra : ["even", "odd"],
-			        filter_reset : ".reset"
-			    },
-			    headers: { 3: { sorter: false } }
-			});
+			buildChapterTable(html);
 		})
 		.fail(function() {
 			$(".chapter-results").removeClass("loader");
@@ -145,7 +149,6 @@
 	
 	// click-to-remove-tag management for chapter
 	$(".chapter-results").on("click", ".chapter-tag-list .glyphicon-remove", function(){
-		console.log(this, $(this).parent().text().trim());
 		if($(this).parent().hasClass("type")){
 			$("#mulsel1").multiselect('deselect', $(this).parent().text().trim());
 		}
@@ -214,7 +217,6 @@
 			$(".annotation-results").html("<p>Vous devez indiquer un terme pour la recherche.</p>");
 			return false;
 		}
-		console.log("ICI 1");
 		searchAnnotations();
 	});
 	
@@ -235,7 +237,6 @@
 				});
 			}
 			else{
-				console.log("ICI 2");
 				searchAnnotations();
 			}
 		}
@@ -262,12 +263,11 @@
 			}
 		}
 		// Update number displayed
-		$(".nb-annotations-found").html($('.annotation-results > div:visible').length);
+		$(".nb-annotations-found").html($('.annotation-results > div.searched-annot:visible').length);
 	});
 	
 	// click-to-remove-tag management for annotations
 	$(".annotation-results").on("click", ".annot-tag-list .glyphicon-remove", function(){
-		console.log(this, $(this).parent().text().trim());
 		if($(this).parent().children().first().hasClass("glyphicon-search")){
 			$("#search-input").val("");
 		}
@@ -280,5 +280,38 @@
 			$(".annotation-results").html('');
 		}
 	});
+	
+	// Annotations to chapters management
+	$(".annotation-results").on("click", ".annot-to-chapter", function(){
+		// First we unselect chapter filters
+	    $('option', $('#mulsel1')).each(function(element) { $(this).removeAttr('selected').prop('selected', false); });
+	    $('#mulsel1').multiselect('refresh');
+	    $('option', $('#mulsel2')).each(function(element) { $(this).removeAttr('selected').prop('selected', false); });
+	    $('#mulsel2').multiselect('refresh');
+	    $('option', $('#mulsel3')).each(function(element) { $(this).removeAttr('selected').prop('selected', false); });
+	    $('#mulsel3').multiselect('refresh');
+	    // Then we request
+		$(".chapter-results").html("<br/><br/>");
+		$(".chapter-results").addClass("loader");
+		var bla = [];
+		$('.annotation-results > div.searched-annot:visible').each(function(index){
+			bla.push({data: $(this).attr("data-start"), iri_id: $(this).attr("data-content") }); 
+		});
+		if(bla.length==0){ return; }
+		$.post(
+			urlChapters,
+			{
+				annotations: JSON.stringify(bla), 
+				csrfmiddlewaretoken: csrf_token
+			}, 
+			function( html ) {
+				buildChapterTable(html);
+			}
+		)
+		.fail(function() {
+			$(".chapter-results").removeClass("loader");
+			$(".chapter-results").html('<p class="bg-danger">Erreur de chargement</p>');
+		});
+	});
 
 });
--- a/src/spel/templates/partial/spel_annotations.html	Wed Mar 19 15:55:34 2014 +0100
+++ b/src/spel/templates/partial/spel_annotations.html	Fri Mar 21 11:22:42 2014 +0100
@@ -1,15 +1,22 @@
 {% load static %}
 {% load front_tags %}
 {% load spel_tags %}
-<ul class="list-inline annot-tag-list">
-    {% if q != "" %}<li class="small searched-tag"><span class="glyphicon glyphicon-search small"></span> : {{ q }} <span class="glyphicon glyphicon-remove small"></span></li>{% endif %}
-    {% for t in searched_tags %}{% if t != "" %}<li class="small searched-tag">{{ t }} <span class="glyphicon glyphicon-remove small"></span></li>{% endif %}{% endfor %}
-</ul>
-<p><span class="nb-annotations-found">{{ nb_annotations }}</span> annotation(s) trouvée(s) sur {{ nb_contents }} vidéo(s)</p>
+<div class="row">
+  <div class="col-md-8">
+	<ul class="list-inline annot-tag-list">
+	    {% if q != "" %}<li class="small searched-tag"><span class="glyphicon glyphicon-search small"></span> : {{ q }} <span class="glyphicon glyphicon-remove small"></span></li>{% endif %}
+	    {% for t in searched_tags %}{% if t != "" %}<li class="small searched-tag">{{ t }} <span class="glyphicon glyphicon-remove small"></span></li>{% endif %}{% endfor %}
+	</ul>
+	<p><span class="nb-annotations-found">{{ nb_annotations }}</span> annotation(s) trouvée(s) sur {{ nb_contents }} vidéo(s)</p>
+  </div>
+  <div class="col-md-4">
+    <button class="annot-to-chapter btn btn-primary multiline btn-sm">Chapitres correspondant aux annotations</button>
+  </div>
+</div>
 {% for res in results %}
   {% for s in res.list %}
   {% with s.tags|get_tags:"type_inter" as tag_list %}
-<div class="searched-annot" data-type-inter="{{ tag_list|join:', ' }}">
+<div class="searched-annot" data-type-inter="{{ tag_list|join:', ' }}" data-start="{{ s.start_ts }}" data-content="{{ s.iri_id }}">
 <p>{{ res.content.title }} / {{ s.start_ts|str_duration }} - {{ s.start_ts|add:s.duration|str_duration }} / {{ s.cutting_id }}</p>
 <p class="small">{{ s.tags|get_tags:"modscen_and_perso"|join:', ' }}</p>
 <hr/>
--- a/src/spel/templates/partial/spel_chapters.html	Wed Mar 19 15:55:34 2014 +0100
+++ b/src/spel/templates/partial/spel_chapters.html	Fri Mar 21 11:22:42 2014 +0100
@@ -1,6 +1,9 @@
 {% load static %}
 {% load front_tags %}
 {% load spel_tags %}
+{% if annot_chapters %}
+<p class="bg-primary info">Chapitres correspondant aux annotations :</p>
+{% endif %}
 <ul class="list-inline chapter-tag-list">
     {% for t in annot_types %}<li class="small searched-tag type">{{ t|remove_tag_key }} <span class="glyphicon glyphicon-remove small"></span></li>{% endfor %}
     {% for t in mod_scen %}<li class="small searched-tag modscen">{{ t|remove_tag_key }} <span class="glyphicon glyphicon-remove small"></span></li>{% endfor %}
@@ -13,7 +16,7 @@
     </thead>
     <tbody>
   {% for s in segments %}
-    <tr><td>{{ s.cutting_id }}</td><td>{{ s.tags|get_tags:"ref_text"|join:', ' }}</td><td>{{ s.content.title }}</td>
+    <tr><td>{{ s.cutting_id }}</td><td>{{ s.tags|get_tags:"ref_text"|join:', ' }}</td><td>{% if annot_chapters %}{{ s.ct }}{% else %}{{ s.content.title }}{% endif %}</td>
         <td>
           <button type="button" class="popinfo btn btn-xs btn-default" data-trigger="hover" data-container="body" data-toggle="popover" data-placement="auto" 
                   data-content="<strong>Timecodes:</strong> {{ s.start_ts|str_duration }} - {{ s.start_ts|add:s.duration|str_duration }}<br/>{{ s.tags|get_tags:'group'|safe }}"><span class="glyphicon glyphicon-plus-sign"></span></button>
--- a/src/spel/templates/spel_spectacle.html	Wed Mar 19 15:55:34 2014 +0100
+++ b/src/spel/templates/spel_spectacle.html	Fri Mar 21 11:22:42 2014 +0100
@@ -88,6 +88,7 @@
   var urlTI = "{% url 'api_dispatch_list' resource_name='tags' api_name='1.0' %}?format=json&limit=500&name__startswith=type_inter:";
   var urlChapters = "{% url 'spel_chapters' %}";
   var urlAnnotations = "{% url 'spel_annotations' %}";
+  var csrf_token = "{{csrf_token}}";
   $('.input-daterange').datepicker({
 	    format: "yyyy-mm-dd",
 	    language: "fr",
--- a/src/spel/views.py	Wed Mar 19 15:55:34 2014 +0100
+++ b/src/spel/views.py	Fri Mar 21 11:22:42 2014 +0100
@@ -3,7 +3,9 @@
 
 @author: tcavalie
 '''
+import json
 import time
+from django.db.models import Q
 from django.conf import settings
 from django.views.generic import TemplateView
 from itertools import groupby
@@ -14,6 +16,8 @@
 
 import logging
 from ldt.ldt_utils.views.workspace import get_search_results
+from django.http.response import HttpResponse
+from django.db.models.query import RawQuerySet
 logger = logging.getLogger(__name__)
 
 
@@ -88,6 +92,48 @@
         context = {"annot_types": annot_types, "mod_scen":mod_scen, "perso":perso, "segments": s}
         
         return self.render_to_response(context)
+    
+    def post(self, request):
+        annotations_param = request.POST.get("annotations", "[]")
+        grouped_annotations = []
+        for iri_id, items in groupby(json.loads(annotations_param), itemgetter('iri_id')):
+            # Get segments timecodes
+            s = [ int(i["data"]) for i in items ]
+            grouped_annotations.append({ 'content': iri_id, 'list': s })
+        if len(grouped_annotations)==0:
+            return HttpResponse("")
+        # Request segment : we make a complex query impossible (?) to do with orm, even with Q, Sum and other stuffs.
+        # Here is a SQL example of what we want :
+        # select ldt_utils_segment.id, ldt_utils_segment.cutting_id, ldt_utils_segment.tags, ldt_utils_segment.start_ts, ldt_utils_segment.duration, ldt_utils_content.title AS ct
+        # from ldt_utils_segment 
+        # INNER JOIN ldt_utils_content ON (ldt_utils_segment.content_id = ldt_utils_content.id)
+        # where cutting_id IN ('performance','discussion') 
+        # AND (
+        #   ( ldt_utils_segment.iri_id='CONTENT_ID_1' AND (
+        #     ( ldt_utils_segment.start_ts < TIMECODE_1 AND TIMECODE_1 < (ldt_utils_segment.start_ts + ldt_utils_segment.duration) )
+        #      OR 
+        #     ( ldt_utils_segment.start_ts < TIMECODE_2 AND TIMECODE_2 < (ldt_utils_segment.start_ts + ldt_utils_segment.duration) )
+        #   )) 
+        #   OR 
+        #   ( ldt_utils_segment.iri_id='CONTENT_ID_2' AND (
+        #     ( ldt_utils_segment.start_ts < TIMECODE_3 AND TIMECODE_3 < (ldt_utils_segment.start_ts + ldt_utils_segment.duration) )
+        #   )) 
+        # )
+        
+        raw_query = "select ldt_utils_segment.id, ldt_utils_segment.cutting_id, ldt_utils_segment.tags, ldt_utils_segment.start_ts, ldt_utils_segment.duration, ldt_utils_content.title AS ct \nfrom ldt_utils_segment \nINNER JOIN ldt_utils_content ON (ldt_utils_segment.content_id = ldt_utils_content.id) \nwhere cutting_id IN ('performance','discussion') \nAND ("
+        for i, ga in enumerate(grouped_annotations):
+            if i>0:
+                raw_query += "\n  OR "
+            raw_query += "\n  ( ldt_utils_segment.iri_id='" + ga["content"] + "' AND ("
+            for j, tc in enumerate(ga["list"]):
+                if j>0:
+                    raw_query += "\n     OR "
+                raw_query += "\n    ( ldt_utils_segment.start_ts < " + str(tc) + " AND " + str(tc) + " < (ldt_utils_segment.start_ts + ldt_utils_segment.duration) )"
+            raw_query += "\n  )) "
+        raw_query += "\n) "
+        
+        context = {"annot_chapters": True, "annot_types": [], "mod_scen":[], "perso":[], "segments": list(Segment.objects.raw(raw_query)) }
+        return self.render_to_response(context)
 
 
 
@@ -115,7 +161,7 @@
                 # Get segments from tagged items
                 tagged_segs = TaggedItem.objects.get_by_model(Segment, tags).values()
                 # Prefetch all contents
-                all_contents = Content.objects.filter(iri_id__in=[s['iri_id'] for s in tagged_segs])
+                all_contents = list(Content.objects.filter(iri_id__in=[s['iri_id'] for s in tagged_segs]))
                 for iri_id, items in groupby(tagged_segs, itemgetter('iri_id')):
                     # Get good content
                     content = None