Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (66.2 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 c7b82fdc Sofia Papagiannaki
    AUTH_TOKEN_DURATION, EMAILCHANGE_ACTIVATION_DAYS, LOGGING_LEVEL,
70 8bc397e8 Sofia Papagiannaki
    SITENAME, SERVICES, MODERATION_ENABLED, RESOURCES_PRESENTATION_DATA)
71 c4b1a172 Kostas Papadimitriou
from astakos.im import settings as astakos_settings
72 c0b26605 Sofia Papagiannaki
from astakos.im.endpoints.qh import (
73 21e0fdad Giorgos Korfiatis
    register_resources, qh_add_quota, QuotaLimits,
74 ee45eb81 Giorgos Korfiatis
    qh_query_serials, qh_ack_serials)
75 d2633501 Kostas Papadimitriou
from astakos.im import auth_providers
76 9c01d9d1 Sofia Papagiannaki
77 ae497612 Olga Brani
import astakos.im.messages as astakos_messages
78 ee45eb81 Giorgos Korfiatis
from .managers import ForUpdateManager
79 64cd4730 Antony Chazapis
80 c11dc0ce Giorgos Korfiatis
from synnefo.lib.quotaholder.api import QH_PRACTICALLY_INFINITE
81 c11dc0ce Giorgos Korfiatis
from synnefo.lib.db.intdecimalfield import intDecimalField
82 c11dc0ce Giorgos Korfiatis
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 7795764b Kostas Papadimitriou
    order = models.PositiveIntegerField(default=0)
114 7795764b Kostas Papadimitriou
115 7795764b Kostas Papadimitriou
    class Meta:
116 7795764b Kostas Papadimitriou
        ordering = ('order', )
117 5ce3ce4f Sofia Papagiannaki
118 08494423 Sofia Papagiannaki
    def renew_token(self, expiration_date=None):
119 8e45d6fd Sofia Papagiannaki
        md5 = hashlib.md5()
120 8e45d6fd Sofia Papagiannaki
        md5.update(self.name.encode('ascii', 'ignore'))
121 8e45d6fd Sofia Papagiannaki
        md5.update(self.url.encode('ascii', 'ignore'))
122 8e45d6fd Sofia Papagiannaki
        md5.update(asctime())
123 8e45d6fd Sofia Papagiannaki
124 8e45d6fd Sofia Papagiannaki
        self.auth_token = b64encode(md5.digest())
125 8e45d6fd Sofia Papagiannaki
        self.auth_token_created = datetime.now()
126 08494423 Sofia Papagiannaki
        if expiration_date:
127 08494423 Sofia Papagiannaki
            self.auth_token_expires = expiration_date
128 08494423 Sofia Papagiannaki
        else:
129 08494423 Sofia Papagiannaki
            self.auth_token_expires = None
130 5ce3ce4f Sofia Papagiannaki
131 8e45d6fd Sofia Papagiannaki
    def __str__(self):
132 8e45d6fd Sofia Papagiannaki
        return self.name
133 8e45d6fd Sofia Papagiannaki
134 9a06d96f Olga Brani
    @property
135 9a06d96f Olga Brani
    def resources(self):
136 9a06d96f Olga Brani
        return self.resource_set.all()
137 9a06d96f Olga Brani
138 9a06d96f Olga Brani
    @resources.setter
139 9a06d96f Olga Brani
    def resources(self, resources):
140 9a06d96f Olga Brani
        for s in resources:
141 9a06d96f Olga Brani
            self.resource_set.create(**s)
142 2e90e3ec Kostas Papadimitriou
143 5ce3ce4f Sofia Papagiannaki
144 8e45d6fd Sofia Papagiannaki
class ResourceMetadata(models.Model):
145 e1a80257 Sofia Papagiannaki
    key = models.CharField(_('Name'), max_length=255, unique=True, db_index=True)
146 e1a80257 Sofia Papagiannaki
    value = models.CharField(_('Value'), max_length=255)
147 8e45d6fd Sofia Papagiannaki
148 8bc397e8 Sofia Papagiannaki
_presentation_data = {}
149 8bc397e8 Sofia Papagiannaki
def get_presentation(resource):
150 8bc397e8 Sofia Papagiannaki
    global _presentation_data
151 8bc397e8 Sofia Papagiannaki
    presentation = _presentation_data.get(resource, {})
152 8bc397e8 Sofia Papagiannaki
    if not presentation:
153 8bc397e8 Sofia Papagiannaki
        resource_presentation = RESOURCES_PRESENTATION_DATA.get('resources', {})
154 8bc397e8 Sofia Papagiannaki
        presentation = resource_presentation.get(resource, {})
155 8bc397e8 Sofia Papagiannaki
        _presentation_data[resource] = presentation
156 3c7528c9 Kostas Papadimitriou
    return presentation
157 5ce3ce4f Sofia Papagiannaki
158 8e45d6fd Sofia Papagiannaki
class Resource(models.Model):
159 43e09b6c Sofia Papagiannaki
    name = models.CharField(_('Name'), max_length=255)
160 8e45d6fd Sofia Papagiannaki
    meta = models.ManyToManyField(ResourceMetadata)
161 8e45d6fd Sofia Papagiannaki
    service = models.ForeignKey(Service)
162 e1a80257 Sofia Papagiannaki
    desc = models.TextField(_('Description'), null=True)
163 e1a80257 Sofia Papagiannaki
    unit = models.CharField(_('Name'), null=True, max_length=255)
164 e1a80257 Sofia Papagiannaki
    group = models.CharField(_('Group'), null=True, max_length=255)
165 425e2e95 Sofia Papagiannaki
166 43e09b6c Sofia Papagiannaki
    class Meta:
167 43e09b6c Sofia Papagiannaki
        unique_together = ("name", "service")
168 5ce3ce4f Sofia Papagiannaki
169 8e45d6fd Sofia Papagiannaki
    def __str__(self):
170 9a06d96f Olga Brani
        return '%s%s%s' % (self.service, RESOURCE_SEPARATOR, self.name)
171 8e45d6fd Sofia Papagiannaki
172 8bc397e8 Sofia Papagiannaki
    @property
173 8bc397e8 Sofia Papagiannaki
    def help_text(self):
174 8bc397e8 Sofia Papagiannaki
        return get_presentation(str(self)).get('help_text', '')
175 3c7528c9 Kostas Papadimitriou
176 8bc397e8 Sofia Papagiannaki
    @property
177 8bc397e8 Sofia Papagiannaki
    def help_text_input_each(self):
178 8bc397e8 Sofia Papagiannaki
        return get_presentation(str(self)).get('help_text_input_each', '')
179 8bc397e8 Sofia Papagiannaki
180 8bc397e8 Sofia Papagiannaki
    @property
181 8bc397e8 Sofia Papagiannaki
    def is_abbreviation(self):
182 8bc397e8 Sofia Papagiannaki
        return get_presentation(str(self)).get('is_abbreviation', False)
183 8bc397e8 Sofia Papagiannaki
184 8bc397e8 Sofia Papagiannaki
    @property
185 8bc397e8 Sofia Papagiannaki
    def report_desc(self):
186 8bc397e8 Sofia Papagiannaki
        return get_presentation(str(self)).get('report_desc', '')
187 8bc397e8 Sofia Papagiannaki
188 8bc397e8 Sofia Papagiannaki
    @property
189 8bc397e8 Sofia Papagiannaki
    def placeholder(self):
190 8bc397e8 Sofia Papagiannaki
        return get_presentation(str(self)).get('placeholder', '')
191 8bc397e8 Sofia Papagiannaki
192 8bc397e8 Sofia Papagiannaki
    @property
193 8bc397e8 Sofia Papagiannaki
    def verbose_name(self):
194 8bc397e8 Sofia Papagiannaki
        return get_presentation(str(self)).get('verbose_name', '')
195 8bc397e8 Sofia Papagiannaki
196 8bc397e8 Sofia Papagiannaki
197 b4789608 Sofia Papagiannaki
_default_quota = {}
198 b4789608 Sofia Papagiannaki
def get_default_quota():
199 b4789608 Sofia Papagiannaki
    global _default_quota
200 b4789608 Sofia Papagiannaki
    if _default_quota:
201 b4789608 Sofia Papagiannaki
        return _default_quota
202 b4789608 Sofia Papagiannaki
    for s, data in SERVICES.iteritems():
203 b4789608 Sofia Papagiannaki
        map(
204 b4789608 Sofia Papagiannaki
            lambda d:_default_quota.update(
205 b4789608 Sofia Papagiannaki
                {'%s%s%s' % (s, RESOURCE_SEPARATOR, d.get('name')):d.get('uplimit', 0)}
206 b4789608 Sofia Papagiannaki
            ),
207 b4789608 Sofia Papagiannaki
            data.get('resources', {})
208 b4789608 Sofia Papagiannaki
        )
209 b4789608 Sofia Papagiannaki
    return _default_quota
210 d2633501 Kostas Papadimitriou
211 43332a76 Kostas Papadimitriou
212 6b9a334b Sofia Papagiannaki
class AstakosUserManager(UserManager):
213 d2633501 Kostas Papadimitriou
214 d2633501 Kostas Papadimitriou
    def get_auth_provider_user(self, provider, **kwargs):
215 d2633501 Kostas Papadimitriou
        """
216 d2633501 Kostas Papadimitriou
        Retrieve AstakosUser instance associated with the specified third party
217 d2633501 Kostas Papadimitriou
        id.
218 d2633501 Kostas Papadimitriou
        """
219 d2633501 Kostas Papadimitriou
        kwargs = dict(map(lambda x: ('auth_providers__%s' % x[0], x[1]),
220 d2633501 Kostas Papadimitriou
                          kwargs.iteritems()))
221 d2633501 Kostas Papadimitriou
        return self.get(auth_providers__module=provider, **kwargs)
222 d2633501 Kostas Papadimitriou
223 c630fee6 Kostas Papadimitriou
    def get_by_email(self, email):
224 c630fee6 Kostas Papadimitriou
        return self.get(email=email)
225 c630fee6 Kostas Papadimitriou
226 e5966bd9 Kostas Papadimitriou
    def get_by_identifier(self, email_or_username, **kwargs):
227 e5966bd9 Kostas Papadimitriou
        try:
228 e5966bd9 Kostas Papadimitriou
            return self.get(email__iexact=email_or_username, **kwargs)
229 e5966bd9 Kostas Papadimitriou
        except AstakosUser.DoesNotExist:
230 e5966bd9 Kostas Papadimitriou
            return self.get(username__iexact=email_or_username, **kwargs)
231 e5966bd9 Kostas Papadimitriou
232 e5966bd9 Kostas Papadimitriou
    def user_exists(self, email_or_username, **kwargs):
233 e5966bd9 Kostas Papadimitriou
        qemail = Q(email__iexact=email_or_username)
234 e5966bd9 Kostas Papadimitriou
        qusername = Q(username__iexact=email_or_username)
235 43332a76 Kostas Papadimitriou
        qextra = Q(**kwargs)
236 43332a76 Kostas Papadimitriou
        return self.filter((qemail | qusername) & qextra).exists()
237 43332a76 Kostas Papadimitriou
238 43332a76 Kostas Papadimitriou
    def verified_user_exists(self, email_or_username):
239 43332a76 Kostas Papadimitriou
        return self.user_exists(email_or_username, email_verified=True)
240 43332a76 Kostas Papadimitriou
241 43332a76 Kostas Papadimitriou
    def verified(self):
242 43332a76 Kostas Papadimitriou
        return self.filter(email_verified=True)
243 e5966bd9 Kostas Papadimitriou
244 61a1b2d2 Georgios D. Tsoukalas
    def verified(self):
245 61a1b2d2 Georgios D. Tsoukalas
        return self.filter(email_verified=True)
246 e5966bd9 Kostas Papadimitriou
247 e5966bd9 Kostas Papadimitriou
248 0905ccd2 Sofia Papagiannaki
class AstakosUser(User):
249 890b0eaf Sofia Papagiannaki
    """
250 890b0eaf Sofia Papagiannaki
    Extends ``django.contrib.auth.models.User`` by defining additional fields.
251 890b0eaf Sofia Papagiannaki
    """
252 e1a80257 Sofia Papagiannaki
    affiliation = models.CharField(_('Affiliation'), max_length=255, blank=True,
253 d2633501 Kostas Papadimitriou
                                   null=True)
254 d2633501 Kostas Papadimitriou
255 d2633501 Kostas Papadimitriou
    # DEPRECATED FIELDS: provider, third_party_identifier moved in
256 d2633501 Kostas Papadimitriou
    #                    AstakosUserProvider model.
257 e1a80257 Sofia Papagiannaki
    provider = models.CharField(_('Provider'), max_length=255, blank=True,
258 d2633501 Kostas Papadimitriou
                                null=True)
259 d2633501 Kostas Papadimitriou
    # ex. screen_name for twitter, eppn for shibboleth
260 e1a80257 Sofia Papagiannaki
    third_party_identifier = models.CharField(_('Third-party identifier'),
261 d2633501 Kostas Papadimitriou
                                              max_length=255, null=True,
262 d2633501 Kostas Papadimitriou
                                              blank=True)
263 d2633501 Kostas Papadimitriou
264 6c736ed7 Kostas Papadimitriou
265 64cd4730 Antony Chazapis
    #for invitations
266 92defad4 Sofia Papagiannaki
    user_level = DEFAULT_USER_LEVEL
267 e1a80257 Sofia Papagiannaki
    level = models.IntegerField(_('Inviter level'), default=user_level)
268 5ce3ce4f Sofia Papagiannaki
    invitations = models.IntegerField(
269 e1a80257 Sofia Papagiannaki
        _('Invitations left'), default=INVITATIONS_PER_LEVEL.get(user_level, 0))
270 6c736ed7 Kostas Papadimitriou
271 e1a80257 Sofia Papagiannaki
    auth_token = models.CharField(_('Authentication Token'), max_length=32,
272 0905ccd2 Sofia Papagiannaki
                                  null=True, blank=True)
273 e1a80257 Sofia Papagiannaki
    auth_token_created = models.DateTimeField(_('Token creation date'), null=True)
274 5ce3ce4f Sofia Papagiannaki
    auth_token_expires = models.DateTimeField(
275 e1a80257 Sofia Papagiannaki
        _('Token expiration date'), null=True)
276 6c736ed7 Kostas Papadimitriou
277 e1a80257 Sofia Papagiannaki
    updated = models.DateTimeField(_('Update date'))
278 e1a80257 Sofia Papagiannaki
    is_verified = models.BooleanField(_('Is verified?'), default=False)
279 6c736ed7 Kostas Papadimitriou
280 e1a80257 Sofia Papagiannaki
    email_verified = models.BooleanField(_('Email verified?'), default=False)
281 6c736ed7 Kostas Papadimitriou
282 e1a80257 Sofia Papagiannaki
    has_credits = models.BooleanField(_('Has credits?'), default=False)
283 5ce3ce4f Sofia Papagiannaki
    has_signed_terms = models.BooleanField(
284 e1a80257 Sofia Papagiannaki
        _('I agree with the terms'), default=False)
285 5ce3ce4f Sofia Papagiannaki
    date_signed_terms = models.DateTimeField(
286 e1a80257 Sofia Papagiannaki
        _('Signed terms date'), null=True, blank=True)
287 5ce3ce4f Sofia Papagiannaki
288 5ce3ce4f Sofia Papagiannaki
    activation_sent = models.DateTimeField(
289 e1a80257 Sofia Papagiannaki
        _('Activation sent data'), null=True, blank=True)
290 5ce3ce4f Sofia Papagiannaki
291 5ce3ce4f Sofia Papagiannaki
    policy = models.ManyToManyField(
292 5ce3ce4f Sofia Papagiannaki
        Resource, null=True, through='AstakosUserQuota')
293 5ce3ce4f Sofia Papagiannaki
294 836a0fb0 Kostas Papadimitriou
    uuid = models.CharField(max_length=255, null=True, blank=False, unique=True)
295 836a0fb0 Kostas Papadimitriou
296 18ffbee1 Sofia Papagiannaki
    __has_signed_terms = False
297 e1a80257 Sofia Papagiannaki
    disturbed_quota = models.BooleanField(_('Needs quotaholder syncing'),
298 9a06d96f Olga Brani
                                           default=False, db_index=True)
299 d2633501 Kostas Papadimitriou
300 d2633501 Kostas Papadimitriou
    objects = AstakosUserManager()
301 fbaa4f3c Kostas Papadimitriou
302 18ffbee1 Sofia Papagiannaki
    def __init__(self, *args, **kwargs):
303 18ffbee1 Sofia Papagiannaki
        super(AstakosUser, self).__init__(*args, **kwargs)
304 18ffbee1 Sofia Papagiannaki
        self.__has_signed_terms = self.has_signed_terms
305 a3637508 Sofia Papagiannaki
        if not self.id:
306 18ffbee1 Sofia Papagiannaki
            self.is_active = False
307 5ce3ce4f Sofia Papagiannaki
308 0905ccd2 Sofia Papagiannaki
    @property
309 0905ccd2 Sofia Papagiannaki
    def realname(self):
310 5ce3ce4f Sofia Papagiannaki
        return '%s %s' % (self.first_name, self.last_name)
311 6c736ed7 Kostas Papadimitriou
312 0905ccd2 Sofia Papagiannaki
    @realname.setter
313 0905ccd2 Sofia Papagiannaki
    def realname(self, value):
314 0905ccd2 Sofia Papagiannaki
        parts = value.split(' ')
315 0905ccd2 Sofia Papagiannaki
        if len(parts) == 2:
316 0905ccd2 Sofia Papagiannaki
            self.first_name = parts[0]
317 0905ccd2 Sofia Papagiannaki
            self.last_name = parts[1]
318 0905ccd2 Sofia Papagiannaki
        else:
319 0905ccd2 Sofia Papagiannaki
            self.last_name = parts[0]
320 6c736ed7 Kostas Papadimitriou
321 9a06d96f Olga Brani
    def add_permission(self, pname):
322 9a06d96f Olga Brani
        if self.has_perm(pname):
323 9a06d96f Olga Brani
            return
324 e65c21df Georgios D. Tsoukalas
        p, created = Permission.objects.get_or_create(
325 e65c21df Georgios D. Tsoukalas
                                    codename=pname,
326 e65c21df Georgios D. Tsoukalas
                                    name=pname.capitalize(),
327 e65c21df Georgios D. Tsoukalas
                                    content_type=get_content_type())
328 9a06d96f Olga Brani
        self.user_permissions.add(p)
329 9a06d96f Olga Brani
330 9a06d96f Olga Brani
    def remove_permission(self, pname):
331 9a06d96f Olga Brani
        if self.has_perm(pname):
