Statistics
| Branch: | Tag: | Revision:

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

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

915 49790d9d Sofia Papagiannaki
        If the key is valid and has not expired, return the ``User``
916 49790d9d Sofia Papagiannaki
        after activating.
917 49790d9d Sofia Papagiannaki

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

920 49790d9d Sofia Papagiannaki
        If the key is valid but the ``User`` is already active,
921 49790d9d Sofia Papagiannaki
        return ``None``.
922 49790d9d Sofia Papagiannaki

923 49790d9d Sofia Papagiannaki
        After successful email change the activation record is deleted.
924 49790d9d Sofia Papagiannaki

925 49790d9d Sofia Papagiannaki
        Throws ValueError if there is already
926 49790d9d Sofia Papagiannaki
        """
927 49790d9d Sofia Papagiannaki
        try:
928 5ce3ce4f Sofia Papagiannaki
            email_change = self.model.objects.get(
929 5ce3ce4f Sofia Papagiannaki
                activation_key=activation_key)
930 49790d9d Sofia Papagiannaki
            if email_change.activation_key_expired():
931 49790d9d Sofia Papagiannaki
                email_change.delete()
932 49790d9d Sofia Papagiannaki
                raise EmailChange.DoesNotExist
933 49790d9d Sofia Papagiannaki
            # is there an active user with this address?
934 49790d9d Sofia Papagiannaki
            try:
935 789a5951 Sofia Papagiannaki
                AstakosUser.objects.get(email__iexact=email_change.new_email_address)
936 49790d9d Sofia Papagiannaki
            except AstakosUser.DoesNotExist:
937 49790d9d Sofia Papagiannaki
                pass
938 49790d9d Sofia Papagiannaki
            else:
939 ae497612 Olga Brani
                raise ValueError(_(astakos_messages.NEW_EMAIL_ADDR_RESERVED))
940 49790d9d Sofia Papagiannaki
            # update user
941 49790d9d Sofia Papagiannaki
            user = AstakosUser.objects.get(pk=email_change.user_id)
942 49790d9d Sofia Papagiannaki
            user.email = email_change.new_email_address
943 49790d9d Sofia Papagiannaki
            user.save()
944 49790d9d Sofia Papagiannaki
            email_change.delete()
945 49790d9d Sofia Papagiannaki
            return user
946 49790d9d Sofia Papagiannaki
        except EmailChange.DoesNotExist:
947 ae497612 Olga Brani
            raise ValueError(_(astakos_messages.INVALID_ACTIVATION_KEY))
948 49790d9d Sofia Papagiannaki
949 49790d9d Sofia Papagiannaki
950 49790d9d Sofia Papagiannaki
class EmailChange(models.Model):
951 9a06d96f Olga Brani
    new_email_address = models.EmailField(_(u'new e-mail address'),
952 ae497612 Olga Brani
                                          help_text=_(astakos_messages.EMAIL_CHANGE_NEW_ADDR_HELP))
953 5ce3ce4f Sofia Papagiannaki
    user = models.ForeignKey(
954 5ce3ce4f Sofia Papagiannaki
        AstakosUser, unique=True, related_name='emailchange_user')
955 49790d9d Sofia Papagiannaki
    requested_at = models.DateTimeField(default=datetime.now())
956 5ce3ce4f Sofia Papagiannaki
    activation_key = models.CharField(
957 5ce3ce4f Sofia Papagiannaki
        max_length=40, unique=True, db_index=True)
958 49790d9d Sofia Papagiannaki
959 49790d9d Sofia Papagiannaki
    objects = EmailChangeManager()
960 49790d9d Sofia Papagiannaki
961 49790d9d Sofia Papagiannaki
    def activation_key_expired(self):
962 49790d9d Sofia Papagiannaki
        expiration_date = timedelta(days=EMAILCHANGE_ACTIVATION_DAYS)
963 ff9290ec Sofia Papagiannaki
        return self.requested_at + expiration_date < datetime.now()
964 ff9290ec Sofia Papagiannaki
965 6b03a847 Sofia Papagiannaki
966 ca828a10 Sofia Papagiannaki
class AdditionalMail(models.Model):
967 ca828a10 Sofia Papagiannaki
    """
968 ca828a10 Sofia Papagiannaki
    Model for registring invitations
969 ca828a10 Sofia Papagiannaki
    """
970 ca828a10 Sofia Papagiannaki
    owner = models.ForeignKey(AstakosUser)
971 1eec103a Sofia Papagiannaki
    email = models.EmailField()
972 ca828a10 Sofia Papagiannaki
973 5ce3ce4f Sofia Papagiannaki
974 fc1e2f02 Sofia Papagiannaki
def _generate_invitation_code():
975 fc1e2f02 Sofia Papagiannaki
    while True:
976 5ce3ce4f Sofia Papagiannaki
        code = randint(1, 2L ** 63 - 1)
977 fc1e2f02 Sofia Papagiannaki
        try:
978 fc1e2f02 Sofia Papagiannaki
            Invitation.objects.get(code=code)
979 fc1e2f02 Sofia Papagiannaki
            # An invitation with this code already exists, try again
980 fc1e2f02 Sofia Papagiannaki
        except Invitation.DoesNotExist:
981 fc1e2f02 Sofia Papagiannaki
            return code
982 fc1e2f02 Sofia Papagiannaki
983 5ce3ce4f Sofia Papagiannaki
984 fc1e2f02 Sofia Papagiannaki
def get_latest_terms():
985 fc1e2f02 Sofia Papagiannaki
    try:
986 fc1e2f02 Sofia Papagiannaki
        term = ApprovalTerms.objects.order_by('-id')[0]
987 fc1e2f02 Sofia Papagiannaki
        return term
988 fc1e2f02 Sofia Papagiannaki
    except IndexError:
989 fc1e2f02 Sofia Papagiannaki
        pass
990 fc1e2f02 Sofia Papagiannaki
    return None
991 fc1e2f02 Sofia Papagiannaki
992 ef20ea07 Sofia Papagiannaki
class PendingThirdPartyUser(models.Model):
993 ef20ea07 Sofia Papagiannaki
    """
994 ef20ea07 Sofia Papagiannaki
    Model for registring successful third party user authentications
