Progress V
authorSofia Papagiannaki <papagian@gmail.com>
Tue, 7 Aug 2012 18:00:46 +0000 (21:00 +0300)
committerSofia Papagiannaki <papagian@gmail.com>
Tue, 7 Aug 2012 18:00:46 +0000 (21:00 +0300)
snf-astakos-app/astakos/im/api/__init__.py
snf-astakos-app/astakos/im/management/commands/group_add.py
snf-astakos-app/astakos/im/management/commands/group_list.py
snf-astakos-app/astakos/im/management/commands/group_remove_permissions.py [deleted file]
snf-astakos-app/astakos/im/management/commands/group_update.py [moved from snf-astakos-app/astakos/im/management/commands/group_add_permissions.py with 62% similarity]
snf-astakos-app/astakos/im/models.py
snf-astakos-app/astakos/im/templates/im/astakosgroup_detail.html
snf-astakos-app/astakos/im/templates/im/astakosgroup_list.html
snf-astakos-app/astakos/im/templates/im/astakosuserquota_list.html [new file with mode: 0644]
snf-astakos-app/astakos/im/urls.py
snf-astakos-app/astakos/im/views.py

index 579b28d..d1fdeac 100644 (file)
@@ -172,6 +172,7 @@ def get_menu(request, with_extra_links=False, with_signout=True):
                 l.append(dict(url=absolute(reverse('invite')), name="Invitations"))
             l.append(dict(url=absolute(reverse('feedback')), name="Feedback"))
             l.append(dict(url=absolute(reverse('group_list')), name="Groups"))
+            l.append(dict(url=absolute(reverse('resource_list')), name="Resources"))
         if with_signout:
             l.append(dict(url=absolute(reverse('logout')), name="Sign out"))
 
index 25620da..9a87dc0 100644 (file)
@@ -39,7 +39,8 @@ from time import time
 from os.path import abspath
 
 from django.core.management.base import BaseCommand, CommandError
-from django.contrib.auth.models import Group
+
+from astakos.im.models import AstakosGroup
 
 from ._common import add_group_permission
 
@@ -54,10 +55,10 @@ class Command(BaseCommand):
         name = args[0].decode('utf8')
         
         try:
-            Group.objects.get(name=name)
+            AstakosGroup.objects.get(name=name)
             raise CommandError("A group with this name already exists")
-        except Group.DoesNotExist, e:
-            group = Group(name=name)
+        except AstakosGroup.DoesNotExist, e:
+            group = AstakosGroup(name=name)
             group.save()
             msg = "Created group id %d" % (group.id,)
             self.stdout.write(msg + '\n')
index 9846370..85df802 100644 (file)
 from optparse import make_option
 
 from django.core.management.base import BaseCommand, CommandError
-from django.contrib.auth.models import Group
 
-from astakos.im.models import AstakosUser
+from astakos.im.models import AstakosUser, AstakosGroup
 
 from ._common import format_bool
 
 
 class Command(BaseCommand):
-    help = "List g"
+    help = "List groups"
     
     option_list = BaseCommand.option_list + (
         make_option('-c',
@@ -50,28 +49,38 @@ class Command(BaseCommand):
             dest='csv',
             default=False,
             help="Use pipes to separate values"),
+        make_option('-p',
+            action='store_true',
+            dest='pending',
+            default=False,
+            help="List only groups pending enable"),
     )
     
     def handle(self, *args, **options):
         if args:
             raise CommandError("Command doesn't accept any arguments")
         
-        groups = Group.objects.all()
+        groups = AstakosGroup.objects.all()
+        
+        if options.get('pending'):
+            groups = filter(lambda g: g.is_disabled, groups)
         
-        labels = ('id', 'name', 'permissions')
-        columns = (3, 12, 50)
+        labels = ('id', 'name', 'enabled', 'permissions')
+        columns = (3, 12, 12, 50)
         
-        if not options['csv']:
+        if not options.get('csv'):
             line = ' '.join(l.rjust(w) for l, w in zip(labels, columns))
             self.stdout.write(line + '\n')
             sep = '-' * len(line)
             self.stdout.write(sep + '\n')
         
         for group in groups:
-            fields = (str(group.id), group.name,
-                      ','.join(p.codename for p in group.permissions.all()))
+            fields = (  str(group.id),
+                        group.name,
+                        format_bool(group.is_enabled),
+                        ','.join(p.codename for p in group.permissions.all()))
             
