Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / models.py @ be62cfa8

History | View | Annotate | Download (72 kB)

1 aba1e498 Antony Chazapis
# Copyright 2011-2012 GRNET S.A. All rights reserved.
2 6c736ed7 Kostas Papadimitriou
#
3 64cd4730 Antony Chazapis
# Redistribution and use in source and binary forms, with or
4 64cd4730 Antony Chazapis
# without modification, are permitted provided that the following
5 64cd4730 Antony Chazapis
# conditions are met:
6 6c736ed7 Kostas Papadimitriou
#
7 64cd4730 Antony Chazapis
#   1. Redistributions of source code must retain the above
8 64cd4730 Antony Chazapis
#      copyright notice, this list of conditions and the following
9 64cd4730 Antony Chazapis
#      disclaimer.
10 6c736ed7 Kostas Papadimitriou
#
11 64cd4730 Antony Chazapis
#   2. Redistributions in binary form must reproduce the above
12 64cd4730 Antony Chazapis
#      copyright notice, this list of conditions and the following
13 64cd4730 Antony Chazapis
#      disclaimer in the documentation and/or other materials
14 64cd4730 Antony Chazapis
#      provided with the distribution.
15 6c736ed7 Kostas Papadimitriou
#
16 64cd4730 Antony Chazapis
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 64cd4730 Antony Chazapis
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 64cd4730 Antony Chazapis
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 64cd4730 Antony Chazapis
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 64cd4730 Antony Chazapis
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 64cd4730 Antony Chazapis
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 64cd4730 Antony Chazapis
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 64cd4730 Antony Chazapis
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 64cd4730 Antony Chazapis
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 64cd4730 Antony Chazapis
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 64cd4730 Antony Chazapis
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 64cd4730 Antony Chazapis
# POSSIBILITY OF SUCH DAMAGE.
28 6c736ed7 Kostas Papadimitriou
#
29 64cd4730 Antony Chazapis
# The views and conclusions contained in the software and
30 64cd4730 Antony Chazapis
# documentation are those of the authors and should not be
31 64cd4730 Antony Chazapis
# interpreted as representing official policies, either expressed
32 64cd4730 Antony Chazapis
# or implied, of GRNET S.A.
33 64cd4730 Antony Chazapis
34 64cd4730 Antony Chazapis
import hashlib
35 15efc749 Sofia Papagiannaki
import uuid
36 18ffbee1 Sofia Papagiannaki
import logging
37 3a72a5d4 Kostas Papadimitriou
import json
38 64cd4730 Antony Chazapis
39 d6fdc91e Georgios D. Tsoukalas
from time import asctime, sleep
40 64cd4730 Antony Chazapis
from datetime import datetime, timedelta
41 64cd4730 Antony Chazapis
from base64 import b64encode
42 ef20ea07 Sofia Papagiannaki
from urlparse import urlparse
43 d2633501 Kostas Papadimitriou
from urllib import quote
44 8f5a3a06 Sofia Papagiannaki
from random import randint
45 65360c65 Georgios D. Tsoukalas
from collections import defaultdict, namedtuple
46 64cd4730 Antony Chazapis
47 d6fdc91e Georgios D. Tsoukalas
from django.db import models, IntegrityError, transaction, connection
48 9a06d96f Olga Brani
from django.contrib.auth.models import User, UserManager, Group, Permission
49 0a569195 Sofia Papagiannaki
from django.utils.translation import ugettext as _
50 49790d9d Sofia Papagiannaki
from django.db import transaction
51 0a569195 Sofia Papagiannaki
from django.core.exceptions import ValidationError
52 c0b26605 Sofia Papagiannaki
from django.db.models.signals import (
53 73fbaec4 Sofia Papagiannaki
    pre_save, post_save, post_syncdb, post_delete)
54 9a06d96f Olga Brani
from django.contrib.contenttypes.models import ContentType
55 9a06d96f Olga Brani
56 fc1e2f02 Sofia Papagiannaki
from django.dispatch import Signal
57 e6759494 Sofia Papagiannaki
from django.db.models import Q
58 d2633501 Kostas Papadimitriou
from django.core.urlresolvers import reverse
59 d2633501 Kostas Papadimitriou
from django.utils.http import int_to_base36
60 d2633501 Kostas Papadimitriou
from django.contrib.auth.tokens import default_token_generator
61 8f8c43b2 Sofia Papagiannaki
from django.conf import settings
62 bf0c6de5 Sofia Papagiannaki
from django.utils.importlib import import_module
63 c4b1a172 Kostas Papadimitriou
from django.utils.safestring import mark_safe
64 d2633501 Kostas Papadimitriou
from django.core.validators import email_re
65 73fbaec4 Sofia Papagiannaki
from django.core.exceptions import PermissionDenied, ObjectDoesNotExist
66 64cd4730 Antony Chazapis
67 e1a80257 Sofia Papagiannaki
from astakos.im.settings import (
68 e1a80257 Sofia Papagiannaki
    DEFAULT_USER_LEVEL, INVITATIONS_PER_LEVEL,
69 e1a80257 Sofia Papagiannaki
    AUTH_TOKEN_DURATION, BILLING_FIELDS,
70 71a38edf Sofia Papagiannaki
    EMAILCHANGE_ACTIVATION_DAYS, LOGGING_LEVEL,
71 47b77c8b Sofia Papagiannaki
    SITENAME, SERVICES, MODERATION_ENABLED)
72 c4b1a172 Kostas Papadimitriou
from astakos.im import settings as astakos_settings
73 c0b26605 Sofia Papagiannaki
from astakos.im.endpoints.qh import (
74 ee45eb81 Giorgos Korfiatis
    register_users, send_quota, register_resources, qh_add_quota, QuotaLimits,
75 ee45eb81 Giorgos Korfiatis
    qh_query_serials, qh_ack_serials)
76 d2633501 Kostas Papadimitriou
from astakos.im import auth_providers
77 73fbaec4 Sofia Papagiannaki
#from astakos.im.endpoints.aquarium.producer import report_user_event
78 f8f86e83 root
#from astakos.im.tasks import propagate_groupmembers_quota
79 9c01d9d1 Sofia Papagiannaki
80 ae497612 Olga Brani
import astakos.im.messages as astakos_messages
81 ee45eb81 Giorgos Korfiatis
from .managers import ForUpdateManager
82 64cd4730 Antony Chazapis
83 18ffbee1 Sofia Papagiannaki
logger = logging.getLogger(__name__)
84 18ffbee1 Sofia Papagiannaki
85 9a06d96f Olga Brani
DEFAULT_CONTENT_TYPE = None
86 e65c21df Georgios D. Tsoukalas
_content_type = None
87 e65c21df Georgios D. Tsoukalas
88 bfe23b13 Sofia Papagiannaki
PENDING, APPROVED, REPLACED, UNKNOWN = 'Pending', 'Approved', 'Replaced', 'Unknown'
89 bfe23b13 Sofia Papagiannaki
90 e65c21df Georgios D. Tsoukalas
def get_content_type():
91 e65c21df Georgios D. Tsoukalas
    global _content_type
92 e65c21df Georgios D. Tsoukalas
    if _content_type is not None:
93 e65c21df Georgios D. Tsoukalas
        return _content_type
94 e65c21df Georgios D. Tsoukalas
95 e65c21df Georgios D. Tsoukalas
    try:
96 e65c21df Georgios D. Tsoukalas
        content_type = ContentType.objects.get(app_label='im', model='astakosuser')
97 e65c21df Georgios D. Tsoukalas
    except:
98 e65c21df Georgios D. Tsoukalas
        content_type = DEFAULT_CONTENT_TYPE
99 e65c21df Georgios D. Tsoukalas
    _content_type = content_type
100 e65c21df Georgios D. Tsoukalas
    return content_type
101 9a06d96f Olga Brani
102 9a06d96f Olga Brani
RESOURCE_SEPARATOR = '.'
103 9a06d96f Olga Brani
104 9ee0c6a2 Sofia Papagiannaki
inf = float('inf')
105 5ce3ce4f Sofia Papagiannaki
106 8e45d6fd Sofia Papagiannaki
class Service(models.Model):
107 e1a80257 Sofia Papagiannaki
    name = models.CharField(_('Name'), max_length=255, unique=True, db_index=True)
108 8e45d6fd Sofia Papagiannaki
    url = models.FilePathField()
109 8e45d6fd Sofia Papagiannaki
    icon = models.FilePathField(blank=True)
110 e1a80257 Sofia Papagiannaki
    auth_token = models.CharField(_('Authentication Token'), max_length=32,
111 8e45d6fd Sofia Papagiannaki
                                  null=True, blank=True)
112 e1a80257 Sofia Papagiannaki
    auth_token_created = models.DateTimeField(_('Token creation date'), null=True)
113 5ce3ce4f Sofia Papagiannaki
    auth_token_expires = models.DateTimeField(
114 e1a80257 Sofia Papagiannaki
        _('Token expiration date'), null=True)
115 5ce3ce4f Sofia Papagiannaki
116 8e45d6fd Sofia Papagiannaki
    def renew_token(self):
117 8e45d6fd Sofia Papagiannaki
        md5 = hashlib.md5()
118 8e45d6fd Sofia Papagiannaki
        md5.update(self.name.encode('ascii', 'ignore'))
119 8e45d6fd Sofia Papagiannaki
        md5.update(self.url.encode('ascii', 'ignore'))
120 8e45d6fd Sofia Papagiannaki
        md5.update(asctime())
121 8e45d6fd Sofia Papagiannaki
122 8e45d6fd Sofia Papagiannaki
        self.auth_token = b64encode(md5.digest())
123 8e45d6fd Sofia Papagiannaki
        self.auth_token_created = datetime.now()
124 8e45d6fd Sofia Papagiannaki
        self.auth_token_expires = self.auth_token_created + \
125 5ce3ce4f Sofia Papagiannaki
            timedelta(hours=AUTH_TOKEN_DURATION)
126 5ce3ce4f Sofia Papagiannaki
127 8e45d6fd Sofia Papagiannaki
    def __str__(self):
128 8e45d6fd Sofia Papagiannaki
        return self.name
129 8e45d6fd Sofia Papagiannaki
130 9a06d96f Olga Brani
    @property
131 9a06d96f Olga Brani
    def resources(self):
132 9a06d96f Olga Brani
        return self.resource_set.all()
133 9a06d96f Olga Brani
134 9a06d96f Olga Brani
    @resources.setter
135 9a06d96f Olga Brani
    def resources(self, resources):
136 9a06d96f Olga Brani
        for s in resources:
137 9a06d96f Olga Brani
            self.resource_set.create(**s)
138 2e90e3ec Kostas Papadimitriou
139 9a06d96f Olga Brani
    def add_resource(self, service, resource, uplimit, update=True):
140 9a06d96f Olga Brani
        """Raises ObjectDoesNotExist, IntegrityError"""
141 9a06d96f Olga Brani
        resource = Resource.objects.get(service__name=service, name=resource)
142 9a06d96f Olga Brani
        if update:
143 9a06d96f Olga Brani
            AstakosUserQuota.objects.update_or_create(user=self,
144 9a06d96f Olga Brani
                                                      resource=resource,
145 9a06d96f Olga Brani
                                                      defaults={'uplimit': uplimit})
146 9a06d96f Olga Brani
        else:
147 9a06d96f Olga Brani
            q = self.astakosuserquota_set
148 9a06d96f Olga Brani
            q.create(resource=resource, uplimit=uplimit)
149 9a06d96f Olga Brani
150 5ce3ce4f Sofia Papagiannaki
151 8e45d6fd Sofia Papagiannaki
class ResourceMetadata(models.Model):
152 e1a80257 Sofia Papagiannaki
    key = models.CharField(_('Name'), max_length=255, unique=True, db_index=True)
153 e1a80257 Sofia Papagiannaki
    value = models.CharField(_('Value'), max_length=255)
154 8e45d6fd Sofia Papagiannaki
155 5ce3ce4f Sofia Papagiannaki
156 8e45d6fd Sofia Papagiannaki
class Resource(models.Model):
157 43e09b6c Sofia Papagiannaki
    name = models.CharField(_('Name'), max_length=255)
158 8e45d6fd Sofia Papagiannaki
    meta = models.ManyToManyField(ResourceMetadata)
159 8e45d6fd Sofia Papagiannaki
    service = models.ForeignKey(Service)
160 e1a80257 Sofia Papagiannaki
    desc = models.TextField(_('Description'), null=True)
161 e1a80257 Sofia Papagiannaki
    unit = models.CharField(_('Name'), null=True, max_length=255)
162 e1a80257 Sofia Papagiannaki
    group = models.CharField(_('Group'), null=True, max_length=255)
163 425e2e95 Sofia Papagiannaki
164 43e09b6c Sofia Papagiannaki
    class Meta:
165 43e09b6c Sofia Papagiannaki
        unique_together = ("name", "service")
166 5ce3ce4f Sofia Papagiannaki
167 8e45d6fd Sofia Papagiannaki
    def __str__(self):
168 9a06d96f Olga Brani
        return '%s%s%s' % (self.service, RESOURCE_SEPARATOR, self.name)
169 8e45d6fd Sofia Papagiannaki
170 5ce3ce4f Sofia Papagiannaki
171 8e45d6fd Sofia Papagiannaki
class GroupKind(models.Model):
172 e1a80257 Sofia Papagiannaki
    name = models.CharField(_('Name'), max_length=255, unique=True, db_index=True)
173 5ce3ce4f Sofia Papagiannaki
174 8e45d6fd Sofia Papagiannaki
    def __str__(self):
175 8e45d6fd Sofia Papagiannaki
        return self.name
176 8e45d6fd Sofia Papagiannaki
177 5ce3ce4f Sofia Papagiannaki
178 8e45d6fd Sofia Papagiannaki
class AstakosGroup(Group):
179 8e45d6fd Sofia Papagiannaki
    kind = models.ForeignKey(GroupKind)
180 2b1a5f5d Olga Brani
    homepage = models.URLField(
181 e1a80257 Sofia Papagiannaki
        _('Homepage Url'), max_length=255, null=True, blank=True)
182 e1a80257 Sofia Papagiannaki
    desc = models.TextField(_('Description'), null=True)
183 9a06d96f Olga Brani
    policy = models.ManyToManyField(
184 9a06d96f Olga Brani
        Resource,
185 9a06d96f Olga Brani
        null=True,
186 9a06d96f Olga Brani
        blank=True,
187 9a06d96f Olga Brani
        through='AstakosGroupQuota'
188 9a06d96f Olga Brani
    )
189 9a06d96f Olga Brani
    creation_date = models.DateTimeField(
190 e1a80257 Sofia Papagiannaki
        _('Creation date'),
191 9a06d96f Olga Brani
        default=datetime.now()
192 9a06d96f Olga Brani
    )
193 8327782d Sofia Papagiannaki
    issue_date = models.DateTimeField(
194 8327782d Sofia Papagiannaki
        _('Start date'),
195 8327782d Sofia Papagiannaki
        null=True
196 8327782d Sofia Papagiannaki
    )
197 9a06d96f Olga Brani
    expiration_date = models.DateTimeField(
198 e1a80257 Sofia Papagiannaki
        _('Expiration date'),
199 e1a80257 Sofia Papagiannaki
        null=True
200 9a06d96f Olga Brani
    )
201 9a06d96f Olga Brani
    moderation_enabled = models.BooleanField(
202 e1a80257 Sofia Papagiannaki
        _('Moderated membership?'),
203 9a06d96f Olga Brani
        default=True
204 9a06d96f Olga Brani
    )
205 9a06d96f Olga Brani
    approval_date = models.DateTimeField(
206 e1a80257 Sofia Papagiannaki
        _('Activation date'),
207 9a06d96f Olga Brani
        null=True,
208 9a06d96f Olga Brani
        blank=True
209 9a06d96f Olga Brani
    )
210 9a06d96f Olga Brani
    estimated_participants = models.PositiveIntegerField(
211 e1a80257 Sofia Papagiannaki
        _('Estimated #members'),
212 9a06d96f Olga Brani
        null=True,
213 9a06d96f Olga Brani
        blank=True,
214 9a06d96f Olga Brani
    )
215 9a06d96f Olga Brani
    max_participants = models.PositiveIntegerField(
216 e1a80257 Sofia Papagiannaki
        _('Maximum numder of participants'),
217 9a06d96f Olga Brani
        null=True,
218 9a06d96f Olga Brani
        blank=True
219 9a06d96f Olga Brani
    )
220 2e90e3ec Kostas Papadimitriou
221 8e45d6fd Sofia Papagiannaki
    @property
222 8e45d6fd Sofia Papagiannaki
    def is_disabled(self):
223 ffb1e7a8 Sofia Papagiannaki
        if not self.approval_date:
224 ffb1e7a8 Sofia Papagiannaki
            return True
225 ffb1e7a8 Sofia Papagiannaki
        return False
226 5ce3ce4f Sofia Papagiannaki
227 8e45d6fd Sofia Papagiannaki
    @property
228 ffb1e7a8 Sofia Papagiannaki
    def is_enabled(self):
229 8e45d6fd Sofia Papagiannaki
        if self.is_disabled:
230 8e45d6fd Sofia Papagiannaki
            return False
231 8e45d6fd Sofia Papagiannaki
        if not self.issue_date:
232 8e45d6fd Sofia Papagiannaki
            return False
233 8e45d6fd Sofia Papagiannaki
        if not self.expiration_date:
234 8e45d6fd Sofia Papagiannaki
            return True
235 8e45d6fd Sofia Papagiannaki
        now = datetime.now()
236 8e45d6fd Sofia Papagiannaki
        if self.issue_date > now:
237 8e45d6fd Sofia Papagiannaki
            return False
238 8e45d6fd Sofia Papagiannaki
        if now >= self.expiration_date:
239 8e45d6fd Sofia Papagiannaki
            return False
240 8e45d6fd Sofia Papagiannaki
        return True
241 5ce3ce4f Sofia Papagiannaki
242 ffb1e7a8 Sofia Papagiannaki
    def enable(self):
243 fc1e2f02 Sofia Papagiannaki
        if self.is_enabled:
244 fc1e2f02 Sofia Papagiannaki
            return
245 8e45d6fd Sofia Papagiannaki
        self.approval_date = datetime.now()
246 8e45d6fd Sofia Papagiannaki
        self.save()
247 20d50182 Sofia Papagiannaki
        quota_disturbed.send(sender=self, users=self.approved_members)
248 f8f86e83 root
        #propagate_groupmembers_quota.apply_async(
249 f8f86e83 root
        #    args=[self], eta=self.issue_date)
250 f8f86e83 root
        #propagate_groupmembers_quota.apply_async(
251 f8f86e83 root
        #    args=[self], eta=self.expiration_date)
252 5ce3ce4f Sofia Papagiannaki
253 ffb1e7a8 Sofia Papagiannaki
    def disable(self):
254 fc1e2f02 Sofia Papagiannaki
        if self.is_disabled:
255 fc1e2f02 Sofia Papagiannaki
            return
256 8e45d6fd Sofia Papagiannaki
        self.approval_date = None
257 8e45d6fd Sofia Papagiannaki
        self.save()
258 fc1e2f02 Sofia Papagiannaki
        quota_disturbed.send(sender=self, users=self.approved_members)
259 5ce3ce4f Sofia Papagiannaki
260 01ac12d5 Sofia Papagiannaki
    def approve_member(self, person):
261 0f4fa26d Sofia Papagiannaki
        m, created = self.membership_set.get_or_create(person=person)
262 e1a80257 Sofia Papagiannaki
        m.approve()
263 5ce3ce4f Sofia Papagiannaki
264 01ac12d5 Sofia Papagiannaki
    @property
265 01ac12d5 Sofia Papagiannaki
    def members(self):
266 032ade79 Sofia Papagiannaki
        q = self.membership_set.select_related().all()
267 032ade79 Sofia Papagiannaki
        return [m.person for m in q]
268 2e90e3ec Kostas Papadimitriou
269 01ac12d5 Sofia Papagiannaki
    @property
270 01ac12d5 Sofia Papagiannaki
    def approved_members(self):
271 032ade79 Sofia Papagiannaki
        q = self.membership_set.select_related().all()
272 032ade79 Sofia Papagiannaki
        return [m.person for m in q if m.is_approved]
273 5ce3ce4f Sofia Papagiannaki
274 01ac12d5 Sofia Papagiannaki
    @property
275 ffb1e7a8 Sofia Papagiannaki
    def quota(self):
276 0f4fa26d Sofia Papagiannaki
        d = defaultdict(int)
277 dfdc64d2 Sofia Papagiannaki
        for q in self.astakosgroupquota_set.select_related().all():
278 9ee0c6a2 Sofia Papagiannaki
            d[q.resource] += q.uplimit or inf
279 ffb1e7a8 Sofia Papagiannaki
        return d
280 2e90e3ec Kostas Papadimitriou
281 9a06d96f Olga Brani
    def add_policy(self, service, resource, uplimit, update=True):
282 9a06d96f Olga Brani
        """Raises ObjectDoesNotExist, IntegrityError"""
283 9a06d96f Olga Brani
        resource = Resource.objects.get(service__name=service, name=resource)
