Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / models.py @ 992b81b6

History | View | Annotate | Download (82.2 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 0514bcc7 Giorgos Korfiatis
    QuotaValues, add_quota_values)
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 _quota_values(capacity):
259 0514bcc7 Giorgos Korfiatis
    return QuotaValues(
260 0514bcc7 Giorgos Korfiatis
        quantity = 0,
261 0514bcc7 Giorgos Korfiatis
        capacity = capacity,
262 0514bcc7 Giorgos Korfiatis
        import_limit = QH_PRACTICALLY_INFINITE,
263 0514bcc7 Giorgos Korfiatis
        export_limit = QH_PRACTICALLY_INFINITE)
264 0514bcc7 Giorgos Korfiatis
265 0514bcc7 Giorgos Korfiatis
def get_default_quota():
266 0514bcc7 Giorgos Korfiatis
    _DEFAULT_QUOTA = {}
267 a989b48e Giorgos Korfiatis
    resources = Resource.objects.select_related('service').all()
268 0514bcc7 Giorgos Korfiatis
    for resource in resources:
269 0514bcc7 Giorgos Korfiatis
        capacity = resource.uplimit
270 0514bcc7 Giorgos Korfiatis
        limits = _quota_values(capacity)
271 0514bcc7 Giorgos Korfiatis
        _DEFAULT_QUOTA[resource.full_name()] = limits
272 0514bcc7 Giorgos Korfiatis
273 0514bcc7 Giorgos Korfiatis
    return _DEFAULT_QUOTA
274 0514bcc7 Giorgos Korfiatis
275 0514bcc7 Giorgos Korfiatis
def get_resource_names():
276 0514bcc7 Giorgos Korfiatis
    _RESOURCE_NAMES = []
277 a989b48e Giorgos Korfiatis
    resources = Resource.objects.select_related('service').all()
278 0514bcc7 Giorgos Korfiatis
    _RESOURCE_NAMES = [resource.full_name() for resource in resources]
279 0514bcc7 Giorgos Korfiatis
    return _RESOURCE_NAMES
280 d2633501 Kostas Papadimitriou
281 43332a76 Kostas Papadimitriou
282 6b9a334b Sofia Papagiannaki
class AstakosUserManager(UserManager):
283 d2633501 Kostas Papadimitriou
284 d2633501 Kostas Papadimitriou
    def get_auth_provider_user(self, provider, **kwargs):
285 d2633501 Kostas Papadimitriou
        """
286 d2633501 Kostas Papadimitriou
        Retrieve AstakosUser instance associated with the specified third party
287 d2633501 Kostas Papadimitriou
        id.
288 d2633501 Kostas Papadimitriou
        """
289 d2633501 Kostas Papadimitriou
        kwargs = dict(map(lambda x: ('auth_providers__%s' % x[0], x[1]),
290 d2633501 Kostas Papadimitriou
                          kwargs.iteritems()))
291 d2633501 Kostas Papadimitriou
        return self.get(auth_providers__module=provider, **kwargs)
292 d2633501 Kostas Papadimitriou
293 c630fee6 Kostas Papadimitriou
    def get_by_email(self, email):
294 c630fee6 Kostas Papadimitriou
        return self.get(email=email)
295 c630fee6 Kostas Papadimitriou
296 e5966bd9 Kostas Papadimitriou
    def get_by_identifier(self, email_or_username, **kwargs):
297 e5966bd9 Kostas Papadimitriou
        try:
298 e5966bd9 Kostas Papadimitriou
            return self.get(email__iexact=email_or_username, **kwargs)
299 e5966bd9 Kostas Papadimitriou
        except AstakosUser.DoesNotExist:
300 e5966bd9 Kostas Papadimitriou
            return self.get(username__iexact=email_or_username, **kwargs)
301 e5966bd9 Kostas Papadimitriou
302 e5966bd9 Kostas Papadimitriou
    def user_exists(self, email_or_username, **kwargs):
303 e5966bd9 Kostas Papadimitriou
        qemail = Q(email__iexact=email_or_username)
304 e5966bd9 Kostas Papadimitriou
        qusername = Q(username__iexact=email_or_username)
305 43332a76 Kostas Papadimitriou
        qextra = Q(**kwargs)
306 43332a76 Kostas Papadimitriou
        return self.filter((qemail | qusername) & qextra).exists()
307 43332a76 Kostas Papadimitriou
308 43332a76 Kostas Papadimitriou
    def verified_user_exists(self, email_or_username):
309 43332a76 Kostas Papadimitriou
        return self.user_exists(email_or_username, email_verified=True)
310 43332a76 Kostas Papadimitriou
311 43332a76 Kostas Papadimitriou
    def verified(self):
312 43332a76 Kostas Papadimitriou
        return self.filter(email_verified=True)
313 e5966bd9 Kostas Papadimitriou
314 890c2065 Sofia Papagiannaki
    def uuid_catalog(self, l=None):
315 890c2065 Sofia Papagiannaki
        """
316 890c2065 Sofia Papagiannaki
        Returns a uuid to username mapping for the uuids appearing in l.
317 890c2065 Sofia Papagiannaki
        If l is None returns the mapping for all existing users.
318 890c2065 Sofia Papagiannaki
        """
319 890c2065 Sofia Papagiannaki
        q = self.filter(uuid__in=l) if l != None else self
320 890c2065 Sofia Papagiannaki
        return dict(q.values_list('uuid', 'username'))
321 890c2065 Sofia Papagiannaki
322 890c2065 Sofia Papagiannaki
    def displayname_catalog(self, l=None):
323 890c2065 Sofia Papagiannaki
        """
324 890c2065 Sofia Papagiannaki
        Returns a username to uuid mapping for the usernames appearing in l.
325 890c2065 Sofia Papagiannaki
        If l is None returns the mapping for all existing users.
326 890c2065 Sofia Papagiannaki
        """
327 ea05c568 Sofia Papagiannaki
        if l is not None:
328 ea05c568 Sofia Papagiannaki
            lmap = dict((x.lower(), x) for x in l)
329 ea05c568 Sofia Papagiannaki
            q = self.filter(username__in=lmap.keys())
330 ea05c568 Sofia Papagiannaki
            values = ((lmap[n], u) for n, u in q.values_list('username', 'uuid'))
331 ea05c568 Sofia Papagiannaki
        else:
332 ea05c568 Sofia Papagiannaki
            q = self
333 ea05c568 Sofia Papagiannaki
            values = self.values_list('username', 'uuid')
334 ea05c568 Sofia Papagiannaki
        return dict(values)
335 890c2065 Sofia Papagiannaki
336 890c2065 Sofia Papagiannaki
337 e5966bd9 Kostas Papadimitriou
338 0905ccd2 Sofia Papagiannaki
class AstakosUser(User):
339 890b0eaf Sofia Papagiannaki
    """
340 890b0eaf Sofia Papagiannaki
    Extends ``django.contrib.auth.models.User`` by defining additional fields.
341 890b0eaf Sofia Papagiannaki
    """
342 e1a80257 Sofia Papagiannaki
    affiliation = models.CharField(_('Affiliation'), max_length=255, blank=True,
343 d2633501 Kostas Papadimitriou
                                   null=True)
344 d2633501 Kostas Papadimitriou
345 d2633501 Kostas Papadimitriou
    # DEPRECATED FIELDS: provider, third_party_identifier moved in
346 d2633501 Kostas Papadimitriou
    #                    AstakosUserProvider model.
347 e1a80257 Sofia Papagiannaki
    provider = models.CharField(_('Provider'), max_length=255, blank=True,
348 d2633501 Kostas Papadimitriou
                                null=True)
349 d2633501 Kostas Papadimitriou
    # ex. screen_name for twitter, eppn for shibboleth
350 e1a80257 Sofia Papagiannaki
    third_party_identifier = models.CharField(_('Third-party identifier'),
351 d2633501 Kostas Papadimitriou
                                              max_length=255, null=True,
352 d2633501 Kostas Papadimitriou
                                              blank=True)
353 d2633501 Kostas Papadimitriou
354 6c736ed7 Kostas Papadimitriou
355 64cd4730 Antony Chazapis
    #for invitations
356 92defad4 Sofia Papagiannaki
    user_level = DEFAULT_USER_LEVEL
357 e1a80257 Sofia Papagiannaki
    level = models.IntegerField(_('Inviter level'), default=user_level)
358 5ce3ce4f Sofia Papagiannaki
    invitations = models.IntegerField(
359 e1a80257 Sofia Papagiannaki
        _('Invitations left'), default=INVITATIONS_PER_LEVEL.get(user_level, 0))
360 6c736ed7 Kostas Papadimitriou
361 9d20fe23 Kostas Papadimitriou
    auth_token = models.CharField(_('Authentication Token'),
362 d6ea9b3d Olga Brani
                                  max_length=32,
363 9d20fe23 Kostas Papadimitriou
                                  null=True,
364 9d20fe23 Kostas Papadimitriou
                                  blank=True,
365 c9a6e558 Constantinos Venetsanopoulos
                                  help_text = _('Renew your authentication '
366 c9a6e558 Constantinos Venetsanopoulos
                                                'token. Make sure to set the new '
367 c9a6e558 Constantinos Venetsanopoulos
                                                'token in any client you may be '
368 c9a6e558 Constantinos Venetsanopoulos
                                                'using, to preserve its '
369 c9a6e558 Constantinos Venetsanopoulos
                                                'functionality.'))
370 9d20fe23 Kostas Papadimitriou
    auth_token_created = models.DateTimeField(_('Token creation date'),
371 d6ea9b3d Olga Brani
                                              null=True)
372 5ce3ce4f Sofia Papagiannaki
    auth_token_expires = models.DateTimeField(
373 e1a80257 Sofia Papagiannaki
        _('Token expiration date'), null=True)
374 6c736ed7 Kostas Papadimitriou
375 e1a80257 Sofia Papagiannaki
    updated = models.DateTimeField(_('Update date'))
376 e1a80257 Sofia Papagiannaki
    is_verified = models.BooleanField(_('Is verified?'), default=False)
377 6c736ed7 Kostas Papadimitriou
378 e1a80257 Sofia Papagiannaki
    email_verified = models.BooleanField(_('Email verified?'), default=False)
379 6c736ed7 Kostas Papadimitriou
380 e1a80257 Sofia Papagiannaki
    has_credits = models.BooleanField(_('Has credits?'), default=False)
381 5ce3ce4f Sofia Papagiannaki
    has_signed_terms = models.BooleanField(
382 e1a80257 Sofia Papagiannaki
        _('I agree with the terms'), default=False)
383 5ce3ce4f Sofia Papagiannaki
    date_signed_terms = models.DateTimeField(
384 e1a80257 Sofia Papagiannaki
        _('Signed terms date'), null=True, blank=True)
385 5ce3ce4f Sofia Papagiannaki
386 5ce3ce4f Sofia Papagiannaki
    activation_sent = models.DateTimeField(
387 e1a80257 Sofia Papagiannaki
        _('Activation sent data'), null=True, blank=True)
388 5ce3ce4f Sofia Papagiannaki
389 5ce3ce4f Sofia Papagiannaki
    policy = models.ManyToManyField(
390 5ce3ce4f Sofia Papagiannaki
        Resource, null=True, through='AstakosUserQuota')
391 5ce3ce4f Sofia Papagiannaki
392 836a0fb0 Kostas Papadimitriou
    uuid = models.CharField(max_length=255, null=True, blank=False, unique=True)
393 836a0fb0 Kostas Papadimitriou
394 18ffbee1 Sofia Papagiannaki
    __has_signed_terms = False
395 e1a80257 Sofia Papagiannaki
    disturbed_quota = models.BooleanField(_('Needs quotaholder syncing'),
396 9a06d96f Olga Brani
                                           default=False, db_index=True)
397 d2633501 Kostas Papadimitriou
398 d2633501 Kostas Papadimitriou
    objects = AstakosUserManager()
399 fbaa4f3c Kostas Papadimitriou
400 18ffbee1 Sofia Papagiannaki
    def __init__(self, *args, **kwargs):
401 18ffbee1 Sofia Papagiannaki
        super(AstakosUser, self).__init__(*args, **kwargs)
402 18ffbee1 Sofia Papagiannaki
        self.__has_signed_terms = self.has_signed_terms
403 a3637508 Sofia Papagiannaki
        if not self.id:
404 18ffbee1 Sofia Papagiannaki
            self.is_active = False
405 5ce3ce4f Sofia Papagiannaki
406 0905ccd2 Sofia Papagiannaki
    @property
407 0905ccd2 Sofia Papagiannaki
    def realname(self):
408 5ce3ce4f Sofia Papagiannaki
        return '%s %s' % (self.first_name, self.last_name)
409 6c736ed7 Kostas Papadimitriou
410 5df4c364 Kostas Papadimitriou
    @property
411 5df4c364 Kostas Papadimitriou
    def log_display(self):
412 5df4c364 Kostas Papadimitriou
        """
413 5df4c364 Kostas Papadimitriou
        Should be used in all logger.* calls that refer to a user so that
414 5df4c364 Kostas Papadimitriou
        user display is consistent across log entries.
415 5df4c364 Kostas Papadimitriou
        """
416 5d5ce247 Kostas Papadimitriou
        return '%s::%s' % (self.uuid, self.email)
417 5df4c364 Kostas Papadimitriou
418 0905ccd2 Sofia Papagiannaki
    @realname.setter
419 0905ccd2 Sofia Papagiannaki
    def realname(self, value):
420 0905ccd2 Sofia Papagiannaki
        parts = value.split(' ')
421 0905ccd2 Sofia Papagiannaki
        if len(parts) == 2:
422 0905ccd2 Sofia Papagiannaki
            self.first_name = parts[0]
423 0905ccd2 Sofia Papagiannaki
            self.last_name = parts[1]
424 0905ccd2 Sofia Papagiannaki
        else:
425 0905ccd2 Sofia Papagiannaki
            self.last_name = parts[0]
426 6c736ed7 Kostas Papadimitriou
427 9a06d96f Olga Brani
    def add_permission(self, pname):
428 9a06d96f Olga Brani
        if self.has_perm(pname):
429 9a06d96f Olga Brani
            return
430 e65c21df Georgios D. Tsoukalas
        p, created = Permission.objects.get_or_create(
431 e65c21df Georgios D. Tsoukalas
                                    codename=pname,
432 e65c21df Georgios D. Tsoukalas
                                    name=pname.capitalize(),
433 e65c21df Georgios D. Tsoukalas
                                    content_type=get_content_type())
434 9a06d96f Olga Brani
        self.user_permissions.add(p)
435 9a06d96f Olga Brani
436 9a06d96f Olga Brani
    def remove_permission(self, pname):
437 9a06d96f Olga Brani
        if self.has_perm(pname):
438 9a06d96f Olga Brani
            return
439 9a06d96f Olga Brani
        p = Permission.objects.get(codename=pname,
440 e65c21df Georgios D. Tsoukalas
                                   content_type=get_content_type())
441 9a06d96f Olga Brani
        self.user_permissions.remove(p)
442 9a06d96f Olga Brani
443 8e1a5af5 Georgios D. Tsoukalas
    def is_project_admin(self, application_id=None):
444 8e1a5af5 Georgios D. Tsoukalas
        return self.uuid in PROJECT_ADMINS
445 8e1a5af5 Georgios D. Tsoukalas
446 64cd4730 Antony Chazapis
    @property
447 64cd4730 Antony Chazapis
    def invitation(self):
448 64cd4730 Antony Chazapis
        try:
449 9fb8e808 Sofia Papagiannaki
            return Invitation.objects.get(username=self.email)
450 64cd4730 Antony Chazapis
        except Invitation.DoesNotExist:
451 64cd4730 Antony Chazapis
            return None
452 6c736ed7 Kostas Papadimitriou
453 9a06d96f Olga Brani
    @property
454 9a06d96f Olga Brani
    def policies(self):
455 9a06d96f Olga Brani
        return self.astakosuserquota_set.select_related().all()
456 9a06d96f Olga Brani
457 9a06d96f Olga Brani
    @policies.setter
