24 Commits

Author SHA1 Message Date
447800ad73 complete rapla async 2024-04-10 13:10:56 +02:00
ccb088e36d optimize mensa async 2024-04-09 11:04:18 +02:00
0d31d84d48 complete mensa async 2024-04-09 08:50:05 +02:00
490ee7f02f complete dualis async 2024-04-09 00:16:59 +02:00
211ec18887 async test 2024-04-08 20:48:34 +02:00
56ec2cfb15 Refreshs 2024-04-02 17:58:30 +02:00
4260ca0e92 Mensa-Fix 2024-01-22 18:10:41 +01:00
3b8cf7e53d Nested Noten 2024-01-11 14:50:28 +01:00
b8c2bea4ae Content Security Policy 2023-12-15 15:19:26 +01:00
937bf9787c Analytics-Link 2023-12-15 10:18:37 +01:00
c77f6017d4 Quickfix Rapla-Pipeline 2023-12-15 09:55:40 +01:00
8ddd7a972c Navbar-Hover + Analytics 2023-12-15 09:29:59 +01:00
1c971975dd Plan-Buttons 2023-12-14 22:29:39 +01:00
2d964e0a29 Plan-Animation 2023-12-14 22:25:19 +01:00
4f9fb5ac4d Quickfix Plan-Anon 2023-12-14 22:11:51 +01:00
fc97411177 Errorpage + RAPLA-Styling 2023-12-14 21:43:13 +01:00
a6372086db Launch prep 2023-12-14 18:49:45 +01:00
64115fe2d0 Merge remote-tracking branch 'origin/Frontend' into Backend
# Conflicts:
#	static/style.css
#	templates/index.html
2023-12-14 13:13:12 +01:00
07d4dd098f Dasboard 2023-12-14 13:10:22 +01:00
65d14f51cd Dashboard 2023-12-14 13:09:36 +01:00
15954af651 Plan + Tests 2023-12-13 18:52:59 +01:00
ae8b0206fc Plan prep frontend 2023-12-12 23:43:29 +01:00
b302ce4d49 Plan prep backend 2023-12-12 23:29:40 +01:00
3a3b6c895c plan.html Quickfix 2023-12-11 15:14:43 +01:00
32 changed files with 2847 additions and 654 deletions

View File

@ -1,16 +1,22 @@
import time
import asyncio
import icalendar
import datetime
import recurring_ical_events
from fetchMENSA import getMeals
import pytz
shortnames = ["mon", "tue", "wed", "thu", "fri", "sat"]
longnames = ["Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag"]
months = ["Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober",
"November", "Dezember"]
def getWeek(weekstart: datetime, file: str, showsat: bool):
async def getWeek(weekstart: datetime, file: str, showsat: bool):
"""
Liefert alle Events einer Woche zurück.
Wochenstart wird automatisch auf den Montag der Woche gelegt.
Liefert alle Events einer Woche zurück. \n
Wochenstart wird automatisch auf den Montag der Woche gelegt. \n
:param weekstart:
:param file:
:param showsat:
@ -20,16 +26,30 @@ def getWeek(weekstart: datetime, file: str, showsat: bool):
start_date = datetime.date.today()
else:
start_date = weekstart
nextw = start_date + datetime.timedelta(weeks=1)
prevw = start_date - datetime.timedelta(weeks=1)
start_date -= datetime.timedelta(days=start_date.weekday() % 7)
end_date = start_date + datetime.timedelta(days=7)
end_date = start_date + datetime.timedelta(days=6)
mon = months[start_date.month - 1]
if start_date.month != end_date.month:
if start_date.year != end_date.year:
mon += " " + str(start_date.year) + " | " + months[end_date.month - 1]
else:
mon += " | " + months[end_date.month-1]
mon += " " + str(end_date.year)
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
if str(estart.tzinfo) != "Europe/Berlin":
estart = estart.astimezone(pytz.timezone("Europe/Berlin"))
formstart = str(estart.hour) + ":" + str(estart.minute)
eend = event["DTEND"].dt
if str(eend.tzinfo) != "Europe/Berlin":
eend = eend.astimezone(pytz.timezone("Europe/Berlin"))
formend = str(eend.hour) + ":" + str(eend.minute)
forml = [formstart, formend]
for i in range(2):
@ -50,10 +70,10 @@ def getWeek(weekstart: datetime, file: str, showsat: bool):
"day": estart.day
}
eventl += [eventdict]
return eventl, daylist(start_date, showsat)
return eventl, await daylist(start_date, showsat), prevw, nextw, mon
def daylist(weekstart: datetime, showsat: bool):
async def daylist(weekstart: datetime, showsat: bool):
"""
Gibt die Essen einer Woche zurück.
:param weekstart:
@ -66,15 +86,19 @@ def daylist(weekstart: datetime, showsat: bool):
r = 6
else:
r = 5
essen = []
for i in range(r):
essen = getMeals(weekday)
essen += [getMeals(weekday)]
dayl += [{
"day": weekday.day,
"short": shortnames[i],
"long": longnames[i],
"mensa": essen
"mensa": i
}]
weekday += datetime.timedelta(days=1)
essenl = await asyncio.gather(*essen, return_exceptions=True)
for day in range(r):
dayl[day]["mensa"] = essenl[day]
return dayl

View File

@ -1,3 +1,2 @@
{
}

View File

