Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / models.py @ 07ad7b1c

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

1082 49790d9d Sofia Papagiannaki
        If the key is valid and has not expired, return the ``User``
1083 49790d9d Sofia Papagiannaki
        after activating.
1084 49790d9d Sofia Papagiannaki

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

1087 49790d9d Sofia Papagiannaki
        If the key is valid but the ``User`` is already active,
1088 49790d9d Sofia Papagiannaki
        return ``None``.
1089 49790d9d Sofia Papagiannaki

1090 49790d9d Sofia Papagiannaki
        After successful email change the activation record is deleted.
1091 49790d9d Sofia Papagiannaki

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