work on models + auth register/login system + adapted existing app and templates so editing annotations is working as before + created empty templates to fill
--- 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
--- 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)
]
--- 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
--- 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(),
--- /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
--- /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'),
+ ),
+ ]
--- 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)
-
--- 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',
]
--- 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)
-
-
--- 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 %}
- <script src="{% static 'iconolab/js/dist/bundle.js' %}" type="text/javascript"></script>
-{% endblock%}
-
-{% block content %}
- <div>
-
- <div class="col-md-4">
- <div id="fragmentContainer">
- {% thumbnail annotation.image.media "100x100" crop="center" as im %}
- <img src=" {{ im.url }}" width="{{ im.width }}" height="{{ im.height }}">
- {% empty %}
- <p>No image</p>
- {% endthumbnail %}
- </div>
- </div>
-
- <div class="col-md-4">
-
- <div id='iconolab-image' style='position:relative'>
- <img src="{% static annotation.image.media.url %}"
- width="{{ annotation.image.width }}"
- height="{{ annotation.image.height }}" />
- <!-- deal with path here -->
- <svg class='cut-canvas' style='border: 1px solid red;'></svg>
- <path class='image-path' d='M150 0 L75 200 L225 200 Z' />
- </div>
-
- <div sytle='border: 1px solid red; width: 300px; height:500px'>
-
- {% thumbnail annotation.image.media "100x100" crop="center" as im %}
- <img src="{{ im.url }}" width="{{ im.width }}" height="{{ im.height }}">
- {% endthumbnail %}
-
- <p>Test iri</p>
- </div>
-
- </div>
-
- </div>
-{% endblock %}
-
-{% block footer_js %}
- <script>
- iconolab.initCutoutComponent({ imageId: '#iconolab-image' });
- </script>
-{% endblock %}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/iconolab/templates/iconolab/change_annotation.html Tue Jun 21 14:38:09 2016 +0200
@@ -0,0 +1,173 @@
+{% extends 'iconolab_base.html' %}
+
+{% load staticfiles %}
+
+{% load thumbnail %}
+
+{% block content %}
+
+ <div id="drawing-zone" class="row" style="padding-top: 10px; border:1px solid orange">
+
+ <div v-show='!formView' class="editor-wrapper col-md-12">
+ <div class='col-md-2'>
+ <ul class="form-drawing-wrapper list-inline">
+ <p class='form-drawing pullright'><strong>Type de dessin</strong></p>
+ <li @click="setDrawingMode('RECT')" v-bind:class="{ 'selected': isRect }" class='pull-md-right drawingModeBtn'>Rect.</li>
+ <li @click="setDrawingMode('FREE')" v-bind:class="{ 'selected': !isRect }" class='pull-md-right drawingModeBtn'>Libre</li>
+ </ul>
+
+ <ul class='form-drawing-wrapper list-inline'>
+ <p class='form-drawing pullright'><strong>Actions</strong></p>
+
+ <li @click="clear" class='pull-md-right drawingModeBtn'><i class='fa fa-trash'></i> Effacer</li>
+
+ <li @click="save" class='pull-md-right drawingModeBtn'><i class='fa fa-plus'></i> Créer le fragment</li>
+ </ul>
+ </div>
+
+ <div class="col-md-8">
+ <div v-el:image id="iconolab-image-wrapper">
+ {% thumbnail annotation.image.media "x800" crop="center" as im %}
+ <img src="{{ im.url }}" width="{{ im.width }}" height="{{ im.height }}">
+ <path class="image-path" d="{{ annotation.current_revision.fragment }}"></path>
+ {% endthumbnail %}
+ </div>
+ </div>
+
+ </div>
+
+ <div v-show="formView" class="col-md-12">
+
+ <div class="col-xs-6">
+ <div class="small-image-wrapper" style="position: relative">
+ {% thumbnail annotation.image.media "x300" crop="center" as im %}
+ <img v-el:small-image @click="showEditor" src="{{ im.url }}" width="{{ im.width }}" height="{{ im.height }}">
+
+ <svg width="{{ im.width }}" height="{{ im.height }}" version="1.1" style="position:absolute; top:0px; left: 0px">
+
+ <defs>
+ <mask xmlns="http://www.w3.org/2000/svg" id="smallImage">
+ <rect x="0" y="0" width="{{ im.width }}", height="{{ im.height }}" fill="white"/>
+ <g v-bind:transform="transformMatrix">
+ <path v-bind:d="fragmentPath"></path>
+ </g>
+ </mask>
+ </defs>
+
+ <g v-show="!displayMask" v-bind:transform="transformMatrix">
+ <path v-bind:d="fragmentPath" opacity="0.7" fill="orange"></path>
+ </g>
+
+ <rect v-show="displayMask" v-el:small-mask x="0" y="0" mask="url(#smallImage)" opacity="0.7" fill="white" width="{{ im.width }}" height="{{ im.height }}"></rect>
+ </svg>
+
+ {% endthumbnail %}
+ </div>
+ <ul class="inline">
+ <li @click="showEditor" class="showPointer"> <i class='fa fa-edit'></i> Editer le fragment</li>
+ <li v-show="!displayMask" @click="highLightZone" class="showPointer"> <i class='fa fa-eye-slash'></i> Afficher la zone</li>
+ <li v-show="displayMask" @click="highLightZone" class="showPointer"> <i class='fa fa-eye-slash'></i> Masquer la zone</li>
+ </ul>
+ </div>
+
+ <div class='col-xs-6' style="">
+ <form method="POST" class="post-form">
+ {% csrf_token %}
+ {{ form.as_p }}
+ <input v-model="normalizePath" type="hidden" name="fragment"></input>
+ <button type="submit" class="save btn btn-default">Enregister</button>
+ </form>
+ </div>
+
+ </div>
+ </div>
+
+{% endblock %}
+
+{% block footer_js %}
+ <script>
+ var drawingVue = new Vue({
+ el: '#drawing-zone',
+ MODE_RECT: 'RECT',
+ MODE_FREE: 'FREE',
+
+ data: {
+ mode:"",
+ isRect: true,
+ normalizePath: "",
+ readOnly: false,
+ formView: false,
+ useClipPath: false,
+ transformMatrix: "",
+ fragmentPath: "",
+ displayMask: false
+
+ },
+
+ init: function () {
+ var self = this;
+ this.drawingComponent = iconolab.initCutoutComponent({
+ wrapperId: '#iconolab-image-wrapper',
+ actionWrapper: '#action-wrapper',
+ readOnly: false,
+ onDrawingModeChange: function (mode) {
+ self.$nextTick(function () {
+ self.setDrawingMode(mode, false);
+ });
+ }
+ });
+ },
+
+ methods: {
+
+ setDrawingMode: function (mode, updateComponent) {
+ var updateComponent = (typeof updateComponent === "boolean") ? updateComponent: true;
+ this.mode = this.$options['MODE_' + mode];
+ this.isRect = (this.mode === this.$options.MODE_RECT) ? true: false;
+ if (updateComponent) {
+ this.drawingComponent.setDrawingMode(this.mode);
+ }
+ },
+
+ showEditor: function () {
+ this.formView = false;
+ console.log(this.$els.smallImage);
+ },
+
+ highLightZone: function () {
+ if (!this.displayMask) {
+ this.displayMask = true;
+ }
+ else {
+ this.displayMask = false;
+ }
+ //this.maskFill = "orange";
+ //this.fragmentFill = "none";
+ },
+
+ displayEditedPath: function () {
+ /* path to save */
+ var normalizePath = this.drawingComponent.getPath();
+ },
+
+ save: function () {
+ this.normalizePath = this.drawingComponent.getPath();
+ var smallImage = this.$els.smallImage;
+ this.formView = true;
+ /* 100x = smallImageHeight && 100x=smallImageWidth | 100 = ViewBoxBound */
+ var xRatio = smallImage.width / 100;
+ var yRatio = smallImage.height / 100;
+ var transformMatrix = [xRatio, 0, 0, yRatio, 0, 0].join(',');
+ this.transformMatrix ="matrix(" + transformMatrix + ")";
+ this.fragmentPath = this.normalizePath.split(';')[0];
+ console.log(this.fragmentPath);
+ console.log(this.transformMatrix);
+ },
+
+ clear: function () {
+ this.drawingComponent.clear();
+ }
+ }
+ });
+ </script>
+{% endblock %}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/iconolab/templates/iconolab/collection_home.html Tue Jun 21 14:38:09 2016 +0200
@@ -0,0 +1,9 @@
+{% extends 'iconolab_base.html' %}
+
+{% load staticfiles %}
+
+{% load thumbnail %}
+
+{% load iconolab_tags %}
+
+{% block content %}collectionhome{% endblock %}
\ No newline at end of file
--- a/src/iconolab/templates/iconolab/create_annotation.html Mon Jun 20 15:05:23 2016 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,10 +0,0 @@
-{% extends 'iconolab_base.html'%}
-
-{% block content %}
-
-<h2>Créer un annotation</h2>
-<div class='col-md-8'>
- <p> Afficher le formulaire de création de fragment </p>
-</div>
-
-{% endblock %}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/iconolab/templates/iconolab/detail_annotation.html Tue Jun 21 14:38:09 2016 +0200
@@ -0,0 +1,42 @@
+{% extends 'iconolab_base.html' %}
+
+{% load staticfiles %}
+
+{% load thumbnail %}
+
+{% load iconolab_tags %}
+
+{% block content %}
+ <div id="annotation-wrapper" class="row" style="border: 1px solid gray">
+
+ <div v-show="formView" class="col-md-12">
+ <div class="col-xs-6">
+ <div class="small-image-wrapper" style="position: relative">
+ {% thumbnail annotation.image.media "x300" crop="center" as im %}
+ <img v-el:small-image src="{{ im.url }}" width="{{ im.width }}" height="{{ im.height }}" />
+
+ <svg width="{{ im.width }}" height="{{ im.height }}" version="1.1" style="position:absolute; top:0px; left: 0px">
+
+ <g transform="matrix({% transform_matrix im_width=im.width im_height=im.height max_x=100 max_y=100 %})">
+ <path d="{{ annotation.current_revision.fragment }}" opacity="0.7" fill="orange"></path>
+ </g>
+
+ </svg>
+
+ {% endthumbnail %}
+ </div>
+ </div>
+
+ <div class='col-xs-6' style="">
+ <p> <strong>Title:</strong> {{ annotation.current_revision.title }}</p>
+ <p> <strong>Description:</strong> {{ annotation.current_revision.description }}</p>
+ <p> <strong>Tags:</strong> {{ annotation.current_revision.tags }}</p>
+ <p> <strong>Fragment:</strong> {{ annotation.current_revision.fragment }}</p>
+
+ <a href="{% url 'annotation_edit' collection_name image_ref annotation_guid %}">Editer</a> |
+ <a href="{% url 'annotation_edit' collection_name image_ref annotation_guid %}">Proposer une révision</a>
+ </div>
+ </div>
+ </div>
+
+{% endblock %}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/iconolab/templates/iconolab/detail_image.html Tue Jun 21 14:38:09 2016 +0200
@@ -0,0 +1,9 @@
+{% extends 'iconolab_base.html' %}
+
+{% load staticfiles %}
+
+{% load thumbnail %}
+
+{% load iconolab_tags %}
+
+{% block content %}detailimage{% endblock %}
\ No newline at end of file
--- a/src/iconolab/templates/iconolab/edit_annotation.html Mon Jun 20 15:05:23 2016 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,173 +0,0 @@
-{% extends 'iconolab_base.html' %}
-
-{% load staticfiles %}
-
-{% load thumbnail %}
-
-{% block content %}
-
- <div id="drawing-zone" class="row" style="padding-top: 10px; border:1px solid orange">
-
- <div v-show='!formView' class="editor-wrapper col-md-12">
- <div class='col-md-2'>
- <ul class="form-drawing-wrapper list-inline">
- <p class='form-drawing pullright'><strong>Type de dessin</strong></p>
- <li @click="setDrawingMode('RECT')" v-bind:class="{ 'selected': isRect }" class='pull-md-right drawingModeBtn'>Rect.</li>
- <li @click="setDrawingMode('FREE')" v-bind:class="{ 'selected': !isRect }" class='pull-md-right drawingModeBtn'>Libre</li>
- </ul>
-
- <ul class='form-drawing-wrapper list-inline'>
- <p class='form-drawing pullright'><strong>Actions</strong></p>
-
- <li @click="clear" class='pull-md-right drawingModeBtn'><i class='fa fa-trash'></i> Effacer</li>
-
- <li @click="save" class='pull-md-right drawingModeBtn'><i class='fa fa-plus'></i> Créer le fragment</li>
- </ul>
- </div>
-
- <div class="col-md-8">
- <div v-el:image id="iconolab-image-wrapper">
- {% thumbnail annotation.image.media "x800" crop="center" as im %}
- <img src="{{ im.url }}" width="{{ im.width }}" height="{{ im.height }}">
- <path class="image-path" d="{{ annotation.fragment }}"></path>
- {% endthumbnail %}
- </div>
- </div>
-
- </div>
-
- <div v-show="formView" class="col-md-12">
-
- <div class="col-xs-6">
- <div class="small-image-wrapper" style="position: relative">
- {% thumbnail annotation.image.media "x300" crop="center" as im %}
- <img v-el:small-image @click="showEditor" src="{{ im.url }}" width="{{ im.width }}" height="{{ im.height }}">
-
- <svg width="{{ im.width }}" height="{{ im.height }}" version="1.1" style="position:absolute; top:0px; left: 0px">
-
- <defs>
- <mask xmlns="http://www.w3.org/2000/svg" id="smallImage">
- <rect x="0" y="0" width="{{ im.width }}", height="{{ im.height }}" fill="white"/>
- <g v-bind:transform="transformMatrix">
- <path v-bind:d="fragmentPath"></path>
- </g>
- </mask>
- </defs>
-
- <g v-show="!displayMask" v-bind:transform="transformMatrix">
- <path v-bind:d="fragmentPath" opacity="0.7" fill="orange"></path>
- </g>
-
- <rect v-show="displayMask" v-el:small-mask x="0" y="0" mask="url(#smallImage)" opacity="0.7" fill="white" width="{{ im.width }}" height="{{ im.height }}"></rect>
- </svg>
-
- {% endthumbnail %}
- </div>
- <ul class="inline">
- <li @click="showEditor" class="showPointer"> <i class='fa fa-edit'></i> Editer le fragment</li>
- <li v-show="!displayMask" @click="highLightZone" class="showPointer"> <i class='fa fa-eye-slash'></i> Afficher la zone</li>
- <li v-show="displayMask" @click="highLightZone" class="showPointer"> <i class='fa fa-eye-slash'></i> Masquer la zone</li>
- </ul>
- </div>
-
- <div class='col-xs-6' style="">
- <form method="POST" class="post-form">
- {% csrf_token %}
- {{ form.as_p }}
- <input v-model="normalizePath" type="hidden" name="fragment"></input>
- <button type="submit" class="save btn btn-default">Enregister</button>
- </form>
- </div>
-
- </div>
- </div>
-
-{% endblock %}
-
-{% block footer_js %}
- <script>
- var drawingVue = new Vue({
- el: '#drawing-zone',
- MODE_RECT: 'RECT',
- MODE_FREE: 'FREE',
-
- data: {
- mode:"",
- isRect: true,
- normalizePath: "",
- readOnly: false,
- formView: false,
- useClipPath: false,
- transformMatrix: "",
- fragmentPath: "",
- displayMask: false
-
- },
-
- init: function () {
- var self = this;
- this.drawingComponent = iconolab.initCutoutComponent({
- wrapperId: '#iconolab-image-wrapper',
- actionWrapper: '#action-wrapper',
- readOnly: false,
- onDrawingModeChange: function (mode) {
- self.$nextTick(function () {
- self.setDrawingMode(mode, false);
- });
- }
- });
- },
-
- methods: {
-
- setDrawingMode: function (mode, updateComponent) {
- var updateComponent = (typeof updateComponent === "boolean") ? updateComponent: true;
- this.mode = this.$options['MODE_' + mode];
- this.isRect = (this.mode === this.$options.MODE_RECT) ? true: false;
- if (updateComponent) {
- this.drawingComponent.setDrawingMode(this.mode);
- }
- },
-
- showEditor: function () {
- this.formView = false;
- console.log(this.$els.smallImage);
- },
-
- highLightZone: function () {
- if (!this.displayMask) {
- this.displayMask = true;
- }
- else {
- this.displayMask = false;
- }
- //this.maskFill = "orange";
- //this.fragmentFill = "none";
- },
-
- displayEditedPath: function () {
- /* path to save */
- var normalizePath = this.drawingComponent.getPath();
- },
-
- save: function () {
- this.normalizePath = this.drawingComponent.getPath();
- var smallImage = this.$els.smallImage;
- this.formView = true;
- /* 100x = smallImageHeight && 100x=smallImageWidth | 100 = ViewBoxBound */
- var xRatio = smallImage.width / 100;
- var yRatio = smallImage.height / 100;
- var transformMatrix = [xRatio, 0, 0, yRatio, 0, 0].join(',');
- this.transformMatrix ="matrix(" + transformMatrix + ")";
- this.fragmentPath = this.normalizePath.split(';')[0];
- console.log(this.fragmentPath);
- console.log(this.transformMatrix);
- },
-
- clear: function () {
- this.drawingComponent.clear();
- }
- }
- });
- </script>
-{% endblock %}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/iconolab/templates/iconolab/home.html Tue Jun 21 14:38:09 2016 +0200
@@ -0,0 +1,9 @@
+{% extends 'iconolab_base.html' %}
+
+{% load staticfiles %}
+
+{% load thumbnail %}
+
+{% load iconolab_tags %}
+
+{% block content %}globalhome{% endblock %}
\ No newline at end of file
--- a/src/iconolab/templates/iconolab/show_annotation.html Mon Jun 20 15:05:23 2016 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-{% extends 'iconolab_base.html' %}
-
-{% load staticfiles %}
-
-{% load thumbnail %}
-
-{% load iconolab_tags %}
-
-{% block content %}
- <div id="annotation-wrapper" class="row" style="border: 1px solid gray">
-
- <div v-show="formView" class="col-md-12">
- <div class="col-xs-6">
- <div class="small-image-wrapper" style="position: relative">
- {% thumbnail annotation.image.media "x300" crop="center" as im %}
- <img v-el:small-image src="{{ im.url }}" width="{{ im.width }}" height="{{ im.height }}" />
-
- <svg width="{{ im.width }}" height="{{ im.height }}" version="1.1" style="position:absolute; top:0px; left: 0px">
-
- <g transform="matrix({% transform_matrix im_width=im.width im_height=im.height max_x=100 max_y=100 %})">
- <path d="{{ annotation.fragment }}" opacity="0.7" fill="orange"></path>
- </g>
-
- </svg>
-
- {% endthumbnail %}
- </div>
- </div>
-
- <div class='col-xs-6' style="">
- <p> <strong>Title:</strong> {{ annotation.title }}</p>
- <p> <strong>Description:</strong> {{ annotation.description }}</p>
- <p> <strong>Tags:</strong> {{ annotation.tags }}</p>
- <p> <strong>Fragment:</strong> {{ annotation.fragment }}</p>
-
- <a href="{% url 'annotation_edit' annotation.id %}">Editer</a> |
- <a href="{% url 'annotation_edit' annotation.id %}">Proposer une révision</a>
- </div>
- </div>
- </div>
-
-{% endblock %}
--- a/src/iconolab/templates/partials/header.html Mon Jun 20 15:05:23 2016 +0200
+++ b/src/iconolab/templates/partials/header.html Tue Jun 21 14:38:09 2016 +0200
@@ -1,33 +1,38 @@
<nav class="navbar navbar-default" style="">
- <div class="container-fluid">
- <div class="navbar-header">
- <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
- <span class="sr-only">{{ greeting }}</span>
- <span class="icon-bar"></span>
- <span class="icon-bar"></span>
- <span class="icon-bar"></span>
- </button>
- <a class="navbar-brand" href="#">Iconolab</a>
- </div>
- <div id="navbar" class="navbar-collapse collapse">
- <ul class="nav navbar-nav">
- <li class="active"><a href="#">Accueil</a></li>
- <li><a href="#">Le projet</a></li>
- <li><a href="#">Contribuer</a></li>
- </ul>
-
- <form class="navbar-form navbar-left" role="search">
- <div class="form-group">
- <input type="text" class="form-control" placeholder="Trouver une image...">
- </div>
- <button type="submit" class="btn btn-default">Rechercher</button>
- </form>
+ <div class="container-fluid">
+ <div class="navbar-header">
+ <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
+ <span class="sr-only">{{ greeting }}</span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ </button>
+ <a class="navbar-brand" href="#">Iconolab</a>
+ </div>
+ <div id="navbar" class="navbar-collapse collapse">
+ <ul class="nav navbar-nav">
+ <li class="active"><a href="#">Accueil</a></li>
+ <li><a href="#">Le projet</a></li>
+ <li><a href="#">Contribuer</a></li>
+ </ul>
+
+ <form class="navbar-form navbar-left" role="search">
+ <div class="form-group">
+ <input type="text" class="form-control" placeholder="Trouver une image...">
+ </div>
+ <button type="submit" class="btn btn-default">Rechercher</button>
+ </form>
- <ul class="nav navbar-nav navbar-right">
- <li role="presentation"><a href="#">Notifications <span class="badge">3</span></a></li>
- <li><a href="{% url 'account:register' %}">Créer un compte</a></li>
- <li><a href="{% url 'account:login' %}">Se Connecter</a></li>
- </ul>
- </div><!--/.nav-collapse -->
- </div><!--/.container-fluid -->
- </nav>
\ No newline at end of file
+ <ul class="nav navbar-nav navbar-right">
+ <li role="presentation"><a href="#">Notifications <span class="badge">3</span></a></li>
+ {% if user.is_authenticated %}
+ <li><a href="#">{{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>
+ <li><a href="{% url 'account:login' %}">Se Connecter</a></li>
+ {% endif %}
+ </ul>
+ </div><!--/.nav-collapse -->
+ </div><!--/.container-fluid -->
+</nav>
\ No newline at end of file
--- a/src/iconolab/templates/registration/login.html Mon Jun 20 15:05:23 2016 +0200
+++ b/src/iconolab/templates/registration/login.html Tue Jun 21 14:38:09 2016 +0200
@@ -17,25 +17,19 @@
<div class='col-md-6 center centerer'>
<h3>Se connecter</h3>
- <form method="post" action="{% url 'account:login' %}">
- {% csrf_token %}
-
- <div class="form-group">
- <label for="exampleInputEmail1">{{form.username.label_tag}}</label>
- <input type="text" class="form-control" id="username" placeholder="Login ou utilisateur">
- </div>
-
- <div class="form-group">
- <label for="exampleInputPassword1">{{form.password.label_tag}}</label>
- <input type="password" class="form-control" id="exampleInputPassword1" placeholder="Password">
- </div>
-
- <input type="hidden" name="next" value="{{ next }}" />
- <button type="submit" class="btn btn-default">Submit</button>
- <p><a href="{% url 'account:password_reset' %}">Lost password?</a></p>
-
+ <form class="form" action="{% url 'account:login' %}" method="POST">
+ {% csrf_token %}
+ {% for field in form %}
+ <fieldset class="form-group">
+ <label class="control-label" for="id_{{ field.name }}">{{ field.label }}</label>
+ <input type="{% if field.label == "Username" %}text{% else %}password{% endif %}" class="form-control"
+ name="{{ field.name }}"
+ id="id_{{ field.name }}" >
+ {% if field.errors %}{{ field.errors }}{% endif %}
+ </fieldset>
+ {% endfor %}
+ <input type="submit" value="Submit" class="btn btn-primary">
</form>
-
</div>
{% endblock %}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/iconolab/templates/registration/register.html Tue Jun 21 14:38:09 2016 +0200
@@ -0,0 +1,22 @@
+{% extends "iconolab_base.html" %}
+
+{% block content %}
+
+<div class='col-md-6 center centerer'>
+ <h3>S'enregistrer</h3>
+ <form class="form" action="{% url 'account:register' %}" method="POST">
+ {% csrf_token %}
+ {% for field in form %}
+ <fieldset class="form-group">
+ <label class="control-label" for="id_{{ field.name }}">{{ field.label }}</label>
+ <input type="{% if field.label == "Username" %}text{% else %}password{% endif %}" class="form-control"
+ name="{{ field.name }}"
+ id="id_{{ field.name }}" >
+ {% if field.errors %}{{ field.errors }}{% else %}<p class="help-text">{{ field.help_text }}</p>{% endif %}
+ </fieldset>
+ {% endfor %}
+ <input type="submit" value="Submit" class="btn btn-primary">
+ </form>
+</div>
+
+{% endblock %}
\ No newline at end of file
--- a/src/iconolab/urls.py Mon Jun 20 15:05:23 2016 +0200
+++ b/src/iconolab/urls.py Tue Jun 21 14:38:09 2016 +0200
@@ -15,23 +15,23 @@
"""
from django.conf.urls import url, include
from django.contrib import admin
-from notifications import urls
from . import views
from . import settings
from django.conf.urls.static import static
+from django.contrib.auth.decorators import login_required
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
urlpatterns = [
url(r'^admin/', admin.site.urls),
- url(r'^home', views.index, name="home"),
- url(r'^annotation/(?P<pk>[0-9]+)/$', views.show_annotation, name='annotation_show'),
- url(r'^annotation/(?P<pk>[0-9]+)/fragment', views.draw_fragment, name='annotation_fragment'),
- url(r'^annotation/create', views.create_annotation, name='annotation_create'),
- url(r'^annotation/(?P<pk>[0-9]+)/edit', views.edit_annotation, name='annotation_edit'),
-
+ 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]+)/images/(?P<image_ref>[a-z0-9]+)$', views.ShowImageView.as_view(), name='image_detail'),
+ url(r'^collections/(?P<collection_name>[a-z]+)/images/(?P<image_ref>[a-z0-9]+)/annotations/create$', login_required(views.CreateAnnotationView.as_view()), name='annotation_create'),
+ url(r'^collections/(?P<collection_name>[a-z]+)/images/(?P<image_ref>[a-z0-9]+)/annotations/(?P<annotation_guid>[^/]+)/detail$', views.ShowAnnotationView.as_view(), name='annotation_detail'),
+ url(r'^collections/(?P<collection_name>[a-z]+)/images/(?P<image_ref>[a-z0-9]+)/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_ref>[a-z0-9]+)/annotations/(?P<annotation_guid>[^/]+)/revisions/(?P<revision_guid>[0-9]+)/merge$', login_required(views.MergeProposalView.as_view()), name='annotation_merge'),
url(r'^rest', include('restapi.urls')),
- url(r'^inbox/notifications/', include(urls, namespace='notifications')),
url(r'^account/', include('iconolab.auth.urls', namespace='account')),
]
--- a/src/iconolab/views.py Mon Jun 20 15:05:23 2016 +0200
+++ b/src/iconolab/views.py Tue Jun 21 14:38:09 2016 +0200
@@ -1,46 +1,128 @@
-from django.shortcuts import HttpResponse, get_object_or_404, render, redirect
-from iconolab.models import Annotation
+from django.shortcuts import HttpResponse, get_object_or_404, render
+from iconolab.models import Annotation, Collection
from django.http import Http404
from django.contrib.auth.decorators import login_required
-from .forms import AnnotationForm
+from django.views.generic import View, RedirectView
+from django.views.generic.base import ContextMixin
+from django.core.urlresolvers import reverse
+from .forms.annotations import AnnotationRevisionForm
from pprint import pprint
-@login_required
-def index(request):
- print(request.user)
- return HttpResponse("<p>Home</p>");
+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