Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (56.9 kB)

1 aba1e498 Antony Chazapis
# Copyright 2011-2012 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 64cd4730 Antony Chazapis
38 64cd4730 Antony Chazapis
from time import asctime
39 64cd4730 Antony Chazapis
from datetime import datetime, timedelta
40 64cd4730 Antony Chazapis
from base64 import b64encode
41 ef20ea07 Sofia Papagiannaki
from urlparse import urlparse
42 d2633501 Kostas Papadimitriou
from urllib import quote
43 8f5a3a06 Sofia Papagiannaki
from random import randint
44 ffb1e7a8 Sofia Papagiannaki
from collections import defaultdict
45 64cd4730 Antony Chazapis
46 49790d9d Sofia Papagiannaki
from django.db import models, IntegrityError
47 9a06d96f Olga Brani
from django.contrib.auth.models import User, UserManager, Group, Permission
48 0a569195 Sofia Papagiannaki
from django.utils.translation import ugettext as _
49 49790d9d Sofia Papagiannaki
from django.db import transaction
50 0a569195 Sofia Papagiannaki
from django.core.exceptions import ValidationError
51 c0b26605 Sofia Papagiannaki
from django.db.models.signals import (
52 c0b26605 Sofia Papagiannaki
    pre_save, post_save, post_syncdb, post_delete
53 c0b26605 Sofia Papagiannaki
)
54 9a06d96f Olga Brani
from django.contrib.contenttypes.models import ContentType
55 9a06d96f Olga Brani
56 fc1e2f02 Sofia Papagiannaki
from django.dispatch import Signal
57 e6759494 Sofia Papagiannaki
from django.db.models import Q
58 d2633501 Kostas Papadimitriou
from django.core.urlresolvers import reverse
59 d2633501 Kostas Papadimitriou
from django.utils.http import int_to_base36
60 d2633501 Kostas Papadimitriou
from django.contrib.auth.tokens import default_token_generator
61 8f8c43b2 Sofia Papagiannaki
from django.conf import settings
62 bf0c6de5 Sofia Papagiannaki
from django.utils.importlib import import_module
63 d2633501 Kostas Papadimitriou
from django.core.validators import email_re
64 64cd4730 Antony Chazapis
65 e1a80257 Sofia Papagiannaki
from astakos.im.settings import (
66 e1a80257 Sofia Papagiannaki
    DEFAULT_USER_LEVEL, INVITATIONS_PER_LEVEL,
67 e1a80257 Sofia Papagiannaki
    AUTH_TOKEN_DURATION, BILLING_FIELDS,
68 71a38edf Sofia Papagiannaki
    EMAILCHANGE_ACTIVATION_DAYS, LOGGING_LEVEL,
69 71a38edf Sofia Papagiannaki
    GROUP_CREATION_SUBJECT
70 e1a80257 Sofia Papagiannaki
)
71 c0b26605 Sofia Papagiannaki
from astakos.im.endpoints.qh import (
72 c0b26605 Sofia Papagiannaki
    register_users, send_quota, register_resources
73 bf0c6de5 Sofia Papagiannaki
)
74 d2633501 Kostas Papadimitriou
from astakos.im import auth_providers
75 fc1e2f02 Sofia Papagiannaki
from astakos.im.endpoints.aquarium.producer import report_user_event
76 9a06d96f Olga Brani
from astakos.im.functions import send_invitation
77 f8f86e83 root
#from astakos.im.tasks import propagate_groupmembers_quota
78 9c01d9d1 Sofia Papagiannaki
79 e1a80257 Sofia Papagiannaki
from astakos.im.notifications import build_notification
80 e1a80257 Sofia Papagiannaki
81 ae497612 Olga Brani
import astakos.im.messages as astakos_messages
82 64cd4730 Antony Chazapis
83 18ffbee1 Sofia Papagiannaki
logger = logging.getLogger(__name__)
84 18ffbee1 Sofia Papagiannaki
85 9a06d96f Olga Brani
DEFAULT_CONTENT_TYPE = None
86 e65c21df Georgios D. Tsoukalas
_content_type = None
87 e65c21df Georgios D. Tsoukalas
88 e65c21df Georgios D. Tsoukalas
def get_content_type():
89 e65c21df Georgios D. Tsoukalas
    global _content_type
90 e65c21df Georgios D. Tsoukalas
    if _content_type is not None:
91 e65c21df Georgios D. Tsoukalas
        return _content_type
92 e65c21df Georgios D. Tsoukalas
93 e65c21df Georgios D. Tsoukalas
    try:
94 e65c21df Georgios D. Tsoukalas
        content_type = ContentType.objects.get(app_label='im', model='astakosuser')
95 e65c21df Georgios D. Tsoukalas
    except:
96 e65c21df Georgios D. Tsoukalas
        content_type = DEFAULT_CONTENT_TYPE
97 e65c21df Georgios D. Tsoukalas
    _content_type = content_type
98 e65c21df Georgios D. Tsoukalas
    return content_type
99 9a06d96f Olga Brani
100 9a06d96f Olga Brani
RESOURCE_SEPARATOR = '.'
101 9a06d96f Olga Brani
102 9ee0c6a2 Sofia Papagiannaki
inf = float('inf')
103 5ce3ce4f Sofia Papagiannaki
104 8e45d6fd Sofia Papagiannaki
class Service(models.Model):
105 e1a80257 Sofia Papagiannaki
    name = models.CharField(_('Name'), max_length=255, unique=True, db_index=True)
106 8e45d6fd Sofia Papagiannaki
    url = models.FilePathField()
107 8e45d6fd Sofia Papagiannaki
    icon = models.FilePathField(blank=True)
108 e1a80257 Sofia Papagiannaki
    auth_token = models.CharField(_('Authentication Token'), max_length=32,
109 8e45d6fd Sofia Papagiannaki
                                  null=True, blank=True)
110 e1a80257 Sofia Papagiannaki
    auth_token_created = models.DateTimeField(_('Token creation date'), null=True)
111 5ce3ce4f Sofia Papagiannaki
    auth_token_expires = models.DateTimeField(
112 e1a80257 Sofia Papagiannaki
        _('Token expiration date'), null=True)
113 5ce3ce4f Sofia Papagiannaki
114 8e45d6fd Sofia Papagiannaki
    def renew_token(self):
115 8e45d6fd Sofia Papagiannaki
        md5 = hashlib.md5()
116 8e45d6fd Sofia Papagiannaki
        md5.update(self.name.encode('ascii', 'ignore'))
117 8e45d6fd Sofia Papagiannaki
        md5.update(self.url.encode('ascii', 'ignore'))
118 8e45d6fd Sofia Papagiannaki
        md5.update(asctime())
119 8e45d6fd Sofia Papagiannaki
120 8e45d6fd Sofia Papagiannaki
        self.auth_token = b64encode(md5.digest())
121 8e45d6fd Sofia Papagiannaki
        self.auth_token_created = datetime.now()
122 8e45d6fd Sofia Papagiannaki
        self.auth_token_expires = self.auth_token_created + \
123 5ce3ce4f Sofia Papagiannaki
            timedelta(hours=AUTH_TOKEN_DURATION)
124 5ce3ce4f Sofia Papagiannaki
125 8e45d6fd Sofia Papagiannaki
    def __str__(self):
126 8e45d6fd Sofia Papagiannaki
        return self.name
127 8e45d6fd Sofia Papagiannaki
128 9a06d96f Olga Brani
    @property
129 9a06d96f Olga Brani
    def resources(self):
130 9a06d96f Olga Brani
        return self.resource_set.all()
131 9a06d96f Olga Brani
132 9a06d96f Olga Brani
    @resources.setter
133 9a06d96f Olga Brani
    def resources(self, resources):
134 9a06d96f Olga Brani
        for s in resources:
135 9a06d96f Olga Brani
            self.resource_set.create(**s)
136 2e90e3ec Kostas Papadimitriou
137 9a06d96f Olga Brani
    def add_resource(self, service, resource, uplimit, update=True):
138 9a06d96f Olga Brani
        """Raises ObjectDoesNotExist, IntegrityError"""
139 9a06d96f Olga Brani
        resource = Resource.objects.get(service__name=service, name=resource)
140 9a06d96f Olga Brani
        if update:
141 9a06d96f Olga Brani
            AstakosUserQuota.objects.update_or_create(user=self,
142 9a06d96f Olga Brani
                                                      resource=resource,
143 9a06d96f Olga Brani
                                                      defaults={'uplimit': uplimit})
144 9a06d96f Olga Brani
        else:
145 9a06d96f Olga Brani
            q = self.astakosuserquota_set
146 9a06d96f Olga Brani
            q.create(resource=resource, uplimit=uplimit)
147 9a06d96f Olga Brani
148 5ce3ce4f Sofia Papagiannaki
149 8e45d6fd Sofia Papagiannaki
class ResourceMetadata(models.Model):
150 e1a80257 Sofia Papagiannaki
    key = models.CharField(_('Name'), max_length=255, unique=True, db_index=True)
151 e1a80257 Sofia Papagiannaki
    value = models.CharField(_('Value'), max_length=255)
152 8e45d6fd Sofia Papagiannaki
153 5ce3ce4f Sofia Papagiannaki
154 8e45d6fd Sofia Papagiannaki
class Resource(models.Model):
155 e1a80257 Sofia Papagiannaki
    name = models.CharField(_('Name'), max_length=255, unique=True, db_index=True)
156 8e45d6fd Sofia Papagiannaki
    meta = models.ManyToManyField(ResourceMetadata)
157 8e45d6fd Sofia Papagiannaki
    service = models.ForeignKey(Service)
158 e1a80257 Sofia Papagiannaki
    desc = models.TextField(_('Description'), null=True)
159 e1a80257 Sofia Papagiannaki
    unit = models.CharField(_('Name'), null=True, max_length=255)
160 e1a80257 Sofia Papagiannaki
    group = models.CharField(_('Group'), null=True, max_length=255)
161 5ce3ce4f Sofia Papagiannaki
162 8e45d6fd Sofia Papagiannaki
    def __str__(self):
163 9a06d96f Olga Brani
        return '%s%s%s' % (self.service, RESOURCE_SEPARATOR, self.name)
164 8e45d6fd Sofia Papagiannaki
165 5ce3ce4f Sofia Papagiannaki
166 8e45d6fd Sofia Papagiannaki
class GroupKind(models.Model):
167 e1a80257 Sofia Papagiannaki
    name = models.CharField(_('Name'), max_length=255, unique=True, db_index=True)
168 5ce3ce4f Sofia Papagiannaki
169 8e45d6fd Sofia Papagiannaki
    def __str__(self):
170 8e45d6fd Sofia Papagiannaki
        return self.name
171 8e45d6fd Sofia Papagiannaki
172 5ce3ce4f Sofia Papagiannaki
173 8e45d6fd Sofia Papagiannaki
class AstakosGroup(Group):
174 8e45d6fd Sofia Papagiannaki
    kind = models.ForeignKey(GroupKind)
175 2b1a5f5d Olga Brani
    homepage = models.URLField(
176 e1a80257 Sofia Papagiannaki
        _('Homepage Url'), max_length=255, null=True, blank=True)
177 e1a80257 Sofia Papagiannaki
    desc = models.TextField(_('Description'), null=True)
178 9a06d96f Olga Brani
    policy = models.ManyToManyField(
179 9a06d96f Olga Brani
        Resource,
180 9a06d96f Olga Brani
        null=True,
181 9a06d96f Olga Brani
        blank=True,
182 9a06d96f Olga Brani
        through='AstakosGroupQuota'
183 9a06d96f Olga Brani
    )
184 9a06d96f Olga Brani
    creation_date = models.DateTimeField(
185 e1a80257 Sofia Papagiannaki
        _('Creation date'),
186 9a06d96f Olga Brani
        default=datetime.now()
187 9a06d96f Olga Brani
    )
188 8327782d Sofia Papagiannaki
    issue_date = models.DateTimeField(
189 8327782d Sofia Papagiannaki
        _('Start date'),
190 8327782d Sofia Papagiannaki
        null=True
191 8327782d Sofia Papagiannaki
    )
192 9a06d96f Olga Brani
    expiration_date = models.DateTimeField(
193 e1a80257 Sofia Papagiannaki
        _('Expiration date'),
194 e1a80257 Sofia Papagiannaki
        null=True
195 9a06d96f Olga Brani
    )
196 9a06d96f Olga Brani
    moderation_enabled = models.BooleanField(
197 e1a80257 Sofia Papagiannaki
        _('Moderated membership?'),
198 9a06d96f Olga Brani
        default=True
199 9a06d96f Olga Brani
    )
200 9a06d96f Olga Brani
    approval_date = models.DateTimeField(
201 e1a80257 Sofia Papagiannaki
        _('Activation date'),
202 9a06d96f Olga Brani
        null=True,
203 9a06d96f Olga Brani
        blank=True
204 9a06d96f Olga Brani
    )
205 9a06d96f Olga Brani
    estimated_participants = models.PositiveIntegerField(
206 e1a80257 Sofia Papagiannaki
        _('Estimated #members'),
207 9a06d96f Olga Brani
        null=True,
208 9a06d96f Olga Brani
        blank=True,
209 9a06d96f Olga Brani
    )
210 9a06d96f Olga Brani
    max_participants = models.PositiveIntegerField(
211 e1a80257 Sofia Papagiannaki
        _('Maximum numder of participants'),
212 9a06d96f Olga Brani
        null=True,
213 9a06d96f Olga Brani
        blank=True
214 9a06d96f Olga Brani
    )
215 2e90e3ec Kostas Papadimitriou
216 8e45d6fd Sofia Papagiannaki
    @property
217 8e45d6fd Sofia Papagiannaki
    def is_disabled(self):
218 ffb1e7a8 Sofia Papagiannaki
        if not self.approval_date:
219 ffb1e7a8 Sofia Papagiannaki
            return True
220 ffb1e7a8 Sofia Papagiannaki
        return False
221 5ce3ce4f Sofia Papagiannaki
222 8e45d6fd Sofia Papagiannaki
    @property
223 ffb1e7a8 Sofia Papagiannaki
    def is_enabled(self):
224 8e45d6fd Sofia Papagiannaki
        if self.is_disabled:
225 8e45d6fd Sofia Papagiannaki
            return False
226 8e45d6fd Sofia Papagiannaki
        if not self.issue_date:
227 8e45d6fd Sofia Papagiannaki
            return False
228 8e45d6fd Sofia Papagiannaki
        if not self.expiration_date:
229 8e45d6fd Sofia Papagiannaki
            return True
230 8e45d6fd Sofia Papagiannaki
        now = datetime.now()
231 8e45d6fd Sofia Papagiannaki
        if self.issue_date > now:
232 8e45d6fd Sofia Papagiannaki
            return False
233 8e45d6fd Sofia Papagiannaki
        if now >= self.expiration_date:
234 8e45d6fd Sofia Papagiannaki
            return False
235 8e45d6fd Sofia Papagiannaki
        return True
236 5ce3ce4f Sofia Papagiannaki
237 ffb1e7a8 Sofia Papagiannaki
    def enable(self):
238 fc1e2f02 Sofia Papagiannaki
        if self.is_enabled:
239 fc1e2f02 Sofia Papagiannaki
            return
240 8e45d6fd Sofia Papagiannaki
        self.approval_date = datetime.now()
241 8e45d6fd Sofia Papagiannaki
        self.save()
242 20d50182 Sofia Papagiannaki
        quota_disturbed.send(sender=self, users=self.approved_members)
243 f8f86e83 root
        #propagate_groupmembers_quota.apply_async(
244 f8f86e83 root
        #    args=[self], eta=self.issue_date)
245 f8f86e83 root
        #propagate_groupmembers_quota.apply_async(
246 f8f86e83 root
        #    args=[self], eta=self.expiration_date)
247 5ce3ce4f Sofia Papagiannaki
248 ffb1e7a8 Sofia Papagiannaki
    def disable(self):
249 fc1e2f02 Sofia Papagiannaki
        if self.is_disabled:
250 fc1e2f02 Sofia Papagiannaki
            return
251 8e45d6fd Sofia Papagiannaki
        self.approval_date = None
