Gitlab CSE Unil

annotables.py 7.65 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'

Julien Furrer's avatar
Julien Furrer committed
31

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

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

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


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"


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

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

Julien Furrer's avatar
Julien Furrer committed
109
    image = models.ImageField(upload_to=get_image_path, verbose_name=_("image"), blank=True, null=True)
110
    image_url = models.CharField(max_length=512, verbose_name=_("image url"), blank=True, null=True, default="")
Julien Furrer's avatar
Julien Furrer committed
111
112
113
114

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

    # ----- eav attributes
Julien Furrer's avatar
Julien Furrer committed
115
116
117
    # 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
118
119
120
121
122
123
124

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

    def __unicode__(self):
125
        return "{}".format(self.name)
Julien Furrer's avatar
Julien Furrer committed
126
127

    def save(self, *args, **kwargs):
128
129
130
131
132
133
134
135
136
137
138
        """
        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
139
        if not self.uuid:
140
            self.set_uuid(no_save=True)
141
            new_anobj = True
Julien Furrer's avatar
Julien Furrer committed
142
143

        super(AnObj, self).save(*args, **kwargs)
144
145
146
        if new_anobj:
            if len(self.owners.all()) == 0:
                self.owners.add(self.owner)
Julien Furrer's avatar
Julien Furrer committed
147

148
    def set_uuid(self, no_save=False):
149
150
151
152
153
        """
        Calculate and set the UUID, only if not already set
        :param no_save:
        :return:
        """
Julien Furrer's avatar
Julien Furrer committed
154
155
156
157
158
159
160
        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()

161
162
163
164
165
    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
166
        # memberships = []
167
        for user in args:
168
169
170
171
172
            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)
173
174
175
176
177
178
179

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

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


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

233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248

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"