signals refactoring followup: fixed circular import + added a profile view and template and link in header + refactored views into a views module
authordurandn
Mon, 01 Aug 2016 12:59:18 +0200
changeset 94 a47934ff37ec
parent 93 30cd69620039
child 95 54d8dab80297
signals refactoring followup: fixed circular import + added a profile view and template and link in header + refactored views into a views module
src/iconolab/signals/handlers.py
src/iconolab/templates/iconolab/user_page.html
src/iconolab/templates/partials/header.html
src/iconolab/urls.py
src/iconolab/views.py
src/iconolab/views/__init__.py
src/iconolab/views/comments.py
src/iconolab/views/iconolab.py
--- a/src/iconolab/signals/handlers.py	Mon Aug 01 11:51:50 2016 +0200
+++ b/src/iconolab/signals/handlers.py	Mon Aug 01 12:59:18 2016 +0200
@@ -2,7 +2,6 @@
 from django.db.models.signals import post_save
 from django.dispatch import Signal, receiver
 
-from iconolab.models import Annotation, AnnotationRevision, IconolabComment
 
 # Signal sent during method Annotation.validate_existing_revision to update stats
 revision_accepted = Signal(providing_args=["instance"])
@@ -10,22 +9,25 @@
 
 
 def increment_stats_on_new_revision(sender, instance, **kwargs):
-    # Annotation stats
-    annotation = instance.annotation
-    annotation.stats.submitted_revisions_count += 1
-    if instance.state in [AnnotationRevision.ACCEPTED, AnnotationRevision.STUDIED]:
-        annotation.stats.accepted_revisions_count += 1
-    annotation.stats.set_tags_stats()
-    annotation.stats.save()
-    # Image stats
-    image = instance.annotation.image
-    image.stats.submitted_revisions_count += 1
-    image.stats.set_tags_stats()
-    image.stats.save()
+    from iconolab.models import AnnotationRevision
+    if sender == AnnotationRevision:
+        # Annotation stats
+        annotation = instance.annotation
+        annotation.stats.submitted_revisions_count += 1
+        if instance.state in [AnnotationRevision.ACCEPTED, AnnotationRevision.STUDIED]:
+            annotation.stats.accepted_revisions_count += 1
+        annotation.stats.set_tags_stats()
+        annotation.stats.save()
+        # Image stats
+        image = instance.annotation.image
+        image.stats.submitted_revisions_count += 1
+        image.stats.set_tags_stats()
+        image.stats.save()
 
     
 def increment_stats_on_new_comments(sender, instance, created, **kwargs):
-    if created:
+    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)
@@ -36,20 +38,23 @@
 
 
 def increment_accepted_revisions(sender, instance, **kwargs):
-    annotation = instance.annotation
-    annotation.stats.accepted_revisions_count += 1
-    annotation.stats.save()
+    from iconolab.models import AnnotationRevision
+    if sender == AnnotationRevision:
+        annotation = instance.annotation
+        annotation.stats.accepted_revisions_count += 1
+        annotation.stats.save()
 
 def increment_annotations_count(sender, instance, created, **kwargs):
-    if created:
+    from iconolab.models import Annotation
+    if created and sender == Annotation:
         image = instance.image
         image.stats.annotations_count += 1
         image.stats.submitted_revisions_count += 1
         image.stats.set_tags_stats()
         image.stats.save()
 