252 8e45d6fd Sofia Papagiannaki
        self.save()
253 fc1e2f02 Sofia Papagiannaki
        quota_disturbed.send(sender=self, users=self.approved_members)
254 5ce3ce4f Sofia Papagiannaki
255 01ac12d5 Sofia Papagiannaki
    def approve_member(self, person):
256 0f4fa26d Sofia Papagiannaki
        m, created = self.membership_set.get_or_create(person=person)
257 e1a80257 Sofia Papagiannaki
        m.approve()
258 5ce3ce4f Sofia Papagiannaki
259 01ac12d5 Sofia Papagiannaki
    @property
260 01ac12d5 Sofia Papagiannaki
    def members(self):
261 032ade79 Sofia Papagiannaki
        q = self.membership_set.select_related().all()
262 032ade79 Sofia Papagiannaki
        return [m.person for m in q]
263 2e90e3ec Kostas Papadimitriou
264 01ac12d5 Sofia Papagiannaki
    @property
265 01ac12d5 Sofia Papagiannaki
    def approved_members(self):
266 032ade79 Sofia Papagiannaki
        q = self.membership_set.select_related().all()
267 032ade79 Sofia Papagiannaki
        return [m.person for m in q if m.is_approved]
268 5ce3ce4f Sofia Papagiannaki
269 01ac12d5 Sofia Papagiannaki
    @property
270 ffb1e7a8 Sofia Papagiannaki
    def quota(self):
271 0f4fa26d Sofia Papagiannaki
        d = defaultdict(int)
272 dfdc64d2 Sofia Papagiannaki
        for q in self.astakosgroupquota_set.select_related().all():
273 9ee0c6a2 Sofia Papagiannaki
            d[q.resource] += q.uplimit or inf
274 ffb1e7a8 Sofia Papagiannaki
        return d
275 2e90e3ec Kostas Papadimitriou
276 9a06d96f Olga Brani
    def add_policy(self, service, resource, uplimit, update=True):
277 9a06d96f Olga Brani
        """Raises ObjectDoesNotExist, IntegrityError"""
278 9a06d96f Olga Brani
        resource = Resource.objects.get(service__name=service, name=resource)
279 9a06d96f Olga Brani
        if update:
280 9a06d96f Olga Brani
            AstakosGroupQuota.objects.update_or_create(
281 9a06d96f Olga Brani
                group=self,
282 9a06d96f Olga Brani
                resource=resource,
283 9a06d96f Olga Brani
                defaults={'uplimit': uplimit}
284 9a06d96f Olga Brani
            )
285 9a06d96f Olga Brani
        else:
286 9a06d96f Olga Brani
            q = self.astakosgroupquota_set
287 9a06d96f Olga Brani
            q.create(resource=resource, uplimit=uplimit)
288 2e90e3ec Kostas Papadimitriou
289 9a06d96f Olga Brani
    @property
290 9a06d96f Olga Brani
    def policies(self):
291 9a06d96f Olga Brani
        return self.astakosgroupquota_set.select_related().all()
292 9a06d96f Olga Brani
293 9a06d96f Olga Brani
    @policies.setter
294 9a06d96f Olga Brani
    def policies(self, policies):
295 9a06d96f Olga Brani
        for p in policies:
296 9a06d96f Olga Brani
            service = p.get('service', None)
297 9a06d96f Olga Brani
            resource = p.get('resource', None)
298 9a06d96f Olga Brani
            uplimit = p.get('uplimit', 0)
299 9a06d96f Olga Brani
            update = p.get('update', True)
300 9a06d96f Olga Brani
            self.add_policy(service, resource, uplimit, update)
301 2e90e3ec Kostas Papadimitriou
302 01ac12d5 Sofia Papagiannaki
    @property
303 0f4fa26d Sofia Papagiannaki
    def owners(self):
304 0f4fa26d Sofia Papagiannaki
        return self.owner.all()
305 5ce3ce4f Sofia Papagiannaki
306 032ade79 Sofia Papagiannaki
    @property
307 032ade79 Sofia Papagiannaki
    def owner_details(self):
308 032ade79 Sofia Papagiannaki
        return self.owner.select_related().all()
309 032ade79 Sofia Papagiannaki
310 0f4fa26d Sofia Papagiannaki
    @owners.setter
311 0f4fa26d Sofia Papagiannaki
    def owners(self, l):
312 0f4fa26d Sofia Papagiannaki
        self.owner = l
313 0f4fa26d Sofia Papagiannaki
        map(self.approve_member, l)
314 8e45d6fd Sofia Papagiannaki
315 5ce3ce4f Sofia Papagiannaki
316 d2633501 Kostas Papadimitriou
317 6b9a334b Sofia Papagiannaki
class AstakosUserManager(UserManager):
318 d2633501 Kostas Papadimitriou
319 d2633501 Kostas Papadimitriou
    def get_auth_provider_user(self, provider, **kwargs):
320 d2633501 Kostas Papadimitriou
        """
321 d2633501 Kostas Papadimitriou
        Retrieve AstakosUser instance associated with the specified third party
322 d2633501 Kostas Papadimitriou
        id.
323 d2633501 Kostas Papadimitriou
        """
324 d2633501 Kostas Papadimitriou
        kwargs = dict(map(lambda x: ('auth_providers__%s' % x[0], x[1]),
325 d2633501 Kostas Papadimitriou
                          kwargs.iteritems()))
326 d2633501 Kostas Papadimitriou
        return self.get(auth_providers__module=provider, **kwargs)
327 d2633501 Kostas Papadimitriou
328 0905ccd2 Sofia Papagiannaki
class AstakosUser(User):
329 890b0eaf Sofia Papagiannaki
    """
330 890b0eaf Sofia Papagiannaki
    Extends ``django.contrib.auth.models.User`` by defining additional fields.
331 890b0eaf Sofia Papagiannaki
    """
332 e1a80257 Sofia Papagiannaki
    affiliation = models.CharField(_('Affiliation'), max_length=255, blank=True,
333 d2633501 Kostas Papadimitriou
                                   null=True)
334 d2633501 Kostas Papadimitriou
335 d2633501 Kostas Papadimitriou
    # DEPRECATED FIELDS: provider, third_party_identifier moved in
336 d2633501 Kostas Papadimitriou
    #                    AstakosUserProvider model.
337 e1a80257 Sofia Papagiannaki
    provider = models.CharField(_('Provider'), max_length=255, blank=True,
338 d2633501 Kostas Papadimitriou
                                null=True)
339 d2633501 Kostas Papadimitriou
    # ex. screen_name for twitter, eppn for shibboleth
340 e1a80257 Sofia Papagiannaki
    third_party_identifier = models.CharField(_('Third-party identifier'),
341 d2633501 Kostas Papadimitriou
                                              max_length=255, null=True,
342 d2633501 Kostas Papadimitriou
                                              blank=True)
343 d2633501 Kostas Papadimitriou
344 6c736ed7 Kostas Papadimitriou
345 64cd4730 Antony Chazapis
    #for invitations
346 92defad4 Sofia Papagiannaki
    user_level = DEFAULT_USER_LEVEL
347 e1a80257 Sofia Papagiannaki
    level = models.IntegerField(_('Inviter level'), default=user_level)
348 5ce3ce4f Sofia Papagiannaki
    invitations = models.IntegerField(
349 e1a80257 Sofia Papagiannaki
        _('Invitations left'), default=INVITATIONS_PER_LEVEL.get(user_level, 0))
350 6c736ed7 Kostas Papadimitriou
351 e1a80257 Sofia Papagiannaki
    auth_token = models.CharField(_('Authentication Token'), max_length=32,
352 0905ccd2 Sofia Papagiannaki
                                  null=True, blank=True)
353 e1a80257 Sofia Papagiannaki
    auth_token_created = models.DateTimeField(_('Token creation date'), null=True)
354 5ce3ce4f Sofia Papagiannaki
    auth_token_expires = models.DateTimeField(
355 e1a80257 Sofia Papagiannaki
        _('Token expiration date'), null=True)
356 6c736ed7 Kostas Papadimitriou
357 e1a80257 Sofia Papagiannaki
    updated = models.DateTimeField(_('Update date'))
358 e1a80257 Sofia Papagiannaki
    is_verified = models.BooleanField(_('Is verified?'), default=False)
359 6c736ed7 Kostas Papadimitriou
360 e1a80257 Sofia Papagiannaki
    email_verified = models.BooleanField(_('Email verified?'), default=False)
361 6c736ed7 Kostas Papadimitriou
362 e1a80257 Sofia Papagiannaki
    has_credits = models.BooleanField(_('Has credits?'), default=False)
363 5ce3ce4f Sofia Papagiannaki
    has_signed_terms = models.BooleanField(
364 e1a80257 Sofia Papagiannaki
        _('I agree with the terms'), default=False)
365 5ce3ce4f Sofia Papagiannaki
    date_signed_terms = models.DateTimeField(
366 e1a80257 Sofia Papagiannaki
        _('Signed terms date'), null=True, blank=True)
367 5ce3ce4f Sofia Papagiannaki
368 5ce3ce4f Sofia Papagiannaki
    activation_sent = models.DateTimeField(
369 e1a80257 Sofia Papagiannaki
        _('Activation sent data'), null=True, blank=True)
370 5ce3ce4f Sofia Papagiannaki
371 5ce3ce4f Sofia Papagiannaki
    policy = models.ManyToManyField(
372 5ce3ce4f Sofia Papagiannaki
        Resource, null=True, through='AstakosUserQuota')
373 5ce3ce4f Sofia Papagiannaki
374 5ce3ce4f Sofia Papagiannaki
    astakos_groups = models.ManyToManyField(
375 5ce3ce4f Sofia Papagiannaki
        AstakosGroup, verbose_name=_('agroups'), blank=True,
376 ae497612 Olga Brani
        help_text=_(astakos_messages.ASTAKOSUSER_GROUPS_HELP),
377 8e45d6fd Sofia Papagiannaki
        through='Membership')
378 5ce3ce4f Sofia Papagiannaki
379 18ffbee1 Sofia Papagiannaki
    __has_signed_terms = False
380 e1a80257 Sofia Papagiannaki
    disturbed_quota = models.BooleanField(_('Needs quotaholder syncing'),
381 9a06d96f Olga Brani
                                           default=False, db_index=True)
382 d2633501 Kostas Papadimitriou
383 d2633501 Kostas Papadimitriou
    objects = AstakosUserManager()
384 fbaa4f3c Kostas Papadimitriou
385 5ce3ce4f Sofia Papagiannaki
    owner = models.ManyToManyField(
386 5ce3ce4f Sofia Papagiannaki
        AstakosGroup, related_name='owner', null=True)
387 5ce3ce4f Sofia Papagiannaki
388 74b273d8 Sofia Papagiannaki
    class Meta:
389 74b273d8 Sofia Papagiannaki
        unique_together = ("provider", "third_party_identifier")
390 d2633501 Kostas Papadimitriou
391 18ffbee1 Sofia Papagiannaki
    def __init__(self, *args, **kwargs):
392 18ffbee1 Sofia Papagiannaki
        super(AstakosUser, self).__init__(*args, **kwargs)
393 18ffbee1 Sofia Papagiannaki
        self.__has_signed_terms = self.has_signed_terms
394 a3637508 Sofia Papagiannaki
        if not self.id:
395 18ffbee1 Sofia Papagiannaki
            self.is_active = False
396 5ce3ce4f Sofia Papagiannaki
397 0905ccd2 Sofia Papagiannaki
    @property
398 0905ccd2 Sofia Papagiannaki
    def realname(self):
399 5ce3ce4f Sofia Papagiannaki
        return '%s %s' % (self.first_name, self.last_name)
400 6c736ed7 Kostas Papadimitriou
401 0905ccd2 Sofia Papagiannaki
    @realname.setter
402 0905ccd2 Sofia Papagiannaki
    def realname(self, value):
403 0905ccd2 Sofia Papagiannaki
        parts = value.split(' ')
404 0905ccd2 Sofia Papagiannaki
        if len(parts) == 2:
405 0905ccd2 Sofia Papagiannaki
            self.first_name = parts[0]
406 0905ccd2 Sofia Papagiannaki
            self.last_name = parts[1]
407 0905ccd2 Sofia Papagiannaki
        else:
408 0905ccd2 Sofia Papagiannaki
            self.last_name = parts[0]
409 6c736ed7 Kostas Papadimitriou
410 9a06d96f Olga Brani
    def add_permission(self, pname):
411 9a06d96f Olga Brani
        if self.has_perm(pname):
412 9a06d96f Olga Brani
            return
413 e65c21df Georgios D. Tsoukalas
        p, created = Permission.objects.get_or_create(
414 e65c21df Georgios D. Tsoukalas
                                    codename=pname,
415 e65c21df Georgios D. Tsoukalas
                                    name=pname.capitalize(),
416 e65c21df Georgios D. Tsoukalas
                                    content_type=get_content_type())
417 9a06d96f Olga Brani
        self.user_permissions.add(p)
418 9a06d96f Olga Brani
419 9a06d96f Olga Brani
    def remove_permission(self, pname):
420 9a06d96f Olga Brani
        if self.has_perm(pname):
421 9a06d96f Olga Brani
            return
422 9a06d96f Olga Brani
        p = Permission.objects.get(codename=pname,
423 e65c21df Georgios D. Tsoukalas
                                   content_type=get_content_type())
424 9a06d96f Olga Brani
        self.user_permissions.remove(p)
425 9a06d96f Olga Brani
426 64cd4730 Antony Chazapis
    @property
427 64cd4730 Antony Chazapis
    def invitation(self):
428 64cd4730 Antony Chazapis
        try:
429 9fb8e808 Sofia Papagiannaki
            return Invitation.objects.get(username=self.email)
430 64cd4730 Antony Chazapis
        except Invitation.DoesNotExist:
431 64cd4730 Antony Chazapis
            return None
432 6c736ed7 Kostas Papadimitriou
433 9a06d96f Olga Brani
    def invite(self, email, realname):
434 9a06d96f Olga Brani
        inv = Invitation(inviter=self, username=email, realname=realname)
435 9a06d96f Olga Brani
        inv.save()
436 9a06d96f Olga Brani
        send_invitation(inv)
437 9a06d96f Olga Brani
        self.invitations = max(0, self.invitations - 1)
438 9a06d96f Olga Brani
        self.save()
439 9a06d96f Olga Brani
440 ffb1e7a8 Sofia Papagiannaki
    @property
441 ffb1e7a8 Sofia Papagiannaki
    def quota(self):
442 9a06d96f Olga Brani
        """Returns a dict with the sum of quota limits per resource"""
443 ffb1e7a8 Sofia Papagiannaki
        d = defaultdict(int)
444 9a06d96f Olga Brani
        for q in self.policies:
445 9ee0c6a2 Sofia Papagiannaki
            d[q.resource] += q.uplimit or inf
446 ccab6eb5 Sofia Papagiannaki
        for m in self.projectmembership_set.select_related().all():
447 ccab6eb5 Sofia Papagiannaki
            if not m.acceptance_date:
448 fc1e2f02 Sofia Papagiannaki
                continue
449 ccab6eb5 Sofia Papagiannaki
            p = m.project
450 ccab6eb5 Sofia Papagiannaki
            if not p.is_active:
451 ffb1e7a8 Sofia Papagiannaki
                continue
452 ccab6eb5 Sofia Papagiannaki
            grants = p.application.definition.projectresourcegrant_set.all()
453 ccab6eb5 Sofia Papagiannaki
            for g in grants:
454 ccab6eb5 Sofia Papagiannaki
                d[g.resource] += g.member_limit or inf
455 ffb1e7a8 Sofia Papagiannaki
        # TODO set default for remaining
456 ffb1e7a8 Sofia Papagiannaki
        return d
457 5ce3ce4f Sofia Papagiannaki
458 9a06d96f Olga Brani
    @property
459 9a06d96f Olga Brani
    def policies(self):
460 9a06d96f Olga Brani
        return self.astakosuserquota_set.select_related().all()
461 9a06d96f Olga Brani
462 9a06d96f Olga Brani
    @policies.setter
463 9a06d96f Olga Brani
    def policies(self, policies):
