source: asadb/forms/views.py @ ecb0997

space-accessstablestage
Last change on this file since ecb0997 was ecb0997, checked in by Alex Dehnert <adehnert@…>, 13 years ago

Minor cleanup of person membership update view

  • Property mode set to 100644
File size: 18.4 KB
Line 
1import forms.models
2import groups.models
3import groups.views
4import settings
5import util.emails
6
7from django.contrib.auth.decorators import user_passes_test, login_required, permission_required
8from django.views.generic import list_detail, ListView, DetailView
9from django.shortcuts import render_to_response, get_object_or_404
10from django.template import RequestContext
11from django.template import Context, Template
12from django.template.loader import get_template
13from django.http import Http404, HttpResponseRedirect, HttpResponse
14from django.core.urlresolvers import reverse
15from django.core.mail import EmailMessage, mail_admins
16from django.forms import Form
17from django.forms import ModelForm
18from django.forms import ModelChoiceField, ModelMultipleChoiceField
19from django.forms import ValidationError
20from django.db import connection
21from django.db.models import Q, Count
22
23import csv
24import datetime
25import StringIO
26
27#################
28# GENERIC VIEWS #
29#################
30
31class SelectGroupForm(Form):
32    group = ModelChoiceField(queryset=groups.models.Group.objects.all())
33    def __init__(self, *args, **kwargs):
34        queryset = None
35        if 'queryset' in kwargs:
36            queryset = kwargs['queryset']
37            del kwargs['queryset']
38        super(SelectGroupForm, self).__init__(*args, **kwargs)
39        if queryset is not None:
40            self.fields["group"].queryset = queryset
41
42def select_group(request, url_name_after, pagename='homepage', queryset=None, ):
43    if request.method == 'POST': # If the form has been submitted...
44        # A form bound to the POST data
45        form = SelectGroupForm(request.POST, queryset=queryset, )
46        if form.is_valid(): # All validation rules pass
47            group = form.cleaned_data['group'].id
48            return HttpResponseRedirect(reverse(url_name_after, args=[group],)) # Redirect after POST
49    else:
50        form = SelectGroupForm(queryset=queryset, ) # An unbound form
51
52    context = {
53        'form':form,
54        'pagename':pagename,
55    }
56    return render_to_response('forms/select.html', context, context_instance=RequestContext(request), )
57
58#############################
59# FIRST-YEAR SUMMER MAILING #
60#############################
61
62@login_required
63def fysm_by_years(request, year, category, ):
64    if year is None: year = datetime.date.today().year
65    queryset = forms.models.FYSM.objects.filter(year=year).order_by('group__name')
66    category_obj = None
67    category_name = 'main'
68    if category != None:
69        category_obj = get_object_or_404(forms.models.FYSMCategory, slug=category)
70        category_name = category_obj.name
71        queryset = queryset.filter(categories=category_obj)
72    forms.models.FYSMView.record_metric(request=request, fysm=None, year=year, page=category_name, )
73    categories = forms.models.FYSMCategory.objects.all()
74    return list_detail.object_list(
75        request,
76        queryset=queryset,
77        template_name="fysm/fysm_listing.html",
78        template_object_name="fysm",
79        extra_context={
80            "year": year,
81            "pagename": "fysm",
82            "category": category_obj,
83            "categories": categories,
84        }
85    )
86
87@login_required
88def fysm_view(request, year, submission, ):
89    submit_obj = get_object_or_404(forms.models.FYSM, pk=submission,)
90    all = forms.models.FYSM.objects.only("id", "display_name", )
91    try:
92        prev = all.filter(display_name__lt=submit_obj.display_name).order_by("-display_name")[0]
93    except IndexError:
94        prev = None
95    try:
96        next = all.filter(display_name__gt=submit_obj.display_name).order_by("display_name")[0]
97    except IndexError:
98        next = None
99    forms.models.FYSMView.record_metric(request=request, fysm=submit_obj, year=year, page="detail", )
100    return list_detail.object_detail(
101        request,
102        forms.models.FYSM.objects,
103        object_id=submission,
104        template_name="fysm/fysm_detail.html",
105        template_object_name="fysm",
106        extra_context={
107            "year": year,
108            "pagename": "fysm",
109            "prev": prev,
110            "next": next,
111        },
112    )
113
114def fysm_link(request, year, link_type, submission, ):
115    submit_obj = get_object_or_404(forms.models.FYSM, pk=submission,)
116    if submit_obj.year != int(year):
117        raise Http404("Year mismatch: fysm.year='%s', request's year='%s'" % (submit_obj.year, year, ))
118    if link_type == 'join':
119        url = submit_obj.join_url
120    elif link_type == 'website':
121        url = submit_obj.website
122    else:
123        raise Http404("Unknown link type")
124    forms.models.FYSMView.record_metric(request=request, fysm=submit_obj, year=year, page=link_type, )
125    return HttpResponseRedirect(url)
126
127def select_group_fysm(request, ):
128    qobj = Q(activity_category__isnull = True) | ~(Q(activity_category__name='Dorm') | Q(activity_category__name='FSILG'))
129    queryset = groups.models.Group.active_groups.filter(qobj)
130    return select_group(
131        request,
132        url_name_after='fysm-manage',
133        pagename='fysm',
134        queryset=queryset,
135    )
136
137class FYSMRequestForm(ModelForm):
138    class Meta:
139        model = forms.models.FYSM
140        fields = (
141            'display_name',
142            'website',
143            'join_url',
144            'contact_email',
145            'description',
146            'logo',
147            'slide',
148            'tags',
149            'categories',
150        )
151
152    def clean_display_name(self, ):
153        name = self.cleaned_data['display_name']
154        if ',' in name:
155            raise ValidationError("""In general, commas in a display name are a mistake and will look bad (group names like "Punctuation Society, MIT" should probably be "Punctuation Society"). If you do want a comma, contact asa-fysm@mit.edu and we'll put it in for you.""")
156        return name
157
158    def clean_description(self, ):
159        description = self.cleaned_data['description']
160        length = len(description)
161        if length > 400:
162            raise ValidationError("Descriptions are capped at 400 characters, and this one is %d characters." % (length, ))
163        return description
164
165@login_required
166def fysm_manage(request, group, ):
167    year = datetime.date.today().year
168    group_obj = get_object_or_404(groups.models.Group, pk=group)
169
170    initial = {}
171    try:
172        fysm_obj = forms.models.FYSM.objects.get(group=group_obj, year=year, )
173        print "Successfully found", fysm_obj.__dict__
174    except forms.models.FYSM.DoesNotExist:
175        fysm_obj = forms.models.FYSM()
176        fysm_obj.group = group_obj
177        fysm_obj.year = year
178        initial['display_name'] = group_obj.name
179        initial['year'] = year
180        initial['website'] = group_obj.website_url
181        initial['join_url'] = group_obj.website_url
182        initial['contact_email'] = group_obj.officer_email
183
184    if request.method == 'POST': # If the form has been submitted...
185        form = FYSMRequestForm(request.POST, request.FILES, instance=fysm_obj, ) # A form bound to the POST data
186
187        if form.is_valid(): # All validation rules pass
188            request_obj = form.save()
189
190            view_path = reverse('fysm-view', args=[year, request_obj.pk, ])
191            view_uri = '%s://%s%s' % (request.is_secure() and 'https' or 'http',
192                 request.get_host(), view_path)
193
194            # Send email
195            email = util.emails.email_from_template(
196                tmpl='fysm/update_email.txt',
197                context = Context({
198                    'group': group_obj,
199                    'fysm': fysm_obj,
200                    'view_uri': view_uri,
201                    'submitter': request.user,
202                    'request': request,
203                    'sender': "ASA FYSM team",
204                }),
205                subject='FYSM entry for "%s" updated by "%s"' % (
206                    group_obj.name,
207                    request.user,
208                ),
209                to=[group_obj.officer_email, request.user.email, ],
210                from_email='asa-fysm@mit.edu',
211            )
212            email.bcc = ['asa-fysm-submissions@mit.edu']
213            email.send()
214            return HttpResponseRedirect(reverse('fysm-thanks', args=[fysm_obj.pk],)) # Redirect after POST
215
216    else:
217        form = FYSMRequestForm(instance=fysm_obj, initial=initial, ) # An unbound form
218
219    context = {
220        'group':group_obj,
221        'fysm':fysm_obj,
222        'form':form,
223        'categories':forms.models.FYSMCategory.objects.all(),
224        'pagename':'fysm',
225    }
226    return render_to_response('fysm/submit.html', context, context_instance=RequestContext(request), )
227
228
229def fysm_thanks(request, fysm, ):
230    year = datetime.date.today().year
231    fysm_obj = get_object_or_404(forms.models.FYSM, pk=fysm)
232
233    context = {
234        'group':fysm_obj.group,
235        'fysm':fysm_obj,
236        'pagename':'fysm',
237    }
238    return render_to_response('fysm/thanks.html', context, context_instance=RequestContext(request), )
239
240#####################
241# Membership update #
242#####################
243
244membership_update_qs = groups.models.Group.objects.filter(group_status__slug__in=['active', 'suspended', ])
245
246class Form_GroupMembershipUpdate(ModelForm):
247    group = ModelChoiceField(queryset=membership_update_qs)
248
249    def __init__(self, *args, **kwargs):
250        super(Form_GroupMembershipUpdate, self).__init__(*args, **kwargs)
251        self.fields['no_hazing'].required = True
252
253    class Meta:
254        model = forms.models.GroupMembershipUpdate
255        fields = [
256            'group',
257            'updater_title',
258            'group_email',
259            'officer_email',
260            'email_preface',
261            'no_hazing',
262            'no_discrimination',
263            'membership_definition',
264            'num_undergrads',
265            'num_grads',
266            'num_alum',
267            'num_other_affiliate',
268            'num_other',
269            'membership_list',
270        ]
271
272@login_required
273def group_membership_update(request, ):
274    year = datetime.date.today().year
275
276    initial = {
277    }
278    update_obj = forms.models.GroupMembershipUpdate()
279    update_obj.update_time  = datetime.datetime.now()
280    update_obj.updater_name = request.user.username
281
282    confirm_path = reverse('membership-confirm', )
283    confirm_uri = '%s://%s%s' % (request.is_secure() and 'https' or 'http',
284         request.get_host(), confirm_path)
285
286    if request.method == 'POST': # If the form has been submitted...
287        form = Form_GroupMembershipUpdate(request.POST, request.FILES, instance=update_obj) # A form bound to the POST data
288
289        if form.is_valid(): # All validation rules pass
290            request_obj = form.save()
291            group_obj = request_obj.group
292
293
294            # Send email
295            tmpl = get_template('membership/anti-hazing.txt')
296            ctx = Context({
297                'update': request_obj,
298                'group': group_obj,
299                'submitter': request.user,
300            })
301            body = tmpl.render(ctx)
302            email = EmailMessage(
303                subject='Anti-Hazing and Non-Discrimination Acknowledgement for %s' % (
304                    group_obj.name,
305                ),
306                body=body,
307                from_email=request.user.email,
308                to=[request_obj.group_email, ],
309                cc=[request_obj.officer_email, ],
310                bcc=['asa-db-outgoing@mit.edu', ],
311            )
312            email.send()
313
314            # Send email
315            tmpl = get_template('membership/submit-confirm-email.txt')
316            ctx = Context({
317                'update': request_obj,
318                'group': group_obj,
319                'submitter': request.user,
320                'confirm_uri': confirm_uri,
321            })
322            body = tmpl.render(ctx)
323            email = EmailMessage(
324                subject='ASA Membership Information for %s' % (
325                    group_obj.name,
326                ),
327                body=body,
328                from_email=request.user.email,
329                to=[request_obj.officer_email, ],
330                bcc=['asa-db-outgoing@mit.edu', ],
331            )
332            email.send()
333
334            return HttpResponseRedirect(reverse('membership-thanks', )) # Redirect after POST
335
336    else:
337        form = Form_GroupMembershipUpdate(initial=initial, ) # An unbound form
338
339    context = {
340        'form':form,
341        'confirm_uri': confirm_uri,
342        'pagename':'groups',
343    }
344    return render_to_response('membership/update.html', context, context_instance=RequestContext(request), )
345
346class Form_PersonMembershipUpdate(ModelForm):
347    groups = ModelMultipleChoiceField(queryset=membership_update_qs)
348    class Meta:
349        model = forms.models.PersonMembershipUpdate
350        fields = [
351            'groups',
352        ]
353
354@login_required
355def person_membership_update(request, ):
356    year = datetime.date.today().year
357
358    initial = {
359    }
360    cycle = forms.models.GroupConfirmationCycle.latest()
361
362    # Initialize/find the PersonMembershipUpdate for this user
363    try:
364        update_obj = forms.models.PersonMembershipUpdate.objects.get(
365            username=request.user.username,
366            deleted__isnull=True,
367            cycle=cycle,
368        )
369        selected_groups = update_obj.groups.all()
370        print "Got update"
371    except forms.models.PersonMembershipUpdate.DoesNotExist:
372        update_obj = forms.models.PersonMembershipUpdate()
373        update_obj.update_time  = datetime.datetime.now()
374        update_obj.username = request.user.username
375        update_obj.cycle = cycle
376        selected_groups = []
377
378    # Determine whether the submitter is a student or not
379    accounts = groups.models.AthenaMoiraAccount
380    try:
381        person = accounts.active_accounts.get(username=request.user.username)
382        if person.is_student():
383            update_obj.valid = forms.models.VALID_AUTOVALIDATED
384        else:
385            update_obj.valid = forms.models.VALID_AUTOREJECTED
386    except accounts.DoesNotExist:
387        pass
388        update_obj.valid = forms.models.VALID_AUTOREJECTED
389
390    update_obj.save()
391
392    # Find groups that list a role for the user
393    office_holders = groups.models.OfficeHolder.current_holders.filter(person=request.user.username)
394    role_groups = {}
395    for office_holder in office_holders:
396        if office_holder.group.pk not in role_groups:
397            role_groups[office_holder.group.pk] = (office_holder.group, set())
398        role_groups[office_holder.group.pk][1].add(office_holder.role.display_name)
399    print role_groups
400
401    # Find groups the user searched for
402    filterset = groups.views.GroupFilter(request.GET, membership_update_qs)
403    filtered_groups = filterset.qs.all()
404    show_filtered_groups = ('search' in request.GET)
405
406    message = ""
407    message_type = "info"
408
409    # Handle the single group add/remove forms
410    # * removing previously confirmed groups
411    # * add/remove groups that list the user in a role
412    # * add/remove groups the user searched for
413    if request.method == 'POST' and 'add-remove' in request.POST:
414        group = groups.models.Group.objects.get(id=request.POST['group'])
415        if request.POST['action'] == 'remove':
416            if group in update_obj.groups.all():
417                update_obj.groups.remove(group)
418                message = "You have successfully unconfirmed membership in %s." % (group, )
419            else:
420                message = "Removal failed because you had not confirmed membership in %s." % (group, )
421                message_type = "warn"
422        elif request.POST['action'] == 'add':
423            if group in update_obj.groups.all():
424                message = "Membership in %s already confirmed." % (group, )
425                message_type = "warn"
426            else:
427                update_obj.groups.add(group)
428                message = "You have successfully confirmed membership in %s." % (group, )
429        else:
430            message = "Uh, somehow you tried to do something besides adding and removing..."
431            message_type = "alert"
432
433    # Handle the big list of groups
434    if request.method == 'POST' and 'list' in request.POST:
435        form = Form_PersonMembershipUpdate(request.POST, request.FILES, instance=update_obj)
436        if form.is_valid():
437            request_obj = form.save()
438            message = "Update saved"
439    else:
440        form = Form_PersonMembershipUpdate(initial=initial, instance=update_obj, )
441
442    # Render the page
443    context = {
444        'role_groups':role_groups,
445        'form':form,
446        'filter':filterset,
447        'show_filtered_groups':show_filtered_groups,
448        'filtered_groups':filtered_groups,
449        'member_groups':selected_groups,
450        'message': message,
451        'message_type': message_type,
452        'pagename':'groups',
453    }
454    return render_to_response('membership/confirm.html', context, context_instance=RequestContext(request), )
455
456
457class View_GroupMembershipList(ListView):
458    context_object_name = "group_list"
459    template_name = "membership/submitted.html"
460
461    def get_queryset(self):
462        group_updates = forms.models.GroupMembershipUpdate.objects.all()
463        group_updates = group_updates.filter(
464            group__personmembershipupdate__deleted__isnull=True,
465            group__personmembershipupdate__valid__gt=0,
466        )
467        group_updates = group_updates.annotate(num_confirms=Count('group__personmembershipupdate'))
468        #print len(list(group_updates))
469        #for query in connection.queries: print query
470        return group_updates
471
472
473@permission_required('groups.view_group_private_info')
474def group_confirmation_issues(request, ):
475    active_groups = groups.models.Group.active_groups
476    group_updates = forms.models.GroupMembershipUpdate.objects.all()
477    people_confirmations = forms.models.PersonMembershipUpdate.objects.filter(
478        deleted__isnull=True,
479        valid__gt=0,
480    )
481
482    buf = StringIO.StringIO()
483    output = csv.writer(buf)
484    output.writerow(['group_id', 'group_name', 'issue', 'num_confirm', 'officer_email', ])
485
486    q_present = Q(id__in=group_updates.values('group'))
487    missing_groups = active_groups.filter(~q_present)
488    #print len(list(group_updates))
489    for group in missing_groups:
490        num_confirms = len(people_confirmations.filter(groups=group))
491        output.writerow([
492            group.id,
493            group.name,
494            'unsubmitted',
495            num_confirms,
496            group.officer_email,
497        ])
498
499    for group_update in group_updates:
500        group = group_update.group
501        num_confirms = len(people_confirmations.filter(groups=group))
502        if num_confirms < 5:
503            output.writerow([
504                group.id,
505                group.name,
506                'confirmations',
507                num_confirms,
508                group.officer_email,
509            ])
510
511
512    return HttpResponse(buf.getvalue(), mimetype='text/plain', )
Note: See TracBrowser for help on using the repository browser.