diff --git a/README.md b/README.md index a6dac4f..ce86b32 100644 --- a/README.md +++ b/README.md @@ -14,10 +14,14 @@ File format ----------- Basic record format is - name;free-form info;filter + name;time range;free-form info;filter Comments (`#`) are supported and `;` can be excaped with `\` as usual +Time range format +----------------- +Time ranges are `start-end`, where start and end may be either HH:MM or just the hour (then it's assumed the minutes field is 0) + Filter format ------------- Filters are written in a sexpr based language. A function can be either a filter or a conjunction. Currently three different filters (`date`, `week`, and `weekday`) and four conjunctions (`and`, `or`, `not`, and `if`) are supported. diff --git a/lukkari/__init__.py b/lukkari/__init__.py index e2f8da1..f9f239a 100644 --- a/lukkari/__init__.py +++ b/lukkari/__init__.py @@ -36,26 +36,26 @@ def main(): courses_parsed = parse_coursefile.parse(f.read()) courses = [] - for name, info, parsed_filter in courses_parsed: + for name, time_range, info, parsed_filter in courses_parsed: date_filter = dsl.compile(parsed_filter) - courses.append((name, info, date_filter)) + courses.append((name, time_range, info, date_filter)) timetable = generate_timetable.generate_timetable(dates, courses) timetable_by_date = [] current_date = None current_date_entries = [] - for date, name, info in timetable: + for date, time_range, name, info in timetable: if current_date is None: current_date = date if date == current_date: - current_date_entries.append((name, info)) + current_date_entries.append((time_range, name, info)) else: timetable_by_date.append((current_date, current_date_entries)) current_date = date current_date_entries = [] - current_date_entries.append((name, info)) + current_date_entries.append((time_range, name, info)) if current_date is not None and current_date_entries != []: timetable_by_date.append((current_date, current_date_entries)) @@ -64,6 +64,12 @@ def main(): print() for date, entries in timetable_by_date: + entries.sort(key = lambda x: x[0].range()[0]) + previous_time_range = None print(date) - for name, info in entries: - print('\t%s: %s' % (name, info)) + for time_range, name, info in entries: + print('\t%s %s: %s' % (time_range, name, info)) + if previous_time_range is not None: + if time_range.overlaps(previous_time_range): + print('\t\tOverlap') + previous_time_range = time_range diff --git a/lukkari/generate_timetable.py b/lukkari/generate_timetable.py index 096e321..1e52de7 100644 --- a/lukkari/generate_timetable.py +++ b/lukkari/generate_timetable.py @@ -5,9 +5,9 @@ def generate_timetable(day_range, courses): date = start_date appointments = [] while True: - for name, info, date_filter in courses: + for name, time_range, info, date_filter in courses: if date_filter(date): - appointments.append((date, name, info)) + appointments.append((date, time_range, name, info)) if date == end_date: break diff --git a/lukkari/parse_coursefile.py b/lukkari/parse_coursefile.py index baf12ea..e3efe72 100644 --- a/lukkari/parse_coursefile.py +++ b/lukkari/parse_coursefile.py @@ -1,3 +1,5 @@ +from . import timerange + def strip_comments(text): lines = [] for line in text.split('\n'): @@ -8,6 +10,13 @@ def strip_comments(text): lines.append(line) return '\n'.join(lines) +def parse_timerange(text): + ends = [i.split(':') for i in text.split('-')] + assert(len(ends) == 2) + start, end = ((int(i[0]), 0) if len(i) == 1 else tuple(map(int, i)) for i in ends) + assert(len(start) == 2 and len(end) == 2) + return timerange.between(start, end) + def parse_filter(text): weekdays = {'mon': 1, 'tue': 2, 'wed': 3, 'thu': 4, 'fri': 5, 'sat': 6, 'sun': 7} functions = ['date', 'week', 'weekday', 'and', 'or', 'not', 'if'] @@ -117,6 +126,9 @@ def parse(text): name = read_field() match(';') + time_range = parse_timerange(read_field()) + match(';') + info = read_field() match(';') @@ -124,7 +136,7 @@ def parse(text): if not eof(): match('\n') - courses.append((name, info, date_filter)) + courses.append((name, time_range, info, date_filter)) assert(eof()) diff --git a/lukkari/timerange.py b/lukkari/timerange.py new file mode 100644 index 0000000..64b279e --- /dev/null +++ b/lukkari/timerange.py @@ -0,0 +1,43 @@ +import datetime + +class Timerange: + def __init__(self, start, length): + self.start = start + self.length = length + + def range(self): + end = self.start + self.length + return (self.start, end) + + def __contains__(self, day): + delta = day - self.start + return datetime.timedelta(seconds = 0) <= delta and delta < self.length + + def overlaps(self, other): + if other.start < self.start: + return other.overlaps(self) + + assert(self.start <= other.start) + return other.start < self.start + self.length + + def __repr__(self): + return 'Timerange(%s, %s)' % (repr(self.start), repr(self.length)) + + def __str__(self): + start, end = (i.strftime('%H:%M') for i in self.range()) + return '%s - %s' % (start, end) + + def __eq__(self, other): + return self.start == other.start and self.length == other.length + + def __ne__(self, other): + return not self == other + +def between(start, end): + assert(len(start) == 2 and len(end) == 2) + start_hour, start_minute = start + end_hour, end_minute = end + start_obj = datetime.datetime(1970, 1, 1, start_hour, start_minute) + end_obj = datetime.datetime(1970, 1, 1, end_hour, end_minute) + assert(end_obj - start_obj > datetime.timedelta(seconds = 0)) + return Timerange(start_obj, end_obj - start_obj)