def build_days(self,days,timestr): # merge days by term/week inweek = collections.defaultdict(set) for (term,week,day) in days: inweek[(term,week)].add(day_names[day]) # flip the map around so index is by day set outweek = collections.defaultdict(set) for ((term,week),days) in inweek.iteritems(): daystr = "".join(sorted(list(days),reverse=True)) outweek[daystr].add((term,week)) # For each day combination build term/week specs twout = collections.defaultdict(str) for (daystr,termweeks) in outweek.iteritems(): for term in range(0,3): weeks = set() for (term2,week) in termweeks: if term == term2: weeks.add(week) if len(weeks): week_str = util.number_range_text(weeks) twout[int(term)*100+int(min(weeks))] += "%s%s %s" % (term_names[term],week_str,daystr) out = FullPattern() for k in sorted(twout.keys()): out.addOne("%s%s" % (twout[k],timestr)) return out
def update_details(path,cid,rs): logger = logging.getLogger('indium') changed = False for rv in rs: if rv.changedp(): changed = True break if not changed: return logger.info("updating %s" % cid) old_det = load_or_new_details(path,cid) new_det = old_det.new_same_header() # Index values by eid and take copy as orig els = {} orig_els = {} for g in old_det.getGroups(): for e in g.elements: id = e.eid(g.term) els[id] = copy.deepcopy(e) orig_els[id] = e # Change values for rv in rs: # We need the orgi as only changes to rectangles should be propagated to the element to avoid the # risk of reversion if multiple rectangles map to an element and only early rectangles change. orig = orig_els[rv.key] new = els[rv.key] # what, where, who rv.update_to_element(new,orig) # did it move? if rv.saved_dt != rv.dt: # Remove the corresponding old daytimes for all patterns for this rectangle tws = [] wout = FullPattern() for p in new.when.each(): hit = p.removeDayTimeRangeDirect(rv.saved_dt) wout.addOne(p) if hit: # derive term/weeks from deleted patterns tws.append(p) # Add in new daytime newp = patternatom.PatternAtom(False) newp.addDayTimeRangeDirect(rv.dt) for p in tws: newp.addTermWeeksFrom(p) wout.addOne(newp) new.when = wout # Populate based on changed values for (eid,e) in els.iteritems(): new_det.addRow(element.Element(e.who,e.what,e.where,FullPattern(e.when),e.merge,e.type,new_det.name)) if old_det.to_json(True) != new_det.to_json(True): print " saving" det_fn = os.path.join(path,"details_%s.json" % cid) j = new_det.to_json() # outside open to allow exceptions c = open(det_fn,"wb") json.dump(j,c) c.close() return True else: logger.debug(" didn't seem to change") return False
def __init__(self, dt, course, type, key, order, merged, rcodehash=None): self.dt = dt self.type = type self._termweeks = [[], [], []] self._course = course self.meta = element.Element(type=type, course=course.cname) self.key = key self._patterns = FullPattern() self.order = order self._merged = merged self.text = None self.saved_dt = None self.rcodehash = rcodehash
def to_json(self): when = FullPattern( self.meta.when) if self.meta.specified('when') else '' rcode = "%s%d%2.2d%2.2d%2.2d%2.2d" % \ ('Y' if self._merged else 'X',self.dt.day,self.dt.start[0],self.dt.start[1],self.dt.end[0],self.dt.end[1]) out = { "day": self.dt.day, "starttime": self.dt.start, "endtime": self.dt.end, "cid": self._course.cid, "rid": rcode, "text": self.text if self.text is not None else self.meta.what, "cname": self._course.cname, "what": self.meta.what, "organiser": self.meta.who, "where": self.meta.where, "type": self.type, "termweeks": self._termweeks, "code": self.key, "when": str(when) } rcodehash = datahash.datahash(out) out['rid'] = "%s:%s:%s" % (rcodehash, out['rid'], util.rndid( rcode, "R")) return out
def process_new_rectangle(path, caldata, r): print "New rectangle!" # identify cid cid = None for c in caldata.courses.itervalues(): if c['name'] == r['cname']: cid = c['id'] if cid is None: raise Exception("Unknown course") c = course.Course(cid, r['cname']) # load relevant details file det = load_or_new_details(path, cid) # build element el = element.Element(r['organiser'], r['what'], r['where'], FullPattern(r['when']), False, r['type'], c) # add to groups for term in range(0, 3): e = copy.deepcopy(el) if e.restrictToTerm(term): g = det.getGroup(r['type'], term) g.group.append(e) # save details det_fn = os.path.join(path, "details_%s.json" % cid) j = det.to_json() # outside open to allow exceptions c = open(det_fn, "wb") json.dump(j, c) c.close() return cid
def __init__(self,dt,course,type,key,order,merged,rcodehash = None): self.dt = dt self.type = type self._termweeks = [[],[],[]] self._course = course self.meta = element.Element(type = type,course = course.cname) self.key = key self._patterns = FullPattern() self.order = order self._merged = merged self.text = None self.saved_dt = None self.rcodehash = rcodehash
def from_json(data, order): dt = daytime.DayTimeRange(data['day'], data['starttime'], data['endtime']) c = course.Course(data['cid'], data['cname']) (rcodehash, rid, _) = data['rid'].split(':') merge = rid[0] == 'Y' if 'rid' in data else False # Is this correct? out = Rectangle(dt, c, data['type'], data['code'], order, merge, rcodehash) out._termweeks = data['termweeks'] el = element.Element(what=data['what'], where=data['where'], who=data['organiser'], when=FullPattern(data['when']), type=data['type'], course=data['cname'], merge=merge) out.meta.additional(el) out._patterns = FullPattern(data['when']) # Extract saved daytime out.saved_dt = daytime.DayTimeRange(int(rid[1]), int(rid[2:4]), int(rid[4:6]), int(rid[6:8]), int(rid[8:10])) return out
def merge(self, mergeState): # Fakes for cid in mergeState.courseIds: print >> sys.stderr, "MISSING %s" % cid ds = details.Details(cid, mergeState.names[cid], "Example organiser", "Example location", { "notes": "", "course-description": "" }) for term in ['Michaelmas', 'Lent', 'Easter']: for type in ['Lecture', 'Practical']: ds.addRow( element.Element( "Example person", mergeState.names[cid], "Example location", FullPattern(term[:2] + ' ' + self.fake_time()), False, type, mergeState.names[cid])) filepaths.saveDetailFile(ds.to_json(), cid)
class Rectangle(object): default_metadata = ['notes','course-description','special-instructions'] def __init__(self,dt,course,type,key,order,merged,rcodehash = None): self.dt = dt self.type = type self._termweeks = [[],[],[]] self._course = course self.meta = element.Element(type = type,course = course.cname) self.key = key self._patterns = FullPattern() self.order = order self._merged = merged self.text = None self.saved_dt = None self.rcodehash = rcodehash def addEvent(self,termweeks,elements,pattern): for (term,week) in termweeks.each(): self._termweeks[term].append(week) self.meta.additional(elements) self._patterns.addOne(pattern) def __repr__(self): return "<dt=%s type=%s course=%s termweeks=%s>" % (self.dt.data(),self.type,self._course.cid,self._termweeks) def to_json(self): when = FullPattern(self.meta.when) if self.meta.specified('when') else '' rcode = "%s%d%2.2d%2.2d%2.2d%2.2d" % \ ('Y' if self._merged else 'X',self.dt.day,self.dt.start[0],self.dt.start[1],self.dt.end[0],self.dt.end[1]) out = { "day": self.dt.day, "starttime": self.dt.start, "endtime": self.dt.end, "cid": self._course.cid, "rid": rcode, "text": self.text if self.text is not None else self.meta.what, "cname": self._course.cname, "what": self.meta.what, "organiser": self.meta.who, "where": self.meta.where, "type": self.type, "termweeks": self._termweeks, "code": self.key, "when": str(when) } rcodehash = datahash.datahash(out) out['rid'] = "%s:%s:%s" % (rcodehash,out['rid'],util.rndid(rcode,"R")) return out def changedp(self): if self.rcodehash is None: return False cmp = copy.deepcopy(self.to_json()) cmp['rid'] = cmp['rid'].split(":")[1] calced = datahash.datahash(cmp) return self.rcodehash != calced def update_to_element(self,el,orig): el.update_with(self.meta,orig) def add_rectangles_from_all(self,rs): for t in range(0,3): v = set(self._termweeks[t]) for r in rs: v |= set(r._termweeks[t]) self._termweeks[t] = sorted(list(v)) @staticmethod def from_json(data,order): dt = daytime.DayTimeRange(data['day'],data['starttime'],data['endtime']) c = course.Course(data['cid'],data['cname']) (rcodehash,rid,_) = data['rid'].split(':') merge = rid[0] == 'Y' if 'rid' in data else False # Is this correct? out = Rectangle(dt,c,data['type'],data['code'],order,merge,rcodehash) out._termweeks = data['termweeks'] el = element.Element(what = data['what'], where = data['where'], who = data['organiser'], when = FullPattern(data['when']), type = data['type'],course = data['cname'],merge = merge) out.meta.additional(el) out._patterns = FullPattern(data['when']) # Extract saved daytime out.saved_dt = daytime.DayTimeRange(int(rid[1]),int(rid[2:4]),int(rid[4:6]),int(rid[6:8]),int(rid[8:10])) return out def generate_new_details(self): metadata = {} for key in Rectangle.default_metadata: metadata[key] = "" return details.Details(self.course.cid,self.meta.what,self.meta.who,self.meta.where,metadata) def generate_direct_pattern(self,term): p = patternatom.PatternAtom(False) for w in self._termweeks[term]: p.addTermWeek(term,w) p.addDayTimeRangeDirect(self.dt) return p
class Rectangle(object): default_metadata = ['notes', 'course-description', 'special-instructions'] def __init__(self, dt, course, type, key, order, merged, rcodehash=None): self.dt = dt self.type = type self._termweeks = [[], [], []] self._course = course self.meta = element.Element(type=type, course=course.cname) self.key = key self._patterns = FullPattern() self.order = order self._merged = merged self.text = None self.saved_dt = None self.rcodehash = rcodehash def addEvent(self, termweeks, elements, pattern): for (term, week) in termweeks.each(): self._termweeks[term].append(week) self.meta.additional(elements) self._patterns.addOne(pattern) def __repr__(self): return "<dt=%s type=%s course=%s termweeks=%s>" % ( self.dt.data(), self.type, self._course.cid, self._termweeks) def to_json(self): when = FullPattern( self.meta.when) if self.meta.specified('when') else '' rcode = "%s%d%2.2d%2.2d%2.2d%2.2d" % \ ('Y' if self._merged else 'X',self.dt.day,self.dt.start[0],self.dt.start[1],self.dt.end[0],self.dt.end[1]) out = { "day": self.dt.day, "starttime": self.dt.start, "endtime": self.dt.end, "cid": self._course.cid, "rid": rcode, "text": self.text if self.text is not None else self.meta.what, "cname": self._course.cname, "what": self.meta.what, "organiser": self.meta.who, "where": self.meta.where, "type": self.type, "termweeks": self._termweeks, "code": self.key, "when": str(when) } rcodehash = datahash.datahash(out) out['rid'] = "%s:%s:%s" % (rcodehash, out['rid'], util.rndid( rcode, "R")) return out def changedp(self): if self.rcodehash is None: return False cmp = copy.deepcopy(self.to_json()) cmp['rid'] = cmp['rid'].split(":")[1] calced = datahash.datahash(cmp) return self.rcodehash != calced def update_to_element(self, el, orig): el.update_with(self.meta, orig) def add_rectangles_from_all(self, rs): for t in range(0, 3): v = set(self._termweeks[t]) for r in rs: v |= set(r._termweeks[t]) self._termweeks[t] = sorted(list(v)) @staticmethod def from_json(data, order): dt = daytime.DayTimeRange(data['day'], data['starttime'], data['endtime']) c = course.Course(data['cid'], data['cname']) (rcodehash, rid, _) = data['rid'].split(':') merge = rid[0] == 'Y' if 'rid' in data else False # Is this correct? out = Rectangle(dt, c, data['type'], data['code'], order, merge, rcodehash) out._termweeks = data['termweeks'] el = element.Element(what=data['what'], where=data['where'], who=data['organiser'], when=FullPattern(data['when']), type=data['type'], course=data['cname'], merge=merge) out.meta.additional(el) out._patterns = FullPattern(data['when']) # Extract saved daytime out.saved_dt = daytime.DayTimeRange(int(rid[1]), int(rid[2:4]), int(rid[4:6]), int(rid[6:8]), int(rid[8:10])) return out def generate_new_details(self): metadata = {} for key in Rectangle.default_metadata: metadata[key] = "" return details.Details(self.course.cid, self.meta.what, self.meta.who, self.meta.where, metadata) def generate_direct_pattern(self, term): p = patternatom.PatternAtom(False) for w in self._termweeks[term]: p.addTermWeek(term, w) p.addDayTimeRangeDirect(self.dt) return p
def update_details(path, cid, rs): logger = logging.getLogger('indium') changed = False for rv in rs: if rv.changedp(): changed = True break if not changed: return logger.info("updating %s" % cid) old_det = load_or_new_details(path, cid) new_det = old_det.new_same_header() # Index values by eid and take copy as orig els = {} orig_els = {} for g in old_det.getGroups(): for e in g.elements: id = e.eid(g.term) els[id] = copy.deepcopy(e) orig_els[id] = e # Change values for rv in rs: # We need the orgi as only changes to rectangles should be propagated to the element to avoid the # risk of reversion if multiple rectangles map to an element and only early rectangles change. orig = orig_els[rv.key] new = els[rv.key] # what, where, who rv.update_to_element(new, orig) # did it move? if rv.saved_dt != rv.dt: # Remove the corresponding old daytimes for all patterns for this rectangle tws = [] wout = FullPattern() for p in new.when.each(): hit = p.removeDayTimeRangeDirect(rv.saved_dt) wout.addOne(p) if hit: # derive term/weeks from deleted patterns tws.append(p) # Add in new daytime newp = patternatom.PatternAtom(False) newp.addDayTimeRangeDirect(rv.dt) for p in tws: newp.addTermWeeksFrom(p) wout.addOne(newp) new.when = wout # Populate based on changed values for (eid, e) in els.iteritems(): new_det.addRow( element.Element(e.who, e.what, e.where, FullPattern(e.when), e.merge, e.type, new_det.name)) if old_det.to_json(True) != new_det.to_json(True): print " saving" det_fn = os.path.join(path, "details_%s.json" % cid) j = new_det.to_json() # outside open to allow exceptions c = open(det_fn, "wb") json.dump(j, c) c.close() return True else: logger.debug(" didn't seem to change") return False