Gitlab CSE Unil

annotables.py 7.79 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')
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
34
AO_ENVIRON = (
    ('', 'Standard'),
    ('cimaf', 'CIMAF'),
)
Julien Furrer's avatar
Julien Furrer committed
35

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

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

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


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"


82
83
84
85
86
87
88
89
90
91
92
93
94
95
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
96
class AnObj(models.Model):
Julien Furrer's avatar
Julien Furrer committed
97
98
99
100
101
102
    """
    Annotable Object
    """
    uuid = models.CharField(max_length=32, unique=True, blank=True)

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

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

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

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

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

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

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

    def save(self, *args, **kwargs):
134
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
        # set the uuid upon object creation
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
    def set_uuid(self, no_save=False):
155
156
157
158
159
        """
        Calculate and set the UUID, only if not already set
        :param no_save:
        :return:
        """
Julien Furrer's avatar
Julien Furrer committed
160
161
162
163
164
165
166
        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()

167
168
169
170
171
    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
172
        # memberships = []
173
        for user in args:
174
175
176
177
178
            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)
179
180
181
182
183
184
185

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

186
187
188
189
190
191
192
193
194
    @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
195
196
197
198
199
200
201
202
203
204
205
206
207
    # 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
208
209
210
    @property
    def thumb_url(self):
        if not self._thumb_url:
Julien Furrer's avatar
Julien Furrer committed
211
212
213
214
215
216
217
            # 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
218
219
220
221
222
223
224
            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
225
226
227
228
229
230
231
    # @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
232
233
234
235
236
237
238


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

239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254

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"