284 9a06d96f Olga Brani
        if update:
285 9a06d96f Olga Brani
            AstakosGroupQuota.objects.update_or_create(
286 9a06d96f Olga Brani
                group=self,
287 9a06d96f Olga Brani
                resource=resource,
288 9a06d96f Olga Brani
                defaults={'uplimit': uplimit}
289 9a06d96f Olga Brani
            )
290 9a06d96f Olga Brani
        else:
291 9a06d96f Olga Brani
            q = self.astakosgroupquota_set
292 9a06d96f Olga Brani
            q.create(resource=resource, uplimit=uplimit)
293 2e90e3ec Kostas Papadimitriou
294 9a06d96f Olga Brani
    @property
295 9a06d96f Olga Brani
    def policies(self):
296 9a06d96f Olga Brani
        return self.astakosgroupquota_set.select_related().all()
297 9a06d96f Olga Brani
298 9a06d96f Olga Brani
    @policies.setter
299 9a06d96f Olga Brani
    def policies(self, policies):
300 9a06d96f Olga Brani
        for p in policies:
301 9a06d96f Olga Brani
            service = p.get('service', None)
302 9a06d96f Olga Brani
            resource = p.get('resource', None)
303 9a06d96f Olga Brani
            uplimit = p.get('uplimit', 0)
304 9a06d96f Olga Brani
            update = p.get('update', True)
305 9a06d96f Olga Brani
            self.add_policy(service, resource, uplimit, update)
306 2e90e3ec Kostas Papadimitriou
307 01ac12d5 Sofia Papagiannaki
    @property
308 0f4fa26d Sofia Papagiannaki
    def owners(self):
309 0f4fa26d Sofia Papagiannaki
        return self.owner.all()
310 5ce3ce4f Sofia Papagiannaki
311 032ade79 Sofia Papagiannaki
    @property
312 032ade79 Sofia Papagiannaki
    def owner_details(self):
313 032ade79 Sofia Papagiannaki
        return self.owner.select_related().all()
314 032ade79 Sofia Papagiannaki
315 0f4fa26d Sofia Papagiannaki
    @owners.setter
316 0f4fa26d Sofia Papagiannaki
    def owners(self, l):
317 0f4fa26d Sofia Papagiannaki
        self.owner = l
318 0f4fa26d Sofia Papagiannaki
        map(self.approve_member, l)
319 8e45d6fd Sofia Papagiannaki
320 b4789608 Sofia Papagiannaki
_default_quota = {}
321 b4789608 Sofia Papagiannaki
def get_default_quota():
322 b4789608 Sofia Papagiannaki
    global _default_quota
323 b4789608 Sofia Papagiannaki
    if _default_quota:
324 b4789608 Sofia Papagiannaki
        return _default_quota
325 b4789608 Sofia Papagiannaki
    for s, data in SERVICES.iteritems():
326 b4789608 Sofia Papagiannaki
        map(
327 b4789608 Sofia Papagiannaki
            lambda d:_default_quota.update(
328 b4789608 Sofia Papagiannaki
                {'%s%s%s' % (s, RESOURCE_SEPARATOR, d.get('name')):d.get('uplimit', 0)}
329 b4789608 Sofia Papagiannaki
            ),
330 b4789608 Sofia Papagiannaki
            data.get('resources', {})
331 b4789608 Sofia Papagiannaki
        )
332 b4789608 Sofia Papagiannaki
    return _default_quota
333 d2633501 Kostas Papadimitriou
334 6b9a334b Sofia Papagiannaki
class AstakosUserManager(UserManager):
335 d2633501 Kostas Papadimitriou
336 d2633501 Kostas Papadimitriou
    def get_auth_provider_user(self, provider, **kwargs):
337 d2633501 Kostas Papadimitriou
        """
338 d2633501 Kostas Papadimitriou
        Retrieve AstakosUser instance associated with the specified third party
339 d2633501 Kostas Papadimitriou
        id.
340 d2633501 Kostas Papadimitriou
        """
341 d2633501 Kostas Papadimitriou
        kwargs = dict(map(lambda x: ('auth_providers__%s' % x[0], x[1]),
342 d2633501 Kostas Papadimitriou
                          kwargs.iteritems()))
343 d2633501 Kostas Papadimitriou
        return self.get(auth_providers__module=provider, **kwargs)
344 d2633501 Kostas Papadimitriou
345 c630fee6 Kostas Papadimitriou
    def get_by_email(self, email):
346 c630fee6 Kostas Papadimitriou
        return self.get(email=email)
347 c630fee6 Kostas Papadimitriou
348 e5966bd9 Kostas Papadimitriou
    def get_by_identifier(self, email_or_username, **kwargs):
349 e5966bd9 Kostas Papadimitriou
        try:
350 e5966bd9 Kostas Papadimitriou
            return self.get(email__iexact=email_or_username, **kwargs)
351 e5966bd9 Kostas Papadimitriou
        except AstakosUser.DoesNotExist:
352 e5966bd9 Kostas Papadimitriou
            return self.get(username__iexact=email_or_username, **kwargs)
353 e5966bd9 Kostas Papadimitriou
354 e5966bd9 Kostas Papadimitriou
    def user_exists(self, email_or_username, **kwargs):
355 e5966bd9 Kostas Papadimitriou
        qemail = Q(email__iexact=email_or_username)
356 e5966bd9 Kostas Papadimitriou
        qusername = Q(username__iexact=email_or_username)
357 e5966bd9 Kostas Papadimitriou
        return self.filter(qemail | qusername).exists()
358 e5966bd9 Kostas Papadimitriou
359 e5966bd9 Kostas Papadimitriou
360 0905ccd2 Sofia Papagiannaki
class AstakosUser(User):
361 890b0eaf Sofia Papagiannaki
    """
362 890b0eaf Sofia Papagiannaki
    Extends ``django.contrib.auth.models.User`` by defining additional fields.
363 890b0eaf Sofia Papagiannaki
    """
364 e1a80257 Sofia Papagiannaki
    affiliation = models.CharField(_('Affiliation'), max_length=255, blank=True,
365 d2633501 Kostas Papadimitriou
                                   null=True)
366 d2633501 Kostas Papadimitriou
367 d2633501 Kostas Papadimitriou
    # DEPRECATED FIELDS: provider, third_party_identifier moved in
368 d2633501 Kostas Papadimitriou
    #                    AstakosUserProvider model.
369 e1a80257 Sofia Papagiannaki
    provider = models.CharField(_('Provider'), max_length=255, blank=True,
370 d2633501 Kostas Papadimitriou
                                null=True)
371 d2633501 Kostas Papadimitriou
    # ex. screen_name for twitter, eppn for shibboleth
372 e1a80257 Sofia Papagiannaki
    third_party_identifier = models.CharField(_('Third-party identifier'),
373 d2633501 Kostas Papadimitriou
                                              max_length=255, null=True,
374 d2633501 Kostas Papadimitriou
                                              blank=True)
375 d2633501 Kostas Papadimitriou
376 6c736ed7 Kostas Papadimitriou
377 64cd4730 Antony Chazapis
    #for invitations
378 92defad4 Sofia Papagiannaki
    user_level = DEFAULT_USER_LEVEL
379 e1a80257 Sofia Papagiannaki
    level = models.IntegerField(_('Inviter level'), default=user_level)
380 5ce3ce4f Sofia Papagiannaki
    invitations = models.IntegerField(
381 e1a80257 Sofia Papagiannaki
        _('Invitations left'), default=INVITATIONS_PER_LEVEL.get(user_level, 0))
382 6c736ed7 Kostas Papadimitriou
383 e1a80257 Sofia Papagiannaki
    auth_token = models.CharField(_('Authentication Token'), max_length=32,
384 0905ccd2 Sofia Papagiannaki
                                  null=True, blank=True)
385 e1a80257 Sofia Papagiannaki
    auth_token_created = models.DateTimeField(_('Token creation date'), null=True)
386 5ce3ce4f Sofia Papagiannaki
    auth_token_expires = models.DateTimeField(
387 e1a80257 Sofia Papagiannaki
        _('Token expiration date'), null=True)
388 6c736ed7 Kostas Papadimitriou
389 e1a80257 Sofia Papagiannaki
    updated = models.DateTimeField(_('Update date'))
390 e1a80257 Sofia Papagiannaki
    is_verified = models.BooleanField(_('Is verified?'), default=False)
391 6c736ed7 Kostas Papadimitriou
392 e1a80257 Sofia Papagiannaki
    email_verified = models.BooleanField(_('Email verified?'), default=False)
393 6c736ed7 Kostas Papadimitriou
394 e1a80257 Sofia Papagiannaki
    has_credits = models.BooleanField(_('Has credits?'), default=False)
395 5ce3ce4f Sofia Papagiannaki
    has_signed_terms = models.BooleanField(
396 e1a80257 Sofia Papagiannaki
        _('I agree with the terms'), default=False)
397 5ce3ce4f Sofia Papagiannaki
    date_signed_terms = models.DateTimeField(
398 e1a80257 Sofia Papagiannaki
        _('Signed terms date'), null=True, blank=True)
399 5ce3ce4f Sofia Papagiannaki
400 5ce3ce4f Sofia Papagiannaki
    activation_sent = models.DateTimeField(
401 e1a80257 Sofia Papagiannaki
        _('Activation sent data'), null=True, blank=True)
402 5ce3ce4f Sofia Papagiannaki
403 5ce3ce4f Sofia Papagiannaki
    policy = models.ManyToManyField(
404 5ce3ce4f Sofia Papagiannaki
        Resource, null=True, through='AstakosUserQuota')
405 5ce3ce4f Sofia Papagiannaki
406 836a0fb0 Kostas Papadimitriou
    uuid = models.CharField(max_length=255, null=True, blank=False, unique=True)
407 836a0fb0 Kostas Papadimitriou
408 5ce3ce4f Sofia Papagiannaki
    astakos_groups = models.ManyToManyField(
409 5ce3ce4f Sofia Papagiannaki
        AstakosGroup, verbose_name=_('agroups'), blank=True,
410 ae497612 Olga Brani
        help_text=_(astakos_messages.ASTAKOSUSER_GROUPS_HELP),
411 8e45d6fd Sofia Papagiannaki
        through='Membership')
412 5ce3ce4f Sofia Papagiannaki
413 18ffbee1 Sofia Papagiannaki
    __has_signed_terms = False
414 e1a80257 Sofia Papagiannaki
    disturbed_quota = models.BooleanField(_('Needs quotaholder syncing'),
415 9a06d96f Olga Brani
                                           default=False, db_index=True)
416 d2633501 Kostas Papadimitriou
417 d2633501 Kostas Papadimitriou
    objects = AstakosUserManager()
418 fbaa4f3c Kostas Papadimitriou
419 836a0fb0 Kostas Papadimitriou
420 5ce3ce4f Sofia Papagiannaki
    owner = models.ManyToManyField(
421 5ce3ce4f Sofia Papagiannaki
        AstakosGroup, related_name='owner', null=True)
422 5ce3ce4f Sofia Papagiannaki
423 18ffbee1 Sofia Papagiannaki
    def __init__(self, *args, **kwargs):
424 18ffbee1 Sofia Papagiannaki
        super(AstakosUser, self).__init__(*args, **kwargs)
425 18ffbee1 Sofia Papagiannaki
        self.__has_signed_terms = self.has_signed_terms
426 a3637508 Sofia Papagiannaki
        if not self.id:
427 18ffbee1 Sofia Papagiannaki
            self.is_active = False
428 5ce3ce4f Sofia Papagiannaki
429 0905ccd2 Sofia Papagiannaki
    @property
430 0905ccd2 Sofia Papagiannaki
    def realname(self):
431 5ce3ce4f Sofia Papagiannaki
        return '%s %s' % (self.first_name, self.last_name)
432 6c736ed7 Kostas Papadimitriou
433 0905ccd2 Sofia Papagiannaki
    @realname.setter
434 0905ccd2 Sofia Papagiannaki
    def realname(self, value):
435 0905ccd2 Sofia Papagiannaki
        parts = value.split(' ')
436 0905ccd2 Sofia Papagiannaki
        if len(parts) == 2:
437 0905ccd2 Sofia Papagiannaki
            self.first_name = parts[0]
438 0905ccd2 Sofia Papagiannaki
            self.last_name = parts[1]
439 0905ccd2 Sofia Papagiannaki
        else:
440 0905ccd2 Sofia Papagiannaki
            self.last_name = parts[0]
441 6c736ed7 Kostas Papadimitriou
442 9a06d96f Olga Brani
    def add_permission(self, pname):
443 9a06d96f Olga Brani
        if self.has_perm(pname):
444 9a06d96f Olga Brani
            return
445 e65c21df Georgios D. Tsoukalas
        p, created = Permission.objects.get_or_create(
446 e65c21df Georgios D. Tsoukalas
                                    codename=pname,
447 e65c21df Georgios D. Tsoukalas
                                    name=pname.capitalize(),
448 e65c21df Georgios D. Tsoukalas
                                    content_type=get_content_type())
449 9a06d96f Olga Brani
        self.user_permissions.add(p)
450 9a06d96f Olga Brani
451 9a06d96f Olga Brani
    def remove_permission(self, pname):
452 9a06d96f Olga Brani
        if self.has_perm(pname):
453 9a06d96f Olga Brani
            return
454 9a06d96f Olga Brani
        p = Permission.objects.get(codename=pname,
455 e65c21df Georgios D. Tsoukalas
                                   content_type=get_content_type())
456 9a06d96f Olga Brani
        self.user_permissions.remove(p)
457 9a06d96f Olga Brani
458 64cd4730 Antony Chazapis
    @property
459 64cd4730 Antony Chazapis
    def invitation(self):
460 64cd4730 Antony Chazapis
        try:
461 9fb8e808 Sofia Papagiannaki
            return Invitation.objects.get(username=self.email)
462 64cd4730 Antony Chazapis
        except Invitation.DoesNotExist:
463 64cd4730 Antony Chazapis
            return None
464 6c736ed7 Kostas Papadimitriou
465 ffb1e7a8 Sofia Papagiannaki
    @property
466 ffb1e7a8 Sofia Papagiannaki
    def quota(self):
467 9a06d96f Olga Brani
        """Returns a dict with the sum of quota limits per resource"""
468 ffb1e7a8 Sofia Papagiannaki
        d = defaultdict(int)
469 b4789608 Sofia Papagiannaki
        default_quota = get_default_quota()
470 b4789608 Sofia Papagiannaki
        d.update(default_quota)
471 9a06d96f Olga Brani
        for q in self.policies:
472 9ee0c6a2 Sofia Papagiannaki
            d[q.resource] += q.uplimit or inf
473 ccab6eb5 Sofia Papagiannaki
        for m in self.projectmembership_set.select_related().all():
474 ccab6eb5 Sofia Papagiannaki
            if not m.acceptance_date:
475 fc1e2f02 Sofia Papagiannaki
                continue
476 ccab6eb5 Sofia Papagiannaki
            p = m.project
477 ccab6eb5 Sofia Papagiannaki
            if not p.is_active:
478 ffb1e7a8 Sofia Papagiannaki
                continue
479 ee45eb81 Giorgos Korfiatis
            grants = p.application.projectresourcegrant_set.all()
480 ccab6eb5 Sofia Papagiannaki
            for g in grants:
481 73fbaec4 Sofia Papagiannaki
                d[str(g.resource)] += g.member_capacity or inf
482 ffb1e7a8 Sofia Papagiannaki
        # TODO set default for remaining
483 ffb1e7a8 Sofia Papagiannaki
        return d
484 5ce3ce4f Sofia Papagiannaki
485 9a06d96f Olga Brani
    @property
486 9a06d96f Olga Brani
    def policies(self):
487 9a06d96f Olga Brani
        return self.astakosuserquota_set.select_related().all()
488 9a06d96f Olga Brani
489 9a06d96f Olga Brani
    @policies.setter
490 9a06d96f Olga Brani
    def policies(self, policies):
491 9a06d96f Olga Brani
        for p in policies:
492 9a06d96f Olga Brani
            service = policies.get('service', None)
493 9a06d96f Olga Brani
            resource = policies.get('resource', None)
494 9a06d96f Olga Brani
            uplimit = policies.get('uplimit', 0)
495 9a06d96f Olga Brani
            update = policies.get('update', True)
496 9a06d96f Olga Brani
            self.add_policy(service, resource, uplimit, update)
497 9a06d96f Olga Brani
498 9a06d96f Olga Brani
    def add_policy(self, service, resource, uplimit, update=True):
499 9a06d96f Olga Brani
        """Raises ObjectDoesNotExist, IntegrityError"""
500 9a06d96f Olga Brani
        resource = Resource.objects.get(service__name=service, name=resource)
501 9a06d96f Olga Brani
        if update:
502 9a06d96f Olga Brani
            AstakosUserQuota.objects.update_or_create(user=self,
503 9a06d96f Olga Brani
                                                      resource=resource,
504 9a06d96f Olga Brani
                                                      defaults={'uplimit': uplimit})
505 9a06d96f Olga Brani
        else:
506 9a06d96f Olga Brani
            q = self.astakosuserquota_set
507 9a06d96f Olga Brani
            q.create(resource=resource, uplimit=uplimit)
508 9a06d96f Olga Brani
509 9a06d96f Olga Brani
    def remove_policy(self, service, resource):
510 9a06d96f Olga Brani
        """Raises ObjectDoesNotExist, IntegrityError"""
511 9a06d96f Olga Brani
        resource = Resource.objects.get(service__name=service, name=resource)
512 9a06d96f Olga Brani
        q = self.policies.get(resource=resource).delete()
513 9a06d96f Olga Brani
514 836a0fb0 Kostas Papadimitriou
    def update_uuid(self):
515 836a0fb0 Kostas Papadimitriou
        while not self.uuid:
516 836a0fb0 Kostas Papadimitriou
            uuid_val =  str(uuid.uuid4())
517 836a0fb0 Kostas Papadimitriou
            try:
518 836a0fb0 Kostas Papadimitriou
                AstakosUser.objects.get(uuid=uuid_val)
519 836a0fb0 Kostas Papadimitriou
            except AstakosUser.DoesNotExist, e:
520 836a0fb0 Kostas Papadimitriou
                self.uuid = uuid_val
521 836a0fb0 Kostas Papadimitriou
        return self.uuid
522 836a0fb0 Kostas Papadimitriou
523 9a06d96f Olga Brani
    @property
524 9a06d96f Olga Brani
    def extended_groups(self):
525 9a06d96f Olga Brani
        return self.membership_set.select_related().all()
526 9a06d96f Olga Brani
527 9a06d96f Olga Brani
    @extended_groups.setter
528 9a06d96f Olga Brani
    def extended_groups(self, groups):
529 9a06d96f Olga Brani
        #TODO exceptions
530 40a0cd8b Sofia Papagiannaki
        for name in (groups or ()):
531 9a06d96f Olga Brani
            group = AstakosGroup.objects.get(name=name)
532 9a06d96f Olga Brani
            self.membership_set.create(group=group)
533 9a06d96f Olga Brani
534 64cd4730 Antony Chazapis
    def save(self, update_timestamps=True, **kwargs):
535 64cd4730 Antony Chazapis
        if update_timestamps:
536 64cd4730 Antony Chazapis
            if not self.id:
537 0905ccd2 Sofia Papagiannaki
                self.date_joined = datetime.now()
538 64cd4730 Antony Chazapis
            self.updated = datetime.now()
539 5ce3ce4f Sofia Papagiannaki
540 18ffbee1 Sofia Papagiannaki
        # update date_signed_terms if necessary
541 18ffbee1 Sofia Papagiannaki
        if self.__has_signed_terms != self.has_signed_terms:
542 18ffbee1 Sofia Papagiannaki
            self.date_signed_terms = datetime.now()
543 5ce3ce4f Sofia Papagiannaki
544 836a0fb0 Kostas Papadimitriou
        self.update_uuid()
545 836a0fb0 Kostas Papadimitriou
546 836a0fb0 Kostas Papadimitriou
        if self.username != self.email.lower():
547 9c01d9d1 Sofia Papagiannaki
            # set username
548 e5966bd9 Kostas Papadimitriou
            self.username = self.email.lower()
549 fbaa4f3c Kostas Papadimitriou
550 591d0505 Sofia Papagiannaki
        self.validate_unique_email_isactive()
551 5ce3ce4f Sofia Papagiannaki
552 0905ccd2 Sofia Papagiannaki
        super(AstakosUser, self).save(**kwargs)
553 2e90e3ec Kostas Papadimitriou
554 bf0c6de5 Sofia Papagiannaki
    def renew_token(self, flush_sessions=False, current_key=None):
555 64cd4730 Antony Chazapis
        md5 = hashlib.md5()
556 8f8c43b2 Sofia Papagiannaki
        md5.update(settings.SECRET_KEY)
557 0905ccd2 Sofia Papagiannaki
        md5.update(self.username)
558 64cd4730 Antony Chazapis
        md5.update(self.realname.encode('ascii', 'ignore'))
559 64cd4730 Antony Chazapis
        md5.update(asctime())