458 9a06d96f Olga Brani
    def policies(self, policies):
459 9a06d96f Olga Brani
        for p in policies:
460 6c997921 Sofia Papagiannaki
            p.setdefault('resource', '')
461 6c997921 Sofia Papagiannaki
            p.setdefault('capacity', 0)
462 6c997921 Sofia Papagiannaki
            p.setdefault('quantity', 0)
463 6c997921 Sofia Papagiannaki
            p.setdefault('import_limit', 0)
464 6c997921 Sofia Papagiannaki
            p.setdefault('export_limit', 0)
465 6c997921 Sofia Papagiannaki
            p.setdefault('update', True)
466 6c997921 Sofia Papagiannaki
            self.add_resource_policy(**p)
467 6c997921 Sofia Papagiannaki
468 6c997921 Sofia Papagiannaki
    def add_resource_policy(
469 6c997921 Sofia Papagiannaki
            self, resource, capacity, quantity, import_limit,
470 6c997921 Sofia Papagiannaki
            export_limit, update=True):
471 9a06d96f Olga Brani
        """Raises ObjectDoesNotExist, IntegrityError"""
472 6c997921 Sofia Papagiannaki
        s, sep, r = resource.partition(RESOURCE_SEPARATOR)
473 6c997921 Sofia Papagiannaki
        resource = Resource.objects.get(service__name=s, name=r)
474 9a06d96f Olga Brani
        if update:
475 6c997921 Sofia Papagiannaki
            AstakosUserQuota.objects.update_or_create(
476 6c997921 Sofia Papagiannaki
                user=self, resource=resource, defaults={
477 6c997921 Sofia Papagiannaki
                    'capacity':capacity,
478 6c997921 Sofia Papagiannaki
                    'quantity': quantity,
479 6c997921 Sofia Papagiannaki
                    'import_limit':import_limit,
480 6c997921 Sofia Papagiannaki
                    'export_limit':export_limit})
481 9a06d96f Olga Brani
        else:
482 9a06d96f Olga Brani
            q = self.astakosuserquota_set
483 6c997921 Sofia Papagiannaki
            q.create(
484 6c997921 Sofia Papagiannaki
                resource=resource, capacity=capacity, quanity=quantity,
485 6c997921 Sofia Papagiannaki
                import_limit=import_limit, export_limit=export_limit)
486 9a06d96f Olga Brani
487 8d1636b5 Giorgos Korfiatis
    def get_resource_policy(self, resource):
488 8d1636b5 Giorgos Korfiatis
        s, sep, r = resource.partition(RESOURCE_SEPARATOR)
489 8d1636b5 Giorgos Korfiatis
        resource = Resource.objects.get(service__name=s, name=r)
490 88f4d3b3 Georgios D. Tsoukalas
        default_capacity = resource.uplimit
491 8d1636b5 Giorgos Korfiatis
        try:
492 88f4d3b3 Georgios D. Tsoukalas
            policy = AstakosUserQuota.objects.get(user=self, resource=resource)
493 88f4d3b3 Georgios D. Tsoukalas
            return policy, default_capacity
494 8d1636b5 Giorgos Korfiatis
        except AstakosUserQuota.DoesNotExist:
495 88f4d3b3 Georgios D. Tsoukalas
            return None, default_capacity
496 8d1636b5 Giorgos Korfiatis
497 6c997921 Sofia Papagiannaki
    def remove_resource_policy(self, service, resource):
498 9a06d96f Olga Brani
        """Raises ObjectDoesNotExist, IntegrityError"""
499 9a06d96f Olga Brani
        resource = Resource.objects.get(service__name=service, name=resource)
500 9a06d96f Olga Brani
        q = self.policies.get(resource=resource).delete()
501 9a06d96f Olga Brani
502 836a0fb0 Kostas Papadimitriou
    def update_uuid(self):
503 836a0fb0 Kostas Papadimitriou
        while not self.uuid:
504 836a0fb0 Kostas Papadimitriou
            uuid_val =  str(uuid.uuid4())
505 836a0fb0 Kostas Papadimitriou
            try:
506 836a0fb0 Kostas Papadimitriou
                AstakosUser.objects.get(uuid=uuid_val)
507 836a0fb0 Kostas Papadimitriou
            except AstakosUser.DoesNotExist, e:
508 836a0fb0 Kostas Papadimitriou
                self.uuid = uuid_val
509 836a0fb0 Kostas Papadimitriou
        return self.uuid
510 836a0fb0 Kostas Papadimitriou
511 64cd4730 Antony Chazapis
    def save(self, update_timestamps=True, **kwargs):
512 64cd4730 Antony Chazapis
        if update_timestamps:
513 64cd4730 Antony Chazapis
            if not self.id:
514 0905ccd2 Sofia Papagiannaki
                self.date_joined = datetime.now()
515 64cd4730 Antony Chazapis
            self.updated = datetime.now()
516 5ce3ce4f Sofia Papagiannaki
517 18ffbee1 Sofia Papagiannaki
        # update date_signed_terms if necessary
518 18ffbee1 Sofia Papagiannaki
        if self.__has_signed_terms != self.has_signed_terms:
519 18ffbee1 Sofia Papagiannaki
            self.date_signed_terms = datetime.now()
520 5ce3ce4f Sofia Papagiannaki
521 836a0fb0 Kostas Papadimitriou
        self.update_uuid()
522 836a0fb0 Kostas Papadimitriou
523 836a0fb0 Kostas Papadimitriou
        if self.username != self.email.lower():
524 9c01d9d1 Sofia Papagiannaki
            # set username
525 e5966bd9 Kostas Papadimitriou
            self.username = self.email.lower()
526 fbaa4f3c Kostas Papadimitriou
527 0905ccd2 Sofia Papagiannaki
        super(AstakosUser, self).save(**kwargs)
528 2e90e3ec Kostas Papadimitriou
529 bf0c6de5 Sofia Papagiannaki
    def renew_token(self, flush_sessions=False, current_key=None):
530 64cd4730 Antony Chazapis
        md5 = hashlib.md5()
531 8f8c43b2 Sofia Papagiannaki
        md5.update(settings.SECRET_KEY)
532 0905ccd2 Sofia Papagiannaki
        md5.update(self.username)
533 64cd4730 Antony Chazapis
        md5.update(self.realname.encode('ascii', 'ignore'))
534 64cd4730 Antony Chazapis
        md5.update(asctime())
535 2e90e3ec Kostas Papadimitriou
536 64cd4730 Antony Chazapis
        self.auth_token = b64encode(md5.digest())
537 64cd4730 Antony Chazapis
        self.auth_token_created = datetime.now()
538 64cd4730 Antony Chazapis
        self.auth_token_expires = self.auth_token_created + \
539 92defad4 Sofia Papagiannaki
                                  timedelta(hours=AUTH_TOKEN_DURATION)
540 bf0c6de5 Sofia Papagiannaki
        if flush_sessions:
541 bf0c6de5 Sofia Papagiannaki
            self.flush_sessions(current_key)
542 111f3da6 Sofia Papagiannaki
        msg = 'Token renewed for %s' % self.email
543 aab4d540 Sofia Papagiannaki
        logger.log(LOGGING_LEVEL, msg)
544 6c736ed7 Kostas Papadimitriou
545 bf0c6de5 Sofia Papagiannaki
    def flush_sessions(self, current_key=None):
546 bf0c6de5 Sofia Papagiannaki
        q = self.sessions
547 bf0c6de5 Sofia Papagiannaki
        if current_key:
548 bf0c6de5 Sofia Papagiannaki
            q = q.exclude(session_key=current_key)
549 2e90e3ec Kostas Papadimitriou
550 bf0c6de5 Sofia Papagiannaki
        keys = q.values_list('session_key', flat=True)
551 bf0c6de5 Sofia Papagiannaki
        if keys:
552 bf0c6de5 Sofia Papagiannaki
            msg = 'Flushing sessions: %s' % ','.join(keys)
553 c0b26605 Sofia Papagiannaki
            logger.log(LOGGING_LEVEL, msg, [])
554 bf0c6de5 Sofia Papagiannaki
        engine = import_module(settings.SESSION_ENGINE)
555 bf0c6de5 Sofia Papagiannaki
        for k in keys:
556 bf0c6de5 Sofia Papagiannaki
            s = engine.SessionStore(k)
557 bf0c6de5 Sofia Papagiannaki
            s.flush()
558 bf0c6de5 Sofia Papagiannaki
559 64cd4730 Antony Chazapis
    def __unicode__(self):
560 3abf6c78 Sofia Papagiannaki
        return '%s (%s)' % (self.realname, self.email)
561 5ce3ce4f Sofia Papagiannaki
562 0a569195 Sofia Papagiannaki
    def conflicting_email(self):
563 5ce3ce4f Sofia Papagiannaki
        q = AstakosUser.objects.exclude(username=self.username)
564 789a5951 Sofia Papagiannaki
        q = q.filter(email__iexact=self.email)
565 0a569195 Sofia Papagiannaki
        if q.count() != 0:
566 0a569195 Sofia Papagiannaki
            return True
567 0a569195 Sofia Papagiannaki
        return False
568 5ce3ce4f Sofia Papagiannaki
569 34a76cdb Kostas Papadimitriou
    def email_change_is_pending(self):
570 34a76cdb Kostas Papadimitriou
        return self.emailchanges.count() > 0
571 34a76cdb Kostas Papadimitriou
572 fcf90160 Sofia Papagiannaki
    @property
573 09e7393c Sofia Papagiannaki
    def signed_terms(self):
574 09e7393c Sofia Papagiannaki
        term = get_latest_terms()
575 09e7393c Sofia Papagiannaki
        if not term:
576 09e7393c Sofia Papagiannaki
            return True
577 09e7393c Sofia Papagiannaki
        if not self.has_signed_terms:
578 09e7393c Sofia Papagiannaki
            return False
579 09e7393c Sofia Papagiannaki
        if not self.date_signed_terms:
580 09e7393c Sofia Papagiannaki
            return False
581 09e7393c Sofia Papagiannaki
        if self.date_signed_terms < term.date:
582 09e7393c Sofia Papagiannaki
            self.has_signed_terms = False
583 f0f92965 Sofia Papagiannaki
            self.date_signed_terms = None
584 09e7393c Sofia Papagiannaki
            self.save()
585 09e7393c Sofia Papagiannaki
            return False
586 09e7393c Sofia Papagiannaki
        return True
587 09e7393c Sofia Papagiannaki
588 d2633501 Kostas Papadimitriou
    def set_invitations_level(self):
589 d2633501 Kostas Papadimitriou
        """
590 d2633501 Kostas Papadimitriou
        Update user invitation level
591 d2633501 Kostas Papadimitriou
        """
592 d2633501 Kostas Papadimitriou
        level = self.invitation.inviter.level + 1
593 d2633501 Kostas Papadimitriou
        self.level = level
594 d2633501 Kostas Papadimitriou
        self.invitations = INVITATIONS_PER_LEVEL.get(level, 0)
595 d2633501 Kostas Papadimitriou
596 9d20fe23 Kostas Papadimitriou
    def can_change_password(self):
597 9d20fe23 Kostas Papadimitriou
        return self.has_auth_provider('local', auth_backend='astakos')
598 d2633501 Kostas Papadimitriou
599 9d20fe23 Kostas Papadimitriou
    def can_change_email(self):
600 9d20fe23 Kostas Papadimitriou
        if not self.has_auth_provider('local'):
601 9d20fe23 Kostas Papadimitriou
            return True
602 63836eda Kostas Papadimitriou
603 9d20fe23 Kostas Papadimitriou
        local = self.get_auth_provider('local')._instance
604 9d20fe23 Kostas Papadimitriou
        return local.auth_backend == 'astakos'
605 63836eda Kostas Papadimitriou
606 9d20fe23 Kostas Papadimitriou
    # Auth providers related methods
607 9d20fe23 Kostas Papadimitriou
    def get_auth_provider(self, module=None, identifier=None, **filters):
608 9d20fe23 Kostas Papadimitriou
        if not module:
609 9d20fe23 Kostas Papadimitriou
            return self.auth_providers.active()[0].settings
610 63836eda Kostas Papadimitriou
611 97246b51 Kostas Papadimitriou
        params = {'module': module}
612 97246b51 Kostas Papadimitriou
        if identifier:
613 97246b51 Kostas Papadimitriou
            params['identifier'] = identifier
614 97246b51 Kostas Papadimitriou
        params.update(filters)
615 97246b51 Kostas Papadimitriou
        return self.auth_providers.active().get(**params).settings
616 d2633501 Kostas Papadimitriou
617 9d20fe23 Kostas Papadimitriou
    def has_auth_provider(self, provider, **kwargs):
618 9d20fe23 Kostas Papadimitriou
        return bool(self.auth_providers.active().filter(module=provider,
619 9d20fe23 Kostas Papadimitriou
                                                        **kwargs).count())
620 d2633501 Kostas Papadimitriou
621 9d20fe23 Kostas Papadimitriou
    def get_required_providers(self, **kwargs):
622 9d20fe23 Kostas Papadimitriou
        return auth.REQUIRED_PROVIDERS.keys()
623 649f2d36 Kostas Papadimitriou
624 9d20fe23 Kostas Papadimitriou
    def missing_required_providers(self):
625 9d20fe23 Kostas Papadimitriou
        required = self.get_required_providers()
626 9d20fe23 Kostas Papadimitriou
        missing = []
627 63836eda Kostas Papadimitriou
        for provider in required:
628 63836eda Kostas Papadimitriou
            if not self.has_auth_provider(provider):
629 9d20fe23 Kostas Papadimitriou
                missing.append(auth.get_provider(provider, self))
630 9d20fe23 Kostas Papadimitriou
        return missing
631 63836eda Kostas Papadimitriou
632 9d20fe23 Kostas Papadimitriou
    def get_available_auth_providers(self, **filters):
633 d2633501 Kostas Papadimitriou
        """
634 9d20fe23 Kostas Papadimitriou
        Returns a list of providers available for add by the user.
635 d2633501 Kostas Papadimitriou
        """
636 9d20fe23 Kostas Papadimitriou
        modules = astakos_settings.IM_MODULES
637 9d20fe23 Kostas Papadimitriou
        providers = []
638 9d20fe23 Kostas Papadimitriou
        for p in modules:
639 9d20fe23 Kostas Papadimitriou
            providers.append(auth.get_provider(p, self))
640 9d20fe23 Kostas Papadimitriou
        available = []
641 9d20fe23 Kostas Papadimitriou
642 9d20fe23 Kostas Papadimitriou
        for p in providers:
643 9d20fe23 Kostas Papadimitriou
            if p.get_add_policy:
644 9d20fe23 Kostas Papadimitriou
                available.append(p)
645 9d20fe23 Kostas Papadimitriou
        return available
646 9d20fe23 Kostas Papadimitriou
647 9d20fe23 Kostas Papadimitriou
    def get_disabled_auth_providers(self, **filters):
648 9d20fe23 Kostas Papadimitriou
        providers = self.get_auth_providers(**filters)
649 9d20fe23 Kostas Papadimitriou
        disabled = []
650 9d20fe23 Kostas Papadimitriou
        for p in providers:
651 9d20fe23 Kostas Papadimitriou
            if not p.get_login_policy:
652 9d20fe23 Kostas Papadimitriou
                disabled.append(p)
653 9d20fe23 Kostas Papadimitriou
        return disabled
654 9d20fe23 Kostas Papadimitriou
655 9d20fe23 Kostas Papadimitriou
    def get_enabled_auth_providers(self, **filters):
656 9d20fe23 Kostas Papadimitriou
        providers = self.get_auth_providers(**filters)
657 9d20fe23 Kostas Papadimitriou
        enabled = []
658 9d20fe23 Kostas Papadimitriou
        for p in providers:
659 9d20fe23 Kostas Papadimitriou
            if p.get_login_policy:
660 9d20fe23 Kostas Papadimitriou
                enabled.append(p)
661 9d20fe23 Kostas Papadimitriou
        return enabled