-post_save.connect(increment_annotations_count, sender=Annotation)
-post_save.connect(increment_stats_on_new_comments, sender=IconolabComment)
-revision_created.connect(increment_stats_on_new_revision, sender=AnnotationRevision)
-revision_accepted.connect(increment_accepted_revisions, sender=AnnotationRevision)
+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)
         
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/iconolab/templates/iconolab/user_page.html	Mon Aug 01 12:59:18 2016 +0200
@@ -0,0 +1,11 @@
+{% 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	Mon Aug 01 11:51:50 2016 +0200
+++ b/src/iconolab/templates/partials/header.html	Mon Aug 01 12:59:18 2016 +0200
@@ -25,7 +25,7 @@
 
       <ul class="nav navbar-nav navbar-right">
         {% if user.is_authenticated %}
-          <li><a href="#">{{user.username}}: Mon espace</a></li>
+          <li><a href="{% url 'user_profile' %}">{{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/urls.py	Mon Aug 01 11:51:50 2016 +0200
+++ b/src/iconolab/urls.py	Mon Aug 01 12:59:18 2016 +0200
@@ -16,7 +16,7 @@
 from django.core.urlresolvers import reverse_lazy
 from django.conf.urls import url, include
 from django.contrib import admin
-from . import views
+from iconolab import views
 from . import settings
 
 from django.conf.urls.static import static
@@ -24,22 +24,23 @@
 from django.contrib.staticfiles.urls import staticfiles_urlpatterns
 
 urlpatterns = [
-    url(r'^$', views.RedirectView.as_view(url=reverse_lazy("home"))),
+    url(r'^$', views.iconolab.RedirectView.as_view(url=reverse_lazy("home"))),
     url(r'^admin/', admin.site.urls),
-    url(r'^home$', views.GlobalHomepageView.as_view(), name="home"),
-    url(r'^collections/(?P<collection_name>[a-z]+)$', views.CollectionHomepageView.as_view(), name='collection_home'), # Home fond
-    url(r'^collections/(?P<collection_name>[a-z]+)/items/(?P<item_guid>[^/]+)$', views.ShowItemView.as_view(), name='item_detail'),
-    url(r'^collections/(?P<collection_name>[a-z]+)/images/(?P<image_guid>[^/]+)$', views.ShowImageView.as_view(), name='image_detail'),
-    url(r'^collections/(?P<collection_name>[a-z]+)/images/(?P<image_guid>[^/]+)/annotations/create$', login_required(views.CreateAnnotationView.as_view()), name='annotation_create'),
-    url(r'^collections/(?P<collection_name>[a-z]+)/images/(?P<image_guid>[^/]+)/annotations/(?P<annotation_guid>[^/]+)/detail$', views.ShowAnnotationView.as_view(), name='annotation_detail'),
-    url(r'^collections/(?P<collection_name>[a-z]+)/images/(?P<image_guid>[^/]+)/annotations/(?P<annotation_guid>[^/]+)/edit$', login_required(views.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.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.MergeProposalView.as_view()), name='annotation_merge'),
-    url(r'errors/404', views.NotFoundErrorView.as_view(), name="404error"),
+    url(r'^home$', views.iconolab.GlobalHomepageView.as_view(), name="home"),
+    url(r'^collections/(?P<collection_name>[a-z]+)$', views.iconolab.CollectionHomepageView.as_view(), name='collection_home'), # Home fond
+    url(r'^collections/(?P<collection_name>[a-z]+)/items/(?P<item_guid>[^/]+)$', views.iconolab.ShowItemView.as_view(), name='item_detail'),
+    url(r'^collections/(?P<collection_name>[a-z]+)/images/(?P<image_guid>[^/]+)$', views.iconolab.ShowImageView.as_view(), name='image_detail'),
+    url(r'^collections/(?P<collection_name>[a-z]+)/images/(?P<image_guid>[^/]+)/annotations/create$', login_required(views.iconolab.CreateAnnotationView.as_view()), name='annotation_create'),
+    url(r'^collections/(?P<collection_name>[a-z]+)/images/(?P<image_guid>[^/]+)/annotations/(?P<annotation_guid>[^/]+)/detail$', views.iconolab.ShowAnnotationView.as_view(), name='annotation_detail'),
+    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), name="user_profile"),
+    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.post_comment_iconolab, name="post_comment")
+    url(r'^comments/annotation/post', views.comments.post_comment_iconolab, name="post_comment")
 ]
 
 urlpatterns += staticfiles_urlpatterns()
