From 08d785124896c9b67faea2763ad3ba8be1701271 Mon Sep 17 00:00:00 2001 From: paulmart-n Date: Wed, 29 Nov 2023 11:10:01 +0100 Subject: [PATCH] RAPLA-POC, LOGIN-Flow --- .gitignore | 1 + app.py | 72 +++++++++++++++++------- dualisauth.py | 37 +++++++----- fetchRAPLA.py | 4 +- parseICAL.py | 62 +++++++++++++++++++++ static/cal.scss | 133 ++++++++++++++++++++++++++++++++++++++++++++ static/style.css | 2 +- templates/kurs.html | 22 ++++++++ templates/plan.html | 51 +++++++++++++++++ 9 files changed, 348 insertions(+), 36 deletions(-) create mode 100644 parseICAL.py create mode 100644 static/cal.scss create mode 100644 templates/kurs.html create mode 100644 templates/plan.html diff --git a/.gitignore b/.gitignore index c1d8d53..4579e6f 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ set/ VIRTUAL_ENV/ calendars/ .idea/ +calendars/list.json diff --git a/app.py b/app.py index def3421..dc0f81f 100644 --- a/app.py +++ b/app.py @@ -5,12 +5,13 @@ from flask_login import login_user, login_required, current_user, LoginManager, from flask_sqlalchemy import SQLAlchemy from werkzeug.security import generate_password_hash, check_password_hash import hashlib +import time import dualisauth import requesthelpers from fetchRAPLA import * from get_mysql import get_mysql -import time +from parseICAL import getWeek def create(): @@ -55,23 +56,42 @@ class Dualis(db.Model): @app.route("/") def index(): - return render_template('index.html', headermessage='Header', message='DualHub') + return redirect(url_for("login")) @app.route("/welcome") @login_required def welcome(): - d = Dualis.query.filter_by(uid=current_user.id).first() - if not current_user.kurs: - kurs = dualisauth.getKurs(d.token, d.cookie) - current_user.kurs = kurs - db.session.commit() - else: - kurs = current_user.kurs + kurs = current_user.kurs name = current_user.name return render_template('index.html', headermessage='DualHub', message="Hallo, " + name + " (" + kurs + ")") + +@app.route("/backendpoc/set-up") +@login_required +def getKurs(): + d = Dualis.query.filter_by(uid=current_user.id).first() + if d: + e = False + if not current_user.kurs: + kurs = dualisauth.getKurs(d.token, d.cookie) + if kurs != 0: + current_user.kurs = kurs + db.session.commit() + else: + e = True + else: + kurs = current_user.kurs + current_user.kurs = kurs + db.session.commit() + else: + e = True + kurs = "" + return render_template('kurs.html', detected=(kurs, e)) + + + @app.route("/backendpoc/error") def error(ecode): if ecode == 900: @@ -91,7 +111,7 @@ def chooseRaplas(): @login_required -@app.route("/backendpoc/plan", methods=["POST"]) +@app.route("/backendpoc/rapla", methods=["POST"]) def getRapla(): file = str(request.form.get("file")) url = str(request.form.get("url")) @@ -100,16 +120,25 @@ def getRapla(): if file != "None": User.query.filter_by(id=current_user.id).first().kurs = file[5:-5] db.session.commit() - return send_file("calendars/" + file) + #return send_file("calendars/" + file) elif url != "None": file = getNewRapla(url) if type(file) is not int: User.query.filter_by(id=current_user.id).first().kurs = file[5:-5] db.session.commit() - return send_file("calendars/" + file) + #return send_file("calendars/" + file) else: return redirect(url_for("error", ecode=file + 900)) - return render_template("index.html") + return redirect(url_for("welcome")) + +@login_required +@app.route("/backendpoc/plan", methods=["GET"]) +def displayRapla(): + samstag = request.args.get("samstag") + if not samstag: + samstag = False + events = getWeek("today", "rapla"+current_user.kurs+".ical", samstag) + return render_template("plan.html", events=events[0], eventdays=events[1]) @app.route("/backendpoc/log-in") @@ -127,21 +156,19 @@ def login_post(): if n: success = redirect(n) else: - success = redirect(url_for("welcome")) + success = redirect(url_for("getKurs")) user = User.query.filter_by(email=email).first() if user: dualis = Dualis.query.filter_by(uid=user.id).first() if check_password_hash(user.password, password): - if not dualisauth.checkLifetime(dualis.token_created): + if not dualis.token or not dualisauth.checkLifetime(dualis.token_created): new_token = dualisauth.checkUser(email, password) dualis.token = new_token[0] dualis.cookie = requesthelpers.getCookie(new_token[1].cookies) dualis.token_created = time.time() db.session.commit() - login_user(user) - return success else: t = dualisauth.checkUser(email, password) if t[0] == -2: @@ -152,8 +179,10 @@ def login_post(): dualis.cookie = requesthelpers.getCookie(t[1].cookies) dualis.token_created = time.time() db.session.commit() - login_user(user) - return success + login_user(user) + if user.kurs: + success = redirect(url_for("welcome")) + return success t = dualisauth.checkUser(email, password) if t[0] == -2: @@ -180,6 +209,11 @@ def login_post(): @app.route("/backendpoc/log-out") def logout(): + dualis = Dualis.query.filter_by(uid=current_user.id).first() + dualisauth.logOut(dualis.token, dualis.cookie) + dualis.cookie = None + dualis.token = None + db.session.commit() logout_user() return redirect(url_for("login", code=1)) diff --git a/dualisauth.py b/dualisauth.py index 1ef4aca..396dc7e 100644 --- a/dualisauth.py +++ b/dualisauth.py @@ -29,22 +29,33 @@ def checkUser(email: str, password: str): return token, s -def getKurs(token: str, cookie: int): - headers["Cookie"] = "cnsc=" + str(cookie) - response = requests.request("GET", - url + "?APPNAME=CampusNet&PRGNAME=COURSERESULTS&ARGUMENTS=-N" + token + ",-N000307,", - headers=headers, data={}) - html = BeautifulSoup(response.text, 'lxml') - link = html.body.find('a', attrs={'id': "Popup_details0001"})['href'] - response = requests.request("GET", url+link[21:], headers=headers, data={}) - html = BeautifulSoup(response.text, 'lxml') - content = html.body.find('td', attrs={'class': 'level02'}).text - start = content.find(" ")+4 - end = start + (content[start:].find(" ")) - kurs = content[start:end] +def getKurs(token: int, cookie: str): + try: + headers["Cookie"] = "cnsc=" + cookie + token = str(token) + response = requests.request("GET", + url + "?APPNAME=CampusNet&PRGNAME=COURSERESULTS&ARGUMENTS=-N" + token + ",-N000307,", + headers=headers, data={}) + html = BeautifulSoup(response.text, 'lxml') + link = html.body.find('a', attrs={'id': "Popup_details0001"})['href'] + response = requests.request("GET", url + link[21:], headers=headers, data={}) + html = BeautifulSoup(response.text, 'lxml') + content = html.body.find('td', attrs={'class': 'level02'}).text + start = content.find(" ") + 4 + end = start + (content[start:].find(" ")) + kurs = content[start:end] + except: + kurs = 0 return kurs +def logOut(token: int, cookie: str): + headers["Cookie"] = "cnsc=" + cookie + response = requests.request("GET", url + "?APPNAME=CampusNet&PRGNAME=LOGOUT&ARGUMENTS=-N" + str(token) + ", -N001", + headers=headers, data={}) + + + def checkLifetime(timecode: float): if time.time() - timecode > 1800: return False diff --git a/fetchRAPLA.py b/fetchRAPLA.py index 7cb0765..6f7eb81 100644 --- a/fetchRAPLA.py +++ b/fetchRAPLA.py @@ -2,6 +2,7 @@ import urllib.error from urllib.request import urlretrieve import icalendar import json +import recurring_ical_events def parseURL(url: str): @@ -37,9 +38,6 @@ def getNewRapla(url: str): except urllib.error.URLError: return -1 - with open("calendars/rapla" + kurs + ".ical") as f: - calendar = icalendar.Calendar.from_ical(f.read()) - # events = calendar.walk("VEVENT") file = open("calendars/list.json", "r+") jsoncal = json.load(file) jsoncal.update({kurs: "rapla" + kurs + ".ical"}) diff --git a/parseICAL.py b/parseICAL.py new file mode 100644 index 0000000..96bc6b4 --- /dev/null +++ b/parseICAL.py @@ -0,0 +1,62 @@ +import icalendar +import datetime +import recurring_ical_events + + +def getWeek(weekstart: datetime, file: str, showsat: bool): + if weekstart == "today": + start_date = datetime.date.today() + else: + start_date = weekstart + start_date -= datetime.timedelta(days=start_date.weekday() % 7) + end_date = start_date + datetime.timedelta(days=7) + with open("calendars/" + file) as f: + calendar = icalendar.Calendar.from_ical(f.read()) + events = recurring_ical_events.of(calendar).between(start_date, end_date) + eventl = [] + for event in events: + estart = event["DTSTART"].dt + formstart = str(estart.hour) + ":" + str(estart.minute) + eend = event["DTEND"].dt + formend = str(eend.hour) + ":" + str(eend.minute) + forml = [formstart, formend] + for i in range(2): + if len(forml[i]) != 5: + if forml[i][-2] == ":": + forml[i] = forml[i] + "0" + if forml[i][1] == ":": + forml[i] = "0" + forml[i] + formstart = forml[0] + formend = forml[1] + eventdict = { + "start": formstart, + "end": formend, + "dur": str(event["DTEND"].dt - event["DTSTART"].dt)[:-3], + "name": event["SUMMARY"], + "room": event["LOCATION"], + "weekday": estart.weekday(), + "day": estart.day + } + eventl += [eventdict] + return eventl, daylist(start_date, showsat) + + +shortnames = ["mon", "tue", "wed", "thu", "fri", "sat"] +longnames = ["Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag"] + + +def daylist(weekstart: datetime, showsat: bool): + weekday = weekstart + dayl = [] + if showsat: + r = 6 + else: + r = 5 + for i in range(r): + dayl += [{ + "day": weekday.day, + "short": shortnames[i], + "long": longnames[i], + }] + weekday += datetime.timedelta(days=1) + return dayl diff --git a/static/cal.scss b/static/cal.scss new file mode 100644 index 0000000..43f014e --- /dev/null +++ b/static/cal.scss @@ -0,0 +1,133 @@ +:root { + --numDays: 5; + --numHours: 10; + --timeHeight: 60px; + --calBgColor: #fff1f8; + --eventBorderColor: #f2d3d8; + --eventColor1: #ffd6d1; + --eventColor2: #fafaa3; + --eventColor3: #e2f8ff; + --eventColor4: #d1ffe6; +} + +.calendar { + display: grid; + gap: 10px; + grid-template-columns: auto 1fr; + margin: 2rem; +} + +.timeline { + display: grid; + grid-template-rows: repeat(var(--numHours), var(--timeHeight)); +} + +.days { + display: grid; + grid-column: 2; + gap: 5px; + grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); +} + +.events { + display: grid; + grid-template-rows: repeat(var(--numHours), var(--timeHeight)); + border-radius: 5px; + background: var(--calBgColor); +} + +// Place on Timeline +.start-1000 { + grid-row-start: 2; +} + +.start-1200 { + grid-row-start: 4; +} + +.start-1300 { + grid-row-start: 5; +} + +.start-1400 { + grid-row-start: 6; +} + +.end-1200 { + grid-row-end: 4; +} + +.end-1300 { + grid-row-end: 5; +} + +.end-1500 { + grid-row-end: 7; +} + +.end-1600 { + grid-row-end: 8; +} + +.end-1700 { + grid-row-end: 9; +} + +// Events + +.title { + font-weight: 600; + margin-bottom: 0.25rem; +} + +.event { + border: 1px solid var(--eventBorderColor); + border-radius: 5px; + padding: 0.5rem; + margin: 0 0.5rem; + background: white; +} + +.space, +.date { + height: 60px +} + +// Global / Etc + +body { + font-family: system-ui, sans-serif; +} + +.corp-fi { + background: var(--eventColor1); +} + +.ent-law { + background: var(--eventColor2); +} + +.writing { + background: var(--eventColor3); +} + +.securities { + background: var(--eventColor4); +} + +.date { + display: flex; + gap: 1em; +} + +.date-num { + font-size: 3rem; + font-weight: 600; + display: inline; +} + +.date-day { + display: inline; + font-size: 3rem; + font-weight: 100; +} \ No newline at end of file diff --git a/static/style.css b/static/style.css index e518228..50bc523 100644 --- a/static/style.css +++ b/static/style.css @@ -10,7 +10,7 @@ body } input { - width: 100px; + width: 150px; height: 50px } diff --git a/templates/kurs.html b/templates/kurs.html new file mode 100644 index 0000000..ab95eeb --- /dev/null +++ b/templates/kurs.html @@ -0,0 +1,22 @@ + + + + + Kurs wählen + + + + + {% if not detected[1] %} +

Wir haben {{ detected[0] }} als deinen Kurs ermittelt. Falls er nicht stimmt, kannst du ihn unten auswählen.

+
+ +
+ {% else %} +

Dein Kurs konnte leider nicht ermittelt werden. Klicke den Button, um direkt zur Auswahl zu kommen.

+ {% endif %} +
+ +
+ + \ No newline at end of file diff --git a/templates/plan.html b/templates/plan.html new file mode 100644 index 0000000..9648f60 --- /dev/null +++ b/templates/plan.html @@ -0,0 +1,51 @@ + + + + + Vorlesungsplan + + + +
+
+
+
08
+
09
+
10
+
11
+
12
+
13
+
14
+
15
+
16
+
17
+
18
+
19
+
+
+ {% for e in range (eventdays|length) %} +
+
+

{{ eventdays[e]["day"] }}

+

{{ eventdays[e]["long"] }}

+
+
+ {% for i in events %} + {% if i["weekday"] == e %} +
+

{{ i["name"] }}

+

{{ i["room"] }}

+

{{ i["dur"] }}

+

{{ i["start"] }} - {{ i["end"] }}

+
+ {% endif %} + {% endfor %} +
+
+ {% endfor %} +
+
+ + + + \ No newline at end of file