Changeset 5680065


Ignore:
Timestamp:
Mar 29, 2012, 12:53:54 AM (14 years ago)
Author:
Alex Dehnert <adehnert@…>
Branches:
master, space-access, stable, stage
Children:
a53f8ee
Parents:
94f3a39
git-author:
Alex Dehnert <adehnert@…> (03/29/12 00:53:54)
git-committer:
Alex Dehnert <adehnert@…> (03/29/12 00:53:54)
Message:

Make locker updates work

I *think* this whole nasty pile of code may *finally* do what it's supposed to.

Fixes ASA Trac #15 and #7, maybe?

Location:
asadb
Files:
2 added
6 edited

Legend:

Unmodified
Added
Removed
  • asadb/space/admin.py

    rf1e4d44 r5680065  
    88        'number',
    99        'asa_owned',
     10        'merged_acl',
    1011    )
    1112    list_display_links = ( 'id', 'number', )
  • asadb/space/diffs.py

    r94f3a39 r5680065  
    2323
    2424role = {
    25     'office': groups.models.OfficerRole.objects.get(slug='office-access')
     25    'office': groups.models.OfficerRole.objects.get(slug='office-access'),
     26    'locker': groups.models.OfficerRole.objects.get(slug='locker-access'),
    2627}
    2728
     
    6061        self.group = group
    6162        self.offices = {}  # Space.pk -> (ID -> (Set name, Set name))
    62         if not role:
    63             role['office'] = groups.models.OfficerRole.objects.get(slug='office-access')
    64 
    65     def learn_access(self, space_pk, old, new):
     63        self.locker_acl = {}
     64        self.locker_messages = []
     65        self.changes = False
     66
     67    def learn_office_access(self, space_pk, old, new):
    6668        group_pk = self.group.pk
    6769        if group_pk in old:
     
    9799        self.add_office_signatories_per_time(1, new_time)
    98100
    99     def list_changes(self, ):
     101    def list_office_changes(self, ):
    100102        cac_lines = []
    101103        group_lines = []
     
    103105            cac_lines.append("%s:\t%s:\t%s" % (mit_id, verb, name))
    104106            group_lines.append("%s:\t%s" % (verb, name))
    105         changes = False
    106107        for space_pk, space_data in self.offices.items():
    107108            line = "Changes in %s:" % (all_spaces[space_pk].number, )
     
    113114                    pass
    114115                else:
    115                     changes = True
     116                    self.changes = True
    116117                    for name in old_names:
    117118                        if name in new_names:
     
    129130        cac_msg = "\n".join(cac_lines)
    130131        group_msg = "\n".join(group_lines)
    131         return changes, cac_msg, group_msg
     132        return cac_msg, group_msg
     133
     134    def add_locker_signatories(self, space_access, time):
     135        # space_access: ID -> (Name -> (Set Group.pk))
     136        if time in self.locker_acl:
     137            locker_acl = self.locker_acl[time]
     138        else:
     139            locker_acl = self.group.officers(as_of=time, role=role['locker'])
     140            self.locker_acl[time] = locker_acl
     141        for holder in locker_acl:
     142            fill_people(holder)
     143            holder_name = people_name[holder.person]
     144            holder_id = people_id[holder.person]
     145            space_access[holder_id][holder_name].add(self.group.pk)
    132146
    133147def init_groups(the_groups, assignments):
     
    137151            the_groups[group.id] = GroupInfo(group)
    138152
    139 def space_specific_access(group_data, old_time, new_time, ):
    140     process_spaces =  space.models.Space.objects.all()
    141     #process_spaces = process_spaces.filter(number="W20-467")
    142     for the_space in process_spaces:
    143         old_data = the_space.build_access(time=old_time)
    144         new_data = the_space.build_access(time=new_time)
    145         all_spaces[the_space.pk] = the_space
    146         init_groups(group_data, old_data[2])
    147         init_groups(group_data, new_data[2])
    148         for group_pk, group in group_data.items():
    149             if group_pk in old_data[0] or group_pk in new_data[0]:
    150                 group.learn_access(the_space.pk, old_data[0], new_data[0])
    151 
     153def flip_dict(dct):
     154    new = collections.defaultdict(set)
     155    for key, vals in dct.items():
     156        for val in vals:
     157            new[val].add(key)
     158    return new
     159
     160def joint_keys(dct1, dct2):
     161    return set(dct1.keys()).union(dct2.keys())
     162
     163class LockerAccessChangeEntry(object):
     164    def __init__(self, mit_id, verb, name, groups):
     165        self.mit_id = mit_id
     166        self.verb = verb
     167        self.name = name
     168        self.cac_msgs = ""
     169        self.group_msgs = ""
     170        self.groups = groups
     171
     172    def cac_format(self):
     173        return "%s\t%s\t%s\t%s" % (self.mit_id, self.verb, self.name, self.cac_msgs)
     174
     175    def group_format(self):
     176        return "%s\t%s\t%s" % (self.verb, self.name, self.group_msgs)
     177
     178def safe_add_change_real(change_by_name, change):
     179    name = change.name
     180    if name in change_by_name:
     181        if change_by_name[name].verb != change.verb or change_by_name[name].groups != change.groups:
     182            print "change_by_name=%s" % (change_by_name, )
     183            print "change: old=%s; new=%s" % (change_by_name[name], change)
     184            assert False
     185    else:
     186        change_by_name[name] = change
     187
     188def locker_access_diff(the_space, group_data, old_access, new_access, ):
     189    cac_msgs = [] # [String]
     190    for mit_id in joint_keys(old_access, new_access):
     191        change_by_name = {} # name -> LockerAccessChangeEntry
     192        def safe_add(change):
     193            safe_add_change_real(change_by_name, change)
     194        old_by_names = old_access[mit_id]
     195        new_by_names = new_access[mit_id]
     196        old_by_group = flip_dict(old_by_names)
     197        new_by_group = flip_dict(new_by_names)
     198        unchanged = (old_by_names == new_by_names)
     199        if unchanged: continue
     200        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        for group_pk in joint_keys(old_by_group, new_by_group):
     202            old_names = old_by_group[group_pk]
     203            new_names = new_by_group[group_pk]
     204            for name in old_names.union(new_names):
     205                changed_groups = old_by_names[name] ^ new_by_names[name]
     206
     207                def mkchange(verb):
     208                    change = LockerAccessChangeEntry(
     209                        mit_id=mit_id,
     210                        verb=verb,
     211                        name=name,
     212                        groups=changed_groups,
     213                    )
     214                    if verb == "Keep":
     215                        change.group_msgs = "(other groups involved)"
     216                    change.cac_msgs = "(groups: %s -> %s)" % (old_by_names[name], new_by_names[name])
     217                    return change
     218
     219                if name in old_names and name in new_names: # keep
     220                    safe_add(mkchange("Keep"))
     221                elif name in old_names: # remove from this group
     222                    if new_by_names[name]: # keep b/c other groups
     223                        safe_add(mkchange("Keep"))
     224                    else:
     225                        safe_add(mkchange("Remove"))
     226                elif name in new_names: # add for this group
     227                    if old_by_names[name]: # keep b/c other groups
     228                        safe_add(mkchange("Keep"))
     229                    else:
     230                        safe_add(mkchange("Add"))
     231                else:
     232                    assert False, "in old_names or new_names, but not in both, one, or the other..."
     233
     234        # Handle reporting the results...
     235        for change in change_by_name.values():
     236            cac_msgs.append(change.cac_format())
     237            group_msg = "%s\t%s" % (the_space.number, change.group_format())
     238            for group_pk in change.groups:
     239                group_data[group_pk].locker_messages.append(group_msg)
     240                group_data[group_pk].changes = True
     241
     242    return cac_msgs
     243
     244def space_specific_access(the_space, group_data, old_time, new_time, ):
     245    old_data = the_space.build_access(time=old_time)
     246    new_data = the_space.build_access(time=new_time)
     247    all_spaces[the_space.pk] = the_space
     248    init_groups(group_data, old_data[2])
     249    init_groups(group_data, new_data[2])
     250    for group_pk, group_info in group_data.items():
     251        if group_pk in old_data[0] or group_pk in new_data[0]:
     252            if the_space.merged_acl:
     253                group_info.add_locker_signatories(old_data[1], old_time)
     254                group_info.add_locker_signatories(new_data[1], new_time)
     255            else:
     256                group_info.learn_office_access(the_space.pk, old_data[0], new_data[0])
     257    cac_msgs = []
     258    if the_space.merged_acl:
     259        cac_msgs = locker_access_diff(the_space, group_data, old_data[1], new_data[1])
     260    return cac_msgs
    152261
    153262def space_access_diffs():
     
    156265    bulk_fill_people([old_time, new_time])
    157266    group_data = {} # Group.pk -> GroupInfo
     267    cac_locker_msgs = []
     268
     269    process_spaces =  space.models.Space.objects.all()
     270    for the_space in process_spaces:
     271        new_cac_msgs = space_specific_access(the_space, group_data, old_time, new_time)
     272        if new_cac_msgs:
     273            cac_locker_msgs.append("%s\n%s\n" % (the_space.number, "\n".join(new_cac_msgs)))
     274
    158275    changed_groups = []
    159     space_specific_access(group_data, old_time, new_time)
    160276    for group_pk, group_info in group_data.items():
    161277        group_info.add_office_signatories(old_time, new_time)
    162         changes, cac_changes, group_changes = group_info.list_changes()
    163         if changes:
    164             changed_groups.append((group_info.group, cac_changes, group_changes))
     278        cac_changes, group_office_changes = group_info.list_office_changes()
     279        if group_info.changes:
     280            changed_groups.append((group_info.group, cac_changes, group_office_changes, group_info.locker_messages, ))
    165281
    166282    asa_rcpts = ['asa-space@mit.edu', 'asa-db@mit.edu', ]
    167283    util.emails.email_from_template(
    168284        tmpl='space/cac-change-email.txt',
    169         context={'changed_groups': changed_groups},
    170         subject="Office access updates",
     285        context={'changed_groups': changed_groups, 'locker_msgs':cac_locker_msgs, },
     286        subject="Space access updates",
    171287        to=['caclocks@mit.edu'],
    172288        cc=asa_rcpts,
    173289    ).send()
    174     for group, cac_msg, group_msg in changed_groups:
     290    for group, cac_msg, group_office_msg, group_locker_msgs in changed_groups:
    175291        util.emails.email_from_template(
    176292            tmpl='space/group-change-email.txt',
    177293            context={
    178294                'group':group,
    179                 'msg':group_msg,
     295                'office_msg':group_office_msg,
     296                'locker_msgs':group_locker_msgs,
    180297            },
    181             subject="Office access updates",
     298            subject="[ASA DB] Space access updates",
    182299            to=[group.officer_email],
    183300            cc=asa_rcpts,
    184301        ).send()
    185        
     302
    186303
    187304if __name__ == "__main__":
  • asadb/space/import_into_db.py

    ra86a924 r5680065  
    2222        print "Ignoring missing group: %s: %s" % (line['group'], line, )
    2323        return
    24     the_space, created = space.models.Space.objects.get_or_create(number=line['office_number'])
     24    the_space, created = space.models.Space.objects.get_or_create(
     25        number=line['office_number'],
     26        defaults=dict(merged_acl=bool(line['locker_number'])),
     27    )
    2528    group = groups.models.Group.objects.get(id=group_id)
    2629    try:
  • asadb/space/models.py

    r94f3a39 r5680065  
    1414    number = models.CharField(max_length=20, unique=True, )
    1515    asa_owned = models.BooleanField(default=True, )
     16    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.")
    1617    notes = models.TextField(blank=True, )
    1718
     
    7071            aces = aces.filter(group=group)
    7172        access = {}    # Group.pk -> (ID -> Set name)
    72         access_by_id = {} # ID -> (Name -> (Set Group.pk))
     73        # ID -> (Name -> (Set Group.pk))
     74        access_by_id = collections.defaultdict(lambda: collections.defaultdict(set))
    7375        for assignment in assignments:
    7476            if assignment.group.pk not in access:
     
    7779            if ace.group.pk in access:
    7880                access[ace.group.pk][ace.card_number].add(ace.name)
    79                 if ace.card_number not in access_by_id:
    80                     access_by_id[ace.card_number] = collections.defaultdict(set)
    8181                access_by_id[ace.card_number][ace.name].add(ace.group.pk)
    8282            else:
  • asadb/template/space/cac-change-email.txt

    rbec7760 r5680065  
    66ASA Exec
    77
    8 {% for group, cac_changes, group_changes in changed_groups %}
     8{% if locker_msgs %}Locker room access:
     9{% for msg in locker_msgs %}
     10{{msg}}
     11{% endfor %}
     12{% endif %}
     13
     14
     15{% for group, cac_changes, group_office_changes, group_locker_changes in changed_groups %}
    916Group: {{group.name}} (ID#{{group.pk}})
    1017Officer email: {{group.officer_email}}
    1118
    12 {{cac_changes}}
     19{% if cac_changes %}{{cac_changes}}
    1320
    14 
    15 {% endfor %}{% endautoescape %}
     21{% endif %}
     22{% endfor %}
     23{% endautoescape %}
  • asadb/template/space/group-change-email.txt

    rbec7760 r5680065  
    11{% autoescape off %}
    22Hi {{group.name}},
    3     Thank you for updating office access on the ASA database today. We've forwarded the following changes on to CAC:
     3    Thank you for updating space access on the ASA database today. We've forwarded the following changes on to CAC:
    44
    5 {{msg}}
     5{% if office_msg %}
     6Office access:
     7
     8{{office_msg}}
     9{% endif %}{% if locker_msgs %}
     10Locker (shared storage) access:
     11{% for msg in locker_msgs %}
     12{{msg}}{% endfor %}
     13{% endif %}
     14
     15If you believe these changes are incorrect in any way, please let us know --- beyond just notifying CAC, we also want to find out why the errors happened.
    616
    717Thanks,
Note: See TracChangeset for help on using the changeset viewer.