Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / models.py @ 8ed8ea10

History | View | Annotate | Download (51 kB)

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

875 49790d9d Sofia Papagiannaki
        If the key is valid and has not expired, return the ``User``
876 49790d9d Sofia Papagiannaki
        after activating.
877 49790d9d Sofia Papagiannaki

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

880 49790d9d Sofia Papagiannaki
        If the key is valid but the ``User`` is already active,
881 49790d9d Sofia Papagiannaki
        return ``None``.
882 49790d9d Sofia Papagiannaki

883 49790d9d Sofia Papagiannaki
        After successful email change the activation record is deleted.
884 49790d9d Sofia Papagiannaki

885 49790d9d Sofia Papagiannaki
        Throws ValueError if there is already
886 49790d9d Sofia Papagiannaki
        """
887 49790d9d Sofia Papagiannaki
        try:
888 5ce3ce4f Sofia Papagiannaki
            email_change = self.model.objects.get(
889 5ce3ce4f Sofia Papagiannaki
                activation_key=activation_key)
890 49790d9d Sofia Papagiannaki
            if email_change.activation_key_expired():
891 49790d9d Sofia Papagiannaki
                email_change.delete()
892 49790d9d Sofia Papagiannaki
                raise EmailChange.DoesNotExist
893 49790d9d Sofia Papagiannaki
            # is there an active user with this address?
894 49790d9d Sofia Papagiannaki
            try:
895 789a5951 Sofia Papagiannaki
                AstakosUser.objects.get(email__iexact=email_change.new_email_address)
896 49790d9d Sofia Papagiannaki
            except AstakosUser.DoesNotExist:
897 49790d9d Sofia Papagiannaki
                pass
898 49790d9d Sofia Papagiannaki
            else:
899 ae497612 Olga Brani
                raise ValueError(_(astakos_messages.NEW_EMAIL_ADDR_RESERVED))
900 49790d9d Sofia Papagiannaki
            # update user
901 49790d9d Sofia Papagiannaki
            user = AstakosUser.objects.get(pk=email_change.user_id)
902 49790d9d Sofia Papagiannaki
            user.email = email_change.new_email_address
903 49790d9d Sofia Papagiannaki
            user.save()
904 49790d9d Sofia Papagiannaki
            email_change.delete()
905 49790d9d Sofia Papagiannaki
            return user
906 49790d9d Sofia Papagiannaki
        except EmailChange.DoesNotExist:
907 ae497612 Olga Brani
            raise ValueError(_(astakos_messages.INVALID_ACTIVATION_KEY))
908 49790d9d Sofia Papagiannaki
909 49790d9d Sofia Papagiannaki
910 49790d9d Sofia Papagiannaki
class EmailChange(models.Model):
911 9a06d96f Olga Brani
    new_email_address = models.EmailField(_(u'new e-mail address'),
912 ae497612 Olga Brani
                                          help_text=_(astakos_messages.EMAIL_CHANGE_NEW_ADDR_HELP))
913 5ce3ce4f Sofia Papagiannaki
    user = models.ForeignKey(
914 5ce3ce4f Sofia Papagiannaki
        AstakosUser, unique=True, related_name='emailchange_user')
915 49790d9d Sofia Papagiannaki
    requested_at = models.DateTimeField(default=datetime.now())
916 5ce3ce4f Sofia Papagiannaki
    activation_key = models.CharField(
917 5ce3ce4f Sofia Papagiannaki
        max_length=40, unique=True, db_index=True)
918 49790d9d Sofia Papagiannaki
919 49790d9d Sofia Papagiannaki
    objects = EmailChangeManager()
920 49790d9d Sofia Papagiannaki
921 49790d9d Sofia Papagiannaki
    def activation_key_expired(self):
922 49790d9d Sofia Papagiannaki
        expiration_date = timedelta(days=EMAILCHANGE_ACTIVATION_DAYS)
923 ff9290ec Sofia Papagiannaki
        return self.requested_at + expiration_date < datetime.now()
924 ff9290ec Sofia Papagiannaki
925 6b03a847 Sofia Papagiannaki
926 ca828a10 Sofia Papagiannaki
class AdditionalMail(models.Model):
927 ca828a10 Sofia Papagiannaki
    """
928 ca828a10 Sofia Papagiannaki
    Model for registring invitations
929 ca828a10 Sofia Papagiannaki
    """
930 ca828a10 Sofia Papagiannaki
    owner = models.ForeignKey(AstakosUser)
931 1eec103a Sofia Papagiannaki
    email = models.EmailField()
932 ca828a10 Sofia Papagiannaki
933 5ce3ce4f Sofia Papagiannaki
934 fc1e2f02 Sofia Papagiannaki
def _generate_invitation_code():
935 fc1e2f02 Sofia Papagiannaki
    while True:
936 5ce3ce4f Sofia Papagiannaki
        code = randint(1, 2L ** 63 - 1)
937 fc1e2f02 Sofia Papagiannaki
        try:
938 fc1e2f02 Sofia Papagiannaki
            Invitation.objects.get(code=code)
939 fc1e2f02 Sofia Papagiannaki
            # An invitation with this code already exists, try again
940 fc1e2f02 Sofia Papagiannaki
        except Invitation.DoesNotExist:
941 fc1e2f02 Sofia Papagiannaki
            return code
942 fc1e2f02 Sofia Papagiannaki
943 5ce3ce4f Sofia Papagiannaki
944 fc1e2f02 Sofia Papagiannaki
def get_latest_terms():
945 fc1e2f02 Sofia Papagiannaki
    try:
946 fc1e2f02 Sofia Papagiannaki
        term = ApprovalTerms.objects.order_by('-id')[0]
947 fc1e2f02 Sofia Papagiannaki
        return term
948 fc1e2f02 Sofia Papagiannaki
    except IndexError:
949 fc1e2f02 Sofia Papagiannaki
        pass
950 fc1e2f02 Sofia Papagiannaki
    return None
951 fc1e2f02 Sofia Papagiannaki
952 ef20ea07 Sofia Papagiannaki
class PendingThirdPartyUser(models.Model):
953 ef20ea07 Sofia Papagiannaki
    """
954 ef20ea07 Sofia Papagiannaki
    Model for registring successful third party user authentications
955 ef20ea07 Sofia Papagiannaki
    """
956 e1a80257 Sofia Papagiannaki
    third_party_identifier = models.CharField(_('Third-party identifier'), max_length=255, null=True, blank=True)
957 e1a80257 Sofia Papagiannaki
    provider = models.CharField(_('Provider'), max_length=255, blank=True)
958 678b2236 Sofia Papagiannaki
    email = models.EmailField(_('e-mail address'), blank=True, null=True)
959 ef20ea07 Sofia Papagiannaki
    first_name = models.CharField(_('first name'), max_length=30, blank=True)
960 ef20ea07 Sofia Papagiannaki
    last_name = models.CharField(_('last name'), max_length=30, blank=True)
961 ef20ea07 Sofia Papagiannaki
    affiliation = models.CharField('Affiliation', max_length=255, blank=True)
962 ef20ea07 Sofia Papagiannaki
    username = models.CharField(_('username'), max_length=30, unique=True, help_text=_("Required. 30 characters or fewer. Letters, numbers and @/./+/-/_ characters"))
963 e1a80257 Sofia Papagiannaki
    token = models.CharField(_('Token'), max_length=255, null=True, blank=True)
964 d2633501 Kostas Papadimitriou
    created = models.DateTimeField(auto_now_add=True, null=True, blank=True)
965 d2633501 Kostas Papadimitriou
966 678b2236 Sofia Papagiannaki
    class Meta:
967 678b2236 Sofia Papagiannaki
        unique_together = ("provider", "third_party_identifier")
968 ef20ea07 Sofia Papagiannaki
969 ef20ea07 Sofia Papagiannaki
    @property
970 ef20ea07 Sofia Papagiannaki
    def realname(self):
971 ef20ea07 Sofia Papagiannaki
        return '%s %s' %(self.first_name, self.last_name)
972 ef20ea07 Sofia Papagiannaki
973 ef20ea07 Sofia Papagiannaki
    @realname.setter
974 ef20ea07 Sofia Papagiannaki
    def realname(self, value):
975 ef20ea07 Sofia Papagiannaki
        parts = value.split(' ')
976 ef20ea07 Sofia Papagiannaki
        if len(parts) == 2:
977 ef20ea07 Sofia Papagiannaki
            self.first_name = parts[0]
978 ef20ea07 Sofia Papagiannaki
            self.last_name = parts[1]
979 ef20ea07 Sofia Papagiannaki
        else:
980 ef20ea07 Sofia Papagiannaki
            self.last_name = parts[0]
981 2e90e3ec Kostas Papadimitriou
982 ef20ea07 Sofia Papagiannaki
    def save(self, **kwargs):
983 ef20ea07 Sofia Papagiannaki
        if not self.id:
984 ef20ea07 Sofia Papagiannaki
            # set username
985 ef20ea07 Sofia Papagiannaki
            while not self.username:
986 ef20ea07 Sofia Papagiannaki
                username =  uuid.uuid4().hex[:30]
987 ef20ea07 Sofia Papagiannaki
                try:
988 ef20ea07 Sofia Papagiannaki
                    AstakosUser.objects.get(username = username)
989 ef20ea07 Sofia Papagiannaki
                except AstakosUser.DoesNotExist, e:
990 ef20ea07 Sofia Papagiannaki
                    self.username = username
991 ef20ea07 Sofia Papagiannaki
        super(PendingThirdPartyUser, self).save(**kwargs)
992 ef20ea07 Sofia Papagiannaki
993 d2633501 Kostas Papadimitriou
    def generate_token(self):
994 d2633501 Kostas Papadimitriou
        self.password = self.third_party_identifier
995 d2633501 Kostas Papadimitriou
        self.last_login = datetime.now()
996 d2633501 Kostas Papadimitriou
        self.token = default_token_generator.make_token(self)
997 d2633501 Kostas Papadimitriou
998 bf0c6de5 Sofia Papagiannaki
class SessionCatalog(models.Model):
999 bf0c6de5 Sofia Papagiannaki
    session_key = models.CharField(_('session key'), max_length=40)
1000 bf0c6de5 Sofia Papagiannaki
    user = models.ForeignKey(AstakosUser, related_name='sessions', null=True)
1001 bf0c6de5 Sofia Papagiannaki
1002 e1a80257 Sofia Papagiannaki
class MemberAcceptPolicy(models.Model):
1003 e1a80257 Sofia Papagiannaki
    policy = models.CharField(_('Policy'), max_length=255, unique=True, db_index=True)
1004 e1a80257 Sofia Papagiannaki
    description = models.CharField(_('Description'), max_length=80)
1005 e1a80257 Sofia Papagiannaki
1006 e1a80257 Sofia Papagiannaki
    def __str__(self):
1007 e1a80257 Sofia Papagiannaki
        return self.policy
1008 e1a80257 Sofia Papagiannaki
1009 e1a80257 Sofia Papagiannaki
try:
1010 e1a80257 Sofia Papagiannaki
    auto_accept = MemberAcceptPolicy.objects.get(policy='auto_accept')
1011 e1a80257 Sofia Papagiannaki
except:
1012 e1a80257 Sofia Papagiannaki
    auto_accept = None
1013 e1a80257 Sofia Papagiannaki
1014 e1a80257 Sofia Papagiannaki
class ProjectDefinition(models.Model):
1015 e1a80257 Sofia Papagiannaki
    name = models.CharField(max_length=80)
1016 e1a80257 Sofia Papagiannaki
    homepage = models.URLField(max_length=255, null=True, blank=True)
1017 e1a80257 Sofia Papagiannaki
    description = models.TextField(null=True)
1018 e1a80257 Sofia Papagiannaki
    start_date = models.DateTimeField()
1019 e1a80257 Sofia Papagiannaki
    end_date = models.DateTimeField()
1020 e1a80257 Sofia Papagiannaki
    member_accept_policy = models.ForeignKey(MemberAcceptPolicy)
1021 e1a80257 Sofia Papagiannaki
    limit_on_members_number = models.PositiveIntegerField(null=True,blank=True)
1022 e1a80257 Sofia Papagiannaki
    resource_grants = models.ManyToManyField(
1023 e1a80257 Sofia Papagiannaki
        Resource,
1024 e1a80257 Sofia Papagiannaki
        null=True,
1025 e1a80257 Sofia Papagiannaki
        blank=True,
1026 e1a80257 Sofia Papagiannaki
        through='ProjectResourceGrant'
1027 e1a80257 Sofia Papagiannaki
    )
1028 e1a80257 Sofia Papagiannaki
    
1029 e1a80257 Sofia Papagiannaki
    def save(self):
1030 e1a80257 Sofia Papagiannaki
        self.validate_name()
1031 e1a80257 Sofia Papagiannaki
        super(ProjectDefinition, self).save()
1032 e1a80257 Sofia Papagiannaki
    
1033 e1a80257 Sofia Papagiannaki
    def validate_name(self):
1034 e1a80257 Sofia Papagiannaki
        """
