Handle time ranges (breaks file format)
This commit is contained in:
parent
2e8fed156d
commit
50dab6c0eb
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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())
|
||||
|
||||
|
|
|
@ -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)
|
Loading…
Reference in New Issue