# HG changeset patch # User durandn # Date 1466512689 -7200 # Node ID 6b6b183447a2e0820ea553c470bec022562aee98 # Parent 713882b2bbb40e751df38180116c8189bde7735f work on models + auth register/login system + adapted existing app and templates so editing annotations is working as before + created empty templates to fill diff -r 713882b2bbb4 -r 6b6b183447a2 src/iconolab/admin.py --- a/src/iconolab/admin.py Mon Jun 20 15:05:23 2016 +0200 +++ b/src/iconolab/admin.py Tue Jun 21 14:38:09 2016 +0200 @@ -1,25 +1,1 @@ -from django.contrib import admin - -# Register your models here. -from iconolab.models import (Tag, Annotation, Collection, Image, - Comment, CommentAttachement, MetaCategory, Activity, Notification) - - -class CommentAttachmentInline(admin.TabularInline): - model = CommentAttachement - extra = 1 - - -class CommentAdmin(admin.ModelAdmin): - inlines = [CommentAttachmentInline] - - -#reversion -admin.site.register(Image) -admin.site.register(Collection) -admin.site.register(Tag) -admin.site.register(Annotation) -admin.site.register(MetaCategory) -admin.site.register(Comment, CommentAdmin) -admin.site.register(Activity) -admin.site.register(Notification) \ No newline at end of file +from django.contrib import admin \ No newline at end of file diff -r 713882b2bbb4 -r 6b6b183447a2 src/iconolab/auth/urls.py --- a/src/iconolab/auth/urls.py Mon Jun 20 15:05:23 2016 +0200 +++ b/src/iconolab/auth/urls.py Tue Jun 21 14:38:09 2016 +0200 @@ -1,13 +1,13 @@ # -*- coding: utf-8 -*- from django.conf.urls import url -from django.contrib.auth.views import login, password_change +from django.contrib.auth.views import login, logout, password_change from . import views urlpatterns = [ - url(r'^login/', login, name='login'), - url(r'^password/reset', password_change, name='password_reset'), - url(r'^register', login, name='register') - #url(r'^logout/', views.logout_view, name='logout'), + url(r'^register/$', views.RegisterView.as_view(), name='register'), + url(r'^login/$', views.LoginView.as_view(), name='login'), + url(r'^password/reset$', password_change, name='password_reset'), + url(r'^logout/', views.LogoutView.as_view(), name='logout'), #url(r'^password/reset', view) ] diff -r 713882b2bbb4 -r 6b6b183447a2 src/iconolab/auth/views.py --- a/src/iconolab/auth/views.py Mon Jun 20 15:05:23 2016 +0200 +++ b/src/iconolab/auth/views.py Tue Jun 21 14:38:09 2016 +0200 @@ -1,6 +1,14 @@ -from django.contrib.auth import authenticate, login -from django.shortcuts import redirect, render, HttpResponse +from django.utils.http import is_safe_url +from django.contrib.auth import authenticate, login, logout, get_user_model +from django.contrib.auth.forms import UserCreationForm +from django.shortcuts import redirect, render, HttpResponseRedirect +from django.core.urlresolvers import reverse_lazy +from django.contrib.auth.forms import AuthenticationForm +from django.views.generic import FormView +from django.views.generic.base import RedirectView +from django.views.generic.edit import CreateView +User = get_user_model() def login_form(request): pass @@ -9,4 +17,51 @@ return HttpResponse('show show a login form') def logout_view(request): - logout(request) \ No newline at end of file + logout(request) + + +class LoginView(FormView): + template_name = "registration/login.html" + form_class = AuthenticationForm + success_url = reverse_lazy('home') + redirect_field_name = "next" + + def get(self, request, *args, **kwargs): + if request.user.is_authenticated(): + return HttpResponseRedirect(self.success_url) + return super(LoginView, self).get(request, *args, **kwargs) + + def form_valid(self, form): + login(self.request, form.get_user()) + return super(LoginView, self).form_valid(form) + + def get_success_url(self): + redirect_to = self.request.GET.get(self.redirect_field_name) + if not is_safe_url(url=redirect_to, host=self.request.get_host()): + redirect_to = self.success_url + return redirect_to + +class LogoutView(RedirectView): + url = reverse_lazy("home") + + def get(self, request, *args, **kwargs): + logout(request) + return super(LogoutView, self).get(request, *args, **kwargs) + +class RegisterView(CreateView): + model = User + template_name = 'registration/register.html' + form_class = UserCreationForm + success_url = reverse_lazy("home") + + def post(self, request, *args, **kwargs): + self.object = None + 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) + else: + return self.form_invalid(form) \ No newline at end of file diff -r 713882b2bbb4 -r 6b6b183447a2 src/iconolab/forms/__init__.py --- a/src/iconolab/forms/__init__.py Mon Jun 20 15:05:23 2016 +0200 +++ b/src/iconolab/forms/__init__.py Tue Jun 21 14:38:09 2016 +0200 @@ -1,10 +1,10 @@ from django.forms import ModelForm, HiddenInput -from iconolab.models import Annotation +from iconolab.models import AnnotationRevision -class AnnotationForm(ModelForm): +class AnnotationRevisionForm(ModelForm): class Meta: - model = Annotation + model = AnnotationRevision fields = ('title', 'description', 'fragment',) widgets = { 'fragment': HiddenInput(), diff -r 713882b2bbb4 -r 6b6b183447a2 src/iconolab/forms/annotations.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/iconolab/forms/annotations.py Tue Jun 21 14:38:09 2016 +0200 @@ -0,0 +1,11 @@ +from django.forms import ModelForm, HiddenInput +from iconolab.models import AnnotationRevision + + +class AnnotationRevisionForm(ModelForm): + class Meta: + model = AnnotationRevision + fields = ('title', 'description', 'fragment',) + widgets = { + 'fragment': HiddenInput(), + } \ No newline at end of file diff -r 713882b2bbb4 -r 6b6b183447a2 src/iconolab/migrations/0001_initial.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/iconolab/migrations/0001_initial.py Tue Jun 21 14:38:09 2016 +0200 @@ -0,0 +1,155 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.7 on 2016-06-21 12:07 +from __future__ import unicode_literals + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import uuid + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Annotation', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('annotation_guid', models.UUIDField(default=uuid.uuid4, editable=False)), + ('created', models.DateTimeField(auto_now_add=True, null=True)), + ('author', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='AnnotationRevision', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('revision_guid', models.UUIDField(default=uuid.uuid4)), + ('title', models.CharField(max_length=255)), + ('description', models.TextField(null=True)), + ('fragment', models.TextField()), + ('state', models.IntegerField(choices=[(0, 'awaiting'), (1, 'accepted'), (2, 'rejected')], default=0)), + ('created', models.DateTimeField(auto_now_add=True, null=True)), + ('annotation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='revisions', to='iconolab.Annotation')), + ('author', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ('merge_parent_revision', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='reverse_merge_parent_revision', to='iconolab.AnnotationRevision')), + ('parent_revision', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='reverse_parent_revision', to='iconolab.AnnotationRevision')), + ], + ), + migrations.CreateModel( + name='AnnotationStats', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('submitted_revisions_count', models.IntegerField(blank=True, default=1, null=True)), + ('accepted_revisions_count', models.IntegerField(blank=True, default=1, null=True)), + ('contributors_count', models.IntegerField(blank=True, default=1, null=True)), + ('views_count', models.IntegerField(blank=True, default=0, null=True)), + ('comments_count', models.IntegerField(blank=True, default=0, null=True)), + ('tag_count', models.IntegerField(blank=True, default=0, null=True)), + ('annotation', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='stats', to='iconolab.Annotation')), + ], + ), + migrations.CreateModel( + name='Collection', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=50, unique=True)), + ('description', models.CharField(max_length=255)), + ], + ), + migrations.CreateModel( + name='Image', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=200)), + ('media', models.ImageField(height_field='height', upload_to='uploads/', width_field='width')), + ('image_ref', models.CharField(max_length=255, unique=True)), + ('height', models.IntegerField()), + ('width', models.IntegerField()), + ('created', models.DateTimeField(auto_now_add=True, null=True)), + ], + ), + migrations.CreateModel( + name='ImageStats', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('views_count', models.IntegerField(blank=True, default=0, null=True)), + ('annotations_count', models.IntegerField(blank=True, default=0, null=True)), + ('submitted_revisions_count', models.IntegerField(blank=True, default=0, null=True)), + ('comments_count', models.IntegerField(blank=True, default=0, null=True)), + ('folders_inclusion_count', models.IntegerField(blank=True, default=0, null=True)), + ('tag_count', models.IntegerField(blank=True, default=0, null=True)), + ('image', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='stats', to='iconolab.Image')), + ], + ), + migrations.CreateModel( + name='Item', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('collection', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='items', to='iconolab.Collection')), + ], + ), + migrations.CreateModel( + name='ItemMetadata', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('joconde_ref', models.CharField(max_length=20, unique=True)), + ('domain', models.CharField(max_length=255)), + ('title', models.CharField(max_length=255)), + ('description', models.CharField(max_length=255)), + ('item', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='metadatas', to='iconolab.Item')), + ], + ), + migrations.CreateModel( + name='Tag', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('type', models.IntegerField(choices=[(0, 'dbpedia'), (1, 'iconolab')])), + ('label', models.SlugField()), + ('link', models.URLField(blank=True, null=True)), + ('description', models.CharField(max_length=255)), + ('collection', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='iconolab.Collection')), + ], + ), + migrations.CreateModel( + name='TaggingInfo', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('accuracy', models.IntegerField()), + ('relevancy', models.IntegerField()), + ('revision', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='iconolab.AnnotationRevision')), + ('tag', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='iconolab.Tag')), + ], + ), + migrations.AddField( + model_name='image', + name='item', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='images', to='iconolab.Item'), + ), + migrations.AddField( + model_name='annotationrevision', + name='tags', + field=models.ManyToManyField(blank=True, null=True, through='iconolab.TaggingInfo', to='iconolab.Tag'), + ), + migrations.AddField( + model_name='annotation', + name='current_revision', + field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='current_for_annotation', to='iconolab.AnnotationRevision'), + ), + migrations.AddField( + model_name='annotation', + name='image', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='annotations', to='iconolab.Image'), + ), + migrations.AddField( + model_name='annotation', + name='source_revision', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='source_related_annotation', to='iconolab.AnnotationRevision'), + ), + ] diff -r 713882b2bbb4 -r 6b6b183447a2 src/iconolab/migrations/__init__.py diff -r 713882b2bbb4 -r 6b6b183447a2 src/iconolab/models.py --- a/src/iconolab/models.py Mon Jun 20 15:05:23 2016 +0200 +++ b/src/iconolab/models.py Tue Jun 21 14:38:09 2016 +0200 @@ -1,157 +1,295 @@ -from django.db import models +from django.db import models, transaction from django.contrib.auth.models import User from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.models import ContentType -from reversion import revisions as reversion -from reversion.models import Revision +import uuid class Tag(models.Model): + DBPEDIA = 0 + ICONOLAB = 1 + TAG_TYPES = ( + (DBPEDIA, 'dbpedia'), + (ICONOLAB, 'iconolab') + ) + + type = models.IntegerField(choices=TAG_TYPES) label = models.SlugField() - link = models.URLField() - description = models.TextField() + link = models.URLField(blank=True, null=True) + description = models.CharField(max_length=255) + collection = models.ForeignKey('Collection', blank=True, null=True) - def __str__(self): - return '%d:%s' % (self.id, self.label) - +class TaggingInfo(models.Model): + revision = models.ForeignKey('AnnotationRevision', on_delete=models.CASCADE) + tag = models.ForeignKey('Tag', on_delete=models.CASCADE) + accuracy = models.IntegerField() + relevancy = models.IntegerField() + # fonds Ingres - Musee de la Poste class Collection(models.Model): - name = models.CharField(max_length=50) + name = models.CharField(max_length=50, unique=True) description = models.CharField(max_length=255) def __str__(self): return self.name -# class image_metadata + +class ItemMetadata(models.Model): + item = models.OneToOneField('Item', related_name='metadatas') + joconde_ref = models.CharField(max_length=20, null=False, blank=False, unique=True) + domain = models.CharField(max_length=255) + title = models.CharField(max_length=255) + description = models.CharField(max_length=255) + +class Item(models.Model): + collection = models.ForeignKey(Collection, related_name="items") + +class ImageStats(models.Model): + image = models.OneToOneField('Image', related_name='stats', blank=False, null=False) + views_count = models.IntegerField(blank=True, null=True, default=0) + annotations_count = models.IntegerField(blank=True, null=True, default=0) + submitted_revisions_count = models.IntegerField(blank=True, null=True, default=0) + comments_count = models.IntegerField(blank=True, null=True, default=0) + folders_inclusion_count = models.IntegerField(blank=True, null=True, default=0) + tag_count = models.IntegerField(blank=True, null=True, default=0) + class Image(models.Model): name = models.CharField(max_length=200) media = models.ImageField(upload_to='uploads/', height_field='height', width_field='width') + image_ref = models.CharField(max_length=255, unique=True) + item = models.ForeignKey('Item', related_name='images', null=True, blank=True) height = models.IntegerField(null=False, blank=False) width = models.IntegerField(null=False, blank=False) - mimetype = models.CharField(null=True, blank=True, max_length=1024) - exif = models.TextField(null=True, blank=True) - collection = models.ForeignKey(Collection) - date_created = models.DateTimeField(auto_now_add=True, null=True) - date_modified = models.DateTimeField(auto_now=True, null=True) + created = models.DateTimeField(auto_now_add=True, null=True) def __str__(self): return self.name -# Folders -class Folder(models.Model): - label = models.CharField(max_length=255) - owner = models.ForeignKey(User) - images = models.ManyToManyField(Image) +# # Folders +# class Folder(models.Model): +# label = models.CharField(max_length=255) +# owner = models.ForeignKey(User) +# images = models.ManyToManyField(Image) +# +# def __str__(self): +# return label + +class AnnotationManager(models.Manager): + + # Call Annotation.objects.create_annotation to initialize a new Annotation with its associated AnnotationStats and initial AnnotationRevision + @transaction.atomic + def create_annotation(self, image, author, title='', description='', fragment='', tags=None): + # Create annotation object + new_annotation = Annotation( + image=image, + author=author + ) + new_annotation.save() + + # Create initial revision + initial_revision = AnnotationRevision( + annotation=new_annotation, + author=author, + title=title, + description=description, + fragment=fragment, + state=AnnotationRevision.ACCEPTED + ) + initial_revision.save() + new_annotation.current_revision = initial_revision + new_annotation.save() + + # Create stats object + new_annotation_stats = AnnotationStats(annotation=new_annotation) + new_annotation_stats.save() + new_annotation.stats = new_annotation_stats + new_annotation.save() + +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) + 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) + comments_count = models.IntegerField(blank=True, null=True, default=0) + tag_count = models.IntegerField(blank=True, null=True, default=0) + + def contributors(self): + # returns users that submitted an accepted revision + return + - def __str__(self): - return label +class Annotation(models.Model): + annotation_guid = models.UUIDField(default=uuid.uuid4, editable=False) + image = models.ForeignKey('Image', related_name='annotations', on_delete=models.CASCADE) + source_revision = models.ForeignKey('AnnotationRevision', related_name='source_related_annotation', blank=True, null=True) + current_revision = models.OneToOneField('AnnotationRevision', related_name='current_for_annotation', blank=True, null=True) + author = models.ForeignKey(User, null=True) + created = models.DateTimeField(auto_now_add=True, null=True) + + objects = AnnotationManager() + + def update_stats(self): + pass + + # Call to create a new revision, possibly from a merge + @transaction.atomic + def make_new_revision(self, author, title, description, fragment, tags): + if author == self.author: + # We're creating an automatically accepted revision + new_revision_state = AnnotationRevision.ACCEPTED + else: + # Revision will require validation + new_revision_state = AnnotationRevision.AWAITING + new_revision = AnnotationRevision( + annotation = self, + parent_revision=self.current_revision, + title=title, + description=description, + author=author, + fragment=fragment, + state=new_revision_state + ) + new_revision.save() + if new_revision.state == AnnotationRevision.ACCEPTED: + self.current_revision = new_revision + self.save() + return new_revision + + # Call when we're validating an awaiting revision whose parent is the current revision AS IT WAS CREATED + @transaction.atomic + def validate_existing_revision(self, revision_to_validate): + if revision_to_validate.parent_revision == current_revision: + self.current_revision = revision_to_validate + revision_to_validate.state = AnnotationRevision.ACCEPTED + revision_to_validate.save() + self.save() + + # Call when we're validating an awaiting revision whose parent isn't the current revision OR IF IT WAS CHANGED BY THE ANNOTATION AUTHOR + @transaction.atomic + def merge_existing_revision(self, title, description, fragment, tags, revision_to_merge): + merged_revision = self.make_new_revision(author=self.author, title=title, description=description, fragment=fragment, tags=tags) + merged_revision.merge_parent_revision = revision_to_merge + merged_revision.save() + self.current_revision=merged_revision + self.save() -@reversion.register() -class Annotation(models.Model): +class AnnotationRevision(models.Model): + + AWAITING = 0 + ACCEPTED = 1 + REJECTED = 2 + + REVISION_STATES = ( + (AWAITING, 'awaiting'), + (ACCEPTED, 'accepted'), + (REJECTED, 'rejected') + ) + + + revision_guid = models.UUIDField(default=uuid.uuid4) + annotation = models.ForeignKey('Annotation', related_name='revisions', null=False, blank=False) + parent_revision = models.ForeignKey('AnnotationRevision', related_name='reverse_parent_revision', blank=True, null=True) + merge_parent_revision = models.ForeignKey('AnnotationRevision', related_name='reverse_merge_parent_revision', blank=True, null=True) + author = models.ForeignKey(User, null=True) title = models.CharField(max_length=255) description = models.TextField(null=True) fragment = models.TextField() # path string - tags = models.ManyToManyField(Tag) - image = models.ForeignKey(Image, default=0, on_delete=models.CASCADE) - author = models.ForeignKey(User, null=True) - date_created = models.DateTimeField(auto_now_add=True, null=True) - date_modified = models.DateTimeField(auto_now=True, null=True) - - def __str__(self): - return self.title - -# comments - -LINK = 1 -IMAGE = 2 -PDF = 3 - -COMMENT_CHOICES = ( - (LINK, 'link'), - (IMAGE, 'image'), - (PDF, 'pdf') - ) - - -class MetaCategory(models.Model): - collection = models.ForeignKey(Collection) - label = models.CharField(max_length=200) - - def __str__(self): - return self.label - - class Meta: - verbose_name_plural = "Metacategories" - -class Comment(models.Model): - author = models.ForeignKey(User) - created_date = models.DateTimeField(blank=False, null=False, auto_now_add=True) - annotation = models.ForeignKey(Annotation, null=True) # revision de l'utilisateur? - target = models.ForeignKey("self") # - #thread_id #django-contrib-comment #threadedcomment - content = models.TextField(blank=True) - metacategories = models.ManyToManyField(MetaCategory) - - -class CommentAttachement(models.Model): - comment = models.ForeignKey(Comment, on_delete=models.CASCADE, related_name='attachments') - main_annotation = models.ForeignKey(Annotation) - attachment_type = models.IntegerField(choices=COMMENT_CHOICES, default=1) - created_date = models.DateTimeField(auto_now_add=True) - data = models.TextField(blank=False) + tags = models.ManyToManyField('Tag', through='TaggingInfo', through_fields=('revision', 'tag'), blank=True, null=True) + state = models.IntegerField(choices=REVISION_STATES, default=AWAITING) + created = models.DateTimeField(auto_now_add=True, null=True) -# Activity & Notification - -class Activity(models.Model): - - NEW_COMMENT = 1 - NEW_REVISION = 2 - NEW_REVISION_COMMENT = 3 - NEW_EXPERT_ANSWER = 4 - NEW_REFERENCE = 5 - EXPERT_CALL = 6 - - ACTIVITY_VERBS =( - (NEW_COMMENT, 'Nouveau commentaire'), - (NEW_REVISION, 'Nouvelle revision'), - (NEW_REVISION_COMMENT, 'Nouveau commentaire de revision'), - (NEW_EXPERT_ANSWER, 'New expert ans') - ) - - verb = models.IntegerField(choices=ACTIVITY_VERBS) - actor = models.ForeignKey(User) - - target_content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) - target_object_id = models.PositiveIntegerField() - target = GenericForeignKey('target_content_type', 'target_object_id') +#class MetaCategory(models.Model): +# collection = models.ForeignKey(Collection) +# label = models.CharField(max_length=200) +# +# def __str__(self): +# return self.label +# +# class Meta: +# verbose_name_plural = 'Metacategories' +# +# +# class Comment(models.Model): +# author = models.ForeignKey(User) +# created = models.DateTimeField(blank=False, null=False, auto_now_add=True) +# annotation_revision = models.ForeignKey(AnnotationRevision, blank=True, null=True) +# target = models.ForeignKey('Comment', blank=True, null=True) +# content = models.TextField(blank=True) +# metacategories = models.ManyToManyField(MetaCategory) +# +# +# class CommentAttachement(models.Model): +# +# LINK = 0 +# IMAGE = 1 +# PDF = 2 +# COMMENT_CHOICES = ( +# (LINK, 'link'), +# (IMAGE, 'image'), +# (PDF, 'pdf') +# ) +# comment = models.ForeignKey(Comment, on_delete=models.CASCADE, related_name='attachments') +# main_annotation = models.ForeignKey(Annotation) +# attachment_type = models.IntegerField(choices=COMMENT_CHOICES, default=0) +# created_date = models.DateTimeField(auto_now_add=True) +# data = models.TextField(blank=False) +# +# +# # Activity & Notification +# +# class Activity(models.Model): +# +# NEW_COMMENT = 0 +# NEW_REVISION = 1 +# NEW_COMMENT_ON_REVISION = 2 +# NEW_EXPERT_CALL = 3 +# NEW_EXPERT_ANSWER = 4 +# NEW_REFERENCE = 5 +# +# ACTIVITY_VERBS = ( +# (NEW_COMMENT, 'New comment'), +# (NEW_REVISION, 'New revision'), +# (NEW_COMMENT_ON_REVISION, 'New comment on a revision'), +# (NEW_EXPERT_CALL, 'New expert call'), +# (NEW_EXPERT_ANSWER, 'New expert answer'), +# (NEW_REFERENCE, 'New reference'), +# ) +# +# verb = models.IntegerField(choices=ACTIVITY_VERBS) +# actor = models.ForeignKey(User) +# +# target_content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) +# target_object_id = models.PositiveIntegerField() +# target = GenericForeignKey('target_content_type', 'target_object_id') +# +# action_content_type = models.ForeignKey(ContentType, related_name='activity_action', on_delete=models.CASCADE, null=True, blank=True) +# action_object_id = models.PositiveIntegerField(null=True, blank=True) +# action_content = GenericForeignKey('action_content_type', 'action_object_id') +# +# created_date = models.DateTimeField(auto_now_add=True) +# +# def __str__(self): +# return '%s:%s' % (author.name, verbe) +# +# +# class Notification(models.Model): +# +# UNREAD = 0 +# READ = 1 +# DELETED = 2 +# +# STATUS = ( +# (UNREAD, 'Unread'), +# (READ, 'Read'), +# (DELETED, 'Deleted') +# ) +# +# activity = models.ForeignKey(Activity) +# user = models.ForeignKey(User) +# status = models.IntegerField(choices=STATUS, default=UNREAD) +# created_date = models.DateTimeField(auto_now_add=True) - action_content_type = models.ForeignKey(ContentType, related_name='activity_action', on_delete=models.CASCADE, null=True, blank=True) - action_object_id = models.PositiveIntegerField(null=True, blank=True) - action_content = GenericForeignKey('action_content_type', 'action_object_id') - - created_date = models.DateTimeField(auto_now_add=True) - - def __str__(self): - return '%s:%s' % (author.name, verbe) - - -class Notification(models.Model): - - UNREAD = 0 - READ = 1 - DELETED = 2 - - STATUS = ( - (READ, 'Lu'), - (UNREAD, 'Non lu'), - (DELETED, 'Efface') - ) - - activity = models.ForeignKey(Activity) - user = models.ForeignKey(User) - status = models.IntegerField(choices=STATUS, default=UNREAD) - created_date = models.DateTimeField(auto_now_add=True) - diff -r 713882b2bbb4 -r 6b6b183447a2 src/iconolab/settings/__init__.py --- a/src/iconolab/settings/__init__.py Mon Jun 20 15:05:23 2016 +0200 +++ b/src/iconolab/settings/__init__.py Tue Jun 21 14:38:09 2016 +0200 @@ -71,7 +71,6 @@ 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', - 'reversion.middleware.RevisionMiddleware', ] diff -r 713882b2bbb4 -r 6b6b183447a2 src/iconolab/signals/handlers.py --- a/src/iconolab/signals/handlers.py Mon Jun 20 15:05:23 2016 +0200 +++ b/src/iconolab/signals/handlers.py Tue Jun 21 14:38:09 2016 +0200 @@ -1,32 +0,0 @@ -from iconolab.models import Comment, Annotation -from django.db.models import signals -from django.dispatch import receiver -from pprint import pprint -from notifications.signals import notify -from iconolab.utils.utils import NotificationManager -import pprint - - -#handle metacategories here -''' - -''' - -@receiver(signals.post_save, sender=Comment) -def handle_metacategory(sender, **kwargs): - commentInstance = kwargs.get('instance') - if commentInstance is not None: - metacategories_list = [mcat.label for mcat in commentInstance.metacategories.all()] - print(", ".join(metacategories_list)) - - -@receiver(signals.post_save, sender=Annotation) -def handle_new_annotation(sender, **kwargs): - object_instance = kwargs.get('instance') - if object_instance is None: - pass - - notification = NotificationManager.create_notification(object_instance.author, verb=NotificationManager.NEW_ANNOTATION) - NotificationManager.notify(notification=notification) - - diff -r 713882b2bbb4 -r 6b6b183447a2 src/iconolab/templates/iconolab/annotation.html --- a/src/iconolab/templates/iconolab/annotation.html Mon Jun 20 15:05:23 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,54 +0,0 @@ -{% extends 'iconolab_base.html' %} - -{% load staticfiles %} - -{% load thumbnail %} - - -{% block page_js %} - -{% endblock%} - -{% block content %} -
No image
- {% endthumbnail %} -Test iri
-Type de dessin
+Actions
+ +Afficher le formulaire de création de fragment
-Title: {{ annotation.current_revision.title }}
+Description: {{ annotation.current_revision.description }}
+Tags: {{ annotation.current_revision.tags }}
+Fragment: {{ annotation.current_revision.fragment }}
+ + Editer | + Proposer une révision +Type de dessin
-Actions
- -Title: {{ annotation.title }}
-Description: {{ annotation.description }}
-Tags: {{ annotation.tags }}
-Fragment: {{ annotation.fragment }}
- - Editer | - Proposer une révision -Home
"); +class GlobalHomepageView(View): + def get(self, request, *args, **kwargs): + # Handle homepage view here + return render(request, 'iconolab/home.html'); + + +class CollectionHomepageView(View, ContextMixin): + def get(self, request, *args, **kwargs): + context = super(CollectionHomepageView, self).get_context_data(**kwargs) + context["collection_name"] = self.kwargs.get("collection_name", "") + return render(request, 'iconolab/collection_home.html', context); + + +class ShowImageView(View): + def get(self, request, *args, **kwargs): + context = super(CollectionHomepageView, self).get_context_data(**kwargs) + context["collection_name"] = self.kwargs.get("collection_name", "") + context["image_ref"] = self.kwargs.get("image_ref", "") + return render(request, 'iconolab/collection_home.html', context); -def draw_fragment(request, pk): - annotation = get_object_or_404(Annotation, pk=pk) - return render(request, 'iconolab/fragment_draw.html', {'annotation': annotation}) +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_ref"] = self.kwargs.get("image_ref", "") + context["annotation_guid"] = self.kwargs.get("annotation_guid", "") + return context + + def check_kwargs(self, kwargs): + try: + collection = Collection.objects.get(name=kwargs.get("collection_name", "")) + except Collection.DoesNotExist: + return RedirectView.as_view(url=reverse("404error")) + try: + annotation = Annotation.objects.get(annotation_guid=kwargs.get("annotation_guid", "")) + except Annotation.DoesNotExist: + return RedirectView.as_view(url=reverse("404error")) + return collection, annotation + + def get(self, request, *args, **kwargs): + collection, annotation = self.check_kwargs(kwargs) + self.check_kwargs(kwargs) + context = self.get_context_data(**kwargs) + context["annotation"] = annotation + print(annotation) + return render(request, 'iconolab/detail_annotation.html', context) -def create_annotation(request): - return render(request, 'iconolab/create_annotation.html') - pass - -def show_annotation(request, pk): - annotation = get_object_or_404(Annotation, pk=pk) - return render(request, 'iconolab/show_annotation.html', {'annotation': annotation}) + +class CreateAnnotationView(View): + + def get(self, request, *args, **kwargs): + print("test") + return render(request, 'iconolab/change_annotation.html') + + def post(self, request, *args, **kwargs): + # Handle annotation submit here + pass + +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_ref"] = self.kwargs.get("image_ref", "") + context["annotation_guid"] = self.kwargs.get("annotation_guid", "") + return context + + def check_kwargs(self, kwargs): + try: + collection = Collection.objects.get(name=kwargs.get("collection_name", "")) + except Collection.DoesNotExist: + return RedirectView.as_view(url=reverse("404error")) + try: + annotation = Annotation.objects.get(annotation_guid=kwargs.get("annotation_guid", "")) + except Annotation.DoesNotExist: + return RedirectView.as_view(url=reverse("404error")) + return collection, annotation + + def get(self, request, *args, **kwargs): + collection, annotation = self.check_kwargs(kwargs) + annotation_form = AnnotationRevisionForm() + context = self.get_context_data(**kwargs) + context["annotation"] = annotation + context["form"] = annotation_form + return render(request, 'iconolab/change_annotation.html', context) + + def post(self, request, *args, **kwargs): + collection, annotation = self.check_kwargs(kwargs) + collection_name = kwargs["collection_name"] + image_ref = kwargs["image_ref"] + 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"] + annotation.make_new_revision(revision_author, revision_title, revision_description, revision_fragment, None) + return RedirectView.as_view(url=reverse("annotation_detail", kwargs={'collection_name': collection_name, 'image_ref': image_ref, 'annotation_guid': annotation_guid}))(request) -# save annotation -# handle revision -def save_annotation(request): - pass - -def edit_annotation(request, pk): - - #enregistrer title, description, fragment:nmz - annotation = get_object_or_404(Annotation, pk=pk) - - if request.method == 'POST': - annotation_form = AnnotationForm(request.POST, instance=annotation) - if annotation_form.is_valid(): - annotation = annotation_form.save() - return redirect('iconolab.views.show_annotation', pk=pk) - else: - Annotation_form = AnnotationForm(instance = annotation) - - return render(request, 'iconolab/edit_annotation.html', {'annotation': annotation, 'form': Annotation_form }) - \ No newline at end of file +class MergeProposalView(View): + + def get(self, request, *args, **kwargs): + # Handle merge form display here + pass + + def post(self, request, *args, **kwargs): + # Handle merge form submit here + pass + + +class NotFoundErrorView(View): + def get(self, request, *args, **kwargs): + # Handle image display here + pass \ No newline at end of file