Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / models.py @ 222a4f6a

History | View | Annotate | Download (74 kB)

1 f557d10a Giorgos Korfiatis
# Copyright 2011, 2012, 2013 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 b98e1df0 Sofia Papagiannaki
import math
39 a989b48e Giorgos Korfiatis
import copy
40 64cd4730 Antony Chazapis
41 57f5ea5c Giorgos Korfiatis
from time import asctime
42 64cd4730 Antony Chazapis
from datetime import datetime, timedelta
43 64cd4730 Antony Chazapis
from base64 import b64encode
44 ef20ea07 Sofia Papagiannaki
from urlparse import urlparse
45 d2633501 Kostas Papadimitriou
from urllib import quote
46 8f5a3a06 Sofia Papagiannaki
from random import randint
47 65360c65 Georgios D. Tsoukalas
from collections import defaultdict, namedtuple
48 64cd4730 Antony Chazapis
49 57f5ea5c Giorgos Korfiatis
from django.db import models, IntegrityError, transaction
50 9a06d96f Olga Brani
from django.contrib.auth.models import User, UserManager, Group, Permission
51 0a569195 Sofia Papagiannaki
from django.utils.translation import ugettext as _
52 0a569195 Sofia Papagiannaki
from django.core.exceptions import ValidationError
53 c0b26605 Sofia Papagiannaki
from django.db.models.signals import (
54 73fbaec4 Sofia Papagiannaki
    pre_save, post_save, post_syncdb, post_delete)
55 9a06d96f Olga Brani
from django.contrib.contenttypes.models import ContentType
56 9a06d96f Olga Brani
57 fc1e2f02 Sofia Papagiannaki
from django.dispatch import Signal
58 4391de3d Giorgos Korfiatis
from django.db.models import Q, Max
59 d2633501 Kostas Papadimitriou
from django.core.urlresolvers import reverse
60 d2633501 Kostas Papadimitriou
from django.utils.http import int_to_base36
61 d2633501 Kostas Papadimitriou
from django.contrib.auth.tokens import default_token_generator
62 8f8c43b2 Sofia Papagiannaki
from django.conf import settings
63 bf0c6de5 Sofia Papagiannaki
from django.utils.importlib import import_module
64 c4b1a172 Kostas Papadimitriou
from django.utils.safestring import mark_safe
65 d2633501 Kostas Papadimitriou
from django.core.validators import email_re
66 73fbaec4 Sofia Papagiannaki
from django.core.exceptions import PermissionDenied, ObjectDoesNotExist
67 64cd4730 Antony Chazapis
68 e1a80257 Sofia Papagiannaki
from astakos.im.settings import (
69 e1a80257 Sofia Papagiannaki
    DEFAULT_USER_LEVEL, INVITATIONS_PER_LEVEL,
70 c7b82fdc Sofia Papagiannaki
    AUTH_TOKEN_DURATION, EMAILCHANGE_ACTIVATION_DAYS, LOGGING_LEVEL,
71 4161cb70 Giorgos Korfiatis
    SITENAME, MODERATION_ENABLED,
72 8e1a5af5 Georgios D. Tsoukalas
    PROJECT_MEMBER_JOIN_POLICIES, PROJECT_MEMBER_LEAVE_POLICIES, PROJECT_ADMINS)
73 c4b1a172 Kostas Papadimitriou
from astakos.im import settings as astakos_settings
74 9d20fe23 Kostas Papadimitriou
from astakos.im import auth_providers as auth
75 9c01d9d1 Sofia Papagiannaki
76 ae497612 Olga Brani
import astakos.im.messages as astakos_messages
77 222a4f6a Giorgos Korfiatis
from snf_django.lib.db.managers import ForUpdateManager
78 0156e40c Kostas Papadimitriou
from synnefo.lib.ordereddict import OrderedDict
79 64cd4730 Antony Chazapis
80 c11dc0ce Giorgos Korfiatis
from synnefo.lib.db.intdecimalfield import intDecimalField
81 b6eaca30 Giorgos Korfiatis
from synnefo.util.text import uenc, udec
82 75380308 Kostas Papadimitriou
from astakos.im import presentation
83 c11dc0ce Giorgos Korfiatis
84 18ffbee1 Sofia Papagiannaki
logger = logging.getLogger(__name__)
85 18ffbee1 Sofia Papagiannaki
86 9a06d96f Olga Brani
DEFAULT_CONTENT_TYPE = None
87 e65c21df Georgios D. Tsoukalas
_content_type = None
88 e65c21df Georgios D. Tsoukalas
89 0156e40c Kostas Papadimitriou
90 e65c21df Georgios D. Tsoukalas
def get_content_type():
91 e65c21df Georgios D. Tsoukalas
    global _content_type
92 e65c21df Georgios D. Tsoukalas
    if _content_type is not None:
93 e65c21df Georgios D. Tsoukalas
        return _content_type
94 e65c21df Georgios D. Tsoukalas
95 e65c21df Georgios D. Tsoukalas
    try:
96 0156e40c Kostas Papadimitriou
        content_type = ContentType.objects.get(app_label='im',
97 0156e40c Kostas Papadimitriou
                                               model='astakosuser')
98 e65c21df Georgios D. Tsoukalas
    except:
99 e65c21df Georgios D. Tsoukalas
        content_type = DEFAULT_CONTENT_TYPE
100 e65c21df Georgios D. Tsoukalas
    _content_type = content_type
101 e65c21df Georgios D. Tsoukalas
    return content_type
102 9a06d96f Olga Brani
103 9ee0c6a2 Sofia Papagiannaki
inf = float('inf')
104 5ce3ce4f Sofia Papagiannaki
105 0156e40c Kostas Papadimitriou
106 0156e40c Kostas Papadimitriou
def dict_merge(a, b):
107 0156e40c Kostas Papadimitriou
    """
108 0156e40c Kostas Papadimitriou
    http://www.xormedia.com/recursively-merge-dictionaries-in-python/
109 0156e40c Kostas Papadimitriou
    """
110 0156e40c Kostas Papadimitriou
    if not isinstance(b, dict):
111 0156e40c Kostas Papadimitriou
        return b
112 0156e40c Kostas Papadimitriou
    result = copy.deepcopy(a)
113 0156e40c Kostas Papadimitriou
    for k, v in b.iteritems():
114 0156e40c Kostas Papadimitriou
        if k in result and isinstance(result[k], dict):
115 0156e40c Kostas Papadimitriou
                result[k] = dict_merge(result[k], v)
116 0156e40c Kostas Papadimitriou
        else:
117 0156e40c Kostas Papadimitriou
            result[k] = copy.deepcopy(v)
118 0156e40c Kostas Papadimitriou
    return result
119 0156e40c Kostas Papadimitriou
120 0156e40c Kostas Papadimitriou
121 8e45d6fd Sofia Papagiannaki
class Service(models.Model):
122 0156e40c Kostas Papadimitriou
    name = models.CharField(_('Name'), max_length=255, unique=True,
123 0156e40c Kostas Papadimitriou
                            db_index=True)
124 54bac253 Kostas Papadimitriou
    api_url = models.CharField(_('Service API url'), max_length=255, null=True)
125 e1a80257 Sofia Papagiannaki
    auth_token = models.CharField(_('Authentication Token'), max_length=32,
126 8e45d6fd Sofia Papagiannaki
                                  null=True, blank=True)
127 0156e40c Kostas Papadimitriou
    auth_token_created = models.DateTimeField(_('Token creation date'),
128 0156e40c Kostas Papadimitriou
                                              null=True)
129 0156e40c Kostas Papadimitriou
    auth_token_expires = models.DateTimeField(_('Token expiration date'),
130 0156e40c Kostas Papadimitriou
                                              null=True)
131 5ce3ce4f Sofia Papagiannaki
132 08494423 Sofia Papagiannaki
    def renew_token(self, expiration_date=None):
133 8e45d6fd Sofia Papagiannaki
        md5 = hashlib.md5()
134 8e45d6fd Sofia Papagiannaki
        md5.update(self.name.encode('ascii', 'ignore'))
135 0156e40c Kostas Papadimitriou
        md5.update(self.api_url.encode('ascii', 'ignore'))
136 8e45d6fd Sofia Papagiannaki
        md5.update(asctime())
137 8e45d6fd Sofia Papagiannaki
138 8e45d6fd Sofia Papagiannaki
        self.auth_token = b64encode(md5.digest())
139 8e45d6fd Sofia Papagiannaki
        self.auth_token_created = datetime.now()
140 08494423 Sofia Papagiannaki
        if expiration_date:
141 08494423 Sofia Papagiannaki
            self.auth_token_expires = expiration_date
142 08494423 Sofia Papagiannaki
        else:
143 08494423 Sofia Papagiannaki
            self.auth_token_expires = None
144 5ce3ce4f Sofia Papagiannaki
145 8e45d6fd Sofia Papagiannaki
    def __str__(self):
146 8e45d6fd Sofia Papagiannaki
        return self.name
147 8e45d6fd Sofia Papagiannaki
148 0156e40c Kostas Papadimitriou
    @classmethod
149 0156e40c Kostas Papadimitriou
    def catalog(cls, orderfor=None):
150 0156e40c Kostas Papadimitriou
        catalog = {}
151 0156e40c Kostas Papadimitriou
        services = list(cls.objects.all())
152 0156e40c Kostas Papadimitriou
        metadata = presentation.SERVICES
153 0156e40c Kostas Papadimitriou
        metadata = dict_merge(presentation.SERVICES,
154 0156e40c Kostas Papadimitriou
                              astakos_settings.SERVICES_META)
155 0156e40c Kostas Papadimitriou
        for service in services:
156 0156e40c Kostas Papadimitriou
            if service.name in metadata:
157 0156e40c Kostas Papadimitriou
                d = {'api_url': service.api_url, 'name': service.name}
158 0156e40c Kostas Papadimitriou
                metadata[service.name].update(d)
159 0156e40c Kostas Papadimitriou
160 0156e40c Kostas Papadimitriou
        def service_by_order(s):
161 0156e40c Kostas Papadimitriou
            return s[1].get('order')
162 0156e40c Kostas Papadimitriou
163 0156e40c Kostas Papadimitriou
        def service_by_dashbaord_order(s):
164 0156e40c Kostas Papadimitriou
            return s[1].get('dashboard').get('order')
165 0156e40c Kostas Papadimitriou
166 0156e40c Kostas Papadimitriou
        for service, info in metadata.iteritems():
167 0156e40c Kostas Papadimitriou
            default_meta = presentation.service_defaults(service)
168 0156e40c Kostas Papadimitriou
            base_meta = metadata.get(service, {})
169 0156e40c Kostas Papadimitriou
            settings_meta = astakos_settings.SERVICES_META.get(service, {})
170 0156e40c Kostas Papadimitriou
            service_meta = dict_merge(default_meta, base_meta)
171 0156e40c Kostas Papadimitriou
            meta = dict_merge(service_meta, settings_meta)
172 0156e40c Kostas Papadimitriou
            catalog[service] = meta
173 0156e40c Kostas Papadimitriou
174 0156e40c Kostas Papadimitriou
        order_key = service_by_order
175 0156e40c Kostas Papadimitriou
        if orderfor == 'dashboard':
176 0156e40c Kostas Papadimitriou
            order_key = service_by_dashbaord_order
177 0156e40c Kostas Papadimitriou
178 0156e40c Kostas Papadimitriou
        ordered_catalog = OrderedDict(sorted(catalog.iteritems(),
179 0156e40c Kostas Papadimitriou
                                             key=order_key))
180 0156e40c Kostas Papadimitriou
        return ordered_catalog
181 0156e40c Kostas Papadimitriou
182 5ce3ce4f Sofia Papagiannaki
183 8bc397e8 Sofia Papagiannaki
_presentation_data = {}
184 8bc397e8 Sofia Papagiannaki
def get_presentation(resource):
185 8bc397e8 Sofia Papagiannaki
    global _presentation_data
186 75380308 Kostas Papadimitriou
    resource_presentation = _presentation_data.get(resource, {})
187 75380308 Kostas Papadimitriou
    if not resource_presentation:
188 75380308 Kostas Papadimitriou
        resources_presentation = presentation.RESOURCES.get('resources', {})
189 75380308 Kostas Papadimitriou
        resource_presentation = resources_presentation.get(resource, {})
190 75380308 Kostas Papadimitriou
        _presentation_data[resource] = resource_presentation
191 75380308 Kostas Papadimitriou
    return resource_presentation
192 5ce3ce4f Sofia Papagiannaki
193 8e45d6fd Sofia Papagiannaki
class Resource(models.Model):
194 34d3883a Giorgos Korfiatis
    name = models.CharField(_('Name'), max_length=255, unique=True)
195 e1a80257 Sofia Papagiannaki
    desc = models.TextField(_('Description'), null=True)
196 26551b92 Kostas Papadimitriou
    service = models.CharField(_('Service identifier'), max_length=255,
197 26551b92 Kostas Papadimitriou
                               null=True)
198 26551b92 Kostas Papadimitriou
    unit = models.CharField(_('Unit'), null=True, max_length=255)
199 0514bcc7 Giorgos Korfiatis
    uplimit = intDecimalField(default=0)
200 425e2e95 Sofia Papagiannaki
201 b1ea24f3 Giorgos Korfiatis
    objects = ForUpdateManager()
202 b1ea24f3 Giorgos Korfiatis
203 8e45d6fd Sofia Papagiannaki
    def __str__(self):
204 34d3883a Giorgos Korfiatis
        return self.name
205 8e45d6fd Sofia Papagiannaki
206 0514bcc7 Giorgos Korfiatis
    def full_name(self):
207 0514bcc7 Giorgos Korfiatis
        return str(self)
208 0514bcc7 Giorgos Korfiatis
209 1028e568 Giorgos Korfiatis
    def get_info(self):
210 1028e568 Giorgos Korfiatis
        return {'service': str(self.service),
211 1028e568 Giorgos Korfiatis
                'description': self.desc,
212 1028e568 Giorgos Korfiatis
                'unit': self.unit,
213 1028e568 Giorgos Korfiatis
                }
214 1028e568 Giorgos Korfiatis
215 8bc397e8 Sofia Papagiannaki
    @property
216 26551b92 Kostas Papadimitriou
    def group(self):
217 26551b92 Kostas Papadimitriou
        default = self.name
218 26551b92 Kostas Papadimitriou
        return get_presentation(str(self)).get('group', default)
219 26551b92 Kostas Papadimitriou
220 26551b92 Kostas Papadimitriou
    @property
221 8bc397e8 Sofia Papagiannaki
    def help_text(self):
222 26551b92 Kostas Papadimitriou
        default = "%s resource" % self.name
223 26551b92 Kostas Papadimitriou
        return get_presentation(str(self)).get('help_text', default)
224 3c7528c9 Kostas Papadimitriou
225 8bc397e8 Sofia Papagiannaki
    @property
226 8bc397e8 Sofia Papagiannaki
    def help_text_input_each(self):
227 26551b92 Kostas Papadimitriou
        default = "%s resource" % self.name
228 26551b92 Kostas Papadimitriou
        return get_presentation(str(self)).get('help_text_input_each', default)
229 8bc397e8 Sofia Papagiannaki
230 8bc397e8 Sofia Papagiannaki
    @property
231 8bc397e8 Sofia Papagiannaki
    def is_abbreviation(self):
232 8bc397e8 Sofia Papagiannaki
        return get_presentation(str(self)).get('is_abbreviation', False)
233 8bc397e8 Sofia Papagiannaki
234 8bc397e8 Sofia Papagiannaki
    @property
235 8bc397e8 Sofia Papagiannaki
    def report_desc(self):
236 26551b92 Kostas Papadimitriou
        default = "%s resource" % self.name
237 26551b92 Kostas Papadimitriou
        return get_presentation(str(self)).get('report_desc', default)
238 8bc397e8 Sofia Papagiannaki
239 8bc397e8 Sofia Papagiannaki
    @property
240 8bc397e8 Sofia Papagiannaki
    def placeholder(self):
241 26551b92 Kostas Papadimitriou
        return get_presentation(str(self)).get('placeholder', self.unit)
242 8bc397e8 Sofia Papagiannaki
243 8bc397e8 Sofia Papagiannaki
    @property
244 8bc397e8 Sofia Papagiannaki
    def verbose_name(self):
245 26551b92 Kostas Papadimitriou
        return get_presentation(str(self)).get('verbose_name', self.name)
246 8bc397e8 Sofia Papagiannaki
247 b98e1df0 Sofia Papagiannaki
    @property
248 b98e1df0 Sofia Papagiannaki
    def display_name(self):
249 b98e1df0 Sofia Papagiannaki
        name = self.verbose_name
250 b98e1df0 Sofia Papagiannaki
        if self.is_abbreviation:
251 b98e1df0 Sofia Papagiannaki
            name = name.upper()
252 b98e1df0 Sofia Papagiannaki
        return name
253 b98e1df0 Sofia Papagiannaki
254 b98e1df0 Sofia Papagiannaki
    @property
255 b98e1df0 Sofia Papagiannaki
    def pluralized_display_name(self):
256 b98e1df0 Sofia Papagiannaki
        if not self.unit:
257 b98e1df0 Sofia Papagiannaki
            return '%ss' % self.display_name
258 b98e1df0 Sofia Papagiannaki
        return self.display_name
259 b98e1df0 Sofia Papagiannaki
260 0514bcc7 Giorgos Korfiatis
def get_resource_names():
261 0514bcc7 Giorgos Korfiatis
    _RESOURCE_NAMES = []
262 a989b48e Giorgos Korfiatis
    resources = Resource.objects.select_related('service').all()
263 0514bcc7 Giorgos Korfiatis
    _RESOURCE_NAMES = [resource.full_name() for resource in resources]
264 0514bcc7 Giorgos Korfiatis
    return _RESOURCE_NAMES
265 d2633501 Kostas Papadimitriou
266 43332a76 Kostas Papadimitriou
267 6b9a334b Sofia Papagiannaki
class AstakosUserManager(UserManager):
268 d2633501 Kostas Papadimitriou
269 d2633501 Kostas Papadimitriou
    def get_auth_provider_user(self, provider, **kwargs):
270 d2633501 Kostas Papadimitriou
        """
271 d2633501 Kostas Papadimitriou
        Retrieve AstakosUser instance associated with the specified third party
272 d2633501 Kostas Papadimitriou
        id.
273 d2633501 Kostas Papadimitriou
        """
274 d2633501 Kostas Papadimitriou
        kwargs = dict(map(lambda x: ('auth_providers__%s' % x[0], x[1]),
275 d2633501 Kostas Papadimitriou
                          kwargs.iteritems()))
276 d2633501 Kostas Papadimitriou
        return self.get(auth_providers__module=provider, **kwargs)
277 d2633501 Kostas Papadimitriou
278 c630fee6 Kostas Papadimitriou
    def get_by_email(self, email):
279 c630fee6 Kostas Papadimitriou
        return self.get(email=email)
280 c630fee6 Kostas Papadimitriou
281 e5966bd9 Kostas Papadimitriou
    def get_by_identifier(self, email_or_username, **kwargs):
282 e5966bd9 Kostas Papadimitriou
        try:
283 e5966bd9 Kostas Papadimitriou
            return self.get(email__iexact=email_or_username, **kwargs)
284 e5966bd9 Kostas Papadimitriou
        except AstakosUser.DoesNotExist:
285 e5966bd9 Kostas Papadimitriou
            return self.get(username__iexact=email_or_username, **kwargs)
286 e5966bd9 Kostas Papadimitriou
287 e5966bd9 Kostas Papadimitriou
    def user_exists(self, email_or_username, **kwargs):
288 e5966bd9 Kostas Papadimitriou
        qemail = Q(email__iexact=email_or_username)
289 e5966bd9 Kostas Papadimitriou
        qusername = Q(username__iexact=email_or_username)
290 43332a76 Kostas Papadimitriou
        qextra = Q(**kwargs)
291 43332a76 Kostas Papadimitriou
        return self.filter((qemail | qusername) & qextra).exists()
292 43332a76 Kostas Papadimitriou
293 43332a76 Kostas Papadimitriou
    def verified_user_exists(self, email_or_username):
294 43332a76 Kostas Papadimitriou
        return self.user_exists(email_or_username, email_verified=True)
295 43332a76 Kostas Papadimitriou
296 43332a76 Kostas Papadimitriou
    def verified(self):
297 43332a76 Kostas Papadimitriou
        return self.filter(email_verified=True)
298 e5966bd9 Kostas Papadimitriou
299 890c2065 Sofia Papagiannaki
    def uuid_catalog(self, l=None):
300 890c2065 Sofia Papagiannaki
        """
301 890c2065 Sofia Papagiannaki
        Returns a uuid to username mapping for the uuids appearing in l.
302 890c2065 Sofia Papagiannaki
        If l is None returns the mapping for all existing users.
303 890c2065 Sofia Papagiannaki
        """
304 890c2065 Sofia Papagiannaki
        q = self.filter(uuid__in=l) if l != None else self
305 890c2065 Sofia Papagiannaki
        return dict(q.values_list('uuid', 'username'))
306 890c2065 Sofia Papagiannaki
307 890c2065 Sofia Papagiannaki
    def displayname_catalog(self, l=None):
308 890c2065 Sofia Papagiannaki
        """
309 890c2065 Sofia Papagiannaki
        Returns a username to uuid mapping for the usernames appearing in l.
310 890c2065 Sofia Papagiannaki
        If l is None returns the mapping for all existing users.
311 890c2065 Sofia Papagiannaki
        """
