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
|
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
|
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
|
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.
|
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_parsed = parse_coursefile.parse(f.read())
|
||||||
|
|
||||||
courses = []
|
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)
|
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 = generate_timetable.generate_timetable(dates, courses)
|
||||||
|
|
||||||
timetable_by_date = []
|
timetable_by_date = []
|
||||||
current_date = None
|
current_date = None
|
||||||
current_date_entries = []
|
current_date_entries = []
|
||||||
for date, name, info in timetable:
|
for date, time_range, name, info in timetable:
|
||||||
if current_date is None:
|
if current_date is None:
|
||||||
current_date = date
|
current_date = date
|
||||||
|
|
||||||
if date == current_date:
|
if date == current_date:
|
||||||
current_date_entries.append((name, info))
|
current_date_entries.append((time_range, name, info))
|
||||||
else:
|
else:
|
||||||
timetable_by_date.append((current_date, current_date_entries))
|
timetable_by_date.append((current_date, current_date_entries))
|
||||||
current_date = date
|
current_date = date
|
||||||
current_date_entries = []
|
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 != []:
|
if current_date is not None and current_date_entries != []:
|
||||||
timetable_by_date.append((current_date, current_date_entries))
|
timetable_by_date.append((current_date, current_date_entries))
|
||||||
|
@ -64,6 +64,12 @@ def main():
|
||||||
print()
|
print()
|
||||||
|
|
||||||
for date, entries in timetable_by_date:
|
for date, entries in timetable_by_date:
|
||||||
|
entries.sort(key = lambda x: x[0].range()[0])
|
||||||
|
previous_time_range = None
|
||||||
print(date)
|
print(date)
|
||||||
for name, info in entries:
|
for time_range, name, info in entries:
|
||||||
print('\t%s: %s' % (name, info))
|
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
|
date = start_date
|
||||||
appointments = []
|
appointments = []
|
||||||
while True:
|
while True:
|
||||||
for name, info, date_filter in courses:
|
for name, time_range, info, date_filter in courses:
|
||||||
if date_filter(date):
|
if date_filter(date):
|
||||||
appointments.append((date, name, info))
|
appointments.append((date, time_range, name, info))
|
||||||
|
|
||||||
if date == end_date:
|
if date == end_date:
|
||||||
break
|
break
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
from . import timerange
|
||||||
|
|
||||||
def strip_comments(text):
|
def strip_comments(text):
|
||||||
lines = []
|
lines = []
|
||||||
for line in text.split('\n'):
|
for line in text.split('\n'):
|
||||||
|
@ -8,6 +10,13 @@ def strip_comments(text):
|
||||||
lines.append(line)
|
lines.append(line)
|
||||||
return '\n'.join(lines)
|
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):
|
def parse_filter(text):
|
||||||
weekdays = {'mon': 1, 'tue': 2, 'wed': 3, 'thu': 4, 'fri': 5, 'sat': 6, 'sun': 7}
|
weekdays = {'mon': 1, 'tue': 2, 'wed': 3, 'thu': 4, 'fri': 5, 'sat': 6, 'sun': 7}
|
||||||
functions = ['date', 'week', 'weekday', 'and', 'or', 'not', 'if']
|
functions = ['date', 'week', 'weekday', 'and', 'or', 'not', 'if']
|
||||||
|
@ -117,6 +126,9 @@ def parse(text):
|
||||||
name = read_field()
|
name = read_field()
|
||||||
match(';')
|
match(';')
|
||||||
|
|
||||||
|
time_range = parse_timerange(read_field())
|
||||||
|
match(';')
|
||||||
|
|
||||||
info = read_field()
|
info = read_field()
|
||||||
match(';')
|
match(';')
|
||||||
|
|
||||||
|
@ -124,7 +136,7 @@ def parse(text):
|
||||||
if not eof():
|
if not eof():
|
||||||
match('\n')
|
match('\n')
|
||||||
|
|
||||||
courses.append((name, info, date_filter))
|
courses.append((name, time_range, info, date_filter))
|
||||||
|
|
||||||
assert(eof())
|
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