Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (67.6 kB)

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

1108 49790d9d Sofia Papagiannaki
        If the key is valid and has not expired, return the ``User``
1109 49790d9d Sofia Papagiannaki
        after activating.
1110 49790d9d Sofia Papagiannaki

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

1113 49790d9d Sofia Papagiannaki
        If the key is valid but the ``User`` is already active,
1114 49790d9d Sofia Papagiannaki
        return ``None``.
1115 49790d9d Sofia Papagiannaki

1116 49790d9d Sofia Papagiannaki
        After successful email change the activation record is deleted.
1117 49790d9d Sofia Papagiannaki

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