Gitlab CSE Unil

annotables.py 8.86 KB
Newer Older
Julien Furrer's avatar
Julien Furrer committed
1
2
3
# coding=utf-8
from __future__ import unicode_literals
import os
4
5
6
import uuid
import random

Julien Furrer's avatar
Julien Furrer committed
7
from django.db import models
8
9


10
# from django.contrib.contenttypes import generic
Julien Furrer's avatar
Julien Furrer committed
11
12
13
from django.utils.translation import ugettext as _
from django.contrib.auth.models import User
from django.utils import timezone
14
from jsonfield import JSONField
Julien Furrer's avatar
Julien Furrer committed
15
# from PIL import Image
Julien Furrer's avatar
Julien Furrer committed
16

Julien Furrer's avatar
Julien Furrer committed
17
# from sorl.thumbnail import ImageField, get_thumbnail
Julien Furrer's avatar
Julien Furrer committed
18

19
from eav.models import BaseSchema, BaseAttribute, BaseChoice
Julien Furrer's avatar
Julien Furrer committed
20

21
from adim_utils.decorators import cache
22

23
__all__ = ('AOType', 'AOSchema', 'AOChoice', 'AOAttribute', 'AnObj', 'AnObjMembership', 'EnvParam')
Julien Furrer's avatar
Julien Furrer committed
24
25
26
27
28
29
30

# 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'

31
32
33
AO_ENVIRON = (
    ('', 'Standard'),
    ('cimaf', 'CIMAF'),
34
    ('anodate', 'Datation'),
35
)
Julien Furrer's avatar
Julien Furrer committed
36

Julien Furrer's avatar
Julien Furrer committed
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
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):
Julien Furrer's avatar
Julien Furrer committed
53
    ao_types = models.ManyToManyField(AOType, blank=True)
Julien Furrer's avatar
Julien Furrer committed
54
55
56
57
58
59

    class Meta:
        app_label = "adim"
        verbose_name = "Annotable Object Schema"
        verbose_name_plural = "Annotable Object Schemata"

60
# AOSchema._meta.get_field_by_name('datatype')[0]._choices += (('image', u'image file'), )
Julien Furrer's avatar
Julien Furrer committed
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82


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"


83
84
85
86
87
88
89
90
91
92
93
94
95
96
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
    )


Julien Furrer's avatar
Julien Furrer committed
97
class AnObj(models.Model):
Julien Furrer's avatar
Julien Furrer committed
98
99
100
101
102
103
    """
    Annotable Object
    """
    uuid = models.CharField(max_length=32, unique=True, blank=True)

    name = models.CharField(max_length=125)
104
    owner = models.ForeignKey(User, verbose_name=_("owner"), related_name='anobjs')
105
    owners = models.ManyToManyField(User, verbose_name=_("owners"), related_name='owned_anobjs', blank=True)
106
107
108
109
    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)
110
    allow_public_publishing = models.BooleanField(verbose_name=_("allow public publishing"), default=False)
111
    members = models.ManyToManyField(User, verbose_name=_("members"), through='AnObjMembership',
Julien Furrer's avatar
Julien Furrer committed
112
                                     related_name='shared_anobjs', blank=True)
Julien Furrer's avatar
Julien Furrer committed
113

114
115
    env = models.CharField(max_length=64, choices=AO_ENVIRON, blank=True)

Julien Furrer's avatar
Julien Furrer committed
116
    image = models.ImageField(upload_to=get_image_path, verbose_name=_("image"), blank=True, null=True)
117
    image_url = models.CharField(max_length=512, verbose_name=_("image url"), blank=True, null=True, default="")
Julien Furrer's avatar
Julien Furrer committed
118
119
120
121

    _thumb_url = models.CharField(max_length=512, blank=True, null=True)

    # ----- eav attributes
Julien Furrer's avatar
Julien Furrer committed
122
123
124
    # 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')
Julien Furrer's avatar
Julien Furrer committed
125
126
127
128
129
130
131

    class Meta:
        app_label = "adim"
        verbose_name = "Annotable Object"
        ordering = ["-id"]

    def __unicode__(self):
