Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / models.py @ 2e7924de

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

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

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

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

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

1039 49790d9d Sofia Papagiannaki
        Throws ValueError if there is already
1040 49790d9d Sofia Papagiannaki
        """
1041 49790d9d Sofia Papagiannaki
        try:
1042 5ce3ce4f Sofia Papagiannaki
            email_change = self.model.objects.get(
1043 5ce3ce4f Sofia Papagiannaki
                activation_key=activation_key)
1044 49790d9d Sofia Papagiannaki
            if email_change.activation_key_expired():
1045 49790d9d Sofia Papagiannaki
                email_change.delete()
1046 49790d9d Sofia Papagiannaki
                raise EmailChange.DoesNotExist
1047 49790d9d Sofia Papagiannaki
            # is there an active user with this address?
1048 49790d9d Sofia Papagiannaki
            try:
1049 789a5951 Sofia Papagiannaki
                AstakosUser.objects.get(email__iexact=email_change.new_email_address)
1050 49790d9d Sofia Papagiannaki
            except AstakosUser.DoesNotExist:
1051 49790d9d Sofia Papagiannaki
                pass
1052 49790d9d Sofia Papagiannaki
            else:
1053 73fbaec4 Sofia Papagiannaki
                raise ValueError(_('The new email address is reserved.'))
1054 49790d9d Sofia Papagiannaki
            # update user
1055 49790d9d Sofia Papagiannaki
            user = AstakosUser.objects.get(pk=email_change.user_id)
1056 34a76cdb Kostas Papadimitriou
            old_email = user.email
1057 49790d9d Sofia Papagiannaki
            user.email = email_change.new_email_address
1058 49790d9d Sofia Papagiannaki
            user.save()
1059 49790d9d Sofia Papagiannaki
            email_change.delete()
1060 5d5ce247 Kostas Papadimitriou
            msg = "User %s changed email from %s to %s" % (user.log_display,
1061 5d5ce247 Kostas Papadimitriou
                                                           old_email,
1062 5d5ce247 Kostas Papadimitriou
                                                           user.email)
1063 34a76cdb Kostas Papadimitriou
            logger.log(LOGGING_LEVEL, msg)
1064 49790d9d Sofia Papagiannaki
            return user
1065 49790d9d Sofia Papagiannaki
        except EmailChange.DoesNotExist:
1066 73fbaec4 Sofia Papagiannaki
            raise ValueError(_('Invalid activation key.'))
1067 49790d9d Sofia Papagiannaki
1068 49790d9d Sofia Papagiannaki
1069 49790d9d Sofia Papagiannaki
class EmailChange(models.Model):
1070 73fbaec4 Sofia Papagiannaki
    new_email_address = models.EmailField(
1071 73fbaec4 Sofia Papagiannaki
        _(u'new e-mail address'),
1072 b8b8fdde Constantinos Venetsanopoulos
        help_text=_('Provide a new email address. Until you verify the new '
1073 b8b8fdde Constantinos Venetsanopoulos
                    'address by following the activation link that will be '
1074 b8b8fdde Constantinos Venetsanopoulos
                    'sent to it, your old email address will remain active.'))
1075 5ce3ce4f Sofia Papagiannaki
    user = models.ForeignKey(
1076 34a76cdb Kostas Papadimitriou
        AstakosUser, unique=True, related_name='emailchanges')
1077 3c638f72 Giorgos Korfiatis
    requested_at = models.DateTimeField(auto_now_add=True)
1078 5ce3ce4f Sofia Papagiannaki
    activation_key = models.CharField(
1079 5ce3ce4f Sofia Papagiannaki
        max_length=40, unique=True, db_index=True)
1080 49790d9d Sofia Papagiannaki
1081 49790d9d Sofia Papagiannaki
    objects = EmailChangeManager()
1082 49790d9d Sofia Papagiannaki
1083 34a76cdb Kostas Papadimitriou
    def get_url(self):
1084 34a76cdb Kostas Papadimitriou
        return reverse('email_change_confirm',
1085 34a76cdb Kostas Papadimitriou
                      kwargs={'activation_key': self.activation_key})
1086 34a76cdb Kostas Papadimitriou
1087 49790d9d Sofia Papagiannaki
    def activation_key_expired(self):
1088 49790d9d Sofia Papagiannaki
        expiration_date = timedelta(days=EMAILCHANGE_ACTIVATION_DAYS)
1089 ff9290ec Sofia Papagiannaki
        return self.requested_at + expiration_date < datetime.now()
1090 ff9290ec Sofia Papagiannaki
1091 6b03a847 Sofia Papagiannaki
1092 ca828a10 Sofia Papagiannaki
class AdditionalMail(models.Model):
1093 ca828a10 Sofia Papagiannaki
    """
1094 ca828a10 Sofia Papagiannaki
    Model for registring invitations
1095 ca828a10 Sofia Papagiannaki
    """
1096 ca828a10 Sofia Papagiannaki
    owner = models.ForeignKey(AstakosUser)
1097 1eec103a Sofia Papagiannaki
    email = models.EmailField()
1098 ca828a10 Sofia Papagiannaki
1099 5ce3ce4f Sofia Papagiannaki
1100 fc1e2f02 Sofia Papagiannaki
def _generate_invitation_code():
1101 fc1e2f02 Sofia Papagiannaki
    while True:
1102 5ce3ce4f Sofia Papagiannaki
        code = randint(1, 2L ** 63 - 1)
1103 fc1e2f02 Sofia Papagiannaki
        try:
1104 fc1e2f02 Sofia Papagiannaki
            Invitation.objects.get(code=code)
1105 fc1e2f02 Sofia Papagiannaki
            # An invitation with this code already exists, try again
1106 fc1e2f02 Sofia Papagiannaki
        except Invitation.DoesNotExist:
1107 fc1e2f02 Sofia Papagiannaki
            return code
1108 fc1e2f02 Sofia Papagiannaki
1109 5ce3ce4f Sofia Papagiannaki
1110 fc1e2f02 Sofia Papagiannaki
def get_latest_terms():
1111 fc1e2f02 Sofia Papagiannaki
    try:
1112 fc1e2f02 Sofia Papagiannaki
        term = ApprovalTerms.objects.order_by('-id')[0]
1113 fc1e2f02 Sofia Papagiannaki
        return term
1114 fc1e2f02 Sofia Papagiannaki
    except IndexError:
1115 fc1e2f02 Sofia Papagiannaki
        pass
1116 fc1e2f02 Sofia Papagiannaki
    return None
1117 fc1e2f02 Sofia Papagiannaki
1118 9d20fe23 Kostas Papadimitriou
1119 ef20ea07 Sofia Papagiannaki
class PendingThirdPartyUser(models.Model):
1120 ef20ea07 Sofia Papagiannaki
    """
1121 ef20ea07 Sofia Papagiannaki
    Model for registring successful third party user authentications
1122 ef20ea07 Sofia Papagiannaki
    """
1123 e1a80257 Sofia Papagiannaki
    third_party_identifier = models.CharField(_('Third-party identifier'), max_length=255, null=True, blank=True)
1124 e1a80257 Sofia Papagiannaki
    provider = models.CharField(_('Provider'), max_length=255, blank=True)
1125 678b2236 Sofia Papagiannaki
    email = models.EmailField(_('e-mail address'), blank=True, null=True)
1126 564a2292 Kostas Papadimitriou
    first_name = models.CharField(_('first name'), max_length=30, blank=True,
1127 564a2292 Kostas Papadimitriou
                                  null=True)
1128 564a2292 Kostas Papadimitriou
    last_name = models.CharField(_('last name'), max_length=30, blank=True,
1129 564a2292 Kostas Papadimitriou
                                 null=True)
1130 564a2292 Kostas Papadimitriou
    affiliation = models.CharField('Affiliation', max_length=255, blank=True,
1131 564a2292 Kostas Papadimitriou
                                   null=True)
1132 9d20fe23 Kostas Papadimitriou
    username = models.CharField(_('username'), max_length=30, unique=True,
1133 d6ea9b3d Olga Brani
                                help_text=_("Required. 30 characters or fewer. Letters, numbers and @/./+/-/_ characters"))
1134 e1a80257 Sofia Papagiannaki
    token = models.CharField(_('Token'), max_length=255, null=True, blank=True)
1135 d2633501 Kostas Papadimitriou
    created = models.DateTimeField(auto_now_add=True, null=True, blank=True)
1136 c630fee6 Kostas Papadimitriou
    info = models.TextField(default="", null=True, blank=True)
1137 d2633501 Kostas Papadimitriou
1138 678b2236 Sofia Papagiannaki
    class Meta:
1139 678b2236 Sofia Papagiannaki
        unique_together = ("provider", "third_party_identifier")
1140 ef20ea07 Sofia Papagiannaki
1141 c630fee6 Kostas Papadimitriou
    def get_user_instance(self):
1142 e7cb4085 Kostas Papadimitriou
        """
1143 e7cb4085 Kostas Papadimitriou
        Create a new AstakosUser instance based on details provided when user
1144 e7cb4085 Kostas Papadimitriou
        initially signed up.
