Handle time ranges (breaks file format)

This commit is contained in:
Juhani Haverinen 2016-10-07 23:58:19 +03:00
parent 2e8fed156d
commit 50dab6c0eb
5 changed files with 76 additions and 11 deletions

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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())

43
lukkari/timerange.py Normal file
View File

@ -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)