import json from flask import Flask, render_template, request, g import psycopg2 import psycopg2.extras import time from datetime import datetime import sys import dateutil import dateutil.tz ### Flask app object ### app = Flask(__name__) app.config.from_pyfile('config.py') ### Database connection ### db_connection = None db = None # XXX: This is safe only because we never write to the database. Otherwise, we would # have to handle transactions and roll them back if an exception occurs during # processing of the request. def db_connect(): global db_connection, db db_connection = psycopg2.connect( host = 'localhost', user = app.config['DB_USER'], password = app.config['DB_PASSWD'], dbname = app.config['DB_NAME'], ) db = db_connection.cursor(cursor_factory=psycopg2.extras.NamedTupleCursor) def db_query(query, args=()): if db is None: db_connect() try: db.execute(query, args) except psycopg2.DatabaseError: # Reconnect if the connection died (timeout, server restart etc.) db_connect() db.execute(query, args) ### Schedule ### def get_date(): """Return a datetime object corresponding to the start of the given date.""" try: d = request.args.get('date', "") dt = datetime.strptime(d, "%Y-%m-%d") except ValueError: dt = datetime.today() tz = dateutil.tz.tzlocal() return dt.replace(hour=0, minute=0, second=0, microsecond=0, tzinfo=tz) room_list = { 'i': [ ('Z1', 'zoom-1@d3s.mff.cuni.cz'), ('Z2', 'zoom-2@d3s.mff.cuni.cz'), ('Z3', 'zoom-3@d3s.mff.cuni.cz'), ('Z4', 'zoom-4@d3s.mff.cuni.cz'), ('Z5', 'zoom-5@d3s.mff.cuni.cz'), ('Z6', 'zoom-6@d3s.mff.cuni.cz'), ('Z7', 'zoom-7@d3s.mff.cuni.cz'), ('Z8', 'zoom-8@d3s.mff.cuni.cz'), ], 'm': [ ('ZM1', 'zoom-m-1@d3s.mff.cuni.cz'), ('ZM2', 'zoom-m-2@d3s.mff.cuni.cz'), ('ZM3', 'zoom-m-3@d3s.mff.cuni.cz'), ('ZM4', 'zoom-m-4@d3s.mff.cuni.cz'), ('ZM7', 'zoom-m-7@d3s.mff.cuni.cz'), ('ZM8', 'zoom-m-8@d3s.mff.cuni.cz'), ], } @app.route('/') def main_page(): dt = get_date() g.date = dt.strftime("%Y-%m-%d") g.dow = dt.strftime("%A") hours_arg = request.args.get("hours", "") if hours_arg in ["0", "1"]: g.hours = int(hours_arg) else: g.hours = 0 if g.hours == 0: hour_min = 8 hour_max = 24 else: hour_min = 0 hour_max = 24 num_hours = hour_max - hour_min g.rooms = request.args.get("rooms", "") if g.rooms not in room_list: g.rooms = "i" rooms = room_list[g.rooms] num_rooms = len(rooms) email_to_room_index = { rooms[i][1]: i for i in range(num_rooms) } room_box_width = 100 room_hour_height = 50 g.total_width = room_box_width * num_rooms g.total_height = num_hours * room_hour_height g.room_boxes = [{ "x": i * room_box_width + 1, "w": room_box_width - 1, "h": g.total_height - 1, "name": rooms[i][0], } for i in range(num_rooms)] g.hour_boxes = [{ "x": 1, "y": i * room_hour_height + 1, "w": num_rooms * room_box_width - 1, "h": room_hour_height - 1, } for i in range(num_hours)] # XXX: No meeting is ever longer than 24 hours db_query(""" SELECT m.meeting_id, m.topic, s.start_time, s.duration, u.email, u.full_name FROM zoom_meetings m JOIN zoom_users u ON u.uid = m.host_uid JOIN zoom_schedule s ON s.mid = m.mid WHERE s.start_time >= DATE %s - INTERVAL '1 day' AND s.start_time < DATE %s + INTERVAL '1 day' ORDER BY u.email, s.start_time """, (dt, dt)) g.meetings = [] prev_room_i = -1 prev_end_t = None for r in db.fetchall(): i = email_to_room_index.get(r.email, -1) if i < 0: continue # Datetime in the DB is in UTC, but psycopg2 interprets it as local time start_dt = r.start_time.replace(tzinfo=dateutil.tz.tz.tzutc()) start_t = int(start_dt.timestamp()) end_t = start_t + r.duration*60 rel_start = start_t - dt.timestamp() - hour_min*3600 rel_end = rel_start + r.duration*60 start = max(0, int(rel_start)) end = min(num_hours * 3600, int(rel_end)) app.logger.debug("Meeting: %s start_t=%s start=%s end=%s room_i=%s", r, start_t, start, end, i) if start < end: coll = (i == prev_room_i and start_t < prev_end_t) g.meetings.append({ "x": i * room_box_width + 4 + 10*int(coll), "y": int(start / 3600. * room_hour_height), "w": room_box_width - 7 - 10*int(coll), "h": int((end - start) / 3600 * room_hour_height), "start": time.strftime("%H:%M", time.localtime(start_t)), "end": time.strftime("%H:%M", time.localtime(end_t)), "topic": r.topic, "coll": coll, }) prev_room_i = i prev_end_t = end_t return render_template('main.html')