def __call__(self): term = getTermForDate(self.event.date) if term is None: return old_date = self.event.date new_date = self.event.replacement_date schoolyear = ISchoolYear(term) timetables = ITimetableContainer(schoolyear) for timetable in timetables.values(): if IScheduleExceptions.providedBy(timetable): modified = False scheduled = DateRange(timetable.first, timetable.last) meeting_exceptions = PersistentList() if old_date in scheduled: meetings = list(timetable.iterMeetings(old_date)) for meeting in meetings: meeting_exceptions.append( MeetingException( meeting.dtstart.replace(year=new_date.year, month=new_date.month, day=new_date.day), meeting.duration, period=meeting.period, meeting_id=meeting.meeting_id)) timetable.exceptions[old_date] = PersistentList() modified = True if new_date in scheduled: timetable.exceptions[new_date] = meeting_exceptions modified = True if modified: zope.lifecycleevent.modified(timetable)
def export_school_timetables(self, wb): self.task_progress.force('export_school_timetables', active=True) ws = wb.add_sheet("School Timetables") school_years = sorted(ISchoolYearContainer(self.context).values(), key=lambda s: s.first) row = 0 for ny, school_year in enumerate(sorted(school_years, key=lambda i: i.last)): timetables = ITimetableContainer(school_year) for nt, timetable in enumerate(sorted(timetables.values(), key=lambda i: i.__name__)): row = self.format_school_timetable(timetable, ws, row) + 1 self.progress('export_school_timetables', normalized_progress( ny, len(school_years), nt, len(timetables))) self.finish('export_school_timetables')
def setUpTimetables(): app = ISchoolToolApplication(None) TimetableStartUp(app)() syc = ISchoolYearContainer(app) sy = syc.getActiveSchoolYear() timetables = ITimetableContainer(sy) timetables[u'rotating'] = tt_rot = Timetable(sy.first, sy.last, title=u"Rotating") tt_rot.periods = CalendarDayTemplates() initTemplates(tt_rot.periods) tt_rot.time_slots = CalendarDayTemplates() initTemplates(tt_rot.time_slots) addTimetableDays(tt_rot, [('1', 'Day 1'), ('2', 'Day 2'), ('3', 'Day 3')], ['A', 'B', 'C']) timetables[u'weekly'] = tt_week = Timetable(sy.first, sy.last, title=u"Weekly") tt_week.periods = WeekDayTemplates() initTemplates(tt_week.periods) tt_week.time_slots = WeekDayTemplates() initTemplates(tt_week.time_slots) dows = [ 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday' ] week_days = [(unicode(n), title) for n, title in enumerate(dows)] addTimetableDays(tt_week, week_days, ['A', 'B', 'C'])
def export_school_timetables(self, wb): self.task_progress.force('export_school_timetables', active=True) ws = wb.add_sheet("School Timetables") school_years = sorted(ISchoolYearContainer(self.context).values(), key=lambda s: s.first) row = 0 for ny, school_year in enumerate( sorted(school_years, key=lambda i: i.last)): timetables = ITimetableContainer(school_year) for nt, timetable in enumerate( sorted(timetables.values(), key=lambda i: i.__name__)): row = self.format_school_timetable(timetable, ws, row) + 1 self.progress( 'export_school_timetables', normalized_progress(ny, len(school_years), nt, len(timetables))) self.finish('export_school_timetables')
def updateSection(self, section, term, course, instructor, periods, dry_run=True): """Create a section. `periods` is a list of tuples (day_id, period_id). A title is generated from the titles of `course` and `instructor`. If an existing section with the same title is found, it is used instead of creating a new one. The created section is returned, or None if dry_run is True. """ if dry_run: return None # Establish links to course and to teacher for c in list(section.courses): if c is not course: section.remove(c) if course not in section.courses: section.courses.add(course) for i in list(section.instructors): if i is not instructor: section.instructor.remove(i) if instructor not in section.instructors: section.instructors.add(instructor) timetable_container = ITimetableContainer(self.schoolyear) timetables = [timetable_container[ttid] for ttid in sorted(periods)] schedules = IScheduleContainer(section) for timetable in timetables: selected = periods[timetable.__name__] schedule = None for s in schedules.values(): if sameProxiedObjects(s.timetable, timetable): schedule = s break if schedule is None: schedule = SelectedPeriodsSchedule(timetable, term.first, term.last, title=timetable.title, timezone=timetable.timezone) for period in selected: schedule.addPeriod(period) schedules[timetable.__name__] = schedule else: for period in schedule.periods: if period not in selected: schedule.removePeriod(period) for period in selected: schedule.addPeriod(period)
def importAllTimetables(self): if not self.shouldImportAllTimetables(): return oldTimetables = ITimetableContainer(self.activeSchoolyear) newTimetables = ITimetableContainer(self.newSchoolyear) chooser = INameChooser(newTimetables) app = ISchoolToolApplication(None) tzname = IApplicationPreferences(app).timezone for schooltt in oldTimetables.values(): newSchooltt = Timetable( self.newSchoolyear.first, self.newSchoolyear.last, title=schooltt.title, timezone=tzname) name = chooser.chooseName(schooltt.__name__, newSchooltt) newTimetables[name] = newSchooltt self.setUpTimetable(newSchooltt, schooltt) if (oldTimetables.default is not None and sameProxiedObjects(oldTimetables.default, schooltt)): newTimetables.default = newSchooltt
def createSection(self, term, course, instructor, periods, section_id=None, dry_run=True): """Create a section. `periods` is a list of tuples (day_id, period_id). A title is generated from the titles of `course` and `instructor`. If an existing section with the same title is found, it is used instead of creating a new one. The created section is returned, or None if dry_run is True. """ if dry_run: return None sections = ISectionContainer(term) section = Section() chooser = INameChooser(sections) auto_name = chooser.chooseName('', section) section.title = u"%s (%s)" % (course.title, auto_name) if section_id is None: section_id = auto_name sections[section_id] = section # Establish links to course and to teacher if course not in section.courses: section.courses.add(course) if instructor not in section.instructors: section.instructors.add(instructor) timetable_container = ITimetableContainer(self.schoolyear) timetables = [timetable_container[ttid] for ttid in sorted(periods)] schedules = IScheduleContainer(section) for timetable in timetables: selected = periods[timetable.__name__] schedule = SelectedPeriodsSchedule(timetable, term.first, term.last, title=timetable.title, timezone=timetable.timezone) for period in selected: schedule.addPeriod(period) schedules[timetable.__name__] = schedule return section
def expand(self, start, end): app = ISchoolToolApplication(None) school_timetables = ITimetableContainer(app, None) if school_timetables is None or school_timetables.default is None: return [] calendar = ImmutableScheduleCalendar(school_timetables.default) events = [] events.extend(calendar.expand(start, end)) timetable_calendar = ImmutableCalendar(events) resource_calendars = getSelectedResourceCalendars(self.request) booking_calendar = createBookingCalendar(timetable_calendar, resource_calendars, event_factory=BookingTimetableEvent) return booking_calendar.expand(start, end)
def importAllTimetables(self): if not self.shouldImportAllTimetables(): return oldTimetables = ITimetableContainer(self.activeSchoolyear) newTimetables = ITimetableContainer(self.newSchoolyear) chooser = INameChooser(newTimetables) app = ISchoolToolApplication(None) tzname = IApplicationPreferences(app).timezone for schooltt in oldTimetables.values(): newSchooltt = Timetable(self.newSchoolyear.first, self.newSchoolyear.last, title=schooltt.title, timezone=tzname) name = chooser.chooseName(schooltt.__name__, newSchooltt) newTimetables[name] = newSchooltt self.setUpTimetable(newSchooltt, schooltt) if (oldTimetables.default is not None and sameProxiedObjects(oldTimetables.default, schooltt)): newTimetables.default = newSchooltt
def container(self): return ITimetableContainer(self.schoolyear)
def hasTimetableSchemas(self, schoolyear): timetables = ITimetableContainer(schoolyear) return bool(timetables)
def schoolyear(self): container = ITimetableContainer(self.context) return ISchoolYear(IHaveTimetables(container))
def actualContext(self, context): return ITimetableContainer(self.schoolyear)
def handleApply(self, action): container = ITimetableContainer(self.context) container.default = removeSecurityProxy(self.context) self.ajax_settings['dialog'] = 'close'
def timetables(self): timetables = ITimetableContainer(self.schoolyear, None) if timetables is not None: return sorted(timetables.values(), key=lambda t:t.first, reverse=True)
def delete(self): container = ITimetableContainer(self.context) del container[self.context.__name__]
def importChunk(self, rows, line, dry_run=True): """Import a chunk of data that describes a section. You should run this method with dry_run=True before trying the real thing, or you might get in trouble. """ terms = self.listTerms() row = rows[0] if len(row) not in (2, 2 + len(terms)): err_msg = _('Wrong section header on line ${line_no} (it should contain a' ' course id, an instructor id and optional SchoolTool ' 'section IDs for each of the terms)', mapping={'line_no': line}) return section_ids = None if len(row) == 2: course_id, instructor_id = row[:2] else: course_id = row[0] instructor_id = row[1] section_ids = row[2:] course = ICourseContainer(self.schoolyear).get(course_id, None) if course is None: self.errors.courses.append(course_id) instructor = self.persons.get(instructor_id, None) if instructor is None: self.errors.persons.append(instructor_id) line_ofs = 1 finished = False timetables = ITimetableContainer(self.schoolyear) timetable = None periods = {} for row in rows[1:]: line_ofs += 1 if row == ['***']: finished = True break if len(row) == 1: tt = timetables.get(row[0]) if tt is None: err_msg = _("Malformed line ${line_no}" " (it should contain either a timetable id or" " day id and a period id)", mapping={'line_no': line + line_ofs - 1}) self.errors.generic.append(err_msg) continue timetable = tt continue elif len(row) == 2: day_id, period_id = row else: err_msg = _("Malformed line ${line_no}" " (it should contain either a timetable id or" " day id and a period id)", mapping={'line_no': line + line_ofs - 1}) self.errors.generic.append(err_msg) continue if timetable is None: err_msg = _("Timetable id must be specified before" " day id and a period id" " at at line ${line_no}", mapping={'line_no': line + line_ofs - 1}) continue # check day_id ttday = None for day in timetable.periods.templates.values(): if day.title == day_id: ttday = day break if ttday is None: errkey = (timetable.__name__, day_id) if errkey not in self.errors.day_ids: self.errors.day_ids.append(errkey) continue ttperiod = None for period in ttday.values(): if period.title == period_id: ttperiod = period break # check period_id if ttperiod is None: errkey = (timetable.__name__, day_id, period_id) if period_id not in self.errors.periods: self.errors.periods.append(errkey) continue if timetable.__name__ not in periods: periods[timetable.__name__] = [] periods[timetable.__name__].append(period) if not finished: err_msg = _("Incomplete section description on line ${line}", mapping = {'line': line}) self.errors.generic.append(err_msg) return if len(rows) == line_ofs: err_msg = _("No students in section (line ${line})", mapping = {'line': line + line_ofs}) self.errors.generic.append(err_msg) return sections = [] for n, term in enumerate(terms): section_container = ISectionContainer(term) section_id = None if section_ids is not None: section_id = section_ids[n] if (section_id is not None and section_id in section_container): section = section_container[section_id] self.updateSection( section, term, course, instructor, periods, dry_run=dry_run) else: section = self.createSection( term, course, instructor, periods, section_id=section_id, dry_run=dry_run) self.importPersons(rows[line_ofs:], section, dry_run=dry_run) if section is not None: sections.append(section) if not self.errors.anyErrors(): for n, section in enumerate(sections[:-1]): section.next = sections[n+1]
def container(self): owner = IHaveSchedule(self.context) return ITimetableContainer(ISchoolYear(ITerm(owner)), {})
def timetables(self): timetables = ITimetableContainer(self.schoolyear, None) if timetables is not None: return sorted(timetables.values(), key=lambda t: t.first, reverse=True)
def importChunk(self, rows, line, dry_run=True): """Import a chunk of data that describes a section. You should run this method with dry_run=True before trying the real thing, or you might get in trouble. """ terms = self.listTerms() row = rows[0] if len(row) not in (2, 2 + len(terms)): err_msg = _( 'Wrong section header on line ${line_no} (it should contain a' ' course id, an instructor id and optional SchoolTool ' 'section IDs for each of the terms)', mapping={'line_no': line}) return section_ids = None if len(row) == 2: course_id, instructor_id = row[:2] else: course_id = row[0] instructor_id = row[1] section_ids = row[2:] course = ICourseContainer(self.schoolyear).get(course_id, None) if course is None: self.errors.courses.append(course_id) instructor = self.persons.get(instructor_id, None) if instructor is None: self.errors.persons.append(instructor_id) line_ofs = 1 finished = False timetables = ITimetableContainer(self.schoolyear) timetable = None periods = {} for row in rows[1:]: line_ofs += 1 if row == ['***']: finished = True break if len(row) == 1: tt = timetables.get(row[0]) if tt is None: err_msg = _( "Malformed line ${line_no}" " (it should contain either a timetable id or" " day id and a period id)", mapping={'line_no': line + line_ofs - 1}) self.errors.generic.append(err_msg) continue timetable = tt continue elif len(row) == 2: day_id, period_id = row else: err_msg = _( "Malformed line ${line_no}" " (it should contain either a timetable id or" " day id and a period id)", mapping={'line_no': line + line_ofs - 1}) self.errors.generic.append(err_msg) continue if timetable is None: err_msg = _( "Timetable id must be specified before" " day id and a period id" " at at line ${line_no}", mapping={'line_no': line + line_ofs - 1}) continue # check day_id ttday = None for day in timetable.periods.templates.values(): if day.title == day_id: ttday = day break if ttday is None: errkey = (timetable.__name__, day_id) if errkey not in self.errors.day_ids: self.errors.day_ids.append(errkey) continue ttperiod = None for period in ttday.values(): if period.title == period_id: ttperiod = period break # check period_id if ttperiod is None: errkey = (timetable.__name__, day_id, period_id) if period_id not in self.errors.periods: self.errors.periods.append(errkey) continue if timetable.__name__ not in periods: periods[timetable.__name__] = [] periods[timetable.__name__].append(period) if not finished: err_msg = _("Incomplete section description on line ${line}", mapping={'line': line}) self.errors.generic.append(err_msg) return if len(rows) == line_ofs: err_msg = _("No students in section (line ${line})", mapping={'line': line + line_ofs}) self.errors.generic.append(err_msg) return sections = [] for n, term in enumerate(terms): section_container = ISectionContainer(term) section_id = None if section_ids is not None: section_id = section_ids[n] if (section_id is not None and section_id in section_container): section = section_container[section_id] self.updateSection(section, term, course, instructor, periods, dry_run=dry_run) else: section = self.createSection(term, course, instructor, periods, section_id=section_id, dry_run=dry_run) self.importPersons(rows[line_ofs:], section, dry_run=dry_run) if section is not None: sections.append(section) if not self.errors.anyErrors(): for n, section in enumerate(sections[:-1]): section.next = sections[n + 1]