332 9a06d96f Olga Brani
            return
333 9a06d96f Olga Brani
        p = Permission.objects.get(codename=pname,
334 e65c21df Georgios D. Tsoukalas
                                   content_type=get_content_type())
335 9a06d96f Olga Brani
        self.user_permissions.remove(p)
336 9a06d96f Olga Brani
337 64cd4730 Antony Chazapis
    @property
338 64cd4730 Antony Chazapis
    def invitation(self):
339 64cd4730 Antony Chazapis
        try:
340 9fb8e808 Sofia Papagiannaki
            return Invitation.objects.get(username=self.email)
341 64cd4730 Antony Chazapis
        except Invitation.DoesNotExist:
342 64cd4730 Antony Chazapis
            return None
343 6c736ed7 Kostas Papadimitriou
344 ffb1e7a8 Sofia Papagiannaki
    @property
345 ffb1e7a8 Sofia Papagiannaki
    def quota(self):
346 9a06d96f Olga Brani
        """Returns a dict with the sum of quota limits per resource"""
347 ffb1e7a8 Sofia Papagiannaki
        d = defaultdict(int)
348 b4789608 Sofia Papagiannaki
        default_quota = get_default_quota()
349 b4789608 Sofia Papagiannaki
        d.update(default_quota)
350 9a06d96f Olga Brani
        for q in self.policies:
351 6c997921 Sofia Papagiannaki
            d[q.resource] = q.capacity or inf
352 ccab6eb5 Sofia Papagiannaki
        for m in self.projectmembership_set.select_related().all():
353 ccab6eb5 Sofia Papagiannaki
            if not m.acceptance_date:
354 fc1e2f02 Sofia Papagiannaki
                continue
355 ccab6eb5 Sofia Papagiannaki
            p = m.project
356 b6fe8bb8 Giorgos Korfiatis
            if not p.is_active_strict():
357 ffb1e7a8 Sofia Papagiannaki
                continue
358 ee45eb81 Giorgos Korfiatis
            grants = p.application.projectresourcegrant_set.all()
359 ccab6eb5 Sofia Papagiannaki
            for g in grants:
360 73fbaec4 Sofia Papagiannaki
                d[str(g.resource)] += g.member_capacity or inf
361 ffb1e7a8 Sofia Papagiannaki
        return d
362 5ce3ce4f Sofia Papagiannaki
363 9a06d96f Olga Brani
    @property
364 9a06d96f Olga Brani
    def policies(self):
365 9a06d96f Olga Brani
        return self.astakosuserquota_set.select_related().all()
366 9a06d96f Olga Brani
367 9a06d96f Olga Brani
    @policies.setter
368 9a06d96f Olga Brani
    def policies(self, policies):
369 9a06d96f Olga Brani
        for p in policies:
370 6c997921 Sofia Papagiannaki
            p.setdefault('resource', '')
371 6c997921 Sofia Papagiannaki
            p.setdefault('capacity', 0)
372 6c997921 Sofia Papagiannaki
            p.setdefault('quantity', 0)
373 6c997921 Sofia Papagiannaki
            p.setdefault('import_limit', 0)
374 6c997921 Sofia Papagiannaki
            p.setdefault('export_limit', 0)
375 6c997921 Sofia Papagiannaki
            p.setdefault('update', True)
376 6c997921 Sofia Papagiannaki
            self.add_resource_policy(**p)
377 6c997921 Sofia Papagiannaki
378 6c997921 Sofia Papagiannaki
    def add_resource_policy(
379 6c997921 Sofia Papagiannaki
            self, resource, capacity, quantity, import_limit,
380 6c997921 Sofia Papagiannaki
            export_limit, update=True):
381 9a06d96f Olga Brani
        """Raises ObjectDoesNotExist, IntegrityError"""
382 6c997921 Sofia Papagiannaki
        s, sep, r = resource.partition(RESOURCE_SEPARATOR)
383 6c997921 Sofia Papagiannaki
        resource = Resource.objects.get(service__name=s, name=r)
384 9a06d96f Olga Brani
        if update:
385 6c997921 Sofia Papagiannaki
            AstakosUserQuota.objects.update_or_create(
386 6c997921 Sofia Papagiannaki
                user=self, resource=resource, defaults={
387 6c997921 Sofia Papagiannaki
                    'capacity':capacity,
388 6c997921 Sofia Papagiannaki
                    'quantity': quantity,
389 6c997921 Sofia Papagiannaki
                    'import_limit':import_limit,
390 6c997921 Sofia Papagiannaki
                    'export_limit':export_limit})
391 9a06d96f Olga Brani
        else:
392 9a06d96f Olga Brani
            q = self.astakosuserquota_set
393 6c997921 Sofia Papagiannaki
            q.create(
394 6c997921 Sofia Papagiannaki
                resource=resource, capacity=capacity, quanity=quantity,
395 6c997921 Sofia Papagiannaki
                import_limit=import_limit, export_limit=export_limit)
396 9a06d96f Olga Brani
397 6c997921 Sofia Papagiannaki
    def remove_resource_policy(self, service, resource):
398 9a06d96f Olga Brani
        """Raises ObjectDoesNotExist, IntegrityError"""
399 9a06d96f Olga Brani
        resource = Resource.objects.get(service__name=service, name=resource)
400 9a06d96f Olga Brani
        q = self.policies.get(resource=resource).delete()
401 9a06d96f Olga Brani
402 836a0fb0 Kostas Papadimitriou
    def update_uuid(self):
403 836a0fb0 Kostas Papadimitriou
        while not self.uuid:
404 836a0fb0 Kostas Papadimitriou
            uuid_val =  str(uuid.uuid4())
405 836a0fb0 Kostas Papadimitriou
            try:
406 836a0fb0 Kostas Papadimitriou
                AstakosUser.objects.get(uuid=uuid_val)
407 836a0fb0 Kostas Papadimitriou
            except AstakosUser.DoesNotExist, e:
408 836a0fb0 Kostas Papadimitriou
                self.uuid = uuid_val
409 836a0fb0 Kostas Papadimitriou
        return self.uuid
410 836a0fb0 Kostas Papadimitriou
411 64cd4730 Antony Chazapis
    def save(self, update_timestamps=True, **kwargs):
412 64cd4730 Antony Chazapis
        if update_timestamps:
413 64cd4730 Antony Chazapis
            if not self.id:
414 0905ccd2 Sofia Papagiannaki
                self.date_joined = datetime.now()
415 64cd4730 Antony Chazapis
            self.updated = datetime.now()
416 5ce3ce4f Sofia Papagiannaki
417 18ffbee1 Sofia Papagiannaki
        # update date_signed_terms if necessary
418 18ffbee1 Sofia Papagiannaki
        if self.__has_signed_terms != self.has_signed_terms:
419 18ffbee1 Sofia Papagiannaki
            self.date_signed_terms = datetime.now()
420 5ce3ce4f Sofia Papagiannaki
421 836a0fb0 Kostas Papadimitriou
        self.update_uuid()
422 836a0fb0 Kostas Papadimitriou
423 836a0fb0 Kostas Papadimitriou
        if self.username != self.email.lower():
424 9c01d9d1 Sofia Papagiannaki
            # set username
425 e5966bd9 Kostas Papadimitriou
            self.username = self.email.lower()
426 fbaa4f3c Kostas Papadimitriou
427 591d0505 Sofia Papagiannaki
        self.validate_unique_email_isactive()
428 5ce3ce4f Sofia Papagiannaki
429 0905ccd2 Sofia Papagiannaki
        super(AstakosUser, self).save(**kwargs)
430 2e90e3ec Kostas Papadimitriou
431 bf0c6de5 Sofia Papagiannaki
    def renew_token(self, flush_sessions=False, current_key=None):
432 64cd4730 Antony Chazapis
        md5 = hashlib.md5()
433 8f8c43b2 Sofia Papagiannaki
        md5.update(settings.SECRET_KEY)
434 0905ccd2 Sofia Papagiannaki
        md5.update(self.username)
435 64cd4730 Antony Chazapis
        md5.update(self.realname.encode('ascii', 'ignore'))
436 64cd4730 Antony Chazapis
        md5.update(asctime())
437 2e90e3ec Kostas Papadimitriou
438 64cd4730 Antony Chazapis
        self.auth_token = b64encode(md5.digest())
439 64cd4730 Antony Chazapis
        self.auth_token_created = datetime.now()
440 64cd4730 Antony Chazapis
        self.auth_token_expires = self.auth_token_created + \
441 92defad4 Sofia Papagiannaki
                                  timedelta(hours=AUTH_TOKEN_DURATION)
442 bf0c6de5 Sofia Papagiannaki
        if flush_sessions:
443 bf0c6de5 Sofia Papagiannaki
            self.flush_sessions(current_key)
444 111f3da6 Sofia Papagiannaki
        msg = 'Token renewed for %s' % self.email
445 aab4d540 Sofia Papagiannaki
        logger.log(LOGGING_LEVEL, msg)
446 6c736ed7 Kostas Papadimitriou
447 bf0c6de5 Sofia Papagiannaki
    def flush_sessions(self, current_key=None):
448 bf0c6de5 Sofia Papagiannaki
        q = self.sessions
449 bf0c6de5 Sofia Papagiannaki
        if current_key:
450 bf0c6de5 Sofia Papagiannaki
            q = q.exclude(session_key=current_key)
451 2e90e3ec Kostas Papadimitriou
452 bf0c6de5 Sofia Papagiannaki
        keys = q.values_list('session_key', flat=True)
453 bf0c6de5 Sofia Papagiannaki
        if keys:
454 bf0c6de5 Sofia Papagiannaki
            msg = 'Flushing sessions: %s' % ','.join(keys)
455 c0b26605 Sofia Papagiannaki
            logger.log(LOGGING_LEVEL, msg, [])
456 bf0c6de5 Sofia Papagiannaki
        engine = import_module(settings.SESSION_ENGINE)
457 bf0c6de5 Sofia Papagiannaki
        for k in keys:
458 bf0c6de5 Sofia Papagiannaki
            s = engine.SessionStore(k)
459 bf0c6de5 Sofia Papagiannaki
            s.flush()
460 bf0c6de5 Sofia Papagiannaki
461 64cd4730 Antony Chazapis
    def __unicode__(self):
462 3abf6c78 Sofia Papagiannaki
        return '%s (%s)' % (self.realname, self.email)
463 5ce3ce4f Sofia Papagiannaki
464 0a569195 Sofia Papagiannaki
    def conflicting_email(self):
465 5ce3ce4f Sofia Papagiannaki
        q = AstakosUser.objects.exclude(username=self.username)
466 789a5951 Sofia Papagiannaki
        q = q.filter(email__iexact=self.email)
467 0a569195 Sofia Papagiannaki
        if q.count() != 0:
468 0a569195 Sofia Papagiannaki
            return True
469 0a569195 Sofia Papagiannaki
        return False
470 5ce3ce4f Sofia Papagiannaki
471 591d0505 Sofia Papagiannaki
    def validate_unique_email_isactive(self):
472 0a569195 Sofia Papagiannaki
        """
473 0a569195 Sofia Papagiannaki
        Implements a unique_together constraint for email and is_active fields.
474 0a569195 Sofia Papagiannaki
        """
475 e6759494 Sofia Papagiannaki
        q = AstakosUser.objects.all()
476 0a569195 Sofia Papagiannaki
        q = q.filter(email = self.email)
477 e6759494 Sofia Papagiannaki
        if self.id:
478 e6759494 Sofia Papagiannaki
            q = q.filter(~Q(id = self.id))
479 0a569195 Sofia Papagiannaki
        if q.count() != 0:
480 73fbaec4 Sofia Papagiannaki
            m = 'Another account with the same email = %(email)s & \
481 425e2e95 Sofia Papagiannaki
                is_active = %(is_active)s found.' % self.__dict__
482 73fbaec4 Sofia Papagiannaki
            raise ValidationError(m)
483 5ce3ce4f Sofia Papagiannaki
484 34a76cdb Kostas Papadimitriou
    def email_change_is_pending(self):
485 34a76cdb Kostas Papadimitriou
        return self.emailchanges.count() > 0
486 34a76cdb Kostas Papadimitriou
487 0712a55c Kostas Papadimitriou
    def email_change_is_pending(self):
488 0712a55c Kostas Papadimitriou
        return self.emailchanges.count() > 0
489 0712a55c Kostas Papadimitriou
490 fcf90160 Sofia Papagiannaki
    @property
491 09e7393c Sofia Papagiannaki
    def signed_terms(self):
492 09e7393c Sofia Papagiannaki
        term = get_latest_terms()
493 09e7393c Sofia Papagiannaki
        if not term:
494 09e7393c Sofia Papagiannaki
            return True
495 09e7393c Sofia Papagiannaki
        if not self.has_signed_terms:
496 09e7393c Sofia Papagiannaki
            return False
497 09e7393c Sofia Papagiannaki
        if not self.date_signed_terms:
498 09e7393c Sofia Papagiannaki
            return False
499 09e7393c Sofia Papagiannaki
        if self.date_signed_terms < term.date:
500 09e7393c Sofia Papagiannaki
            self.has_signed_terms = False
501 f0f92965 Sofia Papagiannaki
            self.date_signed_terms = None
502 09e7393c Sofia Papagiannaki
            self.save()
503 09e7393c Sofia Papagiannaki
            return False
504 09e7393c Sofia Papagiannaki
        return True
505 09e7393c Sofia Papagiannaki
506 d2633501 Kostas Papadimitriou
    def set_invitations_level(self):
507 d2633501 Kostas Papadimitriou
        """
508 d2633501 Kostas Papadimitriou
        Update user invitation level
509 d2633501 Kostas Papadimitriou
        """
510 d2633501 Kostas Papadimitriou
        level = self.invitation.inviter.level + 1
511 d2633501 Kostas Papadimitriou
        self.level = level
512 d2633501 Kostas Papadimitriou
        self.invitations = INVITATIONS_PER_LEVEL.get(level, 0)
513 d2633501 Kostas Papadimitriou
514 d2633501 Kostas Papadimitriou
    def can_login_with_auth_provider(self, provider):
515 d2633501 Kostas Papadimitriou
        if not self.has_auth_provider(provider):
516 d2633501 Kostas Papadimitriou
            return False
517 d2633501 Kostas Papadimitriou
        else:
518 d2633501 Kostas Papadimitriou
            return auth_providers.get_provider(provider).is_available_for_login()
519 d2633501 Kostas Papadimitriou
520 f432088a Kostas Papadimitriou
    def can_add_auth_provider(self, provider, **kwargs):
521 d2633501 Kostas Papadimitriou
        provider_settings = auth_providers.get_provider(provider)
522 222d8e52 Kostas Papadimitriou
523 222d8e52 Kostas Papadimitriou
        if not provider_settings.is_available_for_add():
524 d2633501 Kostas Papadimitriou
            return False
525 f432088a Kostas Papadimitriou
526 d2633501 Kostas Papadimitriou
        if self.has_auth_provider(provider) and \
527 d2633501 Kostas Papadimitriou
           provider_settings.one_per_user:
528 d2633501 Kostas Papadimitriou
            return False
529 f432088a Kostas Papadimitriou
530 c630fee6 Kostas Papadimitriou
        if 'provider_info' in kwargs:
531 c630fee6 Kostas Papadimitriou
            kwargs.pop('provider_info')
532 c630fee6 Kostas Papadimitriou
533 f432088a Kostas Papadimitriou
        if 'identifier' in kwargs:
534 f432088a Kostas Papadimitriou
            try:
535 f432088a Kostas Papadimitriou
                # provider with specified params already exist
536 f432088a Kostas Papadimitriou
                existing_user = AstakosUser.objects.get_auth_provider_user(provider,
537 f432088a Kostas Papadimitriou
                                                                   **kwargs)
538 f432088a Kostas Papadimitriou
            except AstakosUser.DoesNotExist:
539 f432088a Kostas Papadimitriou
                return True
540 f432088a Kostas Papadimitriou
            else:
541 f432088a Kostas Papadimitriou
                return False
542 f432088a Kostas Papadimitriou
543 d2633501 Kostas Papadimitriou
        return True
544 d2633501 Kostas Papadimitriou
545 63836eda Kostas Papadimitriou
    def can_remove_auth_provider(self, module):
546 63836eda Kostas Papadimitriou
        provider = auth_providers.get_provider(module)
547 63836eda Kostas Papadimitriou
        existing = self.get_active_auth_providers()
548 63836eda Kostas Papadimitriou
        existing_for_provider = self.get_active_auth_providers(module=module)
549 63836eda Kostas Papadimitriou
550 63836eda Kostas Papadimitriou
        if len(existing) <= 1:
551 63836eda Kostas Papadimitriou
            return False
552 63836eda Kostas Papadimitriou
553 63836eda Kostas Papadimitriou
        if len(existing_for_provider) == 1 and provider.is_required():
554 d2633501 Kostas Papadimitriou
            return False
555 63836eda Kostas Papadimitriou
556 d2633501 Kostas Papadimitriou
        return True
557 d2633501 Kostas Papadimitriou
558 d2633501 Kostas Papadimitriou
    def can_change_password(self):
559 d2633501 Kostas Papadimitriou
        return self.has_auth_provider('local', auth_backend='astakos')
560 d2633501 Kostas Papadimitriou
561 63836eda Kostas Papadimitriou
    def has_required_auth_providers(self):
562 63836eda Kostas Papadimitriou
        required = auth_providers.REQUIRED_PROVIDERS
563 63836eda Kostas Papadimitriou
        for provider in required:
564 63836eda Kostas Papadimitriou
            if not self.has_auth_provider(provider):
565 63836eda Kostas Papadimitriou
                return False
566 63836eda Kostas Papadimitriou
        return True
567 63836eda Kostas Papadimitriou
568 d2633501 Kostas Papadimitriou
    def has_auth_provider(self, provider, **kwargs):
569 d2633501 Kostas Papadimitriou
        return bool(self.auth_providers.filter(module=provider,
570 d2633501 Kostas Papadimitriou
                                               **kwargs).count())
571 d2633501 Kostas Papadimitriou
572 d2633501 Kostas Papadimitriou
    def add_auth_provider(self, provider, **kwargs):
573 3a72a5d4 Kostas Papadimitriou
        info_data = ''
574 3a72a5d4 Kostas Papadimitriou
        if 'provider_info' in kwargs:
575 c630fee6 Kostas Papadimitriou
            info_data = kwargs.pop('provider_info')
576 c630fee6 Kostas Papadimitriou
            if isinstance(info_data, dict):
577 c630fee6 Kostas Papadimitriou
                info_data = json.dumps(info_data)
578 3a72a5d4 Kostas Papadimitriou
579 f432088a Kostas Papadimitriou
        if self.can_add_auth_provider(provider, **kwargs):