662 9d20fe23 Kostas Papadimitriou
663 9d20fe23 Kostas Papadimitriou
    def get_auth_providers(self, **filters):
664 9d20fe23 Kostas Papadimitriou
        providers = []
665 9d20fe23 Kostas Papadimitriou
        for provider in self.auth_providers.active(**filters):
666 9d20fe23 Kostas Papadimitriou
            if provider.settings.module_enabled:
667 9d20fe23 Kostas Papadimitriou
                providers.append(provider.settings)
668 d2633501 Kostas Papadimitriou
669 9d20fe23 Kostas Papadimitriou
        modules = astakos_settings.IM_MODULES
670 d2633501 Kostas Papadimitriou
671 9d20fe23 Kostas Papadimitriou
        def key(p):
672 9d20fe23 Kostas Papadimitriou
            if not p.module in modules:
673 9d20fe23 Kostas Papadimitriou
                return 100
674 9d20fe23 Kostas Papadimitriou
            return modules.index(p.module)
675 9d20fe23 Kostas Papadimitriou
676 9d20fe23 Kostas Papadimitriou
        providers = sorted(providers, key=key)
677 9d20fe23 Kostas Papadimitriou
        return providers
678 d2633501 Kostas Papadimitriou
679 9d20fe23 Kostas Papadimitriou
    # URL methods
680 9d20fe23 Kostas Papadimitriou
    @property
681 9d20fe23 Kostas Papadimitriou
    def auth_providers_display(self):
682 9d20fe23 Kostas Papadimitriou
        return ",".join(["%s:%s" % (p.module, p.get_username_msg) for p in
683 9d20fe23 Kostas Papadimitriou
                         self.get_enabled_auth_providers()])
684 d2633501 Kostas Papadimitriou
685 9d20fe23 Kostas Papadimitriou
    def add_auth_provider(self, module='local', identifier=None, **params):
686 9d20fe23 Kostas Papadimitriou
        provider = auth.get_provider(module, self, identifier, **params)
687 9d20fe23 Kostas Papadimitriou
        provider.add_to_user()
688 d2633501 Kostas Papadimitriou
689 d2633501 Kostas Papadimitriou
    def get_resend_activation_url(self):
690 c630fee6 Kostas Papadimitriou
        return reverse('send_activation', kwargs={'user_id': self.pk})
691 c630fee6 Kostas Papadimitriou
692 d2633501 Kostas Papadimitriou
    def get_activation_url(self, nxt=False):
693 d2633501 Kostas Papadimitriou
        url = "%s?auth=%s" % (reverse('astakos.im.views.activate'),
694 d2633501 Kostas Papadimitriou
                                 quote(self.auth_token))
695 d2633501 Kostas Papadimitriou
        if nxt:
696 d2633501 Kostas Papadimitriou
            url += "&next=%s" % quote(nxt)
697 d2633501 Kostas Papadimitriou
        return url
698 d2633501 Kostas Papadimitriou
699 d2633501 Kostas Papadimitriou
    def get_password_reset_url(self, token_generator=default_token_generator):
700 d2633501 Kostas Papadimitriou
        return reverse('django.contrib.auth.views.password_reset_confirm',
701 d2633501 Kostas Papadimitriou
                          kwargs={'uidb36':int_to_base36(self.id),
702 d2633501 Kostas Papadimitriou
                                  'token':token_generator.make_token(self)})
703 d2633501 Kostas Papadimitriou
704 9d20fe23 Kostas Papadimitriou
    def get_inactive_message(self, provider_module, identifier=None):
705 9d20fe23 Kostas Papadimitriou
        provider = self.get_auth_provider(provider_module, identifier)
706 d2633501 Kostas Papadimitriou
707 c4b1a172 Kostas Papadimitriou
        msg_extra = ''
708 c4b1a172 Kostas Papadimitriou
        message = ''
709 9d20fe23 Kostas Papadimitriou
710 9d20fe23 Kostas Papadimitriou
        msg_inactive = provider.get_account_inactive_msg
711 9d20fe23 Kostas Papadimitriou
        msg_pending = provider.get_pending_activation_msg
712 9d20fe23 Kostas Papadimitriou
        msg_pending_help = _(astakos_messages.ACCOUNT_PENDING_ACTIVATION_HELP)
713 9d20fe23 Kostas Papadimitriou
        #msg_resend_prompt = _(astakos_messages.ACCOUNT_RESEND_ACTIVATION)
714 9d20fe23 Kostas Papadimitriou
        msg_pending_mod = provider.get_pending_moderation_msg
715 9d20fe23 Kostas Papadimitriou
        msg_resend = _(astakos_messages.ACCOUNT_RESEND_ACTIVATION)
716 9d20fe23 Kostas Papadimitriou
717 c4b1a172 Kostas Papadimitriou
        if self.activation_sent:
718 c4b1a172 Kostas Papadimitriou
            if self.email_verified:
719 9d20fe23 Kostas Papadimitriou
                message = msg_inactive
720 c4b1a172 Kostas Papadimitriou
            else:
721 9d20fe23 Kostas Papadimitriou
                message = msg_pending
722 9d20fe23 Kostas Papadimitriou
                url = self.get_resend_activation_url()
723 9d20fe23 Kostas Papadimitriou
                msg_extra = msg_pending_help + \
724 9d20fe23 Kostas Papadimitriou
                            u' ' + \
725 9d20fe23 Kostas Papadimitriou
                            '<a href="%s">%s?</a>' % (url, msg_resend)
726 c4b1a172 Kostas Papadimitriou
        else:
727 fc655b6f Kostas Papadimitriou
            if astakos_settings.MODERATION_ENABLED:
728 9d20fe23 Kostas Papadimitriou
                message = msg_pending_mod
729 c4b1a172 Kostas Papadimitriou
            else:
730 9d20fe23 Kostas Papadimitriou
                message = msg_pending
731 c4b1a172 Kostas Papadimitriou
                url = self.get_resend_activation_url()
732 9d20fe23 Kostas Papadimitriou
                msg_extra = '<a href="%s">%s?</a>' % (url, \
733 9d20fe23 Kostas Papadimitriou
                                msg_resend)
734 c4b1a172 Kostas Papadimitriou
735 a15a19b2 Kostas Papadimitriou
        return mark_safe(message + u' '+ msg_extra)
736 c4b1a172 Kostas Papadimitriou
737 7184f408 Giorgos Korfiatis
    def owns_application(self, application):
738 7184f408 Giorgos Korfiatis
        return application.owner == self
739 7184f408 Giorgos Korfiatis
740 e87bbb41 Kostas Papadimitriou
    def owns_project(self, project):
741 7184f408 Giorgos Korfiatis
        return project.application.owner == self
742 e87bbb41 Kostas Papadimitriou
743 d4660e00 Giorgos Korfiatis
    def is_associated(self, project):
744 d4660e00 Giorgos Korfiatis
        try:
745 d4660e00 Giorgos Korfiatis
            m = ProjectMembership.objects.get(person=self, project=project)
746 d4660e00 Giorgos Korfiatis
            return m.state in ProjectMembership.ASSOCIATED_STATES
747 d4660e00 Giorgos Korfiatis
        except ProjectMembership.DoesNotExist:
748 d4660e00 Giorgos Korfiatis
            return False
749 d4660e00 Giorgos Korfiatis
750 aad0e329 Giorgos Korfiatis
    def get_membership(self, project):
751 aad0e329 Giorgos Korfiatis
        try:
752 aad0e329 Giorgos Korfiatis
            return ProjectMembership.objects.get(
753 aad0e329 Giorgos Korfiatis
                project=project,
754 aad0e329 Giorgos Korfiatis
                person=self)
755 aad0e329 Giorgos Korfiatis
        except ProjectMembership.DoesNotExist:
756 aad0e329 Giorgos Korfiatis
            return None
757 d2633501 Kostas Papadimitriou
758 d4660e00 Giorgos Korfiatis
    def membership_display(self, project):
759 d4660e00 Giorgos Korfiatis
        m = self.get_membership(project)
760 d4660e00 Giorgos Korfiatis
        if m is None:
761 d4660e00 Giorgos Korfiatis
            return _('Not a member')
762 d4660e00 Giorgos Korfiatis
        else:
763 d4660e00 Giorgos Korfiatis
            return m.user_friendly_state_display()
764 d4660e00 Giorgos Korfiatis
765 7f31a7a3 Giorgos Korfiatis
    def non_owner_can_view(self, maybe_project):
766 8e1a5af5 Georgios D. Tsoukalas
        if self.is_project_admin():
767 8e1a5af5 Georgios D. Tsoukalas
            return True
768 7f31a7a3 Giorgos Korfiatis
        if maybe_project is None:
769 7f31a7a3 Giorgos Korfiatis
            return False
770 7f31a7a3 Giorgos Korfiatis
        project = maybe_project
771 7f31a7a3 Giorgos Korfiatis
        if self.is_associated(project):
772 7f31a7a3 Giorgos Korfiatis
            return True
773 7f31a7a3 Giorgos Korfiatis
        if project.is_deactivated():
774 7f31a7a3 Giorgos Korfiatis
            return False
775 7f31a7a3 Giorgos Korfiatis
        return True
776 7f31a7a3 Giorgos Korfiatis
777 c7c0ec58 Giorgos Korfiatis
    def settings(self):
778 c7c0ec58 Giorgos Korfiatis
        return UserSetting.objects.filter(user=self)
779 c7c0ec58 Giorgos Korfiatis
780 9efc25a1 Giorgos Korfiatis
    def all_quotas(self):
781 9efc25a1 Giorgos Korfiatis
        quotas = users_quotas([self])
782 9efc25a1 Giorgos Korfiatis
        try:
783 9efc25a1 Giorgos Korfiatis
            return quotas[self.uuid]
784 9efc25a1 Giorgos Korfiatis
        except:
785 9efc25a1 Giorgos Korfiatis
            raise ValueError("could not compute quotas")
786 9efc25a1 Giorgos Korfiatis
787 d4660e00 Giorgos Korfiatis
788 a989b48e Giorgos Korfiatis
def initial_quotas(users):
789 a989b48e Giorgos Korfiatis
    initial = {}
790 a989b48e Giorgos Korfiatis
    default_quotas = get_default_quota()
791 a989b48e Giorgos Korfiatis
792 a989b48e Giorgos Korfiatis
    for user in users:
793 a989b48e Giorgos Korfiatis
        uuid = user.uuid
794 a989b48e Giorgos Korfiatis
        initial[uuid] = dict(default_quotas)
795 a989b48e Giorgos Korfiatis
796 a989b48e Giorgos Korfiatis
    objs = AstakosUserQuota.objects.select_related()
797 a989b48e Giorgos Korfiatis
    orig_quotas = objs.filter(user__in=users)
798 a989b48e Giorgos Korfiatis
    for user_quota in orig_quotas:
799 9efc25a1 Giorgos Korfiatis
        uuid = user_quota.user.uuid
800 a989b48e Giorgos Korfiatis
        user_init = initial.get(uuid, {})
801 a989b48e Giorgos Korfiatis
        resource = user_quota.resource.full_name()
802 a989b48e Giorgos Korfiatis
        user_init[resource] = user_quota.quota_values()
803 a989b48e Giorgos Korfiatis
        initial[uuid] = user_init
804 a989b48e Giorgos Korfiatis
805 a989b48e Giorgos Korfiatis
    return initial
806 a989b48e Giorgos Korfiatis
807 a989b48e Giorgos Korfiatis
808 a989b48e Giorgos Korfiatis
def users_quotas(users, initial=None):
809 a989b48e Giorgos Korfiatis
    if initial is None:
810 a989b48e Giorgos Korfiatis
        quotas = initial_quotas(users)
811 a989b48e Giorgos Korfiatis
    else:
812 a989b48e Giorgos Korfiatis
        quotas = copy.deepcopy(initial)
813 a989b48e Giorgos Korfiatis
814 a989b48e Giorgos Korfiatis
    objs = ProjectMembership.objects.select_related('application', 'person')
815 a989b48e Giorgos Korfiatis
    memberships = objs.filter(person__in=users, is_active=True)
816 a989b48e Giorgos Korfiatis
817 a989b48e Giorgos Korfiatis
    apps = set(m.application for m in memberships if m.application is not None)
818 a989b48e Giorgos Korfiatis
    objs = ProjectResourceGrant.objects.select_related()
819 a989b48e Giorgos Korfiatis
    grants = objs.filter(project_application__in=apps)
820 a989b48e Giorgos Korfiatis
821 a989b48e Giorgos Korfiatis
    for membership in memberships:
822 a989b48e Giorgos Korfiatis
        uuid = membership.person.uuid
823 a989b48e Giorgos Korfiatis
        userquotas = quotas.get(uuid, {})
824 a989b48e Giorgos Korfiatis
825 a989b48e Giorgos Korfiatis
        application = membership.application
826 a989b48e Giorgos Korfiatis
        if application is None:
827 a989b48e Giorgos Korfiatis
            m = _("missing application for active membership %s"
828 a989b48e Giorgos Korfiatis
                  % (membership,))
829 a989b48e Giorgos Korfiatis
            raise AssertionError(m)
830 a989b48e Giorgos Korfiatis
831 a989b48e Giorgos Korfiatis
        for grant in grants:
832 a989b48e Giorgos Korfiatis
            if grant.project_application_id != application.id:
833 a989b48e Giorgos Korfiatis
                continue
834 a989b48e Giorgos Korfiatis
            resource = grant.resource.full_name()
835 a989b48e Giorgos Korfiatis
            prev = userquotas.get(resource, 0)
836 a989b48e Giorgos Korfiatis
            new = add_quota_values(prev, grant.member_quota_values())
837 a989b48e Giorgos Korfiatis
            userquotas[resource] = new
838 a989b48e Giorgos Korfiatis
        quotas[uuid] = userquotas
839 a989b48e Giorgos Korfiatis
840 a989b48e Giorgos Korfiatis
    return quotas
841 a989b48e Giorgos Korfiatis
842 a989b48e Giorgos Korfiatis
843 d2633501 Kostas Papadimitriou
class AstakosUserAuthProviderManager(models.Manager):
844 d2633501 Kostas Papadimitriou
845 63836eda Kostas Papadimitriou
    def active(self, **filters):
846 63836eda Kostas Papadimitriou
        return self.filter(active=True, **filters)
847 d2633501 Kostas Papadimitriou
848 564a2292 Kostas Papadimitriou
    def remove_unverified_providers(self, provider, **filters):
849 564a2292 Kostas Papadimitriou
        try:
850 9d20fe23 Kostas Papadimitriou
            existing = self.filter(module=provider, user__email_verified=False,
851 9d20fe23 Kostas Papadimitriou
                                   **filters)
852 564a2292 Kostas Papadimitriou
            for p in existing:
853 564a2292 Kostas Papadimitriou
                p.user.delete()
854 564a2292 Kostas Papadimitriou
        except:
855 564a2292 Kostas Papadimitriou
            pass
856 564a2292 Kostas Papadimitriou
857 9d20fe23 Kostas Papadimitriou
    def unverified(self, provider, **filters):
858 9d20fe23 Kostas Papadimitriou
        try:
859 9d20fe23 Kostas Papadimitriou
            return self.get(module=provider, user__email_verified=False,
860 9d20fe23 Kostas Papadimitriou
                            **filters).settings
861 9d20fe23 Kostas Papadimitriou
        except AstakosUserAuthProvider.DoesNotExist:
862 9d20fe23 Kostas Papadimitriou
            return None
863 9d20fe23 Kostas Papadimitriou
864 9d20fe23 Kostas Papadimitriou
    def verified(self, provider, **filters):
865 9d20fe23 Kostas Papadimitriou
        try:
866 9d20fe23 Kostas Papadimitriou
            return self.get(module=provider, user__email_verified=True,
867 9d20fe23 Kostas Papadimitriou
                            **filters).settings
868 9d20fe23 Kostas Papadimitriou
        except AstakosUserAuthProvider.DoesNotExist:
869 9d20fe23 Kostas Papadimitriou
            return None