560 2e90e3ec Kostas Papadimitriou
561 64cd4730 Antony Chazapis
        self.auth_token = b64encode(md5.digest())
562 64cd4730 Antony Chazapis
        self.auth_token_created = datetime.now()
563 64cd4730 Antony Chazapis
        self.auth_token_expires = self.auth_token_created + \
564 92defad4 Sofia Papagiannaki
                                  timedelta(hours=AUTH_TOKEN_DURATION)
565 bf0c6de5 Sofia Papagiannaki
        if flush_sessions:
566 bf0c6de5 Sofia Papagiannaki
            self.flush_sessions(current_key)
567 111f3da6 Sofia Papagiannaki
        msg = 'Token renewed for %s' % self.email
568 aab4d540 Sofia Papagiannaki
        logger.log(LOGGING_LEVEL, msg)
569 6c736ed7 Kostas Papadimitriou
570 bf0c6de5 Sofia Papagiannaki
    def flush_sessions(self, current_key=None):
571 bf0c6de5 Sofia Papagiannaki
        q = self.sessions
572 bf0c6de5 Sofia Papagiannaki
        if current_key:
573 bf0c6de5 Sofia Papagiannaki
            q = q.exclude(session_key=current_key)
574 2e90e3ec Kostas Papadimitriou
575 bf0c6de5 Sofia Papagiannaki
        keys = q.values_list('session_key', flat=True)
576 bf0c6de5 Sofia Papagiannaki
        if keys:
577 bf0c6de5 Sofia Papagiannaki
            msg = 'Flushing sessions: %s' % ','.join(keys)
578 c0b26605 Sofia Papagiannaki
            logger.log(LOGGING_LEVEL, msg, [])
579 bf0c6de5 Sofia Papagiannaki
        engine = import_module(settings.SESSION_ENGINE)
580 bf0c6de5 Sofia Papagiannaki
        for k in keys:
581 bf0c6de5 Sofia Papagiannaki
            s = engine.SessionStore(k)
582 bf0c6de5 Sofia Papagiannaki
            s.flush()
583 bf0c6de5 Sofia Papagiannaki
584 64cd4730 Antony Chazapis
    def __unicode__(self):
585 3abf6c78 Sofia Papagiannaki
        return '%s (%s)' % (self.realname, self.email)
586 5ce3ce4f Sofia Papagiannaki
587 0a569195 Sofia Papagiannaki
    def conflicting_email(self):
588 5ce3ce4f Sofia Papagiannaki
        q = AstakosUser.objects.exclude(username=self.username)
589 789a5951 Sofia Papagiannaki
        q = q.filter(email__iexact=self.email)
590 0a569195 Sofia Papagiannaki
        if q.count() != 0:
591 0a569195 Sofia Papagiannaki
            return True
592 0a569195 Sofia Papagiannaki
        return False
593 5ce3ce4f Sofia Papagiannaki
594 591d0505 Sofia Papagiannaki
    def validate_unique_email_isactive(self):
595 0a569195 Sofia Papagiannaki
        """
596 0a569195 Sofia Papagiannaki
        Implements a unique_together constraint for email and is_active fields.
597 0a569195 Sofia Papagiannaki
        """
598 e6759494 Sofia Papagiannaki
        q = AstakosUser.objects.all()
599 0a569195 Sofia Papagiannaki
        q = q.filter(email = self.email)
600 e6759494 Sofia Papagiannaki
        if self.id:
601 e6759494 Sofia Papagiannaki
            q = q.filter(~Q(id = self.id))
602 0a569195 Sofia Papagiannaki
        if q.count() != 0:
603 73fbaec4 Sofia Papagiannaki
            m = 'Another account with the same email = %(email)s & \
604 425e2e95 Sofia Papagiannaki
                is_active = %(is_active)s found.' % self.__dict__
605 73fbaec4 Sofia Papagiannaki
            raise ValidationError(m)
606 5ce3ce4f Sofia Papagiannaki
607 34a76cdb Kostas Papadimitriou
    def email_change_is_pending(self):
608 34a76cdb Kostas Papadimitriou
        return self.emailchanges.count() > 0
609 34a76cdb Kostas Papadimitriou
610 fcf90160 Sofia Papagiannaki
    @property
611 09e7393c Sofia Papagiannaki
    def signed_terms(self):
612 09e7393c Sofia Papagiannaki
        term = get_latest_terms()
613 09e7393c Sofia Papagiannaki
        if not term:
614 09e7393c Sofia Papagiannaki
            return True
615 09e7393c Sofia Papagiannaki
        if not self.has_signed_terms:
616 09e7393c Sofia Papagiannaki
            return False
617 09e7393c Sofia Papagiannaki
        if not self.date_signed_terms:
618 09e7393c Sofia Papagiannaki
            return False
619 09e7393c Sofia Papagiannaki
        if self.date_signed_terms < term.date:
620 09e7393c Sofia Papagiannaki
            self.has_signed_terms = False
621 f0f92965 Sofia Papagiannaki
            self.date_signed_terms = None
622 09e7393c Sofia Papagiannaki
            self.save()
623 09e7393c Sofia Papagiannaki
            return False
624 09e7393c Sofia Papagiannaki
        return True
625 09e7393c Sofia Papagiannaki
626 d2633501 Kostas Papadimitriou
    def set_invitations_level(self):
627 d2633501 Kostas Papadimitriou
        """
628 d2633501 Kostas Papadimitriou
        Update user invitation level
629 d2633501 Kostas Papadimitriou
        """
630 d2633501 Kostas Papadimitriou
        level = self.invitation.inviter.level + 1
631 d2633501 Kostas Papadimitriou
        self.level = level
632 d2633501 Kostas Papadimitriou
        self.invitations = INVITATIONS_PER_LEVEL.get(level, 0)
633 d2633501 Kostas Papadimitriou
634 d2633501 Kostas Papadimitriou
    def can_login_with_auth_provider(self, provider):
635 d2633501 Kostas Papadimitriou
        if not self.has_auth_provider(provider):
636 d2633501 Kostas Papadimitriou
            return False
637 d2633501 Kostas Papadimitriou
        else:
638 d2633501 Kostas Papadimitriou
            return auth_providers.get_provider(provider).is_available_for_login()
639 d2633501 Kostas Papadimitriou
640 f432088a Kostas Papadimitriou
    def can_add_auth_provider(self, provider, **kwargs):
641 d2633501 Kostas Papadimitriou
        provider_settings = auth_providers.get_provider(provider)
642 c838d05c Kostas Papadimitriou
643 c838d05c Kostas Papadimitriou
        if not provider_settings.is_available_for_add():
644 d2633501 Kostas Papadimitriou
            return False
645 f432088a Kostas Papadimitriou
646 d2633501 Kostas Papadimitriou
        if self.has_auth_provider(provider) and \
647 d2633501 Kostas Papadimitriou
           provider_settings.one_per_user:
648 d2633501 Kostas Papadimitriou
            return False
649 f432088a Kostas Papadimitriou
650 c630fee6 Kostas Papadimitriou
        if 'provider_info' in kwargs:
651 c630fee6 Kostas Papadimitriou
            kwargs.pop('provider_info')
652 c630fee6 Kostas Papadimitriou
653 f432088a Kostas Papadimitriou
        if 'identifier' in kwargs:
654 f432088a Kostas Papadimitriou
            try:
655 f432088a Kostas Papadimitriou
                # provider with specified params already exist
656 f432088a Kostas Papadimitriou
                existing_user = AstakosUser.objects.get_auth_provider_user(provider,
657 f432088a Kostas Papadimitriou
                                                                   **kwargs)
658 f432088a Kostas Papadimitriou
            except AstakosUser.DoesNotExist:
659 f432088a Kostas Papadimitriou
                return True
660 f432088a Kostas Papadimitriou
            else:
661 f432088a Kostas Papadimitriou
                return False
662 f432088a Kostas Papadimitriou
663 d2633501 Kostas Papadimitriou
        return True
664 d2633501 Kostas Papadimitriou
665 badcb2a9 Kostas Papadimitriou
    def can_remove_auth_provider(self, module):
666 badcb2a9 Kostas Papadimitriou
        provider = auth_providers.get_provider(module)
667 badcb2a9 Kostas Papadimitriou
        existing = self.get_active_auth_providers()
668 badcb2a9 Kostas Papadimitriou
        existing_for_provider = self.get_active_auth_providers(module=module)
669 badcb2a9 Kostas Papadimitriou
670 badcb2a9 Kostas Papadimitriou
        if len(existing) <= 1:
671 badcb2a9 Kostas Papadimitriou
            return False
672 badcb2a9 Kostas Papadimitriou
673 badcb2a9 Kostas Papadimitriou
        if len(existing_for_provider) == 1 and provider.is_required():
674 d2633501 Kostas Papadimitriou
            return False
675 badcb2a9 Kostas Papadimitriou
676 d2633501 Kostas Papadimitriou
        return True
677 d2633501 Kostas Papadimitriou
678 d2633501 Kostas Papadimitriou
    def can_change_password(self):
679 d2633501 Kostas Papadimitriou
        return self.has_auth_provider('local', auth_backend='astakos')
680 d2633501 Kostas Papadimitriou
681 badcb2a9 Kostas Papadimitriou
    def has_required_auth_providers(self):
682 badcb2a9 Kostas Papadimitriou
        required = auth_providers.REQUIRED_PROVIDERS
683 badcb2a9 Kostas Papadimitriou
        for provider in required:
684 badcb2a9 Kostas Papadimitriou
            if not self.has_auth_provider(provider):
685 badcb2a9 Kostas Papadimitriou
                return False
686 badcb2a9 Kostas Papadimitriou
        return True
687 badcb2a9 Kostas Papadimitriou
688 d2633501 Kostas Papadimitriou
    def has_auth_provider(self, provider, **kwargs):
689 d2633501 Kostas Papadimitriou
        return bool(self.auth_providers.filter(module=provider,
690 d2633501 Kostas Papadimitriou
                                               **kwargs).count())
691 d2633501 Kostas Papadimitriou
692 d2633501 Kostas Papadimitriou
    def add_auth_provider(self, provider, **kwargs):
693 3a72a5d4 Kostas Papadimitriou
        info_data = ''
694 3a72a5d4 Kostas Papadimitriou
        if 'provider_info' in kwargs:
695 c630fee6 Kostas Papadimitriou
            info_data = kwargs.pop('provider_info')
696 c630fee6 Kostas Papadimitriou
            if isinstance(info_data, dict):
697 c630fee6 Kostas Papadimitriou
                info_data = json.dumps(info_data)
698 3a72a5d4 Kostas Papadimitriou
699 f432088a Kostas Papadimitriou
        if self.can_add_auth_provider(provider, **kwargs):
700 3a72a5d4 Kostas Papadimitriou
            self.auth_providers.create(module=provider, active=True,
701 3a72a5d4 Kostas Papadimitriou
                                       info_data=info_data,
702 3a72a5d4 Kostas Papadimitriou
                                       **kwargs)
703 f432088a Kostas Papadimitriou
        else:
704 f432088a Kostas Papadimitriou
            raise Exception('Cannot add provider')
705 d2633501 Kostas Papadimitriou
706 d2633501 Kostas Papadimitriou
    def add_pending_auth_provider(self, pending):
707 d2633501 Kostas Papadimitriou
        """
708 d2633501 Kostas Papadimitriou
        Convert PendingThirdPartyUser object to AstakosUserAuthProvider entry for
709 d2633501 Kostas Papadimitriou
        the current user.
710 d2633501 Kostas Papadimitriou
        """
711 d2633501 Kostas Papadimitriou
        if not isinstance(pending, PendingThirdPartyUser):
712 d2633501 Kostas Papadimitriou
            pending = PendingThirdPartyUser.objects.get(token=pending)
713 d2633501 Kostas Papadimitriou
714 d2633501 Kostas Papadimitriou
        provider = self.add_auth_provider(pending.provider,
715 c630fee6 Kostas Papadimitriou
                               identifier=pending.third_party_identifier,
716 c630fee6 Kostas Papadimitriou
                                affiliation=pending.affiliation,
717 c630fee6 Kostas Papadimitriou
                                          provider_info=pending.info)
718 d2633501 Kostas Papadimitriou
719 fbaa4f3c Kostas Papadimitriou
        if email_re.match(pending.email or '') and pending.email != self.email:
720 d2633501 Kostas Papadimitriou
            self.additionalmail_set.get_or_create(email=pending.email)
721 d2633501 Kostas Papadimitriou
722 d2633501 Kostas Papadimitriou
        pending.delete()
723 d2633501 Kostas Papadimitriou
        return provider
724 d2633501 Kostas Papadimitriou
725 d2633501 Kostas Papadimitriou
    def remove_auth_provider(self, provider, **kwargs):
726 d2633501 Kostas Papadimitriou
        self.auth_providers.get(module=provider, **kwargs).delete()
727 d2633501 Kostas Papadimitriou
728 d2633501 Kostas Papadimitriou
    # user urls
729 d2633501 Kostas Papadimitriou
    def get_resend_activation_url(self):
730 c630fee6 Kostas Papadimitriou
        return reverse('send_activation', kwargs={'user_id': self.pk})
731 c630fee6 Kostas Papadimitriou
732 c630fee6 Kostas Papadimitriou
    def get_provider_remove_url(self, module, **kwargs):
733 c630fee6 Kostas Papadimitriou
        return reverse('remove_auth_provider', kwargs={
734 c630fee6 Kostas Papadimitriou
            'pk': self.auth_providers.get(module=module, **kwargs).pk})
735 d2633501 Kostas Papadimitriou
736 d2633501 Kostas Papadimitriou
    def get_activation_url(self, nxt=False):
737 d2633501 Kostas Papadimitriou
        url = "%s?auth=%s" % (reverse('astakos.im.views.activate'),
738 d2633501 Kostas Papadimitriou
                                 quote(self.auth_token))
739 d2633501 Kostas Papadimitriou
        if nxt:
740 d2633501 Kostas Papadimitriou
            url += "&next=%s" % quote(nxt)
741 d2633501 Kostas Papadimitriou
        return url
742 d2633501 Kostas Papadimitriou
743 d2633501 Kostas Papadimitriou
    def get_password_reset_url(self, token_generator=default_token_generator):
744 d2633501 Kostas Papadimitriou
        return reverse('django.contrib.auth.views.password_reset_confirm',
745 d2633501 Kostas Papadimitriou
                          kwargs={'uidb36':int_to_base36(self.id),
746 d2633501 Kostas Papadimitriou
                                  'token':token_generator.make_token(self)})
747 d2633501 Kostas Papadimitriou
748 d2633501 Kostas Papadimitriou
    def get_auth_providers(self):
749 d2633501 Kostas Papadimitriou
        return self.auth_providers.all()
750 d2633501 Kostas Papadimitriou
751 d2633501 Kostas Papadimitriou
    def get_available_auth_providers(self):
752 d2633501 Kostas Papadimitriou
        """
753 d2633501 Kostas Papadimitriou
        Returns a list of providers available for user to connect to.
754 d2633501 Kostas Papadimitriou
        """
755 d2633501 Kostas Papadimitriou
        providers = []
756 d2633501 Kostas Papadimitriou
        for module, provider_settings in auth_providers.PROVIDERS.iteritems():
757 f432088a Kostas Papadimitriou
            if self.can_add_auth_provider(module):
758 d2633501 Kostas Papadimitriou
                providers.append(provider_settings(self))
759 d2633501 Kostas Papadimitriou
760 d2633501 Kostas Papadimitriou
        return providers
761 d2633501 Kostas Papadimitriou
762 badcb2a9 Kostas Papadimitriou
    def get_active_auth_providers(self, **filters):
763 d2633501 Kostas Papadimitriou
        providers = []
764 badcb2a9 Kostas Papadimitriou
        for provider in self.auth_providers.active(**filters):
765 d2633501 Kostas Papadimitriou
            if auth_providers.get_provider(provider.module).is_available_for_login():
766 d2633501 Kostas Papadimitriou
                providers.append(provider)
767 d2633501 Kostas Papadimitriou
        return providers
768 d2633501 Kostas Papadimitriou
769 b778b6fa Kostas Papadimitriou
    @property
770 b778b6fa Kostas Papadimitriou
    def auth_providers_display(self):
771 b778b6fa Kostas Papadimitriou
        return ",".join(map(lambda x:unicode(x), self.auth_providers.active()))
772 b778b6fa Kostas Papadimitriou
773 c4b1a172 Kostas Papadimitriou
    def get_inactive_message(self):
774 c4b1a172 Kostas Papadimitriou
        msg_extra = ''
775 c4b1a172 Kostas Papadimitriou
        message = ''
776 c4b1a172 Kostas Papadimitriou
        if self.activation_sent:
777 c4b1a172 Kostas Papadimitriou
            if self.email_verified:
778 c4b1a172 Kostas Papadimitriou
                message = _(astakos_messages.ACCOUNT_INACTIVE)
779 c4b1a172 Kostas Papadimitriou
            else:
780 c4b1a172 Kostas Papadimitriou
                message = _(astakos_messages.ACCOUNT_PENDING_ACTIVATION)
781 47b77c8b Sofia Papagiannaki
                if MODERATION_ENABLED:
782 c4b1a172 Kostas Papadimitriou
                    msg_extra = _(astakos_messages.ACCOUNT_PENDING_ACTIVATION_HELP)
783 c4b1a172 Kostas Papadimitriou
                else:
784 c4b1a172 Kostas Papadimitriou
                    url = self.get_resend_activation_url()
785 c4b1a172 Kostas Papadimitriou
                    msg_extra = mark_safe(_(astakos_messages.ACCOUNT_PENDING_ACTIVATION_HELP) + \
786 a15a19b2 Kostas Papadimitriou
                                u' ' + \
787 c4b1a172 Kostas Papadimitriou
                                _('<a href="%s">%s?</a>') % (url,
788 c4b1a172 Kostas Papadimitriou
                                _(astakos_messages.ACCOUNT_RESEND_ACTIVATION_PROMPT)))
789 c4b1a172 Kostas Papadimitriou
        else:
790 47b77c8b Sofia Papagiannaki
            if MODERATION_ENABLED:
791 c4b1a172 Kostas Papadimitriou
                message = _(astakos_messages.ACCOUNT_PENDING_MODERATION)
792 c4b1a172 Kostas Papadimitriou
            else:
793 c4b1a172 Kostas Papadimitriou
                message = astakos_messages.ACCOUNT_PENDING_ACTIVATION
794 c4b1a172 Kostas Papadimitriou
                url = self.get_resend_activation_url()
795 c4b1a172 Kostas Papadimitriou
                msg_extra = mark_safe(_('<a href="%s">%s?</a>') % (url,
796 c4b1a172 Kostas Papadimitriou
                            _(astakos_messages.ACCOUNT_RESEND_ACTIVATION_PROMPT)))
797 c4b1a172 Kostas Papadimitriou
798 a15a19b2 Kostas Papadimitriou
        return mark_safe(message + u' '+ msg_extra)
799 c4b1a172 Kostas Papadimitriou
800 d2633501 Kostas Papadimitriou
801 d2633501 Kostas Papadimitriou
class AstakosUserAuthProviderManager(models.Manager):
802 d2633501 Kostas Papadimitriou
803 badcb2a9 Kostas Papadimitriou
    def active(self, **filters):
804 badcb2a9 Kostas Papadimitriou
        return self.filter(active=True, **filters)
805 d2633501 Kostas Papadimitriou
806 d2633501 Kostas Papadimitriou
807 d2633501 Kostas Papadimitriou
class AstakosUserAuthProvider(models.Model):
808 d2633501 Kostas Papadimitriou
    """
809 d2633501 Kostas Papadimitriou
    Available user authentication methods.
810 d2633501 Kostas Papadimitriou
    """
811 e1a80257 Sofia Papagiannaki
    affiliation = models.CharField(_('Affiliation'), max_length=255, blank=True,
812 d2633501 Kostas Papadimitriou
                                   null=True, default=None)
813 d2633501 Kostas Papadimitriou
    user = models.ForeignKey(AstakosUser, related_name='auth_providers')
814 e1a80257 Sofia Papagiannaki
    module = models.CharField(_('Provider'), max_length=255, blank=False,
815 d2633501 Kostas Papadimitriou
                                default='local')
816 e1a80257 Sofia Papagiannaki
    identifier = models.CharField(_('Third-party identifier'),
817 d2633501 Kostas Papadimitriou
                                              max_length=255, null=True,
818 d2633501 Kostas Papadimitriou
                                              blank=True)
819 d2633501 Kostas Papadimitriou
    active = models.BooleanField(default=True)
820 e1a80257 Sofia Papagiannaki
    auth_backend = models.CharField(_('Backend'), max_length=255, blank=False,
821 d2633501 Kostas Papadimitriou
                                   default='astakos')
822 3a72a5d4 Kostas Papadimitriou
    info_data = models.TextField(default="", null=True, blank=True)
823 c630fee6 Kostas Papadimitriou
    created = models.DateTimeField('Creation date', auto_now_add=True)
824 d2633501 Kostas Papadimitriou
825 d2633501 Kostas Papadimitriou
    objects = AstakosUserAuthProviderManager()