1145 e7cb4085 Kostas Papadimitriou
        """
1146 3dfb68fe Kostas Papadimitriou
        d = copy.copy(self.__dict__)
1147 c630fee6 Kostas Papadimitriou
        d.pop('_state', None)
1148 c630fee6 Kostas Papadimitriou
        d.pop('id', None)
1149 c630fee6 Kostas Papadimitriou
        d.pop('token', None)
1150 c630fee6 Kostas Papadimitriou
        d.pop('created', None)
1151 c630fee6 Kostas Papadimitriou
        d.pop('info', None)
1152 3dfb68fe Kostas Papadimitriou
        d.pop('affiliation', None)
1153 3dfb68fe Kostas Papadimitriou
        d.pop('provider', None)
1154 3dfb68fe Kostas Papadimitriou
        d.pop('third_party_identifier', None)
1155 c630fee6 Kostas Papadimitriou
        user = AstakosUser(**d)
1156 c630fee6 Kostas Papadimitriou
1157 c630fee6 Kostas Papadimitriou
        return user
1158 c630fee6 Kostas Papadimitriou
1159 ef20ea07 Sofia Papagiannaki
    @property
1160 ef20ea07 Sofia Papagiannaki
    def realname(self):
1161 ef20ea07 Sofia Papagiannaki
        return '%s %s' %(self.first_name, self.last_name)
1162 ef20ea07 Sofia Papagiannaki
1163 ef20ea07 Sofia Papagiannaki
    @realname.setter
1164 ef20ea07 Sofia Papagiannaki
    def realname(self, value):
1165 ef20ea07 Sofia Papagiannaki
        parts = value.split(' ')
1166 ef20ea07 Sofia Papagiannaki
        if len(parts) == 2:
1167 ef20ea07 Sofia Papagiannaki
            self.first_name = parts[0]
1168 ef20ea07 Sofia Papagiannaki
            self.last_name = parts[1]
1169 ef20ea07 Sofia Papagiannaki
        else:
1170 ef20ea07 Sofia Papagiannaki
            self.last_name = parts[0]
1171 2e90e3ec Kostas Papadimitriou
1172 ef20ea07 Sofia Papagiannaki
    def save(self, **kwargs):
1173 ef20ea07 Sofia Papagiannaki
        if not self.id:
1174 ef20ea07 Sofia Papagiannaki
            # set username
1175 ef20ea07 Sofia Papagiannaki
            while not self.username:
1176 ef20ea07 Sofia Papagiannaki
                username =  uuid.uuid4().hex[:30]
1177 ef20ea07 Sofia Papagiannaki
                try:
1178 ef20ea07 Sofia Papagiannaki
                    AstakosUser.objects.get(username = username)
1179 ef20ea07 Sofia Papagiannaki
                except AstakosUser.DoesNotExist, e:
1180 ef20ea07 Sofia Papagiannaki
                    self.username = username
1181 ef20ea07 Sofia Papagiannaki
        super(PendingThirdPartyUser, self).save(**kwargs)
1182 ef20ea07 Sofia Papagiannaki
1183 d2633501 Kostas Papadimitriou
    def generate_token(self):
1184 d2633501 Kostas Papadimitriou
        self.password = self.third_party_identifier
1185 d2633501 Kostas Papadimitriou
        self.last_login = datetime.now()
1186 d2633501 Kostas Papadimitriou
        self.token = default_token_generator.make_token(self)
1187 d2633501 Kostas Papadimitriou
1188 606dea8d Kostas Papadimitriou
    def existing_user(self):
1189 606dea8d Kostas Papadimitriou
        return AstakosUser.objects.filter(auth_providers__module=self.provider,
1190 606dea8d Kostas Papadimitriou
                                         auth_providers__identifier=self.third_party_identifier)
1191 606dea8d Kostas Papadimitriou
1192 9d20fe23 Kostas Papadimitriou
    def get_provider(self, user):
1193 9d20fe23 Kostas Papadimitriou
        params = {
1194 9d20fe23 Kostas Papadimitriou
            'info_data': self.info,
1195 9d20fe23 Kostas Papadimitriou
            'affiliation': self.affiliation
1196 9d20fe23 Kostas Papadimitriou
        }
1197 9d20fe23 Kostas Papadimitriou
        return auth.get_provider(self.provider, user,
1198 9d20fe23 Kostas Papadimitriou
                                 self.third_party_identifier, **params)
1199 9d20fe23 Kostas Papadimitriou
1200 bf0c6de5 Sofia Papagiannaki
class SessionCatalog(models.Model):
1201 bf0c6de5 Sofia Papagiannaki
    session_key = models.CharField(_('session key'), max_length=40)
1202 bf0c6de5 Sofia Papagiannaki
    user = models.ForeignKey(AstakosUser, related_name='sessions', null=True)
1203 bf0c6de5 Sofia Papagiannaki
1204 fcc1e93f Sofia Papagiannaki
1205 c7c0ec58 Giorgos Korfiatis
class UserSetting(models.Model):
1206 c7c0ec58 Giorgos Korfiatis
    user = models.ForeignKey(AstakosUser)
1207 c7c0ec58 Giorgos Korfiatis
    setting = models.CharField(max_length=255)
1208 c7c0ec58 Giorgos Korfiatis
    value = models.IntegerField()
1209 c7c0ec58 Giorgos Korfiatis
1210 c7c0ec58 Giorgos Korfiatis
    objects = ForUpdateManager()
1211 c7c0ec58 Giorgos Korfiatis
1212 c7c0ec58 Giorgos Korfiatis
    class Meta:
1213 c7c0ec58 Giorgos Korfiatis
        unique_together = ("user", "setting")
1214 c7c0ec58 Giorgos Korfiatis
1215 c7c0ec58 Giorgos Korfiatis
1216 fcc1e93f Sofia Papagiannaki
### PROJECTS ###
1217 fcc1e93f Sofia Papagiannaki
################
1218 fcc1e93f Sofia Papagiannaki
1219 2529745f Giorgos Korfiatis
class ChainManager(ForUpdateManager):
1220 2529745f Giorgos Korfiatis
1221 2529745f Giorgos Korfiatis
    def search_by_name(self, *search_strings):
1222 2529745f Giorgos Korfiatis
        projects = Project.objects.search_by_name(*search_strings)
1223 2529745f Giorgos Korfiatis
        chains = [p.id for p in projects]
1224 2529745f Giorgos Korfiatis
        apps  = ProjectApplication.objects.search_by_name(*search_strings)
1225 2529745f Giorgos Korfiatis
        apps = (app for app in apps if app.is_latest())
1226 2529745f Giorgos Korfiatis
        app_chains = [app.chain for app in apps if app.chain not in chains]
1227 2529745f Giorgos Korfiatis
        return chains + app_chains
1228 2529745f Giorgos Korfiatis
1229 2529745f Giorgos Korfiatis
    def all_full_state(self):
1230 2529745f Giorgos Korfiatis
        chains = self.all()
1231 4391de3d Giorgos Korfiatis
        cids = [c.chain for c in chains]
1232 4391de3d Giorgos Korfiatis
        projects = Project.objects.select_related('application').in_bulk(cids)
1233 4391de3d Giorgos Korfiatis
1234 4391de3d Giorgos Korfiatis
        objs = Chain.objects.annotate(latest=Max('chained_apps__id'))
1235 4391de3d Giorgos Korfiatis
        chain_latest = dict(objs.values_list('chain', 'latest'))
1236 4391de3d Giorgos Korfiatis
1237 4391de3d Giorgos Korfiatis
        objs = ProjectApplication.objects.select_related('applicant')
1238 4391de3d Giorgos Korfiatis
        apps = objs.in_bulk(chain_latest.values())
1239 4391de3d Giorgos Korfiatis
1240 4391de3d Giorgos Korfiatis
        d = {}
1241 2529745f Giorgos Korfiatis
        for chain in chains:
1242 4391de3d Giorgos Korfiatis
            pk = chain.pk
1243 4391de3d Giorgos Korfiatis
            project = projects.get(pk, None)
1244 4391de3d Giorgos Korfiatis
            app = apps[chain_latest[pk]]
1245 4391de3d Giorgos Korfiatis
            d[chain.pk] = chain.get_state(project, app)
1246 4391de3d Giorgos Korfiatis
1247 2529745f Giorgos Korfiatis
        return d
1248 2529745f Giorgos Korfiatis
1249 2529745f Giorgos Korfiatis
    def of_project(self, project):
1250 2529745f Giorgos Korfiatis
        if project is None:
1251 2529745f Giorgos Korfiatis
            return None
1252 2529745f Giorgos Korfiatis
        try:
1253 2529745f Giorgos Korfiatis
            return self.get(chain=project.id)
1254 2529745f Giorgos Korfiatis
        except Chain.DoesNotExist:
1255 2529745f Giorgos Korfiatis
            raise AssertionError('project with no chain')
1256 2529745f Giorgos Korfiatis
1257 2529745f Giorgos Korfiatis
1258 033f2822 Giorgos Korfiatis
class Chain(models.Model):
1259 033f2822 Giorgos Korfiatis
    chain  =   models.AutoField(primary_key=True)
1260 033f2822 Giorgos Korfiatis
1261 033f2822 Giorgos Korfiatis
    def __str__(self):
1262 033f2822 Giorgos Korfiatis
        return "%s" % (self.chain,)
1263 033f2822 Giorgos Korfiatis
1264 2529745f Giorgos Korfiatis
    objects = ChainManager()
1265 2529745f Giorgos Korfiatis
1266 2529745f Giorgos Korfiatis
    PENDING            = 0
1267 2529745f Giorgos Korfiatis
    DENIED             = 3
1268 2529745f Giorgos Korfiatis
    DISMISSED          = 4
1269 2529745f Giorgos Korfiatis
    CANCELLED          = 5
1270 2529745f Giorgos Korfiatis
1271 2529745f Giorgos Korfiatis
    APPROVED           = 10
1272 2529745f Giorgos Korfiatis
    APPROVED_PENDING   = 11
1273 2529745f Giorgos Korfiatis
    SUSPENDED          = 12
1274 2529745f Giorgos Korfiatis
    SUSPENDED_PENDING  = 13
1275 2529745f Giorgos Korfiatis
    TERMINATED         = 14
1276 2529745f Giorgos Korfiatis
    TERMINATED_PENDING = 15
1277 2529745f Giorgos Korfiatis
1278 2529745f Giorgos Korfiatis
    PENDING_STATES = [PENDING,
1279 2529745f Giorgos Korfiatis
                      APPROVED_PENDING,
1280 2529745f Giorgos Korfiatis
                      SUSPENDED_PENDING,
1281 2529745f Giorgos Korfiatis
                      TERMINATED_PENDING,
1282 2529745f Giorgos Korfiatis
                      ]
1283 2529745f Giorgos Korfiatis
1284 f557d10a Giorgos Korfiatis
    MODIFICATION_STATES = [APPROVED_PENDING,
1285 f557d10a Giorgos Korfiatis
                           SUSPENDED_PENDING,
1286 f557d10a Giorgos Korfiatis
                           TERMINATED_PENDING,
1287 f557d10a Giorgos Korfiatis
                           ]
1288 f557d10a Giorgos Korfiatis
1289 f557d10a Giorgos Korfiatis
    RELEVANT_STATES = [PENDING,
1290 f557d10a Giorgos Korfiatis
                       DENIED,
1291 f557d10a Giorgos Korfiatis
                       APPROVED,
1292 f557d10a Giorgos Korfiatis
                       APPROVED_PENDING,
1293 f557d10a Giorgos Korfiatis
                       SUSPENDED,
1294 f557d10a Giorgos Korfiatis
                       SUSPENDED_PENDING,
1295 f557d10a Giorgos Korfiatis
                       TERMINATED_PENDING,
1296 f557d10a Giorgos Korfiatis
                       ]
1297 f557d10a Giorgos Korfiatis
1298 2529745f Giorgos Korfiatis
    SKIP_STATES = [DISMISSED,
1299 2529745f Giorgos Korfiatis
                   CANCELLED,
1300 2529745f Giorgos Korfiatis
                   TERMINATED]
1301 2529745f Giorgos Korfiatis
1302 2529745f Giorgos Korfiatis
    STATE_DISPLAY = {
1303 5d209685 Giorgos Korfiatis
        PENDING            : _("Pending"),
1304 2529745f Giorgos Korfiatis
        DENIED             : _("Denied"),
1305 2529745f Giorgos Korfiatis
        DISMISSED          : _("Dismissed"),
1306 2529745f Giorgos Korfiatis
        CANCELLED          : _("Cancelled"),
1307 2529745f Giorgos Korfiatis
        APPROVED           : _("Active"),
1308 2529745f Giorgos Korfiatis
        APPROVED_PENDING   : _("Active - Pending"),
1309 2529745f Giorgos Korfiatis
        SUSPENDED          : _("Suspended"),
1310 2529745f Giorgos Korfiatis
        SUSPENDED_PENDING  : _("Suspended - Pending"),
1311 2529745f Giorgos Korfiatis
        TERMINATED         : _("Terminated"),
1312 2529745f Giorgos Korfiatis
        TERMINATED_PENDING : _("Terminated - Pending"),
1313 2529745f Giorgos Korfiatis
        }
1314 2529745f Giorgos Korfiatis
1315 2529745f Giorgos Korfiatis
1316 2529745f Giorgos Korfiatis
    @classmethod
1317 2529745f Giorgos Korfiatis
    def _chain_state(cls, project_state, app_state):
1318 2529745f Giorgos Korfiatis
        s = CHAIN_STATE.get((project_state, app_state), None)
1319 2529745f Giorgos Korfiatis
        if s is None:
1320 2529745f Giorgos Korfiatis
            raise AssertionError('inconsistent chain state')
1321 2529745f Giorgos Korfiatis
        return s
1322 2529745f Giorgos Korfiatis
1323 2529745f Giorgos Korfiatis
    @classmethod
1324 2529745f Giorgos Korfiatis
    def chain_state(cls, project, app):
1325 2529745f Giorgos Korfiatis
        p_state = project.state if project else None
1326 2529745f Giorgos Korfiatis
        return cls._chain_state(p_state, app.state)
1327 2529745f Giorgos Korfiatis
1328 2529745f Giorgos Korfiatis
    @classmethod
1329 2529745f Giorgos Korfiatis
    def state_display(cls, s):
1330 2529745f Giorgos Korfiatis
        if s is None:
1331 2529745f Giorgos Korfiatis
            return _("Unknown")
1332 2529745f Giorgos Korfiatis
        return cls.STATE_DISPLAY.get(s, _("Inconsistent"))
1333 2529745f Giorgos Korfiatis
1334 2529745f Giorgos Korfiatis
    def last_application(self):
1335 2529745f Giorgos Korfiatis
        return self.chained_apps.order_by('-id')[0]
1336 2529745f Giorgos Korfiatis
1337 2529745f Giorgos Korfiatis
    def get_project(self):
1338 2529745f Giorgos Korfiatis
        try:
1339 2529745f Giorgos Korfiatis
            return self.chained_project
1340 2529745f Giorgos Korfiatis
        except Project.DoesNotExist:
1341 2529745f Giorgos Korfiatis
            return None
1342 2529745f Giorgos Korfiatis
1343 2529745f Giorgos Korfiatis
    def get_elements(self):
1344 2529745f Giorgos Korfiatis
        project = self.get_project()
1345 2529745f Giorgos Korfiatis
        app = self.last_application()
1346 2529745f Giorgos Korfiatis
        return project, app
1347 2529745f Giorgos Korfiatis
1348 4391de3d Giorgos Korfiatis
    def get_state(self, project, app):
1349 2529745f Giorgos Korfiatis
        s = self.chain_state(project, app)
1350 2529745f Giorgos Korfiatis
        return s, project, app
1351 2529745f Giorgos Korfiatis
1352 4391de3d Giorgos Korfiatis
    def full_state(self):
1353 4391de3d Giorgos Korfiatis
        project, app = self.get_elements()
1354 4391de3d Giorgos Korfiatis
        return self.get_state(project, app)
1355 4391de3d Giorgos Korfiatis
1356 4391de3d Giorgos Korfiatis
1357 033f2822 Giorgos Korfiatis
def new_chain():
1358 033f2822 Giorgos Korfiatis
    c = Chain.objects.create()
1359 033f2822 Giorgos Korfiatis
    return c
1360 033f2822 Giorgos Korfiatis
1361 033f2822 Giorgos Korfiatis
1362 6dcf53eb Kostas Papadimitriou
class ProjectApplicationManager(ForUpdateManager):
1363 5550bcfb Kostas Papadimitriou
1364 05617ab9 Kostas Papadimitriou
    def user_visible_projects(self, *filters, **kw_filters):
1365 689226c3 Giorgos Korfiatis
        model = self.model
1366 689226c3 Giorgos Korfiatis
        return self.filter(model.Q_PENDING | model.Q_APPROVED)
1367 05617ab9 Kostas Papadimitriou
1368 7184f408 Giorgos Korfiatis
    def user_visible_by_chain(self, flt):
1369 689226c3 Giorgos Korfiatis
        model = self.model
1370 3e3743f2 Giorgos Korfiatis
        pending = self.filter(model.Q_PENDING | model.Q_DENIED).values_list('chain')
1371 689226c3 Giorgos Korfiatis
        approved = self.filter(model.Q_APPROVED).values_list('chain')
1372 a3530159 Georgios D. Tsoukalas
        by_chain = dict(pending.annotate(models.Max('id')))
1373 a3530159 Georgios D. Tsoukalas
        by_chain.update(approved.annotate(models.Max('id')))
1374 7184f408 Giorgos Korfiatis
        return self.filter(flt, id__in=by_chain.values())
1375 05617ab9 Kostas Papadimitriou
1376 05617ab9 Kostas Papadimitriou
    def user_accessible_projects(self, user):
1377 5550bcfb Kostas Papadimitriou
        """
