Started work on notification and user homepage + method for a comment to find back its page in his annotation's comments
authordurandn
Wed, 03 Aug 2016 15:47:03 +0200
changeset 97 f747c112e8f4
parent 96 09b2da30cd93
child 98 2b738b88d483
Started work on notification and user homepage + method for a comment to find back its page in his annotation's comments
src/iconolab/auth/views.py
src/iconolab/fixtures/demo_data.json
src/iconolab/migrations/0005_auto_20160802_1211.py
src/iconolab/migrations/0006_metacategory_triggers_notifications.py
src/iconolab/models.py
src/iconolab/settings/__init__.py
src/iconolab/settings/dev.py.tmpl
src/iconolab/signals/handlers.py
src/iconolab/static/iconolab/css/iconolab.css
src/iconolab/templates/iconolab/user_home.html
src/iconolab/templates/iconolab/user_page.html
src/iconolab/templates/partials/header.html
src/iconolab/templates/partials/image_annotations_list.html
src/iconolab/urls.py
src/iconolab/views/comments.py
src/iconolab/views/iconolab.py
--- a/src/iconolab/auth/views.py	Tue Aug 02 11:13:52 2016 +0200
+++ b/src/iconolab/auth/views.py	Wed Aug 03 15:47:03 2016 +0200
@@ -62,7 +62,6 @@
 		form = self.get_form()
 		if form.is_valid():
 			form.save()
-			print(request.POST)
 			user = authenticate(username=request.POST["username"], password=request.POST["password1"])
 			login(request, user)
 			return HttpResponseRedirect(self.success_url)
--- a/src/iconolab/fixtures/demo_data.json	Tue Aug 02 11:13:52 2016 +0200
+++ b/src/iconolab/fixtures/demo_data.json	Wed Aug 03 15:47:03 2016 +0200
@@ -222,7 +222,7 @@
 	    "pk": 2,
 	    "fields": {
 	    	"collection": 1,
-	    	"label": "Appel à expert"
+	    	"label": "Appel à expertise"
 	    }
 	},{
 	    "model": "iconolab.MetaCategory",
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/iconolab/migrations/0005_auto_20160802_1211.py	Wed Aug 03 15:47:03 2016 +0200
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.7 on 2016-08-02 12:11
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('iconolab', '0004_auto_20160711_1514'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='iconolabcomment',
+            options={'ordering': ['thread_id', 'id']},
+        ),
+        migrations.AddField(
+            model_name='annotationstats',
+            name='awaiting_revisions_count',
+            field=models.IntegerField(blank=True, default=0, null=True),
+        ),
+    ]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/iconolab/migrations/0006_metacategory_triggers_notifications.py	Wed Aug 03 15:47:03 2016 +0200
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.7 on 2016-08-02 12:52
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('iconolab', '0005_auto_20160802_1211'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='metacategory',
+            name='triggers_notifications',
+            field=models.IntegerField(choices=[(0, 'none'), (1, 'contributors'), (2, 'commenters'), (3, 'collection admins')], default=0),
+        ),
+    ]
--- a/src/iconolab/models.py	Tue Aug 02 11:13:52 2016 +0200
+++ b/src/iconolab/models.py	Wed Aug 03 15:47:03 2016 +0200
@@ -132,6 +132,7 @@
 class AnnotationStats(models.Model):
     annotation = models.OneToOneField('Annotation', related_name='stats', blank=False, null=False)
     submitted_revisions_count = models.IntegerField(blank=True, null=True, default=1)
+    awaiting_revisions_count = models.IntegerField(blank=True, null=True, default=0)
     accepted_revisions_count = models.IntegerField(blank=True, null=True, default=1)
     contributors_count = models.IntegerField(blank=True, null=True, default=1)
     views_count = models.IntegerField(blank=True, null=True, default=0)
@@ -140,11 +141,13 @@
     
     @property
     def contributors(self):