826 d2633501 Kostas Papadimitriou
827 d2633501 Kostas Papadimitriou
    class Meta:
828 d2633501 Kostas Papadimitriou
        unique_together = (('identifier', 'module', 'user'), )
829 c630fee6 Kostas Papadimitriou
        ordering = ('module', 'created')
830 d2633501 Kostas Papadimitriou
831 3a72a5d4 Kostas Papadimitriou
    def __init__(self, *args, **kwargs):
832 3a72a5d4 Kostas Papadimitriou
        super(AstakosUserAuthProvider, self).__init__(*args, **kwargs)
833 3a72a5d4 Kostas Papadimitriou
        try:
834 3a72a5d4 Kostas Papadimitriou
            self.info = json.loads(self.info_data)
835 c630fee6 Kostas Papadimitriou
            if not self.info:
836 c630fee6 Kostas Papadimitriou
                self.info = {}
837 c630fee6 Kostas Papadimitriou
        except Exception, e:
838 3a72a5d4 Kostas Papadimitriou
            self.info = {}
839 c630fee6 Kostas Papadimitriou
840 3a72a5d4 Kostas Papadimitriou
        for key,value in self.info.iteritems():
841 3a72a5d4 Kostas Papadimitriou
            setattr(self, 'info_%s' % key, value)
842 3a72a5d4 Kostas Papadimitriou
843 d2633501 Kostas Papadimitriou
844 d2633501 Kostas Papadimitriou
    @property
845 d2633501 Kostas Papadimitriou
    def settings(self):
846 d2633501 Kostas Papadimitriou
        return auth_providers.get_provider(self.module)
847 d2633501 Kostas Papadimitriou
848 d2633501 Kostas Papadimitriou
    @property
849 d2633501 Kostas Papadimitriou
    def details_display(self):
850 c630fee6 Kostas Papadimitriou
        try:
851 c630fee6 Kostas Papadimitriou
          return self.settings.get_details_tpl_display % self.__dict__
852 c630fee6 Kostas Papadimitriou
        except:
853 c630fee6 Kostas Papadimitriou
          return ''
854 3a72a5d4 Kostas Papadimitriou
855 3a72a5d4 Kostas Papadimitriou
    @property
856 3a72a5d4 Kostas Papadimitriou
    def title_display(self):
857 3a72a5d4 Kostas Papadimitriou
        title_tpl = self.settings.get_title_display
858 3a72a5d4 Kostas Papadimitriou
        try:
859 3a72a5d4 Kostas Papadimitriou
            if self.settings.get_user_title_display:
860 3a72a5d4 Kostas Papadimitriou
                title_tpl = self.settings.get_user_title_display
861 3a72a5d4 Kostas Papadimitriou
        except Exception, e:
862 3a72a5d4 Kostas Papadimitriou
            pass
863 c630fee6 Kostas Papadimitriou
        try:
864 c630fee6 Kostas Papadimitriou
          return title_tpl % self.__dict__
865 c630fee6 Kostas Papadimitriou
        except:
866 c630fee6 Kostas Papadimitriou
          return self.settings.get_title_display % self.__dict__
867 d2633501 Kostas Papadimitriou
868 d2633501 Kostas Papadimitriou
    def can_remove(self):
869 d2633501 Kostas Papadimitriou
        return self.user.can_remove_auth_provider(self.module)
870 d2633501 Kostas Papadimitriou
871 d2633501 Kostas Papadimitriou
    def delete(self, *args, **kwargs):
872 d2633501 Kostas Papadimitriou
        ret = super(AstakosUserAuthProvider, self).delete(*args, **kwargs)
873 5156e663 Kostas Papadimitriou
        if self.module == 'local':
874 5156e663 Kostas Papadimitriou
            self.user.set_unusable_password()
875 5156e663 Kostas Papadimitriou
            self.user.save()
876 d2633501 Kostas Papadimitriou
        return ret
877 d2633501 Kostas Papadimitriou
878 f432088a Kostas Papadimitriou
    def __repr__(self):
879 f432088a Kostas Papadimitriou
        return '<AstakosUserAuthProvider %s:%s>' % (self.module, self.identifier)
880 f432088a Kostas Papadimitriou
881 b778b6fa Kostas Papadimitriou
    def __unicode__(self):
882 b778b6fa Kostas Papadimitriou
        if self.identifier:
883 b778b6fa Kostas Papadimitriou
            return "%s:%s" % (self.module, self.identifier)
884 b778b6fa Kostas Papadimitriou
        if self.auth_backend:
885 b778b6fa Kostas Papadimitriou
            return "%s:%s" % (self.module, self.auth_backend)
886 b778b6fa Kostas Papadimitriou
        return self.module
887 b778b6fa Kostas Papadimitriou
888 3a72a5d4 Kostas Papadimitriou
    def save(self, *args, **kwargs):
889 3a72a5d4 Kostas Papadimitriou
        self.info_data = json.dumps(self.info)
890 3a72a5d4 Kostas Papadimitriou
        return super(AstakosUserAuthProvider, self).save(*args, **kwargs)
891 b778b6fa Kostas Papadimitriou
892 d2633501 Kostas Papadimitriou
893 8e45d6fd Sofia Papagiannaki
class Membership(models.Model):
894 8e45d6fd Sofia Papagiannaki
    person = models.ForeignKey(AstakosUser)
895 8e45d6fd Sofia Papagiannaki
    group = models.ForeignKey(AstakosGroup)
896 01ac12d5 Sofia Papagiannaki
    date_requested = models.DateField(default=datetime.now(), blank=True)
897 01ac12d5 Sofia Papagiannaki
    date_joined = models.DateField(null=True, db_index=True, blank=True)
898 5ce3ce4f Sofia Papagiannaki
899 8e45d6fd Sofia Papagiannaki
    class Meta:
900 8e45d6fd Sofia Papagiannaki
        unique_together = ("person", "group")
901 5ce3ce4f Sofia Papagiannaki
902 ffb1e7a8 Sofia Papagiannaki
    def save(self, *args, **kwargs):
903 28252c7f Sofia Papagiannaki
        if not self.id:
904 28252c7f Sofia Papagiannaki
            if not self.group.moderation_enabled:
905 28252c7f Sofia Papagiannaki
                self.date_joined = datetime.now()
906 ffb1e7a8 Sofia Papagiannaki
        super(Membership, self).save(*args, **kwargs)
907 5ce3ce4f Sofia Papagiannaki
908 8e45d6fd Sofia Papagiannaki
    @property
909 8e45d6fd Sofia Papagiannaki
    def is_approved(self):
910 8e45d6fd Sofia Papagiannaki
        if self.date_joined:
911 8e45d6fd Sofia Papagiannaki
            return True
912 8e45d6fd Sofia Papagiannaki
        return False
913 5ce3ce4f Sofia Papagiannaki
914 01ac12d5 Sofia Papagiannaki
    def approve(self):
915 c0b26605 Sofia Papagiannaki
        if self.is_approved:
916 c0b26605 Sofia Papagiannaki
            return
917 ae497612 Olga Brani
        if self.group.max_participants:
918 a4075f5a root
            assert len(self.group.approved_members) + 1 <= self.group.max_participants, \
919 c0b26605 Sofia Papagiannaki
            'Maximum participant number has been reached.'
920 01ac12d5 Sofia Papagiannaki
        self.date_joined = datetime.now()
921 01ac12d5 Sofia Papagiannaki
        self.save()
922 fc1e2f02 Sofia Papagiannaki
        quota_disturbed.send(sender=self, users=(self.person,))
923 5ce3ce4f Sofia Papagiannaki
924 01ac12d5 Sofia Papagiannaki
    def disapprove(self):
925 e1a80257 Sofia Papagiannaki
        approved = self.is_approved()
926 01ac12d5 Sofia Papagiannaki
        self.delete()
927 e1a80257 Sofia Papagiannaki
        if approved:
928 e1a80257 Sofia Papagiannaki
            quota_disturbed.send(sender=self, users=(self.person,))
929 8e45d6fd Sofia Papagiannaki
930 e1a80257 Sofia Papagiannaki
class ExtendedManager(models.Manager):
931 9a06d96f Olga Brani
    def _update_or_create(self, **kwargs):
932 9a06d96f Olga Brani
        assert kwargs, \
933 9a06d96f Olga Brani
            'update_or_create() must be passed at least one keyword argument'
934 9a06d96f Olga Brani
        obj, created = self.get_or_create(**kwargs)
935 9a06d96f Olga Brani
        defaults = kwargs.pop('defaults', {})
936 9a06d96f Olga Brani
        if created:
937 9a06d96f Olga Brani
            return obj, True, False
938 9a06d96f Olga Brani
        else:
939 9a06d96f Olga Brani
            try:
940 9a06d96f Olga Brani
                params = dict(
941 9a06d96f Olga Brani
                    [(k, v) for k, v in kwargs.items() if '__' not in k])
942 9a06d96f Olga Brani
                params.update(defaults)
943 9a06d96f Olga Brani
                for attr, val in params.items():
944 9a06d96f Olga Brani
                    if hasattr(obj, attr):
945 9a06d96f Olga Brani
                        setattr(obj, attr, val)
946 9a06d96f Olga Brani
                sid = transaction.savepoint()
947 9a06d96f Olga Brani
                obj.save(force_update=True)
948 9a06d96f Olga Brani
                transaction.savepoint_commit(sid)
949 9a06d96f Olga Brani
                return obj, False, True
950 9a06d96f Olga Brani
            except IntegrityError, e:
951 9a06d96f Olga Brani
                transaction.savepoint_rollback(sid)
952 9a06d96f Olga Brani
                try:
953 9a06d96f Olga Brani
                    return self.get(**kwargs), False, False
954 9a06d96f Olga Brani
                except self.model.DoesNotExist:
955 9a06d96f Olga Brani
                    raise e
956 9a06d96f Olga Brani
957 9a06d96f Olga Brani
    update_or_create = _update_or_create
958 5ce3ce4f Sofia Papagiannaki
959 8e45d6fd Sofia Papagiannaki
class AstakosGroupQuota(models.Model):
960 e1a80257 Sofia Papagiannaki
    objects = ExtendedManager()
961 e1a80257 Sofia Papagiannaki
    limit = models.PositiveIntegerField(_('Limit'), null=True)    # obsolete field
962 e1a80257 Sofia Papagiannaki
    uplimit = models.BigIntegerField(_('Up limit'), null=True)
963 8e45d6fd Sofia Papagiannaki
    resource = models.ForeignKey(Resource)
964 8e45d6fd Sofia Papagiannaki
    group = models.ForeignKey(AstakosGroup, blank=True)
965 5ce3ce4f Sofia Papagiannaki
966 8e45d6fd Sofia Papagiannaki
    class Meta:
967 8e45d6fd Sofia Papagiannaki
        unique_together = ("resource", "group")
968 8e45d6fd Sofia Papagiannaki
969 8e45d6fd Sofia Papagiannaki
class AstakosUserQuota(models.Model):
970 e1a80257 Sofia Papagiannaki
    objects = ExtendedManager()
971 e1a80257 Sofia Papagiannaki
    limit = models.PositiveIntegerField(_('Limit'), null=True)    # obsolete field
972 e1a80257 Sofia Papagiannaki
    uplimit = models.BigIntegerField(_('Up limit'), null=True)
973 8e45d6fd Sofia Papagiannaki
    resource = models.ForeignKey(Resource)
974 8e45d6fd Sofia Papagiannaki
    user = models.ForeignKey(AstakosUser)
975 5ce3ce4f Sofia Papagiannaki
976 8e45d6fd Sofia Papagiannaki
    class Meta:
977 8e45d6fd Sofia Papagiannaki
        unique_together = ("resource", "user")
978 09e7393c Sofia Papagiannaki
979 5ce3ce4f Sofia Papagiannaki
980 270dd48d Sofia Papagiannaki
class ApprovalTerms(models.Model):
981 270dd48d Sofia Papagiannaki
    """
982 270dd48d Sofia Papagiannaki
    Model for approval terms
983 270dd48d Sofia Papagiannaki
    """
984 6c736ed7 Kostas Papadimitriou
985 5ce3ce4f Sofia Papagiannaki
    date = models.DateTimeField(
986 e1a80257 Sofia Papagiannaki
        _('Issue date'), db_index=True, default=datetime.now())
987 e1a80257 Sofia Papagiannaki
    location = models.CharField(_('Terms location'), max_length=255)
988 270dd48d Sofia Papagiannaki
989 5ce3ce4f Sofia Papagiannaki
990 64cd4730 Antony Chazapis
class Invitation(models.Model):
991 890b0eaf Sofia Papagiannaki
    """
992 890b0eaf Sofia Papagiannaki
    Model for registring invitations
993 890b0eaf Sofia Papagiannaki
    """
994 0905ccd2 Sofia Papagiannaki
    inviter = models.ForeignKey(AstakosUser, related_name='invitations_sent',
995 64cd4730 Antony Chazapis
                                null=True)
996 e1a80257 Sofia Papagiannaki
    realname = models.CharField(_('Real name'), max_length=255)
997 e1a80257 Sofia Papagiannaki
    username = models.CharField(_('Unique ID'), max_length=255, unique=True)
998 e1a80257 Sofia Papagiannaki
    code = models.BigIntegerField(_('Invitation code'), db_index=True)
999 e1a80257 Sofia Papagiannaki
    is_consumed = models.BooleanField(_('Consumed?'), default=False)
1000 e1a80257 Sofia Papagiannaki
    created = models.DateTimeField(_('Creation date'), auto_now_add=True)
1001 e1a80257 Sofia Papagiannaki
    consumed = models.DateTimeField(_('Consumption date'), null=True, blank=True)
1002 5ce3ce4f Sofia Papagiannaki
1003 18ffbee1 Sofia Papagiannaki
    def __init__(self, *args, **kwargs):
1004 18ffbee1 Sofia Papagiannaki
        super(Invitation, self).__init__(*args, **kwargs)
1005 8f5a3a06 Sofia Papagiannaki
        if not self.id:
1006 8f5a3a06 Sofia Papagiannaki
            self.code = _generate_invitation_code()
1007 5ce3ce4f Sofia Papagiannaki
1008 64cd4730 Antony Chazapis
    def consume(self):
1009 64cd4730 Antony Chazapis
        self.is_consumed = True
1010 64cd4730 Antony Chazapis
        self.consumed = datetime.now()
1011 64cd4730 Antony Chazapis
        self.save()
1012 6c736ed7 Kostas Papadimitriou
1013 64cd4730 Antony Chazapis
    def __unicode__(self):
1014 0905ccd2 Sofia Papagiannaki
        return '%s -> %s [%d]' % (self.inviter, self.username, self.code)
1015 9c01d9d1 Sofia Papagiannaki
1016 49790d9d Sofia Papagiannaki
1017 49790d9d Sofia Papagiannaki
class EmailChangeManager(models.Manager):
1018 34a76cdb Kostas Papadimitriou
1019 49790d9d Sofia Papagiannaki
    @transaction.commit_on_success
1020 49790d9d Sofia Papagiannaki
    def change_email(self, activation_key):
1021 49790d9d Sofia Papagiannaki
        """
1022 49790d9d Sofia Papagiannaki
        Validate an activation key and change the corresponding
1023 49790d9d Sofia Papagiannaki
        ``User`` if valid.
1024 49790d9d Sofia Papagiannaki

1025 49790d9d Sofia Papagiannaki
        If the key is valid and has not expired, return the ``User``
1026 49790d9d Sofia Papagiannaki
        after activating.
1027 49790d9d Sofia Papagiannaki

1028 49790d9d Sofia Papagiannaki
        If the key is not valid or has expired, return ``None``.
1029 49790d9d Sofia Papagiannaki

1030 49790d9d Sofia Papagiannaki
        If the key is valid but the ``User`` is already active,
1031 49790d9d Sofia Papagiannaki
        return ``None``.
1032 49790d9d Sofia Papagiannaki

1033 49790d9d Sofia Papagiannaki
        After successful email change the activation record is deleted.
1034 49790d9d Sofia Papagiannaki

1035 49790d9d Sofia Papagiannaki
        Throws ValueError if there is already
1036 49790d9d Sofia Papagiannaki
        """
1037 49790d9d Sofia Papagiannaki
        try:
1038 5ce3ce4f Sofia Papagiannaki
            email_change = self.model.objects.get(
1039 5ce3ce4f Sofia Papagiannaki
                activation_key=activation_key)
1040 49790d9d Sofia Papagiannaki
            if email_change.activation_key_expired():
1041 49790d9d Sofia Papagiannaki
                email_change.delete()
1042 49790d9d Sofia Papagiannaki
                raise EmailChange.DoesNotExist
1043 49790d9d Sofia Papagiannaki
            # is there an active user with this address?
1044 49790d9d Sofia Papagiannaki
            try:
1045 789a5951 Sofia Papagiannaki
                AstakosUser.objects.get(email__iexact=email_change.new_email_address)
1046 49790d9d Sofia Papagiannaki
            except AstakosUser.DoesNotExist:
1047 49790d9d Sofia Papagiannaki
                pass
1048 49790d9d Sofia Papagiannaki
            else:
1049 73fbaec4 Sofia Papagiannaki
                raise ValueError(_('The new email address is reserved.'))
1050 49790d9d Sofia Papagiannaki
            # update user
1051 49790d9d Sofia Papagiannaki
            user = AstakosUser.objects.get(pk=email_change.user_id)
1052 34a76cdb Kostas Papadimitriou
            old_email = user.email
1053 49790d9d Sofia Papagiannaki
            user.email = email_change.new_email_address
1054 49790d9d Sofia Papagiannaki
            user.save()
1055 49790d9d Sofia Papagiannaki
            email_change.delete()
1056 34a76cdb Kostas Papadimitriou
            msg = "User %d changed email from %s to %s" % (user.pk, old_email,
1057 34a76cdb Kostas Papadimitriou
                                                          user.email)
1058 34a76cdb Kostas Papadimitriou
            logger.log(LOGGING_LEVEL, msg)
1059 49790d9d Sofia Papagiannaki
            return user
1060 49790d9d Sofia Papagiannaki
        except EmailChange.DoesNotExist:
1061 73fbaec4 Sofia Papagiannaki
            raise ValueError(_('Invalid activation key.'))
1062 49790d9d Sofia Papagiannaki
1063 49790d9d Sofia Papagiannaki
1064 49790d9d Sofia Papagiannaki
class EmailChange(models.Model):
1065 73fbaec4 Sofia Papagiannaki
    new_email_address = models.EmailField(
1066 73fbaec4 Sofia Papagiannaki
        _(u'new e-mail address'),
1067 73fbaec4 Sofia Papagiannaki
        help_text=_('Your old email address will be used until you verify your new one.'))
1068 5ce3ce4f Sofia Papagiannaki
    user = models.ForeignKey(
1069 34a76cdb Kostas Papadimitriou
        AstakosUser, unique=True, related_name='emailchanges')
1070 49790d9d Sofia Papagiannaki
    requested_at = models.DateTimeField(default=datetime.now())
1071 5ce3ce4f Sofia Papagiannaki
    activation_key = models.CharField(
1072 5ce3ce4f Sofia Papagiannaki
        max_length=40, unique=True, db_index=True)
1073 49790d9d Sofia Papagiannaki
1074 49790d9d Sofia Papagiannaki
    objects = EmailChangeManager()
1075 49790d9d Sofia Papagiannaki
1076 34a76cdb Kostas Papadimitriou
    def get_url(self):
1077 34a76cdb Kostas Papadimitriou
        return reverse('email_change_confirm',
1078 34a76cdb Kostas Papadimitriou
                      kwargs={'activation_key': self.activation_key})
1079 34a76cdb Kostas Papadimitriou
1080 49790d9d Sofia Papagiannaki
    def activation_key_expired(self):
1081 49790d9d Sofia Papagiannaki
        expiration_date = timedelta(days=EMAILCHANGE_ACTIVATION_DAYS)
1082 ff9290ec Sofia Papagiannaki
        return self.requested_at + expiration_date < datetime.now()
1083 ff9290ec Sofia Papagiannaki
1084 6b03a847 Sofia Papagiannaki
1085 ca828a10 Sofia Papagiannaki
class AdditionalMail(models.Model):
1086 ca828a10 Sofia Papagiannaki
    """
1087 ca828a10 Sofia Papagiannaki
    Model for registring invitations
1088 ca828a10 Sofia Papagiannaki
    """
1089 ca828a10 Sofia Papagiannaki
    owner = models.ForeignKey(AstakosUser)
1090 1eec103a Sofia Papagiannaki
    email = models.EmailField()
1091 ca828a10 Sofia Papagiannaki
1092 5ce3ce4f Sofia Papagiannaki
1093 fc1e2f02 Sofia Papagiannaki
def _generate_invitation_code():
1094 fc1e2f02 Sofia Papagiannaki
    while True:
1095 5ce3ce4f Sofia Papagiannaki
        code = randint(1, 2L ** 63 - 1)
1096 fc1e2f02 Sofia Papagiannaki
        try:
1097 fc1e2f02 Sofia Papagiannaki
            Invitation.objects.get(code=code)
1098 fc1e2f02 Sofia Papagiannaki
            # An invitation with this code already exists, try again
