Skip to content
Snippets Groups Projects
Select Git revision
  • 0ad8cc94ac6cfb2e4f638f6b6439bd78dcf57851
  • master default protected
  • intros
  • live_sources
  • bootstrap4
  • modules
6 results

sorter.py

Blame
  • 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)