-        contributors = []
-        for revision in self.annotation.revisions.filter(state__in=[AnnotationRevision.ACCEPTED, AnnotationRevision.STUDIED]):
-            if revision.author not in contributors:
-                contributors.append(revision.author)
-        return contributors
+        user_ids_list = self.annotation.revisions.filter(state__in=[AnnotationRevision.ACCEPTED, AnnotationRevision.STUDIED]).values_list("author__id", flat=True)
+        return User.objects.filter(id__in=user_ids_list).distinct()
+    
+    @property
+    def commenters(self):
+        user_ids_list = IconolabComment.objects.filter(content_type__app_label="iconolab", content_type__model="annotation", object_pk=self.annotation.id).values_list("user__id", flat=True)
+        return User.objects.filter(id__in=user_ids_list).distinct()
     
     def set_tags_stats(self):
         self.tag_count = Tag.objects.filter(tagginginfo__revision__annotation = self.annotation).distinct().count()
@@ -155,6 +158,8 @@
         # submitted_revisions_count 
         annotation_revisions = self.annotation.revisions
         self.submitted_revisions_count = annotation_revisions.count()
+        # aawaiting_revisions_count
+        self.awaiting_revisions_count = annotation_revisions.filter(state=AnnotationRevision.AWAITING).count()
         # accepted_revisions_count
         self.accepted_revisions_count = annotation_revisions.filter(state=AnnotationRevision.ACCEPTED).count() + annotation_revisions.filter(state=AnnotationRevision.STUDIED).count()
         # comment_count
@@ -178,6 +183,11 @@
     
     objects = AnnotationManager()
     
+    @property
+    def awaiting_revisions_count(self):
+        return self.revisions.filter(state=AnnotationRevision.AWAITING).distinct().count()
+    
+    
     # Call to create a new revision, possibly from a merge
     @transaction.atomic
     def make_new_revision(self, author, title, description, fragment, tags_json):
@@ -362,10 +372,31 @@
     class Meta:
         ordering = ["thread_id", "id"]
     
+    # Get page for considered comment, with COMMENTS_PER_PAGE_DEFAULT comments per page
+    def get_comment_page(self):
+        return self._default_manager.filter(
+            object_pk=self.object_pk, 
+            content_type__app_label=self.content_type.app_label, 
+            content_type__model=self.content_type.model
+        ).order_by("thread_id", "-order").filter(thread_id__lt=self.thread_id, order__gt=self.order).count() // settings.COMMENTS_PER_PAGE_DEFAULT + 1
+    
 class MetaCategory(models.Model):
+    NONE = 0 # Notifies nobody
+    CONTRIBUTORS = 1 # Notifies contributors (revision owners) on target annotation
+    COMMENTERS = 2 # Notifies commenters (contributors + comment owners) on target annotation
+    COLLECTION_ADMINS = 3 # Notifies collection admins
+    
+    NOTIFIED_USERS = (
+        (NONE, 'none'),
+        (CONTRIBUTORS, 'contributors'),
+        (COMMENTERS, 'commenters'),
+        (COLLECTION_ADMINS, 'collection admins'),
+    )
+    
     collection = models.ForeignKey(Collection)
     label = models.CharField(max_length=255)
-
+    triggers_notifications = models.IntegerField(choices=NOTIFIED_USERS, default=NONE)
+    
     def __str__(self):
         return self.label
     
--- a/src/iconolab/settings/__init__.py	Tue Aug 02 11:13:52 2016 +0200
+++ b/src/iconolab/settings/__init__.py	Wed Aug 03 15:47:03 2016 +0200
@@ -58,12 +58,14 @@
     'django_comments_xtd',
     'iconolab.apps.IconolabApp',
     'sorl.thumbnail',
+    'notifications'
 ]
 
 COMMENTS_APP = "django_comments_xtd"
 COMMENTS_XTD_MODEL = "iconolab.models.IconolabComment"
 COMMENTS_XTD_FORM_CLASS = 'iconolab.forms.comments.IconolabCommentForm'
 COMMENTS_XTD_MAX_THREAD_LEVEL = 100
+COMMENTS_PER_PAGE_DEFAULT = 10
 
 SITE_ID = 1
 