1099 fc1e2f02 Sofia Papagiannaki
        except Invitation.DoesNotExist:
1100 fc1e2f02 Sofia Papagiannaki
            return code
1101 fc1e2f02 Sofia Papagiannaki
1102 5ce3ce4f Sofia Papagiannaki
1103 fc1e2f02 Sofia Papagiannaki
def get_latest_terms():
1104 fc1e2f02 Sofia Papagiannaki
    try:
1105 fc1e2f02 Sofia Papagiannaki
        term = ApprovalTerms.objects.order_by('-id')[0]
1106 fc1e2f02 Sofia Papagiannaki
        return term
1107 fc1e2f02 Sofia Papagiannaki
    except IndexError:
1108 fc1e2f02 Sofia Papagiannaki
        pass
1109 fc1e2f02 Sofia Papagiannaki
    return None
1110 fc1e2f02 Sofia Papagiannaki
1111 ef20ea07 Sofia Papagiannaki
class PendingThirdPartyUser(models.Model):
1112 ef20ea07 Sofia Papagiannaki
    """
1113 ef20ea07 Sofia Papagiannaki
    Model for registring successful third party user authentications
1114 ef20ea07 Sofia Papagiannaki
    """
1115 e1a80257 Sofia Papagiannaki
    third_party_identifier = models.CharField(_('Third-party identifier'), max_length=255, null=True, blank=True)
1116 e1a80257 Sofia Papagiannaki
    provider = models.CharField(_('Provider'), max_length=255, blank=True)
1117 678b2236 Sofia Papagiannaki
    email = models.EmailField(_('e-mail address'), blank=True, null=True)
1118 ef20ea07 Sofia Papagiannaki
    first_name = models.CharField(_('first name'), max_length=30, blank=True)
1119 ef20ea07 Sofia Papagiannaki
    last_name = models.CharField(_('last name'), max_length=30, blank=True)
1120 ef20ea07 Sofia Papagiannaki
    affiliation = models.CharField('Affiliation', max_length=255, blank=True)
1121 ef20ea07 Sofia Papagiannaki
    username = models.CharField(_('username'), max_length=30, unique=True, help_text=_("Required. 30 characters or fewer. Letters, numbers and @/./+/-/_ characters"))
1122 e1a80257 Sofia Papagiannaki
    token = models.CharField(_('Token'), max_length=255, null=True, blank=True)
1123 d2633501 Kostas Papadimitriou
    created = models.DateTimeField(auto_now_add=True, null=True, blank=True)
1124 c630fee6 Kostas Papadimitriou
    info = models.TextField(default="", null=True, blank=True)
1125 d2633501 Kostas Papadimitriou
1126 678b2236 Sofia Papagiannaki
    class Meta:
1127 678b2236 Sofia Papagiannaki
        unique_together = ("provider", "third_party_identifier")
1128 ef20ea07 Sofia Papagiannaki
1129 c630fee6 Kostas Papadimitriou
    def get_user_instance(self):
1130 c630fee6 Kostas Papadimitriou
        d = self.__dict__
1131 c630fee6 Kostas Papadimitriou
        d.pop('_state', None)
1132 c630fee6 Kostas Papadimitriou
        d.pop('id', None)
1133 c630fee6 Kostas Papadimitriou
        d.pop('token', None)
1134 c630fee6 Kostas Papadimitriou
        d.pop('created', None)
1135 c630fee6 Kostas Papadimitriou
        d.pop('info', None)
1136 c630fee6 Kostas Papadimitriou
        user = AstakosUser(**d)
1137 c630fee6 Kostas Papadimitriou
1138 c630fee6 Kostas Papadimitriou
        return user
1139 c630fee6 Kostas Papadimitriou
1140 ef20ea07 Sofia Papagiannaki
    @property
1141 ef20ea07 Sofia Papagiannaki
    def realname(self):
1142 ef20ea07 Sofia Papagiannaki
        return '%s %s' %(self.first_name, self.last_name)
1143 ef20ea07 Sofia Papagiannaki
1144 ef20ea07 Sofia Papagiannaki
    @realname.setter
1145 ef20ea07 Sofia Papagiannaki
    def realname(self, value):
1146 ef20ea07 Sofia Papagiannaki
        parts = value.split(' ')
1147 ef20ea07 Sofia Papagiannaki
        if len(parts) == 2:
1148 ef20ea07 Sofia Papagiannaki
            self.first_name = parts[0]
1149 ef20ea07 Sofia Papagiannaki
            self.last_name = parts[1]
1150 ef20ea07 Sofia Papagiannaki
        else:
1151 ef20ea07 Sofia Papagiannaki
            self.last_name = parts[0]
1152 2e90e3ec Kostas Papadimitriou
1153 ef20ea07 Sofia Papagiannaki
    def save(self, **kwargs):
1154 ef20ea07 Sofia Papagiannaki
        if not self.id:
1155 ef20ea07 Sofia Papagiannaki
            # set username
1156 ef20ea07 Sofia Papagiannaki
            while not self.username:
1157 ef20ea07 Sofia Papagiannaki
                username =  uuid.uuid4().hex[:30]
1158 ef20ea07 Sofia Papagiannaki
                try:
1159 ef20ea07 Sofia Papagiannaki
                    AstakosUser.objects.get(username = username)
1160 ef20ea07 Sofia Papagiannaki
                except AstakosUser.DoesNotExist, e:
1161 ef20ea07 Sofia Papagiannaki
                    self.username = username
1162 ef20ea07 Sofia Papagiannaki
        super(PendingThirdPartyUser, self).save(**kwargs)
1163 ef20ea07 Sofia Papagiannaki
1164 d2633501 Kostas Papadimitriou
    def generate_token(self):
1165 d2633501 Kostas Papadimitriou
        self.password = self.third_party_identifier
1166 d2633501 Kostas Papadimitriou
        self.last_login = datetime.now()
1167 d2633501 Kostas Papadimitriou
        self.token = default_token_generator.make_token(self)
1168 d2633501 Kostas Papadimitriou
1169 bf0c6de5 Sofia Papagiannaki
class SessionCatalog(models.Model):
1170 bf0c6de5 Sofia Papagiannaki
    session_key = models.CharField(_('session key'), max_length=40)
1171 bf0c6de5 Sofia Papagiannaki
    user = models.ForeignKey(AstakosUser, related_name='sessions', null=True)
1172 bf0c6de5 Sofia Papagiannaki
1173 ccab6eb5 Sofia Papagiannaki
class MemberJoinPolicy(models.Model):
1174 e1a80257 Sofia Papagiannaki
    policy = models.CharField(_('Policy'), max_length=255, unique=True, db_index=True)
1175 e1a80257 Sofia Papagiannaki
    description = models.CharField(_('Description'), max_length=80)
1176 e1a80257 Sofia Papagiannaki
1177 e1a80257 Sofia Papagiannaki
    def __str__(self):
1178 e1a80257 Sofia Papagiannaki
        return self.policy
1179 e1a80257 Sofia Papagiannaki
1180 ccab6eb5 Sofia Papagiannaki
class MemberLeavePolicy(models.Model):
1181 b22de10a Sofia Papagiannaki
    policy = models.CharField(_('Policy'), max_length=255, unique=True, db_index=True)
1182 b22de10a Sofia Papagiannaki
    description = models.CharField(_('Description'), max_length=80)
1183 b22de10a Sofia Papagiannaki
1184 b22de10a Sofia Papagiannaki
    def __str__(self):
1185 b22de10a Sofia Papagiannaki
        return self.policy
1186 b22de10a Sofia Papagiannaki
1187 ccab6eb5 Sofia Papagiannaki
_auto_accept_join = False
1188 ccab6eb5 Sofia Papagiannaki
def get_auto_accept_join():
1189 bfe23b13 Sofia Papagiannaki
    global _auto_accept_join
1190 bfe23b13 Sofia Papagiannaki
    if _auto_accept_join is not False:
1191 bfe23b13 Sofia Papagiannaki
        return _auto_accept_join
1192 e65c21df Georgios D. Tsoukalas
    try:
1193 ccab6eb5 Sofia Papagiannaki
        auto_accept = MemberJoinPolicy.objects.get(policy='auto_accept')
1194 ccab6eb5 Sofia Papagiannaki
    except:
1195 ccab6eb5 Sofia Papagiannaki
        auto_accept = None
1196 bfe23b13 Sofia Papagiannaki
    _auto_accept_join = auto_accept
1197 ccab6eb5 Sofia Papagiannaki
    return auto_accept
1198 ccab6eb5 Sofia Papagiannaki
1199 bfe23b13 Sofia Papagiannaki
_closed_join = False
1200 bfe23b13 Sofia Papagiannaki
def get_closed_join():
1201 bfe23b13 Sofia Papagiannaki
    global _closed_join
1202 bfe23b13 Sofia Papagiannaki
    if _closed_join is not False:
1203 bfe23b13 Sofia Papagiannaki
        return _closed_join
1204 bfe23b13 Sofia Papagiannaki
    try:
1205 bfe23b13 Sofia Papagiannaki
        closed = MemberJoinPolicy.objects.get(policy='closed')
1206 bfe23b13 Sofia Papagiannaki
    except:
1207 bfe23b13 Sofia Papagiannaki
        closed = None
1208 bfe23b13 Sofia Papagiannaki
    _closed_join = closed
1209 bfe23b13 Sofia Papagiannaki
    return closed
1210 bfe23b13 Sofia Papagiannaki
1211 ccab6eb5 Sofia Papagiannaki
_auto_accept_leave = False
1212 ccab6eb5 Sofia Papagiannaki
def get_auto_accept_leave():
1213 bfe23b13 Sofia Papagiannaki
    global _auto_accept_leave
1214 bfe23b13 Sofia Papagiannaki
    if _auto_accept_leave is not False:
1215 bfe23b13 Sofia Papagiannaki
        return _auto_accept_leave
1216 ccab6eb5 Sofia Papagiannaki
    try:
1217 ccab6eb5 Sofia Papagiannaki
        auto_accept = MemberLeavePolicy.objects.get(policy='auto_accept')
1218 e65c21df Georgios D. Tsoukalas
    except:
1219 e65c21df Georgios D. Tsoukalas
        auto_accept = None
1220 bfe23b13 Sofia Papagiannaki
    _auto_accept_leave = auto_accept
1221 e65c21df Georgios D. Tsoukalas
    return auto_accept
1222 e1a80257 Sofia Papagiannaki
1223 bfe23b13 Sofia Papagiannaki
_closed_leave = False
1224 bfe23b13 Sofia Papagiannaki
def get_closed_leave():
1225 bfe23b13 Sofia Papagiannaki
    global _closed_leave
1226 bfe23b13 Sofia Papagiannaki
    if _closed_leave is not False:
1227 bfe23b13 Sofia Papagiannaki
        return _closed_leave
1228 bfe23b13 Sofia Papagiannaki
    try:
1229 bfe23b13 Sofia Papagiannaki
        closed = MemberLeavePolicy.objects.get(policy='closed')
1230 bfe23b13 Sofia Papagiannaki
    except:
1231 bfe23b13 Sofia Papagiannaki
        closed = None
1232 bfe23b13 Sofia Papagiannaki
    _closed_leave = closed
1233 bfe23b13 Sofia Papagiannaki
    return closeds
1234 bfe23b13 Sofia Papagiannaki
1235 65360c65 Georgios D. Tsoukalas
1236 65360c65 Georgios D. Tsoukalas
### PROJECTS ###
1237 65360c65 Georgios D. Tsoukalas
################
1238 65360c65 Georgios D. Tsoukalas
1239 65360c65 Georgios D. Tsoukalas
1240 e546df49 Georgios D. Tsoukalas
def synced_model_metaclass(class_name, class_parents, class_attributes):
1241 65360c65 Georgios D. Tsoukalas
1242 e546df49 Georgios D. Tsoukalas
    new_attributes = {}
1243 e546df49 Georgios D. Tsoukalas
    sync_attributes = {}
1244 65360c65 Georgios D. Tsoukalas
1245 e546df49 Georgios D. Tsoukalas
    for name, value in class_attributes.iteritems():
1246 e546df49 Georgios D. Tsoukalas
        sync, underscore, rest = name.partition('_')
1247 e546df49 Georgios D. Tsoukalas
        if sync == 'sync' and underscore == '_':
1248 e546df49 Georgios D. Tsoukalas
            sync_attributes[rest] = value
1249 e546df49 Georgios D. Tsoukalas
        else:
1250 e546df49 Georgios D. Tsoukalas
            new_attributes[name] = value
1251 65360c65 Georgios D. Tsoukalas
1252 e546df49 Georgios D. Tsoukalas
    if 'prefix' not in sync_attributes:
1253 e546df49 Georgios D. Tsoukalas
        m = ("you did not specify a 'sync_prefix' attribute "
1254 e546df49 Georgios D. Tsoukalas
             "in class '%s'" % (class_name,))
1255 e546df49 Georgios D. Tsoukalas
        raise ValueError(m)
1256 65360c65 Georgios D. Tsoukalas
1257 e546df49 Georgios D. Tsoukalas
    prefix = sync_attributes.pop('prefix')
1258 e546df49 Georgios D. Tsoukalas
    class_name = sync_attributes.pop('classname', prefix + '_model')
1259 65360c65 Georgios D. Tsoukalas
1260 e546df49 Georgios D. Tsoukalas
    for name, value in sync_attributes.iteritems():
1261 e546df49 Georgios D. Tsoukalas
        newname = prefix + '_' + name
1262 e546df49 Georgios D. Tsoukalas
        if newname in new_attributes:
1263 e546df49 Georgios D. Tsoukalas
            m = ("class '%s' was specified with prefix '%s' "
1264 e546df49 Georgios D. Tsoukalas
                 "but it already has an attribute named '%s'"
1265 e546df49 Georgios D. Tsoukalas
                 % (class_name, prefix, newname))
1266 e546df49 Georgios D. Tsoukalas
            raise ValueError(m)
1267 65360c65 Georgios D. Tsoukalas
1268 e546df49 Georgios D. Tsoukalas
        new_attributes[newname] = value
1269 e546df49 Georgios D. Tsoukalas
1270 e546df49 Georgios D. Tsoukalas
    newclass = type(class_name, class_parents, new_attributes)
1271 e546df49 Georgios D. Tsoukalas
    return newclass
1272 e546df49 Georgios D. Tsoukalas
1273 e546df49 Georgios D. Tsoukalas
1274 e546df49 Georgios D. Tsoukalas
def make_synced(prefix='sync', name='SyncedState'):
1275 e546df49 Georgios D. Tsoukalas
1276 e546df49 Georgios D. Tsoukalas
    the_name = name
1277 e546df49 Georgios D. Tsoukalas
    the_prefix = prefix
1278 e546df49 Georgios D. Tsoukalas
1279 e546df49 Georgios D. Tsoukalas
    class SyncedState(models.Model):
1280 e546df49 Georgios D. Tsoukalas
1281 e546df49 Georgios D. Tsoukalas
        sync_classname      = the_name
1282 e546df49 Georgios D. Tsoukalas
        sync_prefix         = the_prefix
1283 e546df49 Georgios D. Tsoukalas
        __metaclass__       = synced_model_metaclass
1284 e546df49 Georgios D. Tsoukalas
1285 e546df49 Georgios D. Tsoukalas
        sync_new_state      = models.BigIntegerField(null=True)
1286 e546df49 Georgios D. Tsoukalas
        sync_synced_state   = models.BigIntegerField(null=True)
1287 e546df49 Georgios D. Tsoukalas
        STATUS_SYNCED       = 0
1288 e546df49 Georgios D. Tsoukalas
        STATUS_PENDING      = 1
1289 e546df49 Georgios D. Tsoukalas
        sync_status         = models.IntegerField(db_index=True)
1290 e546df49 Georgios D. Tsoukalas
1291 e546df49 Georgios D. Tsoukalas
        class Meta:
1292 e546df49 Georgios D. Tsoukalas
            abstract = True
1293 e546df49 Georgios D. Tsoukalas
1294 e546df49 Georgios D. Tsoukalas
        class NotSynced(Exception):
1295 e546df49 Georgios D. Tsoukalas
            pass
1296 e546df49 Georgios D. Tsoukalas
1297 e546df49 Georgios D. Tsoukalas
        def sync_init_state(self, state):
1298 e546df49 Georgios D. Tsoukalas
            self.sync_synced_state = state
1299 e546df49 Georgios D. Tsoukalas
            self.sync_new_state = state
1300 65360c65 Georgios D. Tsoukalas
            self.sync_status = self.STATUS_SYNCED
1301 65360c65 Georgios D. Tsoukalas
1302 e546df49 Georgios D. Tsoukalas
        def sync_get_status(self):
1303 e546df49 Georgios D. Tsoukalas
            return self.sync_status
1304 65360c65 Georgios D. Tsoukalas
1305 e546df49 Georgios D. Tsoukalas
        def sync_set_status(self):
1306 e546df49 Georgios D. Tsoukalas
            if self.sync_new_state != self.sync_synced_state:
1307 e546df49 Georgios D. Tsoukalas
                self.sync_status = self.STATUS_PENDING
1308 e546df49 Georgios D. Tsoukalas
            else:
1309 e546df49 Georgios D. Tsoukalas
                self.sync_status = self.STATUS_SYNCED
1310 65360c65 Georgios D. Tsoukalas
1311 e546df49 Georgios D. Tsoukalas
        def sync_set_synced(self):
1312 e546df49 Georgios D. Tsoukalas
            self.sync_synced_state = self.sync_new_state
1313 e546df49 Georgios D. Tsoukalas
            self.sync_status = self.STATUS_SYNCED
1314 65360c65 Georgios D. Tsoukalas
1315 e546df49 Georgios D. Tsoukalas
        def sync_get_synced_state(self):
1316 e546df49 Georgios D. Tsoukalas
            return self.sync_synced_state
1317 65360c65 Georgios D. Tsoukalas
1318 e546df49 Georgios D. Tsoukalas
        def sync_set_new_state(self, new_state):
1319 e546df49 Georgios D. Tsoukalas
            self.sync_new_state = new_state
1320 e546df49 Georgios D. Tsoukalas
            self.sync_set_status()
1321 65360c65 Georgios D. Tsoukalas
1322 e546df49 Georgios D. Tsoukalas
        def sync_get_new_state(self):
1323 e546df49 Georgios D. Tsoukalas
            return self.sync_new_state
1324 65360c65 Georgios D. Tsoukalas
1325 e546df49 Georgios D. Tsoukalas
        def sync_set_synced_state(self, synced_state):
1326 e546df49 Georgios D. Tsoukalas
            self.sync_synced_state = synced_state
1327 e546df49 Georgios D. Tsoukalas
            self.sync_set_status()
1328 65360c65 Georgios D. Tsoukalas
1329 e546df49 Georgios D. Tsoukalas
        def sync_get_pending_objects(self):
1330 e546df49 Georgios D. Tsoukalas
            kw = dict((the_prefix + '_status', self.STATUS_PENDING))
1331 e546df49 Georgios D. Tsoukalas
            return self.objects.filter(**kw)
1332 65360c65 Georgios D. Tsoukalas
1333 e546df49 Georgios D. Tsoukalas
        def sync_get_synced_objects(self):
1334 e546df49 Georgios D. Tsoukalas
            kw = dict((the_prefix + '_status', self.STATUS_SYNCED))
1335 e546df49 Georgios D. Tsoukalas
            return self.objects.filter(**kw)
1336 65360c65 Georgios D. Tsoukalas
1337 e546df49 Georgios D. Tsoukalas
        def sync_verify_get_synced_state(self):
1338 e546df49 Georgios D. Tsoukalas
            status = self.sync_get_status()
1339 e546df49 Georgios D. Tsoukalas
            state = self.sync_get_synced_state()
1340 e546df49 Georgios D. Tsoukalas
            verified = (status == self.STATUS_SYNCED)
1341 e546df49 Georgios D. Tsoukalas
            return state, verified
1342 e546df49 Georgios D. Tsoukalas
1343 e546df49 Georgios D. Tsoukalas
        def sync_is_synced(self):
1344 e546df49 Georgios D. Tsoukalas
            state, verified = self.sync_verify_get_synced_state()
1345 e546df49 Georgios D. Tsoukalas
            return verified
1346 e546df49 Georgios D. Tsoukalas
1347 e546df49 Georgios D. Tsoukalas
    return SyncedState
1348 e546df49 Georgios D. Tsoukalas
1349 e546df49 Georgios D. Tsoukalas
SyncedState = make_synced(prefix='sync', name='SyncedState')
1350 425e2e95 Sofia Papagiannaki
1351 425e2e95 Sofia Papagiannaki
1352 8aed306c Giorgos Korfiatis
class ProjectApplication(models.Model):
1353 425e2e95 Sofia Papagiannaki
1354 425e2e95 Sofia Papagiannaki
    applicant               =   models.ForeignKey(
1355 425e2e95 Sofia Papagiannaki
                                    AstakosUser,
1356 d6fdc91e Georgios D. Tsoukalas
                                    related_name='projects_applied',
1357 d6fdc91e Georgios D. Tsoukalas
                                    db_index=True)
