source: asadb/forms/views.py @ ef00d33

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

Remove some dead code

Membership confirmations don't use the year...

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