870 9d20fe23 Kostas Papadimitriou
871 9d20fe23 Kostas Papadimitriou
872 9d20fe23 Kostas Papadimitriou
class AuthProviderPolicyProfileManager(models.Manager):
873 9d20fe23 Kostas Papadimitriou
874 9d20fe23 Kostas Papadimitriou
    def active(self):
875 9d20fe23 Kostas Papadimitriou
        return self.filter(active=True)
876 9d20fe23 Kostas Papadimitriou
877 9d20fe23 Kostas Papadimitriou
    def for_user(self, user, provider):
878 9d20fe23 Kostas Papadimitriou
        policies = {}
879 9d20fe23 Kostas Papadimitriou
        exclusive_q1 = Q(provider=provider) & Q(is_exclusive=False)
880 9d20fe23 Kostas Papadimitriou
        exclusive_q2 = ~Q(provider=provider) & Q(is_exclusive=True)
881 9d20fe23 Kostas Papadimitriou
        exclusive_q = exclusive_q1 | exclusive_q2
882 9d20fe23 Kostas Papadimitriou
883 9d20fe23 Kostas Papadimitriou
        for profile in user.authpolicy_profiles.active().filter(exclusive_q):
884 9d20fe23 Kostas Papadimitriou
            policies.update(profile.policies)
885 9d20fe23 Kostas Papadimitriou
886 9d20fe23 Kostas Papadimitriou
        user_groups = user.groups.all().values('pk')
887 9d20fe23 Kostas Papadimitriou
        for profile in self.active().filter(groups__in=user_groups).filter(
888 9d20fe23 Kostas Papadimitriou
                exclusive_q):
889 9d20fe23 Kostas Papadimitriou
            policies.update(profile.policies)
890 9d20fe23 Kostas Papadimitriou
        return policies
891 9d20fe23 Kostas Papadimitriou
892 9d20fe23 Kostas Papadimitriou
    def add_policy(self, name, provider, group_or_user, exclusive=False,
893 9d20fe23 Kostas Papadimitriou
                   **policies):
894 9d20fe23 Kostas Papadimitriou
        is_group = isinstance(group_or_user, Group)
895 9d20fe23 Kostas Papadimitriou
        profile, created = self.get_or_create(name=name, provider=provider,
896 9d20fe23 Kostas Papadimitriou
                                              is_exclusive=exclusive)
897 9d20fe23 Kostas Papadimitriou
        profile.is_exclusive = exclusive
898 9d20fe23 Kostas Papadimitriou
        profile.save()
899 9d20fe23 Kostas Papadimitriou
        if is_group:
900 9d20fe23 Kostas Papadimitriou
            profile.groups.add(group_or_user)
901 9d20fe23 Kostas Papadimitriou
        else:
902 9d20fe23 Kostas Papadimitriou
            profile.users.add(group_or_user)
903 9d20fe23 Kostas Papadimitriou
        profile.set_policies(policies)
904 9d20fe23 Kostas Papadimitriou
        profile.save()
905 9d20fe23 Kostas Papadimitriou
        return profile
906 9d20fe23 Kostas Papadimitriou
907 9d20fe23 Kostas Papadimitriou
908 9d20fe23 Kostas Papadimitriou
class AuthProviderPolicyProfile(models.Model):
909 9d20fe23 Kostas Papadimitriou
    name = models.CharField(_('Name'), max_length=255, blank=False,
910 9d20fe23 Kostas Papadimitriou
                            null=False, db_index=True)
911 9d20fe23 Kostas Papadimitriou
    provider = models.CharField(_('Provider'), max_length=255, blank=False,
912 9d20fe23 Kostas Papadimitriou
                                null=False)
913 9d20fe23 Kostas Papadimitriou
914 9d20fe23 Kostas Papadimitriou
    # apply policies to all providers excluding the one set in provider field
915 9d20fe23 Kostas Papadimitriou
    is_exclusive = models.BooleanField(default=False)
916 9d20fe23 Kostas Papadimitriou
917 9d20fe23 Kostas Papadimitriou
    policy_add = models.NullBooleanField(null=True, default=None)
918 9d20fe23 Kostas Papadimitriou
    policy_remove = models.NullBooleanField(null=True, default=None)
919 9d20fe23 Kostas Papadimitriou
    policy_create = models.NullBooleanField(null=True, default=None)
920 9d20fe23 Kostas Papadimitriou
    policy_login = models.NullBooleanField(null=True, default=None)
921 9d20fe23 Kostas Papadimitriou
    policy_limit = models.IntegerField(null=True, default=None)
922 9d20fe23 Kostas Papadimitriou
    policy_required = models.NullBooleanField(null=True, default=None)
923 9d20fe23 Kostas Papadimitriou
    policy_automoderate = models.NullBooleanField(null=True, default=None)
924 9d20fe23 Kostas Papadimitriou
    policy_switch = models.NullBooleanField(null=True, default=None)
925 9d20fe23 Kostas Papadimitriou
926 9d20fe23 Kostas Papadimitriou
    POLICY_FIELDS = ('add', 'remove', 'create', 'login', 'limit', 'required',
927 9d20fe23 Kostas Papadimitriou
                     'automoderate')
928 9d20fe23 Kostas Papadimitriou
929 9d20fe23 Kostas Papadimitriou
    priority = models.IntegerField(null=False, default=1)
930 9d20fe23 Kostas Papadimitriou
    groups = models.ManyToManyField(Group, related_name='authpolicy_profiles')
931 9d20fe23 Kostas Papadimitriou
    users = models.ManyToManyField(AstakosUser,
932 9d20fe23 Kostas Papadimitriou
                                   related_name='authpolicy_profiles')
933 9d20fe23 Kostas Papadimitriou
    active = models.BooleanField(default=True)
934 9d20fe23 Kostas Papadimitriou
935 9d20fe23 Kostas Papadimitriou
    objects = AuthProviderPolicyProfileManager()
936 9d20fe23 Kostas Papadimitriou
937 9d20fe23 Kostas Papadimitriou
    class Meta:
938 9d20fe23 Kostas Papadimitriou
        ordering = ['priority']
939 9d20fe23 Kostas Papadimitriou
940 9d20fe23 Kostas Papadimitriou
    @property
941 9d20fe23 Kostas Papadimitriou
    def policies(self):
942 9d20fe23 Kostas Papadimitriou
        policies = {}
943 9d20fe23 Kostas Papadimitriou
        for pkey in self.POLICY_FIELDS:
944 9d20fe23 Kostas Papadimitriou
            value = getattr(self, 'policy_%s' % pkey, None)
945 9d20fe23 Kostas Papadimitriou
            if value is None:
946 9d20fe23 Kostas Papadimitriou
                continue
947 9d20fe23 Kostas Papadimitriou
            policies[pkey] = value
948 9d20fe23 Kostas Papadimitriou
        return policies
949 9d20fe23 Kostas Papadimitriou
950 9d20fe23 Kostas Papadimitriou
    def set_policies(self, policies_dict):
951 9d20fe23 Kostas Papadimitriou
        for key, value in policies_dict.iteritems():
952 9d20fe23 Kostas Papadimitriou
            if key in self.POLICY_FIELDS:
953 9d20fe23 Kostas Papadimitriou
                setattr(self, 'policy_%s' % key, value)
954 9d20fe23 Kostas Papadimitriou
        return self.policies
955 564a2292 Kostas Papadimitriou
956 d2633501 Kostas Papadimitriou
957 d2633501 Kostas Papadimitriou
class AstakosUserAuthProvider(models.Model):
958 d2633501 Kostas Papadimitriou
    """
959 d2633501 Kostas Papadimitriou
    Available user authentication methods.
960 d2633501 Kostas Papadimitriou
    """
961 e1a80257 Sofia Papagiannaki
    affiliation = models.CharField(_('Affiliation'), max_length=255, blank=True,
962 d2633501 Kostas Papadimitriou
                                   null=True, default=None)
963 d2633501 Kostas Papadimitriou
    user = models.ForeignKey(AstakosUser, related_name='auth_providers')
964 e1a80257 Sofia Papagiannaki
    module = models.CharField(_('Provider'), max_length=255, blank=False,
965 d2633501 Kostas Papadimitriou
                                default='local')
966 e1a80257 Sofia Papagiannaki
    identifier = models.CharField(_('Third-party identifier'),
967 d2633501 Kostas Papadimitriou
                                              max_length=255, null=True,
968 d2633501 Kostas Papadimitriou
                                              blank=True)
969 d2633501 Kostas Papadimitriou
    active = models.BooleanField(default=True)
970 e1a80257 Sofia Papagiannaki
    auth_backend = models.CharField(_('Backend'), max_length=255, blank=False,
971 d2633501 Kostas Papadimitriou
                                   default='astakos')
972 3a72a5d4 Kostas Papadimitriou
    info_data = models.TextField(default="", null=True, blank=True)
973 c630fee6 Kostas Papadimitriou
    created = models.DateTimeField('Creation date', auto_now_add=True)
974 d2633501 Kostas Papadimitriou
975 d2633501 Kostas Papadimitriou
    objects = AstakosUserAuthProviderManager()
976 d2633501 Kostas Papadimitriou
977 d2633501 Kostas Papadimitriou
    class Meta:
978 d2633501 Kostas Papadimitriou
        unique_together = (('identifier', 'module', 'user'), )
979 c630fee6 Kostas Papadimitriou
        ordering = ('module', 'created')
980 d2633501 Kostas Papadimitriou
981 3a72a5d4 Kostas Papadimitriou
    def __init__(self, *args, **kwargs):
982 3a72a5d4 Kostas Papadimitriou
        super(AstakosUserAuthProvider, self).__init__(*args, **kwargs)
983 3a72a5d4 Kostas Papadimitriou
        try:
984 3a72a5d4 Kostas Papadimitriou
            self.info = json.loads(self.info_data)
985 c630fee6 Kostas Papadimitriou
            if not self.info:
986 c630fee6 Kostas Papadimitriou
                self.info = {}
987 c630fee6 Kostas Papadimitriou
        except Exception, e:
988 3a72a5d4 Kostas Papadimitriou
            self.info = {}
989 c630fee6 Kostas Papadimitriou
990 3a72a5d4 Kostas Papadimitriou
        for key,value in self.info.iteritems():
991 3a72a5d4 Kostas Papadimitriou
            setattr(self, 'info_%s' % key, value)
992 3a72a5d4 Kostas Papadimitriou
993 d2633501 Kostas Papadimitriou
    @property
994 d2633501 Kostas Papadimitriou
    def settings(self):
995 9d20fe23 Kostas Papadimitriou
        extra_data = {}
996 d2633501 Kostas Papadimitriou
997 9d20fe23 Kostas Papadimitriou
        info_data = {}
998 9d20fe23 Kostas Papadimitriou
        if self.info_data:
999 9d20fe23 Kostas Papadimitriou
            info_data = json.loads(self.info_data)
1000 3a72a5d4 Kostas Papadimitriou
1001 9d20fe23 Kostas Papadimitriou
        extra_data['info'] = info_data
1002 d2633501 Kostas Papadimitriou
1003 9d20fe23 Kostas Papadimitriou
        for key in ['active', 'auth_backend', 'created', 'pk', 'affiliation']:
1004 9d20fe23 Kostas Papadimitriou
            extra_data[key] = getattr(self, key)
1005 d2633501 Kostas Papadimitriou
1006 9d20fe23 Kostas Papadimitriou
        extra_data['instance'] = self
1007 9d20fe23 Kostas Papadimitriou
        return auth.get_provider(self.module, self.user,
1008 9d20fe23 Kostas Papadimitriou
                                           self.identifier, **extra_data)
1009 d2633501 Kostas Papadimitriou
1010 f432088a Kostas Papadimitriou
    def __repr__(self):
1011 f432088a Kostas Papadimitriou
        return '<AstakosUserAuthProvider %s:%s>' % (self.module, self.identifier)
1012 f432088a Kostas Papadimitriou
1013 b778b6fa Kostas Papadimitriou
    def __unicode__(self):
1014 b778b6fa Kostas Papadimitriou
        if self.identifier:
1015 b778b6fa Kostas Papadimitriou
            return "%s:%s" % (self.module, self.identifier)
1016 b778b6fa Kostas Papadimitriou
        if self.auth_backend:
1017 b778b6fa Kostas Papadimitriou
            return "%s:%s" % (self.module, self.auth_backend)
1018 b778b6fa Kostas Papadimitriou
        return self.module
1019 b778b6fa Kostas Papadimitriou
1020 3a72a5d4 Kostas Papadimitriou
    def save(self, *args, **kwargs):
1021 3a72a5d4 Kostas Papadimitriou
        self.info_data = json.dumps(self.info)
1022 3a72a5d4 Kostas Papadimitriou
        return super(AstakosUserAuthProvider, self).save(*args, **kwargs)
1023 b778b6fa Kostas Papadimitriou
1024 d2633501 Kostas Papadimitriou
1025 e1a80257 Sofia Papagiannaki
class ExtendedManager(models.Manager):
1026 9a06d96f Olga Brani
    def _update_or_create(self, **kwargs):
1027 9a06d96f Olga Brani
        assert kwargs, \
1028 9a06d96f Olga Brani
            'update_or_create() must be passed at least one keyword argument'
1029 9a06d96f Olga Brani
        obj, created = self.get_or_create(**kwargs)
1030 9a06d96f Olga Brani
        defaults = kwargs.pop('defaults', {})
1031 9a06d96f Olga Brani
        if created:
1032 9a06d96f Olga Brani
            return obj, True, False
1033 9a06d96f Olga Brani
        else:
1034 9a06d96f Olga Brani
            try:
1035 9a06d96f Olga Brani
                params = dict(
1036 9a06d96f Olga Brani
                    [(k, v) for k, v in kwargs.items() if '__' not in k])
1037 9a06d96f Olga Brani
                params.update(defaults)
1038 9a06d96f Olga Brani
                for attr, val in params.items():
1039 9a06d96f Olga Brani
                    if hasattr(obj, attr):
1040 9a06d96f Olga Brani
                        setattr(obj, attr, val)
1041 9a06d96f Olga Brani
                sid = transaction.savepoint()
1042 9a06d96f Olga Brani
                obj.save(force_update=True)
1043 9a06d96f Olga Brani
                transaction.savepoint_commit(sid)
1044 9a06d96f Olga Brani
                return obj, False, True
1045 9a06d96f Olga Brani
            except IntegrityError, e:
1046 9a06d96f Olga Brani
                transaction.savepoint_rollback(sid)
1047 9a06d96f Olga Brani
                try:
1048 9a06d96f Olga Brani
                    return self.get(**kwargs), False, False
1049 9a06d96f Olga Brani
                except self.model.DoesNotExist:
1050 9a06d96f Olga Brani
                    raise e
1051 9a06d96f Olga Brani
1052 9a06d96f Olga Brani
    update_or_create = _update_or_create
1053 5ce3ce4f Sofia Papagiannaki
1054 8e45d6fd Sofia Papagiannaki
1055 8e45d6fd Sofia Papagiannaki
class AstakosUserQuota(models.Model):
1056 e1a80257 Sofia Papagiannaki
    objects = ExtendedManager()
1057 9deadfcd Georgios D. Tsoukalas
    capacity = intDecimalField()
1058 9deadfcd Georgios D. Tsoukalas
    quantity = intDecimalField(default=0)
1059 9deadfcd Georgios D. Tsoukalas
    export_limit = intDecimalField(default=QH_PRACTICALLY_INFINITE)
1060 9deadfcd Georgios D. Tsoukalas
    import_limit = intDecimalField(default=QH_PRACTICALLY_INFINITE)
1061 8e45d6fd Sofia Papagiannaki
    resource = models.ForeignKey(Resource)
1062 8e45d6fd Sofia Papagiannaki
    user = models.ForeignKey(AstakosUser)
1063 5ce3ce4f Sofia Papagiannaki
1064 8e45d6fd Sofia Papagiannaki
    class Meta:
1065 8e45d6fd Sofia Papagiannaki
        unique_together = ("resource", "user")
1066 09e7393c Sofia Papagiannaki
1067 8989f446 Sofia Papagiannaki
    def quota_values(self):
