# HG changeset patch # User durandn # Date 1478697409 -3600 # Node ID ad770589f0fe9ae61ab8cb81ef8bd8d0d0a9e7d0 # Parent d710b051cdc5f7728ef70adb57ca1d9896b2cfd6 Work on admin interface and user pages + added stat metacategories count for annotations + refactored views module #41 diff -r d710b051cdc5 -r ad770589f0fe src/iconolab/migrations/0014_auto_20161108_1458.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/iconolab/migrations/0014_auto_20161108_1458.py Wed Nov 09 14:16:49 2016 +0100 @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10 on 2016-11-08 14:58 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('iconolab', '0013_auto_20160923_1404'), + ] + + operations = [ + migrations.CreateModel( + name='MetaCategoriesCountInfo', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('count', models.IntegerField(default=1)), + ('annotation_stats_obj', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='iconolab.AnnotationStats')), + ('metacategory', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='iconolab.MetaCategory')), + ], + ), + migrations.AddField( + model_name='annotationstats', + name='metacategories', + field=models.ManyToManyField(through='iconolab.MetaCategoriesCountInfo', to='iconolab.MetaCategory'), + ), + ] diff -r d710b051cdc5 -r ad770589f0fe src/iconolab/models.py --- a/src/iconolab/models.py Mon Oct 31 10:08:18 2016 +0100 +++ b/src/iconolab/models.py Wed Nov 09 14:16:49 2016 +0100 @@ -1,7 +1,7 @@ from django.db import models, transaction from django.conf import settings from django.contrib.auth.models import User -from django.contrib.contenttypes.fields import GenericForeignKey +from django.contrib.contenttypes.fields import GenericRelation from django.contrib.contenttypes.models import ContentType from django_comments_xtd.models import XtdComment from django.utils.text import slugify @@ -209,6 +209,93 @@ iconolab_signals.revision_created.send(sender=AnnotationRevision, instance=initial_revision) return new_annotation + @transaction.atomic + def get_annotations_contributed_for_user(self, user): + """ + user is the user whom we want to get the contributed annotations + + Returns the list of all the annotations on which the user submitted a revision but did not create the annotation + List of dict in the format: + + { + "annotation_obj": annotation object, + "revisions_count": revisions count for user + "awaiting_count": awaiting revisions for user on this annotation + "accepted_count": accepted revisions for user + "latest_submitted_revision": date of the latest submitted revision from user on annotation + } + """ + latest_revision_on_annotations = [] + user_contributed_annotations = Annotation.objects.filter(revisions__author=user).exclude(author=user).prefetch_related( + 'current_revision', + 'revisions', + 'image', + 'image__item', + 'image__item__collection').distinct() + for annotation in user_contributed_annotations.all(): + latest_revision_on_annotations.append(annotation.revisions.filter(author=user).latest(field_name="created")) + contributed_list = [] + if latest_revision_on_annotations: + latest_revision_on_annotations.sort(key=lambda item:item.created, reverse=True) + contributed_list= [ + { + "annotation_obj": revision.annotation, + "revisions_count": revision.annotation.revisions.filter(author=user).count(), + "awaiting_count": revision.annotation.revisions.filter(author=user, state=AnnotationRevision.AWAITING).count(), + "accepted_count": revision.annotation.revisions.filter(author=user, state=AnnotationRevision.ACCEPTED).count(), + "latest_submitted_revision": revision.created + } + for revision in latest_revision_on_annotations + ] + logger.debug(contributed_list) + return contributed_list + + @transaction.atomic + def get_annotations_commented_for_user(self, user, ignore_revisions_comments=True): + """ + user is the user for which we want to get the commented annotations + ignore_revisions_comment allows to filter comments that are associated with a revision + + + Returns a list of all annotations on which a given user commented with user-comments-related data + List of dict in the format: + + { + "annotation_obj": annotation object, + "comment_count": comment count for user + "latest_comment_date": date of the latest comment from user on annotation + } + """ + user_comments = IconolabComment.objects.filter(user=user, content_type__app_label='iconolab', content_type__model='annotation').order_by('-submit_date') + if ignore_revisions_comments: + logger.debug(user_comments.count()) + user_comments = user_comments.filter(revision__isnull=True) + logger.debug(user_comments.count()) + all_user_comments_data = [(comment.object_pk, comment.submit_date) for comment in user_comments] + unique_ordered_comments_data = [] + for (id, submit_date) in all_user_comments_data: + if id not in [item["annotation_id"] for item in unique_ordered_comments_data]: + unique_ordered_comments_data.append({"annotation_id": id, "latest_comment_date": submit_date}) + commented_annotations = Annotation.objects.filter(id__in=[item["annotation_id"] for item in unique_ordered_comments_data]).prefetch_related( + 'current_revision', + 'revisions', + 'image', + 'image__item', + 'image__item__collection' + ).distinct() + sorted_annotations_list = [] + logger.debug(unique_ordered_comments_data) + for comment_data in unique_ordered_comments_data: + annotation_obj = commented_annotations.get(id=comment_data["annotation_id"]) + sorted_annotations_list.append( + { + "annotation_obj": annotation_obj, + "comment_count_for_user": user_comments.filter(object_pk=annotation_obj.id).count(), + "latest_comment_date": comment_data["latest_comment_date"] + } + ) + return sorted_annotations_list + class AnnotationStats(models.Model): annotation = models.OneToOneField('Annotation', related_name='stats', blank=False, null=False) @@ -219,6 +306,7 @@ views_count = models.IntegerField(blank=True, null=True, default=0) comments_count = models.IntegerField(blank=True, null=True, default=0) tag_count = models.IntegerField(blank=True, null=True, default=0) + metacategories = models.ManyToManyField('MetaCategory', through='MetaCategoriesCountInfo', through_fields=('annotation_stats_obj', 'metacategory')) def __str__(self): return "stats:for:"+str(self.annotation.annotation_guid) @@ -261,8 +349,35 @@ # contributors_count self.contributors_count = len(self.contributors) # tag_count + + annotation_comments_with_metacategories = IconolabComment.objects.filter( + content_type__app_label="iconolab", + content_type__model="annotation", + object_pk=self.annotation.id, + metacategories__collection=self.annotation.image.item.collection + ) + m2m_objects = MetaCategoriesCountInfo.objects.filter(annotation_stats_obj=self) + for obj in m2m_objects.all(): + obj.count = 0 + obj.save() + for comment in annotation_comments_with_metacategories.all(): + for metacategory in comment.metacategories.all(): + if metacategory not in self.metacategories.all(): + MetaCategoriesCountInfo.objects.create(annotation_stats_obj=self, metacategory=metacategory, count=1) + else: + m2m_object = MetaCategoriesCountInfo.objects.filter(annotation_stats_obj=self, metacategory=metacategory).first() + m2m_object.count += 1 + m2m_object.save() self.set_tags_stats() self.save() + +class MetaCategoriesCountInfo(models.Model): + annotation_stats_obj = models.ForeignKey('AnnotationStats', on_delete=models.CASCADE) + metacategory = models.ForeignKey('MetaCategory', on_delete=models.CASCADE) + count = models.IntegerField(default=1, blank=False, null=False) + + def __str__(self): + return "metacategory_count_for:"+self.metacategory.label+":on:"+str(self.annotation_stats_obj.annotation.annotation_guid) class Annotation(models.Model): @@ -272,6 +387,7 @@ current_revision = models.OneToOneField('AnnotationRevision', related_name='current_for_annotation', blank=True, null=True) 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') objects = AnnotationManager() @@ -307,6 +423,11 @@ current_revision_tags = json.loads(self.current_revision.get_tags_json()) return [tag_infos['tag_label'] for tag_infos in current_revision_tags if tag_infos.get('tag_label') is not None ] + def latest_revision_for_user(self, user): + user_revisions = self.revisions.filter(creator=user) + if user_revisions.exists(): + return user_revisions.filter(creator=author).order_by("-created").first() + return None # Call to create a new revision, possibly from a merge @transaction.atomic @@ -509,6 +630,12 @@ class Meta: ordering = ["thread_id", "id"] + @property + def annotation(self): + if self.content_type.app_label == "iconolab" and self.content_type.model == "annotation": + return Annotation.objects.get(pk=self.object_pk) + return None + # Get page for considered comment, with COMMENTS_PER_PAGE_DEFAULT comments per page def get_comment_page(self): return (IconolabComment.objects.for_app_models("iconolab.annotation").filter( diff -r d710b051cdc5 -r ad770589f0fe src/iconolab/signals/handlers.py --- a/src/iconolab/signals/handlers.py Mon Oct 31 10:08:18 2016 +0100 +++ b/src/iconolab/signals/handlers.py Wed Nov 09 14:16:49 2016 +0100 @@ -2,6 +2,9 @@ from django.db.models.signals import post_save from django.dispatch import Signal, receiver from notifications.signals import notify +import logging + +logger = logging.getLogger(__name__) # Signal sent during method Annotation.validate_existing_revision to update stats revision_accepted = Signal(providing_args=['instance']) @@ -28,20 +31,32 @@ image = instance.annotation.image image.stats.submitted_revisions_count += 1 image.stats.set_tags_stats() - image.stats.save() - + image.stats.save() def increment_stats_on_new_comment(sender, instance, created, **kwargs): from iconolab.models import IconolabComment if created and sender == IconolabComment: 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 = model.objects.get(pk=object_pk) annotation.stats.comments_count +=1 annotation.stats.save() annotation.image.stats.comments_count +=1 annotation.image.stats.save() +def increment_stats_on_new_metacategory(sender, instance, created, **kwargs): + from iconolab.models import MetaCategoryInfo, MetaCategoriesCountInfo + if created and sender == MetaCategoryInfo: + metacategory = instance.metacategory + comment = instance.comment + annotation = comment.annotation + if metacategory not in annotation.stats.metacategories.all(): + MetaCategoriesCountInfo.objects.create(annotation_stats_obj=annotation.stats, metacategory=metacategory, count=1) + else: + 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): from iconolab.models import AnnotationRevision @@ -82,8 +97,7 @@ if parent_comment.user == comment_annotation.author: notified_author = True if instance.user != comment_annotation.author and not notified_author: - notify.send(instance.user, recipient=comment_annotation.author, verb='a écrit un commentaire sur votre annotation', action_object=instance, target=comment_annotation) - + 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): from iconolab.models import MetaCategory, MetaCategoryInfo, Annotation, UserProfile @@ -106,15 +120,13 @@ from iconolab.models import AnnotationRevision if sender == AnnotationRevision: if instance.author != instance.annotation.author: - notify.send(instance.author, recipient=instance.annotation.author, verb='a proposé une révision sur votre annotation', action_object=instance, target=instance.annotation) - + notify.send(instance.author, recipient=instance.annotation.author, verb='a proposé une révision sur votre annotation', action_object=instance, target=instance.annotation) def notify_users_on_accepted_revision(sender, instance, **kwargs): from iconolab.models import AnnotationRevision if sender == AnnotationRevision: if instance.author != instance.annotation.author and instance.state in [AnnotationRevision.ACCEPTED, AnnotationRevision.STUDIED]: - notify.send(instance.annotation.author, recipient=instance.author, verb='a étudié votre révision', action_object=instance, target=instance.annotation) - + notify.send(instance.annotation.author, recipient=instance.author, verb='a étudié votre révision', action_object=instance, target=instance.annotation) def create_user_profile(sender, instance, created, **kwargs): from iconolab.models import UserProfile @@ -128,6 +140,7 @@ # Stats handlers connect post_save.connect(increment_annotations_count) post_save.connect(increment_stats_on_new_comment) +post_save.connect(increment_stats_on_new_metacategory) revision_created.connect(increment_stats_on_new_revision) revision_accepted.connect(increment_stats_on_accepted_revision) revision_rejected.connect(increment_stats_on_rejected_revision) diff -r d710b051cdc5 -r ad770589f0fe src/iconolab/static/iconolab/css/iconolab.css --- a/src/iconolab/static/iconolab/css/iconolab.css Mon Oct 31 10:08:18 2016 +0100 +++ b/src/iconolab/static/iconolab/css/iconolab.css Wed Nov 09 14:16:49 2016 +0100 @@ -7,11 +7,25 @@ .form-drawing-wrapper .selected {border: 1px solid orange; color: white; background-color: orange} .showPointer {cursor: pointer;} -.zoom-action-list {padding-left:21px;} -.zoomTarget-wrapper { padding: 0px;} -#zoomTarget, .cut-canvas {border: 1px solid #C3C3C3; padding-top: 2px; padding-bottom: 2px} +.zoom-action-list { + padding-left:21px; +} + +.zoomTarget-wrapper { + padding: 0px; +} -.no-padding {padding-left: 0; padding-right: 0;} +#zoomTarget, .cut-canvas { + border: 1px solid #C3C3C3; + padding-top: 2px; + padding-bottom: 2px +} + +.no-padding { + padding-left: 0; + padding-right: 0; +} + .annotation-content{ margin-top: 15px; margin-bottom: 15px; @@ -21,7 +35,9 @@ border: 1px solid orange; } -.revision-proposal {background-color: #ECF0F1;} +.revision-proposal { + background-color: #ECF0F1; +} .collection-home-btn{ margin-top: 5px; @@ -101,11 +117,38 @@ min-width: 535px; } .annotation-detail{ - vertical-align: middle; + display:inline-block; + margin-right: 5px; + margin-left: 5px; + margin-top: 5px; + white-space: normal; + vertical-align: top; + padding: 10px; +} + +.stats-annotation-userpage{ display:inline-block; - margin-right: 15px; - margin-left: 15px; + vertical-align: top; + padding: 10px; +} + +.image-detail{ + display:inline-block; + vertical-align: top; + width:150px; } +.large-image-detail{ + display:inline-block; + vertical-align: top; + width:450px; +} + + +.panel .dl-horizontal dt { + white-space: normal; + text-align: left; +} + .no-user-annotation{ margin-left: 15px; } @@ -115,8 +158,7 @@ .userpage-annotation-btn{ display:inline-block; vertical-align: top; - margin-top: 15px; - margin-right: 15px; + margin-bottom:5px; } /* COLLECTION HOME PAGE */ diff -r d710b051cdc5 -r ad770589f0fe src/iconolab/templates/iconolab/user_annotations.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/iconolab/templates/iconolab/user_annotations.html Wed Nov 09 14:16:49 2016 +0100 @@ -0,0 +1,63 @@ +{% extends 'iconolab_base.html' %} + +{% load staticfiles %} + +{% load thumbnail %} + +{% load iconolab_tags %} + +{% load notifications_tags %} + +{% block content %} +
+

