1 | #!/usr/bin/python |
---|
2 | import collections |
---|
3 | import datetime |
---|
4 | import os |
---|
5 | import sys |
---|
6 | |
---|
7 | if __name__ == '__main__': |
---|
8 | cur_file = os.path.abspath(__file__) |
---|
9 | django_dir = os.path.abspath(os.path.join(os.path.dirname(cur_file), '..')) |
---|
10 | sys.path.append(django_dir) |
---|
11 | os.environ['DJANGO_SETTINGS_MODULE'] = 'settings' |
---|
12 | |
---|
13 | from django.core.mail import EmailMessage |
---|
14 | from django.db import connection |
---|
15 | from django.db.models import Q |
---|
16 | from django.template import Context, Template |
---|
17 | from django.template.loader import get_template |
---|
18 | |
---|
19 | import groups.diffs |
---|
20 | import groups.models |
---|
21 | import space.models |
---|
22 | import util.emails |
---|
23 | |
---|
24 | role = { |
---|
25 | 'office': groups.models.OfficerRole.objects.get(slug='office-access') |
---|
26 | } |
---|
27 | |
---|
28 | people_name = {} # username -> full name |
---|
29 | people_id = {} # username -> MIT ID |
---|
30 | |
---|
31 | all_spaces = {} # Space.pk -> Space |
---|
32 | |
---|
33 | def bulk_fill_people(times): |
---|
34 | max_time = max(times) |
---|
35 | min_time = min(times) |
---|
36 | active_holders = groups.models.OfficeHolder.objects.filter( |
---|
37 | start_time__lte=max_time, |
---|
38 | end_time__gte=min_time, |
---|
39 | role__in=role.values(), |
---|
40 | ) |
---|
41 | usernames = active_holders.values_list('person', flat=True,) |
---|
42 | people = groups.models.AthenaMoiraAccount.objects.filter(username__in=usernames) |
---|
43 | for person in people: |
---|
44 | people_name[person.username] = person.format() |
---|
45 | people_id[person.username] = person.mit_id |
---|
46 | |
---|
47 | def fill_people(holder): |
---|
48 | if not holder.person in people_name: |
---|
49 | #print "Person %s not pre-cached" % (holder.person, ) |
---|
50 | try: |
---|
51 | person = groups.models.AthenaMoiraAccount.objects.get(username=holder.person) |
---|
52 | people_name[holder.person] = person.format() |
---|
53 | people_id[holder.person] = person.mit_id |
---|
54 | except groups.models.AthenaMoiraAccount.DoesNotExist: |
---|
55 | people_name[holder.person] = "<%s>" % (holder.person, ) |
---|
56 | people_id[holder.person] = None |
---|
57 | |
---|
58 | class GroupInfo(object): |
---|
59 | def __init__(self, group, ): |
---|
60 | self.group = group |
---|
61 | 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): |
---|
66 | group_pk = self.group.pk |
---|
67 | if group_pk in old: |
---|
68 | old_access = old[group_pk] |
---|
69 | else: old_access = {} |
---|
70 | if group_pk in new: |
---|
71 | new_access = new[group_pk] |
---|
72 | else: new_access = {} |
---|
73 | assert space_pk not in self.offices |
---|
74 | |
---|
75 | # Let's fill out the self.offices set. |
---|
76 | self.offices[space_pk] = collections.defaultdict(lambda: (set(), set())) |
---|
77 | space_data = self.offices[space_pk] |
---|
78 | for mit_id, old_set in old_access.items(): |
---|
79 | space_data[mit_id][0].update(old_set) |
---|
80 | for mit_id, new_set in new_access.items(): |
---|
81 | space_data[mit_id][1].update(new_set) |
---|
82 | |
---|
83 | def add_office_signatories_per_time(self, ind, time): |
---|
84 | group = self.group |
---|
85 | people = group.officers(as_of=time, role=role['office']) |
---|
86 | for holder in people: |
---|
87 | fill_people(holder) |
---|
88 | for office_id, office_data in self.offices.items(): |
---|
89 | for holder in people: |
---|
90 | holder_name = people_name[holder.person] |
---|
91 | holder_id = people_id[holder.person] |
---|
92 | office_data[holder_id][ind].add(holder_name) |
---|
93 | |
---|
94 | def add_office_signatories(self, old_time, new_time, ): |
---|
95 | group = self.group |
---|
96 | self.add_office_signatories_per_time(0, old_time) |
---|
97 | self.add_office_signatories_per_time(1, new_time) |
---|
98 | |
---|
99 | def list_changes(self, ): |
---|
100 | cac_lines = [] |
---|
101 | group_lines = [] |
---|
102 | def append_change(mit_id, verb, name): |
---|
103 | cac_lines.append("%s:\t%s:\t%s" % (mit_id, verb, name)) |
---|
104 | group_lines.append("%s:\t%s" % (verb, name)) |
---|
105 | changes = False |
---|
106 | for space_pk, space_data in self.offices.items(): |
---|
107 | line = "Changes in %s:" % (all_spaces[space_pk].number, ) |
---|
108 | cac_lines.append(line) |
---|
109 | group_lines.append(line) |
---|
110 | for mit_id, (old_names, new_names) in space_data.items(): |
---|
111 | if mit_id is None: mit_id = "ID unknown" |
---|
112 | if old_names == new_names: |
---|
113 | pass |
---|
114 | else: |
---|
115 | changes = True |
---|
116 | for name in old_names: |
---|
117 | if name in new_names: |
---|
118 | append_change(mit_id, "Keep", name) |
---|
119 | else: |
---|
120 | append_change(mit_id, "Remove", name) |
---|
121 | for name in new_names: |
---|
122 | if name in old_names: |
---|
123 | pass |
---|
124 | else: |
---|
125 | append_change(mit_id, "Add", name) |
---|
126 | cac_lines.append("") |
---|
127 | group_lines.append("") |
---|
128 | |
---|
129 | cac_msg = "\n".join(cac_lines) |
---|
130 | group_msg = "\n".join(group_lines) |
---|
131 | return changes, cac_msg, group_msg |
---|
132 | |
---|
133 | def init_groups(the_groups, assignments): |
---|
134 | for assignment in assignments: |
---|
135 | group = assignment.group |
---|
136 | if group.id not in the_groups: |
---|
137 | the_groups[group.id] = GroupInfo(group) |
---|
138 | |
---|
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[1]) |
---|
147 | init_groups(group_data, new_data[1]) |
---|
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 | |
---|
152 | |
---|
153 | def space_access_diffs(): |
---|
154 | new_time = datetime.datetime.utcnow() |
---|
155 | old_time = new_time - datetime.timedelta(days=1) |
---|
156 | bulk_fill_people([old_time, new_time]) |
---|
157 | group_data = {} # Group.pk -> GroupInfo |
---|
158 | changed_groups = [] |
---|
159 | space_specific_access(group_data, old_time, new_time) |
---|
160 | for group_pk, group_info in group_data.items(): |
---|
161 | 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)) |
---|
165 | |
---|
166 | asa_rcpts = ['asa-space@mit.edu', 'asa-db@mit.edu', ] |
---|
167 | util.emails.email_from_template( |
---|
168 | tmpl='space/cac-change-email.txt', |
---|
169 | context={'changed_groups': changed_groups}, |
---|
170 | subject="Office access updates", |
---|
171 | to=['caclocks@mit.edu'], |
---|
172 | cc=asa_rcpts, |
---|
173 | ).send() |
---|
174 | for group, cac_msg, group_msg in changed_groups: |
---|
175 | util.emails.email_from_template( |
---|
176 | tmpl='space/group-change-email.txt', |
---|
177 | context={ |
---|
178 | 'group':group, |
---|
179 | 'msg':group_msg, |
---|
180 | }, |
---|
181 | subject="Office access updates", |
---|
182 | to=[group.officer_email], |
---|
183 | cc=asa_rcpts, |
---|
184 | ).send() |
---|
185 | |
---|
186 | |
---|
187 | if __name__ == "__main__": |
---|
188 | space_access_diffs() |
---|