1068 0514bcc7 Giorgos Korfiatis
        return QuotaValues(
1069 0514bcc7 Giorgos Korfiatis
            quantity = self.quantity,
1070 0514bcc7 Giorgos Korfiatis
            capacity = self.capacity,
1071 0514bcc7 Giorgos Korfiatis
            import_limit = self.import_limit,
1072 0514bcc7 Giorgos Korfiatis
            export_limit = self.export_limit)
1073 0514bcc7 Giorgos Korfiatis
1074 5ce3ce4f Sofia Papagiannaki
1075 270dd48d Sofia Papagiannaki
class ApprovalTerms(models.Model):
1076 270dd48d Sofia Papagiannaki
    """
1077 270dd48d Sofia Papagiannaki
    Model for approval terms
1078 270dd48d Sofia Papagiannaki
    """
1079 6c736ed7 Kostas Papadimitriou
1080 5ce3ce4f Sofia Papagiannaki
    date = models.DateTimeField(
1081 3c638f72 Giorgos Korfiatis
        _('Issue date'), db_index=True, auto_now_add=True)
1082 e1a80257 Sofia Papagiannaki
    location = models.CharField(_('Terms location'), max_length=255)
1083 270dd48d Sofia Papagiannaki
1084 5ce3ce4f Sofia Papagiannaki
1085 64cd4730 Antony Chazapis
class Invitation(models.Model):
1086 890b0eaf Sofia Papagiannaki
    """
1087 890b0eaf Sofia Papagiannaki
    Model for registring invitations
1088 890b0eaf Sofia Papagiannaki
    """
1089 0905ccd2 Sofia Papagiannaki
    inviter = models.ForeignKey(AstakosUser, related_name='invitations_sent',
1090 64cd4730 Antony Chazapis
                                null=True)
1091 e1a80257 Sofia Papagiannaki
    realname = models.CharField(_('Real name'), max_length=255)
1092 e1a80257 Sofia Papagiannaki
    username = models.CharField(_('Unique ID'), max_length=255, unique=True)
1093 e1a80257 Sofia Papagiannaki
    code = models.BigIntegerField(_('Invitation code'), db_index=True)
1094 e1a80257 Sofia Papagiannaki
    is_consumed = models.BooleanField(_('Consumed?'), default=False)
1095 e1a80257 Sofia Papagiannaki
    created = models.DateTimeField(_('Creation date'), auto_now_add=True)
1096 e1a80257 Sofia Papagiannaki
    consumed = models.DateTimeField(_('Consumption date'), null=True, blank=True)
1097 5ce3ce4f Sofia Papagiannaki
1098 18ffbee1 Sofia Papagiannaki
    def __init__(self, *args, **kwargs):
1099 18ffbee1 Sofia Papagiannaki
        super(Invitation, self).__init__(*args, **kwargs)
1100 8f5a3a06 Sofia Papagiannaki
        if not self.id:
1101 8f5a3a06 Sofia Papagiannaki
            self.code = _generate_invitation_code()
1102 5ce3ce4f Sofia Papagiannaki
1103 64cd4730 Antony Chazapis
    def consume(self):
1104 64cd4730 Antony Chazapis
        self.is_consumed = True
1105 64cd4730 Antony Chazapis
        self.consumed = datetime.now()
1106 64cd4730 Antony Chazapis
        self.save()
1107 6c736ed7 Kostas Papadimitriou
1108 64cd4730 Antony Chazapis
    def __unicode__(self):
1109 0905ccd2 Sofia Papagiannaki
        return '%s -> %s [%d]' % (self.inviter, self.username, self.code)
1110 9c01d9d1 Sofia Papagiannaki
1111 49790d9d Sofia Papagiannaki
1112 49790d9d Sofia Papagiannaki
class EmailChangeManager(models.Manager):
1113 34a76cdb Kostas Papadimitriou
1114 49790d9d Sofia Papagiannaki
    @transaction.commit_on_success
1115 49790d9d Sofia Papagiannaki
    def change_email(self, activation_key):
1116 49790d9d Sofia Papagiannaki
        """
1117 49790d9d Sofia Papagiannaki
        Validate an activation key and change the corresponding
1118 49790d9d Sofia Papagiannaki
        ``User`` if valid.
1119 49790d9d Sofia Papagiannaki

1120 49790d9d Sofia Papagiannaki
        If the key is valid and has not expired, return the ``User``
1121 49790d9d Sofia Papagiannaki
        after activating.
1122 49790d9d Sofia Papagiannaki

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

1125 49790d9d Sofia Papagiannaki
        If the key is valid but the ``User`` is already active,
1126 49790d9d Sofia Papagiannaki
        return ``None``.
1127 49790d9d Sofia Papagiannaki

1128 49790d9d Sofia Papagiannaki
        After successful email change the activation record is deleted.
1129 49790d9d Sofia Papagiannaki

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

1724 2553efae Sofia Papagiannaki
        Raises:
1725 b8f05f8d Sofia Papagiannaki
            PermissionDenied
1726 ccab6eb5 Sofia Papagiannaki
        """
1727 4f22664f Georgios D. Tsoukalas
1728 4f22664f Georgios D. Tsoukalas
        if not transaction.is_managed():
1729 4f22664f Georgios D. Tsoukalas
            raise AssertionError("NOPE")
1730 4f22664f Georgios D. Tsoukalas
1731 73fbaec4 Sofia Papagiannaki
        new_project_name = self.name
1732 01bdbd17 Giorgos Korfiatis
        if not self.can_approve():
1733 65360c65 Georgios D. Tsoukalas
            m = _("cannot approve: project '%s' in state '%s'") % (
1734 65360c65 Georgios D. Tsoukalas
                    new_project_name, self.state)
1735 01bdbd17 Giorgos Korfiatis
            raise AssertionError(m) # invalid argument
1736 262e04c6 Giorgos Korfiatis
1737 fdafae27 Giorgos Korfiatis
        now = datetime.now()
1738 b6fe8bb8 Giorgos Korfiatis
        project = self._get_project_for_update()
1739 3cc9637a Giorgos Korfiatis
1740 99463445 Giorgos Korfiatis
        try:
1741 99463445 Giorgos Korfiatis
            q = Q(name=new_project_name) & ~Q(state=Project.TERMINATED)
1742 99463445 Giorgos Korfiatis
            conflicting_project = Project.objects.get(q)
1743 99463445 Giorgos Korfiatis
            if (conflicting_project != project):
1744 3cc9637a Giorgos Korfiatis
                m = (_("cannot approve: project with name '%s' "
1745 e1017df9 Giorgos Korfiatis
                       "already exists (id: %s)") % (
1746 3cc9637a Giorgos Korfiatis
                        new_project_name, conflicting_project.id))
1747 3cc9637a Giorgos Korfiatis
                raise PermissionDenied(m) # invalid argument
1748 99463445 Giorgos Korfiatis
        except Project.DoesNotExist:
1749 99463445 Giorgos Korfiatis
            pass
1750 3cc9637a Giorgos Korfiatis
1751 4bf02ea5 Giorgos Korfiatis
        new_project = False
1752 4f22664f Georgios D. Tsoukalas
        if project is None:
1753 4bf02ea5 Giorgos Korfiatis
            new_project = True
1754 3c638f72 Giorgos Korfiatis
            project = Project(id=self.chain)
1755 fdafae27 Giorgos Korfiatis
1756 3cc9637a Giorgos Korfiatis
        project.name = new_project_name
1757 ee45eb81 Giorgos Korfiatis
        project.application = self
1758 4bf02ea5 Giorgos Korfiatis
        project.last_approval_date = now
1759 a769d7ba Sofia Papagiannaki
        if not new_project:
1760 a769d7ba Sofia Papagiannaki
            project.is_modified = True
1761 4bf02ea5 Giorgos Korfiatis
1762 a769d7ba Sofia Papagiannaki
        project.save()
1763 425e2e95 Sofia Papagiannaki
1764 85d444db Sofia Papagiannaki
        self.state = self.APPROVED
1765 3c638f72 Giorgos Korfiatis
        self.response_date = now
1766 bfe23b13 Sofia Papagiannaki
        self.save()
1767 262e04c6 Giorgos Korfiatis
1768 b98e1df0 Sofia Papagiannaki
    @property
1769 b98e1df0 Sofia Papagiannaki
    def member_join_policy_display(self):
1770 b98e1df0 Sofia Papagiannaki
        return PROJECT_MEMBER_JOIN_POLICIES.get(str(self.member_join_policy))
1771 b98e1df0 Sofia Papagiannaki
1772 b98e1df0 Sofia Papagiannaki
    @property
1773 b98e1df0 Sofia Papagiannaki
    def member_leave_policy_display(self):
1774 b98e1df0 Sofia Papagiannaki
        return PROJECT_MEMBER_LEAVE_POLICIES.get(str(self.member_leave_policy))
1775 b98e1df0 Sofia Papagiannaki
1776 73fbaec4 Sofia Papagiannaki
class ProjectResourceGrant(models.Model):
1777 e1a80257 Sofia Papagiannaki
1778 425e2e95 Sofia Papagiannaki
    resource                =   models.ForeignKey(Resource)
1779 425e2e95 Sofia Papagiannaki
    project_application     =   models.ForeignKey(ProjectApplication,
1780 5200e864 Sofia Papagiannaki
                                                  null=True)
1781 c11dc0ce Giorgos Korfiatis
    project_capacity        =   intDecimalField(default=QH_PRACTICALLY_INFINITE)
1782 c11dc0ce Giorgos Korfiatis
    project_import_limit    =   intDecimalField(default=QH_PRACTICALLY_INFINITE)
1783 c11dc0ce Giorgos Korfiatis
    project_export_limit    =   intDecimalField(default=QH_PRACTICALLY_INFINITE)
1784 c11dc0ce Giorgos Korfiatis
    member_capacity         =   intDecimalField(default=QH_PRACTICALLY_INFINITE)
1785 c11dc0ce Giorgos Korfiatis
    member_import_limit     =   intDecimalField(default=QH_PRACTICALLY_INFINITE)
1786 c11dc0ce Giorgos Korfiatis
    member_export_limit     =   intDecimalField(default=QH_PRACTICALLY_INFINITE)
1787 73fbaec4 Sofia Papagiannaki
1788 73fbaec4 Sofia Papagiannaki
    objects = ExtendedManager()
1789 73fbaec4 Sofia Papagiannaki
1790 73fbaec4 Sofia Papagiannaki
    class Meta:
1791 73fbaec4 Sofia Papagiannaki
        unique_together = ("resource", "project_application")
1792 8327782d Sofia Papagiannaki
1793 0514bcc7 Giorgos Korfiatis
    def member_quota_values(self):
1794 0514bcc7 Giorgos Korfiatis
        return QuotaValues(
1795 0514bcc7 Giorgos Korfiatis
            quantity = 0,
1796 0514bcc7 Giorgos Korfiatis
            capacity = self.member_capacity,
1797 0514bcc7 Giorgos Korfiatis
            import_limit = self.member_import_limit,
1798 0514bcc7 Giorgos Korfiatis
            export_limit = self.member_export_limit)
1799 0514bcc7 Giorgos Korfiatis
1800 b98e1df0 Sofia Papagiannaki
    def display_member_capacity(self):
1801 b98e1df0 Sofia Papagiannaki
        if self.member_capacity:
1802 b98e1df0 Sofia Papagiannaki
            if self.resource.unit:
1803 b98e1df0 Sofia Papagiannaki
                return ProjectResourceGrant.display_filesize(
1804 b98e1df0 Sofia Papagiannaki
                    self.member_capacity)
1805 b98e1df0 Sofia Papagiannaki
            else:
1806 b98e1df0 Sofia Papagiannaki
                if math.isinf(self.member_capacity):
1807 b98e1df0 Sofia Papagiannaki
                    return 'Unlimited'
1808 b98e1df0 Sofia Papagiannaki
                else:
1809 b98e1df0 Sofia Papagiannaki
                    return self.member_capacity
1810 b98e1df0 Sofia Papagiannaki
        else:
1811 b98e1df0 Sofia Papagiannaki
            return 'Unlimited'
1812 b98e1df0 Sofia Papagiannaki
1813 b98e1df0 Sofia Papagiannaki
    def __str__(self):
1814 b98e1df0 Sofia Papagiannaki
        return 'Max %s per user: %s' % (self.resource.pluralized_display_name,
1815 b98e1df0 Sofia Papagiannaki
                                        self.display_member_capacity())
1816 b98e1df0 Sofia Papagiannaki
1817 b98e1df0 Sofia Papagiannaki
    @classmethod
1818 b98e1df0 Sofia Papagiannaki
    def display_filesize(cls, value):
1819 b98e1df0 Sofia Papagiannaki
        try:
1820 b98e1df0 Sofia Papagiannaki
            value = float(value)
1821 b98e1df0 Sofia Papagiannaki
        except:
1822 b98e1df0 Sofia Papagiannaki
            return
1823 b98e1df0 Sofia Papagiannaki
        else:
1824 b98e1df0 Sofia Papagiannaki
            if math.isinf(value):
1825 b98e1df0 Sofia Papagiannaki
                return 'Unlimited'
1826 b98e1df0 Sofia Papagiannaki
            if value > 1:
1827 b98e1df0 Sofia Papagiannaki
                unit_list = zip(['bytes', 'kB', 'MB', 'GB', 'TB', 'PB'],
1828 b98e1df0 Sofia Papagiannaki
                                [0, 0, 0, 0, 0, 0])
1829 b98e1df0 Sofia Papagiannaki
                exponent = min(int(math.log(value, 1024)), len(unit_list) - 1)
1830 b98e1df0 Sofia Papagiannaki
                quotient = float(value) / 1024**exponent
1831 b98e1df0 Sofia Papagiannaki
                unit, value_decimals = unit_list[exponent]
1832 b98e1df0 Sofia Papagiannaki
                format_string = '{0:.%sf} {1}' % (value_decimals)
1833 b98e1df0 Sofia Papagiannaki
                return format_string.format(quotient, unit)
1834 b98e1df0 Sofia Papagiannaki
            if value == 0:
1835 b98e1df0 Sofia Papagiannaki
                return '0 bytes'
1836 b98e1df0 Sofia Papagiannaki
            if value == 1:
1837 b98e1df0 Sofia Papagiannaki
                return '1 byte'
1838 b98e1df0 Sofia Papagiannaki
            else:
1839 b98e1df0 Sofia Papagiannaki
               return '0'
1840 b98e1df0 Sofia Papagiannaki
1841 e546df49 Georgios D. Tsoukalas
1842 123be68a Giorgos Korfiatis
class ProjectManager(ForUpdateManager):
1843 123be68a Giorgos Korfiatis
1844 123be68a Giorgos Korfiatis
    def terminated_projects(self):
1845 689226c3 Giorgos Korfiatis
        q = self.model.Q_TERMINATED
1846 123be68a Giorgos Korfiatis
        return self.filter(q)
1847 123be68a Giorgos Korfiatis
1848 123be68a Giorgos Korfiatis
    def not_terminated_projects(self):
1849 689226c3 Giorgos Korfiatis
        q = ~self.model.Q_TERMINATED
1850 123be68a Giorgos Korfiatis
        return self.filter(q)
1851 123be68a Giorgos Korfiatis
1852 b6fe8bb8 Giorgos Korfiatis
    def terminating_projects(self):
1853 689226c3 Giorgos Korfiatis
        q = self.model.Q_TERMINATED & Q(is_active=True)
1854 b6fe8bb8 Giorgos Korfiatis
        return self.filter(q)
1855 b6fe8bb8 Giorgos Korfiatis
1856 db99f198 Giorgos Korfiatis
    def deactivated_projects(self):
1857 689226c3 Giorgos Korfiatis
        q = self.model.Q_DEACTIVATED
1858 db99f198 Giorgos Korfiatis
        return self.filter(q)
1859 db99f198 Giorgos Korfiatis
1860 db99f198 Giorgos Korfiatis
    def deactivating_projects(self):
