Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / models.py @ 85d444db

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

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

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

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

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

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

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