--- a/src/iconolab/settings/dev.py.tmpl	Tue Aug 02 11:13:52 2016 +0200
+++ b/src/iconolab/settings/dev.py.tmpl	Wed Aug 03 15:47:03 2016 +0200
@@ -57,11 +57,14 @@
     'reversion_compare',
     'iconolab.apps.IconolabApp',
     'sorl.thumbnail',
+    'notifications'
 ]
 
 COMMENTS_APP = "django_comments_xtd"
 COMMENTS_XTD_MODEL = "iconolab.models.IconolabComment"
+COMMENTS_XTD_FORM_CLASS = 'iconolab.forms.comments.IconolabCommentForm'
 COMMENTS_XTD_MAX_THREAD_LEVEL = 100
+COMMENTS_PER_PAGE_DEFAULT = 10
 
 SITE_ID = 1
 
--- a/src/iconolab/signals/handlers.py	Tue Aug 02 11:13:52 2016 +0200
+++ b/src/iconolab/signals/handlers.py	Wed Aug 03 15:47:03 2016 +0200
@@ -1,11 +1,12 @@
 from django.apps import apps
 from django.db.models.signals import post_save
 from django.dispatch import Signal, receiver
-
+from notifications.signals import notify
+from django_comments.signals import comment_was_posted
 
 # Signal sent during method Annotation.validate_existing_revision to update stats
-revision_accepted = Signal(providing_args=["instance"])
-revision_created = Signal(providing_args=["instance"])
+revision_accepted = Signal(providing_args=['instance'])
+revision_created = Signal(providing_args=['instance'])
 
 
 def increment_stats_on_new_revision(sender, instance, **kwargs):
@@ -16,6 +17,8 @@
         annotation.stats.submitted_revisions_count += 1
         if instance.state in [AnnotationRevision.ACCEPTED, AnnotationRevision.STUDIED]:
             annotation.stats.accepted_revisions_count += 1
+        if instance.state in [AnnotationRevision.AWAITING]:
+            annotation.stats.awaiting_revisions_count += 1
         annotation.stats.set_tags_stats()
         annotation.stats.save()
         # Image stats
@@ -23,7 +26,7 @@
         image.stats.submitted_revisions_count += 1
         image.stats.set_tags_stats()
         image.stats.save()
-
+        
     
 def increment_stats_on_new_comments(sender, instance, created, **kwargs):
     from iconolab.models import IconolabComment
@@ -53,8 +56,50 @@
         image.stats.set_tags_stats()
         image.stats.save()
 
+
+def notify_users_on_new_comment(sender, comment, **kwargs):
+    from iconolab.models import IconolabComment, Annotation, MetaCategory
+    if sender == IconolabComment and instance.content_type.app_label == 'iconolab' and instance.content_type.model == 'annotation':
+        comment_annotation = Annotation.objects.get(id=instance.object_pk)
+        # Notifying new user comment
+        if instance.user != comment_annotation.author:
+            notify.send(instance.user, recipient=comment_annotation.author, verb='a écrit un commentaire sur votre annotation', action_object=instance, target=comment_annotation)
+        print(instance.parent_id)
+        print(instance.id)
+        if instance.level > 0:
+            parent_comment = IconolabComment.objects.get(id=instance.parent_id)
+            notify.send(instance.user, recipient=parent_comment.user, verb='a répondu à votre commentaire', action_object=instance, target=comment_annotation)
+        for metacategory in instance.metacategories.all():
+            if metacategory.triggers_notifications == MetaCategory.COMMENTERS:
+                for commenter in comment_annotation.stats.commenters.exclude(id=instance.user.id).all():
+                    notify.send(instance.user, recipient=commenter, verb='a fait un appel à contribution', action_object=instance, target=comment_annotation)
+            elif metacategory.triggers_notifications == MetaCategory.CONTRIBUTORS:
+                for contributor in comment_annotation.stats.contributors.exclude(id=instance.user.id).all():
+                    notify.send(instance.user, recipient=contributor, verb='a fait un appel à contribution', action_object=instance, target=comment_annotation)
+            if metacategory.triggers_notifications == MetaCategory.COLLECTION_ADMINS:
+                pass
+        
+def notify_users_on_new_revision(sender, instance, **kwargs):
+    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)
+        
+        
+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)
+        
+
+# Stats handlers connect
 post_save.connect(increment_annotations_count)
 post_save.connect(increment_stats_on_new_comments)
 revision_created.connect(increment_stats_on_new_revision)
 revision_accepted.connect(increment_accepted_revisions)