1378 5550bcfb Kostas Papadimitriou
        Return projects accessed by specified user.
1379 5550bcfb Kostas Papadimitriou
        """
1380 8e1a5af5 Georgios D. Tsoukalas
        if user.is_project_admin():
1381 8e1a5af5 Georgios D. Tsoukalas
            participates_filters = Q()
1382 8e1a5af5 Georgios D. Tsoukalas
        else:
1383 8e1a5af5 Georgios D. Tsoukalas
            participates_filters = Q(owner=user) | Q(applicant=user) | \
1384 8e1a5af5 Georgios D. Tsoukalas
                                   Q(project__projectmembership__person=user)
1385 05617ab9 Kostas Papadimitriou
1386 a3530159 Georgios D. Tsoukalas
        return self.user_visible_by_chain(participates_filters).order_by('issue_date').distinct()
1387 5550bcfb Kostas Papadimitriou
1388 a5cef8d0 Kostas Papadimitriou
    def search_by_name(self, *search_strings):
1389 a5cef8d0 Kostas Papadimitriou
        q = Q()
1390 a5cef8d0 Kostas Papadimitriou
        for s in search_strings:
1391 a5cef8d0 Kostas Papadimitriou
            q = q | Q(name__icontains=s)
1392 a5cef8d0 Kostas Papadimitriou
        return self.filter(q)
1393 a5cef8d0 Kostas Papadimitriou
1394 ff67242a Giorgos Korfiatis
    def latest_of_chain(self, chain_id):
1395 ff67242a Giorgos Korfiatis
        try:
1396 ff67242a Giorgos Korfiatis
            return self.filter(chain=chain_id).order_by('-id')[0]
1397 ff67242a Giorgos Korfiatis
        except IndexError:
1398 ff67242a Giorgos Korfiatis
            return None
1399 a5cef8d0 Kostas Papadimitriou
1400 a9ba418f Giorgos Korfiatis
1401 8aed306c Giorgos Korfiatis
class ProjectApplication(models.Model):
1402 425e2e95 Sofia Papagiannaki
    applicant               =   models.ForeignKey(
1403 425e2e95 Sofia Papagiannaki
                                    AstakosUser,
1404 d6fdc91e Georgios D. Tsoukalas
                                    related_name='projects_applied',
1405 d6fdc91e Georgios D. Tsoukalas
                                    db_index=True)
1406 d6fdc91e Georgios D. Tsoukalas
1407 d0e78bbe Giorgos Korfiatis
    PENDING     =    0
1408 d0e78bbe Giorgos Korfiatis
    APPROVED    =    1
1409 d0e78bbe Giorgos Korfiatis
    REPLACED    =    2
1410 d0e78bbe Giorgos Korfiatis
    DENIED      =    3
1411 3c638f72 Giorgos Korfiatis
    DISMISSED   =    4
1412 3c638f72 Giorgos Korfiatis
    CANCELLED   =    5
1413 d0e78bbe Giorgos Korfiatis
1414 69ab4df9 Giorgos Korfiatis
    state                   =   models.IntegerField(default=PENDING,
1415 69ab4df9 Giorgos Korfiatis
                                                    db_index=True)
1416 d6fdc91e Georgios D. Tsoukalas
1417 425e2e95 Sofia Papagiannaki
    owner                   =   models.ForeignKey(
1418 425e2e95 Sofia Papagiannaki
                                    AstakosUser,
1419 d6fdc91e Georgios D. Tsoukalas
                                    related_name='projects_owned',
1420 d6fdc91e Georgios D. Tsoukalas
                                    db_index=True)
1421 d6fdc91e Georgios D. Tsoukalas
1422 5195c0e9 Giorgos Korfiatis
    chain                   =   models.ForeignKey(Chain,
1423 5195c0e9 Giorgos Korfiatis
                                                  related_name='chained_apps',
1424 5195c0e9 Giorgos Korfiatis
                                                  db_column='chain')
1425 3c638f72 Giorgos Korfiatis
    precursor_application   =   models.ForeignKey('ProjectApplication',
1426 3c638f72 Giorgos Korfiatis
                                                  null=True,
1427 3c638f72 Giorgos Korfiatis
                                                  blank=True)
1428 425e2e95 Sofia Papagiannaki
1429 67980f56 Georgios D. Tsoukalas
    name                    =   models.CharField(max_length=80)
1430 94e49e22 Kostas Papadimitriou
    homepage                =   models.URLField(max_length=255, null=True,
1431 94e49e22 Kostas Papadimitriou
                                                verify_exists=False)
1432 67980f56 Georgios D. Tsoukalas
    description             =   models.TextField(null=True, blank=True)
1433 e729f165 Kostas Papadimitriou
    start_date              =   models.DateTimeField(null=True, blank=True)
1434 67980f56 Georgios D. Tsoukalas
    end_date                =   models.DateTimeField()
1435 272cf735 Sofia Papagiannaki
    member_join_policy      =   models.IntegerField()
1436 272cf735 Sofia Papagiannaki
    member_leave_policy     =   models.IntegerField()
1437 67980f56 Georgios D. Tsoukalas
    limit_on_members_number =   models.PositiveIntegerField(null=True)
1438 425e2e95 Sofia Papagiannaki
    resource_grants         =   models.ManyToManyField(
1439 425e2e95 Sofia Papagiannaki
                                    Resource,
1440 425e2e95 Sofia Papagiannaki
                                    null=True,
1441 425e2e95 Sofia Papagiannaki
                                    blank=True,
1442 d6fdc91e Georgios D. Tsoukalas
                                    through='ProjectResourceGrant')
1443 425e2e95 Sofia Papagiannaki
    comments                =   models.TextField(null=True, blank=True)
1444 3c638f72 Giorgos Korfiatis
    issue_date              =   models.DateTimeField(auto_now_add=True)
1445 3c638f72 Giorgos Korfiatis
    response_date           =   models.DateTimeField(null=True, blank=True)
1446 2b745492 Giorgos Korfiatis
    response                =   models.TextField(null=True, blank=True)
1447 6dcf53eb Kostas Papadimitriou
1448 5550bcfb Kostas Papadimitriou
    objects                 =   ProjectApplicationManager()
1449 7729e9cc Giorgos Korfiatis
1450 689226c3 Giorgos Korfiatis
    # Compiled queries
1451 689226c3 Giorgos Korfiatis
    Q_PENDING  = Q(state=PENDING)
1452 689226c3 Giorgos Korfiatis
    Q_APPROVED = Q(state=APPROVED)
1453 3e3743f2 Giorgos Korfiatis
    Q_DENIED   = Q(state=DENIED)
1454 689226c3 Giorgos Korfiatis
1455 c4892cd2 Sofia Papagiannaki
    class Meta:
1456 c4892cd2 Sofia Papagiannaki
        unique_together = ("chain", "id")
1457 c4892cd2 Sofia Papagiannaki
1458 f3a45fc6 Kostas Papadimitriou
    def __unicode__(self):
1459 f3a45fc6 Kostas Papadimitriou
        return "%s applied by %s" % (self.name, self.applicant)
1460 f3a45fc6 Kostas Papadimitriou
1461 d0e78bbe Giorgos Korfiatis
    # TODO: Move to a more suitable place
1462 9307cd46 Giorgos Korfiatis
    APPLICATION_STATE_DISPLAY = {
1463 3c638f72 Giorgos Korfiatis
        PENDING  : _('Pending review'),
1464 d77b32f2 Giorgos Korfiatis
        APPROVED : _('Approved'),
1465 3c638f72 Giorgos Korfiatis
        REPLACED : _('Replaced'),
1466 3c638f72 Giorgos Korfiatis
        DENIED   : _('Denied'),
1467 3c638f72 Giorgos Korfiatis
        DISMISSED: _('Dismissed'),
1468 3c638f72 Giorgos Korfiatis
        CANCELLED: _('Cancelled')
1469 bd9af366 Kostas Papadimitriou
    }
1470 d0e78bbe Giorgos Korfiatis
1471 f30f0170 Giorgos Korfiatis
    @property
1472 f30f0170 Giorgos Korfiatis
    def log_display(self):
1473 f30f0170 Giorgos Korfiatis
        return "application %s (%s) for project %s" % (
1474 f30f0170 Giorgos Korfiatis
            self.id, self.name, self.chain)
1475 f30f0170 Giorgos Korfiatis
1476 a3530159 Georgios D. Tsoukalas
    def get_project(self):
1477 a3530159 Georgios D. Tsoukalas
        try:
1478 a3530159 Georgios D. Tsoukalas
            project = Project.objects.get(id=self.chain, state=Project.APPROVED)
1479 a3530159 Georgios D. Tsoukalas
            return Project
1480 a3530159 Georgios D. Tsoukalas
        except Project.DoesNotExist, e:
1481 a3530159 Georgios D. Tsoukalas
            return None
1482 a3530159 Georgios D. Tsoukalas
1483 db9a498c Kostas Papadimitriou
    def state_display(self):
1484 9307cd46 Giorgos Korfiatis
        return self.APPLICATION_STATE_DISPLAY.get(self.state, _('Unknown'))
1485 db9a498c Kostas Papadimitriou
1486 d4660e00 Giorgos Korfiatis
    def project_state_display(self):
1487 d4660e00 Giorgos Korfiatis
        try:
1488 d4660e00 Giorgos Korfiatis
            project = self.project
1489 d4660e00 Giorgos Korfiatis
            return project.state_display()
1490 d4660e00 Giorgos Korfiatis
        except Project.DoesNotExist:
1491 d4660e00 Giorgos Korfiatis
            return self.state_display()
1492 d4660e00 Giorgos Korfiatis
1493 eee9ec4d Giorgos Korfiatis
    def add_resource_policy(self, resource, uplimit):
1494 e1a80257 Sofia Papagiannaki
        """Raises ObjectDoesNotExist, IntegrityError"""
1495 a7aba804 Sofia Papagiannaki
        q = self.projectresourcegrant_set
1496 26551b92 Kostas Papadimitriou
        resource = Resource.objects.get(name=resource)
1497 a7aba804 Sofia Papagiannaki
        q.create(resource=resource, member_capacity=uplimit)
1498 e1a80257 Sofia Papagiannaki
1499 5550bcfb Kostas Papadimitriou
    def members_count(self):
1500 5550bcfb Kostas Papadimitriou
        return self.project.approved_memberships.count()
1501 5550bcfb Kostas Papadimitriou
1502 669cfe19 Olga Brani
    @property
1503 669cfe19 Olga Brani
    def grants(self):
1504 26551b92 Kostas Papadimitriou
        return self.projectresourcegrant_set.values('member_capacity',
1505 26551b92 Kostas Papadimitriou
                                                    'resource__name')
1506 5550bcfb Kostas Papadimitriou
1507 e1a80257 Sofia Papagiannaki
    @property
1508 e1a80257 Sofia Papagiannaki
    def resource_policies(self):
1509 b98e1df0 Sofia Papagiannaki
        return [str(rp) for rp in self.projectresourcegrant_set.all()]
1510 e1a80257 Sofia Papagiannaki
1511 eee9ec4d Giorgos Korfiatis
    def set_resource_policies(self, policies):
1512 eee9ec4d Giorgos Korfiatis
        for resource, uplimit in policies:
1513 eee9ec4d Giorgos Korfiatis
            self.add_resource_policy(resource, uplimit)
1514 425e2e95 Sofia Papagiannaki
1515 a75dbd7b Giorgos Korfiatis
    def pending_modifications_incl_me(self):
1516 3e3743f2 Giorgos Korfiatis
        q = self.chained_applications()
1517 a75dbd7b Giorgos Korfiatis
        q = q.filter(Q(state=self.PENDING))
1518 3e3743f2 Giorgos Korfiatis
        return q
1519 ece3b66e Giorgos Korfiatis
1520 a75dbd7b Giorgos Korfiatis
    def last_pending_incl_me(self):
1521 a75dbd7b Giorgos Korfiatis
        try:
1522 a75dbd7b Giorgos Korfiatis
            return self.pending_modifications_incl_me().order_by('-id')[0]
1523 a75dbd7b Giorgos Korfiatis
        except IndexError:
1524 a75dbd7b Giorgos Korfiatis
            return None
1525 a75dbd7b Giorgos Korfiatis
1526 a75dbd7b Giorgos Korfiatis
    def pending_modifications(self):
1527 a75dbd7b Giorgos Korfiatis
        return self.pending_modifications_incl_me().filter(~Q(id=self.id))
1528 a75dbd7b Giorgos Korfiatis
1529 3e3743f2 Giorgos Korfiatis
    def last_pending(self):
1530 9b32e2fb Kostas Papadimitriou
        try:
1531 3e3743f2 Giorgos Korfiatis
            return self.pending_modifications().order_by('-id')[0]
1532 9b32e2fb Kostas Papadimitriou
        except IndexError:
1533 05617ab9 Kostas Papadimitriou
            return None
1534 05617ab9 Kostas Papadimitriou
1535 efc58b65 Kostas Papadimitriou
    def is_modification(self):
1536 d4660e00 Giorgos Korfiatis
        # if self.state != self.PENDING:
1537 d4660e00 Giorgos Korfiatis
        #     return False
1538 efc58b65 Kostas Papadimitriou
        parents = self.chained_applications().filter(id__lt=self.id)
1539 efc58b65 Kostas Papadimitriou
        parents = parents.filter(state__in=[self.APPROVED])
1540 efc58b65 Kostas Papadimitriou
        return parents.count() > 0
1541 efc58b65 Kostas Papadimitriou
1542 efc58b65 Kostas Papadimitriou
    def chained_applications(self):
1543 efc58b65 Kostas Papadimitriou
        return ProjectApplication.objects.filter(chain=self.chain)
1544 efc58b65 Kostas Papadimitriou
1545 2529745f Giorgos Korfiatis
    def is_latest(self):
1546 2529745f Giorgos Korfiatis
        return self.chained_applications().order_by('-id')[0] == self
1547 2529745f Giorgos Korfiatis
1548 05617ab9 Kostas Papadimitriou
    def has_pending_modifications(self):
1549 3e3743f2 Giorgos Korfiatis
        return bool(self.last_pending())
1550 05617ab9 Kostas Papadimitriou
1551 022cc8e2 Giorgos Korfiatis
    def denied_modifications(self):
1552 022cc8e2 Giorgos Korfiatis
        q = self.chained_applications()
1553 022cc8e2 Giorgos Korfiatis
        q = q.filter(Q(state=self.DENIED))
1554 022cc8e2 Giorgos Korfiatis
        q = q.filter(~Q(id=self.id))
1555 022cc8e2 Giorgos Korfiatis
        return q
1556 022cc8e2 Giorgos Korfiatis
1557 022cc8e2 Giorgos Korfiatis
    def last_denied(self):
1558 022cc8e2 Giorgos Korfiatis
        try:
1559 022cc8e2 Giorgos Korfiatis
            return self.denied_modifications().order_by('-id')[0]
1560 022cc8e2 Giorgos Korfiatis
        except IndexError:
1561 022cc8e2 Giorgos Korfiatis
            return None
1562 022cc8e2 Giorgos Korfiatis
1563 022cc8e2 Giorgos Korfiatis
    def has_denied_modifications(self):
1564 022cc8e2 Giorgos Korfiatis
        return bool(self.last_denied())
1565 022cc8e2 Giorgos Korfiatis
1566 2529745f Giorgos Korfiatis
    def is_applied(self):
1567 2529745f Giorgos Korfiatis
        try:
1568 2529745f Giorgos Korfiatis
            self.project
1569 2529745f Giorgos Korfiatis
            return True
1570 2529745f Giorgos Korfiatis
        except Project.DoesNotExist:
1571 2529745f Giorgos Korfiatis
            return False
1572 2529745f Giorgos Korfiatis
1573 05617ab9 Kostas Papadimitriou
    def get_project(self):
1574 05617ab9 Kostas Papadimitriou
        try:
1575 05617ab9 Kostas Papadimitriou
            return Project.objects.get(id=self.chain)
1576 05617ab9 Kostas Papadimitriou
        except Project.DoesNotExist:
1577 9b32e2fb Kostas Papadimitriou
            return None
1578 4f22664f Georgios D. Tsoukalas
1579 d74111be Giorgos Korfiatis
    def project_exists(self):
1580 d74111be Giorgos Korfiatis
        return self.get_project() is not None
1581 d74111be Giorgos Korfiatis
1582 b6fe8bb8 Giorgos Korfiatis
    def _get_project_for_update(self):
1583 a9ba418f Giorgos Korfiatis
        try:
1584 ea1e5d9f Giorgos Korfiatis
            objects = Project.objects
1585 ea1e5d9f Giorgos Korfiatis
            project = objects.get_for_update(id=self.chain)
1586 a9ba418f Giorgos Korfiatis
            return project
1587 a9ba418f Giorgos Korfiatis
        except Project.DoesNotExist:
1588 a9ba418f Giorgos Korfiatis
            return None
1589 4f22664f Georgios D. Tsoukalas
1590 01bdbd17 Giorgos Korfiatis
    def can_cancel(self):
1591 01bdbd17 Giorgos Korfiatis
        return self.state == self.PENDING
1592 01bdbd17 Giorgos Korfiatis
1593 3c638f72 Giorgos Korfiatis
    def cancel(self):
1594 01bdbd17 Giorgos Korfiatis
        if not self.can_cancel():
1595 3c638f72 Giorgos Korfiatis
            m = _("cannot cancel: application '%s' in state '%s'") % (
1596 3c638f72 Giorgos Korfiatis
                    self.id, self.state)
1597 3c638f72 Giorgos Korfiatis
            raise AssertionError(m)
1598 3c638f72 Giorgos Korfiatis
1599 3c638f72 Giorgos Korfiatis
        self.state = self.CANCELLED
1600 3c638f72 Giorgos Korfiatis
        self.save()
1601 3c638f72 Giorgos Korfiatis
1602 01bdbd17 Giorgos Korfiatis
    def can_dismiss(self):
1603 01bdbd17 Giorgos Korfiatis
        return self.state == self.DENIED
1604 01bdbd17 Giorgos Korfiatis
1605 3c638f72 Giorgos Korfiatis
    def dismiss(self):
1606 01bdbd17 Giorgos Korfiatis
        if not self.can_dismiss():
1607 3c638f72 Giorgos Korfiatis
            m = _("cannot dismiss: application '%s' in state '%s'") % (
1608 3c638f72 Giorgos Korfiatis
                    self.id, self.state)
1609 3c638f72 Giorgos Korfiatis
            raise AssertionError(m)
1610 3c638f72 Giorgos Korfiatis
1611 3c638f72 Giorgos Korfiatis
        self.state = self.DISMISSED
1612 3c638f72 Giorgos Korfiatis
        self.save()
1613 3c638f72 Giorgos Korfiatis
1614 01bdbd17 Giorgos Korfiatis
    def can_deny(self):
1615 01bdbd17 Giorgos Korfiatis
        return self.state == self.PENDING
1616 01bdbd17 Giorgos Korfiatis
1617 2b745492 Giorgos Korfiatis
    def deny(self, reason):
1618 01bdbd17 Giorgos Korfiatis
        if not self.can_deny():
1619 19eb3ee6 Giorgos Korfiatis
            m = _("cannot deny: application '%s' in state '%s'") % (
1620 19eb3ee6 Giorgos Korfiatis
                    self.id, self.state)
1621 19eb3ee6 Giorgos Korfiatis
            raise AssertionError(m)
1622 19eb3ee6 Giorgos Korfiatis
1623 19eb3ee6 Giorgos Korfiatis
        self.state = self.DENIED
1624 3c638f72 Giorgos Korfiatis
        self.response_date = datetime.now()
1625 2b745492 Giorgos Korfiatis
        self.response = reason
1626 19eb3ee6 Giorgos Korfiatis
        self.save()
1627 19eb3ee6 Giorgos Korfiatis
1628 01bdbd17 Giorgos Korfiatis
    def can_approve(self):
1629 01bdbd17 Giorgos Korfiatis
        return self.state == self.PENDING
1630 01bdbd17 Giorgos Korfiatis
1631 64d0c13e Giorgos Korfiatis
    def approve(self, reason):
1632 73fbaec4 Sofia Papagiannaki
        new_project_name = self.name
1633 01bdbd17 Giorgos Korfiatis
        if not self.can_approve():
1634 65360c65 Georgios D. Tsoukalas
            m = _("cannot approve: project '%s' in state '%s'") % (
1635 65360c65 Georgios D. Tsoukalas
                    new_project_name, self.state)
1636 01bdbd17 Giorgos Korfiatis
            raise AssertionError(m) # invalid argument
1637 262e04c6 Giorgos Korfiatis
1638 fdafae27 Giorgos Korfiatis
        now = datetime.now()
1639 b6fe8bb8 Giorgos Korfiatis
        project = self._get_project_for_update()
1640 3cc9637a Giorgos Korfiatis
1641 99463445 Giorgos Korfiatis
        try:
1642 99463445 Giorgos Korfiatis
            q = Q(name=new_project_name) & ~Q(state=Project.TERMINATED)
1643 99463445 Giorgos Korfiatis
            conflicting_project = Project.objects.get(q)
1644 99463445 Giorgos Korfiatis
            if (conflicting_project != project):
1645 3cc9637a Giorgos Korfiatis
                m = (_("cannot approve: project with name '%s' "
1646 e1017df9 Giorgos Korfiatis
                       "already exists (id: %s)") % (
1647 3cc9637a Giorgos Korfiatis
                        new_project_name, conflicting_project.id))
1648 3cc9637a Giorgos Korfiatis
                raise PermissionDenied(m) # invalid argument
1649 99463445 Giorgos Korfiatis
        except Project.DoesNotExist:
1650 99463445 Giorgos Korfiatis
            pass
1651 3cc9637a Giorgos Korfiatis
1652 4bf02ea5 Giorgos Korfiatis
        new_project = False
1653 4f22664f Georgios D. Tsoukalas
        if project is None:
1654 4bf02ea5 Giorgos Korfiatis
            new_project = True
1655 3c638f72 Giorgos Korfiatis
            project = Project(id=self.chain)
1656 fdafae27 Giorgos Korfiatis
1657 3cc9637a Giorgos Korfiatis
        project.name = new_project_name
1658 ee45eb81 Giorgos Korfiatis
        project.application = self
1659 4bf02ea5 Giorgos Korfiatis
        project.last_approval_date = now
1660 4bf02ea5 Giorgos Korfiatis
1661 a769d7ba Sofia Papagiannaki
        project.save()
1662 425e2e95 Sofia Papagiannaki
1663 85d444db Sofia Papagiannaki
        self.state = self.APPROVED
1664 3c638f72 Giorgos Korfiatis
        self.response_date = now
1665 64d0c13e Giorgos Korfiatis
        self.response = reason
1666 bfe23b13 Sofia Papagiannaki
        self.save()
1667 570015d2 Giorgos Korfiatis
        return project
1668 262e04c6 Giorgos Korfiatis
1669 b98e1df0 Sofia Papagiannaki
    @property
1670 b98e1df0 Sofia Papagiannaki
    def member_join_policy_display(self):
1671 251b83be Giorgos Korfiatis
        policy = self.member_join_policy
1672 251b83be Giorgos Korfiatis
        return presentation.PROJECT_MEMBER_JOIN_POLICIES.get(policy)
1673 b98e1df0 Sofia Papagiannaki
1674 b98e1df0 Sofia Papagiannaki
    @property
1675 b98e1df0 Sofia Papagiannaki
    def member_leave_policy_display(self):
1676 251b83be Giorgos Korfiatis
        policy = self.member_leave_policy
1677 251b83be Giorgos Korfiatis
        return presentation.PROJECT_MEMBER_LEAVE_POLICIES.get(policy)
1678 b98e1df0 Sofia Papagiannaki
1679 73fbaec4 Sofia Papagiannaki
class ProjectResourceGrant(models.Model):
1680 e1a80257 Sofia Papagiannaki
1681 425e2e95 Sofia Papagiannaki
    resource                =   models.ForeignKey(Resource)
1682 425e2e95 Sofia Papagiannaki
    project_application     =   models.ForeignKey(ProjectApplication,
1683 5200e864 Sofia Papagiannaki
                                                  null=True)
1684 0cdc5cc5 Giorgos Korfiatis
    project_capacity        =   intDecimalField(null=True)
1685 0cdc5cc5 Giorgos Korfiatis
    member_capacity         =   intDecimalField(default=0)
1686 73fbaec4 Sofia Papagiannaki
1687 73fbaec4 Sofia Papagiannaki
    objects = ExtendedManager()
1688 73fbaec4 Sofia Papagiannaki
1689 73fbaec4 Sofia Papagiannaki
    class Meta:
1690 73fbaec4 Sofia Papagiannaki
        unique_together = ("resource", "project_application")
1691 8327782d Sofia Papagiannaki
1692 b98e1df0 Sofia Papagiannaki
    def display_member_capacity(self):
1693 b98e1df0 Sofia Papagiannaki
        if self.member_capacity:
1694 b98e1df0 Sofia Papagiannaki
            if self.resource.unit:
1695 b98e1df0 Sofia Papagiannaki
                return ProjectResourceGrant.display_filesize(
1696 b98e1df0 Sofia Papagiannaki
                    self.member_capacity)
1697 b98e1df0 Sofia Papagiannaki
            else:
1698 b98e1df0 Sofia Papagiannaki
                if math.isinf(self.member_capacity):
1699 b98e1df0 Sofia Papagiannaki
                    return 'Unlimited'
1700 b98e1df0 Sofia Papagiannaki
                else:
1701 b98e1df0 Sofia Papagiannaki
                    return self.member_capacity
1702 b98e1df0 Sofia Papagiannaki
        else:
1703 b98e1df0 Sofia Papagiannaki
            return 'Unlimited'
1704 b98e1df0 Sofia Papagiannaki
1705 b98e1df0 Sofia Papagiannaki
    def __str__(self):
1706 b98e1df0 Sofia Papagiannaki
        return 'Max %s per user: %s' % (self.resource.pluralized_display_name,
1707 b98e1df0 Sofia Papagiannaki
                                        self.display_member_capacity())
1708 b98e1df0 Sofia Papagiannaki
1709 b98e1df0 Sofia Papagiannaki
    @classmethod
1710 b98e1df0 Sofia Papagiannaki
    def display_filesize(cls, value):
1711 b98e1df0 Sofia Papagiannaki
        try:
1712 b98e1df0 Sofia Papagiannaki
            value = float(value)
1713 b98e1df0 Sofia Papagiannaki
        except:
1714 b98e1df0 Sofia Papagiannaki
            return
1715 b98e1df0 Sofia Papagiannaki
        else:
1716 b98e1df0 Sofia Papagiannaki
            if math.isinf(value):
1717 b98e1df0 Sofia Papagiannaki
                return 'Unlimited'
1718 b98e1df0 Sofia Papagiannaki
            if value > 1:
1719 b98e1df0 Sofia Papagiannaki
                unit_list = zip(['bytes', 'kB', 'MB', 'GB', 'TB', 'PB'],
1720 b98e1df0 Sofia Papagiannaki
                                [0, 0, 0, 0, 0, 0])
1721 b98e1df0 Sofia Papagiannaki
                exponent = min(int(math.log(value, 1024)), len(unit_list) - 1)
1722 b98e1df0 Sofia Papagiannaki
                quotient = float(value) / 1024**exponent
1723 b98e1df0 Sofia Papagiannaki
                unit, value_decimals = unit_list[exponent]
1724 b98e1df0 Sofia Papagiannaki
                format_string = '{0:.%sf} {1}' % (value_decimals)
1725 b98e1df0 Sofia Papagiannaki
                return format_string.format(quotient, unit)
1726 b98e1df0 Sofia Papagiannaki
            if value == 0:
1727 b98e1df0 Sofia Papagiannaki
                return '0 bytes'
1728 b98e1df0 Sofia Papagiannaki
            if value == 1:
1729 b98e1df0 Sofia Papagiannaki
                return '1 byte'
1730 b98e1df0 Sofia Papagiannaki
            else:
1731 b98e1df0 Sofia Papagiannaki
               return '0'
1732 b98e1df0 Sofia Papagiannaki
1733 e546df49 Georgios D. Tsoukalas
1734 123be68a Giorgos Korfiatis
class ProjectManager(ForUpdateManager):
1735 123be68a Giorgos Korfiatis
1736 123be68a Giorgos Korfiatis
    def terminated_projects(self):
1737 689226c3 Giorgos Korfiatis
        q = self.model.Q_TERMINATED
1738 123be68a Giorgos Korfiatis
        return self.filter(q)
1739 123be68a Giorgos Korfiatis
1740 123be68a Giorgos Korfiatis
    def not_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 db99f198 Giorgos Korfiatis
    def deactivated_projects(self):
1745 689226c3 Giorgos Korfiatis
        q = self.model.Q_DEACTIVATED
1746 db99f198 Giorgos Korfiatis
        return self.filter(q)
1747 db99f198 Giorgos Korfiatis
1748 7eadc230 Giorgos Korfiatis
    def expired_projects(self):
1749 7eadc230 Giorgos Korfiatis
        q = (~Q(state=Project.TERMINATED) &
1750 7eadc230 Giorgos Korfiatis
              Q(application__end_date__lt=datetime.now()))
1751 7eadc230 Giorgos Korfiatis
        return self.filter(q)
1752 7eadc230 Giorgos Korfiatis
1753 d77b32f2 Giorgos Korfiatis
    def search_by_name(self, *search_strings):
1754 d77b32f2 Giorgos Korfiatis
        q = Q()
1755 d77b32f2 Giorgos Korfiatis
        for s in search_strings:
1756 d77b32f2 Giorgos Korfiatis
            q = q | Q(name__icontains=s)
1757 d77b32f2 Giorgos Korfiatis
        return self.filter(q)
1758 d77b32f2 Giorgos Korfiatis
1759 7eadc230 Giorgos Korfiatis
1760 d6fdc91e Georgios D. Tsoukalas
class Project(models.Model):
1761 e546df49 Georgios D. Tsoukalas
1762 5195c0e9 Giorgos Korfiatis
    id                          =   models.OneToOneField(Chain,
1763 5195c0e9 Giorgos Korfiatis
                                                      related_name='chained_project',
1764 5195c0e9 Giorgos Korfiatis
                                                      db_column='id',
1765 5195c0e9 Giorgos Korfiatis
                                                      primary_key=True)
1766 5195c0e9 Giorgos Korfiatis
1767 ee45eb81 Giorgos Korfiatis
    application                 =   models.OneToOneField(
1768 4f22664f Georgios D. Tsoukalas
                                            ProjectApplication,
1769 782d9118 Giorgos Korfiatis
                                            related_name='project')
1770 4f22664f Georgios D. Tsoukalas
    last_approval_date          =   models.DateTimeField(null=True)
1771 4f22664f Georgios D. Tsoukalas
1772 4f22664f Georgios D. Tsoukalas
    members                     =   models.ManyToManyField(
1773 4f22664f Georgios D. Tsoukalas
                                            AstakosUser,
1774 4f22664f Georgios D. Tsoukalas
                                            through='ProjectMembership')
1775 4f22664f Georgios D. Tsoukalas
1776 5b9e9530 Giorgos Korfiatis
    deactivation_reason         =   models.CharField(max_length=255, null=True)
1777 5b9e9530 Giorgos Korfiatis
    deactivation_date           =   models.DateTimeField(null=True)
1778 4f22664f Georgios D. Tsoukalas
1779 3c638f72 Giorgos Korfiatis
    creation_date               =   models.DateTimeField(auto_now_add=True)
1780 4f22664f Georgios D. Tsoukalas
    name                        =   models.CharField(
1781 4f22664f Georgios D. Tsoukalas
                                            max_length=80,
1782 e1017df9 Giorgos Korfiatis
                                            null=True,
1783 4f22664f Georgios D. Tsoukalas
                                            db_index=True,
1784 4f22664f Georgios D. Tsoukalas
                                            unique=True)
1785 425e2e95 Sofia Papagiannaki
1786 b6fe8bb8 Giorgos Korfiatis
    APPROVED    = 1
1787 b6fe8bb8 Giorgos Korfiatis
    SUSPENDED   = 10
1788 b6fe8bb8 Giorgos Korfiatis
    TERMINATED  = 100
1789 5b9e9530 Giorgos Korfiatis
1790 b6fe8bb8 Giorgos Korfiatis
    state                       =   models.IntegerField(default=APPROVED,
1791 123be68a Giorgos Korfiatis
                                                        db_index=True)
1792 123be68a Giorgos Korfiatis
1793 123be68a Giorgos Korfiatis
    objects     =   ProjectManager()
1794 7729e9cc Giorgos Korfiatis
1795 689226c3 Giorgos Korfiatis
    # Compiled queries
1796 689226c3 Giorgos Korfiatis
    Q_TERMINATED  = Q(state=TERMINATED)
1797 689226c3 Giorgos Korfiatis
    Q_SUSPENDED   = Q(state=SUSPENDED)
1798 689226c3 Giorgos Korfiatis
    Q_DEACTIVATED = Q_TERMINATED | Q_SUSPENDED
1799 689226c3 Giorgos Korfiatis
1800 8c7b8bb8 Giorgos Korfiatis
    def __str__(self):
1801 b6eaca30 Giorgos Korfiatis
        return uenc(_("<project %s '%s'>") %
1802 b6eaca30 Giorgos Korfiatis
                    (self.id, udec(self.application.name)))
1803 8c7b8bb8 Giorgos Korfiatis
1804 8c7b8bb8 Giorgos Korfiatis
    __repr__ = __str__
1805 8c7b8bb8 Giorgos Korfiatis
1806 b6eaca30 Giorgos Korfiatis
    def __unicode__(self):
1807 b6eaca30 Giorgos Korfiatis
        return _("<project %s '%s'>") % (self.id, self.application.name)
1808 b6eaca30 Giorgos Korfiatis
1809 e1f31e63 Giorgos Korfiatis
    STATE_DISPLAY = {
1810 d77b32f2 Giorgos Korfiatis
        APPROVED   : 'Active',
1811 d77b32f2 Giorgos Korfiatis
        SUSPENDED  : 'Suspended',
1812 d77b32f2 Giorgos Korfiatis
        TERMINATED : 'Terminated'
1813 e1f31e63 Giorgos Korfiatis
        }
1814 e1f31e63 Giorgos Korfiatis
1815 e1f31e63 Giorgos Korfiatis
    def state_display(self):
1816 e1f31e63 Giorgos Korfiatis
        return self.STATE_DISPLAY.get(self.state, _('Unknown'))
1817 e1f31e63 Giorgos Korfiatis
1818 7eadc230 Giorgos Korfiatis
    def expiration_info(self):
1819 7eadc230 Giorgos Korfiatis
        return (str(self.id), self.name, self.state_display(),
1820 7eadc230 Giorgos Korfiatis
                str(self.application.end_date))
1821 7eadc230 Giorgos Korfiatis
1822 b6fe8bb8 Giorgos Korfiatis
    def is_deactivated(self, reason=None):
1823 b6fe8bb8 Giorgos Korfiatis
        if reason is not None:
1824 b6fe8bb8 Giorgos Korfiatis
            return self.state == reason
1825 425e2e95 Sofia Papagiannaki
1826 b6fe8bb8 Giorgos Korfiatis
        return self.state != self.APPROVED
1827 123be68a Giorgos Korfiatis
1828 123be68a Giorgos Korfiatis
    ### Deactivation calls
1829 425e2e95 Sofia Papagiannaki
1830 123be68a Giorgos Korfiatis
    def terminate(self):
1831 123be68a Giorgos Korfiatis
        self.deactivation_reason = 'TERMINATED'
1832 3d4cef9e Giorgos Korfiatis
        self.deactivation_date = datetime.now()
1833 b6fe8bb8 Giorgos Korfiatis
        self.state = self.TERMINATED
1834 e1017df9 Giorgos Korfiatis
        self.name = None
1835 123be68a Giorgos Korfiatis
        self.save()
1836 8aed306c Giorgos Korfiatis
1837 db99f198 Giorgos Korfiatis
    def suspend(self):
1838 db99f198 Giorgos Korfiatis
        self.deactivation_reason = 'SUSPENDED'
1839 3d4cef9e Giorgos Korfiatis
        self.deactivation_date = datetime.now()
1840 db99f198 Giorgos Korfiatis
        self.state = self.SUSPENDED
1841 db99f198 Giorgos Korfiatis
        self.save()
1842 db99f198 Giorgos Korfiatis
1843 db99f198 Giorgos Korfiatis
    def resume(self):
1844 db99f198 Giorgos Korfiatis
        self.deactivation_reason = None
1845 3d4cef9e Giorgos Korfiatis
        self.deactivation_date = None
1846 db99f198 Giorgos Korfiatis
        self.state = self.APPROVED
1847 db99f198 Giorgos Korfiatis
        self.save()
1848 123be68a Giorgos Korfiatis
1849 123be68a Giorgos Korfiatis
    ### Logical checks
1850 425e2e95 Sofia Papagiannaki
1851 e1a80257 Sofia Papagiannaki
    def is_inconsistent(self):
1852 e1a80257 Sofia Papagiannaki
        now = datetime.now()
1853 5b9e9530 Giorgos Korfiatis
        dates = [self.creation_date,
1854 5b9e9530 Giorgos Korfiatis
                 self.last_approval_date,
1855 5b9e9530 Giorgos Korfiatis
                 self.deactivation_date]
1856 5b9e9530 Giorgos Korfiatis
        return any([date > now for date in dates])
1857 5b9e9530 Giorgos Korfiatis
1858 db99f198 Giorgos Korfiatis
    def is_approved(self):
1859 db99f198 Giorgos Korfiatis
        return self.state == self.APPROVED
1860 db99f198 Giorgos Korfiatis
1861 123be68a Giorgos Korfiatis
    @property
1862 123be68a Giorgos Korfiatis
    def is_alive(self):
1863 72a6e1e8 Giorgos Korfiatis
        return not self.is_terminated
1864 123be68a Giorgos Korfiatis
1865 123be68a Giorgos Korfiatis
    @property
1866 123be68a Giorgos Korfiatis
    def is_terminated(self):
1867 123be68a Giorgos Korfiatis
        return self.is_deactivated(self.TERMINATED)
1868 123be68a Giorgos Korfiatis
1869 123be68a Giorgos Korfiatis
    @property
1870 123be68a Giorgos Korfiatis
    def is_suspended(self):
1871 db99f198 Giorgos Korfiatis
        return self.is_deactivated(self.SUSPENDED)
1872 5b9e9530 Giorgos Korfiatis
1873 5b9e9530 Giorgos Korfiatis
    def violates_resource_grants(self):
1874 e1a80257 Sofia Papagiannaki
        return False
1875 65360c65 Georgios D. Tsoukalas
1876 5b9e9530 Giorgos Korfiatis
    def violates_members_limit(self, adding=0):
1877 5b9e9530 Giorgos Korfiatis
        application = self.application
1878 943d5554 Giorgos Korfiatis
        limit = application.limit_on_members_number
1879 022c61cd Sofia Papagiannaki
        if limit is None:
1880 022c61cd Sofia Papagiannaki
            return False
1881 022c61cd Sofia Papagiannaki
        return (len(self.approved_members) + adding > limit)
1882 5b9e9530 Giorgos Korfiatis
1883 123be68a Giorgos Korfiatis
1884 123be68a Giorgos Korfiatis
    ### Other
1885 5b9e9530 Giorgos Korfiatis
1886 7db8c163 Georgios D. Tsoukalas
    def count_pending_memberships(self):
1887 7db8c163 Georgios D. Tsoukalas
        memb_set = self.projectmembership_set
1888 7db8c163 Georgios D. Tsoukalas
        memb_count = memb_set.filter(state=ProjectMembership.REQUESTED).count()
1889 7db8c163 Georgios D. Tsoukalas
        return memb_count
1890 7db8c163 Georgios D. Tsoukalas
1891 d77b32f2 Giorgos Korfiatis
    def members_count(self):
1892 d77b32f2 Giorgos Korfiatis
        return self.approved_memberships.count()
1893 d77b32f2 Giorgos Korfiatis
1894 425e2e95 Sofia Papagiannaki
    @property
1895 425e2e95 Sofia Papagiannaki
    def approved_memberships(self):
1896 689226c3 Giorgos Korfiatis
        query = ProjectMembership.Q_ACCEPTED_STATES
1897 5b9e9530 Giorgos Korfiatis
        return self.projectmembership_set.filter(query)
1898 4f22664f Georgios D. Tsoukalas
1899 425e2e95 Sofia Papagiannaki
    @property
1900 425e2e95 Sofia Papagiannaki
    def approved_members(self):
1901 425e2e95 Sofia Papagiannaki
        return [m.person for m in self.approved_memberships]
1902 4f22664f Georgios D. Tsoukalas
1903 425e2e95 Sofia Papagiannaki
1904 2529745f Giorgos Korfiatis
CHAIN_STATE = {
1905 2529745f Giorgos Korfiatis
    (Project.APPROVED,   ProjectApplication.PENDING)  : Chain.APPROVED_PENDING,
1906 2529745f Giorgos Korfiatis
    (Project.APPROVED,   ProjectApplication.APPROVED) : Chain.APPROVED,
1907 2529745f Giorgos Korfiatis
    (Project.APPROVED,   ProjectApplication.DENIED)   : Chain.APPROVED,
1908 2529745f Giorgos Korfiatis
    (Project.APPROVED,   ProjectApplication.DISMISSED): Chain.APPROVED,
1909 2529745f Giorgos Korfiatis
    (Project.APPROVED,   ProjectApplication.CANCELLED): Chain.APPROVED,
1910 2529745f Giorgos Korfiatis
1911 2529745f Giorgos Korfiatis
    (Project.SUSPENDED,  ProjectApplication.PENDING)  : Chain.SUSPENDED_PENDING,
1912 2529745f Giorgos Korfiatis
    (Project.SUSPENDED,  ProjectApplication.APPROVED) : Chain.SUSPENDED,
1913 2529745f Giorgos Korfiatis
    (Project.SUSPENDED,  ProjectApplication.DENIED)   : Chain.SUSPENDED,
1914 2529745f Giorgos Korfiatis
    (Project.SUSPENDED,  ProjectApplication.DISMISSED): Chain.SUSPENDED,
1915 2529745f Giorgos Korfiatis
    (Project.SUSPENDED,  ProjectApplication.CANCELLED): Chain.SUSPENDED,
1916 2529745f Giorgos Korfiatis
1917 2529745f Giorgos Korfiatis
    (Project.TERMINATED, ProjectApplication.PENDING)  : Chain.TERMINATED_PENDING,
1918 2529745f Giorgos Korfiatis
    (Project.TERMINATED, ProjectApplication.APPROVED) : Chain.TERMINATED,
1919 2529745f Giorgos Korfiatis
    (Project.TERMINATED, ProjectApplication.DENIED)   : Chain.TERMINATED,
1920 2529745f Giorgos Korfiatis
    (Project.TERMINATED, ProjectApplication.DISMISSED): Chain.TERMINATED,
1921 2529745f Giorgos Korfiatis
    (Project.TERMINATED, ProjectApplication.CANCELLED): Chain.TERMINATED,
1922 2529745f Giorgos Korfiatis
1923 2529745f Giorgos Korfiatis
    (None,               ProjectApplication.PENDING)  : Chain.PENDING,
1924 2529745f Giorgos Korfiatis
    (None,               ProjectApplication.DENIED)   : Chain.DENIED,
1925 2529745f Giorgos Korfiatis
    (None,               ProjectApplication.DISMISSED): Chain.DISMISSED,
1926 2529745f Giorgos Korfiatis
    (None,               ProjectApplication.CANCELLED): Chain.CANCELLED,
1927 2529745f Giorgos Korfiatis
    }
1928 2529745f Giorgos Korfiatis
1929 2529745f Giorgos Korfiatis
1930 db99f198 Giorgos Korfiatis
class ProjectMembershipManager(ForUpdateManager):
1931 d77b32f2 Giorgos Korfiatis
1932 d77b32f2 Giorgos Korfiatis
    def any_accepted(self):
1933 96efed67 Giorgos Korfiatis
        q = self.model.Q_ACTUALLY_ACCEPTED
1934 d77b32f2 Giorgos Korfiatis
        return self.filter(q)
1935 d77b32f2 Giorgos Korfiatis
1936 c1007621 Giorgos Korfiatis
    def actually_accepted(self):
1937 c1007621 Giorgos Korfiatis
        q = self.model.Q_ACTUALLY_ACCEPTED
1938 c1007621 Giorgos Korfiatis
        return self.filter(q)
1939 c1007621 Giorgos Korfiatis
1940 d77b32f2 Giorgos Korfiatis
    def requested(self):
1941 d77b32f2 Giorgos Korfiatis
        return self.filter(state=ProjectMembership.REQUESTED)
1942 d77b32f2 Giorgos Korfiatis
1943 d77b32f2 Giorgos Korfiatis
    def suspended(self):
1944 d77b32f2 Giorgos Korfiatis
        return self.filter(state=ProjectMembership.USER_SUSPENDED)
1945 db99f198 Giorgos Korfiatis
1946 d6fdc91e Georgios D. Tsoukalas
class ProjectMembership(models.Model):
1947 4f22664f Georgios D. Tsoukalas
1948 425e2e95 Sofia Papagiannaki
    person              =   models.ForeignKey(AstakosUser)
1949 95f33116 Giorgos Korfiatis
    request_date        =   models.DateTimeField(auto_now_add=True)
1950 d6fdc91e Georgios D. Tsoukalas
    project             =   models.ForeignKey(Project)
1951 d6fdc91e Georgios D. Tsoukalas
1952 db99f198 Giorgos Korfiatis
    REQUESTED           =   0
1953 db99f198 Giorgos Korfiatis
    ACCEPTED            =   1
1954 c1007621 Giorgos Korfiatis
    LEAVE_REQUESTED     =   5
1955 db99f198 Giorgos Korfiatis
    # User deactivation
1956 db99f198 Giorgos Korfiatis
    USER_SUSPENDED      =   10
1957 b6fe8bb8 Giorgos Korfiatis
1958 db99f198 Giorgos Korfiatis
    REMOVED             =   200
1959 db99f198 Giorgos Korfiatis
1960 db99f198 Giorgos Korfiatis
    ASSOCIATED_STATES   =   set([REQUESTED,
1961 db99f198 Giorgos Korfiatis
                                 ACCEPTED,
1962 c1007621 Giorgos Korfiatis
                                 LEAVE_REQUESTED,
1963 db99f198 Giorgos Korfiatis
                                 USER_SUSPENDED,
1964 96efed67 Giorgos Korfiatis
                                 ])
1965 db99f198 Giorgos Korfiatis
1966 db99f198 Giorgos Korfiatis
    ACCEPTED_STATES     =   set([ACCEPTED,
1967 c1007621 Giorgos Korfiatis
                                 LEAVE_REQUESTED,
1968 db99f198 Giorgos Korfiatis
                                 USER_SUSPENDED,
1969 96efed67 Giorgos Korfiatis
                                 ])
1970 05617ab9 Kostas Papadimitriou
1971 c1007621 Giorgos Korfiatis
    ACTUALLY_ACCEPTED   =   set([ACCEPTED, LEAVE_REQUESTED])
1972 c1007621 Giorgos Korfiatis
1973 b6fe8bb8 Giorgos Korfiatis
    state               =   models.IntegerField(default=REQUESTED,
1974 b6fe8bb8 Giorgos Korfiatis
                                                db_index=True)
1975 95f33116 Giorgos Korfiatis
    acceptance_date     =   models.DateTimeField(null=True, db_index=True)
1976 95f33116 Giorgos Korfiatis
    leave_request_date  =   models.DateTimeField(null=True)
1977 2a965273 Sofia Papagiannaki
1978 db99f198 Giorgos Korfiatis
    objects     =   ProjectMembershipManager()
1979 ee45eb81 Giorgos Korfiatis
1980 689226c3 Giorgos Korfiatis
    # Compiled queries
1981 689226c3 Giorgos Korfiatis
    Q_ACCEPTED_STATES = ~Q(state=REQUESTED) & ~Q(state=REMOVED)
1982 c1007621 Giorgos Korfiatis
    Q_ACTUALLY_ACCEPTED = Q(state=ACCEPTED) | Q(state=LEAVE_REQUESTED)
1983 5b9e9530 Giorgos Korfiatis
1984 d77b32f2 Giorgos Korfiatis
    MEMBERSHIP_STATE_DISPLAY = {
1985 d4660e00 Giorgos Korfiatis
        REQUESTED           : _('Requested'),
1986 d4660e00 Giorgos Korfiatis
        ACCEPTED            : _('Accepted'),
1987 c1007621 Giorgos Korfiatis
        LEAVE_REQUESTED     : _('Leave Requested'),
1988 d4660e00 Giorgos Korfiatis
        USER_SUSPENDED      : _('Suspended'),
1989 d4660e00 Giorgos Korfiatis
        REMOVED             : _('Pending removal'),
1990 d4660e00 Giorgos Korfiatis
        }
1991 d4660e00 Giorgos Korfiatis
1992 d4660e00 Giorgos Korfiatis
    USER_FRIENDLY_STATE_DISPLAY = {
1993 d4660e00 Giorgos Korfiatis
        REQUESTED           : _('Join requested'),
1994 d4660e00 Giorgos Korfiatis
        ACCEPTED            : _('Accepted member'),
1995 c1007621 Giorgos Korfiatis
        LEAVE_REQUESTED     : _('Requested to leave'),
1996 d4660e00 Giorgos Korfiatis
        USER_SUSPENDED      : _('Suspended member'),
1997 d4660e00 Giorgos Korfiatis
        REMOVED             : _('Pending removal'),
1998 d77b32f2 Giorgos Korfiatis
        }
1999 d77b32f2 Giorgos Korfiatis
2000 d77b32f2 Giorgos Korfiatis
    def state_display(self):
2001 d77b32f2 Giorgos Korfiatis
        return self.MEMBERSHIP_STATE_DISPLAY.get(self.state, _('Unknown'))
2002 d77b32f2 Giorgos Korfiatis
2003 d4660e00 Giorgos Korfiatis
    def user_friendly_state_display(self):
2004 d4660e00 Giorgos Korfiatis
        return self.USER_FRIENDLY_STATE_DISPLAY.get(self.state, _('Unknown'))
2005 d4660e00 Giorgos Korfiatis
2006 0cc22d47 Sofia Papagiannaki
    class Meta:
2007 0cc22d47 Sofia Papagiannaki
        unique_together = ("person", "project")
2008 d6fdc91e Georgios D. Tsoukalas
        #index_together = [["project", "state"]]
2009 bfe23b13 Sofia Papagiannaki
2010 65360c65 Georgios D. Tsoukalas
    def __str__(self):
2011 b6eaca30 Giorgos Korfiatis
        return uenc(_("<'%s' membership in '%s'>") % (
2012 b6eaca30 Giorgos Korfiatis
                self.person.username, self.project))
2013 65360c65 Georgios D. Tsoukalas
2014 65360c65 Georgios D. Tsoukalas
    __repr__ = __str__
2015 65360c65 Georgios D. Tsoukalas
2016 65360c65 Georgios D. Tsoukalas
    def __init__(self, *args, **kwargs):
2017 ee45eb81 Giorgos Korfiatis
        self.state = self.REQUESTED
2018 65360c65 Georgios D. Tsoukalas
        super(ProjectMembership, self).__init__(*args, **kwargs)
2019 65360c65 Georgios D. Tsoukalas
2020 4f22664f Georgios D. Tsoukalas
    def _set_history_item(self, reason, date=None):
2021 4f22664f Georgios D. Tsoukalas
        if isinstance(reason, basestring):
2022 4f22664f Georgios D. Tsoukalas
            reason = ProjectMembershipHistory.reasons.get(reason, -1)
2023 4f22664f Georgios D. Tsoukalas
2024 4f22664f Georgios D. Tsoukalas
        history_item = ProjectMembershipHistory(
2025 4f22664f Georgios D. Tsoukalas
                            serial=self.id,
2026 d0e78bbe Giorgos Korfiatis
                            person=self.person_id,
2027 02d2519e Giorgos Korfiatis
                            project=self.project_id,
2028 8f975b72 Sofia Papagiannaki
                            date=date or datetime.now(),
2029 4f22664f Georgios D. Tsoukalas
                            reason=reason)
2030 4f22664f Georgios D. Tsoukalas
        history_item.save()
2031 4f22664f Georgios D. Tsoukalas
        serial = history_item.id
2032 4f22664f Georgios D. Tsoukalas
2033 14f7f6a5 Giorgos Korfiatis
    def can_accept(self):
2034 14f7f6a5 Giorgos Korfiatis
        return self.state == self.REQUESTED
2035 14f7f6a5 Giorgos Korfiatis
2036 4f22664f Georgios D. Tsoukalas
    def accept(self):
2037 14f7f6a5 Giorgos Korfiatis
        if not self.can_accept():
2038 14f7f6a5 Giorgos Korfiatis
            m = _("%s: attempt to accept in state '%s'") % (self, self.state)
2039 65360c65 Georgios D. Tsoukalas
            raise AssertionError(m)
2040 4f22664f Georgios D. Tsoukalas
2041 65360c65 Georgios D. Tsoukalas
        now = datetime.now()
2042 65360c65 Georgios D. Tsoukalas
        self.acceptance_date = now
2043 65360c65 Georgios D. Tsoukalas
        self._set_history_item(reason='ACCEPT', date=now)
2044 96efed67 Giorgos Korfiatis
        self.state = self.ACCEPTED
2045 65360c65 Georgios D. Tsoukalas
        self.save()
2046 4f22664f Georgios D. Tsoukalas
2047 14f7f6a5 Giorgos Korfiatis
    def can_leave(self):
2048 c1007621 Giorgos Korfiatis
        return self.state in self.ACCEPTED_STATES
2049 c1007621 Giorgos Korfiatis
2050 c1007621 Giorgos Korfiatis
    def leave_request(self):
2051 c1007621 Giorgos Korfiatis
        if not self.can_leave():
2052 c1007621 Giorgos Korfiatis
            m = _("%s: attempt to request to leave in state '%s'") % (
2053 c1007621 Giorgos Korfiatis
                self, self.state)
2054 c1007621 Giorgos Korfiatis
            raise AssertionError(m)
2055 c1007621 Giorgos Korfiatis
2056 c1007621 Giorgos Korfiatis
        self.leave_request_date = datetime.now()
2057 c1007621 Giorgos Korfiatis
        self.state = self.LEAVE_REQUESTED
2058 c1007621 Giorgos Korfiatis
        self.save()
2059 c1007621 Giorgos Korfiatis
2060 c1007621 Giorgos Korfiatis
    def can_deny_leave(self):
2061 c1007621 Giorgos Korfiatis
        return self.state == self.LEAVE_REQUESTED
2062 c1007621 Giorgos Korfiatis
2063 c1007621 Giorgos Korfiatis
    def leave_request_deny(self):
2064 c1007621 Giorgos Korfiatis
        if not self.can_deny_leave():
2065 c1007621 Giorgos Korfiatis
            m = _("%s: attempt to deny leave request in state '%s'") % (
2066 c1007621 Giorgos Korfiatis
                self, self.state)
2067 c1007621 Giorgos Korfiatis
            raise AssertionError(m)
2068 c1007621 Giorgos Korfiatis
2069 c1007621 Giorgos Korfiatis
        self.leave_request_date = None
2070 c1007621 Giorgos Korfiatis
        self.state = self.ACCEPTED
2071 c1007621 Giorgos Korfiatis
        self.save()
2072 c1007621 Giorgos Korfiatis
2073 c1007621 Giorgos Korfiatis
    def can_cancel_leave(self):
2074 c1007621 Giorgos Korfiatis
        return self.state == self.LEAVE_REQUESTED
2075 c1007621 Giorgos Korfiatis
2076 c1007621 Giorgos Korfiatis
    def leave_request_cancel(self):
2077 c1007621 Giorgos Korfiatis
        if not self.can_cancel_leave():
2078 c1007621 Giorgos Korfiatis
            m = _("%s: attempt to cancel leave request in state '%s'") % (
2079 c1007621 Giorgos Korfiatis
                self, self.state)
2080 c1007621 Giorgos Korfiatis
            raise AssertionError(m)
2081 c1007621 Giorgos Korfiatis
2082 c1007621 Giorgos Korfiatis
        self.leave_request_date = None
2083 c1007621 Giorgos Korfiatis
        self.state = self.ACCEPTED
2084 c1007621 Giorgos Korfiatis
        self.save()
2085 14f7f6a5 Giorgos Korfiatis
2086 14f7f6a5 Giorgos Korfiatis
    def can_remove(self):
2087 14f7f6a5 Giorgos Korfiatis
        return self.state in self.ACCEPTED_STATES
2088 14f7f6a5 Giorgos Korfiatis
2089 65360c65 Georgios D. Tsoukalas
    def remove(self):
2090 14f7f6a5 Giorgos Korfiatis
        if not self.can_remove():
2091 14f7f6a5 Giorgos Korfiatis
            m = _("%s: attempt to remove in state '%s'") % (self, self.state)
2092 65360c65 Georgios D. Tsoukalas
            raise AssertionError(m)
2093 4f22664f Georgios D. Tsoukalas
2094 ee45eb81 Giorgos Korfiatis
        self._set_history_item(reason='REMOVE')
2095 8cbea11d Giorgos Korfiatis
        self.delete()
2096 b8f05f8d Sofia Papagiannaki
2097 14f7f6a5 Giorgos Korfiatis
    def can_reject(self):
2098 14f7f6a5 Giorgos Korfiatis
        return self.state == self.REQUESTED
2099 14f7f6a5 Giorgos Korfiatis
2100 65360c65 Georgios D. Tsoukalas
    def reject(self):
2101 14f7f6a5 Giorgos Korfiatis
        if not self.can_reject():
2102 14f7f6a5 Giorgos Korfiatis
            m = _("%s: attempt to reject in state '%s'") % (self, self.state)
2103 65360c65 Georgios D. Tsoukalas
            raise AssertionError(m)
2104 65360c65 Georgios D. Tsoukalas
2105 65360c65 Georgios D. Tsoukalas
        # rejected requests don't need sync,
2106 65360c65 Georgios D. Tsoukalas
        # because they were never effected
2107 65360c65 Georgios D. Tsoukalas
        self._set_history_item(reason='REJECT')
2108 0cc22d47 Sofia Papagiannaki
        self.delete()
2109 b8f05f8d Sofia Papagiannaki
2110 aad0e329 Giorgos Korfiatis
    def can_cancel(self):
2111 aad0e329 Giorgos Korfiatis
        return self.state == self.REQUESTED
2112 aad0e329 Giorgos Korfiatis
2113 aad0e329 Giorgos Korfiatis
    def cancel(self):
2114 aad0e329 Giorgos Korfiatis
        if not self.can_cancel():
2115 aad0e329 Giorgos Korfiatis
            m = _("%s: attempt to cancel in state '%s'") % (self, self.state)
2116 aad0e329 Giorgos Korfiatis
            raise AssertionError(m)
2117 aad0e329 Giorgos Korfiatis
2118 aad0e329 Giorgos Korfiatis
        # rejected requests don't need sync,
2119 aad0e329 Giorgos Korfiatis
        # because they were never effected
2120 aad0e329 Giorgos Korfiatis
        self._set_history_item(reason='CANCEL')
2121 aad0e329 Giorgos Korfiatis
        self.delete()
2122 aad0e329 Giorgos Korfiatis
2123 49b74233 Georgios D. Tsoukalas
2124 ee45eb81 Giorgos Korfiatis
class Serial(models.Model):
2125 ee45eb81 Giorgos Korfiatis
    serial  =   models.AutoField(primary_key=True)
2126 ee45eb81 Giorgos Korfiatis
2127 60ca2f6f Giorgos Korfiatis
2128 0cc22d47 Sofia Papagiannaki
class ProjectMembershipHistory(models.Model):
2129 425e2e95 Sofia Papagiannaki
    reasons_list    =   ['ACCEPT', 'REJECT', 'REMOVE']
2130 425e2e95 Sofia Papagiannaki
    reasons         =   dict((k, v) for v, k in enumerate(reasons_list))
2131 425e2e95 Sofia Papagiannaki
2132 d0e78bbe Giorgos Korfiatis
    person  =   models.BigIntegerField()
2133 02d2519e Giorgos Korfiatis
    project =   models.BigIntegerField()
2134 95f33116 Giorgos Korfiatis
    date    =   models.DateTimeField(auto_now_add=True)
2135 425e2e95 Sofia Papagiannaki
    reason  =   models.IntegerField()
2136 425e2e95 Sofia Papagiannaki
    serial  =   models.BigIntegerField()
2137 fc655b6f Kostas Papadimitriou
2138 fcc1e93f Sofia Papagiannaki
### SIGNALS ###
2139 fcc1e93f Sofia Papagiannaki
################
2140 b22de10a Sofia Papagiannaki
2141 ff9290ec Sofia Papagiannaki
def create_astakos_user(u):
2142 ff9290ec Sofia Papagiannaki
    try:
2143 ff9290ec Sofia Papagiannaki
        AstakosUser.objects.get(user_ptr=u.pk)
2144 ff9290ec Sofia Papagiannaki
    except AstakosUser.DoesNotExist:
2145 ff9290ec Sofia Papagiannaki
        extended_user = AstakosUser(user_ptr_id=u.pk)
2146 ff9290ec Sofia Papagiannaki
        extended_user.__dict__.update(u.__dict__)
2147 ff9290ec Sofia Papagiannaki
        extended_user.save()
2148 67be1883 Olga Brani
        if not extended_user.has_auth_provider('local'):
2149 67be1883 Olga Brani
            extended_user.add_auth_provider('local')
2150 fc1e2f02 Sofia Papagiannaki
    except BaseException, e:
2151 fc1e2f02 Sofia Papagiannaki
        logger.exception(e)
2152 ff9290ec Sofia Papagiannaki
2153 a7752e95 Sofia Papagiannaki
def fix_superusers():
2154 fc1e2f02 Sofia Papagiannaki
    # Associate superusers with AstakosUser
2155 ff9290ec Sofia Papagiannaki
    admins = User.objects.filter(is_superuser=True)
2156 ff9290ec Sofia Papagiannaki
    for u in admins:
2157 ff9290ec Sofia Papagiannaki
        create_astakos_user(u)
2158 ff9290ec Sofia Papagiannaki
2159 fc1e2f02 Sofia Papagiannaki
def user_post_save(sender, instance, created, **kwargs):
2160 aa4109d4 Sofia Papagiannaki
    if not created:
2161 aa4109d4 Sofia Papagiannaki
        return
2162 fc1e2f02 Sofia Papagiannaki
    create_astakos_user(instance)
2163 bfe23b13 Sofia Papagiannaki
post_save.connect(user_post_save, sender=User)
2164 ff9290ec Sofia Papagiannaki
2165 fc1e2f02 Sofia Papagiannaki
def astakosuser_post_save(sender, instance, created, **kwargs):
2166 21e0fdad Giorgos Korfiatis
    pass
2167 21e0fdad Giorgos Korfiatis
2168 bfe23b13 Sofia Papagiannaki
post_save.connect(astakosuser_post_save, sender=AstakosUser)
2169 fc1e2f02 Sofia Papagiannaki
2170 bd4f356c Sofia Papagiannaki
def resource_post_save(sender, instance, created, **kwargs):
2171 0514bcc7 Giorgos Korfiatis
    pass
2172 0514bcc7 Giorgos Korfiatis
2173 bfe23b13 Sofia Papagiannaki
post_save.connect(resource_post_save, sender=Resource)
2174 bfe23b13 Sofia Papagiannaki
2175 bfe23b13 Sofia Papagiannaki
def renew_token(sender, instance, **kwargs):
2176 bfe23b13 Sofia Papagiannaki
    if not instance.auth_token:
2177 bfe23b13 Sofia Papagiannaki
        instance.renew_token()
2178 bf0c6de5 Sofia Papagiannaki
pre_save.connect(renew_token, sender=AstakosUser)
2179 2e90e3ec Kostas Papadimitriou
pre_save.connect(renew_token, sender=Service)