1358 d6fdc91e Georgios D. Tsoukalas
1359 5200e864 Sofia Papagiannaki
    project                 =   models.ForeignKey('Project',
1360 5200e864 Sofia Papagiannaki
                                                  related_name='applications',
1361 5200e864 Sofia Papagiannaki
                                                  null=True)
1362 d6fdc91e Georgios D. Tsoukalas
1363 d6fdc91e Georgios D. Tsoukalas
    state                   =   models.CharField(max_length=80,
1364 a7aba804 Sofia Papagiannaki
                                                default=UNKNOWN)
1365 d6fdc91e Georgios D. Tsoukalas
1366 425e2e95 Sofia Papagiannaki
    owner                   =   models.ForeignKey(
1367 425e2e95 Sofia Papagiannaki
                                    AstakosUser,
1368 d6fdc91e Georgios D. Tsoukalas
                                    related_name='projects_owned',
1369 d6fdc91e Georgios D. Tsoukalas
                                    db_index=True)
1370 d6fdc91e Georgios D. Tsoukalas
1371 425e2e95 Sofia Papagiannaki
    precursor_application   =   models.OneToOneField('ProjectApplication',
1372 425e2e95 Sofia Papagiannaki
                                                     null=True,
1373 425e2e95 Sofia Papagiannaki
                                                     blank=True,
1374 d6fdc91e Georgios D. Tsoukalas
                                                     db_index=True)
1375 425e2e95 Sofia Papagiannaki
1376 425e2e95 Sofia Papagiannaki
    name                    =   models.CharField(max_length=80)
1377 425e2e95 Sofia Papagiannaki
    homepage                =   models.URLField(max_length=255, null=True,
1378 425e2e95 Sofia Papagiannaki
                                                blank=True)
1379 172ce682 Sofia Papagiannaki
    description             =   models.TextField(null=True, blank=True)
1380 425e2e95 Sofia Papagiannaki
    start_date              =   models.DateTimeField()
1381 425e2e95 Sofia Papagiannaki
    end_date                =   models.DateTimeField()
1382 425e2e95 Sofia Papagiannaki
    member_join_policy      =   models.ForeignKey(MemberJoinPolicy)
1383 425e2e95 Sofia Papagiannaki
    member_leave_policy     =   models.ForeignKey(MemberLeavePolicy)
1384 425e2e95 Sofia Papagiannaki
    limit_on_members_number =   models.PositiveIntegerField(null=True,
1385 425e2e95 Sofia Papagiannaki
                                                            blank=True)
1386 425e2e95 Sofia Papagiannaki
    resource_grants         =   models.ManyToManyField(
1387 425e2e95 Sofia Papagiannaki
                                    Resource,
1388 425e2e95 Sofia Papagiannaki
                                    null=True,
1389 425e2e95 Sofia Papagiannaki
                                    blank=True,
1390 d6fdc91e Georgios D. Tsoukalas
                                    through='ProjectResourceGrant')
1391 425e2e95 Sofia Papagiannaki
    comments                =   models.TextField(null=True, blank=True)
1392 425e2e95 Sofia Papagiannaki
    issue_date              =   models.DateTimeField()
1393 425e2e95 Sofia Papagiannaki
1394 425e2e95 Sofia Papagiannaki
    states_list =   [PENDING, APPROVED, REPLACED, UNKNOWN]
1395 425e2e95 Sofia Papagiannaki
    states      =   dict((k, v) for v, k in enumerate(states_list))
1396 425e2e95 Sofia Papagiannaki
1397 a7aba804 Sofia Papagiannaki
    def add_resource_policy(self, service, resource, uplimit):
1398 e1a80257 Sofia Papagiannaki
        """Raises ObjectDoesNotExist, IntegrityError"""
1399 a7aba804 Sofia Papagiannaki
        q = self.projectresourcegrant_set
1400 e1a80257 Sofia Papagiannaki
        resource = Resource.objects.get(service__name=service, name=resource)
1401 a7aba804 Sofia Papagiannaki
        q.create(resource=resource, member_capacity=uplimit)
1402 e1a80257 Sofia Papagiannaki
1403 e1a80257 Sofia Papagiannaki
    @property
1404 e1a80257 Sofia Papagiannaki
    def resource_policies(self):
1405 f3342849 Sofia Papagiannaki
        return self.projectresourcegrant_set.all()
1406 e1a80257 Sofia Papagiannaki
1407 e1a80257 Sofia Papagiannaki
    @resource_policies.setter
1408 e1a80257 Sofia Papagiannaki
    def resource_policies(self, policies):
1409 e1a80257 Sofia Papagiannaki
        for p in policies:
1410 e1a80257 Sofia Papagiannaki
            service = p.get('service', None)
1411 e1a80257 Sofia Papagiannaki
            resource = p.get('resource', None)
1412 e1a80257 Sofia Papagiannaki
            uplimit = p.get('uplimit', 0)
1413 a7aba804 Sofia Papagiannaki
            self.add_resource_policy(service, resource, uplimit)
1414 425e2e95 Sofia Papagiannaki
1415 ccab6eb5 Sofia Papagiannaki
    @property
1416 ccab6eb5 Sofia Papagiannaki
    def follower(self):
1417 ccab6eb5 Sofia Papagiannaki
        try:
1418 ccab6eb5 Sofia Papagiannaki
            return ProjectApplication.objects.get(precursor_application=self)
1419 ccab6eb5 Sofia Papagiannaki
        except ProjectApplication.DoesNotExist:
1420 ccab6eb5 Sofia Papagiannaki
            return
1421 ccab6eb5 Sofia Papagiannaki
1422 73fbaec4 Sofia Papagiannaki
    def submit(self, resource_policies, applicant, comments,
1423 30a6c330 Sofia Papagiannaki
               precursor_application=None):
1424 ece3b66e Giorgos Korfiatis
1425 8327782d Sofia Papagiannaki
        if precursor_application:
1426 73fbaec4 Sofia Papagiannaki
            self.precursor_application = precursor_application
1427 73fbaec4 Sofia Papagiannaki
            self.owner = precursor_application.owner
1428 bfe23b13 Sofia Papagiannaki
        else:
1429 73fbaec4 Sofia Papagiannaki
            self.owner = applicant
1430 73fbaec4 Sofia Papagiannaki
1431 73fbaec4 Sofia Papagiannaki
        self.id = None
1432 73fbaec4 Sofia Papagiannaki
        self.applicant = applicant
1433 73fbaec4 Sofia Papagiannaki
        self.comments = comments
1434 73fbaec4 Sofia Papagiannaki
        self.issue_date = datetime.now()
1435 73fbaec4 Sofia Papagiannaki
        self.state = PENDING
1436 d6fdc91e Georgios D. Tsoukalas
        self.save()
1437 a7aba804 Sofia Papagiannaki
        self.resource_policies = resource_policies
1438 ece3b66e Giorgos Korfiatis
1439 4f22664f Georgios D. Tsoukalas
    def _get_project(self):
1440 4f22664f Georgios D. Tsoukalas
        precursor = self
1441 4f22664f Georgios D. Tsoukalas
        while precursor:
1442 4f22664f Georgios D. Tsoukalas
            try:
1443 4f22664f Georgios D. Tsoukalas
                project = precursor.project
1444 4f22664f Georgios D. Tsoukalas
                return project
1445 4f22664f Georgios D. Tsoukalas
            except Project.DoesNotExist:
1446 4f22664f Georgios D. Tsoukalas
                pass
1447 4f22664f Georgios D. Tsoukalas
            precursor = precursor.precursor_application
1448 4f22664f Georgios D. Tsoukalas
1449 4f22664f Georgios D. Tsoukalas
        return None
1450 4f22664f Georgios D. Tsoukalas
1451 ccab6eb5 Sofia Papagiannaki
    def approve(self, approval_user=None):
1452 ccab6eb5 Sofia Papagiannaki
        """
1453 ccab6eb5 Sofia Papagiannaki
        If approval_user then during owner membership acceptance
1454 ccab6eb5 Sofia Papagiannaki
        it is checked whether the request_user is eligible.
1455 262e04c6 Giorgos Korfiatis

1456 2553efae Sofia Papagiannaki
        Raises:
1457 b8f05f8d Sofia Papagiannaki
            PermissionDenied
1458 ccab6eb5 Sofia Papagiannaki
        """
1459 4f22664f Georgios D. Tsoukalas
1460 4f22664f Georgios D. Tsoukalas
        if not transaction.is_managed():
1461 4f22664f Georgios D. Tsoukalas
            raise AssertionError("NOPE")
1462 4f22664f Georgios D. Tsoukalas
1463 73fbaec4 Sofia Papagiannaki
        new_project_name = self.name
1464 bfe23b13 Sofia Papagiannaki
        if self.state != PENDING:
1465 65360c65 Georgios D. Tsoukalas
            m = _("cannot approve: project '%s' in state '%s'") % (
1466 65360c65 Georgios D. Tsoukalas
                    new_project_name, self.state)
1467 4f22664f Georgios D. Tsoukalas
            raise PermissionDenied(m) # invalid argument
1468 262e04c6 Giorgos Korfiatis
1469 fdafae27 Giorgos Korfiatis
        now = datetime.now()
1470 4f22664f Georgios D. Tsoukalas
        project = self._get_project()
1471 4f22664f Georgios D. Tsoukalas
        if project is None:
1472 4f22664f Georgios D. Tsoukalas
            try:
1473 d6fdc91e Georgios D. Tsoukalas
                # needs SERIALIZABLE
1474 4f22664f Georgios D. Tsoukalas
                conflicting_project = Project.objects.get(name=new_project_name)
1475 8aed306c Giorgos Korfiatis
                if conflicting_project.is_alive:
1476 8aed306c Giorgos Korfiatis
                    m = _("cannot approve: project with name '%s' "
1477 65360c65 Georgios D. Tsoukalas
                          "already exists (serial: %s)") % (
1478 65360c65 Georgios D. Tsoukalas
                            new_project_name, conflicting_project.id)
1479 8aed306c Giorgos Korfiatis
                    raise PermissionDenied(m) # invalid argument
1480 4f22664f Georgios D. Tsoukalas
            except Project.DoesNotExist:
1481 4f22664f Georgios D. Tsoukalas
                pass
1482 4f22664f Georgios D. Tsoukalas
            project = Project(creation_date=now)
1483 fdafae27 Giorgos Korfiatis
1484 ee45eb81 Giorgos Korfiatis
        project.application = self
1485 425e2e95 Sofia Papagiannaki
1486 ee45eb81 Giorgos Korfiatis
        # This will block while syncing,
1487 ee45eb81 Giorgos Korfiatis
        # but unblock before setting the membership state.
1488 ee45eb81 Giorgos Korfiatis
        # See ProjectMembership.set_sync()
1489 ee45eb81 Giorgos Korfiatis
        project.set_membership_pending_sync()
1490 425e2e95 Sofia Papagiannaki
1491 fdafae27 Giorgos Korfiatis
        project.last_approval_date = now
1492 73fbaec4 Sofia Papagiannaki
        project.save()
1493 4f22664f Georgios D. Tsoukalas
        #ProjectMembership.add_to_project(self)
1494 4f22664f Georgios D. Tsoukalas
        project.add_member(self.owner)
1495 fdafae27 Giorgos Korfiatis
1496 4f22664f Georgios D. Tsoukalas
        precursor = self.precursor_application
1497 4f22664f Georgios D. Tsoukalas
        while precursor:
1498 4f22664f Georgios D. Tsoukalas
            precursor.state = REPLACED
1499 4f22664f Georgios D. Tsoukalas
            precursor.save()
1500 4f22664f Georgios D. Tsoukalas
            precursor = precursor.precursor_application
1501 262e04c6 Giorgos Korfiatis
1502 bfe23b13 Sofia Papagiannaki
        self.state = APPROVED
1503 bfe23b13 Sofia Papagiannaki
        self.save()
1504 262e04c6 Giorgos Korfiatis
1505 4f22664f Georgios D. Tsoukalas
        transaction.commit()
1506 d6fdc91e Georgios D. Tsoukalas
        trigger_sync()
1507 d6fdc91e Georgios D. Tsoukalas
1508 ccab6eb5 Sofia Papagiannaki
1509 73fbaec4 Sofia Papagiannaki
class ProjectResourceGrant(models.Model):
1510 e1a80257 Sofia Papagiannaki
1511 425e2e95 Sofia Papagiannaki
    resource                =   models.ForeignKey(Resource)
1512 425e2e95 Sofia Papagiannaki
    project_application     =   models.ForeignKey(ProjectApplication,
1513 5200e864 Sofia Papagiannaki
                                                  null=True)
1514 425e2e95 Sofia Papagiannaki
    project_capacity        =   models.BigIntegerField(null=True)
1515 425e2e95 Sofia Papagiannaki
    project_import_limit    =   models.BigIntegerField(null=True)
1516 425e2e95 Sofia Papagiannaki
    project_export_limit    =   models.BigIntegerField(null=True)
1517 425e2e95 Sofia Papagiannaki
    member_capacity         =   models.BigIntegerField(null=True)
1518 425e2e95 Sofia Papagiannaki
    member_import_limit     =   models.BigIntegerField(null=True)
1519 425e2e95 Sofia Papagiannaki
    member_export_limit     =   models.BigIntegerField(null=True)
1520 73fbaec4 Sofia Papagiannaki
1521 73fbaec4 Sofia Papagiannaki
    objects = ExtendedManager()
1522 73fbaec4 Sofia Papagiannaki
1523 73fbaec4 Sofia Papagiannaki
    class Meta:
1524 73fbaec4 Sofia Papagiannaki
        unique_together = ("resource", "project_application")
1525 8327782d Sofia Papagiannaki
1526 e546df49 Georgios D. Tsoukalas
1527 d6fdc91e Georgios D. Tsoukalas
class Project(models.Model):
1528 e546df49 Georgios D. Tsoukalas
1529 ee45eb81 Giorgos Korfiatis
    application                 =   models.OneToOneField(
1530 4f22664f Georgios D. Tsoukalas
                                            ProjectApplication,
1531 5200e864 Sofia Papagiannaki
                                            related_name='app_project')
1532 4f22664f Georgios D. Tsoukalas
    last_approval_date          =   models.DateTimeField(null=True)
1533 4f22664f Georgios D. Tsoukalas
1534 4f22664f Georgios D. Tsoukalas
    members                     =   models.ManyToManyField(
1535 4f22664f Georgios D. Tsoukalas
                                            AstakosUser,
1536 4f22664f Georgios D. Tsoukalas
                                            through='ProjectMembership')
1537 4f22664f Georgios D. Tsoukalas
1538 4f22664f Georgios D. Tsoukalas
    termination_start_date      =   models.DateTimeField(null=True)
1539 4f22664f Georgios D. Tsoukalas
    termination_date            =   models.DateTimeField(null=True)
1540 4f22664f Georgios D. Tsoukalas
1541 4f22664f Georgios D. Tsoukalas
    creation_date               =   models.DateTimeField()
1542 4f22664f Georgios D. Tsoukalas
    name                        =   models.CharField(
1543 4f22664f Georgios D. Tsoukalas
                                            max_length=80,
1544 4f22664f Georgios D. Tsoukalas
                                            db_index=True,
1545 4f22664f Georgios D. Tsoukalas
                                            unique=True)
1546 425e2e95 Sofia Papagiannaki
1547 e1a80257 Sofia Papagiannaki
    @property
1548 8aed306c Giorgos Korfiatis
    def violated_resource_grants(self):
1549 8aed306c Giorgos Korfiatis
        return False
1550 425e2e95 Sofia Papagiannaki
1551 b22de10a Sofia Papagiannaki
    @property
1552 b22de10a Sofia Papagiannaki
    def violated_members_number_limit(self):
1553 ee45eb81 Giorgos Korfiatis
        application = self.application
1554 4e78511a Giorgos Korfiatis
        return len(self.approved_members) > application.limit_on_members_number
1555 425e2e95 Sofia Papagiannaki
1556 e1a80257 Sofia Papagiannaki
    @property
1557 8aed306c Giorgos Korfiatis
    def is_terminated(self):
1558 8aed306c Giorgos Korfiatis
        return bool(self.termination)
1559 425e2e95 Sofia Papagiannaki
1560 8aed306c Giorgos Korfiatis
    @property
1561 8aed306c Giorgos Korfiatis
    def is_still_approved(self):
1562 8aed306c Giorgos Korfiatis
        return bool(self.last_approval_date)
1563 8aed306c Giorgos Korfiatis
1564 8aed306c Giorgos Korfiatis
    @property
1565 e1a80257 Sofia Papagiannaki
    def is_active(self):
1566 8aed306c Giorgos Korfiatis
        if (self.is_terminated or
1567 8aed306c Giorgos Korfiatis
            not self.is_still_approved or
1568 8aed306c Giorgos Korfiatis
            self.violated_resource_grants):
1569 e1a80257 Sofia Papagiannaki
            return False
1570 b22de10a Sofia Papagiannaki
#         if self.violated_members_number_limit:
1571 b22de10a Sofia Papagiannaki
#             return False
1572 e1a80257 Sofia Papagiannaki
        return True
1573 e1a80257 Sofia Papagiannaki
    
1574 e1a80257 Sofia Papagiannaki
    @property
1575 e1a80257 Sofia Papagiannaki
    def is_suspended(self):
1576 8aed306c Giorgos Korfiatis
        if (self.is_terminated or
1577 8aed306c Giorgos Korfiatis
            self.is_still_approved or
1578 8aed306c Giorgos Korfiatis
            not self.violated_resource_grants):
1579 e1a80257 Sofia Papagiannaki
            return False
1580 b22de10a Sofia Papagiannaki
#             if not self.violated_members_number_limit:
1581 b22de10a Sofia Papagiannaki
#                 return False
1582 e1a80257 Sofia Papagiannaki
        return True
1583 425e2e95 Sofia Papagiannaki
1584 e1a80257 Sofia Papagiannaki
    @property
1585 e1a80257 Sofia Papagiannaki
    def is_alive(self):
1586 e1a80257 Sofia Papagiannaki
        return self.is_active or self.is_suspended
1587 425e2e95 Sofia Papagiannaki
1588 e1a80257 Sofia Papagiannaki
    @property
1589 e1a80257 Sofia Papagiannaki
    def is_inconsistent(self):
1590 e1a80257 Sofia Papagiannaki
        now = datetime.now()
1591 e1a80257 Sofia Papagiannaki
        if self.creation_date > now:
1592 e1a80257 Sofia Papagiannaki
            return True
1593 e1a80257 Sofia Papagiannaki
        if self.last_approval_date > now:
1594 e1a80257 Sofia Papagiannaki
            return True
1595 e1a80257 Sofia Papagiannaki
        if self.terminaton_date > now:
1596 e1a80257 Sofia Papagiannaki
            return True
1597 e1a80257 Sofia Papagiannaki
        return False
1598 65360c65 Georgios D. Tsoukalas
1599 425e2e95 Sofia Papagiannaki
    @property
1600 425e2e95 Sofia Papagiannaki
    def approved_memberships(self):
1601 ee45eb81 Giorgos Korfiatis
        ACCEPTED = ProjectMembership.ACCEPTED
1602 ee45eb81 Giorgos Korfiatis
        PENDING  = ProjectMembership.PENDING
1603 425e2e95 Sofia Papagiannaki
        return self.projectmembership_set.filter(
1604 ee45eb81 Giorgos Korfiatis
            Q(state=ACCEPTED) | Q(state=PENDING))
1605 4f22664f Georgios D. Tsoukalas
1606 425e2e95 Sofia Papagiannaki
    @property
1607 425e2e95 Sofia Papagiannaki
    def approved_members(self):
1608 425e2e95 Sofia Papagiannaki
        return [m.person for m in self.approved_memberships]
1609 4f22664f Georgios D. Tsoukalas
1610 ee45eb81 Giorgos Korfiatis
    def set_membership_pending_sync(self):
1611 ee45eb81 Giorgos Korfiatis
        ACCEPTED = ProjectMembership.ACCEPTED
1612 ee45eb81 Giorgos Korfiatis
        PENDING  = ProjectMembership.PENDING
1613 ee45eb81 Giorgos Korfiatis
        sfu = self.projectmembership_set.select_for_update()
1614 ee45eb81 Giorgos Korfiatis
        members = sfu.filter(Q(state=ACCEPTED) | Q(state=PENDING))
1615 4f22664f Georgios D. Tsoukalas
1616 425e2e95 Sofia Papagiannaki
        for member in members:
1617 ee45eb81 Giorgos Korfiatis
            member.state = member.PENDING
1618 425e2e95 Sofia Papagiannaki
            member.save()
1619 4f22664f Georgios D. Tsoukalas
1620 4f22664f Georgios D. Tsoukalas
    def add_member(self, user):
1621 bfe23b13 Sofia Papagiannaki
        """
1622 bfe23b13 Sofia Papagiannaki
        Raises:
1623 bfe23b13 Sofia Papagiannaki
            django.exceptions.PermissionDenied
1624 bfe23b13 Sofia Papagiannaki
            astakos.im.models.AstakosUser.DoesNotExist
1625 bfe23b13 Sofia Papagiannaki
        """
