from itertools import islice from functools import partial import re from collections import defaultdict import matplotlib.pyplot as plt from matplotlib.ticker import FuncFormatter fields = ( 'session_id', 'session_year', 'session_month', 'group_full', 'parallel', 'group', 'is_not_student', 'job_id', 'person_id', 'grade' ) aliases = { 'session_id': ['session'], 'session_year': ['year'], 'session_month': ['month'], 'job_id': ['job'], 'person_id': ['person'], } parallels = "T W K S P A0 A A' B B' C C' D E F M".split() parallel_colors = { "T": [192, 192, 192], "W": [176, 176, 176], "K": [160, 160, 160], "S": [144, 144, 144], "P": [0, 128, 128], "A0": [0, 0, 0], "A": [165, 42, 42], "A'": [255, 0, 0], "B": [255, 128, 64], "B'": [255, 255, 0], "C": [0, 255, 0], "C'": [78, 146, 88], "D": [0, 0, 255], "E": [21, 137, 255], "F": [59, 156, 156], "M": [255, 0, 255], } for color in parallel_colors.values(): for i in range(len(color)): color[i] /= 255 grade_colors = { 5: 'cyan', 6: 'lime', 7: 'yellow', 8: 'orange', 9: 'red', 10: 'brown', 11: 'black', } parallel_levels = { "F": 0, "E": 1, "M": 1, "D": 2, "C'": 3, "C": 4, "B'": 5, "B": 6, "A'": 7, "A": 8, "A0": 9, "Z": 9, "prep": 10, } level_names = defaultdict(list) for parallel, level in parallel_levels.items(): level_names[level].append(parallel) for parallel, level in tuple(parallel_levels.items()): if parallel != 'prep': parallel_levels[parallel + '+'] = level class HistoryEntry: def __init__(self, row, winter_levels = False, local_levels = None): self.__winter_levels = winter_levels self.__levels = parallel_levels.copy() if local_levels is not None: for parallel, level in local_levels.items(): self.__levels[parallel] = level if parallel != 'prep': self.__levels[parallel + '+'] = level for key, value in zip(fields, row.strip().split('\t')): if re.search(r'^\d+$', value): value = int(value) elif value in ('', '?'): value = None if key == 'grade' and value == 0: value = None for alias in aliases.get(key, []) + [key]: setattr(self, alias, value) @property def level(self): if self.prep: return self.__levels['prep'] elif self.parallel_short and (self.month != 12 or self.__winter_levels): return self.__levels.get(self.parallel_short, None) else: return None @property def prep_level(self): if self.parallel_short and (self.month != 12 or self.__winter_levels): return self.__levels.get(self.parallel_short, None) else: return None @property def winter(self): return self.session_month == 12 @property def student(self): return not self.is_not_student @property def prep(self): return self.is_not_student and self.job_id in (0, 19) @property def parallel_short(self): if self.parallel is None: return None elif self.parallel == 'Z': return 'A0' parallel = re.search(r'^([^.+]+)(?:[.+])?', self.parallel).groups()[0] if parallel in parallels: return parallel else: return None def __str__(self): return str({field: getattr(self, field) for field in fields}) def __repr__(self): return str(self) DATA_FILE = 'sis_data.tsv' def history(count = None, file = DATA_FILE, winter_levels = False, levels = None): return map(partial(HistoryEntry, winter_levels = winter_levels, local_levels = levels), islice(open(file), count)) plt.rc('font', family = 'verdana') level_formatter = FuncFormatter(lambda level, pos: '/'.join(level_names[round(level)])) summer_session_formatter = FuncFormatter(lambda session, pos: { 18: '2010.July', 19: '2010.August', 21: '2011.July', 22: '2011.August', 24: '2012.July', 25: '2012.August', 27: '2013.July', 28: '2013.August', }.get(session, ''))