1861 689226c3 Giorgos Korfiatis
        q = self.model.Q_DEACTIVATED & Q(is_active=True)
1862 db99f198 Giorgos Korfiatis
        return self.filter(q)
1863 db99f198 Giorgos Korfiatis
1864 b6fe8bb8 Giorgos Korfiatis
    def modified_projects(self):
1865 b6fe8bb8 Giorgos Korfiatis
        return self.filter(is_modified=True)
1866 b6fe8bb8 Giorgos Korfiatis
1867 db99f198 Giorgos Korfiatis
    def reactivating_projects(self):
1868 db99f198 Giorgos Korfiatis
        return self.filter(state=Project.APPROVED, is_active=False)
1869 b6fe8bb8 Giorgos Korfiatis
1870 7eadc230 Giorgos Korfiatis
    def expired_projects(self):
1871 7eadc230 Giorgos Korfiatis
        q = (~Q(state=Project.TERMINATED) &
1872 7eadc230 Giorgos Korfiatis
              Q(application__end_date__lt=datetime.now()))
1873 7eadc230 Giorgos Korfiatis
        return self.filter(q)
1874 7eadc230 Giorgos Korfiatis
1875 d77b32f2 Giorgos Korfiatis
    def search_by_name(self, *search_strings):
1876 d77b32f2 Giorgos Korfiatis
        q = Q()
1877 d77b32f2 Giorgos Korfiatis
        for s in search_strings:
1878 d77b32f2 Giorgos Korfiatis
            q = q | Q(name__icontains=s)
1879 d77b32f2 Giorgos Korfiatis
        return self.filter(q)
1880 d77b32f2 Giorgos Korfiatis
1881 7eadc230 Giorgos Korfiatis
1882 d6fdc91e Georgios D. Tsoukalas
class Project(models.Model):
1883 e546df49 Georgios D. Tsoukalas
1884 5195c0e9 Giorgos Korfiatis
    id                          =   models.OneToOneField(Chain,
1885 5195c0e9 Giorgos Korfiatis
                                                      related_name='chained_project',
1886 5195c0e9 Giorgos Korfiatis
                                                      db_column='id',
1887 5195c0e9 Giorgos Korfiatis
                                                      primary_key=True)
1888 5195c0e9 Giorgos Korfiatis
1889 ee45eb81 Giorgos Korfiatis
    application                 =   models.OneToOneField(
1890 4f22664f Georgios D. Tsoukalas
                                            ProjectApplication,
1891 782d9118 Giorgos Korfiatis
                                            related_name='project')
1892 4f22664f Georgios D. Tsoukalas
    last_approval_date          =   models.DateTimeField(null=True)
1893 4f22664f Georgios D. Tsoukalas
1894 4f22664f Georgios D. Tsoukalas
    members                     =   models.ManyToManyField(
1895 4f22664f Georgios D. Tsoukalas
                                            AstakosUser,
1896 4f22664f Georgios D. Tsoukalas
                                            through='ProjectMembership')
1897 4f22664f Georgios D. Tsoukalas
1898 5b9e9530 Giorgos Korfiatis
    deactivation_reason         =   models.CharField(max_length=255, null=True)
1899 5b9e9530 Giorgos Korfiatis
    deactivation_date           =   models.DateTimeField(null=True)
1900 4f22664f Georgios D. Tsoukalas
1901 3c638f72 Giorgos Korfiatis
    creation_date               =   models.DateTimeField(auto_now_add=True)
1902 4f22664f Georgios D. Tsoukalas
    name                        =   models.CharField(
1903 4f22664f Georgios D. Tsoukalas
                                            max_length=80,
1904 e1017df9 Giorgos Korfiatis
                                            null=True,
1905 4f22664f Georgios D. Tsoukalas
                                            db_index=True,
1906 4f22664f Georgios D. Tsoukalas
                                            unique=True)
1907 425e2e95 Sofia Papagiannaki
1908 b6fe8bb8 Giorgos Korfiatis
    APPROVED    = 1
1909 b6fe8bb8 Giorgos Korfiatis
    SUSPENDED   = 10
1910 b6fe8bb8 Giorgos Korfiatis
    TERMINATED  = 100
1911 5b9e9530 Giorgos Korfiatis
1912 b6fe8bb8 Giorgos Korfiatis
    is_modified                 =   models.BooleanField(default=False,
1913 b6fe8bb8 Giorgos Korfiatis
                                                        db_index=True)
1914 b6fe8bb8 Giorgos Korfiatis
    is_active                   =   models.BooleanField(default=True,
1915 b6fe8bb8 Giorgos Korfiatis
                                                        db_index=True)
1916 b6fe8bb8 Giorgos Korfiatis
    state                       =   models.IntegerField(default=APPROVED,
1917 123be68a Giorgos Korfiatis
                                                        db_index=True)
1918 123be68a Giorgos Korfiatis
1919 123be68a Giorgos Korfiatis
    objects     =   ProjectManager()
1920 7729e9cc Giorgos Korfiatis
1921 689226c3 Giorgos Korfiatis
    # Compiled queries
1922 689226c3 Giorgos Korfiatis
    Q_TERMINATED  = Q(state=TERMINATED)
1923 689226c3 Giorgos Korfiatis
    Q_SUSPENDED   = Q(state=SUSPENDED)
1924 689226c3 Giorgos Korfiatis
    Q_DEACTIVATED = Q_TERMINATED | Q_SUSPENDED
1925 689226c3 Giorgos Korfiatis
1926 8c7b8bb8 Giorgos Korfiatis
    def __str__(self):
1927 b6eaca30 Giorgos Korfiatis
        return uenc(_("<project %s '%s'>") %
1928 b6eaca30 Giorgos Korfiatis
                    (self.id, udec(self.application.name)))
1929 8c7b8bb8 Giorgos Korfiatis
1930 8c7b8bb8 Giorgos Korfiatis
    __repr__ = __str__
1931 8c7b8bb8 Giorgos Korfiatis
1932 b6eaca30 Giorgos Korfiatis
    def __unicode__(self):
1933 b6eaca30 Giorgos Korfiatis
        return _("<project %s '%s'>") % (self.id, self.application.name)
1934 b6eaca30 Giorgos Korfiatis
1935 e1f31e63 Giorgos Korfiatis
    STATE_DISPLAY = {
1936 d77b32f2 Giorgos Korfiatis
        APPROVED   : 'Active',
1937 d77b32f2 Giorgos Korfiatis
        SUSPENDED  : 'Suspended',
1938 d77b32f2 Giorgos Korfiatis
        TERMINATED : 'Terminated'
1939 e1f31e63 Giorgos Korfiatis
        }
1940 e1f31e63 Giorgos Korfiatis
1941 e1f31e63 Giorgos Korfiatis
    def state_display(self):
1942 e1f31e63 Giorgos Korfiatis
        return self.STATE_DISPLAY.get(self.state, _('Unknown'))
1943 e1f31e63 Giorgos Korfiatis
1944 2a2c6876 Giorgos Korfiatis
    def admin_state_display(self):
1945 2a2c6876 Giorgos Korfiatis
        s = self.state_display()
1946 2a2c6876 Giorgos Korfiatis
        if self.sync_pending():
1947 2a2c6876 Giorgos Korfiatis
            s += ' (sync pending)'
1948 2a2c6876 Giorgos Korfiatis
        return s
1949 2a2c6876 Giorgos Korfiatis
1950 2a2c6876 Giorgos Korfiatis
    def sync_pending(self):
1951 2a2c6876 Giorgos Korfiatis
        if self.state != self.APPROVED:
1952 2a2c6876 Giorgos Korfiatis
            return self.is_active
1953 2a2c6876 Giorgos Korfiatis
        return not self.is_active or self.is_modified
1954 2a2c6876 Giorgos Korfiatis
1955 7eadc230 Giorgos Korfiatis
    def expiration_info(self):
1956 7eadc230 Giorgos Korfiatis
        return (str(self.id), self.name, self.state_display(),
1957 7eadc230 Giorgos Korfiatis
                str(self.application.end_date))
1958 7eadc230 Giorgos Korfiatis
1959 b6fe8bb8 Giorgos Korfiatis
    def is_deactivated(self, reason=None):
1960 b6fe8bb8 Giorgos Korfiatis
        if reason is not None:
1961 b6fe8bb8 Giorgos Korfiatis
            return self.state == reason
1962 425e2e95 Sofia Papagiannaki
1963 b6fe8bb8 Giorgos Korfiatis
        return self.state != self.APPROVED
1964 123be68a Giorgos Korfiatis
1965 123be68a Giorgos Korfiatis
    def is_deactivating(self, reason=None):
1966 b6fe8bb8 Giorgos Korfiatis
        if not self.is_active:
1967 b6fe8bb8 Giorgos Korfiatis
            return False
1968 123be68a Giorgos Korfiatis
1969 b6fe8bb8 Giorgos Korfiatis
        return self.is_deactivated(reason)
1970 123be68a Giorgos Korfiatis
1971 b6fe8bb8 Giorgos Korfiatis
    def is_deactivated_strict(self, reason=None):
1972 b6fe8bb8 Giorgos Korfiatis
        if self.is_active:
1973 b6fe8bb8 Giorgos Korfiatis
            return False
1974 123be68a Giorgos Korfiatis
1975 b6fe8bb8 Giorgos Korfiatis
        return self.is_deactivated(reason)
1976 425e2e95 Sofia Papagiannaki
1977 123be68a Giorgos Korfiatis
    ### Deactivation calls
1978 425e2e95 Sofia Papagiannaki
1979 96efed67 Giorgos Korfiatis
    def unset_modified(self):
1980 96efed67 Giorgos Korfiatis
        self.is_modified = False
1981 96efed67 Giorgos Korfiatis
        self.save()
1982 96efed67 Giorgos Korfiatis
1983 123be68a Giorgos Korfiatis
    def deactivate(self):
1984 b6fe8bb8 Giorgos Korfiatis
        self.deactivation_date = datetime.now()
1985 b6fe8bb8 Giorgos Korfiatis
        self.is_active = False
1986 96efed67 Giorgos Korfiatis
        self.save()
1987 425e2e95 Sofia Papagiannaki
1988 db99f198 Giorgos Korfiatis
    def reactivate(self):
1989 db99f198 Giorgos Korfiatis
        self.deactivation_date = None
1990 db99f198 Giorgos Korfiatis
        self.is_active = True
1991 96efed67 Giorgos Korfiatis
        self.save()
1992 db99f198 Giorgos Korfiatis
1993 123be68a Giorgos Korfiatis
    def terminate(self):
1994 123be68a Giorgos Korfiatis
        self.deactivation_reason = 'TERMINATED'
1995 b6fe8bb8 Giorgos Korfiatis
        self.state = self.TERMINATED
1996 e1017df9 Giorgos Korfiatis
        self.name = None
1997 123be68a Giorgos Korfiatis
        self.save()
1998 8aed306c Giorgos Korfiatis
1999 db99f198 Giorgos Korfiatis
    def suspend(self):
2000 db99f198 Giorgos Korfiatis
        self.deactivation_reason = 'SUSPENDED'
2001 db99f198 Giorgos Korfiatis
        self.state = self.SUSPENDED
2002 db99f198 Giorgos Korfiatis
        self.save()
2003 db99f198 Giorgos Korfiatis
2004 db99f198 Giorgos Korfiatis
    def resume(self):
2005 db99f198 Giorgos Korfiatis
        self.deactivation_reason = None
2006 db99f198 Giorgos Korfiatis
        self.state = self.APPROVED
2007 db99f198 Giorgos Korfiatis
        self.save()
2008 123be68a Giorgos Korfiatis
2009 123be68a Giorgos Korfiatis
    ### Logical checks
2010 425e2e95 Sofia Papagiannaki
2011 e1a80257 Sofia Papagiannaki
    def is_inconsistent(self):
2012 e1a80257 Sofia Papagiannaki
        now = datetime.now()
2013 5b9e9530 Giorgos Korfiatis
        dates = [self.creation_date,
2014 5b9e9530 Giorgos Korfiatis
                 self.last_approval_date,
2015 5b9e9530 Giorgos Korfiatis
                 self.deactivation_date]
2016 5b9e9530 Giorgos Korfiatis
        return any([date > now for date in dates])
2017 5b9e9530 Giorgos Korfiatis
2018 b6fe8bb8 Giorgos Korfiatis
    def is_active_strict(self):
2019 b6fe8bb8 Giorgos Korfiatis
        return self.is_active and self.state == self.APPROVED
2020 5b9e9530 Giorgos Korfiatis
2021 db99f198 Giorgos Korfiatis
    def is_approved(self):
2022 db99f198 Giorgos Korfiatis
        return self.state == self.APPROVED
2023 db99f198 Giorgos Korfiatis
2024 123be68a Giorgos Korfiatis
    @property
2025 123be68a Giorgos Korfiatis
    def is_alive(self):
2026 72a6e1e8 Giorgos Korfiatis
        return not self.is_terminated
2027 123be68a Giorgos Korfiatis
2028 123be68a Giorgos Korfiatis
    @property
2029 123be68a Giorgos Korfiatis
    def is_terminated(self):
2030 123be68a Giorgos Korfiatis
        return self.is_deactivated(self.TERMINATED)
2031 123be68a Giorgos Korfiatis
2032 123be68a Giorgos Korfiatis
    @property
2033 123be68a Giorgos Korfiatis
    def is_suspended(self):
2034 db99f198 Giorgos Korfiatis
        return self.is_deactivated(self.SUSPENDED)
2035 5b9e9530 Giorgos Korfiatis
2036 5b9e9530 Giorgos Korfiatis
    def violates_resource_grants(self):
2037 e1a80257 Sofia Papagiannaki
        return False
2038 65360c65 Georgios D. Tsoukalas
2039 5b9e9530 Giorgos Korfiatis
    def violates_members_limit(self, adding=0):
2040 5b9e9530 Giorgos Korfiatis
        application = self.application
2041 943d5554 Giorgos Korfiatis
        limit = application.limit_on_members_number
2042 022c61cd Sofia Papagiannaki
        if limit is None:
2043 022c61cd Sofia Papagiannaki
            return False
2044 022c61cd Sofia Papagiannaki
        return (len(self.approved_members) + adding > limit)
2045 5b9e9530 Giorgos Korfiatis
2046 123be68a Giorgos Korfiatis
2047 123be68a Giorgos Korfiatis
    ### Other
2048 5b9e9530 Giorgos Korfiatis
2049 7db8c163 Georgios D. Tsoukalas
    def count_pending_memberships(self):
2050 7db8c163 Georgios D. Tsoukalas
        memb_set = self.projectmembership_set
2051 7db8c163 Georgios D. Tsoukalas
        memb_count = memb_set.filter(state=ProjectMembership.REQUESTED).count()
2052 7db8c163 Georgios D. Tsoukalas
        return memb_count
2053 7db8c163 Georgios D. Tsoukalas
2054 d77b32f2 Giorgos Korfiatis
    def members_count(self):
2055 d77b32f2 Giorgos Korfiatis
        return self.approved_memberships.count()
2056 d77b32f2 Giorgos Korfiatis
2057 425e2e95 Sofia Papagiannaki
    @property
2058 425e2e95 Sofia Papagiannaki
    def approved_memberships(self):
2059 689226c3 Giorgos Korfiatis
        query = ProjectMembership.Q_ACCEPTED_STATES
2060 5b9e9530 Giorgos Korfiatis
        return self.projectmembership_set.filter(query)
2061 4f22664f Georgios D. Tsoukalas
2062 425e2e95 Sofia Papagiannaki
    @property
2063 425e2e95 Sofia Papagiannaki
    def approved_members(self):
2064 425e2e95 Sofia Papagiannaki
        return [m.person for m in self.approved_memberships]
2065 4f22664f Georgios D. Tsoukalas
2066 4f22664f Georgios D. Tsoukalas
    def add_member(self, user):