1626 2a965273 Sofia Papagiannaki
        if isinstance(user, int):
1627 4f22664f Georgios D. Tsoukalas
            user = AstakosUser.objects.get(user=user)
1628 4f22664f Georgios D. Tsoukalas
1629 ccab6eb5 Sofia Papagiannaki
        m, created = ProjectMembership.objects.get_or_create(
1630 ccab6eb5 Sofia Papagiannaki
            person=user, project=self
1631 2a965273 Sofia Papagiannaki
        )
1632 4f22664f Georgios D. Tsoukalas
        m.accept()
1633 ccab6eb5 Sofia Papagiannaki
1634 4f22664f Georgios D. Tsoukalas
    def remove_member(self, user):
1635 bfe23b13 Sofia Papagiannaki
        """
1636 bfe23b13 Sofia Papagiannaki
        Raises:
1637 bfe23b13 Sofia Papagiannaki
            django.exceptions.PermissionDenied
1638 bfe23b13 Sofia Papagiannaki
            astakos.im.models.AstakosUser.DoesNotExist
1639 bfe23b13 Sofia Papagiannaki
            astakos.im.models.ProjectMembership.DoesNotExist
1640 bfe23b13 Sofia Papagiannaki
        """
1641 ccab6eb5 Sofia Papagiannaki
        if isinstance(user, int):
1642 4f22664f Georgios D. Tsoukalas
            user = AstakosUser.objects.get(user=user)
1643 4f22664f Georgios D. Tsoukalas
1644 bfe23b13 Sofia Papagiannaki
        m = ProjectMembership.objects.get(person=user, project=self)
1645 bfe23b13 Sofia Papagiannaki
        m.remove()
1646 4f22664f Georgios D. Tsoukalas
1647 b22de10a Sofia Papagiannaki
    def terminate(self):
1648 b22de10a Sofia Papagiannaki
        self.termination_start_date = datetime.now()
1649 b22de10a Sofia Papagiannaki
        self.terminaton_date = None
1650 b22de10a Sofia Papagiannaki
        self.save()
1651 425e2e95 Sofia Papagiannaki
1652 b22de10a Sofia Papagiannaki
        rejected = self.sync()
1653 b22de10a Sofia Papagiannaki
        if not rejected:
1654 b22de10a Sofia Papagiannaki
            self.termination_start_date = None
1655 75fae793 Sofia Papagiannaki
            self.termination_date = datetime.now()
1656 b22de10a Sofia Papagiannaki
            self.save()
1657 425e2e95 Sofia Papagiannaki
1658 73fbaec4 Sofia Papagiannaki
#         try:
1659 73fbaec4 Sofia Papagiannaki
#             notification = build_notification(
1660 73fbaec4 Sofia Papagiannaki
#                 settings.SERVER_EMAIL,
1661 73fbaec4 Sofia Papagiannaki
#                 [self.current_application.owner.email],
1662 73fbaec4 Sofia Papagiannaki
#                 _(PROJECT_TERMINATION_SUBJECT) % self.__dict__,
1663 73fbaec4 Sofia Papagiannaki
#                 template='im/projects/project_termination_notification.txt',
1664 73fbaec4 Sofia Papagiannaki
#                 dictionary={'object':self.current_application}
1665 73fbaec4 Sofia Papagiannaki
#             ).send()
1666 73fbaec4 Sofia Papagiannaki
#         except NotificationError, e:
1667 73fbaec4 Sofia Papagiannaki
#             logger.error(e.messages)
1668 b22de10a Sofia Papagiannaki
1669 b22de10a Sofia Papagiannaki
    def suspend(self):
1670 b22de10a Sofia Papagiannaki
        self.last_approval_date = None
1671 b22de10a Sofia Papagiannaki
        self.save()
1672 c4d1b547 Sofia Papagiannaki
        self.sync()
1673 b8f05f8d Sofia Papagiannaki
1674 73fbaec4 Sofia Papagiannaki
#         try:
1675 73fbaec4 Sofia Papagiannaki
#             notification = build_notification(
1676 73fbaec4 Sofia Papagiannaki
#                 settings.SERVER_EMAIL,
1677 73fbaec4 Sofia Papagiannaki
#                 [self.current_application.owner.email],
1678 73fbaec4 Sofia Papagiannaki
#                 _(PROJECT_SUSPENSION_SUBJECT) % self.definition.__dict__,
1679 73fbaec4 Sofia Papagiannaki
#                 template='im/projects/project_suspension_notification.txt',
1680 73fbaec4 Sofia Papagiannaki
#                 dictionary={'object':self.current_application}
1681 73fbaec4 Sofia Papagiannaki
#             ).send()
1682 73fbaec4 Sofia Papagiannaki
#         except NotificationError, e:
1683 73fbaec4 Sofia Papagiannaki
#             logger.error(e.messages)
1684 b22de10a Sofia Papagiannaki
1685 4f22664f Georgios D. Tsoukalas
1686 65360c65 Georgios D. Tsoukalas
1687 65360c65 Georgios D. Tsoukalas
class ExclusiveOrRaise(object):
1688 65360c65 Georgios D. Tsoukalas
    """Context Manager to exclusively execute a critical code section.
1689 65360c65 Georgios D. Tsoukalas
       The exclusion must be global.
1690 65360c65 Georgios D. Tsoukalas
       (IPC semaphores will not protect across OS,
1691 65360c65 Georgios D. Tsoukalas
        DB locks will if it's the same DB)
1692 65360c65 Georgios D. Tsoukalas
    """
1693 65360c65 Georgios D. Tsoukalas
1694 65360c65 Georgios D. Tsoukalas
    class Busy(Exception):
1695 65360c65 Georgios D. Tsoukalas
        pass
1696 65360c65 Georgios D. Tsoukalas
1697 65360c65 Georgios D. Tsoukalas
    def __init__(self, locked=False):
1698 65360c65 Georgios D. Tsoukalas
        init = 0 if locked else 1
1699 73fbaec4 Sofia Papagiannaki
        from multiprocessing import Semaphore
1700 65360c65 Georgios D. Tsoukalas
        self._sema = Semaphore(init)
1701 65360c65 Georgios D. Tsoukalas
1702 65360c65 Georgios D. Tsoukalas
    def enter(self):
1703 65360c65 Georgios D. Tsoukalas
        acquired = self._sema.acquire(False)
1704 65360c65 Georgios D. Tsoukalas
        if not acquired:
1705 65360c65 Georgios D. Tsoukalas
            raise self.Busy()
1706 65360c65 Georgios D. Tsoukalas
1707 65360c65 Georgios D. Tsoukalas
    def leave(self):
1708 65360c65 Georgios D. Tsoukalas
        self._sema.release()
1709 65360c65 Georgios D. Tsoukalas
1710 65360c65 Georgios D. Tsoukalas
    def __enter__(self):
1711 65360c65 Georgios D. Tsoukalas
        self.enter()
1712 65360c65 Georgios D. Tsoukalas
        return self
1713 65360c65 Georgios D. Tsoukalas
1714 65360c65 Georgios D. Tsoukalas
    def __exit__(self, exc_type, exc_value, exc_traceback):
1715 65360c65 Georgios D. Tsoukalas
        self.leave()
1716 65360c65 Georgios D. Tsoukalas
1717 65360c65 Georgios D. Tsoukalas
1718 65360c65 Georgios D. Tsoukalas
exclusive_or_raise = ExclusiveOrRaise(locked=False)
1719 65360c65 Georgios D. Tsoukalas
1720 65360c65 Georgios D. Tsoukalas
1721 d6fdc91e Georgios D. Tsoukalas
class ProjectMembership(models.Model):
1722 4f22664f Georgios D. Tsoukalas
1723 425e2e95 Sofia Papagiannaki
    person              =   models.ForeignKey(AstakosUser)
1724 425e2e95 Sofia Papagiannaki
    request_date        =   models.DateField(default=datetime.now())
1725 d6fdc91e Georgios D. Tsoukalas
    project             =   models.ForeignKey(Project)
1726 d6fdc91e Georgios D. Tsoukalas
1727 d6fdc91e Georgios D. Tsoukalas
    state               =   models.IntegerField(default=0)
1728 5200e864 Sofia Papagiannaki
    application         =   models.ForeignKey(
1729 5200e864 Sofia Papagiannaki
                                ProjectApplication,
1730 5200e864 Sofia Papagiannaki
                                null=True,
1731 5200e864 Sofia Papagiannaki
                                related_name='memberships')
1732 5200e864 Sofia Papagiannaki
    pending_application =   models.ForeignKey(
1733 5200e864 Sofia Papagiannaki
                                ProjectApplication,
1734 5200e864 Sofia Papagiannaki
                                null=True,
1735 5200e864 Sofia Papagiannaki
                                related_name='pending_memebrships')
1736 d6fdc91e Georgios D. Tsoukalas
    pending_serial      =   models.BigIntegerField(null=True, db_index=True)
1737 425e2e95 Sofia Papagiannaki
1738 425e2e95 Sofia Papagiannaki
    acceptance_date     =   models.DateField(null=True, db_index=True)
1739 425e2e95 Sofia Papagiannaki
    leave_request_date  =   models.DateField(null=True)
1740 2a965273 Sofia Papagiannaki
1741 ee45eb81 Giorgos Korfiatis
    objects     =   ForUpdateManager()
1742 ee45eb81 Giorgos Korfiatis
1743 65360c65 Georgios D. Tsoukalas
    REQUESTED   =   0
1744 d6fdc91e Georgios D. Tsoukalas
    PENDING     =   1
1745 d6fdc91e Georgios D. Tsoukalas
    ACCEPTED    =   2
1746 d6fdc91e Georgios D. Tsoukalas
    REMOVING    =   3
1747 d6fdc91e Georgios D. Tsoukalas
    REMOVED     =   4
1748 65360c65 Georgios D. Tsoukalas
1749 0cc22d47 Sofia Papagiannaki
    class Meta:
1750 0cc22d47 Sofia Papagiannaki
        unique_together = ("person", "project")
1751 d6fdc91e Georgios D. Tsoukalas
        #index_together = [["project", "state"]]
1752 bfe23b13 Sofia Papagiannaki
1753 65360c65 Georgios D. Tsoukalas
    def __str__(self):
1754 65360c65 Georgios D. Tsoukalas
        return _("<'%s' membership in project '%s'>") % (
1755 65360c65 Georgios D. Tsoukalas
                self.person.username, self.project.application)
1756 65360c65 Georgios D. Tsoukalas
1757 65360c65 Georgios D. Tsoukalas
    __repr__ = __str__
1758 65360c65 Georgios D. Tsoukalas
1759 65360c65 Georgios D. Tsoukalas
    def __init__(self, *args, **kwargs):
1760 ee45eb81 Giorgos Korfiatis
        self.state = self.REQUESTED
1761 65360c65 Georgios D. Tsoukalas
        super(ProjectMembership, self).__init__(*args, **kwargs)
1762 65360c65 Georgios D. Tsoukalas
1763 4f22664f Georgios D. Tsoukalas
    def _set_history_item(self, reason, date=None):
1764 4f22664f Georgios D. Tsoukalas
        if isinstance(reason, basestring):
1765 4f22664f Georgios D. Tsoukalas
            reason = ProjectMembershipHistory.reasons.get(reason, -1)
1766 4f22664f Georgios D. Tsoukalas
1767 4f22664f Georgios D. Tsoukalas
        history_item = ProjectMembershipHistory(
1768 4f22664f Georgios D. Tsoukalas
                            serial=self.id,
1769 4f22664f Georgios D. Tsoukalas
                            person=self.person,
1770 4f22664f Georgios D. Tsoukalas
                            project=self.project,
1771 4f22664f Georgios D. Tsoukalas
                            date=date,
1772 4f22664f Georgios D. Tsoukalas
                            reason=reason)
1773 4f22664f Georgios D. Tsoukalas
        history_item.save()
1774 4f22664f Georgios D. Tsoukalas
        serial = history_item.id
1775 4f22664f Georgios D. Tsoukalas
1776 4f22664f Georgios D. Tsoukalas
    def accept(self):
1777 5200e864 Sofia Papagiannaki
        state = self.state
1778 65360c65 Georgios D. Tsoukalas
        if state != self.REQUESTED:
1779 d6fdc91e Georgios D. Tsoukalas
            m = _("%s: attempt to accept in state [%s]") % (self, state)
1780 65360c65 Georgios D. Tsoukalas
            raise AssertionError(m)
1781 4f22664f Georgios D. Tsoukalas
1782 65360c65 Georgios D. Tsoukalas
        now = datetime.now()
1783 65360c65 Georgios D. Tsoukalas
        self.acceptance_date = now
1784 65360c65 Georgios D. Tsoukalas
        self._set_history_item(reason='ACCEPT', date=now)
1785 d6fdc91e Georgios D. Tsoukalas
        self.state = self.PENDING
1786 65360c65 Georgios D. Tsoukalas
        self.save()
1787 ee45eb81 Giorgos Korfiatis
        trigger_sync()
1788 4f22664f Georgios D. Tsoukalas
1789 65360c65 Georgios D. Tsoukalas
    def remove(self):
1790 65360c65 Georgios D. Tsoukalas
        if state != self.ACCEPTED:
1791 65360c65 Georgios D. Tsoukalas
            m = _("%s: attempt to remove in state '%s'") % (self, state)
1792 65360c65 Georgios D. Tsoukalas
            raise AssertionError(m)
1793 4f22664f Georgios D. Tsoukalas
1794 ee45eb81 Giorgos Korfiatis
        self._set_history_item(reason='REMOVE')
1795 d6fdc91e Georgios D. Tsoukalas
        self.state = self.REMOVING
1796 0cc22d47 Sofia Papagiannaki
        self.save()
1797 ee45eb81 Giorgos Korfiatis
        trigger_sync()
1798 b8f05f8d Sofia Papagiannaki
1799 65360c65 Georgios D. Tsoukalas
    def reject(self):
1800 65360c65 Georgios D. Tsoukalas
        if state != self.REQUESTED:
1801 65360c65 Georgios D. Tsoukalas
            m = _("%s: attempt to remove in state '%s'") % (self, state)
1802 65360c65 Georgios D. Tsoukalas
            raise AssertionError(m)
1803 65360c65 Georgios D. Tsoukalas
1804 65360c65 Georgios D. Tsoukalas
        # rejected requests don't need sync,
1805 65360c65 Georgios D. Tsoukalas
        # because they were never effected
1806 65360c65 Georgios D. Tsoukalas
        self._set_history_item(reason='REJECT')
1807 0cc22d47 Sofia Papagiannaki
        self.delete()
1808 b8f05f8d Sofia Papagiannaki
1809 d6fdc91e Georgios D. Tsoukalas
    def get_diff_quotas(self, limits_list=None, remove=False):
1810 65360c65 Georgios D. Tsoukalas
        if limits_list is None:
1811 65360c65 Georgios D. Tsoukalas
            limits_list = []
1812 d6fdc91e Georgios D. Tsoukalas
1813 65360c65 Georgios D. Tsoukalas
        append = limits_list.append
1814 82d7e9ef Georgios D. Tsoukalas
        holder = self.person.username
1815 d6fdc91e Georgios D. Tsoukalas
        key = "1"
1816 d6fdc91e Georgios D. Tsoukalas
1817 d6fdc91e Georgios D. Tsoukalas
        tmp_grants = {}
1818 d6fdc91e Georgios D. Tsoukalas
        synced_application = self.application
1819 d6fdc91e Georgios D. Tsoukalas
        if synced_application is not None:
1820 d6fdc91e Georgios D. Tsoukalas
            # first, inverse all current limits, and index them by resource name
1821 d6fdc91e Georgios D. Tsoukalas
            cur_grants = synced_application.resource_grants.all()
1822 d6fdc91e Georgios D. Tsoukalas
            f = -1
1823 d6fdc91e Georgios D. Tsoukalas
            for grant in cur_grants:
1824 d6fdc91e Georgios D. Tsoukalas
                name = grant.resource.name
1825 d6fdc91e Georgios D. Tsoukalas
                tmp_grants[name] = QuotaLimits(
1826 d6fdc91e Georgios D. Tsoukalas
                                holder       = holder,
1827 d6fdc91e Georgios D. Tsoukalas
                                resource     = name,
1828 d6fdc91e Georgios D. Tsoukalas
                                capacity     = f * grant.member_capacity,
1829 d6fdc91e Georgios D. Tsoukalas
                                import_limit = f * grant.member_import_limit,
1830 d6fdc91e Georgios D. Tsoukalas
                                export_limit = f * grant.member_export_limit)
1831 d6fdc91e Georgios D. Tsoukalas
1832 d6fdc91e Georgios D. Tsoukalas
        if not remove:
1833 d6fdc91e Georgios D. Tsoukalas
            # second, add each new limit to its inverted current
1834 d6fdc91e Georgios D. Tsoukalas
            new_grants = self.pending_application.resource_grants.all()
1835 d6fdc91e Georgios D. Tsoukalas
            for new_grant in new_grants:
1836 d6fdc91e Georgios D. Tsoukalas
                name = grant.resource.name
1837 d6fdc91e Georgios D. Tsoukalas
                cur_grant = tmp_grants.pop(name, None)
1838 d6fdc91e Georgios D. Tsoukalas
                if cur_grant is None:
1839 d6fdc91e Georgios D. Tsoukalas
                    # if limits on a new resource, set 0 current values
1840 d6fdc91e Georgios D. Tsoukalas
                    capacity = 0
1841 d6fdc91e Georgios D. Tsoukalas
                    import_limit = 0
1842 d6fdc91e Georgios D. Tsoukalas
                    export_limit = 0
1843 d6fdc91e Georgios D. Tsoukalas
                else:
1844 d6fdc91e Georgios D. Tsoukalas
                    capacity = cur_grant.capacity
1845 d6fdc91e Georgios D. Tsoukalas
                    import_limit = cur_grant.import_limit
1846 d6fdc91e Georgios D. Tsoukalas
                    export_limit = cur_grant.export_limit
1847 d6fdc91e Georgios D. Tsoukalas
1848 ee45eb81 Giorgos Korfiatis
                capacity += new_grant.member_capacity
1849 ee45eb81 Giorgos Korfiatis
                import_limit += new_grant.member_import_limit
1850 ee45eb81 Giorgos Korfiatis
                export_limit += new_grant.member_export_limit
1851 d6fdc91e Georgios D. Tsoukalas
1852 d6fdc91e Georgios D. Tsoukalas
                append(QuotaLimits(holder       = holder,
1853 d6fdc91e Georgios D. Tsoukalas
                                   key          = key,
1854 d6fdc91e Georgios D. Tsoukalas
                                   resource     = name,
1855 d6fdc91e Georgios D. Tsoukalas
                                   capacity     = capacity,
1856 d6fdc91e Georgios D. Tsoukalas
                                   import_limit = import_limit,
1857 d6fdc91e Georgios D. Tsoukalas
                                   export_limit = export_limit))
1858 d6fdc91e Georgios D. Tsoukalas
1859 d6fdc91e Georgios D. Tsoukalas
        # third, append all the inverted current limits for removed resources
1860 d6fdc91e Georgios D. Tsoukalas
        limits_list.extend(tmp_grants.itervalues())
1861 65360c65 Georgios D. Tsoukalas
        return limits_list
1862 65360c65 Georgios D. Tsoukalas
1863 ee45eb81 Giorgos Korfiatis
    def set_sync(self):
1864 ee45eb81 Giorgos Korfiatis
        state = self.state
1865 ee45eb81 Giorgos Korfiatis
        if state == self.PENDING:
1866 ee45eb81 Giorgos Korfiatis
            pending_application = self.pending_application
1867 ee45eb81 Giorgos Korfiatis
            if pending_application is None:
1868 ee45eb81 Giorgos Korfiatis
                m = _("%s: attempt to sync an empty pending application") % (
1869 ee45eb81 Giorgos Korfiatis
                    self, state)
1870 ee45eb81 Giorgos Korfiatis
                raise AssertionError(m)
1871 ee45eb81 Giorgos Korfiatis
            self.application = pending_application
1872 ee45eb81 Giorgos Korfiatis
            self.pending_application = None
1873 ee45eb81 Giorgos Korfiatis
            self.pending_serial = None
1874 ee45eb81 Giorgos Korfiatis
1875 ee45eb81 Giorgos Korfiatis
            # project.application may have changed in the meantime,
1876 ee45eb81 Giorgos Korfiatis
            # in which case we stay PENDING;
1877 ee45eb81 Giorgos Korfiatis
            # we are safe to check due to select_for_update
1878 ee45eb81 Giorgos Korfiatis
            if self.application == self.project.application:
1879 ee45eb81 Giorgos Korfiatis
                self.state = self.ACCEPTED
1880 ee45eb81 Giorgos Korfiatis
            self.save()
1881 ee45eb81 Giorgos Korfiatis
        elif state == self.REMOVING:
1882 ee45eb81 Giorgos Korfiatis
            self.delete()
1883 ee45eb81 Giorgos Korfiatis
        else:
1884 ee45eb81 Giorgos Korfiatis
            m = _("%s: attempt to sync in state '%s'") % (self, state)
