# coding=utf-8 from __future__ import unicode_literals import os import user import uuid, random from django.db import models from django.conf import settings # from django.contrib.contenttypes import generic from django.utils.translation import ugettext as _ from django.contrib.auth.models import User from django.utils import timezone from jsonfield import JSONField # from PIL import Image # from sorl.thumbnail import ImageField, get_thumbnail from eav.models import BaseEntity, BaseSchema, BaseAttribute, BaseChoice from adim_project.utils.decorators import cache __all__ = ('AOType', 'AOSchema', 'AOChoice', 'AOAttribute', 'AnObj', 'AnObjMembership') # code from from uuid._random_getnode() RANDOM_NODE = random.randrange(0, 1 << 48L) | 0x010000000000L # Destination path for uploaded images, relative to MEDIA_ROOT AO_IMAGES_PATH = 'ao_images' class AOType(models.Model): name = models.CharField(max_length=128) title = models.CharField(max_length=512, blank=True, default="") class Meta: app_label = "adim" verbose_name = "Annotable Object Type" def __unicode__(self): if self.title: return u"{} ({})".format(self.title, self.name) else: return self.name class AOSchema(BaseSchema): ao_types = models.ManyToManyField(AOType, blank=True) class Meta: app_label = "adim" verbose_name = "Annotable Object Schema" verbose_name_plural = "Annotable Object Schemata" # AOSchema._meta.get_field_by_name('datatype')[0]._choices += (('image', u'image file'), ) class AOChoice(BaseChoice): schema = models.ForeignKey(AOSchema, related_name='choices') class Meta: app_label = "adim" verbose_name = "Annotable Object Choice" class AOAttribute(BaseAttribute): schema = models.ForeignKey(AOSchema, related_name='attrs') choice = models.ForeignKey(AOChoice, blank=True, null=True) # value_image = models.ImageField(upload_to="ao", blank=True, null=True) class Meta: app_label = "adim" verbose_name = "Annotable Object Attribute" verbose_name_plural = "Annotable Object Attributes" def get_image_path(instance, filename): now = timezone.now() instance.set_uuid() file_ext = os.path.splitext(filename)[1] filename = instance.uuid + file_ext return os.path.join( AO_IMAGES_PATH, now.strftime("%Y"), now.strftime("%m"), now.strftime("%d"), filename ) class AnObj(models.Model): """ Annotable Object """ uuid = models.CharField(max_length=32, unique=True, blank=True) name = models.CharField(max_length=125) owner = models.ForeignKey(User, verbose_name=_("owner"), related_name='anobjs') owners = models.ManyToManyField(User, verbose_name=_("owners"), related_name='owned_anobjs', blank=True) locked = models.BooleanField(verbose_name=_("locked"), default=False) sharing_mode = models.IntegerField(verbose_name=_("sharing mode"), default=0, blank=True) sharing_opts = JSONField(verbose_name=_("sharing options"), default="{}", blank=True) allow_public_publishing = models.BooleanField(verbose_name=_("allow public publishing"), default=False) members = models.ManyToManyField(User, verbose_name=_("members"), through='AnObjMembership', related_name='shared_anobjs', blank=True) image = models.ImageField(upload_to=get_image_path, verbose_name=_("image"), blank=True, null=True) image_url = models.CharField(max_length=512, verbose_name=_("image url"), blank=True, null=True, default="") _thumb_url = models.CharField(max_length=512, blank=True, null=True) # ----- eav attributes # ao_type = models.ForeignKey(AOType, verbose_name="type", blank=True, null=True) # attrs = generic.GenericRelation(AOAttribute, object_id_field='entity_id', # content_type_field='entity_type') class Meta: app_label = "adim" verbose_name = "Annotable Object" ordering = ["-id"] def __unicode__(self): return u"{}".format(self.name) def save(self, *args, **kwargs): """ Override save method to add actions on creation: caluclate the `uuid` append to `owner` to the `owners` list :param args: :param kwargs: :return: """ new_anobj = False # set the uuid upon object creation if not self.uuid: self.set_uuid(no_save=True) new_anobj = True super(AnObj, self).save(*args, **kwargs) if new_anobj: if len(self.owners.all()) == 0: self.owners.add(self.owner) def set_uuid(self, no_save=False): """ Calculate and set the UUID, only if not already set :param no_save: :return: """ if not self.uuid: # Using the random node initialized at module level # to avoid compromising network address self.uuid = uuid.uuid1(node=RANDOM_NODE).hex if not no_save: self.save() def add_members(self, *args, **kwargs): """ Add members through Membership, with specified publish mode """ publish_mode = kwargs.get('publish_mode', 0) memberships = [] for user in args: AnObjMembership.objects.get_or_create(anobj=self, user=user, defaults={'publish_mode': publish_mode}) # # memberships.append(AnObjMembership(anobj=self, user=user, publish_mode=publish_mode)) # if memberships: # AnObjMembership.objects.bulk_create(memberships) def remove_members(self, *args): """ Remove members through Membership """ AnObjMembership.objects.filter(anobj=self, user__in=args).delete() @cache(seconds=10) def is_owned(self, user_id): """ Return True if the User if an owner of the anobj This is used as function, so the result can be cached """ return user_id in self.owners.all().values_list('id', flat=True) # def _create_thumbnail(self): # if not self.image: # return # # th_path = os.path.splitext(self.image.path)[0] + "__.png" # if os.path.exists(th_path): # return th_path # # im = Image.open(self.image.path) # im.thumbnail(settings.ADIM_THUMB_SIZE) # im.save(th_path) # return th_path @property def thumb_url(self): if not self._thumb_url: # th_size = (150, 150) # th_path = os.path.splitext(self.image.path)[0] + "__.png" # im = Image.open(self.image.path) # im.thumbnail(th_size) # im.save(th_path) # im = get_thumbnail(self.image.name, '150x150', quality=80) self._thumb_url = self._create_thumbnail() self.save() return self._thumb_url @thumb_url.setter def thumb_url(self, url): self._thumb_url = url # @classmethod # def get_schemata_for_model(cls): # return AOSchema.objects.all() # # def get_schemata_for_instance(self, qs): # qs = qs.filter(models.Q(ao_types__isnull=True) | models.Q(ao_types=self.ao_type)) # return qs def clear_thumbnails(qs=None): if qs is None or qs.model is not AnObj: qs = AnObj.objects.all() qs.update(_thumb_url="") PUBLISHING_MODES = ( (0, _("private")), (1, _("owner only")), (2, _("all members")) ) class AnObjMembership(models.Model): anobj = models.ForeignKey(AnObj) user = models.ForeignKey(User) publish_mode = models.IntegerField(choices=PUBLISHING_MODES, default=0) class Meta: app_label = "adim" verbose_name = "Annotable Object Membership"