collection home work (list and tabs) + adjusted image list template+ various design fixes
authordurandn
Thu, 18 Aug 2016 15:29:09 +0200
changeset 139 3e0a5286b257
parent 138 2c2d394904db
child 140 b0aae3bc25d2
collection home work (list and tabs) + adjusted image list template+ various design fixes
src/iconolab/models.py
src/iconolab/static/iconolab/css/iconolab.css
src/iconolab/templates/iconolab/collection_home.html
src/iconolab/templates/iconolab/detail_image.html
src/iconolab/templates/iconolab/detail_item.html
src/iconolab/templates/partials/header_search_form.html
src/iconolab/templates/partials/image_annotations_list.html
src/iconolab/views/iconolab.py
--- a/src/iconolab/models.py	Thu Aug 18 15:26:21 2016 +0200
+++ b/src/iconolab/models.py	Thu Aug 18 15:29:09 2016 +0200
@@ -266,6 +266,22 @@
         return self.revisions.filter(state=AnnotationRevision.AWAITING).distinct().count()
     
     @property
+    def accepted_revisions_count(self):
+        return self.revisions.filter(state=AnnotationRevision.ACCEPTED).distinct().count()
+    
+    @property
+    def rejected_revisions_count(self):
+        return self.revisions.filter(state=AnnotationRevision.REJECTED).distinct().count()
+    
+    @property
+    def studied_revisions_count(self):
+        return self.revisions.filter(state=AnnotationRevision.STUDIED).distinct().count()
+      
+    @property
+    def total_revisions_count(self):
+        return self.revisions.distinct().count()
+   
+    @property
     def collection(self):
         return self.image.collection
 
@@ -495,7 +511,7 @@
         (COLLECTION_ADMINS, 'collection admins'),
     )
     
-    collection = models.ForeignKey(Collection)
+    collection = models.ForeignKey(Collection, related_name="metacategories")
     label = models.CharField(max_length=255)
     triggers_notifications = models.IntegerField(choices=NOTIFIED_USERS, default=NONE)
     
--- a/src/iconolab/static/iconolab/css/iconolab.css	Thu Aug 18 15:26:21 2016 +0200
+++ b/src/iconolab/static/iconolab/css/iconolab.css	Thu Aug 18 15:29:09 2016 +0200
@@ -117,4 +117,29 @@
 	vertical-align: top; 
 	margin-top: 15px; 
 	margin-right: 15px;
+}
+
+/* COLLECTION HOME PAGE */
+.image-list-wrapper{
+	margin-top: 15px; 
+	margin-left:10px;
+}
+li.image-list-li{
+	margin-bottom: 5px; 
+	width: 370px; 
+	height: 350px; 
+	vertical-align:middle; 
+	padding:5px;
+}
+.image-list-image-container{
+	 position: relative;
+}
+.object-info{
+	margin-bottom:10px;
+}
+.collection-home-item-btn{
+	margin-bottom:5px;
+}
+.collection-home-tab{
+	cursor: pointer;
 }
\ No newline at end of file
--- a/src/iconolab/templates/iconolab/collection_home.html	Thu Aug 18 15:26:21 2016 +0200
+++ b/src/iconolab/templates/iconolab/collection_home.html	Thu Aug 18 15:29:09 2016 +0200
@@ -7,27 +7,60 @@
 {% load iconolab_tags %}
 
 {% block content %}
-  <h2>Fonds {{collection.verbose_name}}</h2>
-  
- 
-  <p><strong>Images du fonds</strong></p>
-<ul class="image-list-wrapper list-inline">
+<h2><small>Page principale: </small>{{collection.verbose_name}}</h2><br>
+
+<ul class="nav nav-tabs">
+  <li id="tab-recent" role="presentation" {% if recent_annotations %} class="active" {% endif %}><a class="collection-home-tab" id="show-recent">Annotations récentes</a></li>
+  <li id="tab-revised" role="presentation"><a class="collection-home-tab" id="show-revised">Annotations les plus révisées</a></li>
+  <li id="tab-contribution" role="presentation"><a class="collection-home-tab" id="show-contribution">Appels à contribution</a></li>
+  <li id="tab-items" role="presentation" class="{% if not recent_annotations %}active{% endif %} pull-right"><a class="collection-home-tab" id="show-items">Objets du fond {{collection.verbose_name}}</a></li>
+</ul>
+
+<div id="list-recent" class="recent-ann-wrapper collection-home-block {% if recent_annotations %}selected{% endif %}">
+  {% include "partials/image_annotations_list.html" with annotation_list=recent_annotations.all %}
+</div>
 