580 3a72a5d4 Kostas Papadimitriou
            self.auth_providers.create(module=provider, active=True,
581 3a72a5d4 Kostas Papadimitriou
                                       info_data=info_data,
582 3a72a5d4 Kostas Papadimitriou
                                       **kwargs)
583 f432088a Kostas Papadimitriou
        else:
584 f432088a Kostas Papadimitriou
            raise Exception('Cannot add provider')
585 d2633501 Kostas Papadimitriou
586 d2633501 Kostas Papadimitriou
    def add_pending_auth_provider(self, pending):
587 d2633501 Kostas Papadimitriou
        """
588 d2633501 Kostas Papadimitriou
        Convert PendingThirdPartyUser object to AstakosUserAuthProvider entry for
589 d2633501 Kostas Papadimitriou
        the current user.
590 d2633501 Kostas Papadimitriou
        """
591 d2633501 Kostas Papadimitriou
        if not isinstance(pending, PendingThirdPartyUser):
592 d2633501 Kostas Papadimitriou
            pending = PendingThirdPartyUser.objects.get(token=pending)
593 d2633501 Kostas Papadimitriou
594 d2633501 Kostas Papadimitriou
        provider = self.add_auth_provider(pending.provider,
595 c630fee6 Kostas Papadimitriou
                               identifier=pending.third_party_identifier,
596 c630fee6 Kostas Papadimitriou
                                affiliation=pending.affiliation,
597 c630fee6 Kostas Papadimitriou
                                          provider_info=pending.info)
598 d2633501 Kostas Papadimitriou
599 fbaa4f3c Kostas Papadimitriou
        if email_re.match(pending.email or '') and pending.email != self.email:
600 d2633501 Kostas Papadimitriou
            self.additionalmail_set.get_or_create(email=pending.email)
601 d2633501 Kostas Papadimitriou
602 d2633501 Kostas Papadimitriou
        pending.delete()
603 d2633501 Kostas Papadimitriou
        return provider
604 d2633501 Kostas Papadimitriou
605 d2633501 Kostas Papadimitriou
    def remove_auth_provider(self, provider, **kwargs):
606 d2633501 Kostas Papadimitriou
        self.auth_providers.get(module=provider, **kwargs).delete()
607 d2633501 Kostas Papadimitriou
608 d2633501 Kostas Papadimitriou
    # user urls
609 d2633501 Kostas Papadimitriou
    def get_resend_activation_url(self):
610 c630fee6 Kostas Papadimitriou
        return reverse('send_activation', kwargs={'user_id': self.pk})
611 c630fee6 Kostas Papadimitriou
612 c630fee6 Kostas Papadimitriou
    def get_provider_remove_url(self, module, **kwargs):
613 c630fee6 Kostas Papadimitriou
        return reverse('remove_auth_provider', kwargs={
614 c630fee6 Kostas Papadimitriou
            'pk': self.auth_providers.get(module=module, **kwargs).pk})
615 d2633501 Kostas Papadimitriou
616 d2633501 Kostas Papadimitriou
    def get_activation_url(self, nxt=False):
617 d2633501 Kostas Papadimitriou
        url = "%s?auth=%s" % (reverse('astakos.im.views.activate'),
618 d2633501 Kostas Papadimitriou
                                 quote(self.auth_token))
619 d2633501 Kostas Papadimitriou
        if nxt:
620 d2633501 Kostas Papadimitriou
            url += "&next=%s" % quote(nxt)
621 d2633501 Kostas Papadimitriou
        return url
622 d2633501 Kostas Papadimitriou
623 d2633501 Kostas Papadimitriou
    def get_password_reset_url(self, token_generator=default_token_generator):
624 d2633501 Kostas Papadimitriou
        return reverse('django.contrib.auth.views.password_reset_confirm',
625 d2633501 Kostas Papadimitriou
                          kwargs={'uidb36':int_to_base36(self.id),
626 d2633501 Kostas Papadimitriou
                                  'token':token_generator.make_token(self)})
627 d2633501 Kostas Papadimitriou
628 d2633501 Kostas Papadimitriou
    def get_auth_providers(self):
629 d2633501 Kostas Papadimitriou
        return self.auth_providers.all()
630 d2633501 Kostas Papadimitriou
631 d2633501 Kostas Papadimitriou
    def get_available_auth_providers(self):
632 d2633501 Kostas Papadimitriou
        """
633 d2633501 Kostas Papadimitriou
        Returns a list of providers available for user to connect to.
634 d2633501 Kostas Papadimitriou
        """
635 d2633501 Kostas Papadimitriou
        providers = []
636 d2633501 Kostas Papadimitriou
        for module, provider_settings in auth_providers.PROVIDERS.iteritems():
637 f432088a Kostas Papadimitriou
            if self.can_add_auth_provider(module):
638 d2633501 Kostas Papadimitriou
                providers.append(provider_settings(self))
639 d2633501 Kostas Papadimitriou
640 d2633501 Kostas Papadimitriou
        return providers
641 d2633501 Kostas Papadimitriou
642 63836eda Kostas Papadimitriou
    def get_active_auth_providers(self, **filters):
643 d2633501 Kostas Papadimitriou
        providers = []
644 63836eda Kostas Papadimitriou
        for provider in self.auth_providers.active(**filters):
645 d2633501 Kostas Papadimitriou
            if auth_providers.get_provider(provider.module).is_available_for_login():
646 d2633501 Kostas Papadimitriou
                providers.append(provider)
647 d2633501 Kostas Papadimitriou
        return providers
648 d2633501 Kostas Papadimitriou
649 b778b6fa Kostas Papadimitriou
    @property
650 b778b6fa Kostas Papadimitriou
    def auth_providers_display(self):
651 b778b6fa Kostas Papadimitriou
        return ",".join(map(lambda x:unicode(x), self.auth_providers.active()))
652 b778b6fa Kostas Papadimitriou
653 c4b1a172 Kostas Papadimitriou
    def get_inactive_message(self):
654 c4b1a172 Kostas Papadimitriou
        msg_extra = ''
655 c4b1a172 Kostas Papadimitriou
        message = ''
656 c4b1a172 Kostas Papadimitriou
        if self.activation_sent:
657 c4b1a172 Kostas Papadimitriou
            if self.email_verified:
658 c4b1a172 Kostas Papadimitriou
                message = _(astakos_messages.ACCOUNT_INACTIVE)
659 c4b1a172 Kostas Papadimitriou
            else:
660 c4b1a172 Kostas Papadimitriou
                message = _(astakos_messages.ACCOUNT_PENDING_ACTIVATION)
661 fc655b6f Kostas Papadimitriou
                if astakos_settings.MODERATION_ENABLED:
662 c4b1a172 Kostas Papadimitriou
                    msg_extra = _(astakos_messages.ACCOUNT_PENDING_ACTIVATION_HELP)
663 c4b1a172 Kostas Papadimitriou
                else:
664 c4b1a172 Kostas Papadimitriou
                    url = self.get_resend_activation_url()
665 c4b1a172 Kostas Papadimitriou
                    msg_extra = mark_safe(_(astakos_messages.ACCOUNT_PENDING_ACTIVATION_HELP) + \
666 a15a19b2 Kostas Papadimitriou
                                u' ' + \
667 c4b1a172 Kostas Papadimitriou
                                _('<a href="%s">%s?</a>') % (url,
668 c4b1a172 Kostas Papadimitriou
                                _(astakos_messages.ACCOUNT_RESEND_ACTIVATION_PROMPT)))
669 c4b1a172 Kostas Papadimitriou
        else:
670 fc655b6f Kostas Papadimitriou
            if astakos_settings.MODERATION_ENABLED:
671 c4b1a172 Kostas Papadimitriou
                message = _(astakos_messages.ACCOUNT_PENDING_MODERATION)
672 c4b1a172 Kostas Papadimitriou
            else:
673 c4b1a172 Kostas Papadimitriou
                message = astakos_messages.ACCOUNT_PENDING_ACTIVATION
674 c4b1a172 Kostas Papadimitriou
                url = self.get_resend_activation_url()
675 c4b1a172 Kostas Papadimitriou
                msg_extra = mark_safe(_('<a href="%s">%s?</a>') % (url,
676 c4b1a172 Kostas Papadimitriou
                            _(astakos_messages.ACCOUNT_RESEND_ACTIVATION_PROMPT)))
677 c4b1a172 Kostas Papadimitriou
678 a15a19b2 Kostas Papadimitriou
        return mark_safe(message + u' '+ msg_extra)
679 c4b1a172 Kostas Papadimitriou
680 e87bbb41 Kostas Papadimitriou
    def owns_project(self, project):
681 db9a498c Kostas Papadimitriou
        return project.owner == self
682 e87bbb41 Kostas Papadimitriou
683 e87bbb41 Kostas Papadimitriou
    def is_project_member(self, project):
684 e87bbb41 Kostas Papadimitriou
        return project.user_status(self) in [0,1,2,3]
685 e87bbb41 Kostas Papadimitriou
686 480ce06b Sofia Papagiannaki
    def is_project_accepted_member(self, project):
687 480ce06b Sofia Papagiannaki
        return project.user_status(self) == 2
688 480ce06b Sofia Papagiannaki
689 d2633501 Kostas Papadimitriou
690 d2633501 Kostas Papadimitriou
class AstakosUserAuthProviderManager(models.Manager):
691 d2633501 Kostas Papadimitriou
692 63836eda Kostas Papadimitriou
    def active(self, **filters):
693 63836eda Kostas Papadimitriou
        return self.filter(active=True, **filters)
694 d2633501 Kostas Papadimitriou
695 d2633501 Kostas Papadimitriou
696 d2633501 Kostas Papadimitriou
class AstakosUserAuthProvider(models.Model):
697 d2633501 Kostas Papadimitriou
    """
698 d2633501 Kostas Papadimitriou
    Available user authentication methods.
699 d2633501 Kostas Papadimitriou
    """
700 e1a80257 Sofia Papagiannaki
    affiliation = models.CharField(_('Affiliation'), max_length=255, blank=True,
701 d2633501 Kostas Papadimitriou
                                   null=True, default=None)
702 d2633501 Kostas Papadimitriou
    user = models.ForeignKey(AstakosUser, related_name='auth_providers')
703 e1a80257 Sofia Papagiannaki
    module = models.CharField(_('Provider'), max_length=255, blank=False,
704 d2633501 Kostas Papadimitriou
                                default='local')
705 e1a80257 Sofia Papagiannaki
    identifier = models.CharField(_('Third-party identifier'),
706 d2633501 Kostas Papadimitriou
                                              max_length=255, null=True,
707 d2633501 Kostas Papadimitriou
                                              blank=True)
708 d2633501 Kostas Papadimitriou
    active = models.BooleanField(default=True)
709 e1a80257 Sofia Papagiannaki
    auth_backend = models.CharField(_('Backend'), max_length=255, blank=False,
710 d2633501 Kostas Papadimitriou
                                   default='astakos')
711 3a72a5d4 Kostas Papadimitriou
    info_data = models.TextField(default="", null=True, blank=True)
712 c630fee6 Kostas Papadimitriou
    created = models.DateTimeField('Creation date', auto_now_add=True)
713 d2633501 Kostas Papadimitriou
714 d2633501 Kostas Papadimitriou
    objects = AstakosUserAuthProviderManager()
715 d2633501 Kostas Papadimitriou
716 d2633501 Kostas Papadimitriou
    class Meta:
717 d2633501 Kostas Papadimitriou
        unique_together = (('identifier', 'module', 'user'), )
718 c630fee6 Kostas Papadimitriou
        ordering = ('module', 'created')
719 d2633501 Kostas Papadimitriou
720 3a72a5d4 Kostas Papadimitriou
    def __init__(self, *args, **kwargs):
721 3a72a5d4 Kostas Papadimitriou
        super(AstakosUserAuthProvider, self).__init__(*args, **kwargs)
722 3a72a5d4 Kostas Papadimitriou
        try:
723 3a72a5d4 Kostas Papadimitriou
            self.info = json.loads(self.info_data)
724 c630fee6 Kostas Papadimitriou
            if not self.info:
725 c630fee6 Kostas Papadimitriou
                self.info = {}
726 c630fee6 Kostas Papadimitriou
        except Exception, e:
727 3a72a5d4 Kostas Papadimitriou
            self.info = {}
728 c630fee6 Kostas Papadimitriou
729 3a72a5d4 Kostas Papadimitriou
        for key,value in self.info.iteritems():
730 3a72a5d4 Kostas Papadimitriou
            setattr(self, 'info_%s' % key, value)
731 3a72a5d4 Kostas Papadimitriou
732 d2633501 Kostas Papadimitriou
733 d2633501 Kostas Papadimitriou
    @property
734 d2633501 Kostas Papadimitriou
    def settings(self):
735 d2633501 Kostas Papadimitriou
        return auth_providers.get_provider(self.module)
736 d2633501 Kostas Papadimitriou
737 d2633501 Kostas Papadimitriou
    @property
738 d2633501 Kostas Papadimitriou
    def details_display(self):
739 c630fee6 Kostas Papadimitriou
        try:
740 c630fee6 Kostas Papadimitriou
          return self.settings.get_details_tpl_display % self.__dict__
741 c630fee6 Kostas Papadimitriou
        except:
742 c630fee6 Kostas Papadimitriou
          return ''
743 3a72a5d4 Kostas Papadimitriou
744 3a72a5d4 Kostas Papadimitriou
    @property
745 3a72a5d4 Kostas Papadimitriou
    def title_display(self):
746 3a72a5d4 Kostas Papadimitriou
        title_tpl = self.settings.get_title_display
747 3a72a5d4 Kostas Papadimitriou
        try:
748 3a72a5d4 Kostas Papadimitriou
            if self.settings.get_user_title_display:
749 3a72a5d4 Kostas Papadimitriou
                title_tpl = self.settings.get_user_title_display
750 3a72a5d4 Kostas Papadimitriou
        except Exception, e:
751 3a72a5d4 Kostas Papadimitriou
            pass
752 c630fee6 Kostas Papadimitriou
        try:
753 c630fee6 Kostas Papadimitriou
          return title_tpl % self.__dict__
754 c630fee6 Kostas Papadimitriou
        except:
755 c630fee6 Kostas Papadimitriou
          return self.settings.get_title_display % self.__dict__
756 d2633501 Kostas Papadimitriou
757 d2633501 Kostas Papadimitriou
    def can_remove(self):
758 d2633501 Kostas Papadimitriou
        return self.user.can_remove_auth_provider(self.module)
759 d2633501 Kostas Papadimitriou
760 d2633501 Kostas Papadimitriou
    def delete(self, *args, **kwargs):
761 d2633501 Kostas Papadimitriou
        ret = super(AstakosUserAuthProvider, self).delete(*args, **kwargs)
762 5156e663 Kostas Papadimitriou
        if self.module == 'local':
763 5156e663 Kostas Papadimitriou
            self.user.set_unusable_password()
764 5156e663 Kostas Papadimitriou
            self.user.save()
765 d2633501 Kostas Papadimitriou
        return ret
766 d2633501 Kostas Papadimitriou
767 f432088a Kostas Papadimitriou
    def __repr__(self):
768 f432088a Kostas Papadimitriou
        return '<AstakosUserAuthProvider %s:%s>' % (self.module, self.identifier)
769 f432088a Kostas Papadimitriou
770 b778b6fa Kostas Papadimitriou
    def __unicode__(self):
771 b778b6fa Kostas Papadimitriou
        if self.identifier:
772 b778b6fa Kostas Papadimitriou
            return "%s:%s" % (self.module, self.identifier)
773 b778b6fa Kostas Papadimitriou
        if self.auth_backend:
774 b778b6fa Kostas Papadimitriou
            return "%s:%s" % (self.module, self.auth_backend)
775 b778b6fa Kostas Papadimitriou
        return self.module
776 b778b6fa Kostas Papadimitriou
777 3a72a5d4 Kostas Papadimitriou
    def save(self, *args, **kwargs):
778 3a72a5d4 Kostas Papadimitriou
        self.info_data = json.dumps(self.info)
779 3a72a5d4 Kostas Papadimitriou
        return super(AstakosUserAuthProvider, self).save(*args, **kwargs)
780 b778b6fa Kostas Papadimitriou
781 d2633501 Kostas Papadimitriou
782 e1a80257 Sofia Papagiannaki
class ExtendedManager(models.Manager):
783 9a06d96f Olga Brani
    def _update_or_create(self, **kwargs):
784 9a06d96f Olga Brani
        assert kwargs, \
785 9a06d96f Olga Brani
            'update_or_create() must be passed at least one keyword argument'
786 9a06d96f Olga Brani
        obj, created = self.get_or_create(**kwargs)
787 9a06d96f Olga Brani
        defaults = kwargs.pop('defaults', {})
788 9a06d96f Olga Brani
        if created:
789 9a06d96f Olga Brani
            return obj, True, False
790 9a06d96f Olga Brani
        else:
791 9a06d96f Olga Brani
            try:
792 9a06d96f Olga Brani
                params = dict(
793 9a06d96f Olga Brani
                    [(k, v) for k, v in kwargs.items() if '__' not in k])
794 9a06d96f Olga Brani
                params.update(defaults)
795 9a06d96f Olga Brani
                for attr, val in params.items():
796 9a06d96f Olga Brani
                    if hasattr(obj, attr):
797 9a06d96f Olga Brani
                        setattr(obj, attr, val)
798 9a06d96f Olga Brani
                sid = transaction.savepoint()
799 9a06d96f Olga Brani
                obj.save(force_update=True)
800 9a06d96f Olga Brani
                transaction.savepoint_commit(sid)
801 9a06d96f Olga Brani
                return obj, False, True
802 9a06d96f Olga Brani
            except IntegrityError, e:
803 9a06d96f Olga Brani
                transaction.savepoint_rollback(sid)
804 9a06d96f Olga Brani
                try:
805 9a06d96f Olga Brani
                    return self.get(**kwargs), False, False
806 9a06d96f Olga Brani
                except self.model.DoesNotExist:
807 9a06d96f Olga Brani
                    raise e
808 9a06d96f Olga Brani
809 9a06d96f Olga Brani
    update_or_create = _update_or_create
810 5ce3ce4f Sofia Papagiannaki
811 8e45d6fd Sofia Papagiannaki
812 8e45d6fd Sofia Papagiannaki
class AstakosUserQuota(models.Model):
813 e1a80257 Sofia Papagiannaki
    objects = ExtendedManager()
814 6c997921 Sofia Papagiannaki
    capacity = models.BigIntegerField(_('Capacity'), null=True)