1885 ee45eb81 Giorgos Korfiatis
            raise AssertionError(m)
1886 ee45eb81 Giorgos Korfiatis
1887 ee45eb81 Giorgos Korfiatis
class Serial(models.Model):
1888 ee45eb81 Giorgos Korfiatis
    serial  =   models.AutoField(primary_key=True)
1889 ee45eb81 Giorgos Korfiatis
1890 ee45eb81 Giorgos Korfiatis
def new_serial():
1891 5200e864 Sofia Papagiannaki
    s = Serial.objects.create()
1892 ee45eb81 Giorgos Korfiatis
    return s.serial
1893 82d7e9ef Georgios D. Tsoukalas
1894 d6fdc91e Georgios D. Tsoukalas
def sync_finish_serials():
1895 ee45eb81 Giorgos Korfiatis
    serials_to_ack = set(qh_query_serials([]))
1896 d6fdc91e Georgios D. Tsoukalas
    sfu = ProjectMembership.objects.select_for_update()
1897 d6fdc91e Georgios D. Tsoukalas
    memberships = sfu.filter(pending_serial__isnull=False)
1898 82d7e9ef Georgios D. Tsoukalas
1899 d6fdc91e Georgios D. Tsoukalas
    for membership in memberships:
1900 5200e864 Sofia Papagiannaki
        serial = membership.pending_serial
1901 d6fdc91e Georgios D. Tsoukalas
        # just make sure the project row is selected for update
1902 d6fdc91e Georgios D. Tsoukalas
        project = membership.project
1903 d6fdc91e Georgios D. Tsoukalas
        if serial in serials_to_ack:
1904 d6fdc91e Georgios D. Tsoukalas
            membership.set_sync()
1905 425e2e95 Sofia Papagiannaki
1906 d6fdc91e Georgios D. Tsoukalas
    transaction.commit()
1907 ee45eb81 Giorgos Korfiatis
    qh_ack_serials(list(serials_to_ack))
1908 82d7e9ef Georgios D. Tsoukalas
1909 d6fdc91e Georgios D. Tsoukalas
def sync_projects():
1910 d6fdc91e Georgios D. Tsoukalas
    sync_finish_serials()
1911 82d7e9ef Georgios D. Tsoukalas
1912 d6fdc91e Georgios D. Tsoukalas
    PENDING = ProjectMembership.PENDING
1913 d6fdc91e Georgios D. Tsoukalas
    REMOVING = ProjectMembership.REMOVING
1914 d6fdc91e Georgios D. Tsoukalas
    objects = ProjectMembership.objects.select_for_update()
1915 82d7e9ef Georgios D. Tsoukalas
1916 d6fdc91e Georgios D. Tsoukalas
    quotas = []
1917 65360c65 Georgios D. Tsoukalas
1918 ee45eb81 Giorgos Korfiatis
    serial = new_serial()
1919 d6fdc91e Georgios D. Tsoukalas
1920 d6fdc91e Georgios D. Tsoukalas
    pending = objects.filter(state=PENDING)
1921 d6fdc91e Georgios D. Tsoukalas
    for membership in pending:
1922 d6fdc91e Georgios D. Tsoukalas
1923 d6fdc91e Georgios D. Tsoukalas
        if membership.pending_application:
1924 d6fdc91e Georgios D. Tsoukalas
            m = "%s: impossible: pending_application is not None (%s)" % (
1925 d6fdc91e Georgios D. Tsoukalas
                membership, membership.pending_application)
1926 d6fdc91e Georgios D. Tsoukalas
            raise AssertionError(m)
1927 d6fdc91e Georgios D. Tsoukalas
        if membership.pending_serial:
1928 d6fdc91e Georgios D. Tsoukalas
            m = "%s: impossible: pending_serial is not None (%s)" % (
1929 d6fdc91e Georgios D. Tsoukalas
                membership, membership.pending_serial)
1930 65360c65 Georgios D. Tsoukalas
            raise AssertionError(m)
1931 b8f05f8d Sofia Papagiannaki
1932 ee45eb81 Giorgos Korfiatis
        membership.pending_application = membership.project.application
1933 d6fdc91e Georgios D. Tsoukalas
        membership.pending_serial = serial
1934 d6fdc91e Georgios D. Tsoukalas
        membership.get_diff_quotas(quotas)
1935 d6fdc91e Georgios D. Tsoukalas
        membership.save()
1936 65360c65 Georgios D. Tsoukalas
1937 d6fdc91e Georgios D. Tsoukalas
    removing = objects.filter(state=REMOVING)
1938 d6fdc91e Georgios D. Tsoukalas
    for membership in removing:
1939 bfe23b13 Sofia Papagiannaki
1940 d6fdc91e Georgios D. Tsoukalas
        if membership.pending_application:
1941 d6fdc91e Georgios D. Tsoukalas
            m = ("%s: impossible: removing pending_application is not None (%s)"
1942 d6fdc91e Georgios D. Tsoukalas
                % (membership, membership.pending_application))
1943 d6fdc91e Georgios D. Tsoukalas
            raise AssertionError(m)
1944 d6fdc91e Georgios D. Tsoukalas
        if membership.pending_serial:
1945 d6fdc91e Georgios D. Tsoukalas
            m = "%s: impossible: pending_serial is not None (%s)" % (
1946 d6fdc91e Georgios D. Tsoukalas
                membership, membership.pending_serial)
1947 d6fdc91e Georgios D. Tsoukalas
            raise AssertionError(m)
1948 d6fdc91e Georgios D. Tsoukalas
1949 d6fdc91e Georgios D. Tsoukalas
        membership.pending_serial = serial
1950 d6fdc91e Georgios D. Tsoukalas
        membership.get_diff_quotas(quotas, remove=True)
1951 d6fdc91e Georgios D. Tsoukalas
        membership.save()
1952 d6fdc91e Georgios D. Tsoukalas
1953 d6fdc91e Georgios D. Tsoukalas
    transaction.commit()
1954 ee45eb81 Giorgos Korfiatis
    # ProjectApplication.approve() unblocks here
1955 ee45eb81 Giorgos Korfiatis
    # and can set PENDING an already PENDING membership
1956 ee45eb81 Giorgos Korfiatis
    # which has been scheduled to sync with the old project.application
1957 ee45eb81 Giorgos Korfiatis
    # Need to check in ProjectMembership.set_sync()
1958 ee45eb81 Giorgos Korfiatis
1959 ee45eb81 Giorgos Korfiatis
    qh_add_quota(serial, quotas)
1960 d6fdc91e Georgios D. Tsoukalas
    sync_finish_serials()
1961 d6fdc91e Georgios D. Tsoukalas
1962 d6fdc91e Georgios D. Tsoukalas
1963 d6fdc91e Georgios D. Tsoukalas
def trigger_sync(retries=3, retry_wait=1.0):
1964 d6fdc91e Georgios D. Tsoukalas
    cursor = connection.cursor()
1965 d6fdc91e Georgios D. Tsoukalas
    locked = True
1966 d6fdc91e Georgios D. Tsoukalas
    try:
1967 d6fdc91e Georgios D. Tsoukalas
        while 1:
1968 d6fdc91e Georgios D. Tsoukalas
            cursor.execute("SELECT pg_try_advisory_lock(1)")
1969 d6fdc91e Georgios D. Tsoukalas
            r = cursor.fetchone()
1970 d6fdc91e Georgios D. Tsoukalas
            if r is None:
1971 d6fdc91e Georgios D. Tsoukalas
                m = "Impossible"
1972 d6fdc91e Georgios D. Tsoukalas
                raise AssertionError(m)
1973 d6fdc91e Georgios D. Tsoukalas
            locked = r[0]
1974 d6fdc91e Georgios D. Tsoukalas
            if locked:
1975 d6fdc91e Georgios D. Tsoukalas
                break
1976 d6fdc91e Georgios D. Tsoukalas
1977 d6fdc91e Georgios D. Tsoukalas
            retries -= 1
1978 d6fdc91e Georgios D. Tsoukalas
            if retries <= 0:
1979 d6fdc91e Georgios D. Tsoukalas
                return False
1980 d6fdc91e Georgios D. Tsoukalas
            sleep(retry_wait)
1981 d6fdc91e Georgios D. Tsoukalas
1982 d6fdc91e Georgios D. Tsoukalas
        sync_projects()
1983 d6fdc91e Georgios D. Tsoukalas
        return True
1984 d6fdc91e Georgios D. Tsoukalas
1985 d6fdc91e Georgios D. Tsoukalas
    finally:
1986 d6fdc91e Georgios D. Tsoukalas
        if locked:
1987 d6fdc91e Georgios D. Tsoukalas
            cursor.execute("SELECT pg_advisory_unlock(1)")
1988 d6fdc91e Georgios D. Tsoukalas
            cursor.fetchall()
1989 4f22664f Georgios D. Tsoukalas
1990 e1a80257 Sofia Papagiannaki
1991 0cc22d47 Sofia Papagiannaki
class ProjectMembershipHistory(models.Model):
1992 425e2e95 Sofia Papagiannaki
    reasons_list    =   ['ACCEPT', 'REJECT', 'REMOVE']
1993 425e2e95 Sofia Papagiannaki
    reasons         =   dict((k, v) for v, k in enumerate(reasons_list))
1994 425e2e95 Sofia Papagiannaki
1995 425e2e95 Sofia Papagiannaki
    person  =   models.ForeignKey(AstakosUser)
1996 425e2e95 Sofia Papagiannaki
    project =   models.ForeignKey(Project)
1997 425e2e95 Sofia Papagiannaki
    date    =   models.DateField(default=datetime.now)
1998 425e2e95 Sofia Papagiannaki
    reason  =   models.IntegerField()
1999 425e2e95 Sofia Papagiannaki
    serial  =   models.BigIntegerField()
2000 d6fdc91e Georgios D. Tsoukalas
    
2001 0cc22d47 Sofia Papagiannaki
2002 e1a80257 Sofia Papagiannaki
def filter_queryset_by_property(q, property):
2003 e1a80257 Sofia Papagiannaki
    """
2004 e1a80257 Sofia Papagiannaki
    Incorporate list comprehension for filtering querysets by property
2005 e1a80257 Sofia Papagiannaki
    since Queryset.filter() operates on the database level.
2006 e1a80257 Sofia Papagiannaki
    """
2007 e1a80257 Sofia Papagiannaki
    return (p for p in q if getattr(p, property, False))
2008 e1a80257 Sofia Papagiannaki
2009 e1a80257 Sofia Papagiannaki
def get_alive_projects():
2010 e1a80257 Sofia Papagiannaki
    return filter_queryset_by_property(
2011 e1a80257 Sofia Papagiannaki
        Project.objects.all(),
2012 e1a80257 Sofia Papagiannaki
        'is_alive'
2013 e1a80257 Sofia Papagiannaki
    )
2014 e1a80257 Sofia Papagiannaki
2015 e1a80257 Sofia Papagiannaki
def get_active_projects():
2016 e1a80257 Sofia Papagiannaki
    return filter_queryset_by_property(
2017 e1a80257 Sofia Papagiannaki
        Project.objects.all(),
2018 e1a80257 Sofia Papagiannaki
        'is_active'
2019 e1a80257 Sofia Papagiannaki
    )
2020 e1a80257 Sofia Papagiannaki
2021 e1a80257 Sofia Papagiannaki
def _create_object(model, **kwargs):
2022 e1a80257 Sofia Papagiannaki
    o = model.objects.create(**kwargs)
2023 e1a80257 Sofia Papagiannaki
    o.save()
2024 e1a80257 Sofia Papagiannaki
    return o
2025 e1a80257 Sofia Papagiannaki
2026 b22de10a Sofia Papagiannaki
2027 ff9290ec Sofia Papagiannaki
def create_astakos_user(u):
2028 ff9290ec Sofia Papagiannaki
    try:
2029 ff9290ec Sofia Papagiannaki
        AstakosUser.objects.get(user_ptr=u.pk)
2030 ff9290ec Sofia Papagiannaki
    except AstakosUser.DoesNotExist:
2031 ff9290ec Sofia Papagiannaki
        extended_user = AstakosUser(user_ptr_id=u.pk)
2032 ff9290ec Sofia Papagiannaki
        extended_user.__dict__.update(u.__dict__)
2033 ff9290ec Sofia Papagiannaki
        extended_user.save()
2034 67be1883 Olga Brani
        if not extended_user.has_auth_provider('local'):
2035 67be1883 Olga Brani
            extended_user.add_auth_provider('local')
2036 fc1e2f02 Sofia Papagiannaki
    except BaseException, e:
2037 fc1e2f02 Sofia Papagiannaki
        logger.exception(e)
2038 ff9290ec Sofia Papagiannaki
2039 5ce3ce4f Sofia Papagiannaki
2040 fc1e2f02 Sofia Papagiannaki
def fix_superusers(sender, **kwargs):
2041 fc1e2f02 Sofia Papagiannaki
    # Associate superusers with AstakosUser
2042 ff9290ec Sofia Papagiannaki
    admins = User.objects.filter(is_superuser=True)
2043 ff9290ec Sofia Papagiannaki
    for u in admins:
2044 ff9290ec Sofia Papagiannaki
        create_astakos_user(u)
2045 bfe23b13 Sofia Papagiannaki
post_syncdb.connect(fix_superusers)
2046 ff9290ec Sofia Papagiannaki
2047 ff9290ec Sofia Papagiannaki
2048 fc1e2f02 Sofia Papagiannaki
def user_post_save(sender, instance, created, **kwargs):
2049 aa4109d4 Sofia Papagiannaki
    if not created:
2050 aa4109d4 Sofia Papagiannaki
        return
2051 fc1e2f02 Sofia Papagiannaki
    create_astakos_user(instance)
2052 bfe23b13 Sofia Papagiannaki
post_save.connect(user_post_save, sender=User)
2053 ff9290ec Sofia Papagiannaki
2054 bf0c6de5 Sofia Papagiannaki
2055 73fbaec4 Sofia Papagiannaki
# def astakosuser_pre_save(sender, instance, **kwargs):
2056 73fbaec4 Sofia Papagiannaki
#     instance.aquarium_report = False
2057 73fbaec4 Sofia Papagiannaki
#     instance.new = False
2058 73fbaec4 Sofia Papagiannaki
#     try:
2059 73fbaec4 Sofia Papagiannaki
#         db_instance = AstakosUser.objects.get(id=instance.id)
2060 73fbaec4 Sofia Papagiannaki
#     except AstakosUser.DoesNotExist:
2061 73fbaec4 Sofia Papagiannaki
#         # create event
2062 73fbaec4 Sofia Papagiannaki
#         instance.aquarium_report = True
2063 73fbaec4 Sofia Papagiannaki
#         instance.new = True
2064 73fbaec4 Sofia Papagiannaki
#     else:
2065 73fbaec4 Sofia Papagiannaki
#         get = AstakosUser.__getattribute__
2066 73fbaec4 Sofia Papagiannaki
#         l = filter(lambda f: get(db_instance, f) != get(instance, f),
2067 73fbaec4 Sofia Papagiannaki
#                    BILLING_FIELDS)
2068 73fbaec4 Sofia Papagiannaki
#         instance.aquarium_report = True if l else False
2069 73fbaec4 Sofia Papagiannaki
# pre_save.connect(astakosuser_pre_save, sender=AstakosUser)
2070 73fbaec4 Sofia Papagiannaki
2071 73fbaec4 Sofia Papagiannaki
# def set_default_group(user):
2072 73fbaec4 Sofia Papagiannaki
#     try:
2073 73fbaec4 Sofia Papagiannaki
#         default = AstakosGroup.objects.get(name='default')
2074 73fbaec4 Sofia Papagiannaki
#         Membership(
2075 73fbaec4 Sofia Papagiannaki
#             group=default, person=user, date_joined=datetime.now()).save()
2076 73fbaec4 Sofia Papagiannaki
#     except AstakosGroup.DoesNotExist, e:
2077 73fbaec4 Sofia Papagiannaki
#         logger.exception(e)
2078 fc1e2f02 Sofia Papagiannaki
2079 5ce3ce4f Sofia Papagiannaki
2080 fc1e2f02 Sofia Papagiannaki
def astakosuser_post_save(sender, instance, created, **kwargs):
2081 73fbaec4 Sofia Papagiannaki
#     if instance.aquarium_report:
2082 73fbaec4 Sofia Papagiannaki
#         report_user_event(instance, create=instance.new)
2083 fc1e2f02 Sofia Papagiannaki
    if not created:
2084 fc1e2f02 Sofia Papagiannaki
        return
2085 73fbaec4 Sofia Papagiannaki
#     set_default_group(instance)
2086 fc1e2f02 Sofia Papagiannaki
    # TODO handle socket.error & IOError
2087 fc1e2f02 Sofia Papagiannaki
    register_users((instance,))
2088 bfe23b13 Sofia Papagiannaki
post_save.connect(astakosuser_post_save, sender=AstakosUser)
2089 fc1e2f02 Sofia Papagiannaki
2090 5ce3ce4f Sofia Papagiannaki
2091 bd4f356c Sofia Papagiannaki
def resource_post_save(sender, instance, created, **kwargs):
2092 bd4f356c Sofia Papagiannaki
    if not created:
2093 bd4f356c Sofia Papagiannaki
        return
2094 bd4f356c Sofia Papagiannaki
    register_resources((instance,))
2095 bfe23b13 Sofia Papagiannaki
post_save.connect(resource_post_save, sender=Resource)
2096 bfe23b13 Sofia Papagiannaki
2097 bfe23b13 Sofia Papagiannaki
2098 73fbaec4 Sofia Papagiannaki
# def on_quota_disturbed(sender, users, **kwargs):
2099 73fbaec4 Sofia Papagiannaki
# #     print '>>>', locals()
2100 73fbaec4 Sofia Papagiannaki
#     if not users:
2101 73fbaec4 Sofia Papagiannaki
#         return
2102 73fbaec4 Sofia Papagiannaki
#     send_quota(users)
2103 425e2e95 Sofia Papagiannaki
#
2104 73fbaec4 Sofia Papagiannaki
# quota_disturbed = Signal(providing_args=["users"])
2105 73fbaec4 Sofia Papagiannaki
# quota_disturbed.connect(on_quota_disturbed)
2106 73fbaec4 Sofia Papagiannaki
2107 73fbaec4 Sofia Papagiannaki
2108 73fbaec4 Sofia Papagiannaki
# def send_quota_disturbed(sender, instance, **kwargs):
2109 73fbaec4 Sofia Papagiannaki
#     users = []
2110 73fbaec4 Sofia Papagiannaki
#     extend = users.extend
2111 73fbaec4 Sofia Papagiannaki
#     if sender == Membership:
2112 73fbaec4 Sofia Papagiannaki
#         if not instance.group.is_enabled:
2113 73fbaec4 Sofia Papagiannaki
#             return
2114 73fbaec4 Sofia Papagiannaki
#         extend([instance.person])
2115 73fbaec4 Sofia Papagiannaki
#     elif sender == AstakosUserQuota:
2116 73fbaec4 Sofia Papagiannaki
#         extend([instance.user])
2117 73fbaec4 Sofia Papagiannaki
#     elif sender == AstakosGroupQuota:
2118 73fbaec4 Sofia Papagiannaki
#         if not instance.group.is_enabled:
2119 73fbaec4 Sofia Papagiannaki
#             return
2120 73fbaec4 Sofia Papagiannaki
#         extend(instance.group.astakosuser_set.all())
2121 73fbaec4 Sofia Papagiannaki
#     elif sender == AstakosGroup:
2122 73fbaec4 Sofia Papagiannaki
#         if not instance.is_enabled:
2123 73fbaec4 Sofia Papagiannaki
#             return
2124 73fbaec4 Sofia Papagiannaki
#     quota_disturbed.send(sender=sender, users=users)
2125 73fbaec4 Sofia Papagiannaki
# post_delete.connect(send_quota_disturbed, sender=AstakosGroup)
2126 73fbaec4 Sofia Papagiannaki
# post_delete.connect(send_quota_disturbed, sender=Membership)
2127 73fbaec4 Sofia Papagiannaki
# post_save.connect(send_quota_disturbed, sender=AstakosUserQuota)
2128 73fbaec4 Sofia Papagiannaki
# post_delete.connect(send_quota_disturbed, sender=AstakosUserQuota)
2129 73fbaec4 Sofia Papagiannaki
# post_save.connect(send_quota_disturbed, sender=AstakosGroupQuota)
2130 73fbaec4 Sofia Papagiannaki
# post_delete.connect(send_quota_disturbed, sender=AstakosGroupQuota)
2131 c0b26605 Sofia Papagiannaki
2132 bfe23b13 Sofia Papagiannaki
2133 bfe23b13 Sofia Papagiannaki
def renew_token(sender, instance, **kwargs):
2134 bfe23b13 Sofia Papagiannaki
    if not instance.auth_token:
2135 bfe23b13 Sofia Papagiannaki
        instance.renew_token()
2136 bf0c6de5 Sofia Papagiannaki
pre_save.connect(renew_token, sender=AstakosUser)
2137 2e90e3ec Kostas Papadimitriou
pre_save.connect(renew_token, sender=Service)