@ -1,9 +1,10 @@
import requests
import urllib.parse
import time
from bs4 import BeautifulSoup
from flask import redirect, url_for
from init import Dualis
import asyncio
import httpx
headers = {
'Cookie': 'cnsc=0',
@ -16,31 +17,32 @@ headers = {
url = "https://dualis.dhbw.de/scripts/mgrqispi.dll"
def checkUser(email: str, password: str):
async def checkUser(email: str, password: str):
"""
Erhält von Dualis den Token und Cookie für User.
:param email:
:param password:
:return (Token, Session):
"""
s = requests.Session()
async with httpx.AsyncClient() as s:
fpw = urllib.parse.quote(password, safe='', encoding=None, errors=None)
fmail = urllib.parse.quote(email, safe='', encoding=None, errors=None)
payload = 'usrname=' + fmail + '&pass=' + fpw + ('&ARGUMENTS=clino%2Cusrname%2Cpass%2Cmenuno%2Cmenu_type%2Cbrowser'
'%2Cplatform&APPNAME=CampusNet&PRGNAME=LOGINCHECK')
response = s.post(url, headers=headers, data=payload)
content = (f'usrname={fmail}&pass={fpw}&ARGUMENTS=clino%2Cusrname%2Cpass%2Cmenuno%2Cmenu_type%2Cbrowser'
f'%2Cplatform&APPNAME=CampusNet&PRGNAME=LOGINCHECK')
response = await s.post(url=url, headers=headers, content=content)
header = response.headers
try:
refresh = header["REFRESH"]
arg = refresh.find("=-N") + 3
komma = refresh[arg:].find(",")
cookie = s.cookies.get("cnsc")
except KeyError:
return -2, s
token = refresh[arg:komma + arg]
return token, s
return token, cookie
def getKurs(token: int, cookie: str):
async def getKurs(token: int, cookie: str):
"""
Bestimmt aus der ersten Prüfung den Kursbezeichner des Users.
TODO: Umstellen auf Bezeichner INKL. Standort
@ -51,12 +53,12 @@ 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={})
async with httpx.AsyncClient as s:
response = await s.get(url=f"{url}?APPNAME=CampusNet&PRGNAME=COURSERESULTS&ARGUMENTS=-N{token},-N000307,",
headers=headers)
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={})
response = await s.get(url=f"{url}{link[21:]}", headers=headers)
html = BeautifulSoup(response.text, 'lxml')
content = html.body.find('td', attrs={'class': 'level02'}).text
start = content.find(" ") + 4
@ -67,18 +69,18 @@ def getKurs(token: int, cookie: str):
return kurs
def logOut(token: int, cookie: str):
async def logOut(token: int, cookie: str):
"""
Invalidiert Token und Cookie bei Dualis.
:param token:
:param cookie:
"""
headers["Cookie"] = "cnsc=" + cookie
requests.request("GET", url + "?APPNAME=CampusNet&PRGNAME=LOGOUT&ARGUMENTS=-N" + str(token)
+ ", -N001", headers=headers, data={})
async with httpx.AsyncClient() as s:
await s.get(url=f"{url}?APPNAME=CampusNet&PRGNAME=LOGOUT&ARGUMENTS=-N{token}, -N001", headers=headers)
def getSem(token: int, cookie: str):
async def getSem(token: int, cookie: str):
"""
Liefert die Liste aller auf Dualis verfügbaren Semester.
:param token:
@ -87,9 +89,9 @@ def getSem(token: int, cookie: str):
"""
headers["Cookie"] = "cnsc=" + cookie
token = str(token)
response = requests.request("GET", url +
"?APPNAME=CampusNet&PRGNAME=COURSERESULTS&ARGUMENTS=-N" + token + ",-N000307,",
headers=headers, data={})
async with httpx.AsyncClient() as s:
response = await s.get(url=f"{url}?APPNAME=CampusNet&PRGNAME=COURSERESULTS&ARGUMENTS=-N{token},-N000307,",
headers=headers)
html = BeautifulSoup(response.text, 'lxml')
select = html.find('select')
select = select.find_all(value=True)
@ -101,7 +103,7 @@ def getSem(token: int, cookie: str):
return optlist
def getResults(token, cookie: str, resl: str):
async def getResults(token, cookie: str, resl: str):
"""
Liefert die Liste aller Prüfungsergebnisse eines Semesters.
:param token:
@ -110,42 +112,57 @@ def getResults(token, cookie: str, resl: str):
:return [[Name, Note, Credits], ...]:
"""
headers["Cookie"] = "cnsc=" + cookie
response = requests.request("GET", url + "?APPNAME=CampusNet&PRGNAME=COURSERESULTS&ARGUMENTS=-N" + token +
",-N000307," + ",-N" + resl, headers=headers, data={})
async with httpx.AsyncClient() as s:
response = await s.get(
url=f"{url}?APPNAME=CampusNet&PRGNAME=COURSERESULTS&ARGUMENTS=-N{token},-N000307,,-N{resl}",
headers=headers)
html = BeautifulSoup(response.content.decode("utf-8"), 'lxml')
table = html.find('table', attrs={"class": "nb list"})
body = table.find("tbody")
vorl = body.find_all("tr")
vorlist = []
tasks = []
i = 0
for row in vorl:
cols = row.find_all("td")
col = [e.text.strip() for e in cols]
col = [[e.text.strip()] for e in cols]
if len(col) != 0:
if len(col[4]) == 0:
col[2] = getPruefung(row.find("a")["href"])
if len(col[4][0]) == 0:
tasks += [getPruefung(s, row.find("a")["href"])]
col[2] = i
i += 1
vorlist += [col[1:4]]
return vorlist
notlisted = await asyncio.gather(*tasks, return_exceptions=True)
for i in vorlist:
for e in range(0, len(i)):
if isinstance(i[e], int):
i[e] = notlisted[i[e]]
return vorlist[:-1]
def getPruefung(url):
async def getPruefung(s, url):
"""
Ermittelt Noten "geschachtelter" Prüfungen, die nicht auf der Hauptseite angezeigt werden.
TODO: Namen der spezifischen Prüfungen auch zurückgeben, um Zusammensetzung zu spezifizieren.
:param s:
:param url:
:return:
:return list:
"""
response = requests.request("GET", "https://dualis.dhbw.de" + url, headers=headers, data={})
response = await s.get("https://dualis.dhbw.de" + url, headers=headers)
html = BeautifulSoup(response.content.decode("utf-8"), 'lxml')
table = html.find('table')
pruefung = table.find_all("tr")
ret = " "
ret = []
for row in pruefung:
cols = row.find_all("td")
col = [e.text.strip() for e in cols]
if len(col) == 6:
ret = col[3][:3]
if ret[1] != ",":
ret = "noch nicht gesetzt"
if len(col) == 6 and len(col[3]) <= 13:
if len(col[3]) != 0:
ret += [col[0] + ": " + col[3][:3]]
if ret[-1][0] == ':':
ret[-1] = "Gesamt" + ret[-1]
if len(ret) == 0:
ret = ["Noch nicht gesetzt"]
return ret

View File

@ -1,13 +1,16 @@
import json
from init import db, Meals, scheduler
import asyncio
from init import db, Meals, scheduler, app
import datetime
import requests
import time
import httpx
nomeal = 'Essen nicht (mehr) verfügbar'
def getMeals(day: datetime):
async def getMeals(day: datetime):
"""
Liefert alle Mahlzeiten eines Tages. \n
Befinden sie sich schon in der Datenbank, werden diese zurückgegeben. \n
@ -23,10 +26,10 @@ def getMeals(day: datetime):
essen += [i.name]
essen.sort(key=len, reverse=True)
return essen
return getMealsFromAPI(day, dbentry=True)
return await getMealsFromAPI(day, dbentry=True)
def getMealsFromAPI(day: str, dbentry: bool = False):
async def getMealsFromAPI(day: str, dbentry: bool = False):
"""
Fragt die Mensa-API nach den Mahlzeiten eines Tages ab. \n
Wenn dbentry: Schreibt die Ergebnisse in die Datenbank. \n
@ -35,12 +38,11 @@ def getMealsFromAPI(day: str, dbentry: bool = False):
:param dbentry:
:return [Name1, Name2, ...]:
"""
url = "https://dh-api.paulmartin.cloud/plans/" + day + "?canteens=erzberger"
response = requests.request("GET", url)
async with httpx.AsyncClient() as s:
response = await s.get(url=f"https://dh-api.paulmartin.cloud/plans/{day}?canteens=erzberger")
response = response.content
jres = json.loads(response.decode("utf-8"))
essen = []
try:
num = len(jres["data"][0]["lines"])
for i in range(num):
@ -67,7 +69,7 @@ def getMealsFromAPI(day: str, dbentry: bool = False):
essen += [name]
if dbentry:
mid = int(time.time()*1000) % 100000
mid = int(time.time() * 1000) % 100000
neu = Meals(date=day, name=name, id=mid, vegan=vegan, vegetarian=veget, schwein=schwein)
db.session.add(neu)
db.session.commit()
@ -99,22 +101,25 @@ def formatDay(day: datetime):
:param day:
:return str:
"""
if day.month < 10:
mon = "0" + str(day.month)
else:
mon = str(day.month)
if day.day < 10:
tag = "0" + str(day.day)
else:
tag = str(day.day)
day = str(day.year) + "-" + str(day.month) + "-" + tag
day = str(day.year) + "-" + mon + "-" + tag
return day
@scheduler.task('cron', id="refreshMeals", hour='8-11', day_of_week='*', minute='15', week='*', second='30')
def refreshMeals():
async def refreshMeals():
"""
Aktualisiert immer vormittags alle Mahlzeiten in der Datenbank. \n
Datenbankeinträge werden ersetzt, wenn die API andere Mahlzeiten liefert.
"""
print("Aktualisiere Essenspläne...\n")
with scheduler.app.app_context():
table = Meals.query.all()
dates = []
for i in table:
@ -123,13 +128,20 @@ def refreshMeals():
for i in range(len(dates)):
dates[i] = formatDay(dates[i])
for i in dates:
apinames = getMealsFromAPI(i)
apinames = await getMealsFromAPI(i)
apinames = await getMealsFromAPI(i)
dbmeals = Meals.query.filter_by(date=i).all()
dbnames = []
for m in dbmeals:
dbnames += [m.name]
if set (dbnames) != set(apinames) and nomeal not in apinames:
if set(dbnames) != set(apinames) and nomeal not in apinames:
for n in dbnames:
db.session.delete(Meals.query.filter_by(date=i, name=n).first())
db.session.commit()
getMealsFromAPI(i, True)
await getMealsFromAPI(i, True)
@scheduler.task('cron', id="mensaschedule", hour='8-11', day_of_week='*', minute='*/15', week='*', second='5')
def mensaschedule():
with app.app_context():
asyncio.run(refreshMeals())

View File

@ -1,9 +1,24 @@
import urllib.error
from urllib.request import urlretrieve
import asyncio
import httpx
import icalendar
import json
import recurring_ical_events
from init import scheduler
from init import scheduler, app
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 parseURL(url: str):
@ -34,7 +49,7 @@ def parseURL(url: str):
return 0
def getNewRapla(url: str):
async def getNewRapla(url: str):
"""
Speichert den iCal eines Raplas auf dem Server. \n
Gibt Namen der Datei zurück. \n
@ -50,16 +65,18 @@ def getNewRapla(url: str):
try:
urlretrieve(url, "calendars/rapla" + kurs + ".ical")
async with httpx.AsyncClient() as s:
response = await fetchPlan(s, url)
writeToFile(f"calendars/rapla{kurs}.ical", response)
except urllib.error.URLError:
return -1
file = open("calendars/list.json", "r+")
jsoncal = json.load(file)
jsoncal.update({kurs: ["rapla" + kurs + ".ical", url]})
jsoncal.update({kurs: [f"rapla{kurs}.ical", url]})
file.close()
file = open("calendars/list.json", "w")
json.dump(jsoncal, file, indent=4)
return "rapla" + kurs + ".ical"
return f"rapla{kurs}.ical"
def getIcal(kurs: str):
@ -93,13 +110,23 @@ def getRaplas():
return sorted(kursl), sorted(filel), sorted(urll)
@scheduler.task("interval", id="refreshRapla", minutes=30)
def refreshRapla():
async def refreshRapla():
"""
Aktualisiert alle 30 Minuten alle gespeicherten Raplas.
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)):
urlretrieve(urll[i], "calendars/"+filel[i])
print("Update die Kalender...")
print(f"Update Rapla: {filel[i][:-5]}")
jobl += [fetchPlan(s, urll[i])]
callist = await asyncio.gather(*jobl, return_exceptions=True)
for cal in range(len(callist)):
writeToFile(f"calendars/{filel[cal]}", callist[cal])
@scheduler.task('cron', id="raplaschedule", hour='*', day_of_week='*', minute='*/15', week='*')
def raplaschedule():
with app.app_context():
asyncio.run(refreshRapla())

40
genstarts.py Normal file
View File

@ -0,0 +1,40 @@
from flask import url_for
css = ""
for se in ("start", "end"):
acht = 0
for i in range(0, 28):
if i % 4 == 0:
acht += 100
h = str(acht)
else:
h = str(acht + (i % 4 * 15))
if len(h) == 3:
h = "0" + h
css += "." + se + "-" + h + " {\n"
if se == "start":
css += "grid-row-" + se + ": " + "1" + "\n}\n"
else:
css += "grid-row-" + se + ": " + "4" + "\n}\n"
css += "\n\n\n"
for se in ("start", "end"):
acht = 700
for i in range(0, 45):
if i % 4 == 0:
acht += 100
h = str(acht)
else:
h = str(acht + (i % 4 * 15))
if len(h) == 3:
h = "0" + h
css += "." + se + "-" + h + " {\n"
css += "grid-row-" + se + ": " + str(i+1) + "\n}\n"
css += "\n\n\n"
f = open("static/cal.scss", "a")
f.write ("\n // Generated by genstarts.py\n\n")
f.write(css)
f.close()

View File

@ -1,6 +1,6 @@
#!/bin/sh
#Erstellt alle benötigten MySQL-Tabellen.
mysql -e "USE paulmrtn_DUALHUB; CREATE TABLE user ( id int NOT NULL, email VARCHAR(255), password VARCHAR(255), name VARCHAR(255), kurs VARCHAR (15), PRIMARY KEY (ID), UNIQUE (ID, EMAIL) );"
mysql -e "USE paulmrtn_DUALHUB; CREATE TABLE user ( id int NOT NULL, email VARCHAR(255), name VARCHAR(255), kurs VARCHAR (15), PRIMARY KEY (ID), UNIQUE (ID, EMAIL) );"
mysql -e "USE paulmrtn_DUALHUB; CREATE TABLE dualis ( uid int NOT NULL, token VARCHAR(255), result_list VARCHAR(15), token_created INT, PRIMARY KEY (uid));"
mysql -e "USE paulmrtn_DUALHUB; CREATE TABLE meals ( id int NOT NULL, date date, name VARCHAR(200), vegetarian tinyint(1), vegan tinyint(1), schwein tinyint(1), PRIMARY KEY (id));"

23
init.py
View File

@ -1,7 +1,9 @@
import apscheduler
from flask import Flask
from flask_login import LoginManager, UserMixin
from flask_sqlalchemy import SQLAlchemy
from talisman import Talisman
from flask_talisman import Talisman
from get_mysql import get_mysql
import atexit
from flask_apscheduler import APScheduler
@ -43,7 +45,6 @@ class User(UserMixin, db.Model):
"""
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(255), unique=True)
password = db.Column(db.String(255))
name = db.Column(db.String(255))
kurs = db.Column(db.String(15))
@ -55,7 +56,17 @@ class Dualis(db.Model):
token = db.Column(db.String(255), unique=True)
uid = db.Column(db.Integer, primary_key=True)
token_created = db.Column(db.Integer)
result_list = db.Column(db.String(15))
semester = db.Column(db.String(15))
class Semesterlist(db.Model):
"""
Datenbank-Modell für Semester-Liste.
"""
uid = db.Column(db.Integer)
semestername = db.Column(db.String(25))
semesterid = db.Column(db.String(15))
itemid = db.Column(db.Integer, primary_key=True)
class Meals(db.Model):
@ -70,9 +81,11 @@ class Meals(db.Model):
schwein = db.Column(db.Boolean)
scheduler = APScheduler()
scheduler = APScheduler ()
app = create()
Talisman(app)
def_src = ["*.paulmartin.cloud", '\'self\'']
Talisman(app, content_security_policy={"default-src": def_src, "script-src": def_src # + ["'unsafe-inline'"]
})
scheduler.init_app(app)
scheduler.start()
scheduler.api_enabled = True

View File

@ -1,3 +1,6 @@
from init import Semesterlist, User
def getCookie(cookies):
"""
Liefert (letzten) Cookie der Cookies-Liste zurück.
@ -8,3 +11,16 @@ def getCookie(cookies):
for c in cookies:
cookie = c.value
return cookie
def getSemesterList(uid):
semesterlist = Semesterlist.query.filter_by(uid=uid).all()
semester = []
for s in semesterlist:
semester += [[s.semestername, s.semesterid]]
semester.sort(key=lambda x: x[-1], reverse=True)
return semester
def loadUser(uid):
return User.query.filter_by(id=uid).first()

View File

@ -1,11 +1,17 @@
beautifulsoup4
Flask
beautifulsoup4~=4.12.2
Flask~=3.0.3
Flask_APScheduler
Flask_Login
flask_sqlalchemy
icalendar
icalendar~=5.0.11
recurring_ical_events
Requests
Requests~=2.31.0
talisman
Werkzeug
Werkzeug~=3.0.0
lxml
bs4~=0.0.1
pytz~=2023.3.post1
asyncio~=3.4.3
httpx~=1.0.0b0
celery~=5.4.0rc2

View File

@ -7,10 +7,12 @@ from werkzeug.security import generate_password_hash, check_password_hash
import hashlib
import datetime
import time
import random
import fetchDUALIS
import fetchMENSA
import fetchRAPLA
import requesthelpers
from requesthelpers import *
from fetchRAPLA import *
from calendar_generation import getWeek
from init import *
@ -25,45 +27,63 @@ def index():
return redirect(url_for("login"))
@app.route("/welcome")
@app.route("/dashboard")
@login_required
def welcome():
"""
Interim Homepage
Dashboard
:return HTML:
"""
if not current_user.kurs:
return redirect(url_for("getKurs", next=url_for(request.endpoint)))
sel = request.args.get("sel")
if not sel:
sel = "theorie"
kurs = current_user.kurs
name = current_user.name
return render_template('index.html', headermessage='DualHub', message="Hallo, "
+ name + " (" + kurs + ")")
if sel == "theorie":
t = ""
p = "hidden"
else:
t = "hidden"
p = ""
return render_template('dashboard.html', kurs=kurs, name=name, theorie=t, praxis=p)
@app.route("/backendpoc/noten")
@app.route("/theorie/noten", methods=["GET", "POST"])
@login_required
def displayNoten():
async def displayNoten():
"""
Zeigt die Noten aus Dualis an. Hierfür ist ein aktives Token nötig.
:return HTML:
"""
d = Dualis.query.filter_by(uid=current_user.id).first()
if request.method == "POST":
d.semester = request.form.get("sem")
db.session.commit()
if not d.semester:
return redirect(url_for("getSemester", next=url_for(request.endpoint)))
t = d.token
sem = d.result_list
chosensemester = d.semester
c = request.cookies.get("cnsc")
timeout = fetchDUALIS.timeOut(d, c, "displayNoten")
if timeout:
return timeout
res = fetchDUALIS.getResults(t, c, sem)
return render_template("noten.html", noten=res, semester=fetchDUALIS.getSem(t, c), sel=sem)
semester = getSemesterList(current_user.id)
noten = await fetchDUALIS.getResults(t, c, chosensemester)
return render_template("noten.html", noten=noten, semester=semester, sel=chosensemester, s="n", praxis="hidden")
@app.route("/backendpoc/plan", methods=["GET"])
@app.route("/plan", methods=["GET"])
@login_required
def displayRapla():
async def displayRapla():
"""
Zeigt den Stundenplan für eingeloggte User an. \n
TODO: Persönliche Filter, Notizen, Essensvorlieben etc. berücksichtigen.
:return HTML:
"""
if not current_user.kurs:
return redirect(url_for("getKurs", next=url_for(request.endpoint)))
week = request.args.get("week")
if week:
week = datetime.datetime.strptime(week, "%Y-%m-%d")
@ -72,12 +92,14 @@ def displayRapla():
samstag = request.args.get("samstag")
if not samstag:
samstag = False
events = getWeek(week, fetchRAPLA.getIcal(current_user.kurs), samstag)
return render_template("plan.html", events=events[0], eventdays=events[1])
events = await getWeek(week, fetchRAPLA.getIcal(current_user.kurs), samstag)
return render_template("plan-user.html", events=events[0], eventdays=events[1],
name=current_user.name, prev=str(events[2])[:10], next=str(events[3])[:10], mon=events[4],
s="p", praxis="hidden")
@app.route("/backendpoc/plan/<string:kurs>")
def displayPlan(kurs):
@app.route("/plan/<string:kurs>")
async def displayPlan(kurs):
"""
Zeigt den Stundenplan ohne Login an. \n
Präferenzen werden nicht berücksichtigt.
@ -94,18 +116,20 @@ def displayPlan(kurs):
return redirect(url_for("displayRapla"))
except AttributeError:
pass
plan = fetchRAPLA.getIcal(kurs.upper())
kurs = kurs.upper()
plan = fetchRAPLA.getIcal(kurs)
if plan:
samstag = request.args.get("samstag")
if not samstag:
samstag = False
events = getWeek(week, plan, samstag)
return render_template("plan-anon.html", events=events[0], eventdays=events[1])
events = await getWeek(week, plan, samstag)
return render_template("plan-anon.html", events=events[0], eventdays=events[1], kurs=kurs,
prev=str(events[2])[:10], next=str(events[3])[:10], mon=events[4], praxis="hidden")
else:
return redirect(url_for("login"))
@app.route("/backendpoc/set-up")
@app.route("/set-up")
def redKurs():
"""
Setup beginnt mit Kurs.
@ -114,9 +138,9 @@ def redKurs():
return redirect(url_for("getKurs"))
@app.route("/backendpoc/set-up/kurs")
@app.route("/set-up/kurs")
@login_required
def getKurs():
async def getKurs():
"""
Automatische Kurs-Auswahl. \n
Aktives Dualis-Token benötigt.
@ -130,8 +154,11 @@ def getKurs():
return timeout
e = False
if not current_user.kurs:
kurs = fetchDUALIS.getKurs(d.token, cookie)
kurs = await fetchDUALIS.getKurs(d.token, cookie)
if kurs != 0:
if not fetchRAPLA.getIcal(kurs):
return render_template('kurs.html', detected=(kurs, e), s="s", theorie="hidden", praxis="hidden",
file=False)
current_user.kurs = kurs
db.session.commit()
else:
@ -143,23 +170,31 @@ def getKurs():
else:
e = True
kurs = ""
return render_template('kurs.html', detected=(kurs, e))
return render_template('kurs.html', detected=(kurs, e), s="s", theorie="hidden", praxis="hidden", file=True)
@app.route("/backendpoc/set-up/semester")
@app.route("/set-up/semester")
@login_required
def getSemester():
async def getSemester():
"""
Manuelle Semester-Auswahl.
:return HTML:
"""
t = Dualis.query.filter_by(uid=current_user.id).first().token
c = request.cookies.get("cnsc")
return render_template("semester.html", semester=fetchDUALIS.getSem(t, c))
semesterlist = Semesterlist.query.filter_by(uid=current_user.id).all()
if not semesterlist:
semester = await fetchDUALIS.getSem(t, c)
for i in semester:
semitem = Semesterlist(semestername=i[0], semesterid=i[1], uid=current_user.id, itemid=current_user.id*int(i[1][-7:])//1000000)
db.session.add(semitem)
db.session.commit()
else:
semester = getSemesterList(current_user.id)
return render_template("semester.html", semester=semester, s="s", theorie="hidden", praxis="hidden")
@app.route("/backendpoc/set-up/semester", methods=["POST"])
@app.route("/set-up/semester", methods=["POST"])
@login_required
def setSemester():
"""
@ -170,12 +205,12 @@ def setSemester():
if not n:
n = url_for("welcome")
d = Dualis.query.filter_by(uid=current_user.id).first()
d.result_list = request.form.get("sem")
d.semester = request.form.get("sem")
db.session.commit()
return redirect(n)
@app.route("/backendpoc/set-up/rapla")
@app.route("/set-up/rapla")
@login_required
def chooseRaplas():
"""
@ -183,12 +218,12 @@ def chooseRaplas():
:return HTML:
"""
r = getRaplas()
return render_template("rapla.html", raplas=r)
return render_template("rapla.html", raplas=r, s="s", theorie="hidden", praxis="hidden")
@app.route("/backendpoc/set-up/rapla", methods=["POST"])
@app.route("/set-up/rapla", methods=["POST"])
@login_required
def getRapla():
async def getRapla():
"""
Verarbeitet die Eingabe von chooseRaplas().
:return HTML:
@ -198,29 +233,29 @@ def getRapla():
if file == url == "None":
return redirect(url_for("chooseRaplas"))
if file != "None":
User.query.filter_by(id=current_user.id).first().kurs = file[5:-5]
loadUser(current_user.id).kurs = file[5:-5]
db.session.commit()
elif url != "None":
file = getNewRapla(url)
file = await getNewRapla(url)
if type(file) is not int:
User.query.filter_by(id=current_user.id).first().kurs = file[5:-5]
db.session.commit()
else:
return redirect(url_for("error", ecode=file + 900))
return redirect(url_for("error", ecode=900))
return redirect(url_for("welcome"))
@app.route("/backendpoc/log-in")
@app.route("/log-in")
def login():
"""
Login-Maske.
:return HTML:
"""
return render_template("login.html")
return render_template("login.html", theorie="hidden", praxis="hidden", s="s")
@app.route("/backendpoc/log-in", methods=["POST"])
def login_post():
@app.route("/log-in", methods=["POST"])
async def login_post():
"""
Verarbeitet die Eingabe von login(). \n
Falls der User schon angelegt ist, wird das Passwort verglichen. \n
@ -236,44 +271,29 @@ def login_post():
success = make_response(redirect(url_for("getKurs")))
user = User.query.filter_by(email=email).first()
newcookie = ""
if user:
dualis = Dualis.query.filter_by(uid=user.id).first()
if check_password_hash(user.password, password):
cookie = request.cookies.get("cnsc")
if not dualis.token or not fetchDUALIS.checkLifetime(dualis.token_created) or not cookie:
new_token = fetchDUALIS.checkUser(email, password)
dualis.token = new_token[0]
newcookie = requesthelpers.getCookie(new_token[1].cookies)
dualis.token_created = time.time()
db.session.commit()
else:
t = fetchDUALIS.checkUser(email, password)
t = await fetchDUALIS.checkUser(email, password)
if t[0] == -2:
return redirect(url_for("login", code=-2))
else:
user.password = generate_password_hash(password, method="pbkdf2:sha256")
if user:
dualis = Dualis.query.filter_by(uid=user.id).first()
dualis.token = t[0]
newcookie = requesthelpers.getCookie(t[1].cookies)
newcookie = t[1]
dualis.token_created = time.time()
db.session.commit()
login_user(user)
if user.kurs:
if not n:
if not dualis.semester:
success = make_response(redirect(url_for("getSemester")))
elif not n:
success = make_response(redirect(url_for("welcome")))
success.set_cookie("cnsc", value=newcookie, httponly=True, secure=True)
return success
t = fetchDUALIS.checkUser(email, password)
if t[0] == -2:
return redirect(url_for("login", code=-2))
else:
hashid = int(hashlib.sha1(email.encode("utf-8")).hexdigest(), 16) % (10 ** 8)
hashpw = generate_password_hash(password, method="pbkdf2:sha256")
pname = email.find(".") + 1
ename = min(email[pname:].find("."), email[pname:].find("@"))
name = email[pname:pname + ename].capitalize()
new_user = User(email=email, password=hashpw, name=name, id=hashid)
new_user = User(email=email, name=name, id=hashid)
db.session.add(new_user)
cookie = requesthelpers.getCookie(t[1].cookies)
@ -282,21 +302,20 @@ def login_post():
db.session.add(new_dualis)
db.session.commit()
login_user(new_user)
newcookie = cookie
success.set_cookie("cnsc", value=newcookie, httponly=True, secure=True)
success.set_cookie("cnsc", value=cookie, httponly=True, secure=True)
return success
@app.route("/backendpoc/log-out")
@app.route("/log-out")
@login_required
def logout():
async def logout():
"""
Loggt den User aus.
:return Empty Token:
"""
cookie = request.cookies.get("cnsc")
dualis = Dualis.query.filter_by(uid=current_user.id).first()
fetchDUALIS.logOut(dualis.token, cookie)
await fetchDUALIS.logOut(dualis.token, cookie)
dualis.token = None
db.session.commit()
logout_user()
@ -307,29 +326,30 @@ def logout():
return red
@app.route("/backendpoc/error<int:ecode>")
def error(ecode):
@app.route("/error")
def error():
"""
Error Page für custom-Errors. \n
TODO: Funktion depreciaten. Ersetzen durch Errors auf den entsprechenden Seiten.
:param ecode:
:return:
"""
if ecode == 900:
error = request.args.get("ecode")
if error == "900":
msg = "Ungültige RAPLA-URL! Sicher, dass der Link zum DHBW-Rapla führt?"
elif ecode == 899:
elif error == "899":
msg = "Der Kalender wurde nicht gefunden! Sicher, dass der Link korrekt ist?"
else:
msg = "Unbekannter Fehler!"
return render_template('index.html', message=msg, headermessage="DualHub")
msg = str(error)
return render_template('display-message.html', message=msg)
@app.route("/error")
@app.errorhandler(HTTPException)
def handle(e):
""""
HTTP-Exception-Handler
"""
return render_template('index.html', message=e, headermessage="DualHub")
return render_template('display-message.html', message=e)
if __name__ == "__main__":

View File

@ -1,13 +1,17 @@
:root {
--numDays: 5;
--numHours: 10;
--numHours: 13;
--numHalfHours: 23;
--numQuartHours: 45;
--timeHeight: 60px;
--calBgColor: #fff1f8;
--eventBorderColor: #f2d3d8;
--eventColor1: #ffd6d1;
--halfTimeHeight: 30px;
--quartTimeHeight: 15px;
--calBgColor: #3f2d2d;
--eventBorderColor: #000000;
--eventColor1: #b82424;
--eventColor2: #fafaa3;
--eventColor3: #e2f8ff;
--eventColor4: #d1ffe6;
--eventColor3: #1894bb;
--eventColor4: rgba(184, 36, 36, 0.65);
}
.calendar {
@ -17,9 +21,86 @@
margin: 2rem;
}
select {
width: 100%;
height: 40px;
border-radius: 5px;
color: white;
border-color: transparent;
background-color: var(--calBgColor);
text-align: center;
}
.userbuttons {
width: 95%;
position: absolute;
display: flex;
justify-content: end;
}
.changeweek {
font-size: 2.5em;
text-decoration: none;
font-weight: 600;
color: white;
}
.changeweek:hover {
color: transparent;
-webkit-text-fill-color: transparent;
-webkit-text-stroke: 3px white;
}
h2 {
font-weight: 300;
margin: 5px;
}
.headerblock {
display: flex;
justify-content: space-between;
padding-right: 20%;
padding-left: 20%;
}
.customheader {
display: flex;
justify-content: space-evenly;
}
button {
border-radius: 5px;
border-color: white;
color: white;
border-style: solid;
background: transparent;
font-weight: 600;
width: 30px;
height: 30px;
margin: 2px;
line-height: 25px;
}
button:hover {
cursor: pointer;
}
.plusbutton {
font-size: 1.5em;
}
form {
margin-top: 5px;
}
option {
width: 300px;
font-size: 1rem;
}
.timeline {
display: grid;
grid-template-rows: repeat(var(--numHours), var(--timeHeight));
grid-template-rows: repeat(12, 58px);
}
.days {
@ -31,50 +112,28 @@
.events {
display: grid;
grid-template-rows: repeat(var(--numHours), var(--timeHeight));
grid-template-rows: repeat(var(--numQuartHours), var(--quartTimeHeight));
border-radius: 5px;
color: black;
background: var(--calBgColor);
}
.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;
}
.title {
width: 70%;
font-weight: 600;
margin-bottom: 0.25rem;
margin-top: 0px;
margin-bottom: 0px;
}
.room,
.time {
display: block;
margin-top: 0px;
margin-bottom: 0px;
}
.room {
width: 70%;
}
.event {
@ -82,36 +141,28 @@
border-radius: 5px;
padding: 0.5rem;
margin: 0 0.5rem;
background: white;
color: white;
background: var(--eventColor4);
position: relative;
}
.event:hover {
transform: scale(102%);
}
.space,
.date {
height: 60px;
height: 100px;
}
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);
font-family: "Asap", "Calibri", "Arial", sans-serif;
color: white;
background-color: black;
}
.date {
display: flex;
margin-bottom: 1em;
gap: 1em;
}
@ -127,4 +178,675 @@ body {
font-weight: 100;
}
.cs1 {
margin-left: 220px;
padding: 20px;
}
nav ul {
list-style-type: none;
margin: 0;
padding: 0;
width: 200px;
background-color: #333;
position: fixed;
top: 0;
left: 0;
height: 100%;
overflow-y: auto;
font-size: 150%;
}
nav li {
border-top: none;
}
nav li.top {
border-style: solid;
border-radius: 5px;
border-color: white;
margin: 10px;
text-align: center;
}
nav a.top:hover {
background: transparent;
}
nav li.start a {
border-top: 3px solid white;
margin-top: 20px;
padding-top: 30px;
}
nav a.selected {
color: red;
background-color: black;
}
nav li.top.selected {
border-color: red;
}
nav li a {
display: block;
color: white;
padding: 12px 16px;
text-decoration: none;
}
nav li a:hover {
background-color: red;
color: black;
}
nav li .bottom {
position: absolute;
bottom: 0;
width: 200px;
}
.space {
width: 1%;
}
.footerdiv {
width: 100%;
justify-content: right;
display: flex;
}
a.footer {
color: white;
text-decoration: none;
}
a.footer:hover {
font-weight: 600;
}
.start-0100 {
grid-row-start: 1;
}
.start-0115 {
grid-row-start: 1;
}
.start-0130 {
grid-row-start: 1;
}
.start-0145 {
grid-row-start: 1;
}
.start-0200 {
grid-row-start: 1;
}
.start-0215 {
grid-row-start: 1;
}
.start-0230 {
grid-row-start: 1;
}
.start-0245 {
grid-row-start: 1;
}
.start-0300 {
grid-row-start: 1;
}
.start-0315 {
grid-row-start: 1;
}
.start-0330 {
grid-row-start: 1;
}
.start-0345 {
grid-row-start: 1;
}
.start-0400 {
grid-row-start: 1;
}
.start-0415 {
grid-row-start: 1;
}
.start-0430 {
grid-row-start: 1;
}
.start-0445 {
grid-row-start: 1;
}
.start-0500 {
grid-row-start: 1;
}
.start-0515 {
grid-row-start: 1;
}
.start-0530 {
grid-row-start: 1;
}
.start-0545 {
grid-row-start: 1;
}
.start-0600 {
grid-row-start: 1;
}
.start-0615 {
grid-row-start: 1;
}
.start-0630 {
grid-row-start: 1;
}
.start-0645 {
grid-row-start: 1;
}
.start-0700 {
grid-row-start: 1;
}
.start-0715 {
grid-row-start: 1;
}
.start-0730 {
grid-row-start: 1;
}
.start-0745 {
grid-row-start: 1;
}
.end-0100 {
grid-row-end: 4;
}
.end-0115 {
grid-row-end: 4;
}
.end-0130 {
grid-row-end: 4;
}
.end-0145 {
grid-row-end: 4;
}
.end-0200 {
grid-row-end: 4;
}
.end-0215 {
grid-row-end: 4;
}
.end-0230 {
grid-row-end: 4;
}
.end-0245 {
grid-row-end: 4;
}
.end-0300 {
grid-row-end: 4;
}
.end-0315 {
grid-row-end: 4;
}
.end-0330 {
grid-row-end: 4;
}
.end-0345 {
grid-row-end: 4;
}
.end-0400 {
grid-row-end: 4;
}
.end-0415 {
grid-row-end: 4;
}
.end-0430 {
grid-row-end: 4;
}
.end-0445 {
grid-row-end: 4;
}
.end-0500 {
grid-row-end: 4;
}
.end-0515 {
grid-row-end: 4;
}
.end-0530 {
grid-row-end: 4;
}
.end-0545 {
grid-row-end: 4;
}
.end-0600 {
grid-row-end: 4;
}
.end-0615 {
grid-row-end: 4;
}
.end-0630 {
grid-row-end: 4;
}
.end-0645 {
grid-row-end: 4;
}
.end-0700 {
grid-row-end: 4;
}
.end-0715 {
grid-row-end: 4;
}
.end-0730 {
grid-row-end: 4;
}
.end-0745 {
grid-row-end: 4;
}
.start-0800 {
grid-row-start: 1;
}
.start-0815 {
grid-row-start: 2;
}
.start-0830 {
grid-row-start: 3;
}
.start-0845 {
grid-row-start: 4;
}
.start-0900 {
grid-row-start: 5;
}
.start-0915 {
grid-row-start: 6;
}
.start-0930 {
grid-row-start: 7;
}
.start-0945 {
grid-row-start: 8;
}
.start-1000 {
grid-row-start: 9;
}
.start-1015 {
grid-row-start: 10;
}
.start-1030 {
grid-row-start: 11;
}
.start-1045 {
grid-row-start: 12;
}
.start-1100 {
grid-row-start: 13;
}
.start-1115 {
grid-row-start: 14;
}
.start-1130 {
grid-row-start: 15;
}
.start-1145 {
grid-row-start: 16;
}
.start-1200 {
grid-row-start: 17;
}
.start-1215 {
grid-row-start: 18;
}
.start-1230 {
grid-row-start: 19;
}
.start-1245 {
grid-row-start: 20;
}
.start-1300 {
grid-row-start: 21;
}
.start-1315 {
grid-row-start: 22;
}
.start-1330 {
grid-row-start: 23;
}
.start-1345 {
grid-row-start: 24;
}
.start-1400 {
grid-row-start: 25;
}
.start-1415 {
grid-row-start: 26;
}
.start-1430 {
grid-row-start: 27;
}
.start-1445 {
grid-row-start: 28;
}
.start-1500 {
grid-row-start: 29;
}
.start-1515 {
grid-row-start: 30;
}
.start-1530 {
grid-row-start: 31;
}
.start-1545 {
grid-row-start: 32;
}
.start-1600 {
grid-row-start: 33;
}
.start-1615 {
grid-row-start: 34;
}
.start-1630 {
grid-row-start: 35;
}
.start-1645 {
grid-row-start: 36;
}
.start-1700 {
grid-row-start: 37;
}
.start-1715 {
grid-row-start: 38;
}
.start-1730 {
grid-row-start: 39;
}
.start-1745 {
grid-row-start: 40;
}
.start-1800 {
grid-row-start: 41;
}
.start-1815 {
grid-row-start: 42;
}
.start-1830 {
grid-row-start: 43;
}
.start-1845 {
grid-row-start: 44;
}
.start-1900 {
grid-row-start: 45;
}
.end-0800 {
grid-row-end: 1;
}
.end-0815 {
grid-row-end: 2;
}
.end-0830 {
grid-row-end: 3;
}
.end-0845 {
grid-row-end: 4;
}
.end-0900 {
grid-row-end: 5;
}
.end-0915 {
grid-row-end: 6;
}
.end-0930 {
grid-row-end: 7;
}
.end-0945 {
grid-row-end: 8;
}
.end-1000 {
grid-row-end: 9;
}
.end-1015 {
grid-row-end: 10;
}
.end-1030 {
grid-row-end: 11;
}
.end-1045 {
grid-row-end: 12;
}
.end-1100 {
grid-row-end: 13;
}
.end-1115 {
grid-row-end: 14;
}
.end-1130 {
grid-row-end: 15;
}
.end-1145 {
grid-row-end: 16;
}
.end-1200 {
grid-row-end: 17;
}
.end-1215 {
grid-row-end: 18;
}
.end-1230 {
grid-row-end: 19;
}
.end-1245 {
grid-row-end: 20;
}
.end-1300 {
grid-row-end: 21;
}
.end-1315 {
grid-row-end: 22;
}
.end-1330 {
grid-row-end: 23;
}
.end-1345 {
grid-row-end: 24;
}
.end-1400 {
grid-row-end: 25;
}
.end-1415 {
grid-row-end: 26;
}
.end-1430 {
grid-row-end: 27;
}
.end-1445 {
grid-row-end: 28;
}
.end-1500 {
grid-row-end: 29;
}
.end-1515 {
grid-row-end: 30;
}
.end-1530 {
grid-row-end: 31;
}
.end-1545 {
grid-row-end: 32;
}
.end-1600 {
grid-row-end: 33;
}
.end-1615 {
grid-row-end: 34;
}
.end-1630 {
grid-row-end: 35;
}
.end-1645 {
grid-row-end: 36;
}
.end-1700 {
grid-row-end: 37;
}
.end-1715 {
grid-row-end: 38;
}
.end-1730 {
grid-row-end: 39;
}
.end-1745 {
grid-row-end: 40;
}
.end-1800 {
grid-row-end: 41;
}
.end-1815 {
grid-row-end: 42;
}
.end-1830 {
grid-row-end: 43;
}
.end-1845 {
grid-row-end: 44;
}
.end-1900 {
grid-row-end: 45;
}
/*# sourceMappingURL=cal.css.map */

View File

@ -1 +1 @@
{"version":3,"sourceRoot":"","sources":["cal.scss"],"names":[],"mappings":"AAAA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAIF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAKF;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;AAAA;EAEE;;;AAKF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA","file":"cal.css"}
{"version":3,"sourceRoot":"","sources":["cal.scss"],"names":[],"mappings":"AAAA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;EACA;;;AAMF;EACE;EACA;EACA;EACA;;;AAGF;AAAA;EAEE;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;AAAA;EAEE;;;AAKF;EACE;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAKF;EACI;EACA;;;AAIJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI;EACA;;;AAGJ;EACI;;;AAIJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI;EACA;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI;EACA;;;AAGJ;EACE;;;AAQF;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAKA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAKA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAKA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA;;;AAEA;EACA","file":"cal.css"}

View File

@ -1,13 +1,17 @@
:root {
--numDays: 5;
--numHours: 10;
--numHours: 13;
--numHalfHours: 23;
--numQuartHours: 45;
--timeHeight: 60px;
--calBgColor: #fff1f8;
--eventBorderColor: #f2d3d8;
--eventColor1: #ffd6d1;
--halfTimeHeight: 30px;
--quartTimeHeight: 15px;
--calBgColor: #3f2d2d;
--eventBorderColor: #000000;
--eventColor1: #b82424;
--eventColor2: #fafaa3;
--eventColor3: #e2f8ff;
--eventColor4: #d1ffe6;
--eventColor3: #1894bb;
--eventColor4: rgba(184, 36, 36, 0.65);
}
.calendar {
@ -17,9 +21,86 @@
margin: 2rem;
}
select {
width: 100%;
height: 40px;
border-radius: 5px;
color: white;
border-color: transparent;
background-color: var(--calBgColor);
text-align: center;
}
.userbuttons {
width: 95%;
position: absolute;
display: flex;
justify-content: end;
}
.changeweek {
font-size: 2.5em;
text-decoration: none;
font-weight: 600;
color: white;
}
.changeweek:hover {
color: transparent;
-webkit-text-fill-color: transparent;
-webkit-text-stroke: 3px white;
}
h2 {
font-weight: 300;
margin: 5px;
}
.headerblock {
display: flex;
justify-content: space-between;
padding-right: 20%;
padding-left: 20%;
}
.customheader {
display: flex;
justify-content: space-evenly;
}
button {
border-radius: 5px;
border-color: white;
color: white;
border-style: solid;
background: transparent;
font-weight: 600;
width: 30px;
height: 30px;
margin: 2px;
line-height: 25px;
}
button:hover{
cursor: pointer;
}
.plusbutton {
font-size: 1.5em
}
form {
margin-top: 5px;
}
option {
width: 300px;
font-size: 1rem;
}
.timeline {
display: grid;
grid-template-rows: repeat(var(--numHours), var(--timeHeight));
grid-template-rows: repeat(12, 58px);
}
.days {
@ -28,56 +109,33 @@
gap: 5px;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}
.events {
display: grid;
grid-template-rows: repeat(var(--numHours), var(--timeHeight));
grid-template-rows: repeat(var(--numQuartHours), var(--quartTimeHeight));
border-radius: 5px;
color: black;
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 {
width: 70%;
font-weight: 600;
margin-bottom: 0.25rem;
margin-top: 0px;
margin-bottom: 0px;
}
.room,
.time {
display: block;
margin-top: 0px;
margin-bottom: 0px;
}
.room {
width: 70%;
}
.event {
@ -85,38 +143,30 @@
border-radius: 5px;
padding: 0.5rem;
margin: 0 0.5rem;
background: white;
color: white;
background: var(--eventColor4);
position: relative;
}
.event:hover{
transform: scale(102%);
}
.space,
.date {
height: 60px
height: 100px
}
// 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);
font-family: "Asap", "Calibri", "Arial", sans-serif;
color: white;
background-color: black;
}
.date {
display: flex;
margin-bottom: 1em;
gap: 1em;
}
@ -131,3 +181,550 @@ body {
font-size: 3rem;
font-weight: 100;
}
// Navbar
.cs1 {
margin-left: 220px;
padding: 20px;
}
nav ul {
list-style-type: none;
margin: 0;
padding: 0;
width: 200px;
background-color: #333;
position: fixed;
top: 0;
left: 0;
height: 100%;
overflow-y: auto;
font-size: 150%;
}
nav li {
border-top: none;
}
nav li.top {
border-style: solid;
border-radius: 5px;
border-color: white;
margin: 10px;
text-align: center;
}
nav a.top:hover {
background: transparent;
}
nav li.start a {
border-top: 3px solid white;
margin-top: 20px;
padding-top: 30px;
}
nav a.selected {
color: red;
background-color: black
}
nav li.top.selected {
border-color: red;
}
nav li a {
display: block;
color: white;
padding: 12px 16px;
text-decoration: none;
}
nav li a:hover {
background-color: red;
color: black;
}
nav li .bottom {
position: absolute;
bottom: 0;
width: 200px;
}
.space {
width: 1%;
}
.footerdiv {
width: 100%;
justify-content: right;
display: flex;
}
a.footer {
color: white;
text-decoration: none;
}
a.footer:hover {
font-weight: 600;
}
// Place on Timeline
// Generated by genstarts.py
.start-0100 {
grid-row-start: 1
}
.start-0115 {
grid-row-start: 1
}
.start-0130 {
grid-row-start: 1
}
.start-0145 {
grid-row-start: 1
}
.start-0200 {
grid-row-start: 1
}
.start-0215 {
grid-row-start: 1
}
.start-0230 {
grid-row-start: 1
}
.start-0245 {
grid-row-start: 1
}
.start-0300 {
grid-row-start: 1
}
.start-0315 {
grid-row-start: 1
}
.start-0330 {
grid-row-start: 1
}
.start-0345 {
grid-row-start: 1
}
.start-0400 {
grid-row-start: 1
}
.start-0415 {
grid-row-start: 1
}
.start-0430 {
grid-row-start: 1
}
.start-0445 {
grid-row-start: 1
}
.start-0500 {
grid-row-start: 1
}
.start-0515 {
grid-row-start: 1
}
.start-0530 {
grid-row-start: 1
}
.start-0545 {
grid-row-start: 1
}
.start-0600 {
grid-row-start: 1
}
.start-0615 {
grid-row-start: 1
}
.start-0630 {
grid-row-start: 1
}
.start-0645 {
grid-row-start: 1
}
.start-0700 {
grid-row-start: 1
}
.start-0715 {
grid-row-start: 1
}
.start-0730 {
grid-row-start: 1
}
.start-0745 {
grid-row-start: 1
}
.end-0100 {
grid-row-end: 4
}
.end-0115 {
grid-row-end: 4
}
.end-0130 {
grid-row-end: 4
}
.end-0145 {
grid-row-end: 4
}
.end-0200 {
grid-row-end: 4
}
.end-0215 {
grid-row-end: 4
}
.end-0230 {
grid-row-end: 4
}
.end-0245 {
grid-row-end: 4
}
.end-0300 {
grid-row-end: 4
}
.end-0315 {
grid-row-end: 4
}
.end-0330 {
grid-row-end: 4
}
.end-0345 {
grid-row-end: 4
}
.end-0400 {
grid-row-end: 4
}
.end-0415 {
grid-row-end: 4
}
.end-0430 {
grid-row-end: 4
}
.end-0445 {
grid-row-end: 4
}
.end-0500 {
grid-row-end: 4
}
.end-0515 {
grid-row-end: 4
}
.end-0530 {
grid-row-end: 4
}
.end-0545 {
grid-row-end: 4
}
.end-0600 {
grid-row-end: 4
}
.end-0615 {
grid-row-end: 4
}
.end-0630 {
grid-row-end: 4
}
.end-0645 {
grid-row-end: 4
}
.end-0700 {
grid-row-end: 4
}
.end-0715 {
grid-row-end: 4
}
.end-0730 {
grid-row-end: 4
}
.end-0745 {
grid-row-end: 4
}
.start-0800 {
grid-row-start: 1
}
.start-0815 {
grid-row-start: 2
}
.start-0830 {
grid-row-start: 3
}
.start-0845 {
grid-row-start: 4
}
.start-0900 {
grid-row-start: 5
}
.start-0915 {
grid-row-start: 6
}
.start-0930 {
grid-row-start: 7
}
.start-0945 {
grid-row-start: 8
}
.start-1000 {
grid-row-start: 9
}
.start-1015 {
grid-row-start: 10
}
.start-1030 {
grid-row-start: 11
}
.start-1045 {
grid-row-start: 12
}
.start-1100 {
grid-row-start: 13
}
.start-1115 {
grid-row-start: 14
}
.start-1130 {
grid-row-start: 15
}
.start-1145 {
grid-row-start: 16
}
.start-1200 {
grid-row-start: 17
}
.start-1215 {
grid-row-start: 18
}
.start-1230 {
grid-row-start: 19
}
.start-1245 {
grid-row-start: 20
}
.start-1300 {
grid-row-start: 21
}
.start-1315 {
grid-row-start: 22
}
.start-1330 {
grid-row-start: 23
}
.start-1345 {
grid-row-start: 24
}
.start-1400 {
grid-row-start: 25
}
.start-1415 {
grid-row-start: 26
}
.start-1430 {
grid-row-start: 27
}
.start-1445 {
grid-row-start: 28
}
.start-1500 {
grid-row-start: 29
}
.start-1515 {
grid-row-start: 30
}
.start-1530 {
grid-row-start: 31
}
.start-1545 {
grid-row-start: 32
}
.start-1600 {
grid-row-start: 33
}
.start-1615 {
grid-row-start: 34
}
.start-1630 {
grid-row-start: 35
}
.start-1645 {
grid-row-start: 36
}
.start-1700 {
grid-row-start: 37
}
.start-1715 {
grid-row-start: 38
}
.start-1730 {
grid-row-start: 39
}
.start-1745 {
grid-row-start: 40
}
.start-1800 {
grid-row-start: 41
}
.start-1815 {
grid-row-start: 42
}
.start-1830 {
grid-row-start: 43
}
.start-1845 {
grid-row-start: 44
}
.start-1900 {
grid-row-start: 45
}
.end-0800 {
grid-row-end: 1
}
.end-0815 {
grid-row-end: 2
}
.end-0830 {
grid-row-end: 3
}
.end-0845 {
grid-row-end: 4
}
.end-0900 {
grid-row-end: 5
}
.end-0915 {
grid-row-end: 6
}
.end-0930 {
grid-row-end: 7
}
.end-0945 {
grid-row-end: 8
}
.end-1000 {
grid-row-end: 9
}
.end-1015 {
grid-row-end: 10
}
.end-1030 {
grid-row-end: 11
}
.end-1045 {
grid-row-end: 12
}
.end-1100 {
grid-row-end: 13
}
.end-1115 {
grid-row-end: 14
}
.end-1130 {
grid-row-end: 15
}
.end-1145 {
grid-row-end: 16
}
.end-1200 {
grid-row-end: 17
}
.end-1215 {
grid-row-end: 18
}
.end-1230 {
grid-row-end: 19
}
.end-1245 {
grid-row-end: 20
}
.end-1300 {
grid-row-end: 21
}
.end-1315 {
grid-row-end: 22
}
.end-1330 {
grid-row-end: 23
}
.end-1345 {
grid-row-end: 24
}
.end-1400 {
grid-row-end: 25
}
.end-1415 {
grid-row-end: 26
}
.end-1430 {
grid-row-end: 27
}
.end-1445 {
grid-row-end: 28
}
.end-1500 {
grid-row-end: 29
}
.end-1515 {
grid-row-end: 30
}
.end-1530 {
grid-row-end: 31
}
.end-1545 {
grid-row-end: 32
}
.end-1600 {
grid-row-end: 33
}
.end-1615 {
grid-row-end: 34
}
.end-1630 {
grid-row-end: 35
}
.end-1645 {
grid-row-end: 36
}
.end-1700 {
grid-row-end: 37
}
.end-1715 {
grid-row-end: 38
}
.end-1730 {
grid-row-end: 39
}
.end-1745 {
grid-row-end: 40
}
.end-1800 {
grid-row-end: 41
}
.end-1815 {
grid-row-end: 42
}
.end-1830 {
grid-row-end: 43
}
.end-1845 {
grid-row-end: 44
}
.end-1900 {
grid-row-end: 45
}

7
static/dropdown.js Normal file
View File

@ -0,0 +1,7 @@
document.addEventListener("DOMContentLoaded", function(event) {
const dropdown = document.getElementById("dropdown");
dropdown.addEventListener("change", function() {
dropdown.submit();
});
});

View File

@ -1,17 +1,155 @@
body
{
background-color: #1d1919;
:root {
--numBoxes: 2;
--numRows: 2;
}
body {
background-color: black;
font-family: "Asap", "Calibri", "Arial", sans-serif;
font-size: 150%;
font-family: "Asap","Calibri", "Arial";
color: #fff1f1;
animation-name: HG;
animation-duration: 20s;
animation-iteration-count: infinite
margin: 0;
color: white;
}
nav ul {
list-style-type: none;
margin: 0;
padding: 0;
width: 200px;
background-color: #333;
position: fixed;
top: 0;
left: 0;
height: 100%;
overflow-y: auto;
}
nav li {
border-top: none;
}
nav li.top {
border-style: solid;
border-radius: 5px;
border-color: white;
margin: 10px;
text-align: center;
}
nav a.top:hover {
background: transparent;
}
nav li.start a {
border-top: 3px solid white;
margin-top: 20px;
padding-top: 30px;
}
nav a.selected {
color: red;
}
nav li.top.selected {
border-color: red;
}
nav li a.selected {
background-color: black;
}
nav li a {
display: block;
color: white;
padding: 12px 16px;
text-decoration: none;
}
nav li a:hover {
background-color: red;
color: black;
}
nav li .bottom {
position: absolute;
bottom: 0;
width: 200px;
}
.cs1 {
margin-left: 220px;
padding: 20px;
}
.cs {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
padding: 20px;
}
.container {
display: flex;
flex-direction: column;
}
/* Reihen */
.row {
display: flex;
justify-content: space-evenly;
margin-bottom: 10px;
}
.box {
width: calc(100% / var(--numBoxes));
height: 300px;
border: 5px solid red;
margin: 7px;
border-radius: 10px;
}
.box:hover {
transform: scale(101%);
}
.cs1 h1 {
margin-left: 0;
}
.notification-icon {
position: fixed;
top: 10px;
right: 10px;
width: 50px;
}
.space {
width: 1%;
}
.footerdiv {
position: absolute;
width: 100%;
justify-content: right;
bottom: 10px;
right: 10px;
font-size: 80%;
display: flex;
}
a.footer {
color: white;
text-decoration: none;
}
a.footer:hover {
font-weight: 600;
}
input {
width: 150px;
height: 50px
height: 50px;
}
input[type=url], input[type=email], input[type=password] {
@ -20,88 +158,7 @@ input[type=url], input[type=email], input[type=password] {
select {
width: 200px;
height: 50px
}
.cs
{
position: absolute;
top: 38%;
left:12%;
margin: 0;
width: 75%;
/*border: 3px solid red;*/
}
.cs h1, .cs h2, .cs form
{
display: flex;
text-align: center;
display: flex;
justify-content: center;
align-items: center;
height: 200%;
/*border: 3px solid green;*/
}
h1
{
text-align: center;
font-weight: bold;
font-family: "Asap","Calibri","Arial";
font-size: 300%;
color: #e7dbdb;
}
h2
{
font-size: 100%;
position: sticky;
bottom: 0;
text-align: left;
}
p
{
line-height: 150%;
}
a:link
{
font-style: italic;
text-decoration: none;
font-weight: bolder;
color: #adadad
}
a:visited
{
color: #9e9393;
}
a:hover
{
color: #726868;
font-size: 105%;
transition: font-size 100ms ease-in;
}
a, img
{
transition: 100ms ease-out
}
img
{
width: 30%;
height: auto
}
img:hover
{
width: 32%;
height: auto;
opacity: 75%;
transition: 100ms ease-in;
height: 50px;
}
/*# sourceMappingURL=style.css.map */

1
static/style.css.map Normal file
View File

@ -0,0 +1 @@
{"version":3,"sourceRoot":"","sources":["style.scss"],"names":[],"mappings":"AAAA;EACI;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI;EACA;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;;;AAIJ;EACI;EACA;;;AAGJ;AACA;EACI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;EACH;;;AAGD;EACI;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;;;AAGJ;EACE;;;AAGF;EACI;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA","file":"style.css"}

163
static/style.scss Normal file
View File

@ -0,0 +1,163 @@
:root {
--numBoxes: 2;
--numRows: 2;
}
body {
background-color: black;
font-family: "Asap", "Calibri", "Arial", sans-serif;
font-size: 150%;
margin: 0;
color: white;
}
nav ul {
list-style-type: none;
margin: 0;
padding: 0;
width: 200px;
background-color: #333;
position: fixed;
top: 0;
left: 0;
height: 100%;
overflow-y: auto;
}
nav li {
border-top: none;
}
nav li.top {
border-style: solid;
border-radius: 5px;
border-color: white;
margin: 10px;
text-align: center;
}
nav a.top:hover {
background: transparent;
}
nav li.start a {
border-top: 3px solid white;
margin-top: 20px;
padding-top: 30px;
}
nav a.selected {
color: red;
}
nav li.top.selected {
border-color: red;
}
nav li a.selected {
background-color: black;
}
nav li a {
display: block;
color: white;
padding: 12px 16px;
text-decoration: none;
}
nav li a:hover {
background-color: red;
color: black;
}
nav li .bottom {
position: absolute;
bottom: 0;
width: 200px;
}
.cs1 {
margin-left: 220px;
padding: 20px;
}
.cs {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
padding: 20px;
}
.container {
display: flex;
flex-direction: column;
}
/* Reihen */
.row {
display: flex;
justify-content: space-evenly;
margin-bottom: 10px;
}
.box {
width: calc(100%/var(--numBoxes));
height: 300px;
border: 5px solid red;
margin: 7px;
border-radius: 10px;
}
.box:hover {
transform: scale(101%);
}
.cs1 h1 {
margin-left: 0;
}
.notification-icon {
position: fixed;
top: 10px;
right: 10px;
width:50px;
}
.space {
width: 1%;
}
.footerdiv {
position: absolute;
width: 100%;
justify-content: right;
bottom: 10px;
right: 10px;
font-size: 80%;
display: flex;
}
a.footer {
color: white;
text-decoration: none;
}
a.footer:hover {
font-weight: 600;
}
input {
width: 150px;
height: 50px
}
input[type=url], input[type=email], input[type=password] {
width: 500px;
}
select {
width: 200px;
height: 50px
}

57
templates/dashboard.html Normal file
View File

@ -0,0 +1,57 @@
{% extends "index.html" %}
{% block content %}
<div class="cs">
<h1> Willkommen, {{ name }} ({{ kurs }})!</h1>
</div>
{% if praxis == "hidden" %}
<div class="container">
<div class="row">
<div class="box">
<div class="cs">
<h2>Aktueller GPA</h2>
</div>
</div>
<div class="box">
<div class="cs">
<h2>Countdown bis zur nächsten Klausur</h2>
</div>
</div>
</div>
<div class="row">
<div class="box">
<div class="cs">
<h2>Lernplan</h2>
</div>
</div>
<div class="box">
<div class="cs">
<h2>Nächste Vorlesung</h2>
</div>
</div>
<div class="box">
<div class="cs">
<h2>Neueste Chatnachricht</h2>
</div>
</div>
</div>
</div>
{% else %}
<div class="container">
<div class="row">
<div class="box">
<div class="cs">
<h2>ToDos</h2>
</div>
</div>
<div class="box">
<div class="cs">
<h2>Literatursammlung</h2>
</div>
</div>
</div>
</div>
{% endif %}
{% endblock %}

View File

@ -0,0 +1,4 @@
{% extends "index.html" %}
{% block content %}
<h2>{{ message }}</h2>
{% endblock %}

View File

@ -1,16 +1,41 @@
<!DOCTYPE html>
<html lang="de">
<head>
<title> {{headermessage}} 👀</title>
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='style.css') }}">
<head>
<title>DualHub</title>
<link rel="stylesheet" type="text/css" href={{ url_for("static", filename="style.css") }}>
<meta http-equiv="refresh" content="510">
<script async src="https://analytics.paulmartin.cloud/script.js" data-website-id="459fa66e-e255-4393-8e89-ead8b1572d0d"></script>
{% block head %}
{% endblock %}
</head>
<body>
<nav>
<ul>
{% if theorie=="hidden" and praxis=="hidden" %}
<li class="top"><a class="top" href={{ url_for("welcome", sel="theorie") }}>Theorie</a></li>
<li class="top"><a class="top" href={{ url_for("welcome", sel="praxis") }}>Praxis</a></li>
{% elif theorie == "hidden" %}
<li class="top"><a class="top" href={{ url_for("welcome", sel="theorie") }}>Theorie</a></li>
<li class="top selected"><a class="top selected" href={{ url_for("welcome", sel="praxis") }}>Praxis</a></li>
{% else %}
<li class="top selected"><a class="top selected" href={{ url_for("welcome", sel="theorie") }}>Theorie</a></li>
<li class="top"><a class="top" href={{ url_for("welcome", sel="praxis") }}>Praxis</a></li>
{% endif %}
<li {{ theorie }}><a {% if s == "n" %} class="selected" {% endif %} href={{ url_for("displayNoten") }}>Noten</a></li>
<li {{ theorie }}><a {% if s == "p" %} class="selected" {% endif %} href={{ url_for("displayRapla") }}>Stundenplan</a></li>
<li {{ praxis }}><a {% if s == "t" %} class="selected" {% endif %} href="">To Dos</a></li>
<li><a {% if s == "s" %} class="selected" {% endif %} href={{ url_for("redKurs") }}>Konfiguration</a></li>
{% if request.endpoint %}
{% if request.endpoint[:7] != "login" %}
<li><a class="bottom" href={{ url_for("logout") }}>Log-Out</a></li>
{% endif %}
{% endif %}
</ul>
</nav>
</head>
<body>
<div class="cs">
<h1>{{message}}</h1>
</div>
</body>
<div class="cs1">
{% block content %}
{% endblock %}
</div>
</body>
</html>

View File

@ -1,22 +1,22 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Kurs wählen</title>
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='style.css') }}">
<script async src="https://analytics.paulmartin.cloud/script.js" data-website-id="459fa66e-e255-4393-8e89-ead8b1572d0d"></script>
</head>
<body>
{% extends "index.html" %}
{% block content %}
{% if not detected[1] %}
{% if file %}
<h1>Wir haben {{ detected[0] }} als deinen Kurs ermittelt. Falls er nicht stimmt, kannst du ihn unten auswählen.</h1>
{% if not request.args.get("next") %}
<form action={{ url_for("getSemester") }}>
{% else %}
<form action={{ request.args.get("next") }}>
{% endif %}
<input type="submit" value="Der Kurs stimmt!">
</form>
{% else %}
<h1>Dein Kurs konnte leider nicht ermittelt werden. Klicke den Button, um direkt zur Auswahl zu kommen.</h1>
<h1>Wir haben {{ detected[0] }} als deinen Kurs ermittelt. Für ihn ist aber noch kein Rapla hinterlegt.</h1>
{% endif %}
<form action={{ url_for("chooseRaplas") }}>
{% else %}
<h1>Dein Kurs konnte leider nicht ermittelt werden. Klicke den Button, um zur Auswahl zu kommen.</h1>
{% endif %}
<form action={{ url_for("chooseRaplas", next=request.args.get("next")) }}>
<input type="submit" value="Manuell auswählen!">
</form>
</body>
</html>
{% endblock %}

View File

@ -1,14 +1,8 @@
<!DOCTYPE html>
<html lang="de">
<head>
<title> Log In 👀</title>
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='style.css') }}">
<script async src="https://analytics.paulmartin.cloud/script.js" data-website-id="459fa66e-e255-4393-8e89-ead8b1572d0d"></script>
</head>
<body>
{% extends "index.html" %}
{% block content %}
<div class="cs">
<h1>Einloggen</h1>
</div>
<div class="cs">
<form method="post" action={{ url_for ("login_post", next=request.args.get("next")) }}>
<label for="email" >Dualis-Email: </label>
@ -18,5 +12,4 @@
<button class="button">Login</button>
</form>
</div>
</body>
</html>
{% endblock %}

View File

@ -1,23 +1,38 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Noten</title>
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='style.css') }}">
<script async src="https://analytics.paulmartin.cloud/script.js" data-website-id="459fa66e-e255-4393-8e89-ead8b1572d0d"></script>
</head>
<body>
{% for i in noten %}
<h2>{{ i[0] }}: {{ i[1].capitalize() }} (Credits: {{ i[2] }})</h2>
{% extends "index.html" %}
{% block head %}
<script type="text/javascript" src="{{ url_for("static", filename="dropdown.js") }}"></script>
<meta http-equiv="Refresh" content="600">
{% endblock %}
{% block content %}
{% for i in range (semester|length) %}
{% if semester[i][1]==sel %}
<h1>Deine Noten im {{ semester[i][0] }}</h1>
{% endif %}
{% endfor %}
<form method="post" action={{ url_for ("setSemester", next=url_for("displayNoten")) }}>
<br>
<br>
{% for i in noten %}
{% if i[1]|length == 1 %}
{% if i[1][0][0].isnumeric() or i[1][0][-1].isalpha()%}
<h2>{{ i[0][0] }}: {{ i[1][0] }} (Credits: {{ i[2][0] }})</h2>
{% else %}
<h2>{{ i[0][0] }}: {{ i[1][0][-3:] }} (Credits: {{ i[2][0] }})</h2>
{% endif %}
{% else %}
<h2>{{ i[0][0] }} (Credits: {{ i[2][0] }}):</h2>
{% for e in i[1] %}
<h2>&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;{{ e }}</h2>
{% endfor %}
{% endif %}
{% endfor %}
<br>
<br>
<form id= "dropdown" method="post" action={{ url_for ("displayNoten") }}>
<label for="sem">Semester wählen! </label>
<select name="sem" id="sem">
{% for i in range (semester|length) %}
<option value= {{ semester [i] [1] }} {% if semester[i][1]==sel %} selected {% endif %}>{{ semester [i] [0] }} </option>
{% endfor %}
</select>
<input type="submit" value="Semester setzen!">
</form>
</body>
</html>
{% endblock %}

View File

@ -1,57 +1,9 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Vorlesungsplan</title>
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='cal.css') }}">
</head>
<body>
<div class="calendar">
<div class="timeline">
<div class="spacer"></div>
<div class="time-marker">08</div>
<div class="time-marker">09</div>
<div class="time-marker">10</div>
<div class="time-marker">11</div>
<div class="time-marker">12</div>
<div class="time-marker">13</div>
<div class="time-marker">14</div>
<div class="time-marker">15</div>
<div class="time-marker">16</div>
<div class="time-marker">17</div>
<div class="time-marker">18</div>
<div class="time-marker">19</div>
</div>
<div class="days">
{% for e in range (eventdays|length) %}
<div class="day {{ eventdays[e]["short"] }}">
<div class="date">
<p class="date-num">{{ eventdays[e]["day"] }}</p>
<p class="date-day">{{ eventdays[e]["long"] }}</p>
<form>
{% for n in eventdays [e]["mensa"] %}
<p class="mensa"> {{ n }}</p>
{% endfor %}
</form>
</div>
<div class="events">
{% for i in events %}
{% if i["weekday"] == e %}
<div class="event start-{{ i["start"][:2]+i["start"][3:] }} end-{{ i["end"][:2]+i["end"][3:]}}">
<p class="title">{{ i["name"] }}</p>
<p class="room">{{ i["room"] }}</p>
<p class="duration">{{ i["dur"] }}</p>
<p class="time">{{ i["start"] }} - {{ i["end"] }}</p>
</div>
{% endif %}
{% endfor %}
</div>
</div>
{% endfor %}
</div>
</div>
</div>
</div>
<a href={{ url_for("login") }}>Einloggen, um alle Features zu nutzen!</a>
</body>
</html>
{% extends "plan-general.html"%}
{% block loginout %}
<li><a class="bottom" href={{ url_for("login") }}>Log-In</a></li>
{% endblock %}
{% block startcontent %}
<h2>Vorlesungsplan {{ kurs }}</h2>
{% endblock %}
{% block endcontent %}
{% endblock %}

View File

@ -3,9 +3,32 @@
<head>
<meta charset="UTF-8">
<title>Vorlesungsplan</title>
<link rel<="s>tylesheet" type="text/css" href="{{ url_for('static', filename='cal.css') }}">
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='cal.css') }}">
<script async src="https://analytics.paulmartin.cloud/script.js" data-website-id="459fa66e-e255-4393-8e89-ead8b1572d0d"></script>
</head>
<body>
<nav>
<ul>
<li class="top selected"><a class="top selected" href={{ url_for("welcome", sel="theorie") }}>Theorie</a></li>
<li class="top"><a class="top" href={{ url_for("welcome", sel="praxis") }}>Praxis</a></li>
<li {{ theorie }}><a href={{ url_for("displayNoten") }}>Noten</a></li>
<li {{ theorie }}><a class="selected" href={{ url_for("displayRapla") }}>Stundenplan</a></li>
<li {{ praxis }}><a href={{ url_for("redKurs") }}>To Dos</a></li>
<li><a href={{ url_for("redKurs") }}>Konfiguration</a></li>
{% block loginout %}
{% endblock %}
</ul>
</nav>
<div class="cs1">
<div class="customheader">
{% block startcontent %}
{% endblock %}
</div>
<div class="headerblock">
<a href="?week={{prev}}" class="changeweek">🢐</a>
<a href="?today" class="changeweek">{{ mon }}</a>
<a href="?week={{next}}" class="changeweek">🢒</a>
</div>
<div class="calendar">
<div class="timeline">
<div class="spacer"></div>
@ -29,19 +52,22 @@
<p class="date-num">{{ eventdays[e]["day"] }}</p>
<p class="date-day">{{ eventdays[e]["long"] }}</p>
<form>
<select>
<option value="none" selected disabled> Mensa</option>
{% for n in eventdays [e]["mensa"] %}
<p class="mensa"> {{ n }}</p>
<option class="mensa"> {{ n }}</option>
{% endfor %}
</select>
</form>
</div>
<div class="events">
{% for i in events %}
{% if i["weekday"] == e %}
<div class="event start-{{ i["start"][:2]+i["start"][3:] }} end-{{ i["end"][:2]+i["end"][3:]}}">
{% block event %} {% endblock %}
<p class="title">{{ i["name"] }}</p>
<p class="room">{{ i["room"] }}</p>
<p class="duration">{{ i["dur"] }}</p>
<p class="time">{{ i["start"] }} - {{ i["end"] }}</p>
<p class="time">{{ i["start"] }} - {{ i["end"] }} ({{ i["dur"] }}h)</p>
</div>
{% endif %}
{% endfor %}
@ -50,7 +76,8 @@
{% endfor %}
</div>
</div>
</div>
</div>
{% block endcontent %}
{% endblock %}
</div>
</body>
</html>

15
templates/plan-user.html Normal file
View File

@ -0,0 +1,15 @@
{% extends "plan-general.html" %}
{% block loginout %}
<li><a class="bottom" href={{ url_for("logout") }}>Log-Out</a></li>
{% endblock %}
{% block startcontent %}
<h2>{{ name }}s Vorlesungsplan</h2>
{% endblock %}
{% block event %}
<div class="userbuttons">
<button>👁</button>
<button class="plusbutton">+</button>
</div>
{% endblock %}
{% block endcontent %}
{% endblock %}

View File

@ -1,11 +1,5 @@
<!DOCTYPE html>
<html lang="de">
<head>
<title> RAPLA importieren 👀</title>
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='style.css') }}">
<script async src="https://analytics.paulmartin.cloud/script.js" data-website-id="459fa66e-e255-4393-8e89-ead8b1572d0d"></script>
</head>
<body>
{% extends "index.html" %}
{% block content %}
<h1>Verfügbare Raplas </h1>
<form method="post" action={{ url_for ("getRapla") }}>
<label for="file">Vefügbaren RAPLA wählen! </label>
@ -20,9 +14,8 @@
</form>
<h1>Eigenen Rapla hinzufügen</h1>
<form method="post" action={{ url_for ("getRapla") }}>
<label for="url">Rapla-URL eingeben, falls Du deinen Kurs nicht siehst:</label>
<label for="url">Rapla-URL eingeben, falls Du Deinen Kurs nicht siehst:</label>
<input type="url" name="url" id="url">
<input type="submit" value="Importieren!">
</form>
</body>
</html>
{% endblock %}

View File

@ -1,14 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Semester wählen</title>
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='style.css') }}">
<script async src="https://analytics.paulmartin.cloud/script.js" data-website-id="459fa66e-e255-4393-8e89-ead8b1572d0d"></script>
</head>
<body>
{% extends "index.html" %}
{% block head %}
<script type="text/javascript" src="{{ url_for("static", filename="dropdown.js") }}"></script>
{% endblock %}
{% block content %}
<h1>Bitte wähle aus der unten stehenden Liste das Semester, dessen Noten du sehen möchtest.</h1>
<form method="post" action={{ url_for ("setSemester") }}>
<form id="dropdown" method="post" action={{ url_for ("setSemester", next=request.args.get("next")) }}>
<label for="sem">Semester wählen! </label>
<select name="sem" id="sem">
<option value = "none" selected disabled hidden></option>
@ -16,7 +12,5 @@
<option value= {{ semester [i] [1] }}>{{ semester [i] [0] }} </option>
{% endfor %}
</select>
<input type="submit" value="Semester setzen!">
</form>
</body>
</html>
{% endblock %}

16
templates/testpages.html Normal file
View File

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Test Pages</title>
</head>
<body>
<a href={{ url_for("kurs") }} > kurs </a> <br>
<a href={{ url_for("login") }} > login </a><br>
<a href={{ url_for("displayNoten") }} > noten </a><br>
<a href={{ url_for("plananon", kurs="tinf22b3") }} > plan-anon </a><br>
<a href={{ url_for("planuser") }} > plan-user </a><br>
<a href={{ url_for("chooseRaplas") }} > rapla </a><br>
<a href={{ url_for("getSemester") }} > semester </a><br>
</body>
</html>

View File

@ -0,0 +1,97 @@
import datetime
import flask
from flask import render_template, Flask, url_for, redirect
app = Flask(__name__)
sampleweek = ([{'start': '08:30', 'end': '11:00', 'dur': '2:30', 'name': "Rechnerarchitekturen",
'room': "A266 Hörsaal", 'weekday': 0, 'day': 11},
{'start': '09:30', 'end': '12:00', 'dur': '2:30', 'name': "Netztechnik 1", 'room': "", 'weekday': 1,
'day': 12},
{'start': '08:30', 'end': '12:00', 'dur': '3:30', 'name': "Info3", 'room': "", 'weekday': 2,
'day': 13},
{'start': '08:30', 'end': '12:00', 'dur': '3:30', 'name': "Info3", 'room': "", 'weekday': 3,
'day': 14}, {'start': '11:00', 'end': '12:30', 'dur': '1:30', 'name': "Systemnahes Programmieren",
'room': "A266 Hörsaal", 'weekday': 0, 'day': 11},
{'start': '13:00', 'end': '16:15', 'dur': '3:15', 'name': "Java", 'room': "A266 Hörsaal",
'weekday': 3, 'day': 14}], [{'day': 11, 'short': 'mon', 'long': 'Montag', 'mensa': [
'Frikadelle Hausfrauen Art mit Kräutersoße und Risoleekartoffeln Kräutersoße',
'Veganes Gemüseschnitzel Risoleekartoffeln Kräutersoße', 'Kaiserschmarrn mit Rosinen und Apfelmus Vanillesoße']},
{'day': 12, 'short': 'tue', 'long': 'Dienstag', 'mensa': [
'Alaska Seelachsfilet in Backteig hausgemachter Kartoffelsalat Dip',
'Gemüsefrikadellen hausgemachter Kartoffelsalat Dip']},
{'day': 13, 'short': 'wed', 'long': 'Mittwoch', 'mensa': [
'Roter Curry - Gemüseeintopf mit Hähnchenstreifen und Baguettebrötchen',
'Roter Curry - Gemüseeintopf mit Sojastreifen und Baguettebrötchen',
'Gebratene Gnocchis mit Karotten und Schnittlauchsoße']},
{'day': 14, 'short': 'thu', 'long': 'Donnerstag', 'mensa': [
'Pasta mit Paprika, getrockneten Tomaten, Pinienkernen, Basilikum und Reibekäse',
'2010: Königsberger Klopse in Kapernsoße und Salzkartoffeln',
'Pasta mit Hackfleisch - Champignon - Soße, Reibekäse']},
{'day': 15, 'short': 'fri', 'long': 'Freitag', 'mensa': [
'Griechische Nudelpfanne mit Sojastreifen, Gemüse, Pinienkerne und Tomatensoße',
'Griechische Nudelpfanne mit Geflügel, Gemüse, Pinienkerne und Tomatensoße']}],
datetime.date(2023, 12, 6), datetime.date(2023, 12, 20), 'Dezember 2023')
@app.route("/")
def index():
return render_template('testpages.html')
@app.route("/kurs")
def kurs():
return render_template("kurs.html", detected=("TINF22B3", False))
@app.route("/login")
def login():
return render_template("login.html")
@app.route("/noten")
def displayNoten():
return render_template("noten.html", noten=[["Info", "nicht bestanden", 2000]],
semester=[["Sommersemester", "SoSe"]], sel="SoSe")
@app.route("/plan/<string:kurs>")
def plananon(kurs):
return render_template("plan-anon.html", events=sampleweek[0], eventdays=sampleweek[1], kurs=kurs,
prev=str(sampleweek[2])[:10], next=str(sampleweek[3])[:10], mon=sampleweek[4])
@app.route("/plan")
def planuser():
return render_template("plan-user.html", events=sampleweek[0], eventdays=sampleweek[1], kurs=kurs,
prev=str(sampleweek[2])[:10], next=str(sampleweek[3])[:10], mon=sampleweek[4], name="Studi")
@app.route("/rapla")
def chooseRaplas():
return render_template("rapla.html", raplas=[["TINF22B3"], ["TINF22B3"], ["TINF22B3"]])
@app.route("/semester")
def getSemester():
return render_template("semester.html", semester=[["Sommersemester 2023", "SoSeID"]])
@app.route("/setsemester", methods=["POST"])
def setSemester():
return redirect(url_for("index"))
@app.route("/getrapla", methods=["POST"])
def getRapla():
return redirect(url_for("index"))
@app.route("/login", methods=["POST"])
def login_post():
return redirect(url_for("index"))
if __name__ == "__main__":
app.run(host='0.0.0.0', port=2024, debug=True)

284
tests_examples/vergleich.py Normal file
View File

@ -0,0 +1,284 @@
import asyncio
import httpx
import time
import urllib.parse
import requests
from flask import Flask
from bs4 import BeautifulSoup
from celery import Celery
import fetchDUALIS
from login_data import passwort, email #CREATE LOCAL login_data.py!!!
app = Flask(__name__)
app.config['CELERY_BROKER_URL'] = 'redis://localhost:6379/0'
app.config['CELERY_RESULT_BACKEND'] = 'redis://localhost:6379/0'
celery = Celery(app.name, broker=app.config['CELERY_BROKER_URL'])
celery.conf.update(app.config)
headers = {
'Cookie': 'cnsc=0',
'Content-Type': 'application/x-www-form-urlencoded',
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
'Chrome/58.0.3029.110 Safari/537.36',
'Accept-Encoding': 'utf-8'
}
url = "https://dualis.dhbw.de/scripts/mgrqispi.dll"
fpw = urllib.parse.quote(passwort, safe='', encoding=None, errors=None)
fmail = urllib.parse.quote(email, safe='', encoding=None, errors=None)
@celery.task(bind=True)
def celery_requests(self):
with httpx.Client() as s:
content = (f'usrname={fmail}&pass={fpw}&ARGUMENTS=clino%2Cusrname%2Cpass%2Cmenuno%2Cmenu_type%2Cbrowser'
f'%2Cplatform&APPNAME=CampusNet&PRGNAME=LOGINCHECK')
response = s.post(url=url, headers=headers, content=content)
return response, s
# noinspection DuplicatedCode
async def checkUser_celery():
req = celery_requests.apply_async()
response = req[0]
s = req[1]
header = response.headers
try:
refresh = header["REFRESH"]
arg = refresh.find("=-N") + 3
komma = refresh[arg:].find(",")
cookie = s.cookies.get("cnsc")
except KeyError:
return -2, s
token = refresh[arg:komma + arg]
return token, cookie
# noinspection DuplicatedCode
def checkUser_normal():
"""
Erhält von Dualis den Token und Cookie für User.
:param email:
:param password:
:return (Token, Session):
"""
s = requests.Session()
payload = 'usrname=' + fmail + '&pass=' + fpw + ('&ARGUMENTS=clino%2Cusrname%2Cpass%2Cmenuno%2Cmenu_type%2Cbrowser'
'%2Cplatform&APPNAME=CampusNet&PRGNAME=LOGINCHECK')
response = s.post(url, headers=headers, data=payload)
header = response.headers
try:
refresh = header["REFRESH"]
arg = refresh.find("=-N") + 3
komma = refresh[arg:].find(",")
except KeyError:
return -2, s
token = refresh[arg:komma + arg]
cookies = s.cookies
cookie = 0
for c in cookies:
cookie = c.value
return token, cookie
# noinspection DuplicatedCode
async def checkUser_async():
"""
Erhält von Dualis den Token und Cookie für User.
:param email:
:param password:
:return (Token, Session):
"""
# noinspection DuplicatedCode
async with httpx.AsyncClient() as s:
content = (f'usrname={fmail}&pass={fpw}&ARGUMENTS=clino%2Cusrname%2Cpass%2Cmenuno%2Cmenu_type%2Cbrowser'
f'%2Cplatform&APPNAME=CampusNet&PRGNAME=LOGINCHECK')
response = await s.post(url=url, headers=headers, content=content)
header = response.headers
try:
refresh = header["REFRESH"]
arg = refresh.find("=-N") + 3
komma = refresh[arg:].find(",")
cookie = s.cookies.get("cnsc")
except KeyError:
return -2, s
token = refresh[arg:komma + arg]
return token, cookie
# noinspection DuplicatedCode
def getSem_normal(token: int, cookie: str):
"""
Liefert die Liste aller auf Dualis verfügbaren Semester.
:param token:
:param cookie:
:return Liste [[Semester, Semester-ID], ...]:
"""
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')
select = html.find('select')
select = select.find_all(value=True)
optlist = []
for i in select:
t = i.text.replace("Wi", "Winter").replace("So", "Sommer")
t = t.replace("Se", "semester")
optlist += [[t, i['value']]]
return optlist
# noinspection DuplicatedCode
async def getSem_async(token, cookie):
"""
Liefert die Liste aller auf Dualis verfügbaren Semester.
:param token:
:param cookie:
:return Liste [[Semester, Semester-ID], ...]:
"""
headers["Cookie"] = "cnsc=" + cookie
token = str(token)
async with httpx.AsyncClient() as s:
response = await s.get(url=url +
"?APPNAME=CampusNet&PRGNAME=COURSERESULTS&ARGUMENTS=-N" + token + ",-N000307,",
headers=headers)
html = BeautifulSoup(response.text, 'lxml')
select = html.find('select')
select = select.find_all(value=True)
optlist = []
for i in select:
t = i.text.replace("Wi", "Winter").replace("So", "Sommer")
t = t.replace("Se", "semester")
optlist += [[t, i['value']]]
return optlist
async def getSem_celery(token, cookie):
pass
# noinspection DuplicatedCode
async def getResults_async(token, cookie, resl):
headers["Cookie"] = "cnsc=" + cookie
async with httpx.AsyncClient() as s:
response = await s.get(url=url + "?APPNAME=CampusNet&PRGNAME=COURSERESULTS&ARGUMENTS=-N" + token +
",-N000307," + ",-N" + resl, headers=headers)
html = BeautifulSoup(response.content.decode("utf-8"), 'lxml')
table = html.find('table', attrs={"class": "nb list"})
body = table.find("tbody")
vorl = body.find_all("tr")
vorlist = []
tasks = []
i = 0
for row in vorl:
cols = row.find_all("td")
col = [[e.text.strip()] for e in cols]
if len(col) != 0:
if len(col[4][0]) == 0:
tasks += [getPruefung_async(s, row.find("a")["href"])]
col[2] = i
i += 1
vorlist += [col[1:4]]
extrakurse = await asyncio.gather(*tasks, return_exceptions=True)
for i in vorlist:
for e in range(0, len(i)):
if isinstance(i[e], int):
i[e] = extrakurse[i[e]]
return vorlist[:-1]
# noinspection DuplicatedCode
async def getPruefung_async(s, url):
response = await s.get("https://dualis.dhbw.de" + url, headers=headers)
html = BeautifulSoup(response.content.decode("utf-8"), 'lxml')
table = html.find('table')
pruefung = table.find_all("tr")
ret = []
for row in pruefung:
cols = row.find_all("td")
col = [e.text.strip() for e in cols]
if len(col) == 6 and len(col[3]) <= 13:
if len(col[3]) != 0:
ret += [col[0] + ": " + col[3][:3]]
if ret[-1][0] == ':':
ret[-1] = "Gesamt" + ret[-1]
if len(ret) == 0:
ret = ["Noch nicht gesetzt"]
return ret
def normal():
login = checkUser_normal()
token = login[0]
cookie = str(login[1])
semlist = getSem_normal(token, cookie)
for i in range(1, len(semlist)):
results = fetchDUALIS.getResults(token, cookie, semlist[i][1])
return [token, cookie, results]
async def async_normal():
login = await checkUser_async()
token = login[0]
cookie = str(login[1])
semlist = await getSem_async(token, cookie)
for i in range(1, len(semlist)):
results = await getResults_async(token, cookie, semlist[i][1])
return [token, cookie, results]
@app.route('/c')
async def celery_normal():
login = await checkUser_celery()
token = login[0]
cookie = str(login[1])
#print(token, cookie)
#semlist = await getSem_celery(token, cookie)
#print(semlist)
#for i in range(0, len(semlist)):
# results = await getResults_celery(token, cookie, semlist[i][1])
# print(results)
return [token, cookie]
@app.route('/')
async def tests():
normaltime = 0
asynctime = 0
iter = 20
print("Starting...")
for i in range(iter):
start = time.perf_counter()
n = normal()
end = time.perf_counter()
normaltimeloop = end - start
await fetchDUALIS.logOut(n[0], n[1])
start = time.perf_counter()
a = await async_normal()
end = time.perf_counter()
asynctimeloop = end - start
await fetchDUALIS.logOut(a[0], a[1])
if a[2] == n[2]:
normaltime += normaltimeloop
asynctime += asynctimeloop
print(str(((i + 1) / iter) * 100) + '%')
#vergl = "Gleicher Inhalt!"
#else:
# vergl = "ACHTUNG! Ungleicher Inhalt!"
#return "<br><h2>" + vergl + "</h2><br> <h1> Normal: " + str(normaltime) + "</h2><br><br> <h1> Async: " + str(asynctime) + "</h1>"
delta = normaltime / iter - asynctime / iter
return f"<br><h2> Durchschnitt normal: {normaltime / iter} </h2><br><h2> Durchschnitt asynchron: {asynctime / iter} </h2><br><h1> Durchschnittliches Delta: {delta} </h1><br>"
if __name__ == '__main__':
app.run(host='0.0.0.0', port=1024, debug=True)