Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (77 kB)

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

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

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

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

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

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

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