Changeset 0ac15a1 for asadb/groups


Ignore:
Timestamp:
Apr 5, 2012, 1:12:37 AM (14 years ago)
Author:
Alex Dehnert <adehnert@…>
Branches:
master, space-access, stable, stage
Children:
a03cb61
Parents:
ffecc7f (diff), a9a8f3d (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
git-author:
Alex Dehnert <adehnert@…> (04/05/12 01:12:37)
git-committer:
Alex Dehnert <adehnert@…> (04/05/12 01:12:37)
Message:

Merge branch 'office-access'

Location:
asadb/groups
Files:
1 added
3 edited

Legend:

Unmodified
Added
Removed
  • asadb/groups/urls.py

    r8708a86 r0ac15a1  
    22
    33import groups.views
     4import space.views
    45
    56group_patterns = patterns('',
     
    89    url(r'^edit/officers$', groups.views.manage_officers, name='group-manage-officers', ),
    910    url(r'^history/$', groups.views.GroupHistoryView.as_view(), name='group-manage-history', ),
     11    url(r'^space/$', space.views.manage_access, name='group-space-access', ),
    1012)
    1113
  • asadb/groups/models.py

    rfd5cc07 r4325823  
    9797    def __str__(self, ):
    9898        return self.name
     99
     100    @classmethod
     101    def reporting_prefetch(cls, ):
     102        return set(['activity_category', 'group_class', 'group_status', 'group_funding'])
     103
     104    @classmethod
     105    def reporting_fields(cls, ):
     106        fields = cls._meta.fields
     107        return [(f.name, f.verbose_name) for f in fields]
    99108
    100109    class Meta:
  • asadb/groups/views.py

    r58a5c6a rffecc7f  
    22
    33import collections
     4import csv
    45import datetime
    56
     
    1415from django.template import Context, Template
    1516from django.template.loader import get_template
    16 from django.http import Http404, HttpResponseRedirect
     17from django.http import HttpResponse, Http404, HttpResponseRedirect
    1718from django.core.urlresolvers import reverse
     19from django.core.validators import URLValidator, EmailValidator, email_re
    1820from django.core.mail import EmailMessage, mail_admins
    1921from django import forms
     
    2123from django.db import connection
    2224from django.db.models import Q
     25from django.utils import html
    2326from django.utils.safestring import mark_safe
    2427
     
    2932from util.db_form_utils import StaticWidget
    3033from util.emails import email_from_template
     34
     35urlvalidator = URLValidator()
     36emailvalidator = EmailValidator(email_re)
    3137
    3238
     
    142148            ('size', {
    143149                'legend':'Membership Numbers',
     150                'description':'Count each person in your group exactly once. Count only MIT students as "undergrads" or "grads". "Community" should be MIT community members who are not students, such as alums and staff.',
    144151                'fields': ['num_undergrads', 'num_grads', 'num_community', 'num_other',],
    145152            }),
     
    202209    }
    203210    return render_to_response('groups/group_change_main.html', context, context_instance=RequestContext(request), )
     211
     212
     213
     214##################
     215# ACCOUNT LOOKUP #
     216##################
     217
     218class AccountLookupForm(forms.Form):
     219    account_number = forms.IntegerField()
     220    username = forms.CharField(help_text="Athena username of person to check")
     221
     222def account_lookup(request, ):
     223    msg = None
     224    msg_type = ""
     225    account_number = None
     226    username = None
     227    group = None
     228    office_holders = []
     229
     230    visible_roles  = groups.models.OfficerRole.objects.filter(publicly_visible=True)
     231
     232    initial = {}
     233
     234    if 'search' in request.GET: # If the form has been submitted...
     235        # A form bound to the POST data
     236        form = AccountLookupForm(request.GET)
     237
     238        if form.is_valid(): # All validation rules pass
     239            account_number = form.cleaned_data['account_number']
     240            username = form.cleaned_data['username']
     241            account_q = Q(main_account_id=account_number) | Q(funding_account_id=account_number)
     242            try:
     243                group = groups.models.Group.objects.get(account_q)
     244                office_holders = group.officers(person=username)
     245                office_holders = office_holders.filter(role__in=visible_roles)
     246            except groups.models.Group.DoesNotExist:
     247                msg = "Group not found"
     248                msg_type = "error"
     249
     250    else:
     251        form = AccountLookupForm()
     252
     253    context = {
     254        'username':     username,
     255        'account_number': account_number,
     256        'group':        group,
     257        'office_holders': office_holders,
     258        'form':         form,
     259        'msg':          msg,
     260        'msg_type':     msg_type,
     261        'visible_roles':    visible_roles,
     262    }
     263    return render_to_response('groups/account_lookup.html', context, context_instance=RequestContext(request), )
     264
    204265
    205266
     
    387448            group.set_updater(request.user)
    388449            form.save()
    389             officer_emails = create_group_officers(group, form.cleaned_data, )
     450            officer_emails = create_group_officers(group, form.cleaned_data, save=True, )
    390451
    391452            return redirect(reverse('groups:group-detail', args=[group.pk]))
     
    471532    }
    472533    return render_to_response('groups/create/startup.html', context, context_instance=RequestContext(request), )
    473 
    474 class GroupRecognitionForm(forms.Form):
    475     test = forms.BooleanField()
    476534
    477535@permission_required('groups.recognize_group')
     
    547605
    548606
     607
    549608##################
    550609# Multiple group #
     
    567626
    568627    def __init__(self, data=None, *args, **kwargs):
    569         if data is None: data = {}
    570         else: data = data.copy()
     628        if not data: data = None
     629        super(GroupFilter, self).__init__(data, *args, **kwargs)
    571630        active_pk = groups.models.GroupStatus.objects.get(slug='active').pk
    572         data.setdefault('group_status', active_pk, )
    573         super(GroupFilter, self).__init__(data, *args, **kwargs)
     631        self.form.initial['group_status'] = active_pk
    574632
    575633
     
    764822        dest = reverse('groups:signatories')
    765823        print dest
    766     elif 'group-info' in request.GET:
     824    elif 'group-goto' in request.GET:
     825        if len(groups_filterset.qs) == 1:
     826            group = groups_filterset.qs[0]
     827            return redirect(reverse('groups:group-detail', kwargs={'pk':group.pk}))
     828        else:
     829            dest = reverse('groups:list')
     830    elif 'group-list' in request.GET:
    767831        dest = reverse('groups:list')
    768832
     
    807871
    808872
    809 class AccountLookupForm(forms.Form):
    810     account_number = forms.IntegerField()
    811     username = forms.CharField(help_text="Athena username of person to check")
    812 
    813 def account_lookup(request, ):
    814     msg = None
    815     msg_type = ""
    816     account_number = None
    817     username = None
    818     group = None
    819     office_holders = []
    820 
    821     visible_roles  = groups.models.OfficerRole.objects.filter(publicly_visible=True)
    822 
    823     initial = {}
    824 
    825     if 'search' in request.GET: # If the form has been submitted...
    826         # A form bound to the POST data
    827         form = AccountLookupForm(request.GET)
    828 
    829         if form.is_valid(): # All validation rules pass
    830             account_number = form.cleaned_data['account_number']
    831             username = form.cleaned_data['username']
    832             account_q = Q(main_account_id=account_number) | Q(funding_account_id=account_number)
    833             try:
    834                 group = groups.models.Group.objects.get(account_q)
    835                 office_holders = group.officers(person=username)
    836                 office_holders = office_holders.filter(role__in=visible_roles)
    837             except groups.models.Group.DoesNotExist:
    838                 msg = "Group not found"
    839                 msg_type = "error"
    840 
     873
     874#######################
     875# REPORTING COMPONENT #
     876#######################
     877
     878class ReportingForm(form_utils.forms.BetterForm):
     879    basic_fields_choices = groups.models.Group.reporting_fields()
     880    basic_fields_labels = dict(basic_fields_choices) # name -> verbose_name
     881    basic_fields = forms.fields.MultipleChoiceField(
     882        choices=basic_fields_choices,
     883        widget=forms.CheckboxSelectMultiple,
     884        initial = ['id', 'name'],
     885    )
     886
     887    people_fields = forms.models.ModelMultipleChoiceField(
     888        queryset=groups.models.OfficerRole.objects.all(),
     889        widget=forms.CheckboxSelectMultiple,
     890        required=False,
     891    )
     892    show_as_emails = forms.BooleanField(
     893        help_text='Append "@mit.edu" to each value of people fields to allow use as email addresses?',
     894        required=False,
     895    )
     896
     897    _format_choices = [
     898        ('html/inline',     "Web (HTML)", ),
     899        ('csv/inline',      "Spreadsheet (CSV) --- in browser", ),
     900        ('csv/download',    "Spreadsheet (CSV) --- download", ),
     901    ]
     902    output_format = forms.fields.ChoiceField(choices=_format_choices, widget=forms.RadioSelect, initial='html/inline')
     903
     904    class Meta:
     905        fieldsets = [
     906            ('filter', {
     907                'legend': 'Filter Groups',
     908                'fields': ['name', 'abbreviation', 'activity_category', 'group_class', 'group_status', 'group_funding', ],
     909            }),
     910            ('fields', {
     911                'legend': 'Data to display',
     912                'fields': ['basic_fields', 'people_fields', 'show_as_emails', ],
     913            }),
     914            ('final', {
     915                'legend': 'Final options',
     916                'fields': ['o', 'output_format', ],
     917            }),
     918        ]
     919
     920class GroupReportingFilter(GroupFilter):
     921    class Meta(GroupFilter.Meta):
     922        form = ReportingForm
     923        order_by = True # we customize the field, so the value needs to be true-like but doesn't matter otherwise
     924
     925    def get_ordering_field(self):
     926        return forms.ChoiceField(label="Ordering", required=False, choices=ReportingForm.basic_fields_choices)
     927
     928    def __init__(self, data=None, *args, **kwargs):
     929        super(GroupReportingFilter, self).__init__(data, *args, **kwargs)
     930
     931def format_id(pk):
     932    url = reverse('groups:group-detail', kwargs={'pk':pk})
     933    return mark_safe("<a href='%s'>%d</a>" % (url, pk))
     934
     935def format_url(url):
     936    try:
     937        urlvalidator(url)
     938    except ValidationError:
     939        return url
    841940    else:
    842         form = AccountLookupForm()
    843 
     941        escaped = html.escape(url)
     942        return mark_safe("<a href='%s'>%s</a>" % (escaped, escaped))
     943
     944def format_email(email):
     945    try:
     946        emailvalidator(email)
     947    except ValidationError:
     948        return email
     949    else:
     950        escaped = html.escape(email)
     951        return mark_safe("<a href='mailto:%s'>%s</a>" % (escaped, escaped))
     952
     953reporting_html_formatters = {
     954    'id': format_id,
     955    'website_url': format_url,
     956    'constitution_url': format_url,
     957    'group_email': format_email,
     958    'officer_email': format_email,
     959}
     960
     961@permission_required('groups.view_group_private_info')
     962def reporting(request, ):
     963    the_groups = groups.models.Group.objects.all()
     964    groups_filterset = GroupReportingFilter(request.GET, the_groups)
     965    form = groups_filterset.form
     966
     967    col_labels = []
     968    report_groups = []
     969    run_report = 'go' in request.GET and form.is_valid()
     970    if run_report:
     971        basic_fields = form.cleaned_data['basic_fields']
     972        output_format, output_disposition = form.cleaned_data['output_format'].split('/')
     973        col_labels = [form.basic_fields_labels[field] for field in basic_fields]
     974
     975        # Set up query
     976        qs = groups_filterset.qs
     977        # Prefetch foreign keys
     978        prefetch_fields = groups.models.Group.reporting_prefetch()
     979        prefetch_fields = prefetch_fields.intersection(basic_fields)
     980        if prefetch_fields:
     981            qs = qs.select_related(*list(prefetch_fields))
     982
     983        # Set up people
     984        people_fields = form.cleaned_data['people_fields']
     985        people_data = groups.models.OfficeHolder.current_holders.filter(group__in=qs, role__in=people_fields)
     986        # Group.pk -> (OfficerRole.pk -> set(username))
     987        people_map = collections.defaultdict(lambda: collections.defaultdict(set))
     988        for holder in people_data:
     989            people_map[holder.group_id][holder.role_id].add(holder.person)
     990        for field in people_fields:
     991            col_labels.append(field.display_name)
     992
     993        # Assemble data
     994        if output_format == 'html':
     995            formatters = reporting_html_formatters
     996        else:
     997            formatters = {}
     998        show_as_emails = form.cleaned_data['show_as_emails']
     999        def fetch_item(group, field):
     1000            val = getattr(group, field)
     1001            if field in formatters:
     1002                val = formatters[field](val)
     1003            return val
     1004        for group in qs:
     1005            group_data = [fetch_item(group, field) for field in basic_fields]
     1006            for field in people_fields:
     1007                people = people_map[group.pk][field.pk]
     1008                if show_as_emails: people = ["%s@mit.edu" % p for p in people]
     1009                group_data.append(", ".join(people))
     1010
     1011            report_groups.append(group_data)
     1012
     1013        # Handle output as CSV
     1014        if output_format == 'csv':
     1015            if output_disposition == 'download':
     1016                mimetype = 'text/csv'
     1017            else:
     1018                # Firefox, at least, downloads text/csv regardless
     1019                mimetype = 'text/plain'
     1020            response = HttpResponse(mimetype=mimetype)
     1021            if output_disposition == 'download':
     1022                response['Content-Disposition'] = 'attachment; filename=asa-db-report.csv'
     1023            writer = csv.writer(response)
     1024            writer.writerow(col_labels)
     1025            for row in report_groups: writer.writerow(row)
     1026            return response
     1027
     1028    # Handle output as HTML
    8441029    context = {
    845         'username':     username,
    846         'account_number': account_number,
    847         'group':        group,
    848         'office_holders': office_holders,
    849         'form':         form,
    850         'msg':          msg,
    851         'msg_type':     msg_type,
    852         'visible_roles':    visible_roles,
     1030        'form': form,
     1031        'run_report': run_report,
     1032        'column_labels': col_labels,
     1033        'report_groups': report_groups,
     1034        'pagename': 'groups',
    8531035    }
    854     return render_to_response('groups/account_lookup.html', context, context_instance=RequestContext(request), )
     1036    return render_to_response('groups/reporting.html', context, context_instance=RequestContext(request), )
Note: See TracChangeset for help on using the changeset viewer.