feat: working coronang

This commit is contained in:
eneller
2025-04-14 22:30:30 +02:00
parent ce9a476adf
commit 36bac6f18c
2 changed files with 34 additions and 20 deletions

View File

@@ -1,6 +1,6 @@
[project] [project]
name = "uulm-utils" name = "uulm-utils"
version = "0.1.0" version = "1.0"
description = "Collection of helpers for Ulm University" description = "Collection of helpers for Ulm University"
readme = "README.md" readme = "README.md"
requires-python = ">=3.12" requires-python = ">=3.12"

View File

@@ -1,6 +1,6 @@
import asyncclick as click import asyncclick as click
import questionary import questionary
from playwright.async_api import async_playwright, Playwright from playwright.async_api import async_playwright, Playwright, expect
import pandas as pd import pandas as pd
from dotenv import load_dotenv from dotenv import load_dotenv
@@ -9,7 +9,7 @@ from enum import Enum
import asyncio import asyncio
from time import sleep from time import sleep
import logging import logging
from datetime import datetime from datetime import datetime, timedelta
load_dotenv() # take environment variables load_dotenv() # take environment variables
@@ -54,6 +54,8 @@ async def cli(ctx, username, password, headful, debug):
if(debug): logger.setLevel(logging.DEBUG) if(debug): logger.setLevel(logging.DEBUG)
ctx.ensure_object(dict) ctx.ensure_object(dict)
ctx.obj['HEADLESS'] = not headful ctx.obj['HEADLESS'] = not headful
ctx.obj['USERNAME'] = username
ctx.obj['PASSWORD'] = password
@cli.command() @cli.command()
@click.pass_context @click.pass_context
@@ -73,7 +75,7 @@ async def campusonline(ctx):
selection = await selection_or_walk(options) selection = await selection_or_walk(options)
print(selection) print(selection)
sleep(2) sleep(2)
raise NotImplementedError return
@cli.command() @cli.command()
@click.argument('target_times', nargs=-1, type=click.DateTime( ['%H:%M:%S']), required=True) @click.argument('target_times', nargs=-1, type=click.DateTime( ['%H:%M:%S']), required=True)
@@ -85,31 +87,41 @@ async def coronang(ctx, target_times, before):
Please beware that CoronaNG only allows one active session at all times. Please beware that CoronaNG only allows one active session at all times.
''' '''
CORONANG_VERSION='v1.8.00' CORONANG_VERSION='v1.8.00'
CORONANG_URL="https://campusonline.uni-ulm.de/CoronaNG/user/mycorona.html"
logger.info('Parsed input times as %s', target_times) logger.info('Parsed input times as %s', target_times)
before_seconds = timedelta(seconds=before)
target_times = sorted(list(target_times))
async for page, browser, context in run_playwright(ctx.obj['HEADLESS']): async for page, browser, context in run_playwright(ctx.obj['HEADLESS']):
await page.goto("https://campusonline.uni-ulm.de/CoronaNG/user/mycorona.html") await page.goto(CORONANG_URL)
server_version = await page.locator("css=#mblock_innen > a:nth-child(1)").inner_text() server_version = await page.locator("css=#mblock_innen > a:nth-child(1)").inner_text()
if(server_version != CORONANG_VERSION): if(server_version != CORONANG_VERSION):
logger.warning('Read CoronaNG version %s. Last tested version is %s. Please use --headful flag to ensure that everything is working.', logger.warning('Read CoronaNG version %s. Last tested version is %s. Please use --headful flag to ensure that everything is working.',
server_version, CORONANG_VERSION) server_version, CORONANG_VERSION)
# iterate over staggered login
for target_time in target_times: for target_time in target_times:
# wait for execution
while True: while True:
server_str = await page.locator("css=#mblock_innen").inner_text() server_str = await page.locator("css=#mblock_innen").inner_text()
server_time = datetime.strptime(server_str.split().pop(), "%H:%M:%S") server_time = datetime.strptime(server_str.split().pop(), "%H:%M:%S")
break dtime = target_time -server_time
dtime_before = dtime - before_seconds
raise NotImplementedError logger.debug('Server Time: %s, delta: %s', server_time, dtime_before)
# execute
if dtime_before < timedelta(0):
# login necessary?
if (await page.locator("input[name=\"uid\"]").count()) >0:
await page.locator("input[name=\"uid\"]").click() await page.locator("input[name=\"uid\"]").click()
await page.locator("input[name=\"uid\"]").fill(ctx.obj['USERNAME']) await page.locator("input[name=\"uid\"]").fill(ctx.obj['USERNAME'])
await page.locator("input[name=\"password\"]").click() await page.locator("input[name=\"password\"]").click()
await page.locator("input[name=\"password\"]").fill(ctx.obj['PASSWORD']) await page.locator("input[name=\"password\"]").fill(ctx.obj['PASSWORD'])
await page.get_by_role("button", name="Anmelden").click() await page.get_by_role("button", name="Anmelden").click()
await page.get_by_role("link", name="Beobachtungen & Teilnahmen").click() await page.goto(CORONANG_URL)
await page.get_by_role("table", name="Ihre Beobachtungen. Sie kö").get_by_role("button").click() await page.get_by_role("table", name="Ihre Beobachtungen. Sie kö").get_by_role("button").click()
await page.get_by_role("table", name="Ihre Beobachtungen. Sie kö").get_by_role("combobox").select_option("5") await page.get_by_role("table", name="Ihre Beobachtungen. Sie kö").get_by_role("combobox").select_option("5")
await page.get_by_role("cell", name="An Markierten teilnehmen Ausf").get_by_role("button").click() await page.get_by_role("cell", name="An Markierten teilnehmen Ausf").get_by_role("button").click()
await page.reload() await page.reload()
return
@cli.command() @cli.command()
@click.argument('target_times', nargs=-1, type=click.DateTime( ["%H:%M:%S"]), required=True) @click.argument('target_times', nargs=-1, type=click.DateTime( ["%H:%M:%S"]), required=True)
@@ -124,9 +136,11 @@ async def sport(ctx, target_times, target_course, before):
print(target_course) print(target_course)
# TODO Check Version in HTML Head # TODO Check Version in HTML Head
logger.info('Parsed input times as %s', target_times) logger.info('Parsed input times as %s', target_times)
before_seconds = timedelta(seconds=before)
target_times = sorted(list(target_times))
async for page, browser, context in run_playwright(ctx.obj['HEADLESS']): async for page, browser, context in run_playwright(ctx.obj['HEADLESS']):
pass pass
raise NotImplementedError return
@cli.command() @cli.command()
@click.argument('filename', type=click.Path(exists=True)) @click.argument('filename', type=click.Path(exists=True))
@@ -152,7 +166,7 @@ def grades(filename, target_lp:int):
acc_note = acc_note + weight * row['grade'] acc_note = acc_note + weight * row['grade']
logger.debug('Added "%s" with %d/%d credits and grade %.1f', row['name'], weight, row['credits'], row['grade']) logger.debug('Added "%s" with %d/%d credits and grade %.1f', row['name'], weight, row['credits'], row['grade'])
acc_note: float = acc_note / acc_lp acc_note: float = acc_note / acc_lp
print(f'Final Grade: {acc_note}') print(f'Final Grade: {acc_note} with {acc_lp}/{target_lp} credits')
if __name__ == "__main__": if __name__ == "__main__":
asyncio.run(cli.main()) asyncio.run(cli.main())