added fields on metacategories, annotations to track the validation state of the annotation, with example handler in signals that check and update the state when a relevant metacategory is posted.
authordurandn
Fri, 16 Dec 2016 11:35:10 +0100
changeset 284 f52b0f6e2cd9
parent 283 d8fcfac848ed
child 285 aa0f3e186d29
added fields on metacategories, annotations to track the validation state of the annotation, with example handler in signals that check and update the state when a relevant metacategory is posted.
src/iconolab/migrations/0018_auto_20161215_1731.py
src/iconolab/models.py
src/iconolab/signals/handlers.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/iconolab/migrations/0018_auto_20161215_1731.py	Fri Dec 16 11:35:10 2016 +0100
@@ -0,0 +1,30 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10 on 2016-12-15 17:31
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('iconolab', '0017_collection_show_image_on_home'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='annotation',
+            name='validation_state',
+            field=models.IntegerField(choices=[(0, 'unvalidated'), (1, 'validated')], default=0),
+        ),
+        migrations.AddField(
+            model_name='annotation',
+            name='validation_state_overriden',
+            field=models.BooleanField(default=False),
+        ),
+        migrations.AddField(
+            model_name='metacategory',
+            name='validation_value',
+            field=models.IntegerField(choices=[(0, 'neutral'), (1, 'agreement'), (2, 'disagreement')], default=0),
+        ),
+    ]
--- a/src/iconolab/models.py	Thu Dec 15 16:35:01 2016 +0100
+++ b/src/iconolab/models.py	Fri Dec 16 11:35:10 2016 +0100
@@ -313,7 +313,17 @@
         that will be displayed by default in most pages. 
         
         Annotation data (title, description, fragment) is thus stored in the revision.
+        
+        Annotations can be considered validated or not depending on the metacategories posted in their comments through the attribute validation_state. Their validation state
+        can also be overriden and in such case we can use validation_state_overriden attribute to remember it in the model (so for instance if an admin un-validates an annotation
+        we could block it from being validated again)
     """
+    UNVALIDATED = 0
+    VALIDATED = 1
+    VALIDATION_STATES = (
+        (UNVALIDATED, 'unvalidated'),
+        (VALIDATED, 'validated'),
+    )
     annotation_guid = models.UUIDField(default=uuid.uuid4, editable=False)
     image = models.ForeignKey('Image', related_name='annotations', on_delete=models.CASCADE)
     source_revision = models.ForeignKey('AnnotationRevision', related_name='source_related_annotation', blank=True, null=True)
@@ -321,6 +331,8 @@
     author = models.ForeignKey(User, null=True)
     created = models.DateTimeField(auto_now_add=True, null=True)
     comments = GenericRelation('IconolabComment', content_type_field='content_type_id', object_id_field='object_pk')
+    validation_state = models.IntegerField(choices=VALIDATION_STATES, default=UNVALIDATED)
+    validation_state_overriden = models.BooleanField(default=False)
     
     objects = AnnotationManager()
     
@@ -769,12 +781,18 @@
             - CONTRIBUTORS : Notifies contributors (revision owners) on target annotation
             - COMMENTERS : Notifies commenters (contributors + comment owners) on target annotation
             - COLLECTION_ADMINS : Notifies collection admins
+            
+        Metacategories can be used to consider an annotation as "validated" if a certain agreement threshold is reached using their validation_value property
+        
+            - NEUTRAL : The metacategory doesn't affect the validation state
+            - AGREEMENT : The metacategory can be used to validate the annotation when linked to a comment on said annotation
+            - DISAGREEMENT : The metacategory can be used to unvalidate the annotation when linked to a comment on said annotation
+   
     """
     NONE = 0
     CONTRIBUTORS = 1
     COMMENTERS = 2
     COLLECTION_ADMINS = 3
-    
     NOTIFIED_USERS = (
         (NONE, 'none'),
         (CONTRIBUTORS, 'contributors'),
@@ -782,9 +800,19 @@
         (COLLECTION_ADMINS, 'collection admins'),
     )
     
+    NEUTRAL = 0
+    AGREEMENT = 1
+    DISAGREEMENT = 2
+    VALIDATION_VALUES = (
+        (NEUTRAL, 'neutral'),
+        (AGREEMENT, 'agreement'),
+        (DISAGREEMENT, 'disagreement'),
+    )
+    
     collection = models.ForeignKey(Collection, related_name="metacategories")
     label = models.CharField(max_length=255)
     triggers_notifications = models.IntegerField(choices=NOTIFIED_USERS, default=NONE)
