#!/usr/bin/python3 import json import os import flask import argparse import caldav import datetime as dt import markdown2 VEREIN_SECTIONS_DIR = "sections/" MAIN_LINKS_DIR = "main-links/" NEWS_DIR = "news/" app = flask.Flask("athq-landing-page") mainConfig = dict() with open("config.json") as f: mainConfig = json.load(f) caldavUrl = None caldavPassword = None caldavUsername = None def updateEventsFromCalDav(): if app.config["USE_CALENDAR"]: client = caldav.DAVClient(url=caldavUrl, username=caldavUsername, password=caldavPassword) authenticatedClient = client.principal() defaultCal = authenticatedClient.calendars()[0] start = dt.datetime.now() start = start - dt.timedelta(seconds=start.timestamp() % dt.timedelta(days=1).total_seconds()) end = start + dt.timedelta(days=90) # TODO remove this # start = start - dt.timedelta(days=90) events = sorted(defaultCal.date_search(start, end), key=lambda e: e.vobject_instance.vevent.dtstart.value) eventsDictList = [] for e in events: date = e.vobject_instance.vevent.dtstart.value date += dt.timedelta(hours=2) newEventDict = { "description" : e.vobject_instance.vevent.summary.value, "time" : date.strftime("%H:%M"), "day" : date.strftime("%d"), "month" : date.strftime("%b"), "year" : date.strftime("%Y") } try: newEventDict.update({ "location" : e.vobject_instance.vevent.location.value }) except AttributeError: pass eventsDictList += [newEventDict] else: eventsDictList = [] with open("cache.json", "w") as f: json.dump(eventsDictList, f) def getEventsCache(): with open("cache.json", "r") as f: return json.load(f) def readJsonDir(basedir): # load json files from projects/ dir # jsonDictList =[] for root, dirs, files in os.walk(basedir): for filename in sorted(files): if filename.endswith(".json"): with open(os.path.join(basedir, filename)) as f: jsonDictList += [json.load(f)] return jsonDictList def parseNewsDirWithTimeout(): TIMEOUT_RELATIVE = "timeout-relative-weeks" TIMEOUT_FIXED = "timeout-fixed" PARSED_TIME = "parsed-time" ACTIVE = "active" DATE = "date" news = readJsonDir(NEWS_DIR) now = dt.datetime.now() for n in news: n.update( { PARSED_TIME : dt.datetime.fromtimestamp(n[DATE]) } ) if n.get(ACTIVE): continue if n.get(TIMEOUT_FIXED): if dt.datetime.fromtimestamp(n[TIMEOUT_FIXED]) < now: n[ACTIVE] = False elif n.get(TIMEOUT_RELATIVE): if n[PARSED_TIME] + dt.timedelta(weeks=n[TIMEOUT_RELATIVE]) < now: n[ACTIVE] = False else: raise ValueError("No timeout for news {} specified!", n) return sorted(news, key=lambda n: n[PARSED_TIME], reverse=True) @app.route("/invalidate") def invalidateEventCache(): updateEventsFromCalDav(); return ("", 204) @app.route("/") def root(): announcements = parseNewsDirWithTimeout() return flask.render_template("index.html", mainLinks=readJsonDir(MAIN_LINKS_DIR), siteTitle=mainConfig["siteTitle"], conf=mainConfig, events=getEventsCache(), moreEvents=len(getEventsCache())>3, vereinSections=readJsonDir(VEREIN_SECTIONS_DIR), announcements=announcements) @app.route("/impressum") def impressum(): return flask.render_template("impressum.html", conf=mainConfig) @app.route("/verein") def verein(): return flask.render_template("verein.html", conf=mainConfig) @app.route("/stammtisch") def stammtisch(): return flask.render_template("stammtisch.html", conf=mainConfig) @app.route("/people") def people(): return flask.render_template("people.html", conf=mainConfig, people=readJsonDir("people/")) @app.route("/news") def news(): uid = int(flask.request.args.get("uid")) news = parseNewsDirWithTimeout() newsDict = dict() for n in news: newsDict.update( { n["uid"] : n } ) if not uid or not newsDict[uid]: return ("", 404) article = newsDict[uid] try: with open(article["markdown-file"]) as f: article.update( { "markdown-content" : markdown2.markdown(f.read()) } ) except FileNotFoundError as e: return ("File not found Error ({})".format(e), 404) return flask.render_template("news.html", conf=mainConfig, article=article) @app.route("/static/") def sendStatic(path): if "pictures" in path: cache_timeout = 2592000 else: cache_timeout = None return flask.send_from_directory('static', path, cache_timeout=cache_timeout) @app.route('/defaultFavicon.ico') def icon(): return app.send_static_file('defaultFavicon.ico') if __name__ == "__main__": parser = argparse.ArgumentParser(description='Projects Showcase', formatter_class=argparse.ArgumentDefaultsHelpFormatter) # general parameters # parser.add_argument("-i", "--interface", default="0.0.0.0", help="Interface to listen on") parser.add_argument("-p", "--port", default="5000", help="Port to listen on") parser.add_argument("--cal-info", help="File Containing a public calendar link") parser.add_argument("--no-update-on-start", action="store_const", const=True, default=False, help="Don't update the calendar on start") # startup # app.config['TEMPLATES_AUTO_RELOAD'] = True args = parser.parse_args() if args.cal_info: app.config["USE_CALENDAR"] = True with open(args.cal_info) as f: caldavUrl, caldavUsername, caldavPassword = f.read().strip().split(",") else: app.config["USE_CALENDAR"] = False if not args.no_update_on_start: updateEventsFromCalDav() app.run(host=args.interface, port=args.port)