995 ef20ea07 Sofia Papagiannaki
    """
996 e1a80257 Sofia Papagiannaki
    third_party_identifier = models.CharField(_('Third-party identifier'), max_length=255, null=True, blank=True)
997 e1a80257 Sofia Papagiannaki
    provider = models.CharField(_('Provider'), max_length=255, blank=True)
998 678b2236 Sofia Papagiannaki
    email = models.EmailField(_('e-mail address'), blank=True, null=True)
999 ef20ea07 Sofia Papagiannaki
    first_name = models.CharField(_('first name'), max_length=30, blank=True)
1000 ef20ea07 Sofia Papagiannaki
    last_name = models.CharField(_('last name'), max_length=30, blank=True)
1001 ef20ea07 Sofia Papagiannaki
    affiliation = models.CharField('Affiliation', max_length=255, blank=True)
1002 ef20ea07 Sofia Papagiannaki
    username = models.CharField(_('username'), max_length=30, unique=True, help_text=_("Required. 30 characters or fewer. Letters, numbers and @/./+/-/_ characters"))
1003 e1a80257 Sofia Papagiannaki
    token = models.CharField(_('Token'), max_length=255, null=True, blank=True)
1004 d2633501 Kostas Papadimitriou
    created = models.DateTimeField(auto_now_add=True, null=True, blank=True)
1005 d2633501 Kostas Papadimitriou
1006 678b2236 Sofia Papagiannaki
    class Meta:
1007 678b2236 Sofia Papagiannaki
        unique_together = ("provider", "third_party_identifier")
1008 ef20ea07 Sofia Papagiannaki
1009 ef20ea07 Sofia Papagiannaki
    @property
1010 ef20ea07 Sofia Papagiannaki
    def realname(self):
1011 ef20ea07 Sofia Papagiannaki
        return '%s %s' %(self.first_name, self.last_name)
1012 ef20ea07 Sofia Papagiannaki
1013 ef20ea07 Sofia Papagiannaki
    @realname.setter
1014 ef20ea07 Sofia Papagiannaki
    def realname(self, value):
1015 ef20ea07 Sofia Papagiannaki
        parts = value.split(' ')
1016 ef20ea07 Sofia Papagiannaki
        if len(parts) == 2:
1017 ef20ea07 Sofia Papagiannaki
            self.first_name = parts[0]
1018 ef20ea07 Sofia Papagiannaki
            self.last_name = parts[1]
1019 ef20ea07 Sofia Papagiannaki
        else:
1020 ef20ea07 Sofia Papagiannaki
            self.last_name = parts[0]
1021 2e90e3ec Kostas Papadimitriou
1022 ef20ea07 Sofia Papagiannaki
    def save(self, **kwargs):
1023 ef20ea07 Sofia Papagiannaki
        if not self.id:
1024 ef20ea07 Sofia Papagiannaki
            # set username
1025 ef20ea07 Sofia Papagiannaki
            while not self.username:
1026 ef20ea07 Sofia Papagiannaki
                username =  uuid.uuid4().hex[:30]
1027 ef20ea07 Sofia Papagiannaki
                try:
1028 ef20ea07 Sofia Papagiannaki
                    AstakosUser.objects.get(username = username)
1029 ef20ea07 Sofia Papagiannaki
                except AstakosUser.DoesNotExist, e:
1030 ef20ea07 Sofia Papagiannaki
                    self.username = username
1031 ef20ea07 Sofia Papagiannaki
        super(PendingThirdPartyUser, self).save(**kwargs)
1032 ef20ea07 Sofia Papagiannaki
1033 d2633501 Kostas Papadimitriou
    def generate_token(self):
1034 d2633501 Kostas Papadimitriou
        self.password = self.third_party_identifier
1035 d2633501 Kostas Papadimitriou
        self.last_login = datetime.now()
1036 d2633501 Kostas Papadimitriou
        self.token = default_token_generator.make_token(self)
1037 d2633501 Kostas Papadimitriou
1038 bf0c6de5 Sofia Papagiannaki
class SessionCatalog(models.Model):
1039 bf0c6de5 Sofia Papagiannaki
    session_key = models.CharField(_('session key'), max_length=40)
1040 bf0c6de5 Sofia Papagiannaki
    user = models.ForeignKey(AstakosUser, related_name='sessions', null=True)
1041 bf0c6de5 Sofia Papagiannaki
1042 ccab6eb5 Sofia Papagiannaki
class MemberJoinPolicy(models.Model):
1043 e1a80257 Sofia Papagiannaki
    policy = models.CharField(_('Policy'), max_length=255, unique=True, db_index=True)
1044 e1a80257 Sofia Papagiannaki
    description = models.CharField(_('Description'), max_length=80)
1045 e1a80257 Sofia Papagiannaki
1046 e1a80257 Sofia Papagiannaki
    def __str__(self):
1047 e1a80257 Sofia Papagiannaki
        return self.policy
1048 e1a80257 Sofia Papagiannaki
1049 ccab6eb5 Sofia Papagiannaki
class MemberLeavePolicy(models.Model):
1050 b22de10a Sofia Papagiannaki
    policy = models.CharField(_('Policy'), max_length=255, unique=True, db_index=True)
1051 b22de10a Sofia Papagiannaki
    description = models.CharField(_('Description'), max_length=80)
1052 b22de10a Sofia Papagiannaki
1053 b22de10a Sofia Papagiannaki
    def __str__(self):
1054 b22de10a Sofia Papagiannaki
        return self.policy
1055 b22de10a Sofia Papagiannaki
1056 ccab6eb5 Sofia Papagiannaki
_auto_accept_join = False
1057 ccab6eb5 Sofia Papagiannaki
def get_auto_accept_join():
1058 bfe23b13 Sofia Papagiannaki
    global _auto_accept_join
1059 bfe23b13 Sofia Papagiannaki
    if _auto_accept_join is not False:
1060 bfe23b13 Sofia Papagiannaki
        return _auto_accept_join
1061 e65c21df Georgios D. Tsoukalas
    try:
1062 ccab6eb5 Sofia Papagiannaki
        auto_accept = MemberJoinPolicy.objects.get(policy='auto_accept')
1063 ccab6eb5 Sofia Papagiannaki
    except:
1064 ccab6eb5 Sofia Papagiannaki
        auto_accept = None
1065 bfe23b13 Sofia Papagiannaki
    _auto_accept_join = auto_accept
1066 ccab6eb5 Sofia Papagiannaki
    return auto_accept
1067 ccab6eb5 Sofia Papagiannaki
1068 bfe23b13 Sofia Papagiannaki
_closed_join = False
1069 bfe23b13 Sofia Papagiannaki
def get_closed_join():
1070 bfe23b13 Sofia Papagiannaki
    global _closed_join
1071 bfe23b13 Sofia Papagiannaki
    if _closed_join is not False:
1072 bfe23b13 Sofia Papagiannaki
        return _closed_join
1073 bfe23b13 Sofia Papagiannaki
    try:
1074 bfe23b13 Sofia Papagiannaki
        closed = MemberJoinPolicy.objects.get(policy='closed')
1075 bfe23b13 Sofia Papagiannaki
    except:
1076 bfe23b13 Sofia Papagiannaki
        closed = None
1077 bfe23b13 Sofia Papagiannaki
    _closed_join = closed
1078 bfe23b13 Sofia Papagiannaki
    return closed
1079 bfe23b13 Sofia Papagiannaki
1080 ccab6eb5 Sofia Papagiannaki
_auto_accept_leave = False
1081 ccab6eb5 Sofia Papagiannaki
def get_auto_accept_leave():
1082 bfe23b13 Sofia Papagiannaki
    global _auto_accept_leave
1083 bfe23b13 Sofia Papagiannaki
    if _auto_accept_leave is not False:
1084 bfe23b13 Sofia Papagiannaki
        return _auto_accept_leave
1085 ccab6eb5 Sofia Papagiannaki
    try:
1086 ccab6eb5 Sofia Papagiannaki
        auto_accept = MemberLeavePolicy.objects.get(policy='auto_accept')
1087 e65c21df Georgios D. Tsoukalas
    except:
1088 e65c21df Georgios D. Tsoukalas
        auto_accept = None
1089 bfe23b13 Sofia Papagiannaki
    _auto_accept_leave = auto_accept
1090 e65c21df Georgios D. Tsoukalas
    return auto_accept
1091 e1a80257 Sofia Papagiannaki
1092 bfe23b13 Sofia Papagiannaki
_closed_leave = False
1093 bfe23b13 Sofia Papagiannaki
def get_closed_leave():
1094 bfe23b13 Sofia Papagiannaki
    global _closed_leave
1095 bfe23b13 Sofia Papagiannaki
    if _closed_leave is not False:
1096 bfe23b13 Sofia Papagiannaki
        return _closed_leave
1097 bfe23b13 Sofia Papagiannaki
    try:
1098 bfe23b13 Sofia Papagiannaki
        closed = MemberLeavePolicy.objects.get(policy='closed')
1099 bfe23b13 Sofia Papagiannaki
    except:
1100 bfe23b13 Sofia Papagiannaki
        closed = None
1101 bfe23b13 Sofia Papagiannaki
    _closed_leave = closed
1102 bfe23b13 Sofia Papagiannaki
    return closeds
1103 bfe23b13 Sofia Papagiannaki
1104 e1a80257 Sofia Papagiannaki
class ProjectDefinition(models.Model):
1105 e1a80257 Sofia Papagiannaki
    name = models.CharField(max_length=80)
1106 e1a80257 Sofia Papagiannaki
    homepage = models.URLField(max_length=255, null=True, blank=True)
1107 e1a80257 Sofia Papagiannaki
    description = models.TextField(null=True)
1108 e1a80257 Sofia Papagiannaki
    start_date = models.DateTimeField()
1109 e1a80257 Sofia Papagiannaki
    end_date = models.DateTimeField()
1110 ccab6eb5 Sofia Papagiannaki
    member_join_policy = models.ForeignKey(MemberJoinPolicy)
1111 ccab6eb5 Sofia Papagiannaki
    member_leave_policy = models.ForeignKey(MemberLeavePolicy)
1112 e1a80257 Sofia Papagiannaki
    limit_on_members_number = models.PositiveIntegerField(null=True,blank=True)
1113 e1a80257 Sofia Papagiannaki
    resource_grants = models.ManyToManyField(
1114 e1a80257 Sofia Papagiannaki
        Resource,
1115 e1a80257 Sofia Papagiannaki
        null=True,
1116 e1a80257 Sofia Papagiannaki
        blank=True,
1117 e1a80257 Sofia Papagiannaki
        through='ProjectResourceGrant'
1118 e1a80257 Sofia Papagiannaki
    )
1119 e1a80257 Sofia Papagiannaki
    
1120 e1a80257 Sofia Papagiannaki
    @property
1121 e1a80257 Sofia Papagiannaki
    def violated_resource_grants(self):
1122 e1a80257 Sofia Papagiannaki
        return False
1123 e1a80257 Sofia Papagiannaki
    
1124 e1a80257 Sofia Papagiannaki
    def add_resource_policy(self, service, resource, uplimit, update=True):
1125 e1a80257 Sofia Papagiannaki
        """Raises ObjectDoesNotExist, IntegrityError"""
1126 e1a80257 Sofia Papagiannaki
        resource = Resource.objects.get(service__name=service, name=resource)
1127 e1a80257 Sofia Papagiannaki
        if update:
1128 f3342849 Sofia Papagiannaki
            ProjectResourceGrant.objects.update_or_create(
1129 f3342849 Sofia Papagiannaki
                project_definition=self,
1130 e1a80257 Sofia Papagiannaki
                resource=resource,
1131 f3342849 Sofia Papagiannaki
                defaults={'member_limit': uplimit}
1132 e1a80257 Sofia Papagiannaki
            )
1133 e1a80257 Sofia Papagiannaki
        else:
1134 f3342849 Sofia Papagiannaki
            q = self.projectresourcegrant_set
1135 f3342849 Sofia Papagiannaki
            q.create(resource=resource, member_limit=uplimit)
1136 e1a80257 Sofia Papagiannaki
1137 e1a80257 Sofia Papagiannaki
    @property
1138 e1a80257 Sofia Papagiannaki
    def resource_policies(self):
1139 f3342849 Sofia Papagiannaki
        return self.projectresourcegrant_set.all()
1140 e1a80257 Sofia Papagiannaki
1141 e1a80257 Sofia Papagiannaki
    @resource_policies.setter
1142 e1a80257 Sofia Papagiannaki
    def resource_policies(self, policies):
1143 e1a80257 Sofia Papagiannaki
        for p in policies:
1144 e1a80257 Sofia Papagiannaki
            service = p.get('service', None)
1145 e1a80257 Sofia Papagiannaki
            resource = p.get('resource', None)
1146 e1a80257 Sofia Papagiannaki
            uplimit = p.get('uplimit', 0)
1147 e1a80257 Sofia Papagiannaki
            update = p.get('update', True)
1148 e1a80257 Sofia Papagiannaki
            self.add_resource_policy(service, resource, uplimit, update)
1149 0cc22d47 Sofia Papagiannaki
    
1150 0cc22d47 Sofia Papagiannaki
    def validate_name(self):
1151 0cc22d47 Sofia Papagiannaki
        """