+# Notifications handlers connect
+comment_was_posted.connect(notify_users_on_new_comment)
+revision_created.connect(notify_users_on_new_revision)
+revision_accepted.connect(notify_users_on_accepted_revision)
         
\ No newline at end of file
--- a/src/iconolab/static/iconolab/css/iconolab.css	Tue Aug 02 11:13:52 2016 +0200
+++ b/src/iconolab/static/iconolab/css/iconolab.css	Wed Aug 03 15:47:03 2016 +0200
@@ -68,4 +68,39 @@
 
 .pagination-shortcut{
 	cursor: pointer;	
+}
+
+.badge-error {
+  background-color: #b94a48;
+}
+.badge-error:hover {
+  background-color: #953b39;
+}
+.badge-warning {
+  background-color: #f89406;
+}
+.badge-warning:hover {
+  background-color: #c67605;
+}
+.badge-success {
+  background-color: #468847;
+}
+.badge-success:hover {
+  background-color: #356635;
+}
+.badge-info {
+  background-color: #3a87ad;
+}
+.badge-info:hover {
+  background-color: #2d6987;
+}
+.badge-inverse {
+  background-color: #333333;
+}
+.badge-inverse:hover {
+  background-color: #1a1a1a;
+}
+
+.notif-badge{
+  margin-bottom: 5px;
 }
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/iconolab/templates/iconolab/user_home.html	Wed Aug 03 15:47:03 2016 +0200
@@ -0,0 +1,106 @@
+{% extends 'iconolab_base.html' %}
+
+{% load staticfiles %}
+
+{% load thumbnail %}
+
+{% load iconolab_tags %}
+{% load notifications_tags %}
+
+{% block content %}
+  <div id="user-profile-block" class="row" style="border: 1px solid gray;">
+    <div class="col-md-12">
+      
+      <h3>{% if profile_user == request.user %}Mon espace:{% else %}Profil: {% endif %} {{profile_user.username}}</h3>
+      <div class="panel panel-default" style="padding-left: 10px; padding-right: 10px;">
+        {% if profile_user == request.user %}
+        <h4><span class="badge notif-badge {% if notifications.unread %}badge-error{% endif %}">{{notifications.unread.count}}</span> Notifications non lues 
+          <a href=# class="btn btn-default btn-xs">Voir toutes mes notifications</a>
+          <a href=# class="btn btn-default btn-xs">Tout marquer comme lu</a>
+        </h4>
+        <div class="row">
+          <div class="col-md-12">
+            {% if notifications %}
+              <ul class="list-group">
+              {% for notification in notifications.unread %}
+                <a  
+                  {% if notification.target and notification.action_object %}
+                    {% with notification.target as annotation %}
+                      class="list-group-item"
+                      {% if notification.action_object.get_comment_page %}
+                      href="{% url 'annotation_detail' annotation.image.item.collection.name annotation.image.image_guid annotation.annotation_guid %}?page={{notification.action_object.get_comment_page}}#c{{notification.action_object.id}}"
+                      {% else %}
+                      href="{% url 'revision_detail' annotation.image.item.collection.name annotation.image.image_guid annotation.annotation_guid notification.action_object.revision_guid %}"
+                      {% endif %}
+                    {% endwith %}
+                  {% else %}
+                    class="list-group-item disabled"
+                  {% endif %}>
+                  <b>{{notification.actor.username}}</b> {{notification.verb}} <small> - {{notification.timesince}}</small>
+                </a>
+              {% endfor %}
+              </ul>
+            {% endif %}
+            <hr>
+          </div>
+        </div>
+        {% endif %}
+        <div class="row">
+          <div class="col-md-6">
+            <h4>{% if profile_user == request.user %}Mes annotations:{% else %}Annotations de {{profile_user.username}}{% endif %} </h4>
+            <ul class="list-inline">
+            {% for annotation in user_annotations.all %}
+            <li>
+              <div class="panel panel-default" style="min-width: 375px;">
+                  <div class="panel-heading">Title</div>
+                  <div class="fragment-container" style="position:relative; display:inline-block">
+                    {% thumbnail annotation.image.media "150x150" crop=False as im %}
+                      <a href="{% url 'annotation_detail' annotation.image.item.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 %})">
+                            <path d="{{ annotation.current_revision.fragment|clean_path }}" opacity="0.7" fill="orange"></path>
+                          </g>
+                        </svg>
+                      </a>
+                    {% endthumbnail %}
+                  </div>
+                  <div class="annotation-detail" style="display:inline-block; position:relative; margin-right: 15px;">
+                    <dl class="dl-horizontal">
+                      <dt>Commentaires: </dt>
+                      <dd><span class="badge">{{annotation.stats.comments_count}}</span></dd>
+                      <dt>Révisions en attente:</dt>
+                      <dd><span class="badge {% if annotation.awaiting_revisions_count > 0 %}badge-warning{% endif %}">{{annotation.awaiting_revisions_count}}</span></dd>
+                    </dl>
+                  </div>
+              </div>
+            </li>
+            {% endfor %}
+            </ul>
+          </div>
+          <div class="col-md-6">
+            <h4>{% if profile_user == request.user %}Mes autres contributions:{% else %}Contributions de {{profile_user.username}}{% endif %} </h4>
+            <dl>
+              <dt>
+              {% if profile_user == request.user %}
+                Annotations sur lesquelles j'ai commenté:
+              {% else %}
+                Annotations sur lesquelles {{profile_user.username}} a commenté:
+              {% endif %}
+              </dt>
+              <dd>{{user_comments_annotations.all}}</dd>
+              <dt>
+              {% if profile_user == request.user %}
+                Annotations sur lesquelles j'ai proposé des révisions
+              {% else %}
+                Annotations sur lesquelles {{profile_user.username}} a proposé des révisions:
+              {% endif %} 
+              </dt>
+              <dd>{{user_revisions_annotations.all}}</dd>
+            </dl>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+{% endblock %}
\ No newline at end of file
--- a/src/iconolab/templates/iconolab/user_page.html	Tue Aug 02 11:13:52 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,11 +0,0 @@
-{% extends 'iconolab_base.html' %}
-
-{% load staticfiles %}
-
-{% load thumbnail %}
-
-{% load iconolab_tags %}
-
-{% block content %}
-  <div id="user-profile-block" class="row" style="border: 1px solid gray;padding-top: 10px;"></div>
-{% endblock %}
\ No newline at end of file
--- a/src/iconolab/templates/partials/header.html	Tue Aug 02 11:13:52 2016 +0200
+++ b/src/iconolab/templates/partials/header.html	Wed Aug 03 15:47:03 2016 +0200
@@ -24,8 +24,8 @@
       </form>
 
       <ul class="nav navbar-nav navbar-right">
