Changeset 3c1b20b
- Timestamp:
- Jun 4, 2013, 3:40:41 AM (13 years ago)
- Branches:
- master, space-access, stable, stage
- Children:
- 6aa3517
- Parents:
- 1927d79 (diff), 1e08e97 (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@…> (06/04/13 03:40:41)
- git-committer:
- Alex Dehnert <adehnert@…> (06/04/13 03:40:41)
- Files:
-
- 14 added
- 28 edited
-
.gitignore (modified) (1 diff)
-
asadb/forms/admin.py (modified) (2 diffs)
-
asadb/forms/migrations/0014_midway.py (added)
-
asadb/forms/migrations/0015_midway_perms.py (added)
-
asadb/forms/models.py (modified) (2 diffs)
-
asadb/forms/views.py (modified) (4 diffs)
-
asadb/groups/admin.py (modified) (4 diffs)
-
asadb/groups/diffs.py (modified) (3 diffs)
-
asadb/groups/listdiff.py (added)
-
asadb/groups/models.py (modified) (5 diffs)
-
asadb/groups/views.py (modified) (20 diffs)
-
asadb/media/style/style.css (modified) (2 diffs)
-
asadb/settings.py (modified) (1 diff)
-
asadb/settings/local_settings.scripts.py (added)
-
asadb/settings/local_settings_after.scripts.py (added)
-
asadb/space/admin.py (modified) (4 diffs)
-
asadb/template/base.html (modified) (1 diff)
-
asadb/template/groups/account_lookup.html (modified) (2 diffs)
-
asadb/template/groups/create/startup_review.html (modified) (1 diff)
-
asadb/template/groups/diffs/new-group-announce.txt (modified) (1 diff)
-
asadb/template/groups/group_change_main.html (modified) (1 diff)
-
asadb/template/groups/group_list.html (modified) (1 diff)
-
asadb/template/groups/reporting.html (modified) (2 diffs)
-
asadb/template/groups/reporting/non-students.html (modified) (2 diffs)
-
asadb/template/index.html (modified) (1 diff)
-
asadb/template/midway/map.html (added)
-
asadb/template/midway/upload.html (added)
-
asadb/urls.py (modified) (3 diffs)
-
asadb/util/admin.py (added)
-
asadb/util/db_filters.py (added)
-
asadb/util/diff_static_data.sh (modified) (1 diff)
-
docs/install-on-scripts.txt (added)
-
asadb/space/diffs.py (modified) (7 diffs)
-
asadb/space/fixtures/LockTypes.xml (added)
-
asadb/space/migrations/0005_add_lock_type.py (added)
-
asadb/space/migrations/0006_lock_types_setup.py (added)
-
asadb/space/models.py (modified) (1 diff)
-
asadb/space/views.py (modified) (3 diffs)
-
asadb/template/space/group-change-email.txt (modified) (1 diff)
-
asadb/template/space/lock_types.html (added)
-
asadb/template/space/manage-access.html (modified) (3 diffs)
-
asadb/template/space/summary.html (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
.gitignore
r7ef7143 r7d7801f 7 7 asadb/media/fysm/slides/ 8 8 asadb/media/page-previews/fysm/ 9 asadb/media/midway/maps/ 9 10 asadb/util/saved-data/ 10 11 asadb/util/static-data/ -
asadb/forms/admin.py
rbda4d86 r26826c9 1 from django.contrib import admin 2 1 3 import forms.models 2 from django.contrib import admin3 4 4 5 class FYSMAdmin(admin.ModelAdmin): … … 66 67 search_fields = ('username', 'groups__officer_email', 'groups__name', 'groups__abbreviation', ) 67 68 admin.site.register(forms.models.PersonMembershipUpdate, Admin_PersonMembershipUpdate) 69 70 class Admin_Midway(admin.ModelAdmin): 71 list_display = ( 72 'pk', 73 'name', 74 'slug', 75 'date', 76 ) 77 list_display_links = list_display 78 search_fields = ('name', ) 79 prepopulated_fields = {"slug": ("name",)} 80 admin.site.register(forms.models.Midway, Admin_Midway) 81 82 class Admin_MidwayAssignment(admin.ModelAdmin): 83 list_display = ( 84 'pk', 85 'midway', 86 'location', 87 'group', 88 ) 89 list_display_links = list_display 90 list_filter = ('midway', ) 91 search_fields = ('location', 'group__name', ) 92 admin.site.register(forms.models.MidwayAssignment, Admin_MidwayAssignment) -
asadb/forms/models.py
rc7b6a3a r26826c9 1 from django.db import models2 3 1 import datetime 4 2 import os, errno 5 3 6 import settings 4 from django.conf import settings 5 from django.db import models 6 7 7 import groups.models 8 8 from util.misc import log_and_ignore_failures, mkdir_p … … 205 205 def __unicode__(self, ): 206 206 return "PersonMembershipUpdate for %s" % (self.username, ) 207 208 209 210 ########## 211 # MIDWAY # 212 ########## 213 214 215 class Midway(models.Model): 216 name = models.CharField(max_length=50) 217 slug = models.SlugField() 218 date = models.DateTimeField() 219 table_map = models.ImageField(upload_to='midway/maps') 220 221 def __str__(self, ): 222 return "%s" % (self.name, ) 223 224 class MidwayAssignment(models.Model): 225 midway = models.ForeignKey(Midway) 226 location = models.CharField(max_length=20) 227 group = models.ForeignKey(groups.models.Group) 228 229 def __str__(self, ): 230 return "<MidwayAssignment: %s at %s at %s>" % (self.group, self.location, self.midway, ) -
asadb/forms/views.py
rd6f8984 r83759bf 1 import forms.models2 import groups.models3 import groups.views4 import settings5 import util.emails 6 1 import collections 2 import csv 3 import datetime 4 import StringIO 5 6 from django.conf import settings 7 7 from django.contrib.auth.decorators import user_passes_test, login_required, permission_required 8 8 from django.core.exceptions import PermissionDenied … … 15 15 from django.core.urlresolvers import reverse 16 16 from django.core.mail import EmailMessage, mail_admins 17 from django.forms import FileField 17 18 from django.forms import Form 18 19 from django.forms import ModelForm … … 22 23 from django.db.models import Q, Count 23 24 24 import csv 25 import datetime 26 import StringIO 25 import django_filters 26 27 import forms.models 28 import groups.models 29 import groups.views 30 import util.emails 27 31 28 32 ################# … … 554 558 555 559 return HttpResponse(buf.getvalue(), mimetype='text/csv', ) 560 561 562 563 ########## 564 # Midway # 565 ########## 566 567 568 class View_Midways(ListView): 569 context_object_name = "midway_list" 570 template_name = "midway/midway_list.html" 571 572 def get_queryset(self): 573 midways = forms.models.Midway.objects.order_by('date') 574 return midways 575 576 def get_context_data(self, **kwargs): 577 context = super(View_Midways, self).get_context_data(**kwargs) 578 context['pagename'] = 'midway' 579 return context 580 581 def midway_map_latest(request, ): 582 midways = forms.models.Midway.objects.order_by('-date')[:1] 583 if len(midways) == 0: 584 raise Http404("No midways found.") 585 else: 586 url = reverse('midway-map', args=(midways[0].slug, )) 587 return HttpResponseRedirect(url) 588 589 590 class MidwayAssignmentFilter(django_filters.FilterSet): 591 name = django_filters.CharFilter(name='group__name', lookup_type='icontains', label="Name contains") 592 abbreviation = django_filters.CharFilter(name='group__abbreviation', lookup_type='iexact', label="Abbreviation is") 593 activity_category = django_filters.ModelChoiceFilter( 594 label='Activity category', 595 name='group__activity_category', 596 queryset=groups.models.ActivityCategory.objects, 597 ) 598 599 class Meta: 600 model = forms.models.MidwayAssignment 601 fields = [ 602 'name', 603 'abbreviation', 604 'activity_category', 605 ] 606 order_by = ( 607 ('group__name', 'Name', ), 608 ('group__abbreviation', 'Abbreviation', ), 609 ('group__activity_category__name', 'Activity category', ), 610 ('location', 'Location', ), 611 ) 612 613 614 class MidwayMapView(DetailView): 615 context_object_name = "midway" 616 model = forms.models.Midway 617 template_name = 'midway/map.html' 618 619 def get_context_data(self, **kwargs): 620 # Call the base implementation first to get a context 621 context = super(MidwayMapView, self).get_context_data(**kwargs) 622 623 filterset = MidwayAssignmentFilter(self.request.GET) 624 context['assignments'] = filterset.qs 625 context['filter'] = filterset 626 context['pagename'] = 'midway' 627 628 return context 629 630 631 class MidwayAssignmentsUploadForm(Form): 632 def validate_csv_fields(upload_file): 633 reader = csv.reader(upload_file) 634 row = reader.next() 635 for col in ('Group', 'officers', 'Table', ): 636 if col not in row: 637 raise ValidationError('Please upload a CSV file with (at least) columns "Group", "officers", and "Table". (Missing at least "%s".)' % (col, )) 638 639 assignments = FileField(validators=[validate_csv_fields]) 640 641 @permission_required('forms.add_midwayassignment') 642 def midway_assignment_upload(request, slug, ): 643 midway = get_object_or_404(forms.models.Midway, slug=slug, ) 644 645 uploaded = False 646 found = [] 647 issues = collections.defaultdict(list) 648 649 if request.method == 'POST': # If the form has been submitted... 650 form = MidwayAssignmentsUploadForm(request.POST, request.FILES, ) # A form bound to the POST data 651 652 if form.is_valid(): # All validation rules pass 653 uploaded = True 654 reader = csv.DictReader(request.FILES['assignments']) 655 for row in reader: 656 group_name = row['Group'] 657 group_officers = row['officers'] 658 table = row['Table'] 659 issue = False 660 try: 661 group = groups.models.Group.objects.get(name=group_name) 662 assignment = forms.models.MidwayAssignment( 663 midway=midway, 664 location=table, 665 group=group, 666 ) 667 assignment.save() 668 found.append(assignment) 669 status = group.group_status.slug 670 if status != 'active': 671 issue = 'status=%s (added anyway)' % (status, ) 672 except groups.models.Group.DoesNotExist: 673 issue = 'unknown group (ignored)' 674 except groups.models.Group.MultipleObjectsReturned: 675 issue = 'multiple groups found (ignored)' 676 if issue: 677 issues[issue].append((group_name, group_officers, table)) 678 for issue in issues: 679 issues[issue] = sorted(issues[issue], key=lambda x: x[0]) 680 681 else: 682 form = MidwayAssignmentsUploadForm() # An unbound form 683 684 context = { 685 'midway':midway, 686 'form':form, 687 'uploaded': uploaded, 688 'found': found, 689 'issues': dict(issues), 690 'pagename':'midway', 691 } 692 return render_to_response('midway/upload.html', context, context_instance=RequestContext(request), ) -
asadb/groups/admin.py
rcbffe98 rfa218b1 1 import datetime 2 3 from django.contrib import admin 4 from django.utils.translation import ugettext_lazy 5 6 from reversion.admin import VersionAdmin 7 1 8 import groups.models 2 from django.contrib import admin 3 from reversion.admin import VersionAdmin 9 import util.admin 4 10 5 11 class GroupAdmin(VersionAdmin): … … 98 104 99 105 class OfficeHolderAdmin(VersionAdmin): 106 class OfficeHolderPeriodFilter(util.admin.TimePeriodFilter): 107 start_field = 'start_time' 108 end_field = 'end_time' 109 110 def expire_holders(self, request, queryset): 111 rows_updated = queryset.update(end_time=datetime.datetime.now()) 112 if rows_updated == 1: 113 message_bit = "1 entry was" 114 else: 115 message_bit = "%s entries were" % rows_updated 116 self.message_user(request, "%s successfully expired." % message_bit) 117 expire_holders.short_description = ugettext_lazy("Expire selected %(verbose_name_plural)s") 118 119 actions = ['expire_holders'] 120 100 121 list_display = ( 101 122 'id', … … 121 142 list_filter = [ 122 143 'role', 144 OfficeHolderPeriodFilter, 123 145 ] 124 146 admin.site.register(groups.models.OfficeHolder, OfficeHolderAdmin) … … 188 210 list_display_links = ( 'id', 'username', ) 189 211 search_fields = ( 'username', 'mit_id', 'first_name', 'last_name', 'account_class', ) 212 list_filter = ( 'account_class', 'mutable', ) 190 213 admin.site.register(groups.models.AthenaMoiraAccount, Admin_AthenaMoiraAccount) -
asadb/groups/diffs.py
r14f594b r9af1bb4 11 11 os.environ['DJANGO_SETTINGS_MODULE'] = 'settings' 12 12 13 from django.conf import settings 13 14 from django.contrib.contenttypes.models import ContentType 14 15 from django.core.mail import EmailMessage, mail_admins … … 21 22 22 23 import groups.models 23 import settings24 24 import util.emails 25 25 import util.mailinglist … … 244 244 245 245 def default_active_pred(): 246 status_objs = groups.models.GroupStatus.objects.filter(slug__in=['active', 'suspended', 'nge'])246 status_objs = groups.models.GroupStatus.objects.filter(slug__in=['active', 'suspended', ]) 247 247 status_pks = [status.pk for status in status_objs] 248 248 def pred(version, fields): -
asadb/groups/models.py
r6559695 rc4282e2 1 from django.db import models2 from django.core.validators import RegexValidator3 from django.contrib.auth.models import User4 from django.template.defaultfilters import slugify5 import reversion6 7 1 import datetime 8 2 import filecmp … … 16 10 import urllib2 17 11 18 import settings 12 from django.conf import settings 13 from django.db import models 14 from django.core.validators import RegexValidator 15 from django.contrib.auth.models import User 16 from django.template.defaultfilters import slugify 17 18 import reversion 19 19 20 20 import mit … … 28 28 ) 29 29 30 locker_validator = RegexValidator(regex=r'^[-A-Za-z0-9_.]+$', message='Enter a valid Athena locker. ')30 locker_validator = RegexValidator(regex=r'^[-A-Za-z0-9_.]+$', message='Enter a valid Athena locker. This should be the single "word" that appears in "/mit/word/" or "web.mit.edu/word/", with no slashes, spaces, etc..') 31 31 32 32 class Group(models.Model): … … 98 98 if as_of == "now": as_of = datetime.datetime.now() 99 99 office_holders = office_holders.filter(start_time__lte=as_of, end_time__gte=as_of) 100 office_holders = office_holders.order_by('role', 'person') 100 101 return office_holders 101 102 … … 504 505 505 506 def __str__(self, ): 506 active = "" 507 if not self.is_active: 508 active = " (inactive)" 509 return "%s%s" % (self.name, active, ) 507 return self.name 510 508 511 509 class Meta: -
asadb/groups/views.py
rbfa60e8 r8ccd88f 4 4 import csv 5 5 import datetime 6 7 import groups.models8 6 9 7 from django.contrib.auth.decorators import user_passes_test, login_required, permission_required … … 30 28 import django_filters 31 29 30 import groups.models 32 31 from util.db_form_utils import StaticWidget 32 import util.db_filters 33 33 from util.emails import email_from_template 34 34 … … 129 129 for field in self.force_required: 130 130 self.fields[field].required = True 131 self.fields['constitution_url'].help_text = mark_safe(" ""Please put your current constitution URL or AFS path.<br>If you don't currently know where your constitution is, put "http://mit.edu/asa/start/constitution-req.html" and draft a constitution soon.""")131 self.fields['constitution_url'].help_text = mark_safe("Please put your current constitution URL or AFS path.") 132 132 133 133 exec_only_fields = [ … … 221 221 def manage_officers_load_officers(group, ): 222 222 officers = group.officers() 223 people = list(set([ officer.person for officer in officers ]))223 people = sorted(set([ officer.person for officer in officers ])) 224 224 roles = groups.models.OfficerRole.objects.all() 225 225 … … 573 573 treasurer_name = forms.CharField(max_length=50) 574 574 treasurer_kerberos = forms.CharField(min_length=3, max_length=8, ) 575 def clean_president (self, ):575 def clean_president_kerberos(self, ): 576 576 username = self.cleaned_data['president_kerberos'] 577 577 validate_athena(username, True, ) 578 578 return username 579 579 580 def clean_treasurer (self, ):580 def clean_treasurer_kerberos(self, ): 581 581 username = self.cleaned_data['treasurer_kerberos'] 582 582 validate_athena(username, True, ) … … 631 631 self.fields['constitution_url'].help_text = "Please put a copy of your finalized constitution on a publicly-accessible website (e.g. your group's, or your own, Public folder), and link to it in the box above." 632 632 self.fields['athena_locker'].required = True 633 self.fields['athena_locker'].help_text = "In general, this is limited to twelve characters. You should stick to letters, numbers, and hyphens. (Underscores and dots are also acceptable, but may cause problems in some situations.)" 634 635 # Specifically, if the group ends up wanting to use scripts.mit.edu, 636 # they will currently be assigned locker.scripts.mit.edu. If they try 637 # to use foo.bar, then https://foo.bar.scripts.mit.edu/ will produce a 638 # certificate name mismatch. Officially, underscores are not allowed in 639 # hostnames, so foo_.scripts.mit.edu may fail with some software. 633 640 634 641 class Meta(GroupCreateForm.Meta): … … 900 907 name = django_filters.CharFilter(lookup_type='icontains', label="Name contains") 901 908 abbreviation = django_filters.CharFilter(lookup_type='iexact', label="Abbreviation is") 909 officer_email = django_filters.CharFilter(lookup_type='icontains', label="Officers' list contains") 910 911 account_filter = util.db_filters.MultiNumberFilter( 912 lookup_type='exact', label="Account number", 913 names=('main_account_id', 'funding_account_id', ), 914 ) 902 915 903 916 class Meta: … … 906 919 'name', 907 920 'abbreviation', 921 'officer_email', 908 922 'activity_category', 909 923 'group_class', 910 924 'group_status', 911 925 'group_funding', 926 'account_filter', 912 927 ] 913 928 … … 942 957 @permission_required('groups.view_signatories') 943 958 def view_signatories(request, ): 944 # TODO:945 # * limit which columns (roles) get displayed946 # This might want to wait for the generic reporting infrastructure, since947 # I'd imagine some of it can be reused.948 949 959 the_groups = groups.models.Group.objects.all() 950 960 groups_filterset = GroupFilter(request.GET, the_groups) 951 961 the_groups = groups_filterset.qs 962 952 963 officers = groups.models.OfficeHolder.objects.filter(start_time__lte=datetime.datetime.now(), end_time__gte=datetime.datetime.now()) 953 964 officers = officers.filter(group__in=the_groups) 954 965 officers = officers.select_related(depth=1) 955 roles = groups.models.OfficerRole.objects.all() 966 967 role_slugs = ['president', 'treasurer', 'financial', 'reservation'] 968 roles = groups.models.OfficerRole.objects.filter(slug__in=role_slugs) 969 roles = sorted(roles, key=lambda r: role_slugs.index(r.slug)) 970 956 971 officers_map = collections.defaultdict(lambda: collections.defaultdict(set)) 957 972 for officer in officers: … … 961 976 role_list = [] 962 977 for role in roles: 963 role_list.append( officers_map[group][role])978 role_list.append(sorted(officers_map[group][role])) 964 979 officers_data.append((group, role_list)) 965 980 … … 1007 1022 if 'pk' in self.kwargs: 1008 1023 group = get_object_or_404(groups.models.Group, pk=self.kwargs['pk']) 1009 history_entries = reversion. models.Version.objects.get_for_object(group)1024 history_entries = reversion.get_for_object(group) 1010 1025 else: 1011 1026 history_entries = reversion.models.Version.objects.all() … … 1100 1115 ) 1101 1116 1117 special_fields_choices = ( 1118 ('option_entry', '<option> entry', ), 1119 ) 1120 special_fields = forms.fields.MultipleChoiceField( 1121 choices=special_fields_choices, 1122 widget=forms.CheckboxSelectMultiple, 1123 required=False, 1124 ) 1125 1102 1126 _format_choices = [ 1103 1127 ('html/inline', "Web (HTML)", ), … … 1115 1139 ('fields', { 1116 1140 'legend': 'Data to display', 1117 'fields': ['basic_fields', 'people_fields', 'show_as_emails', ],1141 'fields': ['basic_fields', 'people_fields', 'show_as_emails', 'special_fields', ], 1118 1142 }), 1119 1143 ('final', { … … 1155 1179 escaped = html.escape(email) 1156 1180 return mark_safe("<a href='mailto:%s'>%s</a>" % (escaped, escaped)) 1181 1182 def format_option_entry(group): 1183 name = html.escape(group.name) 1184 return '<option value="%s">%s</option>' % (name, name, ) 1157 1185 1158 1186 reporting_html_formatters = { … … 1195 1223 for field in people_fields: 1196 1224 col_labels.append(field.display_name) 1225 1226 # Set up special fields 1227 special_formatters = [] 1228 if 'option_entry' in form.cleaned_data['special_fields']: 1229 col_labels.append('option_entry') 1230 special_formatters.append(format_option_entry) 1197 1231 1198 1232 # Assemble data … … 1207 1241 val = formatters[field](val) 1208 1242 return val 1243 1209 1244 for group in qs: 1210 1245 group_data = [fetch_item(group, field) for field in basic_fields] … … 1213 1248 if show_as_emails: people = ["%s@mit.edu" % p for p in people] 1214 1249 group_data.append(", ".join(people)) 1250 1251 for formatter in special_formatters: 1252 group_data.append(formatter(group)) 1215 1253 1216 1254 report_groups.append(group_data) … … 1228 1266 writer = csv.writer(response) 1229 1267 writer.writerow(col_labels) 1230 for row in report_groups: writer.writerow(row) 1268 for row in report_groups: 1269 utf8_row = [unicode(cell).encode("utf-8") for cell in row] 1270 writer.writerow(utf8_row) 1231 1271 return response 1232 1272 … … 1251 1291 office_holders = office_holders.filter(role__in=student_roles) 1252 1292 office_holders = office_holders.exclude(person__in=students.values('username')) 1253 office_holders = office_holders.select_related('group', ' role')1293 office_holders = office_holders.select_related('group', 'group__group_status', 'role') 1254 1294 1255 1295 msg = None … … 1257 1297 if 'sort' in request.GET: 1258 1298 if request.GET['sort'] == 'group': 1259 office_holders = office_holders.order_by('group__name', 'role', 'person', ) 1299 office_holders = office_holders.order_by('group__name', 'group__group_status', 'role', 'person', ) 1300 elif request.GET['sort'] == 'status': 1301 office_holders = office_holders.order_by('group__group_status', 'group__name', 'role', 'person', ) 1260 1302 elif request.GET['sort'] == 'role': 1261 office_holders = office_holders.order_by('role', 'group__ name', 'person', )1303 office_holders = office_holders.order_by('role', 'group__group_status', 'group__name', 'person', ) 1262 1304 elif request.GET['sort'] == 'person': 1263 office_holders = office_holders.order_by('person', 'group__ name', 'role', )1305 office_holders = office_holders.order_by('person', 'group__group_status', 'group__name', 'role', ) 1264 1306 else: 1265 1307 msg = 'Unknown sort key "%s".' % (request.GET['sort'], ) -
asadb/media/style/style.css
rd6f8984 r1e08e97 234 234 .group-detail-page.group-status-active h1 { color: black; } 235 235 .group-detail-page.group-status-applying h1 { color: red; } 236 .group-detail-page.group-status-nge h1 { color: yellow; }236 .group-detail-page.group-status-nge h1 { color: #00ffff; } 237 237 .group-detail-page.group-status-provisional h1 { color: yellow; } 238 238 .group-detail-page.group-status-derecognized h1 { color: red; } … … 254 254 color: black; 255 255 } 256 .group-list-page tr.group-status-nge td.group-status 257 { 258 background-color: #00ffff; 259 color: black; 260 } 256 261 257 262 -
asadb/settings.py
red5797d rb4cc0cc 121 121 # Always use forward slashes, even on Windows. 122 122 # Don't forget to use absolute paths, not relative paths. 123 'template',123 os.path.join(SITE_ROOT, 'template'), 124 124 ) 125 125 -
asadb/space/admin.py
r21f7242 r3c1b20b 1 from django.contrib import admin 2 3 from reversion.admin import VersionAdmin 4 1 5 import space.models 2 from django.contrib import admin 3 from reversion.admin import VersionAdmin 6 import util.admin 4 7 5 8 class Admin_LockType(VersionAdmin): … … 30 33 31 34 class Admin_SpaceAssignment(admin.ModelAdmin): 35 class AssignmentPeriodFilter(util.admin.TimePeriodFilter): 36 start_field = 'start' 37 end_field = 'end' 38 32 39 list_max_show_all = 500 33 40 list_display = ( … … 39 46 ) 40 47 list_display_links = list_display 41 list_filter = ( 'space', )48 list_filter = (AssignmentPeriodFilter, 'space', ) 42 49 search_fields = ( 'group__name', 'group__officer_email', 'space__number', ) 43 50 admin.site.register(space.models.SpaceAssignment, Admin_SpaceAssignment) 44 51 45 52 class Admin_SpaceAccessListEntry(admin.ModelAdmin): 53 class AccessPeriodFilter(util.admin.TimePeriodFilter): 54 start_field = 'start' 55 end_field = 'end' 56 46 57 list_display = ( 47 58 'group', … … 52 63 ) 53 64 list_display_links = list_display 65 list_filter = (AccessPeriodFilter, 'space', ) 54 66 search_fields = ( 55 67 'group__name', 'group__officer_email', -
asadb/template/base.html
rcec082b r35280b4 14 14 <li{% ifequal pagename "groups" %} class='selected'{% endifequal %}><a href="{% url groups:list %}">Groups</a></li> 15 15 <li{% ifequal pagename "fysm" %} class='selected'{% endifequal %}><a href="{% url fysm %}">FYSM</a></li> 16 <li{% ifequal pagename "midway" %} class='selected'{% endifequal %}><a href="{% url midway-list %}">Midway</a></li> 16 17 <li{% ifequal pagename "about" %} class='selected'{% endifequal %}><a href="{% url about %}">Help & About</a></li> 17 18 {% if user.is_staff %}<li><a href='{% url admin:index %}'>Admin</a></li>{% endif %} -
asadb/template/groups/account_lookup.html
rb928edc rcfde3dc 17 17 <th>Group</th> 18 18 <td>{{group}}</td> 19 </tr> 20 <tr> 21 <th>Status</th> 22 <td>{{group.group_status}}</td> 19 23 </tr> 20 24 <tr> … … 46 50 </table> 47 51 52 <h2>Group Status</h2> 53 54 <table class='pretty-table'> 55 <tr> 56 <th>Status</th> 57 <th>Description</th> 58 </tr> 59 <tr> 60 <th>Active</th> 61 <td>Active groups are normal ASA groups in good standing.</td> 62 </tr> 63 <tr> 64 <th>Suspended and Derecognized</th> 65 <td>Suspended and derecognized groups are <strong>not</strong> in good standing, and should generally not be permitted to spend money, reserve rooms, etc..</td> 66 </tr> 67 <tr> 68 <th>Non-Group Entity</th> 69 <td>Non-Group Entities do not necessarily have any ASA recognition, but are included in the ASA Database as a courtesy to other MIT offices. You may assume the roles listed are authoritative. However, the ASA does not grant them any privileges.</td> 70 </tr> 71 </table> 72 73 48 74 <h2>Available Roles</h2> 49 75 -
asadb/template/groups/create/startup_review.html
rac93a0c r172255b 14 14 <tr><th>Description</th><td>{{group.description}}</td></tr> 15 15 16 <tr><th colspan='2'> Type</th></tr>16 <tr><th colspan='2'>Officers</th></tr> 17 17 <tr><th>President</th><td>{{startup.president_name}} ({{startup.president_kerberos}})</td></tr> 18 18 <tr><th>Treasurer</th><td>{{startup.treasurer_name}} ({{startup.treasurer_kerberos}})</td></tr> -
asadb/template/groups/diffs/new-group-announce.txt
r264efa0 r798ec5b 12 12 Your group's entry is at: 13 13 14 http ://asa.mit.edu/{% url groups:group-detail group.pk %}14 https://asa.mit.edu{% url groups:group-detail group.pk %} 15 15 16 16 You should make sure to do the following: -
asadb/template/groups/group_change_main.html
rcb9b105 r4752b74 9 9 {% include "groups/group_tools.part.html" %} 10 10 11 <p> We're using the transition from the old ASA Database to this new one as an opportunity to verify that old information is still accurate. Thus, we have intentionally limited the amount of information we copied from the old database. While filling this out, feel free to refer to the <a href='https://sisapp2.mit.edu/asa/student_group_detail.do?action=detail&studentGroupId={{group.pk}}'>old database</a>. However, please verify that information is accurate as you copy it over. As always, if you have trouble, please <a href='mailto:asa-exec@mit.edu'>contact us</a>.</p>11 <p>Please make sure to keep the information about your group up-to-date. As always, if you have trouble (or need to update a field that you don't have access to), please <a href='mailto:asa-exec@mit.edu'>contact us</a>.</p> 12 12 13 13 {% if msg %} -
asadb/template/groups/group_list.html
r8f46374 r58fe19c 17 17 18 18 <h2>The Groups</h2> 19 20 <p>Found {{group_list|length}} groups:</p> 19 21 20 22 <table class='pretty-table group-list'> -
asadb/template/groups/reporting.html
r1b10de0 r58fe19c 7 7 <h1>Reporting</h1> 8 8 9 <h2>Results</h2> 9 <div class='toolbox'> 10 <h2>Sections</h2> 11 <ul> 12 <li><a href='#results'>Results</a></li> 13 <li><a href='#config'>Configuration</a></li> 14 </ul> 15 </div> 16 17 <h2><a name='results'></a>Results</h2> 10 18 {% if run_report %} 19 20 <p>Found {{report_groups|length}} groups:</p> 21 11 22 <table class='pretty-table'> 12 23 <thead> … … 27 38 {% endif %} 28 39 29 <h2> Configuration</h2>40 <h2><a name='config'></a>Configuration</h2> 30 41 <form action="" method="get"> 31 42 {{ form|render }} -
asadb/template/groups/reporting/non-students.html
r213dd57 r52fb1d5 35 35 <tr> 36 36 <th><a href='?sort=group'>Group</a></th> 37 <th><a href='?sort=status'>Status</a></th> 37 38 <th><a href='?sort=role'>Role</a></th> 38 39 <th><a href='?sort=person'>Person</a></th> … … 41 42 <tr> 42 43 <td><a href='{% url groups:group-detail holder.group.pk %}'>{{holder.group.name}}</a></td> 44 <td>{{holder.group.group_status}}</td> 43 45 <td>{{holder.role.display_name}}</td> 44 46 <td>{{holder.person}}</td> -
asadb/template/index.html
rf6982d4 r3c1b20b 44 44 <li><a href='{%url fysm-select%}'>Submit an entry</a></li> 45 45 </ul></li> 46 <li><a href='{% url midway-list %}'>Midways</a>: <a href='{% url midway-map-latest %}'>latest map</a></li> 46 47 <li>Membership updates<ul> 47 48 <li><a href='{%url membership-update-cycle %}'>Group update</a></li> -
asadb/urls.py
rf6982d4 r3c1b20b 1 from django.conf import settings 1 2 from django.conf.urls.defaults import * 2 3 from django.contrib.auth.views import login, logout … … 6 7 from django.contrib import admin 7 8 admin.autodiscover() 8 9 import settings10 9 11 10 import groups.urls … … 74 73 url(r'^membership/admin/issues.csv$', forms.views.group_confirmation_issues, name='membership-issues', ), 75 74 75 # Midway 76 url(r'^midway/$', forms.views.View_Midways.as_view(), name='midway-list', ), 77 url(r'^midway/latest/$', forms.views.midway_map_latest, name='midway-map-latest', ), 78 url(r'^midway/(?P<slug>[\w-]+)/$', forms.views.MidwayMapView.as_view(), name='midway-map', ), 79 url(r'^midway/(?P<slug>[\w-]+)/assign/$', forms.views.midway_assignment_upload, name='midway-assign', ), 80 76 81 # Group list 77 82 (r'^groups/', include(groups.urls.urls(), ), ), -
asadb/util/diff_static_data.sh
re2ceffa r3c1b20b 5 5 date 6 6 7 cd static-data7 cd "$(dirname "$0")/static-data" 8 8 9 9 ../dump_group_perms.py > group-perms.py -
asadb/space/diffs.py
rc8f4eea r59506d6 8 8 cur_file = os.path.abspath(__file__) 9 9 django_dir = os.path.abspath(os.path.join(os.path.dirname(cur_file), '..')) 10 django_dir_parent = os.path.abspath(os.path.join(os.path.dirname(cur_file), '../..')) 10 11 sys.path.append(django_dir) 12 sys.path.append(django_dir_parent) 11 13 os.environ['DJANGO_SETTINGS_MODULE'] = 'settings' 12 14 13 15 from django.core.mail import EmailMessage 16 from django.core.urlresolvers import reverse 14 17 from django.db import connection 15 18 from django.db.models import Q … … 100 103 101 104 def list_office_changes(self, ): 102 cac_lines = [] 105 systems_lines = { 106 'cac-card': [], 107 'none': [], 108 } 103 109 group_lines = [] 104 def append_change(mit_id, verb, name):105 cac_lines.append("%s:\t%s:\t%s" % (mit_id, verb, name))106 group_lines.append("%s:\t%s" % (verb, name))107 110 for space_pk, space_data in self.offices.items(): 111 lock_type = all_spaces[space_pk].lock_type 112 system_lines = systems_lines[lock_type.db_update] 113 def append_change(mit_id, verb, name): 114 system_lines.append("%s:\t%s:\t%s" % (mit_id, verb, name)) 115 group_lines.append("%s:\t%s" % (verb, name)) 116 108 117 line = "Changes in %s:" % (all_spaces[space_pk].number, ) 109 cac_lines.append(line)118 system_lines.append(line) 110 119 group_lines.append(line) 120 121 if lock_type.db_update == 'none': 122 tmpl = 'Warning: You submitted changes effecting this space, but this space is ' + \ 123 'a "%s" space, and is not managed through the ASA DB. See ' + \ 124 'https://asa.mit.edu/%s for details on how to update spaces of this type.' 125 line = tmpl % (lock_type.name, reverse('space-lock-type'), ) 126 group_lines.append(line) 127 111 128 for mit_id, (old_names, new_names) in space_data.items(): 112 129 if mit_id is None: mit_id = "ID unknown" … … 125 142 else: 126 143 append_change(mit_id, "Add", name) 127 cac_lines.append("")144 system_lines.append("") 128 145 group_lines.append("") 129 146 130 cac_msg = "\n".join(cac_lines) 147 systems_msg = dict([ 148 (system, '\n'.join(lines), ) for (system, lines) in systems_lines.items() 149 ]) 131 150 group_msg = "\n".join(group_lines) 132 return cac_msg, group_msg151 return systems_msg, group_msg 133 152 134 153 def add_locker_signatories(self, space_access, time): … … 177 196 178 197 def safe_add_change_real(change_by_name, change): 198 """Add a new change to our dict of pending changes. 199 200 If a different change has already been added for this person (eg, "Remove" 201 instead of "Keep", or with a different list of groups), error. This should 202 always succeed; if it doesn't, the code is buggy. We worry about this 203 because we want to be really sure that the email that goes to just CAC is 204 compatible with the emails that go to each groups. Since we iterate over 205 the changes once per group, we want to be sure that for each group 206 iteration we're building compatible information. 207 """ 208 179 209 name = change.name 180 210 if name in change_by_name: … … 200 230 print "ID=%s (%s):\n\t%s\t(%s)\n\t%s\t(%s)\n" % (mit_id, unchanged, old_by_names, old_by_group, new_by_names, new_by_group, ), 201 231 for group_pk in joint_keys(old_by_group, new_by_group): 232 # TODO: Do we need to do an iteration for each group? This seems 233 # slightly questionable. Can we just loop over all known names? 234 202 235 old_names = old_by_group[group_pk] 203 236 new_names = new_by_group[group_pk] … … 267 300 cac_locker_msgs = [] 268 301 269 process_spaces = space.models.Space.objects.all() 302 process_spaces = space.models.Space.objects.all().select_related('lock_type') 270 303 for the_space in process_spaces: 271 304 new_cac_msgs = space_specific_access(the_space, group_data, old_time, new_time) … … 274 307 275 308 changed_groups = [] 309 cac_chars = 0 276 310 for group_pk, group_info in group_data.items(): 277 311 group_info.add_office_signatories(old_time, new_time) 278 cac_changes, group_office_changes = group_info.list_office_changes()312 systems_changes, group_office_changes = group_info.list_office_changes() 279 313 if group_info.changes: 280 changed_groups.append((group_info.group, cac_changes, group_office_changes, group_info.locker_messages, )) 314 cac_chars += len(systems_changes['cac-card']) 315 changed_groups.append((group_info.group, systems_changes['cac-card'], group_office_changes, group_info.locker_messages, )) 281 316 282 317 asa_rcpts = ['asa-space@mit.edu', 'asa-db@mit.edu', ] 283 if c hanged_groups:318 if cac_chars > 0 or cac_locker_msgs: 284 319 util.emails.email_from_template( 285 320 tmpl='space/cac-change-email.txt', -
asadb/space/models.py
r7eea15c r8aea837 11 11 EXPIRE_OFFSET = datetime.timedelta(seconds=1) 12 12 13 LOCK_DB_UPDATE_NONE = 'none' 14 LOCK_DB_UPDATE_CAC_CARD = 'cac-card' 15 lock_db_update_choices = ( 16 (LOCK_DB_UPDATE_NONE, "No database management"), 17 (LOCK_DB_UPDATE_CAC_CARD, "CAC-managed card-based access"), 18 ) 19 20 class LockType(models.Model): 21 name = models.CharField(max_length=50) 22 slug = models.SlugField(unique=True, ) 23 description = models.TextField() 24 info_addr = models.EmailField(default='asa-exec@mit.edu', help_text='Address groups should email to get more information about managing access through this lock type.') 25 info_url = models.URLField(blank=True, help_text='URL that groups can visit to get more information about this lock type.') 26 db_update = models.CharField(max_length=20, default='none', choices=lock_db_update_choices) 27 28 def __unicode__(self, ): 29 return self.name 30 31 13 32 class Space(models.Model): 14 33 number = models.CharField(max_length=20, unique=True, ) 15 34 asa_owned = models.BooleanField(default=True, ) 35 lock_type = models.ForeignKey(LockType) 16 36 merged_acl = models.BooleanField(default=False, help_text="Does this room have a single merged ACL, that combines all groups together, or CAC maintain a separate ACL per-group? Generally, the shared storage offices get a merged ACL and everything else doesn't.") 17 37 notes = models.TextField(blank=True, ) -
asadb/space/views.py
rde2f0ac rf6982d4 90 90 'allow_edit': allow_edit, 91 91 'extras_indices': extras_indices, 92 'pagename':'group ',92 'pagename':'groups', 93 93 } 94 94 return render_to_response('space/manage-access.html', context, context_instance=RequestContext(request), ) … … 112 112 'locker_num', 113 113 'group__name', 114 ).select_related('space', ' group')114 ).select_related('space', 'space__lock_type', 'group') 115 115 office_assignments = assignments.filter(locker_num='') 116 116 … … 127 127 'offices': office_assignments, 128 128 'lockers': locker_rooms, 129 'pagename':'group ',129 'pagename':'groups', 130 130 } 131 131 return render_to_response('space/summary.html', context, context_instance=RequestContext(request), ) 132 133 def lock_types(request, ): 134 lock_types = space.models.LockType.objects.order_by('name') 135 context = { 136 'lock_types': lock_types, 137 'pagename': 'groups', 138 } 139 return render_to_response('space/lock_types.html', context, context_instance=RequestContext(request), ) -
asadb/template/space/group-change-email.txt
rd768e47 r210d042 1 1 {% autoescape off %}Hi {{group.name}}, 2 Thank you for updating space access on the ASA database today. We've forwarded the following changes on to CAC: 2 3 Thank you for updating space access on the ASA database today. We are forwarding these changes to CAC, which generally updates access within [interval]. They will reply when the update has been done; if you don't get a reply, the message may have been lost and you should reply-all to this email reminding them. 4 5 We believe you made the following changes: 3 6 4 7 {% if office_msg %} -
asadb/template/space/manage-access.html
r78de8cb r1927d79 1 1 {% extends "base.html" %} 2 2 3 {% block title %}{{group.name}}: Office Access{% endblock %}3 {% block title %}{{group.name}}: Space Access{% endblock %} 4 4 {% block content %} 5 5 6 <h1>{{group.name}}: Office Access</h1>6 <h1>{{group.name}}: Space Access</h1> 7 7 8 8 {% include "groups/group_tools.part.html" %} … … 48 48 <th>{{assignment.space}}</th> 49 49 <td> 50 {% with assignment.space.lock_type as lock_type %} 51 {% if lock_type.db_update == "none" %} 52 <p><strong>Warning: Access to this office is not managed through the ASA DB.</strong></p> 53 <p><em><a href='{% url space-lock-type %}'>{{lock_type.name}}</a></em>: {{lock_type.description}}{% if lock_type.info_url %} <a href='{{lock_type.info_url}}'>Details.</a>{%endif%}</p> 54 <p>Contact <a href='mailto:{{lock_type.info_addr}}'>{{lock_type.info_addr}}</a> for more information.</p> 55 56 {% else %} 57 58 <p>We recommend managing access on the <a href='{% url groups:group-manage-officers group.id %}'>update people</a> page if possible. You should only need to use this page if:</p> 59 <ul> 60 <li>You need to grant access to somebody who does not have an Athena account, or</li> 61 <li>Your group has several offices, and somebody needs access to one or more of the offices, but should not have access to all of them</li> 62 </ul> 63 50 64 <table class='pretty-table'> 51 65 <tr> … … 67 81 </tr>{% endfor %} 68 82 </table> 83 84 {% endif %} 85 {% endwith %} 86 69 87 </td> 70 88 </tr> -
asadb/template/space/summary.html
rde2f0ac rf6982d4 27 27 <th>Room</th> 28 28 <th>Group</th> 29 <th><a href='{% url space-lock-type %}'>Lock Type</a></th> 29 30 <th>Access</th> 30 31 </tr> … … 33 34 <td>{{office.space.number}}</td> 34 35 <td><a href='{% url groups:group-detail office.group.pk %}'>{{office.group}}</a></td> 36 <td>{{office.space.lock_type.name}}</td> 35 37 <td><a href='{% url groups:group-space-access office.group.pk %}'>Access</a></td> 36 38 </tr>
Note: See TracChangeset
for help on using the changeset viewer.