464 9a06d96f Olga Brani
        for p in policies:
465 9a06d96f Olga Brani
            service = policies.get('service', None)
466 9a06d96f Olga Brani
            resource = policies.get('resource', None)
467 9a06d96f Olga Brani
            uplimit = policies.get('uplimit', 0)
468 9a06d96f Olga Brani
            update = policies.get('update', True)
469 9a06d96f Olga Brani
            self.add_policy(service, resource, uplimit, update)
470 9a06d96f Olga Brani
471 9a06d96f Olga Brani
    def add_policy(self, service, resource, uplimit, update=True):
472 9a06d96f Olga Brani
        """Raises ObjectDoesNotExist, IntegrityError"""
473 9a06d96f Olga Brani
        resource = Resource.objects.get(service__name=service, name=resource)
474 9a06d96f Olga Brani
        if update:
475 9a06d96f Olga Brani
            AstakosUserQuota.objects.update_or_create(user=self,
476 9a06d96f Olga Brani
                                                      resource=resource,
477 9a06d96f Olga Brani
                                                      defaults={'uplimit': uplimit})
478 9a06d96f Olga Brani
        else:
479 9a06d96f Olga Brani
            q = self.astakosuserquota_set
480 9a06d96f Olga Brani
            q.create(resource=resource, uplimit=uplimit)
481 9a06d96f Olga Brani
482 9a06d96f Olga Brani
    def remove_policy(self, service, resource):
483 9a06d96f Olga Brani
        """Raises ObjectDoesNotExist, IntegrityError"""
484 9a06d96f Olga Brani
        resource = Resource.objects.get(service__name=service, name=resource)
485 9a06d96f Olga Brani
        q = self.policies.get(resource=resource).delete()
486 9a06d96f Olga Brani
487 9a06d96f Olga Brani
    @property
488 9a06d96f Olga Brani
    def extended_groups(self):
489 9a06d96f Olga Brani
        return self.membership_set.select_related().all()
490 9a06d96f Olga Brani
491 9a06d96f Olga Brani
    @extended_groups.setter
492 9a06d96f Olga Brani
    def extended_groups(self, groups):
493 9a06d96f Olga Brani
        #TODO exceptions
494 40a0cd8b Sofia Papagiannaki
        for name in (groups or ()):
495 9a06d96f Olga Brani
            group = AstakosGroup.objects.get(name=name)
496 9a06d96f Olga Brani
            self.membership_set.create(group=group)
497 9a06d96f Olga Brani
498 64cd4730 Antony Chazapis
    def save(self, update_timestamps=True, **kwargs):
499 64cd4730 Antony Chazapis
        if update_timestamps:
500 64cd4730 Antony Chazapis
            if not self.id:
501 0905ccd2 Sofia Papagiannaki
                self.date_joined = datetime.now()
502 64cd4730 Antony Chazapis
            self.updated = datetime.now()
503 5ce3ce4f Sofia Papagiannaki
504 18ffbee1 Sofia Papagiannaki
        # update date_signed_terms if necessary
505 18ffbee1 Sofia Papagiannaki
        if self.__has_signed_terms != self.has_signed_terms:
506 18ffbee1 Sofia Papagiannaki
            self.date_signed_terms = datetime.now()
507 5ce3ce4f Sofia Papagiannaki
508 9c01d9d1 Sofia Papagiannaki
        if not self.id:
509 9c01d9d1 Sofia Papagiannaki
            # set username
510 a706ae94 Sofia Papagiannaki
            self.username = self.email
511 fbaa4f3c Kostas Papadimitriou
512 591d0505 Sofia Papagiannaki
        self.validate_unique_email_isactive()
513 751d24cf Sofia Papagiannaki
        if self.is_active and self.activation_sent:
514 751d24cf Sofia Papagiannaki
            # reset the activation sent
515 751d24cf Sofia Papagiannaki
            self.activation_sent = None
516 5ce3ce4f Sofia Papagiannaki
517 0905ccd2 Sofia Papagiannaki
        super(AstakosUser, self).save(**kwargs)
518 2e90e3ec Kostas Papadimitriou
519 bf0c6de5 Sofia Papagiannaki
    def renew_token(self, flush_sessions=False, current_key=None):
520 64cd4730 Antony Chazapis
        md5 = hashlib.md5()
521 8f8c43b2 Sofia Papagiannaki
        md5.update(settings.SECRET_KEY)
522 0905ccd2 Sofia Papagiannaki
        md5.update(self.username)
523 64cd4730 Antony Chazapis
        md5.update(self.realname.encode('ascii', 'ignore'))
524 64cd4730 Antony Chazapis
        md5.update(asctime())
525 2e90e3ec Kostas Papadimitriou
526 64cd4730 Antony Chazapis
        self.auth_token = b64encode(md5.digest())
527 64cd4730 Antony Chazapis
        self.auth_token_created = datetime.now()
528 64cd4730 Antony Chazapis
        self.auth_token_expires = self.auth_token_created + \
529 92defad4 Sofia Papagiannaki
                                  timedelta(hours=AUTH_TOKEN_DURATION)
530 bf0c6de5 Sofia Papagiannaki
        if flush_sessions:
531 bf0c6de5 Sofia Papagiannaki
            self.flush_sessions(current_key)
532 111f3da6 Sofia Papagiannaki
        msg = 'Token renewed for %s' % self.email
533 aab4d540 Sofia Papagiannaki
        logger.log(LOGGING_LEVEL, msg)
534 6c736ed7 Kostas Papadimitriou
535 bf0c6de5 Sofia Papagiannaki
    def flush_sessions(self, current_key=None):
536 bf0c6de5 Sofia Papagiannaki
        q = self.sessions
537 bf0c6de5 Sofia Papagiannaki
        if current_key:
538 bf0c6de5 Sofia Papagiannaki
            q = q.exclude(session_key=current_key)
539 2e90e3ec Kostas Papadimitriou
540 bf0c6de5 Sofia Papagiannaki
        keys = q.values_list('session_key', flat=True)
541 bf0c6de5 Sofia Papagiannaki
        if keys:
542 bf0c6de5 Sofia Papagiannaki
            msg = 'Flushing sessions: %s' % ','.join(keys)
543 c0b26605 Sofia Papagiannaki
            logger.log(LOGGING_LEVEL, msg, [])
544 bf0c6de5 Sofia Papagiannaki
        engine = import_module(settings.SESSION_ENGINE)
545 bf0c6de5 Sofia Papagiannaki
        for k in keys:
546 bf0c6de5 Sofia Papagiannaki
            s = engine.SessionStore(k)
547 bf0c6de5 Sofia Papagiannaki
            s.flush()
548 bf0c6de5 Sofia Papagiannaki
549 64cd4730 Antony Chazapis
    def __unicode__(self):
550 3abf6c78 Sofia Papagiannaki
        return '%s (%s)' % (self.realname, self.email)
551 5ce3ce4f Sofia Papagiannaki
552 0a569195 Sofia Papagiannaki
    def conflicting_email(self):
553 5ce3ce4f Sofia Papagiannaki
        q = AstakosUser.objects.exclude(username=self.username)
554 789a5951 Sofia Papagiannaki
        q = q.filter(email__iexact=self.email)
555 0a569195 Sofia Papagiannaki
        if q.count() != 0:
556 0a569195 Sofia Papagiannaki
            return True
557 0a569195 Sofia Papagiannaki
        return False
558 5ce3ce4f Sofia Papagiannaki
559 591d0505 Sofia Papagiannaki
    def validate_unique_email_isactive(self):
560 0a569195 Sofia Papagiannaki
        """
561 0a569195 Sofia Papagiannaki
        Implements a unique_together constraint for email and is_active fields.
562 0a569195 Sofia Papagiannaki
        """
563 e6759494 Sofia Papagiannaki
        q = AstakosUser.objects.all()
564 0a569195 Sofia Papagiannaki
        q = q.filter(email = self.email)
565 0a569195 Sofia Papagiannaki
        q = q.filter(is_active = self.is_active)
566 e6759494 Sofia Papagiannaki
        if self.id:
567 e6759494 Sofia Papagiannaki
            q = q.filter(~Q(id = self.id))
568 0a569195 Sofia Papagiannaki
        if q.count() != 0:
569 ae497612 Olga Brani
            raise ValidationError({'__all__': [_(astakos_messages.UNIQUE_EMAIL_IS_ACTIVE_CONSTRAIN_ERR)]})
570 5ce3ce4f Sofia Papagiannaki
571 fcf90160 Sofia Papagiannaki
    @property
572 09e7393c Sofia Papagiannaki
    def signed_terms(self):
573 09e7393c Sofia Papagiannaki
        term = get_latest_terms()
574 09e7393c Sofia Papagiannaki
        if not term:
575 09e7393c Sofia Papagiannaki
            return True
576 09e7393c Sofia Papagiannaki
        if not self.has_signed_terms:
577 09e7393c Sofia Papagiannaki
            return False
578 09e7393c Sofia Papagiannaki
        if not self.date_signed_terms:
579 09e7393c Sofia Papagiannaki
            return False
580 09e7393c Sofia Papagiannaki
        if self.date_signed_terms < term.date:
581 09e7393c Sofia Papagiannaki
            self.has_signed_terms = False
582 f0f92965 Sofia Papagiannaki
            self.date_signed_terms = None
583 09e7393c Sofia Papagiannaki
            self.save()
584 09e7393c Sofia Papagiannaki
            return False
585 09e7393c Sofia Papagiannaki
        return True
586 09e7393c Sofia Papagiannaki
587 d2633501 Kostas Papadimitriou
    def set_invitations_level(self):
588 d2633501 Kostas Papadimitriou
        """
589 d2633501 Kostas Papadimitriou
        Update user invitation level
590 d2633501 Kostas Papadimitriou
        """
591 d2633501 Kostas Papadimitriou
        level = self.invitation.inviter.level + 1
592 d2633501 Kostas Papadimitriou
        self.level = level
593 d2633501 Kostas Papadimitriou
        self.invitations = INVITATIONS_PER_LEVEL.get(level, 0)
594 d2633501 Kostas Papadimitriou
595 d2633501 Kostas Papadimitriou
    def can_login_with_auth_provider(self, provider):
596 d2633501 Kostas Papadimitriou
        if not self.has_auth_provider(provider):
597 d2633501 Kostas Papadimitriou
            return False
598 d2633501 Kostas Papadimitriou
        else:
599 d2633501 Kostas Papadimitriou
            return auth_providers.get_provider(provider).is_available_for_login()
600 d2633501 Kostas Papadimitriou
601 f432088a Kostas Papadimitriou
    def can_add_auth_provider(self, provider, **kwargs):
602 d2633501 Kostas Papadimitriou
        provider_settings = auth_providers.get_provider(provider)
603 d2633501 Kostas Papadimitriou
        if not provider_settings.is_available_for_login():
604 d2633501 Kostas Papadimitriou
            return False
605 f432088a Kostas Papadimitriou
606 d2633501 Kostas Papadimitriou
        if self.has_auth_provider(provider) and \
607 d2633501 Kostas Papadimitriou
           provider_settings.one_per_user:
608 d2633501 Kostas Papadimitriou
            return False
609 f432088a Kostas Papadimitriou
610 f432088a Kostas Papadimitriou
        if 'identifier' in kwargs:
611 f432088a Kostas Papadimitriou
            try:
612 f432088a Kostas Papadimitriou
                # provider with specified params already exist
613 f432088a Kostas Papadimitriou
                existing_user = AstakosUser.objects.get_auth_provider_user(provider,
614 f432088a Kostas Papadimitriou
                                                                   **kwargs)
615 f432088a Kostas Papadimitriou
            except AstakosUser.DoesNotExist:
616 f432088a Kostas Papadimitriou
                return True
617 f432088a Kostas Papadimitriou
            else:
618 f432088a Kostas Papadimitriou
                return False
619 f432088a Kostas Papadimitriou
620 d2633501 Kostas Papadimitriou
        return True
621 d2633501 Kostas Papadimitriou
622 d2633501 Kostas Papadimitriou
    def can_remove_auth_provider(self, provider):
623 d2633501 Kostas Papadimitriou
        if len(self.get_active_auth_providers()) <= 1:
624 d2633501 Kostas Papadimitriou
            return False
625 d2633501 Kostas Papadimitriou
        return True
626 d2633501 Kostas Papadimitriou
627 d2633501 Kostas Papadimitriou
    def can_change_password(self):
628 d2633501 Kostas Papadimitriou
        return self.has_auth_provider('local', auth_backend='astakos')
629 d2633501 Kostas Papadimitriou
630 d2633501 Kostas Papadimitriou
    def has_auth_provider(self, provider, **kwargs):
631 d2633501 Kostas Papadimitriou
        return bool(self.auth_providers.filter(module=provider,
632 d2633501 Kostas Papadimitriou
                                               **kwargs).count())
633 d2633501 Kostas Papadimitriou
634 d2633501 Kostas Papadimitriou
    def add_auth_provider(self, provider, **kwargs):
635 f432088a Kostas Papadimitriou
        if self.can_add_auth_provider(provider, **kwargs):
636 f432088a Kostas Papadimitriou
            self.auth_providers.create(module=provider, active=True, **kwargs)
637 f432088a Kostas Papadimitriou
        else:
638 f432088a Kostas Papadimitriou
            raise Exception('Cannot add provider')
639 d2633501 Kostas Papadimitriou
640 d2633501 Kostas Papadimitriou
    def add_pending_auth_provider(self, pending):
641 d2633501 Kostas Papadimitriou
        """
642 d2633501 Kostas Papadimitriou
        Convert PendingThirdPartyUser object to AstakosUserAuthProvider entry for
643 d2633501 Kostas Papadimitriou
        the current user.
644 d2633501 Kostas Papadimitriou
        """
645 d2633501 Kostas Papadimitriou
        if not isinstance(pending, PendingThirdPartyUser):
646 d2633501 Kostas Papadimitriou
            pending = PendingThirdPartyUser.objects.get(token=pending)
647 d2633501 Kostas Papadimitriou
648 d2633501 Kostas Papadimitriou
        provider = self.add_auth_provider(pending.provider,
649 d2633501 Kostas Papadimitriou
                               identifier=pending.third_party_identifier)
650 d2633501 Kostas Papadimitriou
651 fbaa4f3c Kostas Papadimitriou
        if email_re.match(pending.email or '') and pending.email != self.email:
652 d2633501 Kostas Papadimitriou
            self.additionalmail_set.get_or_create(email=pending.email)
653 d2633501 Kostas Papadimitriou
654 d2633501 Kostas Papadimitriou
        pending.delete()
655 d2633501 Kostas Papadimitriou
        return provider
656 d2633501 Kostas Papadimitriou
657 d2633501 Kostas Papadimitriou
    def remove_auth_provider(self, provider, **kwargs):
658 d2633501 Kostas Papadimitriou
        self.auth_providers.get(module=provider, **kwargs).delete()
659 d2633501 Kostas Papadimitriou
660 d2633501 Kostas Papadimitriou
    # user urls
661 d2633501 Kostas Papadimitriou
    def get_resend_activation_url(self):
662 d2633501 Kostas Papadimitriou
        return reverse('send_activation', {'user_id': self.pk})
663 d2633501 Kostas Papadimitriou
664 d2633501 Kostas Papadimitriou
    def get_activation_url(self, nxt=False):
665 d2633501 Kostas Papadimitriou
        url = "%s?auth=%s" % (reverse('astakos.im.views.activate'),
666 d2633501 Kostas Papadimitriou
                                 quote(self.auth_token))
667 d2633501 Kostas Papadimitriou
        if nxt:
668 d2633501 Kostas Papadimitriou
            url += "&next=%s" % quote(nxt)
669 d2633501 Kostas Papadimitriou
        return url
670 d2633501 Kostas Papadimitriou
671 d2633501 Kostas Papadimitriou
    def get_password_reset_url(self, token_generator=default_token_generator):
672 d2633501 Kostas Papadimitriou
        return reverse('django.contrib.auth.views.password_reset_confirm',
673 d2633501 Kostas Papadimitriou
                          kwargs={'uidb36':int_to_base36(self.id),
674 d2633501 Kostas Papadimitriou
                                  'token':token_generator.make_token(self)})