815 6c997921 Sofia Papagiannaki
    quantity = models.BigIntegerField(_('Quantity'), null=True)
816 6c997921 Sofia Papagiannaki
    export_limit = models.BigIntegerField(_('Export limit'), null=True)
817 6c997921 Sofia Papagiannaki
    import_limit = models.BigIntegerField(_('Import limit'), null=True)
818 8e45d6fd Sofia Papagiannaki
    resource = models.ForeignKey(Resource)
819 8e45d6fd Sofia Papagiannaki
    user = models.ForeignKey(AstakosUser)
820 5ce3ce4f Sofia Papagiannaki
821 8e45d6fd Sofia Papagiannaki
    class Meta:
822 8e45d6fd Sofia Papagiannaki
        unique_together = ("resource", "user")
823 09e7393c Sofia Papagiannaki
824 5ce3ce4f Sofia Papagiannaki
825 270dd48d Sofia Papagiannaki
class ApprovalTerms(models.Model):
826 270dd48d Sofia Papagiannaki
    """
827 270dd48d Sofia Papagiannaki
    Model for approval terms
828 270dd48d Sofia Papagiannaki
    """
829 6c736ed7 Kostas Papadimitriou
830 5ce3ce4f Sofia Papagiannaki
    date = models.DateTimeField(
831 e1a80257 Sofia Papagiannaki
        _('Issue date'), db_index=True, default=datetime.now())
832 e1a80257 Sofia Papagiannaki
    location = models.CharField(_('Terms location'), max_length=255)
833 270dd48d Sofia Papagiannaki
834 5ce3ce4f Sofia Papagiannaki
835 64cd4730 Antony Chazapis
class Invitation(models.Model):
836 890b0eaf Sofia Papagiannaki
    """
837 890b0eaf Sofia Papagiannaki
    Model for registring invitations
838 890b0eaf Sofia Papagiannaki
    """
839 0905ccd2 Sofia Papagiannaki
    inviter = models.ForeignKey(AstakosUser, related_name='invitations_sent',
840 64cd4730 Antony Chazapis
                                null=True)
841 e1a80257 Sofia Papagiannaki
    realname = models.CharField(_('Real name'), max_length=255)
842 e1a80257 Sofia Papagiannaki
    username = models.CharField(_('Unique ID'), max_length=255, unique=True)
843 e1a80257 Sofia Papagiannaki
    code = models.BigIntegerField(_('Invitation code'), db_index=True)
844 e1a80257 Sofia Papagiannaki
    is_consumed = models.BooleanField(_('Consumed?'), default=False)
845 e1a80257 Sofia Papagiannaki
    created = models.DateTimeField(_('Creation date'), auto_now_add=True)
846 e1a80257 Sofia Papagiannaki
    consumed = models.DateTimeField(_('Consumption date'), null=True, blank=True)
847 5ce3ce4f Sofia Papagiannaki
848 18ffbee1 Sofia Papagiannaki
    def __init__(self, *args, **kwargs):
849 18ffbee1 Sofia Papagiannaki
        super(Invitation, self).__init__(*args, **kwargs)
850 8f5a3a06 Sofia Papagiannaki
        if not self.id:
851 8f5a3a06 Sofia Papagiannaki
            self.code = _generate_invitation_code()
852 5ce3ce4f Sofia Papagiannaki
853 64cd4730 Antony Chazapis
    def consume(self):
854 64cd4730 Antony Chazapis
        self.is_consumed = True
855 64cd4730 Antony Chazapis
        self.consumed = datetime.now()
856 64cd4730 Antony Chazapis
        self.save()
857 6c736ed7 Kostas Papadimitriou
858 64cd4730 Antony Chazapis
    def __unicode__(self):
859 0905ccd2 Sofia Papagiannaki
        return '%s -> %s [%d]' % (self.inviter, self.username, self.code)
860 9c01d9d1 Sofia Papagiannaki
861 49790d9d Sofia Papagiannaki
862 49790d9d Sofia Papagiannaki
class EmailChangeManager(models.Manager):
863 34a76cdb Kostas Papadimitriou
864 49790d9d Sofia Papagiannaki
    @transaction.commit_on_success
865 49790d9d Sofia Papagiannaki
    def change_email(self, activation_key):
866 49790d9d Sofia Papagiannaki
        """
867 49790d9d Sofia Papagiannaki
        Validate an activation key and change the corresponding
868 49790d9d Sofia Papagiannaki
        ``User`` if valid.
869 49790d9d Sofia Papagiannaki

870 49790d9d Sofia Papagiannaki
        If the key is valid and has not expired, return the ``User``
871 49790d9d Sofia Papagiannaki
        after activating.
872 49790d9d Sofia Papagiannaki

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

875 49790d9d Sofia Papagiannaki
        If the key is valid but the ``User`` is already active,
876 49790d9d Sofia Papagiannaki
        return ``None``.
877 49790d9d Sofia Papagiannaki

878 49790d9d Sofia Papagiannaki
        After successful email change the activation record is deleted.
879 49790d9d Sofia Papagiannaki

880 49790d9d Sofia Papagiannaki
        Throws ValueError if there is already
881 49790d9d Sofia Papagiannaki
        """
882 49790d9d Sofia Papagiannaki
        try:
883 5ce3ce4f Sofia Papagiannaki
            email_change = self.model.objects.get(
884 5ce3ce4f Sofia Papagiannaki
                activation_key=activation_key)
885 49790d9d Sofia Papagiannaki
            if email_change.activation_key_expired():
886 49790d9d Sofia Papagiannaki
                email_change.delete()
887 49790d9d Sofia Papagiannaki
                raise EmailChange.DoesNotExist
888 49790d9d Sofia Papagiannaki
            # is there an active user with this address?
889 49790d9d Sofia Papagiannaki
            try:
890 789a5951 Sofia Papagiannaki
                AstakosUser.objects.get(email__iexact=email_change.new_email_address)
891 49790d9d Sofia Papagiannaki
            except AstakosUser.DoesNotExist:
892 49790d9d Sofia Papagiannaki
                pass
893 49790d9d Sofia Papagiannaki
            else:
894 73fbaec4 Sofia Papagiannaki
                raise ValueError(_('The new email address is reserved.'))
895 49790d9d Sofia Papagiannaki
            # update user
896 49790d9d Sofia Papagiannaki
            user = AstakosUser.objects.get(pk=email_change.user_id)
897 34a76cdb Kostas Papadimitriou
            old_email = user.email
898 49790d9d Sofia Papagiannaki
            user.email = email_change.new_email_address
899 49790d9d Sofia Papagiannaki
            user.save()
900 49790d9d Sofia Papagiannaki
            email_change.delete()
901 34a76cdb Kostas Papadimitriou
            msg = "User %d changed email from %s to %s" % (user.pk, old_email,
902 34a76cdb Kostas Papadimitriou
                                                          user.email)
903 34a76cdb Kostas Papadimitriou
            logger.log(LOGGING_LEVEL, msg)
904 49790d9d Sofia Papagiannaki
            return user
905 49790d9d Sofia Papagiannaki
        except EmailChange.DoesNotExist:
906 73fbaec4 Sofia Papagiannaki
            raise ValueError(_('Invalid activation key.'))
907 49790d9d Sofia Papagiannaki
908 49790d9d Sofia Papagiannaki
909 49790d9d Sofia Papagiannaki
class EmailChange(models.Model):
910 73fbaec4 Sofia Papagiannaki
    new_email_address = models.EmailField(
911 73fbaec4 Sofia Papagiannaki
        _(u'new e-mail address'),
912 73fbaec4 Sofia Papagiannaki
        help_text=_('Your old email address will be used until you verify your new one.'))
913 5ce3ce4f Sofia Papagiannaki
    user = models.ForeignKey(
914 34a76cdb Kostas Papadimitriou
        AstakosUser, unique=True, related_name='emailchanges')
915 49790d9d Sofia Papagiannaki
    requested_at = models.DateTimeField(default=datetime.now())
916 5ce3ce4f Sofia Papagiannaki
    activation_key = models.CharField(
917 5ce3ce4f Sofia Papagiannaki
        max_length=40, unique=True, db_index=True)
918 49790d9d Sofia Papagiannaki
919 49790d9d Sofia Papagiannaki
    objects = EmailChangeManager()
920 49790d9d Sofia Papagiannaki
921 34a76cdb Kostas Papadimitriou
    def get_url(self):
922 34a76cdb Kostas Papadimitriou
        return reverse('email_change_confirm',
923 34a76cdb Kostas Papadimitriou
                      kwargs={'activation_key': self.activation_key})
924 34a76cdb Kostas Papadimitriou
925 49790d9d Sofia Papagiannaki
    def activation_key_expired(self):
926 49790d9d Sofia Papagiannaki
        expiration_date = timedelta(days=EMAILCHANGE_ACTIVATION_DAYS)
927 ff9290ec Sofia Papagiannaki
        return self.requested_at + expiration_date < datetime.now()
928 ff9290ec Sofia Papagiannaki
929 6b03a847 Sofia Papagiannaki
930 ca828a10 Sofia Papagiannaki
class AdditionalMail(models.Model):
931 ca828a10 Sofia Papagiannaki
    """
932 ca828a10 Sofia Papagiannaki
    Model for registring invitations
933 ca828a10 Sofia Papagiannaki
    """
934 ca828a10 Sofia Papagiannaki
    owner = models.ForeignKey(AstakosUser)
935 1eec103a Sofia Papagiannaki
    email = models.EmailField()
936 ca828a10 Sofia Papagiannaki
937 5ce3ce4f Sofia Papagiannaki
938 fc1e2f02 Sofia Papagiannaki
def _generate_invitation_code():
939 fc1e2f02 Sofia Papagiannaki
    while True:
940 5ce3ce4f Sofia Papagiannaki
        code = randint(1, 2L ** 63 - 1)
941 fc1e2f02 Sofia Papagiannaki
        try:
942 fc1e2f02 Sofia Papagiannaki
            Invitation.objects.get(code=code)
943 fc1e2f02 Sofia Papagiannaki
            # An invitation with this code already exists, try again
944 fc1e2f02 Sofia Papagiannaki
        except Invitation.DoesNotExist:
945 fc1e2f02 Sofia Papagiannaki
            return code
946 fc1e2f02 Sofia Papagiannaki
947 5ce3ce4f Sofia Papagiannaki
948 fc1e2f02 Sofia Papagiannaki
def get_latest_terms():
949 fc1e2f02 Sofia Papagiannaki
    try:
950 fc1e2f02 Sofia Papagiannaki
        term = ApprovalTerms.objects.order_by('-id')[0]
951 fc1e2f02 Sofia Papagiannaki
        return term
952 fc1e2f02 Sofia Papagiannaki
    except IndexError:
953 fc1e2f02 Sofia Papagiannaki
        pass
954 fc1e2f02 Sofia Papagiannaki
    return None
955 fc1e2f02 Sofia Papagiannaki
956 ef20ea07 Sofia Papagiannaki
class PendingThirdPartyUser(models.Model):
957 ef20ea07 Sofia Papagiannaki
    """
958 ef20ea07 Sofia Papagiannaki
    Model for registring successful third party user authentications
959 ef20ea07 Sofia Papagiannaki
    """
960 e1a80257 Sofia Papagiannaki
    third_party_identifier = models.CharField(_('Third-party identifier'), max_length=255, null=True, blank=True)
961 e1a80257 Sofia Papagiannaki
    provider = models.CharField(_('Provider'), max_length=255, blank=True)
962 678b2236 Sofia Papagiannaki
    email = models.EmailField(_('e-mail address'), blank=True, null=True)
963 ef20ea07 Sofia Papagiannaki
    first_name = models.CharField(_('first name'), max_length=30, blank=True)
964 ef20ea07 Sofia Papagiannaki
    last_name = models.CharField(_('last name'), max_length=30, blank=True)
965 ef20ea07 Sofia Papagiannaki
    affiliation = models.CharField('Affiliation', max_length=255, blank=True)
966 ef20ea07 Sofia Papagiannaki
    username = models.CharField(_('username'), max_length=30, unique=True, help_text=_("Required. 30 characters or fewer. Letters, numbers and @/./+/-/_ characters"))
967 e1a80257 Sofia Papagiannaki
    token = models.CharField(_('Token'), max_length=255, null=True, blank=True)
968 d2633501 Kostas Papadimitriou
    created = models.DateTimeField(auto_now_add=True, null=True, blank=True)
969 c630fee6 Kostas Papadimitriou
    info = models.TextField(default="", null=True, blank=True)
970 d2633501 Kostas Papadimitriou
971 678b2236 Sofia Papagiannaki
    class Meta:
972 678b2236 Sofia Papagiannaki
        unique_together = ("provider", "third_party_identifier")
973 ef20ea07 Sofia Papagiannaki
974 c630fee6 Kostas Papadimitriou
    def get_user_instance(self):
975 c630fee6 Kostas Papadimitriou
        d = self.__dict__
976 c630fee6 Kostas Papadimitriou
        d.pop('_state', None)
977 c630fee6 Kostas Papadimitriou
        d.pop('id', None)
978 c630fee6 Kostas Papadimitriou
        d.pop('token', None)
979 c630fee6 Kostas Papadimitriou
        d.pop('created', None)
980 c630fee6 Kostas Papadimitriou
        d.pop('info', None)
981 c630fee6 Kostas Papadimitriou
        user = AstakosUser(**d)
982 c630fee6 Kostas Papadimitriou
983 c630fee6 Kostas Papadimitriou
        return user
984 c630fee6 Kostas Papadimitriou
985 ef20ea07 Sofia Papagiannaki
    @property
986 ef20ea07 Sofia Papagiannaki
    def realname(self):
987 ef20ea07 Sofia Papagiannaki
        return '%s %s' %(self.first_name, self.last_name)
988 ef20ea07 Sofia Papagiannaki
989 ef20ea07 Sofia Papagiannaki
    @realname.setter
990 ef20ea07 Sofia Papagiannaki
    def realname(self, value):
991 ef20ea07 Sofia Papagiannaki
        parts = value.split(' ')
992 ef20ea07 Sofia Papagiannaki
        if len(parts) == 2:
993 ef20ea07 Sofia Papagiannaki
            self.first_name = parts[0]
994 ef20ea07 Sofia Papagiannaki
            self.last_name = parts[1]
995 ef20ea07 Sofia Papagiannaki
        else:
996 ef20ea07 Sofia Papagiannaki
            self.last_name = parts[0]
997 2e90e3ec Kostas Papadimitriou
998 ef20ea07 Sofia Papagiannaki
    def save(self, **kwargs):
999 ef20ea07 Sofia Papagiannaki
        if not self.id:
1000 ef20ea07 Sofia Papagiannaki
            # set username
1001 ef20ea07 Sofia Papagiannaki
            while not self.username:
1002 ef20ea07 Sofia Papagiannaki
                username =  uuid.uuid4().hex[:30]
1003 ef20ea07 Sofia Papagiannaki
                try:
1004 ef20ea07 Sofia Papagiannaki
                    AstakosUser.objects.get(username = username)
1005 ef20ea07 Sofia Papagiannaki
                except AstakosUser.DoesNotExist, e:
1006 ef20ea07 Sofia Papagiannaki
                    self.username = username
1007 ef20ea07 Sofia Papagiannaki
        super(PendingThirdPartyUser, self).save(**kwargs)
1008 ef20ea07 Sofia Papagiannaki
1009 d2633501 Kostas Papadimitriou
    def generate_token(self):
1010 d2633501 Kostas Papadimitriou
        self.password = self.third_party_identifier
1011 d2633501 Kostas Papadimitriou
        self.last_login = datetime.now()
1012 d2633501 Kostas Papadimitriou
        self.token = default_token_generator.make_token(self)
1013 d2633501 Kostas Papadimitriou
1014 bf0c6de5 Sofia Papagiannaki
class SessionCatalog(models.Model):
1015 bf0c6de5 Sofia Papagiannaki
    session_key = models.CharField(_('session key'), max_length=40)
1016 bf0c6de5 Sofia Papagiannaki
    user = models.ForeignKey(AstakosUser, related_name='sessions', null=True)
1017 bf0c6de5 Sofia Papagiannaki
1018 fcc1e93f Sofia Papagiannaki
1019 fcc1e93f Sofia Papagiannaki
### PROJECTS ###
1020 fcc1e93f Sofia Papagiannaki
################
1021 fcc1e93f Sofia Papagiannaki
1022 e546df49 Georgios D. Tsoukalas
def synced_model_metaclass(class_name, class_parents, class_attributes):
1023 65360c65 Georgios D. Tsoukalas
1024 e546df49 Georgios D. Tsoukalas
    new_attributes = {}
1025 e546df49 Georgios D. Tsoukalas
    sync_attributes = {}
1026 65360c65 Georgios D. Tsoukalas
1027 e546df49 Georgios D. Tsoukalas
    for name, value in class_attributes.iteritems():
1028 e546df49 Georgios D. Tsoukalas
        sync, underscore, rest = name.partition('_')
1029 e546df49 Georgios D. Tsoukalas
        if sync == 'sync' and underscore == '_':
1030 e546df49 Georgios D. Tsoukalas
            sync_attributes[rest] = value
1031 e546df49 Georgios D. Tsoukalas
        else:
1032 e546df49 Georgios D. Tsoukalas
            new_attributes[name] = value
1033 65360c65 Georgios D. Tsoukalas
1034 e546df49 Georgios D. Tsoukalas
    if 'prefix' not in sync_attributes:
1035 e546df49 Georgios D. Tsoukalas
        m = ("you did not specify a 'sync_prefix' attribute "
1036 e546df49 Georgios D. Tsoukalas
             "in class '%s'" % (class_name,))
1037 e546df49 Georgios D. Tsoukalas
        raise ValueError(m)
1038 65360c65 Georgios D. Tsoukalas
1039 e546df49 Georgios D. Tsoukalas
    prefix = sync_attributes.pop('prefix')
1040 e546df49 Georgios D. Tsoukalas
    class_name = sync_attributes.pop('classname', prefix + '_model')
1041 65360c65 Georgios D. Tsoukalas
1042 e546df49 Georgios D. Tsoukalas
    for name, value in sync_attributes.iteritems():
1043 e546df49 Georgios D. Tsoukalas
        newname = prefix + '_' + name
1044 e546df49 Georgios D. Tsoukalas
        if newname in new_attributes:
1045 e546df49 Georgios D. Tsoukalas
            m = ("class '%s' was specified with prefix '%s' "
1046 e546df49 Georgios D. Tsoukalas
                 "but it already has an attribute named '%s'"
1047 e546df49 Georgios D. Tsoukalas
                 % (class_name, prefix, newname))
1048 e546df49 Georgios D. Tsoukalas
            raise ValueError(m)
1049 65360c65 Georgios D. Tsoukalas
1050 e546df49 Georgios D. Tsoukalas
        new_attributes[newname] = value
1051 e546df49 Georgios D. Tsoukalas
1052 e546df49 Georgios D. Tsoukalas
    newclass = type(class_name, class_parents, new_attributes)
1053 e546df49 Georgios D. Tsoukalas
    return newclass
1054 e546df49 Georgios D. Tsoukalas
1055 e546df49 Georgios D. Tsoukalas
1056 e546df49 Georgios D. Tsoukalas
def make_synced(prefix='sync', name='SyncedState'):
1057 e546df49 Georgios D. Tsoukalas
1058 e546df49 Georgios D. Tsoukalas
    the_name = name
1059 e546df49 Georgios D. Tsoukalas
    the_prefix = prefix
1060 e546df49 Georgios D. Tsoukalas
1061 e546df49 Georgios D. Tsoukalas
    class SyncedState(models.Model):
1062 e546df49 Georgios D. Tsoukalas
1063 e546df49 Georgios D. Tsoukalas
        sync_classname      = the_name
1064 e546df49 Georgios D. Tsoukalas
        sync_prefix         = the_prefix
1065 e546df49 Georgios D. Tsoukalas
        __metaclass__       = synced_model_metaclass
1066 e546df49 Georgios D. Tsoukalas
1067 e546df49 Georgios D. Tsoukalas
        sync_new_state      = models.BigIntegerField(null=True)
1068 e546df49 Georgios D. Tsoukalas
        sync_synced_state   = models.BigIntegerField(null=True)
1069 e546df49 Georgios D. Tsoukalas
        STATUS_SYNCED       = 0
1070 e546df49 Georgios D. Tsoukalas
        STATUS_PENDING      = 1
1071 e546df49 Georgios D. Tsoukalas
        sync_status         = models.IntegerField(db_index=True)
1072 e546df49 Georgios D. Tsoukalas
1073 e546df49 Georgios D. Tsoukalas
        class Meta:
1074 e546df49 Georgios D. Tsoukalas
            abstract = True
1075 e546df49 Georgios D. Tsoukalas
1076 e546df49 Georgios D. Tsoukalas
        class NotSynced(Exception):
1077 e546df49 Georgios D. Tsoukalas
            pass
1078 e546df49 Georgios D. Tsoukalas
1079 e546df49 Georgios D. Tsoukalas
        def sync_init_state(self, state):
1080 e546df49 Georgios D. Tsoukalas
            self.sync_synced_state = state
1081 e546df49 Georgios D. Tsoukalas
            self.sync_new_state = state
1082 65360c65 Georgios D. Tsoukalas
            self.sync_status = self.STATUS_SYNCED
1083 65360c65 Georgios D. Tsoukalas
1084 e546df49 Georgios D. Tsoukalas
        def sync_get_status(self):
1085 e546df49 Georgios D. Tsoukalas
            return self.sync_status
1086 65360c65 Georgios D. Tsoukalas
1087 e546df49 Georgios D. Tsoukalas
        def sync_set_status(self):
1088 e546df49 Georgios D. Tsoukalas
            if self.sync_new_state != self.sync_synced_state:
1089 e546df49 Georgios D. Tsoukalas
                self.sync_status = self.STATUS_PENDING
1090 e546df49 Georgios D. Tsoukalas
            else:
1091 e546df49 Georgios D. Tsoukalas
                self.sync_status = self.STATUS_SYNCED
1092 65360c65 Georgios D. Tsoukalas
1093 e546df49 Georgios D. Tsoukalas
        def sync_set_synced(self):
1094 e546df49 Georgios D. Tsoukalas
            self.sync_synced_state = self.sync_new_state
1095 e546df49 Georgios D. Tsoukalas
            self.sync_status = self.STATUS_SYNCED
1096 65360c65 Georgios D. Tsoukalas
1097 e546df49 Georgios D. Tsoukalas
        def sync_get_synced_state(self):
1098 e546df49 Georgios D. Tsoukalas
            return self.sync_synced_state
1099 65360c65 Georgios D. Tsoukalas
1100 e546df49 Georgios D. Tsoukalas
        def sync_set_new_state(self, new_state):
1101 e546df49 Georgios D. Tsoukalas
            self.sync_new_state = new_state
1102 e546df49 Georgios D. Tsoukalas
            self.sync_set_status()
1103 65360c65 Georgios D. Tsoukalas
1104 e546df49 Georgios D. Tsoukalas
        def sync_get_new_state(self):
1105 e546df49 Georgios D. Tsoukalas
            return self.sync_new_state
1106 65360c65 Georgios D. Tsoukalas
1107 e546df49 Georgios D. Tsoukalas
        def sync_set_synced_state(self, synced_state):
1108 e546df49 Georgios D. Tsoukalas
            self.sync_synced_state = synced_state
1109 e546df49 Georgios D. Tsoukalas
            self.sync_set_status()
1110 65360c65 Georgios D. Tsoukalas
1111 e546df49 Georgios D. Tsoukalas
        def sync_get_pending_objects(self):
1112 e546df49 Georgios D. Tsoukalas
            kw = dict((the_prefix + '_status', self.STATUS_PENDING))
1113 e546df49 Georgios D. Tsoukalas
            return self.objects.filter(**kw)
1114 65360c65 Georgios D. Tsoukalas
1115 e546df49 Georgios D. Tsoukalas
        def sync_get_synced_objects(self):
1116 e546df49 Georgios D. Tsoukalas
            kw = dict((the_prefix + '_status', self.STATUS_SYNCED))
1117 e546df49 Georgios D. Tsoukalas
            return self.objects.filter(**kw)
1118 65360c65 Georgios D. Tsoukalas
1119 e546df49 Georgios D. Tsoukalas
        def sync_verify_get_synced_state(self):
1120 e546df49 Georgios D. Tsoukalas
            status = self.sync_get_status()
1121 e546df49 Georgios D. Tsoukalas
            state = self.sync_get_synced_state()
1122 e546df49 Georgios D. Tsoukalas
            verified = (status == self.STATUS_SYNCED)
1123 e546df49 Georgios D. Tsoukalas
            return state, verified
1124 e546df49 Georgios D. Tsoukalas
1125 e546df49 Georgios D. Tsoukalas
        def sync_is_synced(self):
1126 e546df49 Georgios D. Tsoukalas
            state, verified = self.sync_verify_get_synced_state()
1127 e546df49 Georgios D. Tsoukalas
            return verified
1128 e546df49 Georgios D. Tsoukalas
1129 e546df49 Georgios D. Tsoukalas
    return SyncedState
1130 e546df49 Georgios D. Tsoukalas
1131 e546df49 Georgios D. Tsoukalas
SyncedState = make_synced(prefix='sync', name='SyncedState')
1132 425e2e95 Sofia Papagiannaki
1133 425e2e95 Sofia Papagiannaki
1134 6dcf53eb Kostas Papadimitriou
class ProjectApplicationManager(ForUpdateManager):
1135 5550bcfb Kostas Papadimitriou
1136 5550bcfb Kostas Papadimitriou
    def user_projects(self, user):
1137 5550bcfb Kostas Papadimitriou
        """
1138 5550bcfb Kostas Papadimitriou
        Return projects accessed by specified user.
1139 5550bcfb Kostas Papadimitriou
        """
1140 9b32e2fb Kostas Papadimitriou
        participates_fitlers = Q(owner=user) | Q(applicant=user) | \
1141 9b32e2fb Kostas Papadimitriou
                               Q(project__projectmembership__person=user)
1142 9b32e2fb Kostas Papadimitriou
        state_filters = (Q(state=ProjectApplication.PENDING) & \
1143 9b32e2fb Kostas Papadimitriou
                        Q(precursor_application__isnull=True)) | \
1144 9b32e2fb Kostas Papadimitriou
                        Q(state=ProjectApplication.APPROVED)
1145 9b32e2fb Kostas Papadimitriou
        return self.filter(participates_fitlers & state_filters).order_by('issue_date').distinct()
1146 5550bcfb Kostas Papadimitriou
1147 a5cef8d0 Kostas Papadimitriou
    def search_by_name(self, *search_strings):
1148 a5cef8d0 Kostas Papadimitriou
        q = Q()
1149 a5cef8d0 Kostas Papadimitriou
        for s in search_strings:
1150 a5cef8d0 Kostas Papadimitriou
            q = q | Q(name__icontains=s)
1151 a5cef8d0 Kostas Papadimitriou
        return self.filter(q)
1152 a5cef8d0 Kostas Papadimitriou
1153 a5cef8d0 Kostas Papadimitriou
1154 db9a498c Kostas Papadimitriou
PROJECT_STATE_DISPLAY = {
1155 db9a498c Kostas Papadimitriou
    'Pending': _('Pending review'),
1156 db9a498c Kostas Papadimitriou
    'Approved': _('Active'),
1157 db9a498c Kostas Papadimitriou
    'Replaced': _('Replaced'),
1158 db9a498c Kostas Papadimitriou
    'Unknown': _('Unknown')
1159 db9a498c Kostas Papadimitriou
}
1160 db9a498c Kostas Papadimitriou
1161 db9a498c Kostas Papadimitriou
USER_STATUS_DISPLAY = {
1162 db9a498c Kostas Papadimitriou
    100: _('Owner'),
1163 db9a498c Kostas Papadimitriou
      0: _('Join requested'),
1164 db9a498c Kostas Papadimitriou
      1: _('Pending'),
1165 db9a498c Kostas Papadimitriou
      2: _('Accepted member'),
1166 db9a498c Kostas Papadimitriou
      3: _('Removing'),
1167 db9a498c Kostas Papadimitriou
      4: _('Removed'),
1168 db9a498c Kostas Papadimitriou
     -1: _('Not a member'),
1169 db9a498c Kostas Papadimitriou
}
1170 db9a498c Kostas Papadimitriou
1171 a9ba418f Giorgos Korfiatis
class Chain(models.Model):
1172 a9ba418f Giorgos Korfiatis
    chain  =   models.AutoField(primary_key=True)
1173 a9ba418f Giorgos Korfiatis
1174 a9ba418f Giorgos Korfiatis
def new_chain():
1175 a9ba418f Giorgos Korfiatis
    c = Chain.objects.create()
1176 a9ba418f Giorgos Korfiatis
    chain = c.chain
1177 a9ba418f Giorgos Korfiatis
    c.delete()
1178 a9ba418f Giorgos Korfiatis
    return chain
1179 a9ba418f Giorgos Korfiatis
1180 a9ba418f Giorgos Korfiatis
1181 8aed306c Giorgos Korfiatis
class ProjectApplication(models.Model):
1182 85d444db Sofia Papagiannaki
    PENDING, APPROVED, REPLACED, UNKNOWN = 'Pending', 'Approved', 'Replaced', 'Unknown'
1183 425e2e95 Sofia Papagiannaki
    applicant               =   models.ForeignKey(
1184 425e2e95 Sofia Papagiannaki
                                    AstakosUser,
1185 d6fdc91e Georgios D. Tsoukalas
                                    related_name='projects_applied',
1186 d6fdc91e Georgios D. Tsoukalas
                                    db_index=True)
1187 d6fdc91e Georgios D. Tsoukalas
1188 d6fdc91e Georgios D. Tsoukalas
    state                   =   models.CharField(max_length=80,
1189 69c822cc Giorgos Korfiatis
                                                default=PENDING)
1190 d6fdc91e Georgios D. Tsoukalas
1191 425e2e95 Sofia Papagiannaki
    owner                   =   models.ForeignKey(
1192 425e2e95 Sofia Papagiannaki
                                    AstakosUser,
1193 d6fdc91e Georgios D. Tsoukalas
                                    related_name='projects_owned',
1194 d6fdc91e Georgios D. Tsoukalas
                                    db_index=True)
1195 d6fdc91e Georgios D. Tsoukalas
1196 a9ba418f Giorgos Korfiatis
    chain                   =   models.IntegerField(db_index=True)
1197 425e2e95 Sofia Papagiannaki
    precursor_application   =   models.OneToOneField('ProjectApplication',
1198 425e2e95 Sofia Papagiannaki
                                                     null=True,
1199 425e2e95 Sofia Papagiannaki
                                                     blank=True,
1200 d6fdc91e Georgios D. Tsoukalas
                                                     db_index=True)
1201 425e2e95 Sofia Papagiannaki
1202 67980f56 Georgios D. Tsoukalas
    name                    =   models.CharField(max_length=80)
1203 67980f56 Georgios D. Tsoukalas
    homepage                =   models.URLField(max_length=255, null=True)
1204 67980f56 Georgios D. Tsoukalas
    description             =   models.TextField(null=True, blank=True)
1205 e729f165 Kostas Papadimitriou
    start_date              =   models.DateTimeField(null=True, blank=True)
1206 67980f56 Georgios D. Tsoukalas
    end_date                =   models.DateTimeField()
1207 272cf735 Sofia Papagiannaki
    member_join_policy      =   models.IntegerField()
1208 272cf735 Sofia Papagiannaki
    member_leave_policy     =   models.IntegerField()
1209 67980f56 Georgios D. Tsoukalas
    limit_on_members_number =   models.PositiveIntegerField(null=True)
1210 425e2e95 Sofia Papagiannaki
    resource_grants         =   models.ManyToManyField(
1211 425e2e95 Sofia Papagiannaki
                                    Resource,
1212 425e2e95 Sofia Papagiannaki
                                    null=True,
1213 425e2e95 Sofia Papagiannaki
                                    blank=True,
1214 d6fdc91e Georgios D. Tsoukalas
                                    through='ProjectResourceGrant')
1215 425e2e95 Sofia Papagiannaki
    comments                =   models.TextField(null=True, blank=True)
1216 69c822cc Giorgos Korfiatis
    issue_date              =   models.DateTimeField(default=datetime.now)
1217 425e2e95 Sofia Papagiannaki
1218 6dcf53eb Kostas Papadimitriou
1219 5550bcfb Kostas Papadimitriou
    objects                 =   ProjectApplicationManager()
1220 7729e9cc Giorgos Korfiatis
1221 f3a45fc6 Kostas Papadimitriou
    def __unicode__(self):
1222 f3a45fc6 Kostas Papadimitriou
        return "%s applied by %s" % (self.name, self.applicant)
1223 f3a45fc6 Kostas Papadimitriou
1224 db9a498c Kostas Papadimitriou
    def state_display(self):
1225 db9a498c Kostas Papadimitriou
        return PROJECT_STATE_DISPLAY.get(self.state, _('Unknown'))
1226 db9a498c Kostas Papadimitriou
1227 a7aba804 Sofia Papagiannaki
    def add_resource_policy(self, service, resource, uplimit):
1228 e1a80257 Sofia Papagiannaki
        """Raises ObjectDoesNotExist, IntegrityError"""
1229 a7aba804 Sofia Papagiannaki
        q = self.projectresourcegrant_set
1230 e1a80257 Sofia Papagiannaki
        resource = Resource.objects.get(service__name=service, name=resource)
1231 a7aba804 Sofia Papagiannaki
        q.create(resource=resource, member_capacity=uplimit)
1232 e1a80257 Sofia Papagiannaki
1233 e87bbb41 Kostas Papadimitriou
    def user_status(self, user):
1234 e87bbb41 Kostas Papadimitriou
        """
1235 e87bbb41 Kostas Papadimitriou
        100 OWNER
1236 e87bbb41 Kostas Papadimitriou
        0   REQUESTED
1237 e87bbb41 Kostas Papadimitriou
        1   PENDING
1238 e87bbb41 Kostas Papadimitriou
        2   ACCEPTED
1239 e87bbb41 Kostas Papadimitriou
        3   REMOVING
1240 e87bbb41 Kostas Papadimitriou
        4   REMOVED
1241 e87bbb41 Kostas Papadimitriou
       -1   User has no association with the project
1242 e87bbb41 Kostas Papadimitriou
        """
1243 db9a498c Kostas Papadimitriou
        try:
1244 db9a498c Kostas Papadimitriou
            membership = self.project.projectmembership_set.get(person=user)
1245 db9a498c Kostas Papadimitriou
            status = membership.state
1246 db9a498c Kostas Papadimitriou
        except Project.DoesNotExist:
1247 db9a498c Kostas Papadimitriou
            status = -1
1248 db9a498c Kostas Papadimitriou
        except ProjectMembership.DoesNotExist:
1249 db9a498c Kostas Papadimitriou
            status = -1
1250 5550bcfb Kostas Papadimitriou
1251 5550bcfb Kostas Papadimitriou
        return status
1252 5550bcfb Kostas Papadimitriou
1253 db9a498c Kostas Papadimitriou
    def user_status_display(self, user):
1254 db9a498c Kostas Papadimitriou
        return USER_STATUS_DISPLAY.get(self.user_status(user), _('Unknown'))
1255 db9a498c Kostas Papadimitriou
1256 5550bcfb Kostas Papadimitriou
    def members_count(self):
1257 5550bcfb Kostas Papadimitriou
        return self.project.approved_memberships.count()
1258 5550bcfb Kostas Papadimitriou
1259 669cfe19 Olga Brani
    @property
1260 669cfe19 Olga Brani
    def grants(self):
1261 669cfe19 Olga Brani
        return self.projectresourcegrant_set.values('member_capacity', 'resource__name', 'resource__service__name')
1262 5550bcfb Kostas Papadimitriou
1263 e1a80257 Sofia Papagiannaki
    @property
1264 e1a80257 Sofia Papagiannaki
    def resource_policies(self):
1265 f3342849 Sofia Papagiannaki
        return self.projectresourcegrant_set.all()
1266 e1a80257 Sofia Papagiannaki
1267 e1a80257 Sofia Papagiannaki
    @resource_policies.setter
1268 e1a80257 Sofia Papagiannaki
    def resource_policies(self, policies):
1269 e1a80257 Sofia Papagiannaki
        for p in policies:
1270 e1a80257 Sofia Papagiannaki
            service = p.get('service', None)
1271 e1a80257 Sofia Papagiannaki
            resource = p.get('resource', None)
1272 e1a80257 Sofia Papagiannaki
            uplimit = p.get('uplimit', 0)
1273 a7aba804 Sofia Papagiannaki
            self.add_resource_policy(service, resource, uplimit)
1274 425e2e95 Sofia Papagiannaki
1275 ccab6eb5 Sofia Papagiannaki
    @property
1276 ccab6eb5 Sofia Papagiannaki
    def follower(self):
1277 ccab6eb5 Sofia Papagiannaki
        try:
1278 ccab6eb5 Sofia Papagiannaki
            return ProjectApplication.objects.get(precursor_application=self)
1279 ccab6eb5 Sofia Papagiannaki
        except ProjectApplication.DoesNotExist:
1280 ccab6eb5 Sofia Papagiannaki
            return
1281 ccab6eb5 Sofia Papagiannaki
1282 9b32e2fb Kostas Papadimitriou
    def followers(self):
1283 9b32e2fb Kostas Papadimitriou
        current = self
1284 9b32e2fb Kostas Papadimitriou
        try:
1285 9b32e2fb Kostas Papadimitriou
            while current.projectapplication:
1286 9b32e2fb Kostas Papadimitriou
                yield current.follower
1287 9b32e2fb Kostas Papadimitriou
                current = current.follower
1288 9b32e2fb Kostas Papadimitriou
        except:
1289 9b32e2fb Kostas Papadimitriou
            pass
1290 ece3b66e Giorgos Korfiatis
1291 9b32e2fb Kostas Papadimitriou
    def last_follower(self):
1292 9b32e2fb Kostas Papadimitriou
        try:
1293 9b32e2fb Kostas Papadimitriou
            return list(self.followers())[-1]
1294 9b32e2fb Kostas Papadimitriou
        except IndexError:
1295 9b32e2fb Kostas Papadimitriou
            return None
1296 4f22664f Georgios D. Tsoukalas
1297 b6fe8bb8 Giorgos Korfiatis
    def _get_project_for_update(self):
1298 a9ba418f Giorgos Korfiatis
        try:
1299 a9ba418f Giorgos Korfiatis
            objects = Project.objects.select_for_update()
1300 a9ba418f Giorgos Korfiatis
            project = objects.get(id=self.chain)
1301 a9ba418f Giorgos Korfiatis
            return project
1302 a9ba418f Giorgos Korfiatis
        except Project.DoesNotExist:
1303 a9ba418f Giorgos Korfiatis
            return None
1304 4f22664f Georgios D. Tsoukalas
1305 ccab6eb5 Sofia Papagiannaki
    def approve(self, approval_user=None):
1306 ccab6eb5 Sofia Papagiannaki
        """
1307 ccab6eb5 Sofia Papagiannaki
        If approval_user then during owner membership acceptance
1308 ccab6eb5 Sofia Papagiannaki
        it is checked whether the request_user is eligible.
1309 262e04c6 Giorgos Korfiatis

1310 2553efae Sofia Papagiannaki
        Raises:
1311 b8f05f8d Sofia Papagiannaki
            PermissionDenied
1312 ccab6eb5 Sofia Papagiannaki
        """
1313 4f22664f Georgios D. Tsoukalas
1314 4f22664f Georgios D. Tsoukalas
        if not transaction.is_managed():
1315 4f22664f Georgios D. Tsoukalas
            raise AssertionError("NOPE")
1316 4f22664f Georgios D. Tsoukalas
1317 73fbaec4 Sofia Papagiannaki
        new_project_name = self.name
1318 85d444db Sofia Papagiannaki
        if self.state != self.PENDING:
1319 65360c65 Georgios D. Tsoukalas
            m = _("cannot approve: project '%s' in state '%s'") % (
1320 65360c65 Georgios D. Tsoukalas
                    new_project_name, self.state)
1321 4f22664f Georgios D. Tsoukalas
            raise PermissionDenied(m) # invalid argument
1322 262e04c6 Giorgos Korfiatis
1323 fdafae27 Giorgos Korfiatis
        now = datetime.now()
1324 b6fe8bb8 Giorgos Korfiatis
        project = self._get_project_for_update()
1325 3cc9637a Giorgos Korfiatis
1326 3cc9637a Giorgos Korfiatis
        try:
1327 3cc9637a Giorgos Korfiatis
            # needs SERIALIZABLE
1328 3cc9637a Giorgos Korfiatis
            conflicting_project = Project.objects.get(name=new_project_name)
1329 3cc9637a Giorgos Korfiatis
            if (conflicting_project.is_alive and
1330 37705b5f Giorgos Korfiatis
                conflicting_project != project):
1331 3cc9637a Giorgos Korfiatis
                m = (_("cannot approve: project with name '%s' "
1332 3cc9637a Giorgos Korfiatis
                       "already exists (serial: %s)") % (
1333 3cc9637a Giorgos Korfiatis
                        new_project_name, conflicting_project.id))
1334 3cc9637a Giorgos Korfiatis
                raise PermissionDenied(m) # invalid argument
1335 3cc9637a Giorgos Korfiatis
        except Project.DoesNotExist:
1336 3cc9637a Giorgos Korfiatis
            pass
1337 3cc9637a Giorgos Korfiatis
1338 4bf02ea5 Giorgos Korfiatis
        new_project = False
1339 4f22664f Georgios D. Tsoukalas
        if project is None:
1340 4bf02ea5 Giorgos Korfiatis
            new_project = True
1341 a9ba418f Giorgos Korfiatis
            project = Project(id=self.chain, creation_date=now)
1342 fdafae27 Giorgos Korfiatis
1343 3cc9637a Giorgos Korfiatis
        project.name = new_project_name
1344 ee45eb81 Giorgos Korfiatis
        project.application = self
1345 4bf02ea5 Giorgos Korfiatis
        project.last_approval_date = now
1346 a769d7ba Sofia Papagiannaki
        if not new_project:
1347 a769d7ba Sofia Papagiannaki
            project.is_modified = True
1348 4bf02ea5 Giorgos Korfiatis
1349 a769d7ba Sofia Papagiannaki
        project.save()
1350 425e2e95 Sofia Papagiannaki
1351 85d444db Sofia Papagiannaki
        self.state = self.APPROVED
1352 bfe23b13 Sofia Papagiannaki
        self.save()
1353 262e04c6 Giorgos Korfiatis
1354 15ca2bea Giorgos Korfiatis
def submit_application(**kw):
1355 15ca2bea Giorgos Korfiatis
1356 15ca2bea Giorgos Korfiatis
    resource_policies = kw.pop('resource_policies', None)
1357 15ca2bea Giorgos Korfiatis
    application = ProjectApplication(**kw)
1358 15ca2bea Giorgos Korfiatis
1359 15ca2bea Giorgos Korfiatis
    precursor = kw['precursor_application']
1360 15ca2bea Giorgos Korfiatis
1361 15ca2bea Giorgos Korfiatis
    if precursor is not None:
1362 15ca2bea Giorgos Korfiatis
        precursor.state = ProjectApplication.REPLACED
1363 15ca2bea Giorgos Korfiatis
        precursor.save()
1364 a9ba418f Giorgos Korfiatis
        application.chain = precursor.chain
1365 a9ba418f Giorgos Korfiatis
    else:
1366 a9ba418f Giorgos Korfiatis
        application.chain = new_chain()
1367 15ca2bea Giorgos Korfiatis
1368 15ca2bea Giorgos Korfiatis
    application.save()
1369 15ca2bea Giorgos Korfiatis
    application.resource_policies = resource_policies
1370 15ca2bea Giorgos Korfiatis
    return application
1371 ccab6eb5 Sofia Papagiannaki
1372 73fbaec4 Sofia Papagiannaki
class ProjectResourceGrant(models.Model):
1373 e1a80257 Sofia Papagiannaki
1374 425e2e95 Sofia Papagiannaki
    resource                =   models.ForeignKey(Resource)
1375 425e2e95 Sofia Papagiannaki
    project_application     =   models.ForeignKey(ProjectApplication,
1376 5200e864 Sofia Papagiannaki
                                                  null=True)
1377 c11dc0ce Giorgos Korfiatis
    project_capacity        =   intDecimalField(default=QH_PRACTICALLY_INFINITE)
1378 c11dc0ce Giorgos Korfiatis
    project_import_limit    =   intDecimalField(default=QH_PRACTICALLY_INFINITE)
1379 c11dc0ce Giorgos Korfiatis
    project_export_limit    =   intDecimalField(default=QH_PRACTICALLY_INFINITE)
1380 c11dc0ce Giorgos Korfiatis
    member_capacity         =   intDecimalField(default=QH_PRACTICALLY_INFINITE)
1381 c11dc0ce Giorgos Korfiatis
    member_import_limit     =   intDecimalField(default=QH_PRACTICALLY_INFINITE)
1382 c11dc0ce Giorgos Korfiatis
    member_export_limit     =   intDecimalField(default=QH_PRACTICALLY_INFINITE)
1383 73fbaec4 Sofia Papagiannaki
1384 73fbaec4 Sofia Papagiannaki
    objects = ExtendedManager()
1385 73fbaec4 Sofia Papagiannaki
1386 73fbaec4 Sofia Papagiannaki
    class Meta:
1387 73fbaec4 Sofia Papagiannaki
        unique_together = ("resource", "project_application")
1388 8327782d Sofia Papagiannaki
1389 e546df49 Georgios D. Tsoukalas
1390 123be68a Giorgos Korfiatis
class ProjectManager(ForUpdateManager):
1391 123be68a Giorgos Korfiatis
1392 123be68a Giorgos Korfiatis
    def _q_terminated(self):
1393 b6fe8bb8 Giorgos Korfiatis
        return Q(state=Project.TERMINATED)
1394 123be68a Giorgos Korfiatis
1395 123be68a Giorgos Korfiatis
    def terminated_projects(self):
1396 123be68a Giorgos Korfiatis
        q = self._q_terminated()
1397 123be68a Giorgos Korfiatis
        return self.filter(q)
1398 123be68a Giorgos Korfiatis
1399 123be68a Giorgos Korfiatis
    def not_terminated_projects(self):
1400 123be68a Giorgos Korfiatis
        q = ~self._q_terminated()
1401 123be68a Giorgos Korfiatis
        return self.filter(q)
1402 123be68a Giorgos Korfiatis
1403 b6fe8bb8 Giorgos Korfiatis
    def terminating_projects(self):
1404 b6fe8bb8 Giorgos Korfiatis
        q = self._q_terminated() & Q(is_active=True)
1405 b6fe8bb8 Giorgos Korfiatis
        return self.filter(q)
1406 b6fe8bb8 Giorgos Korfiatis
1407 b6fe8bb8 Giorgos Korfiatis
    def modified_projects(self):
1408 b6fe8bb8 Giorgos Korfiatis
        return self.filter(is_modified=True)
1409 b6fe8bb8 Giorgos Korfiatis
1410 b6fe8bb8 Giorgos Korfiatis
1411 d6fdc91e Georgios D. Tsoukalas
class Project(models.Model):
1412 e546df49 Georgios D. Tsoukalas
1413 ee45eb81 Giorgos Korfiatis
    application                 =   models.OneToOneField(
1414 4f22664f Georgios D. Tsoukalas
                                            ProjectApplication,
1415 782d9118 Giorgos Korfiatis
                                            related_name='project')
1416 4f22664f Georgios D. Tsoukalas
    last_approval_date          =   models.DateTimeField(null=True)
1417 4f22664f Georgios D. Tsoukalas
1418 4f22664f Georgios D. Tsoukalas
    members                     =   models.ManyToManyField(
1419 4f22664f Georgios D. Tsoukalas
                                            AstakosUser,
1420 4f22664f Georgios D. Tsoukalas
                                            through='ProjectMembership')
1421 4f22664f Georgios D. Tsoukalas
1422 5b9e9530 Giorgos Korfiatis
    deactivation_reason         =   models.CharField(max_length=255, null=True)
1423 5b9e9530 Giorgos Korfiatis
    deactivation_date           =   models.DateTimeField(null=True)
1424 4f22664f Georgios D. Tsoukalas
1425 4f22664f Georgios D. Tsoukalas
    creation_date               =   models.DateTimeField()
1426 4f22664f Georgios D. Tsoukalas
    name                        =   models.CharField(
1427 4f22664f Georgios D. Tsoukalas
                                            max_length=80,
1428 4f22664f Georgios D. Tsoukalas
                                            db_index=True,
1429 4f22664f Georgios D. Tsoukalas
                                            unique=True)
1430 425e2e95 Sofia Papagiannaki
1431 b6fe8bb8 Giorgos Korfiatis
    APPROVED    = 1
1432 b6fe8bb8 Giorgos Korfiatis
    SUSPENDED   = 10
1433 b6fe8bb8 Giorgos Korfiatis
    TERMINATED  = 100
1434 5b9e9530 Giorgos Korfiatis
1435 b6fe8bb8 Giorgos Korfiatis
    is_modified                 =   models.BooleanField(default=False,
1436 b6fe8bb8 Giorgos Korfiatis
                                                        db_index=True)
1437 b6fe8bb8 Giorgos Korfiatis
    is_active                   =   models.BooleanField(default=True,
1438 b6fe8bb8 Giorgos Korfiatis
                                                        db_index=True)
1439 b6fe8bb8 Giorgos Korfiatis
    state                       =   models.IntegerField(default=APPROVED,
1440 123be68a Giorgos Korfiatis
                                                        db_index=True)
1441 123be68a Giorgos Korfiatis
1442 123be68a Giorgos Korfiatis
    objects     =   ProjectManager()
1443 7729e9cc Giorgos Korfiatis
1444 8c7b8bb8 Giorgos Korfiatis
    def __str__(self):
1445 8c7b8bb8 Giorgos Korfiatis
        return _("<project %s '%s'>") % (self.id, self.application.name)
1446 8c7b8bb8 Giorgos Korfiatis
1447 8c7b8bb8 Giorgos Korfiatis
    __repr__ = __str__
1448 8c7b8bb8 Giorgos Korfiatis
1449 b6fe8bb8 Giorgos Korfiatis
    def is_deactivated(self, reason=None):
1450 b6fe8bb8 Giorgos Korfiatis
        if reason is not None:
1451 b6fe8bb8 Giorgos Korfiatis
            return self.state == reason
1452 425e2e95 Sofia Papagiannaki
1453 b6fe8bb8 Giorgos Korfiatis
        return self.state != self.APPROVED
1454 123be68a Giorgos Korfiatis
1455 123be68a Giorgos Korfiatis
    def is_deactivating(self, reason=None):
1456 b6fe8bb8 Giorgos Korfiatis
        if not self.is_active:
1457 b6fe8bb8 Giorgos Korfiatis
            return False
1458 123be68a Giorgos Korfiatis
1459 b6fe8bb8 Giorgos Korfiatis
        return self.is_deactivated(reason)
1460 123be68a Giorgos Korfiatis
1461 b6fe8bb8 Giorgos Korfiatis
    def is_deactivated_strict(self, reason=None):
1462 b6fe8bb8 Giorgos Korfiatis
        if self.is_active:
1463 b6fe8bb8 Giorgos Korfiatis
            return False
1464 123be68a Giorgos Korfiatis
1465 b6fe8bb8 Giorgos Korfiatis
        return self.is_deactivated(reason)
1466 425e2e95 Sofia Papagiannaki
1467 123be68a Giorgos Korfiatis
    ### Deactivation calls
1468 425e2e95 Sofia Papagiannaki
1469 123be68a Giorgos Korfiatis
    def deactivate(self):
1470 b6fe8bb8 Giorgos Korfiatis
        self.deactivation_date = datetime.now()
1471 b6fe8bb8 Giorgos Korfiatis
        self.is_active = False
1472 425e2e95 Sofia Papagiannaki
1473 123be68a Giorgos Korfiatis
    def terminate(self):
1474 123be68a Giorgos Korfiatis
        self.deactivation_reason = 'TERMINATED'
1475 b6fe8bb8 Giorgos Korfiatis
        self.state = self.TERMINATED
1476 123be68a Giorgos Korfiatis
        self.save()
1477 8aed306c Giorgos Korfiatis
1478 123be68a Giorgos Korfiatis
1479 123be68a Giorgos Korfiatis
    ### Logical checks
1480 425e2e95 Sofia Papagiannaki
1481 e1a80257 Sofia Papagiannaki
    def is_inconsistent(self):
1482 e1a80257 Sofia Papagiannaki
        now = datetime.now()
1483 5b9e9530 Giorgos Korfiatis
        dates = [self.creation_date,
1484 5b9e9530 Giorgos Korfiatis
                 self.last_approval_date,
1485 5b9e9530 Giorgos Korfiatis
                 self.deactivation_date]
1486 5b9e9530 Giorgos Korfiatis
        return any([date > now for date in dates])
1487 5b9e9530 Giorgos Korfiatis
1488 b6fe8bb8 Giorgos Korfiatis
    def is_active_strict(self):
1489 b6fe8bb8 Giorgos Korfiatis
        return self.is_active and self.state == self.APPROVED
1490 5b9e9530 Giorgos Korfiatis
1491 123be68a Giorgos Korfiatis
    @property
1492 123be68a Giorgos Korfiatis
    def is_alive(self):
1493 b6fe8bb8 Giorgos Korfiatis
        return self.is_active_strict()
1494 123be68a Giorgos Korfiatis
1495 123be68a Giorgos Korfiatis
    @property
1496 123be68a Giorgos Korfiatis
    def is_terminated(self):
1497 123be68a Giorgos Korfiatis
        return self.is_deactivated(self.TERMINATED)
1498 123be68a Giorgos Korfiatis
1499 123be68a Giorgos Korfiatis
    @property
1500 123be68a Giorgos Korfiatis
    def is_suspended(self):
1501 123be68a Giorgos Korfiatis
        return False
1502 5b9e9530 Giorgos Korfiatis
1503 5b9e9530 Giorgos Korfiatis
    def violates_resource_grants(self):
1504 e1a80257 Sofia Papagiannaki
        return False
1505 65360c65 Georgios D. Tsoukalas
1506 5b9e9530 Giorgos Korfiatis
    def violates_members_limit(self, adding=0):
1507 5b9e9530 Giorgos Korfiatis
        application = self.application
1508 5b9e9530 Giorgos Korfiatis
        return (len(self.approved_members) + adding >
1509 5b9e9530 Giorgos Korfiatis
                application.limit_on_members_number)
1510 5b9e9530 Giorgos Korfiatis
1511 123be68a Giorgos Korfiatis
1512 123be68a Giorgos Korfiatis
    ### Other
1513 5b9e9530 Giorgos Korfiatis
1514 425e2e95 Sofia Papagiannaki
    @property
1515 425e2e95 Sofia Papagiannaki
    def approved_memberships(self):
1516 5b9e9530 Giorgos Korfiatis
        query = ProjectMembership.query_approved()
1517 5b9e9530 Giorgos Korfiatis
        return self.projectmembership_set.filter(query)
1518 4f22664f Georgios D. Tsoukalas
1519 425e2e95 Sofia Papagiannaki
    @property
1520 425e2e95 Sofia Papagiannaki
    def approved_members(self):
1521 425e2e95 Sofia Papagiannaki
        return [m.person for m in self.approved_memberships]
1522 4f22664f Georgios D. Tsoukalas
1523 4f22664f Georgios D. Tsoukalas
    def add_member(self, user):