+<div id="list-revised" class="revised-ann-wrapper collection-home-block">
+  {% include "partials/image_annotations_list.html" with annotation_list=revised_annotations.all %}
+</div>
+
+<div id="list-contribution" class="contribution-ann-wrapper collection-home-block">
+  {% include "partials/image_annotations_list.html" with annotation_list=contribution_calls_annotations_list %}
+</div>
+
+<div id="list-items" class="collection-home-block {% if not recent_annotations %}selected{% endif %}">
+<ul class="image-list-wrapper list-inline">
   {% for item in collection.items.all %}
-  <li class="small-image-wrapper" style="margin-bottom: 5px;">
-    <div class="image-container text-center" style="position: relative">
+  <li class="image-list-li small-image-wrapper panel panel-default">
+    <div class="image-container text-center image-list-image-container">
     {% with item.images.first as image %}
-      {% thumbnail image.media "300x300" crop=False as im %}
+      {% thumbnail image.media "350x300" crop=False as im %}
+        <div class="object-infos">
+          <a class="btn btn-default btn-xs collection-home-item-btn" href="{% url 'item_detail' collection_name item.item_guid %}"><i class="fa fa-eye"></i> Détail de l'objet</a>
+        </div>
+        <div>
         <a href="{% url 'item_detail' collection_name item.item_guid %}">
           <img v-el:small-image src="{{ im.url }}" width="{{ im.width }}" height="{{ im.height }}" />
         </a>
+        </div>
       {% endthumbnail %}
-        <div class="object-infos">
-          <a class="btn btn-default btn-xs collection-home-btn" href="{% url 'item_detail' collection_name item.item_guid %}"><i class="fa fa-eye"></i> Détail de l'objet</a>
-        </div>
     {% endwith %}
    </li>
 {% endfor %}
 </ul>
+</div>
 
+{% endblock %}
+
+{% block footer_js %}
+<script>
+  $(".collection-home-block:not(.selected)").hide();
+  $(".collection-home-tab").click(function(){
+      $(".collection-home-tab").parent().removeClass("active");
+      $(this).parent().addClass("active");
+      clickedTab = /show\-([0-9a-z\-]+)/.exec($(this).attr("id"))[1];
+      $(".collection-home-block").hide();
+      $("#list-"+clickedTab).show();
+  })
+</script>
 {% endblock %}
\ No newline at end of file
--- a/src/iconolab/templates/iconolab/detail_image.html	Thu Aug 18 15:26:21 2016 +0200
+++ b/src/iconolab/templates/iconolab/detail_image.html	Thu Aug 18 15:29:09 2016 +0200
@@ -21,6 +21,6 @@
 	</div>
 </div>
 
-{% include "partials/image_annotations_list.html" with annotation_list=image.annotations.all %}
+{% include "partials/image_annotations_list.html" with annotation_list=image.annotations.all header="Annotations de l'image" %}
 
 {% endblock %}
\ No newline at end of file
--- a/src/iconolab/templates/iconolab/detail_item.html	Thu Aug 18 15:26:21 2016 +0200
+++ b/src/iconolab/templates/iconolab/detail_item.html	Thu Aug 18 15:29:09 2016 +0200
@@ -53,7 +53,7 @@
     </div>
       {% for image in item.images.all %}  
       <div id="annotations-{{image.image_guid}}" class="col-md-12 image-annotations-list">
-        {% include "partials/image_annotations_list.html" with annotation_list=image.annotations.all image_guid=image.image_guid %}  
+        {% include "partials/image_annotations_list.html" with annotation_list=image.annotations.all header="Annotation de l'image" %}  
       </div>
       {% endfor %}  
 </div>
--- a/src/iconolab/templates/partials/header_search_form.html	Thu Aug 18 15:26:21 2016 +0200
+++ b/src/iconolab/templates/partials/header_search_form.html	Thu Aug 18 15:29:09 2016 +0200
@@ -16,5 +16,5 @@
 		</select>
 	</div>
 
-	<button type="submit" class="btn btn-default">Rechercher</button>
+	<button type="submit" class="btn btn-default btn-sm"><i class="fa fa-search" aria-hidden="true"></i></button>
 </form>
--- a/src/iconolab/templates/partials/image_annotations_list.html	Thu Aug 18 15:26:21 2016 +0200
+++ b/src/iconolab/templates/partials/image_annotations_list.html	Thu Aug 18 15:29:09 2016 +0200
@@ -2,10 +2,13 @@
 {% load iconolab_tags %}
 
 <ul class="annotation-list-wrapper list-inline">
-
-	<h4><strong>Annotations de l'image</strong></h4>  
+    {% if header %}
+	<h4><strong>Annotations de l'image</strong></h4>
+    {% endif %}
+    
+    
     {% if not annotation_list %}
-        <p> Aucune annotation pour cette image </p>
+        <h3 class="text-center"><small>Aucune annotation à afficher</small></p>
     {% else %}
         <table class="table table-condensed">
           <thead>