1152 0cc22d47 Sofia Papagiannaki
        Validate name uniqueness among all active projects.
1153 0cc22d47 Sofia Papagiannaki
        """
1154 529f3c49 Sofia Papagiannaki
        q = list(get_alive_projects())
1155 529f3c49 Sofia Papagiannaki
        q = filter(lambda p: p.definition.name == self.name , q)
1156 529f3c49 Sofia Papagiannaki
        q = filter(lambda p: p.application.id != self.projectapplication.id, q)
1157 529f3c49 Sofia Papagiannaki
        if self.projectapplication.precursor_application:
1158 529f3c49 Sofia Papagiannaki
            q = filter(lambda p: p.application.id != \
1159 529f3c49 Sofia Papagiannaki
                self.projectapplication.precursor_application.id, q)
1160 0cc22d47 Sofia Papagiannaki
        if q:
1161 0cc22d47 Sofia Papagiannaki
            raise ValidationError(
1162 185b2190 Sofia Papagiannaki
                _(astakos_messages.UNIQUE_PROJECT_NAME_CONSTRAIN_ERR)
1163 0cc22d47 Sofia Papagiannaki
            )
1164 0cc22d47 Sofia Papagiannaki
1165 e1a80257 Sofia Papagiannaki
1166 e1a80257 Sofia Papagiannaki
class ProjectResourceGrant(models.Model):
1167 e1a80257 Sofia Papagiannaki
    objects = ExtendedManager()
1168 e1a80257 Sofia Papagiannaki
    member_limit = models.BigIntegerField(null=True)
1169 e1a80257 Sofia Papagiannaki
    project_limit = models.BigIntegerField(null=True)
1170 e1a80257 Sofia Papagiannaki
    resource = models.ForeignKey(Resource)
1171 e1a80257 Sofia Papagiannaki
    project_definition = models.ForeignKey(ProjectDefinition, blank=True)
1172 e1a80257 Sofia Papagiannaki
1173 e1a80257 Sofia Papagiannaki
    class Meta:
1174 e1a80257 Sofia Papagiannaki
        unique_together = ("resource", "project_definition")
1175 e1a80257 Sofia Papagiannaki
1176 bfe23b13 Sofia Papagiannaki
1177 e1a80257 Sofia Papagiannaki
class ProjectApplication(models.Model):
1178 bfe23b13 Sofia Papagiannaki
    states_list = [PENDING, APPROVED, REPLACED, UNKNOWN]
1179 ccab6eb5 Sofia Papagiannaki
    states = dict((k, v) for k, v in enumerate(states_list))
1180 ccab6eb5 Sofia Papagiannaki
1181 b22de10a Sofia Papagiannaki
    applicant = models.ForeignKey(
1182 b22de10a Sofia Papagiannaki
        AstakosUser,
1183 b22de10a Sofia Papagiannaki
        related_name='my_project_applications',
1184 b22de10a Sofia Papagiannaki
        db_index=True)
1185 b22de10a Sofia Papagiannaki
    owner = models.ForeignKey(
1186 b22de10a Sofia Papagiannaki
        AstakosUser,
1187 b22de10a Sofia Papagiannaki
        related_name='own_project_applications',
1188 b22de10a Sofia Papagiannaki
        db_index=True
1189 b22de10a Sofia Papagiannaki
    )
1190 e1a80257 Sofia Papagiannaki
    comments = models.TextField(null=True, blank=True)
1191 e1a80257 Sofia Papagiannaki
    definition = models.OneToOneField(ProjectDefinition)
1192 e1a80257 Sofia Papagiannaki
    issue_date = models.DateTimeField()
1193 e1a80257 Sofia Papagiannaki
    precursor_application = models.OneToOneField('ProjectApplication',
1194 e1a80257 Sofia Papagiannaki
        null=True,
1195 ccab6eb5 Sofia Papagiannaki
        blank=True,
1196 ccab6eb5 Sofia Papagiannaki
        db_index=True
1197 e1a80257 Sofia Papagiannaki
    )
1198 bfe23b13 Sofia Papagiannaki
    state = models.CharField(max_length=80, default=UNKNOWN)
1199 0cc22d47 Sofia Papagiannaki
    
1200 ccab6eb5 Sofia Papagiannaki
    @property
1201 ccab6eb5 Sofia Papagiannaki
    def follower(self):
1202 ccab6eb5 Sofia Papagiannaki
        try:
1203 ccab6eb5 Sofia Papagiannaki
            return ProjectApplication.objects.get(precursor_application=self)
1204 ccab6eb5 Sofia Papagiannaki
        except ProjectApplication.DoesNotExist:
1205 ccab6eb5 Sofia Papagiannaki
            return
1206 ccab6eb5 Sofia Papagiannaki
1207 0cc22d47 Sofia Papagiannaki
    def save(self):
1208 ccab6eb5 Sofia Papagiannaki
        self.definition.save()
1209 ccab6eb5 Sofia Papagiannaki
        self.definition = self.definition
1210 0cc22d47 Sofia Papagiannaki
        super(ProjectApplication, self).save()
1211 0cc22d47 Sofia Papagiannaki
1212 bfe23b13 Sofia Papagiannaki
1213 0cc22d47 Sofia Papagiannaki
    @staticmethod
1214 ece3b66e Giorgos Korfiatis
    def submit(definition, resource_policies, applicant, comments,
1215 30a6c330 Sofia Papagiannaki
               precursor_application=None):
1216 ece3b66e Giorgos Korfiatis
1217 ece3b66e Giorgos Korfiatis
        application = ProjectApplication()
1218 8327782d Sofia Papagiannaki
        if precursor_application:
1219 ece3b66e Giorgos Korfiatis
            application.precursor_application = precursor_application
1220 ece3b66e Giorgos Korfiatis
            application.owner = precursor_application.owner
1221 bfe23b13 Sofia Papagiannaki
        else:
1222 ece3b66e Giorgos Korfiatis
            application.owner = applicant
1223 ece3b66e Giorgos Korfiatis
1224 0cc22d47 Sofia Papagiannaki
        application.definition = definition
1225 083d32f9 Sofia Papagiannaki
        application.definition.id = None
1226 0cc22d47 Sofia Papagiannaki
        application.applicant = applicant
1227 0cc22d47 Sofia Papagiannaki
        application.comments = comments
1228 0cc22d47 Sofia Papagiannaki
        application.issue_date = datetime.now()
1229 bfe23b13 Sofia Papagiannaki
        application.state = PENDING
1230 30a6c330 Sofia Papagiannaki
        application.save()
1231 30a6c330 Sofia Papagiannaki
        application.definition.resource_policies = resource_policies
1232 c4d1b547 Sofia Papagiannaki
1233 c4d1b547 Sofia Papagiannaki
        notification = build_notification(
1234 c4d1b547 Sofia Papagiannaki
            settings.SERVER_EMAIL,
1235 c4d1b547 Sofia Papagiannaki
            [i[1] for i in settings.ADMINS],
1236 c4d1b547 Sofia Papagiannaki
            _(PROJECT_CREATION_SUBJECT) % application.definition.__dict__,
1237 c4d1b547 Sofia Papagiannaki
            template='im/projects/project_creation_notification.txt',
1238 c4d1b547 Sofia Papagiannaki
            dictionary={'object':application}
1239 c4d1b547 Sofia Papagiannaki
        )
1240 c4d1b547 Sofia Papagiannaki
        notification.send()
1241 0cc22d47 Sofia Papagiannaki
        return application
1242 ece3b66e Giorgos Korfiatis
1243 ccab6eb5 Sofia Papagiannaki
    def approve(self, approval_user=None):
1244 ccab6eb5 Sofia Papagiannaki
        """
1245 ccab6eb5 Sofia Papagiannaki
        If approval_user then during owner membership acceptance
1246 ccab6eb5 Sofia Papagiannaki
        it is checked whether the request_user is eligible.
1247 262e04c6 Giorgos Korfiatis

1248 2553efae Sofia Papagiannaki
        Raises:
1249 2553efae Sofia Papagiannaki
            ValidationError: if there is other alive project with the same name
1250 262e04c6 Giorgos Korfiatis

1251 ccab6eb5 Sofia Papagiannaki
        """
1252 185b2190 Sofia Papagiannaki
        try:
1253 185b2190 Sofia Papagiannaki
            self.definition.validate_name()
1254 185b2190 Sofia Papagiannaki
        except ValidationError, e:
1255 185b2190 Sofia Papagiannaki
            raise PermissionDenied(e.messages[0])
1256 bfe23b13 Sofia Papagiannaki
        if self.state != PENDING:
1257 185b2190 Sofia Papagiannaki
            raise PermissionDenied(_(PROJECT_ALREADY_ACTIVE))
1258 262e04c6 Giorgos Korfiatis
1259 fdafae27 Giorgos Korfiatis
        now = datetime.now()
1260 75fae793 Sofia Papagiannaki
        precursor = self.precursor_application
1261 bfe23b13 Sofia Papagiannaki
        try:
1262 262e04c6 Giorgos Korfiatis
            project = precursor.project
1263 262e04c6 Giorgos Korfiatis
        except:
1264 fdafae27 Giorgos Korfiatis
            project = Project()
1265 fdafae27 Giorgos Korfiatis
            project.creation_date = now
1266 ccab6eb5 Sofia Papagiannaki
            project.accept_member(self.owner, approval_user)
1267 fdafae27 Giorgos Korfiatis
1268 fdafae27 Giorgos Korfiatis
        project.last_application_approved = self
1269 fdafae27 Giorgos Korfiatis
        project.last_approval_date = now
1270 fdafae27 Giorgos Korfiatis
        project.save()
1271 fdafae27 Giorgos Korfiatis
1272 75fae793 Sofia Papagiannaki
        p = precursor
1273 75fae793 Sofia Papagiannaki
        while p:
1274 75fae793 Sofia Papagiannaki
            p.state = REPLACED
1275 75fae793 Sofia Papagiannaki
            p.save()
1276 75fae793 Sofia Papagiannaki
            p = p.precursor_application
1277 262e04c6 Giorgos Korfiatis
1278 bfe23b13 Sofia Papagiannaki
        self.state = APPROVED
1279 bfe23b13 Sofia Papagiannaki
        self.save()
1280 262e04c6 Giorgos Korfiatis
1281 fdafae27 Giorgos Korfiatis
        transaction.commit()
1282 fdafae27 Giorgos Korfiatis
1283 bfe23b13 Sofia Papagiannaki
        notification = build_notification(
1284 bfe23b13 Sofia Papagiannaki
            settings.SERVER_EMAIL,
1285 bfe23b13 Sofia Papagiannaki
            [self.owner.email],
1286 c4d1b547 Sofia Papagiannaki
            _(PROJECT_APPROVED_SUBJECT) % self.definition.__dict__,
1287 c4d1b547 Sofia Papagiannaki
            template='im/projects/project_approval_notification.txt',
1288 c4d1b547 Sofia Papagiannaki
            dictionary={'object':self}
1289 bfe23b13 Sofia Papagiannaki
        )
1290 bfe23b13 Sofia Papagiannaki
        notification.send()
1291 ccab6eb5 Sofia Papagiannaki
1292 ccab6eb5 Sofia Papagiannaki
        rejected = self.project.sync()
1293 fdafae27 Giorgos Korfiatis
        if not rejected:
1294 fdafae27 Giorgos Korfiatis
            project.application = self
1295 ccab6eb5 Sofia Papagiannaki
            project.save()
1296 e1a80257 Sofia Papagiannaki
1297 8327782d Sofia Papagiannaki
1298 e1a80257 Sofia Papagiannaki
class Project(models.Model):
1299 fdafae27 Giorgos Korfiatis
    application = models.OneToOneField(
1300 fdafae27 Giorgos Korfiatis
        ProjectApplication, related_name='project', null=True, blank=True)
1301 e1a80257 Sofia Papagiannaki
    creation_date = models.DateTimeField()
1302 2a965273 Sofia Papagiannaki
    last_approval_date = models.DateTimeField(null=True)
1303 b22de10a Sofia Papagiannaki
    termination_start_date = models.DateTimeField(null=True)
1304 2a965273 Sofia Papagiannaki
    termination_date = models.DateTimeField(null=True)
1305 e1a80257 Sofia Papagiannaki
    members = models.ManyToManyField(AstakosUser, through='ProjectMembership')
1306 b22de10a Sofia Papagiannaki
    membership_dirty = models.BooleanField(default=False)
1307 fdafae27 Giorgos Korfiatis
    last_application_approved = models.OneToOneField(
1308 fdafae27 Giorgos Korfiatis
        ProjectApplication, related_name='last_project')
1309 e1a80257 Sofia Papagiannaki
    
1310 8327782d Sofia Papagiannaki
    
1311 e1a80257 Sofia Papagiannaki
    @property
1312 e1a80257 Sofia Papagiannaki
    def definition(self):
1313 e1a80257 Sofia Papagiannaki
        return self.application.definition
1314 b22de10a Sofia Papagiannaki
1315 b22de10a Sofia Papagiannaki
    @property
1316 b22de10a Sofia Papagiannaki
    def violated_members_number_limit(self):
1317 b22de10a Sofia Papagiannaki
        return len(self.approved_members) <= self.definition.limit_on_members_number
1318 b22de10a Sofia Papagiannaki
        
1319 e1a80257 Sofia Papagiannaki
    @property
1320 e1a80257 Sofia Papagiannaki
    def is_active(self):
1321 e1a80257 Sofia Papagiannaki
        if not self.last_approval_date:
1322 e1a80257 Sofia Papagiannaki
            return False
1323 e1a80257 Sofia Papagiannaki
        if self.termination_date:
1324 e1a80257 Sofia Papagiannaki
            return False
1325 e1a80257 Sofia Papagiannaki
        if self.definition.violated_resource_grants:
1326 e1a80257 Sofia Papagiannaki
            return False
1327 b22de10a Sofia Papagiannaki
#         if self.violated_members_number_limit:
1328 b22de10a Sofia Papagiannaki
#             return False
1329 e1a80257 Sofia Papagiannaki
        return True
1330 e1a80257 Sofia Papagiannaki
    
1331 e1a80257 Sofia Papagiannaki
    @property
1332 e1a80257 Sofia Papagiannaki
    def is_terminated(self):
1333 e1a80257 Sofia Papagiannaki
        if not self.termination_date:
1334 e1a80257 Sofia Papagiannaki
            return False
1335 e1a80257 Sofia Papagiannaki
        return True
1336 e1a80257 Sofia Papagiannaki
    
1337 e1a80257 Sofia Papagiannaki
    @property
1338 e1a80257 Sofia Papagiannaki
    def is_suspended(self):
1339 ad60cbe2 Giorgos Korfiatis
        if self.termination_date:
1340 e1a80257 Sofia Papagiannaki
            return False
1341 ad60cbe2 Giorgos Korfiatis
        if self.last_approval_date:
1342 e1a80257 Sofia Papagiannaki
            if not self.definition.violated_resource_grants:
1343 e1a80257 Sofia Papagiannaki
                return False
1344 b22de10a Sofia Papagiannaki
#             if not self.violated_members_number_limit:
1345 b22de10a Sofia Papagiannaki
#                 return False
1346 e1a80257 Sofia Papagiannaki
        return True
1347 e1a80257 Sofia Papagiannaki
    
1348 e1a80257 Sofia Papagiannaki
    @property
1349 e1a80257 Sofia Papagiannaki
    def is_alive(self):
1350 e1a80257 Sofia Papagiannaki
        return self.is_active or self.is_suspended
1351 e1a80257 Sofia Papagiannaki
    
1352 e1a80257 Sofia Papagiannaki
    @property
1353 e1a80257 Sofia Papagiannaki
    def is_inconsistent(self):
1354 e1a80257 Sofia Papagiannaki
        now = datetime.now()
1355 e1a80257 Sofia Papagiannaki
        if self.creation_date > now:
1356 e1a80257 Sofia Papagiannaki
            return True
1357 e1a80257 Sofia Papagiannaki
        if self.last_approval_date > now:
1358 e1a80257 Sofia Papagiannaki
            return True
1359 e1a80257 Sofia Papagiannaki
        if self.terminaton_date > now:
1360 e1a80257 Sofia Papagiannaki
            return True
1361 e1a80257 Sofia Papagiannaki
        return False
1362 e1a80257 Sofia Papagiannaki
    
1363 e1a80257 Sofia Papagiannaki
    @property
1364 b22de10a Sofia Papagiannaki
    def is_synchronized(self):
1365 fdafae27 Giorgos Korfiatis
        return self.last_application_approved == self.application and \
1366 b22de10a Sofia Papagiannaki
            not self.membership_dirty and \
1367 b22de10a Sofia Papagiannaki
            (not self.termination_start_date or termination_date)
1368 e1a80257 Sofia Papagiannaki
    
1369 b22de10a Sofia Papagiannaki
    @property
1370 b22de10a Sofia Papagiannaki
    def approved_members(self):
1371 0cc22d47 Sofia Papagiannaki
        return [m.person for m in self.projectmembership_set.filter(~Q(acceptance_date=None))]
1372 b22de10a Sofia Papagiannaki
        
1373 b22de10a Sofia Papagiannaki
    def sync(self, specific_members=()):
1374 ccab6eb5 Sofia Papagiannaki
        if self.is_synchronized:
1375 b22de10a Sofia Papagiannaki
            return
1376 b22de10a Sofia Papagiannaki
        members = specific_members or self.approved_members
1377 ccab6eb5 Sofia Papagiannaki
        c, rejected = send_quota(self.approved_members)
1378 e1a80257 Sofia Papagiannaki
        return rejected
1379 2a965273 Sofia Papagiannaki
    
1380 ccab6eb5 Sofia Papagiannaki
    def accept_member(self, user, request_user=None):
1381 bfe23b13 Sofia Papagiannaki
        """
