Skip to content
Snippets Groups Projects
Select Git revision
  • d985ea7639dd8a63fc8157f00ed69a200477a10d
  • master default
  • zs-dobrichovice
3 results

zoom.py

Blame
  • zoom.py 5.17 KiB
    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')