675 d2633501 Kostas Papadimitriou
676 d2633501 Kostas Papadimitriou
    def get_auth_providers(self):
677 d2633501 Kostas Papadimitriou
        return self.auth_providers.all()
678 d2633501 Kostas Papadimitriou
679 d2633501 Kostas Papadimitriou
    def get_available_auth_providers(self):
680 d2633501 Kostas Papadimitriou
        """
681 d2633501 Kostas Papadimitriou
        Returns a list of providers available for user to connect to.
682 d2633501 Kostas Papadimitriou
        """
683 d2633501 Kostas Papadimitriou
        providers = []
684 d2633501 Kostas Papadimitriou
        for module, provider_settings in auth_providers.PROVIDERS.iteritems():
685 f432088a Kostas Papadimitriou
            if self.can_add_auth_provider(module):
686 d2633501 Kostas Papadimitriou
                providers.append(provider_settings(self))
687 d2633501 Kostas Papadimitriou
688 d2633501 Kostas Papadimitriou
        return providers
689 d2633501 Kostas Papadimitriou
690 d2633501 Kostas Papadimitriou
    def get_active_auth_providers(self):
691 d2633501 Kostas Papadimitriou
        providers = []
692 d2633501 Kostas Papadimitriou
        for provider in self.auth_providers.active():
693 d2633501 Kostas Papadimitriou
            if auth_providers.get_provider(provider.module).is_available_for_login():
694 d2633501 Kostas Papadimitriou
                providers.append(provider)
695 d2633501 Kostas Papadimitriou
        return providers
696 d2633501 Kostas Papadimitriou
697 b778b6fa Kostas Papadimitriou
    @property
698 b778b6fa Kostas Papadimitriou
    def auth_providers_display(self):
699 b778b6fa Kostas Papadimitriou
        return ",".join(map(lambda x:unicode(x), self.auth_providers.active()))
700 b778b6fa Kostas Papadimitriou
701 d2633501 Kostas Papadimitriou
702 d2633501 Kostas Papadimitriou
class AstakosUserAuthProviderManager(models.Manager):
703 d2633501 Kostas Papadimitriou
704 d2633501 Kostas Papadimitriou
    def active(self):
705 d2633501 Kostas Papadimitriou
        return self.filter(active=True)
706 d2633501 Kostas Papadimitriou
707 d2633501 Kostas Papadimitriou
708 d2633501 Kostas Papadimitriou
class AstakosUserAuthProvider(models.Model):
709 d2633501 Kostas Papadimitriou
    """
710 d2633501 Kostas Papadimitriou
    Available user authentication methods.
711 d2633501 Kostas Papadimitriou
    """
712 e1a80257 Sofia Papagiannaki
    affiliation = models.CharField(_('Affiliation'), max_length=255, blank=True,
713 d2633501 Kostas Papadimitriou
                                   null=True, default=None)
714 d2633501 Kostas Papadimitriou
    user = models.ForeignKey(AstakosUser, related_name='auth_providers')
715 e1a80257 Sofia Papagiannaki
    module = models.CharField(_('Provider'), max_length=255, blank=False,
716 d2633501 Kostas Papadimitriou
                                default='local')
717 e1a80257 Sofia Papagiannaki
    identifier = models.CharField(_('Third-party identifier'),
718 d2633501 Kostas Papadimitriou
                                              max_length=255, null=True,
719 d2633501 Kostas Papadimitriou
                                              blank=True)
720 d2633501 Kostas Papadimitriou
    active = models.BooleanField(default=True)
721 e1a80257 Sofia Papagiannaki
    auth_backend = models.CharField(_('Backend'), max_length=255, blank=False,
722 d2633501 Kostas Papadimitriou
                                   default='astakos')
723 d2633501 Kostas Papadimitriou
724 d2633501 Kostas Papadimitriou
    objects = AstakosUserAuthProviderManager()
725 d2633501 Kostas Papadimitriou
726 d2633501 Kostas Papadimitriou
    class Meta:
727 d2633501 Kostas Papadimitriou
        unique_together = (('identifier', 'module', 'user'), )
728 d2633501 Kostas Papadimitriou
729 d2633501 Kostas Papadimitriou
    @property
730 d2633501 Kostas Papadimitriou
    def settings(self):
731 d2633501 Kostas Papadimitriou
        return auth_providers.get_provider(self.module)
732 d2633501 Kostas Papadimitriou
733 d2633501 Kostas Papadimitriou
    @property
734 d2633501 Kostas Papadimitriou
    def details_display(self):
735 d2633501 Kostas Papadimitriou
        return self.settings.details_tpl % self.__dict__
736 d2633501 Kostas Papadimitriou
737 d2633501 Kostas Papadimitriou
    def can_remove(self):
738 d2633501 Kostas Papadimitriou
        return self.user.can_remove_auth_provider(self.module)
739 d2633501 Kostas Papadimitriou
740 d2633501 Kostas Papadimitriou
    def delete(self, *args, **kwargs):
741 d2633501 Kostas Papadimitriou
        ret = super(AstakosUserAuthProvider, self).delete(*args, **kwargs)
742 5156e663 Kostas Papadimitriou
        if self.module == 'local':
743 5156e663 Kostas Papadimitriou
            self.user.set_unusable_password()
744 5156e663 Kostas Papadimitriou
            self.user.save()
745 d2633501 Kostas Papadimitriou
        return ret
746 d2633501 Kostas Papadimitriou
747 f432088a Kostas Papadimitriou
    def __repr__(self):
748 f432088a Kostas Papadimitriou
        return '<AstakosUserAuthProvider %s:%s>' % (self.module, self.identifier)
749 f432088a Kostas Papadimitriou
750 b778b6fa Kostas Papadimitriou
    def __unicode__(self):
751 b778b6fa Kostas Papadimitriou
        if self.identifier:
752 b778b6fa Kostas Papadimitriou
            return "%s:%s" % (self.module, self.identifier)
753 b778b6fa Kostas Papadimitriou
        if self.auth_backend:
754 b778b6fa Kostas Papadimitriou
            return "%s:%s" % (self.module, self.auth_backend)
755 b778b6fa Kostas Papadimitriou
        return self.module
756 b778b6fa Kostas Papadimitriou
757 b778b6fa Kostas Papadimitriou
758 d2633501 Kostas Papadimitriou
759 8e45d6fd Sofia Papagiannaki
class Membership(models.Model):
760 8e45d6fd Sofia Papagiannaki
    person = models.ForeignKey(AstakosUser)
761 8e45d6fd Sofia Papagiannaki
    group = models.ForeignKey(AstakosGroup)
762 01ac12d5 Sofia Papagiannaki
    date_requested = models.DateField(default=datetime.now(), blank=True)
763 01ac12d5 Sofia Papagiannaki
    date_joined = models.DateField(null=True, db_index=True, blank=True)
764 5ce3ce4f Sofia Papagiannaki
765 8e45d6fd Sofia Papagiannaki
    class Meta:
766 8e45d6fd Sofia Papagiannaki
        unique_together = ("person", "group")
767 5ce3ce4f Sofia Papagiannaki
768 ffb1e7a8 Sofia Papagiannaki
    def save(self, *args, **kwargs):
769 28252c7f Sofia Papagiannaki
        if not self.id:
770 28252c7f Sofia Papagiannaki
            if not self.group.moderation_enabled:
771 28252c7f Sofia Papagiannaki
                self.date_joined = datetime.now()
772 ffb1e7a8 Sofia Papagiannaki
        super(Membership, self).save(*args, **kwargs)
773 5ce3ce4f Sofia Papagiannaki
774 8e45d6fd Sofia Papagiannaki
    @property
775 8e45d6fd Sofia Papagiannaki
    def is_approved(self):
776 8e45d6fd Sofia Papagiannaki
        if self.date_joined:
777 8e45d6fd Sofia Papagiannaki
            return True
778 8e45d6fd Sofia Papagiannaki
        return False
779 5ce3ce4f Sofia Papagiannaki
780 01ac12d5 Sofia Papagiannaki
    def approve(self):
781 c0b26605 Sofia Papagiannaki
        if self.is_approved:
782 c0b26605 Sofia Papagiannaki
            return
783 ae497612 Olga Brani
        if self.group.max_participants:
784 a4075f5a root
            assert len(self.group.approved_members) + 1 <= self.group.max_participants, \
785 c0b26605 Sofia Papagiannaki
            'Maximum participant number has been reached.'
786 01ac12d5 Sofia Papagiannaki
        self.date_joined = datetime.now()
787 01ac12d5 Sofia Papagiannaki
        self.save()
788 fc1e2f02 Sofia Papagiannaki
        quota_disturbed.send(sender=self, users=(self.person,))
789 5ce3ce4f Sofia Papagiannaki
790 01ac12d5 Sofia Papagiannaki
    def disapprove(self):
791 e1a80257 Sofia Papagiannaki
        approved = self.is_approved()
792 01ac12d5 Sofia Papagiannaki
        self.delete()
793 e1a80257 Sofia Papagiannaki
        if approved:
794 e1a80257 Sofia Papagiannaki
            quota_disturbed.send(sender=self, users=(self.person,))
795 8e45d6fd Sofia Papagiannaki
796 e1a80257 Sofia Papagiannaki
class ExtendedManager(models.Manager):
797 9a06d96f Olga Brani
    def _update_or_create(self, **kwargs):
798 9a06d96f Olga Brani
        assert kwargs, \
799 9a06d96f Olga Brani
            'update_or_create() must be passed at least one keyword argument'
800 9a06d96f Olga Brani
        obj, created = self.get_or_create(**kwargs)
801 9a06d96f Olga Brani
        defaults = kwargs.pop('defaults', {})
802 9a06d96f Olga Brani
        if created:
803 9a06d96f Olga Brani
            return obj, True, False
804 9a06d96f Olga Brani
        else:
805 9a06d96f Olga Brani
            try:
806 9a06d96f Olga Brani
                params = dict(
807 9a06d96f Olga Brani
                    [(k, v) for k, v in kwargs.items() if '__' not in k])
808 9a06d96f Olga Brani
                params.update(defaults)
809 9a06d96f Olga Brani
                for attr, val in params.items():
810 9a06d96f Olga Brani
                    if hasattr(obj, attr):
811 9a06d96f Olga Brani
                        setattr(obj, attr, val)
812 9a06d96f Olga Brani
                sid = transaction.savepoint()
813 9a06d96f Olga Brani
                obj.save(force_update=True)
814 9a06d96f Olga Brani
                transaction.savepoint_commit(sid)
815 9a06d96f Olga Brani
                return obj, False, True
816 9a06d96f Olga Brani
            except IntegrityError, e:
817 9a06d96f Olga Brani
                transaction.savepoint_rollback(sid)
818 9a06d96f Olga Brani
                try:
819 9a06d96f Olga Brani
                    return self.get(**kwargs), False, False
820 9a06d96f Olga Brani
                except self.model.DoesNotExist:
821 9a06d96f Olga Brani
                    raise e
822 9a06d96f Olga Brani
823 9a06d96f Olga Brani
    update_or_create = _update_or_create
824 5ce3ce4f Sofia Papagiannaki
825 8e45d6fd Sofia Papagiannaki
class AstakosGroupQuota(models.Model):
826 e1a80257 Sofia Papagiannaki
    objects = ExtendedManager()
827 e1a80257 Sofia Papagiannaki
    limit = models.PositiveIntegerField(_('Limit'), null=True)    # obsolete field
828 e1a80257 Sofia Papagiannaki
    uplimit = models.BigIntegerField(_('Up limit'), null=True)
829 8e45d6fd Sofia Papagiannaki
    resource = models.ForeignKey(Resource)
830 8e45d6fd Sofia Papagiannaki
    group = models.ForeignKey(AstakosGroup, blank=True)
831 5ce3ce4f Sofia Papagiannaki
832 8e45d6fd Sofia Papagiannaki
    class Meta:
833 8e45d6fd Sofia Papagiannaki
        unique_together = ("resource", "group")
834 8e45d6fd Sofia Papagiannaki
835 8e45d6fd Sofia Papagiannaki
class AstakosUserQuota(models.Model):
836 e1a80257 Sofia Papagiannaki
    objects = ExtendedManager()
837 e1a80257 Sofia Papagiannaki
    limit = models.PositiveIntegerField(_('Limit'), null=True)    # obsolete field
838 e1a80257 Sofia Papagiannaki
    uplimit = models.BigIntegerField(_('Up limit'), null=True)
839 8e45d6fd Sofia Papagiannaki
    resource = models.ForeignKey(Resource)
840 8e45d6fd Sofia Papagiannaki
    user = models.ForeignKey(AstakosUser)
841 5ce3ce4f Sofia Papagiannaki
842 8e45d6fd Sofia Papagiannaki
    class Meta:
843 8e45d6fd Sofia Papagiannaki
        unique_together = ("resource", "user")
844 09e7393c Sofia Papagiannaki
845 5ce3ce4f Sofia Papagiannaki
846 270dd48d Sofia Papagiannaki
class ApprovalTerms(models.Model):
847 270dd48d Sofia Papagiannaki
    """
848 270dd48d Sofia Papagiannaki
    Model for approval terms
849 270dd48d Sofia Papagiannaki
    """
850 6c736ed7 Kostas Papadimitriou
851 5ce3ce4f Sofia Papagiannaki
    date = models.DateTimeField(
852 e1a80257 Sofia Papagiannaki
        _('Issue date'), db_index=True, default=datetime.now())
853 e1a80257 Sofia Papagiannaki
    location = models.CharField(_('Terms location'), max_length=255)
854 270dd48d Sofia Papagiannaki
855 5ce3ce4f Sofia Papagiannaki
856 64cd4730 Antony Chazapis
class Invitation(models.Model):
857 890b0eaf Sofia Papagiannaki
    """
858 890b0eaf Sofia Papagiannaki
    Model for registring invitations
859 890b0eaf Sofia Papagiannaki
    """
860 0905ccd2 Sofia Papagiannaki
    inviter = models.ForeignKey(AstakosUser, related_name='invitations_sent',
861 64cd4730 Antony Chazapis
                                null=True)
862 e1a80257 Sofia Papagiannaki
    realname = models.CharField(_('Real name'), max_length=255)
863 e1a80257 Sofia Papagiannaki
    username = models.CharField(_('Unique ID'), max_length=255, unique=True)
864 e1a80257 Sofia Papagiannaki
    code = models.BigIntegerField(_('Invitation code'), db_index=True)
865 e1a80257 Sofia Papagiannaki
    is_consumed = models.BooleanField(_('Consumed?'), default=False)
866 e1a80257 Sofia Papagiannaki
    created = models.DateTimeField(_('Creation date'), auto_now_add=True)
867 e1a80257 Sofia Papagiannaki
    consumed = models.DateTimeField(_('Consumption date'), null=True, blank=True)
868 5ce3ce4f Sofia Papagiannaki
869 18ffbee1 Sofia Papagiannaki
    def __init__(self, *args, **kwargs):
870 18ffbee1 Sofia Papagiannaki
        super(Invitation, self).__init__(*args, **kwargs)
871 8f5a3a06 Sofia Papagiannaki
        if not self.id:
872 8f5a3a06 Sofia Papagiannaki
            self.code = _generate_invitation_code()
873 5ce3ce4f Sofia Papagiannaki
874 64cd4730 Antony Chazapis
    def consume(self):
875 64cd4730 Antony Chazapis
        self.is_consumed = True
876 64cd4730 Antony Chazapis
        self.consumed = datetime.now()
877 64cd4730 Antony Chazapis
        self.save()
878 6c736ed7 Kostas Papadimitriou
879 64cd4730 Antony Chazapis
    def __unicode__(self):
880 0905ccd2 Sofia Papagiannaki
        return '%s -> %s [%d]' % (self.inviter, self.username, self.code)
881 9c01d9d1 Sofia Papagiannaki
882 49790d9d Sofia Papagiannaki
883 49790d9d Sofia Papagiannaki
class EmailChangeManager(models.Manager):
884 49790d9d Sofia Papagiannaki
    @transaction.commit_on_success