132
        return "{}".format(self.name)
Julien Furrer's avatar
Julien Furrer committed
133
134

    def save(self, *args, **kwargs):
135
136
137
138
139
140
141
142
143
144
        """
        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
Julien Furrer's avatar
Julien Furrer committed
145
        if not self.uuid:
146
            self.set_uuid(no_save=True)
147
            new_anobj = True
Julien Furrer's avatar
Julien Furrer committed
148
149

        super(AnObj, self).save(*args, **kwargs)
150
151
152
        if new_anobj:
            if len(self.owners.all()) == 0:
                self.owners.add(self.owner)
Julien Furrer's avatar
Julien Furrer committed
153

154
155
156
157
        if self.env:
            envparam, _ = EnvParam.objects.get_or_create(anobj=self)
            envparam.init()

158
    def set_uuid(self, no_save=False):
159
160
161
162
163
        """
        Calculate and set the UUID, only if not already set
        :param no_save:
        :return:
        """
Julien Furrer's avatar
Julien Furrer committed
164
165
166
167
168
169
170
        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()

171
172
173
174
175
    def add_members(self, *args, **kwargs):
        """
        Add members through Membership, with specified publish mode
        """
        publish_mode = kwargs.get('publish_mode', 0)
Julien Furrer's avatar
Julien Furrer committed
176
        # memberships = []
177
        for user in args:
178
179
180
181
182
            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)
183
184
185
186
187
188
189

    def remove_members(self, *args):
        """
        Remove members through Membership
        """
        AnObjMembership.objects.filter(anobj=self, user__in=args).delete()

190
191
192
193
194
195
196
197
198
    @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)

Julien Furrer's avatar
Julien Furrer committed
199
200
201
202
203
204
205
206
207
208
209
210
211
    # 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

Julien Furrer's avatar
Julien Furrer committed
212
213
214
    @property
    def thumb_url(self):
        if not self._thumb_url:
Julien Furrer's avatar
Julien Furrer committed
215
216
217
218
219
220
221
            # 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()
Julien Furrer's avatar
Julien Furrer committed
222
223
224
225
226
227
228
            self.save()
        return self._thumb_url

    @thumb_url.setter
    def thumb_url(self, url):
        self._thumb_url = url

Julien Furrer's avatar
Julien Furrer committed
229
230
231
232
233
234
235
    # @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
Julien Furrer's avatar
Julien Furrer committed
236
237
238
239
240
241
242


def clear_thumbnails(qs=None):
    if qs is None or qs.model is not AnObj:
        qs = AnObj.objects.all()
    qs.update(_thumb_url="")

243
244
245
246
247
248
249
250
251
252
253
254
255
256
257

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"
258
259
260
261
262
263
        verbose_name = "Annotable Object Membership"


class EnvParam(models.Model):
    anobj = models.OneToOneField(AnObj)

264
265
266
    # ----- Categories Set
    category_set_name = models.CharField(max_length=64, blank=True)

267
268
269
270
271
272
273
    # ----- CIMAF
    cimaf_cut_pos = models.IntegerField(blank=True, null=True)
    cimaf_cut_margin = models.IntegerField(blank=True, null=True)

    class Meta:
        app_label = "adim"
        verbose_name = "Environment params"
274
275
        permissions = (
            ('set_env_cimaf', "Set CIMAF environment"),
Julien Furrer's avatar
Julien Furrer committed
276
            ('set_env_anodate', "Set Anodate environment"),
277
        )
278
279
280
281
282
283
284
285
286
287
288
289
290
291

    def init(self):
        need_to_save = False
        if self.anobj.env == 'cimaf':
            # --- CIMAF ---
            if self.cimaf_cut_pos is None:
                self.cimaf_cut_pos = self.anobj.image.width / 2
                need_to_save = True
            if self.cimaf_cut_margin is None:
                self.cimaf_cut_margin = 25
                need_to_save = True

        if need_to_save:
            self.save()