@@ -22,7 +25,7 @@
             <td>
               <div class="fragment-container" style="position: relative">
                 {% thumbnail annotation.image.media "150x150" crop=False as im %}
-                  <a href="{% url 'annotation_detail' collection_name image_guid annotation.annotation_guid %}">
+                  <a href="{% url 'annotation_detail' collection_name annotation.image.image_guid annotation.annotation_guid %}">
                     <img v-el:small-image src="{{ im.url }}" width="{{ im.width }}" height="{{ im.height }}" />
                     <svg width="{{ im.width }}" height="{{ im.height }}" version="1.1" style="position:absolute; top:0px; left: 0px">
                       <g transform="matrix({% transform_matrix im_width=im.width im_height=im.height max_x=100 max_y=100 %})">
--- a/src/iconolab/views/iconolab.py	Thu Aug 18 15:26:21 2016 +0200
+++ b/src/iconolab/views/iconolab.py	Thu Aug 18 15:29:09 2016 +0200
@@ -1,5 +1,6 @@
 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
@@ -20,13 +21,13 @@
 class GlobalHomepageView(View):
     def get(self, request, *args, **kwargs):
         context = {}
-        context["collections"] = Collection.objects
+        context['collections'] = Collection.objects
         return render(request, 'iconolab/home.html', context)
 
 
 class UserHomeView(DetailView):
     model = User
-    slug_field = "id"
+    slug_field = 'id'
     
     def get_context_data(self, **kwargs):
         context = super(UserHomeView, self).get_context_data(**kwargs)
@@ -36,34 +37,34 @@
         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['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"
+        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_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"
+        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):
+            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)
+            context['notifications'] = Notification.objects.filter(recipient=request.user)
         return render(request, 'iconolab/user_home.html', context)
 
 class UserNotificationsView(View):
@@ -71,8 +72,8 @@
     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)
+        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)
@@ -80,38 +81,38 @@
             notifications_list = paginator.page(1)
         except EmptyPage:
             notifications_list = paginator.page(paginator.num_pages)
-        context["notifications"] = notifications_list
+        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():
+        if 'collection_name' in kwargs.keys():
             try:
-                objects_tuple += (Collection.objects.prefetch_related("items", "items__images").get(name=kwargs.get('collection_name')),)
+                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():
+        if 'item_guid' in kwargs.keys():
             try:
-                objects_tuple += (Item.objects.prefetch_related("images").get(item_guid=kwargs.get('item_guid')),)
+                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():
+        if 'image_guid' in kwargs.keys():
             try:
-                objects_tuple += (Image.objects.prefetch_related("annotations", "item").get(image_guid=kwargs.get('image_guid')),)
+                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():
+        if 'annotation_guid' in kwargs.keys():
             try:
-                objects_tuple += (Annotation.objects.select_related('current_revision').get(annotation_guid=kwargs.get('annotation_guid')),)
+                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():
+        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):
@@ -128,6 +129,29 @@
         context = super(CollectionHomepageView, self).get_context_data(**kwargs)
         context['collection_name'] = self.kwargs.get('collection_name', '')
         context['collection'] = collection
+        
+        # Recent annotations
+        context['recent_annotations'] = Annotation.objects.filter(image__item__collection__name=collection.name).prefetch_related(
+            'current_revision', 
+            'stats'
+        ).order_by('-current_revision__created')
+        
+        # Recent annotations
+        context['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')
+        
+        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()
+        logger.debug(collection_annotations)
+        collection_ann_dict = dict([(str(annotation.id), annotation) for annotation in collection_annotations])
+        context["contribution_calls_annotations_list"] = [collection_ann_dict[id] for id in contrib_calls_annotations_ids]
+        
         return render(request, 'iconolab/collection_home.html', context)
     
 
@@ -239,9 +263,9 @@
         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")
+        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)
@@ -249,19 +273,19 @@
             comments_list = paginator.page(1)
         except EmptyPage:
             comments_list = paginator.page(paginator.num_pages)
-        context["comments"] = comments_list
+        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",
+                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"] ]
+            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()
@@ -359,29 +383,29 @@
         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_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_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
+                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_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_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
+                context['notified_revision'] = True
         return render(request, 'iconolab/detail_revision.html', context)
 
         
@@ -414,7 +438,7 @@
                 )
             )(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:
+        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', 
@@ -426,7 +450,7 @@
                 )
             )(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"]:
+        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', 
@@ -510,7 +534,7 @@
     
     
 class NotFoundErrorView(TemplateView):
-    template_name="errors/404error.html"
+    template_name='errors/404error.html'
     
 class HelpView(TemplateView):
-    template_name="iconolab/glossary.html"
\ No newline at end of file
+    template_name='iconolab/glossary.html'
\ No newline at end of file