src/iconolab/models.py
author Harris Baptiste <harris.baptiste@iri.centrepompidou.fr>
Fri, 01 Jul 2016 10:40:02 +0200
changeset 43 ccc449ef6f16
parent 42 51257e2701d9
child 47 aec0f3381736
permissions -rw-r--r--
Fixing drawing component

from django.db import models, transaction
from django.conf import settings
from django.contrib.auth.models import User
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django_comments_xtd.models import XtdComment
import uuid, json


class Tag(models.Model):
	label = models.SlugField(blank=True, null=True)
	link = models.URLField(unique=True)
	description = models.CharField(max_length=255, blank=True, null=True)
	collection = models.ForeignKey('Collection', blank=True, null=True)
	
	def is_internal(self):
		return self.link.startswith(settings.BASE_URL)


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()


class Collection(models.Model):
	name =	models.CharField(max_length=50, unique=True)
	description = models.CharField(max_length=255)

	def __str__(self):
		return self.name


class Item(models.Model):
	collection = models.ForeignKey(Collection, related_name="items")

	
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 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):
	image_guid = models.UUIDField(default=uuid.uuid4, editable=False)
	name = models.CharField(max_length=200)
	media = models.ImageField(upload_to='uploads/', height_field='height', width_field='width')
	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)
	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)
# 
# 	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, author, image, title='', description='', fragment='', tags_json='[]'):
		# 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()
		initial_revision.set_tags(tags_json)
		
		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()
		return new_annotation

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


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_json):
		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()
		new_revision.set_tags(tags_json)
		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()


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='child_revisions', blank=True, null=True)
	merge_parent_revision = models.ForeignKey('AnnotationRevision', related_name='child_revisions_merge', blank=True, null=True)
	author = models.ForeignKey(User, null=True)
	title = models.CharField(max_length=255)
	description = models.TextField(null=True)
	fragment = models.TextField()
	tags = models.ManyToManyField('Tag', through='TaggingInfo', through_fields=('revision', 'tag'))
	state = models.IntegerField(choices=REVISION_STATES, default=AWAITING)
	created = models.DateTimeField(auto_now_add=True, null=True)

	def set_tags(self, tags_json_string):
		try:
			tags_dict = json.loads(tags_json_string)
		except ValueError:
			pass
		for tag_data in tags_dict:
			tag_string = tag_data.get("tag_input")
			tag_accuracy = tag_data.get("accuracy", 0)
			tag_relevancy = tag_data.get("relevancy", 0)
			
			if tag_string.startswith("http://"): #check if url
				if Tag.objects.filter(link=tag_string).exists(): #check if tag already exists
					tag_obj = Tag.objects.get(link=tag_string)
				else:
					tag_obj = Tag.objects.create(
						link = settings.BASE_URL+tag_string,
					)
			else:
				tag_obj = Tag.objects.create(
					label = tag_string,
					description = "",
					link = settings.BASE_URL+'/'+tag_string,
					collection = self.annotation.image.item.collection
				)
			tag_info = TaggingInfo.objects.create(
				tag=tag_obj, 
				revision=self,
				accuracy = tag_accuracy,
				relevancy = tag_relevancy
			)
		
	def get_tags_json(self):
		final_list = []
		for tagging_info in self.tagginginfo_set.select_related("tag").all():
			if tagging_info.tag.is_internal():
				final_list.append({
					"tag_label": tagging_info.tag.label,
					"tag_link": tagging_info.tag.link,
					"accuracy": tagging_info.accuracy,
					"relevancy": tagging_info.relevancy
				})
			else:
				#import label from dbpedia
				final_list.append({
					"tag_label": "",
					"tag_link": tagging_info.tag.link,
					"accuracy": tagging_info.accuracy,
					"relevancy": tagging_info.relevancy
				})
		return json.dumps(final_list) 
	
class IconolabComment(XtdComment):
	revision = models.ForeignKey('AnnotationRevision', related_name='creation_comment', null=True, blank=True)
	metacategories = models.ManyToManyField('MetaCategory', through='MetaCategoryInfo', through_fields=('comment', 'metacategory'))

	
class MetaCategory(models.Model):
	collection = models.ForeignKey(Collection)
	label = models.CharField(max_length=255)

	def __str__(self):
		return self.label
	

class MetaCategoryInfo(models.Model):
	comment = models.ForeignKey('IconolabComment', on_delete=models.CASCADE)
	metacategory = models.ForeignKey('MetaCategory', on_delete=models.CASCADE)


class CommentAttachement(models.Model):
 	
	LINK = 0
	IMAGE = 1
	PDF = 2
	COMMENT_CHOICES = (
		(LINK, 'link'),
		(IMAGE, 'image'),
		(PDF, 'pdf')
	)
	
	comment = models.ForeignKey('IconolabComment', related_name='attachments', on_delete=models.CASCADE)
	attachment_type = models.IntegerField(choices=COMMENT_CHOICES, default=0)
	data = models.TextField(blank=False)