1382 bfe23b13 Sofia Papagiannaki
        Raises:
1383 bfe23b13 Sofia Papagiannaki
            django.exceptions.PermissionDenied
1384 bfe23b13 Sofia Papagiannaki
            astakos.im.models.AstakosUser.DoesNotExist
1385 bfe23b13 Sofia Papagiannaki
        """
1386 2a965273 Sofia Papagiannaki
        if isinstance(user, int):
1387 bfe23b13 Sofia Papagiannaki
            try:
1388 bfe23b13 Sofia Papagiannaki
                user = lookup_object(AstakosUser, user, None, None)
1389 bfe23b13 Sofia Papagiannaki
            except Http404:
1390 bfe23b13 Sofia Papagiannaki
                raise AstakosUser.DoesNotExist()
1391 ccab6eb5 Sofia Papagiannaki
        m, created = ProjectMembership.objects.get_or_create(
1392 ccab6eb5 Sofia Papagiannaki
            person=user, project=self
1393 2a965273 Sofia Papagiannaki
        )
1394 9715d553 Sofia Papagiannaki
        if m.acceptance_date:
1395 9715d553 Sofia Papagiannaki
            return
1396 97896995 Sofia Papagiannaki
        m.accept(delete_on_failure=created, request_user=None)
1397 ccab6eb5 Sofia Papagiannaki
1398 ccab6eb5 Sofia Papagiannaki
    def reject_member(self, user, request_user=None):
1399 bfe23b13 Sofia Papagiannaki
        """
1400 bfe23b13 Sofia Papagiannaki
        Raises:
1401 bfe23b13 Sofia Papagiannaki
            django.exceptions.PermissionDenied
1402 bfe23b13 Sofia Papagiannaki
            astakos.im.models.AstakosUser.DoesNotExist
1403 bfe23b13 Sofia Papagiannaki
            astakos.im.models.ProjectMembership.DoesNotExist
1404 bfe23b13 Sofia Papagiannaki
        """
1405 ccab6eb5 Sofia Papagiannaki
        if isinstance(user, int):
1406 bfe23b13 Sofia Papagiannaki
            try:
1407 bfe23b13 Sofia Papagiannaki
                user = lookup_object(AstakosUser, user, None, None)
1408 bfe23b13 Sofia Papagiannaki
            except Http404:
1409 bfe23b13 Sofia Papagiannaki
                raise AstakosUser.DoesNotExist()
1410 bfe23b13 Sofia Papagiannaki
        m = ProjectMembership.objects.get(person=user, project=self)
1411 bfe23b13 Sofia Papagiannaki
        m.reject()
1412 b22de10a Sofia Papagiannaki
        
1413 ccab6eb5 Sofia Papagiannaki
    def remove_member(self, user, request_user=None):
1414 bfe23b13 Sofia Papagiannaki
        """
1415 bfe23b13 Sofia Papagiannaki
        Raises:
1416 bfe23b13 Sofia Papagiannaki
            django.exceptions.PermissionDenied
1417 bfe23b13 Sofia Papagiannaki
            astakos.im.models.AstakosUser.DoesNotExist
1418 bfe23b13 Sofia Papagiannaki
            astakos.im.models.ProjectMembership.DoesNotExist