2067 bfe23b13 Sofia Papagiannaki
        """
2068 bfe23b13 Sofia Papagiannaki
        Raises:
2069 bfe23b13 Sofia Papagiannaki
            django.exceptions.PermissionDenied
2070 bfe23b13 Sofia Papagiannaki
            astakos.im.models.AstakosUser.DoesNotExist
2071 bfe23b13 Sofia Papagiannaki
        """
2072 8c7229a8 Giorgos Korfiatis
        if isinstance(user, (int, long)):
2073 4f22664f Georgios D. Tsoukalas
            user = AstakosUser.objects.get(user=user)
2074 4f22664f Georgios D. Tsoukalas
2075 ccab6eb5 Sofia Papagiannaki
        m, created = ProjectMembership.objects.get_or_create(
2076 ccab6eb5 Sofia Papagiannaki
            person=user, project=self
2077 2a965273 Sofia Papagiannaki
        )
2078 4f22664f Georgios D. Tsoukalas
        m.accept()
2079 ccab6eb5 Sofia Papagiannaki
2080 4f22664f Georgios D. Tsoukalas
    def remove_member(self, user):
2081 bfe23b13 Sofia Papagiannaki
        """
2082 bfe23b13 Sofia Papagiannaki
        Raises:
2083 bfe23b13 Sofia Papagiannaki
            django.exceptions.PermissionDenied
2084 bfe23b13 Sofia Papagiannaki
            astakos.im.models.AstakosUser.DoesNotExist
2085 bfe23b13 Sofia Papagiannaki
            astakos.im.models.ProjectMembership.DoesNotExist
2086 bfe23b13 Sofia Papagiannaki
        """
2087 8c7229a8 Giorgos Korfiatis
        if isinstance(user, (int, long)):
2088 4f22664f Georgios D. Tsoukalas
            user = AstakosUser.objects.get(user=user)
2089 4f22664f Georgios D. Tsoukalas
2090 bfe23b13 Sofia Papagiannaki
        m = ProjectMembership.objects.get(person=user, project=self)
2091 bfe23b13 Sofia Papagiannaki
        m.remove()
2092 4f22664f Georgios D. Tsoukalas
2093 425e2e95 Sofia Papagiannaki
2094 2529745f Giorgos Korfiatis
CHAIN_STATE = {
2095 2529745f Giorgos Korfiatis
    (Project.APPROVED,   ProjectApplication.PENDING)  : Chain.APPROVED_PENDING,
2096 2529745f Giorgos Korfiatis
    (Project.APPROVED,   ProjectApplication.APPROVED) : Chain.APPROVED,
2097 2529745f Giorgos Korfiatis
    (Project.APPROVED,   ProjectApplication.DENIED)   : Chain.APPROVED,
2098 2529745f Giorgos Korfiatis
    (Project.APPROVED,   ProjectApplication.DISMISSED): Chain.APPROVED,
2099 2529745f Giorgos Korfiatis
    (Project.APPROVED,   ProjectApplication.CANCELLED): Chain.APPROVED,
2100 2529745f Giorgos Korfiatis
2101 2529745f Giorgos Korfiatis
    (Project.SUSPENDED,  ProjectApplication.PENDING)  : Chain.SUSPENDED_PENDING,
2102 2529745f Giorgos Korfiatis
    (Project.SUSPENDED,  ProjectApplication.APPROVED) : Chain.SUSPENDED,
2103 2529745f Giorgos Korfiatis
    (Project.SUSPENDED,  ProjectApplication.DENIED)   : Chain.SUSPENDED,
2104 2529745f Giorgos Korfiatis
    (Project.SUSPENDED,  ProjectApplication.DISMISSED): Chain.SUSPENDED,
2105 2529745f Giorgos Korfiatis
    (Project.SUSPENDED,  ProjectApplication.CANCELLED): Chain.SUSPENDED,
2106 2529745f Giorgos Korfiatis
2107 2529745f Giorgos Korfiatis
    (Project.TERMINATED, ProjectApplication.PENDING)  : Chain.TERMINATED_PENDING,
2108 2529745f Giorgos Korfiatis
    (Project.TERMINATED, ProjectApplication.APPROVED) : Chain.TERMINATED,
2109 2529745f Giorgos Korfiatis
    (Project.TERMINATED, ProjectApplication.DENIED)   : Chain.TERMINATED,
2110 2529745f Giorgos Korfiatis
    (Project.TERMINATED, ProjectApplication.DISMISSED): Chain.TERMINATED,
2111 2529745f Giorgos Korfiatis
    (Project.TERMINATED, ProjectApplication.CANCELLED): Chain.TERMINATED,
2112 2529745f Giorgos Korfiatis
2113 2529745f Giorgos Korfiatis
    (None,               ProjectApplication.PENDING)  : Chain.PENDING,
2114 2529745f Giorgos Korfiatis
    (None,               ProjectApplication.DENIED)   : Chain.DENIED,
2115 2529745f Giorgos Korfiatis
    (None,               ProjectApplication.DISMISSED): Chain.DISMISSED,
2116 2529745f Giorgos Korfiatis
    (None,               ProjectApplication.CANCELLED): Chain.CANCELLED,
2117 2529745f Giorgos Korfiatis
    }
2118 2529745f Giorgos Korfiatis
2119 2529745f Giorgos Korfiatis
2120 db99f198 Giorgos Korfiatis
class ProjectMembershipManager(ForUpdateManager):
2121 d77b32f2 Giorgos Korfiatis
2122 d77b32f2 Giorgos Korfiatis
    def any_accepted(self):
2123 96efed67 Giorgos Korfiatis
        q = self.model.Q_ACTUALLY_ACCEPTED
2124 d77b32f2 Giorgos Korfiatis
        return self.filter(q)
2125 d77b32f2 Giorgos Korfiatis
2126 c1007621 Giorgos Korfiatis
    def actually_accepted(self):
2127 c1007621 Giorgos Korfiatis
        q = self.model.Q_ACTUALLY_ACCEPTED
2128 c1007621 Giorgos Korfiatis
        return self.filter(q)
2129 c1007621 Giorgos Korfiatis
2130 d77b32f2 Giorgos Korfiatis
    def requested(self):
2131 d77b32f2 Giorgos Korfiatis
        return self.filter(state=ProjectMembership.REQUESTED)
2132 d77b32f2 Giorgos Korfiatis
2133 d77b32f2 Giorgos Korfiatis
    def suspended(self):
2134 d77b32f2 Giorgos Korfiatis
        return self.filter(state=ProjectMembership.USER_SUSPENDED)
2135 db99f198 Giorgos Korfiatis
2136 d6fdc91e Georgios D. Tsoukalas
class ProjectMembership(models.Model):
2137 4f22664f Georgios D. Tsoukalas
2138 425e2e95 Sofia Papagiannaki
    person              =   models.ForeignKey(AstakosUser)
2139 3c638f72 Giorgos Korfiatis
    request_date        =   models.DateField(auto_now_add=True)
2140 d6fdc91e Georgios D. Tsoukalas
    project             =   models.ForeignKey(Project)
2141 d6fdc91e Georgios D. Tsoukalas
2142 db99f198 Giorgos Korfiatis
    REQUESTED           =   0
2143 db99f198 Giorgos Korfiatis
    ACCEPTED            =   1
2144 c1007621 Giorgos Korfiatis
    LEAVE_REQUESTED     =   5
2145 db99f198 Giorgos Korfiatis
    # User deactivation
2146 db99f198 Giorgos Korfiatis
    USER_SUSPENDED      =   10
2147 b6fe8bb8 Giorgos Korfiatis
2148 db99f198 Giorgos Korfiatis
    REMOVED             =   200
2149 db99f198 Giorgos Korfiatis
2150 db99f198 Giorgos Korfiatis
    ASSOCIATED_STATES   =   set([REQUESTED,
2151 db99f198 Giorgos Korfiatis
                                 ACCEPTED,
2152 c1007621 Giorgos Korfiatis
                                 LEAVE_REQUESTED,
2153 db99f198 Giorgos Korfiatis
                                 USER_SUSPENDED,
2154 96efed67 Giorgos Korfiatis
                                 ])
2155 db99f198 Giorgos Korfiatis
2156 db99f198 Giorgos Korfiatis
    ACCEPTED_STATES     =   set([ACCEPTED,
2157 c1007621 Giorgos Korfiatis
                                 LEAVE_REQUESTED,
2158 db99f198 Giorgos Korfiatis
                                 USER_SUSPENDED,
2159 96efed67 Giorgos Korfiatis
                                 ])
2160 05617ab9 Kostas Papadimitriou
2161 c1007621 Giorgos Korfiatis
    ACTUALLY_ACCEPTED   =   set([ACCEPTED, LEAVE_REQUESTED])
2162 c1007621 Giorgos Korfiatis
2163 b6fe8bb8 Giorgos Korfiatis
    state               =   models.IntegerField(default=REQUESTED,
2164 b6fe8bb8 Giorgos Korfiatis
                                                db_index=True)
2165 b6fe8bb8 Giorgos Korfiatis
    is_pending          =   models.BooleanField(default=False, db_index=True)
2166 b6fe8bb8 Giorgos Korfiatis
    is_active           =   models.BooleanField(default=False, db_index=True)
2167 5200e864 Sofia Papagiannaki
    application         =   models.ForeignKey(
2168 5200e864 Sofia Papagiannaki
                                ProjectApplication,
2169 5200e864 Sofia Papagiannaki
                                null=True,
2170 5200e864 Sofia Papagiannaki
                                related_name='memberships')
2171 5200e864 Sofia Papagiannaki
    pending_application =   models.ForeignKey(
2172 5200e864 Sofia Papagiannaki
                                ProjectApplication,
2173 5200e864 Sofia Papagiannaki
                                null=True,
2174 cd633c29 Giorgos Korfiatis
                                related_name='pending_memberships')
2175 d6fdc91e Georgios D. Tsoukalas
    pending_serial      =   models.BigIntegerField(null=True, db_index=True)
2176 425e2e95 Sofia Papagiannaki
2177 425e2e95 Sofia Papagiannaki
    acceptance_date     =   models.DateField(null=True, db_index=True)
2178 425e2e95 Sofia Papagiannaki
    leave_request_date  =   models.DateField(null=True)
2179 2a965273 Sofia Papagiannaki
2180 db99f198 Giorgos Korfiatis
    objects     =   ProjectMembershipManager()
2181 ee45eb81 Giorgos Korfiatis
2182 689226c3 Giorgos Korfiatis
    # Compiled queries
2183 689226c3 Giorgos Korfiatis
    Q_ACCEPTED_STATES = ~Q(state=REQUESTED) & ~Q(state=REMOVED)
2184 c1007621 Giorgos Korfiatis
    Q_ACTUALLY_ACCEPTED = Q(state=ACCEPTED) | Q(state=LEAVE_REQUESTED)
2185 5b9e9530 Giorgos Korfiatis
2186 d77b32f2 Giorgos Korfiatis
    MEMBERSHIP_STATE_DISPLAY = {
2187 d4660e00 Giorgos Korfiatis
        REQUESTED           : _('Requested'),
2188 d4660e00 Giorgos Korfiatis
        ACCEPTED            : _('Accepted'),
2189 c1007621 Giorgos Korfiatis
        LEAVE_REQUESTED     : _('Leave Requested'),
2190 d4660e00 Giorgos Korfiatis
        USER_SUSPENDED      : _('Suspended'),
2191 d4660e00 Giorgos Korfiatis
        REMOVED             : _('Pending removal'),
2192 d4660e00 Giorgos Korfiatis
        }
2193 d4660e00 Giorgos Korfiatis
2194 d4660e00 Giorgos Korfiatis
    USER_FRIENDLY_STATE_DISPLAY = {
2195 d4660e00 Giorgos Korfiatis
        REQUESTED           : _('Join requested'),
2196 d4660e00 Giorgos Korfiatis
        ACCEPTED            : _('Accepted member'),
2197 c1007621 Giorgos Korfiatis
        LEAVE_REQUESTED     : _('Requested to leave'),
2198 d4660e00 Giorgos Korfiatis
        USER_SUSPENDED      : _('Suspended member'),
2199 d4660e00 Giorgos Korfiatis
        REMOVED             : _('Pending removal'),
2200 d77b32f2 Giorgos Korfiatis
        }
2201 d77b32f2 Giorgos Korfiatis
2202 d77b32f2 Giorgos Korfiatis
    def state_display(self):
2203 d77b32f2 Giorgos Korfiatis
        return self.MEMBERSHIP_STATE_DISPLAY.get(self.state, _('Unknown'))
2204 d77b32f2 Giorgos Korfiatis
2205 d4660e00 Giorgos Korfiatis
    def user_friendly_state_display(self):
2206 d4660e00 Giorgos Korfiatis
        return self.USER_FRIENDLY_STATE_DISPLAY.get(self.state, _('Unknown'))
2207 d4660e00 Giorgos Korfiatis
2208 0cc22d47 Sofia Papagiannaki
    class Meta:
2209 0cc22d47 Sofia Papagiannaki
        unique_together = ("person", "project")
2210 d6fdc91e Georgios D. Tsoukalas
        #index_together = [["project", "state"]]
2211 bfe23b13 Sofia Papagiannaki
2212 65360c65 Georgios D. Tsoukalas
    def __str__(self):
2213 b6eaca30 Giorgos Korfiatis
        return uenc(_("<'%s' membership in '%s'>") % (
2214 b6eaca30 Giorgos Korfiatis
                self.person.username, self.project))
2215 65360c65 Georgios D. Tsoukalas
2216 65360c65 Georgios D. Tsoukalas
    __repr__ = __str__
2217 65360c65 Georgios D. Tsoukalas
2218 65360c65 Georgios D. Tsoukalas
    def __init__(self, *args, **kwargs):
2219 ee45eb81 Giorgos Korfiatis
        self.state = self.REQUESTED
2220 65360c65 Georgios D. Tsoukalas
        super(ProjectMembership, self).__init__(*args, **kwargs)
2221 65360c65 Georgios D. Tsoukalas
2222 4f22664f Georgios D. Tsoukalas
    def _set_history_item(self, reason, date=None):
2223 4f22664f Georgios D. Tsoukalas
        if isinstance(reason, basestring):
2224 4f22664f Georgios D. Tsoukalas
            reason = ProjectMembershipHistory.reasons.get(reason, -1)
2225 4f22664f Georgios D. Tsoukalas
2226 4f22664f Georgios D. Tsoukalas
        history_item = ProjectMembershipHistory(
2227 4f22664f Georgios D. Tsoukalas
                            serial=self.id,
2228 d0e78bbe Giorgos Korfiatis
                            person=self.person_id,
2229 02d2519e Giorgos Korfiatis
                            project=self.project_id,
2230 8f975b72 Sofia Papagiannaki
                            date=date or datetime.now(),
2231 4f22664f Georgios D. Tsoukalas
                            reason=reason)
2232 4f22664f Georgios D. Tsoukalas
        history_item.save()
2233 4f22664f Georgios D. Tsoukalas
        serial = history_item.id
2234 4f22664f Georgios D. Tsoukalas
2235 14f7f6a5 Giorgos Korfiatis
    def can_accept(self):
2236 14f7f6a5 Giorgos Korfiatis
        return self.state == self.REQUESTED
2237 14f7f6a5 Giorgos Korfiatis
2238 4f22664f Georgios D. Tsoukalas
    def accept(self):
2239 14f7f6a5 Giorgos Korfiatis
        if not self.can_accept():
2240 14f7f6a5 Giorgos Korfiatis
            m = _("%s: attempt to accept in state '%s'") % (self, self.state)
2241 65360c65 Georgios D. Tsoukalas
            raise AssertionError(m)
2242 4f22664f Georgios D. Tsoukalas
2243 65360c65 Georgios D. Tsoukalas
        now = datetime.now()
2244 65360c65 Georgios D. Tsoukalas
        self.acceptance_date = now
2245 65360c65 Georgios D. Tsoukalas
        self._set_history_item(reason='ACCEPT', date=now)
2246 96efed67 Giorgos Korfiatis
        self.state = self.ACCEPTED
2247 65360c65 Georgios D. Tsoukalas
        self.save()
