Changeset 7444bbe
- Timestamp:
- Aug 6, 2011, 5:43:39 AM (14 years ago)
- Branches:
- master, space-access, stable, stage, test-hooks
- Children:
- f27faaf
- Parents:
- 89be44c (diff), bb674c2 (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@…> (08/06/11 05:43:39)
- git-committer:
- Alex Dehnert <adehnert@…> (08/06/11 05:43:39)
- Files:
-
- 10 added
- 13 edited
-
asadb/forms/admin.py (modified) (1 diff)
-
asadb/forms/models.py (modified) (1 diff)
-
asadb/groups/admin.py (modified) (1 diff)
-
asadb/mit/__init__.py (modified) (4 diffs)
-
asadb/settings.py (modified) (1 diff)
-
asadb/template/forms/select.html (modified) (1 diff)
-
asadb/template/fysm/submit.html (modified) (1 diff)
-
.gitignore (modified) (1 diff)
-
asadb/groups/load_people.py (added)
-
asadb/groups/migrations/0002_add_officers.py (added)
-
asadb/groups/migrations/0003_add_people.py (added)
-
asadb/groups/migrations/0004_add_require_student.py (added)
-
asadb/groups/models.py (modified) (4 diffs)
-
asadb/groups/views.py (modified) (1 diff)
-
asadb/media/style/style.css (modified) (3 diffs)
-
asadb/template/form_utils/better_form.html (added)
-
asadb/template/form_utils/fields_as_trs.html (added)
-
asadb/template/groups/group_change_main.html (added)
-
asadb/template/groups/group_change_officers.html (added)
-
asadb/template/groups/group_detail.html (added)
-
asadb/template/groups/group_version.html (added)
-
asadb/urls.py (modified) (3 diffs)
-
design/tools/audit-trail.txt (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
asadb/forms/admin.py
rc27da9e rbb674c2 11 11 ) 12 12 list_display_links = ('group', 'display_name', 'year', ) 13 list_filter = ('year', 'categories', ) 14 search_fields = ('group__name', 'group__abbreviation', 'display_name', 'year', ) 13 15 14 16 class FYSMCategoryAdmin(admin.ModelAdmin): -
asadb/forms/models.py
rc71d5c7 rbb674c2 30 30 ) 31 31 super(FYSM, self).save(*args, **kwargs) # Call the "real" save() method. 32 33 def __str__(self, ): 34 return "%s (%d)" % (self.display_name, self.year, ) 32 35 33 36 class Meta: -
asadb/groups/admin.py
r89be44c r7444bbe 17 17 ) 18 18 list_display_links = ('id', 'name', ) 19 list_filter = [ 'activity_category', ] 20 date_hierarchy = 'update_date' 19 21 search_fields = [ 'id', 'name', 'abbreviation', 'officer_email', 'athena_locker', ] 20 22 -
asadb/mit/__init__.py
r1c68fbb r9c1f0b2 1 import subprocess 2 import ldap 3 import ldap.filter 4 1 5 from django.contrib.auth.middleware import RemoteUserMiddleware 2 6 from django.contrib.auth.backends import RemoteUserBackend … … 8 12 import settings 9 13 10 def zephyr(msg, clas='remit', instance='log', rcpt='adehnert',): 11 import os 12 os.system("zwrite -d -c '%s' -i '%s' '%s' -m '%s'" % (clas, instance, rcpt, msg, )) 14 def zephyr(msg, clas='message', instance='log', rcpt='nobody',): 15 proc = subprocess.Popen( 16 ['zwrite', '-d', '-n', '-c', clas, '-i', instance, rcpt, ], 17 stdin=subprocess.PIPE, stdout=subprocess.PIPE 18 ) 19 proc.communicate(msg) 13 20 14 21 class ScriptsRemoteUserMiddleware(RemoteUserMiddleware): … … 25 32 def configure_user(self, user, ): 26 33 username = user.username 27 import ldap34 user.password = "ScriptsSSLAuth" 28 35 con = ldap.open('ldap.mit.edu') 29 36 con.simple_bind_s("", "") 30 37 dn = "dc=mit,dc=edu" 31 38 fields = ['cn', 'sn', 'givenName', 'mail', ] 32 result = con.search_s('dc=mit,dc=edu', ldap.SCOPE_SUBTREE, 'uid=%s'%username, fields) 39 userfilter = ldap.filter.filter_format('uid=%s', [username]) 40 result = con.search_s('dc=mit,dc=edu', ldap.SCOPE_SUBTREE, userfilter, fields) 33 41 if len(result) == 1: 34 42 user.first_name = result[0][1]['givenName'][0] … … 39 47 except ObjectDoesNotExist: 40 48 print "Failed to retrieve mit group" 41 user.save() 49 else: 50 raise ValueError, ("Could not find user with username '%s' (filter '%s')"%(username, userfilter)) 42 51 try: 43 52 user.groups.add(auth.models.Group.objects.get(name='autocreated')) 44 53 except ObjectDoesNotExist: 45 54 print "Failed to retrieve autocreated group" 55 user.save() 46 56 return user 47 57 -
asadb/settings.py
r062a126 r7444bbe 72 72 'django.contrib.auth.middleware.AuthenticationMiddleware', 73 73 'django.middleware.transaction.TransactionMiddleware', 74 'django.middleware.csrf.CsrfViewMiddleware', 74 75 'reversion.middleware.RevisionMiddleware', 75 76 ] -
asadb/template/forms/select.html
r90afb00 rc9d8369 7 7 8 8 <form method="post" action=""> 9 {% csrf_token %} 9 10 <table> 10 11 {{ form.as_table }} -
asadb/template/fysm/submit.html
r3356151 rc9d8369 10 10 11 11 <form enctype="multipart/form-data" method="post" action=""> 12 {% csrf_token %} 12 13 <table class='pretty-table'> 13 14 {{ form.as_table }} -
.gitignore
ra76118f r3ffb210 6 6 asadb/media/fysm/logos/ 7 7 asadb/media/fysm/slides/ 8 asadb/media/page-previews/fysm/ 8 9 asadb/util/saved-data/ 9 10 asadb/util/warehouse-dump.csv -
asadb/groups/models.py
r2a6907a r89be44c 1 1 from django.db import models 2 3 import datetime 2 4 3 5 # Create your models here. … … 24 26 updater = models.CharField(max_length=30) # match Django username field 25 27 28 def officers(self, role=None, person=None, as_of="now",): 29 """Get the set of people holding some office. 30 31 If None is passed for role, person, or as_of, that field will not 32 be constrained. If as_of is "now" (default) the status will be 33 required to be current. If any of the three parameters are set 34 to another value, the corresponding filter will be applied. 35 """ 36 office_holders = OfficeHolder.objects.filter(group=self,) 37 if role: 38 if isinstance(role, str): 39 office_holders = office_holders.filter(role__slug=role) 40 else: 41 office_holders = office_holders.filter(role=role) 42 if person: 43 office_holders = office_holders.filter(person=person) 44 if as_of: 45 if as_of == "now": as_of = datetime.datetime.now() 46 office_holders = office_holders.filter(start_time__lte=as_of, end_time__gte=as_of) 47 return office_holders 48 26 49 def __str__(self, ): 27 50 return self.name … … 29 52 class Meta: 30 53 ordering = ('name', ) 54 55 56 class OfficerRole(models.Model): 57 UNLIMITED = 10000 58 59 display_name = models.CharField(max_length=50) 60 slug = models.SlugField() 61 description = models.TextField() 62 max_count = models.IntegerField(default=UNLIMITED, help_text='Maximum number of holders of this role. Use %d for no limit.' % UNLIMITED) 63 require_student = models.BooleanField(default=False) 64 65 def __str__(self, ): 66 return self.display_name 67 68 @classmethod 69 def retrieve(cls, slug, ): 70 return cls.objects.get(slug=slug) 71 72 73 class OfficeHolder(models.Model): 74 EXPIRE_OFFSET = datetime.timedelta(seconds=1) 75 76 person = models.CharField(max_length=30) 77 role = models.ForeignKey('OfficerRole') 78 group = models.ForeignKey('Group') 79 start_time = models.DateTimeField(default=datetime.datetime.now) 80 end_time = models.DateTimeField(default=datetime.datetime.max) 81 82 def expire(self, ): 83 self.end_time = datetime.datetime.now()-self.EXPIRE_OFFSET 84 self.save() 85 86 def __str__(self, ): 87 return "<OfficeHolder: person=%s, role=%s, group=%s, start_time=%s, end_time=%s>" % ( 88 self.person, self.role, self.group, self.start_time, self.end_time, ) 89 90 def __repr__(self, ): 91 return str(self) 92 31 93 32 94 class ActivityCategory(models.Model): … … 38 100 class Meta: 39 101 verbose_name_plural = "activity categories" 102 103 104 class AthenaMoiraAccount_ActiveManager(models.Manager): 105 def get_query_set(self, ): 106 return super(AthenaMoiraAccount_ActiveManager, self).get_query_set().filter(del_date=None) 107 108 class AthenaMoiraAccount(models.Model): 109 username = models.CharField(max_length=8) 110 mit_id = models.CharField(max_length=15) 111 first_name = models.CharField(max_length=45) 112 last_name = models.CharField(max_length=45) 113 account_class = models.CharField(max_length=10) 114 mutable = models.BooleanField(default=True) 115 add_date = models.DateField(help_text="Date when this person was added to the dump.", ) 116 del_date = models.DateField(help_text="Date when this person was removed from the dump.", blank=True, null=True, ) 117 mod_date = models.DateField(help_text="Date when this person's record was last changed.", blank=True, null=True, ) 118 119 objects = models.Manager() 120 active_accounts = AthenaMoiraAccount_ActiveManager() 121 122 def is_student(self, ): 123 # XXX: Is this... right? 124 return self.account_class == 'G' or self.account_class.isdigit() 125 126 def __str__(self, ): 127 if self.mutable: 128 mutable_str = "" 129 else: 130 mutable_str = " (immutable)" 131 return "<AthenaMoiraAccount: username=%s name='%s, %s' account_class=%s%s>" % ( 132 self.username, self.last_name, self.first_name, 133 self.account_class, mutable_str, 134 ) 135 136 def __repr__(self, ): 137 return str(self) 138 139 class Meta: 140 verbose_name = "Athena (Moira) account" -
asadb/groups/views.py
r5be39cc r89be44c 1 1 # Create your views here. 2 3 import groups.models 4 5 from django.contrib.auth.decorators import user_passes_test, login_required 6 from django.contrib.contenttypes.models import ContentType 7 from django.core.exceptions import PermissionDenied 8 from django.views.generic import ListView, DetailView 9 from django.shortcuts import render_to_response, get_object_or_404 10 from django.template import RequestContext 11 from django.template import Context, Template 12 from django.template.loader import get_template 13 from django.http import Http404, HttpResponseRedirect 14 from django.core.urlresolvers import reverse 15 from django.core.mail import EmailMessage, mail_admins 16 from django.forms import Form 17 from django.forms import ModelForm 18 from django.forms import ModelChoiceField 19 from django.db.models import Q 20 21 import form_utils.forms 22 import reversion.models 23 24 class GroupChangeMainForm(form_utils.forms.BetterModelForm): 25 class Meta: 26 fieldsets = [ 27 ('basic', { 28 'legend': 'Basic Information', 29 'fields': ['name', 'abbreviation', 'description', 'activity_category', ], 30 }), 31 ('size', { 32 'legend':'Membership Numbers', 33 'fields': ['num_undergrads', 'num_grads', 'num_community', 'num_other',], 34 }), 35 ('contact', { 36 'legend': 'Contact Information', 37 'fields': ['website_url', 'meeting_times', 'group_email', 'officer_email', ], 38 }), 39 ('more-info', { 40 'legend': 'Additional Information', 41 'fields': ['constitution_url', 'advisor_name', 'main_account_id', 'funding_account_id', 'athena_locker', 'recognition_date', ], 42 }), 43 ] 44 model = groups.models.Group 45 46 def manage_main(request, group_id, ): 47 group = get_object_or_404(groups.models.Group, pk=group_id) 48 49 if not request.user.has_perm('groups.change_group', group): 50 raise PermissionDenied 51 52 msg = None 53 54 initial = {} 55 if request.method == 'POST': # If the form has been submitted... 56 form = GroupChangeMainForm(request.POST, request.FILES, instance=group, ) # A form bound to the POST data 57 58 if form.is_valid(): # All validation rules pass 59 request_obj = form.save() 60 61 # Send email 62 #tmpl = get_template('fysm/update_email.txt') 63 #ctx = Context({ 64 # 'group': group_obj, 65 # 'fysm': fysm_obj, 66 # 'view_uri': view_uri, 67 # 'submitter': request.user, 68 # 'request': request, 69 # 'sender': "ASA FYSM team", 70 #}) 71 #body = tmpl.render(ctx) 72 #email = EmailMessage( 73 # subject='FYSM entry for "%s" updated by "%s"' % ( 74 # group_obj.name, 75 # request.user, 76 # ), 77 # body=body, 78 # from_email='asa-fysm@mit.edu', 79 # to=[group_obj.officer_email, request.user.email, ], 80 # bcc=['asa-fysm-submissions@mit.edu', ] 81 #) 82 #email.send() 83 msg = "Thanks for editing!" 84 85 else: 86 form = GroupChangeMainForm(instance=group, initial=initial, ) # An unbound form 87 88 context = { 89 'group': group, 90 'form': form, 91 'msg': msg, 92 } 93 return render_to_response('groups/group_change_main.html', context, context_instance=RequestContext(request), ) 94 95 class GroupDetailView(DetailView): 96 context_object_name = "group" 97 model = groups.models.Group 98 def get_context_data(self, **kwargs): 99 # Call the base implementation first to get a context 100 context = super(GroupDetailView, self).get_context_data(**kwargs) 101 group = context['group'] 102 103 # Indicate whether this person should be able to see "private" info 104 context['viewpriv'] = self.request.user.has_perm('groups.view_group_private_info', group) 105 return context 106 107 class GroupHistoryView(ListView): 108 context_object_name = "version_list" 109 template_name = "groups/group_version.html" 110 111 def get_queryset(self): 112 history_entries = None 113 if 'group' in self.kwargs: 114 group = get_object_or_404(groups.models.Group, pk=self.kwargs['group']) 115 history_entries = reversion.models.Version.objects.get_for_object(group) 116 else: 117 history_entries = reversion.models.Version.objects.all() 118 group_content_type = ContentType.objects.get_for_model(groups.models.Group) 119 history_entries = history_entries.filter(content_type=group_content_type) 120 length = len(history_entries) 121 if length > 150: 122 history_entries = history_entries[length-100:] 123 return history_entries 124 125 def get_context_data(self, **kwargs): 126 context = super(GroupHistoryView, self).get_context_data(**kwargs) 127 if 'group' in self.kwargs: 128 group = get_object_or_404(groups.models.Group, pk=self.kwargs['group']) 129 context['title'] = "History for %s" % (group.name, ) 130 else: 131 context['title'] = "Recent Changes" 132 return context 133 134 135 def load_officers(group, ): 136 officers = group.officers() 137 people = list(set([ officer.person for officer in officers ])) 138 roles = groups.models.OfficerRole.objects.all() 139 140 officers_map = {} 141 for officer in officers: 142 officers_map[(officer.person, officer.role)] = officer 143 144 return people, roles, officers_map 145 146 def manage_officers(request, group_id, ): 147 group = get_object_or_404(groups.models.Group, pk=group_id) 148 149 if not request.user.has_perm('groups.change_group', group): 150 raise PermissionDenied 151 152 max_new = 4 153 154 people, roles, officers_map = load_officers(group) 155 156 msgs = [] 157 changes = [] 158 edited = False 159 kept = 0 160 kept_not = 0 161 if request.method == 'POST': # If the form has been submitted 162 edited = True 163 164 new_people = {} 165 moira_accounts = {} 166 for i in range(max_new): 167 key = "extra.%d" % (i, ) 168 if key in request.POST and request.POST[key] != "": 169 username = request.POST[key] 170 try: 171 moira_accounts[username] = groups.models.AthenaMoiraAccount.active_accounts.get(username=username) 172 new_people[i] = username 173 except groups.models.AthenaMoiraAccount.DoesNotExist: 174 msgs.append('Athena account "%s" appears not to exist. Changes involving them have been ignored.' % (username, )) 175 for person in people: 176 try: 177 moira_accounts[person] = groups.models.AthenaMoiraAccount.active_accounts.get(username=person) 178 except groups.models.AthenaMoiraAccount.DoesNotExist: 179 msgs.append('Athena account "%s" appears not to exist. They can not be added to new roles. You should remove them from any roles they hold, if you have not already.' % (person, )) 180 for role in roles: 181 key = "holders.%s" % (role.slug, ) 182 new_holders = set() 183 if key in request.POST: 184 new_holders = set(request.POST.getlist(key, )) 185 if len(new_holders) > role.max_count: 186 msgs.append("You selected %d people for %s; only %d are allowed. No changes to %s have been carried out in this update." % 187 (len(new_holders), role.display_name, role.max_count, role.display_name, ) 188 ) 189 else: 190 for person in people: 191 if person in new_holders: 192 if (person, role) in officers_map: 193 if role.require_student and not moira_accounts[person].is_student(): 194 msgs.append('Only students can have the %s role, and %s does not appear to be a student. You should replace this person ASAP.' % (role, person, )) 195 #changes.append(("Kept", "yellow", person, role)) 196 kept += 1 197 else: 198 if person not in moira_accounts: 199 msgs.append('Could not add nonexistent Athena account "%s" as %s.' % (person, role, )) 200 elif role.require_student and not moira_accounts[person].is_student(): 201 msgs.append('Only students can have the %s role, and %s does not appear to be a student.' % (role, person, )) 202 else: 203 holder = groups.models.OfficeHolder(person=person, role=role, group=group,) 204 holder.save() 205 changes.append(("Added", "green", person, role)) 206 else: 207 if (person, role) in officers_map: 208 officers_map[(person, role)].expire() 209 changes.append(("Removed", "red", person, role)) 210 else: 211 kept_not += 1 212 pass 213 for i in range(max_new): 214 if "extra.%d" % (i, ) in new_holders: 215 if i in new_people: 216 person = new_people[i] 217 if role.require_student and not moira_accounts[person].is_student(): 218 msgs.append('Only students can have the %s role, and %s does not appear to be a student.' % (role, person, )) 219 else: 220 holder = groups.models.OfficeHolder(person=person, role=role, group=group,) 221 holder.save() 222 changes.append(("Added", "green", person, role)) 223 224 # reload the data 225 people, roles, officers_map = load_officers(group) 226 227 officers_data = [] 228 for person in people: 229 role_list = [] 230 for role in roles: 231 if (person, role) in officers_map: 232 role_list.append((role, True)) 233 else: 234 role_list.append((role, False)) 235 officers_data.append((False, person, role_list)) 236 null_role_list = [(role, False) for role in roles] 237 for i in range(max_new): 238 officers_data.append((True, "extra.%d" % (i, ), null_role_list)) 239 240 context = { 241 'group': group, 242 'roles': roles, 243 'people': people, 244 'officers': officers_data, 245 'edited': edited, 246 'changes': changes, 247 'kept': kept, 248 'kept_not': kept_not, 249 'msgs': msgs, 250 } 251 return render_to_response('groups/group_change_officers.html', context, context_instance=RequestContext(request), ) -
asadb/media/style/style.css
r3400018 ree679c6 7 7 { 8 8 border-collapse: collapse; 9 } 10 table.pretty-table th, table.pretty-table td 9 margin: 0.5em; 10 } 11 table.pretty-table th, table.pretty-table td, table.pretty-table caption 11 12 { 12 13 border: 1px solid black; 13 14 padding: 2px; 14 15 } 15 table.pretty-table th 16 { 16 table.pretty-table caption 17 { 18 border-bottom: none; 19 } 20 table.pretty-table th, table.pretty-table caption 21 { 22 font-weight: bold; 17 23 background: #BE2933; 18 24 } … … 202 208 203 209 210 /***************** 211 * GROUP DISPLAY * 212 *****************/ 213 tr.private-info th:before 214 { 215 content: "* "; 216 } 217 218 204 219 /* Reset some stuff */ 205 220 h1, h2, h3, th … … 212 227 margin-left: 0; 213 228 } 229 230 table.group-change-officers-change td 231 { 232 text-align: center; 233 } -
asadb/urls.py
rb01b0ba ree679c6 10 10 11 11 import forms.views 12 import groups.models 13 import groups.views 12 14 13 import groups.models14 15 15 16 urlpatterns = patterns('', … … 35 36 url(r'^fysm/(?:(\d+)/)?(?:([\w-]+)/)?$', forms.views.fysm_by_years, name='fysm', ), 36 37 38 # Group editing 39 url(r'^group/(\d+)/edit/main$', groups.views.manage_main, name='group-manage-main', ), 40 url(r'^group/(\d+)/edit/officers$', groups.views.manage_officers, name='group-manage-officers', ), 41 37 42 # Group list 38 43 url( … … 46 51 name='group-list', 47 52 ), 53 url(r'^group/(?P<pk>\d+)/$', groups.views.GroupDetailView.as_view(), name='group-detail', ), 54 url(r'^groups/recent_changes/$', groups.views.GroupHistoryView.as_view(), name='groups-manage-history', ), 55 url(r'^group/(?P<group>\d+)/history/$', groups.views.GroupHistoryView.as_view(), name='group-manage-history', ), 48 56 49 57 # Uncomment the admin/doc line below and add 'django.contrib.admindocs' -
design/tools/audit-trail.txt
r35d8e68 r5bb1780 34 34 * Design 35 35 * Store all versions of all models in a single table, JSON'd 36 * Metadata stored unpickled 37 * Time 38 * Editor 39 * Some other stuff 36 40 * django-reversion 37 41 * https://github.com/etianen/django-reversion
Note: See TracChangeset
for help on using the changeset viewer.