-            if options['csv']:
+            if options.get('csv'):
                 line = '|'.join(fields)
             else:
                 line = ' '.join(f.rjust(w) for f, w in zip(fields, columns))
diff --git a/snf-astakos-app/astakos/im/management/commands/group_remove_permissions.py b/snf-astakos-app/astakos/im/management/commands/group_remove_permissions.py
deleted file mode 100644 (file)
index 5ead6b0..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-# Copyright 2012 GRNET S.A. All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or
-# without modification, are permitted provided that the following
-# conditions are met:
-#
-#   1. Redistributions of source code must retain the above
-#      copyright notice, this list of conditions and the following
-#      disclaimer.
-#
-#   2. Redistributions in binary form must reproduce the above
-#      copyright notice, this list of conditions and the following
-#      disclaimer in the documentation and/or other materials
-#      provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
-# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
-# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
-# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
-# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-# POSSIBILITY OF SUCH DAMAGE.
-#
-# The views and conclusions contained in the software and
-# documentation are those of the authors and should not be
-# interpreted as representing official policies, either expressed
-# or implied, of GRNET S.A.
-
-from optparse import make_option
-
-from django.core.management.base import BaseCommand, CommandError
-from django.contrib.auth.models import Group
-from django.core.exceptions import ValidationError
-
-from astakos.im.models import AstakosUser
-from ._common import remove_group_permission
-
-class Command(BaseCommand):
-    args = "<groupname> <permission> [<permissions> ...]"
-    help = "Remove group permissions"
-    
-    def handle(self, *args, **options):
-        if len(args) < 2:
-            raise CommandError("Please provide a group name and at least one permission")
-        
-        group = None
-        try:
-            if args[0].isdigit():
-                group = Group.objects.get(id=args[0])
-            else:
-                group = Group.objects.get(name=args[0])
-        except Group.DoesNotExist, e:
-            raise CommandError("Invalid group")
-        
-        try:
-            for pname in args[1:]:
-                r = remove_group_permission(group, pname)
-                if r < 0:
-                    self.stdout.write('Invalid permission codename: %s\n' % pname)
-                elif r == 0:
-                    self.stdout.write('Group has not permission: %s\n' % pname)
-                elif r > 0:
-                    self.stdout.write('Permission: %s removed successfully\n' % pname)
-        except Exception, e:
-            raise CommandError(e)
\ No newline at end of file
 from optparse import make_option
 
 from django.core.management.base import BaseCommand, CommandError
-from django.contrib.auth.models import Group, Permission
+from django.contrib.auth.models import Permission
 from django.contrib.contenttypes.models import ContentType
 from django.core.exceptions import ValidationError
 
-from astakos.im.models import AstakosUser
+from astakos.im.models import AstakosUser, AstakosGroup
 from ._common import add_group_permission
 
 class Command(BaseCommand):
-    args = "<groupname> <permission> [<permissions> ...]"
-    help = "Add group permissions"
+    args = "<groupname>"
+    help = "Update group"
+    
+    option_list = BaseCommand.option_list + (
+        make_option('--add-permission',
+            dest='add-permission',
+            help="Add user permission"),
+        make_option('--delete-permission',
+            dest='delete-permission',
+            help="Delete user permission"),
+        make_option('--enable',
+            action='store_true',
+            dest='enable',
+            default=False,
+            help="Enable group"),
+    )
     
     def handle(self, *args, **options):
-        if len(args) < 2:
-            raise CommandError("Please provide a group name and at least one permission")
+        if len(args) < 1:
+            raise CommandError("Please provide a group identifier")
         
         group = None
         try:
             if args[0].isdigit():
-                group = Group.objects.get(id=args[0])
+                group = AstakosGroup.objects.get(id=args[0])
             else:
-                group = Group.objects.get(name=args[0])
-        except Group.DoesNotExist, e:
+                group = AstakosGroup.objects.get(name=args[0])
+        except AstakosGroup.DoesNotExist, e:
             raise CommandError("Invalid group")
         
         try:
             content_type = ContentType.objects.get(app_label='im',
                                                        model='astakosuser')
-            for pname in args[1:]:
+            
+            pname = options.get('add-permission')
+            if pname:
                 r, created = add_group_permission(group, pname)
                 if created:
                     self.stdout.write('Permission: %s created successfully\n' % pname)