2248 4f22664f Georgios D. Tsoukalas
2249 14f7f6a5 Giorgos Korfiatis
    def can_leave(self):
2250 c1007621 Giorgos Korfiatis
        return self.state in self.ACCEPTED_STATES
2251 c1007621 Giorgos Korfiatis
2252 c1007621 Giorgos Korfiatis
    def leave_request(self):
2253 c1007621 Giorgos Korfiatis
        if not self.can_leave():
2254 c1007621 Giorgos Korfiatis
            m = _("%s: attempt to request to leave in state '%s'") % (
2255 c1007621 Giorgos Korfiatis
                self, self.state)
2256 c1007621 Giorgos Korfiatis
            raise AssertionError(m)
2257 c1007621 Giorgos Korfiatis
2258 c1007621 Giorgos Korfiatis
        self.leave_request_date = datetime.now()
2259 c1007621 Giorgos Korfiatis
        self.state = self.LEAVE_REQUESTED
2260 c1007621 Giorgos Korfiatis
        self.save()
2261 c1007621 Giorgos Korfiatis
2262 c1007621 Giorgos Korfiatis
    def can_deny_leave(self):
2263 c1007621 Giorgos Korfiatis
        return self.state == self.LEAVE_REQUESTED
2264 c1007621 Giorgos Korfiatis
2265 c1007621 Giorgos Korfiatis
    def leave_request_deny(self):
2266 c1007621 Giorgos Korfiatis
        if not self.can_deny_leave():
2267 c1007621 Giorgos Korfiatis
            m = _("%s: attempt to deny leave request in state '%s'") % (
2268 c1007621 Giorgos Korfiatis
                self, self.state)
2269 c1007621 Giorgos Korfiatis
            raise AssertionError(m)
2270 c1007621 Giorgos Korfiatis
2271 c1007621 Giorgos Korfiatis
        self.leave_request_date = None
2272 c1007621 Giorgos Korfiatis
        self.state = self.ACCEPTED
2273 c1007621 Giorgos Korfiatis
        self.save()
2274 c1007621 Giorgos Korfiatis
2275 c1007621 Giorgos Korfiatis
    def can_cancel_leave(self):
2276 c1007621 Giorgos Korfiatis
        return self.state == self.LEAVE_REQUESTED
2277 c1007621 Giorgos Korfiatis
2278 c1007621 Giorgos Korfiatis
    def leave_request_cancel(self):
2279 c1007621 Giorgos Korfiatis
        if not self.can_cancel_leave():
2280 c1007621 Giorgos Korfiatis
            m = _("%s: attempt to cancel leave request in state '%s'") % (
2281 c1007621 Giorgos Korfiatis
                self, self.state)
2282 c1007621 Giorgos Korfiatis
            raise AssertionError(m)
2283 c1007621 Giorgos Korfiatis
2284 c1007621 Giorgos Korfiatis
        self.leave_request_date = None
2285 c1007621 Giorgos Korfiatis
        self.state = self.ACCEPTED
2286 c1007621 Giorgos Korfiatis
        self.save()
2287 14f7f6a5 Giorgos Korfiatis
2288 14f7f6a5 Giorgos Korfiatis
    def can_remove(self):
2289 14f7f6a5 Giorgos Korfiatis
        return self.state in self.ACCEPTED_STATES
2290 14f7f6a5 Giorgos Korfiatis
2291 65360c65 Georgios D. Tsoukalas
    def remove(self):
2292 14f7f6a5 Giorgos Korfiatis
        if not self.can_remove():
2293 14f7f6a5 Giorgos Korfiatis
            m = _("%s: attempt to remove in state '%s'") % (self, self.state)
2294 65360c65 Georgios D. Tsoukalas
            raise AssertionError(m)
2295 4f22664f Georgios D. Tsoukalas
2296 ee45eb81 Giorgos Korfiatis
        self._set_history_item(reason='REMOVE')
2297 b6fe8bb8 Giorgos Korfiatis
        self.state = self.REMOVED
2298 0cc22d47 Sofia Papagiannaki
        self.save()
2299 b8f05f8d Sofia Papagiannaki
2300 14f7f6a5 Giorgos Korfiatis
    def can_reject(self):
2301 14f7f6a5 Giorgos Korfiatis
        return self.state == self.REQUESTED
2302 14f7f6a5 Giorgos Korfiatis
2303 65360c65 Georgios D. Tsoukalas
    def reject(self):
2304 14f7f6a5 Giorgos Korfiatis
        if not self.can_reject():
2305 14f7f6a5 Giorgos Korfiatis
            m = _("%s: attempt to reject in state '%s'") % (self, self.state)
2306 65360c65 Georgios D. Tsoukalas
            raise AssertionError(m)
2307 65360c65 Georgios D. Tsoukalas
2308 65360c65 Georgios D. Tsoukalas
        # rejected requests don't need sync,
2309 65360c65 Georgios D. Tsoukalas
        # because they were never effected
2310 65360c65 Georgios D. Tsoukalas
        self._set_history_item(reason='REJECT')
2311 0cc22d47 Sofia Papagiannaki
        self.delete()
2312 b8f05f8d Sofia Papagiannaki
2313 aad0e329 Giorgos Korfiatis
    def can_cancel(self):
2314 aad0e329 Giorgos Korfiatis
        return self.state == self.REQUESTED
2315 aad0e329 Giorgos Korfiatis
2316 aad0e329 Giorgos Korfiatis
    def cancel(self):
2317 aad0e329 Giorgos Korfiatis
        if not self.can_cancel():
2318 aad0e329 Giorgos Korfiatis
            m = _("%s: attempt to cancel in state '%s'") % (self, self.state)
2319 aad0e329 Giorgos Korfiatis
            raise AssertionError(m)
2320 aad0e329 Giorgos Korfiatis
2321 aad0e329 Giorgos Korfiatis
        # rejected requests don't need sync,
2322 aad0e329 Giorgos Korfiatis
        # because they were never effected
2323 aad0e329 Giorgos Korfiatis
        self._set_history_item(reason='CANCEL')
2324 aad0e329 Giorgos Korfiatis
        self.delete()
2325 aad0e329 Giorgos Korfiatis
2326 b6fe8bb8 Giorgos Korfiatis
    def get_diff_quotas(self, sub_list=None, add_list=None):
2327 d2b32360 Giorgos Korfiatis
        if sub_list is None:
2328 d2b32360 Giorgos Korfiatis
            sub_list = []
2329 d2b32360 Giorgos Korfiatis
2330 d2b32360 Giorgos Korfiatis
        if add_list is None:
2331 d2b32360 Giorgos Korfiatis
            add_list = []
2332 d6fdc91e Georgios D. Tsoukalas
2333 d2b32360 Giorgos Korfiatis
        sub_append = sub_list.append
2334 d2b32360 Giorgos Korfiatis
        add_append = add_list.append
2335 d75c432e Sofia Papagiannaki
        holder = self.person.uuid
2336 d6fdc91e Georgios D. Tsoukalas
2337 d6fdc91e Georgios D. Tsoukalas
        synced_application = self.application
2338 d6fdc91e Georgios D. Tsoukalas
        if synced_application is not None:
2339 5f2e4042 Sofia Papagiannaki
            cur_grants = synced_application.projectresourcegrant_set.all()
2340 d6fdc91e Georgios D. Tsoukalas
            for grant in cur_grants:
2341 d2b32360 Giorgos Korfiatis
                sub_append(QuotaLimits(
2342 d2b32360 Giorgos Korfiatis
                               holder       = holder,
2343 f3e93707 Sofia Papagiannaki
                               resource     = str(grant.resource),
2344 d2b32360 Giorgos Korfiatis
                               capacity     = grant.member_capacity,
2345 d2b32360 Giorgos Korfiatis
                               import_limit = grant.member_import_limit,
2346 d2b32360 Giorgos Korfiatis
                               export_limit = grant.member_export_limit))
2347 d6fdc91e Georgios D. Tsoukalas
2348 b6fe8bb8 Giorgos Korfiatis
        pending_application = self.pending_application
2349 b6fe8bb8 Giorgos Korfiatis
        if pending_application is not None:
2350 b6fe8bb8 Giorgos Korfiatis
            new_grants = pending_application.projectresourcegrant_set.all()
2351 d6fdc91e Georgios D. Tsoukalas
            for new_grant in new_grants:
2352 d2b32360 Giorgos Korfiatis
                add_append(QuotaLimits(
2353 d2b32360 Giorgos Korfiatis
                               holder       = holder,
2354 f3e93707 Sofia Papagiannaki
                               resource     = str(new_grant.resource),
2355 974ee6a6 Sofia Papagiannaki
                               capacity     = new_grant.member_capacity,
2356 974ee6a6 Sofia Papagiannaki
                               import_limit = new_grant.member_import_limit,
2357 974ee6a6 Sofia Papagiannaki
                               export_limit = new_grant.member_export_limit))
2358 d6fdc91e Georgios D. Tsoukalas
2359 d2b32360 Giorgos Korfiatis
        return (sub_list, add_list)
2360 65360c65 Georgios D. Tsoukalas
2361 49b74233 Georgios D. Tsoukalas
2362 ee45eb81 Giorgos Korfiatis
class Serial(models.Model):
2363 ee45eb81 Giorgos Korfiatis
    serial  =   models.AutoField(primary_key=True)
2364 ee45eb81 Giorgos Korfiatis
2365 60ca2f6f Giorgos Korfiatis
2366 f77363c2 Giorgos Korfiatis
def sync_users(users, sync=True):
2367 57f5ea5c Giorgos Korfiatis
    def _sync_users(users, sync):
2368 84a3f701 Giorgos Korfiatis
2369 a989b48e Giorgos Korfiatis
        info = {}
2370 a989b48e Giorgos Korfiatis
        for user in users:
2371 a989b48e Giorgos Korfiatis
            info[user.uuid] = user.email
2372 a989b48e Giorgos Korfiatis
2373 57f5ea5c Giorgos Korfiatis
        resources = get_resource_names()
2374 f77363c2 Giorgos Korfiatis
        qh_limits, qh_counters = qh_get_quotas(users, resources)
2375 a989b48e Giorgos Korfiatis
        astakos_initial = initial_quotas(users)
2376 f77363c2 Giorgos Korfiatis
        astakos_quotas = users_quotas(users)
2377 d6fdc91e Georgios D. Tsoukalas
2378 f6950864 Giorgos Korfiatis
        diff_quotas = {}
2379 f6950864 Giorgos Korfiatis
        for holder, local in astakos_quotas.iteritems():
2380 f6950864 Giorgos Korfiatis
            registered = qh_limits.get(holder, None)
2381 f6950864 Giorgos Korfiatis
            if local != registered:
2382 f6950864 Giorgos Korfiatis
                diff_quotas[holder] = dict(local)
2383 f6950864 Giorgos Korfiatis
2384 57f5ea5c Giorgos Korfiatis
        if sync:
2385 f6950864 Giorgos Korfiatis
            r = send_quotas(diff_quotas)
2386 84a3f701 Giorgos Korfiatis
2387 f77363c2 Giorgos Korfiatis
        return (qh_limits, qh_counters,
2388 2ef1e2d7 Giorgos Korfiatis
                astakos_initial, diff_quotas, info)
2389 f77363c2 Giorgos Korfiatis
2390 57f5ea5c Giorgos Korfiatis
    return _sync_users(users, sync)
2391 84a3f701 Giorgos Korfiatis
2392 a989b48e Giorgos Korfiatis
2393 f77363c2 Giorgos Korfiatis
def sync_all_users(sync=True):
2394 d9907a5e Giorgos Korfiatis
    users = AstakosUser.objects.verified()
2395 f77363c2 Giorgos Korfiatis
    return sync_users(users, sync)
2396 e1a80257 Sofia Papagiannaki
2397 0cc22d47 Sofia Papagiannaki
class ProjectMembershipHistory(models.Model):
2398 425e2e95 Sofia Papagiannaki
    reasons_list    =   ['ACCEPT', 'REJECT', 'REMOVE']
2399 425e2e95 Sofia Papagiannaki
    reasons         =   dict((k, v) for v, k in enumerate(reasons_list))
2400 425e2e95 Sofia Papagiannaki
2401 d0e78bbe Giorgos Korfiatis
    person  =   models.BigIntegerField()
2402 02d2519e Giorgos Korfiatis
    project =   models.BigIntegerField()
2403 3c638f72 Giorgos Korfiatis
    date    =   models.DateField(auto_now_add=True)
2404 425e2e95 Sofia Papagiannaki
    reason  =   models.IntegerField()
2405 425e2e95 Sofia Papagiannaki
    serial  =   models.BigIntegerField()
2406 fc655b6f Kostas Papadimitriou
2407 fcc1e93f Sofia Papagiannaki
### SIGNALS ###
2408 fcc1e93f Sofia Papagiannaki
################
2409 b22de10a Sofia Papagiannaki
2410 ff9290ec Sofia Papagiannaki
def create_astakos_user(u):
2411 ff9290ec Sofia Papagiannaki
    try:
2412 ff9290ec Sofia Papagiannaki
        AstakosUser.objects.get(user_ptr=u.pk)
2413 ff9290ec Sofia Papagiannaki
    except AstakosUser.DoesNotExist:
2414 ff9290ec Sofia Papagiannaki
        extended_user = AstakosUser(user_ptr_id=u.pk)
2415 ff9290ec Sofia Papagiannaki
        extended_user.__dict__.update(u.__dict__)
2416 ff9290ec Sofia Papagiannaki
        extended_user.save()
2417 67be1883 Olga Brani
        if not extended_user.has_auth_provider('local'):
2418 67be1883 Olga Brani
            extended_user.add_auth_provider('local')
2419 fc1e2f02 Sofia Papagiannaki
    except BaseException, e:
2420 fc1e2f02 Sofia Papagiannaki
        logger.exception(e)
2421 ff9290ec Sofia Papagiannaki
2422 a7752e95 Sofia Papagiannaki
def fix_superusers():
2423 fc1e2f02 Sofia Papagiannaki
    # Associate superusers with AstakosUser
2424 ff9290ec Sofia Papagiannaki
    admins = User.objects.filter(is_superuser=True)
2425 ff9290ec Sofia Papagiannaki
    for u in admins:
2426 ff9290ec Sofia Papagiannaki
        create_astakos_user(u)
2427 ff9290ec Sofia Papagiannaki
2428 fc1e2f02 Sofia Papagiannaki
def user_post_save(sender, instance, created, **kwargs):
2429 aa4109d4 Sofia Papagiannaki
    if not created:
2430 aa4109d4 Sofia Papagiannaki
        return
2431 fc1e2f02 Sofia Papagiannaki
    create_astakos_user(instance)
2432 bfe23b13 Sofia Papagiannaki
post_save.connect(user_post_save, sender=User)
2433 ff9290ec Sofia Papagiannaki
2434 fc1e2f02 Sofia Papagiannaki
def astakosuser_post_save(sender, instance, created, **kwargs):
2435 21e0fdad Giorgos Korfiatis
    pass
2436 21e0fdad Giorgos Korfiatis
2437 bfe23b13 Sofia Papagiannaki
post_save.connect(astakosuser_post_save, sender=AstakosUser)
2438 fc1e2f02 Sofia Papagiannaki
2439 bd4f356c Sofia Papagiannaki
def resource_post_save(sender, instance, created, **kwargs):
2440 0514bcc7 Giorgos Korfiatis
    pass
2441 0514bcc7 Giorgos Korfiatis
2442 bfe23b13 Sofia Papagiannaki
post_save.connect(resource_post_save, sender=Resource)
2443 bfe23b13 Sofia Papagiannaki
2444 bfe23b13 Sofia Papagiannaki
def renew_token(sender, instance, **kwargs):
2445 bfe23b13 Sofia Papagiannaki
    if not instance.auth_token:
2446 bfe23b13 Sofia Papagiannaki
        instance.renew_token()
2447 bf0c6de5 Sofia Papagiannaki
pre_save.connect(renew_token, sender=AstakosUser)
2448 2e90e3ec Kostas Papadimitriou
pre_save.connect(renew_token, sender=Service)