-        {% if user.is_authenticated %}
-          <li><a href="{% url 'user_profile' %}">{{user.username}}: Mon espace</a></li>
+        {% if request.user.is_authenticated %}
+          <li><a href="{% url 'user_home' request.user.id %}">{{user.username}}: Mon espace</a></li>
           <li><a href="{% url 'account:logout' %}">Se déconnecter</a></li>
         {% else %}
           <li><a href="{% url 'account:register' %}">Créer un compte</a></li>
--- a/src/iconolab/templates/partials/image_annotations_list.html	Tue Aug 02 11:13:52 2016 +0200
+++ b/src/iconolab/templates/partials/image_annotations_list.html	Wed Aug 03 15:47:03 2016 +0200
@@ -38,8 +38,8 @@
             <td>{{ annotation.created|date:'d-m-Y' }}</td>
             <td>{{ annotation.current_revision.created|date:'d-m-Y' }}</td>
             <td>
-              {% for contributor in annotation.stats.contributors %}
-                  {{ contributor }}{% if not forloop.last %}, {% endif %}
+              {% for contributor in annotation.stats.contributors.all %}
+                  {{ contributor.username }}{% if not forloop.last %}, {% endif %}
               {% endfor %}
             </td>
             <td>
--- a/src/iconolab/urls.py	Tue Aug 02 11:13:52 2016 +0200
+++ b/src/iconolab/urls.py	Wed Aug 03 15:47:03 2016 +0200
@@ -22,6 +22,7 @@
 from django.conf.urls.static import static
 from django.contrib.auth.decorators import login_required
 from django.contrib.staticfiles.urls import staticfiles_urlpatterns