312 ea05c568 Sofia Papagiannaki
        if l is not None:
313 ea05c568 Sofia Papagiannaki
            lmap = dict((x.lower(), x) for x in l)
314 ea05c568 Sofia Papagiannaki
            q = self.filter(username__in=lmap.keys())
315 ea05c568 Sofia Papagiannaki
            values = ((lmap[n], u) for n, u in q.values_list('username', 'uuid'))
316 ea05c568 Sofia Papagiannaki
        else:
317 ea05c568 Sofia Papagiannaki
            q = self
318 ea05c568 Sofia Papagiannaki
            values = self.values_list('username', 'uuid')
319 ea05c568 Sofia Papagiannaki
        return dict(values)
320 890c2065 Sofia Papagiannaki
321 890c2065 Sofia Papagiannaki
322 e5966bd9 Kostas Papadimitriou
323 0905ccd2 Sofia Papagiannaki
class AstakosUser(User):
324 890b0eaf Sofia Papagiannaki
    """
325 890b0eaf Sofia Papagiannaki
    Extends ``django.contrib.auth.models.User`` by defining additional fields.
326 890b0eaf Sofia Papagiannaki
    """
327 e1a80257 Sofia Papagiannaki
    affiliation = models.CharField(_('Affiliation'), max_length=255, blank=True,
328 d2633501 Kostas Papadimitriou
                                   null=True)
329 d2633501 Kostas Papadimitriou
330 d2633501 Kostas Papadimitriou
    # DEPRECATED FIELDS: provider, third_party_identifier moved in
331 d2633501 Kostas Papadimitriou
    #                    AstakosUserProvider model.
332 e1a80257 Sofia Papagiannaki
    provider = models.CharField(_('Provider'), max_length=255, blank=True,
333 d2633501 Kostas Papadimitriou
                                null=True)
334 d2633501 Kostas Papadimitriou
    # ex. screen_name for twitter, eppn for shibboleth
335 e1a80257 Sofia Papagiannaki
    third_party_identifier = models.CharField(_('Third-party identifier'),
336 d2633501 Kostas Papadimitriou
                                              max_length=255, null=True,
337 d2633501 Kostas Papadimitriou
                                              blank=True)
338 d2633501 Kostas Papadimitriou
339 6c736ed7 Kostas Papadimitriou
340 64cd4730 Antony Chazapis
    #for invitations
341 92defad4 Sofia Papagiannaki
    user_level = DEFAULT_USER_LEVEL
342 e1a80257 Sofia Papagiannaki
    level = models.IntegerField(_('Inviter level'), default=user_level)
343 5ce3ce4f Sofia Papagiannaki
    invitations = models.IntegerField(
344 e1a80257 Sofia Papagiannaki
        _('Invitations left'), default=INVITATIONS_PER_LEVEL.get(user_level, 0))
345 6c736ed7 Kostas Papadimitriou
346 9d20fe23 Kostas Papadimitriou
    auth_token = models.CharField(_('Authentication Token'),
347 d6ea9b3d Olga Brani
                                  max_length=32,
348 9d20fe23 Kostas Papadimitriou
                                  null=True,
349 9d20fe23 Kostas Papadimitriou
                                  blank=True,
350 c9a6e558 Constantinos Venetsanopoulos
                                  help_text = _('Renew your authentication '
351 c9a6e558 Constantinos Venetsanopoulos
                                                'token. Make sure to set the new '
352 c9a6e558 Constantinos Venetsanopoulos
                                                'token in any client you may be '
353 c9a6e558 Constantinos Venetsanopoulos
                                                'using, to preserve its '
354 c9a6e558 Constantinos Venetsanopoulos
                                                'functionality.'))
355 9d20fe23 Kostas Papadimitriou
    auth_token_created = models.DateTimeField(_('Token creation date'),
356 d6ea9b3d Olga Brani
                                              null=True)
357 5ce3ce4f Sofia Papagiannaki
    auth_token_expires = models.DateTimeField(
358 e1a80257 Sofia Papagiannaki
        _('Token expiration date'), null=True)
359 6c736ed7 Kostas Papadimitriou
360 e1a80257 Sofia Papagiannaki
    updated = models.DateTimeField(_('Update date'))
361 e1a80257 Sofia Papagiannaki
    is_verified = models.BooleanField(_('Is verified?'), default=False)
362 6c736ed7 Kostas Papadimitriou
363 e1a80257 Sofia Papagiannaki
    email_verified = models.BooleanField(_('Email verified?'), default=False)
364 6c736ed7 Kostas Papadimitriou
365 e1a80257 Sofia Papagiannaki
    has_credits = models.BooleanField(_('Has credits?'), default=False)
366 5ce3ce4f Sofia Papagiannaki
    has_signed_terms = models.BooleanField(
367 e1a80257 Sofia Papagiannaki
        _('I agree with the terms'), default=False)
368 5ce3ce4f Sofia Papagiannaki
    date_signed_terms = models.DateTimeField(
369 e1a80257 Sofia Papagiannaki
        _('Signed terms date'), null=True, blank=True)
370 5ce3ce4f Sofia Papagiannaki
371 5ce3ce4f Sofia Papagiannaki
    activation_sent = models.DateTimeField(
372 e1a80257 Sofia Papagiannaki
        _('Activation sent data'), null=True, blank=True)
373 5ce3ce4f Sofia Papagiannaki
374 5ce3ce4f Sofia Papagiannaki
    policy = models.ManyToManyField(
375 5ce3ce4f Sofia Papagiannaki
        Resource, null=True, through='AstakosUserQuota')
376 5ce3ce4f Sofia Papagiannaki
377 836a0fb0 Kostas Papadimitriou
    uuid = models.CharField(max_length=255, null=True, blank=False, unique=True)
378 836a0fb0 Kostas Papadimitriou
379 18ffbee1 Sofia Papagiannaki
    __has_signed_terms = False
380 e1a80257 Sofia Papagiannaki
    disturbed_quota = models.BooleanField(_('Needs quotaholder syncing'),
381 9a06d96f Olga Brani
                                           default=False, db_index=True)
382 d2633501 Kostas Papadimitriou
383 d2633501 Kostas Papadimitriou
    objects = AstakosUserManager()
384 fbaa4f3c Kostas Papadimitriou
385 8cbea11d Giorgos Korfiatis
    forupdate = ForUpdateManager()
386 8cbea11d Giorgos Korfiatis
387 18ffbee1 Sofia Papagiannaki
    def __init__(self, *args, **kwargs):
388 18ffbee1 Sofia Papagiannaki
        super(AstakosUser, self).__init__(*args, **kwargs)
389 18ffbee1 Sofia Papagiannaki
        self.__has_signed_terms = self.has_signed_terms
390 a3637508 Sofia Papagiannaki
        if not self.id:
391 18ffbee1 Sofia Papagiannaki
            self.is_active = False
392 5ce3ce4f Sofia Papagiannaki
393 0905ccd2 Sofia Papagiannaki
    @property
394 0905ccd2 Sofia Papagiannaki
    def realname(self):
395 5ce3ce4f Sofia Papagiannaki
        return '%s %s' % (self.first_name, self.last_name)
396 6c736ed7 Kostas Papadimitriou
397 5df4c364 Kostas Papadimitriou
    @property
398 5df4c364 Kostas Papadimitriou
    def log_display(self):
399 5df4c364 Kostas Papadimitriou
        """
400 5df4c364 Kostas Papadimitriou
        Should be used in all logger.* calls that refer to a user so that
401 5df4c364 Kostas Papadimitriou
        user display is consistent across log entries.
402 5df4c364 Kostas Papadimitriou
        """
403 5d5ce247 Kostas Papadimitriou
        return '%s::%s' % (self.uuid, self.email)
404 5df4c364 Kostas Papadimitriou
405 0905ccd2 Sofia Papagiannaki
    @realname.setter
406 0905ccd2 Sofia Papagiannaki
    def realname(self, value):
407 0905ccd2 Sofia Papagiannaki
        parts = value.split(' ')
408 0905ccd2 Sofia Papagiannaki
        if len(parts) == 2:
409 0905ccd2 Sofia Papagiannaki
            self.first_name = parts[0]
410 0905ccd2 Sofia Papagiannaki
            self.last_name = parts[1]
411 0905ccd2 Sofia Papagiannaki
        else:
412 0905ccd2 Sofia Papagiannaki
            self.last_name = parts[0]
413 6c736ed7 Kostas Papadimitriou
414 9a06d96f Olga Brani
    def add_permission(self, pname):
415 9a06d96f Olga Brani
        if self.has_perm(pname):
416 9a06d96f Olga Brani
            return
417 e65c21df Georgios D. Tsoukalas
        p, created = Permission.objects.get_or_create(
418 e65c21df Georgios D. Tsoukalas
                                    codename=pname,
419 e65c21df Georgios D. Tsoukalas
                                    name=pname.capitalize(),
420 e65c21df Georgios D. Tsoukalas
                                    content_type=get_content_type())
421 9a06d96f Olga Brani
        self.user_permissions.add(p)
422 9a06d96f Olga Brani
423 9a06d96f Olga Brani
    def remove_permission(self, pname):
424 9a06d96f Olga Brani
        if self.has_perm(pname):
425 9a06d96f Olga Brani
            return
426 9a06d96f Olga Brani
        p = Permission.objects.get(codename=pname,
427 e65c21df Georgios D. Tsoukalas
                                   content_type=get_content_type())
428 9a06d96f Olga Brani
        self.user_permissions.remove(p)
429 9a06d96f Olga Brani
430 8e1a5af5 Georgios D. Tsoukalas
    def is_project_admin(self, application_id=None):
431 8e1a5af5 Georgios D. Tsoukalas
        return self.uuid in PROJECT_ADMINS
432 8e1a5af5 Georgios D. Tsoukalas
433 64cd4730 Antony Chazapis
    @property
434 64cd4730 Antony Chazapis
    def invitation(self):
435 64cd4730 Antony Chazapis
        try:
436 9fb8e808 Sofia Papagiannaki
            return Invitation.objects.get(username=self.email)
437 64cd4730 Antony Chazapis
        except Invitation.DoesNotExist:
438 64cd4730 Antony Chazapis
            return None
439 6c736ed7 Kostas Papadimitriou
440 9a06d96f Olga Brani
    @property
441 9a06d96f Olga Brani
    def policies(self):
442 9a06d96f Olga Brani
        return self.astakosuserquota_set.select_related().all()
443 9a06d96f Olga Brani
444 9a06d96f Olga Brani
    @policies.setter
445 9a06d96f Olga Brani
    def policies(self, policies):
446 9a06d96f Olga Brani
        for p in policies:
447 6c997921 Sofia Papagiannaki
            p.setdefault('resource', '')
448 6c997921 Sofia Papagiannaki
            p.setdefault('capacity', 0)
449 6c997921 Sofia Papagiannaki
            p.setdefault('update', True)
450 6c997921 Sofia Papagiannaki
            self.add_resource_policy(**p)
451 6c997921 Sofia Papagiannaki
452 6c997921 Sofia Papagiannaki
    def add_resource_policy(
453 e496f888 Giorgos Korfiatis
            self, resource, capacity,
454 14a58548 Giorgos Korfiatis
            update=True):
455 9a06d96f Olga Brani
        """Raises ObjectDoesNotExist, IntegrityError"""
456 26551b92 Kostas Papadimitriou
        resource = Resource.objects.get(name=resource)
457 9a06d96f Olga Brani
        if update:
458 6c997921 Sofia Papagiannaki
            AstakosUserQuota.objects.update_or_create(
459 6c997921 Sofia Papagiannaki
                user=self, resource=resource, defaults={
460 6c997921 Sofia Papagiannaki
                    'capacity':capacity,
461 14a58548 Giorgos Korfiatis
                    })
462 9a06d96f Olga Brani
        else:
463 9a06d96f Olga Brani
            q = self.astakosuserquota_set
464 6c997921 Sofia Papagiannaki
            q.create(
465 2ec5a35c Giorgos Korfiatis
                resource=resource, capacity=capacity,
466 14a58548 Giorgos Korfiatis
                )
467 9a06d96f Olga Brani
468 8d1636b5 Giorgos Korfiatis
    def get_resource_policy(self, resource):
469 26551b92 Kostas Papadimitriou
        resource = Resource.objects.get(name=resource)
470 88f4d3b3 Georgios D. Tsoukalas
        default_capacity = resource.uplimit
471 8d1636b5 Giorgos Korfiatis
        try:
472 88f4d3b3 Georgios D. Tsoukalas
            policy = AstakosUserQuota.objects.get(user=self, resource=resource)
473 88f4d3b3 Georgios D. Tsoukalas
            return policy, default_capacity
474 8d1636b5 Giorgos Korfiatis
        except AstakosUserQuota.DoesNotExist:
475 88f4d3b3 Georgios D. Tsoukalas
            return None, default_capacity
476 8d1636b5 Giorgos Korfiatis
477 6c997921 Sofia Papagiannaki
    def remove_resource_policy(self, service, resource):
478 9a06d96f Olga Brani
        """Raises ObjectDoesNotExist, IntegrityError"""
479 26551b92 Kostas Papadimitriou
        resource = Resource.objects.get(name=resource)
480 9a06d96f Olga Brani
        q = self.policies.get(resource=resource).delete()
481 9a06d96f Olga Brani
482 836a0fb0 Kostas Papadimitriou
    def update_uuid(self):
483 836a0fb0 Kostas Papadimitriou
        while not self.uuid:
484 836a0fb0 Kostas Papadimitriou
            uuid_val =  str(uuid.uuid4())
485 836a0fb0 Kostas Papadimitriou
            try:
486 836a0fb0 Kostas Papadimitriou
                AstakosUser.objects.get(uuid=uuid_val)
487 836a0fb0 Kostas Papadimitriou
            except AstakosUser.DoesNotExist, e:
488 836a0fb0 Kostas Papadimitriou
                self.uuid = uuid_val
489 836a0fb0 Kostas Papadimitriou
        return self.uuid
490 836a0fb0 Kostas Papadimitriou
491 64cd4730 Antony Chazapis
    def save(self, update_timestamps=True, **kwargs):
492 64cd4730 Antony Chazapis
        if update_timestamps:
493 64cd4730 Antony Chazapis
            if not self.id:
494 0905ccd2 Sofia Papagiannaki
                self.date_joined = datetime.now()
495 64cd4730 Antony Chazapis
            self.updated = datetime.now()
496 5ce3ce4f Sofia Papagiannaki
497 18ffbee1 Sofia Papagiannaki
        # update date_signed_terms if necessary
498 18ffbee1 Sofia Papagiannaki
        if self.__has_signed_terms != self.has_signed_terms:
499 18ffbee1 Sofia Papagiannaki
            self.date_signed_terms = datetime.now()
500 5ce3ce4f Sofia Papagiannaki
501 836a0fb0 Kostas Papadimitriou
        self.update_uuid()
502 836a0fb0 Kostas Papadimitriou
503 836a0fb0 Kostas Papadimitriou
        if self.username != self.email.lower():
504 9c01d9d1 Sofia Papagiannaki
            # set username
505 e5966bd9 Kostas Papadimitriou
            self.username = self.email.lower()
506 fbaa4f3c Kostas Papadimitriou
507 0905ccd2 Sofia Papagiannaki
        super(AstakosUser, self).save(**kwargs)
508 2e90e3ec Kostas Papadimitriou
509 bf0c6de5 Sofia Papagiannaki
    def renew_token(self, flush_sessions=False, current_key=None):
510 64cd4730 Antony Chazapis
        md5 = hashlib.md5()
511 8f8c43b2 Sofia Papagiannaki
        md5.update(settings.SECRET_KEY)
512 0905ccd2 Sofia Papagiannaki
        md5.update(self.username)
513 64cd4730 Antony Chazapis
        md5.update(self.realname.encode('ascii', 'ignore'))
514 64cd4730 Antony Chazapis
        md5.update(asctime())
515 2e90e3ec Kostas Papadimitriou
516 64cd4730 Antony Chazapis
        self.auth_token = b64encode(md5.digest())
517 64cd4730 Antony Chazapis
        self.auth_token_created = datetime.now()
518 64cd4730 Antony Chazapis
        self.auth_token_expires = self.auth_token_created + \
519 92defad4 Sofia Papagiannaki
                                  timedelta(hours=AUTH_TOKEN_DURATION)
520 bf0c6de5 Sofia Papagiannaki
        if flush_sessions:
521 bf0c6de5 Sofia Papagiannaki
            self.flush_sessions(current_key)
522 111f3da6 Sofia Papagiannaki
        msg = 'Token renewed for %s' % self.email
523 aab4d540 Sofia Papagiannaki
        logger.log(LOGGING_LEVEL, msg)
524 6c736ed7 Kostas Papadimitriou
525 bf0c6de5 Sofia Papagiannaki
    def flush_sessions(self, current_key=None):
526 bf0c6de5 Sofia Papagiannaki
        q = self.sessions
527 bf0c6de5 Sofia Papagiannaki
        if current_key:
528 bf0c6de5 Sofia Papagiannaki
            q = q.exclude(session_key=current_key)
529 2e90e3ec Kostas Papadimitriou
530 bf0c6de5 Sofia Papagiannaki
        keys = q.values_list('session_key', flat=True)
531 bf0c6de5 Sofia Papagiannaki
        if keys:
532 bf0c6de5 Sofia Papagiannaki
            msg = 'Flushing sessions: %s' % ','.join(keys)
533 c0b26605 Sofia Papagiannaki
            logger.log(LOGGING_LEVEL, msg, [])
534 bf0c6de5 Sofia Papagiannaki
        engine = import_module(settings.SESSION_ENGINE)
535 bf0c6de5 Sofia Papagiannaki
        for k in keys:
536 bf0c6de5 Sofia Papagiannaki
            s = engine.SessionStore(k)
537 bf0c6de5 Sofia Papagiannaki
            s.flush()
538 bf0c6de5 Sofia Papagiannaki
539 64cd4730 Antony Chazapis
    def __unicode__(self):
540 3abf6c78 Sofia Papagiannaki
        return '%s (%s)' % (self.realname, self.email)
541 5ce3ce4f Sofia Papagiannaki
542 0a569195 Sofia Papagiannaki
    def conflicting_email(self):
543 5ce3ce4f Sofia Papagiannaki
        q = AstakosUser.objects.exclude(username=self.username)
544 789a5951 Sofia Papagiannaki
        q = q.filter(email__iexact=self.email)
545 0a569195 Sofia Papagiannaki
        if q.count() != 0:
546 0a569195 Sofia Papagiannaki
            return True
547 0a569195 Sofia Papagiannaki
        return False
548 5ce3ce4f Sofia Papagiannaki
549 34a76cdb Kostas Papadimitriou
    def email_change_is_pending(self):
550 34a76cdb Kostas Papadimitriou
        return self.emailchanges.count() > 0
551 34a76cdb Kostas Papadimitriou
552 fcf90160 Sofia Papagiannaki
    @property
553 09e7393c Sofia Papagiannaki
    def signed_terms(self):
554 09e7393c Sofia Papagiannaki
        term = get_latest_terms()
555 09e7393c Sofia Papagiannaki
        if not term:
556 09e7393c Sofia Papagiannaki
            return True
557 09e7393c Sofia Papagiannaki
        if not self.has_signed_terms:
558 09e7393c Sofia Papagiannaki
            return False
559 09e7393c Sofia Papagiannaki
        if not self.date_signed_terms:
560 09e7393c Sofia Papagiannaki
            return False
561 09e7393c Sofia Papagiannaki
        if self.date_signed_terms < term.date:
562 09e7393c Sofia Papagiannaki
            self.has_signed_terms = False
563 f0f92965 Sofia Papagiannaki
            self.date_signed_terms = None
564 09e7393c Sofia Papagiannaki
            self.save()
565 09e7393c Sofia Papagiannaki
            return False
566 09e7393c Sofia Papagiannaki
        return True
567 09e7393c Sofia Papagiannaki
568 d2633501 Kostas Papadimitriou
    def set_invitations_level(self):
569 d2633501 Kostas Papadimitriou
        """
570 d2633501 Kostas Papadimitriou
        Update user invitation level
571 d2633501 Kostas Papadimitriou
        """
572 d2633501 Kostas Papadimitriou
        level = self.invitation.inviter.level + 1
573 d2633501 Kostas Papadimitriou
        self.level = level
574 d2633501 Kostas Papadimitriou
        self.invitations = INVITATIONS_PER_LEVEL.get(level, 0)
575 d2633501 Kostas Papadimitriou
576 9d20fe23 Kostas Papadimitriou
    def can_change_password(self):
577 9d20fe23 Kostas Papadimitriou
        return self.has_auth_provider('local', auth_backend='astakos')
578 d2633501 Kostas Papadimitriou
579 9d20fe23 Kostas Papadimitriou
    def can_change_email(self):
580 9d20fe23 Kostas Papadimitriou
        if not self.has_auth_provider('local'):
581 9d20fe23 Kostas Papadimitriou
            return True
582 63836eda Kostas Papadimitriou
583 9d20fe23 Kostas Papadimitriou
        local = self.get_auth_provider('local')._instance
584 9d20fe23 Kostas Papadimitriou
        return local.auth_backend == 'astakos'
585 63836eda Kostas Papadimitriou
586 9d20fe23 Kostas Papadimitriou
    # Auth providers related methods
587 9d20fe23 Kostas Papadimitriou
    def get_auth_provider(self, module=None, identifier=None, **filters):
