Revision fc1e2f02

b/snf-astakos-app/astakos/im/endpoints/aquarium/producer.py
1
# Copyright 2011-2012 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

  
34
import logging
35

  
36
from functools import wraps
37
from urlparse import urlparse
38

  
39
from astakos.im.settings import QUEUE_CONNECTION
40

  
41
if QUEUE_CONNECTION:
42
    from synnefo.lib.queue import (exchange_connect, exchange_send,
43
            exchange_close, UserEvent, Receipt
44
    )
45

  
46
QUEUE_CLIENT_ID = 3 # Astakos.
47
INSTANCE_ID = '1'
48
RESOURCE = 'addcredits'
49
DEFAULT_CREDITS = 1000
50

  
51
logging.basicConfig(format='%(asctime)s [%(levelname)s] %(name)s %(message)s',
52
        datefmt='%Y-%m-%d %H:%M:%S'
53
)
54
logger = logging.getLogger('endpoint.aquarium')
55

  
56
def wrapper(func):
57
    @wraps(func)
58
    def wrapper(*args, **kwargs):
59
        if not QUEUE_CONNECTION:
60
            return
61
        
62
        try:
63
            body, key = func(*args, **kwargs)
64
            conn = exchange_connect(QUEUE_CONNECTION)
65
            parts = urlparse(QUEUE_CONNECTION)
66
            exchange = parts.path[1:]
67
            routing_key = key % exchange
68
            exchange_send(conn, routing_key, body)
69
            exchange_close(conn)
70
        except BaseException, e:
71
            logger.exception(e)
72
    return wrapper
73

  
74
@wrapper
75
def report_user_event(user, create=False):
76
    eventType = 'create' if not create else 'modify'
77
    body = UserEvent(QUEUE_CLIENT_ID, user.email, user.is_active, eventType, {}
78
    ).format()
79
    routing_key = '%s.user'
80
    return body, routing_key
81

  
82
@wrapper
83
def report_user_credits_event(user):
84
    body = Receipt(QUEUE_CLIENT_ID, user.email, INSTANCE_ID, RESOURCE,
85
        DEFAULT_CREDITS, details={}
86
    ).format()
87
    routing_key = '%s.resource'
88
    return body, routing_key
89

  
90
def report_credits_event():
91
    # better approach?
92
    from astakos.im.models import AstakosUser
93
    map(report_user_credits_event, AstakosUser.objects.all())
b/snf-astakos-app/astakos/im/endpoints/quotaholder.py
1
# Copyright 2011-2012 GRNET S.A. All rights reserved.
2
# 
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
# 
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
# 
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
# 
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
# 
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

  
34
import socket
35
import logging
36

  
37
from django.utils.translation import ugettext as _
38

  
39
from commissioning.clients.quotaholder import QuotaholderHTTP
40

  
41
from astakos.im.settings import QUOTA_HOLDER_URL, LOGGING_LEVEL
42

  
43
ENTITY_KEY = '1'
44

  
45
logger = logging.getLogger(__name__)
46

  
47
def register_users(users, client=None):
48
    if not users:
49
        return
50
    
51
    if not QUOTA_HOLDER_URL:
52
        return
53
    
54
    c = client or QuotaholderHTTP(QUOTA_HOLDER_URL)
55
    data = []
56
    append = data.append
57
    for user in users:
58
        try:
59
            entity = user.email
60
        except AttributeError:
61
            continue
62
        else:    
63
            args = entity, owner, key, ownerkey = (
64
                entity, 'system', ENTITY_KEY, ''
65
            )
66
            append(args)
67
    
68
    if not data:
69
        return
70
    
71
    rejected = c.create_entity(
72
        context={},
73
        create_entity=data,
74
    )
75
    msg = _('Create entities: %s - Rejected: %s' % (data, rejected,))
76
    logger.log(LOGGING_LEVEL, msg)
77
    
78
    created = filter(lambda u: unicode(u.email) not in rejected, users)
79
    send_quota(created, c)
80
    return rejected
81

  
82
def send_quota(users, client=None):
83
    if not users:
84
        return
85
    
86
    if not QUOTA_HOLDER_URL:
87
        return
88
    
89
    c = client or QuotaholderHTTP(QUOTA_HOLDER_URL)
90
    data = []
91
    append = data.append
92
    for user in users:
