Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / models.py @ 9b32e2fb

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

858 49790d9d Sofia Papagiannaki
        If the key is valid and has not expired, return the ``User``
859 49790d9d Sofia Papagiannaki
        after activating.
860 49790d9d Sofia Papagiannaki

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

863 49790d9d Sofia Papagiannaki
        If the key is valid but the ``User`` is already active,
864 49790d9d Sofia Papagiannaki
        return ``None``.
865 49790d9d Sofia Papagiannaki

866 49790d9d Sofia Papagiannaki
        After successful email change the activation record is deleted.
867 49790d9d Sofia Papagiannaki

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

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