--- 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