Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (70.1 kB)

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

979 49790d9d Sofia Papagiannaki
        If the key is valid and has not expired, return the ``User``
980 49790d9d Sofia Papagiannaki
        after activating.
981 49790d9d Sofia Papagiannaki

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

984 49790d9d Sofia Papagiannaki
        If the key is valid but the ``User`` is already active,
985 49790d9d Sofia Papagiannaki
        return ``None``.
986 49790d9d Sofia Papagiannaki

987 49790d9d Sofia Papagiannaki
        After successful email change the activation record is deleted.
988 49790d9d Sofia Papagiannaki

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

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