-
Notifications
You must be signed in to change notification settings - Fork 1
/
checkClashesExcel.py
134 lines (115 loc) · 4.42 KB
/
checkClashesExcel.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
"""
Read an Excel file containing multiple subject lists, as exported by StaffOnline
and report all students doing combinations of subjects & number who have those
"""
import openpyxl
SHEET_NAME = "Sheet1"
__author__ = 'sci-lmw1'
FILENAME = 'data/AllCPstudentsSP22016.xlsx' # Note: Files must be in the (newer) XLSX format.
# FILENAME = 'Classlists.xlsx'
CAMPUSES = ["TSV"] # ["CNS", "TSV"]
MAX_COMBO = 2 # maximum size of subject combination to check
def main():
students, subjects = get_students_subjects()
combinations = make_combinations(subjects)
# print(combinations)
print_clashes(combinations, students)
# print_class_lists(students, subjects)
def print_subjects(subjects):
"""
print a set/frozenset of subjects nicely, sorted
:param subjects: set/frozenset of strings
"""
subjects_to_print = list(subjects)
subjects_to_print.sort()
print(", ".join(subjects_to_print))
def print_clashes(combinations, students):
"""
Print all students who have clashes with any combination of subjects, by campus
:param combinations: list of combinations of multiple subjects
:param students: dictionary of student data containing what subjects they take
"""
for combo in combinations:
print_subjects(combo)
for campus in CAMPUSES:
print(campus, end=": ")
students_to_print = []
count = 0
for student in students[campus]:
# save/print student if they do ALL of the subjects in the combo
if [does_subject in students[campus][student] for does_subject in combo].count(False) == 0:
students_to_print.append(student)
count += 1
print(count, "-", ", ".join(students_to_print))
print()
def print_class_lists(students, subjects):
"""
print class lists by campus
:param students: dictionary of student data
:param subjects: list of all subjects
:return:
"""
for subject in subjects:
print(subject)
for campus in CAMPUSES:
print(campus, end=": ")
for student in students[campus]:
if subject in students[campus][student]:
print(student, end=' ')
print()
def get_students_subjects():
"""
Read Excel class list file and generate dictionary of students and list of subjects
:return: students (dictionary containing subjects they do) and subjects (list)
"""
class_workbook = openpyxl.load_workbook(FILENAME)
class_sheet = class_workbook.get_sheet_by_name(SHEET_NAME)
# map student names to list of subjects in a dictionary with items for each campus
students = {"CNS": {}, "TSV": {}}
subjects = []
# first row is header, last row is total
for i in range(1, class_sheet.max_row - 1):
# convert cells to text in those cells (.value)
cell_text = [cell.value for cell in class_sheet.rows[i]]
# print(cell_text)
if cell_text[2] is None:
cell_text[2] = ""
name = "{} {}".format(cell_text[2], cell_text[1])
subject = cell_text[11]
campus = cell_text[12]
# print(name, subject, campus)
# build list of unique subjects
if subject not in subjects:
subjects.append(subject)
# add student to dictionary
if name not in students[campus]:
students[campus][name] = [subject]
else:
students[campus][name].append(subject)
return students, subjects
def make_combinations(values):
"""
Get all combinations of _multiple_ values within a set of values
technique based on https://en.wikipedia.org/wiki/Combination#Enumerating_k-combinations
:param values: a list of values
:return: list of all combinations
"""
combinations = {frozenset(values)}
# print(combinations)
n = len(values)
# loop through all relevant bit strings (non-zero, non-all-1's)
for i in range(1, 2 ** n - 1):
# print(bin(i))
# print(str(bin(i)))
bit_string = "{:0{}b}".format(i, n)
count_ones = bit_string.count('1')
if 1 < count_ones <= MAX_COMBO:
# print(bit_string)
combo = set()
for j, char in enumerate(bit_string):
if char == '1':
combo.add(values[j])
combinations.add(frozenset(combo))
return combinations
# print(len(combinations), combinations)
main()