1524 bfe23b13 Sofia Papagiannaki
        """
1525 bfe23b13 Sofia Papagiannaki
        Raises:
1526 bfe23b13 Sofia Papagiannaki
            django.exceptions.PermissionDenied
1527 bfe23b13 Sofia Papagiannaki
            astakos.im.models.AstakosUser.DoesNotExist
1528 bfe23b13 Sofia Papagiannaki
        """
1529 2a965273 Sofia Papagiannaki
        if isinstance(user, int):
1530 4f22664f Georgios D. Tsoukalas
            user = AstakosUser.objects.get(user=user)
1531 4f22664f Georgios D. Tsoukalas
1532 ccab6eb5 Sofia Papagiannaki
        m, created = ProjectMembership.objects.get_or_create(
1533 ccab6eb5 Sofia Papagiannaki
            person=user, project=self
1534 2a965273 Sofia Papagiannaki
        )
1535 4f22664f Georgios D. Tsoukalas
        m.accept()
1536 ccab6eb5 Sofia Papagiannaki
1537 4f22664f Georgios D. Tsoukalas
    def remove_member(self, user):
1538 bfe23b13 Sofia Papagiannaki
        """
1539 bfe23b13 Sofia Papagiannaki
        Raises:
1540 bfe23b13 Sofia Papagiannaki
            django.exceptions.PermissionDenied
1541 bfe23b13 Sofia Papagiannaki
            astakos.im.models.AstakosUser.DoesNotExist
1542 bfe23b13 Sofia Papagiannaki
            astakos.im.models.ProjectMembership.DoesNotExist
1543 bfe23b13 Sofia Papagiannaki
        """
1544 ccab6eb5 Sofia Papagiannaki
        if isinstance(user, int):
1545 4f22664f Georgios D. Tsoukalas
            user = AstakosUser.objects.get(user=user)
1546 4f22664f Georgios D. Tsoukalas
1547 bfe23b13 Sofia Papagiannaki
        m = ProjectMembership.objects.get(person=user, project=self)
1548 bfe23b13 Sofia Papagiannaki
        m.remove()
1549 4f22664f Georgios D. Tsoukalas
1550 425e2e95 Sofia Papagiannaki
1551 b6fe8bb8 Giorgos Korfiatis
class PendingMembershipError(Exception):
1552 b6fe8bb8 Giorgos Korfiatis
    pass
1553 b22de10a Sofia Papagiannaki
1554 4f22664f Georgios D. Tsoukalas
1555 d6fdc91e Georgios D. Tsoukalas
class ProjectMembership(models.Model):
1556 4f22664f Georgios D. Tsoukalas
1557 425e2e95 Sofia Papagiannaki
    person              =   models.ForeignKey(AstakosUser)
1558 425e2e95 Sofia Papagiannaki
    request_date        =   models.DateField(default=datetime.now())
1559 d6fdc91e Georgios D. Tsoukalas
    project             =   models.ForeignKey(Project)
1560 d6fdc91e Georgios D. Tsoukalas
1561 b6fe8bb8 Giorgos Korfiatis
    REQUESTED   =   0
1562 b6fe8bb8 Giorgos Korfiatis
    ACCEPTED    =   1
1563 b6fe8bb8 Giorgos Korfiatis
    SUSPENDED   =   10
1564 b6fe8bb8 Giorgos Korfiatis
    TERMINATED  =   100
1565 b6fe8bb8 Giorgos Korfiatis
    REMOVED     =   200
1566 b6fe8bb8 Giorgos Korfiatis
1567 b6fe8bb8 Giorgos Korfiatis
    state               =   models.IntegerField(default=REQUESTED,
1568 b6fe8bb8 Giorgos Korfiatis
                                                db_index=True)
1569 b6fe8bb8 Giorgos Korfiatis
    is_pending          =   models.BooleanField(default=False, db_index=True)
1570 b6fe8bb8 Giorgos Korfiatis
    is_active           =   models.BooleanField(default=False, db_index=True)
1571 5200e864 Sofia Papagiannaki
    application         =   models.ForeignKey(
1572 5200e864 Sofia Papagiannaki
                                ProjectApplication,
1573 5200e864 Sofia Papagiannaki
                                null=True,
1574 5200e864 Sofia Papagiannaki
                                related_name='memberships')
1575 5200e864 Sofia Papagiannaki
    pending_application =   models.ForeignKey(
1576 5200e864 Sofia Papagiannaki
                                ProjectApplication,
1577 5200e864 Sofia Papagiannaki
                                null=True,
1578 5200e864 Sofia Papagiannaki
                                related_name='pending_memebrships')
1579 d6fdc91e Georgios D. Tsoukalas
    pending_serial      =   models.BigIntegerField(null=True, db_index=True)
1580 425e2e95 Sofia Papagiannaki
1581 425e2e95 Sofia Papagiannaki
    acceptance_date     =   models.DateField(null=True, db_index=True)
1582 425e2e95 Sofia Papagiannaki
    leave_request_date  =   models.DateField(null=True)
1583 2a965273 Sofia Papagiannaki
1584 ee45eb81 Giorgos Korfiatis
    objects     =   ForUpdateManager()
1585 ee45eb81 Giorgos Korfiatis
1586 5b9e9530 Giorgos Korfiatis
1587 b6fe8bb8 Giorgos Korfiatis
    def get_combined_state(self):
1588 b6fe8bb8 Giorgos Korfiatis
        return self.state, self.is_active, self.is_pending
1589 5b9e9530 Giorgos Korfiatis
1590 5b9e9530 Giorgos Korfiatis
    @classmethod
1591 5b9e9530 Giorgos Korfiatis
    def query_approved(cls):
1592 b6fe8bb8 Giorgos Korfiatis
        return (~Q(state=cls.REQUESTED) &
1593 b6fe8bb8 Giorgos Korfiatis
                ~Q(state=cls.REMOVED))
1594 65360c65 Georgios D. Tsoukalas
1595 0cc22d47 Sofia Papagiannaki
    class Meta:
1596 0cc22d47 Sofia Papagiannaki
        unique_together = ("person", "project")
1597 d6fdc91e Georgios D. Tsoukalas
        #index_together = [["project", "state"]]
1598 bfe23b13 Sofia Papagiannaki
1599 65360c65 Georgios D. Tsoukalas
    def __str__(self):
1600 8c7b8bb8 Giorgos Korfiatis
        return _("<'%s' membership in '%s'>") % (
1601 8c7b8bb8 Giorgos Korfiatis
                self.person.username, self.project)
1602 65360c65 Georgios D. Tsoukalas
1603 65360c65 Georgios D. Tsoukalas
    __repr__ = __str__
1604 65360c65 Georgios D. Tsoukalas
1605 65360c65 Georgios D. Tsoukalas
    def __init__(self, *args, **kwargs):
1606 ee45eb81 Giorgos Korfiatis
        self.state = self.REQUESTED
1607 65360c65 Georgios D. Tsoukalas
        super(ProjectMembership, self).__init__(*args, **kwargs)
1608 65360c65 Georgios D. Tsoukalas
1609 4f22664f Georgios D. Tsoukalas
    def _set_history_item(self, reason, date=None):
1610 4f22664f Georgios D. Tsoukalas
        if isinstance(reason, basestring):
1611 4f22664f Georgios D. Tsoukalas
            reason = ProjectMembershipHistory.reasons.get(reason, -1)
1612 4f22664f Georgios D. Tsoukalas
1613 4f22664f Georgios D. Tsoukalas
        history_item = ProjectMembershipHistory(
1614 4f22664f Georgios D. Tsoukalas
                            serial=self.id,
1615 02d2519e Giorgos Korfiatis
                            person=self.person.uuid,
1616 02d2519e Giorgos Korfiatis
                            project=self.project_id,
1617 8f975b72 Sofia Papagiannaki
                            date=date or datetime.now(),
1618 4f22664f Georgios D. Tsoukalas
                            reason=reason)
1619 4f22664f Georgios D. Tsoukalas
        history_item.save()
1620 4f22664f Georgios D. Tsoukalas
        serial = history_item.id
1621 4f22664f Georgios D. Tsoukalas
1622 4f22664f Georgios D. Tsoukalas
    def accept(self):
1623 b6fe8bb8 Giorgos Korfiatis
        if self.is_pending:
1624 b6fe8bb8 Giorgos Korfiatis
            m = _("%s: attempt to accept while is pending") % (self,)
1625 b6fe8bb8 Giorgos Korfiatis
            raise AssertionError(m)
1626 b6fe8bb8 Giorgos Korfiatis
1627 5200e864 Sofia Papagiannaki
        state = self.state
1628 65360c65 Georgios D. Tsoukalas
        if state != self.REQUESTED:
1629 5b9e9530 Giorgos Korfiatis
            m = _("%s: attempt to accept in state '%s'") % (self, state)
1630 65360c65 Georgios D. Tsoukalas
            raise AssertionError(m)
1631 4f22664f Georgios D. Tsoukalas
1632 65360c65 Georgios D. Tsoukalas
        now = datetime.now()
1633 65360c65 Georgios D. Tsoukalas
        self.acceptance_date = now
1634 65360c65 Georgios D. Tsoukalas
        self._set_history_item(reason='ACCEPT', date=now)
1635 b6fe8bb8 Giorgos Korfiatis
        if self.project.is_active_strict():
1636 b6fe8bb8 Giorgos Korfiatis
            self.state = self.ACCEPTED
1637 b6fe8bb8 Giorgos Korfiatis
            self.is_pending = True
1638 b6fe8bb8 Giorgos Korfiatis
        else:
1639 b6fe8bb8 Giorgos Korfiatis
            self.state = self.TERMINATED
1640 b6fe8bb8 Giorgos Korfiatis
1641 65360c65 Georgios D. Tsoukalas
        self.save()
1642 4f22664f Georgios D. Tsoukalas
1643 65360c65 Georgios D. Tsoukalas
    def remove(self):
1644 b6fe8bb8 Giorgos Korfiatis
        if self.is_pending:
1645 b6fe8bb8 Giorgos Korfiatis
            m = _("%s: attempt to remove while is pending") % (self,)
1646 b6fe8bb8 Giorgos Korfiatis
            raise AssertionError(m)
1647 b6fe8bb8 Giorgos Korfiatis
1648 974ee6a6 Sofia Papagiannaki
        state = self.state
1649 b6fe8bb8 Giorgos Korfiatis
        if state not in [self.ACCEPTED, self.TERMINATED]:
1650 65360c65 Georgios D. Tsoukalas
            m = _("%s: attempt to remove in state '%s'") % (self, state)
1651 65360c65 Georgios D. Tsoukalas
            raise AssertionError(m)
1652 4f22664f Georgios D. Tsoukalas
1653 ee45eb81 Giorgos Korfiatis
        self._set_history_item(reason='REMOVE')
1654 b6fe8bb8 Giorgos Korfiatis
        self.state = self.REMOVED
1655 b6fe8bb8 Giorgos Korfiatis
        self.is_pending = True
1656 0cc22d47 Sofia Papagiannaki
        self.save()
1657 b8f05f8d Sofia Papagiannaki
1658 65360c65 Georgios D. Tsoukalas
    def reject(self):
1659 b6fe8bb8 Giorgos Korfiatis
        if self.is_pending:
1660 b6fe8bb8 Giorgos Korfiatis
            m = _("%s: attempt to reject while is pending") % (self,)
1661 b6fe8bb8 Giorgos Korfiatis
            raise AssertionError(m)
1662 b6fe8bb8 Giorgos Korfiatis
1663 974ee6a6 Sofia Papagiannaki
        state = self.state
1664 65360c65 Georgios D. Tsoukalas
        if state != self.REQUESTED:
1665 5b9e9530 Giorgos Korfiatis
            m = _("%s: attempt to reject in state '%s'") % (self, state)
1666 65360c65 Georgios D. Tsoukalas
            raise AssertionError(m)
1667 65360c65 Georgios D. Tsoukalas
1668 65360c65 Georgios D. Tsoukalas
        # rejected requests don't need sync,
1669 65360c65 Georgios D. Tsoukalas
        # because they were never effected
1670 65360c65 Georgios D. Tsoukalas
        self._set_history_item(reason='REJECT')
1671 0cc22d47 Sofia Papagiannaki
        self.delete()
1672 b8f05f8d Sofia Papagiannaki
1673 b6fe8bb8 Giorgos Korfiatis
    def get_diff_quotas(self, sub_list=None, add_list=None):
1674 d2b32360 Giorgos Korfiatis
        if sub_list is None:
1675 d2b32360 Giorgos Korfiatis
            sub_list = []
1676 d2b32360 Giorgos Korfiatis
1677 d2b32360 Giorgos Korfiatis
        if add_list is None:
1678 d2b32360 Giorgos Korfiatis
            add_list = []
1679 d6fdc91e Georgios D. Tsoukalas
1680 d2b32360 Giorgos Korfiatis
        sub_append = sub_list.append
1681 d2b32360 Giorgos Korfiatis
        add_append = add_list.append
1682 d75c432e Sofia Papagiannaki
        holder = self.person.uuid
1683 d6fdc91e Georgios D. Tsoukalas
1684 d6fdc91e Georgios D. Tsoukalas
        synced_application = self.application
1685 d6fdc91e Georgios D. Tsoukalas
        if synced_application is not None:
1686 5f2e4042 Sofia Papagiannaki
            cur_grants = synced_application.projectresourcegrant_set.all()
1687 d6fdc91e Georgios D. Tsoukalas
            for grant in cur_grants:
1688 d2b32360 Giorgos Korfiatis
                sub_append(QuotaLimits(
1689 d2b32360 Giorgos Korfiatis
                               holder       = holder,
1690 f3e93707 Sofia Papagiannaki
                               resource     = str(grant.resource),
1691 d2b32360 Giorgos Korfiatis
                               capacity     = grant.member_capacity,
1692 d2b32360 Giorgos Korfiatis
                               import_limit = grant.member_import_limit,
1693 d2b32360 Giorgos Korfiatis
                               export_limit = grant.member_export_limit))
1694 d6fdc91e Georgios D. Tsoukalas
1695 b6fe8bb8 Giorgos Korfiatis
        pending_application = self.pending_application
1696 b6fe8bb8 Giorgos Korfiatis
        if pending_application is not None:
1697 b6fe8bb8 Giorgos Korfiatis
            new_grants = pending_application.projectresourcegrant_set.all()
1698 d6fdc91e Georgios D. Tsoukalas
            for new_grant in new_grants:
1699 d2b32360 Giorgos Korfiatis
                add_append(QuotaLimits(
1700 d2b32360 Giorgos Korfiatis
                               holder       = holder,
1701 f3e93707 Sofia Papagiannaki
                               resource     = str(new_grant.resource),
1702 974ee6a6 Sofia Papagiannaki
                               capacity     = new_grant.member_capacity,
1703 974ee6a6 Sofia Papagiannaki
                               import_limit = new_grant.member_import_limit,
1704 974ee6a6 Sofia Papagiannaki
                               export_limit = new_grant.member_export_limit))
1705 d6fdc91e Georgios D. Tsoukalas
1706 d2b32360 Giorgos Korfiatis
        return (sub_list, add_list)
1707 65360c65 Georgios D. Tsoukalas
1708 ee45eb81 Giorgos Korfiatis
    def set_sync(self):
1709 b6fe8bb8 Giorgos Korfiatis
        if not self.is_pending:
1710 b6fe8bb8 Giorgos Korfiatis
            m = _("%s: attempt to sync a non pending membership") % (self,)
1711 b6fe8bb8 Giorgos Korfiatis
            raise AssertionError(m)
1712 b6fe8bb8 Giorgos Korfiatis
1713 ee45eb81 Giorgos Korfiatis
        state = self.state
1714 b6fe8bb8 Giorgos Korfiatis
        if state == self.ACCEPTED:
1715 ee45eb81 Giorgos Korfiatis
            pending_application = self.pending_application
1716 ee45eb81 Giorgos Korfiatis
            if pending_application is None:
1717 ee45eb81 Giorgos Korfiatis
                m = _("%s: attempt to sync an empty pending application") % (
1718 8c7b8bb8 Giorgos Korfiatis
                    self,)
1719 ee45eb81 Giorgos Korfiatis
                raise AssertionError(m)
1720 b6fe8bb8 Giorgos Korfiatis
1721 ee45eb81 Giorgos Korfiatis
            self.application = pending_application
1722 b6fe8bb8 Giorgos Korfiatis
            self.is_active = True
1723 b6fe8bb8 Giorgos Korfiatis
1724 ee45eb81 Giorgos Korfiatis
            self.pending_application = None
1725 ee45eb81 Giorgos Korfiatis
            self.pending_serial = None
1726 ee45eb81 Giorgos Korfiatis
1727 ee45eb81 Giorgos Korfiatis
            # project.application may have changed in the meantime,
1728 ee45eb81 Giorgos Korfiatis
            # in which case we stay PENDING;
1729 ee45eb81 Giorgos Korfiatis
            # we are safe to check due to select_for_update
1730 ee45eb81 Giorgos Korfiatis
            if self.application == self.project.application:
1731 b6fe8bb8 Giorgos Korfiatis
                self.is_pending = False
1732 ee45eb81 Giorgos Korfiatis
            self.save()
1733 b6fe8bb8 Giorgos Korfiatis
1734 b6fe8bb8 Giorgos Korfiatis
        elif state == self.TERMINATED:
1735 5b9e9530 Giorgos Korfiatis
            if self.pending_application:
1736 5b9e9530 Giorgos Korfiatis
                m = _("%s: attempt to sync in state '%s' "
1737 5b9e9530 Giorgos Korfiatis
                      "with a pending application") % (self, state)
1738 5b9e9530 Giorgos Korfiatis
                raise AssertionError(m)
1739 b6fe8bb8 Giorgos Korfiatis
1740 5b9e9530 Giorgos Korfiatis
            self.application = None
1741 5b9e9530 Giorgos Korfiatis
            self.pending_serial = None
1742 b6fe8bb8 Giorgos Korfiatis
            self.is_pending = False
1743 5b9e9530 Giorgos Korfiatis
            self.save()
1744 b6fe8bb8 Giorgos Korfiatis
1745 b6fe8bb8 Giorgos Korfiatis
        elif state == self.REMOVED:
1746 ee45eb81 Giorgos Korfiatis
            self.delete()
1747 b6fe8bb8 Giorgos Korfiatis
1748 ee45eb81 Giorgos Korfiatis
        else:
1749 ee45eb81 Giorgos Korfiatis
            m = _("%s: attempt to sync in state '%s'") % (self, state)
1750 ee45eb81 Giorgos Korfiatis
            raise AssertionError(m)