@@ -69,5 +85,18 @@ class Command(BaseCommand):
                     self.stdout.write('Group has already permission: %s\n' % pname)
                 else:
                     self.stdout.write('Permission: %s added successfully\n' % pname)
+            
+            pname = options.get('delete-permission')
+            if pname:
+                r = remove_group_permission(group, pname)
+                if r < 0:
+                    self.stdout.write('Invalid permission codename: %s\n' % pname)
+                elif r == 0:
+                    self.stdout.write('Group has not permission: %s\n' % pname)
+                elif r > 0:
+                    self.stdout.write('Permission: %s removed successfully\n' % pname)
+            
+            if options.get('enable'):
+                group.enable()
         except Exception, e:
             raise CommandError(e)
\ No newline at end of file
index c3a0f84..630ee50 100644 (file)
@@ -41,6 +41,7 @@ from datetime import datetime, timedelta
 from base64 import b64encode
 from urlparse import urlparse, urlunparse
 from random import randint
+from collections import defaultdict
 
 from django.db import models, IntegrityError
 from django.contrib.auth.models import User, UserManager, Group
@@ -120,12 +121,12 @@ class AstakosGroup(Group):
     
     @property
     def is_disabled(self):
-        if not approval_date:
-            return False
-        return True
+        if not self.approval_date:
+            return True
+        return False
     
     @property
-    def is_active(self):
+    def is_enabled(self):
         if self.is_disabled:
             return False
         if not self.issue_date:
@@ -143,11 +144,11 @@ class AstakosGroup(Group):
     def participants(self):
         return len(self.approved_members)
     
-    def approve(self):
+    def enable(self):
         self.approval_date = datetime.now()
         self.save()
     
-    def disapprove(self):
+    def disable(self):
         self.approval_date = None
         self.save()
     
@@ -172,8 +173,11 @@ class AstakosGroup(Group):
         return map(lambda m:m.person, f)
     
     @property
-    def policies(self):
-        return self.astakosgroupquota_set.all()
+    def quota(self):
+        d = {}
+        for q in  self.astakosgroupquota_set.all():
+            d[q.resource.name] = q.limit
+        return d
     
     @property
     def has_undefined_policies(self):
@@ -255,7 +259,20 @@ class AstakosUser(User):
             return Invitation.objects.get(username=self.email)
         except Invitation.DoesNotExist:
             return None
-
+    
+    @property
+    def quota(self):
+        d = defaultdict(int)
+        for q in  self.astakosuserquota_set.all():
+            d[q.resource.name] += q.limit
+        for g in self.astakos_groups.all():
+            if not g.is_enabled:
+                continue
+            for r, limit in g.quota.iteritems():
+                d[r] += limit
+        # TODO set default for remaining
+        return d
+        
     def save(self, update_timestamps=True, **kwargs):
         if update_timestamps:
             if not self.id:
@@ -340,14 +357,6 @@ class AstakosUser(User):
             self.save()
             return False
         return True
-    
-    def enroll_group(self, group):
-        self.membership_set.add(group)
-    
-    def get_astakos_groups(self, approved=True):
-        if approved:
-            return self.membership_set().filter(is_approved=True)
-        return self.membership_set().all()
 
 class Membership(models.Model):
     person = models.ForeignKey(AstakosUser)
@@ -358,11 +367,11 @@ class Membership(models.Model):
     class Meta:
         unique_together = ("person", "group")
     
-    def save(self):
+    def save(self, *args, **kwargs):
         if not self.id:
             if not self.group.moderation_enabled:
                 self.date_joined = datetime.now()
-        super(Membership, self).save()
+        super(Membership, self).save(*args, **kwargs)
     
     @property
     def is_approved(self):
index cb33f50..2b14197 100644 (file)
@@ -1,5 +1,7 @@
 {% extends "im/account_base.html" %}
 
+{% load filters %}
+
 {% block page.body %}
 <div class="maincol {% block innerpage.class %}{% endblock %}">
         <table class="zebra-striped id-sorted">
                 <th>Type: {{object.kind}}</th>
               </tr>
               <tr>
-                <th>Issue date: {{object.issue_date}}</th>
+                <th>Issue date: {{object.issue_date|date:"d/m/Y"}}</th>
               </tr>
               <tr>
-                <th>Expiration date: {{object.expiration_date}}</th>
+                <th>Expiration date: {{object.expiration_date|date:"d/m/Y"}}</th>
               </tr>
               <tr>
                 <th>Moderation: {% if object.moderation_enabled%}Yes{% else %}No{% endif %}</th>