1419 bfe23b13 Sofia Papagiannaki
        """
1420 ccab6eb5 Sofia Papagiannaki
        if isinstance(user, int):
1421 bfe23b13 Sofia Papagiannaki
            try:
1422 bfe23b13 Sofia Papagiannaki
                user = lookup_object(AstakosUser, user, None, None)
1423 bfe23b13 Sofia Papagiannaki
            except Http404:
1424 bfe23b13 Sofia Papagiannaki
                raise AstakosUser.DoesNotExist()
1425 bfe23b13 Sofia Papagiannaki
        m = ProjectMembership.objects.get(person=user, project=self)
1426 bfe23b13 Sofia Papagiannaki
        m.remove()
1427 b22de10a Sofia Papagiannaki
    
1428 b22de10a Sofia Papagiannaki
    def terminate(self):
1429 b22de10a Sofia Papagiannaki
        self.termination_start_date = datetime.now()
1430 b22de10a Sofia Papagiannaki
        self.terminaton_date = None
1431 b22de10a Sofia Papagiannaki
        self.save()
1432 b22de10a Sofia Papagiannaki
        
1433 b22de10a Sofia Papagiannaki
        rejected = self.sync()
1434 b22de10a Sofia Papagiannaki
        if not rejected:
1435 b22de10a Sofia Papagiannaki
            self.termination_start_date = None
1436 75fae793 Sofia Papagiannaki
            self.termination_date = datetime.now()
1437 b22de10a Sofia Papagiannaki
            self.save()
1438 b22de10a Sofia Papagiannaki
            
1439 c4d1b547 Sofia Papagiannaki
        notification = build_notification(
1440 c4d1b547 Sofia Papagiannaki
            settings.SERVER_EMAIL,
1441 c4d1b547 Sofia Papagiannaki
            [self.application.owner.email],
1442 c4d1b547 Sofia Papagiannaki
            _(PROJECT_TERMINATION_SUBJECT) % self.definition.__dict__,
1443 c4d1b547 Sofia Papagiannaki
            template='im/projects/project_termination_notification.txt',
1444 c4d1b547 Sofia Papagiannaki
            dictionary={'object':self.application}
1445 c4d1b547 Sofia Papagiannaki
        )
1446 c4d1b547 Sofia Papagiannaki
        notification.send()
1447 b22de10a Sofia Papagiannaki
1448 b22de10a Sofia Papagiannaki
    def suspend(self):
1449 b22de10a Sofia Papagiannaki
        self.last_approval_date = None
1450 b22de10a Sofia Papagiannaki
        self.save()
1451 c4d1b547 Sofia Papagiannaki
        self.sync()
1452 b22de10a Sofia Papagiannaki
        notification = build_notification(
1453 b22de10a Sofia Papagiannaki
            settings.SERVER_EMAIL,
1454 b22de10a Sofia Papagiannaki
            [self.application.owner.email],
1455 c4d1b547 Sofia Papagiannaki
            _(PROJECT_SUSPENSION_SUBJECT) % self.definition.__dict__,
1456 c4d1b547 Sofia Papagiannaki
            template='im/projects/project_suspension_notification.txt',
1457 c4d1b547 Sofia Papagiannaki
            dictionary={'object':self.application}
1458 b22de10a Sofia Papagiannaki
        )
1459 b22de10a Sofia Papagiannaki
        notification.send()
1460 b22de10a Sofia Papagiannaki
1461 0cc22d47 Sofia Papagiannaki
class ProjectMembership(models.Model):
1462 0cc22d47 Sofia Papagiannaki
    person = models.ForeignKey(AstakosUser)
1463 0cc22d47 Sofia Papagiannaki
    project = models.ForeignKey(Project)
1464 0cc22d47 Sofia Papagiannaki
    request_date = models.DateField(default=datetime.now())
1465 0cc22d47 Sofia Papagiannaki
    acceptance_date = models.DateField(null=True, db_index=True)
1466 bfe23b13 Sofia Papagiannaki
    leave_request_date = models.DateField(null=True)
1467 2a965273 Sofia Papagiannaki
1468 0cc22d47 Sofia Papagiannaki
    class Meta:
1469 0cc22d47 Sofia Papagiannaki
        unique_together = ("person", "project")
1470 bfe23b13 Sofia Papagiannaki
1471 bfe23b13 Sofia Papagiannaki
    def accept(self, delete_on_failure=False, request_user=None):
1472 bfe23b13 Sofia Papagiannaki
        """
1473 bfe23b13 Sofia Papagiannaki
            Raises:
1474 bfe23b13 Sofia Papagiannaki
                django.exception.PermissionDenied
1475 bfe23b13 Sofia Papagiannaki
                astakos.im.notifications.NotificationError
1476 bfe23b13 Sofia Papagiannaki
        """
1477 bfe23b13 Sofia Papagiannaki
        try:
1478 bfe23b13 Sofia Papagiannaki
            if request_user and \
1479 bfe23b13 Sofia Papagiannaki
                (not self.project.application.owner == request_user and \
1480 bfe23b13 Sofia Papagiannaki
                    not request_user.is_superuser):
1481 bfe23b13 Sofia Papagiannaki
                raise PermissionDenied(_(astakos_messages.NOT_ALLOWED))
1482 bfe23b13 Sofia Papagiannaki
            if not self.project.is_alive:
1483 bfe23b13 Sofia Papagiannaki
                raise PermissionDenied(_(astakos_messages.NOT_ALIVE_PROJECT) % self.project.__dict__)
1484 bfe23b13 Sofia Papagiannaki
            if len(self.project.approved_members) + 1 > self.project.definition.limit_on_members_number:
1485 bfe23b13 Sofia Papagiannaki
                raise PermissionDenied(_(astakos_messages.MEMBER_NUMBER_LIMIT_REACHED))
1486 bfe23b13 Sofia Papagiannaki
        except PermissionDenied, e:
1487 bfe23b13 Sofia Papagiannaki
            if delete_on_failure:
1488 9715d553 Sofia Papagiannaki
                self.delete()
1489 bfe23b13 Sofia Papagiannaki
            raise
1490 0cc22d47 Sofia Papagiannaki
        if self.acceptance_date:
1491 0cc22d47 Sofia Papagiannaki
            return
1492 0cc22d47 Sofia Papagiannaki
        self.acceptance_date = datetime.now()
1493 0cc22d47 Sofia Papagiannaki
        self.save()
1494 bfe23b13 Sofia Papagiannaki
        notification = build_notification(
1495 bfe23b13 Sofia Papagiannaki
            settings.SERVER_EMAIL,
1496 bfe23b13 Sofia Papagiannaki
            [self.person.email],
1497 c4d1b547 Sofia Papagiannaki
            _(PROJECT_MEMBERSHIP_CHANGE_SUBJECT) % self.project.definition.__dict__,
1498 c4d1b547 Sofia Papagiannaki
            template='im/projects/project_membership_change_notification.txt',
1499 c4d1b547 Sofia Papagiannaki
            dictionary={'object':self.project.application, 'action':'accepted'}
1500 bfe23b13 Sofia Papagiannaki
        ).send()
1501 0cc22d47 Sofia Papagiannaki
        self.sync()
1502 0cc22d47 Sofia Papagiannaki
    
1503 bfe23b13 Sofia Papagiannaki
    def reject(self, request_user=None):
1504 bfe23b13 Sofia Papagiannaki
        """
1505 bfe23b13 Sofia Papagiannaki
            Raises:
1506 bfe23b13 Sofia Papagiannaki
                django.exception.PermissionDenied,
1507 bfe23b13 Sofia Papagiannaki
                astakos.im.notifications.NotificationError
1508 bfe23b13 Sofia Papagiannaki
        """
1509 bfe23b13 Sofia Papagiannaki
        if request_user and \
1510 bfe23b13 Sofia Papagiannaki
            (not self.project.application.owner == request_user and \
1511 bfe23b13 Sofia Papagiannaki
                not request_user.is_superuser):
1512 bfe23b13 Sofia Papagiannaki
            raise PermissionDenied(_(astakos_messages.NOT_ALLOWED))
1513 bfe23b13 Sofia Papagiannaki
        if not self.project.is_alive:
1514 bfe23b13 Sofia Papagiannaki
            raise PermissionDenied(_(astakos_messages.NOT_ALIVE_PROJECT) % project.__dict__)
1515 0cc22d47 Sofia Papagiannaki
        history_item = ProjectMembershipHistory(
1516 0cc22d47 Sofia Papagiannaki
            person=self.person,
1517 0cc22d47 Sofia Papagiannaki
            project=self.project,
1518 0cc22d47 Sofia Papagiannaki
            request_date=self.request_date,
1519 0cc22d47 Sofia Papagiannaki
            rejection_date=datetime.now()
1520 97896995 Sofia Papagiannaki
        )
1521 0cc22d47 Sofia Papagiannaki
        self.delete()
1522 97896995 Sofia Papagiannaki
        history_item.save()
1523 bfe23b13 Sofia Papagiannaki
        notification = build_notification(
1524 bfe23b13 Sofia Papagiannaki
            settings.SERVER_EMAIL,
1525 bfe23b13 Sofia Papagiannaki
            [self.person.email],
1526 c4d1b547 Sofia Papagiannaki
            _(PROJECT_MEMBERSHIP_CHANGE_SUBJECT) % self.project.definition.__dict__,
1527 c4d1b547 Sofia Papagiannaki
            template='im/projects/project_membership_change_notification.txt',
1528 c4d1b547 Sofia Papagiannaki
            dictionary={'object':self.project.application, 'action':'rejected'}
1529 bfe23b13 Sofia Papagiannaki
        ).send()
1530 0cc22d47 Sofia Papagiannaki
    
1531 bfe23b13 Sofia Papagiannaki
    def remove(self, request_user=None):
1532 bfe23b13 Sofia Papagiannaki
        """
1533 bfe23b13 Sofia Papagiannaki
            Raises:
1534 bfe23b13 Sofia Papagiannaki
                django.exception.PermissionDenied
1535 bfe23b13 Sofia Papagiannaki
                astakos.im.notifications.NotificationError
