work on stats: calculatestats command for manage.py, stats in item page, stats in annotations list, stats in annotation page, actions increment stats (view count, annotations count, comment count etc)
authordurandn
Thu, 28 Jul 2016 17:07:28 +0200
changeset 85 49b3f22948d5
parent 84 9f55f0bcb29a
child 86 faa3b3159f0b
work on stats: calculatestats command for manage.py, stats in item page, stats in annotations list, stats in annotation page, actions increment stats (view count, annotations count, comment count etc)
src/iconolab/management/commands/__init__.py
src/iconolab/management/commands/calculatestats.py
src/iconolab/models.py
src/iconolab/signals/handlers.py
src/iconolab/static/iconolab/css/iconolab.css
src/iconolab/templates/iconolab/detail_annotation.html
src/iconolab/templates/iconolab/detail_item.html
src/iconolab/templates/partials/annotation_stats_panel.html
src/iconolab/templates/partials/image_annotations_list.html
src/iconolab/templates/partials/image_stats_panel.html
src/iconolab/views.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/iconolab/management/commands/calculatestats.py	Thu Jul 28 17:07:28 2016 +0200
@@ -0,0 +1,11 @@
+from django.core.management.base import BaseCommand, CommandError
+from iconolab.models import AnnotationStats, ImageStats
+
+class Command(BaseCommand):
+    help = "Re-calculates every stat of every stat object (except view_counts) according to the database state"
+    
+    def handle(self, *args, **kwargs):
+        for ann_stats_obj in AnnotationStats.objects.all():
+            ann_stats_obj.update_stats()
+        for img_stats_obj in ImageStats.objects.all():
+            img_stats_obj.update_stats()
\ No newline at end of file
--- a/src/iconolab/models.py	Thu Jul 28 16:55:44 2016 +0200
+++ b/src/iconolab/models.py	Thu Jul 28 17:07:28 2016 +0200
@@ -5,6 +5,7 @@
 from django.contrib.contenttypes.models import ContentType
 from django_comments_xtd.models import XtdComment
 from django.utils.text import slugify
+from . import signals
 import uuid, json, re, requests, urllib
 
 
@@ -55,6 +56,30 @@
     comments_count = models.IntegerField(blank=True, null=True, default=0)
     folders_inclusion_count = models.IntegerField(blank=True, null=True, default=0)
     tag_count = models.IntegerField(blank=True, null=True, default=0)
+    
+    def set_tags_stats(self):
+        self.tag_count = Tag.objects.filter(tagginginfo__revision__annotation__image = self.image).distinct().count()
+        self.save()
+    
+    def update_stats(self):
+        self.annotations_count = 0
+        self.submitted_revisions_count = 0
+        self.comments_count = 0
+        image_annotations = Annotation.objects.filter(image=self.image)
+        # views_count - Can't do much about views count
+        # annotations_count
+        self.annotations_count = image_annotations.count()
+        # submitted_revisions_count & comment_count
+        for annotation in image_annotations.all():
+            annotation_revisions = annotation.revisions
+            self.submitted_revisions_count += annotation_revisions.count()
+            
+            self.comments_count += XtdComment.objects.for_app_models("iconolab.annotation").filter(
+                object_pk = annotation.pk,
+            ).count()
+        # tag_count
+        self.tag_count = Tag.objects.filter(tagginginfo__revision__annotation__image = self.image).distinct().count()
+        self.save()
 
 class Image(models.Model):
     image_guid = models.UUIDField(default=uuid.uuid4, editable=False)
@@ -107,6 +132,7 @@
         # Create stats object
         new_annotation_stats = AnnotationStats(annotation=new_annotation)
         new_annotation_stats.save()
+        new_annotation_stats.set_tags_stats()
         new_annotation.stats = new_annotation_stats
         new_annotation.save()
         return new_annotation
@@ -120,14 +146,35 @@
     comments_count = models.IntegerField(blank=True, null=True, default=0)
     tag_count = models.IntegerField(blank=True, null=True, default=0)
     