{{profile_user.username}}: Annotations créées Retour au profil

+
+ {% if not user_annotations %} +

Aucune annotation à afficher

+ {% else %} + + + {% if user_annotations.has_previous or user_annotations.has_next %} + + {% endif %} + {% endif %} +
+{% endblock %} \ No newline at end of file diff -r d710b051cdc5 -r ad770589f0fe src/iconolab/templates/iconolab/user_collection_admin.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/iconolab/templates/iconolab/user_collection_admin.html Wed Nov 09 14:16:49 2016 +0100 @@ -0,0 +1,71 @@ +{% extends 'iconolab_base.html' %} + +{% load staticfiles %} + +{% load thumbnail %} + +{% load iconolab_tags %} + +{% load notifications_tags %} + +{% block content %} +
+

Tableau de bord - Fonds {{collection.verbose_name}}

+

Filtres et tri

+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Mots-clés pertinents: qualification minimale
+ Mots-clés précis: qualification minimale
+ Nombre de commentaires: au moins
+ Nombre de révisions: au moins
+ Métacatégories:
+ {% for metacategory in collection.metacategories.all %} + au moins {{metacategory.label}}
+ {% endfor %} +
+
+ +
+

Listes des annotations

+ {% include "partials/image_annotations_list.html" with annotation_list=collection_filtered_annotations collection_name=collection.name %} +
+{% endblock %} \ No newline at end of file diff -r d710b051cdc5 -r ad770589f0fe src/iconolab/templates/iconolab/user_commented.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/iconolab/templates/iconolab/user_commented.html Wed Nov 09 14:16:49 2016 +0100 @@ -0,0 +1,64 @@ +{% extends 'iconolab_base.html' %} + +{% load staticfiles %} + +{% load thumbnail %} + +{% load iconolab_tags %} + +{% load notifications_tags %} + +{% block content %} +
+

{{profile_user.username}}: Annotations commentées Retour au profil

+
+ {% if not user_commented_annotations %} +

Aucune annotation à afficher

+ {% else %} + + + {% if user_commented_annotations.has_previous or user_commented_annotations.has_next %} + + {% endif %} + {% endif %} +
+{% endblock %} \ No newline at end of file diff -r d710b051cdc5 -r ad770589f0fe src/iconolab/templates/iconolab/user_contributed.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/iconolab/templates/iconolab/user_contributed.html Wed Nov 09 14:16:49 2016 +0100 @@ -0,0 +1,64 @@ +{% extends 'iconolab_base.html' %} + +{% load staticfiles %} + +{% load thumbnail %} + +{% load iconolab_tags %} + +{% load notifications_tags %} + +{% block content %} +
+

{{profile_user.username}}: Toutes les contributions Retour au profil

+
+ {% if not user_contributed_annotations %} +

Aucune annotation à afficher

+ {% else %} + + + {% if user_contributed_annotations.has_previous or user_contributed_annotations.has_next %} + + {% endif %} + {% endif %} +
+{% endblock %} \ No newline at end of file diff -r d710b051cdc5 -r ad770589f0fe src/iconolab/templates/iconolab/user_home.html --- a/src/iconolab/templates/iconolab/user_home.html Mon Oct 31 10:08:18 2016 +0100 +++ b/src/iconolab/templates/iconolab/user_home.html Wed Nov 09 14:16:49 2016 +0100 @@ -52,54 +52,63 @@ {% endif %}
-

{% if profile_user == request.user %}Mes annotations :{% else %}Annotations de {{profile_user.username}}{% endif %}

+

{% if profile_user == request.user %}Mes dernières annotations :{% else %}Dernières annotations de {{profile_user.username}}{% endif %}

{% if not user_annotations %}

Aucune annotation à afficher

{% else %} {% endif %}
-

{% if profile_user == request.user %}Mes autres contributions :{% else %}Contributions de {{profile_user.username}}{% endif %}

+

{% if profile_user == request.user %}Mes contributions :{% else %}Contributions de {{profile_user.username}}{% endif %}

{% if profile_user == request.user %} - Annotations sur lesquelles j'ai proposé des révisions : + Dernières annotations sur lesquelles j'ai proposé des révisions : {% else %} - Annotations sur lesquelles {{profile_user.username}} a proposé des révisions : + Dernières annotations sur lesquelles {{profile_user.username}} a proposé des révisions : {% endif %}
- {% if not user_revisions_annotations %} + {% if not user_contributed_annotations %}

Aucune annotation à afficher

{% else %}
    - {% for annotation in user_revisions_annotations.all %} - {% include "partials/user_page_annotation_panel.html" with annotation=annotation %} + {% for annotation_data in user_contributed_annotations %} + {% include "partials/user_pages/annotations_contributed_panel.html" with annotation_data=annotation_data user=profile_user %} {% endfor %} +
  • + Voir toutes les contributions +
{% endif %}
{% if profile_user == request.user %} - Annotations sur lesquelles j'ai commenté (sans proposer de révision) : + Dernières annotation sur lesquelles j'ai commenté (sans proposer de révision) : {% else %} - Annotations sur lesquelles {{profile_user.username}} a commenté (sans proposer de révision) : + Dernières annotations sur lesquelles {{profile_user.username}} a commenté (sans proposer de révision) : {% endif %}
- {% if not user_comments_annotations %} + {% if not user_commented_annotations %}

Aucune annotation à afficher

{% else %} -
    - {% for annotation in user_comments_annotations.all %} - {% include "partials/user_page_annotation_panel.html" with annotation=annotation %} +
      + {% for annotation_data in user_commented_annotations %} + {% include "partials/user_pages/annotations_commented_panel.html" with annotation_data=annotation_data user=profile_user %} {% endfor %} +
    • + Voir toutes les annotations commentées +
    {% endif %}
@@ -117,7 +126,7 @@

Administration du Fonds {{user.profile.administers_collection.verbose_name}}