+    validation_value = models.IntegerField(choices=VALIDATION_VALUES, default=NEUTRAL)
     
     def __str__(self):
         return self.label
--- a/src/iconolab/signals/handlers.py	Thu Dec 15 16:35:01 2016 +0100
+++ b/src/iconolab/signals/handlers.py	Fri Dec 16 11:35:10 2016 +0100
@@ -52,7 +52,7 @@
 
 def increment_stats_on_new_metacategory(sender, instance, created, **kwargs):
     """
-        Signal to increment stats on annotation when a metacategory is linked to a comment
+        Signal to increment stats on annotation when a metacategory is linked to a comment (on comment creation)
     """
     from iconolab.models import MetaCategoryInfo, MetaCategoriesCountInfo
     if created and sender == MetaCategoryInfo:
@@ -65,7 +65,6 @@
             m2m_object = MetaCategoriesCountInfo.objects.get(annotation_stats_obj=annotation.stats, metacategory=metacategory)
             m2m_object.count += 1
             m2m_object.save()
-        logger.debug("NEW METACATEGORY %r on comment %r on annotation %r", metacategory, comment, annotation)
 
 def increment_stats_on_accepted_revision(sender, instance, **kwargs):
     """
@@ -101,7 +100,41 @@
         image.stats.save()
 
 
+def update_annotation_validation_state(sender, instance, created, **kwargs):
+    """
+        Example signal to check if a metacategory added on an annotation is enough to make the annotation "validated"
+        
+        We listen to saves on the m2m for the metacategory count in annotation stats, 
+        check if the corresponding metacategory can affect the validation state, 
+        check if the state wasn't overriden, and compute the state again if checks were passed
+    """
+    from iconolab.models import MetaCategory, MetaCategoryInfo, MetaCategoriesCountInfo
+    if sender == MetaCategoriesCountInfo:
+        metacategory = instance.metacategory
+        annotation = instance.annotation_stats_obj.annotation
+        if metacategory.validation_value != MetaCategory.NEUTRAL and not annotation.validation_state_overriden:
+            non_neutral_mtcgs_count = 0
+            validating_mtcgs_count = 0
+            unvalidating_mtcgs_count = 0
+            # For each metacategory linked to annotation stats we check if it affects the validation state and keep track of counts if this is the case
+            for metacategory_infos in annotation.stats.metacategoriescountinfo_set.all():
+                if metacategory_infos.metacategory.validation_value == MetaCategory.AGREEMENT:
+                    validating_mtcgs_count += metacategory_infos.count
+                    non_neutral_mtcgs_count += metacategory_infos.count
+                if metacategory_infos.metacategory.validation_value == MetaCategory.DISAGREEMENT:
+                    unvalidating_mtcgs_count += metacategory_infos.count
+                    non_neutral_mtcgs_count += metacategory_infos.count
+            if non_neutral_mtcgs_count > 3 and (validating_mtcgs_count / non_neutral_mtcgs_count > 0.6):
+                annotation.validation_state = True
+                annotation.save()
+            if non_neutral_mtcgs_count > 3 and (validating_mtcgs_count / non_neutral_mtcgs_count < 0.4):
+                annotation.validation_state = False
+                annotation.save()
+        
 def notify_users_on_new_comment(sender, instance, **kwargs):
+    """
+        Signal to notify users when a comment is created. Notified users are: annotation author, parent comment author
+    """
     from iconolab.models import IconolabComment, Annotation, MetaCategory, MetaCategoryInfo
     if sender == IconolabComment and instance.content_type.app_label == 'iconolab' and instance.content_type.model == 'annotation':
         comment_annotation = Annotation.objects.get(id=instance.object_pk)
@@ -118,6 +151,9 @@
                 notify.send(instance.user, recipient=comment_annotation.author, verb='a écrit un commentaire sur votre annotation', action_object=instance, target=comment_annotation)         
 
 def notify_users_on_metacategory(sender, instance, created, **kwargs):
+    """
+        Signal to notify users when a comment is created. Notified users are: annotation author, parent comment author
+    """
     from iconolab.models import MetaCategory, MetaCategoryInfo, Annotation, UserProfile
     if sender == MetaCategoryInfo and created:
         related_metacategory = instance.metacategory
@@ -159,6 +195,7 @@
 post_save.connect(increment_annotations_count)
 post_save.connect(increment_stats_on_new_comment)
 post_save.connect(increment_stats_on_new_metacategory)
+post_save.connect(update_annotation_validation_state)
 revision_created.connect(increment_stats_on_new_revision)
 revision_accepted.connect(increment_stats_on_accepted_revision)
 revision_rejected.connect(increment_stats_on_rejected_revision)