+    @property
     def contributors(self):
         contributors = []
         for revision in self.annotation.revisions.filter(state__in=[AnnotationRevision.ACCEPTED, AnnotationRevision.STUDIED]):
             if revision.author not in contributors:
                 contributors.append(revision.author)
-        print(contributors)
         return contributors
-
+    
+    def set_tags_stats(self):
+        self.tag_count = Tag.objects.filter(tagginginfo__revision__annotation = self.annotation).distinct().count()
+        self.save()
+    
+    def update_stats(self):
+        # views_count - Can't do much about views count
+        # submitted_revisions_count 
+        annotation_revisions = self.annotation.revisions
+        self.submitted_revisions_count = annotation_revisions.count()
+        # accepted_revisions_count
+        self.accepted_revisions_count = annotation_revisions.filter(state=AnnotationRevision.ACCEPTED).count() + annotation_revisions.filter(state=AnnotationRevision.STUDIED).count()
+        # comment_count
+        self.comments_count = XtdComment.objects.for_app_models("iconolab.annotation").filter(
+            object_pk = self.annotation.pk,
+        ).count()
+        # contributors_count
+        self.contributors_count = len(self.contributors)
+        # tag_count
+        self.tag_count = Tag.objects.filter(tagginginfo__revision__annotation = self.annotation).distinct().count()
+        
+        self.save()
 
 class Annotation(models.Model):
     annotation_guid = models.UUIDField(default=uuid.uuid4, editable=False)
@@ -139,9 +186,6 @@
     
     objects = AnnotationManager()
     
-    def update_stats(self):
-        pass
-    
     # Call to create a new revision, possibly from a merge
     @transaction.atomic
     def make_new_revision(self, author, title, description, fragment, tags_json):
--- a/src/iconolab/signals/handlers.py	Thu Jul 28 16:55:44 2016 +0200
+++ b/src/iconolab/signals/handlers.py	Thu Jul 28 17:07:28 2016 +0200
@@ -0,0 +1,55 @@
+from django.apps import apps
+from django.db.models.signals import post_save
+from django.dispatch import Signal, receiver
+
+from iconolab.models import Annotation, AnnotationRevision, IconolabComment
+
+# Signal sent during method Annotation.validate_existing_revision to update stats
+revision_accepted = Signal(providing_args=["instance"])
+revision_created = Signal(providing_args=["instance"])
+
+
+def increment_stats_on_new_revision(sender, instance, **kwargs):
+    print("REVISION WAS CREATED")
+    # Annotation stats
+    annotation = instance.annotation
+    annotation.stats.submitted_revisions_count += 1
+    if instance.state in [AnnotationRevision.ACCEPTED, AnnotationRevision.STUDIED]:
+        annotation.stats.accepted_revisions_count += 1
+    annotation.stats.set_tags_stats()
+    annotation.stats.save()
+    # Image stats
+    image = instance.annotation.image
+    image.stats.submitted_revisions_count += 1
+    image.stats.set_tags_stats()
+    image.stats.save()
+
+    
+def increment_stats_on_new_comments(sender, instance, created, **kwargs):
+    if created:
+        print("COMMENT WAS CREATED")
+        model = apps.get_model(instance.content_type.app_label,instance.content_type.model)
+        object_pk = instance.object_pk
+        annotation = model._default_manager.get(pk=object_pk)
+        annotation.stats.comments_count +=1
+        annotation.stats.save()
+
+
+def increment_accepted_revisions(sender, instance, **kwargs):
+    annotation = instance.annotation
+    annotation.stats.accepted_revisions_count += 1
+    annotation.stats.save()
+
+def increment_annotations_count(sender, instance, created, **kwargs):
+    if created:
+        image = instance.image
+        image.stats.annotations_count += 1
+        image.stats.submitted_revisions_count += 1
+        image.stats.set_tags_stats()
+        image.stats.save()
+
+post_save.connect(increment_annotations_count, sender=Annotation)
+post_save.connect(increment_stats_on_new_comments, sender=IconolabComment)
+revision_created.connect(increment_stats_on_new_revision, sender=AnnotationRevision)
+revision_accepted.connect(increment_accepted_revisions, sender=AnnotationRevision)
+        
\ No newline at end of file
--- a/src/iconolab/static/iconolab/css/iconolab.css	Thu Jul 28 16:55:44 2016 +0200
+++ b/src/iconolab/static/iconolab/css/iconolab.css	Thu Jul 28 17:07:28 2016 +0200
@@ -27,6 +27,14 @@
 	margin-top: 5px;
 }
 