885 49790d9d Sofia Papagiannaki
    def change_email(self, activation_key):
886 49790d9d Sofia Papagiannaki
        """
887 49790d9d Sofia Papagiannaki
        Validate an activation key and change the corresponding
888 49790d9d Sofia Papagiannaki
        ``User`` if valid.
889 49790d9d Sofia Papagiannaki

890 49790d9d Sofia Papagiannaki
        If the key is valid and has not expired, return the ``User``
891 49790d9d Sofia Papagiannaki
        after activating.
892 49790d9d Sofia Papagiannaki

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

895 49790d9d Sofia Papagiannaki
        If the key is valid but the ``User`` is already active,
896 49790d9d Sofia Papagiannaki
        return ``None``.
897 49790d9d Sofia Papagiannaki

898 49790d9d Sofia Papagiannaki
        After successful email change the activation record is deleted.
899 49790d9d Sofia Papagiannaki

900 49790d9d Sofia Papagiannaki
        Throws ValueError if there is already
901 49790d9d Sofia Papagiannaki
        """
902 49790d9d Sofia Papagiannaki
        try:
903 5ce3ce4f Sofia Papagiannaki
            email_change = self.model.objects.get(
904 5ce3ce4f Sofia Papagiannaki
                activation_key=activation_key)
905 49790d9d Sofia Papagiannaki
            if email_change.activation_key_expired():
906 49790d9d Sofia Papagiannaki
                email_change.delete()
907 49790d9d Sofia Papagiannaki
                raise EmailChange.DoesNotExist
908 49790d9d Sofia Papagiannaki
            # is there an active user with this address?
909 49790d9d Sofia Papagiannaki
            try:
910 789a5951 Sofia Papagiannaki
                AstakosUser.objects.get(email__iexact=email_change.new_email_address)
911 49790d9d Sofia Papagiannaki
            except AstakosUser.DoesNotExist:
912 49790d9d Sofia Papagiannaki
                pass
913 49790d9d Sofia Papagiannaki
            else:
914 ae497612 Olga Brani
                raise ValueError(_(astakos_messages.NEW_EMAIL_ADDR_RESERVED))
915 49790d9d Sofia Papagiannaki
            # update user
916 49790d9d Sofia Papagiannaki
            user = AstakosUser.objects.get(pk=email_change.user_id)
917 49790d9d Sofia Papagiannaki
            user.email = email_change.new_email_address
918 49790d9d Sofia Papagiannaki
            user.save()
919 49790d9d Sofia Papagiannaki
            email_change.delete()
920 49790d9d Sofia Papagiannaki
            return user
921 49790d9d Sofia Papagiannaki
        except EmailChange.DoesNotExist:
922 ae497612 Olga Brani
            raise ValueError(_(astakos_messages.INVALID_ACTIVATION_KEY))
923 49790d9d Sofia Papagiannaki
924 49790d9d Sofia Papagiannaki
925 49790d9d Sofia Papagiannaki
class EmailChange(models.Model):
926 9a06d96f Olga Brani
    new_email_address = models.EmailField(_(u'new e-mail address'),
927 ae497612 Olga Brani
                                          help_text=_(astakos_messages.EMAIL_CHANGE_NEW_ADDR_HELP))
928 5ce3ce4f Sofia Papagiannaki
    user = models.ForeignKey(
929 5ce3ce4f Sofia Papagiannaki
        AstakosUser, unique=True, related_name='emailchange_user')
930 49790d9d Sofia Papagiannaki
    requested_at = models.DateTimeField(default=datetime.now())
931 5ce3ce4f Sofia Papagiannaki
    activation_key = models.CharField(
932 5ce3ce4f Sofia Papagiannaki
        max_length=40, unique=True, db_index=True)
933 49790d9d Sofia Papagiannaki
934 49790d9d Sofia Papagiannaki
    objects = EmailChangeManager()
935 49790d9d Sofia Papagiannaki
936 49790d9d Sofia Papagiannaki
    def activation_key_expired(self):
937 49790d9d Sofia Papagiannaki
        expiration_date = timedelta(days=EMAILCHANGE_ACTIVATION_DAYS)
938 ff9290ec Sofia Papagiannaki
        return self.requested_at + expiration_date < datetime.now()
939 ff9290ec Sofia Papagiannaki
940 6b03a847 Sofia Papagiannaki
941 ca828a10 Sofia Papagiannaki
class AdditionalMail(models.Model):
942 ca828a10 Sofia Papagiannaki
    """
943 ca828a10 Sofia Papagiannaki
    Model for registring invitations
944 ca828a10 Sofia Papagiannaki
    """
945 ca828a10 Sofia Papagiannaki
    owner = models.ForeignKey(AstakosUser)
946 1eec103a Sofia Papagiannaki
    email = models.EmailField()
947 ca828a10 Sofia Papagiannaki
948 5ce3ce4f Sofia Papagiannaki
949 fc1e2f02 Sofia Papagiannaki
def _generate_invitation_code():
950 fc1e2f02 Sofia Papagiannaki
    while True:
951 5ce3ce4f Sofia Papagiannaki
        code = randint(1, 2L ** 63 - 1)
952 fc1e2f02 Sofia Papagiannaki
        try:
953 fc1e2f02 Sofia Papagiannaki
            Invitation.objects.get(code=code)
954 fc1e2f02 Sofia Papagiannaki
            # An invitation with this code already exists, try again
955 fc1e2f02 Sofia Papagiannaki
        except Invitation.DoesNotExist:
956 fc1e2f02 Sofia Papagiannaki
            return code
957 fc1e2f02 Sofia Papagiannaki
958 5ce3ce4f Sofia Papagiannaki
959 fc1e2f02 Sofia Papagiannaki
def get_latest_terms():
960 fc1e2f02 Sofia Papagiannaki
    try:
961 fc1e2f02 Sofia Papagiannaki
        term = ApprovalTerms.objects.order_by('-id')[0]
962 fc1e2f02 Sofia Papagiannaki
        return term
963 fc1e2f02 Sofia Papagiannaki
    except IndexError:
964 fc1e2f02 Sofia Papagiannaki
        pass
965 fc1e2f02 Sofia Papagiannaki
    return None
966 fc1e2f02 Sofia Papagiannaki
967 ef20ea07 Sofia Papagiannaki
class PendingThirdPartyUser(models.Model):
968 ef20ea07 Sofia Papagiannaki
    """
969 ef20ea07 Sofia Papagiannaki
    Model for registring successful third party user authentications
970 ef20ea07 Sofia Papagiannaki
    """
971 e1a80257 Sofia Papagiannaki
    third_party_identifier = models.CharField(_('Third-party identifier'), max_length=255, null=True, blank=True)
972 e1a80257 Sofia Papagiannaki
    provider = models.CharField(_('Provider'), max_length=255, blank=True)
973 678b2236 Sofia Papagiannaki
    email = models.EmailField(_('e-mail address'), blank=True, null=True)
974 ef20ea07 Sofia Papagiannaki
    first_name = models.CharField(_('first name'), max_length=30, blank=True)
975 ef20ea07 Sofia Papagiannaki
    last_name = models.CharField(_('last name'), max_length=30, blank=True)
976 ef20ea07 Sofia Papagiannaki
    affiliation = models.CharField('Affiliation', max_length=255, blank=True)
977 ef20ea07 Sofia Papagiannaki
    username = models.CharField(_('username'), max_length=30, unique=True, help_text=_("Required. 30 characters or fewer. Letters, numbers and @/./+/-/_ characters"))
978 e1a80257 Sofia Papagiannaki
    token = models.CharField(_('Token'), max_length=255, null=True, blank=True)
979 d2633501 Kostas Papadimitriou
    created = models.DateTimeField(auto_now_add=True, null=True, blank=True)
980 d2633501 Kostas Papadimitriou
981 678b2236 Sofia Papagiannaki
    class Meta:
982 678b2236 Sofia Papagiannaki
        unique_together = ("provider", "third_party_identifier")
983 ef20ea07 Sofia Papagiannaki
984 ef20ea07 Sofia Papagiannaki
    @property
985 ef20ea07 Sofia Papagiannaki
    def realname(self):
986 ef20ea07 Sofia Papagiannaki
        return '%s %s' %(self.first_name, self.last_name)
987 ef20ea07 Sofia Papagiannaki
988 ef20ea07 Sofia Papagiannaki
    @realname.setter
989 ef20ea07 Sofia Papagiannaki
    def realname(self, value):
990 ef20ea07 Sofia Papagiannaki
        parts = value.split(' ')
991 ef20ea07 Sofia Papagiannaki
        if len(parts) == 2:
992 ef20ea07 Sofia Papagiannaki
            self.first_name = parts[0]
993 ef20ea07 Sofia Papagiannaki
            self.last_name = parts[1]
994 ef20ea07 Sofia Papagiannaki
        else:
995 ef20ea07 Sofia Papagiannaki
            self.last_name = parts[0]
996 2e90e3ec Kostas Papadimitriou
997 ef20ea07 Sofia Papagiannaki
    def save(self, **kwargs):
998 ef20ea07 Sofia Papagiannaki
        if not self.id:
999 ef20ea07 Sofia Papagiannaki
            # set username
1000 ef20ea07 Sofia Papagiannaki
            while not self.username:
1001 ef20ea07 Sofia Papagiannaki
                username =  uuid.uuid4().hex[:30]
1002 ef20ea07 Sofia Papagiannaki
                try:
1003 ef20ea07 Sofia Papagiannaki
                    AstakosUser.objects.get(username = username)
1004 ef20ea07 Sofia Papagiannaki
                except AstakosUser.DoesNotExist, e:
1005 ef20ea07 Sofia Papagiannaki
                    self.username = username
1006 ef20ea07 Sofia Papagiannaki
        super(PendingThirdPartyUser, self).save(**kwargs)
1007 ef20ea07 Sofia Papagiannaki
1008 d2633501 Kostas Papadimitriou
    def generate_token(self):
1009 d2633501 Kostas Papadimitriou
        self.password = self.third_party_identifier
1010 d2633501 Kostas Papadimitriou
        self.last_login = datetime.now()
1011 d2633501 Kostas Papadimitriou
        self.token = default_token_generator.make_token(self)
1012 d2633501 Kostas Papadimitriou
1013 bf0c6de5 Sofia Papagiannaki
class SessionCatalog(models.Model):
1014 bf0c6de5 Sofia Papagiannaki
    session_key = models.CharField(_('session key'), max_length=40)
1015 bf0c6de5 Sofia Papagiannaki
    user = models.ForeignKey(AstakosUser, related_name='sessions', null=True)
1016 bf0c6de5 Sofia Papagiannaki
1017 ccab6eb5 Sofia Papagiannaki
class MemberJoinPolicy(models.Model):
1018 e1a80257 Sofia Papagiannaki
    policy = models.CharField(_('Policy'), max_length=255, unique=True, db_index=True)
1019 e1a80257 Sofia Papagiannaki
    description = models.CharField(_('Description'), max_length=80)
1020 e1a80257 Sofia Papagiannaki
1021 e1a80257 Sofia Papagiannaki
    def __str__(self):
1022 e1a80257 Sofia Papagiannaki
        return self.policy
1023 e1a80257 Sofia Papagiannaki
1024 ccab6eb5 Sofia Papagiannaki
class MemberLeavePolicy(models.Model):
1025 b22de10a Sofia Papagiannaki
    policy = models.CharField(_('Policy'), max_length=255, unique=True, db_index=True)
1026 b22de10a Sofia Papagiannaki
    description = models.CharField(_('Description'), max_length=80)
1027 b22de10a Sofia Papagiannaki
1028 b22de10a Sofia Papagiannaki
    def __str__(self):
1029 b22de10a Sofia Papagiannaki
        return self.policy
1030 b22de10a Sofia Papagiannaki
1031 ccab6eb5 Sofia Papagiannaki
_auto_accept_join = False
1032 ccab6eb5 Sofia Papagiannaki
def get_auto_accept_join():
1033 e65c21df Georgios D. Tsoukalas
    global _auto_accept
1034 e65c21df Georgios D. Tsoukalas
    if _auto_accept is not False:
1035 e65c21df Georgios D. Tsoukalas
        return _auto_accept
1036 e65c21df Georgios D. Tsoukalas
    try:
1037 ccab6eb5 Sofia Papagiannaki
        auto_accept = MemberJoinPolicy.objects.get(policy='auto_accept')
1038 ccab6eb5 Sofia Papagiannaki
    except:
1039 ccab6eb5 Sofia Papagiannaki
        auto_accept = None
1040 ccab6eb5 Sofia Papagiannaki
    _auto_accept = auto_accept
1041 ccab6eb5 Sofia Papagiannaki
    return auto_accept
1042 ccab6eb5 Sofia Papagiannaki
1043 ccab6eb5 Sofia Papagiannaki
_auto_accept_leave = False
1044 ccab6eb5 Sofia Papagiannaki
def get_auto_accept_leave():
1045 ccab6eb5 Sofia Papagiannaki
    global _auto_accept
1046 ccab6eb5 Sofia Papagiannaki
    if _auto_accept is not False:
1047 ccab6eb5 Sofia Papagiannaki
        return _auto_accept
1048 ccab6eb5 Sofia Papagiannaki
    try:
1049 ccab6eb5 Sofia Papagiannaki
        auto_accept = MemberLeavePolicy.objects.get(policy='auto_accept')
1050 e65c21df Georgios D. Tsoukalas
    except:
1051 e65c21df Georgios D. Tsoukalas
        auto_accept = None
1052 e65c21df Georgios D. Tsoukalas
    _auto_accept = auto_accept
1053 e65c21df Georgios D. Tsoukalas
    return auto_accept
1054 e1a80257 Sofia Papagiannaki
1055 e1a80257 Sofia Papagiannaki
class ProjectDefinition(models.Model):
1056 e1a80257 Sofia Papagiannaki
    name = models.CharField(max_length=80)
1057 e1a80257 Sofia Papagiannaki
    homepage = models.URLField(max_length=255, null=True, blank=True)
1058 e1a80257 Sofia Papagiannaki
    description = models.TextField(null=True)
1059 e1a80257 Sofia Papagiannaki
    start_date = models.DateTimeField()
1060 e1a80257 Sofia Papagiannaki
    end_date = models.DateTimeField()
1061 ccab6eb5 Sofia Papagiannaki
    member_join_policy = models.ForeignKey(MemberJoinPolicy)
1062 ccab6eb5 Sofia Papagiannaki
    member_leave_policy = models.ForeignKey(MemberLeavePolicy)
1063 e1a80257 Sofia Papagiannaki
    limit_on_members_number = models.PositiveIntegerField(null=True,blank=True)
1064 e1a80257 Sofia Papagiannaki
    resource_grants = models.ManyToManyField(
1065 e1a80257 Sofia Papagiannaki
        Resource,
1066 e1a80257 Sofia Papagiannaki
        null=True,
1067 e1a80257 Sofia Papagiannaki
        blank=True,
1068 e1a80257 Sofia Papagiannaki
        through='ProjectResourceGrant'
1069 e1a80257 Sofia Papagiannaki
    )
1070 e1a80257 Sofia Papagiannaki
    
1071 e1a80257 Sofia Papagiannaki
    def save(self):
1072 e1a80257 Sofia Papagiannaki
        self.validate_name()
1073 e1a80257 Sofia Papagiannaki
        super(ProjectDefinition, self).save()
1074 b22de10a Sofia Papagiannaki
        
1075 e1a80257 Sofia Papagiannaki
    @property
1076 e1a80257 Sofia Papagiannaki
    def violated_resource_grants(self):
1077 e1a80257 Sofia Papagiannaki
        return False
1078 e1a80257 Sofia Papagiannaki
    
1079 e1a80257 Sofia Papagiannaki
    def add_resource_policy(self, service, resource, uplimit, update=True):
1080 e1a80257 Sofia Papagiannaki
        """Raises ObjectDoesNotExist, IntegrityError"""
1081 e1a80257 Sofia Papagiannaki
        resource = Resource.objects.get(service__name=service, name=resource)
1082 e1a80257 Sofia Papagiannaki
        if update:
1083 f3342849 Sofia Papagiannaki
            ProjectResourceGrant.objects.update_or_create(
1084 f3342849 Sofia Papagiannaki
                project_definition=self,
1085 e1a80257 Sofia Papagiannaki
                resource=resource,
1086 f3342849 Sofia Papagiannaki
                defaults={'member_limit': uplimit}
1087 e1a80257 Sofia Papagiannaki
            )
1088 e1a80257 Sofia Papagiannaki
        else:
1089 f3342849 Sofia Papagiannaki
            q = self.projectresourcegrant_set
1090 f3342849 Sofia Papagiannaki
            q.create(resource=resource, member_limit=uplimit)
1091 e1a80257 Sofia Papagiannaki
1092 e1a80257 Sofia Papagiannaki
    @property
1093 e1a80257 Sofia Papagiannaki
    def resource_policies(self):
1094 f3342849 Sofia Papagiannaki
        return self.projectresourcegrant_set.all()
1095 e1a80257 Sofia Papagiannaki
1096 e1a80257 Sofia Papagiannaki
    @resource_policies.setter
1097 e1a80257 Sofia Papagiannaki
    def resource_policies(self, policies):
1098 e1a80257 Sofia Papagiannaki
        for p in policies:
1099 e1a80257 Sofia Papagiannaki
            service = p.get('service', None)
1100 e1a80257 Sofia Papagiannaki
            resource = p.get('resource', None)
1101 e1a80257 Sofia Papagiannaki
            uplimit = p.get('uplimit', 0)
1102 e1a80257 Sofia Papagiannaki
            update = p.get('update', True)
1103 e1a80257 Sofia Papagiannaki
            self.add_resource_policy(service, resource, uplimit, update)
1104 0cc22d47 Sofia Papagiannaki
    
1105 0cc22d47 Sofia Papagiannaki
    def validate_name(self):
1106 0cc22d47 Sofia Papagiannaki
        """
1107 0cc22d47 Sofia Papagiannaki
        Validate name uniqueness among all active projects.
1108 0cc22d47 Sofia Papagiannaki
        """
1109 0cc22d47 Sofia Papagiannaki
        alive_projects = list(get_alive_projects())
1110 0cc22d47 Sofia Papagiannaki
        q = filter(
1111 0cc22d47 Sofia Papagiannaki
            lambda p: p.definition.name == self.name and \
1112 ccab6eb5 Sofia Papagiannaki
                p.application.id != self.projectapplication.id,
1113 0cc22d47 Sofia Papagiannaki
            alive_projects
1114 0cc22d47 Sofia Papagiannaki
        )
1115 0cc22d47 Sofia Papagiannaki
        if q:
1116 0cc22d47 Sofia Papagiannaki
            raise ValidationError(
1117 0cc22d47 Sofia Papagiannaki
                {'name': [_(astakos_messages.UNIQUE_PROJECT_NAME_CONSTRAIN_ERR)]}
1118 0cc22d47 Sofia Papagiannaki
            )
1119 0cc22d47 Sofia Papagiannaki
1120 e1a80257 Sofia Papagiannaki
1121 e1a80257 Sofia Papagiannaki
class ProjectResourceGrant(models.Model):
1122 e1a80257 Sofia Papagiannaki
    objects = ExtendedManager()
1123 e1a80257 Sofia Papagiannaki
    member_limit = models.BigIntegerField(null=True)
1124 e1a80257 Sofia Papagiannaki
    project_limit = models.BigIntegerField(null=True)
1125 e1a80257 Sofia Papagiannaki
    resource = models.ForeignKey(Resource)
1126 e1a80257 Sofia Papagiannaki
    project_definition = models.ForeignKey(ProjectDefinition, blank=True)
1127 e1a80257 Sofia Papagiannaki
1128 e1a80257 Sofia Papagiannaki
    class Meta:
1129 e1a80257 Sofia Papagiannaki
        unique_together = ("resource", "project_definition")
1130 e1a80257 Sofia Papagiannaki
1131 e1a80257 Sofia Papagiannaki
class ProjectApplication(models.Model):
1132 ccab6eb5 Sofia Papagiannaki
    PENDING, APPROVED, REPLACED = range(3)
1133 ccab6eb5 Sofia Papagiannaki
    states_list = ['Pending', 'Approved', 'Replaced']
1134 ccab6eb5 Sofia Papagiannaki
    states = dict((k, v) for k, v in enumerate(states_list))
1135 ccab6eb5 Sofia Papagiannaki
1136 b22de10a Sofia Papagiannaki
    applicant = models.ForeignKey(
1137 b22de10a Sofia Papagiannaki
        AstakosUser,
1138 b22de10a Sofia Papagiannaki
        related_name='my_project_applications',
1139 b22de10a Sofia Papagiannaki
        db_index=True)
1140 b22de10a Sofia Papagiannaki
    owner = models.ForeignKey(
1141 b22de10a Sofia Papagiannaki
        AstakosUser,
1142 b22de10a Sofia Papagiannaki
        related_name='own_project_applications',
1143 b22de10a Sofia Papagiannaki
        db_index=True
1144 b22de10a Sofia Papagiannaki
    )
1145 e1a80257 Sofia Papagiannaki
    comments = models.TextField(null=True, blank=True)
1146 e1a80257 Sofia Papagiannaki
    definition = models.OneToOneField(ProjectDefinition)
1147 e1a80257 Sofia Papagiannaki
    issue_date = models.DateTimeField()
1148 e1a80257 Sofia Papagiannaki
    precursor_application = models.OneToOneField('ProjectApplication',
1149 e1a80257 Sofia Papagiannaki
        null=True,
1150 ccab6eb5 Sofia Papagiannaki
        blank=True,
1151 ccab6eb5 Sofia Papagiannaki
        db_index=True
1152 e1a80257 Sofia Papagiannaki
    )
1153 0cc22d47 Sofia Papagiannaki
    
1154 ccab6eb5 Sofia Papagiannaki
    @property
1155 ccab6eb5 Sofia Papagiannaki
    def follower(self):
1156 ccab6eb5 Sofia Papagiannaki
        try:
1157 ccab6eb5 Sofia Papagiannaki
            return ProjectApplication.objects.get(precursor_application=self)
1158 ccab6eb5 Sofia Papagiannaki
        except ProjectApplication.DoesNotExist:
1159 ccab6eb5 Sofia Papagiannaki
            return
1160 ccab6eb5 Sofia Papagiannaki
1161 0cc22d47 Sofia Papagiannaki
    def save(self):
1162 ccab6eb5 Sofia Papagiannaki
        self.definition.save()
1163 ccab6eb5 Sofia Papagiannaki
        self.definition = self.definition
1164 0cc22d47 Sofia Papagiannaki
        super(ProjectApplication, self).save()
1165 0cc22d47 Sofia Papagiannaki
1166 8327782d Sofia Papagiannaki
    @property
1167 8327782d Sofia Papagiannaki
    def status(self):
1168 ccab6eb5 Sofia Papagiannaki
        if self.follower:
1169 ccab6eb5 Sofia Papagiannaki
            try:
1170 ccab6eb5 Sofia Papagiannaki
                self.follower.project
1171 ccab6eb5 Sofia Papagiannaki
            except:
1172 ccab6eb5 Sofia Papagiannaki
                pass
1173 ccab6eb5 Sofia Papagiannaki
            else:
1174 ccab6eb5 Sofia Papagiannaki
                if self.follower.project.last_approval_date:
1175 ccab6eb5 Sofia Papagiannaki
                    return self.states[self.REPLACED]
1176 8327782d Sofia Papagiannaki
        try:
1177 8327782d Sofia Papagiannaki
            self.project
1178 8327782d Sofia Papagiannaki
        except Project.DoesNotExist:
1179 ccab6eb5 Sofia Papagiannaki
            return self.states[self.PENDING]
1180 ccab6eb5 Sofia Papagiannaki
        if self.project.is_alive:
1181 ccab6eb5 Sofia Papagiannaki
            return self.states[self.APPROVED]
1182 8327782d Sofia Papagiannaki
        
1183 0cc22d47 Sofia Papagiannaki
    @staticmethod
1184 ccab6eb5 Sofia Papagiannaki
    def submit(definition, resource_policies, applicant, comments, precursor_application=None, commit=True):
1185 8327782d Sofia Papagiannaki
        application = None
1186 8327782d Sofia Papagiannaki
        if precursor_application:
1187 8327782d Sofia Papagiannaki
            try:
1188 8327782d Sofia Papagiannaki
                precursor_application.project
1189 8327782d Sofia Papagiannaki
            except:
1190 8327782d Sofia Papagiannaki
                pass
1191 8327782d Sofia Papagiannaki
            else:
1192 ccab6eb5 Sofia Papagiannaki
                if precursor_application.status != 'Pending':
1193 ccab6eb5 Sofia Papagiannaki
                    application = precursor_application
1194 8327782d Sofia Papagiannaki
                    application.precursor_application = precursor_application
1195 ccab6eb5 Sofia Papagiannaki
                    application.id = None
1196 ccab6eb5 Sofia Papagiannaki
                    print '>>>', application.precursor_application.id
1197 8327782d Sofia Papagiannaki
        if not application:
1198 0cc22d47 Sofia Papagiannaki
            application = ProjectApplication(owner=applicant)
1199 0cc22d47 Sofia Papagiannaki
        application.definition = definition
1200 0cc22d47 Sofia Papagiannaki
        application.applicant = applicant
1201 0cc22d47 Sofia Papagiannaki
        application.comments = comments
1202 0cc22d47 Sofia Papagiannaki
        application.issue_date = datetime.now()
1203 ccab6eb5 Sofia Papagiannaki
        application.definition.id = None
1204 ccab6eb5 Sofia Papagiannaki
        application.id = None
1205 0cc22d47 Sofia Papagiannaki
        if commit:
1206 0cc22d47 Sofia Papagiannaki
            application.save()
1207 ccab6eb5 Sofia Papagiannaki
            application.definition.resource_policies = resource_policies
1208 0cc22d47 Sofia Papagiannaki
        if applicant.is_superuser:
1209 0cc22d47 Sofia Papagiannaki
            self.approve_application()
1210 ccab6eb5 Sofia Papagiannaki
#         else:
1211 ccab6eb5 Sofia Papagiannaki
#             notification = build_notification(
1212 ccab6eb5 Sofia Papagiannaki
#                 settings.SERVER_EMAIL,
1213 ccab6eb5 Sofia Papagiannaki
#                 [i[1] for i in settings.ADMINS],
1214 ccab6eb5 Sofia Papagiannaki
#                 _(GROUP_CREATION_SUBJECT) % {'group':application.definition.name},
1215 ccab6eb5 Sofia Papagiannaki
#                 _('An new project application identified by %(id)s has been submitted.') % application.__dict__
1216 ccab6eb5 Sofia Papagiannaki
#             )
1217 ccab6eb5 Sofia Papagiannaki
#             notification.send()
1218 0cc22d47 Sofia Papagiannaki
        return application
1219 ccab6eb5 Sofia Papagiannaki
        
1220 ccab6eb5 Sofia Papagiannaki
    def approve(self, approval_user=None):
1221 ccab6eb5 Sofia Papagiannaki
        """
1222 ccab6eb5 Sofia Papagiannaki
        If approval_user then during owner membership acceptance
1223 ccab6eb5 Sofia Papagiannaki
        it is checked whether the request_user is eligible.
1224 ccab6eb5 Sofia Papagiannaki
        """
1225 ccab6eb5 Sofia Papagiannaki
        if self.status != self.states[self.PENDING]:
1226 ccab6eb5 Sofia Papagiannaki
            return
1227 ccab6eb5 Sofia Papagiannaki
        if not self.precursor_application:
1228 ccab6eb5 Sofia Papagiannaki
            kwargs = {
1229 ccab6eb5 Sofia Papagiannaki
                'application':self,
1230 ccab6eb5 Sofia Papagiannaki
                'creation_date':datetime.now(),
1231 ccab6eb5 Sofia Papagiannaki
                'last_approval_date':datetime.now(),
1232 ccab6eb5 Sofia Papagiannaki
            }
1233 ccab6eb5 Sofia Papagiannaki
            project = _create_object(Project, **kwargs)
1234 ccab6eb5 Sofia Papagiannaki
            project.accept_member(self.owner, approval_user)
1235 ccab6eb5 Sofia Papagiannaki
        else:
1236 ccab6eb5 Sofia Papagiannaki
            project = self.precursor_application.project
1237 ccab6eb5 Sofia Papagiannaki
            project.application = self
1238 ccab6eb5 Sofia Papagiannaki
            project.last_approval_date = datetime.now()
1239 ccab6eb5 Sofia Papagiannaki
            project.save()
1240 ccab6eb5 Sofia Papagiannaki
1241 ccab6eb5 Sofia Papagiannaki
#         notification = build_notification(
1242 ccab6eb5 Sofia Papagiannaki
#             settings.SERVER_EMAIL,
1243 ccab6eb5 Sofia Papagiannaki
#             [project.owner.email],
1244 ccab6eb5 Sofia Papagiannaki
#             _('Project application has been approved on %s alpha2 testing' % SITENAME),
1245 ccab6eb5 Sofia Papagiannaki
#             _('Your application request %(id)s has been apporved.')
1246 ccab6eb5 Sofia Papagiannaki
#         )
1247 ccab6eb5 Sofia Papagiannaki
#         notification.send()
1248 ccab6eb5 Sofia Papagiannaki
1249 ccab6eb5 Sofia Papagiannaki
        rejected = self.project.sync()
1250 ccab6eb5 Sofia Papagiannaki
        if rejected:
1251 ccab6eb5 Sofia Papagiannaki
            # revert to precursor
1252 ccab6eb5 Sofia Papagiannaki
            project.appication = app.precursor_application
1253 ccab6eb5 Sofia Papagiannaki
            if project.application:
1254 ccab6eb5 Sofia Papagiannaki
                project.last_approval_date = last_approval_date
1255 ccab6eb5 Sofia Papagiannaki
                project.save()
1256 ccab6eb5 Sofia Papagiannaki
            rejected = project.sync()
1257 ccab6eb5 Sofia Papagiannaki
            if rejected:
1258 ccab6eb5 Sofia Papagiannaki
                raise Exception(_(astakos_messages.QH_SYNC_ERROR))
1259 ccab6eb5 Sofia Papagiannaki
        else:
1260 ccab6eb5 Sofia Papagiannaki
            project.last_application_synced = app
1261 ccab6eb5 Sofia Papagiannaki
            project.save()
1262 e1a80257 Sofia Papagiannaki
1263 8327782d Sofia Papagiannaki
1264 e1a80257 Sofia Papagiannaki
class Project(models.Model):
1265 e1a80257 Sofia Papagiannaki
    application = models.OneToOneField(ProjectApplication, related_name='project')
1266 e1a80257 Sofia Papagiannaki
    creation_date = models.DateTimeField()
1267 2a965273 Sofia Papagiannaki
    last_approval_date = models.DateTimeField(null=True)
1268 b22de10a Sofia Papagiannaki
    termination_start_date = models.DateTimeField(null=True)
1269 2a965273 Sofia Papagiannaki
    termination_date = models.DateTimeField(null=True)
1270 e1a80257 Sofia Papagiannaki
    members = models.ManyToManyField(AstakosUser, through='ProjectMembership')
1271 b22de10a Sofia Papagiannaki
    membership_dirty = models.BooleanField(default=False)
1272 b22de10a Sofia Papagiannaki
    last_application_synced = models.OneToOneField(
1273 e1a80257 Sofia Papagiannaki
        ProjectApplication, related_name='last_project', null=True, blank=True
1274 e1a80257 Sofia Papagiannaki
    )
1275 e1a80257 Sofia Papagiannaki
    
1276 8327782d Sofia Papagiannaki
    
1277 e1a80257 Sofia Papagiannaki
    @property
1278 e1a80257 Sofia Papagiannaki
    def definition(self):
1279 e1a80257 Sofia Papagiannaki
        return self.application.definition
1280 b22de10a Sofia Papagiannaki
1281 b22de10a Sofia Papagiannaki
    @property
1282 b22de10a Sofia Papagiannaki
    def violated_members_number_limit(self):
