Gitlab CSE Unil

annotables.py 7.7 KB
Newer Older
Julien Furrer's avatar
Julien Furrer committed
1
2
3
# coding=utf-8
from __future__ import unicode_literals
import os
4
import user
Julien Furrer's avatar
Julien Furrer committed
5
6
7
import uuid, random
from django.db import models
from django.conf import settings
8
# from django.contrib.contenttypes import generic
Julien Furrer's avatar
Julien Furrer committed
9
10
11
from django.utils.translation import ugettext as _
from django.contrib.auth.models import User
from django.utils import timezone
12
from jsonfield import JSONField
Julien Furrer's avatar
Julien Furrer committed
13
# from PIL import Image
Julien Furrer's avatar
Julien Furrer committed
14

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

from eav.models import BaseEntity, BaseSchema, BaseAttribute, BaseChoice

19
20
from adim_project.utils.decorators import cache

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

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

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

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

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


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"


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

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

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

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

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

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

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

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

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

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

159
160
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)
        memberships = []
        for user in args:
166
167
168
169
170
            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)
171
172
173
174
175
176
177

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

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


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

231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246

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"