+.img-stats-dt{
+	width: 250px !important;
+}
+
+.img-stats-dd{
+	margin-left: 270px !important;
+}
+
 .revision-link:hover{
   	text-decoration: underline;
 }
--- a/src/iconolab/templates/iconolab/detail_annotation.html	Thu Jul 28 16:55:44 2016 +0200
+++ b/src/iconolab/templates/iconolab/detail_annotation.html	Thu Jul 28 17:07:28 2016 +0200
@@ -35,10 +35,12 @@
             <h4>Annotation créée par {{ annotation.author.username }}</h4>    
   		    <p><strong>Titre:</strong> {{ annotation.current_revision.title }}</p>
   		    <p><strong>Description:</strong> {{ annotation.current_revision.description }}</p>
-            <p><strong>Tags:</strong></p>
-            <typeahead :read-only="1" :tags="{{ tags_data }}"></typeahead>
+            {% if tags_data != "[]" %}
+              <p><strong>Tags:</strong></p>
+              <typeahead :read-only="1" :tags="{{ tags_data }}"></typeahead>
+              <br>
+            {% endif %}
             {% if user.is_authenticated %}
-              <br>
   		      <a href="{% url 'annotation_edit' collection_name image_guid annotation_guid  %}" class="btn btn-default btn-sm">
                 {% if user == annotation.author %}
                   <span class="glyphicon glyphicon-edit"></span> Editer l'annotation
@@ -46,7 +48,9 @@
                   <span class="glyphicon glyphicon-share"></span> Proposer une révision
                 {% endif %}
               </a>  
-		    {% endif %}
+	        {% endif %}
+            <br>
+            {% include "partials/annotation_stats_panel.html" with annotation=annotation label="Statistiques sur cette annotation:" %}  
         </div>
         <!-- zoomView -->
         <div class="col-md-12 zoom-view" style="display:none" v-show="showZoom">
--- a/src/iconolab/templates/iconolab/detail_item.html	Thu Jul 28 16:55:44 2016 +0200
+++ b/src/iconolab/templates/iconolab/detail_item.html	Thu Jul 28 17:07:28 2016 +0200
@@ -21,11 +21,13 @@
         {% endfor %}    
     </div>
     <div class="col-md-6">
-        <h2>{{item.metadatas.title}}</h2>
-        <h4>Description: </h3>
-        <p>{{item.metadatas.description}}</p><br>
+        <h2>{% if item.metadatas.title %}{{item.metadatas.title}}{% else %}Objet sans titre{% endif %}</h2>
+        {% if item.metadatas.description %}
+          <h4>Description: </h4>
+          <p>{{item.metadatas.description}}</p><br>
+        {% endif %}
         {% if item.images.all.count > 1 %}
-          <h4>Autres images pour cet objet: </h3>
+          <h4>Autres images pour cet objet: </h4>
           {% for image in item.images.all %}
               {% thumbnail image.media "150x150" crop=False as im_small %}
                 <img class="item-image-thumbnail" id="thumb-{{image.image_guid}}" src="{{ im_small.url }}" width="{{ im_small.width }}" height="{{ im_small.height }}">
@@ -33,6 +35,13 @@
           {% endfor %} 
           <br><br>
         {% endif %}