1035 e1a80257 Sofia Papagiannaki
        Validate name uniqueness among all active projects.
1036 e1a80257 Sofia Papagiannaki
        """
1037 e1a80257 Sofia Papagiannaki
        alive_projects = list(get_alive_projects())
1038 e1a80257 Sofia Papagiannaki
        q = filter(lambda p: p.definition.name==self.name, alive_projects)
1039 e1a80257 Sofia Papagiannaki
        if q:
1040 e1a80257 Sofia Papagiannaki
            raise ValidationError({'name': [_(astakos_messages.UNIQUE_PROJECT_NAME_CONSTRAIN_ERR)]})
1041 e1a80257 Sofia Papagiannaki
    
1042 e1a80257 Sofia Papagiannaki
    @property
1043 e1a80257 Sofia Papagiannaki
    def violated_resource_grants(self):
1044 e1a80257 Sofia Papagiannaki
        return False
1045 e1a80257 Sofia Papagiannaki
    
1046 e1a80257 Sofia Papagiannaki
    def add_resource_policy(self, service, resource, uplimit, update=True):
1047 e1a80257 Sofia Papagiannaki
        """Raises ObjectDoesNotExist, IntegrityError"""
1048 e1a80257 Sofia Papagiannaki
        resource = Resource.objects.get(service__name=service, name=resource)
1049 e1a80257 Sofia Papagiannaki
        if update:
1050 e1a80257 Sofia Papagiannaki
            ResourceGrant.objects.update_or_create(
1051 e1a80257 Sofia Papagiannaki
                project=self,
1052 e1a80257 Sofia Papagiannaki
                resource=resource,
1053 e1a80257 Sofia Papagiannaki
                defaults={'uplimit': uplimit}
1054 e1a80257 Sofia Papagiannaki
            )
1055 e1a80257 Sofia Papagiannaki
        else:
1056 e1a80257 Sofia Papagiannaki
            q = self.resource_grants_set
1057 e1a80257 Sofia Papagiannaki
            q.create(resource=resource, uplimit=uplimit)
1058 e1a80257 Sofia Papagiannaki
1059 e1a80257 Sofia Papagiannaki
    @property
1060 e1a80257 Sofia Papagiannaki
    def resource_policies(self):
1061 e1a80257 Sofia Papagiannaki
        return self.resource_grants_set.select_related().all()
1062 e1a80257 Sofia Papagiannaki
1063 e1a80257 Sofia Papagiannaki
    @resource_policies.setter
1064 e1a80257 Sofia Papagiannaki
    def resource_policies(self, policies):
1065 e1a80257 Sofia Papagiannaki
        for p in policies:
1066 e1a80257 Sofia Papagiannaki
            service = p.get('service', None)
1067 e1a80257 Sofia Papagiannaki
            resource = p.get('resource', None)
1068 e1a80257 Sofia Papagiannaki
            uplimit = p.get('uplimit', 0)
1069 e1a80257 Sofia Papagiannaki
            update = p.get('update', True)
1070 e1a80257 Sofia Papagiannaki
            self.add_resource_policy(service, resource, uplimit, update)
1071 e1a80257 Sofia Papagiannaki
1072 e1a80257 Sofia Papagiannaki
1073 e1a80257 Sofia Papagiannaki
class ProjectResourceGrant(models.Model):
1074 e1a80257 Sofia Papagiannaki
    objects = ExtendedManager()
1075 e1a80257 Sofia Papagiannaki
    member_limit = models.BigIntegerField(null=True)
1076 e1a80257 Sofia Papagiannaki
    project_limit = models.BigIntegerField(null=True)
1077 e1a80257 Sofia Papagiannaki
    resource = models.ForeignKey(Resource)
1078 e1a80257 Sofia Papagiannaki
    project_definition = models.ForeignKey(ProjectDefinition, blank=True)
1079 e1a80257 Sofia Papagiannaki
1080 e1a80257 Sofia Papagiannaki
    class Meta:
1081 e1a80257 Sofia Papagiannaki
        unique_together = ("resource", "project_definition")
1082 e1a80257 Sofia Papagiannaki
1083 e1a80257 Sofia Papagiannaki
class ProjectApplication(models.Model):
1084 e1a80257 Sofia Papagiannaki
    serial = models.CharField(
1085 e1a80257 Sofia Papagiannaki
        primary_key=True,
1086 e1a80257 Sofia Papagiannaki
        max_length=30,
1087 e1a80257 Sofia Papagiannaki
        unique=True,
1088 e1a80257 Sofia Papagiannaki
        default=uuid.uuid4().hex[:30]
1089 e1a80257 Sofia Papagiannaki
    )
1090 e1a80257 Sofia Papagiannaki
    applicant = models.ForeignKey(AstakosUser, related_name='my_project_applications')
1091 e1a80257 Sofia Papagiannaki
    owner = models.ForeignKey(AstakosUser, related_name='own_project_applications')
1092 e1a80257 Sofia Papagiannaki
    comments = models.TextField(null=True, blank=True)
1093 e1a80257 Sofia Papagiannaki
    definition = models.OneToOneField(ProjectDefinition)
1094 e1a80257 Sofia Papagiannaki
    issue_date = models.DateTimeField()
1095 e1a80257 Sofia Papagiannaki
    precursor_application = models.OneToOneField('ProjectApplication',
1096 e1a80257 Sofia Papagiannaki
        null=True,
1097 e1a80257 Sofia Papagiannaki
        blank=True
1098 e1a80257 Sofia Papagiannaki
    )
1099 e1a80257 Sofia Papagiannaki
1100 e1a80257 Sofia Papagiannaki
class Project(models.Model):
1101 e1a80257 Sofia Papagiannaki
    serial = models.CharField(
1102 e1a80257 Sofia Papagiannaki
        _('username'),
1103 e1a80257 Sofia Papagiannaki
        primary_key=True,
1104 e1a80257 Sofia Papagiannaki
        max_length=30,
1105 e1a80257 Sofia Papagiannaki
        unique=True,
1106 e1a80257 Sofia Papagiannaki
        default=uuid.uuid4().hex[:30]
1107 e1a80257 Sofia Papagiannaki
    )
1108 e1a80257 Sofia Papagiannaki
    application = models.OneToOneField(ProjectApplication, related_name='project')
1109 e1a80257 Sofia Papagiannaki
    creation_date = models.DateTimeField()
1110 e1a80257 Sofia Papagiannaki
    last_approval_date = models.DateTimeField()
1111 e1a80257 Sofia Papagiannaki
    termination_date = models.DateTimeField()
1112 e1a80257 Sofia Papagiannaki
    members = models.ManyToManyField(AstakosUser, through='ProjectMembership')
1113 e1a80257 Sofia Papagiannaki
    last_synced_application = models.OneToOneField(
1114 e1a80257 Sofia Papagiannaki
        ProjectApplication, related_name='last_project', null=True, blank=True
1115 e1a80257 Sofia Papagiannaki
    )
1116 e1a80257 Sofia Papagiannaki
    
1117 e1a80257 Sofia Papagiannaki
    @property
1118 e1a80257 Sofia Papagiannaki
    def definition(self):
1119 e1a80257 Sofia Papagiannaki
        return self.application.definition
1120 e1a80257 Sofia Papagiannaki
    
1121 e1a80257 Sofia Papagiannaki
    @property
1122 e1a80257 Sofia Papagiannaki
    def is_valid(self):
1123 e1a80257 Sofia Papagiannaki
        try:
1124 e1a80257 Sofia Papagiannaki
            self.application.definition.validate_name()
1125 e1a80257 Sofia Papagiannaki
        except ValidationError:
1126 e1a80257 Sofia Papagiannaki
            return False
1127 e1a80257 Sofia Papagiannaki
        else:
1128 e1a80257 Sofia Papagiannaki
            return True
1129 e1a80257 Sofia Papagiannaki
    
1130 e1a80257 Sofia Papagiannaki
    @property
1131 e1a80257 Sofia Papagiannaki
    def is_active(self):
1132 e1a80257 Sofia Papagiannaki
        if not self.is_valid:
1133 e1a80257 Sofia Papagiannaki
            return False
1134 e1a80257 Sofia Papagiannaki
        if not self.last_approval_date:
1135 e1a80257 Sofia Papagiannaki
            return False
1136 e1a80257 Sofia Papagiannaki
        if self.termination_date:
1137 e1a80257 Sofia Papagiannaki
            return False
1138 e1a80257 Sofia Papagiannaki
        if self.definition.violated_resource_grants:
1139 e1a80257 Sofia Papagiannaki
            return False
1140 e1a80257 Sofia Papagiannaki
        return True
1141 e1a80257 Sofia Papagiannaki
    
1142 e1a80257 Sofia Papagiannaki
    @property
1143 e1a80257 Sofia Papagiannaki
    def is_terminated(self):
1144 e1a80257 Sofia Papagiannaki
        if not self.is_valid:
1145 e1a80257 Sofia Papagiannaki
            return False
1146 e1a80257 Sofia Papagiannaki
        if not self.termination_date:
1147 e1a80257 Sofia Papagiannaki
            return False
1148 e1a80257 Sofia Papagiannaki
        return True
1149 e1a80257 Sofia Papagiannaki
    
1150 e1a80257 Sofia Papagiannaki
    @property
1151 e1a80257 Sofia Papagiannaki
    def is_suspended(self):
1152 e1a80257 Sofia Papagiannaki
        if not self.is_valid:
1153 e1a80257 Sofia Papagiannaki
            return False
1154 e1a80257 Sofia Papagiannaki
        if not self.termination_date:
1155 e1a80257 Sofia Papagiannaki
            return False
1156 e1a80257 Sofia Papagiannaki
        if not self.last_approval_date:
1157 e1a80257 Sofia Papagiannaki
            if not self.definition.violated_resource_grants:
1158 e1a80257 Sofia Papagiannaki
                return False
1159 e1a80257 Sofia Papagiannaki
        return True
1160 e1a80257 Sofia Papagiannaki
    
1161 e1a80257 Sofia Papagiannaki
    @property
1162 e1a80257 Sofia Papagiannaki
    def is_alive(self):
1163 e1a80257 Sofia Papagiannaki
        return self.is_active or self.is_suspended
1164 e1a80257 Sofia Papagiannaki
    
1165 e1a80257 Sofia Papagiannaki
    @property
1166 e1a80257 Sofia Papagiannaki
    def is_inconsistent(self):
1167 e1a80257 Sofia Papagiannaki
        now = datetime.now()
1168 e1a80257 Sofia Papagiannaki
        if self.creation_date > now:
1169 e1a80257 Sofia Papagiannaki
            return True
1170 e1a80257 Sofia Papagiannaki
        if self.last_approval_date > now:
1171 e1a80257 Sofia Papagiannaki
            return True
1172 e1a80257 Sofia Papagiannaki
        if self.terminaton_date > now:
1173 e1a80257 Sofia Papagiannaki
            return True
1174 e1a80257 Sofia Papagiannaki
        return False
1175 e1a80257 Sofia Papagiannaki
    
1176 e1a80257 Sofia Papagiannaki
    @property
1177 e1a80257 Sofia Papagiannaki
    def approved_members(self):
1178 e1a80257 Sofia Papagiannaki
        return self.members.filter(is_accepted=True)
1179 e1a80257 Sofia Papagiannaki
    
1180 e1a80257 Sofia Papagiannaki
    def suspend(self):
1181 e1a80257 Sofia Papagiannaki
        self.last_approval_date = None
1182 e1a80257 Sofia Papagiannaki
        self.save()
1183 e1a80257 Sofia Papagiannaki
    
1184 e1a80257 Sofia Papagiannaki
    def terminate(self):
1185 e1a80257 Sofia Papagiannaki
        self.terminaton_date = datetime.now()
1186 e1a80257 Sofia Papagiannaki
        self.save()
1187 e1a80257 Sofia Papagiannaki
    
1188 e1a80257 Sofia Papagiannaki
    def sync(self):
1189 e1a80257 Sofia Papagiannaki
        c, rejected = send_quota(self.approved_members)
1190 e1a80257 Sofia Papagiannaki
        return rejected
1191 e1a80257 Sofia Papagiannaki
1192 e1a80257 Sofia Papagiannaki
class ProjectMembership(models.Model):
1193 e1a80257 Sofia Papagiannaki
    person = models.ForeignKey(AstakosUser)
1194 e1a80257 Sofia Papagiannaki
    project = models.ForeignKey(Project)
1195 e1a80257 Sofia Papagiannaki
    issue_date = models.DateField(default=datetime.now())
1196 e1a80257 Sofia Papagiannaki
    decision_date = models.DateField(null=True, db_index=True)
1197 e1a80257 Sofia Papagiannaki
    is_accepted = models.BooleanField(
1198 e1a80257 Sofia Papagiannaki
        _('Whether the membership application is accepted'),
1199 e1a80257 Sofia Papagiannaki
        default=False
1200 e1a80257 Sofia Papagiannaki
    )
1201 e1a80257 Sofia Papagiannaki
1202 e1a80257 Sofia Papagiannaki
    class Meta:
1203 e1a80257 Sofia Papagiannaki
        unique_together = ("person", "project")
1204 e1a80257 Sofia Papagiannaki
1205 e1a80257 Sofia Papagiannaki
def filter_queryset_by_property(q, property):
1206 e1a80257 Sofia Papagiannaki
    """