1536 bfe23b13 Sofia Papagiannaki
        """
1537 bfe23b13 Sofia Papagiannaki
        if request_user and \
1538 bfe23b13 Sofia Papagiannaki
            (not self.project.application.owner == request_user and \
1539 bfe23b13 Sofia Papagiannaki
                not request_user.is_superuser):
1540 bfe23b13 Sofia Papagiannaki
            raise PermissionDenied(_(astakos_messages.NOT_ALLOWED))
1541 bfe23b13 Sofia Papagiannaki
        if not self.project.is_alive:
1542 bfe23b13 Sofia Papagiannaki
            raise PermissionDenied(_(astakos_messages.NOT_ALIVE_PROJECT) % self.project.__dict__)
1543 0cc22d47 Sofia Papagiannaki
        history_item = ProjectMembershipHistory(
1544 0cc22d47 Sofia Papagiannaki
            id=self.id,
1545 0cc22d47 Sofia Papagiannaki
            person=self.person,
1546 0cc22d47 Sofia Papagiannaki
            project=self.project,
1547 0cc22d47 Sofia Papagiannaki
            request_date=self.request_date,
1548 0cc22d47 Sofia Papagiannaki
            removal_date=datetime.now()
1549 bfe23b13 Sofia Papagiannaki
        )
1550 0cc22d47 Sofia Papagiannaki
        self.delete()
1551 bfe23b13 Sofia Papagiannaki
        history_item.save()
1552 bfe23b13 Sofia Papagiannaki
        notification = build_notification(
1553 bfe23b13 Sofia Papagiannaki
            settings.SERVER_EMAIL,
1554 bfe23b13 Sofia Papagiannaki
            [self.person.email],
1555 c4d1b547 Sofia Papagiannaki
            _(PROJECT_MEMBERSHIP_CHANGE_SUBJECT) % self.project.definition.__dict__,
1556 c4d1b547 Sofia Papagiannaki
            template='im/projects/project_membership_change_notification.txt',
1557 c4d1b547 Sofia Papagiannaki
            dictionary={'object':self.project.application, 'action':'removed'}
1558 bfe23b13 Sofia Papagiannaki
        ).send()
1559 0cc22d47 Sofia Papagiannaki
        self.sync()
1560 0cc22d47 Sofia Papagiannaki
    
1561 bfe23b13 Sofia Papagiannaki
    def leave(self):
1562 bfe23b13 Sofia Papagiannaki
        leave_policy = self.project.application.definition.member_leave_policy
1563 bfe23b13 Sofia Papagiannaki
        if leave_policy == get_auto_accept_leave():
1564 bfe23b13 Sofia Papagiannaki
            self.remove()
1565 bfe23b13 Sofia Papagiannaki
        else:
1566 bfe23b13 Sofia Papagiannaki
            self.leave_request_date = datetime.now()
1567 bfe23b13 Sofia Papagiannaki
            self.save()
1568 bfe23b13 Sofia Papagiannaki
1569 0cc22d47 Sofia Papagiannaki
    def sync(self):
1570 0cc22d47 Sofia Papagiannaki
        # set membership_dirty flag
1571 0cc22d47 Sofia Papagiannaki
        self.project.membership_dirty = True
1572 0cc22d47 Sofia Papagiannaki
        self.project.save()
1573 0cc22d47 Sofia Papagiannaki
        
1574 97896995 Sofia Papagiannaki
        rejected = self.project.sync(specific_members=[self.person])
1575 0cc22d47 Sofia Papagiannaki
        if not rejected:
1576 0cc22d47 Sofia Papagiannaki
            # if syncing was successful unset membership_dirty flag
1577 0cc22d47 Sofia Papagiannaki
            self.membership_dirty = False
1578 9f01cf1d Sofia Papagiannaki
            self.project.save()
1579 0cc22d47 Sofia Papagiannaki
        
1580 e1a80257 Sofia Papagiannaki
1581 0cc22d47 Sofia Papagiannaki
class ProjectMembershipHistory(models.Model):
1582 e1a80257 Sofia Papagiannaki
    person = models.ForeignKey(AstakosUser)
1583 e1a80257 Sofia Papagiannaki
    project = models.ForeignKey(Project)
1584 0cc22d47 Sofia Papagiannaki
    request_date = models.DateField(default=datetime.now())
1585 0cc22d47 Sofia Papagiannaki
    removal_date = models.DateField(null=True)
1586 0cc22d47 Sofia Papagiannaki
    rejection_date = models.DateField(null=True)
1587 e1a80257 Sofia Papagiannaki
1588 0cc22d47 Sofia Papagiannaki
1589 e1a80257 Sofia Papagiannaki
def filter_queryset_by_property(q, property):
1590 e1a80257 Sofia Papagiannaki
    """
1591 e1a80257 Sofia Papagiannaki
    Incorporate list comprehension for filtering querysets by property
1592 e1a80257 Sofia Papagiannaki
    since Queryset.filter() operates on the database level.