+        {% for image in item.images.all %}
+          <div class="item-stats-container" id="stats-{{image.image_guid}}">
+              {% include "partials/image_stats_panel.html" with image=image label="Statistiques sur cette image:" %} 
+          </div>
+        {% endfor %}
+        
+        
         <a class="btn btn-default btn-sm" href="{% url 'collection_home' collection_name %}"><i class="fa fa-list"></i> Retour à la liste d'objets</a>
     </div>
       {% for image in item.images.all %}  
@@ -47,8 +56,10 @@
 <script>
 	$(".item-detail-image-block:not(.selected)").hide();
 	$(".image-annotations-list").hide();
+	$(".item-stats-container").hide();
 	var initialSelectedID = /img\-([0-9a-z\-]+)/.exec($(".item-detail-image-block.selected").attr("id"))[1];
 	$(".item-image-thumbnail#thumb-"+initialSelectedID).hide();
+	$(".item-stats-container#stats-"+initialSelectedID).show();
 	$(".image-annotations-list#annotations-"+initialSelectedID).show();
 	$(".item-image-thumbnail").on("click", function(e){
 	    imageID = /thumb\-([0-9a-z\-]+)/.exec($(this).attr("id"))[1];
@@ -56,6 +67,8 @@
 	    $(".item-detail-image-block").removeClass("selected");
 	    $(".item-detail-image-block#img-"+imageID).show();
 	    $(".item-detail-image-block#img-"+imageID).addClass("selected");
+		$(".item-stats-container").hide();
+	    $(".item-stats-container#stats-"+imageID).show();
 	    $(".item-image-thumbnail").show();
 		$(".item-image-thumbnail#thumb-"+imageID).hide();
 		$(".image-annotations-list").hide();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/iconolab/templates/partials/annotation_stats_panel.html	Thu Jul 28 17:07:28 2016 +0200
@@ -0,0 +1,17 @@
+<div class="panel panel-default">
+  <div class="panel-body">
+    {% if label %}<label>{{label}}</label>{% endif %}
+    <dl class="dl-horizontal">
+      <dt>Vues:</dt>
+      <dd><span class="badge">{{ annotation.stats.views_count }}</span></dd>
+      <dt>Commentaires:</dt>
+      <dd><span class="badge">{{ annotation.stats.comments_count }}</span></dd>
+      <dt>Révisions soumises:</dt>
+      <dd><span class="badge">{{ annotation.stats.submitted_revisions_count }}</span></dd>
+      <dt>Révisions acceptées:</dt>
+      <dd><span class="badge">{{ annotation.stats.accepted_revisions_count }}</span></dd>
+      <dt>Nombre de tags:</dt>
+      <dd><span class="badge">{{ annotation.stats.tag_count }}</span></dd>
+    </dl>
+  </div>
+</div>
\ No newline at end of file
--- a/src/iconolab/templates/partials/image_annotations_list.html	Thu Jul 28 16:55:44 2016 +0200
+++ b/src/iconolab/templates/partials/image_annotations_list.html	Thu Jul 28 17:07:28 2016 +0200
@@ -15,6 +15,7 @@
             <th>Créée le</th>
             <th>Révisée le</th>
             <th>Contributeurs</th>
+            <th>Statistiques</th>
           </thead>
           {% for annotation in annotation_list %}
           <tr>
@@ -41,6 +42,9 @@
                   {{ contributor }}{% if not forloop.last %}, {% endif %}
               {% endfor %}
             </td>
+            <td>
+                {% include "partials/annotation_stats_panel.html" with annotation=annotation %}  
+            </td>
           </tr>
           {% endfor %}
         </table>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/iconolab/templates/partials/image_stats_panel.html	Thu Jul 28 17:07:28 2016 +0200
@@ -0,0 +1,17 @@
+<div class="panel panel-default">
+  <div class="panel-body">
+    {% if label %}<label>{{label}}</label>{% endif %}
+    <dl class="dl-horizontal">
+      <dt class="img-stats-dt">Vues:</dt>
+      <dd class="img-stats-dd"><span class="badge">{{ image.stats.views_count }}</span></dd>
+      <dt class="img-stats-dt">Nombre d'annotations:</dt>
+      <dd class="img-stats-dd"><span class="badge">{{ image.stats.annotations_count }}</span></dd>
+      <dt class="img-stats-dt">Commentaires (total):</dt>
+      <dd class="img-stats-dd"><span class="badge">{{ image.stats.comments_count }}</span></dd>
+      <dt class="img-stats-dt">Révisions soumises (total):</dt>
+      <dd class="img-stats-dd"><span class="badge">{{ image.stats.submitted_revisions_count }}</span></dd>
+      <dt class="img-stats-dt">Nombre de tags (total):</dt>
+      <dd class="img-stats-dd"><span class="badge">{{ image.stats.tag_count }}</span></dd>
+    </dl>
+  </div>
+</div>
\ No newline at end of file
--- a/src/iconolab/views.py	Thu Jul 28 16:55:44 2016 +0200
+++ b/src/iconolab/views.py	Thu Jul 28 17:07:28 2016 +0200
@@ -14,6 +14,7 @@
 from django.conf import settings
 from iconolab.models import Annotation, AnnotationRevision, Collection, Item, Image, IconolabComment, MetaCategory, MetaCategoryInfo
 from iconolab.forms.annotations import AnnotationRevisionForm
+import iconolab.signals.handlers as iconolab_signals
 import datetime
 import django_comments
 from django_comments import signals
@@ -72,6 +73,9 @@
         context['item_guid'] = self.kwargs.get('image_guid', '')
         context['collection'] = collection
         context['item'] = item
+        for image in item.images.all():
+            image.stats.views_count += 1
+            image.stats.save()
         return render(request, 'iconolab/detail_item.html', context);
 
 class ShowImageView(View, ContextMixin):
@@ -214,6 +218,11 @@
             comments_list = paginator.page(paginator.num_pages)
         context["comments"] = comments_list
         
+        
+        image.stats.views_count += 1
+        image.stats.save()
+        annotation.stats.views_count += 1
+        annotation.stats.save()
         return render(request, 'iconolab/detail_annotation.html', context)
 
 
@@ -272,6 +281,7 @@
             revision_fragment = annotation_form.cleaned_data['fragment']
             revision_tags_json = annotation_form.cleaned_data['tags']
             new_revision = annotation.make_new_revision(revision_author, revision_title, revision_description, revision_fragment, revision_tags_json)
+            iconolab_signals.revision_created.send(sender=AnnotationRevision, instance=new_revision)
             revision_comment = annotation_form.cleaned_data['comment']
             comment = IconolabComment.objects.create(
                 comment = revision_comment,
@@ -387,6 +397,7 @@
         # Auto-accepts the revision only if the proper query arg is set and only if the revision parent is the current revision
         if "auto_accept" in request.GET and request.GET["auto_accept"] in ["True", "true", "1", "yes"] and revision.parent_revision == annotation.current_revision:
             annotation.validate_existing_revision(revision)
+            iconolab_signals.revision_accepted.send(sender=AnnotationRevision, instance=revision)
             return RedirectView.as_view(
                 url=reverse('annotation_detail', 
                     kwargs={
@@ -449,6 +460,7 @@
             revision_fragment = merge_revision_form.cleaned_data['fragment']
             revision_tags_json = merge_revision_form.cleaned_data['tags']
             new_revision = annotation.merge_existing_revision(revision_title, revision_description, revision_fragment, revision_tags_json, revision)
+            iconolab_signals.revision_created.send(sender=AnnotationRevision, instance=new_revision)
             revision_comment = merge_revision_form.cleaned_data['comment']
             comment = IconolabComment.objects.create(
                 comment = revision_comment,
@@ -570,7 +582,6 @@
     # Save the comment and signal that it was saved
     comment.save()
     
-    
     signals.comment_was_posted.send(
         sender=comment.__class__,
         comment=comment,