1207 e1a80257 Sofia Papagiannaki
    Incorporate list comprehension for filtering querysets by property
1208 e1a80257 Sofia Papagiannaki
    since Queryset.filter() operates on the database level.
1209 e1a80257 Sofia Papagiannaki
    """
1210 e1a80257 Sofia Papagiannaki
    return (p for p in q if getattr(p, property, False))
1211 e1a80257 Sofia Papagiannaki
1212 e1a80257 Sofia Papagiannaki
def get_alive_projects():
1213 e1a80257 Sofia Papagiannaki
    return filter_queryset_by_property(
1214 e1a80257 Sofia Papagiannaki
        Project.objects.all(),
1215 e1a80257 Sofia Papagiannaki
        'is_alive'
1216 e1a80257 Sofia Papagiannaki
    )
1217 e1a80257 Sofia Papagiannaki
1218 e1a80257 Sofia Papagiannaki
def get_active_projects():
1219 e1a80257 Sofia Papagiannaki
    return filter_queryset_by_property(
1220 e1a80257 Sofia Papagiannaki
        Project.objects.all(),
1221 e1a80257 Sofia Papagiannaki
        'is_active'
1222 e1a80257 Sofia Papagiannaki
    )
1223 e1a80257 Sofia Papagiannaki
1224 e1a80257 Sofia Papagiannaki
def _lookup_object(model, **kwargs):
1225 e1a80257 Sofia Papagiannaki
    """