+import notifications.urls
 
 urlpatterns = [
     url(r'^$', views.iconolab.RedirectView.as_view(url=reverse_lazy("home"))),
@@ -35,12 +36,13 @@
     url(r'^collections/(?P<collection_name>[a-z]+)/images/(?P<image_guid>[^/]+)/annotations/(?P<annotation_guid>[^/]+)/edit$', login_required(views.iconolab.EditAnnotationView.as_view()), name='annotation_edit'),
     url(r'^collections/(?P<collection_name>[a-z]+)/images/(?P<image_guid>[^/]+)/annotations/(?P<annotation_guid>[^/]+)/revisions/(?P<revision_guid>[^/]+)/detail', views.iconolab.ShowRevisionView.as_view(), name='revision_detail'),
     url(r'^collections/(?P<collection_name>[a-z]+)/images/(?P<image_guid>[^/]+)/annotations/(?P<annotation_guid>[^/]+)/revisions/(?P<revision_guid>[^/]+)/merge$', login_required(views.iconolab.MergeProposalView.as_view()), name='annotation_merge'),
-    url(r'^user/profile', login_required(views.iconolab.UserProfileView.as_view()), name="user_profile"),
+    url(r'^user/(?P<slug>[a-z0-9\-]+)/home/', views.iconolab.UserHomeView.as_view(), name="user_home"),
     url(r'^errors/404', views.iconolab.NotFoundErrorView.as_view(), name="404error"),
     url(r'^rest', include('restapi.urls')),
     url(r'^account/', include('iconolab.auth.urls', namespace='account')),
     url(r'^comments/', include('django_comments_xtd.urls')),
-    url(r'^comments/annotation/post', views.comments.post_comment_iconolab, name="post_comment")
+    url(r'^comments/annotation/post', views.comments.post_comment_iconolab, name="post_comment"),
+    url('^user/notifications/', include(notifications.urls, namespace='notifications')),
 ]
 
 urlpatterns += staticfiles_urlpatterns()
--- a/src/iconolab/views/comments.py	Tue Aug 02 11:13:52 2016 +0200
+++ b/src/iconolab/views/comments.py	Wed Aug 03 15:47:03 2016 +0200
@@ -7,6 +7,7 @@
 from django_comments import signals
 from django_comments.views.utils import next_redirect, confirmation_view
 from django_comments.views.comments import CommentPostBadRequest
+from iconolab.models import MetaCategoryInfo
 
 @csrf_protect
 @require_POST
--- a/src/iconolab/views/iconolab.py	Tue Aug 02 11:13:52 2016 +0200
+++ b/src/iconolab/views/iconolab.py	Wed Aug 03 15:47:03 2016 +0200
@@ -1,7 +1,8 @@
 from django.shortcuts import HttpResponse, get_object_or_404, render
 from django.http import Http404
 from django.contrib.auth.decorators import login_required
-from django.views.generic import View, RedirectView
+from django.contrib.auth.models import User
+from django.views.generic import View, DetailView, RedirectView
 from django.views.generic.base import ContextMixin
 from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
 from django.core.urlresolvers import reverse
@@ -9,6 +10,7 @@
 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
 
@@ -20,10 +22,39 @@
         return render(request, 'iconolab/home.html', context)
 
 
-class UserProfileView(View):
+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):
-        context = {}
-        return render(request, 'iconolab/user_page.html', context)
+        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).distinct()
+        if request.user.is_authenticated() and self.object == request.user:
+            context["notifications"] = Notification.objects.filter(recipient=request.user)
+        return render(request, 'iconolab/user_home.html', context)
 
 
 class CollectionHomepageView(View, ContextMixin):