588 9d20fe23 Kostas Papadimitriou
        if not module:
589 9d20fe23 Kostas Papadimitriou
            return self.auth_providers.active()[0].settings
590 63836eda Kostas Papadimitriou
591 97246b51 Kostas Papadimitriou
        params = {'module': module}
592 97246b51 Kostas Papadimitriou
        if identifier:
593 97246b51 Kostas Papadimitriou
            params['identifier'] = identifier
594 97246b51 Kostas Papadimitriou
        params.update(filters)
595 97246b51 Kostas Papadimitriou
        return self.auth_providers.active().get(**params).settings
596 d2633501 Kostas Papadimitriou
597 9d20fe23 Kostas Papadimitriou
    def has_auth_provider(self, provider, **kwargs):
598 9d20fe23 Kostas Papadimitriou
        return bool(self.auth_providers.active().filter(module=provider,
599 9d20fe23 Kostas Papadimitriou
                                                        **kwargs).count())
600 d2633501 Kostas Papadimitriou
601 9d20fe23 Kostas Papadimitriou
    def get_required_providers(self, **kwargs):
602 9d20fe23 Kostas Papadimitriou
        return auth.REQUIRED_PROVIDERS.keys()
603 649f2d36 Kostas Papadimitriou
604 9d20fe23 Kostas Papadimitriou
    def missing_required_providers(self):
605 9d20fe23 Kostas Papadimitriou
        required = self.get_required_providers()
606 9d20fe23 Kostas Papadimitriou
        missing = []
607 63836eda Kostas Papadimitriou
        for provider in required:
608 63836eda Kostas Papadimitriou
            if not self.has_auth_provider(provider):
609 9d20fe23 Kostas Papadimitriou
                missing.append(auth.get_provider(provider, self))
610 9d20fe23 Kostas Papadimitriou
        return missing
611 63836eda Kostas Papadimitriou
612 9d20fe23 Kostas Papadimitriou
    def get_available_auth_providers(self, **filters):
613 d2633501 Kostas Papadimitriou
        """
614 9d20fe23 Kostas Papadimitriou
        Returns a list of providers available for add by the user.
615 d2633501 Kostas Papadimitriou
        """
616 9d20fe23 Kostas Papadimitriou
        modules = astakos_settings.IM_MODULES
617 9d20fe23 Kostas Papadimitriou
        providers = []
618 9d20fe23 Kostas Papadimitriou
        for p in modules:
619 9d20fe23 Kostas Papadimitriou
            providers.append(auth.get_provider(p, self))
620 9d20fe23 Kostas Papadimitriou
        available = []
621 9d20fe23 Kostas Papadimitriou
622 9d20fe23 Kostas Papadimitriou
        for p in providers:
623 9d20fe23 Kostas Papadimitriou
            if p.get_add_policy:
624 9d20fe23 Kostas Papadimitriou
                available.append(p)
625 9d20fe23 Kostas Papadimitriou
        return available
626 9d20fe23 Kostas Papadimitriou
627 9d20fe23 Kostas Papadimitriou
    def get_disabled_auth_providers(self, **filters):
628 9d20fe23 Kostas Papadimitriou
        providers = self.get_auth_providers(**filters)
629 9d20fe23 Kostas Papadimitriou
        disabled = []
630 9d20fe23 Kostas Papadimitriou
        for p in providers:
631 9d20fe23 Kostas Papadimitriou
            if not p.get_login_policy:
632 9d20fe23 Kostas Papadimitriou
                disabled.append(p)
633 9d20fe23 Kostas Papadimitriou
        return disabled
634 9d20fe23 Kostas Papadimitriou
635 9d20fe23 Kostas Papadimitriou
    def get_enabled_auth_providers(self, **filters):
636 9d20fe23 Kostas Papadimitriou
        providers = self.get_auth_providers(**filters)
637 9d20fe23 Kostas Papadimitriou
        enabled = []
638 9d20fe23 Kostas Papadimitriou
        for p in providers:
639 9d20fe23 Kostas Papadimitriou
            if p.get_login_policy:
640 9d20fe23 Kostas Papadimitriou
                enabled.append(p)
641 9d20fe23 Kostas Papadimitriou
        return enabled
642 9d20fe23 Kostas Papadimitriou
643 9d20fe23 Kostas Papadimitriou
    def get_auth_providers(self, **filters):
644 9d20fe23 Kostas Papadimitriou
        providers = []
645 9d20fe23 Kostas Papadimitriou
        for provider in self.auth_providers.active(**filters):
646 9d20fe23 Kostas Papadimitriou
            if provider.settings.module_enabled:
647 9d20fe23 Kostas Papadimitriou
                providers.append(provider.settings)
648 d2633501 Kostas Papadimitriou
649 9d20fe23 Kostas Papadimitriou
        modules = astakos_settings.IM_MODULES
650 d2633501 Kostas Papadimitriou
651 9d20fe23 Kostas Papadimitriou
        def key(p):
652 9d20fe23 Kostas Papadimitriou
            if not p.module in modules:
653 9d20fe23 Kostas Papadimitriou
                return 100
654 9d20fe23 Kostas Papadimitriou
            return modules.index(p.module)
655 9d20fe23 Kostas Papadimitriou
656 9d20fe23 Kostas Papadimitriou
        providers = sorted(providers, key=key)
657 9d20fe23 Kostas Papadimitriou
        return providers
658 d2633501 Kostas Papadimitriou
659 9d20fe23 Kostas Papadimitriou
    # URL methods
660 9d20fe23 Kostas Papadimitriou
    @property
661 9d20fe23 Kostas Papadimitriou
    def auth_providers_display(self):
662 9d20fe23 Kostas Papadimitriou
        return ",".join(["%s:%s" % (p.module, p.get_username_msg) for p in
663 9d20fe23 Kostas Papadimitriou
                         self.get_enabled_auth_providers()])
664 d2633501 Kostas Papadimitriou
665 9d20fe23 Kostas Papadimitriou
    def add_auth_provider(self, module='local', identifier=None, **params):
666 9d20fe23 Kostas Papadimitriou
        provider = auth.get_provider(module, self, identifier, **params)
667 9d20fe23 Kostas Papadimitriou
        provider.add_to_user()
668 d2633501 Kostas Papadimitriou
669 d2633501 Kostas Papadimitriou
    def get_resend_activation_url(self):
670 c630fee6 Kostas Papadimitriou
        return reverse('send_activation', kwargs={'user_id': self.pk})
671 c630fee6 Kostas Papadimitriou
672 d2633501 Kostas Papadimitriou
    def get_activation_url(self, nxt=False):
673 d2633501 Kostas Papadimitriou
        url = "%s?auth=%s" % (reverse('astakos.im.views.activate'),
674 d2633501 Kostas Papadimitriou
                                 quote(self.auth_token))
675 d2633501 Kostas Papadimitriou
        if nxt:
676 d2633501 Kostas Papadimitriou
            url += "&next=%s" % quote(nxt)
677 d2633501 Kostas Papadimitriou
        return url
678 d2633501 Kostas Papadimitriou
679 d2633501 Kostas Papadimitriou
    def get_password_reset_url(self, token_generator=default_token_generator):
680 d2633501 Kostas Papadimitriou
        return reverse('django.contrib.auth.views.password_reset_confirm',
681 d2633501 Kostas Papadimitriou
                          kwargs={'uidb36':int_to_base36(self.id),
682 d2633501 Kostas Papadimitriou
                                  'token':token_generator.make_token(self)})
683 d2633501 Kostas Papadimitriou
684 9d20fe23 Kostas Papadimitriou
    def get_inactive_message(self, provider_module, identifier=None):
685 9d20fe23 Kostas Papadimitriou
        provider = self.get_auth_provider(provider_module, identifier)
686 d2633501 Kostas Papadimitriou
687 c4b1a172 Kostas Papadimitriou
        msg_extra = ''
688 c4b1a172 Kostas Papadimitriou
        message = ''
689 9d20fe23 Kostas Papadimitriou
690 9d20fe23 Kostas Papadimitriou
        msg_inactive = provider.get_account_inactive_msg
691 9d20fe23 Kostas Papadimitriou
        msg_pending = provider.get_pending_activation_msg
692 9d20fe23 Kostas Papadimitriou
        msg_pending_help = _(astakos_messages.ACCOUNT_PENDING_ACTIVATION_HELP)
693 9d20fe23 Kostas Papadimitriou
        #msg_resend_prompt = _(astakos_messages.ACCOUNT_RESEND_ACTIVATION)
694 9d20fe23 Kostas Papadimitriou
        msg_pending_mod = provider.get_pending_moderation_msg
695 9d20fe23 Kostas Papadimitriou
        msg_resend = _(astakos_messages.ACCOUNT_RESEND_ACTIVATION)
696 9d20fe23 Kostas Papadimitriou
697 c4b1a172 Kostas Papadimitriou
        if self.activation_sent:
698 c4b1a172 Kostas Papadimitriou
            if self.email_verified:
699 9d20fe23 Kostas Papadimitriou
                message = msg_inactive
700 c4b1a172 Kostas Papadimitriou
            else:
701 9d20fe23 Kostas Papadimitriou
                message = msg_pending
702 9d20fe23 Kostas Papadimitriou
                url = self.get_resend_activation_url()
703 9d20fe23 Kostas Papadimitriou
                msg_extra = msg_pending_help + \
704 9d20fe23 Kostas Papadimitriou
                            u' ' + \
705 9d20fe23 Kostas Papadimitriou
                            '<a href="%s">%s?</a>' % (url, msg_resend)
706 c4b1a172 Kostas Papadimitriou
        else:
707 fc655b6f Kostas Papadimitriou
            if astakos_settings.MODERATION_ENABLED:
708 9d20fe23 Kostas Papadimitriou
                message = msg_pending_mod
709 c4b1a172 Kostas Papadimitriou
            else:
710 9d20fe23 Kostas Papadimitriou
                message = msg_pending
711 c4b1a172 Kostas Papadimitriou
                url = self.get_resend_activation_url()
712 9d20fe23 Kostas Papadimitriou
                msg_extra = '<a href="%s">%s?</a>' % (url, \
713 9d20fe23 Kostas Papadimitriou
                                msg_resend)
714 c4b1a172 Kostas Papadimitriou
715 a15a19b2 Kostas Papadimitriou
        return mark_safe(message + u' '+ msg_extra)
716 c4b1a172 Kostas Papadimitriou
717 7184f408 Giorgos Korfiatis
    def owns_application(self, application):
718 7184f408 Giorgos Korfiatis
        return application.owner == self
719 7184f408 Giorgos Korfiatis
720 e87bbb41 Kostas Papadimitriou
    def owns_project(self, project):
721 7184f408 Giorgos Korfiatis
        return project.application.owner == self
722 e87bbb41 Kostas Papadimitriou
723 d4660e00 Giorgos Korfiatis
    def is_associated(self, project):
724 d4660e00 Giorgos Korfiatis
        try:
725 d4660e00 Giorgos Korfiatis
            m = ProjectMembership.objects.get(person=self, project=project)
726 d4660e00 Giorgos Korfiatis
            return m.state in ProjectMembership.ASSOCIATED_STATES
727 d4660e00 Giorgos Korfiatis
        except ProjectMembership.DoesNotExist:
728 d4660e00 Giorgos Korfiatis
            return False
729 d4660e00 Giorgos Korfiatis
730 aad0e329 Giorgos Korfiatis
    def get_membership(self, project):
731 aad0e329 Giorgos Korfiatis
        try:
732 aad0e329 Giorgos Korfiatis
            return ProjectMembership.objects.get(
733 aad0e329 Giorgos Korfiatis
                project=project,
734 aad0e329 Giorgos Korfiatis
                person=self)
735 aad0e329 Giorgos Korfiatis
        except ProjectMembership.DoesNotExist:
736 aad0e329 Giorgos Korfiatis
            return None
737 d2633501 Kostas Papadimitriou
738 d4660e00 Giorgos Korfiatis
    def membership_display(self, project):
739 d4660e00 Giorgos Korfiatis
        m = self.get_membership(project)
740 d4660e00 Giorgos Korfiatis
        if m is None:
741 d4660e00 Giorgos Korfiatis
            return _('Not a member')
742 d4660e00 Giorgos Korfiatis
        else:
743 d4660e00 Giorgos Korfiatis
            return m.user_friendly_state_display()
744 d4660e00 Giorgos Korfiatis
745 7f31a7a3 Giorgos Korfiatis
    def non_owner_can_view(self, maybe_project):
746 8e1a5af5 Georgios D. Tsoukalas
        if self.is_project_admin():
747 8e1a5af5 Georgios D. Tsoukalas
            return True
748 7f31a7a3 Giorgos Korfiatis
        if maybe_project is None:
749 7f31a7a3 Giorgos Korfiatis
            return False
750 7f31a7a3 Giorgos Korfiatis
        project = maybe_project
751 7f31a7a3 Giorgos Korfiatis
        if self.is_associated(project):
752 7f31a7a3 Giorgos Korfiatis
            return True
753 7f31a7a3 Giorgos Korfiatis
        if project.is_deactivated():
754 7f31a7a3 Giorgos Korfiatis
            return False
755 7f31a7a3 Giorgos Korfiatis
        return True
756 7f31a7a3 Giorgos Korfiatis
757 c7c0ec58 Giorgos Korfiatis
    def settings(self):
758 c7c0ec58 Giorgos Korfiatis
        return UserSetting.objects.filter(user=self)
759 c7c0ec58 Giorgos Korfiatis
760 a989b48e Giorgos Korfiatis
761 d2633501 Kostas Papadimitriou
class AstakosUserAuthProviderManager(models.Manager):
762 d2633501 Kostas Papadimitriou
763 63836eda Kostas Papadimitriou
    def active(self, **filters):
764 63836eda Kostas Papadimitriou
        return self.filter(active=True, **filters)
765 d2633501 Kostas Papadimitriou
766 564a2292 Kostas Papadimitriou
    def remove_unverified_providers(self, provider, **filters):
767 564a2292 Kostas Papadimitriou
        try:
768 9d20fe23 Kostas Papadimitriou
            existing = self.filter(module=provider, user__email_verified=False,
769 9d20fe23 Kostas Papadimitriou
                                   **filters)
770 564a2292 Kostas Papadimitriou
            for p in existing:
771 564a2292 Kostas Papadimitriou
                p.user.delete()
772 564a2292 Kostas Papadimitriou
        except:
773 564a2292 Kostas Papadimitriou
            pass
774 564a2292 Kostas Papadimitriou
775 9d20fe23 Kostas Papadimitriou
    def unverified(self, provider, **filters):
776 9d20fe23 Kostas Papadimitriou
        try:
777 9d20fe23 Kostas Papadimitriou
            return self.get(module=provider, user__email_verified=False,
778 9d20fe23 Kostas Papadimitriou
                            **filters).settings
779 9d20fe23 Kostas Papadimitriou
        except AstakosUserAuthProvider.DoesNotExist:
780 9d20fe23 Kostas Papadimitriou
            return None
781 9d20fe23 Kostas Papadimitriou
782 9d20fe23 Kostas Papadimitriou
    def verified(self, provider, **filters):
783 9d20fe23 Kostas Papadimitriou
        try:
784 9d20fe23 Kostas Papadimitriou
            return self.get(module=provider, user__email_verified=True,
785 9d20fe23 Kostas Papadimitriou
                            **filters).settings
786 9d20fe23 Kostas Papadimitriou
        except AstakosUserAuthProvider.DoesNotExist:
787 9d20fe23 Kostas Papadimitriou
            return None
788 9d20fe23 Kostas Papadimitriou
789 9d20fe23 Kostas Papadimitriou
790 9d20fe23 Kostas Papadimitriou
class AuthProviderPolicyProfileManager(models.Manager):
791 9d20fe23 Kostas Papadimitriou
792 9d20fe23 Kostas Papadimitriou
    def active(self):
793 9d20fe23 Kostas Papadimitriou
        return self.filter(active=True)
794 9d20fe23 Kostas Papadimitriou
795 9d20fe23 Kostas Papadimitriou
    def for_user(self, user, provider):
796 9d20fe23 Kostas Papadimitriou
        policies = {}
797 9d20fe23 Kostas Papadimitriou
        exclusive_q1 = Q(provider=provider) & Q(is_exclusive=False)
798 9d20fe23 Kostas Papadimitriou
        exclusive_q2 = ~Q(provider=provider) & Q(is_exclusive=True)
799 9d20fe23 Kostas Papadimitriou
        exclusive_q = exclusive_q1 | exclusive_q2
800 9d20fe23 Kostas Papadimitriou
801 9d20fe23 Kostas Papadimitriou
        for profile in user.authpolicy_profiles.active().filter(exclusive_q):
802 9d20fe23 Kostas Papadimitriou
            policies.update(profile.policies)
803 9d20fe23 Kostas Papadimitriou
804 9d20fe23 Kostas Papadimitriou
        user_groups = user.groups.all().values('pk')
805 9d20fe23 Kostas Papadimitriou
        for profile in self.active().filter(groups__in=user_groups).filter(
806 9d20fe23 Kostas Papadimitriou
                exclusive_q):
807 9d20fe23 Kostas Papadimitriou
            policies.update(profile.policies)
808 9d20fe23 Kostas Papadimitriou
        return policies
809 9d20fe23 Kostas Papadimitriou
810 9d20fe23 Kostas Papadimitriou
    def add_policy(self, name, provider, group_or_user, exclusive=False,
811 9d20fe23 Kostas Papadimitriou
                   **policies):
812 9d20fe23 Kostas Papadimitriou
        is_group = isinstance(group_or_user, Group)
813 9d20fe23 Kostas Papadimitriou
        profile, created = self.get_or_create(name=name, provider=provider,
814 9d20fe23 Kostas Papadimitriou
                                              is_exclusive=exclusive)
815 9d20fe23 Kostas Papadimitriou
        profile.is_exclusive = exclusive
816 9d20fe23 Kostas Papadimitriou
        profile.save()
817 9d20fe23 Kostas Papadimitriou
        if is_group:
818 9d20fe23 Kostas Papadimitriou
            profile.groups.add(group_or_user)
819 9d20fe23 Kostas Papadimitriou
        else:
820 9d20fe23 Kostas Papadimitriou
            profile.users.add(group_or_user)
821 9d20fe23 Kostas Papadimitriou
        profile.set_policies(policies)
822 9d20fe23 Kostas Papadimitriou
        profile.save()
823 9d20fe23 Kostas Papadimitriou
        return profile
824 9d20fe23 Kostas Papadimitriou
825 9d20fe23 Kostas Papadimitriou
826 9d20fe23 Kostas Papadimitriou
class AuthProviderPolicyProfile(models.Model):
827 9d20fe23 Kostas Papadimitriou
    name = models.CharField(_('Name'), max_length=255, blank=False,
828 9d20fe23 Kostas Papadimitriou
                            null=False, db_index=True)
829 9d20fe23 Kostas Papadimitriou
    provider = models.CharField(_('Provider'), max_length=255, blank=False,
830 9d20fe23 Kostas Papadimitriou
                                null=False)
831 9d20fe23 Kostas Papadimitriou
832 9d20fe23 Kostas Papadimitriou
    # apply policies to all providers excluding the one set in provider field
833 9d20fe23 Kostas Papadimitriou
    is_exclusive = models.BooleanField(default=False)
834 9d20fe23 Kostas Papadimitriou
835 9d20fe23 Kostas Papadimitriou
    policy_add = models.NullBooleanField(null=True, default=None)
836 9d20fe23 Kostas Papadimitriou
    policy_remove = models.NullBooleanField(null=True, default=None)
837 9d20fe23 Kostas Papadimitriou
    policy_create = models.NullBooleanField(null=True, default=None)
838 9d20fe23 Kostas Papadimitriou
    policy_login = models.NullBooleanField(null=True, default=None)
839 9d20fe23 Kostas Papadimitriou
    policy_limit = models.IntegerField(null=True, default=None)
840 9d20fe23 Kostas Papadimitriou
    policy_required = models.NullBooleanField(null=True, default=None)
841 9d20fe23 Kostas Papadimitriou
    policy_automoderate = models.NullBooleanField(null=True, default=None)
842 9d20fe23 Kostas Papadimitriou
    policy_switch = models.NullBooleanField(null=True, default=None)
843 9d20fe23 Kostas Papadimitriou
844 9d20fe23 Kostas Papadimitriou
    POLICY_FIELDS = ('add', 'remove', 'create', 'login', 'limit', 'required',
845 9d20fe23 Kostas Papadimitriou
                     'automoderate')
846 9d20fe23 Kostas Papadimitriou
847 9d20fe23 Kostas Papadimitriou
    priority = models.IntegerField(null=False, default=1)
848 9d20fe23 Kostas Papadimitriou
    groups = models.ManyToManyField(Group, related_name='authpolicy_profiles')
849 9d20fe23 Kostas Papadimitriou
    users = models.ManyToManyField(AstakosUser,
850 9d20fe23 Kostas Papadimitriou
                                   related_name='authpolicy_profiles')
851 9d20fe23 Kostas Papadimitriou
    active = models.BooleanField(default=True)
852 9d20fe23 Kostas Papadimitriou
853 9d20fe23 Kostas Papadimitriou
    objects = AuthProviderPolicyProfileManager()
854 9d20fe23 Kostas Papadimitriou
855 9d20fe23 Kostas Papadimitriou
    class Meta:
856 9d20fe23 Kostas Papadimitriou
        ordering = ['priority']
