Changeset ff71c3a


Ignore:
Timestamp:
Feb 11, 2014, 2:57:15 AM (12 years ago)
Author:
Alex Dehnert <adehnert@…>
Branches:
master, stable, stage
Children:
f612be5
Parents:
e5c5180 (diff), b523fed (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@…> (02/11/14 02:57:15)
git-committer:
Alex Dehnert <adehnert@…> (02/11/14 02:57:15)
Message:

Allow more flexible filtering in the reporting UI

Merge branch 'filters'

  • filters: Reporting: Allow different name formats (ASA-#259) Comment out filter categories we don't use yet Filters: select by office/locker ownership Filters: use "category", not "group" Proof-of-concept reporting filters (ASA-#193)

This resolves ASA-#193 (and ASA-#259).

Location:
asadb
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • asadb/groups/models.py

    r7431d13 rd4f571c  
     1# -*- coding: utf8 -*-
     2
    13import collections
    24import datetime
     
    1416from django.db import models
    1517from django.db.models import Q
     18from django.core.exceptions import ValidationError
    1619from django.core.validators import RegexValidator
    1720from django.contrib.auth.models import User, Permission
     
    618621    class Meta:
    619622        verbose_name = "Athena (Moira) account"
     623
     624
     625
     626class GroupFilter(object):
     627    def __init__(self, **kwargs):
     628        self.qs_thunk = kwargs['qs_thunk']
     629        self.name = kwargs['name']
     630        self.description = kwargs['desc']
     631
     632    def queryset(self, ):
     633        return self.qs_thunk()
     634
     635
     636class GroupFilterRegistry(object):
     637    def __init__(self, ):
     638        self.filters = {}
     639        self.filter_categories = {}
     640
     641    def register_category(self, name, slug, ):
     642        if slug in self.filter_categories:
     643            raise ValueError, "Duplicate filter category %s" % (slug, )
     644        if slug in self.filters:
     645            raise ValueError, "Category %s matches filter" % (slug, )
     646        self.filter_categories[slug] = dict(
     647            name=name,
     648            filters=[],
     649        )
     650        self.filters[slug] = GroupFilter(
     651            qs_thunk=None,
     652            name=name,
     653            desc=None,
     654        )
     655
     656    def register(self, **kwargs):
     657        slug = kwargs.pop('slug')
     658        category = kwargs.pop('category')
     659        if category not in self.filter_categories:
     660            raise KeyError, "Unknown filter category %s" % (category, )
     661        fltr = GroupFilter(**kwargs)
     662        if slug in self.filters:
     663            raise ValueError, "Duplicate filter %s" % (slug, )
     664        self.filters[slug] = fltr
     665        self.filter_categories[category]['filters'].append(slug)
     666
     667    def get(self, slug):
     668        return self.filters[slug]
     669
     670    def get_choices(self, ):
     671        choices = []
     672        for category_slug, category in self.filter_categories.items():
     673            choices.append((category_slug, "[%s]" % (category['name'], )))
     674            for filter_slug in category['filters']:
     675                choices.append((filter_slug, self.filters[filter_slug].name))
     676            choices.append(("", ""))
     677        return choices[:-1]
     678
     679    def validate_filter_slug(self, value, ):
     680        for slug in value:
     681            if slug == "":
     682                raise ValidationError("Please select only filters")
     683            if slug not in self.filters:
     684                raise ValidationError("%s is an unknown filter" % (slug, ))
     685            if slug in self.filter_categories:
     686                raise ValidationError(u"%s is a filter category — please select only filters" % (self.filter_categories[slug]['name'], ))
     687
     688
     689filter_registry = GroupFilterRegistry()
     690#filter_registry.register_category(name="People", slug='people', )
     691filter_registry.register_category(name="Space", slug='space', )
     692#filter_registry.register_category(name="FYSM", slug='fysm', )
  • asadb/groups/views.py

    r7dde669 rb523fed  
    11281128#######################
    11291129
     1130name_formats = collections.OrderedDict()
     1131name_formats['username']    = ('username',                      False,  '%(user)s')
     1132name_formats['email']       = ('username@mit.edu',              False,  '%(user)s@mit.edu')
     1133name_formats['name']        = ('First Last',                    True,   '%(first)s %(last)s')
     1134name_formats['name-email']  = ('First Last <username@mit.edu>', True,   '%(first)s %(last)s <%(user)s@mit.edu>')
     1135
    11301136class ReportingForm(form_utils.forms.BetterForm):
     1137    special_filters = forms.fields.MultipleChoiceField(
     1138        choices=[],
     1139        widget=forms.SelectMultiple(attrs={'size': 10}),
     1140        validators=[groups.models.filter_registry.validate_filter_slug],
     1141        required=False,
     1142    )
     1143
    11311144    basic_fields_choices = groups.models.Group.reporting_fields()
    11321145    basic_fields_labels = dict(basic_fields_choices) # name -> verbose_name
     
    11421155        required=False,
    11431156    )
    1144     show_as_emails = forms.BooleanField(
    1145         help_text='Append "@mit.edu" to each value of people fields to allow use as email addresses?',
    1146         required=False,
     1157    name_format = forms.ChoiceField(
     1158        help_text='How to format the names of the President, Treasurer, etc., if displayed',
     1159        choices=[(k, v[0]) for k,v in name_formats.items()],
     1160        initial='username',
     1161        required=True,
    11471162    )
    11481163
     
    11671182            ('filter', {
    11681183                'legend': 'Filter Groups',
    1169                 'fields': ['name', 'abbreviation', 'activity_category', 'group_class', 'group_status', 'group_funding', ],
     1184                'fields': [
     1185                    'name', 'abbreviation',
     1186                    'activity_category', 'group_class', 'group_status', 'group_funding',
     1187                    'special_filters',
     1188                ],
    11701189            }),
    11711190            ('fields', {
    11721191                'legend': 'Data to display',
    1173                 'fields': ['basic_fields', 'people_fields', 'show_as_emails', 'special_fields', ],
     1192                'fields': ['basic_fields', 'people_fields', 'name_format', 'special_fields', ],
    11741193            }),
    11751194            ('final', {
     
    11781197            }),
    11791198        ]
     1199
     1200    def __init__(self, *args, **kwargs):
     1201        super(ReportingForm, self).__init__(*args, **kwargs)
     1202
     1203        registry = groups.models.filter_registry
     1204        self.fields['special_filters'].choices = registry.get_choices()
    11801205
    11811206class GroupReportingFilter(GroupFilter):
     
    12401265        # Set up query
    12411266        qs = groups_filterset.qs
     1267        for fltr_slug in form.cleaned_data['special_filters']:
     1268            fltr = groups.models.filter_registry.get(fltr_slug)
     1269            qs = qs.filter(pk__in=fltr.queryset())
     1270
    12421271        # Prefetch foreign keys
    12431272        prefetch_fields = groups.models.Group.reporting_prefetch()
     
    12551284        for field in people_fields:
    12561285            col_labels.append(field.display_name)
     1286        # Figure out the format, and if necessary fetch human names
     1287        nf_slug = form.cleaned_data['name_format']
     1288        nf_label, nf_need_name, nf_fmt = name_formats[nf_slug]
     1289        name_map = collections.defaultdict(lambda: ('???', '???'))
     1290        if nf_need_name:
     1291            people_usernames = people_data.values('person')
     1292            name_data = groups.models.AthenaMoiraAccount.objects.filter(username__in=people_usernames)
     1293            for account in name_data:
     1294                name_map[account.username] = (account.first_name, account.last_name)
    12571295
    12581296        # Set up special fields
     
    12671305        else:
    12681306            formatters = {}
    1269         show_as_emails = form.cleaned_data['show_as_emails']
    12701307        def fetch_item(group, field):
    12711308            val = getattr(group, field)
     
    12781315            for field in people_fields:
    12791316                people = people_map[group.pk][field.pk]
    1280                 if show_as_emails: people = ["%s@mit.edu" % p for p in people]
     1317                if nf_need_name:
     1318                    def fmt(p):
     1319                        first, last = name_map[p]
     1320                        ctx = {'user': p, 'first': first, 'last': last}
     1321                        return nf_fmt % ctx
     1322                    people = [fmt(p) for p in people]
     1323                else:
     1324                    people = [nf_fmt % {'user': p} for p in people]
    12811325                group_data.append(", ".join(people))
    12821326
  • asadb/space/models.py

    r8aea837 ref118cf  
    146146
    147147
     148groups.models.filter_registry.register(
     149    category='space',
     150    slug='space:owners',
     151    name='Space owners',
     152    desc='Groups with space',
     153    qs_thunk=lambda: SpaceAssignment.current.values('group'),
     154)
     155
     156def assignment_filter(building=None, locker=None):
     157    assign = SpaceAssignment.current.all()
     158    if building:
     159        assign = assign.filter(
     160            space__number__startswith="%s-" % (building, ),
     161        )
     162    if locker == True:
     163        assign = assign.exclude(locker_num="")
     164    elif locker == False:
     165        assign = assign.filter(locker_num="")
     166    owners = groups.models.Group.objects.filter(pk__in=assign.values('group'))
     167    return owners
     168
     169groups.models.filter_registry.register(
     170    category='space',
     171    slug='space:locker',
     172    name='Locker owners',
     173    desc='Owners of lockers',
     174    qs_thunk=lambda: assignment_filter(locker=True),
     175)
     176groups.models.filter_registry.register(
     177    category='space',
     178    slug='space:office',
     179    name='Office owners',
     180    desc='Owners of offices',
     181    qs_thunk=lambda: assignment_filter(locker=False),
     182)
     183groups.models.filter_registry.register(
     184    category='space',
     185    slug='space:w20',
     186    name='W20 owners',
     187    desc='Owners of W20 space',
     188    qs_thunk=lambda: assignment_filter(building='W20'),
     189)
     190groups.models.filter_registry.register(
     191    category='space',
     192    slug='space:walker',
     193    name='Walker owners',
     194    desc='Owners of Walker space',
     195    qs_thunk=lambda: assignment_filter(building='50'),
     196)
     197
     198
    148199class CurrentACLEntryManager(models.Manager):
    149200    def get_query_set(self, ):
Note: See TracChangeset for help on using the changeset viewer.