1751 ee45eb81 Giorgos Korfiatis
1752 49b74233 Georgios D. Tsoukalas
    def reset_sync(self):
1753 b6fe8bb8 Giorgos Korfiatis
        if not self.is_pending:
1754 b6fe8bb8 Giorgos Korfiatis
            m = _("%s: attempt to reset a non pending membership") % (self,)
1755 b6fe8bb8 Giorgos Korfiatis
            raise AssertionError(m)
1756 b6fe8bb8 Giorgos Korfiatis
1757 49b74233 Georgios D. Tsoukalas
        state = self.state
1758 b6fe8bb8 Giorgos Korfiatis
        if state in [self.ACCEPTED, self.TERMINATED, self.REMOVED]:
1759 49b74233 Georgios D. Tsoukalas
            self.pending_application = None
1760 49b74233 Georgios D. Tsoukalas
            self.pending_serial = None
1761 49b74233 Georgios D. Tsoukalas
            self.save()
1762 49b74233 Georgios D. Tsoukalas
        else:
1763 49b74233 Georgios D. Tsoukalas
            m = _("%s: attempt to reset sync in state '%s'") % (self, state)
1764 49b74233 Georgios D. Tsoukalas
            raise AssertionError(m)
1765 49b74233 Georgios D. Tsoukalas
1766 ee45eb81 Giorgos Korfiatis
class Serial(models.Model):
1767 ee45eb81 Giorgos Korfiatis
    serial  =   models.AutoField(primary_key=True)
1768 ee45eb81 Giorgos Korfiatis
1769 ee45eb81 Giorgos Korfiatis
def new_serial():
1770 5200e864 Sofia Papagiannaki
    s = Serial.objects.create()
1771 c70968bd Giorgos Korfiatis
    serial = s.serial
1772 c70968bd Giorgos Korfiatis
    s.delete()
1773 c70968bd Giorgos Korfiatis
    return serial
1774 82d7e9ef Georgios D. Tsoukalas
1775 333f6a72 Sofia Papagiannaki
def sync_finish_serials(serials_to_ack=None):
1776 333f6a72 Sofia Papagiannaki
    if serials_to_ack is None:
1777 333f6a72 Sofia Papagiannaki
        serials_to_ack = qh_query_serials([])
1778 333f6a72 Sofia Papagiannaki
1779 333f6a72 Sofia Papagiannaki
    serials_to_ack = set(serials_to_ack)
1780 d6fdc91e Georgios D. Tsoukalas
    sfu = ProjectMembership.objects.select_for_update()
1781 333f6a72 Sofia Papagiannaki
    memberships = list(sfu.filter(pending_serial__isnull=False))
1782 333f6a72 Sofia Papagiannaki
1783 60ca2f6f Giorgos Korfiatis
    if memberships:
1784 60ca2f6f Giorgos Korfiatis
        for membership in memberships:
1785 60ca2f6f Giorgos Korfiatis
            serial = membership.pending_serial
1786 60ca2f6f Giorgos Korfiatis
            if serial in serials_to_ack:
1787 60ca2f6f Giorgos Korfiatis
                membership.set_sync()
1788 60ca2f6f Giorgos Korfiatis
            else:
1789 60ca2f6f Giorgos Korfiatis
                membership.reset_sync()
1790 60ca2f6f Giorgos Korfiatis
1791 60ca2f6f Giorgos Korfiatis
        transaction.commit()
1792 60ca2f6f Giorgos Korfiatis
1793 ee45eb81 Giorgos Korfiatis
    qh_ack_serials(list(serials_to_ack))
1794 333f6a72 Sofia Papagiannaki
    return len(memberships)
1795 82d7e9ef Georgios D. Tsoukalas
1796 b6fe8bb8 Giorgos Korfiatis
def pre_sync():
1797 b6fe8bb8 Giorgos Korfiatis
    ACCEPTED = ProjectMembership.ACCEPTED
1798 b6fe8bb8 Giorgos Korfiatis
    TERMINATED = ProjectMembership.TERMINATED
1799 b6fe8bb8 Giorgos Korfiatis
    psfu = Project.objects.select_for_update()
1800 b6fe8bb8 Giorgos Korfiatis
1801 b6fe8bb8 Giorgos Korfiatis
    modified = psfu.modified_projects()
1802 b6fe8bb8 Giorgos Korfiatis
    for project in modified:
1803 b6fe8bb8 Giorgos Korfiatis
        objects = project.projectmembership_set.select_for_update()
1804 b6fe8bb8 Giorgos Korfiatis
1805 b6fe8bb8 Giorgos Korfiatis
        memberships = objects.filter(state=ACCEPTED)
1806 b6fe8bb8 Giorgos Korfiatis
        for membership in memberships:
1807 b6fe8bb8 Giorgos Korfiatis
            membership.is_pending = True
1808 b6fe8bb8 Giorgos Korfiatis
            membership.save()
1809 82d7e9ef Georgios D. Tsoukalas
1810 b6fe8bb8 Giorgos Korfiatis
    terminating = psfu.terminating_projects()
1811 b6fe8bb8 Giorgos Korfiatis
    for project in terminating:
1812 b6fe8bb8 Giorgos Korfiatis
        objects = project.projectmembership_set.select_for_update()
1813 b6fe8bb8 Giorgos Korfiatis
1814 b6fe8bb8 Giorgos Korfiatis
        memberships = objects.filter(state=ACCEPTED)
1815 b6fe8bb8 Giorgos Korfiatis
        for membership in memberships:
1816 b6fe8bb8 Giorgos Korfiatis
            membership.is_pending = True
1817 b6fe8bb8 Giorgos Korfiatis
            membership.state = TERMINATED
1818 b6fe8bb8 Giorgos Korfiatis
            membership.save()
1819 b6fe8bb8 Giorgos Korfiatis
1820 b6fe8bb8 Giorgos Korfiatis
def do_sync():
1821 82d7e9ef Georgios D. Tsoukalas
1822 b6fe8bb8 Giorgos Korfiatis
    ACCEPTED = ProjectMembership.ACCEPTED
1823 d6fdc91e Georgios D. Tsoukalas
    objects = ProjectMembership.objects.select_for_update()
1824 82d7e9ef Georgios D. Tsoukalas
1825 d2b32360 Giorgos Korfiatis
    sub_quota, add_quota = [], []
1826 65360c65 Georgios D. Tsoukalas
1827 ee45eb81 Giorgos Korfiatis
    serial = new_serial()
1828 d6fdc91e Georgios D. Tsoukalas
1829 b6fe8bb8 Giorgos Korfiatis
    pending = objects.filter(is_pending=True)
1830 d6fdc91e Georgios D. Tsoukalas
    for membership in pending:
1831 d6fdc91e Georgios D. Tsoukalas
1832 d6fdc91e Georgios D. Tsoukalas
        if membership.pending_application:
1833 d6fdc91e Georgios D. Tsoukalas
            m = "%s: impossible: pending_application is not None (%s)" % (
1834 d6fdc91e Georgios D. Tsoukalas
                membership, membership.pending_application)
1835 d6fdc91e Georgios D. Tsoukalas
            raise AssertionError(m)
1836 d6fdc91e Georgios D. Tsoukalas
        if membership.pending_serial:
1837 d6fdc91e Georgios D. Tsoukalas
            m = "%s: impossible: pending_serial is not None (%s)" % (
1838 d6fdc91e Georgios D. Tsoukalas
                membership, membership.pending_serial)
1839 65360c65 Georgios D. Tsoukalas
            raise AssertionError(m)
1840 b8f05f8d Sofia Papagiannaki
1841 b6fe8bb8 Giorgos Korfiatis
        if membership.state == ACCEPTED:
1842 b6fe8bb8 Giorgos Korfiatis
            membership.pending_application = membership.project.application
1843 d6fdc91e Georgios D. Tsoukalas
1844 d6fdc91e Georgios D. Tsoukalas
        membership.pending_serial = serial
1845 b6fe8bb8 Giorgos Korfiatis
        membership.get_diff_quotas(sub_quota, add_quota)
1846 d6fdc91e Georgios D. Tsoukalas
        membership.save()
1847 d6fdc91e Georgios D. Tsoukalas
1848 d6fdc91e Georgios D. Tsoukalas
    transaction.commit()
1849 ee45eb81 Giorgos Korfiatis
    # ProjectApplication.approve() unblocks here
1850 ee45eb81 Giorgos Korfiatis
    # and can set PENDING an already PENDING membership
1851 ee45eb81 Giorgos Korfiatis
    # which has been scheduled to sync with the old project.application
1852 ee45eb81 Giorgos Korfiatis
    # Need to check in ProjectMembership.set_sync()
1853 ee45eb81 Giorgos Korfiatis
1854 5cfd4acb Sofia Papagiannaki
    r = qh_add_quota(serial, sub_quota, add_quota)
1855 333f6a72 Sofia Papagiannaki
    if r:
1856 333f6a72 Sofia Papagiannaki
        m = "cannot sync serial: %d" % serial
1857 333f6a72 Sofia Papagiannaki
        raise RuntimeError(m)
1858 333f6a72 Sofia Papagiannaki
1859 b6fe8bb8 Giorgos Korfiatis
    return serial
1860 5b9e9530 Giorgos Korfiatis
1861 b6fe8bb8 Giorgos Korfiatis
def post_sync():
1862 5b9e9530 Giorgos Korfiatis
    ACCEPTED = ProjectMembership.ACCEPTED
1863 5b9e9530 Giorgos Korfiatis
    psfu = Project.objects.select_for_update()
1864 5b9e9530 Giorgos Korfiatis
1865 b6fe8bb8 Giorgos Korfiatis
    modified = psfu.modified_projects()
1866 b6fe8bb8 Giorgos Korfiatis
    for project in modified:
1867 5b9e9530 Giorgos Korfiatis
        objects = project.projectmembership_set.select_for_update()
1868 5b9e9530 Giorgos Korfiatis
1869 b6fe8bb8 Giorgos Korfiatis
        memberships = list(objects.filter(state=ACCEPTED, is_pending=True))
1870 b6fe8bb8 Giorgos Korfiatis
        if not memberships:
1871 b6fe8bb8 Giorgos Korfiatis
            project.is_modified = False
1872 b6fe8bb8 Giorgos Korfiatis
            project.save()
1873 5b9e9530 Giorgos Korfiatis
1874 b6fe8bb8 Giorgos Korfiatis
    terminating = psfu.terminating_projects()
1875 b6fe8bb8 Giorgos Korfiatis
    for project in terminating:
1876 5b9e9530 Giorgos Korfiatis
        objects = project.projectmembership_set.select_for_update()
1877 b6fe8bb8 Giorgos Korfiatis
1878 5b9e9530 Giorgos Korfiatis
        memberships = list(objects.filter(Q(state=ACCEPTED) |
1879 b6fe8bb8 Giorgos Korfiatis
                                          Q(is_pending=True)))
1880 5b9e9530 Giorgos Korfiatis
        if not memberships:
1881 b6fe8bb8 Giorgos Korfiatis
            project.deactivate()
1882 5b9e9530 Giorgos Korfiatis
            project.save()
1883 5b9e9530 Giorgos Korfiatis
1884 5b9e9530 Giorgos Korfiatis
    transaction.commit()
1885 5b9e9530 Giorgos Korfiatis
1886 5b9e9530 Giorgos Korfiatis
def sync_projects():
1887 b6fe8bb8 Giorgos Korfiatis
    sync_finish_serials()
1888 b6fe8bb8 Giorgos Korfiatis
    pre_sync()
1889 b6fe8bb8 Giorgos Korfiatis
    serial = do_sync()
1890 b6fe8bb8 Giorgos Korfiatis
    sync_finish_serials([serial])
1891 b6fe8bb8 Giorgos Korfiatis
    post_sync()
1892 d6fdc91e Georgios D. Tsoukalas
1893 d6fdc91e Georgios D. Tsoukalas
def trigger_sync(retries=3, retry_wait=1.0):
1894 14695557 Giorgos Korfiatis
    transaction.commit()
1895 14695557 Giorgos Korfiatis
1896 d6fdc91e Georgios D. Tsoukalas
    cursor = connection.cursor()
1897 d6fdc91e Georgios D. Tsoukalas
    locked = True
1898 d6fdc91e Georgios D. Tsoukalas
    try:
1899 d6fdc91e Georgios D. Tsoukalas
        while 1:
1900 d6fdc91e Georgios D. Tsoukalas
            cursor.execute("SELECT pg_try_advisory_lock(1)")
1901 d6fdc91e Georgios D. Tsoukalas
            r = cursor.fetchone()
1902 d6fdc91e Georgios D. Tsoukalas
            if r is None:
1903 d6fdc91e Georgios D. Tsoukalas
                m = "Impossible"
1904 d6fdc91e Georgios D. Tsoukalas
                raise AssertionError(m)
1905 d6fdc91e Georgios D. Tsoukalas
            locked = r[0]
1906 d6fdc91e Georgios D. Tsoukalas
            if locked:
1907 d6fdc91e Georgios D. Tsoukalas
                break
1908 d6fdc91e Georgios D. Tsoukalas
1909 d6fdc91e Georgios D. Tsoukalas
            retries -= 1
1910 d6fdc91e Georgios D. Tsoukalas
            if retries <= 0:
1911 d6fdc91e Georgios D. Tsoukalas
                return False
1912 d6fdc91e Georgios D. Tsoukalas
            sleep(retry_wait)
1913 d6fdc91e Georgios D. Tsoukalas
1914 d6fdc91e Georgios D. Tsoukalas
        sync_projects()
1915 d6fdc91e Georgios D. Tsoukalas
        return True
1916 d6fdc91e Georgios D. Tsoukalas
1917 d6fdc91e Georgios D. Tsoukalas
    finally:
1918 d6fdc91e Georgios D. Tsoukalas
        if locked:
1919 d6fdc91e Georgios D. Tsoukalas
            cursor.execute("SELECT pg_advisory_unlock(1)")
1920 d6fdc91e Georgios D. Tsoukalas
            cursor.fetchall()
1921 4f22664f Georgios D. Tsoukalas
1922 e1a80257 Sofia Papagiannaki
1923 0cc22d47 Sofia Papagiannaki
class ProjectMembershipHistory(models.Model):
1924 425e2e95 Sofia Papagiannaki
    reasons_list    =   ['ACCEPT', 'REJECT', 'REMOVE']
1925 425e2e95 Sofia Papagiannaki
    reasons         =   dict((k, v) for v, k in enumerate(reasons_list))
1926 425e2e95 Sofia Papagiannaki
1927 02d2519e Giorgos Korfiatis
    person  =   models.CharField(max_length=255)
1928 02d2519e Giorgos Korfiatis
    project =   models.BigIntegerField()
1929 425e2e95 Sofia Papagiannaki
    date    =   models.DateField(default=datetime.now)
1930 425e2e95 Sofia Papagiannaki
    reason  =   models.IntegerField()
1931 425e2e95 Sofia Papagiannaki
    serial  =   models.BigIntegerField()
1932 fc655b6f Kostas Papadimitriou
1933 fcc1e93f Sofia Papagiannaki
### SIGNALS ###
1934 fcc1e93f Sofia Papagiannaki
################
1935 b22de10a Sofia Papagiannaki
1936 ff9290ec Sofia Papagiannaki
def create_astakos_user(u):
1937 ff9290ec Sofia Papagiannaki
    try:
1938 ff9290ec Sofia Papagiannaki
        AstakosUser.objects.get(user_ptr=u.pk)
1939 ff9290ec Sofia Papagiannaki
    except AstakosUser.DoesNotExist:
1940 ff9290ec Sofia Papagiannaki
        extended_user = AstakosUser(user_ptr_id=u.pk)
1941 ff9290ec Sofia Papagiannaki
        extended_user.__dict__.update(u.__dict__)
1942 ff9290ec Sofia Papagiannaki
        extended_user.save()
1943 67be1883 Olga Brani
        if not extended_user.has_auth_provider('local'):
1944 67be1883 Olga Brani
            extended_user.add_auth_provider('local')
1945 fc1e2f02 Sofia Papagiannaki
    except BaseException, e:
1946 fc1e2f02 Sofia Papagiannaki
        logger.exception(e)
1947 ff9290ec Sofia Papagiannaki
1948 5ce3ce4f Sofia Papagiannaki
1949 fc1e2f02 Sofia Papagiannaki
def fix_superusers(sender, **kwargs):
1950 fc1e2f02 Sofia Papagiannaki
    # Associate superusers with AstakosUser
1951 ff9290ec Sofia Papagiannaki
    admins = User.objects.filter(is_superuser=True)
1952 ff9290ec Sofia Papagiannaki
    for u in admins:
1953 ff9290ec Sofia Papagiannaki
        create_astakos_user(u)
1954 bfe23b13 Sofia Papagiannaki
post_syncdb.connect(fix_superusers)
1955 ff9290ec Sofia Papagiannaki
1956 ff9290ec Sofia Papagiannaki
1957 fc1e2f02 Sofia Papagiannaki
def user_post_save(sender, instance, created, **kwargs):
1958 aa4109d4 Sofia Papagiannaki
    if not created:
1959 aa4109d4 Sofia Papagiannaki
        return
1960 fc1e2f02 Sofia Papagiannaki
    create_astakos_user(instance)
1961 bfe23b13 Sofia Papagiannaki
post_save.connect(user_post_save, sender=User)
1962 ff9290ec Sofia Papagiannaki
1963 fc1e2f02 Sofia Papagiannaki
def astakosuser_post_save(sender, instance, created, **kwargs):
1964 21e0fdad Giorgos Korfiatis
    pass
1965 21e0fdad Giorgos Korfiatis
1966 bfe23b13 Sofia Papagiannaki
post_save.connect(astakosuser_post_save, sender=AstakosUser)
1967 fc1e2f02 Sofia Papagiannaki
1968 bd4f356c Sofia Papagiannaki
def resource_post_save(sender, instance, created, **kwargs):
1969 bd4f356c Sofia Papagiannaki
    if not created:
1970 bd4f356c Sofia Papagiannaki
        return
1971 bd4f356c Sofia Papagiannaki
    register_resources((instance,))
1972 bfe23b13 Sofia Papagiannaki
post_save.connect(resource_post_save, sender=Resource)
1973 bfe23b13 Sofia Papagiannaki
1974 bfe23b13 Sofia Papagiannaki
def renew_token(sender, instance, **kwargs):
1975 bfe23b13 Sofia Papagiannaki
    if not instance.auth_token:
1976 bfe23b13 Sofia Papagiannaki
        instance.renew_token()
1977 bf0c6de5 Sofia Papagiannaki
pre_save.connect(renew_token, sender=AstakosUser)
1978 2e90e3ec Kostas Papadimitriou
pre_save.connect(renew_token, sender=Service)