- Tableau de bord + Tableau de bord
diff -r d710b051cdc5 -r ad770589f0fe src/iconolab/templates/partials/user_page_annotation_panel.html --- a/src/iconolab/templates/partials/user_page_annotation_panel.html Mon Oct 31 10:08:18 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,31 +0,0 @@ -{% load thumbnail %} -{% load iconolab_tags %} -
  • -
    -
    {{annotation.current_revision.title}}
    - -
    - {% thumbnail annotation.image.media "150x150" crop=False as im %} - - - - - - - - - {% endthumbnail %} -
    -
    -
    -
    Commentaires :
    -
    {{annotation.stats.comments_count}}
    -
    Révisions en attente :
    -
    {{annotation.stats.awaiting_revisions_count}}
    -
    Révisions acceptée :
    -
    {{annotation.stats.accepted_revisions_count}}
    -
    -
    - Voir annotation -
    -
  • \ No newline at end of file diff -r d710b051cdc5 -r ad770589f0fe src/iconolab/templates/partials/user_pages/annotations_commented_panel.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/iconolab/templates/partials/user_pages/annotations_commented_panel.html Wed Nov 09 14:16:49 2016 +0100 @@ -0,0 +1,58 @@ +{% load thumbnail %} +{% load iconolab_tags %} +{% with annotation_data.annotation_obj as annotation %} +
  • +
    +
    + {% if annotation.current_revision.title %} + {{annotation.current_revision.title}} + {% else %} + Annotation sans titre + {% endif %} +
    +
    +
    + {% if large_image %} + {% thumbnail annotation.image.media "450x450" crop=False as im %} + + + + + + + + + {% endthumbnail %} + {% else %} + {% thumbnail annotation.image.media "150x150" crop=False as im %} + + + + + + + + + {% endthumbnail %} + {% endif %} +
    +
    +
    +
    Total commentaires :
    +
    {{annotation.stats.comments_count}}
    +
    .. Ecrits par {{user.username}} :
    +
    {{annotation_data.comment_count_for_user}}
    +
    A commenté le :
    +
    {{annotation_data.latest_comment_date}}
    +
    + Voir annotation +
    + {% if with_stats %} +
    + {% include "partials/annotation_stats_panel.html" with annotation=annotation label="Statistiques sur cette annotation:" %} +
    + {% endif %} +
    +
    +
  • +{% endwith %} \ No newline at end of file diff -r d710b051cdc5 -r ad770589f0fe src/iconolab/templates/partials/user_pages/annotations_contributed_panel.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/iconolab/templates/partials/user_pages/annotations_contributed_panel.html Wed Nov 09 14:16:49 2016 +0100 @@ -0,0 +1,60 @@ +{% load thumbnail %} +{% load iconolab_tags %} +{% with annotation_data.annotation_obj as annotation %} +
  • +
    +
    + {% if annotation.current_revision.title %} + {{annotation.current_revision.title}} + {% else %} + Annotation sans titre + {% endif %} +
    +
    +
    + {% if large_image %} + {% thumbnail annotation.image.media "450x450" crop=False as im %} + + + + + + + + + {% endthumbnail %} + {% else %} + {% thumbnail annotation.image.media "150x150" crop=False as im %} + + + + + + + + + {% endthumbnail %} + {% endif %} +
    +
    +
    +
    Total révisions: {{annotations.stats.revisions_count}}
    +
    {{annotation.stats.comments_count}}
    +
    Soumises par {{user.username}}:
    +
    {{annotation_data.revisions_count}}
    +
    .. Révisions en attente:
    +
    {{annotation_data.awaiting_count}}
    +
    .. Révisions acceptées:
    +
    {{annotation_data.accepted_count}}
    +
    + Voir annotation +
    + {% if with_stats %} +
    + {% include "partials/annotation_stats_panel.html" with annotation=annotation label="Statistiques sur cette annotation:" %} +
    + {% endif %} +
    +
    +
  • +{% endwith %} \ No newline at end of file diff -r d710b051cdc5 -r ad770589f0fe src/iconolab/templates/partials/user_pages/annotations_created_panel.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/iconolab/templates/partials/user_pages/annotations_created_panel.html Wed Nov 09 14:16:49 2016 +0100 @@ -0,0 +1,58 @@ +{% load thumbnail %} +{% load iconolab_tags %} +
  • +
    +
    + {% if annotation.current_revision.title %} + {{annotation.current_revision.title}} + {% else %} + Annotation sans titre + {% endif %} +
    +
    +
    + {% if large_image %} + {% thumbnail annotation.image.media "450x450" crop=False as im %} + + + + + + + + + {% endthumbnail %} + {% else %} + {% thumbnail annotation.image.media "150x150" crop=False as im %} + + + + + + + + + {% endthumbnail %} + {% endif %} +
    +
    +
    +
    +
    A créé l'annotation le :
    +
    {{annotation.created}}
    +
    Commentaires :
    +
    {{annotation.stats.comments_count}}
    +
    Révisions en attente :
    +
    {{annotation.stats.awaiting_revisions_count}}
    +
    +
    + Voir annotation +
    + {% if with_stats %} +
    + {% include "partials/annotation_stats_panel.html" with annotation=annotation label="Statistiques sur cette annotation:" %} +
    + {% endif %} +
    +
    +
  • \ No newline at end of file diff -r d710b051cdc5 -r ad770589f0fe src/iconolab/urls.py --- a/src/iconolab/urls.py Mon Oct 31 10:08:18 2016 +0100 +++ b/src/iconolab/urls.py Wed Nov 09 14:16:49 2016 +0100 @@ -29,38 +29,42 @@ urlpatterns = [ url(r'^$', django_views.generic.RedirectView.as_view(url=reverse_lazy("home"))), url(r'^admin/', admin.site.urls), - url(r'^home$', views.iconolab_objects.GlobalHomepageView.as_view(), name="home"), - url(r'^collections/(?P[a-z0-9\-]+)$', views.iconolab_objects.CollectionHomepageView.as_view(), name='collection_home'), # Home fond - url(r'^collections/(?P[a-z0-9\-]+)/items/(?P[^/]+)$', views.iconolab_objects.ShowItemView.as_view(), name='item_detail'), + url(r'^home$', views.objects.GlobalHomepageView.as_view(), name="home"), + url(r'^collections/(?P[a-z0-9\-]+)$', views.objects.CollectionHomepageView.as_view(), name='collection_home'), # Home fond + url(r'^collections/(?P[a-z0-9\-]+)/items/(?P[^/]+)$', views.objects.ShowItemView.as_view(), name='item_detail'), url(r'^collections/(?P[a-z0-9\-]+)/items/?$', django_views.generic.RedirectView.as_view(pattern_name="collection_home")), url(r'^collections/(?P[a-z0-9\-]+)/images/?$', django_views.generic.RedirectView.as_view(pattern_name="collection_home")), - url(r'^collections/(?P[a-z0-9\-]+)/images/(?P[^/]+)$', views.iconolab_objects.ShowImageView.as_view(), name='image_detail'), + url(r'^collections/(?P[a-z0-9\-]+)/images/(?P[^/]+)$', views.objects.ShowImageView.as_view(), name='image_detail'), url(r'^collections/(?P[a-z0-9\-]+)/images/(?P[^/]+)/annotations/?$', django_views.generic.RedirectView.as_view(pattern_name="image_detail")), - url(r'^collections/(?P[a-z0-9\-]+)/images/(?P[^/]+)/annotations/create$', login_required(views.iconolab_objects.CreateAnnotationView.as_view()), name='annotation_create'), - url(r'^collections/(?P[a-z0-9\-]+)/images/(?P[^/]+)/annotations/(?P[^/]+)/detail$', views.iconolab_objects.ShowAnnotationView.as_view(), name='annotation_detail'), - url(r'^collections/(?P[a-z0-9\-]+)/images/(?P[^/]+)/annotations/(?P[^/]+)/edit$', login_required(views.iconolab_objects.EditAnnotationView.as_view()), name='annotation_edit'), + url(r'^collections/(?P[a-z0-9\-]+)/images/(?P[^/]+)/annotations/create$', login_required(views.objects.CreateAnnotationView.as_view()), name='annotation_create'), + url(r'^collections/(?P[a-z0-9\-]+)/images/(?P[^/]+)/annotations/(?P[^/]+)/detail$', views.objects.ShowAnnotationView.as_view(), name='annotation_detail'), + url(r'^collections/(?P[a-z0-9\-]+)/images/(?P[^/]+)/annotations/(?P[^/]+)/edit$', login_required(views.objects.EditAnnotationView.as_view()), name='annotation_edit'), url(r'^collections/(?P[a-z0-9\-]+)/images/(?P[^/]+)/annotations/(?P[^/]+)/revisions/?$', django_views.generic.RedirectView.as_view(pattern_name="annotation_detail")), - url(r'^collections/(?P[a-z0-9\-]+)/images/(?P[^/]+)/annotations/(?P[^/]+)/revisions/(?P[^/]+)/detail', views.iconolab_objects.ShowRevisionView.as_view(), name='revision_detail'), - url(r'^collections/(?P[a-z0-9\-]+)/images/(?P[^/]+)/annotations/(?P[^/]+)/revisions/(?P[^/]+)/merge$', login_required(views.iconolab_objects.MergeProposalView.as_view()), name='annotation_merge'), + url(r'^collections/(?P[a-z0-9\-]+)/images/(?P[^/]+)/annotations/(?P[^/]+)/revisions/(?P[^/]+)/detail', views.objects.ShowRevisionView.as_view(), name='revision_detail'), + url(r'^collections/(?P[a-z0-9\-]+)/images/(?P[^/]+)/annotations/(?P[^/]+)/revisions/(?P[^/]+)/merge$', login_required(views.objects.MergeProposalView.as_view()), name='annotation_merge'), - url(r'^user/(?P[a-z0-9\-]+)/home/?$', views.iconolab_objects.UserHomeView.as_view(), name="user_home"), - url(r'^user/notifications/all/?$', login_required(views.iconolab_objects.UserNotificationsView.as_view()), name="user_notifications"), + url(r'^user/(?P[a-z0-9\-]+)/home/?$', views.userpages.UserHomeView.as_view(), name="user_home"), + url(r'^user/(?P[a-z0-9\-]+)/commented/?$', views.userpages.UserCommentedView.as_view(), name="user_commented"), + url(r'^user/(?P[a-z0-9\-]+)/contributed/?$', views.userpages.UserContributedView.as_view(), name="user_contributed"), + url(r'^user/(?P[a-z0-9\-]+)/annotations/?$', views.userpages.UserAnnotationsView.as_view(), name="user_annotations"), + url(r'^user/(?P[a-z0-9\-]+)/adminpanel/$', views.userpages.UserCollectionAdminView.as_view(), name="user_admin_panel"), + url(r'^user/notifications/all/?$', login_required(views.userpages.UserNotificationsView.as_view()), name="user_notifications"), url(r'^user/notifications/', include(notifications.urls, namespace='notifications')), - url(r'^errors/404', views.iconolab_misc.NotFoundErrorView.as_view(), name="404error"), + url(r'^errors/404', views.misc.NotFoundErrorView.as_view(), name="404error"), - url(r'^help/', views.iconolab_misc.HelpView.as_view(), name="iconolab_help"), - url(r'^glossary/', views.iconolab_misc.GlossaryView.as_view(), name="iconolab_glossary"), - url(r'^credits/', views.iconolab_misc.CreditsView.as_view(), name="iconolab_credits"), - url(r'^contributioncharter/', views.iconolab_misc.ContributionCharterView.as_view(), name="iconolab_charter"), - url(r'^legalmentions/', views.iconolab_misc.LegalMentionsView.as_view(), name="iconolab_legals"), + url(r'^help/', views.misc.HelpView.as_view(), name="iconolab_help"), + url(r'^glossary/', views.misc.GlossaryView.as_view(), name="iconolab_glossary"), + url(r'^credits/', views.misc.CreditsView.as_view(), name="iconolab_credits"), + url(r'^contributioncharter/', views.misc.ContributionCharterView.as_view(), name="iconolab_charter"), + url(r'^legalmentions/', views.misc.LegalMentionsView.as_view(), name="iconolab_legals"), url(r'^account/', include('iconolab.auth.urls', namespace='account')), url(r'^search/', include('iconolab.search_indexes.urls', namespace='search_indexes')), url(r'^comments/', include('django_comments_xtd.urls')), url(r'^comments/annotation/post', views.comments.post_comment_iconolab, name="post_comment"), - url(r'^compare/$', views.iconolab_objects.TestView.as_view(), name="compare_view") + url(r'^compare/$', views.objects.TestView.as_view(), name="compare_view") #url(r'^search/', include('haystack.urls'), name="search_iconolab"), ] diff -r d710b051cdc5 -r ad770589f0fe src/iconolab/views/__init__.py --- a/src/iconolab/views/__init__.py Mon Oct 31 10:08:18 2016 +0100 +++ b/src/iconolab/views/__init__.py Wed Nov 09 14:16:49 2016 +0100 @@ -1,3 +1,4 @@ from . import comments -from . import iconolab_objects -from . import iconolab_misc \ No newline at end of file +from . import objects +from . import misc +from . import userpages \ No newline at end of file diff -r d710b051cdc5 -r ad770589f0fe src/iconolab/views/iconolab_misc.py --- a/src/iconolab/views/iconolab_misc.py Mon Oct 31 10:08:18 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,20 +0,0 @@ -from django.views.generic import TemplateView - -class NotFoundErrorView(TemplateView): - template_name='errors/404error.html' - -class HelpView(TemplateView): - template_name='iconolab/misc/help.html' - -class GlossaryView(TemplateView): - template_name='iconolab/misc/glossary.html' - -class CreditsView(TemplateView): - template_name='iconolab/misc/credits.html' - -class LegalMentionsView(TemplateView): - template_name='iconolab/misc/legalmentions.html' - -class ContributionCharterView(TemplateView): - template_name='iconolab/misc/charter.html' - diff -r d710b051cdc5 -r ad770589f0fe src/iconolab/views/iconolab_objects.py --- a/src/iconolab/views/iconolab_objects.py Mon Oct 31 10:08:18 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,653 +0,0 @@ -from django.shortcuts import HttpResponse, get_object_or_404, render -from django.http import Http404 -from django.db.models import Count -from django.contrib.auth.decorators import login_required -from django.contrib.auth.models import User -from django.views.generic import View, DetailView, RedirectView, TemplateView -from django.views.generic.base import ContextMixin -from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger -from django.core.urlresolvers import reverse -from django.core.exceptions import ObjectDoesNotExist -from django.contrib.contenttypes.models import ContentType -from django.contrib.sites.models import Site -from django.conf import settings -from notifications.models import Notification -from iconolab.models import Annotation, AnnotationRevision, Collection, Item, Image, IconolabComment, MetaCategory, MetaCategoryInfo -from iconolab.forms.annotations import AnnotationRevisionForm -import logging - -logger = logging.getLogger(__name__) - -class GlobalHomepageView(View): - def get(self, request, *args, **kwargs): - context = {} - context['collections'] = Collection.objects - return render(request, 'iconolab/home.html', context) - -class TestView(View): - template_name = 'iconolab/compare.html' - - def get(self, request, *args, **kwargs): - return render(request, self.template_name) - - - -class UserHomeView(DetailView): - model = User - slug_field = 'id' - - def get_context_data(self, **kwargs): - context = super(UserHomeView, self).get_context_data(**kwargs) - return context - - def get(self, request, *args, **kwargs): - self.object = self.get_object() - context = self.get_context_data() - profile_user = self.object - context['profile_user'] = profile_user - context['user_annotations'] = Annotation.objects.filter(author=profile_user).prefetch_related( - 'current_revision', - 'revisions', - 'image', - 'image__item', - 'image__item__collection' - ) - context['user_revisions_annotations'] = Annotation.objects.filter(revisions__author=profile_user).exclude(author=profile_user).prefetch_related( - 'current_revision', - 'revisions', - 'image', - 'image__item', - 'image__item__collection' - ).distinct() - comments_annotations_str_id = IconolabComment.objects.filter(user=profile_user, content_type__app_label='iconolab', content_type__model='annotation').values_list('object_pk', flat=True) - comments_annotations_id = [int(str_id) for str_id in comments_annotations_str_id] - context['user_comments_annotations'] = Annotation.objects.filter(id__in=comments_annotations_id).exclude(author=profile_user).exclude(annotation_guid__in=context['user_revisions_annotations'].values_list('annotation_guid', flat=True)).prefetch_related( - 'current_revision', - 'revisions', - 'image', - 'image__item', - 'image__item__collection' - ).distinct() - if request.user.is_authenticated() and self.object == request.user: - if request.GET.get('clear_notifications', False): - Notification.objects.filter(recipient=request.user).mark_all_as_read() - context['notifications'] = Notification.objects.filter(recipient=request.user) - return render(request, 'iconolab/user_home.html', context) - -class UserNotificationsView(View): - - def get(self, request, *args, **kwargs): - context = {} - notifications = Notification.objects.filter(recipient=request.user) - context['notifications_unread_ids'] = notifications.unread().values_list('id', flat=True) - page = request.GET.get('page', 1) - paginator = Paginator(notifications, 50) - try: - notifications_list = paginator.page(page) - except PageNotAnInteger: - notifications_list = paginator.page(1) - except EmptyPage: - notifications_list = paginator.page(paginator.num_pages) - context['notifications'] = notifications_list - return render(request, 'iconolab/user_notifications.html', context) - -# Class with check_kwargs method to fetch objects from database depending on what level in the app we're currently at -class IconolabObjectView(object): - def check_kwargs(self, kwargs): - ''' - Returns a boolean depending on wether (True) or not (False) the objects were found and a tuple containing the objects, with a select_related/prefetch_related on relevant related objects - following this ordering: (collection, item, image, annotation, revision) - ''' - - objects_tuple = () - if 'collection_name' in kwargs.keys(): - try: - objects_tuple += (Collection.objects.prefetch_related('items', 'items__images').get(name=kwargs.get('collection_name')),) - except (ValueError, Collection.DoesNotExist): - return False, RedirectView.as_view(url=reverse('404error')) - if 'item_guid' in kwargs.keys(): - try: - objects_tuple += (Item.objects.prefetch_related('images', 'metadatas', 'images__stats').get(item_guid=kwargs.get('item_guid')),) - except (ValueError, Item.DoesNotExist): - return False, RedirectView.as_view(url=reverse('404error')) - if 'image_guid' in kwargs.keys(): - try: - objects_tuple += (Image.objects.prefetch_related('annotations', 'item', 'stats').get(image_guid=kwargs.get('image_guid')),) - except (ValueError, Image.DoesNotExist): - return False, RedirectView.as_view(url=reverse('404error')) - if 'annotation_guid' in kwargs.keys(): - try: - objects_tuple += (Annotation.objects.select_related('current_revision', 'stats', 'image').get(annotation_guid=kwargs.get('annotation_guid')),) - except (ValueError, Annotation.DoesNotExist): - return False, RedirectView.as_view(url=reverse('404error')) - if 'revision_guid' in kwargs.keys(): - try: - objects_tuple += (AnnotationRevision.objects.select_related('parent_revision').get(revision_guid=kwargs.get('revision_guid')),) - except (ValueError, AnnotationRevision.DoesNotExist): - return False, RedirectView.as_view(url=reverse('404error')) - return True, objects_tuple - -class CollectionHomepageView(View, ContextMixin, IconolabObjectView): - def get(self, request, *args, **kwargs): - success, result = self.check_kwargs(kwargs) - if success: - (collection,) = result - else: - return result(request) - context = super(CollectionHomepageView, self).get_context_data(**kwargs) - context['collection_name'] = self.kwargs.get('collection_name', '') - context['collection'] = collection - - # get Pagination and navigation query args - try: - items_page = int(request.GET.get('items_page', '1')) - except ValueError: - items_page = 1 - try: - items_per_page = int(request.GET.get('items_perpage', '12')) - except ValueError: - items_per_page = 12 - - try: - recent_page = int(request.GET.get('recent_page', '1')) - except ValueError: - recent_page = 1 - try: - recent_per_page = int(request.GET.get('recent_perpage', '10')) - except ValueError: - recent_per_page = 10 - - try: - revised_page = int(request.GET.get('revised_page', '1')) - except ValueError: - revised_page = 1 - try: - revised_per_page = int(request.GET.get('revised_perpage', '10')) - except ValueError: - revised_per_page = 10 - - try: - contributions_page = int(request.GET.get('contributions_page', '1')) - except ValueError: - contributions_page = 1 - try: - contributions_per_page = int(request.GET.get('contributions_perpage', '10')) - except ValueError: - contributions_per_page = 10 - - active_list = request.GET.get('show', 'items') - if active_list not in ['items', 'recent', 'revised', 'contributions']: - active_list = 'items' - context["active_list"] = active_list - - # Paginated objects list - context["items_page"] = items_page - context["items_perpage"] = items_per_page - items_paginator = Paginator(collection.items.all(), items_per_page) - try: - context["items_list"] = items_paginator.page(items_page) - except PageNotAnInteger: - context["items_list"] = items_paginator.page(1) - except EmptyPage: - context["items_list"] = items_paginator.page(items_paginator.num_pages) - - # Paginated recent annotations list - context["recent_page"] = recent_page - context["recent_perpage"] = recent_per_page - recent_annotations = Annotation.objects.filter(image__item__collection__name=collection.name).prefetch_related( - 'current_revision', - 'stats' - ).order_by('-current_revision__created') - recent_paginator = Paginator(recent_annotations, recent_per_page) - try: - context["recent_list"] = recent_paginator.page(recent_page) - except PageNotAnInteger: - context["recent_list"] = recent_paginator.page(1) - except EmptyPage: - context["recent_list"] = recent_paginator.page(recent_paginator.num_pages) - - # Paginated revised annotations list - context["revised_page"] = revised_page - context["revised_perpage"] = revised_per_page - revised_annotations = Annotation.objects.filter(image__item__collection__name=collection.name).prefetch_related( - 'current_revision', - 'stats' - ).annotate(revision_count=Count('revisions')).order_by('-revision_count') - revised_paginator = Paginator(revised_annotations, revised_per_page) - try: - context["revised_list"] = revised_paginator.page(revised_page) - except PageNotAnInteger: - context["revised_list"] = revised_paginator.page(1) - except EmptyPage: - context["revised_list"] = revised_paginator.page(revised_paginator.num_pages) - - # Paginated contribution calls annotation list - context["contributions_page"] = contributions_page - context["contributions_perpage"] = contributions_per_page - contrib_calls_annotations_ids = list(set(MetaCategoryInfo.objects.filter( - metacategory__collection__name=collection.name, - metacategory__triggers_notifications=MetaCategory.CONTRIBUTORS - ).order_by('comment__submit_date').values_list('comment__object_pk', flat=True))) - collection_annotations = Annotation.objects.filter(id__in=contrib_calls_annotations_ids).all() - collection_ann_dict = dict([(str(annotation.id), annotation) for annotation in collection_annotations]) - contributions_annotations = [collection_ann_dict[id] for id in contrib_calls_annotations_ids] - contributions_paginator = Paginator(contributions_annotations, contributions_per_page) - try: - context["contributions_list"] = contributions_paginator.page(contributions_page) - except PageNotAnInteger: - context["contributions_list"] = contributions_paginator.page(1) - except EmptyPage: - context["contributions_list"] = contributions_paginator.page(contributions_paginator.num_pages) - - return render(request, 'iconolab/collection_home.html', context) - - - -class ShowItemView(View, ContextMixin, IconolabObjectView): - def get(self, request, *args, **kwargs): - success, result = self.check_kwargs(kwargs) - if success: - (collection, item) = result - else: - return result(request) - - context = super(ShowItemView, self).get_context_data(**kwargs) - image_guid_to_display = request.GET.get("show", str(item.images.first().image_guid)) - if image_guid_to_display not in [str(guid) for guid in item.images.all().values_list("image_guid", flat=True)]: - image_guid_to_display = str(item.images.first().image_guid) - context['display_image'] = image_guid_to_display - try: - displayed_annotations_page = int(request.GET.get('page', '1')) - except ValueError: - displayed_annotations_page = 1 - try: - displayed_annotations_per_page = int(request.GET.get('perpage', '10')) - except ValueError: - displayed_annotations_per_page = 10 - - context['collection_name'] = self.kwargs.get('collection_name', '') - context['item_guid'] = self.kwargs.get('image_guid', '') - context['collection'] = collection - context['item'] = item - context['images'] = [] - for image in item.images.all(): - if str(image.image_guid) == image_guid_to_display: - page = displayed_annotations_page - per_page = displayed_annotations_per_page - else: - page = 1 - per_page = 10 - annotations_paginator = Paginator(image.annotations.all(), per_page) - try: - annotations = annotations_paginator.page(page) - except PageNotAnInteger: - annotations = annotations_paginator.page(1) - except EmptyPage: - annotations = annotations_paginator.page(recent_paginator.num_pages) - context['images'].append({ - 'obj' : image, - 'annotations': annotations - }) - image.stats.views_count += 1 - image.stats.save() - return render(request, 'iconolab/detail_item.html', context); - -class ShowImageView(View, ContextMixin, IconolabObjectView): - def get(self, request, *args, **kwargs): - success, result = self.check_kwargs(kwargs) - if success: - (collection, image) = result - else: - return result(request) - context = super(ShowImageView, self).get_context_data(**kwargs) - context['collection_name'] = self.kwargs.get('collection_name', '') - context['image_guid'] = self.kwargs.get('image_guid', '') - context['collection'] = collection - context['image'] = image - return render(request, 'iconolab/detail_image.html', context) - -class CreateAnnotationView(View, ContextMixin, IconolabObjectView): - - def get_context_data(self, **kwargs): - context = super(CreateAnnotationView, self).get_context_data(**kwargs) - context['collection_name'] = self.kwargs.get('collection_name', '') - context['image_guid'] = self.kwargs.get('image_guid', '') - return context - - def get(self, request, *args, **kwargs): - success, result = self.check_kwargs(kwargs) - if success: - (collection, image,) = result - else: - return result(request) - annotation_form = AnnotationRevisionForm() - context = self.get_context_data(**kwargs) - context['image'] = image - context['form'] = annotation_form - context['tags_data'] = '[]' - return render(request, 'iconolab/change_annotation.html', context) - - def post(self, request, *args, **kwargs): - success, result = self.check_kwargs(kwargs) - if success: - (collection, image) = result - else: - return result(request) - collection_name = kwargs['collection_name'] - image_guid = kwargs['image_guid'] - annotation_form = AnnotationRevisionForm(request.POST) - if annotation_form.is_valid(): - author = request.user - title = annotation_form.cleaned_data['title'] - description = annotation_form.cleaned_data['description'] - fragment = annotation_form.cleaned_data['fragment'] - tags_json = annotation_form.cleaned_data['tags'] - new_annotation = Annotation.objects.create_annotation(author, image, title=title, description=description, fragment=fragment, tags_json=tags_json) - revision_comment = annotation_form.cleaned_data['comment'] - IconolabComment.objects.create( - comment = revision_comment, - revision = new_annotation.current_revision, - content_type = ContentType.objects.get(app_label='iconolab', model='annotation'), - content_object = new_annotation, - site = Site.objects.get(id=settings.SITE_ID), - object_pk = new_annotation.id, - user = request.user, - user_name = request.user.username - ) - return RedirectView.as_view(url=reverse('annotation_detail', kwargs={'collection_name': collection_name, 'image_guid': image_guid, 'annotation_guid': new_annotation.annotation_guid}))(request) - context = self.get_context_data(**kwargs) - context['image'] = image - context['form'] = annotation_form - context['tags_data'] = '[]' - return render(request, 'iconolab/change_annotation.html', context) - -class ShowAnnotationView(View, ContextMixin, IconolabObjectView): - - def get_context_data(self, **kwargs): - context = super(ShowAnnotationView, self).get_context_data(**kwargs) - context['collection_name'] = self.kwargs.get('collection_name', '') - context['image_guid'] = self.kwargs.get('image_guid', '') - context['annotation_guid'] = self.kwargs.get('annotation_guid', '') - return context - - def get(self, request, *args, **kwargs): - success, result = self.check_kwargs(kwargs) - if success: - (collection, image, annotation,) = result - else: - return result(request) - context = self.get_context_data(**kwargs) - context['collection'] = collection - context['image'] = image - context['annotation'] = annotation - context['tags_data'] = annotation.current_revision.get_tags_json() - - page = request.GET.get('page', 1) - per_page = request.GET.get('perpage', 10) - full_comments_list = IconolabComment.objects.for_app_models('iconolab.annotation').filter(object_pk = annotation.pk).order_by('thread_id', '-order') - paginator = Paginator(full_comments_list, per_page) - try: - comments_list = paginator.page(page) - except PageNotAnInteger: - comments_list = paginator.page(1) - except EmptyPage: - comments_list = paginator.page(paginator.num_pages) - context['comments'] = comments_list - - if request.user.is_authenticated(): - user_comment_notifications = Notification.objects.filter( - recipient=request.user, - action_object_content_type__app_label='iconolab', - action_object_content_type__model='iconolabcomment', - target_content_type__app_label='iconolab', - target_content_type__model='annotation', - target_object_id=annotation.id - ).unread() - context['notifications_comments_ids'] = [int(val) for val in user_comment_notifications.values_list('action_object_object_id', flat=True)] - comment_list_ids = [comment.id for comment in context['comments'] ] - for notification in user_comment_notifications.all(): - if int(notification.action_object_object_id) in comment_list_ids: - notification.mark_as_read() - - image.stats.views_count += 1 - image.stats.save() - annotation.stats.views_count += 1 - annotation.stats.save() - return render(request, 'iconolab/detail_annotation.html', context) - - -class EditAnnotationView(View, ContextMixin, IconolabObjectView): - - def get_context_data(self, **kwargs): - context = super(EditAnnotationView, self).get_context_data(**kwargs) - context['collection_name'] = self.kwargs.get('collection_name', '') - context['image_guid'] = self.kwargs.get('image_guid', '') - context['annotation_guid'] = self.kwargs.get('annotation_guid', '') - return context - - def get(self, request, *args, **kwargs): - success, result = self.check_kwargs(kwargs) - if success: - (collection, image, annotation,) = result - else: - return result(request) - annotation_form = AnnotationRevisionForm(instance=annotation.current_revision) - context = self.get_context_data(**kwargs) - context['image'] = image - context['annotation'] = annotation - context['form'] = annotation_form - context['tags_data'] = annotation.current_revision.get_tags_json() - return render(request, 'iconolab/change_annotation.html', context) - - def post(self, request, *args, **kwargs): - success, result = self.check_kwargs(kwargs) - if success: - (collection, image, annotation) = result - else: - return result(request) - collection_name = kwargs['collection_name'] - image_guid = kwargs['image_guid'] - annotation_guid = kwargs['annotation_guid'] - annotation_form = AnnotationRevisionForm(request.POST) - if annotation_form.is_valid(): - revision_author = request.user - revision_title = annotation_form.cleaned_data['title'] - revision_description = annotation_form.cleaned_data['description'] - 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) - revision_comment = annotation_form.cleaned_data['comment'] - comment = IconolabComment.objects.create( - comment = revision_comment, - revision = new_revision, - content_type = ContentType.objects.get(app_label='iconolab', model='annotation'), - content_object = annotation, - site = Site.objects.get(id=settings.SITE_ID), - object_pk = annotation.id, - user = request.user, - user_name = request.user.username - ) - return RedirectView.as_view(url=reverse('annotation_detail', kwargs={'collection_name': collection_name, 'image_guid': image_guid, 'annotation_guid': annotation_guid}))(request) - context = self.get_context_data(**kwargs) - context['image'] = image - context['form'] = annotation_form - context['annotation'] = annotation - context['tags_data'] = annotation.current_revision.get_tags_json() - return render(request, 'iconolab/change_annotation.html', context) - - -class ShowRevisionView(View, ContextMixin, IconolabObjectView): - - def get_context_data(self, **kwargs): - context = super(ShowRevisionView, self).get_context_data(**kwargs) - context['collection_name'] = self.kwargs.get('collection_name', '') - context['image_guid'] = self.kwargs.get('image_guid', '') - context['annotation_guid'] = self.kwargs.get('annotation_guid', '') - context['revision_guid'] = self.kwargs.get('revision_guid', '') - return context - - def get(self, request, *args, **kwargs): - success, result = self.check_kwargs(kwargs) - if success: - (collection, image, annotation, revision,) = result - else: - return result(request) - context = self.get_context_data(**kwargs) - context['collection'] = collection - context['image'] = image - context['annotation'] = annotation - context['revision'] = revision - context['tags_data'] = revision.get_tags_json() - context['comment'] = revision.creation_comment.first() - if request.user.is_authenticated() and annotation.author == request.user: - ann_author_notified = Notification.objects.filter( - recipient=request.user, - action_object_content_type__app_label='iconolab', - action_object_content_type__model='annotationrevision', - action_object_object_id=revision.id, - target_content_type__app_label='iconolab', - target_content_type__model='annotation', - target_object_id=annotation.id - ).unread() - if ann_author_notified: - ann_author_notified.first().mark_as_read() - context['notified_revision'] = True - if request.user.is_authenticated() and revision.author == request.user: - rev_author_notified = Notification.objects.filter( - recipient=request.user, - action_object_content_type__app_label='iconolab', - action_object_content_type__model='annotationrevision', - action_object_object_id=revision.id, - target_content_type__app_label='iconolab', - target_content_type__model='annotation', - target_object_id=annotation.id - ).unread() - if rev_author_notified: - rev_author_notified.first().mark_as_read() - context['notified_revision'] = True - return render(request, 'iconolab/detail_revision.html', context) - - -class MergeProposalView(View, ContextMixin, IconolabObjectView): - - def get_context_data(self, **kwargs): - context = super(MergeProposalView, self).get_context_data(**kwargs) - context['collection_name'] = self.kwargs.get('collection_name', '') - context['image_guid'] = self.kwargs.get('image_guid', '') - context['annotation_guid'] = self.kwargs.get('annotation_guid', '') - context['revision_guid'] = self.kwargs.get('revision_guid', '') - return context - - def get(self, request, *args, **kwargs): - success, result = self.check_kwargs(kwargs) - if success: - (collection, image, annotation, revision,) = result - else: - return result(request) - # Only show merge form if there is a revision to merge AND the current user is the annotation author - if revision.state != AnnotationRevision.AWAITING or request.user != annotation.author: - return RedirectView.as_view( - url=reverse('revision_detail', - kwargs={ - 'collection_name': collection.name, - 'image_guid': image.image_guid, - 'annotation_guid': annotation.annotation_guid, - 'revision_guid': revision.revision_guid - } - ) - )(request) - # 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) - return RedirectView.as_view( - url=reverse('annotation_detail', - kwargs={ - 'collection_name': collection.name, - 'image_guid': image.image_guid, - 'annotation_guid': annotation.annotation_guid - } - ) - )(request) - # Auto-reject the revision only if the proper query arg is set - if 'auto_reject' in request.GET and request.GET['auto_reject'] in ['True', 'true', '1', 'yes']: - annotation.reject_existing_revision(revision) - return RedirectView.as_view( - url=reverse('annotation_detail', - kwargs={ - 'collection_name': collection.name, - 'image_guid': image.image_guid, - 'annotation_guid': annotation.annotation_guid - } - ) - )(request) - - context = self.get_context_data(**kwargs) - context['collection'] = collection - context['image'] = image - context['annotation'] = annotation - # Proposal data - context['proposal_revision'] = revision - context['proposal_tags_data'] = revision.get_tags_json() - context['proposal_comment'] = revision.creation_comment.first() - # Parent data - context['parent_revision'] = revision.parent_revision - context['parent_tags_data'] = revision.parent_revision.get_tags_json() - context['parent_comment'] = revision.parent_revision.creation_comment.first() - # Current data - context['current_revision'] = annotation.current_revision - context['current_tags_data'] = annotation.current_revision.get_tags_json() - context['current_comment'] = annotation.current_revision.creation_comment.first() - - merge_form = AnnotationRevisionForm(instance=revision) - context['merge_form'] = merge_form - return render(request, 'iconolab/merge_revision.html', context) - - def post(self, request, *args, **kwargs): - # Handle merge form submit here - success, result = self.check_kwargs(kwargs) - if success: - (collection, image, annotation, revision) = result - else: - return result(request) - collection_name = kwargs['collection_name'] - image_guid = kwargs['image_guid'] - annotation_guid = kwargs['annotation_guid'] - revision_guid = kwargs['revision_guid'] - - merge_revision_form = AnnotationRevisionForm(request.POST) - if merge_revision_form.is_valid(): - revision_title = merge_revision_form.cleaned_data['title'] - revision_description = merge_revision_form.cleaned_data['description'] - 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) - revision_comment = merge_revision_form.cleaned_data['comment'] - comment = IconolabComment.objects.create( - comment = revision_comment, - revision = new_revision, - content_type = ContentType.objects.get(app_label='iconolab', model='annotation'), - content_object = annotation, - site = Site.objects.get(id=settings.SITE_ID), - object_pk = annotation.id, - user = request.user, - user_name = request.user.username - ) - return RedirectView.as_view(url=reverse('annotation_detail', kwargs={'collection_name': collection_name, 'image_guid': image_guid, 'annotation_guid': annotation_guid}))(request) - context = self.get_context_data(**kwargs) - context['image'] = image - context['merge_form'] = merge_revision_form - context['annotation'] = annotation - # Proposal data - context['proposal_revision'] = revision - context['proposal_tags_data'] = revision.get_tags_json() - context['proposal_comment'] = revision.creation_comment.first() - # Parent data - context['parent_revision'] = revision.parent_revision - context['parent_tags_data'] = revision.parent_revision.get_tags_json() - context['parent_comment'] = revision.parent_revision.creation_comment.first() - # Current data - context['current_revision'] = annotation.current_revision - context['current_tags_data'] = annotation.current_revision.get_tags_json() - context['current_comment'] = annotation.current_revision.creation_comment.first() - return render(request, 'iconolab/merge_revision.html', context) - diff -r d710b051cdc5 -r ad770589f0fe src/iconolab/views/misc.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/iconolab/views/misc.py Wed Nov 09 14:16:49 2016 +0100 @@ -0,0 +1,20 @@ +from django.views.generic import TemplateView + +class NotFoundErrorView(TemplateView): + template_name='errors/404error.html' + +class HelpView(TemplateView): + template_name='iconolab/misc/help.html' + +class GlossaryView(TemplateView): + template_name='iconolab/misc/glossary.html' + +class CreditsView(TemplateView): + template_name='iconolab/misc/credits.html' + +class LegalMentionsView(TemplateView): + template_name='iconolab/misc/legalmentions.html' + +class ContributionCharterView(TemplateView): + template_name='iconolab/misc/charter.html' + diff -r d710b051cdc5 -r ad770589f0fe src/iconolab/views/objects.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/iconolab/views/objects.py Wed Nov 09 14:16:49 2016 +0100 @@ -0,0 +1,592 @@ +from django.shortcuts import HttpResponse, get_object_or_404, render, redirect +from django.http import Http404 +from django.db.models import Count +from django.contrib.auth.decorators import login_required +from django.contrib.auth.models import User +from django.views.generic import View, DetailView, RedirectView, TemplateView +from django.views.generic.base import ContextMixin +from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger +from django.core.urlresolvers import reverse +from django.core.exceptions import ObjectDoesNotExist +from django.contrib.contenttypes.models import ContentType +from django.contrib.sites.models import Site +from django.conf import settings +from notifications.models import Notification +from iconolab.models import Annotation, AnnotationRevision, Collection, Item, Image, IconolabComment, MetaCategory, MetaCategoryInfo +from iconolab.forms.annotations import AnnotationRevisionForm +import logging + +logger = logging.getLogger(__name__) + +class GlobalHomepageView(View): + def get(self, request, *args, **kwargs): + context = {} + context['collections'] = Collection.objects + return render(request, 'iconolab/home.html', context) + +class TestView(View): + template_name = 'iconolab/compare.html' + + def get(self, request, *args, **kwargs): + return render(request, self.template_name) + +# Class with check_kwargs method to fetch objects from database depending on what level in the app we're currently at +class IconolabObjectView(object): + def check_kwargs(self, kwargs): + ''' + Returns a boolean depending on wether (True) or not (False) the objects were found and a tuple containing the objects, with a select_related/prefetch_related on relevant related objects + following this ordering: (collection, item, image, annotation, revision) + ''' + + objects_tuple = () + if 'collection_name' in kwargs.keys(): + try: + objects_tuple += (Collection.objects.prefetch_related('items', 'items__images').get(name=kwargs.get('collection_name')),) + except (ValueError, Collection.DoesNotExist): + return False, RedirectView.as_view(url=reverse('404error')) + if 'item_guid' in kwargs.keys(): + try: + objects_tuple += (Item.objects.prefetch_related('images', 'metadatas', 'images__stats').get(item_guid=kwargs.get('item_guid')),) + except (ValueError, Item.DoesNotExist): + return False, RedirectView.as_view(url=reverse('404error')) + if 'image_guid' in kwargs.keys(): + try: + objects_tuple += (Image.objects.prefetch_related('annotations', 'item', 'stats').get(image_guid=kwargs.get('image_guid')),) + except (ValueError, Image.DoesNotExist): + return False, RedirectView.as_view(url=reverse('404error')) + if 'annotation_guid' in kwargs.keys(): + try: + objects_tuple += (Annotation.objects.prefetch_related('current_revision', 'stats', 'image').get(annotation_guid=kwargs.get('annotation_guid')),) + except (ValueError, Annotation.DoesNotExist): + return False, RedirectView.as_view(url=reverse('404error')) + if 'revision_guid' in kwargs.keys(): + try: + objects_tuple += (AnnotationRevision.objects.prefetch_related('parent_revision').get(revision_guid=kwargs.get('revision_guid')),) + except (ValueError, AnnotationRevision.DoesNotExist): + return False, RedirectView.as_view(url=reverse('404error')) + return True, objects_tuple + +class CollectionHomepageView(View, ContextMixin, IconolabObjectView): + def get(self, request, *args, **kwargs): + success, result = self.check_kwargs(kwargs) + if success: + (collection,) = result + else: + return result(request) + context = super(CollectionHomepageView, self).get_context_data(**kwargs) + context['collection_name'] = self.kwargs.get('collection_name', '') + context['collection'] = collection + + # get Pagination and navigation query args + try: + items_page = int(request.GET.get('items_page', '1')) + except ValueError: + items_page = 1 + try: + items_per_page = int(request.GET.get('items_perpage', '12')) + except ValueError: + items_per_page = 12 + + try: + recent_page = int(request.GET.get('recent_page', '1')) + except ValueError: + recent_page = 1 + try: + recent_per_page = int(request.GET.get('recent_perpage', '10')) + except ValueError: + recent_per_page = 10 + + try: + revised_page = int(request.GET.get('revised_page', '1')) + except ValueError: + revised_page = 1 + try: + revised_per_page = int(request.GET.get('revised_perpage', '10')) + except ValueError: + revised_per_page = 10 + + try: + contributions_page = int(request.GET.get('contributions_page', '1')) + except ValueError: + contributions_page = 1 + try: + contributions_per_page = int(request.GET.get('contributions_perpage', '10')) + except ValueError: + contributions_per_page = 10 + + active_list = request.GET.get('show', 'items') + if active_list not in ['items', 'recent', 'revised', 'contributions']: + active_list = 'items' + context["active_list"] = active_list + + # Paginated objects list + context["items_page"] = items_page + context["items_perpage"] = items_per_page + items_paginator = Paginator(collection.items.order_by("metadatas__inventory_number").all(), items_per_page) + try: + context["items_list"] = items_paginator.page(items_page) + except PageNotAnInteger: + context["items_list"] = items_paginator.page(1) + except EmptyPage: + context["items_list"] = items_paginator.page(items_paginator.num_pages) + + # Paginated recent annotations list + context["recent_page"] = recent_page + context["recent_perpage"] = recent_per_page + recent_annotations = Annotation.objects.filter(image__item__collection__name=collection.name).prefetch_related( + 'current_revision', + 'stats' + ).order_by('-current_revision__created') + recent_paginator = Paginator(recent_annotations, recent_per_page) + try: + context["recent_list"] = recent_paginator.page(recent_page) + except PageNotAnInteger: + context["recent_list"] = recent_paginator.page(1) + except EmptyPage: + context["recent_list"] = recent_paginator.page(recent_paginator.num_pages) + + # Paginated revised annotations list + context["revised_page"] = revised_page + context["revised_perpage"] = revised_per_page + revised_annotations = Annotation.objects.filter(image__item__collection__name=collection.name).prefetch_related( + 'current_revision', + 'stats' + ).annotate(revision_count=Count('revisions')).order_by('-revision_count') + revised_paginator = Paginator(revised_annotations, revised_per_page) + try: + context["revised_list"] = revised_paginator.page(revised_page) + except PageNotAnInteger: + context["revised_list"] = revised_paginator.page(1) + except EmptyPage: + context["revised_list"] = revised_paginator.page(revised_paginator.num_pages) + + # Paginated contribution calls annotation list + context["contributions_page"] = contributions_page + context["contributions_perpage"] = contributions_per_page + contrib_calls_annotations_ids = list(set(MetaCategoryInfo.objects.filter( + metacategory__collection__name=collection.name, + metacategory__triggers_notifications=MetaCategory.CONTRIBUTORS + ).order_by('comment__submit_date').values_list('comment__object_pk', flat=True))) + collection_annotations = Annotation.objects.filter(id__in=contrib_calls_annotations_ids).all() + collection_ann_dict = dict([(str(annotation.id), annotation) for annotation in collection_annotations]) + contributions_annotations = [collection_ann_dict[id] for id in contrib_calls_annotations_ids] + contributions_paginator = Paginator(contributions_annotations, contributions_per_page) + try: + context["contributions_list"] = contributions_paginator.page(contributions_page) + except PageNotAnInteger: + context["contributions_list"] = contributions_paginator.page(1) + except EmptyPage: + context["contributions_list"] = contributions_paginator.page(contributions_paginator.num_pages) + + return render(request, 'iconolab/collection_home.html', context) + + + +class ShowItemView(View, ContextMixin, IconolabObjectView): + def get(self, request, *args, **kwargs): + success, result = self.check_kwargs(kwargs) + if success: + (collection, item) = result + else: + return result(request) + + context = super(ShowItemView, self).get_context_data(**kwargs) + image_guid_to_display = request.GET.get("show", str(item.images.first().image_guid)) + if image_guid_to_display not in [str(guid) for guid in item.images.all().values_list("image_guid", flat=True)]: + image_guid_to_display = str(item.images.first().image_guid) + context['display_image'] = image_guid_to_display + try: + displayed_annotations_page = int(request.GET.get('page', '1')) + except ValueError: + displayed_annotations_page = 1 + try: + displayed_annotations_per_page = int(request.GET.get('perpage', '10')) + except ValueError: + displayed_annotations_per_page = 10 + + context['collection_name'] = self.kwargs.get('collection_name', '') + context['item_guid'] = self.kwargs.get('image_guid', '') + context['collection'] = collection + context['item'] = item + context['images'] = [] + for image in item.images.all(): + if str(image.image_guid) == image_guid_to_display: + page = displayed_annotations_page + per_page = displayed_annotations_per_page + else: + page = 1 + per_page = 10 + annotations_paginator = Paginator(image.annotations.all(), per_page) + try: + annotations = annotations_paginator.page(page) + except PageNotAnInteger: + annotations = annotations_paginator.page(1) + except EmptyPage: + annotations = annotations_paginator.page(recent_paginator.num_pages) + context['images'].append({ + 'obj' : image, + 'annotations': annotations + }) + image.stats.views_count += 1 + image.stats.save() + return render(request, 'iconolab/detail_item.html', context); + +class ShowImageView(View, ContextMixin, IconolabObjectView): + def get(self, request, *args, **kwargs): + success, result = self.check_kwargs(kwargs) + if success: + (collection, image) = result + else: + return result(request) + context = super(ShowImageView, self).get_context_data(**kwargs) + context['collection_name'] = self.kwargs.get('collection_name', '') + context['image_guid'] = self.kwargs.get('image_guid', '') + context['collection'] = collection + context['image'] = image + return render(request, 'iconolab/detail_image.html', context) + +class CreateAnnotationView(View, ContextMixin, IconolabObjectView): + + def get_context_data(self, **kwargs): + context = super(CreateAnnotationView, self).get_context_data(**kwargs) + context['collection_name'] = self.kwargs.get('collection_name', '') + context['image_guid'] = self.kwargs.get('image_guid', '') + return context + + def get(self, request, *args, **kwargs): + success, result = self.check_kwargs(kwargs) + if success: + (collection, image,) = result + else: + return result(request) + annotation_form = AnnotationRevisionForm() + context = self.get_context_data(**kwargs) + context['image'] = image + context['form'] = annotation_form + context['tags_data'] = '[]' + return render(request, 'iconolab/change_annotation.html', context) + + def post(self, request, *args, **kwargs): + success, result = self.check_kwargs(kwargs) + if success: + (collection, image) = result + else: + return result(request) + collection_name = kwargs['collection_name'] + image_guid = kwargs['image_guid'] + annotation_form = AnnotationRevisionForm(request.POST) + if annotation_form.is_valid(): + author = request.user + title = annotation_form.cleaned_data['title'] + description = annotation_form.cleaned_data['description'] + fragment = annotation_form.cleaned_data['fragment'] + tags_json = annotation_form.cleaned_data['tags'] + new_annotation = Annotation.objects.create_annotation(author, image, title=title, description=description, fragment=fragment, tags_json=tags_json) + revision_comment = annotation_form.cleaned_data['comment'] + IconolabComment.objects.create( + comment = revision_comment, + revision = new_annotation.current_revision, + content_type = ContentType.objects.get(app_label='iconolab', model='annotation'), + content_object = new_annotation, + site = Site.objects.get(id=settings.SITE_ID), + object_pk = new_annotation.id, + user = request.user, + user_name = request.user.username + ) + return RedirectView.as_view(url=reverse('annotation_detail', kwargs={'collection_name': collection_name, 'image_guid': image_guid, 'annotation_guid': new_annotation.annotation_guid}))(request) + context = self.get_context_data(**kwargs) + context['image'] = image + context['form'] = annotation_form + context['tags_data'] = '[]' + return render(request, 'iconolab/change_annotation.html', context) + +class ShowAnnotationView(View, ContextMixin, IconolabObjectView): + + def get_context_data(self, **kwargs): + context = super(ShowAnnotationView, self).get_context_data(**kwargs) + context['collection_name'] = self.kwargs.get('collection_name', '') + context['image_guid'] = self.kwargs.get('image_guid', '') + context['annotation_guid'] = self.kwargs.get('annotation_guid', '') + return context + + def get(self, request, *args, **kwargs): + success, result = self.check_kwargs(kwargs) + if success: + (collection, image, annotation,) = result + else: + return result(request) + context = self.get_context_data(**kwargs) + context['collection'] = collection + context['image'] = image + context['annotation'] = annotation + context['tags_data'] = annotation.current_revision.get_tags_json() + + page = request.GET.get('page', 1) + per_page = request.GET.get('perpage', 10) + full_comments_list = IconolabComment.objects.for_app_models('iconolab.annotation').filter(object_pk = annotation.pk).order_by('thread_id', '-order') + paginator = Paginator(full_comments_list, per_page) + try: + comments_list = paginator.page(page) + except PageNotAnInteger: + comments_list = paginator.page(1) + except EmptyPage: + comments_list = paginator.page(paginator.num_pages) + context['comments'] = comments_list + + if request.user.is_authenticated(): + user_comment_notifications = Notification.objects.filter( + recipient=request.user, + action_object_content_type__app_label='iconolab', + action_object_content_type__model='iconolabcomment', + target_content_type__app_label='iconolab', + target_content_type__model='annotation', + target_object_id=annotation.id + ).unread() + context['notifications_comments_ids'] = [int(val) for val in user_comment_notifications.values_list('action_object_object_id', flat=True)] + comment_list_ids = [comment.id for comment in context['comments'] ] + for notification in user_comment_notifications.all(): + if int(notification.action_object_object_id) in comment_list_ids: + notification.mark_as_read() + + image.stats.views_count += 1 + image.stats.save() + annotation.stats.views_count += 1 + annotation.stats.save() + return render(request, 'iconolab/detail_annotation.html', context) + + +class EditAnnotationView(View, ContextMixin, IconolabObjectView): + + def get_context_data(self, **kwargs): + context = super(EditAnnotationView, self).get_context_data(**kwargs) + context['collection_name'] = self.kwargs.get('collection_name', '') + context['image_guid'] = self.kwargs.get('image_guid', '') + context['annotation_guid'] = self.kwargs.get('annotation_guid', '') + return context + + def get(self, request, *args, **kwargs): + success, result = self.check_kwargs(kwargs) + if success: + (collection, image, annotation,) = result + else: + return result(request) + annotation_form = AnnotationRevisionForm(instance=annotation.current_revision) + context = self.get_context_data(**kwargs) + context['image'] = image + context['annotation'] = annotation + context['form'] = annotation_form + context['tags_data'] = annotation.current_revision.get_tags_json() + return render(request, 'iconolab/change_annotation.html', context) + + def post(self, request, *args, **kwargs): + success, result = self.check_kwargs(kwargs) + if success: + (collection, image, annotation) = result + else: + return result(request) + collection_name = kwargs['collection_name'] + image_guid = kwargs['image_guid'] + annotation_guid = kwargs['annotation_guid'] + annotation_form = AnnotationRevisionForm(request.POST) + if annotation_form.is_valid(): + revision_author = request.user + revision_title = annotation_form.cleaned_data['title'] + revision_description = annotation_form.cleaned_data['description'] + 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) + revision_comment = annotation_form.cleaned_data['comment'] + comment = IconolabComment.objects.create( + comment = revision_comment, + revision = new_revision, + content_type = ContentType.objects.get(app_label='iconolab', model='annotation'), + content_object = annotation, + site = Site.objects.get(id=settings.SITE_ID), + object_pk = annotation.id, + user = request.user, + user_name = request.user.username + ) + return RedirectView.as_view(url=reverse('annotation_detail', kwargs={'collection_name': collection_name, 'image_guid': image_guid, 'annotation_guid': annotation_guid}))(request) + context = self.get_context_data(**kwargs) + context['image'] = image + context['form'] = annotation_form + context['annotation'] = annotation + context['tags_data'] = annotation.current_revision.get_tags_json() + return render(request, 'iconolab/change_annotation.html', context) + + +class ShowRevisionView(View, ContextMixin, IconolabObjectView): + + def get_context_data(self, **kwargs): + context = super(ShowRevisionView, self).get_context_data(**kwargs) + context['collection_name'] = self.kwargs.get('collection_name', '') + context['image_guid'] = self.kwargs.get('image_guid', '') + context['annotation_guid'] = self.kwargs.get('annotation_guid', '') + context['revision_guid'] = self.kwargs.get('revision_guid', '') + return context + + def get(self, request, *args, **kwargs): + success, result = self.check_kwargs(kwargs) + if success: + (collection, image, annotation, revision,) = result + else: + return result(request) + context = self.get_context_data(**kwargs) + context['collection'] = collection + context['image'] = image + context['annotation'] = annotation + context['revision'] = revision + context['tags_data'] = revision.get_tags_json() + context['comment'] = revision.creation_comment.first() + if request.user.is_authenticated() and annotation.author == request.user: + ann_author_notified = Notification.objects.filter( + recipient=request.user, + action_object_content_type__app_label='iconolab', + action_object_content_type__model='annotationrevision', + action_object_object_id=revision.id, + target_content_type__app_label='iconolab', + target_content_type__model='annotation', + target_object_id=annotation.id + ).unread() + if ann_author_notified: + ann_author_notified.first().mark_as_read() + context['notified_revision'] = True + if request.user.is_authenticated() and revision.author == request.user: + rev_author_notified = Notification.objects.filter( + recipient=request.user, + action_object_content_type__app_label='iconolab', + action_object_content_type__model='annotationrevision', + action_object_object_id=revision.id, + target_content_type__app_label='iconolab', + target_content_type__model='annotation', + target_object_id=annotation.id + ).unread() + if rev_author_notified: + rev_author_notified.first().mark_as_read() + context['notified_revision'] = True + return render(request, 'iconolab/detail_revision.html', context) + + +class MergeProposalView(View, ContextMixin, IconolabObjectView): + + def get_context_data(self, **kwargs): + context = super(MergeProposalView, self).get_context_data(**kwargs) + context['collection_name'] = self.kwargs.get('collection_name', '') + context['image_guid'] = self.kwargs.get('image_guid', '') + context['annotation_guid'] = self.kwargs.get('annotation_guid', '') + context['revision_guid'] = self.kwargs.get('revision_guid', '') + return context + + def get(self, request, *args, **kwargs): + success, result = self.check_kwargs(kwargs) + if success: + (collection, image, annotation, revision,) = result + else: + return result(request) + # Only show merge form if there is a revision to merge AND the current user is the annotation author + if revision.state != AnnotationRevision.AWAITING or request.user != annotation.author: + return RedirectView.as_view( + url=reverse('revision_detail', + kwargs={ + 'collection_name': collection.name, + 'image_guid': image.image_guid, + 'annotation_guid': annotation.annotation_guid, + 'revision_guid': revision.revision_guid + } + ) + )(request) + # 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) + return RedirectView.as_view( + url=reverse('annotation_detail', + kwargs={ + 'collection_name': collection.name, + 'image_guid': image.image_guid, + 'annotation_guid': annotation.annotation_guid + } + ) + )(request) + # Auto-reject the revision only if the proper query arg is set + if 'auto_reject' in request.GET and request.GET['auto_reject'] in ['True', 'true', '1', 'yes']: + annotation.reject_existing_revision(revision) + return RedirectView.as_view( + url=reverse('annotation_detail', + kwargs={ + 'collection_name': collection.name, + 'image_guid': image.image_guid, + 'annotation_guid': annotation.annotation_guid + } + ) + )(request) + + context = self.get_context_data(**kwargs) + context['collection'] = collection + context['image'] = image + context['annotation'] = annotation + # Proposal data + context['proposal_revision'] = revision + context['proposal_tags_data'] = revision.get_tags_json() + context['proposal_comment'] = revision.creation_comment.first() + # Parent data + context['parent_revision'] = revision.parent_revision + context['parent_tags_data'] = revision.parent_revision.get_tags_json() + context['parent_comment'] = revision.parent_revision.creation_comment.first() + # Current data + context['current_revision'] = annotation.current_revision + context['current_tags_data'] = annotation.current_revision.get_tags_json() + context['current_comment'] = annotation.current_revision.creation_comment.first() + + merge_form = AnnotationRevisionForm(instance=revision) + context['merge_form'] = merge_form + return render(request, 'iconolab/merge_revision.html', context) + + def post(self, request, *args, **kwargs): + # Handle merge form submit here + success, result = self.check_kwargs(kwargs) + if success: + (collection, image, annotation, revision) = result + else: + return result(request) + collection_name = kwargs['collection_name'] + image_guid = kwargs['image_guid'] + annotation_guid = kwargs['annotation_guid'] + revision_guid = kwargs['revision_guid'] + + merge_revision_form = AnnotationRevisionForm(request.POST) + if merge_revision_form.is_valid(): + revision_title = merge_revision_form.cleaned_data['title'] + revision_description = merge_revision_form.cleaned_data['description'] + 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) + revision_comment = merge_revision_form.cleaned_data['comment'] + comment = IconolabComment.objects.create( + comment = revision_comment, + revision = new_revision, + content_type = ContentType.objects.get(app_label='iconolab', model='annotation'), + content_object = annotation, + site = Site.objects.get(id=settings.SITE_ID), + object_pk = annotation.id, + user = request.user, + user_name = request.user.username + ) + return RedirectView.as_view(url=reverse('annotation_detail', kwargs={'collection_name': collection_name, 'image_guid': image_guid, 'annotation_guid': annotation_guid}))(request) + context = self.get_context_data(**kwargs) + context['image'] = image + context['merge_form'] = merge_revision_form + context['annotation'] = annotation + # Proposal data + context['proposal_revision'] = revision + context['proposal_tags_data'] = revision.get_tags_json() + context['proposal_comment'] = revision.creation_comment.first() + # Parent data + context['parent_revision'] = revision.parent_revision + context['parent_tags_data'] = revision.parent_revision.get_tags_json() + context['parent_comment'] = revision.parent_revision.creation_comment.first() + # Current data + context['current_revision'] = annotation.current_revision + context['current_tags_data'] = annotation.current_revision.get_tags_json() + context['current_comment'] = annotation.current_revision.creation_comment.first() + return render(request, 'iconolab/merge_revision.html', context) + diff -r d710b051cdc5 -r ad770589f0fe src/iconolab/views/userpages.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/iconolab/views/userpages.py Wed Nov 09 14:16:49 2016 +0100 @@ -0,0 +1,231 @@ +from django.shortcuts import HttpResponse, get_object_or_404, render, redirect +from django.views.generic import View, DetailView +from django.core.urlresolvers import reverse_lazy +from django.core.exceptions import ObjectDoesNotExist +from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger +from django.contrib.auth.models import User +from django.contrib.contenttypes.models import ContentType +from django.contrib.sites.models import Site +from django.conf import settings +from django.urls import reverse +from notifications.models import Notification +from iconolab.models import Annotation, IconolabComment, Image, MetaCategoriesCountInfo +from uuid import UUID +import logging + +logger = logging.getLogger(__name__) + +class UserHomeView(DetailView): + model = User + slug_field = 'id' + + def get_context_data(self, **kwargs): + context = super(UserHomeView, self).get_context_data(**kwargs) + return context + + def get(self, request, *args, **kwargs): + self.object = self.get_object() + context = self.get_context_data() + profile_user = self.object + context['profile_user'] = profile_user + context['user_annotations'] = Annotation.objects.filter(author=profile_user).prefetch_related( + 'current_revision', + 'revisions', + 'image', + 'image__item', + 'image__item__collection' + ).order_by("-created")[:5] + context['user_contributed_annotations'] = Annotation.objects.get_annotations_contributed_for_user(profile_user)[:5] + context['user_commented_annotations'] = Annotation.objects.get_annotations_commented_for_user(profile_user)[:5] + # .exclude(annotation_guid__in=[annotation.annotation_guid for annotation in context['user_revisions_annotations']]) + + if request.user.is_authenticated() and self.object == request.user: + if request.GET.get('clear_notifications', False): + Notification.objects.filter(recipient=request.user).mark_all_as_read() + logger.debug(Notification.objects.filter(recipient=request.user)) + context['notifications'] = Notification.objects.filter(recipient=request.user) + logger.debug(context) + return render(request, 'iconolab/user_home.html', context) + +class UserNotificationsView(View): + + def get(self, request, *args, **kwargs): + context = {} + notifications = Notification.objects.filter(recipient=request.user) + context['notifications_unread_ids'] = notifications.unread().values_list('id', flat=True) + page = request.GET.get('page', 1) + paginator = Paginator(notifications, 50) + try: + notifications_list = paginator.page(page) + except PageNotAnInteger: + notifications_list = paginator.page(1) + except EmptyPage: + notifications_list = paginator.page(paginator.num_pages) + context['notifications'] = notifications_list + return render(request, 'iconolab/user_notifications.html', context) + +class UserAnnotationsView(DetailView): + model = User + slug_field = 'id' + + def get_context_data(self, **kwargs): + context = super(UserAnnotationsView, self).get_context_data(**kwargs) + return context + + def get(self, request, *args, **kwargs): + self.object = self.get_object() + profile_user = self.object + context = self.get_context_data() + page = request.GET.get('page', 1) + per_page=request.GET.get('perpage', 10) + paginator = Paginator(Annotation.objects.filter(author=profile_user).prefetch_related( + 'current_revision', + 'revisions', + 'image', + 'image__item', + 'image__item__collection' + ).order_by("-created").all(), per_page) + try: + annotations_list = paginator.page(page) + except PageNotAnInteger: + annotations_list = paginator.page(1) + except EmptyPage: + annotations_list = paginator.page(paginator.num_pages) + context['user_annotations'] = annotations_list + context['profile_user'] = profile_user + return render(request, 'iconolab/user_annotations.html', context) + +class UserCommentedView(DetailView): + model = User + slug_field = 'id' + + def get_context_data(self, **kwargs): + context = super(UserCommentedView, self).get_context_data(**kwargs) + return context + + def get(self, request, *args, **kwargs): + self.object = self.get_object() + profile_user = self.object + context = self.get_context_data() + page = request.GET.get('page', 1) + per_page=request.GET.get('perpage', 10) + paginator = Paginator(Annotation.objects.get_annotations_commented_for_user(profile_user), per_page) + try: + contributions_list = paginator.page(page) + except PageNotAnInteger: + contributions_list = paginator.page(1) + except EmptyPage: + contributions_list = paginator.page(paginator.num_pages) + context['user_commented_annotations'] = contributions_list + context['profile_user'] = profile_user + return render(request, 'iconolab/user_commented.html', context) + +class UserContributedView(DetailView): + model = User + slug_field = 'id' + + def get_context_data(self, **kwargs): + context = super(UserContributedView, self).get_context_data(**kwargs) + return context + + def get(self, request, *args, **kwargs): + self.object = self.get_object() + profile_user = self.object + context = self.get_context_data() + page = request.GET.get('page', 1) + per_page=request.GET.get('perpage', 10) + paginator = Paginator(Annotation.objects.get_annotations_contributed_for_user(profile_user), per_page) + try: + commented_list = paginator.page(page) + except PageNotAnInteger: + commented_list = paginator.page(1) + except EmptyPage: + commented_list = paginator.page(paginator.num_pages) + context['user_contributed_annotations'] = commented_list + context['profile_user'] = profile_user + return render(request, 'iconolab/user_contributed.html', context) + +class UserCollectionAdminView(DetailView): + model = User + slug_field = 'id' + + def get_context_data(self, **kwargs): + context = super(UserCollectionAdminView, self).get_context_data(**kwargs) + return context + + def get(self, request, *args, **kwargs): + self.object = self.get_object() + profile_user = self.object + context = self.get_context_data() + + if not request.user.is_staff and not request.user.is_authenticated or profile_user != request.user or not request.user.profile.administers_collection: + return redirect(reverse_lazy('user_home', kwargs={'slug': profile_user.id})) + collection = request.user.profile.administers_collection + + annotation_queryset = Annotation.objects.distinct().filter(image__item__collection=collection).prefetch_related('current_revision', 'stats', 'image', 'image__item') + + # filtering + comments_count_filter = request.GET.get("min_comments", "") + if comments_count_filter and comments_count_filter.isdigit(): + comments_count_filter = int(comments_count_filter) + annotation_queryset = annotation_queryset.filter(stats__comments_count__gte=comments_count_filter) + revisions_count_filter = request.GET.get("min_revisions", "") + if revisions_count_filter and revisions_count_filter.isdigit(): + revisions_count_filter = int(revisions_count_filter) + annotation_queryset = annotation_queryset.filter(stats__submitted_revisions_count__gte=revisions_count_filter) + relevancy_filter = request.GET.get("min_relevancy", "") + if relevancy_filter and relevancy_filter.isdigit(): + min_relevancy = min(int(relevancy_filter), 5) + annotation_queryset = annotation_queryset.filter(current_revision__tagginginfo__relevancy__gte=min_relevancy) + accuracy_filter = request.GET.get("min_accuracy", "") + if accuracy_filter and accuracy_filter.isdigit(): + min_accuracy = min(int(accuracy_filter), 5) + annotation_queryset = annotation_queryset.filter(current_revision__tagginginfo__accuracy__gte=min_accuracy) + on_image_filter = request.GET.get("on_image", "") + is_uuid = True + try: + UUID(on_image_filter, version=4) + except ValueError: + # If it's a value error, then the string + # is not a valid hex code for a UUID. + is_uuid = False + if is_uuid and Image.objects.filter(image_guid = on_image_filter).exists(): + annotation_queryset = annotation_queryset.filter(image__image_guid=on_image_filter) + + metacategories_filter = [] + mtcg_annotations_ids = [] + filtering_on_metacategories = False + for metacategory in collection.metacategories.all(): + mtcg_filter = request.GET.get("min_metacategory_"+str(metacategory.id), "") + if mtcg_filter: + filtering_on_metacategories = True + for annotation in annotation_queryset.all(): + if MetaCategoriesCountInfo.objects.filter(metacategory=metacategory, annotation_stats_obj=annotation.stats, count__gte=int(mtcg_filter)).exists(): + mtcg_annotations_ids.append(annotation.annotation_guid) +# mtcg_annotations_ids.append( +# annotation_queryset.filter( +# stats__metacategoriescountinfo_set__metacategory__id=metacategory.id, +# ).values_list('annotation_guid', flat=True)) + logger.debug("FILTERING %r metacategory %r", str(mtcg_filter), metacategory.label) + if filtering_on_metacategories: + annotation_queryset = annotation_queryset.filter(annotation_guid__in=mtcg_annotations_ids) + + # ordering + ordering = [] + orderby_map = { + "oldest": "created", + "recent": "-created", + "most_commented": "-stats__comments_count", + "most_tagged": "-stats__tag_count", + "most_revised": "-stats__submitted_revisions_count", + "most_viewed": "-stats__views_count" + } + for ordering_qarg in ["first", "second", "third", "fourth"]: + if request.GET.get(ordering_qarg, "") in ["oldest", "recent", "most_commented", "most_tagged", "most_revised", "most_viewed"] and orderby_map.get(request.GET.get(ordering_qarg)) not in ordering: + ordering.append(orderby_map.get(request.GET.get(ordering_qarg))) + annotation_queryset = annotation_queryset.order_by(*ordering) + context["collection_filtered_annotations"] = annotation_queryset + context["collection"] = collection + logger.debug(ordering) + logger.debug(annotation_queryset) + return render(request, 'iconolab/user_collection_admin.html', context) \ No newline at end of file