93
        try:
94
            entity = user.email
95
        except AttributeError:
96
            continue
97
        else:
98
            for resource, limit in user.quota.iteritems():
99
                args = entity, resource, key, quantity, capacity, import_limit , \
100
                    export_limit, flags =(
101
                        entity, resource, ENTITY_KEY, '0', str(limit), 0, 0, 0
102
                )
103
                append(args)
104
    
105
    if not data:
106
        return
107
    
108
    rejected = c.set_quota(context={}, set_quota=data)
109
    msg = _('Set quota: %s - Rejected: %s' % (data, rejected,))
110
    logger.log(LOGGING_LEVEL, msg)
111
    return rejected
112

  
113
def get_quota(users, client=None):
114
    if not users:
115
        return
116
    
117
    if not QUOTA_HOLDER_URL:
118
        return
119
    
120
    c = client or QuotaholderHTTP(QUOTA_HOLDER_URL)
121
    data = []
122
    append = data.append
123
    for user in users:
124
        try:
125
            entity = user.email
126
        except AttributeError:
127
            continue
128
        else:
129
            for r in user.quota.keys():
130
                args = entity, resource, key = entity, r, ENTITY_KEY
131
                append(args)
132
    
133
    if not data:
134
        return
135
    
136
    r = c.get_quota(context={}, get_quota=data)
137
    msg = _('Get quota: %s' % data)
138
    logger.log(LOGGING_LEVEL, msg)
139
    return r
b/snf-astakos-app/astakos/im/management/commands/group_list.py
57 57
    )
58 58
    
59 59
    def handle(self, *args, **options):
60
        if args:
61
            raise CommandError("Command doesn't accept any arguments")
62
        
63 60
        groups = AstakosGroup.objects.all()
64 61
        
65 62
        if options.get('pending'):
b/snf-astakos-app/astakos/im/management/commands/group_update.py
54 54
            dest='enable',
55 55
            default=False,
56 56
            help="Enable group"),
57
        make_option('--disable',
58
            action='store_true',
59
            dest='disable',
60
            default=False,
61
            help="Disable group"),
57 62
    )
58 63
    
59 64
    def handle(self, *args, **options):
......
92 97
            
93 98
            if options.get('enable'):
94 99
                group.enable()
100
            elif options.get('disable'):
101
                group.disable()
102
        
95 103
        except Exception, e:
96 104
            raise CommandError(e)
b/snf-astakos-app/astakos/im/management/commands/quotaholder_bootstrap.py
1
# Copyright 2012 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

  
34
from django.core.management.base import BaseCommand, CommandError
35
from django.db.utils import IntegrityError
36

  
37
from astakos.im.models import AstakosUser
38
from astakos.im.endpoints.quotaholder import register_users
39

  
40
class Command(BaseCommand):
41
    help = "Send user information and resource quota in the Quotaholder"
42
    
43
    def handle(self, *args, **options):
44
        try:
45
            r = register_users(AstakosUser.objects.all())
46
            self.stdout.write("Rejected: %s\n" % r)
47
        except BaseException, e:
48
            print e
49
            raise CommandError("Bootstrap failed.")
b/snf-astakos-app/astakos/im/management/commands/user_update.py
39 39
from django.db.utils import IntegrityError
40 40

  
41 41
from astakos.im.models import AstakosUser, AstakosGroup, Membership
42
from astakos.im.endpoints.aquarium.producer import report_user_credits_event
42 43
from ._common import remove_user_permission, add_user_permission
43 44

  
44 45
class Command(BaseCommand):
......
104 105
        make_option('--delete-permission',
105 106
            dest='delete-permission',
106 107
            help="Delete user permission"),
108
        make_option('--refill-credits',
109
            action='store_true',
110
            dest='refill',
111
            default=False,
112
            help="Refill user credits"),
107 113
        )
108 114
    
109 115
    def handle(self, *args, **options):
......
201 207
        if options['renew_token']:
202 208
            user.renew_token()
203 209
        
210
        if options['refill']:
211
            report_user_credits_event(user)
212
        
204 213
        try:
205 214
            user.save()
206 215
        except ValidationError, e:
b/snf-astakos-app/astakos/im/models.py
38 38
from time import asctime
39 39
from datetime import datetime, timedelta
40 40
from base64 import b64encode
41
from urlparse import urlparse
42 41
from random import randint
43 42
from collections import defaultdict
44 43

  
......
47 46
from django.utils.translation import ugettext as _
48 47
from django.core.exceptions import ValidationError
49 48
from django.db import transaction
50
from django.db.models.signals import post_save, post_syncdb
49
from django.db.models.signals import pre_save, post_save, post_syncdb, post_delete
50
from django.dispatch import Signal
51 51
from django.db.models import Q
52 52

  
53
from astakos.im.settings import DEFAULT_USER_LEVEL, INVITATIONS_PER_LEVEL, \
54
    AUTH_TOKEN_DURATION, BILLING_FIELDS, QUEUE_CONNECTION, \
55
    EMAILCHANGE_ACTIVATION_DAYS, LOGGING_LEVEL
53
from astakos.im.settings import (DEFAULT_USER_LEVEL, INVITATIONS_PER_LEVEL,
54
    AUTH_TOKEN_DURATION, BILLING_FIELDS, EMAILCHANGE_ACTIVATION_DAYS, LOGGING_LEVEL
55
)
56
from astakos.im.endpoints.quotaholder import register_users, send_quota
57
from astakos.im.endpoints.aquarium.producer import report_user_event
56 58

  
57
QUEUE_CLIENT_ID = 3 # Astakos.
59
from astakos.im.tasks import propagate_groupmembers_quota
58 60

  
59 61
logger = logging.getLogger(__name__)
60 62

  
......
108 110
class AstakosGroup(Group):
109 111
    kind = models.ForeignKey(GroupKind)
110 112
    desc = models.TextField('Description', null=True)
111
    policy = models.ManyToManyField(Resource, null=True, blank=True, through='AstakosGroupQuota')
112
    creation_date = models.DateTimeField('Creation date', default=datetime.now())
113
    policy = models.ManyToManyField(Resource, null=True, blank=True,
114
        through='AstakosGroupQuota'
115
    )
116
    creation_date = models.DateTimeField('Creation date',
117
        default=datetime.now()
118
    )
113 119
    issue_date = models.DateTimeField('Issue date', null=True)
114 120
    expiration_date = models.DateTimeField('Expiration date', null=True)
115
    moderation_enabled = models.BooleanField('Moderated membership?', default=True)
116
    approval_date = models.DateTimeField('Activation date', null=True, blank=True)
117
    estimated_participants = models.PositiveIntegerField('Estimated #participants', null=True)
121
    moderation_enabled = models.BooleanField('Moderated membership?',
122
        default=True
123
    )
124
    approval_date = models.DateTimeField('Activation date', null=True,
125
        blank=True
126
    )
127
    estimated_participants = models.PositiveIntegerField('Estimated #members',
128
        null=True
129
    )
118 130
    
119 131
    @property
120 132
    def is_disabled(self):
......
137 149
            return False
138 150
        return True
139 151
    
140
    @property
141
    def participants(self):
142
        return len(self.approved_members)
152
#     @property
153
#     def participants(self):
154
#         return len(self.approved_members)
143 155
    
144 156
    def enable(self):
157
        if self.is_enabled:
158
            return
145 159
        self.approval_date = datetime.now()
146 160
        self.save()
161
        quota_disturbed.send(sender=self, users=approved_members)
162
        update_groupmembers_quota.apply_async(args=[self], eta=self.issue_date)
163
        update_groupmembers_quota.apply_async(args=[self], eta=self.expiration_date)
147 164
    
148 165
    def disable(self):
166
        if self.is_disabled:
167
            return
149 168
        self.approval_date = None
150 169
        self.save()
170
        quota_disturbed.send(sender=self, users=self.approved_members)
151 171
    
152 172
    def approve_member(self, person):
153 173
        m, created = self.membership_set.get_or_create(person=person)
......
160 180
    
161 181
    @property
162 182
    def members(self):
163
        return map(lambda m:m.person, self.membership_set.all())
183
        return [m.person for m in self.membership_set.all()]
164 184
    
165 185
    @property
166 186
    def approved_members(self):
167
        f = filter(lambda m:m.is_approved, self.membership_set.all())
168
        return map(lambda m:m.person, f)
187
        return [m.person for m in self.membership_set.all() if m.is_approved]
169 188
    
170 189
    @property
171 190
    def quota(self):
......
175 194
        return d
176 195
    
177 196
    @property
178
    def has_undefined_policies(self):
179
        # TODO: can avoid query?
180
        return Resource.objects.filter(~Q(astakosgroup=self)).exists()
181
    
182
    @property
183 197
    def owners(self):
184 198
        return self.owner.all()
185 199
    
......
266 280
        d = defaultdict(int)
267 281
        for q in  self.astakosuserquota_set.all():
268 282
            d[q.resource.name] += q.limit
269
        for g in self.astakos_groups.all():
283
        for m in self.membership_set.all():
284
            if not m.is_approved:
285
                continue
286
            g = m.group
270 287
            if not g.is_enabled:
271 288
                continue
272 289
            for r, limit in g.quota.iteritems():
......
294 311
                    self.username = username
295 312
            if not self.provider:
296 313
                self.provider = 'local'
297
        report_user_event(self)
298 314
        self.validate_unique_email_isactive()
299 315
        if self.is_active and self.activation_sent:
300 316
            # reset the activation sent
......
374 390
    def approve(self):
375 391
        self.date_joined = datetime.now()
376 392
        self.save()
377
        
393
        quota_disturbed.send(sender=self, users=(self.person,))
394
    
378 395
    def disapprove(self):
379 396
        self.delete()
397
        quota_disturbed.send(sender=self, users=(self.person,))
380 398

  
381 399
class AstakosGroupQuota(models.Model):
382 400
    limit = models.PositiveIntegerField('Limit')
......
428 446
    def __unicode__(self):
429 447
        return '%s -> %s [%d]' % (self.inviter, self.username, self.code)
430 448

  
431
def report_user_event(user):
432
    def should_send(user):
433
        # report event incase of new user instance
434
        # or if specific fields are modified
435
        if not user.id:
436
            return True
437
        try:
438
            db_instance = AstakosUser.objects.get(id = user.id)
439
        except AstakosUser.DoesNotExist:
440
            return True
441
        for f in BILLING_FIELDS:
442
            if (db_instance.__getattribute__(f) != user.__getattribute__(f)):
443
                return True
444
        return False
445

  
446
    if QUEUE_CONNECTION and should_send(user):
447

  
448
        from astakos.im.queue.userevent import UserEvent
449
        from synnefo.lib.queue import exchange_connect, exchange_send, \
450
                exchange_close
451

  
452
        eventType = 'create' if not user.id else 'modify'
453
        body = UserEvent(QUEUE_CLIENT_ID, user, eventType, {}).format()
454
        conn = exchange_connect(QUEUE_CONNECTION)
455
        parts = urlparse(QUEUE_CONNECTION)
456
        exchange = parts.path[1:]
457
        routing_key = '%s.user' % exchange
458
        exchange_send(conn, routing_key, body)
459
        exchange_close(conn)
460

  
461
def _generate_invitation_code():
462
    while True:
463
        code = randint(1, 2L**63 - 1)
464
        try:
465
            Invitation.objects.get(code=code)
466
            # An invitation with this code already exists, try again
467
        except Invitation.DoesNotExist:
468
            return code
469

  
470
def get_latest_terms():
471
    try:
472
        term = ApprovalTerms.objects.order_by('-id')[0]
473
        return term
474
    except IndexError:
475
        pass
476
    return None
477

  
478 449
class EmailChangeManager(models.Manager):
479 450
    @transaction.commit_on_success
480 451
    def change_email(self, activation_key):
......
534 505
    owner = models.ForeignKey(AstakosUser)
535 506
    email = models.EmailField()
536 507

  
508
def _generate_invitation_code():
509
    while True:
510
        code = randint(1, 2L**63 - 1)
511
        try:
512
            Invitation.objects.get(code=code)
513
            # An invitation with this code already exists, try again
514
        except Invitation.DoesNotExist:
515
            return code
516

  
517
def get_latest_terms():
518
    try:
519
        term = ApprovalTerms.objects.order_by('-id')[0]
520
        return term
521
    except IndexError:
522
        pass
523
    return None
524

  
537 525
def create_astakos_user(u):
538 526
    try:
539 527
        AstakosUser.objects.get(user_ptr=u.pk)
......
542 530
        extended_user.__dict__.update(u.__dict__)
543 531
        extended_user.renew_token()
544 532
        extended_user.save()
545
    except:
533
    except BaseException, e:
534
        logger.exception(e)
546 535
        pass
547 536

  
548
def superuser_post_syncdb(sender, **kwargs):
549
    # if there was created a superuser
550
    # associate it with an AstakosUser
537
def fix_superusers(sender, **kwargs):
538
    # Associate superusers with AstakosUser
551 539
    admins = User.objects.filter(is_superuser=True)
552 540
    for u in admins:
553 541
        create_astakos_user(u)
554 542

  
555
post_syncdb.connect(superuser_post_syncdb)
556

  
557
def superuser_post_save(sender, instance, **kwargs):
558
    if instance.is_superuser:
559
        create_astakos_user(instance)
560

  
561
post_save.connect(superuser_post_save, sender=User)
562

  
563
def set_default_group(sender, instance, created, **kwargs):
543
def user_post_save(sender, instance, created, **kwargs):
564 544
    if not created:
565 545
        return
546
    create_astakos_user(instance)
547

  
548
def set_default_group(user):
566 549
    try:
567 550
        default = AstakosGroup.objects.get(name = 'default')
568
        Membership(group=default, person=instance, date_joined=datetime.now()).save()
551
        Membership(group=default, person=user, date_joined=datetime.now()).save()
569 552
    except AstakosGroup.DoesNotExist, e:
570 553
        logger.exception(e)
571 554

  
572
post_save.connect(set_default_group, sender=AstakosUser)
573

  
574
def get_resources():
575
    # use cache
576
    return Resource.objects.select_related().all()
555
def astakosuser_pre_save(sender, instance, **kwargs):
556
    instance.aquarium_report = False
557
    instance.new = False
558
    try:
559
        db_instance = AstakosUser.objects.get(id = instance.id)
560
    except AstakosUser.DoesNotExist:
561
        # create event
562
        instance.aquarium_report = True
563
        instance.new = True
564
    else:
565
        get = AstakosUser.__getattribute__
566
        l = filter(lambda f: get(db_instance, f) != get(instance, f),
567
            BILLING_FIELDS
568
        )
569
        instance.aquarium_report = True if l else False
570

  
571
def astakosuser_post_save(sender, instance, created, **kwargs):
572
    if instance.aquarium_report:
573
        report_user_event(instance, create=instance.new)
574
    if not created:
575
        return
576
    set_default_group(instance)
577
    # TODO handle socket.error & IOError
578
    register_users((instance,))
579

  
580
def send_quota_disturbed(sender, instance, **kwargs):
581
    users = []
582
    extend = users.extend
583
    if sender == Membership:
584
        if not instance.group.is_enabled:
585
            return
586
        extend([instance.person])
587
    elif sender == AstakosUserQuota:
588
        extend([instance.user])
589
    elif sender == AstakosGroupQuota:
590
        if not instance.group.is_enabled:
591
            return
592
        extend(instance.group.astakosuser_set.all())
593
    elif sender == AstakosGroup:
594
        if not instance.is_enabled:
595
            return
596
    quota_disturbed.send(sender=sender, users=users)
597

  
598
def on_quota_disturbed(sender, users, **kwargs):
599
    print '>>>', locals()
600
    if not users:
601
        return
602
    send_quota(users)
603

  
604
post_syncdb.connect(fix_superusers)
605
post_save.connect(user_post_save, sender=User)
606
pre_save.connect(astakosuser_pre_save, sender=AstakosUser)
607
post_save.connect(astakosuser_post_save, sender=AstakosUser)
608

  
609
quota_disturbed = Signal(providing_args=["users"])
610
quota_disturbed.connect(on_quota_disturbed)
611

  
612
post_delete.connect(send_quota_disturbed, sender=AstakosGroup)
613
post_delete.connect(send_quota_disturbed, sender=Membership)
614
post_save.connect(send_quota_disturbed, sender=AstakosUserQuota)
615
post_delete.connect(send_quota_disturbed, sender=AstakosUserQuota)
616
post_save.connect(send_quota_disturbed, sender=AstakosGroupQuota)
617
post_delete.connect(send_quota_disturbed, sender=AstakosGroupQuota)
b/snf-astakos-app/astakos/im/settings.py
99 99
# Set the astakos main functions logging severity (None to disable)
100 100
from logging import INFO
101 101
LOGGING_LEVEL = getattr(settings, 'ASTAKOS_LOGGING_LEVEL', INFO)
102

  
103
QUOTA_HOLDER_URL = getattr(settings, 'ASTAKOS_QUOTA_HOLDER_URL', '')
b/snf-astakos-app/astakos/im/tasks.py
1
# Copyright 2011 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

  
34
from celery.task import task, periodic_task
35
from celery.schedules import crontab
36

  
37
from functools import wraps
38

  
39
from astakos.im.endpoints.quotaholder import send_quota
40
from astakos.im.endpoints.aquarium.producer import report_credits_event, report_user_event
41

  
42
import logging
43

  
44
logger = logging.getLogger(__name__)
45

  
46
def log(func):
47
    @wraps(func)