857 9d20fe23 Kostas Papadimitriou
858 9d20fe23 Kostas Papadimitriou
    @property
859 9d20fe23 Kostas Papadimitriou
    def policies(self):
860 9d20fe23 Kostas Papadimitriou
        policies = {}
861 9d20fe23 Kostas Papadimitriou
        for pkey in self.POLICY_FIELDS:
862 9d20fe23 Kostas Papadimitriou
            value = getattr(self, 'policy_%s' % pkey, None)
863 9d20fe23 Kostas Papadimitriou
            if value is None:
864 9d20fe23 Kostas Papadimitriou
                continue
865 9d20fe23 Kostas Papadimitriou
            policies[pkey] = value
866 9d20fe23 Kostas Papadimitriou
        return policies
867 9d20fe23 Kostas Papadimitriou
868 9d20fe23 Kostas Papadimitriou
    def set_policies(self, policies_dict):
869 9d20fe23 Kostas Papadimitriou
        for key, value in policies_dict.iteritems():
870 9d20fe23 Kostas Papadimitriou
            if key in self.POLICY_FIELDS:
871 9d20fe23 Kostas Papadimitriou
                setattr(self, 'policy_%s' % key, value)
872 9d20fe23 Kostas Papadimitriou
        return self.policies
873 564a2292 Kostas Papadimitriou
874 d2633501 Kostas Papadimitriou
875 d2633501 Kostas Papadimitriou
class AstakosUserAuthProvider(models.Model):
876 d2633501 Kostas Papadimitriou
    """
877 d2633501 Kostas Papadimitriou
    Available user authentication methods.
878 d2633501 Kostas Papadimitriou
    """
879 e1a80257 Sofia Papagiannaki
    affiliation = models.CharField(_('Affiliation'), max_length=255, blank=True,
880 d2633501 Kostas Papadimitriou
                                   null=True, default=None)
881 d2633501 Kostas Papadimitriou
    user = models.ForeignKey(AstakosUser, related_name='auth_providers')
882 e1a80257 Sofia Papagiannaki
    module = models.CharField(_('Provider'), max_length=255, blank=False,
883 d2633501 Kostas Papadimitriou
                                default='local')
884 e1a80257 Sofia Papagiannaki
    identifier = models.CharField(_('Third-party identifier'),
885 d2633501 Kostas Papadimitriou
                                              max_length=255, null=True,
886 d2633501 Kostas Papadimitriou
                                              blank=True)
887 d2633501 Kostas Papadimitriou
    active = models.BooleanField(default=True)
888 e1a80257 Sofia Papagiannaki
    auth_backend = models.CharField(_('Backend'), max_length=255, blank=False,
889 d2633501 Kostas Papadimitriou
                                   default='astakos')
890 3a72a5d4 Kostas Papadimitriou
    info_data = models.TextField(default="", null=True, blank=True)
891 c630fee6 Kostas Papadimitriou
    created = models.DateTimeField('Creation date', auto_now_add=True)
892 d2633501 Kostas Papadimitriou
893 d2633501 Kostas Papadimitriou
    objects = AstakosUserAuthProviderManager()
894 d2633501 Kostas Papadimitriou
895 d2633501 Kostas Papadimitriou
    class Meta:
896 d2633501 Kostas Papadimitriou
        unique_together = (('identifier', 'module', 'user'), )
897 c630fee6 Kostas Papadimitriou
        ordering = ('module', 'created')
898 d2633501 Kostas Papadimitriou
899 3a72a5d4 Kostas Papadimitriou
    def __init__(self, *args, **kwargs):
900 3a72a5d4 Kostas Papadimitriou
        super(AstakosUserAuthProvider, self).__init__(*args, **kwargs)
901 3a72a5d4 Kostas Papadimitriou
        try:
902 3a72a5d4 Kostas Papadimitriou
            self.info = json.loads(self.info_data)
903 c630fee6 Kostas Papadimitriou
            if not self.info:
904 c630fee6 Kostas Papadimitriou
                self.info = {}
905 c630fee6 Kostas Papadimitriou
        except Exception, e:
906 3a72a5d4 Kostas Papadimitriou
            self.info = {}
907 c630fee6 Kostas Papadimitriou
908 3a72a5d4 Kostas Papadimitriou
        for key,value in self.info.iteritems():
909 3a72a5d4 Kostas Papadimitriou
            setattr(self, 'info_%s' % key, value)
910 3a72a5d4 Kostas Papadimitriou
911 d2633501 Kostas Papadimitriou
    @property
912 d2633501 Kostas Papadimitriou
    def settings(self):
913 9d20fe23 Kostas Papadimitriou
        extra_data = {}
914 d2633501 Kostas Papadimitriou
915 9d20fe23 Kostas Papadimitriou
        info_data = {}
916 9d20fe23 Kostas Papadimitriou
        if self.info_data:
917 9d20fe23 Kostas Papadimitriou
            info_data = json.loads(self.info_data)
918 3a72a5d4 Kostas Papadimitriou
919 9d20fe23 Kostas Papadimitriou
        extra_data['info'] = info_data
920 d2633501 Kostas Papadimitriou
921 9d20fe23 Kostas Papadimitriou
        for key in ['active', 'auth_backend', 'created', 'pk', 'affiliation']:
922 9d20fe23 Kostas Papadimitriou
            extra_data[key] = getattr(self, key)
923 d2633501 Kostas Papadimitriou
924 9d20fe23 Kostas Papadimitriou
        extra_data['instance'] = self
925 9d20fe23 Kostas Papadimitriou
        return auth.get_provider(self.module, self.user,
926 9d20fe23 Kostas Papadimitriou
                                           self.identifier, **extra_data)
927 d2633501 Kostas Papadimitriou
928 f432088a Kostas Papadimitriou
    def __repr__(self):
929 f432088a Kostas Papadimitriou
        return '<AstakosUserAuthProvider %s:%s>' % (self.module, self.identifier)
930 f432088a Kostas Papadimitriou
931 b778b6fa Kostas Papadimitriou
    def __unicode__(self):
932 b778b6fa Kostas Papadimitriou
        if self.identifier:
933 b778b6fa Kostas Papadimitriou
            return "%s:%s" % (self.module, self.identifier)
934 b778b6fa Kostas Papadimitriou
        if self.auth_backend:
935 b778b6fa Kostas Papadimitriou
            return "%s:%s" % (self.module, self.auth_backend)
936 b778b6fa Kostas Papadimitriou
        return self.module
937 b778b6fa Kostas Papadimitriou
938 3a72a5d4 Kostas Papadimitriou
    def save(self, *args, **kwargs):
939 3a72a5d4 Kostas Papadimitriou
        self.info_data = json.dumps(self.info)
940 3a72a5d4 Kostas Papadimitriou
        return super(AstakosUserAuthProvider, self).save(*args, **kwargs)
941 b778b6fa Kostas Papadimitriou
942 d2633501 Kostas Papadimitriou
943 e1a80257 Sofia Papagiannaki
class ExtendedManager(models.Manager):
944 9a06d96f Olga Brani
    def _update_or_create(self, **kwargs):
945 9a06d96f Olga Brani
        assert kwargs, \
946 9a06d96f Olga Brani
            'update_or_create() must be passed at least one keyword argument'
947 9a06d96f Olga Brani
        obj, created = self.get_or_create(**kwargs)
948 9a06d96f Olga Brani
        defaults = kwargs.pop('defaults', {})
949 9a06d96f Olga Brani
        if created:
950 9a06d96f Olga Brani
            return obj, True, False
951 9a06d96f Olga Brani
        else:
952 9a06d96f Olga Brani
            try:
953 9a06d96f Olga Brani
                params = dict(
954 9a06d96f Olga Brani
                    [(k, v) for k, v in kwargs.items() if '__' not in k])
955 9a06d96f Olga Brani
                params.update(defaults)
956 9a06d96f Olga Brani
                for attr, val in params.items():
957 9a06d96f Olga Brani
                    if hasattr(obj, attr):
958 9a06d96f Olga Brani
                        setattr(obj, attr, val)
959 9a06d96f Olga Brani
                sid = transaction.savepoint()
960 9a06d96f Olga Brani
                obj.save(force_update=True)
961 9a06d96f Olga Brani
                transaction.savepoint_commit(sid)
962 9a06d96f Olga Brani
                return obj, False, True
963 9a06d96f Olga Brani
            except IntegrityError, e:
964 9a06d96f Olga Brani
                transaction.savepoint_rollback(sid)
965 9a06d96f Olga Brani
                try:
966 9a06d96f Olga Brani
                    return self.get(**kwargs), False, False
967 9a06d96f Olga Brani
                except self.model.DoesNotExist:
968 9a06d96f Olga Brani
                    raise e
969 9a06d96f Olga Brani
970 9a06d96f Olga Brani
    update_or_create = _update_or_create
971 5ce3ce4f Sofia Papagiannaki
972 8e45d6fd Sofia Papagiannaki
973 8e45d6fd Sofia Papagiannaki
class AstakosUserQuota(models.Model):
974 e1a80257 Sofia Papagiannaki
    objects = ExtendedManager()
975 9deadfcd Georgios D. Tsoukalas
    capacity = intDecimalField()
976 8e45d6fd Sofia Papagiannaki
    resource = models.ForeignKey(Resource)
977 8e45d6fd Sofia Papagiannaki
    user = models.ForeignKey(AstakosUser)
978 5ce3ce4f Sofia Papagiannaki
979 8e45d6fd Sofia Papagiannaki
    class Meta:
980 8e45d6fd Sofia Papagiannaki
        unique_together = ("resource", "user")
981 09e7393c Sofia Papagiannaki
982 5ce3ce4f Sofia Papagiannaki
983 270dd48d Sofia Papagiannaki
class ApprovalTerms(models.Model):
984 270dd48d Sofia Papagiannaki
    """
985 270dd48d Sofia Papagiannaki
    Model for approval terms
986 270dd48d Sofia Papagiannaki
    """
987 6c736ed7 Kostas Papadimitriou
988 5ce3ce4f Sofia Papagiannaki
    date = models.DateTimeField(
989 3c638f72 Giorgos Korfiatis
        _('Issue date'), db_index=True, auto_now_add=True)
990 e1a80257 Sofia Papagiannaki
    location = models.CharField(_('Terms location'), max_length=255)
991 270dd48d Sofia Papagiannaki
992 5ce3ce4f Sofia Papagiannaki
993 64cd4730 Antony Chazapis
class Invitation(models.Model):
994 890b0eaf Sofia Papagiannaki
    """
995 890b0eaf Sofia Papagiannaki
    Model for registring invitations
996 890b0eaf Sofia Papagiannaki
    """
997 0905ccd2 Sofia Papagiannaki
    inviter = models.ForeignKey(AstakosUser, related_name='invitations_sent',
998 64cd4730 Antony Chazapis
                                null=True)
999 e1a80257 Sofia Papagiannaki
    realname = models.CharField(_('Real name'), max_length=255)
1000 e1a80257 Sofia Papagiannaki
    username = models.CharField(_('Unique ID'), max_length=255, unique=True)
1001 e1a80257 Sofia Papagiannaki
    code = models.BigIntegerField(_('Invitation code'), db_index=True)
1002 e1a80257 Sofia Papagiannaki
    is_consumed = models.BooleanField(_('Consumed?'), default=False)
1003 e1a80257 Sofia Papagiannaki
    created = models.DateTimeField(_('Creation date'), auto_now_add=True)
1004 e1a80257 Sofia Papagiannaki
    consumed = models.DateTimeField(_('Consumption date'), null=True, blank=True)
1005 5ce3ce4f Sofia Papagiannaki
1006 18ffbee1 Sofia Papagiannaki
    def __init__(self, *args, **kwargs):
1007 18ffbee1 Sofia Papagiannaki
        super(Invitation, self).__init__(*args, **kwargs)
1008 8f5a3a06 Sofia Papagiannaki
        if not self.id:
1009 8f5a3a06 Sofia Papagiannaki
            self.code = _generate_invitation_code()
1010 5ce3ce4f Sofia Papagiannaki
1011 64cd4730 Antony Chazapis
    def consume(self):
1012 64cd4730 Antony Chazapis
        self.is_consumed = True
1013 64cd4730 Antony Chazapis
        self.consumed = datetime.now()
1014 64cd4730 Antony Chazapis
        self.save()
1015 6c736ed7 Kostas Papadimitriou
1016 64cd4730 Antony Chazapis
    def __unicode__(self):
1017 0905ccd2 Sofia Papagiannaki
        return '%s -> %s [%d]' % (self.inviter, self.username, self.code)
1018 9c01d9d1 Sofia Papagiannaki
1019 49790d9d Sofia Papagiannaki
1020 49790d9d Sofia Papagiannaki
class EmailChangeManager(models.Manager):
1021 34a76cdb Kostas Papadimitriou
1022 49790d9d Sofia Papagiannaki
    @transaction.commit_on_success
1023 49790d9d Sofia Papagiannaki
    def change_email(self, activation_key):
1024 49790d9d Sofia Papagiannaki
        """
1025 49790d9d Sofia Papagiannaki
        Validate an activation key and change the corresponding
1026 49790d9d Sofia Papagiannaki
        ``User`` if valid.
1027 49790d9d Sofia Papagiannaki

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

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

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

1036 49790d9d Sofia Papagiannaki
        After successful email change the activation record is deleted.
1037 49790d9d Sofia Papagiannaki

1038 49790d9d Sofia Papagiannaki
        Throws ValueError if there is already
1039 49790d9d Sofia Papagiannaki
        """
1040 49790d9d Sofia Papagiannaki
        try:
1041 5ce3ce4f Sofia Papagiannaki
            email_change = self.model.objects.get(
1042 5ce3ce4f Sofia Papagiannaki
                activation_key=activation_key)
1043 49790d9d Sofia Papagiannaki
            if email_change.activation_key_expired():
1044 49790d9d Sofia Papagiannaki
                email_change.delete()
1045 49790d9d Sofia Papagiannaki
                raise EmailChange.DoesNotExist
1046 49790d9d Sofia Papagiannaki
            # is there an active user with this address?
1047 49790d9d Sofia Papagiannaki
            try:
1048 789a5951 Sofia Papagiannaki
                AstakosUser.objects.get(email__iexact=email_change.new_email_address)
1049 49790d9d Sofia Papagiannaki
            except AstakosUser.DoesNotExist:
1050 49790d9d Sofia Papagiannaki
                pass
1051 49790d9d Sofia Papagiannaki
            else:
1052 73fbaec4 Sofia Papagiannaki
                raise ValueError(_('The new email address is reserved.'))
1053 49790d9d Sofia Papagiannaki
            # update user
1054 49790d9d Sofia Papagiannaki
            user = AstakosUser.objects.get(pk=email_change.user_id)
1055 34a76cdb Kostas Papadimitriou
            old_email = user.email
1056 49790d9d Sofia Papagiannaki
            user.email = email_change.new_email_address
1057 49790d9d Sofia Papagiannaki
            user.save()
1058 49790d9d Sofia Papagiannaki
            email_change.delete()
1059 5d5ce247 Kostas Papadimitriou
            msg = "User %s changed email from %s to %s" % (user.log_display,
1060 5d5ce247 Kostas Papadimitriou
                                                           old_email,
1061 5d5ce247 Kostas Papadimitriou
                                                           user.email)
1062 34a76cdb Kostas Papadimitriou
            logger.log(LOGGING_LEVEL, msg)
1063 49790d9d Sofia Papagiannaki
            return user
1064 49790d9d Sofia Papagiannaki
        except EmailChange.DoesNotExist:
1065 73fbaec4 Sofia Papagiannaki
            raise ValueError(_('Invalid activation key.'))
1066 49790d9d Sofia Papagiannaki
1067 49790d9d Sofia Papagiannaki
1068 49790d9d Sofia Papagiannaki
class EmailChange(models.Model):
1069 73fbaec4 Sofia Papagiannaki
    new_email_address = models.EmailField(
1070 73fbaec4 Sofia Papagiannaki
        _(u'new e-mail address'),
1071 b8b8fdde Constantinos Venetsanopoulos
        help_text=_('Provide a new email address. Until you verify the new '
1072 b8b8fdde Constantinos Venetsanopoulos
                    'address by following the activation link that will be '
1073 b8b8fdde Constantinos Venetsanopoulos
                    'sent to it, your old email address will remain active.'))
1074 5ce3ce4f Sofia Papagiannaki
    user = models.ForeignKey(
1075 34a76cdb Kostas Papadimitriou
        AstakosUser, unique=True, related_name='emailchanges')
1076 3c638f72 Giorgos Korfiatis
    requested_at = models.DateTimeField(auto_now_add=True)
1077 5ce3ce4f Sofia Papagiannaki
    activation_key = models.CharField(
1078 5ce3ce4f Sofia Papagiannaki
        max_length=40, unique=True, db_index=True)
1079 49790d9d Sofia Papagiannaki
1080 49790d9d Sofia Papagiannaki
    objects = EmailChangeManager()
1081 49790d9d Sofia Papagiannaki
1082 34a76cdb Kostas Papadimitriou
    def get_url(self):
1083 34a76cdb Kostas Papadimitriou
        return reverse('email_change_confirm',
1084 34a76cdb Kostas Papadimitriou
                      kwargs={'activation_key': self.activation_key})
1085 34a76cdb Kostas Papadimitriou
1086 49790d9d Sofia Papagiannaki
    def activation_key_expired(self):
1087 49790d9d Sofia Papagiannaki
        expiration_date = timedelta(days=EMAILCHANGE_ACTIVATION_DAYS)
1088 ff9290ec Sofia Papagiannaki
        return self.requested_at + expiration_date < datetime.now()
1089 ff9290ec Sofia Papagiannaki
1090 6b03a847 Sofia Papagiannaki
1091 ca828a10 Sofia Papagiannaki
class AdditionalMail(models.Model):
1092 ca828a10 Sofia Papagiannaki
    """
1093 ca828a10 Sofia Papagiannaki
    Model for registring invitations
1094 ca828a10 Sofia Papagiannaki
    """
1095 ca828a10 Sofia Papagiannaki
    owner = models.ForeignKey(AstakosUser)
1096 1eec103a Sofia Papagiannaki
    email = models.EmailField()
1097 ca828a10 Sofia Papagiannaki
1098 5ce3ce4f Sofia Papagiannaki
1099 fc1e2f02 Sofia Papagiannaki
def _generate_invitation_code():
1100 fc1e2f02 Sofia Papagiannaki
    while True:
1101 5ce3ce4f Sofia Papagiannaki
        code = randint(1, 2L ** 63 - 1)
1102 fc1e2f02 Sofia Papagiannaki
        try:
1103 fc1e2f02 Sofia Papagiannaki
            Invitation.objects.get(code=code)
1104 fc1e2f02 Sofia Papagiannaki
            # An invitation with this code already exists, try again
1105 fc1e2f02 Sofia Papagiannaki
        except Invitation.DoesNotExist:
1106 fc1e2f02 Sofia Papagiannaki
            return code
1107 fc1e2f02 Sofia Papagiannaki
1108 5ce3ce4f Sofia Papagiannaki
1109 fc1e2f02 Sofia Papagiannaki
def get_latest_terms():
1110 fc1e2f02 Sofia Papagiannaki
    try:
1111 fc1e2f02 Sofia Papagiannaki
        term = ApprovalTerms.objects.order_by('-id')[0]
1112 fc1e2f02 Sofia Papagiannaki
        return term
1113 fc1e2f02 Sofia Papagiannaki
    except IndexError:
1114 fc1e2f02 Sofia Papagiannaki
        pass
1115 fc1e2f02 Sofia Papagiannaki
    return None
1116 fc1e2f02 Sofia Papagiannaki
1117 9d20fe23 Kostas Papadimitriou
1118 ef20ea07 Sofia Papagiannaki
class PendingThirdPartyUser(models.Model):
1119 ef20ea07 Sofia Papagiannaki
    """
1120 ef20ea07 Sofia Papagiannaki
    Model for registring successful third party user authentications
1121 ef20ea07 Sofia Papagiannaki
    """
1122 e1a80257 Sofia Papagiannaki
    third_party_identifier = models.CharField(_('Third-party identifier'), max_length=255, null=True, blank=True)
1123 e1a80257 Sofia Papagiannaki
    provider = models.CharField(_('Provider'), max_length=255, blank=True)
1124 678b2236 Sofia Papagiannaki
    email = models.EmailField(_('e-mail address'), blank=True, null=True)
1125 564a2292 Kostas Papadimitriou
    first_name = models.CharField(_('first name'), max_length=30, blank=True,
1126 564a2292 Kostas Papadimitriou
                                  null=True)
1127 564a2292 Kostas Papadimitriou
    last_name = models.CharField(_('last name'), max_length=30, blank=True,
1128 564a2292 Kostas Papadimitriou
                                 null=True)
1129 564a2292 Kostas Papadimitriou
    affiliation = models.CharField('Affiliation', max_length=255, blank=True,
1130 564a2292 Kostas Papadimitriou
                                   null=True)
1131 9d20fe23 Kostas Papadimitriou
    username = models.CharField(_('username'), max_length=30, unique=True,
1132 d6ea9b3d Olga Brani
                                help_text=_("Required. 30 characters or fewer. Letters, numbers and @/./+/-/_ characters"))
1133 e1a80257 Sofia Papagiannaki
    token = models.CharField(_('Token'), max_length=255, null=True, blank=True)
1134 d2633501 Kostas Papadimitriou
    created = models.DateTimeField(auto_now_add=True, null=True, blank=True)
