Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / models.py @ 2a965273

History | View | Annotate | Download (51.5 kB)

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

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

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

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

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

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