@@ -21,7 +23,7 @@
               <tr>
                 <th>Owner: {% for o in object.owner.all %}
                                 {% if user == o %}
-                                    Me!
+                                    Me
                                 {% else%}
                                     {{o.realname}} ({{o.email}})
                                 
@@ -29,6 +31,9 @@
                             {% endfor %}
                 </th>
               </tr>
+              <tr>
+                <th>Enabled: {% if object.is_enabled %}Yes{% else %}No{% endif %}</th>
+              </tr>
         </table>
     <div class="section">
         <h2>Members:</h2>
@@ -54,8 +59,8 @@
                     {% else %}
                     <td>Pending</td>
                         {% if user in m.group.owner.all %}
-                            <td><a href="{% url approve_member m.id %}">Approve</a></td>
-                            <td><a href="{% url disapprove_member m.id %}">Disapprove</a></td>
+                            <td><a href="{% url approve_member m.group.id m.person.id %}">Approve</a></td>
+                            <td><a href="{% url disapprove_member m.group.id m.person.id  %}">Disapprove</a></td>
                         {% endif %}
                     {% endif %}
                 {% endif %}
               </tr>
             </thead>
             <tbody>
-            {% for q in quota %}
+            {% for k in quota|dkeys %}
                 <tr>
-                    <td>{{q.resource.name}}</td>
-                    <td>{{q.limit}}</td>
+                    <td>{{ k }}</td>
+                    <td>{{ quota|lookup:k }}</td>
                   </tr>
             {% endfor %}
             </tbody>
@@ -90,7 +95,7 @@
             <p>No policies</p>
         {% endif %}
     </div>
-    {% if more_policies %}
+    {% if user in object.owner.all and more_policies %}
     <div class="rightcol">
         <form action="{% url group_policies_add object.id %}" method="post" class="innerlabels signup">{% csrf_token %}
             <h2><span>NEW POLICY</span></h2>
index 89450ac..3341174 100644 (file)
@@ -29,6 +29,7 @@
                 <th>Expiration date</th>
                 <th>Owner?</th>
                 <th>Participants</th>
+                <th>Enabled?</th>
                 <th>Moderation?</th>
                 <th>Enrollment status</th>
               </tr>
               <tr>
                 <td><a class="extra-link" href="{% url group_detail o.id %}">{{o.name}}</a></td>
                 <td>{{o.kind}}</td>
-                <td>{{o.issue_date|date:"D d M Y"}}</td>
-                <td>{{o.expiration_date|date:"D d M Y"}}</td>
+                <td>{{o.issue_date|date:"d/m/Y"}}</td>
+                <td>{{o.expiration_date|date:"d/m/Y"}}</td>
                 <td>{% if user in o.owner.all %}Yes{% else %}No{% endif %}</td>
                 <td>{{ o.approved_members|length }}/{{ o.members|length }}</td>
+                <td>{% if o.is_enabled %}Yes{% else %}No{% endif %}</td>
                 <td>{% if o.moderation_enabled%}Yes{% else %}No{% endif %}</td>
                 {% if user in o.approved_members %}
                     <td>Active</td>
