Select Git revision
Forked from
Video AG Infrastruktur / website
Source project has a limited visibility.
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
timetable.py 4.54 KiB
from server import *
def get_monday(day):
return day-timedelta(days=day.weekday())
def get_week_offset(value):
if value is None:
return 0
day = None
for pattern in ['%d-%m-%Y-1', '%Y-W%W-%w']:
try:
day = datetime.strptime(value+'-1', pattern)
except ValueError:
pass
if day is not None:
break
if day is None:
return 0
return int((get_monday(day) - get_monday(datetime.now())).days/7)
def query_lectures_on_day(start, end):
# What we want to match:
# lecture.time <= end AND lecture.time+lecture.duration >= start
# But there is no SQL statement that does this and is compatible with both sqlite
# and mysql, so we approximate the "lecture.time+lecture.duration" part
rows = query('''SELECT lectures.*, courses.short, "course" AS sep, courses.*
FROM lectures
JOIN courses ON (lectures.course_id = courses.id)
WHERE time <= ? AND time > ?
ORDER BY time ASC''', end, start-timedelta(weeks=2))
lectures = []
for lecture in rows:
if lecture['time']+timedelta(minutes=lecture['duration']) >= start:
lectures.append(lecture)
return lectures
'''Use a sweepline algorithm to find overlapping lectures
For each day the item 'maxcol' will be the number of columns required to display
the overlapping lectures. For each lecture the item 'timetable_col' will be the
index of the column the lecture is going to be rendered in.'''
def timetable_sweepline(days):
earliest_start = time(23, 59)
latest_end = time(0, 0)
for day in days:
sweeplinetupels = [(lecture['time'].time(), True, lecture) for lecture in day['lectures']]
sweeplinetupels += [(lecture['time_end'].time(), False, lecture) for lecture in day['lectures']]
maxcol = 0
curcol = 0
freecol = []
sweeplinetupels.sort(key=lambda row: row[:2])
for timestamp, is_start, lecture in sweeplinetupels:
if is_start:
curcol += 1
maxcol = max(maxcol, curcol)
if freecol:
lecture['timetable_col'] = freecol.pop()
else:
lecture['timetable_col'] = maxcol
earliest_start = min(earliest_start, timestamp)
else:
curcol -= 1
freecol.append(lecture['timetable_col'])
latest_end = max(latest_end, timestamp)
day['maxcol'] = max(maxcol, 1)
return earliest_start, latest_end
@register_navbar('personalisierter Drehplan', icon='calendar', userendpoint=True, endpoint='timetable_user')
@register_navbar('Drehplan', icon='calendar')
@app.route('/internal/timetable')
@app.route('/internal/user/<int:user>/timetable', endpoint='timetable_user')
@mod_required
def timetable(user=None):
if 'kw' in request.args:
week_offset = int(request.args['kw'])
else:
week_offset = get_week_offset(request.args.get('date', None))
start_day = date.today() - timedelta(days=date.today().weekday() - 7*week_offset)
days = [{'date': start_day, 'lectures': [], 'atonce': 0, 'index': 0}]
for i in range(1, 7):
days.append({'date': days[i-1]['date'] + timedelta(days=1), 'atonce': 0, 'index': i, 'lectures': []})
for day in days:
start = datetime.combine(day['date'], time(0, 0))
end = datetime.combine(day['date'], time(23, 59))
day['lectures'] = []
for lecture in query_lectures_on_day(start, end):
lecture['time_end'] = lecture['time']+timedelta(minutes=lecture['duration'])
# "Crop" lecture's timespan to start/end of day
lecture['time'] = max(start, lecture['time'])
lecture['time_end'] = min(end, lecture['time_end'])
# Ensure length > 0
lecture['time_end'] = max(lecture['time_end'], lecture['time']+timedelta(minutes=1))
lecture['duration'] = int((lecture['time_end'] - lecture['time']).total_seconds()/60)
# Filter on responsible user if a user parameter was given
lecture['responsible'] = query('''SELECT users.*
FROM responsible
JOIN users ON (responsible.user_id = users.id AND responsible.course_id = ?)
ORDER BY users.realname ASC''', lecture['course_id'])
if len(lecture['responsible']) == 0:
lecture['responsible'] = [{"realname": "Niemand", "id": -1}]
if not user or user in [r['id'] for r in lecture['responsible']]:
day['lectures'].append(lecture)
earliest_start, latest_end = timetable_sweepline(days)
start = min(earliest_start, time(8, 0))
end = max(latest_end, time(19, 0))
blocks = []
for i in range(start.hour*4, min(int((60*end.hour/15)/4)*4+5, 24*4)):
timestamp = i*15
blocks.append(time(int(timestamp/60), timestamp%60))
weekofyear = '{}-W{:02d}'.format(datetime.today().year, datetime.today().isocalendar()[1])
return render_template('timetable.html',
days=days,
blocks=blocks,
kw=week_offset,
weekofyear=weekofyear,
user=query('SELECT * FROM users WHERE id = ?', user)[0] if user else None)