#!/usr/bin/python import os import sys import time import traceback from calendar import timegm from datetime import datetime, timedelta import pytz from requests import get from hosted import config, node config.restart_on_update() SEND_PREFIX = os.environ["NODE"].replace("root/", "root/plugin/") def log(msg): sys.stderr.write("[pretalx] {}\n".format(msg)) def idle(seconds, event_start, event_end, event_tz, event_days): end = time.time() + seconds log("sleeping for {} seconds".format(seconds)) while time.time() < end: send_data = { "clock": "??", "day": "??", "time": int(time.time()), } if event_start is not None: event_now = datetime.now(event_tz) event_day = None log("event_now = {}".format(event_now.isoformat())) send_data["clock"] = event_now.strftime(config["clock_format"]) for day in event_days: if day["starts"] <= event_now <= day["ends"]: log("Today is day {} according to schedule".format(day["index"])) event_day = day["index"] if event_day is None: log( "Schedule does not contain information for this day, calculating using event start time" ) utc_now = datetime.now(pytz.utc) day_zero = event_tz.localize( event_start.replace(hour=0, minute=0, second=0) ) - timedelta(days=1) day_info = event_now - day_zero log( "Today is day {}, based on day zero at {}".format( day_info.days, day_zero.isoformat() ) ) event_day = day_info.days send_data["day"] = event_day # "single day" is only used in standalone mode to hide the # the day information if event_end is not None: if event_start == event_end: send_data["single_day"] = 1 else: send_data["single_day"] = 0 for k, v in send_data.items(): node.send_raw("{}/{}:{}".format(SEND_PREFIX, k, v)) time.sleep(1) def main(): event_info = None event_start = None event_end = None schedule = None event_tz = pytz.utc while True: schedule_url = config["schedule_url"] room_uuid_mapping = {} event_days = [] if "example.com" in schedule_url: log("default schedule url, waiting for config update") # sleep forever, service will get restarted if the config # changes. time.sleep(99999999) log("event url: {}".format(schedule_url)) log("using json flavour: {}".format(config["json_flavour"])) if config["json_flavour"] == "pretalx-broadcast-tools": if not schedule_url.endswith("/"): schedule_url = schedule_url + "/" try: r = get( schedule_url + "p/broadcast-tools/event.json", ) r.raise_for_status() except Exception as e: log("updating event info failed: {}".format(repr(e))) # Only print the error message. If we have fetched the event # info json blob atleast once, we have all the information # we need. else: event_info = r.json() if event_info is not None: event_start = datetime.strptime(event_info["start"], "%Y-%m-%d") event_end = datetime.strptime(event_info["end"], "%Y-%m-%d") event_tz = pytz.timezone(event_info["timezone"]) for uuid, room_name in event_info.get("rooms", {}).items(): room_uuid_mapping[room_name] = uuid try: r = get( schedule_url + "p/broadcast-tools/schedule.json", ) r.raise_for_status() except Exception as e: log("updating schedule failed: {}".format(repr(e))) # Only print the error message. If we have fetched the schedule # info json blob atleast once, we have all the information # we need. else: schedule = r.json() tracks = {} for talk in schedule["talks"]: talk["start_str"] = ( datetime.fromtimestamp(talk["start_ts"]) .replace(tzinfo=pytz.utc) .astimezone(event_tz) .strftime("%H:%M") ) talk["room_uuid"] = room_uuid_mapping.get("room") if talk["track"]: if talk["track"]["name"] not in tracks: tracks[talk["track"]["name"]] = talk["track"]["color"] schedule["tracks"] = [] for name, color in sorted(tracks.items()): schedule["tracks"].append( { "name": name, "color": color, } ) node.write_json("schedule.json", schedule) log("updated schedule json") elif config["json_flavour"] == "voc-schema": try: r = get(schedule_url) r.raise_for_status() except Exception as e: log("getting schedule.json failed: {}".format(repr(e))) else: raw_schedule = r.json()["schedule"] schedule = { "tracks": raw_schedule["conference"]["tracks"], "talks": [], } for room in raw_schedule["conference"]["rooms"]: room_uuid_mapping[room["name"]] = room["guid"] event_start = datetime.strptime( raw_schedule["conference"]["start"][:10], "%Y-%m-%d" ) event_end = datetime.strptime( raw_schedule["conference"]["end"][:10], "%Y-%m-%d" ) event_tz = pytz.timezone(raw_schedule["conference"]["time_zone_name"]) for day in raw_schedule["conference"]["days"]: event_days.append( { "index": day["index"], "starts": event_tz.localize( datetime.strptime( day["day_start"][:19], "%Y-%m-%dT%H:%M:%S" ) ), "ends": event_tz.localize( datetime.strptime( day["day_end"][:19], "%Y-%m-%dT%H:%M:%S" ) ), } ) for room in day["rooms"].values(): for talk in room: start = event_tz.localize( datetime.strptime( talk["date"][:19], "%Y-%m-%dT%H:%M:%S" ) ) d_h, d_m = talk["duration"].split(":") end = start + timedelta(hours=int(d_h), minutes=int(d_m)) talk["start_ts"] = int( timegm(start.timetuple()) - start.utcoffset().total_seconds() ) talk["start_str"] = talk["start"] talk["end_ts"] = int( timegm(end.timetuple()) - end.utcoffset().total_seconds() ) talk["locale"] = talk["language"] track = None if talk["track"]: for t in raw_schedule["conference"]["tracks"]: if t["name"] == talk["track"]: track = { "color": t["color"], "name": t["name"], } break talk["track"] = track talk["room_uuid"] = room_uuid_mapping.get(talk["room"]) persons = [] for p in talk["persons"]: name = p.get("public_name", p.get("name")) if name: persons.append(name) talk["persons"] = persons schedule["talks"].append(talk) node.write_json("schedule.json", schedule) log("updated schedule json") else: log("unknown json flavour, something is very wrong") uuid_json = {} for name, uuid in room_uuid_mapping.items(): uuid_json[uuid] = name node.write_json("uuid.json", uuid_json) idle(30, event_start, event_end, event_tz, event_days) if __name__ == "__main__": try: main() except Exception: traceback.print_exc() try: idle(30, None, None, None) except Exception: time.sleep(30)