diff --git a/snf-astakos-app/astakos/im/templates/im/astakosuserquota_list.html b/snf-astakos-app/astakos/im/templates/im/astakosuserquota_list.html
new file mode 100644 (file)
index 0000000..917697d
--- /dev/null
@@ -0,0 +1,30 @@
+{% extends "im/account_base.html" %}
+
+{% load filters %}
+
+{% block page.body %}
+<div class="maincol {% block innerpage.class %}{% endblock %}">
+    <div class="section">
+        {% if quota %}
+          <table class="zebra-striped id-sorted">
+            <thead>
+              <tr>
+                <th>Resource</th>
+                <th>Limit</th>
+              </tr>
+            </thead>
+            <tbody>
+            {% for k in quota|dkeys %}
+                <tr>
+                    <td>{{ k }}</td>
+                    <td>{{ quota|lookup:k }}</td>
+                  </tr>
+            {% endfor %}
+            </tbody>
+        </table>
+        {% else %}
+            <p>No policies</p>
+        {% endif %}
+    </div>
+</div>
+{% endblock %}
index b40236a..fcc3c17 100644 (file)
@@ -49,17 +49,18 @@ urlpatterns = patterns('astakos.im.views',
     url(r'^approval_terms/?$', 'approval_terms', {}, name='latest_terms'),
     url(r'^approval_terms/(?P<term_id>\d+)/?$', 'approval_terms'),
     url(r'^password/?$', 'change_password', {}, name='password_change'),
+    url(r'^resources/?$', 'resource_list', {}, name='resource_list'),
     url(r'^group/add/?$', 'group_add', {}, name='group_add'),
     url(r'^group/list/?$', 'group_list', {}, name='group_list'),
     url(r'^group/(?P<group_id>\d+)/?$', 'group_detail', {}, name='group_detail'),
-    url(r'^group/(?P<group_id>\d+)/policies/list/?$', 'group_policies_list', {}, name='group_policies_list'),
+    #url(r'^group/(?P<group_id>\d+)/policies/list/?$', 'group_policies_list', {}, name='group_policies_list'),
     url(r'^group/(?P<group_id>\d+)/policies/add/?$', 'group_policies_add', {}, name='group_policies_add'),
     url(r'^group/search/?$', 'group_search', {}, name='group_search'),
     url(r'^group/(?P<group_id>\d+)/join/?$', 'group_join', {}, name='group_join'),
     url(r'^group/(?P<group_id>\d+)/leave/?$', 'group_leave', {}, name='group_leave'),
-    url(r'^group/(?P<group_id>\d+)/request/approval/?$', 'group_approval_request', {}, name='group_approval_request'),
-    url(r'^group/(?P<membership_id>\d+)/approve/?$', 'approve_member', {}, name='approve_member'),
-    url(r'^group/(?P<membership_id>\d+)/disapprove/?$', 'disapprove_member', {}, name='disapprove_member'),
+    #url(r'^group/(?P<group_id>\d+)/request/approval/?$', 'group_approval_request', {}, name='group_approval_request'),
+    url(r'^group/(?P<group_id>\d+)/(?P<user_id>\d+)/approve/?$', 'approve_member', {}, name='approve_member'),
+    url(r'^group/(?P<group_id>\d+)/(?P<user_id>\d+)/disapprove/?$', 'disapprove_member', {}, name='disapprove_member'),
 )
 
 if EMAILCHANGE_ENABLED:
index 7013906..e004670 100644 (file)
@@ -610,7 +610,7 @@ def group_detail(request, group_id):
                          AstakosGroup.objects.all(),
                          object_id=group_id,
                          extra_context = {'form':get_astakos_group_policy_creation_form(group),
-                                          'quota':group.policies,
+                                          'quota':group.quota,
                                           'more_policies':group.has_undefined_policies})
 
 @signed_terms_required
@@ -631,7 +631,7 @@ def group_policies_add(request, group_id):
                          template_name = 'im/astakosgroup_detail.html',
                          post_save_redirect = reverse('group_detail', kwargs=dict(group_id=group_id)),
                          extra_context = {'group':group,
-                                          'quota':group.policies,
+                                          'quota':group.quota,
                                           'more_policies':group.has_undefined_policies})
 @signed_terms_required
 @login_required
@@ -686,9 +686,9 @@ def group_leave(request, group_id):
 def handle_membership():
     def decorator(func):
         @wraps(func)
-        def wrapper(request, membership_id):
+        def wrapper(request, group_id, user_id):
             try:
-                m = Membership.objects.select_related().get(id=membership_id)
+                m = Membership.objects.select_related().get(group__id=group_id, person__id=user_id)
             except Membership.DoesNotExist:
                 return HttpResponseBadRequest(_('Invalid membership.'))
             else:
@@ -698,7 +698,7 @@ def handle_membership():
                 return render_response(template='im/astakosgroup_detail.html',
                                        context_instance=get_context(request),
                                        object=m.group,
-                                       quota=m.group.policies,
+                                       quota=m.group.quota,
                                        more_policies=m.group.has_undefined_policies)
         return wrapper
     return decorator
@@ -730,3 +730,10 @@ def disapprove_member(request, membership):
         logger.exception(e)
         msg = _('Something went wrong during %s\'s disapproval.' % realname)
         messages.error(request, msg)
+
+@signed_terms_required
+@login_required
+def resource_list(request):
+    return render_response(template='im/astakosuserquota_list.html',
+                           context_instance=get_context(request),
+                           quota=request.user.quota)
\ No newline at end of file