--- a/src/iconolab/views.py	Mon Aug 01 11:51:50 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,596 +0,0 @@
-from django.apps import apps
-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.views.generic.base import ContextMixin
-from django.views.decorators.csrf import csrf_protect
-from django.views.decorators.http import require_POST
-from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
-from django.core.urlresolvers import reverse
-from django.core.exceptions import ObjectDoesNotExist, ValidationError
-from django.contrib.contenttypes.models import ContentType
-from django.contrib.sites.models import Site
-from django.conf import settings
-from iconolab.models import Annotation, AnnotationRevision, Collection, Item, Image, IconolabComment, MetaCategory, MetaCategoryInfo
-from iconolab.forms.annotations import AnnotationRevisionForm
-import datetime
-import django_comments
-from django_comments import signals
-from django_comments.views.utils import next_redirect, confirmation_view
-
-
-class GlobalHomepageView(View):
-    def get(self, request, *args, **kwargs):
-        context = {}
-        context["collections"] = Collection.objects
-        return render(request, 'iconolab/home.html', context)
-
-
-class CollectionHomepageView(View, ContextMixin):
-    def check_kwargs(self, kwargs):
-        try:
-            collection = Collection.objects.prefetch_related("items", "items__images").get(name=kwargs.get('collection_name'))
-        except Collection.DoesNotExist:
-            return False, RedirectView.as_view(url=reverse('404error'))
-        return True, (collection)
-    
-    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
-        return render(request, 'iconolab/collection_home.html', context)
-    
-
-
-class ShowItemView(View, ContextMixin):
-    
-    def check_kwargs(self, kwargs):
-        try:
-            collection = Collection.objects.prefetch_related("items", "items__images").get(name=kwargs.get('collection_name'))
-        except Collection.DoesNotExist:
-            return False, RedirectView.as_view(url=reverse('404error'))
-        try:
-            item = Item.objects.prefetch_related("images").get(item_guid=kwargs.get('item_guid'))
-        except Item.DoesNotExist:
-            return False, RedirectView.as_view(url=reverse('404error'))
-        return True, (collection, item)
-    
-    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)
-        context['collection_name'] = self.kwargs.get('collection_name', '')
-        context['item_guid'] = self.kwargs.get('image_guid', '')
-        context['collection'] = collection
-        context['item'] = item
-        for image in item.images.all():
-            image.stats.views_count += 1
-            image.stats.save()
-        return render(request, 'iconolab/detail_item.html', context);
-
-class ShowImageView(View, ContextMixin):
-    
-    def check_kwargs(self, kwargs):
-        try:
-            collection = Collection.objects.prefetch_related("items", "items__images").get(name=kwargs.get('collection_name'))
-        except Collection.DoesNotExist:
-            return False, RedirectView.as_view(url=reverse('404error'))
-        try:
-            image = Image.objects.prefetch_related("annotations").get(image_guid=kwargs.get('image_guid'))
-        except Image.DoesNotExist:
-            return False, RedirectView.as_view(url=reverse('404error'))
-        return True, (collection, image)
-    
-    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):
-    
-    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 check_kwargs(self, kwargs):
-        try:
-            collection = Collection.objects.prefetch_related("items", "items__images").get(name=kwargs.get('collection_name'))
-        except Collection.DoesNotExist:
-            return False, RedirectView.as_view(url=reverse('404error'))
-        try:
-            image = Image.objects.prefetch_related("annotations").get(image_guid=kwargs.get('image_guid'))
-        except Image.DoesNotExist:
-            return False, RedirectView.as_view(url=reverse('404error'))
-        return True, (collection, image)
-    
-    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'] = '[]'
-        render(request, 'iconolab/change_annotation.html', context)
-
-class ShowAnnotationView(View, ContextMixin):
-    
-    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 check_kwargs(self, kwargs):
-        try:
-            collection = Collection.objects.prefetch_related("items", "items__images").get(name=kwargs.get('collection_name'))
-        except Collection.DoesNotExist:
-            return False, RedirectView.as_view(url=reverse('404error'))
-        try:
-            image = Image.objects.prefetch_related("annotations").get(image_guid=kwargs.get('image_guid'))
-        except Image.DoesNotExist:
-            return False, RedirectView.as_view(url=reverse('404error'))
-        try:
-            annotation = Annotation.objects.select_related('current_revision').get(annotation_guid=kwargs.get('annotation_guid'))
-        except Annotation.DoesNotExist:
-            return False, RedirectView.as_view(url=reverse('404error'))
-        return True, (collection, image, annotation)
-    
-    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
-        
-        
-        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):
-    
-    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 check_kwargs(self, kwargs):
-        try:
-            collection = Collection.objects.prefetch_related("items", "items__images").get(name=kwargs.get('collection_name'))
-        except Collection.DoesNotExist:
-            return False, RedirectView.as_view(url=reverse('404error'))
-        try:
-            image = Image.objects.prefetch_related("annotations").get(image_guid=kwargs.get('image_guid'))
-        except Image.DoesNotExist:
-            return False, RedirectView.as_view(url=reverse('404error'))
-        try:
-            annotation = Annotation.objects.select_related('current_revision').get(annotation_guid=kwargs.get('annotation_guid'))
-        except Annotation.DoesNotExist:
-            return False, RedirectView.as_view(url=reverse('404error'))
-        return True, (collection, image, annotation)
-    
-    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):
-    
-    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 check_kwargs(self, kwargs):
-        try:
-            collection = Collection.objects.prefetch_related("items", "items__images").get(name=kwargs.get('collection_name'))
-        except Collection.DoesNotExist:
-            return False, RedirectView.as_view(url=reverse('404error'))
-        try:
-            image = Image.objects.prefetch_related("annotations").get(image_guid=kwargs.get('image_guid'))
-        except Image.DoesNotExist:
-            return False, RedirectView.as_view(url=reverse('404error'))
-        try:
-            annotation = Annotation.objects.select_related('current_revision').get(annotation_guid=kwargs.get('annotation_guid'))
-        except Annotation.DoesNotExist:
-            return False, RedirectView.as_view(url=reverse('404error'))
-        try:
-            revision = AnnotationRevision.objects.select_related('parent_revision').get(revision_guid=kwargs.get('revision_guid'))
-        except AnnotationRevision.DoesNotExist:
-            return False, RedirectView.as_view(url=reverse('404error'))
-        return True, (collection, image, annotation, revision)
-    
-    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()
-        return render(request, 'iconolab/detail_revision.html', context)
-
-        
-class MergeProposalView(View, ContextMixin):
-    
-    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 check_kwargs(self, kwargs):
-        try:
-            collection = Collection.objects.prefetch_related("items", "items__images").get(name=kwargs.get('collection_name'))
-        except Collection.DoesNotExist:
-            return False, RedirectView.as_view(url=reverse('404error'))
-        try:
-            image = Image.objects.prefetch_related("annotations").get(image_guid=kwargs.get('image_guid'))
-        except Image.DoesNotExist:
-            return False, RedirectView.as_view(url=reverse('404error'))
-        try:
-            annotation = Annotation.objects.select_related('current_revision').get(annotation_guid=kwargs.get('annotation_guid'))
-        except Annotation.DoesNotExist:
-            return False, RedirectView.as_view(url=reverse('404error'))
-        try:
-            revision = AnnotationRevision.objects.select_related('parent_revision').get(revision_guid=kwargs.get('revision_guid'))
-        except AnnotationRevision.DoesNotExist:
-            return False, RedirectView.as_view(url=reverse('404error'))
-        return True, (collection, image, annotation, revision)
-    
-    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)
-    
-    
-class NotFoundErrorView(View):
-    def get(self, request, *args, **kwargs):
-        # Handle image display here
-        pass
-    
-
-@csrf_protect
-@require_POST
-def post_comment_iconolab(request, next=None, using=None):
-    '''
-    Post a comment.
-    HTTP POST is required. If ``POST['submit'] == 'preview'`` or if there are
-    errors a preview template, ``comments/preview.html``, will be rendered.
-    '''
-    # Fill out some initial data fields from an authenticated user, if present
-    data = request.POST.copy()
-    if request.user.is_authenticated():
-        if not data.get('name', ''):
-            data['name'] = request.user.get_full_name() or request.user.get_username()
-        if not data.get('email', ''):
-            data['email'] = request.user.email
-
-    # Look up the object we're trying to comment about
-    ctype = data.get('content_type')
-    object_pk = data.get('object_pk')
-    if ctype is None or object_pk is None:
-        return CommentPostBadRequest('Missing content_type or object_pk field.')
-    try:
-        model = apps.get_model(*ctype.split('.', 1))
-        target = model._default_manager.using(using).get(pk=object_pk)
-    except TypeError:
-        return CommentPostBadRequest(
-            'Invalid content_type value: %r' % escape(ctype))
-    except AttributeError:
-        return CommentPostBadRequest(
-            'The given content-type %r does not resolve to a valid model.' % escape(ctype))
-    except ObjectDoesNotExist:
-        return CommentPostBadRequest(
-            'No object matching content-type %r and object PK %r exists.' % (
-                escape(ctype), escape(object_pk)))
-    except (ValueError, ValidationError) as e:
-        return CommentPostBadRequest(
-            'Attempting go get content-type %r and object PK %r exists raised %s' % (
-                escape(ctype), escape(object_pk), e.__class__.__name__))
-
-    # Do we want to preview the comment?
-    preview = 'preview' in data
-
-    # Construct the comment form
-    form = django_comments.get_form()(target, data=data)
-
-    # Check security information
-    if form.security_errors():
-        return CommentPostBadRequest(
-            'The comment form failed security verification: %s' % escape(str(form.security_errors())))
-
-    # If there are errors or if we requested a preview show the comment
-    if form.errors:
-        return render(request, 'iconolab/detail_annotation.html', {
-                'comment_form': form,
-                'next': data.get('next', next),
-                'annotation': target,
-                'annotation_guid': target.annotation_guid,
-                'image_guid': target.image.image_guid,
-                'collection_name': target.image.item.collection.name,
-                'tags_data': target.current_revision.get_tags_json()
-            },
-        )
-
-    # Otherwise create the comment
-    comment = form.get_comment_object()
-    comment.ip_address = request.META.get('REMOTE_ADDR', None)
-    if request.user.is_authenticated():
-        comment.user = request.user
-
-    # Signal that the comment is about to be saved
-    responses = signals.comment_will_be_posted.send(
-        sender=comment.__class__,
-        comment=comment,
-        request=request
-    )
-
-    for (receiver, response) in responses:
-        if response is False:
-            return CommentPostBadRequest(
-                'comment_will_be_posted receiver %r killed the comment' % receiver.__name__)
-
-    # Save the comment and signal that it was saved
-    comment.save()
-    
-    signals.comment_was_posted.send(
-        sender=comment.__class__,
-        comment=comment,
-        request=request
-    )
-    
-    # Creating metacategories here as apparently there is no way to make it work easily woth django_comments_xtd
-    for metacategory in form.cleaned_data.get("metacategories", []):
-        if 'xtd_comment' in comment:
-            metacategory_info = MetaCategoryInfo.objects.create(
-                comment = comment['xtd_comment'],
-                metacategory = metacategory
-            )
-
-    return next_redirect(request, fallback=next or 'comments-comment-done',
-                         c=comment._get_pk_val())
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/iconolab/views/__init__.py	Mon Aug 01 12:59:18 2016 +0200
@@ -0,0 +1,2 @@
+from . import comments
+from . import iconolab
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/iconolab/views/comments.py	Mon Aug 01 12:59:18 2016 +0200
@@ -0,0 +1,110 @@
+from django.apps import apps
+from django.views.decorators.csrf import csrf_protect
+from django.views.decorators.http import require_POST
+from django.core.exceptions import ObjectDoesNotExist, ValidationError
+import datetime
+import django_comments
+from django_comments import signals
+from django_comments.views.utils import next_redirect, confirmation_view
+from django_comments.views.comments import CommentPostBadRequest
+
+@csrf_protect
+@require_POST
+def post_comment_iconolab(request, next=None, using=None):
+    '''
+    Post a comment.
+    HTTP POST is required. If ``POST['submit'] == 'preview'`` or if there are
+    errors a preview template, ``comments/preview.html``, will be rendered.
+    '''
+    # Fill out some initial data fields from an authenticated user, if present
+    data = request.POST.copy()
+    if request.user.is_authenticated():
+        if not data.get('name', ''):
+            data['name'] = request.user.get_full_name() or request.user.get_username()
+        if not data.get('email', ''):
+            data['email'] = request.user.email
+
+    # Look up the object we're trying to comment about
+    ctype = data.get('content_type')
+    object_pk = data.get('object_pk')
+    if ctype is None or object_pk is None:
+        return CommentPostBadRequest('Missing content_type or object_pk field.')
+    try:
+        model = apps.get_model(*ctype.split('.', 1))
+        target = model._default_manager.using(using).get(pk=object_pk)
+    except TypeError:
+        return CommentPostBadRequest(
+            'Invalid content_type value: %r' % escape(ctype))
+    except AttributeError:
+        return CommentPostBadRequest(
+            'The given content-type %r does not resolve to a valid model.' % escape(ctype))
+    except ObjectDoesNotExist:
+        return CommentPostBadRequest(
+            'No object matching content-type %r and object PK %r exists.' % (
+                escape(ctype), escape(object_pk)))
+    except (ValueError, ValidationError) as e:
+        return CommentPostBadRequest(
+            'Attempting go get content-type %r and object PK %r exists raised %s' % (
+                escape(ctype), escape(object_pk), e.__class__.__name__))
+
+    # Do we want to preview the comment?
+    preview = 'preview' in data
+
+    # Construct the comment form
+    form = django_comments.get_form()(target, data=data)
+
+    # Check security information
+    if form.security_errors():
+        return CommentPostBadRequest(
+            'The comment form failed security verification: %s' % escape(str(form.security_errors())))
+
+    # If there are errors or if we requested a preview show the comment
+    if form.errors:
+        return render(request, 'iconolab/detail_annotation.html', {
+                'comment_form': form,
+                'next': data.get('next', next),
+                'annotation': target,
+                'annotation_guid': target.annotation_guid,
+                'image_guid': target.image.image_guid,
+                'collection_name': target.image.item.collection.name,
+                'tags_data': target.current_revision.get_tags_json()
+            },
+        )
+
+    # Otherwise create the comment
+    comment = form.get_comment_object()
+    comment.ip_address = request.META.get('REMOTE_ADDR', None)
+    if request.user.is_authenticated():
+        comment.user = request.user
+
+    # Signal that the comment is about to be saved
+    responses = signals.comment_will_be_posted.send(
+        sender=comment.__class__,
+        comment=comment,
+        request=request
+    )
+
+    for (receiver, response) in responses:
+        if response is False:
+            return CommentPostBadRequest(
+                'comment_will_be_posted receiver %r killed the comment' % receiver.__name__)
+
+    # Save the comment and signal that it was saved
+    comment.save()
+    
+    signals.comment_was_posted.send(
+        sender=comment.__class__,
+        comment=comment,
+        request=request
+    )
+    
+    # Creating metacategories here as apparently there is no way to make it work easily woth django_comments_xtd
+    for metacategory in form.cleaned_data.get("metacategories", []):
+        if 'xtd_comment' in comment:
+            metacategory_info = MetaCategoryInfo.objects.create(
+                comment = comment['xtd_comment'],
+                metacategory = metacategory
+            )
+
+    return next_redirect(request, fallback=next or 'comments-comment-done',
+                         c=comment._get_pk_val())
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/iconolab/views/iconolab.py	Mon Aug 01 12:59:18 2016 +0200
@@ -0,0 +1,493 @@
+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.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 iconolab.models import Annotation, AnnotationRevision, Collection, Item, Image, IconolabComment, MetaCategory, MetaCategoryInfo
+from iconolab.forms.annotations import AnnotationRevisionForm
+
+
+class GlobalHomepageView(View):
+    def get(self, request, *args, **kwargs):
+        context = {}
+        context["collections"] = Collection.objects
+        return render(request, 'iconolab/home.html', context)
+
+
+class UserProfileView(View):
+    def get(self, request, *args, **kwargs):
+        context = {}
+        return render(request, 'iconolab/user_profile.html', context)
+
+
+class CollectionHomepageView(View, ContextMixin):
+    def check_kwargs(self, kwargs):
+        try:
+            collection = Collection.objects.prefetch_related("items", "items__images").get(name=kwargs.get('collection_name'))
+        except Collection.DoesNotExist:
+            return False, RedirectView.as_view(url=reverse('404error'))
+        return True, (collection)
+    
+    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
+        return render(request, 'iconolab/collection_home.html', context)
+    
+
+
+class ShowItemView(View, ContextMixin):
+    
+    def check_kwargs(self, kwargs):
+        try:
+            collection = Collection.objects.prefetch_related("items", "items__images").get(name=kwargs.get('collection_name'))
+        except Collection.DoesNotExist:
+            return False, RedirectView.as_view(url=reverse('404error'))
+        try:
+            item = Item.objects.prefetch_related("images").get(item_guid=kwargs.get('item_guid'))
+        except Item.DoesNotExist:
+            return False, RedirectView.as_view(url=reverse('404error'))
+        return True, (collection, item)
+    
+    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)
+        context['collection_name'] = self.kwargs.get('collection_name', '')
+        context['item_guid'] = self.kwargs.get('image_guid', '')
+        context['collection'] = collection
+        context['item'] = item
+        for image in item.images.all():
+            image.stats.views_count += 1
+            image.stats.save()
+        return render(request, 'iconolab/detail_item.html', context);
+
+class ShowImageView(View, ContextMixin):
+    
+    def check_kwargs(self, kwargs):
+        try:
+            collection = Collection.objects.prefetch_related("items", "items__images").get(name=kwargs.get('collection_name'))
+        except Collection.DoesNotExist:
+            return False, RedirectView.as_view(url=reverse('404error'))
+        try:
+            image = Image.objects.prefetch_related("annotations").get(image_guid=kwargs.get('image_guid'))
+        except Image.DoesNotExist:
+            return False, RedirectView.as_view(url=reverse('404error'))
+        return True, (collection, image)
+    
+    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):
+    
+    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 check_kwargs(self, kwargs):
+        try:
+            collection = Collection.objects.prefetch_related("items", "items__images").get(name=kwargs.get('collection_name'))
+        except Collection.DoesNotExist:
+            return False, RedirectView.as_view(url=reverse('404error'))
+        try:
+            image = Image.objects.prefetch_related("annotations").get(image_guid=kwargs.get('image_guid'))
+        except Image.DoesNotExist:
+            return False, RedirectView.as_view(url=reverse('404error'))
+        return True, (collection, image)
+    
+    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'] = '[]'
+        render(request, 'iconolab/change_annotation.html', context)
+
+class ShowAnnotationView(View, ContextMixin):
+    
+    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 check_kwargs(self, kwargs):
+        try:
+            collection = Collection.objects.prefetch_related("items", "items__images").get(name=kwargs.get('collection_name'))
+        except Collection.DoesNotExist:
+            return False, RedirectView.as_view(url=reverse('404error'))
+        try:
+            image = Image.objects.prefetch_related("annotations").get(image_guid=kwargs.get('image_guid'))
+        except Image.DoesNotExist:
+            return False, RedirectView.as_view(url=reverse('404error'))
+        try:
+            annotation = Annotation.objects.select_related('current_revision').get(annotation_guid=kwargs.get('annotation_guid'))
+        except Annotation.DoesNotExist:
+            return False, RedirectView.as_view(url=reverse('404error'))
+        return True, (collection, image, annotation)
+    
+    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
+        
+        
+        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):
+    
+    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 check_kwargs(self, kwargs):
+        try:
+            collection = Collection.objects.prefetch_related("items", "items__images").get(name=kwargs.get('collection_name'))
+        except Collection.DoesNotExist:
+            return False, RedirectView.as_view(url=reverse('404error'))
+        try:
+            image = Image.objects.prefetch_related("annotations").get(image_guid=kwargs.get('image_guid'))
+        except Image.DoesNotExist:
+            return False, RedirectView.as_view(url=reverse('404error'))
+        try:
+            annotation = Annotation.objects.select_related('current_revision').get(annotation_guid=kwargs.get('annotation_guid'))
+        except Annotation.DoesNotExist:
+            return False, RedirectView.as_view(url=reverse('404error'))
+        return True, (collection, image, annotation)
+    
+    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):
+    
+    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 check_kwargs(self, kwargs):
+        try:
+            collection = Collection.objects.prefetch_related("items", "items__images").get(name=kwargs.get('collection_name'))
+        except Collection.DoesNotExist:
+            return False, RedirectView.as_view(url=reverse('404error'))
+        try:
+            image = Image.objects.prefetch_related("annotations").get(image_guid=kwargs.get('image_guid'))
+        except Image.DoesNotExist:
+            return False, RedirectView.as_view(url=reverse('404error'))
+        try:
+            annotation = Annotation.objects.select_related('current_revision').get(annotation_guid=kwargs.get('annotation_guid'))
+        except Annotation.DoesNotExist:
+            return False, RedirectView.as_view(url=reverse('404error'))
+        try:
+            revision = AnnotationRevision.objects.select_related('parent_revision').get(revision_guid=kwargs.get('revision_guid'))
+        except AnnotationRevision.DoesNotExist:
+            return False, RedirectView.as_view(url=reverse('404error'))
+        return True, (collection, image, annotation, revision)
+    
+    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()
+        return render(request, 'iconolab/detail_revision.html', context)
+
+        
+class MergeProposalView(View, ContextMixin):
+    
+    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 check_kwargs(self, kwargs):
+        try:
+            collection = Collection.objects.prefetch_related("items", "items__images").get(name=kwargs.get('collection_name'))
+        except Collection.DoesNotExist:
+            return False, RedirectView.as_view(url=reverse('404error'))
+        try:
+            image = Image.objects.prefetch_related("annotations").get(image_guid=kwargs.get('image_guid'))
+        except Image.DoesNotExist:
+            return False, RedirectView.as_view(url=reverse('404error'))
+        try:
+            annotation = Annotation.objects.select_related('current_revision').get(annotation_guid=kwargs.get('annotation_guid'))
+        except Annotation.DoesNotExist:
+            return False, RedirectView.as_view(url=reverse('404error'))
+        try:
+            revision = AnnotationRevision.objects.select_related('parent_revision').get(revision_guid=kwargs.get('revision_guid'))
+        except AnnotationRevision.DoesNotExist:
+            return False, RedirectView.as_view(url=reverse('404error'))
+        return True, (collection, image, annotation, revision)
+    
+    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)
+    
+    
+class NotFoundErrorView(View):
+    def get(self, request, *args, **kwargs):
+        # Handle image display here
+        pass
\ No newline at end of file