1135 c630fee6 Kostas Papadimitriou
    info = models.TextField(default="", null=True, blank=True)
1136 d2633501 Kostas Papadimitriou
1137 678b2236 Sofia Papagiannaki
    class Meta:
1138 678b2236 Sofia Papagiannaki
        unique_together = ("provider", "third_party_identifier")
1139 ef20ea07 Sofia Papagiannaki
1140 c630fee6 Kostas Papadimitriou
    def get_user_instance(self):
1141 c630fee6 Kostas Papadimitriou
        d = self.__dict__
1142 c630fee6 Kostas Papadimitriou
        d.pop('_state', None)
1143 c630fee6 Kostas Papadimitriou
        d.pop('id', None)
1144 c630fee6 Kostas Papadimitriou
        d.pop('token', None)
1145 c630fee6 Kostas Papadimitriou
        d.pop('created', None)
1146 c630fee6 Kostas Papadimitriou
        d.pop('info', None)
1147 c630fee6 Kostas Papadimitriou
        user = AstakosUser(**d)
1148 c630fee6 Kostas Papadimitriou
1149 c630fee6 Kostas Papadimitriou
        return user
1150 c630fee6 Kostas Papadimitriou
1151 ef20ea07 Sofia Papagiannaki
    @property
1152 ef20ea07 Sofia Papagiannaki
    def realname(self):
1153 ef20ea07 Sofia Papagiannaki
        return '%s %s' %(self.first_name, self.last_name)
1154 ef20ea07 Sofia Papagiannaki
1155 ef20ea07 Sofia Papagiannaki
    @realname.setter
1156 ef20ea07 Sofia Papagiannaki
    def realname(self, value):
1157 ef20ea07 Sofia Papagiannaki
        parts = value.split(' ')
1158 ef20ea07 Sofia Papagiannaki
        if len(parts) == 2:
1159 ef20ea07 Sofia Papagiannaki
            self.first_name = parts[0]
1160 ef20ea07 Sofia Papagiannaki
            self.last_name = parts[1]
1161 ef20ea07 Sofia Papagiannaki
        else:
1162 ef20ea07 Sofia Papagiannaki
            self.last_name = parts[0]
1163 2e90e3ec Kostas Papadimitriou
1164 ef20ea07 Sofia Papagiannaki
    def save(self, **kwargs):
1165 ef20ea07 Sofia Papagiannaki
        if not self.id:
1166 ef20ea07 Sofia Papagiannaki
            # set username
1167 ef20ea07 Sofia Papagiannaki
            while not self.username:
1168 ef20ea07 Sofia Papagiannaki
                username =  uuid.uuid4().hex[:30]
1169 ef20ea07 Sofia Papagiannaki
                try:
1170 ef20ea07 Sofia Papagiannaki
                    AstakosUser.objects.get(username = username)
1171 ef20ea07 Sofia Papagiannaki
                except AstakosUser.DoesNotExist, e:
1172 ef20ea07 Sofia Papagiannaki
                    self.username = username
1173 ef20ea07 Sofia Papagiannaki
        super(PendingThirdPartyUser, self).save(**kwargs)
1174 ef20ea07 Sofia Papagiannaki
1175 d2633501 Kostas Papadimitriou
    def generate_token(self):
1176 d2633501 Kostas Papadimitriou
        self.password = self.third_party_identifier
1177 d2633501 Kostas Papadimitriou
        self.last_login = datetime.now()
1178 d2633501 Kostas Papadimitriou
        self.token = default_token_generator.make_token(self)
1179 d2633501 Kostas Papadimitriou
1180 606dea8d Kostas Papadimitriou
    def existing_user(self):
1181 606dea8d Kostas Papadimitriou
        return AstakosUser.objects.filter(auth_providers__module=self.provider,
1182 606dea8d Kostas Papadimitriou
                                         auth_providers__identifier=self.third_party_identifier)
1183 606dea8d Kostas Papadimitriou
1184 9d20fe23 Kostas Papadimitriou
    def get_provider(self, user):
1185 9d20fe23 Kostas Papadimitriou
        params = {
1186 9d20fe23 Kostas Papadimitriou
            'info_data': self.info,
1187 9d20fe23 Kostas Papadimitriou
            'affiliation': self.affiliation
1188 9d20fe23 Kostas Papadimitriou
        }
1189 9d20fe23 Kostas Papadimitriou
        return auth.get_provider(self.provider, user,
1190 9d20fe23 Kostas Papadimitriou
                                 self.third_party_identifier, **params)
1191 9d20fe23 Kostas Papadimitriou
1192 bf0c6de5 Sofia Papagiannaki
class SessionCatalog(models.Model):
1193 bf0c6de5 Sofia Papagiannaki
    session_key = models.CharField(_('session key'), max_length=40)
1194 bf0c6de5 Sofia Papagiannaki
    user = models.ForeignKey(AstakosUser, related_name='sessions', null=True)
1195 bf0c6de5 Sofia Papagiannaki
1196 fcc1e93f Sofia Papagiannaki
1197 c7c0ec58 Giorgos Korfiatis
class UserSetting(models.Model):
1198 c7c0ec58 Giorgos Korfiatis
    user = models.ForeignKey(AstakosUser)
1199 c7c0ec58 Giorgos Korfiatis
    setting = models.CharField(max_length=255)
1200 c7c0ec58 Giorgos Korfiatis
    value = models.IntegerField()
1201 c7c0ec58 Giorgos Korfiatis
1202 c7c0ec58 Giorgos Korfiatis
    objects = ForUpdateManager()
1203 c7c0ec58 Giorgos Korfiatis
1204 c7c0ec58 Giorgos Korfiatis
    class Meta:
1205 c7c0ec58 Giorgos Korfiatis
        unique_together = ("user", "setting")
1206 c7c0ec58 Giorgos Korfiatis
1207 c7c0ec58 Giorgos Korfiatis
1208 fcc1e93f Sofia Papagiannaki
### PROJECTS ###
1209 fcc1e93f Sofia Papagiannaki
################
1210 fcc1e93f Sofia Papagiannaki
1211 2529745f Giorgos Korfiatis
class ChainManager(ForUpdateManager):
1212 2529745f Giorgos Korfiatis
1213 2529745f Giorgos Korfiatis
    def search_by_name(self, *search_strings):
1214 2529745f Giorgos Korfiatis
        projects = Project.objects.search_by_name(*search_strings)
1215 2529745f Giorgos Korfiatis
        chains = [p.id for p in projects]
1216 2529745f Giorgos Korfiatis
        apps  = ProjectApplication.objects.search_by_name(*search_strings)
1217 2529745f Giorgos Korfiatis
        apps = (app for app in apps if app.is_latest())
1218 2529745f Giorgos Korfiatis
        app_chains = [app.chain for app in apps if app.chain not in chains]
1219 2529745f Giorgos Korfiatis
        return chains + app_chains
1220 2529745f Giorgos Korfiatis
1221 2529745f Giorgos Korfiatis
    def all_full_state(self):
1222 2529745f Giorgos Korfiatis
        chains = self.all()
1223 4391de3d Giorgos Korfiatis
        cids = [c.chain for c in chains]
1224 4391de3d Giorgos Korfiatis
        projects = Project.objects.select_related('application').in_bulk(cids)
1225 4391de3d Giorgos Korfiatis
1226 4391de3d Giorgos Korfiatis
        objs = Chain.objects.annotate(latest=Max('chained_apps__id'))
1227 4391de3d Giorgos Korfiatis
        chain_latest = dict(objs.values_list('chain', 'latest'))
1228 4391de3d Giorgos Korfiatis
1229 4391de3d Giorgos Korfiatis
        objs = ProjectApplication.objects.select_related('applicant')
1230 4391de3d Giorgos Korfiatis
        apps = objs.in_bulk(chain_latest.values())
1231 4391de3d Giorgos Korfiatis
1232 4391de3d Giorgos Korfiatis
        d = {}
1233 2529745f Giorgos Korfiatis
        for chain in chains:
1234 4391de3d Giorgos Korfiatis
            pk = chain.pk
1235 4391de3d Giorgos Korfiatis
            project = projects.get(pk, None)
1236 4391de3d Giorgos Korfiatis
            app = apps[chain_latest[pk]]
1237 4391de3d Giorgos Korfiatis
            d[chain.pk] = chain.get_state(project, app)
1238 4391de3d Giorgos Korfiatis
1239 2529745f Giorgos Korfiatis
        return d
1240 2529745f Giorgos Korfiatis
1241 2529745f Giorgos Korfiatis
    def of_project(self, project):
1242 2529745f Giorgos Korfiatis
        if project is None:
1243 2529745f Giorgos Korfiatis
            return None
1244 2529745f Giorgos Korfiatis
        try:
1245 2529745f Giorgos Korfiatis
            return self.get(chain=project.id)
1246 2529745f Giorgos Korfiatis
        except Chain.DoesNotExist:
1247 2529745f Giorgos Korfiatis
            raise AssertionError('project with no chain')
1248 2529745f Giorgos Korfiatis
1249 2529745f Giorgos Korfiatis
1250 033f2822 Giorgos Korfiatis
class Chain(models.Model):
1251 033f2822 Giorgos Korfiatis
    chain  =   models.AutoField(primary_key=True)
1252 033f2822 Giorgos Korfiatis
1253 033f2822 Giorgos Korfiatis
    def __str__(self):
1254 033f2822 Giorgos Korfiatis
        return "%s" % (self.chain,)
1255 033f2822 Giorgos Korfiatis
1256 2529745f Giorgos Korfiatis
    objects = ChainManager()
1257 2529745f Giorgos Korfiatis
1258 2529745f Giorgos Korfiatis
    PENDING            = 0
1259 2529745f Giorgos Korfiatis
    DENIED             = 3
1260 2529745f Giorgos Korfiatis
    DISMISSED          = 4
1261 2529745f Giorgos Korfiatis
    CANCELLED          = 5
1262 2529745f Giorgos Korfiatis
1263 2529745f Giorgos Korfiatis
    APPROVED           = 10
1264 2529745f Giorgos Korfiatis
    APPROVED_PENDING   = 11
1265 2529745f Giorgos Korfiatis
    SUSPENDED          = 12
1266 2529745f Giorgos Korfiatis
    SUSPENDED_PENDING  = 13
1267 2529745f Giorgos Korfiatis
    TERMINATED         = 14
1268 2529745f Giorgos Korfiatis
    TERMINATED_PENDING = 15
1269 2529745f Giorgos Korfiatis
1270 2529745f Giorgos Korfiatis
    PENDING_STATES = [PENDING,
1271 2529745f Giorgos Korfiatis
                      APPROVED_PENDING,
1272 2529745f Giorgos Korfiatis
                      SUSPENDED_PENDING,
1273 2529745f Giorgos Korfiatis
                      TERMINATED_PENDING,
1274 2529745f Giorgos Korfiatis
                      ]
1275 2529745f Giorgos Korfiatis
1276 f557d10a Giorgos Korfiatis
    MODIFICATION_STATES = [APPROVED_PENDING,
1277 f557d10a Giorgos Korfiatis
                           SUSPENDED_PENDING,
1278 f557d10a Giorgos Korfiatis
                           TERMINATED_PENDING,
1279 f557d10a Giorgos Korfiatis
                           ]
1280 f557d10a Giorgos Korfiatis
1281 f557d10a Giorgos Korfiatis
    RELEVANT_STATES = [PENDING,
1282 f557d10a Giorgos Korfiatis
                       DENIED,
1283 f557d10a Giorgos Korfiatis
                       APPROVED,
1284 f557d10a Giorgos Korfiatis
                       APPROVED_PENDING,
1285 f557d10a Giorgos Korfiatis
                       SUSPENDED,
1286 f557d10a Giorgos Korfiatis
                       SUSPENDED_PENDING,
1287 f557d10a Giorgos Korfiatis
                       TERMINATED_PENDING,
1288 f557d10a Giorgos Korfiatis
                       ]
1289 f557d10a Giorgos Korfiatis
1290 2529745f Giorgos Korfiatis
    SKIP_STATES = [DISMISSED,
1291 2529745f Giorgos Korfiatis
                   CANCELLED,
1292 2529745f Giorgos Korfiatis
                   TERMINATED]
1293 2529745f Giorgos Korfiatis
1294 2529745f Giorgos Korfiatis
    STATE_DISPLAY = {
1295 5d209685 Giorgos Korfiatis
        PENDING            : _("Pending"),
1296 2529745f Giorgos Korfiatis
        DENIED             : _("Denied"),
1297 2529745f Giorgos Korfiatis
        DISMISSED          : _("Dismissed"),
1298 2529745f Giorgos Korfiatis
        CANCELLED          : _("Cancelled"),
1299 2529745f Giorgos Korfiatis
        APPROVED           : _("Active"),
1300 2529745f Giorgos Korfiatis
        APPROVED_PENDING   : _("Active - Pending"),
1301 2529745f Giorgos Korfiatis
        SUSPENDED          : _("Suspended"),
1302 2529745f Giorgos Korfiatis
        SUSPENDED_PENDING  : _("Suspended - Pending"),
1303 2529745f Giorgos Korfiatis
        TERMINATED         : _("Terminated"),
1304 2529745f Giorgos Korfiatis
        TERMINATED_PENDING : _("Terminated - Pending"),
1305 2529745f Giorgos Korfiatis
        }
1306 2529745f Giorgos Korfiatis
1307 2529745f Giorgos Korfiatis
1308 2529745f Giorgos Korfiatis
    @classmethod
1309 2529745f Giorgos Korfiatis
    def _chain_state(cls, project_state, app_state):
1310 2529745f Giorgos Korfiatis
        s = CHAIN_STATE.get((project_state, app_state), None)
1311 2529745f Giorgos Korfiatis
        if s is None:
1312 2529745f Giorgos Korfiatis
            raise AssertionError('inconsistent chain state')
1313 2529745f Giorgos Korfiatis
        return s
1314 2529745f Giorgos Korfiatis
1315 2529745f Giorgos Korfiatis
    @classmethod
1316 2529745f Giorgos Korfiatis
    def chain_state(cls, project, app):
1317 2529745f Giorgos Korfiatis
        p_state = project.state if project else None
1318 2529745f Giorgos Korfiatis
        return cls._chain_state(p_state, app.state)
1319 2529745f Giorgos Korfiatis
1320 2529745f Giorgos Korfiatis
    @classmethod
1321 2529745f Giorgos Korfiatis
    def state_display(cls, s):
1322 2529745f Giorgos Korfiatis
        if s is None:
1323 2529745f Giorgos Korfiatis
            return _("Unknown")
1324 2529745f Giorgos Korfiatis
        return cls.STATE_DISPLAY.get(s, _("Inconsistent"))
1325 2529745f Giorgos Korfiatis
1326 2529745f Giorgos Korfiatis
    def last_application(self):
1327 2529745f Giorgos Korfiatis
        return self.chained_apps.order_by('-id')[0]
1328 2529745f Giorgos Korfiatis
1329 2529745f Giorgos Korfiatis
    def get_project(self):
1330 2529745f Giorgos Korfiatis
        try:
1331 2529745f Giorgos Korfiatis
            return self.chained_project
1332 2529745f Giorgos Korfiatis
        except Project.DoesNotExist:
1333 2529745f Giorgos Korfiatis
            return None
1334 2529745f Giorgos Korfiatis
1335 2529745f Giorgos Korfiatis
    def get_elements(self):
1336 2529745f Giorgos Korfiatis
        project = self.get_project()
1337 2529745f Giorgos Korfiatis
        app = self.last_application()
1338 2529745f Giorgos Korfiatis
        return project, app
1339 2529745f Giorgos Korfiatis
1340 4391de3d Giorgos Korfiatis
    def get_state(self, project, app):
1341 2529745f Giorgos Korfiatis
        s = self.chain_state(project, app)
1342 2529745f Giorgos Korfiatis
        return s, project, app
1343 2529745f Giorgos Korfiatis
1344 4391de3d Giorgos Korfiatis
    def full_state(self):
1345 4391de3d Giorgos Korfiatis
        project, app = self.get_elements()
1346 4391de3d Giorgos Korfiatis
        return self.get_state(project, app)
1347 4391de3d Giorgos Korfiatis
1348 4391de3d Giorgos Korfiatis
1349 033f2822 Giorgos Korfiatis
def new_chain():
1350 033f2822 Giorgos Korfiatis
    c = Chain.objects.create()
1351 033f2822 Giorgos Korfiatis
    return c
1352 033f2822 Giorgos Korfiatis
1353 033f2822 Giorgos Korfiatis
1354 6dcf53eb Kostas Papadimitriou
class ProjectApplicationManager(ForUpdateManager):
1355 5550bcfb Kostas Papadimitriou
1356 05617ab9 Kostas Papadimitriou
    def user_visible_projects(self, *filters, **kw_filters):
1357 689226c3 Giorgos Korfiatis
        model = self.model
1358 689226c3 Giorgos Korfiatis
        return self.filter(model.Q_PENDING | model.Q_APPROVED)
1359 05617ab9 Kostas Papadimitriou
1360 7184f408 Giorgos Korfiatis
    def user_visible_by_chain(self, flt):
1361 689226c3 Giorgos Korfiatis
        model = self.model
1362 3e3743f2 Giorgos Korfiatis
        pending = self.filter(model.Q_PENDING | model.Q_DENIED).values_list('chain')
1363 689226c3 Giorgos Korfiatis
        approved = self.filter(model.Q_APPROVED).values_list('chain')
1364 a3530159 Georgios D. Tsoukalas
        by_chain = dict(pending.annotate(models.Max('id')))
1365 a3530159 Georgios D. Tsoukalas
        by_chain.update(approved.annotate(models.Max('id')))
1366 7184f408 Giorgos Korfiatis
        return self.filter(flt, id__in=by_chain.values())
1367 05617ab9 Kostas Papadimitriou
1368 05617ab9 Kostas Papadimitriou
    def user_accessible_projects(self, user):
1369 5550bcfb Kostas Papadimitriou
        """
1370 5550bcfb Kostas Papadimitriou
        Return projects accessed by specified user.
1371 5550bcfb Kostas Papadimitriou
        """
1372 8e1a5af5 Georgios D. Tsoukalas
        if user.is_project_admin():
1373 8e1a5af5 Georgios D. Tsoukalas
            participates_filters = Q()
1374 8e1a5af5 Georgios D. Tsoukalas
        else:
1375 8e1a5af5 Georgios D. Tsoukalas
            participates_filters = Q(owner=user) | Q(applicant=user) | \
1376 8e1a5af5 Georgios D. Tsoukalas
                                   Q(project__projectmembership__person=user)
1377 05617ab9 Kostas Papadimitriou
1378 a3530159 Georgios D. Tsoukalas
        return self.user_visible_by_chain(participates_filters).order_by('issue_date').distinct()
1379 5550bcfb Kostas Papadimitriou
1380 a5cef8d0 Kostas Papadimitriou
    def search_by_name(self, *search_strings):
1381 a5cef8d0 Kostas Papadimitriou
        q = Q()
1382 a5cef8d0 Kostas Papadimitriou
        for s in search_strings:
1383 a5cef8d0 Kostas Papadimitriou
            q = q | Q(name__icontains=s)
1384 a5cef8d0 Kostas Papadimitriou
        return self.filter(q)
1385 a5cef8d0 Kostas Papadimitriou
1386 ff67242a Giorgos Korfiatis
    def latest_of_chain(self, chain_id):
1387 ff67242a Giorgos Korfiatis
        try:
1388 ff67242a Giorgos Korfiatis
            return self.filter(chain=chain_id).order_by('-id')[0]
1389 ff67242a Giorgos Korfiatis
        except IndexError:
1390 ff67242a Giorgos Korfiatis
            return None
1391 a5cef8d0 Kostas Papadimitriou
1392 a9ba418f Giorgos Korfiatis
1393 8aed306c Giorgos Korfiatis
class ProjectApplication(models.Model):
1394 425e2e95 Sofia Papagiannaki
    applicant               =   models.ForeignKey(
1395 425e2e95 Sofia Papagiannaki
                                    AstakosUser,
1396 d6fdc91e Georgios D. Tsoukalas
                                    related_name='projects_applied',
1397 d6fdc91e Georgios D. Tsoukalas
                                    db_index=True)
1398 d6fdc91e Georgios D. Tsoukalas
1399 d0e78bbe Giorgos Korfiatis
    PENDING     =    0
1400 d0e78bbe Giorgos Korfiatis
    APPROVED    =    1
1401 d0e78bbe Giorgos Korfiatis
    REPLACED    =    2
1402 d0e78bbe Giorgos Korfiatis
    DENIED      =    3
1403 3c638f72 Giorgos Korfiatis
    DISMISSED   =    4
1404 3c638f72 Giorgos Korfiatis
    CANCELLED   =    5
1405 d0e78bbe Giorgos Korfiatis
1406 69ab4df9 Giorgos Korfiatis
    state                   =   models.IntegerField(default=PENDING,
1407 69ab4df9 Giorgos Korfiatis
                                                    db_index=True)
1408 d6fdc91e Georgios D. Tsoukalas
1409 425e2e95 Sofia Papagiannaki
    owner                   =   models.ForeignKey(
1410 425e2e95 Sofia Papagiannaki
                                    AstakosUser,
1411 d6fdc91e Georgios D. Tsoukalas
                                    related_name='projects_owned',
1412 d6fdc91e Georgios D. Tsoukalas
                                    db_index=True)
