source: asadb/forms/views.py

stablestage
Last change on this file was 9605a6a, checked in by Alex Dehnert <adehnert@…>, 12 years ago

Add missing column heading (ASA-#258)

  • Property mode set to 100644
File size: 26.9 KB
RevLine 
[26826c9]1import collections
2import csv
3import datetime
4import StringIO
[90afb00]5
[afc5348]6from django.conf import settings
[0b72bfd]7from django.contrib.auth.decorators import user_passes_test, login_required, permission_required
[bda4d86]8from django.core.exceptions import PermissionDenied
[ea42397]9from django.views.generic import list_detail, ListView, DetailView
[aba463b]10from django.shortcuts import render_to_response, get_object_or_404
[90afb00]11from django.template import RequestContext
[f921734]12from django.template import Context, Template
13from django.template.loader import get_template
[0b72bfd]14from django.http import Http404, HttpResponseRedirect, HttpResponse
[90afb00]15from django.core.urlresolvers import reverse
[d199ba4]16from django.core.mail import EmailMessage, mail_admins
[d848b98]17from django.forms import FileField
[90afb00]18from django.forms import Form
19from django.forms import ModelForm
[b1a8ea7]20from django.forms import ModelChoiceField, ModelMultipleChoiceField
[3496370]21from django.forms import ValidationError
[4260e2c]22from django.db import connection
[ea42397]23from django.db.models import Q, Count
[aba463b]24
[f610262]25import django_filters
26
[26826c9]27import forms.models
28import groups.models
29import groups.views
30import util.emails
[aba463b]31
[90afb00]32#################
33# GENERIC VIEWS #
34#################
35
36class SelectGroupForm(Form):
37    group = ModelChoiceField(queryset=groups.models.Group.objects.all())
[2f7114b]38    def __init__(self, *args, **kwargs):
39        queryset = None
40        if 'queryset' in kwargs:
41            queryset = kwargs['queryset']
42            del kwargs['queryset']
[f921734]43        super(SelectGroupForm, self).__init__(*args, **kwargs)
44        if queryset is not None:
45            self.fields["group"].queryset = queryset
[90afb00]46
[bda4d86]47def select_group(request, url_name_after, url_args=[], pagename='homepage', queryset=None, title="", msg=""):
[90afb00]48    if request.method == 'POST': # If the form has been submitted...
[f921734]49        # A form bound to the POST data
50        form = SelectGroupForm(request.POST, queryset=queryset, )
[90afb00]51        if form.is_valid(): # All validation rules pass
52            group = form.cleaned_data['group'].id
[bda4d86]53            return HttpResponseRedirect(reverse(url_name_after, args=url_args+[group],)) # Redirect after POST
[90afb00]54    else:
[f921734]55        form = SelectGroupForm(queryset=queryset, ) # An unbound form
[90afb00]56
[bda4d86]57    if not title: title = "Select group"
[90afb00]58    context = {
59        'form':form,
[bda4d86]60        'title':title,
61        'msg':msg,
[0b488d4]62        'pagename':pagename,
[90afb00]63    }
64    return render_to_response('forms/select.html', context, context_instance=RequestContext(request), )
65
66#############################
67# FIRST-YEAR SUMMER MAILING #
68#############################
69
[00e16ed]70@login_required
[97399af]71def fysm_by_years(request, year, category, ):
[aba463b]72    if year is None: year = datetime.date.today().year
73    queryset = forms.models.FYSM.objects.filter(year=year).order_by('group__name')
[dafa3c8]74    category_obj = None
[a10dd4b]75    category_name = 'main'
[dafa3c8]76    if category != None:
[c27da9e]77        category_obj = get_object_or_404(forms.models.FYSMCategory, slug=category)
[a10dd4b]78        category_name = category_obj.name
[c27da9e]79        queryset = queryset.filter(categories=category_obj)
[a10dd4b]80    forms.models.FYSMView.record_metric(request=request, fysm=None, year=year, page=category_name, )
[c27da9e]81    categories = forms.models.FYSMCategory.objects.all()
[aba463b]82    return list_detail.object_list(
83        request,
84        queryset=queryset,
[90afb00]85        template_name="fysm/fysm_listing.html",
[aba463b]86        template_object_name="fysm",
87        extra_context={
88            "year": year,
89            "pagename": "fysm",
[dafa3c8]90            "category": category_obj,
[189506e]91            "categories": categories,
[aba463b]92        }
93    )
[90afb00]94
[00e16ed]95@login_required
[aaa8e04]96def fysm_view(request, year, submission, ):
[ae881e5]97    submit_obj = get_object_or_404(forms.models.FYSM, pk=submission,)
98    all = forms.models.FYSM.objects.only("id", "display_name", )
99    try:
100        prev = all.filter(display_name__lt=submit_obj.display_name).order_by("-display_name")[0]
101    except IndexError:
102        prev = None
103    try:
104        next = all.filter(display_name__gt=submit_obj.display_name).order_by("display_name")[0]
105    except IndexError:
106        next = None
[a10dd4b]107    forms.models.FYSMView.record_metric(request=request, fysm=submit_obj, year=year, page="detail", )
[aaa8e04]108    return list_detail.object_detail(
109        request,
110        forms.models.FYSM.objects,
111        object_id=submission,
112        template_name="fysm/fysm_detail.html",
113        template_object_name="fysm",
114        extra_context={
115            "year": year,
116            "pagename": "fysm",
[ae881e5]117            "prev": prev,
118            "next": next,
[aaa8e04]119        },
120    )
121
[f462be6]122def fysm_link(request, year, link_type, submission, ):
123    submit_obj = get_object_or_404(forms.models.FYSM, pk=submission,)
124    if submit_obj.year != int(year):
125        raise Http404("Year mismatch: fysm.year='%s', request's year='%s'" % (submit_obj.year, year, ))
126    if link_type == 'join':
127        url = submit_obj.join_url
128    elif link_type == 'website':
129        url = submit_obj.website
130    else:
131        raise Http404("Unknown link type")
[a10dd4b]132    forms.models.FYSMView.record_metric(request=request, fysm=submit_obj, year=year, page=link_type, )
[f462be6]133    return HttpResponseRedirect(url)
134
[f921734]135def select_group_fysm(request, ):
[2c0eaa1]136    qobj = Q(activity_category__isnull = True) | ~(Q(activity_category__name='Dorm') | Q(activity_category__name='FSILG'))
[d3167b9]137    queryset = groups.models.Group.active_groups.filter(qobj)
[f921734]138    return select_group(
139        request,
140        url_name_after='fysm-manage',
141        pagename='fysm',
142        queryset=queryset,
143    )
144
[90afb00]145class FYSMRequestForm(ModelForm):
146    class Meta:
147        model = forms.models.FYSM
148        fields = (
149            'display_name',
150            'website',
151            'join_url',
152            'contact_email',
153            'description',
154            'logo',
[57f8ffa]155            'tags',
[c27da9e]156            'categories',
[90afb00]157        )
158
[3496370]159    def clean_display_name(self, ):
160        name = self.cleaned_data['display_name']
161        if ',' in name:
162            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.""")
163        return name
164
[cbdbd1f]165    def clean_description(self, ):
166        description = self.cleaned_data['description']
167        length = len(description)
168        if length > 400:
169            raise ValidationError("Descriptions are capped at 400 characters, and this one is %d characters." % (length, ))
170        return description
171
[00e16ed]172@login_required
[90afb00]173def fysm_manage(request, group, ):
174    year = datetime.date.today().year
175    group_obj = get_object_or_404(groups.models.Group, pk=group)
176
177    initial = {}
178    try:
179        fysm_obj = forms.models.FYSM.objects.get(group=group_obj, year=year, )
180    except forms.models.FYSM.DoesNotExist:
181        fysm_obj = forms.models.FYSM()
182        fysm_obj.group = group_obj
183        fysm_obj.year = year
184        initial['display_name'] = group_obj.name
185        initial['year'] = year
186        initial['website'] = group_obj.website_url
187        initial['join_url'] = group_obj.website_url
188        initial['contact_email'] = group_obj.officer_email
189
190    if request.method == 'POST': # If the form has been submitted...
191        form = FYSMRequestForm(request.POST, request.FILES, instance=fysm_obj, ) # A form bound to the POST data
192
193        if form.is_valid(): # All validation rules pass
194            request_obj = form.save()
195
[1f4ee0b]196            view_uri = request.build_absolute_uri(reverse('fysm-view', args=[year, request_obj.pk, ]))
[adfe18c]197
[90afb00]198            # Send email
[cd98e17]199            email = util.emails.email_from_template(
200                tmpl='fysm/update_email.txt',
201                context = Context({
202                    'group': group_obj,
203                    'fysm': fysm_obj,
204                    'view_uri': view_uri,
205                    'submitter': request.user,
206                    'request': request,
207                    'sender': "ASA FYSM team",
208                }),
[d199ba4]209                subject='FYSM entry for "%s" updated by "%s"' % (
[90afb00]210                    group_obj.name,
211                    request.user,
212                ),
[c49fbe2]213                to=[group_obj.officer_email, request.user.email, ],
[cd98e17]214                from_email='asa-fysm@mit.edu',
[90afb00]215            )
[cd98e17]216            email.bcc = ['asa-fysm-submissions@mit.edu']
[d199ba4]217            email.send()
[90afb00]218            return HttpResponseRedirect(reverse('fysm-thanks', args=[fysm_obj.pk],)) # Redirect after POST
219
220    else:
221        form = FYSMRequestForm(instance=fysm_obj, initial=initial, ) # An unbound form
222
223    context = {
224        'group':group_obj,
225        'fysm':fysm_obj,
226        'form':form,
[07caee0]227        'categories':forms.models.FYSMCategory.objects.all(),
[90afb00]228        'pagename':'fysm',
229    }
230    return render_to_response('fysm/submit.html', context, context_instance=RequestContext(request), )
231
232
233def fysm_thanks(request, fysm, ):
234    year = datetime.date.today().year
235    fysm_obj = get_object_or_404(forms.models.FYSM, pk=fysm)
236
237    context = {
238        'group':fysm_obj.group,
239        'fysm':fysm_obj,
240        'pagename':'fysm',
241    }
242    return render_to_response('fysm/thanks.html', context, context_instance=RequestContext(request), )
[5b834ab]243
244#####################
245# Membership update #
246#####################
247
[055a7e9]248membership_update_qs = groups.models.Group.objects.filter(group_status__slug__in=['active', 'suspended', ])
249
[bda4d86]250@login_required
251def group_membership_update_select_group(request, ):
252    cycle = forms.models.GroupConfirmationCycle.latest()
253
[88843ac]254    users_groups = groups.models.Group.admin_groups(request.user.username)
[bda4d86]255    qs = membership_update_qs.filter(pk__in=users_groups)
[b1a8ea7]256
[bda4d86]257    return select_group(request=request,
258        url_name_after='membership-update-group',
259        url_args=[cycle.slug],
260        pagename='groups',
261        queryset=qs,
262        title="Submit membership update for...",
263    )
264
265class Form_GroupMembershipUpdate(ModelForm):
[5b834ab]266    def __init__(self, *args, **kwargs):
267        super(Form_GroupMembershipUpdate, self).__init__(*args, **kwargs)
268        self.fields['no_hazing'].required = True
[d9624e8]269        help_text = "If you have a membership list, you can get help turning it into membership breakdown using our <a href='%s'>people lookup</a> tool. You'll need to copy the numbers back over, though." % (reverse('membership-people-lookup'), )
270        self.fields['num_undergrads'].help_text = help_text
[5b834ab]271
272    class Meta:
273        model = forms.models.GroupMembershipUpdate
274        fields = [
275            'updater_title',
276            'group_email',
277            'officer_email',
[185fde1]278            'email_preface',
[5b834ab]279            'no_hazing',
[185fde1]280            'no_discrimination',
[5b834ab]281            'membership_definition',
282            'num_undergrads',
283            'num_grads',
[ceaf3bd]284            'num_alum',
285            'num_other_affiliate',
[5b834ab]286            'num_other',
[e4a35ff]287            #'membership_list',
[5b834ab]288        ]
289
290@login_required
[bda4d86]291def group_membership_update(request, cycle_slug, pk, ):
292    cycle = get_object_or_404(forms.models.GroupConfirmationCycle, slug=cycle_slug)
293    group_obj = get_object_or_404(groups.models.Group, pk=pk)
294    if not request.user.has_perm('groups.admin_group', group_obj):
295        raise PermissionDenied
296
297    try:
298        update_obj = forms.models.GroupMembershipUpdate.objects.get(group=group_obj, cycle=cycle, )
299    except forms.models.GroupMembershipUpdate.DoesNotExist:
300        update_obj = None
[5b834ab]301
[1f4ee0b]302    confirm_uri = request.build_absolute_uri(reverse('membership-confirm'))
[6e247d3]303    submitted_uri = request.build_absolute_uri(reverse('membership-submitted'))
[5b834ab]304
[bda4d86]305    if request.method == 'POST':
[5b834ab]306        form = Form_GroupMembershipUpdate(request.POST, request.FILES, instance=update_obj) # A form bound to the POST data
307
308        if form.is_valid(): # All validation rules pass
[bda4d86]309            # Update the updater info
310            form.instance.group = group_obj
311            form.instance.cycle = cycle
312            form.instance.update_time  = datetime.datetime.now()
313            form.instance.updater_name = request.user.username
[5b834ab]314            request_obj = form.save()
315
316            # Send email
317            tmpl = get_template('membership/anti-hazing.txt')
318            ctx = Context({
319                'update': request_obj,
320                'group': group_obj,
321                'submitter': request.user,
322            })
323            body = tmpl.render(ctx)
324            email = EmailMessage(
[185fde1]325                subject='Anti-Hazing and Non-Discrimination Acknowledgement for %s' % (
[5b834ab]326                    group_obj.name,
327                ),
328                body=body,
329                from_email=request.user.email,
330                to=[request_obj.group_email, ],
331                cc=[request_obj.officer_email, ],
[5c68184]332                bcc=['asa-db-outgoing@mit.edu', ],
[5b834ab]333            )
334            email.send()
335
336            # Send email
337            tmpl = get_template('membership/submit-confirm-email.txt')
338            ctx = Context({
339                'update': request_obj,
340                'group': group_obj,
341                'submitter': request.user,
342                'confirm_uri': confirm_uri,
[6e247d3]343                'submitted_uri': submitted_uri,
[5b834ab]344            })
345            body = tmpl.render(ctx)
346            email = EmailMessage(
347                subject='ASA Membership Information for %s' % (
348                    group_obj.name,
349                ),
350                body=body,
351                from_email=request.user.email,
352                to=[request_obj.officer_email, ],
[5c68184]353                bcc=['asa-db-outgoing@mit.edu', ],
[5b834ab]354            )
355            email.send()
356
357            return HttpResponseRedirect(reverse('membership-thanks', )) # Redirect after POST
358
359    else:
[bda4d86]360        form = Form_GroupMembershipUpdate(instance=update_obj)
[5b834ab]361
362    context = {
363        'form':form,
[bda4d86]364        'group':group_obj,
[c7b6a3a]365        'cycle':cycle,
[5b834ab]366        'confirm_uri': confirm_uri,
367        'pagename':'groups',
368    }
369    return render_to_response('membership/update.html', context, context_instance=RequestContext(request), )
370
371class Form_PersonMembershipUpdate(ModelForm):
[055a7e9]372    groups = ModelMultipleChoiceField(queryset=membership_update_qs)
[5b834ab]373    class Meta:
374        model = forms.models.PersonMembershipUpdate
375        fields = [
376            'groups',
377        ]
378
379@login_required
380def person_membership_update(request, ):
381    initial = {
382    }
[c297267]383    cycle = forms.models.GroupConfirmationCycle.latest()
[ecb0997]384
385    # Initialize/find the PersonMembershipUpdate for this user
[c297267]386    try:
387        update_obj = forms.models.PersonMembershipUpdate.objects.get(
388            username=request.user.username,
389            deleted__isnull=True,
390            cycle=cycle,
391        )
392        selected_groups = update_obj.groups.all()
393    except forms.models.PersonMembershipUpdate.DoesNotExist:
394        update_obj = forms.models.PersonMembershipUpdate()
395        update_obj.update_time  = datetime.datetime.now()
396        update_obj.username = request.user.username
397        update_obj.cycle = cycle
398        selected_groups = []
399
[21c6ad4]400    # Determine whether the submitter is a student or not
[c297267]401    accounts = groups.models.AthenaMoiraAccount
402    try:
403        person = accounts.active_accounts.get(username=request.user.username)
404        if person.is_student():
405            update_obj.valid = forms.models.VALID_AUTOVALIDATED
406        else:
407            update_obj.valid = forms.models.VALID_AUTOREJECTED
408    except accounts.DoesNotExist:
409        pass
410        update_obj.valid = forms.models.VALID_AUTOREJECTED
411
[8dfd3db]412    update_obj.save()
413
[21c6ad4]414    # Find groups that list a role for the user
415    office_holders = groups.models.OfficeHolder.current_holders.filter(person=request.user.username)
416    role_groups = {}
417    for office_holder in office_holders:
418        if office_holder.group.pk not in role_groups:
419            role_groups[office_holder.group.pk] = (office_holder.group, set())
420        role_groups[office_holder.group.pk][1].add(office_holder.role.display_name)
421
[c297267]422    message = ""
[56e8cb8]423    message_type = "info"
424
[11941e1]425    if update_obj.valid <= forms.models.VALID_UNSET:
426        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."
427        message_type = "warn"
428
[ecb0997]429    # Handle the single group add/remove forms
430    # * removing previously confirmed groups
431    # * add/remove groups that list the user in a role
432    # * add/remove groups the user searched for
[56e8cb8]433    if request.method == 'POST' and 'add-remove' in request.POST:
434        group = groups.models.Group.objects.get(id=request.POST['group'])
435        if request.POST['action'] == 'remove':
436            if group in update_obj.groups.all():
437                update_obj.groups.remove(group)
[a50f67b]438                message = "You have successfully unconfirmed membership in %s." % (group, )
[56e8cb8]439            else:
[a50f67b]440                message = "Removal failed because you had not confirmed membership in %s." % (group, )
[56e8cb8]441                message_type = "warn"
442        elif request.POST['action'] == 'add':
443            if group in update_obj.groups.all():
[a50f67b]444                message = "Membership in %s already confirmed." % (group, )
[56e8cb8]445                message_type = "warn"
446            else:
447                update_obj.groups.add(group)
[a50f67b]448                message = "You have successfully confirmed membership in %s." % (group, )
[56e8cb8]449        else:
450            message = "Uh, somehow you tried to do something besides adding and removing..."
451            message_type = "alert"
452
[ecb0997]453    # Handle the big list of groups
454    if request.method == 'POST' and 'list' in request.POST:
455        form = Form_PersonMembershipUpdate(request.POST, request.FILES, instance=update_obj)
456        if form.is_valid():
[5b834ab]457            request_obj = form.save()
[c297267]458            message = "Update saved"
[5b834ab]459    else:
[ecb0997]460        form = Form_PersonMembershipUpdate(initial=initial, instance=update_obj, )
[4d79976]461    form.fields['groups'].widget.attrs['size'] = 20
[5b834ab]462
[ecb0997]463    # Render the page
[5b834ab]464    context = {
[21c6ad4]465        'role_groups':role_groups,
[5b834ab]466        'form':form,
[56e8cb8]467        'member_groups':selected_groups,
[c297267]468        'message': message,
[56e8cb8]469        'message_type': message_type,
[5b834ab]470        'pagename':'groups',
471    }
472    return render_to_response('membership/confirm.html', context, context_instance=RequestContext(request), )
[ea42397]473
474
475class View_GroupMembershipList(ListView):
476    context_object_name = "group_list"
477    template_name = "membership/submitted.html"
478
479    def get_queryset(self):
[bda4d86]480        cycle = forms.models.GroupConfirmationCycle.latest()
[ea42397]481        group_updates = forms.models.GroupMembershipUpdate.objects.all()
[4260e2c]482        group_updates = group_updates.filter(
[bda4d86]483            cycle=cycle,
484            group__personmembershipupdate__cycle=cycle,
[4260e2c]485            group__personmembershipupdate__deleted__isnull=True,
486            group__personmembershipupdate__valid__gt=0,
487        )
488        group_updates = group_updates.annotate(num_confirms=Count('group__personmembershipupdate'))
489        #print len(list(group_updates))
490        #for query in connection.queries: print query
[ea42397]491        return group_updates
492
[0b72bfd]493
[beb46fb]494class View_GroupConfirmationCyclesList(ListView):
495    context_object_name = "cycle_list"
496    template_name = "membership/admin.html"
497    model = forms.models.GroupConfirmationCycle
498
499    def get_context_data(self, **kwargs):
500        context = super(View_GroupConfirmationCyclesList, self).get_context_data(**kwargs)
501        context['pagename'] = 'groups'
502        return context
503
504
[0b72bfd]505@permission_required('groups.view_group_private_info')
[beb46fb]506def group_confirmation_issues(request, slug, ):
[d6f8984]507    account_numbers = ("accounts" in request.GET) and request.GET['accounts'] == "1"
508
[a526ed2]509    check_groups = groups.models.Group.objects.filter(group_status__slug__in=('active', 'suspended', ))
[0e2426b]510    check_groups = check_groups.select_related('group_status')
[beb46fb]511    group_updates = forms.models.GroupMembershipUpdate.objects.filter(cycle__slug=slug, )
[0e2426b]512    group_updates = group_updates.select_related('group', 'group__group_status')
[0b72bfd]513    people_confirmations = forms.models.PersonMembershipUpdate.objects.filter(
514        deleted__isnull=True,
515        valid__gt=0,
[beb46fb]516        cycle__slug=slug,
[0b72bfd]517    )
518
519    buf = StringIO.StringIO()
520    output = csv.writer(buf)
[9605a6a]521    fields = ['group_id', 'group_name', 'group_status', 'recognition_date', 'issue', 'num_confirm', 'officer_email', ]
[d6f8984]522    if account_numbers: fields.append("main_account")
523    output.writerow(fields)
[0b72bfd]524
[08b7660]525    def output_issue(group, issue, num_confirms):
[d6f8984]526        fields = [
[0b72bfd]527            group.id,
528            group.name,
[a526ed2]529            group.group_status.slug,
[2462be8]530            group.recognition_date,
[08b7660]531            issue,
[0b72bfd]532            num_confirms,
533            group.officer_email,
[d6f8984]534        ]
535        if account_numbers: fields.append(group.main_account_id)
536        output.writerow(fields)
[0b72bfd]537
[08b7660]538    q_present = Q(id__in=group_updates.values('group'))
[a526ed2]539    missing_groups = check_groups.filter(~q_present)
[08b7660]540    #print len(list(group_updates))
541    for group in missing_groups:
[0e2426b]542        #num_confirms = len(people_confirmations.filter(groups=group))
543        output_issue(group, 'unsubmitted', '')
[08b7660]544
[0b72bfd]545    for group_update in group_updates:
546        group = group_update.group
[429e162]547        num_confirms = people_confirmations.filter(groups=group).count()
[de025c2]548        problems = []
549
[0b72bfd]550        if num_confirms < 5:
[de025c2]551            problems.append("confirmations")
552
553        num_students = group_update.num_undergrads + group_update.num_grads
554        num_other = group_update.num_alum + group_update.num_other_affiliate + group_update.num_other
555        if num_students < num_other:
556            problems.append("50%")
557
558        for problem in problems:
[08b7660]559            output_issue(group, problem, num_confirms)
[0b72bfd]560
[360b3d4]561    return HttpResponse(buf.getvalue(), mimetype='text/csv', )
[d848b98]562
563
[88a856b]564class PeopleStatusLookupForm(ModelForm):
565    class Meta:
566        model = forms.models.PeopleStatusLookup
567        fields = ('people', )
568
569def people_status_lookup(request, pk=None, ):
570    if pk is None:
571        if request.method == 'POST':
572            form = PeopleStatusLookupForm(request.POST, request.FILES, )
573            if form.is_valid(): # All validation rules pass
574                lookup = form.save(commit=False)
575                lookup.requestor = request.user
576                lookup.referer = request.META['HTTP_REFERER']
[d66942f]577                lookup.update_classified_people()
578                results = lookup.classifications_with_descriptions()
[88a856b]579                lookup.save()
580        else:
581            form = PeopleStatusLookupForm()
582            results = None
583    else:
584        if request.user.has_perm('forms.view_peoplestatusupdate'):
585            lookup = get_object_or_404(forms.models.PeopleStatusLookup, pk=int(pk))
[d66942f]586            results = lookup.classifications_with_descriptions()
[88a856b]587            form = None
588        else:
589            raise PermissionDenied("You don't have permission to view old lookup requests.")
590
591    context = {
592        'form': form,
593        'results': results,
594    }
595
596    return render_to_response('membership/people-lookup.html', context, context_instance=RequestContext(request), )
[d848b98]597
598##########
599# Midway #
600##########
601
[870992c]602
603class View_Midways(ListView):
604    context_object_name = "midway_list"
605    template_name = "midway/midway_list.html"
606
607    def get_queryset(self):
608        midways = forms.models.Midway.objects.order_by('date')
609        return midways
610
611    def get_context_data(self, **kwargs):
612        context = super(View_Midways, self).get_context_data(**kwargs)
613        context['pagename'] = 'midway'
614        return context
615
[1cfab5a]616def midway_map_latest(request, ):
617    midways = forms.models.Midway.objects.order_by('-date')[:1]
618    if len(midways) == 0:
619        raise Http404("No midways found.")
620    else:
621        url = reverse('midway-map', args=(midways[0].slug, ))
622        return HttpResponseRedirect(url)
623
[d848b98]624
[f610262]625class MidwayAssignmentFilter(django_filters.FilterSet):
626    name = django_filters.CharFilter(name='group__name', lookup_type='icontains', label="Name contains")
627    abbreviation = django_filters.CharFilter(name='group__abbreviation', lookup_type='iexact', label="Abbreviation is")
628    activity_category = django_filters.ModelChoiceFilter(
629        label='Activity category',
630        name='group__activity_category',
631        queryset=groups.models.ActivityCategory.objects,
632    )
633
634    class Meta:
635        model = forms.models.MidwayAssignment
636        fields = [
637            'name',
638            'abbreviation',
639            'activity_category',
640        ]
641        order_by = (
642            ('group__name', 'Name', ),
643            ('group__abbreviation', 'Abbreviation', ),
644            ('group__activity_category__name', 'Activity category', ),
645            ('location', 'Location', ),
646        )
[d848b98]647
[1cfab5a]648
[d848b98]649class MidwayMapView(DetailView):
650    context_object_name = "midway"
651    model = forms.models.Midway
652    template_name = 'midway/map.html'
653
654    def get_context_data(self, **kwargs):
655        # Call the base implementation first to get a context
656        context = super(MidwayMapView, self).get_context_data(**kwargs)
657       
[f610262]658        filterset = MidwayAssignmentFilter(self.request.GET)
659        context['assignments'] = filterset.qs
660        context['filter'] = filterset
[7d7801f]661        context['pagename'] = 'midway'
[d848b98]662
663        return context
664
665
666class MidwayAssignmentsUploadForm(Form):
[83759bf]667    def validate_csv_fields(upload_file):
668        reader = csv.reader(upload_file)
669        row = reader.next()
670        for col in ('Group', 'officers', 'Table', ):
671            if col not in row:
672                raise ValidationError('Please upload a CSV file with (at least) columns "Group", "officers", and "Table". (Missing at least "%s".)' % (col, ))
673
674    assignments = FileField(validators=[validate_csv_fields])
[d848b98]675
[5e018d9]676@permission_required('forms.add_midwayassignment')
[d848b98]677def midway_assignment_upload(request, slug, ):
678    midway = get_object_or_404(forms.models.Midway, slug=slug, )
679
[7d7801f]680    uploaded = False
681    found = []
682    issues = collections.defaultdict(list)
683
[d848b98]684    if request.method == 'POST': # If the form has been submitted...
685        form = MidwayAssignmentsUploadForm(request.POST, request.FILES, ) # A form bound to the POST data
686
687        if form.is_valid(): # All validation rules pass
[7d7801f]688            uploaded = True
689            reader = csv.DictReader(request.FILES['assignments'])
690            for row in reader:
691                group_name = row['Group']
692                group_officers = row['officers']
693                table = row['Table']
694                issue = False
695                try:
696                    group = groups.models.Group.objects.get(name=group_name)
697                    assignment = forms.models.MidwayAssignment(
698                        midway=midway,
699                        location=table,
700                        group=group,
701                    )
702                    assignment.save()
703                    found.append(assignment)
704                    status = group.group_status.slug
705                    if status != 'active':
706                        issue = 'status=%s (added anyway)' % (status, )
707                except groups.models.Group.DoesNotExist:
708                    issue = 'unknown group (ignored)'
[c3a5618]709                except groups.models.Group.MultipleObjectsReturned:
710                    issue = 'multiple groups found (ignored)'
[7d7801f]711                if issue:
712                    issues[issue].append((group_name, group_officers, table))
713            for issue in issues:
714                issues[issue] = sorted(issues[issue], key=lambda x: x[0])
715
[d848b98]716    else:
717        form = MidwayAssignmentsUploadForm() # An unbound form
718
719    context = {
720        'midway':midway,
721        'form':form,
[7d7801f]722        'uploaded': uploaded,
723        'found': found,
724        'issues': dict(issues),
[d848b98]725        'pagename':'midway',
726    }
727    return render_to_response('midway/upload.html', context, context_instance=RequestContext(request), )
Note: See TracBrowser for help on using the repository browser.