1283 b22de10a Sofia Papagiannaki
        return len(self.approved_members) <= self.definition.limit_on_members_number
1284 b22de10a Sofia Papagiannaki
        
1285 e1a80257 Sofia Papagiannaki
    @property
1286 e1a80257 Sofia Papagiannaki
    def is_active(self):
1287 e1a80257 Sofia Papagiannaki
        if not self.last_approval_date:
1288 e1a80257 Sofia Papagiannaki
            return False
1289 e1a80257 Sofia Papagiannaki
        if self.termination_date:
1290 e1a80257 Sofia Papagiannaki
            return False
1291 e1a80257 Sofia Papagiannaki
        if self.definition.violated_resource_grants:
1292 e1a80257 Sofia Papagiannaki
            return False
1293 b22de10a Sofia Papagiannaki
#         if self.violated_members_number_limit:
1294 b22de10a Sofia Papagiannaki
#             return False
1295 e1a80257 Sofia Papagiannaki
        return True
1296 e1a80257 Sofia Papagiannaki
    
1297 e1a80257 Sofia Papagiannaki
    @property
1298 e1a80257 Sofia Papagiannaki
    def is_terminated(self):
1299 e1a80257 Sofia Papagiannaki
        if not self.termination_date:
1300 e1a80257 Sofia Papagiannaki
            return False
1301 e1a80257 Sofia Papagiannaki
        return True
1302 e1a80257 Sofia Papagiannaki
    
1303 e1a80257 Sofia Papagiannaki
    @property
1304 e1a80257 Sofia Papagiannaki
    def is_suspended(self):
1305 e1a80257 Sofia Papagiannaki
        if not self.termination_date:
1306 e1a80257 Sofia Papagiannaki
            return False
1307 e1a80257 Sofia Papagiannaki
        if not self.last_approval_date:
1308 e1a80257 Sofia Papagiannaki
            if not self.definition.violated_resource_grants:
1309 e1a80257 Sofia Papagiannaki
                return False
1310 b22de10a Sofia Papagiannaki
#             if not self.violated_members_number_limit:
1311 b22de10a Sofia Papagiannaki
#                 return False
1312 e1a80257 Sofia Papagiannaki
        return True
1313 e1a80257 Sofia Papagiannaki
    
1314 e1a80257 Sofia Papagiannaki
    @property
1315 e1a80257 Sofia Papagiannaki
    def is_alive(self):
1316 e1a80257 Sofia Papagiannaki
        return self.is_active or self.is_suspended
1317 e1a80257 Sofia Papagiannaki
    
1318 e1a80257 Sofia Papagiannaki
    @property
1319 e1a80257 Sofia Papagiannaki
    def is_inconsistent(self):
1320 e1a80257 Sofia Papagiannaki
        now = datetime.now()
1321 e1a80257 Sofia Papagiannaki
        if self.creation_date > now:
1322 e1a80257 Sofia Papagiannaki
            return True
1323 e1a80257 Sofia Papagiannaki
        if self.last_approval_date > now:
1324 e1a80257 Sofia Papagiannaki
            return True
1325 e1a80257 Sofia Papagiannaki
        if self.terminaton_date > now:
1326 e1a80257 Sofia Papagiannaki
            return True
1327 e1a80257 Sofia Papagiannaki
        return False
1328 e1a80257 Sofia Papagiannaki
    
1329 e1a80257 Sofia Papagiannaki
    @property
1330 b22de10a Sofia Papagiannaki
    def is_synchronized(self):
1331 b22de10a Sofia Papagiannaki
        return self.last_application_synced == self.application and \
1332 b22de10a Sofia Papagiannaki
            not self.membership_dirty and \
1333 b22de10a Sofia Papagiannaki
            (not self.termination_start_date or termination_date)
1334 e1a80257 Sofia Papagiannaki
    
1335 b22de10a Sofia Papagiannaki
    @property
1336 b22de10a Sofia Papagiannaki
    def approved_members(self):
1337 0cc22d47 Sofia Papagiannaki
        return [m.person for m in self.projectmembership_set.filter(~Q(acceptance_date=None))]
1338 b22de10a Sofia Papagiannaki
        
1339 b22de10a Sofia Papagiannaki
    def sync(self, specific_members=()):
1340 ccab6eb5 Sofia Papagiannaki
        if self.is_synchronized:
1341 b22de10a Sofia Papagiannaki
            return
1342 b22de10a Sofia Papagiannaki
        members = specific_members or self.approved_members
1343 ccab6eb5 Sofia Papagiannaki
        c, rejected = send_quota(self.approved_members)
1344 e1a80257 Sofia Papagiannaki
        return rejected
1345 2a965273 Sofia Papagiannaki
    
1346 ccab6eb5 Sofia Papagiannaki
    def accept_member(self, user, request_user=None):
1347 2a965273 Sofia Papagiannaki
        if isinstance(user, int):
1348 2a965273 Sofia Papagiannaki
            user = _lookup_object(AstakosUser, id=user)
1349 2a965273 Sofia Papagiannaki
        if request_user and \
1350 2a965273 Sofia Papagiannaki
            (not self.owner == request_user and not request_user.is_superuser):
1351 2a965273 Sofia Papagiannaki
            raise Exception(_(astakos_messages.NOT_ALLOWED))
1352 2a965273 Sofia Papagiannaki
        if not self.is_alive:
1353 2a965273 Sofia Papagiannaki
            raise Exception(_(astakos_messages.NOT_ALIVE_PROJECT) % project.__dict__)
1354 ccab6eb5 Sofia Papagiannaki
        if self.definition.member_join_policy == 'closed':
1355 ccab6eb5 Sofia Papagiannaki
            raise Exception(_(astakos_messages.MEMBER_JOIN_POLICY_CLOSED))
1356 ccab6eb5 Sofia Papagiannaki
        if len(self.approved_members) + 1 > self.definition.limit_on_members_number:
1357 2a965273 Sofia Papagiannaki
            raise Exception(_(astakos_messages.MEMBER_NUMBER_LIMIT_REACHED))
1358 ccab6eb5 Sofia Papagiannaki
        m, created = ProjectMembership.objects.get_or_create(
1359 ccab6eb5 Sofia Papagiannaki
            person=user, project=self
1360 2a965273 Sofia Papagiannaki
        )
1361 0cc22d47 Sofia Papagiannaki
        m.accept()
1362 ccab6eb5 Sofia Papagiannaki
1363 ccab6eb5 Sofia Papagiannaki
    def reject_member(self, user, request_user=None):
1364 ccab6eb5 Sofia Papagiannaki
        if isinstance(user, int):
1365 2a965273 Sofia Papagiannaki
            user = _lookup_object(AstakosUser, id=user)
1366 2a965273 Sofia Papagiannaki
        if request_user and \
1367 2a965273 Sofia Papagiannaki
            (not self.owner == request_user and not request_user.is_superuser):
1368 2a965273 Sofia Papagiannaki
            raise Exception(_(astakos_messages.NOT_ALLOWED))
1369 2a965273 Sofia Papagiannaki
        if not self.is_alive:
1370 2a965273 Sofia Papagiannaki
            raise Exception(_(astakos_messages.NOT_ALIVE_PROJECT) % project.__dict__)
1371 ccab6eb5 Sofia Papagiannaki
        try:
1372 ccab6eb5 Sofia Papagiannaki
            m = ProjectMembership.objects.get(person=user, project=self)
1373 ccab6eb5 Sofia Papagiannaki
        except:
1374 ccab6eb5 Sofia Papagiannaki
            raise Exception(_(astakos_messages.NOT_MEMBERSHIP_REQUEST) % project.__dict__)
1375 b22de10a Sofia Papagiannaki
        else:
1376 ccab6eb5 Sofia Papagiannaki
            m.reject()
1377 b22de10a Sofia Papagiannaki
        
1378 ccab6eb5 Sofia Papagiannaki
    def remove_member(self, user, request_user=None):
1379 ccab6eb5 Sofia Papagiannaki
        if isinstance(user, int):
1380 ccab6eb5 Sofia Papagiannaki
            user = _lookup_object(AstakosUser, id=user)
1381 ccab6eb5 Sofia Papagiannaki
        if request_user and \
1382 ccab6eb5 Sofia Papagiannaki
            (not self.owner == request_user and not request_user.is_superuser):
1383 ccab6eb5 Sofia Papagiannaki
            raise Exception(_(astakos_messages.NOT_ALLOWED))
1384 ccab6eb5 Sofia Papagiannaki
        if not self.is_alive:
1385 ccab6eb5 Sofia Papagiannaki
            raise Exception(_(astakos_messages.NOT_ALIVE_PROJECT) % project.__dict__)
1386 ccab6eb5 Sofia Papagiannaki
        if self.definition.member_leave_policy == 'closed':
1387 ccab6eb5 Sofia Papagiannaki
            raise Exception(_(astakos_messages.MEMBER_LEAVE_POLICY_CLOSED))
1388 ccab6eb5 Sofia Papagiannaki
        try:
1389 ccab6eb5 Sofia Papagiannaki
            m = ProjectMembership.objects.get(person=user, project=self)
1390 ccab6eb5 Sofia Papagiannaki
        except:
1391 ccab6eb5 Sofia Papagiannaki
            raise Exception(_(astakos_messages.NOT_MEMBERSHIP_REQUEST) % project.__dict__)
1392 b22de10a Sofia Papagiannaki
        else:
1393 ccab6eb5 Sofia Papagiannaki
            m.remove()
1394 b22de10a Sofia Papagiannaki
    
1395 b22de10a Sofia Papagiannaki
    def terminate(self):
1396 b22de10a Sofia Papagiannaki
        self.termination_start_date = datetime.now()
1397 b22de10a Sofia Papagiannaki
        self.terminaton_date = None
1398 b22de10a Sofia Papagiannaki
        self.save()
1399 b22de10a Sofia Papagiannaki
        
1400 b22de10a Sofia Papagiannaki
        rejected = self.sync()
1401 b22de10a Sofia Papagiannaki
        if not rejected:
1402 b22de10a Sofia Papagiannaki
            self.termination_start_date = None
1403 b22de10a Sofia Papagiannaki
            self.terminaton_date = datetime.now()
1404 b22de10a Sofia Papagiannaki
            self.save()
1405 b22de10a Sofia Papagiannaki
            
1406 b22de10a Sofia Papagiannaki
            notification = build_notification(
1407 b22de10a Sofia Papagiannaki
                settings.SERVER_EMAIL,
1408 b22de10a Sofia Papagiannaki
                [self.application.owner.email],
1409 b22de10a Sofia Papagiannaki
                _('Project %(name)s has been terminated.') %  self.definition.__dict__,
1410 b22de10a Sofia Papagiannaki
                _('Project %(name)s has been terminated.') %  self.definition.__dict__
1411 b22de10a Sofia Papagiannaki
            )
1412 b22de10a Sofia Papagiannaki
            notification.send()
1413 b22de10a Sofia Papagiannaki
1414 b22de10a Sofia Papagiannaki
    def suspend(self):
1415 b22de10a Sofia Papagiannaki
        self.last_approval_date = None
1416 b22de10a Sofia Papagiannaki
        self.save()
1417 b22de10a Sofia Papagiannaki
        notification = build_notification(
1418 b22de10a Sofia Papagiannaki
            settings.SERVER_EMAIL,
1419 b22de10a Sofia Papagiannaki
            [self.application.owner.email],
1420 b22de10a Sofia Papagiannaki
            _('Project %(name)s has been suspended.') %  self.definition.__dict__,
1421 b22de10a Sofia Papagiannaki
            _('Project %(name)s has been suspended.') %  self.definition.__dict__
1422 b22de10a Sofia Papagiannaki
        )
1423 b22de10a Sofia Papagiannaki
        notification.send()
1424 b22de10a Sofia Papagiannaki
1425 0cc22d47 Sofia Papagiannaki
class ProjectMembership(models.Model):
1426 0cc22d47 Sofia Papagiannaki
    person = models.ForeignKey(AstakosUser)
1427 0cc22d47 Sofia Papagiannaki
    project = models.ForeignKey(Project)
1428 0cc22d47 Sofia Papagiannaki
    request_date = models.DateField(default=datetime.now())
1429 0cc22d47 Sofia Papagiannaki
    acceptance_date = models.DateField(null=True, db_index=True)
1430 2a965273 Sofia Papagiannaki
1431 0cc22d47 Sofia Papagiannaki
    class Meta:
1432 0cc22d47 Sofia Papagiannaki
        unique_together = ("person", "project")
1433 0cc22d47 Sofia Papagiannaki
    
1434 0cc22d47 Sofia Papagiannaki
    def accept(self):
1435 0cc22d47 Sofia Papagiannaki
        if self.acceptance_date:
1436 0cc22d47 Sofia Papagiannaki
            return
1437 0cc22d47 Sofia Papagiannaki
        self.acceptance_date = datetime.now()
1438 0cc22d47 Sofia Papagiannaki
        self.save()
1439 ccab6eb5 Sofia Papagiannaki
#         notification = build_notification(
1440 ccab6eb5 Sofia Papagiannaki
#             settings.SERVER_EMAIL,
1441 ccab6eb5 Sofia Papagiannaki
#             [self.person.email],
1442 ccab6eb5 Sofia Papagiannaki
#             _('Your membership on project %(name)s has been accepted.') % self.project.definition.__dict__,
1443 ccab6eb5 Sofia Papagiannaki
#             _('Your membership on project %(name)s has been accepted.') % self.project.definition.__dict__
1444 ccab6eb5 Sofia Papagiannaki
#         ).send()
1445 0cc22d47 Sofia Papagiannaki
        self.sync()
1446 0cc22d47 Sofia Papagiannaki
    
1447 0cc22d47 Sofia Papagiannaki
    def reject(self):
1448 0cc22d47 Sofia Papagiannaki
        history_item = ProjectMembershipHistory(
1449 0cc22d47 Sofia Papagiannaki
            person=self.person,
1450 0cc22d47 Sofia Papagiannaki
            project=self.project,
1451 0cc22d47 Sofia Papagiannaki
            request_date=self.request_date,
1452 0cc22d47 Sofia Papagiannaki
            rejection_date=datetime.now()
1453 0cc22d47 Sofia Papagiannaki
        ).save()
1454 0cc22d47 Sofia Papagiannaki
        self.delete()
1455 ccab6eb5 Sofia Papagiannaki
#         notification = build_notification(
1456 ccab6eb5 Sofia Papagiannaki
#             settings.SERVER_EMAIL,
1457 ccab6eb5 Sofia Papagiannaki
#             [self.person.email],
1458 ccab6eb5 Sofia Papagiannaki
#             _('Your membership on project %(name)s has been rejected.') % self.project.definition.__dict__,
1459 ccab6eb5 Sofia Papagiannaki
#             _('Your membership on project %(name)s has been rejected.') % self.project.definition.__dict__
1460 ccab6eb5 Sofia Papagiannaki
#         ).send()
1461 0cc22d47 Sofia Papagiannaki
    
1462 0cc22d47 Sofia Papagiannaki
    def remove(self):
1463 0cc22d47 Sofia Papagiannaki
        history_item = ProjectMembershipHistory(
1464 0cc22d47 Sofia Papagiannaki
            id=self.id,
1465 0cc22d47 Sofia Papagiannaki
            person=self.person,
1466 0cc22d47 Sofia Papagiannaki
            project=self.project,
1467 0cc22d47 Sofia Papagiannaki
            request_date=self.request_date,
1468 0cc22d47 Sofia Papagiannaki
            removal_date=datetime.now()
1469 0cc22d47 Sofia Papagiannaki
        ).save()
1470 0cc22d47 Sofia Papagiannaki
        self.delete()
1471 ccab6eb5 Sofia Papagiannaki
#         notification = build_notification(
1472 ccab6eb5 Sofia Papagiannaki
#             settings.SERVER_EMAIL,
1473 ccab6eb5 Sofia Papagiannaki
#             [self.person.email],
1474 ccab6eb5 Sofia Papagiannaki
#             _('Your membership on project %(name)s has been removed.') % self.project.definition.__dict__,
1475 ccab6eb5 Sofia Papagiannaki
#             _('Your membership on project %(name)s has been removed.') % self.project.definition.__dict__
1476 ccab6eb5 Sofia Papagiannaki
#         ).send()
1477 0cc22d47 Sofia Papagiannaki
        self.sync()
