Commit 48e72b9f authored by Sofia Papagiannaki's avatar Sofia Papagiannaki
Browse files

Progress VII

* add policies during group creation
* improve performance (reduce db access)
parent 7585f252
......@@ -36,6 +36,7 @@ from astakos.im.settings import IM_MODULES, INVITATIONS_ENABLED, IM_STATIC_URL,
GLOBAL_MESSAGES, PROFILE_EXTRA_LINKS
from astakos.im.api import get_menu
from astakos.im.util import get_query
from astakos.im.models import GroupKind
from django.conf import settings
from django.core.urlresolvers import reverse
......@@ -79,3 +80,6 @@ def menu(request):
for item in menu_items:
item['is_active'] = absolute(request.path) == item['url']
return {'menu':menu_items}
def group_kinds(request):
return {'group_kinds': GroupKind.objects.exclude(name='default').values_list('name', flat=True)}
\ No newline at end of file
......@@ -490,53 +490,61 @@ class ExtendedPasswordChangeForm(PasswordChangeForm):
user.save()
return user
def get_astakos_group_creation_form(request):
class AstakosGroupCreationForm(forms.ModelForm):
issue_date = forms.DateField(widget=SelectDateWidget(), initial=datetime.now())
# TODO set initial in exact one month
expiration_date = forms.DateField(widget=SelectDateWidget(), initial = datetime.now() + timedelta(days=30))
kind = forms.ModelChoiceField(queryset=GroupKind.objects.all(), empty_label=None)
class AstakosGroupCreationForm(forms.ModelForm):
# issue_date = forms.DateField(widget=SelectDateWidget())
# expiration_date = forms.DateField(widget=SelectDateWidget())
kind = forms.ModelChoiceField(
queryset=GroupKind.objects.all(),
label="",
widget=forms.HiddenInput()
)
name = forms.URLField()
class Meta:
model = AstakosGroup
def __init__(self, *args, **kwargs):
try:
resources = kwargs.pop('resources')
except KeyError:
resources = {}
super(AstakosGroupCreationForm, self).__init__(*args, **kwargs)
self.fields.keyOrder = ['kind', 'name', 'desc', 'issue_date',
'expiration_date', 'estimated_participants',
'moderation_enabled']
def save(self, commit=True):
g = super(AstakosGroupCreationForm, self).save(commit=False)
if commit:
g.save()
g.owner = [request.user]
g.approve_member(request.user)
return g
return AstakosGroupCreationForm
def get_astakos_group_policy_creation_form(astakosgroup):
class AstakosGroupPolicyCreationForm(forms.ModelForm):
choices = Resource.objects.filter(~Q(astakosgroup=astakosgroup))
resource = forms.ModelChoiceField(queryset=choices, empty_label=None)
# TODO check that it does not hit the db
group = forms.ModelChoiceField(queryset=AstakosGroup.objects.all(), initial=astakosgroup, widget=forms.HiddenInput())
class Meta:
model = AstakosGroupQuota
return AstakosGroupPolicyCreationForm
for id, r in resources.iteritems():
self.fields['resource_%s' % id] = forms.IntegerField(
label=r,
required=False,
help_text=_('Leave it blank for no additional quota.')
)
def resources(self):
for name, value in self.cleaned_data.items():
prefix, delimiter, suffix = name.partition('resource_')
if suffix:
# yield only those having a value
if not value:
continue
yield (suffix, value)
class AstakosGroupSearchForm(forms.Form):
q = forms.CharField(max_length=200, label='')
class MembershipCreationForm(forms.ModelForm):
# TODO check not to hit the db
group = forms.ModelChoiceField(queryset=AstakosGroup.objects.all(), widget=forms.HiddenInput())
person = forms.ModelChoiceField(queryset=AstakosUser.objects.all(), widget=forms.HiddenInput())
date_requested = forms.DateField(widget=forms.HiddenInput(), input_formats="%d/%m/%Y")
group = forms.ModelChoiceField(
queryset=AstakosGroup.objects.all(),
widget=forms.HiddenInput()
)
person = forms.ModelChoiceField(
queryset=AstakosUser.objects.all(),
widget=forms.HiddenInput()
)
date_requested = forms.DateField(
widget=forms.HiddenInput(),
input_formats="%d/%m/%Y"
)
class Meta:
model = Membership
......
......@@ -65,8 +65,8 @@ class Command(BaseCommand):
if options.get('pending'):
groups = filter(lambda g: g.is_disabled, groups)
labels = ('id', 'name', 'enabled', 'permissions')
columns = (3, 12, 12, 50)
labels = ('id', 'name', 'enabled', 'moderation', 'permissions')
columns = (3, 25, 12, 12, 50)
if not options.get('csv'):
line = ' '.join(l.rjust(w) for l, w in zip(labels, columns))
......@@ -78,6 +78,7 @@ class Command(BaseCommand):
fields = ( str(group.id),
group.name,
format_bool(group.is_enabled),
format_bool(group.moderation_enabled),
','.join(p.codename for p in group.permissions.all()))
if options.get('csv'):
......
......@@ -154,11 +154,9 @@ class AstakosGroup(Group):
self.save()
def approve_member(self, person):
try:
self.membership_set.create(person=person, date_joined=datetime.now())
except IntegrityError:
m = self.membership_set.get(person=person)
m.date_joined = datetime.now()
m, created = self.membership_set.get_or_create(person=person)
# update date_joined in any case
m.date_joined=datetime.now()
m.save()
def disapprove_member(self, person):
......@@ -175,9 +173,9 @@ class AstakosGroup(Group):
@property
def quota(self):
d = {}
d = defaultdict(int)
for q in self.astakosgroupquota_set.all():
d[q.resource.name] = q.limit
d[q.resource] += q.limit
return d
@property
......@@ -185,6 +183,15 @@ class AstakosGroup(Group):
# TODO: can avoid query?
return Resource.objects.filter(~Q(astakosgroup=self)).exists()
@property
def owners(self):
return self.owner.all()
@owners.setter
def owners(self, l):
self.owner = l
map(self.approve_member, l)
class AstakosUser(User):
"""
Extends ``django.contrib.auth.models.User`` by defining additional fields.
......@@ -568,3 +575,7 @@ def superuser_post_save(sender, instance, **kwargs):
create_astakos_user(instance)
post_save.connect(superuser_post_save, sender=User)
def get_resources():
# use cache
return Resource.objects.select_related().all()
\ No newline at end of file
......@@ -57,6 +57,7 @@ context_processors = [
'astakos.im.context_processors.invitations',
'astakos.im.context_processors.menu',
'astakos.im.context_processors.custom_messages',
'astakos.im.context_processors.group_kinds',
'synnefo.lib.context_processors.cloudbar'
]
......
......@@ -95,16 +95,5 @@
<p>No policies</p>
{% endif %}
</div>
{% 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>
{% include "im/form_render.html" %}
<div class="form-row submit">
<input type="submit" class="submit altcol" value="+" />
</div>
</form>
</div>
{% endif %}
</div>
{% endblock %}
......@@ -7,7 +7,7 @@
<h2><span>NEW GROUP</span></h2>
{% include "im/form_render.html" %}
<div class="form-row submit">
<input type="submit" class="submit altcol" value="ADD POLICIES" />
<input type="submit" class="submit altcol" value="SUBMIT" />
</div>
</form>
</div>
......
......@@ -14,8 +14,10 @@
</form>
{% else %}
<p class="submit-rt">
<a href="{% url group_add %}" class="submit">Create a group</a>
<a href="{% url group_search %}" class="submit">Join a group</a>
{% for gname in group_kinds %}
<a href="{% url group_add gname %}" class="submit">Create {{gname}}</a>
{% endfor %}
<a href="{% url group_search %}" class="submit">Join group</a>
</p>
{% endif %}
{% if object_list %}
......@@ -36,18 +38,21 @@
</thead>
<tbody>
{% for o in object_list %}
{% with o.owner.all as owners %}
{% with o.members as members %}
{% with o.approved_members as approved_members %}
<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/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 user in owners %}Yes{% else %}No{% endif %}</td>
<td>{{ approved_members|length }}/{{ 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 %}
{% if user in approved_members %}
<td>Active</td>
{% if user not in o.owner.all %}
{% if user not in owners %}
<td>
<form action="{% url group_leave o.id %}" method="post"class="login innerlabels">{% csrf_token %}
<div class="form-row submit clearfix">
......@@ -57,7 +62,7 @@
</td>
{% endif %}
{% else %}
{% if user in o.members %}
{% if user in members %}
<td>Pending</td>
{% else %}
<td>Not member</td>
......@@ -76,6 +81,9 @@
{% endif %}
{% endif %}
</tr>
{% endwith %}
{% endwith %}
{% endwith %}
{% endfor %}
</tbody>
</table>
......
......@@ -50,11 +50,9 @@ urlpatterns = patterns('astakos.im.views',
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/add/(?P<kind_name>\w+)?$', '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/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'),
......
......@@ -57,7 +57,7 @@ from django.utils.translation import ugettext as _
from django.views.generic.create_update import *
from django.views.generic.list_detail import *
from astakos.im.models import AstakosUser, Invitation, ApprovalTerms, AstakosGroup
from astakos.im.models import AstakosUser, Invitation, ApprovalTerms, AstakosGroup, Resource
from astakos.im.activation_backends import get_backend, SimpleBackend
from astakos.im.util import get_context, prepare_response, set_cookie, get_query
from astakos.im.forms import *
......@@ -589,15 +589,69 @@ def change_email(request, activation_key=None,
@signed_terms_required
@login_required
def group_add(request):
return create_object(request,
form_class=get_astakos_group_creation_form(request),
post_save_redirect = '/im/group/%(id)s/')
def group_add(request, kind_name='default'):
try:
kind = GroupKind.objects.get(name = kind_name)
except:
return HttpResponseBadRequest(_('No such group kind'))
template_name=None,
template_loader=loader
extra_context=None
post_save_redirect='/im/group/%(id)s/'
login_required=False
context_processors=None
model, form_class = get_model_and_form_class(
model=None,
form_class=AstakosGroupCreationForm
)
# TODO better approach???
resources = dict( (str(r.id), r) for r in Resource.objects.select_related().all() )
if request.method == 'POST':
form = form_class(request.POST, request.FILES, resources=resources)
if form.is_valid():
new_object = form.save()
new_object.owners = [request.user]
for (rid, limit) in form.resources():
try:
r = resources[rid]
except KeyError, e:
logger.exception(e)
# Should I stay or should I go???
continue
else:
new_object.astakosgroupquota_set.create(
resource = r,
limit = limit
)
msg = _("The %(verbose_name)s was created successfully.") %\
{"verbose_name": model._meta.verbose_name}
messages.success(request, msg, fail_silently=True)
return redirect(post_save_redirect, new_object)
else:
now = datetime.now()
data = {
'kind':kind,
'issue_date':now,
'expiration_date':now + timedelta(days=30)
}
form = form_class(data, resources=resources)
# Create the template, context, response
template_name = "%s/%s_form.html" % (
model._meta.app_label,
model._meta.object_name.lower()
)
t = template_loader.get_template(template_name)
c = RequestContext(request, {
'form': form
}, context_processors)
return HttpResponse(t.render(c))
@signed_terms_required
@login_required
def group_list(request):
list = AstakosGroup.objects.filter(membership__person=request.user)
list = request.user.astakos_groups.select_related().all()
return object_list(request, queryset=list)
@signed_terms_required
......@@ -610,32 +664,11 @@ def group_detail(request, group_id):
return object_detail(request,
AstakosGroup.objects.all(),
object_id=group_id,
extra_context = {'form':get_astakos_group_policy_creation_form(group),
'quota':group.quota,
'more_policies':group.has_undefined_policies})
extra_context = {'quota':group.quota}
)
@signed_terms_required
@login_required
def group_policies_list(request, group_id):
list = AstakosGroupQuota.objects.filter(group__id=group_id)
return object_list(request, queryset=list)
@signed_terms_required
@login_required
def group_policies_add(request, group_id):
try:
group = AstakosGroup.objects.select_related().get(id=group_id)
except AstakosGroup.DoesNotExist:
return HttpResponseBadRequest(_('Invalid group.'))
return create_object(request,
form_class=get_astakos_group_policy_creation_form(group),
template_name = 'im/astakosgroup_detail.html',
post_save_redirect = reverse('group_detail', kwargs=dict(group_id=group_id)),
extra_context = {'group':group,
'quota':group.quota,
'more_policies':group.has_undefined_policies})
@signed_terms_required
@login_required
def group_approval_request(request, group_id):
return HttpResponse()
......@@ -653,57 +686,87 @@ def group_search(request, extra_context={}, **kwargs):
queryset = AstakosGroup.objects.select_related().filter(name=q)
f = MembershipCreationForm
for g in queryset:
join_forms[g.name] = f(dict(group=g,
join_forms[g.name] = f(
dict(
group=g,
person=request.user,
date_requested=datetime.now().strftime("%d/%m/%Y")))
return object_list(request,
date_requested=datetime.now().strftime("%d/%m/%Y")
)
)
return object_list(
request,
queryset,
template_name='im/astakosgroup_list.html',
extra_context=dict(form=form, is_search=True, join_forms=join_forms))
return render_response(template='im/astakosgroup_list.html',
extra_context=dict(
form=form,
is_search=True,
join_forms=join_forms
)
)
return render_response(
template='im/astakosgroup_list.html',
form = form,
context_instance=get_context(request))
context_instance=get_context(request)
)
@signed_terms_required
@login_required
def group_join(request, group_id):
return create_object(request,
return create_object(
request,
model=Membership,
template_name='im/astakosgroup_list.html',
post_save_redirect = reverse('group_detail', kwargs=dict(group_id=group_id)))
post_save_redirect = reverse(
'group_detail',
kwargs=dict(group_id=group_id)
)
)
@signed_terms_required
@login_required
def group_leave(request, group_id):
try:
m = Membership.objects.select_related().get(group__id=group_id, person=request.user)
m = Membership.objects.select_related().get(
group__id=group_id,
person=request.user
)
except Membership.DoesNotExist:
return HttpResponseBadRequest(_('Invalid membership.'))
if request.user in m.group.owner.all():
return HttpResponseForbidden(_('Owner can not leave the group.'))
return delete_object(request,
return delete_object(
request,
model=Membership,
object_id = m.id,
template_name='im/astakosgroup_list.html',
post_delete_redirect = reverse('group_detail', kwargs=dict(group_id=group_id)))
post_delete_redirect = reverse(
'group_detail',
kwargs=dict(group_id=group_id)
)
)
def handle_membership():
def decorator(func):
@wraps(func)
def wrapper(request, group_id, user_id):
try:
m = Membership.objects.select_related().get(group__id=group_id, person__id=user_id)
m = Membership.objects.select_related().get(
group__id=group_id,
person__id=user_id
)
except Membership.DoesNotExist:
return HttpResponseBadRequest(_('Invalid membership.'))
else:
if request.user not in m.group.owner.all():
return HttpResponseForbidden(_('User is not a group owner.'))
func(request, m)
return render_response(template='im/astakosgroup_detail.html',
return render_response(
template='im/astakosgroup_detail.html',
context_instance=get_context(request),
object=m.group,
quota=m.group.quota,
more_policies=m.group.has_undefined_policies)
more_policies=m.group.has_undefined_policies
)
return wrapper
return decorator
......@@ -738,6 +801,8 @@ def disapprove_member(request, membership):
@signed_terms_required
@login_required
def resource_list(request):
return render_response(template='im/astakosuserquota_list.html',
return render_response(
template='im/astakosuserquota_list.html',
context_instance=get_context(request),
quota=request.user.quota)
\ No newline at end of file
quota=request.user.quota
)
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment