source: asadb/groups/views.py @ 7b57a9b

space-accessstablestagetest-hooks
Last change on this file since 7b57a9b was 7b57a9b, checked in by Alex Dehnert <adehnert@…>, 14 years ago

Optimize the signatory view

By using select_related, we can make generating the view several times faster

  • Property mode set to 100644
File size: 16.3 KB
Line 
1# Create your views here.
2
3import collections
4import datetime
5
6import groups.models
7
8from django.contrib.auth.decorators import user_passes_test, login_required, permission_required
9from django.contrib.contenttypes.models import ContentType
10from django.core.exceptions import PermissionDenied
11from django.views.generic import ListView, DetailView
12from django.shortcuts import render_to_response, get_object_or_404
13from django.template import RequestContext
14from django.template import Context, Template
15from django.template.loader import get_template
16from django.http import Http404, HttpResponseRedirect
17from django.core.urlresolvers import reverse
18from django.core.mail import EmailMessage, mail_admins
19from django.forms import Form
20from django.forms import ModelForm
21from django.forms import ModelChoiceField
22from django.db import connection
23from django.db.models import Q
24
25import form_utils.forms
26import reversion.models
27import django_filters
28
29from util.db_form_utils import StaticWidget
30
31
32def view_homepage(request, ):
33    users_groups = []
34    groupmsg = ""
35    has_perms = []
36    if request.user.is_authenticated():
37        username = request.user.username
38        current_officers = groups.models.OfficeHolder.current_holders.filter(person=username)
39        users_groups = groups.models.Group.objects.filter(officeholder__in=current_officers).distinct()
40
41        perms = []
42        perms.extend(groups.models.Group._meta.permissions)
43        perms.extend(groups.models.GroupNote._meta.permissions)
44        perms += (
45            ('change_group', 'Change arbitrary group information', ),
46        )
47        for perm_name, perm_desc in perms:
48            if request.user.has_perm('groups.%s' % (perm_name, )):
49                has_perms.append((perm_name, perm_desc, ))
50
51    context = {
52        'groups': users_groups,
53        'groupmsg': groupmsg,
54        'has_perms': has_perms,
55        'pagename': 'homepage',
56    }
57    return render_to_response('index.html', context, context_instance=RequestContext(request), )
58
59
60class GroupChangeMainForm(form_utils.forms.BetterModelForm):
61    def __init__(self, *args, **kwargs):
62        change_restricted = False
63        if 'change_restricted' in kwargs:
64            change_restricted = kwargs['change_restricted']
65            del kwargs['change_restricted']
66        super(GroupChangeMainForm, self).__init__(*args, **kwargs)
67        restricted_fields = list(self.nobody_fields)
68        if change_restricted:
69            restricted_fields.extend(self.exec_only_fields)
70        for field_name in restricted_fields:
71            formfield = self.fields[field_name]
72            value = getattr(self.instance, field_name)
73            StaticWidget.replace_widget(formfield, value)
74
75    exec_only_fields = [
76        'name', 'abbreviation',
77        'group_status', 'group_class',
78        'group_funding', 'main_account_id', 'funding_account_id',
79    ]
80    nobody_fields = [
81        'recognition_date',
82    ]
83
84    class Meta:
85        fieldsets = [
86            ('basic', {
87                'legend': 'Basic Information',
88                'fields': ['name', 'abbreviation', 'activity_category', 'description', ],
89            }),
90            ('size', {
91                'legend':'Membership Numbers',
92                'fields': ['num_undergrads', 'num_grads', 'num_community', 'num_other',],
93            }),
94            ('contact', {
95                'legend': 'Contact Information',
96                'fields': ['website_url', 'meeting_times', 'officer_email', 'group_email', ],
97            }),
98            ('recognition', {
99                'legend': 'Recognition',
100                'fields': ['group_status', 'group_class', 'recognition_date', ],
101            }),
102            ('financial', {
103                'legend': 'Financial Information',
104                'fields': ['group_funding', 'main_account_id', 'funding_account_id', ],
105            }),
106            ('more-info', {
107                'legend': 'Additional Information',
108                'fields': ['constitution_url', 'advisor_name', 'athena_locker', ],
109            }),
110        ]
111        model = groups.models.Group
112
113def manage_main(request, group_id, ):
114    group = get_object_or_404(groups.models.Group, pk=group_id)
115
116    if not request.user.has_perm('groups.admin_group', group):
117        raise PermissionDenied
118    change_restricted = True
119    if request.user.has_perm('groups.change_group', group):
120        change_restricted = False
121
122    msg = None
123
124    initial = {}
125    if request.method == 'POST': # If the form has been submitted...
126        # A form bound to the POST data
127        form = GroupChangeMainForm(
128            request.POST, request.FILES,
129            change_restricted=change_restricted,
130            instance=group,
131        )
132
133        if form.is_valid(): # All validation rules pass
134            request_obj = form.save(commit=False)
135            request_obj.set_updater(request.user)
136            request_obj.save()
137            form.save_m2m()
138
139            # Send email
140            #tmpl = get_template('fysm/update_email.txt')
141            #ctx = Context({
142            #    'group': group_obj,
143            #    'fysm': fysm_obj,
144            #    'view_uri': view_uri,
145            #    'submitter': request.user,
146            #    'request': request,
147            #    'sender': "ASA FYSM team",
148            #})
149            #body = tmpl.render(ctx)
150            #email = EmailMessage(
151            #    subject='FYSM entry for "%s" updated by "%s"' % (
152            #        group_obj.name,
153            #        request.user,
154            #    ),
155            #    body=body,
156            #    from_email='asa-fysm@mit.edu',
157            #    to=[group_obj.officer_email, request.user.email, ],
158            #    bcc=['asa-fysm-submissions@mit.edu', ]
159            #)
160            #email.send()
161            msg = "Thanks for editing!"
162        else:
163            msg = "Validation failed. See below for details."
164
165    else:
166        form = GroupChangeMainForm(change_restricted=change_restricted, instance=group, initial=initial, ) # An unbound form
167
168    context = {
169        'group': group,
170        'form':  form,
171        'msg':   msg,
172    }
173    return render_to_response('groups/group_change_main.html', context, context_instance=RequestContext(request), )
174
175
176class GroupFilter(django_filters.FilterSet):
177    name = django_filters.CharFilter(lookup_type='icontains', label="Name contains")
178    abbreviation = django_filters.CharFilter(lookup_type='iexact', label="Abbreviation is")
179
180    class Meta:
181        model = groups.models.Group
182        fields = [
183            'name',
184            'abbreviation',
185            'activity_category',
186            'group_class',
187            'group_status',
188            'group_funding',
189        ]
190
191
192class GroupListView(ListView):
193    model = groups.models.Group
194    template_object_name = 'group'
195
196    def get(self, *args, **kwargs):
197        qs = super(GroupListView, self).get_queryset()
198        self.filterset = GroupFilter(self.request.GET, qs)
199        return super(GroupListView, self).get(*args, **kwargs)
200
201    def get_queryset(self, ):
202        qs = self.filterset.qs
203        return qs
204
205    def get_context_data(self, **kwargs):
206        context = super(GroupListView, self).get_context_data(**kwargs)
207        # Add in the publisher
208        context['pagename'] = 'groups'
209        context['filter'] = self.filterset
210        return context
211
212
213class GroupDetailView(DetailView):
214    context_object_name = "group"
215    model = groups.models.Group
216    def get_context_data(self, **kwargs):
217        # Call the base implementation first to get a context
218        context = super(GroupDetailView, self).get_context_data(**kwargs)
219        group = context['group']
220
221        # Indicate whether this person should be able to see "private" info
222        context['viewpriv'] = self.request.user.has_perm('groups.view_group_private_info', group)
223        context['adminpriv'] = self.request.user.has_perm('groups.admin_group', group)
224        context['notes'] = group.viewable_notes(self.request.user)
225
226        # People involved in the group
227        just_roles = groups.models.OfficerRole.objects.all()
228        if context['viewpriv'] or self.request.user.has_perm('groups.view_signatories'):
229            # Can see the non-public stuff
230            pass
231        else:
232            just_roles = just_roles.filter(publicly_visible=True)
233        roles = []
234        for role in just_roles:
235            roles.append((role.display_name, role, group.officers(role=role), ))
236        context['roles'] = roles
237
238        return context
239
240
241class GroupHistoryView(ListView):
242    context_object_name = "version_list"
243    template_name = "groups/group_version.html"
244
245    def get_queryset(self):
246        history_entries = None
247        if 'group' in self.kwargs:
248            group = get_object_or_404(groups.models.Group, pk=self.kwargs['group'])
249            history_entries = reversion.models.Version.objects.get_for_object(group)
250        else:
251            history_entries = reversion.models.Version.objects.all()
252            group_content_type = ContentType.objects.get_for_model(groups.models.Group)
253            history_entries = history_entries.filter(content_type=group_content_type)
254        length = len(history_entries)
255        if length > 150:
256            history_entries = history_entries[length-100:]
257        return history_entries
258
259    def get_context_data(self, **kwargs):
260        context = super(GroupHistoryView, self).get_context_data(**kwargs)
261        if 'group' in self.kwargs:
262            group = get_object_or_404(groups.models.Group, pk=self.kwargs['group'])
263            context['title'] = "History for %s" % (group.name, )
264        else:
265            context['title'] = "Recent Changes"
266        return context
267
268
269def load_officers(group, ):
270    officers = group.officers()
271    people = list(set([ officer.person for officer in officers ]))
272    roles  = groups.models.OfficerRole.objects.all()
273
274    officers_map = {}
275    for officer in officers:
276        officers_map[(officer.person, officer.role)] = officer
277
278    return people, roles, officers_map
279
280def manage_officers(request, group_id, ):
281    group = get_object_or_404(groups.models.Group, pk=group_id)
282
283    if not request.user.has_perm('groups.admin_group', group):
284        raise PermissionDenied
285
286    max_new = 4
287
288    people, roles, officers_map = load_officers(group)
289
290    msgs = []
291    changes = []
292    edited = False
293    kept = 0
294    kept_not = 0
295    if request.method == 'POST': # If the form has been submitted
296        edited = True
297
298        new_people = {}
299        moira_accounts = {}
300        for i in range(max_new):
301            key = "extra.%d" % (i, )
302            if key in request.POST and request.POST[key] != "":
303                username = request.POST[key]
304                try:
305                    moira_accounts[username] = groups.models.AthenaMoiraAccount.active_accounts.get(username=username)
306                    new_people[i] = username
307                except groups.models.AthenaMoiraAccount.DoesNotExist:
308                    msgs.append('Athena account "%s" appears not to exist. Changes involving them have been ignored.' % (username, ))
309        for person in people:
310            try:
311                moira_accounts[person] = groups.models.AthenaMoiraAccount.active_accounts.get(username=person)
312            except groups.models.AthenaMoiraAccount.DoesNotExist:
313                msgs.append('Athena account "%s" appears not to exist. They can not be added to new roles. You should remove them from any roles they hold, if you have not already.' % (person, ))
314        for role in roles:
315            key = "holders.%s" % (role.slug, )
316            new_holders = set()
317            if key in request.POST:
318                new_holders = set(request.POST.getlist(key, ))
319            if len(new_holders) > role.max_count:
320                msgs.append("You selected %d people for %s; only %d are allowed. No changes to %s have been carried out in this update." %
321                    (len(new_holders), role.display_name, role.max_count, role.display_name, )
322                )
323            else:
324                for person in people:
325                    if person in new_holders:
326                        if (person, role) in officers_map:
327                            if role.require_student and not moira_accounts[person].is_student():
328                                msgs.append('Only students can have the %s role, and %s does not appear to be a student. (If this is not the case, please contact us.) You should replace this person ASAP.' % (role, person, ))
329                            #changes.append(("Kept", "yellow", person, role))
330                            kept += 1
331                        else:
332                            if person not in moira_accounts:
333                                msgs.append('Could not add nonexistent Athena account "%s" as %s.' % (person, role, ))
334                            elif role.require_student and not moira_accounts[person].is_student():
335                                msgs.append('Only students can have the %s role, and %s does not appear to be a student. (If this is not the case, please contact us.)' % (role, person, ))
336                            else:
337                                holder = groups.models.OfficeHolder(person=person, role=role, group=group,)
338                                holder.save()
339                                changes.append(("Added", "green", person, role))
340                    else:
341                        if (person, role) in officers_map:
342                            officers_map[(person, role)].expire()
343                            changes.append(("Removed", "red", person, role))
344                        else:
345                            kept_not += 1
346                            pass
347                for i in range(max_new):
348                    if "extra.%d" % (i, ) in new_holders:
349                        if i in new_people:
350                            person = new_people[i]
351                            if role.require_student and not moira_accounts[person].is_student():
352                                msgs.append('Only students can have the %s role, and %s does not appear to be a student.' % (role, person, ))
353                            else:
354                                holder = groups.models.OfficeHolder(person=person, role=role, group=group,)
355                                holder.save()
356                                changes.append(("Added", "green", person, role))
357
358        # mark as changed and reload the data
359        if changes:
360            group.set_updater(request.user)
361            group.save()
362        people, roles, officers_map = load_officers(group)
363
364    officers_data = []
365    for person in people:
366        role_list = []
367        for role in roles:
368            if (person, role) in officers_map:
369                role_list.append((role, True))
370            else:
371                role_list.append((role, False))
372        officers_data.append((False, person, role_list))
373    null_role_list = [(role, False) for role in roles]
374    for i in range(max_new):
375        officers_data.append((True, "extra.%d" % (i, ), null_role_list))
376
377    context = {
378        'group': group,
379        'roles': roles,
380        'people': people,
381        'officers': officers_data,
382        'edited': edited,
383        'changes':   changes,
384        'kept': kept,
385        'kept_not': kept_not,
386        'msgs': msgs,
387    }
388    return render_to_response('groups/group_change_officers.html', context, context_instance=RequestContext(request), )
389
390@permission_required('groups.view_signatories')
391def view_signatories(request, ):
392    # TODO:
393    # * limit which columns (roles) get displayed
394    # * limit which rows get displayed (maybe) by category or something
395    # This might want to wait for the generic reporting infrastructure, since
396    # I'd imagine some of it can be reused.
397
398    officers = groups.models.OfficeHolder.objects.filter(start_time__lte=datetime.datetime.now(), end_time__gte=datetime.datetime.now())
399    officers = officers.select_related(depth=1)
400    all_groups = groups.models.Group.objects.all()
401    roles = groups.models.OfficerRole.objects.all()
402    officers_map = collections.defaultdict(lambda: collections.defaultdict(set))
403    for officer in officers:
404        officers_map[officer.group][officer.role].add(officer.person)
405    officers_data = []
406    for group in all_groups:
407        role_list = []
408        for role in roles:
409            role_list.append(officers_map[group][role])
410        officers_data.append((group, role_list))
411
412    context = {
413        'roles': roles,
414        'officers': officers_data,
415    }
416    return render_to_response('groups/groups_signatories.html', context, context_instance=RequestContext(request), )
Note: See TracBrowser for help on using the repository browser.