1226 e1a80257 Sofia Papagiannaki
    Returns an object of the specific model matching the given lookup
1227 e1a80257 Sofia Papagiannaki
    parameters.
1228 e1a80257 Sofia Papagiannaki
    """
1229 e1a80257 Sofia Papagiannaki
    if not kwargs:
1230 e1a80257 Sofia Papagiannaki
        raise MissingIdentifier
1231 e1a80257 Sofia Papagiannaki
    try:
1232 e1a80257 Sofia Papagiannaki
        return model.objects.get(**kwargs)
1233 e1a80257 Sofia Papagiannaki
    except model.DoesNotExist:
1234 e1a80257 Sofia Papagiannaki
        raise ItemNotExists(model._meta.verbose_name, **kwargs)
1235 e1a80257 Sofia Papagiannaki
    except model.MultipleObjectsReturned:
1236 e1a80257 Sofia Papagiannaki
        raise MultipleItemsExist(model._meta.verbose_name, **kwargs)
1237 e1a80257 Sofia Papagiannaki
1238 e1a80257 Sofia Papagiannaki
def _create_object(model, **kwargs):
1239 e1a80257 Sofia Papagiannaki
    o = model.objects.create(**kwargs)
1240 e1a80257 Sofia Papagiannaki
    o.save()
1241 e1a80257 Sofia Papagiannaki
    return o
1242 e1a80257 Sofia Papagiannaki
1243 e1a80257 Sofia Papagiannaki
def _update_object(model, id, save=True, **kwargs):
1244 e1a80257 Sofia Papagiannaki
    o = self._lookup_object(model, id=id)
1245 e1a80257 Sofia Papagiannaki
    if kwargs:
1246 e1a80257 Sofia Papagiannaki
        o.__dict__.update(kwargs)
1247 e1a80257 Sofia Papagiannaki
    if save:
1248 e1a80257 Sofia Papagiannaki
        o.save()
1249 e1a80257 Sofia Papagiannaki
    return o
1250 e1a80257 Sofia Papagiannaki
1251 e1a80257 Sofia Papagiannaki
def submit_application(**kwargs):
1252 e1a80257 Sofia Papagiannaki
    app = self._create_object(ProjectApplication, **kwargs)
1253 e1a80257 Sofia Papagiannaki
    notification = build_notification(
1254 e1a80257 Sofia Papagiannaki
        settings.SERVER_EMAIL,
1255 e1a80257 Sofia Papagiannaki
        [settings.ADMINS],
1256 e1a80257 Sofia Papagiannaki
        _(GROUP_CREATION_SUBJECT) % {'group':app.definition.name},
1257 e1a80257 Sofia Papagiannaki
        _('An new project application identified by %(serial)s has been submitted.') % app.serial
1258 e1a80257 Sofia Papagiannaki
    )
1259 e1a80257 Sofia Papagiannaki
    notification.send()
1260 e1a80257 Sofia Papagiannaki
1261 e1a80257 Sofia Papagiannaki
def list_applications():
1262 e1a80257 Sofia Papagiannaki
    return ProjectAppication.objects.all()
1263 e1a80257 Sofia Papagiannaki
1264 e1a80257 Sofia Papagiannaki
def create_application(definition, applicant, comments, precursor_application=None, commit=True):
1265 e1a80257 Sofia Papagiannaki
    if precursor_application:
1266 e1a80257 Sofia Papagiannaki
        application = precursor_application.copy()
1267 e1a80257 Sofia Papagiannaki
        application.precursor_application = precursor_application
1268 e1a80257 Sofia Papagiannaki
    else:
1269 e1a80257 Sofia Papagiannaki
        application = ProjectApplication(owner=applicant)
1270 e1a80257 Sofia Papagiannaki
    application.definition = definition
1271 e1a80257 Sofia Papagiannaki
    application.applicant = applicant
1272 e1a80257 Sofia Papagiannaki
    application.comments = comments
1273 e1a80257 Sofia Papagiannaki
    application.issue_date = datetime.now()
1274 e1a80257 Sofia Papagiannaki
    if commit:
1275 e1a80257 Sofia Papagiannaki
        definition.save()
1276 e1a80257 Sofia Papagiannaki
        application.save()
1277 e1a80257 Sofia Papagiannaki
    return application
1278 e1a80257 Sofia Papagiannaki
    
1279 e1a80257 Sofia Papagiannaki
def approve_application(serial):
1280 e1a80257 Sofia Papagiannaki
    app = _lookup_object(ProjectAppication, serial=serial)
1281 e1a80257 Sofia Papagiannaki
    notify = False
1282 e1a80257 Sofia Papagiannaki
    if not app.precursor_application:
1283 e1a80257 Sofia Papagiannaki
        kwargs = {
1284 e1a80257 Sofia Papagiannaki
            'application':app,
1285 e1a80257 Sofia Papagiannaki
            'creation_date':datetime.now(),
1286 e1a80257 Sofia Papagiannaki
            'last_approval_date':datetime.now(),
1287 e1a80257 Sofia Papagiannaki
        }
1288 e1a80257 Sofia Papagiannaki
        project = _create_object(Project, **kwargs)
1289 e1a80257 Sofia Papagiannaki
    else:
1290 e1a80257 Sofia Papagiannaki
        project = app.precursor_application.project
1291 e1a80257 Sofia Papagiannaki
        last_approval_date = project.last_approval_date
1292 e1a80257 Sofia Papagiannaki
        if project.is_valid:
1293 e1a80257 Sofia Papagiannaki
            project.application = app
1294 e1a80257 Sofia Papagiannaki
            project.last_approval_date = datetime.now()
1295 e1a80257 Sofia Papagiannaki
            project.save()
1296 e1a80257 Sofia Papagiannaki
        else:
1297 e1a80257 Sofia Papagiannaki
            raise Exception(_(astakos_messages.INVALID_PROJECT) % project.__dict__)
1298 e1a80257 Sofia Papagiannaki
    
1299 e1a80257 Sofia Papagiannaki
    rejected = synchonize_project(project.serial)
1300 e1a80257 Sofia Papagiannaki
    if rejected:
1301 e1a80257 Sofia Papagiannaki
        # revert to precursor
1302 e1a80257 Sofia Papagiannaki
        project.appication = app.precursor_application
1303 e1a80257 Sofia Papagiannaki
        if project.application:
1304 e1a80257 Sofia Papagiannaki
            project.last_approval_date = last_approval_date
1305 e1a80257 Sofia Papagiannaki
        project.save()
1306 e1a80257 Sofia Papagiannaki
        rejected = synchonize_project(project.serial)
1307 e1a80257 Sofia Papagiannaki
        if rejected:
1308 e1a80257 Sofia Papagiannaki
            raise Exception(_(astakos_messages.QH_SYNC_ERROR))
1309 e1a80257 Sofia Papagiannaki
    else:
1310 e1a80257 Sofia Papagiannaki
        project.last_application_synced = app
1311 e1a80257 Sofia Papagiannaki
        project.save()
1312 e1a80257 Sofia Papagiannaki
        sender, recipients, subject, message
1313 e1a80257 Sofia Papagiannaki
        notification = build_notification(
1314 e1a80257 Sofia Papagiannaki
            settings.SERVER_EMAIL,
1315 e1a80257 Sofia Papagiannaki
            [project.owner.email],
1316 e1a80257 Sofia Papagiannaki
            _('Project application has been approved on %s alpha2 testing' % SITENAME),
1317 e1a80257 Sofia Papagiannaki
            _('Your application request %(serial)s has been apporved.')
1318 e1a80257 Sofia Papagiannaki
        )
1319 e1a80257 Sofia Papagiannaki
        notification.send()
1320 e1a80257 Sofia Papagiannaki
1321 e1a80257 Sofia Papagiannaki
1322 e1a80257 Sofia Papagiannaki
def list_projects(filter_property=None):
1323 e1a80257 Sofia Papagiannaki
    if filter_property:
1324 e1a80257 Sofia Papagiannaki
        return filter_queryset_by_property(
1325 e1a80257 Sofia Papagiannaki
            Project.objects.all(),
1326 e1a80257 Sofia Papagiannaki
            filter_property
1327 e1a80257 Sofia Papagiannaki
        )
1328 e1a80257 Sofia Papagiannaki
    return Project.objects.all()
1329 e1a80257 Sofia Papagiannaki
1330 e1a80257 Sofia Papagiannaki
def add_project_member(serial, user_id, request_user):
1331 e1a80257 Sofia Papagiannaki
    project = _lookup_object(Project, serial=serial)
1332 e1a80257 Sofia Papagiannaki
    user = _lookup_object(AstakosUser, id=user_id)
1333 e1a80257 Sofia Papagiannaki
    if not project.owner == request_user:
1334 e1a80257 Sofia Papagiannaki
        raise Exception(_(astakos_messages.NOT_PROJECT_OWNER))
1335 e1a80257 Sofia Papagiannaki
    
1336 e1a80257 Sofia Papagiannaki
    if not project.is_alive:
1337 e1a80257 Sofia Papagiannaki
        raise Exception(_(astakos_messages.NOT_ALIVE_PROJECT) % project.__dict__)
1338 e1a80257 Sofia Papagiannaki
    if len(project.members) + 1 > project.limit_on_members_number:
1339 e1a80257 Sofia Papagiannaki
        raise Exception(_(astakos_messages.MEMBER_NUMBER_LIMIT_REACHED))
1340 e1a80257 Sofia Papagiannaki
    m = self._lookup_object(ProjectMembership, person=user, project=project)
1341 e1a80257 Sofia Papagiannaki
    if m.is_accepted:
1342 e1a80257 Sofia Papagiannaki
        return
1343 e1a80257 Sofia Papagiannaki
    m.is_accepted = True
1344 e1a80257 Sofia Papagiannaki
    m.decision_date = datetime.now()
1345 e1a80257 Sofia Papagiannaki
    m.save()
1346 e1a80257 Sofia Papagiannaki
    notification = build_notification(
1347 e1a80257 Sofia Papagiannaki
        settings.SERVER_EMAIL,
1348 e1a80257 Sofia Papagiannaki
        [user.email],
1349 e1a80257 Sofia Papagiannaki
        _('Your membership on project %(name)s has been accepted.') % project.definition.__dict__, 
1350 e1a80257 Sofia Papagiannaki
        _('Your membership on project %(name)s has been accepted.') % project.definition.__dict__,
1351 e1a80257 Sofia Papagiannaki
    )
1352 e1a80257 Sofia Papagiannaki
    notification.send()
1353 e1a80257 Sofia Papagiannaki
1354 e1a80257 Sofia Papagiannaki
def remove_project_member(serial, user_id, request_user):
1355 e1a80257 Sofia Papagiannaki
    project = _lookup_object(Project, serial=serial)
1356 e1a80257 Sofia Papagiannaki
    if not project.is_alive:
1357 e1a80257 Sofia Papagiannaki
        raise Exception(_(astakos_messages.NOT_ALIVE_PROJECT) % project.__dict__)
1358 e1a80257 Sofia Papagiannaki
    if not project.owner == request_user:
1359 e1a80257 Sofia Papagiannaki
        raise Exception(_(astakos_messages.NOT_PROJECT_OWNER))
1360 e1a80257 Sofia Papagiannaki
    user = self.lookup_user(user_id)
1361 e1a80257 Sofia Papagiannaki
    m = _lookup_object(ProjectMembership, person=user, project=project)
1362 e1a80257 Sofia Papagiannaki
    if not m.is_accepted:
1363 e1a80257 Sofia Papagiannaki
        return
1364 e1a80257 Sofia Papagiannaki
    m.is_accepted = False
1365 e1a80257 Sofia Papagiannaki
    m.decision_date = datetime.now()
1366 e1a80257 Sofia Papagiannaki
    m.save()
1367 e1a80257 Sofia Papagiannaki
    notification = build_notification(
1368 e1a80257 Sofia Papagiannaki
        settings.SERVER_EMAIL,
1369 e1a80257 Sofia Papagiannaki
        [user.email],
1370 e1a80257 Sofia Papagiannaki
        _('Your membership on project %(name)s has been removed.') % project.definition.__dict__,
1371 e1a80257 Sofia Papagiannaki
        _('Your membership on project %(name)s has been removed.') % project.definition.__dict__
1372 e1a80257 Sofia Papagiannaki
    )
1373 e1a80257 Sofia Papagiannaki
    notification.send()    
1374 e1a80257 Sofia Papagiannaki
1375 e1a80257 Sofia Papagiannaki
def suspend_project(serial):
1376 e1a80257 Sofia Papagiannaki
    project = _lookup_object(Project, serial=serial)
1377 e1a80257 Sofia Papagiannaki
    project.suspend()
1378 e1a80257 Sofia Papagiannaki
    notification = build_notification(
1379 e1a80257 Sofia Papagiannaki
        settings.SERVER_EMAIL,
1380 e1a80257 Sofia Papagiannaki
        [project.owner.email],
1381 e1a80257 Sofia Papagiannaki
        _('Project %(name)s has been suspended.') %  project.definition.__dict__,
1382 e1a80257 Sofia Papagiannaki
        _('Project %(name)s has been suspended.') %  project.definition.__dict__
1383 e1a80257 Sofia Papagiannaki
    )
1384 e1a80257 Sofia Papagiannaki
    notification.send()
1385 e1a80257 Sofia Papagiannaki
1386 e1a80257 Sofia Papagiannaki
def terminate_project(serial):
1387 e1a80257 Sofia Papagiannaki
    project = _lookup_object(Project, serial=serial)
1388 e1a80257 Sofia Papagiannaki
    project.termination()
1389 e1a80257 Sofia Papagiannaki
    notification = build_notification(
1390 e1a80257 Sofia Papagiannaki
        settings.SERVER_EMAIL,
1391 e1a80257 Sofia Papagiannaki
        [project.owner.email],
1392 e1a80257 Sofia Papagiannaki
        _('Project %(name)s has been terminated.') %  project.definition.__dict__,
1393 e1a80257 Sofia Papagiannaki
        _('Project %(name)s has been terminated.') %  project.definition.__dict__
1394 e1a80257 Sofia Papagiannaki
    )
1395 e1a80257 Sofia Papagiannaki
    notification.send()
1396 5ce3ce4f Sofia Papagiannaki
1397 e1a80257 Sofia Papagiannaki
def synchonize_project(serial):
1398 e1a80257 Sofia Papagiannaki
    project = _lookup_object(Project, serial=serial)
1399 e1a80257 Sofia Papagiannaki
    if project.app != project.last_application_synced:
1400 e1a80257 Sofia Papagiannaki
        return project.sync()
1401 e1a80257 Sofia Papagiannaki
     
1402 ff9290ec Sofia Papagiannaki
def create_astakos_user(u):
1403 ff9290ec Sofia Papagiannaki
    try:
1404 ff9290ec Sofia Papagiannaki
        AstakosUser.objects.get(user_ptr=u.pk)
1405 ff9290ec Sofia Papagiannaki
    except AstakosUser.DoesNotExist:
1406 ff9290ec Sofia Papagiannaki
        extended_user = AstakosUser(user_ptr_id=u.pk)
1407 ff9290ec Sofia Papagiannaki
        extended_user.__dict__.update(u.__dict__)
1408 ff9290ec Sofia Papagiannaki
        extended_user.save()
1409 67be1883 Olga Brani
        if not extended_user.has_auth_provider('local'):
1410 67be1883 Olga Brani
            extended_user.add_auth_provider('local')
1411 fc1e2f02 Sofia Papagiannaki
    except BaseException, e:
1412 fc1e2f02 Sofia Papagiannaki
        logger.exception(e)
1413 ff9290ec Sofia Papagiannaki
1414 5ce3ce4f Sofia Papagiannaki
1415 fc1e2f02 Sofia Papagiannaki
def fix_superusers(sender, **kwargs):
1416 fc1e2f02 Sofia Papagiannaki
    # Associate superusers with AstakosUser
1417 ff9290ec Sofia Papagiannaki
    admins = User.objects.filter(is_superuser=True)
1418 ff9290ec Sofia Papagiannaki
    for u in admins:
1419 ff9290ec Sofia Papagiannaki
        create_astakos_user(u)
1420 ff9290ec Sofia Papagiannaki
1421 ff9290ec Sofia Papagiannaki
1422 fc1e2f02 Sofia Papagiannaki
def user_post_save(sender, instance, created, **kwargs):
1423 aa4109d4 Sofia Papagiannaki
    if not created:
1424 aa4109d4 Sofia Papagiannaki
        return
1425 fc1e2f02 Sofia Papagiannaki
    create_astakos_user(instance)
1426 fc1e2f02 Sofia Papagiannaki
1427 5ce3ce4f Sofia Papagiannaki
1428 fc1e2f02 Sofia Papagiannaki
def set_default_group(user):
1429 aa4109d4 Sofia Papagiannaki
    try:
1430 5ce3ce4f Sofia Papagiannaki
        default = AstakosGroup.objects.get(name='default')
1431 5ce3ce4f Sofia Papagiannaki
        Membership(
1432 5ce3ce4f Sofia Papagiannaki
            group=default, person=user, date_joined=datetime.now()).save()
1433 aa4109d4 Sofia Papagiannaki
    except AstakosGroup.DoesNotExist, e:
1434 aa4109d4 Sofia Papagiannaki
        logger.exception(e)
1435 ff9290ec Sofia Papagiannaki
1436 bf0c6de5 Sofia Papagiannaki
1437 fc1e2f02 Sofia Papagiannaki
def astakosuser_pre_save(sender, instance, **kwargs):
1438 fc1e2f02 Sofia Papagiannaki
    instance.aquarium_report = False
1439 fc1e2f02 Sofia Papagiannaki
    instance.new = False
1440 fc1e2f02 Sofia Papagiannaki
    try:
1441 5ce3ce4f Sofia Papagiannaki
        db_instance = AstakosUser.objects.get(id=instance.id)
1442 fc1e2f02 Sofia Papagiannaki
    except AstakosUser.DoesNotExist:
1443 fc1e2f02 Sofia Papagiannaki
        # create event
1444 fc1e2f02 Sofia Papagiannaki
        instance.aquarium_report = True
1445 fc1e2f02 Sofia Papagiannaki
        instance.new = True
1446 fc1e2f02 Sofia Papagiannaki
    else:
1447 fc1e2f02 Sofia Papagiannaki
        get = AstakosUser.__getattribute__
1448 fc1e2f02 Sofia Papagiannaki
        l = filter(lambda f: get(db_instance, f) != get(instance, f),
1449 9a06d96f Olga Brani
                   BILLING_FIELDS)
1450 fc1e2f02 Sofia Papagiannaki
        instance.aquarium_report = True if l else False
1451 fc1e2f02 Sofia Papagiannaki
1452 5ce3ce4f Sofia Papagiannaki
1453 fc1e2f02 Sofia Papagiannaki
def astakosuser_post_save(sender, instance, created, **kwargs):
1454 fc1e2f02 Sofia Papagiannaki
    if instance.aquarium_report:
1455 fc1e2f02 Sofia Papagiannaki
        report_user_event(instance, create=instance.new)
1456 fc1e2f02 Sofia Papagiannaki
    if not created:
1457 fc1e2f02 Sofia Papagiannaki
        return
1458 fc1e2f02 Sofia Papagiannaki
    set_default_group(instance)
1459 fc1e2f02 Sofia Papagiannaki
    # TODO handle socket.error & IOError
1460 fc1e2f02 Sofia Papagiannaki
    register_users((instance,))
1461 fc1e2f02 Sofia Papagiannaki
1462 5ce3ce4f Sofia Papagiannaki
1463 bd4f356c Sofia Papagiannaki
def resource_post_save(sender, instance, created, **kwargs):
1464 bd4f356c Sofia Papagiannaki
    if not created:
1465 bd4f356c Sofia Papagiannaki
        return
1466 bd4f356c Sofia Papagiannaki
    register_resources((instance,))
1467 bd4f356c Sofia Papagiannaki
1468 bd4f356c Sofia Papagiannaki
1469 fc1e2f02 Sofia Papagiannaki
def send_quota_disturbed(sender, instance, **kwargs):
1470 fc1e2f02 Sofia Papagiannaki
    users = []
1471 fc1e2f02 Sofia Papagiannaki
    extend = users.extend
1472 fc1e2f02 Sofia Papagiannaki
    if sender == Membership:
1473 fc1e2f02 Sofia Papagiannaki
        if not instance.group.is_enabled:
1474 fc1e2f02 Sofia Papagiannaki
            return
1475 fc1e2f02 Sofia Papagiannaki
        extend([instance.person])
1476 fc1e2f02 Sofia Papagiannaki
    elif sender == AstakosUserQuota:
1477 fc1e2f02 Sofia Papagiannaki
        extend([instance.user])
1478 fc1e2f02 Sofia Papagiannaki
    elif sender == AstakosGroupQuota:
1479 fc1e2f02 Sofia Papagiannaki
        if not instance.group.is_enabled:
1480 fc1e2f02 Sofia Papagiannaki
            return
1481 fc1e2f02 Sofia Papagiannaki
        extend(instance.group.astakosuser_set.all())
1482 fc1e2f02 Sofia Papagiannaki
    elif sender == AstakosGroup:
1483 fc1e2f02 Sofia Papagiannaki
        if not instance.is_enabled:
1484 fc1e2f02 Sofia Papagiannaki
            return
1485 fc1e2f02 Sofia Papagiannaki
    quota_disturbed.send(sender=sender, users=users)
1486 fc1e2f02 Sofia Papagiannaki
1487 5ce3ce4f Sofia Papagiannaki
1488 fc1e2f02 Sofia Papagiannaki
def on_quota_disturbed(sender, users, **kwargs):
1489 c0b26605 Sofia Papagiannaki
#     print '>>>', locals()
1490 fc1e2f02 Sofia Papagiannaki
    if not users:
1491 fc1e2f02 Sofia Papagiannaki
        return
1492 fc1e2f02 Sofia Papagiannaki
    send_quota(users)
1493 bf0c6de5 Sofia Papagiannaki
1494 bf0c6de5 Sofia Papagiannaki
def renew_token(sender, instance, **kwargs):
1495 f9aea9c8 Sofia Papagiannaki
    if not instance.auth_token:
1496 bf0c6de5 Sofia Papagiannaki
        instance.renew_token()
1497 bf0c6de5 Sofia Papagiannaki
1498 fc1e2f02 Sofia Papagiannaki
post_syncdb.connect(fix_superusers)
1499 fc1e2f02 Sofia Papagiannaki
post_save.connect(user_post_save, sender=User)
1500 fc1e2f02 Sofia Papagiannaki
pre_save.connect(astakosuser_pre_save, sender=AstakosUser)
1501 fc1e2f02 Sofia Papagiannaki
post_save.connect(astakosuser_post_save, sender=AstakosUser)
1502 bd4f356c Sofia Papagiannaki
post_save.connect(resource_post_save, sender=Resource)
1503 fc1e2f02 Sofia Papagiannaki
1504 fc1e2f02 Sofia Papagiannaki
quota_disturbed = Signal(providing_args=["users"])
1505 fc1e2f02 Sofia Papagiannaki
quota_disturbed.connect(on_quota_disturbed)
1506 fc1e2f02 Sofia Papagiannaki
1507 fc1e2f02 Sofia Papagiannaki
post_delete.connect(send_quota_disturbed, sender=AstakosGroup)
1508 fc1e2f02 Sofia Papagiannaki
post_delete.connect(send_quota_disturbed, sender=Membership)
1509 fc1e2f02 Sofia Papagiannaki
post_save.connect(send_quota_disturbed, sender=AstakosUserQuota)
1510 fc1e2f02 Sofia Papagiannaki
post_delete.connect(send_quota_disturbed, sender=AstakosUserQuota)
1511 fc1e2f02 Sofia Papagiannaki
post_save.connect(send_quota_disturbed, sender=AstakosGroupQuota)
1512 a4075f5a root
post_delete.connect(send_quota_disturbed, sender=AstakosGroupQuota)
1513 c0b26605 Sofia Papagiannaki
1514 bf0c6de5 Sofia Papagiannaki
pre_save.connect(renew_token, sender=AstakosUser)
1515 2e90e3ec Kostas Papadimitriou
pre_save.connect(renew_token, sender=Service)