Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / models.py @ 9f01cf1d

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

895 49790d9d Sofia Papagiannaki
        If the key is valid and has not expired, return the ``User``
896 49790d9d Sofia Papagiannaki
        after activating.
897 49790d9d Sofia Papagiannaki

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

900 49790d9d Sofia Papagiannaki
        If the key is valid but the ``User`` is already active,
901 49790d9d Sofia Papagiannaki
        return ``None``.
902 49790d9d Sofia Papagiannaki

903 49790d9d Sofia Papagiannaki
        After successful email change the activation record is deleted.
904 49790d9d Sofia Papagiannaki

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