1593 e1a80257 Sofia Papagiannaki
    """
1594 e1a80257 Sofia Papagiannaki
    return (p for p in q if getattr(p, property, False))
1595 e1a80257 Sofia Papagiannaki
1596 e1a80257 Sofia Papagiannaki
def get_alive_projects():
1597 e1a80257 Sofia Papagiannaki
    return filter_queryset_by_property(
1598 e1a80257 Sofia Papagiannaki
        Project.objects.all(),
1599 e1a80257 Sofia Papagiannaki
        'is_alive'
1600 e1a80257 Sofia Papagiannaki
    )
1601 e1a80257 Sofia Papagiannaki
1602 e1a80257 Sofia Papagiannaki
def get_active_projects():
1603 e1a80257 Sofia Papagiannaki
    return filter_queryset_by_property(
1604 e1a80257 Sofia Papagiannaki
        Project.objects.all(),
1605 e1a80257 Sofia Papagiannaki
        'is_active'
1606 e1a80257 Sofia Papagiannaki
    )
1607 e1a80257 Sofia Papagiannaki
1608 e1a80257 Sofia Papagiannaki
def _create_object(model, **kwargs):
1609 e1a80257 Sofia Papagiannaki
    o = model.objects.create(**kwargs)
1610 e1a80257 Sofia Papagiannaki
    o.save()
1611 e1a80257 Sofia Papagiannaki
    return o
1612 e1a80257 Sofia Papagiannaki
1613 b22de10a Sofia Papagiannaki
1614 ff9290ec Sofia Papagiannaki
def create_astakos_user(u):
1615 ff9290ec Sofia Papagiannaki
    try:
1616 ff9290ec Sofia Papagiannaki
        AstakosUser.objects.get(user_ptr=u.pk)
1617 ff9290ec Sofia Papagiannaki
    except AstakosUser.DoesNotExist:
1618 ff9290ec Sofia Papagiannaki
        extended_user = AstakosUser(user_ptr_id=u.pk)
1619 ff9290ec Sofia Papagiannaki
        extended_user.__dict__.update(u.__dict__)
1620 ff9290ec Sofia Papagiannaki
        extended_user.save()
1621 67be1883 Olga Brani
        if not extended_user.has_auth_provider('local'):
1622 67be1883 Olga Brani
            extended_user.add_auth_provider('local')
1623 fc1e2f02 Sofia Papagiannaki
    except BaseException, e:
1624 fc1e2f02 Sofia Papagiannaki
        logger.exception(e)
1625 ff9290ec Sofia Papagiannaki
1626 5ce3ce4f Sofia Papagiannaki
1627 fc1e2f02 Sofia Papagiannaki
def fix_superusers(sender, **kwargs):
1628 fc1e2f02 Sofia Papagiannaki
    # Associate superusers with AstakosUser
1629 ff9290ec Sofia Papagiannaki
    admins = User.objects.filter(is_superuser=True)
1630 ff9290ec Sofia Papagiannaki
    for u in admins:
1631 ff9290ec Sofia Papagiannaki
        create_astakos_user(u)
1632 bfe23b13 Sofia Papagiannaki
post_syncdb.connect(fix_superusers)
1633 ff9290ec Sofia Papagiannaki
1634 ff9290ec Sofia Papagiannaki
1635 fc1e2f02 Sofia Papagiannaki
def user_post_save(sender, instance, created, **kwargs):
1636 aa4109d4 Sofia Papagiannaki
    if not created:
1637 aa4109d4 Sofia Papagiannaki
        return
1638 fc1e2f02 Sofia Papagiannaki
    create_astakos_user(instance)
1639 bfe23b13 Sofia Papagiannaki
post_save.connect(user_post_save, sender=User)
1640 ff9290ec Sofia Papagiannaki
1641 bf0c6de5 Sofia Papagiannaki
1642 fc1e2f02 Sofia Papagiannaki
def astakosuser_pre_save(sender, instance, **kwargs):
1643 fc1e2f02 Sofia Papagiannaki
    instance.aquarium_report = False
1644 fc1e2f02 Sofia Papagiannaki
    instance.new = False
1645 fc1e2f02 Sofia Papagiannaki
    try:
1646 5ce3ce4f Sofia Papagiannaki
        db_instance = AstakosUser.objects.get(id=instance.id)
1647 fc1e2f02 Sofia Papagiannaki
    except AstakosUser.DoesNotExist:
1648 fc1e2f02 Sofia Papagiannaki
        # create event
1649 fc1e2f02 Sofia Papagiannaki
        instance.aquarium_report = True
1650 fc1e2f02 Sofia Papagiannaki
        instance.new = True
1651 fc1e2f02 Sofia Papagiannaki
    else:
1652 fc1e2f02 Sofia Papagiannaki
        get = AstakosUser.__getattribute__
1653 fc1e2f02 Sofia Papagiannaki
        l = filter(lambda f: get(db_instance, f) != get(instance, f),
1654 9a06d96f Olga Brani
                   BILLING_FIELDS)
1655 fc1e2f02 Sofia Papagiannaki
        instance.aquarium_report = True if l else False
1656 bfe23b13 Sofia Papagiannaki
pre_save.connect(astakosuser_pre_save, sender=AstakosUser)
1657 bfe23b13 Sofia Papagiannaki
1658 bfe23b13 Sofia Papagiannaki
def set_default_group(user):
1659 bfe23b13 Sofia Papagiannaki
    try:
1660 bfe23b13 Sofia Papagiannaki
        default = AstakosGroup.objects.get(name='default')
1661 bfe23b13 Sofia Papagiannaki
        Membership(
1662 bfe23b13 Sofia Papagiannaki
            group=default, person=user, date_joined=datetime.now()).save()
1663 bfe23b13 Sofia Papagiannaki
    except AstakosGroup.DoesNotExist, e:
1664 bfe23b13 Sofia Papagiannaki
        logger.exception(e)
1665 fc1e2f02 Sofia Papagiannaki
1666 5ce3ce4f Sofia Papagiannaki
1667 fc1e2f02 Sofia Papagiannaki
def astakosuser_post_save(sender, instance, created, **kwargs):
1668 fc1e2f02 Sofia Papagiannaki
    if instance.aquarium_report:
1669 fc1e2f02 Sofia Papagiannaki
        report_user_event(instance, create=instance.new)
1670 fc1e2f02 Sofia Papagiannaki
    if not created:
1671 fc1e2f02 Sofia Papagiannaki
        return
1672 fc1e2f02 Sofia Papagiannaki
    set_default_group(instance)
1673 fc1e2f02 Sofia Papagiannaki
    # TODO handle socket.error & IOError
1674 fc1e2f02 Sofia Papagiannaki
    register_users((instance,))
1675 bfe23b13 Sofia Papagiannaki
post_save.connect(astakosuser_post_save, sender=AstakosUser)
1676 fc1e2f02 Sofia Papagiannaki
1677 5ce3ce4f Sofia Papagiannaki
1678 bd4f356c Sofia Papagiannaki
def resource_post_save(sender, instance, created, **kwargs):
1679 bd4f356c Sofia Papagiannaki
    if not created:
1680 bd4f356c Sofia Papagiannaki
        return
1681 bd4f356c Sofia Papagiannaki
    register_resources((instance,))
1682 bfe23b13 Sofia Papagiannaki
post_save.connect(resource_post_save, sender=Resource)
1683 bfe23b13 Sofia Papagiannaki
1684 bfe23b13 Sofia Papagiannaki
1685 bfe23b13 Sofia Papagiannaki
def on_quota_disturbed(sender, users, **kwargs):
1686 bfe23b13 Sofia Papagiannaki
#     print '>>>', locals()
1687 bfe23b13 Sofia Papagiannaki
    if not users:
1688 bfe23b13 Sofia Papagiannaki
        return
1689 bfe23b13 Sofia Papagiannaki
    send_quota(users)
1690 bfe23b13 Sofia Papagiannaki
1691 bfe23b13 Sofia Papagiannaki
quota_disturbed = Signal(providing_args=["users"])
1692 bfe23b13 Sofia Papagiannaki
quota_disturbed.connect(on_quota_disturbed)
1693 bd4f356c Sofia Papagiannaki
1694 bd4f356c Sofia Papagiannaki
1695 fc1e2f02 Sofia Papagiannaki
def send_quota_disturbed(sender, instance, **kwargs):
1696 fc1e2f02 Sofia Papagiannaki
    users = []
1697 fc1e2f02 Sofia Papagiannaki
    extend = users.extend
1698 fc1e2f02 Sofia Papagiannaki
    if sender == Membership:
1699 fc1e2f02 Sofia Papagiannaki
        if not instance.group.is_enabled:
1700 fc1e2f02 Sofia Papagiannaki
            return
1701 fc1e2f02 Sofia Papagiannaki
        extend([instance.person])
1702 fc1e2f02 Sofia Papagiannaki
    elif sender == AstakosUserQuota:
1703 fc1e2f02 Sofia Papagiannaki
        extend([instance.user])
1704 fc1e2f02 Sofia Papagiannaki
    elif sender == AstakosGroupQuota:
1705 fc1e2f02 Sofia Papagiannaki
        if not instance.group.is_enabled:
1706 fc1e2f02 Sofia Papagiannaki
            return
1707 fc1e2f02 Sofia Papagiannaki
        extend(instance.group.astakosuser_set.all())
1708 fc1e2f02 Sofia Papagiannaki
    elif sender == AstakosGroup:
1709 fc1e2f02 Sofia Papagiannaki
        if not instance.is_enabled:
1710 fc1e2f02 Sofia Papagiannaki
            return
1711 fc1e2f02 Sofia Papagiannaki
    quota_disturbed.send(sender=sender, users=users)
1712 fc1e2f02 Sofia Papagiannaki
post_delete.connect(send_quota_disturbed, sender=AstakosGroup)
1713 fc1e2f02 Sofia Papagiannaki
post_delete.connect(send_quota_disturbed, sender=Membership)
1714 fc1e2f02 Sofia Papagiannaki
post_save.connect(send_quota_disturbed, sender=AstakosUserQuota)
1715 fc1e2f02 Sofia Papagiannaki
post_delete.connect(send_quota_disturbed, sender=AstakosUserQuota)
1716 fc1e2f02 Sofia Papagiannaki
post_save.connect(send_quota_disturbed, sender=AstakosGroupQuota)
1717 a4075f5a root
post_delete.connect(send_quota_disturbed, sender=AstakosGroupQuota)
1718 c0b26605 Sofia Papagiannaki
1719 bfe23b13 Sofia Papagiannaki
1720 bfe23b13 Sofia Papagiannaki
def renew_token(sender, instance, **kwargs):
1721 bfe23b13 Sofia Papagiannaki
    if not instance.auth_token:
1722 bfe23b13 Sofia Papagiannaki
        instance.renew_token()
1723 bf0c6de5 Sofia Papagiannaki
pre_save.connect(renew_token, sender=AstakosUser)
1724 2e90e3ec Kostas Papadimitriou
pre_save.connect(renew_token, sender=Service)
1725 bfe23b13 Sofia Papagiannaki
1726 bfe23b13 Sofia Papagiannaki
1727 bfe23b13 Sofia Papagiannaki
def check_closed_join_membership_policy(sender, instance, **kwargs):
1728 bfe23b13 Sofia Papagiannaki
    if instance.id:
1729 bfe23b13 Sofia Papagiannaki
        return
1730 2f0ebd7d Sofia Papagiannaki
    if instance.person == instance.project.application.owner:
1731 2f0ebd7d Sofia Papagiannaki
        return
1732 bfe23b13 Sofia Papagiannaki
    join_policy = instance.project.application.definition.member_join_policy
1733 bfe23b13 Sofia Papagiannaki
    if join_policy == get_closed_join():
1734 bfe23b13 Sofia Papagiannaki
        raise PermissionDenied(_(astakos_messages.MEMBER_JOIN_POLICY_CLOSED))
1735 bfe23b13 Sofia Papagiannaki
pre_save.connect(check_closed_join_membership_policy, sender=ProjectMembership)
1736 bfe23b13 Sofia Papagiannaki
1737 bfe23b13 Sofia Papagiannaki
1738 bfe23b13 Sofia Papagiannaki
def check_auto_accept_join_membership_policy(sender, instance, created, **kwargs):
1739 97896995 Sofia Papagiannaki
    if not created:
1740 97896995 Sofia Papagiannaki
        return
1741 9f01cf1d Sofia Papagiannaki
    join_policy = instance.project.application.definition.member_join_policy
1742 75fae793 Sofia Papagiannaki
    if join_policy == get_auto_accept_join() and not instance.acceptance_date:
1743 9f01cf1d Sofia Papagiannaki
        instance.accept()
1744 8a57a69b Giorgos Korfiatis
post_save.connect(check_auto_accept_join_membership_policy, sender=ProjectMembership)