import urllib.error from dateutil.parser import * import asyncio import httpx import icalendar from icalendar import Calendar, Event import json from init import scheduler, flask_app, Rapla, db async def fetchPlan(session, url): return await session.get(url=url) def writeToFile(filename, data): with open(filename, 'w+') as f: f.write(data.text) f.close() def writeToDB(kurs, url): if Rapla.query.filter_by(name=kurs).first() is None: new_kurs = Rapla(name=kurs, link=url, file=f"rapla{kurs}.ical") db.session.add(new_kurs) db.session.commit() def parseURL(url: str): """ Konvertiert URLs ins korrekte Format. \n Konvertiert werden: http, www.; page=calendar \n In: https; page=ical :param url: :return str: """ rapla = url.find("rapla.") if rapla == -1: return 0 elif len(url[:rapla]) == 4 or len(url[:rapla]) > 6: url = url[rapla:] http = url.find(":") if url[:http] == "http": url = f"https{url[http:]}" elif http == -1: url = f"https://{url}" p = url.find("page=") u = url.find("&") if (url[p + 5:u]).lower() == "ical": return 1, url elif p != -1: return 1, f"{url[:p + 5]}ical{url[u:]}" elif url.find("key") != -1: return 2, url else: return 0, 0 async def getNewRapla(url: str): """ Speichert den iCal eines Raplas auf dem Server. \n Gibt Namen der Datei zurück. \n TODO: Standort zu Dateibezeichner hinzufügen, um Konflikte zu vermeiden. :param url: :return str: """ parsed = parseURL(url) if parsed[0] == 0: return 0 elif parsed[0] == 1: url = parsed[1] elif parsed[0] == 2: return await buildFromKey(parsed[1], onlyUpdate=False) urlfile = url.find("file=") kurs = url[urlfile + 5:].upper() if url[-5:] != ".ical": try: async with httpx.AsyncClient() as s: response = await fetchPlan(s, url) writeToFile(f"calendars/rapla{kurs}.ical", response) except urllib.error.URLError: return -1 writeToDB(kurs, url) return f"rapla{kurs}.ical" else: return url def getIcal(kurs: str): """ Liefert den Namen der Datei des mitgegebenen Kurses. :param kurs: :return str: """ rapla = Rapla.query.filter(Rapla.name == kurs).first() try: return rapla.file except AttributeError or KeyError: return None def getRaplas(): """ Liefert alle auf dem Server gespeicherten Raplas. :return (Kursliste, Dateiliste, URL-Liste): """ raplas = Rapla.query.all() kursl = [rapla.name for rapla in raplas] filel = [rapla.file for rapla in raplas] urll = [rapla.link for rapla in raplas] return kursl, filel, urll async def refreshRapla(): """ Aktualisiert alle 5 Minuten alle gespeicherten Raplas. """ filel = getRaplas()[1] urll = getRaplas()[2] jobl = [] async with httpx.AsyncClient() as s: for i in range(len(filel)): print(f"Update Rapla: {filel[i][:-5]}") if urll[i].find("file") != -1: jobl += [fetchPlan(s, urll[i])] else: jobl += [buildFromKey(urll[i], onlyUpdate=True)] callist = await asyncio.gather(*jobl, return_exceptions=True) for cal in range(len(callist)): if callist[cal] != 200: writeToFile(f"calendars/{filel[cal]}", callist[cal]) @scheduler.task('cron', id="raplaschedule", hour='*', day_of_week='*', minute='*/3', week='*', second='40') def raplaSchedule(): with flask_app.app_context(): asyncio.run(refreshRapla()) async def buildFromKey(url, onlyUpdate): async with httpx.AsyncClient() as s: page = await s.get(url=url) if page.text[:10] == "BEGIN:VCAL": info = page.headers['content-disposition'] kursname = info[info.find('filename=')+9:-4].upper() writeToFile(f"rapla{kursname}.ical", page) if not onlyUpdate: writeToDB(kursname, url) return url else: return 200 else: kursname = page.text[page.text.find("