Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / models.py @ 5550bcfb

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

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

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

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

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

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

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