Changeset ef118cf
- Timestamp:
- Feb 11, 2014, 2:22:25 AM (12 years ago)
- Branches:
- master, stable, stage
- Children:
- d4f571c
- Parents:
- 2563230 (diff), e5c5180 (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:22:25)
- git-committer:
- Alex Dehnert <adehnert@…> (02/11/14 02:22:25)
- Files:
-
- 19 added
- 38 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/migrations/0016_peoplestatuslookup.py (added)
-
asadb/forms/models.py (modified) (4 diffs)
-
asadb/forms/views.py (modified) (13 diffs)
-
asadb/groups/admin.py (modified) (5 diffs)
-
asadb/groups/diffs.py (modified) (1 diff)
-
asadb/groups/listdiff.py (added)
-
asadb/groups/load_people.py (modified) (1 diff)
-
asadb/groups/migrations/0028_officerrole_admins.py (added)
-
asadb/groups/migrations/0029_athena_affiliation.py (added)
-
asadb/groups/models.py (modified) (12 diffs)
-
asadb/groups/urls.py (modified) (1 diff)
-
asadb/groups/views.py (modified) (17 diffs)
-
asadb/media/style/style.css (modified) (3 diffs)
-
asadb/mit/__init__.py (modified) (1 diff)
-
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/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/base.html (modified) (2 diffs)
-
asadb/template/fysm/fysm_listing.html (modified) (3 diffs)
-
asadb/template/groups/account_lookup.html (modified) (3 diffs)
-
asadb/template/groups/create/startup_review.html (modified) (2 diffs)
-
asadb/template/groups/diffs/new-group-announce.txt (modified) (3 diffs)
-
asadb/template/groups/group_change_main.html (modified) (1 diff)
-
asadb/template/groups/group_change_officers.html (modified) (3 diffs)
-
asadb/template/groups/group_list.html (modified) (1 diff)
-
asadb/template/groups/reporting.html (modified) (1 diff)
-
asadb/template/groups/reporting/non-students.html (modified) (2 diffs)
-
asadb/template/index.html (modified) (2 diffs)
-
asadb/template/membership/admin.html (modified) (1 diff)
-
asadb/template/membership/anti-hazing.txt (modified) (1 diff)
-
asadb/template/membership/confirm.html (modified) (3 diffs)
-
asadb/template/membership/people-lookup.html (added)
-
asadb/template/membership/thanks.html (modified) (1 diff)
-
asadb/template/membership/update.html (modified) (3 diffs)
-
asadb/template/midway/map.html (added)
-
asadb/template/midway/midway_list.html (added)
-
asadb/template/midway/upload.html (added)
-
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)
-
asadb/urls.py (modified) (2 diffs)
-
asadb/util/admin.py (added)
-
asadb/util/db_filters.py (added)
-
asadb/util/diff_static_data.sh (modified) (2 diffs)
-
docs/install-on-scripts.txt (added)
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
rafc5348 r86986f5 1 import datetime 2 import errno 3 import json 4 import os 5 import re 6 7 import ldap 8 1 9 from django.conf import settings 10 from django.contrib.auth.models import User 2 11 from django.db import models 3 4 import datetime5 import os, errno6 12 7 13 import groups.models … … 18 24 description = models.TextField(help_text="Explain, in no more than 400 characters (including spaces), what your group does and why incoming students should get involved.") 19 25 logo = models.ImageField(upload_to='fysm/logos', blank=True, help_text="Upload a logo (JPG, GIF, or PNG) to display on the main FYSM page as well as the group detail page. This will be scaled to be 100px wide.") 20 slide = models.ImageField(upload_to='fysm/slides', default="", help_text="Upload a slide (JPG, GIF, or PNG) to display on the group detail page. This will be scaled to be at most 600x600 pixels. We recommend making it exactly that size.")26 slide = models.ImageField(upload_to='fysm/slides', blank=True, default="", help_text="Upload a slide (JPG, GIF, or PNG) to display on the group detail page. This will be scaled to be at most 600x600 pixels. We recommend making it exactly that size.") 21 27 tags = models.CharField(max_length=100, blank=True, help_text="Specify some free-form, comma-delimited tags for your group", ) 22 28 categories = models.ManyToManyField('FYSMCategory', blank=True, help_text="Put your group into whichever of our categories seem applicable.", ) … … 168 174 num_other = models.IntegerField(verbose_name="Num non-MIT") 169 175 170 membership_list = models.TextField( help_text="Member emails on separate lines (Athena usernames where applicable)")176 membership_list = models.TextField(blank=True, help_text="Member emails on separate lines (Athena usernames where applicable)") 171 177 172 178 email_preface = models.TextField(blank=True, help_text="If you would like, you may add text here that will preface the text of the policies when it is sent out to the group membership list provided above.") 173 179 174 hazing_statement = "By checking this, I hereby affirm that I have read and understand Chapter 269: Sections 17, 18, and 19 of Massachusetts Law. I furthermore attest that I have provided the appropriate address or will otherwise distribute to group members, pledges, and/or applicants, copies of Massachusetts Law 269: 17, 18, 19 and that our organization, group, or team agrees to comply with the provisions of that law. (See below for text.)"180 hazing_statement = "By checking this, I hereby affirm that I have read and understand <a href='http://web.mit.edu/asa/rules/ma-hazing-law.html'>Chapter 269: Sections 17, 18, and 19 of Massachusetts Law</a>. I furthermore attest that I have provided the appropriate address or will otherwise distribute to group members, pledges, and/or applicants, copies of Massachusetts Law 269: 17, 18, 19 and that our organization, group, or team agrees to comply with the provisions of that law. (See below for text.)" 175 181 no_hazing = models.BooleanField(help_text=hazing_statement) 176 182 177 discrimination_statement = "By checking this, I hereby affirm that I have read and understand the MIT Non-Discrimination Policy. I furthermore attest that our organization, group, or team agrees to not discriminate against individuals on the basis of race, color, sex, sexual orientation, gender identity, religion, disability, age, genetic information, veteran status, ancestry, or national or ethnic origin."183 discrimination_statement = "By checking this, I hereby affirm that I have read and understand the <a href='http://web.mit.edu/referencepubs/nondiscrimination/'>MIT Non-Discrimination Policy</a>. I furthermore attest that our organization, group, or team agrees to not discriminate against individuals on the basis of race, color, sex, sexual orientation, gender identity, religion, disability, age, genetic information, veteran status, ancestry, or national or ethnic origin." 178 184 no_discrimination = models.BooleanField(help_text=discrimination_statement) 179 185 … … 205 211 def __unicode__(self, ): 206 212 return "PersonMembershipUpdate for %s" % (self.username, ) 213 214 215 class PeopleStatusLookup(models.Model): 216 people = models.TextField(help_text="Enter some usernames or email addresses, separated by newlines, to look up here.") 217 requestor = models.ForeignKey(User, null=True, blank=True, ) 218 referer = models.URLField(blank=True) 219 time = models.DateTimeField(default=datetime.datetime.now) 220 classified_people_json = models.TextField() 221 _classified_people = None 222 223 def ldap_classify(self, usernames, ): 224 con = ldap.open('ldap-too.mit.edu') 225 con.simple_bind_s("", "") 226 dn = "ou=users,ou=moira,dc=mit,dc=edu" 227 fields = ['uid', 'eduPersonAffiliation', 'mitDirStudentYear'] 228 229 chunk_size = 100 230 username_chunks = [] 231 ends = range(chunk_size, len(usernames), chunk_size) 232 start = 0 233 end = 0 234 for end in ends: 235 username_chunks.append(usernames[start:end]) 236 start = end 237 extra = usernames[end:] 238 if extra: 239 username_chunks.append(extra) 240 241 results = [] 242 for chunk in username_chunks: 243 filters = [ldap.filter.filter_format('(uid=%s)', [u]) for u in chunk] 244 userfilter = "(|%s)" % (''.join(filters), ) 245 batch_results = con.search_s(dn, ldap.SCOPE_SUBTREE, userfilter, fields) 246 results.extend(batch_results) 247 248 left = set([u.lower() for u in usernames]) 249 undergrads = [] 250 grads = [] 251 staff = [] 252 secret = [] 253 other = [] 254 info = { 255 'undergrads': undergrads, 256 'grads': grads, 257 'staff': staff, 258 'secret': secret, 259 'affiliate': other, 260 } 261 for result in results: 262 username = result[1]['uid'][0] 263 left.remove(username.lower()) 264 affiliation = result[1].get('eduPersonAffiliation', ['secret'])[0] 265 if affiliation == 'student': 266 year = result[1].get('mitDirStudentYear', [None])[0] 267 if year == 'G': 268 grads.append((username, None)) 269 elif year.isdigit(): 270 undergrads.append((username, year)) 271 else: 272 other.append((username, year)) 273 else: 274 info[affiliation].append((username, None, )) 275 info['unknown'] = [(u, None) for u in left] 276 return info 277 278 def classify_people(self, people): 279 mit_usernames = [] 280 alum_addresses = [] 281 other_mit_addresses = [] 282 nonmit_addresses = [] 283 284 for name in people: 285 local, at, domain = name.partition('@') 286 if domain.lower() == 'mit.edu' or domain == '': 287 mit_usernames.append(local) 288 elif domain.lower() == 'alum.mit.edu': 289 alum_addresses.append((name, None)) 290 elif domain.endswith('.mit.edu'): 291 other_mit_addresses.append((name, None)) 292 else: 293 nonmit_addresses.append((name, None)) 294 295 results = self.ldap_classify(mit_usernames) 296 results['alum'] = alum_addresses 297 results['other-mit'] = other_mit_addresses 298 results['non-mit'] = nonmit_addresses 299 return results 300 301 def split_people(self): 302 splitted = re.split(r'[\n,]+', self.people) 303 people = [] 304 for name in splitted: 305 name = name.strip() 306 if len(name) > 2 and (name[0] == '(') and (name[-1] == ')'): 307 name = name[1:-1] 308 name = name.replace(' at ', '@') 309 if name: 310 people.append(name) 311 return people 312 313 def update_classified_people(self): 314 people = self.split_people() 315 self._classified_people = self.classify_people(people) 316 self.classified_people_json = json.dumps(self._classified_people) 317 return self._classified_people 318 319 @property 320 def classified_people(self): 321 if self._classified_people is None: 322 self._classified_people = json.loads(self.classified_people_json) 323 return self._classified_people 324 325 def classifications_with_descriptions(self): 326 descriptions = { 327 'undergrads': 'Undergraduate students (class year in parentheses)', 328 'grads': 'Graduate students', 329 'alum': "Alumni Association addresses", 330 'staff': 'MIT Staff (including faculty)', 331 'affiliate': 'This includes some alumni, group members with Athena accounts sponsored through SAO, and many others.', 332 'secret': 'People with directory information suppressed. These people have Athena accounts, but they could have any MIT affiliation, including just being a student group member.', 333 'unknown': "While this looks like an Athena account, we couldn't find it. This could be a deactivated account, or it might never have existed.", 334 'other-mit': ".mit.edu addresses that aren't @mit.edu or @alum.mit.edu.", 335 'non-mit': "Non-MIT addresses, including outside addresses of MIT students.", 336 } 337 338 names = ( 339 ('undergrads', 'Undergrads', ), 340 ('grads', 'Grad students', ), 341 ('alum', 'Alumni', ), 342 ('staff', 'Staff', ), 343 ('affiliate', 'Affiliates', ), 344 ('secret', 'Secret', ), 345 ('unknown', 'Unknown', ), 346 ('other-mit', 'Other MIT addresses', ), 347 ('non-mit', 'Non-MIT addresses', ), 348 ) 349 350 classifications = self.classified_people 351 sorted_results = [] 352 for k, label in names: 353 sorted_results.append({ 354 'label': label, 355 'description': descriptions[k], 356 'people': sorted(classifications[k]), 357 }) 358 return sorted_results 359 360 361 ########## 362 # MIDWAY # 363 ########## 364 365 366 class Midway(models.Model): 367 name = models.CharField(max_length=50) 368 slug = models.SlugField() 369 date = models.DateTimeField() 370 table_map = models.ImageField(upload_to='midway/maps') 371 372 def __str__(self, ): 373 return "%s" % (self.name, ) 374 375 class MidwayAssignment(models.Model): 376 midway = models.ForeignKey(Midway) 377 location = models.CharField(max_length=20) 378 group = models.ForeignKey(groups.models.Group) 379 380 def __str__(self, ): 381 return "<MidwayAssignment: %s at %s at %s>" % (self.group, self.location, self.midway, ) -
asadb/forms/views.py
rafc5348 r9605a6a 1 import forms.models2 import groups.models3 import groups.views4 import util.emails1 import collections 2 import csv 3 import datetime 4 import StringIO 5 5 6 6 from django.conf import settings … … 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 ################# … … 149 153 'description', 150 154 'logo', 151 'slide',152 155 'tags', 153 156 'categories', … … 249 252 cycle = forms.models.GroupConfirmationCycle.latest() 250 253 251 users_groups = groups.models.Group. involved_groups(request.user.username)254 users_groups = groups.models.Group.admin_groups(request.user.username) 252 255 qs = membership_update_qs.filter(pk__in=users_groups) 253 256 … … 258 261 queryset=qs, 259 262 title="Submit membership update for...", 260 msg="The list below contains only groups that list you as being involved. You must be an administrator of a group to submit an update.",261 263 ) 262 264 … … 265 267 super(Form_GroupMembershipUpdate, self).__init__(*args, **kwargs) 266 268 self.fields['no_hazing'].required = True 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 267 271 268 272 class Meta: … … 281 285 'num_other_affiliate', 282 286 'num_other', 283 'membership_list',287 #'membership_list', 284 288 ] 285 289 … … 415 419 role_groups[office_holder.group.pk] = (office_holder.group, set()) 416 420 role_groups[office_holder.group.pk][1].add(office_holder.role.display_name) 417 418 # Find groups the user searched for419 filterset = groups.views.GroupFilter(request.GET, membership_update_qs)420 filtered_groups = filterset.qs.all()421 show_filtered_groups = ('search' in request.GET)422 421 423 422 message = "" … … 466 465 'role_groups':role_groups, 467 466 'form':form, 468 'filter':filterset,469 'show_filtered_groups':show_filtered_groups,470 'filtered_groups':filtered_groups,471 467 'member_groups':selected_groups, 472 468 'message': message, … … 496 492 497 493 494 class 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 498 505 @permission_required('groups.view_group_private_info') 499 def group_confirmation_issues(request, ):506 def group_confirmation_issues(request, slug, ): 500 507 account_numbers = ("accounts" in request.GET) and request.GET['accounts'] == "1" 501 508 502 active_groups = groups.models.Group.active_groups 503 group_updates = forms.models.GroupMembershipUpdate.objects.all() 509 check_groups = groups.models.Group.objects.filter(group_status__slug__in=('active', 'suspended', )) 510 check_groups = check_groups.select_related('group_status') 511 group_updates = forms.models.GroupMembershipUpdate.objects.filter(cycle__slug=slug, ) 512 group_updates = group_updates.select_related('group', 'group__group_status') 504 513 people_confirmations = forms.models.PersonMembershipUpdate.objects.filter( 505 514 deleted__isnull=True, 506 515 valid__gt=0, 516 cycle__slug=slug, 507 517 ) 508 518 509 519 buf = StringIO.StringIO() 510 520 output = csv.writer(buf) 511 fields = ['group_id', 'group_name', ' issue', 'num_confirm', 'officer_email', ]521 fields = ['group_id', 'group_name', 'group_status', 'recognition_date', 'issue', 'num_confirm', 'officer_email', ] 512 522 if account_numbers: fields.append("main_account") 513 523 output.writerow(fields) 514 524 515 q_present = Q(id__in=group_updates.values('group')) 516 missing_groups = active_groups.filter(~q_present) 517 #print len(list(group_updates)) 518 for group in missing_groups: 519 num_confirms = len(people_confirmations.filter(groups=group)) 525 def output_issue(group, issue, num_confirms): 520 526 fields = [ 521 527 group.id, 522 528 group.name, 523 'unsubmitted', 529 group.group_status.slug, 530 group.recognition_date, 531 issue, 524 532 num_confirms, 525 533 group.officer_email, … … 528 536 output.writerow(fields) 529 537 538 q_present = Q(id__in=group_updates.values('group')) 539 missing_groups = check_groups.filter(~q_present) 540 #print len(list(group_updates)) 541 for group in missing_groups: 542 #num_confirms = len(people_confirmations.filter(groups=group)) 543 output_issue(group, 'unsubmitted', '') 544 530 545 for group_update in group_updates: 531 546 group = group_update.group 532 num_confirms = len(people_confirmations.filter(groups=group))547 num_confirms = people_confirmations.filter(groups=group).count() 533 548 problems = [] 534 549 … … 542 557 543 558 for problem in problems: 544 fields = [ 545 group.id, 546 group.name, 547 problem, 548 num_confirms, 549 group.officer_email, 550 ] 551 if account_numbers: fields.append(group.main_account_id) 552 output.writerow(fields) 553 559 output_issue(group, problem, num_confirms) 554 560 555 561 return HttpResponse(buf.getvalue(), mimetype='text/csv', ) 562 563 564 class PeopleStatusLookupForm(ModelForm): 565 class Meta: 566 model = forms.models.PeopleStatusLookup 567 fields = ('people', ) 568 569 def 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'] 577 lookup.update_classified_people() 578 results = lookup.classifications_with_descriptions() 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)) 586 results = lookup.classifications_with_descriptions() 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), ) 597 598 ########## 599 # Midway # 600 ########## 601 602 603 class 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 616 def 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 624 625 class 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 ) 647 648 649 class 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 658 filterset = MidwayAssignmentFilter(self.request.GET) 659 context['assignments'] = filterset.qs 660 context['filter'] = filterset 661 context['pagename'] = 'midway' 662 663 return context 664 665 666 class MidwayAssignmentsUploadForm(Form): 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]) 675 676 @permission_required('forms.add_midwayassignment') 677 def midway_assignment_upload(request, slug, ): 678 midway = get_object_or_404(forms.models.Midway, slug=slug, ) 679 680 uploaded = False 681 found = [] 682 issues = collections.defaultdict(list) 683 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 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)' 709 except groups.models.Group.MultipleObjectsReturned: 710 issue = 'multiple groups found (ignored)' 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 716 else: 717 form = MidwayAssignmentsUploadForm() # An unbound form 718 719 context = { 720 'midway':midway, 721 'form':form, 722 'uploaded': uploaded, 723 'found': found, 724 'issues': dict(issues), 725 'pagename':'midway', 726 } 727 return render_to_response('midway/upload.html', context, context_instance=RequestContext(request), ) -
asadb/groups/admin.py
rcbffe98 r89165c1 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) … … 181 203 'last_name', 182 204 'account_class', 205 'affiliation_basic', 206 'loose_student', 183 207 'mutable', 184 208 'add_date', … … 188 212 list_display_links = ( 'id', 'username', ) 189 213 search_fields = ( 'username', 'mit_id', 'first_name', 'last_name', 'account_class', ) 214 list_filter = ( 215 'account_class', 216 'affiliation_basic', 'affiliation_detailed', 217 'loose_student', 218 'mutable', 219 ) 190 220 admin.site.register(groups.models.AthenaMoiraAccount, Admin_AthenaMoiraAccount) -
asadb/groups/diffs.py
rafc5348 r9af1bb4 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/load_people.py
r161ce5f r89165c1 26 26 'last_name', 27 27 'account_class', 28 'affiliation_basic', 29 'affiliation_detailed', 28 30 ] 29 31 -
asadb/groups/models.py
r62f73df ref118cf 1 1 # -*- coding: utf8 -*- 2 2 3 from django.conf import settings 4 from django.db import models 5 from django.core.exceptions import ValidationError 6 from django.core.validators import RegexValidator 7 from django.contrib.auth.models import User 8 from django.template.defaultfilters import slugify 9 import reversion 10 3 import collections 11 4 import datetime 12 5 import filecmp … … 20 13 import urllib2 21 14 15 from django.conf import settings 16 from django.db import models 17 from django.db.models import Q 18 from django.core.exceptions import ValidationError 19 from django.core.validators import RegexValidator 20 from django.contrib.auth.models import User, Permission 21 from django.contrib.contenttypes.models import ContentType 22 from django.template.defaultfilters import slugify 23 24 import reversion 25 22 26 import mit 23 27 … … 30 34 ) 31 35 32 locker_validator = RegexValidator(regex=r'^[-A-Za-z0-9_.]+$', message='Enter a valid Athena locker. ')36 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..') 33 37 34 38 class Group(models.Model): … … 100 104 if as_of == "now": as_of = datetime.datetime.now() 101 105 office_holders = office_holders.filter(start_time__lte=as_of, end_time__gte=as_of) 106 office_holders = office_holders.order_by('role', 'person') 102 107 return office_holders 103 108 … … 112 117 current_officers = OfficeHolder.current_holders.filter(person=username) 113 118 users_groups = Group.objects.filter(officeholder__in=current_officers).distinct() 119 120 @staticmethod 121 def admin_groups(username, codename='admin_group'): 122 holders = OfficeHolder.current_holders.filter_perm(codename=codename).filter(person=username) 123 users_groups = Group.objects.filter(officeholder__in=holders).distinct() 114 124 return users_groups 115 125 … … 395 405 396 406 @classmethod 407 def getRolesGrantingPerm(cls, perm=None, model=Group, codename=None, ): 408 """Get all OfficerRole objects granting a permission 409 410 Either `perm` or `codename` must be supplied, but not both. If 411 `codename` is provided (and `perm` is None), then `perm` the 412 permission corresponding to `model` (default: `Group`) and `codename` 413 will be found and used.""" 414 415 if perm is None: 416 ct = ContentType.objects.get_for_model(model) 417 print ct 418 print Permission.objects.filter(content_type=ct) 419 perm = Permission.objects.get(content_type=ct, codename=codename) 420 421 Q_user = Q(user_permissions=perm) 422 Q_group = Q(groups__permissions=perm) 423 users = User.objects.filter(Q_user|Q_group) 424 roles = cls.objects.filter(grant_user__in=users) 425 return roles 426 427 @classmethod 397 428 def retrieve(cls, slug, ): 398 429 return cls.objects.get(slug=slug) 430 399 431 reversion.register(OfficerRole) 400 432 … … 407 439 ) 408 440 441 def filter_perm(self, perm=None, model=Group, codename=None, ): 442 roles = OfficerRole.getRolesGrantingPerm(perm=perm, model=model, codename=codename) 443 return self.get_query_set().filter(role__in=roles) 444 409 445 class OfficeHolder(models.Model): 410 446 EXPIRE_OFFSET = datetime.timedelta(seconds=1) 411 447 END_NEVER = datetime.datetime.max 412 448 413 person = models.CharField(max_length=30, db_index=True, )449 person = models.CharField(max_length=30, db_index=True, help_text='Athena username') 414 450 role = models.ForeignKey('OfficerRole', db_index=True, ) 415 451 group = models.ForeignKey('Group', db_index=True, ) … … 433 469 def __repr__(self, ): 434 470 return str(self) 471 435 472 reversion.register(OfficeHolder) 436 473 … … 506 543 507 544 def __str__(self, ): 508 active = "" 509 if not self.is_active: 510 active = " (inactive)" 511 return "%s%s" % (self.name, active, ) 545 return self.name 512 546 513 547 class Meta: … … 528 562 def get_query_set(self, ): 529 563 return super(AthenaMoiraAccount_ActiveManager, self).get_query_set().filter(del_date=None) 564 565 def student_account_classes(): 566 year = datetime.datetime.now().year 567 return ["G"] + [str(yr) for yr in range(year-5, year+10)] 530 568 531 569 class AthenaMoiraAccount(models.Model): … … 535 573 last_name = models.CharField(max_length=45) 536 574 account_class = models.CharField(max_length=10) 575 affiliation_basic = models.CharField(max_length=10) 576 affiliation_detailed = models.CharField(max_length=40) 577 loose_student = models.BooleanField(default=False, help_text='Whether to use loose or strict determination of student status. Loose means that either the account class or the affiliation should indicate student status; strict means that the affiliation must be student. In general, we use strict; for some people ("secret people") directory information is suppressed and the affiliation will be misleading.') 537 578 mutable = models.BooleanField(default=True) 538 579 add_date = models.DateField(help_text="Date when this person was added to the dump.", ) … … 544 585 545 586 def is_student(self, ): 546 # XXX: Is this... right? 547 return self.account_class == 'G' or self.account_class.isdigit() 587 student_affiliation = (self.affiliation_basic == 'student') 588 student_class = (self.account_class in student_account_classes()) 589 return student_affiliation or (student_class and self.loose_student) 590 591 @staticmethod 592 def student_q(): 593 q_affiliation = Q(affiliation_basic='student') 594 q_class = Q(account_class__in=student_account_classes()) 595 return q_affiliation | (q_class & Q(loose_student=True)) 548 596 549 597 def format(self, ): -
asadb/groups/urls.py
r532a8e9 ra7c08e4 1 1 from django.conf.urls.defaults import * 2 3 import django.shortcuts 2 4 3 5 import groups.views 4 6 import space.views 5 7 8 def redirect_view(to): 9 def _redirect_view(request, **kwargs): 10 return django.shortcuts.redirect(to, **kwargs) 11 return _redirect_view 12 6 13 group_patterns = patterns('', 7 14 url(r'^$', groups.views.GroupDetailView.as_view(), name='group-detail', ), 8 15 url(r'^edit/main$', groups.views.manage_main, name='group-manage-main', ), 9 url(r'^edit/officers$', groups.views.manage_officers, name='group-manage-officers', ), 16 url(r'^edit/people$', groups.views.manage_officers, name='group-manage-officers', ), 17 url(r'^edit/officers$', redirect_view('groups:group-manage-officers'), ), 10 18 url(r'^history/$', groups.views.GroupHistoryView.as_view(), name='group-manage-history', ), 11 19 url(r'^space/$', space.views.manage_access, name='group-space-access', ), -
asadb/groups/views.py
rd7557b8 ref118cf 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, ) … … 630 630 self.fields['constitution_url'].required = 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 self.fields['group_email'].required = True 632 633 self.fields['athena_locker'].required = True 634 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.)" 635 636 # Specifically, if the group ends up wanting to use scripts.mit.edu, 637 # they will currently be assigned locker.scripts.mit.edu. If they try 638 # to use foo.bar, then https://foo.bar.scripts.mit.edu/ will produce a 639 # certificate name mismatch. Officially, underscores are not allowed in 640 # hostnames, so foo_.scripts.mit.edu may fail with some software. 633 641 634 642 class Meta(GroupCreateForm.Meta): … … 688 696 from_email='asa-admin@mit.edu', 689 697 ) 690 # XXX: Handle this better691 if officer_domain != 'mit.edu' or (create_group_list and group_domain != 'mit.edu'):692 accounts_mail.to = ['asa-groups@mit.edu']693 accounts_mail.cc = ['asa-db@mit.edu']694 accounts_mail.subject = "ERROR: " + accounts_mail.subject695 accounts_mail.body = "Bad domain on officer or group list\n\n" + accounts_mail.body696 698 697 699 else: … … 820 822 return render_to_response('groups/create/startup.html', context, context_instance=RequestContext(request), ) 821 823 824 def review_group_check_warnings(group_startup, group, ): 825 warnings = [] 826 827 if group.name.startswith("MIT "): 828 warnings.append('Group name starts with "MIT". Generally, we prefer "Foo, MIT" instead.') 829 if "mit" in group.athena_locker.lower(): 830 warnings.append('Athena locker name contains "mit", which may be redundant with paths like "http://web.mit.edu/mitfoo" or "/mit/foo/".') 831 832 if group_startup.president_kerberos == group_startup.treasurer_kerberos: 833 warnings.append('President matches Treasurer.') 834 if "%s@mit.edu" % (group_startup.president_kerberos, ) in (group.officer_email, group.group_email): 835 warnings.append('President email matches officer and/or group email.') 836 if group.officer_email == group.group_email: 837 warnings.append('Officer email matches group email.') 838 839 if '@mit.edu' not in group.officer_email or '@mit.edu' not in group.group_email: 840 warnings.append('Officer and/or group email are non-MIT. Ensure that they are not requesting the addresses be created, and consider suggesting they use an MIT list instead.') 841 842 if '.' in group.athena_locker: 843 warnings.append('Athena locker contains a ".". This is not compatible with scripts.mit.edu\'s wildcard certificate, and may cause other problems.') 844 if '_' in group.athena_locker: 845 warnings.append('Athena locker contains a "_". If this locker name gets used in a URL (for example, locker.scripts.mit.edu), it will technically violate the hostname specification and may not work in some clients.') 846 if len(group.athena_locker) > 12: 847 warnings.append('Athena locker is more than twelve characters long. In general, twelve characters is the longest Athena locker an ASA-recognized group can get.') 848 849 return warnings 850 822 851 @permission_required('groups.recognize_group') 823 852 def recognize_normal_group(request, pk, ): … … 836 865 return render_to_response('groups/create/err.not-applying.html', context, context_instance=RequestContext(request), ) 837 866 867 context['warnings'] = review_group_check_warnings(group_startup, group) 868 838 869 context['msg'] = "" 839 870 if request.method == 'POST': … … 842 873 group_startup.save() 843 874 844 group.group_status = groups.models.GroupStatus.objects.get(slug=' active')875 group.group_status = groups.models.GroupStatus.objects.get(slug='suspended') 845 876 group.constitution_url = "" 846 877 group.recognition_date = datetime.datetime.now() 847 878 group.set_updater(request.user) 879 880 note = groups.models.GroupNote( 881 author=request.user.username, 882 body="Approved group for recognition.", 883 acl_read_group=True, 884 acl_read_offices=True, 885 group=group, 886 ).save() 848 887 849 888 group.save() … … 900 939 name = django_filters.CharFilter(lookup_type='icontains', label="Name contains") 901 940 abbreviation = django_filters.CharFilter(lookup_type='iexact', label="Abbreviation is") 941 officer_email = django_filters.CharFilter(lookup_type='icontains', label="Officers' list contains") 942 943 account_filter = util.db_filters.MultiNumberFilter( 944 lookup_type='exact', label="Account number", 945 names=('main_account_id', 'funding_account_id', ), 946 ) 902 947 903 948 class Meta: … … 906 951 'name', 907 952 'abbreviation', 953 'officer_email', 908 954 'activity_category', 909 955 'group_class', 910 956 'group_status', 911 957 'group_funding', 958 'account_filter', 912 959 ] 913 960 … … 945 992 groups_filterset = GroupFilter(request.GET, the_groups) 946 993 the_groups = groups_filterset.qs 994 947 995 officers = groups.models.OfficeHolder.objects.filter(start_time__lte=datetime.datetime.now(), end_time__gte=datetime.datetime.now()) 948 996 officers = officers.filter(group__in=the_groups) 949 997 officers = officers.select_related(depth=1) 998 950 999 role_slugs = ['president', 'treasurer', 'financial', 'reservation'] 951 1000 roles = groups.models.OfficerRole.objects.filter(slug__in=role_slugs) 952 1001 roles = sorted(roles, key=lambda r: role_slugs.index(r.slug)) 1002 953 1003 officers_map = collections.defaultdict(lambda: collections.defaultdict(set)) 954 1004 for officer in officers: … … 958 1008 role_list = [] 959 1009 for role in roles: 960 role_list.append( officers_map[group][role])1010 role_list.append(sorted(officers_map[group][role])) 961 1011 officers_data.append((group, role_list)) 962 1012 … … 1004 1054 if 'pk' in self.kwargs: 1005 1055 group = get_object_or_404(groups.models.Group, pk=self.kwargs['pk']) 1006 history_entries = reversion. models.Version.objects.get_for_object(group)1056 history_entries = reversion.get_for_object(group) 1007 1057 else: 1008 1058 history_entries = reversion.models.Version.objects.all() … … 1288 1338 def show_nonstudent_officers(request, ): 1289 1339 student_roles = groups.models.OfficerRole.objects.filter(require_student=True, ) 1290 year = datetime.datetime.now().year 1291 account_classes = ["G"] + [str(yr) for yr in range(year-5, year+10)] 1292 students = groups.models.AthenaMoiraAccount.active_accounts.filter(account_class__in=account_classes) 1340 student_q = groups.models.AthenaMoiraAccount.student_q() 1341 students = groups.models.AthenaMoiraAccount.active_accounts.filter(student_q) 1293 1342 office_holders = groups.models.OfficeHolder.current_holders.order_by('group__name', 'role', ) 1294 1343 office_holders = office_holders.filter(role__in=student_roles) 1295 1344 office_holders = office_holders.exclude(person__in=students.values('username')) 1296 office_holders = office_holders.select_related('group', ' role')1345 office_holders = office_holders.select_related('group', 'group__group_status', 'role') 1297 1346 1298 1347 msg = None … … 1300 1349 if 'sort' in request.GET: 1301 1350 if request.GET['sort'] == 'group': 1302 office_holders = office_holders.order_by('group__name', 'role', 'person', ) 1351 office_holders = office_holders.order_by('group__name', 'group__group_status', 'role', 'person', ) 1352 elif request.GET['sort'] == 'status': 1353 office_holders = office_holders.order_by('group__group_status', 'group__name', 'role', 'person', ) 1303 1354 elif request.GET['sort'] == 'role': 1304 office_holders = office_holders.order_by('role', 'group__ name', 'person', )1355 office_holders = office_holders.order_by('role', 'group__group_status', 'group__name', 'person', ) 1305 1356 elif request.GET['sort'] == 'person': 1306 office_holders = office_holders.order_by('person', 'group__ name', 'role', )1357 office_holders = office_holders.order_by('person', 'group__group_status', 'group__name', 'role', ) 1307 1358 else: 1308 1359 msg = 'Unknown sort key "%s".' % (request.GET['sort'], ) -
asadb/media/style/style.css
rd6f8984 r53e624e 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 … … 267 272 } 268 273 269 table.group- change-officers-change td274 table.group-update-people-change td 270 275 { 271 276 text-align: center; -
asadb/mit/__init__.py
rd0c7563 r8c0a2ff 130 130 def scripts_login(request, **kwargs): 131 131 host = request.META['HTTP_HOST'].split(':')[0] 132 if host == 'localhost':132 if host in ('localhost', '127.0.0.1'): 133 133 return login(request, **kwargs) 134 134 elif request.META['SERVER_PORT'] == '444': -
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
r465eb7a 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 7 8 class Admin_LockType(VersionAdmin): 9 list_display = ( 10 'id', 11 'name', 12 'slug', 13 'info_addr', 14 'info_url', 15 'db_update', 16 ) 17 list_display_links = ( 'id', 'name', 'slug', ) 18 search_fields = ('name', 'slug', 'info_addr', 'info_url', 'db_update', ) 19 admin.site.register(space.models.LockType, Admin_LockType) 4 20 5 21 class Admin_Space(VersionAdmin): … … 8 24 'number', 9 25 'asa_owned', 26 'lock_type', 10 27 'merged_acl', 11 28 ) 12 29 list_display_links = ( 'id', 'number', ) 30 list_filter = ('lock_type', ) 13 31 search_fields = ('number', ) 14 32 admin.site.register(space.models.Space, Admin_Space) 15 33 16 34 class Admin_SpaceAssignment(admin.ModelAdmin): 35 class AssignmentPeriodFilter(util.admin.TimePeriodFilter): 36 start_field = 'start' 37 end_field = 'end' 38 17 39 list_max_show_all = 500 18 40 list_display = ( … … 24 46 ) 25 47 list_display_links = list_display 26 list_filter = ( 'space', )48 list_filter = (AssignmentPeriodFilter, 'space', ) 27 49 search_fields = ( 'group__name', 'group__officer_email', 'space__number', ) 28 50 admin.site.register(space.models.SpaceAssignment, Admin_SpaceAssignment) 29 51 30 52 class Admin_SpaceAccessListEntry(admin.ModelAdmin): 53 class AccessPeriodFilter(util.admin.TimePeriodFilter): 54 start_field = 'start' 55 end_field = 'end' 56 31 57 list_display = ( 32 58 'group', … … 37 63 ) 38 64 list_display_links = list_display 65 list_filter = (AccessPeriodFilter, 'space', ) 39 66 search_fields = ( 40 67 'group__name', 'group__officer_email', -
asadb/space/diffs.py
rc8f4eea r27e09a9 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 affecting 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
r2563230 ref118cf 10 10 11 11 EXPIRE_OFFSET = datetime.timedelta(seconds=1) 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 12 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/base.html
rcec082b r5560f6d 7 7 <link rel="stylesheet" type="text/css" href="{{ MEDIA_URL }}style/style.css" /> 8 8 <link rel="stylesheet" type="text/css" href="{{ MEDIA_URL }}style/page-frame.css" /> 9 {% block extrahead %} 10 {% endblock %} 9 11 10 12 </head> … … 14 16 <li{% ifequal pagename "groups" %} class='selected'{% endifequal %}><a href="{% url groups:list %}">Groups</a></li> 15 17 <li{% ifequal pagename "fysm" %} class='selected'{% endifequal %}><a href="{% url fysm %}">FYSM</a></li> 18 <li{% ifequal pagename "midway" %} class='selected'{% endifequal %}><a href="{% url midway-list %}">Midway</a></li> 16 19 <li{% ifequal pagename "about" %} class='selected'{% endifequal %}><a href="{% url about %}">Help & About</a></li> 17 20 {% if user.is_staff %}<li><a href='{% url admin:index %}'>Admin</a></li>{% endif %} -
asadb/template/fysm/fysm_listing.html
rbd2bc21 r21ab6b3 12 12 <p>Hello incoming MIT students!</p> 13 13 14 <p>Student activities play an important role in student life at MIT & mdash; they're a14 <p>Student activities play an important role in student life at MIT – they're a 15 15 great place to meet people, practice old skills and learn new ones, have fun, 16 16 and give back to the community. Undergraduate and graduate students, along … … 59 59 {% for fysm in fysm_list %} 60 60 <div class='single-fysm-entry'> 61 <h3><a href='{% url fysm-view year fysm.pk %}'>{{fysm.display_name}}</a></h3> 61 {% comment %} 62 If we were cooler, years when we had slides would still link them here. 63 We're not that cool, though, so we just always link the join page. 64 {% endcomment %} 65 <h3><a href='{% url fysm-link year "join" fysm.pk %}'>{{fysm.display_name}}</a></h3> 62 66 <div class='fysm-body'> 63 67 <div class='logo'> … … 69 73 </div> 70 74 <p class='data'>{{fysm.description}}</p> 71 <p class='join'><a href='{% url fysm-link year "join" fysm.pk %}'> Join{{fysm.display_name}}!</a> Have questions? Send them to <a href='mailto:{{fysm.contact_email}}'>{{fysm.contact_email}}</a>!</p>75 <p class='join'><a href='{% url fysm-link year "join" fysm.pk %}'>Learn more about {{fysm.display_name}}!</a> Have questions? Send them to <a href='mailto:{{fysm.contact_email}}'>{{fysm.contact_email}}</a>!</p> 72 76 </div> 73 77 {% if fysm.tags %} -
asadb/template/groups/account_lookup.html
rb928edc r9469a73 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> … … 39 43 {% endif %} 40 44 45 {% if account_number %} 46 <p>You may be able to find information about account {{account_number}} in <a href='https://rolesweb.mit.edu/cgi-bin/roleauth2.pl?category=SAP+SAP-related&func_name=CAN+SPEND+OR+COMMIT+FUNDS&qual_code=F{{account_number}}&skip_root=Y'>Roles</a>.</p> 47 {% endif %} 48 41 49 <table class='pretty-table'> 42 50 <form enctype="multipart/form-data" method="get" action=""> … … 45 53 </form> 46 54 </table> 55 56 <h2>Group Status</h2> 57 58 <table class='pretty-table'> 59 <tr> 60 <th>Status</th> 61 <th>Description</th> 62 </tr> 63 <tr> 64 <th>Active</th> 65 <td>Active groups are normal ASA groups in good standing.</td> 66 </tr> 67 <tr> 68 <th>Suspended and Derecognized</th> 69 <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> 70 </tr> 71 <tr> 72 <th>Non-Group Entity</th> 73 <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> 74 </tr> 75 </table> 76 47 77 48 78 <h2>Available Roles</h2> -
asadb/template/groups/create/startup_review.html
rac93a0c r7dde669 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> … … 51 51 {% endif %} 52 52 53 {% if warnings %} 54 <div class='messagebox warnbox'> 55 <h3>Potential issues</h3> 56 57 <p>The following potential issues were identified with this group's startup application:</p> 58 59 <ul> 60 {% for warning in warnings %} 61 <li>{{warning}}</li> 62 {%endfor%} 63 </ul> 64 65 <p>Please look over these issues. Usually, you should <strong>reach out to the group</strong> and ask them to fix (or consider fixing) each issue before you approve the group. In some cases, it may be appropriate to ignore an issue. (For example, "MIT" is a central part of some group's acronyms (like "HTGAMIT"). In these cases, confirming with the group may not be necessary.)</p> 66 </div> 67 {%endif%} 68 53 69 {% if disp_form %} 54 70 <form enctype="multipart/form-data" method="post" action=""> 55 71 {% csrf_token %} 72 {% if warnings %} 73 <input type='submit' name='approve' value='Approve DESPITE WARNINGS' /> 74 {% else %} 56 75 <input type='submit' name='approve' value='Approve' /> 76 {% endif %} 57 77 <input type='submit' name='reject' value='Reject' /> 58 78 </form> -
asadb/template/groups/diffs/new-group-announce.txt
r264efa0 rf38b6d0 5 5 to the MIT community! 6 6 7 The last step to finalize the group recognition is for you to finish entering the 8 group's complete information into the ASA Database: 7 Your group is currently *suspended*. The last step to finalize the group 8 recognition is for you to finish entering the group's complete information into 9 the ASA Database: 9 10 10 11 http://web.mit.edu/asa/www/asa-db.shtml … … 12 13 Your group's entry is at: 13 14 14 http ://asa.mit.edu/{% url groups:group-detail group.pk %}15 https://asa.mit.edu{% url groups:group-detail group.pk %} 15 16 16 17 You should make sure to do the following: … … 29 30 can modify the information and that MIT personal certificates are 30 31 required to access the group's account in the ASA Database. 32 33 Once you have completed these steps, you must *notify asa-groups@mit.edu* to 34 become an active group. 31 35 32 36 *** Please keep this e-mail for further reference. *** -
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_change_officers.html
rbb1a40e r21ab6b3 1 1 {% extends "base.html" %} 2 2 3 {% block title %}{{group.name}}: Change people{% endblock %}3 {% block title %}{{group.name}}: Update people{% endblock %} 4 4 {% block content %} 5 5 6 <h1>{{group.name}}: Change people</h1>6 <h1>{{group.name}}: Update people</h1> 7 7 8 8 {% include "groups/group_tools.part.html" %} 9 9 10 <p>Please adjust your signatoriesbelow.</p>10 <p>Please adjust the people associated with your group below.</p> 11 11 12 12 <h2>Available roles</h2> … … 52 52 {% endif %} 53 53 54 <h2>Option 1: View and update signatoriesone at a time</h2>54 <h2>Option 1: View and update people one at a time</h2> 55 55 56 56 <p>Please note:</p> 57 57 <ul> 58 <li>We don't track "group membership" & mdash; people will show up in the list below only if they have one of the roles listed</li>59 <li>Type only the <em>Athena username</em> to add people & mdash; do not type full names or names plus username</li>58 <li>We don't track "group membership" – people will show up in the list below only if they have one of the roles listed</li> 59 <li>Type only the <em>Athena username</em> to add people – do not type full names or names plus username</li> 60 60 <li>To add <em>more than four</em> new people, add four at a time and <em>submit multiple times</em></li> 61 61 </ul> … … 64 64 <input type='hidden' name='opt-mode' value='table' /> 65 65 {% csrf_token %} 66 <table class='pretty-table group- change-officers-change'>66 <table class='pretty-table group-update-people-change'> 67 67 <thead> 68 68 <tr> -
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
rea8e8b6 r58fe19c 17 17 <h2><a name='results'></a>Results</h2> 18 18 {% if run_report %} 19 20 <p>Found {{report_groups|length}} groups:</p> 21 19 22 <table class='pretty-table'> 20 23 <thead> -
asadb/template/groups/reporting/non-students.html
r213dd57 r89165c1 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 <td>{ {holder.person}}</td>46 <td>{% if perms.groups.change_athenamoiraaccount %}<a href='{% url admin:groups_athenamoiraaccount_changelist %}?username={{holder.person}}'>{{holder.person}}</a>{% else %}{{holder.person}}{% endif %}</td> 45 47 </tr> 46 48 {% endfor %} -
asadb/template/index.html
rde2f0ac 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> … … 57 58 </ul></li> 58 59 <li><a href='{% url space-summary %}'>Space assignments</a> 60 <ul> 61 <li><a href='{% url space-lock-type %}'>Lock Types</a></li> 59 62 {% if perms.groups.view_group_private_info %} 60 <ul>61 63 <li><a href='{% url space-dump-locker-access %}'>Locker access (CSV)</a></li> 62 64 <li><a href='{% url space-dump-office-access %}'>Office access (CSV)</a></li> 65 {% endif %} 63 66 </ul> 64 {% endif %}65 67 </li> 66 68 <li><a href='{% url about %}'>About the ASA Database</a><ul> -
asadb/template/membership/admin.html
rd6f8984 r21ab6b3 1 1 {% extends "base.html" %} 2 2 3 {% block title %}Membership confirmations & mdash; ASA Exec pages{% endblock %}3 {% block title %}Membership confirmations – ASA Exec pages{% endblock %} 4 4 {% block content %} 5 5 6 <h2>Membership confirmations & mdash; ASA Exec pages</h2>6 <h2>Membership confirmations – ASA Exec pages</h2> 7 7 8 <p>Exec members can check the <a href='{% url membership-issues %}'>list of groups with issues</a> (or a version <a href='{% url membership-issues %}?accounts=1'>with account numbers</a>)</p> 8 <p>Exec members can check the issues:</p> 9 10 <table class='pretty-table'> 11 <tr> 12 <th>Name</th> 13 <th>Create date</th> 14 <th>Issues</th> 15 <th>(with accounts)</th> 16 </tr> 17 {% for cycle in cycle_list %} 18 <tr> 19 <th>{{cycle.name}}</th> 20 <td>{{cycle.create_date}}</td> 21 <td><a href='{% url membership-issues cycle.slug %}'>Issues</a></td> 22 <td><a href='{% url membership-issues cycle.slug %}?accounts=1'>(with accounts)</a></td> 23 </tr> 24 {% endfor %} 25 </table> 9 26 10 27 <p>The list includes the following types of issues:</p> 11 28 <dl> 12 <dt>unsubmitted</dt><dd>active groups ("group status" is "Active") who have not submitted an update (ever — see ASA-#191)</dd>13 <dt>confirmations</dt><dd>groups that have submitted an update (ever) but had less than five members confirm membership (again, ever — ASA-#191)</dd>14 <dt>50%</dt><dd>groups that (ever)submitted a confirmation with more non-students (alum, other affiliates, or other) than students (grad or undergrad)</dd>29 <dt>unsubmitted</dt><dd>active or suspended groups who have not submitted an update 30 <dt>confirmations</dt><dd>groups that have submitted an update but had less than five members confirm membership</dd> 31 <dt>50%</dt><dd>groups that submitted a confirmation with more non-students (alum, other affiliates, or other) than students (grad or undergrad)</dd> 15 32 </dl> 16 33 -
asadb/template/membership/anti-hazing.txt
r43434f6 r70a96d4 15 15 Text of Anti-Hazing Law: 16 16 17 Section 17. Hazing; organizing or participating; hazing defined: Whoever is a principal organizer or 18 participant in the crime of hazing, as defined herein, shall be punished by a fine of not more than three thousand 19 dollars or by imprisonment in a house of correction for not more than one year, or both such fine and imprisonment. 17 Section 17. Hazing; organizing or participating; hazing defined: Whoever is a principal organizer or participant in the crime of hazing, as defined herein, shall be punished by a fine of not more than three thousand dollars or by imprisonment in a house of correction for not more than one year, or both such fine and imprisonment. 20 18 21 The term hazing as used in this section and in sections eighteen and nineteen, shall mean any conduct or method of 22 initiation into any student organization, whether on public or private property, which willfully or recklessly 23 endangers the physical or mental health of any student or other person. Such conduct shall include whipping, beating, 24 branding, forced calisthenics, exposure to the weather, forced consumption of any food, liquor, beverage, drug or 25 other substance, or any other brutal treatment or forced physical activity which is likely to adversely affect the 26 physical health or safety of any such student or other person, or which subjects such student or other person to 27 extreme mental stress, including extended deprivation of sleep or rest or extended isolation. 19 The term hazing as used in this section and in sections eighteen and nineteen, shall mean any conduct or method of initiation into any student organization, whether on public or private property, which willfully or recklessly endangers the physical or mental health of any student or other person. Such conduct shall include whipping, beating, branding, forced calisthenics, exposure to the weather, forced consumption of any food, liquor, beverage, drug or other substance, or any other brutal treatment or forced physical activity which is likely to adversely affect the physical health or safety of any such student or other person, or which subjects such student or other person to extreme mental stress, including extended deprivation of sleep or rest or extended isolation. 28 20 29 Notwithstanding any other provisions of this section to the contrary, consent shall not be available as a defense to 30 any prosecution under this action. 21 Notwithstanding any other provisions of this section to the contrary, consent shall not be available as a defense to any prosecution under this action. 31 22 32 Section 18. Failure to report hazing: Whoever knows that another person is the victim of hazing as defined in 33 section seventeen and is at the scene of such crime shall, to the extent that such person can do so without danger 34 or peril to himself or others, report such crime to an appropriate law enforcement official as soon as reasonably 35 practicable. Whoever fails to report such crime shall be punished by a fine of not more than one thousand dollars. 23 Section 18. Failure to report hazing: Whoever knows that another person is the victim of hazing as defined in section seventeen and is at the scene of such crime shall, to the extent that such person can do so without danger or peril to himself or others, report such crime to an appropriate law enforcement official as soon as reasonably practicable. Whoever fails to report such crime shall be punished by a fine of not more than one thousand dollars. 36 24 37 Section 19. Copy of Secs. 17 to 19; issuance to students and student groups, teams and organizations; report: 38 Each institution of secondary education and each public and private institution of post secondary education shall 39 issue to every student group, student team or student organization which is part of such institution or is 40 recognized by the institution or permitted by the institution to use its name or facilities or is known by the 41 institution to exist as an unaffiliated student group, student team or student organization, a copy of this section 42 and sections seventeen and eighteen; provided, however, that an institution's compliance with this section's 43 requirements that an institution issue copies of this section and sections seventeen and eighteen to unaffiliated 44 student groups, teams or organizations shall not constitute evidence of the institution's recognition or endorsement 45 of said unaffiliated student groups, teams or organizations. 25 Section 19. Copy of Secs. 17 to 19; issuance to students and student groups, teams and organizations; report: Each institution of secondary education and each public and private institution of post secondary education shall issue to every student group, student team or student organization which is part of such institution or is recognized by the institution or permitted by the institution to use its name or facilities or is known by the institution to exist as an unaffiliated student group, student team or student organization, a copy of this section and sections seventeen and eighteen; provided, however, that an institution's compliance with this section's requirements that an institution issue copies of this section and sections seventeen and eighteen to unaffiliated student groups, teams or organizations shall not constitute evidence of the institution's recognition or endorsement of said unaffiliated student groups, teams or organizations. 46 26 47 Each such group, team or organization shall distribute a copy of this section and sections seventeen and eighteen to 48 each of its members, plebes, pledges or applicants for membership. It shall be the duty of each such group, team or 49 organization, acting through its designated officer, to deliver annually, to the institution [in MIT's case the 50 Office of Student Life Programs (with exception of varsity teams and club sports, who will deliver attested 51 acknowledgements to the Department of Athletics, Physical Education and Recreation)] an attested acknowledgement 52 stating that such group, team or organization has received a copy of this section and said sections seventeen and 53 eighteen, that each of its members, plebes, pledges, or applicants has received a copy of sections seventeen and 54 eighteen, and that such group, team or organization understands and agrees to comply with the provisions of this 55 section and sections seventeen and eighteen. 27 Each such group, team or organization shall distribute a copy of this section and sections seventeen and eighteen to each of its members, plebes, pledges or applicants for membership. It shall be the duty of each such group, team or organization, acting through its designated officer, to deliver annually, to the institution [in MIT's case the Office of Student Life Programs (with exception of varsity teams and club sports, who will deliver attested acknowledgements to the Department of Athletics, Physical Education and Recreation)] an attested acknowledgement stating that such group, team or organization has received a copy of this section and said sections seventeen and eighteen, that each of its members, plebes, pledges, or applicants has received a copy of sections seventeen and eighteen, and that such group, team or organization understands and agrees to comply with the provisions of this section and sections seventeen and eighteen. 56 28 57 Each institution of secondary education and each public or private institution of post secondary education shall, at 58 least annually, before or at the start of enrollment, deliver to each person who enrolls as a full time student in 59 such institution a copy of this section and sections seventeen and eighteen. 29 Each institution of secondary education and each public or private institution of post secondary education shall, at least annually, before or at the start of enrollment, deliver to each person who enrolls as a full time student in such institution a copy of this section and sections seventeen and eighteen. 60 30 61 Each institution of secondary education and each public or private institution of post secondary education shall 62 file, at least annually, a report with the board of higher education and in the case of secondary institutions, the 63 board of education, certifying that such institution has complied with its responsibility to inform student groups, 64 teams or organizations and to notify each full time student enrolled by it of the provisions of this section and 65 sections seventeen and eighteen and also certifying that said institution has adopted a disciplinary policy with 66 regard to the organizers and participants of hazing, and that such policy has been set forth with appropriate 67 emphasis in the student handbook or similar means of communicating the institution's policies to its students. The 68 board of higher education and, in the case of secondary institutions, the board of education shall promulgate 69 regulations governing the content and frequency of such reports, and shall forthwith report to the attorney general 70 any such institution which fails to make such report. 31 Each institution of secondary education and each public or private institution of post secondary education shall file, at least annually, a report with the board of higher education and in the case of secondary institutions, the board of education, certifying that such institution has complied with its responsibility to inform student groups, teams or organizations and to notify each full time student enrolled by it of the provisions of this section and sections seventeen and eighteen and also certifying that said institution has adopted a disciplinary policy with regard to the organizers and participants of hazing, and that such policy has been set forth with appropriate emphasis in the student handbook or similar means of communicating the institution's policies to its students. The board of higher education and, in the case of secondary institutions, the board of education shall promulgate regulations governing the content and frequency of such reports, and shall forthwith report to the attorney general any such institution which fails to make such report. 71 32 72 33 -
asadb/template/membership/confirm.html
r11941e1 r5560f6d 1 1 {% extends "base.html" %} 2 3 {% block extrahead %} 4 <link rel="stylesheet" href="{{MEDIA_URL}}js/libs/chosen/chosen.css"> 5 {% endblock %} 2 6 3 7 {% block title %}Membership update{% endblock %} … … 77 81 </table> 78 82 79 <h3>Option 2: Add or remove groups by searching</h3>83 <h3>Option 2: Select all your groups at once</h3> 80 84 81 <form action="" method="get"> 82 <table class='pretty-table'> 83 {{ filter.form.as_table }} 84 </table> 85 <input type="submit" name='search' value="Search" /> 86 <input type="submit" name='stop-search' value="Stop searching" /> 87 </form> 88 89 {% if show_filtered_groups %} 90 <table class='pretty-table'> 91 <thead> 92 <tr> 93 <th>Name</th> 94 <th>Website</th> 95 <th>ASA DB</th> 96 <th>Description</th> 97 <th>Add/Remove</th> 98 </tr> 99 </thead> 100 <tbody> 101 {% for group in filtered_groups %} 102 <tr> 103 <th>{{group.name}}</th> 104 <td>{% if group.website_url %}<a href='{{group.website_url}}'>Website</a>{%endif%}</td> 105 <td><a href='{% url groups:group-detail group.pk %}'>DB Entry</a></td> 106 <td>{{group.description}}</td> 107 <td> 108 <form action="" method="post"> 109 {% csrf_token %} 110 <input type="hidden" name="group" value="{{group.pk}}"> 111 {% if group in member_groups %} 112 <input type="hidden" name="action" value="remove"> 113 <input type="submit" name="add-remove" value="Remove"> 114 {% else %} 115 <input type="hidden" name="action" value="add"> 116 <input type="submit" name="add-remove" value="Add"> 117 {% endif %} 118 </form> 119 </td> 120 </tr> 121 {% endfor %} 122 </tbody> 123 </table> 124 {% else %} 125 <p>Hit "search" to see matching groups.</p> 126 {% endif %} 127 128 129 <h3>Option 3: Select all your groups at once</h3> 130 131 <p>Below is a list of all recognized groups. You can go through picking out the groups you are a member of. However, you should be careful not to deselect any groups (unless you aren't a member of them, of course).</p> 85 <p>Below, you can add or remove any recognized group from the list of groups you are a member of.</p> 132 86 133 87 <form enctype="multipart/form-data" method="post" action=""> … … 139 93 </form> 140 94 95 96 <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js" type="text/javascript"></script> 97 <script src="{{MEDIA_URL}}js/libs/chosen/chosen.jquery.js" type="text/javascript"></script> 98 <script type="text/javascript"> 99 $("#id_groups").chosen(); 100 </script> 101 141 102 {% endblock %} -
asadb/template/membership/thanks.html
r1ed7e51 r21ab6b3 1 1 {% extends "base.html" %} 2 2 3 {% block title %}Membership update & mdash; thanks{% endblock %}3 {% block title %}Membership update – thanks{% endblock %} 4 4 {% block content %} 5 5 6 <h2>Membership update & mdash; thanks</h2>6 <h2>Membership update – thanks</h2> 7 7 8 8 <p>Thanks for confirming your group's activity. You should receive a confirmation email to your officer's list, and a copy of the anti-hazing law to your group list.</p> -
asadb/template/membership/update.html
r6e247d3 r1a5ed66 5 5 6 6 7 <h1>ASA Group Registration & Anti-Hazing Form </h1>7 <h1>ASA Group Registration & Anti-Hazing Form for <a href='{%url groups:group-detail group.pk%}'>{{group.name}}</a></h1> 8 8 9 9 <p>The purpose of this electronic registration is for the ASA to determine which groups are still active and which are not. There are four components to this process:</p> … … 35 35 <h3><a name='components-membership'></a>3. Providing Membership Information</h3> 36 36 37 <p>We are requiring that groups provide membership information & mdash; both membership numbers and a membership list. This information is important for our understanding of your group and ensuring that groups are meeting our membership standards. We will also share the membership numbers with the funding boards (GSC, UA, and LEF/ARCADE) for groups that apply to those sources. <strong>It is very important you are honest when answering these questions</strong>, even if you are concerned your membership does not meet our standards. We will provide time and support for groups to improve their student participation as needed — both in terms of number and proportion of students. Groups that are shown to be dishonest in this process, particularly with regards to meeting our standards, <strong>may have funding and space eligibility revoked or be suspended or derecognized</strong>.</p>37 <p>We are requiring that groups provide membership information – both membership definition and the corresponding numbers. This information is important for our understanding of your group and ensuring that groups are meeting our membership standards. We will also share the membership numbers with the funding boards (GSC, UA, and LEF/ARCADE) for groups that apply to those sources. <strong>It is very important you are honest when answering these questions</strong>, even if you are concerned your membership does not meet our standards. We will provide time and support for groups to improve their student participation as needed – both in terms of number and proportion of students. Groups that are shown to be dishonest in this process, particularly with regards to meeting our standards, <strong>may have funding and space eligibility revoked or be suspended or derecognized</strong>.</p> 38 38 39 39 <dl> 40 <dt>Minimum definition of member</dt><dd><p>For the purposes of this form, the minimum definition of membership you should use is people you would expect to confirm membership in your group (not that they all have to) and people that you consider to be regular or active members of your group. (You may be more stringent, based on how your group views or defines membership internally.) You must state the specific definition of membership you are using below. This does not need to be a membership definition that is in your constitution.</p></dd>40 <dt>Minimum definition of member</dt><dd><p>For the purposes of this form, the minimum definition of membership you use should include only people who (a) you would expect to confirm membership in your group (not that they all have to) if asked and (b) you consider to be regular or active members of your group. Members should satisfy both (a) and (b); you should not count members who satisfy only one or the other. (You may be more stringent, based on how your group views or defines membership internally.) You must state the specific definition of membership you are using below. This does not need to be a membership definition that is in your constitution.</p></dd> 41 41 42 <dt>Privacy</dt><dd><p>The list of members you provide will not be shared. If we need to contact them we will contact your officer/exec list first — this would only be if we have concerns about the information provided or to spot-check the information provided. If feasible, we will ask your officer/exec list to contact your members directly.</p></dd> 43 44 <dt>Recruitment</dt><dd><p>If you are still in the midst of or leading up to your major recruitment period, we encourage you to submit current numbers and a list soon to ensure you have something submitted before the deadline. Then you can submit an updated form later, either before or after the deadline. You don't need to do this if your membership changes slightly, but we recommend you do so if your numbers change significantly.</p></dd> 42 <dt>Recruitment</dt><dd><p>If you are still in the midst of or leading up to your major recruitment period, we encourage you to submit current numbers soon to ensure you have something submitted before the deadline. Then you can submit an updated form later, either before or after the deadline. You don't need to do this if your membership changes slightly, but we recommend you do so if your numbers change significantly.</p></dd> 45 43 46 44 </dl> … … 48 46 <h3><a name='components-confirm'></a>4. Membership Confirmation</h3> 49 47 50 <p>In addition to the membership info provided by you above, please have at least five student members confirm membership using the link below. You do not need to have everyone do this; we are simply looking for <em>five students'</em> confirmation. You can<a href='{% url membership-submitted %}'>see how many have confirmed already</a>.</p>48 <p>In addition to the membership info provided by you above, please have at least five student members confirm membership using the link below. You do not need to have everyone do this; we are simply looking for <em>five students'</em> confirmation. Once you submit this form, you will be able to <a href='{% url membership-submitted %}'>see how many have confirmed already</a>.</p> 51 49 52 50 <p>Link to provide to members: <a href='{{confirm_uri}}'>{{confirm_uri}}</a>.</p> -
asadb/template/space/group-change-email.txt
rd768e47 r72a3d90 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 one week. 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> -
asadb/urls.py
rafc5348 r88a856b 65 65 ), 66 66 url(r'^membership/submitted/$', forms.views.View_GroupMembershipList.as_view(), name='membership-submitted', ), 67 url( 68 r'^membership/admin/$', 69 'django.views.generic.simple.direct_to_template', 70 {'template': 'membership/admin.html', 'extra_context': { 'pagename':'groups' }, }, 71 name='membership-admin', 72 ), 73 url(r'^membership/admin/issues.csv$', forms.views.group_confirmation_issues, name='membership-issues', ), 67 url(r'^membership/admin/$', forms.views.View_GroupConfirmationCyclesList.as_view(), name='membership-admin', ), 68 url(r'^membership/admin/issues/(?P<slug>[\w-]+).csv$', forms.views.group_confirmation_issues, name='membership-issues', ), 69 url(r'^membership/people-lookup/((?P<pk>\d+)/)?$', forms.views.people_status_lookup, name='membership-people-lookup', ), 70 71 # Midway 72 url(r'^midway/$', forms.views.View_Midways.as_view(), name='midway-list', ), 73 url(r'^midway/latest/$', forms.views.midway_map_latest, name='midway-map-latest', ), 74 url(r'^midway/(?P<slug>[\w-]+)/$', forms.views.MidwayMapView.as_view(), name='midway-map', ), 75 url(r'^midway/(?P<slug>[\w-]+)/assign/$', forms.views.midway_assignment_upload, name='midway-assign', ), 74 76 75 77 # Group list … … 80 82 url(r'^space/dump/office-access.csv$', space.views.dump_office_access, name='space-dump-office-access', ), 81 83 url(r'^space/$', space.views.summary, name='space-summary', ), 84 url(r'^space/lock_types.html$', space.views.lock_types, name='space-lock-type', ), 82 85 83 86 # Uncomment the admin/doc line below and add 'django.contrib.admindocs' -
asadb/util/diff_static_data.sh
rc060d8c 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 … … 12 12 ../../manage.py dumpdata --format=xml --indent=4 groups.ActivityCategory > groups_initial_data.xml 13 13 ../../manage.py dumpdata --format=xml --indent=4 forms.FYSMCategory > forms_initial_data.xml 14 git add {groups,forms}_initial_data.xml 14 ../../manage.py dumpdata --format=xml --indent=4 space.LockType --format=xml --indent=4 > space_lock_types.xml 15 git add {groups,forms}_initial_data.xml space_lock_types.xml 15 16 16 17 echo
Note: See TracChangeset
for help on using the changeset viewer.