1478 0cc22d47 Sofia Papagiannaki
    
1479 0cc22d47 Sofia Papagiannaki
    def sync(self):
1480 0cc22d47 Sofia Papagiannaki
        # set membership_dirty flag
1481 0cc22d47 Sofia Papagiannaki
        self.project.membership_dirty = True
1482 0cc22d47 Sofia Papagiannaki
        self.project.save()
1483 0cc22d47 Sofia Papagiannaki
        
1484 ccab6eb5 Sofia Papagiannaki
        rejected = self.project.sync(specific_members=[self])
1485 0cc22d47 Sofia Papagiannaki
        if not rejected:
1486 0cc22d47 Sofia Papagiannaki
            # if syncing was successful unset membership_dirty flag
1487 0cc22d47 Sofia Papagiannaki
            self.membership_dirty = False
1488 0cc22d47 Sofia Papagiannaki
            self.save()
1489 0cc22d47 Sofia Papagiannaki
        
1490 e1a80257 Sofia Papagiannaki
1491 0cc22d47 Sofia Papagiannaki
class ProjectMembershipHistory(models.Model):
1492 e1a80257 Sofia Papagiannaki
    person = models.ForeignKey(AstakosUser)
1493 e1a80257 Sofia Papagiannaki
    project = models.ForeignKey(Project)
1494 0cc22d47 Sofia Papagiannaki
    request_date = models.DateField(default=datetime.now())
1495 0cc22d47 Sofia Papagiannaki
    removal_date = models.DateField(null=True)
1496 0cc22d47 Sofia Papagiannaki
    rejection_date = models.DateField(null=True)
1497 e1a80257 Sofia Papagiannaki
1498 e1a80257 Sofia Papagiannaki
    class Meta:
1499 e1a80257 Sofia Papagiannaki
        unique_together = ("person", "project")
1500 e1a80257 Sofia Papagiannaki
1501 0cc22d47 Sofia Papagiannaki
1502 e1a80257 Sofia Papagiannaki
def filter_queryset_by_property(q, property):
1503 e1a80257 Sofia Papagiannaki
    """
1504 e1a80257 Sofia Papagiannaki
    Incorporate list comprehension for filtering querysets by property
1505 e1a80257 Sofia Papagiannaki
    since Queryset.filter() operates on the database level.
1506 e1a80257 Sofia Papagiannaki
    """
1507 e1a80257 Sofia Papagiannaki
    return (p for p in q if getattr(p, property, False))
1508 e1a80257 Sofia Papagiannaki
1509 e1a80257 Sofia Papagiannaki
def get_alive_projects():
1510 e1a80257 Sofia Papagiannaki
    return filter_queryset_by_property(
1511 e1a80257 Sofia Papagiannaki
        Project.objects.all(),
1512 e1a80257 Sofia Papagiannaki
        'is_alive'
1513 e1a80257 Sofia Papagiannaki
    )
1514 e1a80257 Sofia Papagiannaki
1515 e1a80257 Sofia Papagiannaki
def get_active_projects():
1516 e1a80257 Sofia Papagiannaki
    return filter_queryset_by_property(
1517 e1a80257 Sofia Papagiannaki
        Project.objects.all(),
1518 e1a80257 Sofia Papagiannaki
        'is_active'
1519 e1a80257 Sofia Papagiannaki
    )
1520 e1a80257 Sofia Papagiannaki
1521 e1a80257 Sofia Papagiannaki
def _lookup_object(model, **kwargs):
1522 e1a80257 Sofia Papagiannaki
    """
1523 e1a80257 Sofia Papagiannaki
    Returns an object of the specific model matching the given lookup
1524 e1a80257 Sofia Papagiannaki
    parameters.
1525 e1a80257 Sofia Papagiannaki
    """
1526 e1a80257 Sofia Papagiannaki
    if not kwargs:
1527 e1a80257 Sofia Papagiannaki
        raise MissingIdentifier
1528 ccab6eb5 Sofia Papagiannaki
    return model.objects.get(**kwargs)
1529 e1a80257 Sofia Papagiannaki
1530 e1a80257 Sofia Papagiannaki
def _create_object(model, **kwargs):
1531 e1a80257 Sofia Papagiannaki
    o = model.objects.create(**kwargs)
1532 e1a80257 Sofia Papagiannaki
    o.save()
1533 e1a80257 Sofia Papagiannaki
    return o
1534 e1a80257 Sofia Papagiannaki
1535 e1a80257 Sofia Papagiannaki
def _update_object(model, id, save=True, **kwargs):
1536 e1a80257 Sofia Papagiannaki
    o = self._lookup_object(model, id=id)
1537 e1a80257 Sofia Papagiannaki
    if kwargs:
1538 e1a80257 Sofia Papagiannaki
        o.__dict__.update(kwargs)
1539 e1a80257 Sofia Papagiannaki
    if save:
1540 e1a80257 Sofia Papagiannaki
        o.save()
1541 e1a80257 Sofia Papagiannaki
    return o
1542 e1a80257 Sofia Papagiannaki
1543 0cc22d47 Sofia Papagiannaki
# def list_applications():
1544 0cc22d47 Sofia Papagiannaki
#     return ProjectApplication.objects.all()
1545 0cc22d47 Sofia Papagiannaki
# 
1546 0cc22d47 Sofia Papagiannaki
# 
1547 0cc22d47 Sofia Papagiannaki
# def list_projects(filter_property=None):
1548 0cc22d47 Sofia Papagiannaki
#     if filter_property:
1549 0cc22d47 Sofia Papagiannaki
#         return filter_queryset_by_property(
1550 0cc22d47 Sofia Papagiannaki
#             Project.objects.all(),
1551 0cc22d47 Sofia Papagiannaki
#             filter_property
1552 0cc22d47 Sofia Papagiannaki
#         )
1553 0cc22d47 Sofia Papagiannaki
#     return Project.objects.all()
1554 0cc22d47 Sofia Papagiannaki
# 
1555 0cc22d47 Sofia Papagiannaki
# 
1556 ccab6eb5 Sofia Papagiannaki
# def synchonize_project(id):
1557 ccab6eb5 Sofia Papagiannaki
#     project = _lookup_object(Project, id=id)
1558 0cc22d47 Sofia Papagiannaki
#     return project.sync()
1559 b22de10a Sofia Papagiannaki
1560 b22de10a Sofia Papagiannaki
1561 ff9290ec Sofia Papagiannaki
def create_astakos_user(u):
1562 ff9290ec Sofia Papagiannaki
    try:
1563 ff9290ec Sofia Papagiannaki
        AstakosUser.objects.get(user_ptr=u.pk)
1564 ff9290ec Sofia Papagiannaki
    except AstakosUser.DoesNotExist:
1565 ff9290ec Sofia Papagiannaki
        extended_user = AstakosUser(user_ptr_id=u.pk)
1566 ff9290ec Sofia Papagiannaki
        extended_user.__dict__.update(u.__dict__)
1567 ff9290ec Sofia Papagiannaki
        extended_user.save()
1568 67be1883 Olga Brani
        if not extended_user.has_auth_provider('local'):
1569 67be1883 Olga Brani
            extended_user.add_auth_provider('local')
1570 fc1e2f02 Sofia Papagiannaki
    except BaseException, e:
1571 fc1e2f02 Sofia Papagiannaki
        logger.exception(e)
1572 ff9290ec Sofia Papagiannaki
1573 5ce3ce4f Sofia Papagiannaki
1574 fc1e2f02 Sofia Papagiannaki
def fix_superusers(sender, **kwargs):
1575 fc1e2f02 Sofia Papagiannaki
    # Associate superusers with AstakosUser
1576 ff9290ec Sofia Papagiannaki
    admins = User.objects.filter(is_superuser=True)
1577 ff9290ec Sofia Papagiannaki
    for u in admins:
1578 ff9290ec Sofia Papagiannaki
        create_astakos_user(u)
1579 ff9290ec Sofia Papagiannaki
1580 ff9290ec Sofia Papagiannaki
1581 fc1e2f02 Sofia Papagiannaki
def user_post_save(sender, instance, created, **kwargs):
1582 aa4109d4 Sofia Papagiannaki
    if not created:
1583 aa4109d4 Sofia Papagiannaki
        return
1584 fc1e2f02 Sofia Papagiannaki
    create_astakos_user(instance)
1585 fc1e2f02 Sofia Papagiannaki
1586 5ce3ce4f Sofia Papagiannaki
1587 fc1e2f02 Sofia Papagiannaki
def set_default_group(user):
1588 aa4109d4 Sofia Papagiannaki
    try:
1589 5ce3ce4f Sofia Papagiannaki
        default = AstakosGroup.objects.get(name='default')
1590 5ce3ce4f Sofia Papagiannaki
        Membership(
1591 5ce3ce4f Sofia Papagiannaki
            group=default, person=user, date_joined=datetime.now()).save()
1592 aa4109d4 Sofia Papagiannaki
    except AstakosGroup.DoesNotExist, e:
1593 aa4109d4 Sofia Papagiannaki
        logger.exception(e)
1594 ff9290ec Sofia Papagiannaki
1595 bf0c6de5 Sofia Papagiannaki
1596 fc1e2f02 Sofia Papagiannaki
def astakosuser_pre_save(sender, instance, **kwargs):
1597 fc1e2f02 Sofia Papagiannaki
    instance.aquarium_report = False
1598 fc1e2f02 Sofia Papagiannaki
    instance.new = False
1599 fc1e2f02 Sofia Papagiannaki
    try:
1600 5ce3ce4f Sofia Papagiannaki
        db_instance = AstakosUser.objects.get(id=instance.id)
1601 fc1e2f02 Sofia Papagiannaki
    except AstakosUser.DoesNotExist:
1602 fc1e2f02 Sofia Papagiannaki
        # create event
1603 fc1e2f02 Sofia Papagiannaki
        instance.aquarium_report = True
1604 fc1e2f02 Sofia Papagiannaki
        instance.new = True
1605 fc1e2f02 Sofia Papagiannaki
    else:
1606 fc1e2f02 Sofia Papagiannaki
        get = AstakosUser.__getattribute__
1607 fc1e2f02 Sofia Papagiannaki
        l = filter(lambda f: get(db_instance, f) != get(instance, f),
1608 9a06d96f Olga Brani
                   BILLING_FIELDS)
1609 fc1e2f02 Sofia Papagiannaki
        instance.aquarium_report = True if l else False
1610 fc1e2f02 Sofia Papagiannaki
1611 5ce3ce4f Sofia Papagiannaki
1612 fc1e2f02 Sofia Papagiannaki
def astakosuser_post_save(sender, instance, created, **kwargs):
1613 fc1e2f02 Sofia Papagiannaki
    if instance.aquarium_report:
1614 fc1e2f02 Sofia Papagiannaki
        report_user_event(instance, create=instance.new)
1615 fc1e2f02 Sofia Papagiannaki
    if not created:
1616 fc1e2f02 Sofia Papagiannaki
        return
1617 fc1e2f02 Sofia Papagiannaki
    set_default_group(instance)
1618 fc1e2f02 Sofia Papagiannaki
    # TODO handle socket.error & IOError
1619 fc1e2f02 Sofia Papagiannaki
    register_users((instance,))
1620 fc1e2f02 Sofia Papagiannaki
1621 5ce3ce4f Sofia Papagiannaki
1622 bd4f356c Sofia Papagiannaki
def resource_post_save(sender, instance, created, **kwargs):
1623 bd4f356c Sofia Papagiannaki
    if not created:
1624 bd4f356c Sofia Papagiannaki
        return
1625 bd4f356c Sofia Papagiannaki
    register_resources((instance,))
1626 bd4f356c Sofia Papagiannaki
1627 bd4f356c Sofia Papagiannaki
1628 fc1e2f02 Sofia Papagiannaki
def send_quota_disturbed(sender, instance, **kwargs):
1629 fc1e2f02 Sofia Papagiannaki
    users = []
1630 fc1e2f02 Sofia Papagiannaki
    extend = users.extend
1631 fc1e2f02 Sofia Papagiannaki
    if sender == Membership:
1632 fc1e2f02 Sofia Papagiannaki
        if not instance.group.is_enabled:
1633 fc1e2f02 Sofia Papagiannaki
            return
1634 fc1e2f02 Sofia Papagiannaki
        extend([instance.person])
1635 fc1e2f02 Sofia Papagiannaki
    elif sender == AstakosUserQuota:
1636 fc1e2f02 Sofia Papagiannaki
        extend([instance.user])
1637 fc1e2f02 Sofia Papagiannaki
    elif sender == AstakosGroupQuota:
1638 fc1e2f02 Sofia Papagiannaki
        if not instance.group.is_enabled:
1639 fc1e2f02 Sofia Papagiannaki
            return
1640 fc1e2f02 Sofia Papagiannaki
        extend(instance.group.astakosuser_set.all())
1641 fc1e2f02 Sofia Papagiannaki
    elif sender == AstakosGroup:
1642 fc1e2f02 Sofia Papagiannaki
        if not instance.is_enabled:
1643 fc1e2f02 Sofia Papagiannaki
            return
1644 fc1e2f02 Sofia Papagiannaki
    quota_disturbed.send(sender=sender, users=users)
1645 fc1e2f02 Sofia Papagiannaki
1646 5ce3ce4f Sofia Papagiannaki
1647 fc1e2f02 Sofia Papagiannaki
def on_quota_disturbed(sender, users, **kwargs):
1648 c0b26605 Sofia Papagiannaki
#     print '>>>', locals()
1649 fc1e2f02 Sofia Papagiannaki
    if not users:
1650 fc1e2f02 Sofia Papagiannaki
        return
1651 fc1e2f02 Sofia Papagiannaki
    send_quota(users)
1652 bf0c6de5 Sofia Papagiannaki
1653 bf0c6de5 Sofia Papagiannaki
def renew_token(sender, instance, **kwargs):
1654 f9aea9c8 Sofia Papagiannaki
    if not instance.auth_token:
1655 bf0c6de5 Sofia Papagiannaki
        instance.renew_token()
1656 bf0c6de5 Sofia Papagiannaki
1657 fc1e2f02 Sofia Papagiannaki
post_syncdb.connect(fix_superusers)
1658 fc1e2f02 Sofia Papagiannaki
post_save.connect(user_post_save, sender=User)
1659 fc1e2f02 Sofia Papagiannaki
pre_save.connect(astakosuser_pre_save, sender=AstakosUser)
1660 fc1e2f02 Sofia Papagiannaki
post_save.connect(astakosuser_post_save, sender=AstakosUser)
1661 bd4f356c Sofia Papagiannaki
post_save.connect(resource_post_save, sender=Resource)
1662 fc1e2f02 Sofia Papagiannaki
1663 fc1e2f02 Sofia Papagiannaki
quota_disturbed = Signal(providing_args=["users"])
1664 fc1e2f02 Sofia Papagiannaki
quota_disturbed.connect(on_quota_disturbed)
1665 fc1e2f02 Sofia Papagiannaki
1666 fc1e2f02 Sofia Papagiannaki
post_delete.connect(send_quota_disturbed, sender=AstakosGroup)
1667 fc1e2f02 Sofia Papagiannaki
post_delete.connect(send_quota_disturbed, sender=Membership)
1668 fc1e2f02 Sofia Papagiannaki
post_save.connect(send_quota_disturbed, sender=AstakosUserQuota)
1669 fc1e2f02 Sofia Papagiannaki
post_delete.connect(send_quota_disturbed, sender=AstakosUserQuota)
1670 fc1e2f02 Sofia Papagiannaki
post_save.connect(send_quota_disturbed, sender=AstakosGroupQuota)
1671 a4075f5a root
post_delete.connect(send_quota_disturbed, sender=AstakosGroupQuota)
1672 c0b26605 Sofia Papagiannaki
1673 bf0c6de5 Sofia Papagiannaki
pre_save.connect(renew_token, sender=AstakosUser)
1674 2e90e3ec Kostas Papadimitriou
pre_save.connect(renew_token, sender=Service)