1413 d6fdc91e Georgios D. Tsoukalas
1414 5195c0e9 Giorgos Korfiatis
    chain                   =   models.ForeignKey(Chain,
1415 5195c0e9 Giorgos Korfiatis
                                                  related_name='chained_apps',
1416 5195c0e9 Giorgos Korfiatis
                                                  db_column='chain')
1417 3c638f72 Giorgos Korfiatis
    precursor_application   =   models.ForeignKey('ProjectApplication',
1418 3c638f72 Giorgos Korfiatis
                                                  null=True,
1419 3c638f72 Giorgos Korfiatis
                                                  blank=True)
1420 425e2e95 Sofia Papagiannaki
1421 67980f56 Georgios D. Tsoukalas
    name                    =   models.CharField(max_length=80)
1422 94e49e22 Kostas Papadimitriou
    homepage                =   models.URLField(max_length=255, null=True,
1423 94e49e22 Kostas Papadimitriou
                                                verify_exists=False)
1424 67980f56 Georgios D. Tsoukalas
    description             =   models.TextField(null=True, blank=True)
1425 e729f165 Kostas Papadimitriou
    start_date              =   models.DateTimeField(null=True, blank=True)
1426 67980f56 Georgios D. Tsoukalas
    end_date                =   models.DateTimeField()
1427 272cf735 Sofia Papagiannaki
    member_join_policy      =   models.IntegerField()
1428 272cf735 Sofia Papagiannaki
    member_leave_policy     =   models.IntegerField()
1429 67980f56 Georgios D. Tsoukalas
    limit_on_members_number =   models.PositiveIntegerField(null=True)
1430 425e2e95 Sofia Papagiannaki
    resource_grants         =   models.ManyToManyField(
1431 425e2e95 Sofia Papagiannaki
                                    Resource,
1432 425e2e95 Sofia Papagiannaki
                                    null=True,
1433 425e2e95 Sofia Papagiannaki
                                    blank=True,
1434 d6fdc91e Georgios D. Tsoukalas
                                    through='ProjectResourceGrant')
1435 425e2e95 Sofia Papagiannaki
    comments                =   models.TextField(null=True, blank=True)
1436 3c638f72 Giorgos Korfiatis
    issue_date              =   models.DateTimeField(auto_now_add=True)
1437 3c638f72 Giorgos Korfiatis
    response_date           =   models.DateTimeField(null=True, blank=True)
1438 2b745492 Giorgos Korfiatis
    response                =   models.TextField(null=True, blank=True)
1439 6dcf53eb Kostas Papadimitriou
1440 5550bcfb Kostas Papadimitriou
    objects                 =   ProjectApplicationManager()
1441 7729e9cc Giorgos Korfiatis
1442 689226c3 Giorgos Korfiatis
    # Compiled queries
1443 689226c3 Giorgos Korfiatis
    Q_PENDING  = Q(state=PENDING)
1444 689226c3 Giorgos Korfiatis
    Q_APPROVED = Q(state=APPROVED)
1445 3e3743f2 Giorgos Korfiatis
    Q_DENIED   = Q(state=DENIED)
1446 689226c3 Giorgos Korfiatis
1447 c4892cd2 Sofia Papagiannaki
    class Meta:
1448 c4892cd2 Sofia Papagiannaki
        unique_together = ("chain", "id")
1449 c4892cd2 Sofia Papagiannaki
1450 f3a45fc6 Kostas Papadimitriou
    def __unicode__(self):
1451 f3a45fc6 Kostas Papadimitriou
        return "%s applied by %s" % (self.name, self.applicant)
1452 f3a45fc6 Kostas Papadimitriou
1453 d0e78bbe Giorgos Korfiatis
    # TODO: Move to a more suitable place
1454 9307cd46 Giorgos Korfiatis
    APPLICATION_STATE_DISPLAY = {
1455 3c638f72 Giorgos Korfiatis
        PENDING  : _('Pending review'),
1456 d77b32f2 Giorgos Korfiatis
        APPROVED : _('Approved'),
1457 3c638f72 Giorgos Korfiatis
        REPLACED : _('Replaced'),
1458 3c638f72 Giorgos Korfiatis
        DENIED   : _('Denied'),
1459 3c638f72 Giorgos Korfiatis
        DISMISSED: _('Dismissed'),
1460 3c638f72 Giorgos Korfiatis
        CANCELLED: _('Cancelled')
1461 bd9af366 Kostas Papadimitriou
    }
1462 d0e78bbe Giorgos Korfiatis
1463 f30f0170 Giorgos Korfiatis
    @property
1464 f30f0170 Giorgos Korfiatis
    def log_display(self):
1465 f30f0170 Giorgos Korfiatis
        return "application %s (%s) for project %s" % (
1466 f30f0170 Giorgos Korfiatis
            self.id, self.name, self.chain)
1467 f30f0170 Giorgos Korfiatis
1468 a3530159 Georgios D. Tsoukalas
    def get_project(self):
1469 a3530159 Georgios D. Tsoukalas
        try:
1470 a3530159 Georgios D. Tsoukalas
            project = Project.objects.get(id=self.chain, state=Project.APPROVED)
1471 a3530159 Georgios D. Tsoukalas
            return Project
1472 a3530159 Georgios D. Tsoukalas
        except Project.DoesNotExist, e:
1473 a3530159 Georgios D. Tsoukalas
            return None
1474 a3530159 Georgios D. Tsoukalas
1475 db9a498c Kostas Papadimitriou
    def state_display(self):
1476 9307cd46 Giorgos Korfiatis
        return self.APPLICATION_STATE_DISPLAY.get(self.state, _('Unknown'))
1477 db9a498c Kostas Papadimitriou
1478 d4660e00 Giorgos Korfiatis
    def project_state_display(self):
1479 d4660e00 Giorgos Korfiatis
        try:
1480 d4660e00 Giorgos Korfiatis
            project = self.project
1481 d4660e00 Giorgos Korfiatis
            return project.state_display()
1482 d4660e00 Giorgos Korfiatis
        except Project.DoesNotExist:
1483 d4660e00 Giorgos Korfiatis
            return self.state_display()
1484 d4660e00 Giorgos Korfiatis
1485 a7aba804 Sofia Papagiannaki
    def add_resource_policy(self, service, resource, uplimit):
1486 e1a80257 Sofia Papagiannaki
        """Raises ObjectDoesNotExist, IntegrityError"""
1487 a7aba804 Sofia Papagiannaki
        q = self.projectresourcegrant_set
1488 26551b92 Kostas Papadimitriou
        resource = Resource.objects.get(name=resource)
1489 a7aba804 Sofia Papagiannaki
        q.create(resource=resource, member_capacity=uplimit)
1490 e1a80257 Sofia Papagiannaki
1491 5550bcfb Kostas Papadimitriou
    def members_count(self):
1492 5550bcfb Kostas Papadimitriou
        return self.project.approved_memberships.count()
1493 5550bcfb Kostas Papadimitriou
1494 669cfe19 Olga Brani
    @property
1495 669cfe19 Olga Brani
    def grants(self):
1496 26551b92 Kostas Papadimitriou
        return self.projectresourcegrant_set.values('member_capacity',
1497 26551b92 Kostas Papadimitriou
                                                    'resource__name')
1498 5550bcfb Kostas Papadimitriou
1499 e1a80257 Sofia Papagiannaki
    @property
1500 e1a80257 Sofia Papagiannaki
    def resource_policies(self):
1501 b98e1df0 Sofia Papagiannaki
        return [str(rp) for rp in self.projectresourcegrant_set.all()]
1502 e1a80257 Sofia Papagiannaki
1503 e1a80257 Sofia Papagiannaki
    @resource_policies.setter
1504 e1a80257 Sofia Papagiannaki
    def resource_policies(self, policies):
1505 e1a80257 Sofia Papagiannaki
        for p in policies:
1506 e1a80257 Sofia Papagiannaki
            service = p.get('service', None)
1507 e1a80257 Sofia Papagiannaki
            resource = p.get('resource', None)
1508 e1a80257 Sofia Papagiannaki
            uplimit = p.get('uplimit', 0)
1509 a7aba804 Sofia Papagiannaki
            self.add_resource_policy(service, resource, uplimit)
1510 425e2e95 Sofia Papagiannaki
1511 a75dbd7b Giorgos Korfiatis
    def pending_modifications_incl_me(self):
1512 3e3743f2 Giorgos Korfiatis
        q = self.chained_applications()
1513 a75dbd7b Giorgos Korfiatis
        q = q.filter(Q(state=self.PENDING))
1514 3e3743f2 Giorgos Korfiatis
        return q
1515 ece3b66e Giorgos Korfiatis
1516 a75dbd7b Giorgos Korfiatis
    def last_pending_incl_me(self):
1517 a75dbd7b Giorgos Korfiatis
        try:
1518 a75dbd7b Giorgos Korfiatis
            return self.pending_modifications_incl_me().order_by('-id')[0]
1519 a75dbd7b Giorgos Korfiatis
        except IndexError:
1520 a75dbd7b Giorgos Korfiatis
            return None
1521 a75dbd7b Giorgos Korfiatis
1522 a75dbd7b Giorgos Korfiatis
    def pending_modifications(self):
1523 a75dbd7b Giorgos Korfiatis
        return self.pending_modifications_incl_me().filter(~Q(id=self.id))
1524 a75dbd7b Giorgos Korfiatis
1525 3e3743f2 Giorgos Korfiatis
    def last_pending(self):
1526 9b32e2fb Kostas Papadimitriou
        try:
1527 3e3743f2 Giorgos Korfiatis
            return self.pending_modifications().order_by('-id')[0]
1528 9b32e2fb Kostas Papadimitriou
        except IndexError:
1529 05617ab9 Kostas Papadimitriou
            return None
1530 05617ab9 Kostas Papadimitriou
1531 efc58b65 Kostas Papadimitriou
    def is_modification(self):
1532 d4660e00 Giorgos Korfiatis
        # if self.state != self.PENDING:
1533 d4660e00 Giorgos Korfiatis
        #     return False
1534 efc58b65 Kostas Papadimitriou
        parents = self.chained_applications().filter(id__lt=self.id)
1535 efc58b65 Kostas Papadimitriou
        parents = parents.filter(state__in=[self.APPROVED])
1536 efc58b65 Kostas Papadimitriou
        return parents.count() > 0
1537 efc58b65 Kostas Papadimitriou
1538 efc58b65 Kostas Papadimitriou
    def chained_applications(self):
1539 efc58b65 Kostas Papadimitriou
        return ProjectApplication.objects.filter(chain=self.chain)
1540 efc58b65 Kostas Papadimitriou
1541 2529745f Giorgos Korfiatis
    def is_latest(self):
1542 2529745f Giorgos Korfiatis
        return self.chained_applications().order_by('-id')[0] == self
1543 2529745f Giorgos Korfiatis
1544 05617ab9 Kostas Papadimitriou
    def has_pending_modifications(self):
1545 3e3743f2 Giorgos Korfiatis
        return bool(self.last_pending())
1546 05617ab9 Kostas Papadimitriou
1547 022cc8e2 Giorgos Korfiatis
    def denied_modifications(self):
1548 022cc8e2 Giorgos Korfiatis
        q = self.chained_applications()
1549 022cc8e2 Giorgos Korfiatis
        q = q.filter(Q(state=self.DENIED))
1550 022cc8e2 Giorgos Korfiatis
        q = q.filter(~Q(id=self.id))
1551 022cc8e2 Giorgos Korfiatis
        return q
1552 022cc8e2 Giorgos Korfiatis
1553 022cc8e2 Giorgos Korfiatis
    def last_denied(self):
1554 022cc8e2 Giorgos Korfiatis
        try:
1555 022cc8e2 Giorgos Korfiatis
            return self.denied_modifications().order_by('-id')[0]
1556 022cc8e2 Giorgos Korfiatis
        except IndexError:
1557 022cc8e2 Giorgos Korfiatis
            return None
1558 022cc8e2 Giorgos Korfiatis
1559 022cc8e2 Giorgos Korfiatis
    def has_denied_modifications(self):
1560 022cc8e2 Giorgos Korfiatis
        return bool(self.last_denied())
1561 022cc8e2 Giorgos Korfiatis
1562 2529745f Giorgos Korfiatis
    def is_applied(self):
1563 2529745f Giorgos Korfiatis
        try:
1564 2529745f Giorgos Korfiatis
            self.project
1565 2529745f Giorgos Korfiatis
            return True
1566 2529745f Giorgos Korfiatis
        except Project.DoesNotExist:
1567 2529745f Giorgos Korfiatis
            return False
1568 2529745f Giorgos Korfiatis
1569 05617ab9 Kostas Papadimitriou
    def get_project(self):
1570 05617ab9 Kostas Papadimitriou
        try:
1571 05617ab9 Kostas Papadimitriou
            return Project.objects.get(id=self.chain)
1572 05617ab9 Kostas Papadimitriou
        except Project.DoesNotExist:
1573 9b32e2fb Kostas Papadimitriou
            return None
1574 4f22664f Georgios D. Tsoukalas
1575 d74111be Giorgos Korfiatis
    def project_exists(self):
1576 d74111be Giorgos Korfiatis
        return self.get_project() is not None
1577 d74111be Giorgos Korfiatis
1578 b6fe8bb8 Giorgos Korfiatis
    def _get_project_for_update(self):
1579 a9ba418f Giorgos Korfiatis
        try:
1580 ea1e5d9f Giorgos Korfiatis
            objects = Project.objects
1581 ea1e5d9f Giorgos Korfiatis
            project = objects.get_for_update(id=self.chain)
1582 a9ba418f Giorgos Korfiatis
            return project
1583 a9ba418f Giorgos Korfiatis
        except Project.DoesNotExist:
1584 a9ba418f Giorgos Korfiatis
            return None
1585 4f22664f Georgios D. Tsoukalas
1586 01bdbd17 Giorgos Korfiatis
    def can_cancel(self):
1587 01bdbd17 Giorgos Korfiatis
        return self.state == self.PENDING
1588 01bdbd17 Giorgos Korfiatis
1589 3c638f72 Giorgos Korfiatis
    def cancel(self):
1590 01bdbd17 Giorgos Korfiatis
        if not self.can_cancel():
1591 3c638f72 Giorgos Korfiatis
            m = _("cannot cancel: application '%s' in state '%s'") % (
1592 3c638f72 Giorgos Korfiatis
                    self.id, self.state)
1593 3c638f72 Giorgos Korfiatis
            raise AssertionError(m)
1594 3c638f72 Giorgos Korfiatis
1595 3c638f72 Giorgos Korfiatis
        self.state = self.CANCELLED
1596 3c638f72 Giorgos Korfiatis
        self.save()
1597 3c638f72 Giorgos Korfiatis
1598 01bdbd17 Giorgos Korfiatis
    def can_dismiss(self):
1599 01bdbd17 Giorgos Korfiatis
        return self.state == self.DENIED
1600 01bdbd17 Giorgos Korfiatis
1601 3c638f72 Giorgos Korfiatis
    def dismiss(self):
1602 01bdbd17 Giorgos Korfiatis
        if not self.can_dismiss():
1603 3c638f72 Giorgos Korfiatis
            m = _("cannot dismiss: application '%s' in state '%s'") % (
1604 3c638f72 Giorgos Korfiatis
                    self.id, self.state)
1605 3c638f72 Giorgos Korfiatis
            raise AssertionError(m)
1606 3c638f72 Giorgos Korfiatis
1607 3c638f72 Giorgos Korfiatis
        self.state = self.DISMISSED
1608 3c638f72 Giorgos Korfiatis
        self.save()
1609 3c638f72 Giorgos Korfiatis
1610 01bdbd17 Giorgos Korfiatis
    def can_deny(self):
1611 01bdbd17 Giorgos Korfiatis
        return self.state == self.PENDING
1612 01bdbd17 Giorgos Korfiatis
1613 2b745492 Giorgos Korfiatis
    def deny(self, reason):
1614 01bdbd17 Giorgos Korfiatis
        if not self.can_deny():
1615 19eb3ee6 Giorgos Korfiatis
            m = _("cannot deny: application '%s' in state '%s'") % (
1616 19eb3ee6 Giorgos Korfiatis
                    self.id, self.state)
1617 19eb3ee6 Giorgos Korfiatis
            raise AssertionError(m)
1618 19eb3ee6 Giorgos Korfiatis
1619 19eb3ee6 Giorgos Korfiatis
        self.state = self.DENIED
1620 3c638f72 Giorgos Korfiatis
        self.response_date = datetime.now()
1621 2b745492 Giorgos Korfiatis
        self.response = reason
1622 19eb3ee6 Giorgos Korfiatis
        self.save()
1623 19eb3ee6 Giorgos Korfiatis
1624 01bdbd17 Giorgos Korfiatis
    def can_approve(self):
1625 01bdbd17 Giorgos Korfiatis
        return self.state == self.PENDING
1626 01bdbd17 Giorgos Korfiatis
1627 ccab6eb5 Sofia Papagiannaki
    def approve(self, approval_user=None):
1628 ccab6eb5 Sofia Papagiannaki
        """
1629 ccab6eb5 Sofia Papagiannaki
        If approval_user then during owner membership acceptance
1630 ccab6eb5 Sofia Papagiannaki
        it is checked whether the request_user is eligible.
1631 262e04c6 Giorgos Korfiatis

1632 2553efae Sofia Papagiannaki
        Raises:
1633 b8f05f8d Sofia Papagiannaki
            PermissionDenied
1634 ccab6eb5 Sofia Papagiannaki
        """
1635 4f22664f Georgios D. Tsoukalas
1636 4f22664f Georgios D. Tsoukalas
        if not transaction.is_managed():
1637 4f22664f Georgios D. Tsoukalas
            raise AssertionError("NOPE")
1638 4f22664f Georgios D. Tsoukalas
1639 73fbaec4 Sofia Papagiannaki
        new_project_name = self.name
1640 01bdbd17 Giorgos Korfiatis
        if not self.can_approve():
1641 65360c65 Georgios D. Tsoukalas
            m = _("cannot approve: project '%s' in state '%s'") % (
1642 65360c65 Georgios D. Tsoukalas
                    new_project_name, self.state)
1643 01bdbd17 Giorgos Korfiatis
            raise AssertionError(m) # invalid argument
1644 262e04c6 Giorgos Korfiatis
1645 fdafae27 Giorgos Korfiatis
        now = datetime.now()
1646 b6fe8bb8 Giorgos Korfiatis
        project = self._get_project_for_update()
1647 3cc9637a Giorgos Korfiatis
1648 99463445 Giorgos Korfiatis
        try:
1649 99463445 Giorgos Korfiatis
            q = Q(name=new_project_name) & ~Q(state=Project.TERMINATED)
1650 99463445 Giorgos Korfiatis
            conflicting_project = Project.objects.get(q)
1651 99463445 Giorgos Korfiatis
            if (conflicting_project != project):
1652 3cc9637a Giorgos Korfiatis
                m = (_("cannot approve: project with name '%s' "
1653 e1017df9 Giorgos Korfiatis
                       "already exists (id: %s)") % (
1654 3cc9637a Giorgos Korfiatis
                        new_project_name, conflicting_project.id))
1655 3cc9637a Giorgos Korfiatis
                raise PermissionDenied(m) # invalid argument
1656 99463445 Giorgos Korfiatis
        except Project.DoesNotExist:
1657 99463445 Giorgos Korfiatis
            pass
1658 3cc9637a Giorgos Korfiatis
1659 4bf02ea5 Giorgos Korfiatis
        new_project = False
1660 4f22664f Georgios D. Tsoukalas
        if project is None:
1661 4bf02ea5 Giorgos Korfiatis
            new_project = True
1662 3c638f72 Giorgos Korfiatis
            project = Project(id=self.chain)
1663 fdafae27 Giorgos Korfiatis
1664 3cc9637a Giorgos Korfiatis
        project.name = new_project_name
1665 ee45eb81 Giorgos Korfiatis
        project.application = self
1666 4bf02ea5 Giorgos Korfiatis
        project.last_approval_date = now
1667 4bf02ea5 Giorgos Korfiatis
1668 a769d7ba Sofia Papagiannaki
        project.save()
1669 425e2e95 Sofia Papagiannaki
1670 85d444db Sofia Papagiannaki
        self.state = self.APPROVED
1671 3c638f72 Giorgos Korfiatis
        self.response_date = now
1672 bfe23b13 Sofia Papagiannaki
        self.save()
1673 570015d2 Giorgos Korfiatis
        return project
1674 262e04c6 Giorgos Korfiatis
1675 b98e1df0 Sofia Papagiannaki
    @property
1676 b98e1df0 Sofia Papagiannaki
    def member_join_policy_display(self):
1677 b98e1df0 Sofia Papagiannaki
        return PROJECT_MEMBER_JOIN_POLICIES.get(str(self.member_join_policy))
1678 b98e1df0 Sofia Papagiannaki
1679 b98e1df0 Sofia Papagiannaki
    @property
1680 b98e1df0 Sofia Papagiannaki
    def member_leave_policy_display(self):
1681 b98e1df0 Sofia Papagiannaki
        return PROJECT_MEMBER_LEAVE_POLICIES.get(str(self.member_leave_policy))
1682 b98e1df0 Sofia Papagiannaki
1683 73fbaec4 Sofia Papagiannaki
class ProjectResourceGrant(models.Model):
1684 e1a80257 Sofia Papagiannaki
1685 425e2e95 Sofia Papagiannaki
    resource                =   models.ForeignKey(Resource)
