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)
--- /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,