48
    def wrapper(*args, **kwargs):
49
        logger.info('Starting the %s' % func)
50
        return func(*args, **kwargs)
51
    return wrapper
52

  
53
#@periodic_task(run_every=crontab(day_of_month='1'))
54
@periodic_task(run_every=crontab())
55
@log
56
def propagate_credits_update():
57
    report_credits_event()
58

  
59
@task
60
@log
61
def propagate_groupmembers_quota(group):
62
    if group.is_disabled:
63
        return
64
    send_quota(group.approved_members)
b/snf-astakos-app/astakos/im/templates/im/group_creation_notification.txt
2 2

  
3 3
Έχει δημιουργηθεί το παρακάτω group:
4 4

  
5
Id:                 {{group.id}}}
6
Name:               {{group.name}}
7
Type:               {{group.kind}}
8
Issue date:         {{group.issue_date|date:"d/m/Y"}}
9
Expiration date:    {{group.expiration_date|date:"d/m/Y"}}
10
Moderation:         {{group.moderation_enabled}}
11
Owner:              {{owner}}
5
Id:                             {{group.id}}
6
Name:                           {{group.name}}
7
Type:                           {{group.kind}}
8
Issue date:                     {{group.issue_date|date:"d/m/Y"}}
9
Expiration date:                {{group.expiration_date|date:"d/m/Y"}}
10
Moderation:                     {{group.moderation_enabled}}
11
Owner:                          {{owner}}
12
Expected participant number:    {{group.estimated_participants}}
12 13
Policies:
13 14
{% for p in policies %}
14 15
    {{p}}
......
20 21

  
21 22
The following account has been created:
22 23

  
23
Id:                 {{group.id}}}
24
Name:               {{group.name}}
25
Type:               {{group.kind}}
26
Issue date:         {{group.issue_date|date:"d/m/Y"}}
27
Expiration date:    {{group.expiration_date|date:"d/m/Y"}}
28
Moderation:         {{group.moderation_enabled}}
29
Owner:              {{owner}}
24
Id:                             {{group.id}}
25
Name:                           {{group.name}}
26
Type:                           {{group.kind}}
27
Issue date:                     {{group.issue_date|date:"d/m/Y"}}
28
Expiration date:                {{group.expiration_date|date:"d/m/Y"}}
29
Moderation:                     {{group.moderation_enabled}}
30
Owner:                          {{owner}}
31
Expected participant number:    {{group.estimated_participants}}
30 32
Policies:
31 33
{% for p in policies %}
32 34
    {{p}}
b/snf-astakos-app/astakos/im/views.py
667 667
    else:
668 668
        now = datetime.now()
669 669
        data = {
670
            'kind':kind,
671
            'issue_date':now,
672
            'expiration_date':now + timedelta(days=30)
670
            'kind':kind
673 671
        }
674 672
        form = form_class(data, resources=resources)
675 673

  
......
796 794
                    template='im/astakosgroup_detail.html',
797 795
                    context_instance=get_context(request),
798 796
                    object=m.group,
799
                    quota=m.group.quota,
800
                    more_policies=m.group.has_undefined_policies
797
                    quota=m.group.quota
801 798
                )
802 799
        return wrapper
803 800
    return decorator
b/snf-astakos-app/setup.py
81 81
    'snf-common>=0.9.0',
82 82
    'django-recaptcha',
83 83
    'django-ratelimit==0.1'
84
    'django-ratelimit==0.1',
85
    'commissioning',
86
    'celery'
84 87
]
85 88

  
86 89
EXTRAS_REQUIRES = {

Also available in: Unified diff