1686 425e2e95 Sofia Papagiannaki
    project_application     =   models.ForeignKey(ProjectApplication,
1687 5200e864 Sofia Papagiannaki
                                                  null=True)
1688 0cdc5cc5 Giorgos Korfiatis
    project_capacity        =   intDecimalField(null=True)
1689 0cdc5cc5 Giorgos Korfiatis
    member_capacity         =   intDecimalField(default=0)
1690 73fbaec4 Sofia Papagiannaki
1691 73fbaec4 Sofia Papagiannaki
    objects = ExtendedManager()
1692 73fbaec4 Sofia Papagiannaki
1693 73fbaec4 Sofia Papagiannaki
    class Meta:
1694 73fbaec4 Sofia Papagiannaki
        unique_together = ("resource", "project_application")
1695 8327782d Sofia Papagiannaki
1696 b98e1df0 Sofia Papagiannaki
    def display_member_capacity(self):
1697 b98e1df0 Sofia Papagiannaki
        if self.member_capacity:
1698 b98e1df0 Sofia Papagiannaki
            if self.resource.unit:
1699 b98e1df0 Sofia Papagiannaki
                return ProjectResourceGrant.display_filesize(
1700 b98e1df0 Sofia Papagiannaki
                    self.member_capacity)
1701 b98e1df0 Sofia Papagiannaki
            else:
1702 b98e1df0 Sofia Papagiannaki
                if math.isinf(self.member_capacity):
1703 b98e1df0 Sofia Papagiannaki
                    return 'Unlimited'
1704 b98e1df0 Sofia Papagiannaki
                else:
1705 b98e1df0 Sofia Papagiannaki
                    return self.member_capacity
1706 b98e1df0 Sofia Papagiannaki
        else:
1707 b98e1df0 Sofia Papagiannaki
            return 'Unlimited'
1708 b98e1df0 Sofia Papagiannaki
1709 b98e1df0 Sofia Papagiannaki
    def __str__(self):
1710 b98e1df0 Sofia Papagiannaki
        return 'Max %s per user: %s' % (self.resource.pluralized_display_name,
1711 b98e1df0 Sofia Papagiannaki
                                        self.display_member_capacity())
1712 b98e1df0 Sofia Papagiannaki
1713 b98e1df0 Sofia Papagiannaki
    @classmethod
1714 b98e1df0 Sofia Papagiannaki
    def display_filesize(cls, value):
1715 b98e1df0 Sofia Papagiannaki
        try:
1716 b98e1df0 Sofia Papagiannaki
            value = float(value)
1717 b98e1df0 Sofia Papagiannaki
        except:
1718 b98e1df0 Sofia Papagiannaki
            return
1719 b98e1df0 Sofia Papagiannaki
        else:
1720 b98e1df0 Sofia Papagiannaki
            if math.isinf(value):
1721 b98e1df0 Sofia Papagiannaki
                return 'Unlimited'
1722 b98e1df0 Sofia Papagiannaki
            if value > 1:
1723 b98e1df0 Sofia Papagiannaki
                unit_list = zip(['bytes', 'kB', 'MB', 'GB', 'TB', 'PB'],
1724 b98e1df0 Sofia Papagiannaki
                                [0, 0, 0, 0, 0, 0])
1725 b98e1df0 Sofia Papagiannaki
                exponent = min(int(math.log(value, 1024)), len(unit_list) - 1)
1726 b98e1df0 Sofia Papagiannaki
                quotient = float(value) / 1024**exponent
1727 b98e1df0 Sofia Papagiannaki
                unit, value_decimals = unit_list[exponent]
1728 b98e1df0 Sofia Papagiannaki
                format_string = '{0:.%sf} {1}' % (value_decimals)
1729 b98e1df0 Sofia Papagiannaki
                return format_string.format(quotient, unit)
1730 b98e1df0 Sofia Papagiannaki
            if value == 0:
1731 b98e1df0 Sofia Papagiannaki
                return '0 bytes'
1732 b98e1df0 Sofia Papagiannaki
            if value == 1:
1733 b98e1df0 Sofia Papagiannaki
                return '1 byte'
1734 b98e1df0 Sofia Papagiannaki
            else:
1735 b98e1df0 Sofia Papagiannaki
               return '0'
1736 b98e1df0 Sofia Papagiannaki
1737 e546df49 Georgios D. Tsoukalas
1738 123be68a Giorgos Korfiatis
class ProjectManager(ForUpdateManager):
1739 123be68a Giorgos Korfiatis
1740 123be68a Giorgos Korfiatis
    def terminated_projects(self):
1741 689226c3 Giorgos Korfiatis
        q = self.model.Q_TERMINATED
1742 123be68a Giorgos Korfiatis
        return self.filter(q)
1743 123be68a Giorgos Korfiatis
1744 123be68a Giorgos Korfiatis
    def not_terminated_projects(self):
1745 689226c3 Giorgos Korfiatis
        q = ~self.model.Q_TERMINATED
1746 123be68a Giorgos Korfiatis
        return self.filter(q)
1747 123be68a Giorgos Korfiatis
1748 db99f198 Giorgos Korfiatis
    def deactivated_projects(self):
1749 689226c3 Giorgos Korfiatis
        q = self.model.Q_DEACTIVATED
1750 db99f198 Giorgos Korfiatis
        return self.filter(q)
1751 db99f198 Giorgos Korfiatis
1752 7eadc230 Giorgos Korfiatis
    def expired_projects(self):
1753 7eadc230 Giorgos Korfiatis
        q = (~Q(state=Project.TERMINATED) &
1754 7eadc230 Giorgos Korfiatis
              Q(application__end_date__lt=datetime.now()))
1755 7eadc230 Giorgos Korfiatis
        return self.filter(q)
1756 7eadc230 Giorgos Korfiatis
1757 d77b32f2 Giorgos Korfiatis
    def search_by_name(self, *search_strings):
1758 d77b32f2 Giorgos Korfiatis
        q = Q()
1759 d77b32f2 Giorgos Korfiatis
        for s in search_strings:
1760 d77b32f2 Giorgos Korfiatis
            q = q | Q(name__icontains=s)
1761 d77b32f2 Giorgos Korfiatis
        return self.filter(q)
1762 d77b32f2 Giorgos Korfiatis
1763 7eadc230 Giorgos Korfiatis
1764 d6fdc91e Georgios D. Tsoukalas
class Project(models.Model):
1765 e546df49 Georgios D. Tsoukalas
1766 5195c0e9 Giorgos Korfiatis
    id                          =   models.OneToOneField(Chain,
1767 5195c0e9 Giorgos Korfiatis
                                                      related_name='chained_project',
1768 5195c0e9 Giorgos Korfiatis
                                                      db_column='id',
1769 5195c0e9 Giorgos Korfiatis
                                                      primary_key=True)
1770 5195c0e9 Giorgos Korfiatis
1771 ee45eb81 Giorgos Korfiatis
    application                 =   models.OneToOneField(
1772 4f22664f Georgios D. Tsoukalas
                                            ProjectApplication,
1773 782d9118 Giorgos Korfiatis
                                            related_name='project')
1774 4f22664f Georgios D. Tsoukalas
    last_approval_date          =   models.DateTimeField(null=True)
1775 4f22664f Georgios D. Tsoukalas
1776 4f22664f Georgios D. Tsoukalas
    members                     =   models.ManyToManyField(
1777 4f22664f Georgios D. Tsoukalas
                                            AstakosUser,
1778 4f22664f Georgios D. Tsoukalas
                                            through='ProjectMembership')
1779 4f22664f Georgios D. Tsoukalas
1780 5b9e9530 Giorgos Korfiatis
    deactivation_reason         =   models.CharField(max_length=255, null=True)
1781 5b9e9530 Giorgos Korfiatis
    deactivation_date           =   models.DateTimeField(null=True)
1782 4f22664f Georgios D. Tsoukalas
1783 3c638f72 Giorgos Korfiatis
    creation_date               =   models.DateTimeField(auto_now_add=True)
1784 4f22664f Georgios D. Tsoukalas
    name                        =   models.CharField(
1785 4f22664f Georgios D. Tsoukalas
                                            max_length=80,
1786 e1017df9 Giorgos Korfiatis
                                            null=True,
1787 4f22664f Georgios D. Tsoukalas
                                            db_index=True,
1788 4f22664f Georgios D. Tsoukalas
                                            unique=True)
1789 425e2e95 Sofia Papagiannaki
1790 b6fe8bb8 Giorgos Korfiatis
    APPROVED    = 1
1791 b6fe8bb8 Giorgos Korfiatis
    SUSPENDED   = 10
1792 b6fe8bb8 Giorgos Korfiatis
    TERMINATED  = 100
1793 5b9e9530 Giorgos Korfiatis
1794 b6fe8bb8 Giorgos Korfiatis
    state                       =   models.IntegerField(default=APPROVED,
1795 123be68a Giorgos Korfiatis
                                                        db_index=True)
1796 123be68a Giorgos Korfiatis
1797 123be68a Giorgos Korfiatis
    objects     =   ProjectManager()
1798 7729e9cc Giorgos Korfiatis
1799 689226c3 Giorgos Korfiatis
    # Compiled queries
1800 689226c3 Giorgos Korfiatis
    Q_TERMINATED  = Q(state=TERMINATED)
1801 689226c3 Giorgos Korfiatis
    Q_SUSPENDED   = Q(state=SUSPENDED)
1802 689226c3 Giorgos Korfiatis
    Q_DEACTIVATED = Q_TERMINATED | Q_SUSPENDED
1803 689226c3 Giorgos Korfiatis
1804 8c7b8bb8 Giorgos Korfiatis
    def __str__(self):
1805 b6eaca30 Giorgos Korfiatis
        return uenc(_("<project %s '%s'>") %
1806 b6eaca30 Giorgos Korfiatis
                    (self.id, udec(self.application.name)))
1807 8c7b8bb8 Giorgos Korfiatis
1808 8c7b8bb8 Giorgos Korfiatis
    __repr__ = __str__
1809 8c7b8bb8 Giorgos Korfiatis
1810 b6eaca30 Giorgos Korfiatis
    def __unicode__(self):
1811 b6eaca30 Giorgos Korfiatis
        return _("<project %s '%s'>") % (self.id, self.application.name)
1812 b6eaca30 Giorgos Korfiatis
1813 e1f31e63 Giorgos Korfiatis
    STATE_DISPLAY = {
1814 d77b32f2 Giorgos Korfiatis
        APPROVED   : 'Active',
1815 d77b32f2 Giorgos Korfiatis
        SUSPENDED  : 'Suspended',
1816 d77b32f2 Giorgos Korfiatis
        TERMINATED : 'Terminated'
1817 e1f31e63 Giorgos Korfiatis
        }
1818 e1f31e63 Giorgos Korfiatis
1819 e1f31e63 Giorgos Korfiatis
    def state_display(self):
1820 e1f31e63 Giorgos Korfiatis
        return self.STATE_DISPLAY.get(self.state, _('Unknown'))
1821 e1f31e63 Giorgos Korfiatis
1822 7eadc230 Giorgos Korfiatis
    def expiration_info(self):
1823 7eadc230 Giorgos Korfiatis
        return (str(self.id), self.name, self.state_display(),
1824 7eadc230 Giorgos Korfiatis
                str(self.application.end_date))
1825 7eadc230 Giorgos Korfiatis
1826 b6fe8bb8 Giorgos Korfiatis
    def is_deactivated(self, reason=None):
1827 b6fe8bb8 Giorgos Korfiatis
        if reason is not None:
1828 b6fe8bb8 Giorgos Korfiatis
            return self.state == reason
1829 425e2e95 Sofia Papagiannaki
1830 b6fe8bb8 Giorgos Korfiatis
        return self.state != self.APPROVED
1831 123be68a Giorgos Korfiatis
1832 123be68a Giorgos Korfiatis
    ### Deactivation calls
1833 425e2e95 Sofia Papagiannaki
1834 123be68a Giorgos Korfiatis
    def terminate(self):
1835 123be68a Giorgos Korfiatis
        self.deactivation_reason = 'TERMINATED'
1836 3d4cef9e Giorgos Korfiatis
        self.deactivation_date = datetime.now()
1837 b6fe8bb8 Giorgos Korfiatis
        self.state = self.TERMINATED
1838 e1017df9 Giorgos Korfiatis
        self.name = None
1839 123be68a Giorgos Korfiatis
        self.save()
1840 8aed306c Giorgos Korfiatis
1841 db99f198 Giorgos Korfiatis
    def suspend(self):
1842 db99f198 Giorgos Korfiatis
        self.deactivation_reason = 'SUSPENDED'
1843 3d4cef9e Giorgos Korfiatis
        self.deactivation_date = datetime.now()
1844 db99f198 Giorgos Korfiatis
        self.state = self.SUSPENDED
1845 db99f198 Giorgos Korfiatis
        self.save()
1846 db99f198 Giorgos Korfiatis
1847 db99f198 Giorgos Korfiatis
    def resume(self):
1848 db99f198 Giorgos Korfiatis
        self.deactivation_reason = None
1849 3d4cef9e Giorgos Korfiatis
        self.deactivation_date = None
1850 db99f198 Giorgos Korfiatis
        self.state = self.APPROVED
1851 db99f198 Giorgos Korfiatis
        self.save()
1852 123be68a Giorgos Korfiatis
1853 123be68a Giorgos Korfiatis
    ### Logical checks
1854 425e2e95 Sofia Papagiannaki
1855 e1a80257 Sofia Papagiannaki
    def is_inconsistent(self):
1856 e1a80257 Sofia Papagiannaki
        now = datetime.now()
1857 5b9e9530 Giorgos Korfiatis
        dates = [self.creation_date,
1858 5b9e9530 Giorgos Korfiatis
                 self.last_approval_date,
1859 5b9e9530 Giorgos Korfiatis
                 self.deactivation_date]
1860 5b9e9530 Giorgos Korfiatis
        return any([date > now for date in dates])
1861 5b9e9530 Giorgos Korfiatis
1862 db99f198 Giorgos Korfiatis
    def is_approved(self):
1863 db99f198 Giorgos Korfiatis
        return self.state == self.APPROVED
1864 db99f198 Giorgos Korfiatis
1865 123be68a Giorgos Korfiatis
    @property
1866 123be68a Giorgos Korfiatis
    def is_alive(self):
1867 72a6e1e8 Giorgos Korfiatis
        return not self.is_terminated
1868 123be68a Giorgos Korfiatis
1869 123be68a Giorgos Korfiatis
    @property
1870 123be68a Giorgos Korfiatis
    def is_terminated(self):
1871 123be68a Giorgos Korfiatis
        return self.is_deactivated(self.TERMINATED)
1872 123be68a Giorgos Korfiatis
1873 123be68a Giorgos Korfiatis
    @property
1874 123be68a Giorgos Korfiatis
    def is_suspended(self):
1875 db99f198 Giorgos Korfiatis
        return self.is_deactivated(self.SUSPENDED)
1876 5b9e9530 Giorgos Korfiatis
1877 5b9e9530 Giorgos Korfiatis
    def violates_resource_grants(self):
1878 e1a80257 Sofia Papagiannaki
        return False
1879 65360c65 Georgios D. Tsoukalas
1880 5b9e9530 Giorgos Korfiatis
    def violates_members_limit(self, adding=0):
1881 5b9e9530 Giorgos Korfiatis
        application = self.application
1882 943d5554 Giorgos Korfiatis
        limit = application.limit_on_members_number
1883 022c61cd Sofia Papagiannaki
        if limit is None:
1884 022c61cd Sofia Papagiannaki
            return False
1885 022c61cd Sofia Papagiannaki
        return (len(self.approved_members) + adding > limit)
1886 5b9e9530 Giorgos Korfiatis
1887 123be68a Giorgos Korfiatis
1888 123be68a Giorgos Korfiatis
    ### Other
1889 5b9e9530 Giorgos Korfiatis
1890 7db8c163 Georgios D. Tsoukalas
    def count_pending_memberships(self):
1891 7db8c163 Georgios D. Tsoukalas
        memb_set = self.projectmembership_set
1892 7db8c163 Georgios D. Tsoukalas
        memb_count = memb_set.filter(state=ProjectMembership.REQUESTED).count()
1893 7db8c163 Georgios D. Tsoukalas
        return memb_count
1894 7db8c163 Georgios D. Tsoukalas
1895 d77b32f2 Giorgos Korfiatis
    def members_count(self):
1896 d77b32f2 Giorgos Korfiatis
        return self.approved_memberships.count()
1897 d77b32f2 Giorgos Korfiatis
1898 425e2e95 Sofia Papagiannaki
    @property
1899 425e2e95 Sofia Papagiannaki
    def approved_memberships(self):
1900 689226c3 Giorgos Korfiatis
        query = ProjectMembership.Q_ACCEPTED_STATES
1901 5b9e9530 Giorgos Korfiatis
        return self.projectmembership_set.filter(query)
1902 4f22664f Georgios D. Tsoukalas
1903 425e2e95 Sofia Papagiannaki
    @property
1904 425e2e95 Sofia Papagiannaki
    def approved_members(self):
1905 425e2e95 Sofia Papagiannaki
        return [m.person for m in self.approved_memberships]
1906 4f22664f Georgios D. Tsoukalas
1907 4f22664f Georgios D. Tsoukalas
    def add_member(self, user):
1908 bfe23b13 Sofia Papagiannaki
        """
1909 bfe23b13 Sofia Papagiannaki
        Raises:
1910 bfe23b13 Sofia Papagiannaki
            django.exceptions.PermissionDenied
1911 bfe23b13 Sofia Papagiannaki
            astakos.im.models.AstakosUser.DoesNotExist
1912 bfe23b13 Sofia Papagiannaki
        """
1913 8c7229a8 Giorgos Korfiatis
        if isinstance(user, (int, long)):
1914 4f22664f Georgios D. Tsoukalas
            user = AstakosUser.objects.get(user=user)
1915 4f22664f Georgios D. Tsoukalas
1916 ccab6eb5 Sofia Papagiannaki
        m, created = ProjectMembership.objects.get_or_create(
1917 ccab6eb5 Sofia Papagiannaki
            person=user, project=self
1918 2a965273 Sofia Papagiannaki
        )
1919 4f22664f Georgios D. Tsoukalas
        m.accept()
1920 ccab6eb5 Sofia Papagiannaki
1921 4f22664f Georgios D. Tsoukalas
    def remove_member(self, user):
1922 bfe23b13 Sofia Papagiannaki
        """
1923 bfe23b13 Sofia Papagiannaki
        Raises:
1924 bfe23b13 Sofia Papagiannaki
            django.exceptions.PermissionDenied
1925 bfe23b13 Sofia Papagiannaki
            astakos.im.models.AstakosUser.DoesNotExist
1926 bfe23b13 Sofia Papagiannaki
            astakos.im.models.ProjectMembership.DoesNotExist
1927 bfe23b13 Sofia Papagiannaki
        """
1928 8c7229a8 Giorgos Korfiatis
        if isinstance(user, (int, long)):
1929 4f22664f Georgios D. Tsoukalas
            user = AstakosUser.objects.get(user=user)
1930 4f22664f Georgios D. Tsoukalas
1931 bfe23b13 Sofia Papagiannaki
        m = ProjectMembership.objects.get(person=user, project=self)
1932 bfe23b13 Sofia Papagiannaki
        m.remove()
1933 4f22664f Georgios D. Tsoukalas
1934 425e2e95 Sofia Papagiannaki
1935 2529745f Giorgos Korfiatis
CHAIN_STATE = {
1936 2529745f Giorgos Korfiatis
    (Project.APPROVED,   ProjectApplication.PENDING)  : Chain.APPROVED_PENDING,
1937 2529745f Giorgos Korfiatis
    (Project.APPROVED,   ProjectApplication.APPROVED) : Chain.APPROVED,
1938 2529745f Giorgos Korfiatis
    (Project.APPROVED,   ProjectApplication.DENIED)   : Chain.APPROVED,
1939 2529745f Giorgos Korfiatis
    (Project.APPROVED,   ProjectApplication.DISMISSED): Chain.APPROVED,
1940 2529745f Giorgos Korfiatis
    (Project.APPROVED,   ProjectApplication.CANCELLED): Chain.APPROVED,
1941 2529745f Giorgos Korfiatis
1942 2529745f Giorgos Korfiatis
    (Project.SUSPENDED,  ProjectApplication.PENDING)  : Chain.SUSPENDED_PENDING,
1943 2529745f Giorgos Korfiatis
    (Project.SUSPENDED,  ProjectApplication.APPROVED) : Chain.SUSPENDED,
1944 2529745f Giorgos Korfiatis
    (Project.SUSPENDED,  ProjectApplication.DENIED)   : Chain.SUSPENDED,
1945 2529745f Giorgos Korfiatis
    (Project.SUSPENDED,  ProjectApplication.DISMISSED): Chain.SUSPENDED,
1946 2529745f Giorgos Korfiatis
    (Project.SUSPENDED,  ProjectApplication.CANCELLED): Chain.SUSPENDED,
1947 2529745f Giorgos Korfiatis
1948 2529745f Giorgos Korfiatis
    (Project.TERMINATED, ProjectApplication.PENDING)  : Chain.TERMINATED_PENDING,
1949 2529745f Giorgos Korfiatis
    (Project.TERMINATED, ProjectApplication.APPROVED) : Chain.TERMINATED,
1950 2529745f Giorgos Korfiatis
    (Project.TERMINATED, ProjectApplication.DENIED)   : Chain.TERMINATED,
1951 2529745f Giorgos Korfiatis
    (Project.TERMINATED, ProjectApplication.DISMISSED): Chain.TERMINATED,
1952 2529745f Giorgos Korfiatis
    (Project.TERMINATED, ProjectApplication.CANCELLED): Chain.TERMINATED,
1953 2529745f Giorgos Korfiatis
1954 2529745f Giorgos Korfiatis
    (None,               ProjectApplication.PENDING)  : Chain.PENDING,
1955 2529745f Giorgos Korfiatis
    (None,               ProjectApplication.DENIED)   : Chain.DENIED,
1956 2529745f Giorgos Korfiatis
    (None,               ProjectApplication.DISMISSED): Chain.DISMISSED,
1957 2529745f Giorgos Korfiatis
    (None,               ProjectApplication.CANCELLED): Chain.CANCELLED,
1958 2529745f Giorgos Korfiatis
    }
1959 2529745f Giorgos Korfiatis
1960 2529745f Giorgos Korfiatis
1961 db99f198 Giorgos Korfiatis
class ProjectMembershipManager(ForUpdateManager):
1962 d77b32f2 Giorgos Korfiatis
1963 d77b32f2 Giorgos Korfiatis
    def any_accepted(self):
1964 96efed67 Giorgos Korfiatis
        q = self.model.Q_ACTUALLY_ACCEPTED
1965 d77b32f2 Giorgos Korfiatis
        return self.filter(q)
1966 d77b32f2 Giorgos Korfiatis
1967 c1007621 Giorgos Korfiatis
    def actually_accepted(self):
1968 c1007621 Giorgos Korfiatis
        q = self.model.Q_ACTUALLY_ACCEPTED
1969 c1007621 Giorgos Korfiatis
        return self.filter(q)
1970 c1007621 Giorgos Korfiatis
1971 d77b32f2 Giorgos Korfiatis
    def requested(self):
1972 d77b32f2 Giorgos Korfiatis
        return self.filter(state=ProjectMembership.REQUESTED)
1973 d77b32f2 Giorgos Korfiatis
1974 d77b32f2 Giorgos Korfiatis
    def suspended(self):
1975 d77b32f2 Giorgos Korfiatis
        return self.filter(state=ProjectMembership.USER_SUSPENDED)
1976 db99f198 Giorgos Korfiatis
1977 d6fdc91e Georgios D. Tsoukalas
class ProjectMembership(models.Model):
1978 4f22664f Georgios D. Tsoukalas
1979 425e2e95 Sofia Papagiannaki
    person              =   models.ForeignKey(AstakosUser)
1980 3c638f72 Giorgos Korfiatis
    request_date        =   models.DateField(auto_now_add=True)
1981 d6fdc91e Georgios D. Tsoukalas
    project             =   models.ForeignKey(Project)
1982 d6fdc91e Georgios D. Tsoukalas
1983 db99f198 Giorgos Korfiatis
    REQUESTED           =   0
1984 db99f198 Giorgos Korfiatis
    ACCEPTED            =   1
1985 c1007621 Giorgos Korfiatis
    LEAVE_REQUESTED     =   5
1986 db99f198 Giorgos Korfiatis
    # User deactivation
1987 db99f198 Giorgos Korfiatis
    USER_SUSPENDED      =   10
1988 b6fe8bb8 Giorgos Korfiatis
1989 db99f198 Giorgos Korfiatis
    REMOVED             =   200
1990 db99f198 Giorgos Korfiatis
1991 db99f198 Giorgos Korfiatis
    ASSOCIATED_STATES   =   set([REQUESTED,
1992 db99f198 Giorgos Korfiatis
                                 ACCEPTED,
1993 c1007621 Giorgos Korfiatis
                                 LEAVE_REQUESTED,
1994 db99f198 Giorgos Korfiatis
                                 USER_SUSPENDED,
1995 96efed67 Giorgos Korfiatis
                                 ])
1996 db99f198 Giorgos Korfiatis
1997 db99f198 Giorgos Korfiatis
    ACCEPTED_STATES     =   set([ACCEPTED,
1998 c1007621 Giorgos Korfiatis
                                 LEAVE_REQUESTED,
1999 db99f198 Giorgos Korfiatis
                                 USER_SUSPENDED,
2000 96efed67 Giorgos Korfiatis
                                 ])
2001 05617ab9 Kostas Papadimitriou
2002 c1007621 Giorgos Korfiatis
    ACTUALLY_ACCEPTED   =   set([ACCEPTED, LEAVE_REQUESTED])
2003 c1007621 Giorgos Korfiatis
2004 b6fe8bb8 Giorgos Korfiatis
    state               =   models.IntegerField(default=REQUESTED,
2005 b6fe8bb8 Giorgos Korfiatis
                                                db_index=True)
2006 425e2e95 Sofia Papagiannaki
    acceptance_date     =   models.DateField(null=True, db_index=True)
2007 425e2e95 Sofia Papagiannaki
    leave_request_date  =   models.DateField(null=True)
2008 2a965273 Sofia Papagiannaki
2009 db99f198 Giorgos Korfiatis
    objects     =   ProjectMembershipManager()
2010 ee45eb81 Giorgos Korfiatis
2011 689226c3 Giorgos Korfiatis
    # Compiled queries
2012 689226c3 Giorgos Korfiatis
    Q_ACCEPTED_STATES = ~Q(state=REQUESTED) & ~Q(state=REMOVED)
2013 c1007621 Giorgos Korfiatis
    Q_ACTUALLY_ACCEPTED = Q(state=ACCEPTED) | Q(state=LEAVE_REQUESTED)
2014 5b9e9530 Giorgos Korfiatis
2015 d77b32f2 Giorgos Korfiatis
    MEMBERSHIP_STATE_DISPLAY = {
2016 d4660e00 Giorgos Korfiatis
        REQUESTED           : _('Requested'),
2017 d4660e00 Giorgos Korfiatis
        ACCEPTED            : _('Accepted'),
2018 c1007621 Giorgos Korfiatis
        LEAVE_REQUESTED     : _('Leave Requested'),
2019 d4660e00 Giorgos Korfiatis
        USER_SUSPENDED      : _('Suspended'),
2020 d4660e00 Giorgos Korfiatis
        REMOVED             : _('Pending removal'),
2021 d4660e00 Giorgos Korfiatis
        }
2022 d4660e00 Giorgos Korfiatis
2023 d4660e00 Giorgos Korfiatis
    USER_FRIENDLY_STATE_DISPLAY = {
2024 d4660e00 Giorgos Korfiatis
        REQUESTED           : _('Join requested'),
2025 d4660e00 Giorgos Korfiatis
        ACCEPTED            : _('Accepted member'),
2026 c1007621 Giorgos Korfiatis
        LEAVE_REQUESTED     : _('Requested to leave'),
2027 d4660e00 Giorgos Korfiatis
        USER_SUSPENDED      : _('Suspended member'),
2028 d4660e00 Giorgos Korfiatis
        REMOVED             : _('Pending removal'),
2029 d77b32f2 Giorgos Korfiatis
        }
2030 d77b32f2 Giorgos Korfiatis
2031 d77b32f2 Giorgos Korfiatis
    def state_display(self):
2032 d77b32f2 Giorgos Korfiatis
        return self.MEMBERSHIP_STATE_DISPLAY.get(self.state, _('Unknown'))
2033 d77b32f2 Giorgos Korfiatis
2034 d4660e00 Giorgos Korfiatis
    def user_friendly_state_display(self):
2035 d4660e00 Giorgos Korfiatis
        return self.USER_FRIENDLY_STATE_DISPLAY.get(self.state, _('Unknown'))
2036 d4660e00 Giorgos Korfiatis
2037 0cc22d47 Sofia Papagiannaki
    class Meta:
2038 0cc22d47 Sofia Papagiannaki
        unique_together = ("person", "project")
2039 d6fdc91e Georgios D. Tsoukalas
        #index_together = [["project", "state"]]
2040 bfe23b13 Sofia Papagiannaki
2041 65360c65 Georgios D. Tsoukalas
    def __str__(self):
2042 b6eaca30 Giorgos Korfiatis
        return uenc(_("<'%s' membership in '%s'>") % (
2043 b6eaca30 Giorgos Korfiatis
                self.person.username, self.project))
2044 65360c65 Georgios D. Tsoukalas
2045 65360c65 Georgios D. Tsoukalas
    __repr__ = __str__
2046 65360c65 Georgios D. Tsoukalas
2047 65360c65 Georgios D. Tsoukalas
    def __init__(self, *args, **kwargs):
2048 ee45eb81 Giorgos Korfiatis
        self.state = self.REQUESTED
2049 65360c65 Georgios D. Tsoukalas
        super(ProjectMembership, self).__init__(*args, **kwargs)
2050 65360c65 Georgios D. Tsoukalas
2051 4f22664f Georgios D. Tsoukalas
    def _set_history_item(self, reason, date=None):
2052 4f22664f Georgios D. Tsoukalas
        if isinstance(reason, basestring):
2053 4f22664f Georgios D. Tsoukalas
            reason = ProjectMembershipHistory.reasons.get(reason, -1)
2054 4f22664f Georgios D. Tsoukalas
2055 4f22664f Georgios D. Tsoukalas
        history_item = ProjectMembershipHistory(
2056 4f22664f Georgios D. Tsoukalas
                            serial=self.id,
2057 d0e78bbe Giorgos Korfiatis
                            person=self.person_id,
2058 02d2519e Giorgos Korfiatis
                            project=self.project_id,
2059 8f975b72 Sofia Papagiannaki
                            date=date or datetime.now(),
2060 4f22664f Georgios D. Tsoukalas
                            reason=reason)
2061 4f22664f Georgios D. Tsoukalas
        history_item.save()
2062 4f22664f Georgios D. Tsoukalas
        serial = history_item.id
2063 4f22664f Georgios D. Tsoukalas
2064 14f7f6a5 Giorgos Korfiatis
    def can_accept(self):
2065 14f7f6a5 Giorgos Korfiatis
        return self.state == self.REQUESTED
2066 14f7f6a5 Giorgos Korfiatis
2067 4f22664f Georgios D. Tsoukalas
    def accept(self):
2068 14f7f6a5 Giorgos Korfiatis
        if not self.can_accept():
2069 14f7f6a5 Giorgos Korfiatis
            m = _("%s: attempt to accept in state '%s'") % (self, self.state)
2070 65360c65 Georgios D. Tsoukalas
            raise AssertionError(m)
2071 4f22664f Georgios D. Tsoukalas
2072 65360c65 Georgios D. Tsoukalas
        now = datetime.now()
2073 65360c65 Georgios D. Tsoukalas
        self.acceptance_date = now
2074 65360c65 Georgios D. Tsoukalas
        self._set_history_item(reason='ACCEPT', date=now)
2075 96efed67 Giorgos Korfiatis
        self.state = self.ACCEPTED
2076 65360c65 Georgios D. Tsoukalas
        self.save()
2077 4f22664f Georgios D. Tsoukalas
2078 14f7f6a5 Giorgos Korfiatis
    def can_leave(self):
2079 c1007621 Giorgos Korfiatis
        return self.state in self.ACCEPTED_STATES
2080 c1007621 Giorgos Korfiatis
2081 c1007621 Giorgos Korfiatis
    def leave_request(self):
2082 c1007621 Giorgos Korfiatis
        if not self.can_leave():
2083 c1007621 Giorgos Korfiatis
            m = _("%s: attempt to request to leave in state '%s'") % (
2084 c1007621 Giorgos Korfiatis
                self, self.state)
2085 c1007621 Giorgos Korfiatis
            raise AssertionError(m)
2086 c1007621 Giorgos Korfiatis
2087 c1007621 Giorgos Korfiatis
        self.leave_request_date = datetime.now()
2088 c1007621 Giorgos Korfiatis
        self.state = self.LEAVE_REQUESTED
2089 c1007621 Giorgos Korfiatis
        self.save()
2090 c1007621 Giorgos Korfiatis
2091 c1007621 Giorgos Korfiatis
    def can_deny_leave(self):
2092 c1007621 Giorgos Korfiatis
        return self.state == self.LEAVE_REQUESTED
2093 c1007621 Giorgos Korfiatis
2094 c1007621 Giorgos Korfiatis
    def leave_request_deny(self):
2095 c1007621 Giorgos Korfiatis
        if not self.can_deny_leave():
2096 c1007621 Giorgos Korfiatis
            m = _("%s: attempt to deny leave request in state '%s'") % (
2097 c1007621 Giorgos Korfiatis
                self, self.state)
2098 c1007621 Giorgos Korfiatis
            raise AssertionError(m)
2099 c1007621 Giorgos Korfiatis
2100 c1007621 Giorgos Korfiatis
        self.leave_request_date = None
2101 c1007621 Giorgos Korfiatis
        self.state = self.ACCEPTED
2102 c1007621 Giorgos Korfiatis
        self.save()
2103 c1007621 Giorgos Korfiatis
2104 c1007621 Giorgos Korfiatis
    def can_cancel_leave(self):
2105 c1007621 Giorgos Korfiatis
        return self.state == self.LEAVE_REQUESTED
2106 c1007621 Giorgos Korfiatis
2107 c1007621 Giorgos Korfiatis
    def leave_request_cancel(self):
2108 c1007621 Giorgos Korfiatis
        if not self.can_cancel_leave():
2109 c1007621 Giorgos Korfiatis
            m = _("%s: attempt to cancel leave request in state '%s'") % (
2110 c1007621 Giorgos Korfiatis
                self, self.state)
2111 c1007621 Giorgos Korfiatis
            raise AssertionError(m)
2112 c1007621 Giorgos Korfiatis
2113 c1007621 Giorgos Korfiatis
        self.leave_request_date = None
2114 c1007621 Giorgos Korfiatis
        self.state = self.ACCEPTED
2115 c1007621 Giorgos Korfiatis
        self.save()
2116 14f7f6a5 Giorgos Korfiatis
2117 14f7f6a5 Giorgos Korfiatis
    def can_remove(self):
2118 14f7f6a5 Giorgos Korfiatis
        return self.state in self.ACCEPTED_STATES
2119 14f7f6a5 Giorgos Korfiatis
2120 65360c65 Georgios D. Tsoukalas
    def remove(self):
2121 14f7f6a5 Giorgos Korfiatis
        if not self.can_remove():
2122 14f7f6a5 Giorgos Korfiatis
            m = _("%s: attempt to remove in state '%s'") % (self, self.state)
2123 65360c65 Georgios D. Tsoukalas
            raise AssertionError(m)
2124 4f22664f Georgios D. Tsoukalas
2125 ee45eb81 Giorgos Korfiatis
        self._set_history_item(reason='REMOVE')
2126 8cbea11d Giorgos Korfiatis
        self.delete()
2127 b8f05f8d Sofia Papagiannaki
2128 14f7f6a5 Giorgos Korfiatis
    def can_reject(self):
2129 14f7f6a5 Giorgos Korfiatis
        return self.state == self.REQUESTED
2130 14f7f6a5 Giorgos Korfiatis
2131 65360c65 Georgios D. Tsoukalas
    def reject(self):
2132 14f7f6a5 Giorgos Korfiatis
        if not self.can_reject():
2133 14f7f6a5 Giorgos Korfiatis
            m = _("%s: attempt to reject in state '%s'") % (self, self.state)
2134 65360c65 Georgios D. Tsoukalas
            raise AssertionError(m)
2135 65360c65 Georgios D. Tsoukalas
2136 65360c65 Georgios D. Tsoukalas
        # rejected requests don't need sync,
2137 65360c65 Georgios D. Tsoukalas
        # because they were never effected
2138 65360c65 Georgios D. Tsoukalas
        self._set_history_item(reason='REJECT')
2139 0cc22d47 Sofia Papagiannaki
        self.delete()
2140 b8f05f8d Sofia Papagiannaki
2141 aad0e329 Giorgos Korfiatis
    def can_cancel(self):
2142 aad0e329 Giorgos Korfiatis
        return self.state == self.REQUESTED
2143 aad0e329 Giorgos Korfiatis
2144 aad0e329 Giorgos Korfiatis
    def cancel(self):
2145 aad0e329 Giorgos Korfiatis
        if not self.can_cancel():
2146 aad0e329 Giorgos Korfiatis
            m = _("%s: attempt to cancel in state '%s'") % (self, self.state)
2147 aad0e329 Giorgos Korfiatis
            raise AssertionError(m)
2148 aad0e329 Giorgos Korfiatis
2149 aad0e329 Giorgos Korfiatis
        # rejected requests don't need sync,
2150 aad0e329 Giorgos Korfiatis
        # because they were never effected
2151 aad0e329 Giorgos Korfiatis
        self._set_history_item(reason='CANCEL')
2152 aad0e329 Giorgos Korfiatis
        self.delete()
2153 aad0e329 Giorgos Korfiatis
2154 49b74233 Georgios D. Tsoukalas
2155 ee45eb81 Giorgos Korfiatis
class Serial(models.Model):
2156 ee45eb81 Giorgos Korfiatis
    serial  =   models.AutoField(primary_key=True)
2157 ee45eb81 Giorgos Korfiatis
2158 60ca2f6f Giorgos Korfiatis
2159 0cc22d47 Sofia Papagiannaki
class ProjectMembershipHistory(models.Model):
2160 425e2e95 Sofia Papagiannaki
    reasons_list    =   ['ACCEPT', 'REJECT', 'REMOVE']
2161 425e2e95 Sofia Papagiannaki
    reasons         =   dict((k, v) for v, k in enumerate(reasons_list))
2162 425e2e95 Sofia Papagiannaki
2163 d0e78bbe Giorgos Korfiatis
    person  =   models.BigIntegerField()
2164 02d2519e Giorgos Korfiatis
    project =   models.BigIntegerField()
2165 3c638f72 Giorgos Korfiatis
    date    =   models.DateField(auto_now_add=True)
2166 425e2e95 Sofia Papagiannaki
    reason  =   models.IntegerField()
2167 425e2e95 Sofia Papagiannaki
    serial  =   models.BigIntegerField()
2168 fc655b6f Kostas Papadimitriou
2169 fcc1e93f Sofia Papagiannaki
### SIGNALS ###
2170 fcc1e93f Sofia Papagiannaki
################
2171 b22de10a Sofia Papagiannaki
2172 ff9290ec Sofia Papagiannaki
def create_astakos_user(u):
2173 ff9290ec Sofia Papagiannaki
    try:
2174 ff9290ec Sofia Papagiannaki
        AstakosUser.objects.get(user_ptr=u.pk)
2175 ff9290ec Sofia Papagiannaki
    except AstakosUser.DoesNotExist:
2176 ff9290ec Sofia Papagiannaki
        extended_user = AstakosUser(user_ptr_id=u.pk)
2177 ff9290ec Sofia Papagiannaki
        extended_user.__dict__.update(u.__dict__)
2178 ff9290ec Sofia Papagiannaki
        extended_user.save()
2179 67be1883 Olga Brani
        if not extended_user.has_auth_provider('local'):
2180 67be1883 Olga Brani
            extended_user.add_auth_provider('local')
2181 fc1e2f02 Sofia Papagiannaki
    except BaseException, e:
2182 fc1e2f02 Sofia Papagiannaki
        logger.exception(e)
2183 ff9290ec Sofia Papagiannaki
2184 a7752e95 Sofia Papagiannaki
def fix_superusers():
2185 fc1e2f02 Sofia Papagiannaki
    # Associate superusers with AstakosUser
2186 ff9290ec Sofia Papagiannaki
    admins = User.objects.filter(is_superuser=True)
2187 ff9290ec Sofia Papagiannaki
    for u in admins:
2188 ff9290ec Sofia Papagiannaki
        create_astakos_user(u)
2189 ff9290ec Sofia Papagiannaki
2190 fc1e2f02 Sofia Papagiannaki
def user_post_save(sender, instance, created, **kwargs):
2191 aa4109d4 Sofia Papagiannaki
    if not created:
2192 aa4109d4 Sofia Papagiannaki
        return
2193 fc1e2f02 Sofia Papagiannaki
    create_astakos_user(instance)
2194 bfe23b13 Sofia Papagiannaki
post_save.connect(user_post_save, sender=User)
2195 ff9290ec Sofia Papagiannaki
2196 fc1e2f02 Sofia Papagiannaki
def astakosuser_post_save(sender, instance, created, **kwargs):
2197 21e0fdad Giorgos Korfiatis
    pass
2198 21e0fdad Giorgos Korfiatis
2199 bfe23b13 Sofia Papagiannaki
post_save.connect(astakosuser_post_save, sender=AstakosUser)
2200 fc1e2f02 Sofia Papagiannaki
2201 bd4f356c Sofia Papagiannaki
def resource_post_save(sender, instance, created, **kwargs):
2202 0514bcc7 Giorgos Korfiatis
    pass
2203 0514bcc7 Giorgos Korfiatis
2204 bfe23b13 Sofia Papagiannaki
post_save.connect(resource_post_save, sender=Resource)
2205 bfe23b13 Sofia Papagiannaki
2206 bfe23b13 Sofia Papagiannaki
def renew_token(sender, instance, **kwargs):
2207 bfe23b13 Sofia Papagiannaki
    if not instance.auth_token:
2208 bfe23b13 Sofia Papagiannaki
        instance.renew_token()
2209 bf0c6de5 Sofia Papagiannaki
pre_save.connect(renew_token, sender=AstakosUser)
2210 2e90e3ec Kostas Papadimitriou
pre_save.connect(renew_token, sender=Service)