Styling fix and scraper bug fix

This commit is contained in:
2026-06-14 18:39:55 +02:00
parent 6ed2a3ae0c
commit 566595984f
21 changed files with 2150 additions and 119 deletions
+36 -5
View File
@@ -229,7 +229,9 @@ async def _check_maintenance(page) -> str | None:
# Found maintenance notice; extract the message
# The message is in a nearby .sv-text-portlet-content div
message_el = page.locator("h2.rubrik:has-text('Tekniskt underhål') ~ div .sv-text-portlet-content")
message_el = page.locator(
"h2.rubrik:has-text('Tekniskt underhål') ~ div .sv-text-portlet-content"
)
if await message_el.count() > 0:
message_text = await message_el.first.inner_text(timeout=3_000)
return message_text.strip()
@@ -261,7 +263,8 @@ async def _save_debug_artifacts(page, session_id: str, console_messages: list[st
logger.info(
"scraper debug artifacts saved [id=%s] dir=%s",
session_id, out.resolve(),
session_id,
out.resolve(),
)
except Exception as artifact_exc:
logger.warning("failed to save debug artifacts [id=%s]: %s", session_id, artifact_exc)
@@ -373,7 +376,9 @@ async def _do_login(page, state: _SessionState) -> None:
return
_set_status(state, "authenticating")
logger.debug("[%s] authenticating — waiting for Ansök button (url=%s)", state.session_id, page.url)
logger.debug(
"[%s] authenticating — waiting for Ansök button (url=%s)", state.session_id, page.url
)
# Wait for post-login landing (the Ansök button appears after JS hydrates the page)
await page.wait_for_selector(_SEL.ANSOK_BUTTON, timeout=30_000)
@@ -393,6 +398,22 @@ async def _do_extract(page, state: _SessionState) -> None:
await page.wait_for_selector(_SEL.STEP_DAGAR_KVAR, timeout=30_000)
logger.debug("[%s] valjdagar page loaded (url=%s)", state.session_id, page.url)
# The panel mounts with placeholder zeros, then hydrates from an async fetch.
# Wait until the "av N dagar" total is positive — total is always 240/480, never 0,
# so this is a reliable "real data has arrived" signal (days_left can legitimately be 0).
logger.debug("[%s] waiting for valjdagar data to hydrate", state.session_id)
await page.wait_for_function(
"""(sel) => {
const el = document.querySelector(sel);
if (!el) return false;
const m = el.textContent.match(/av\\s+(\\d+)\\s+dagar/);
return m && parseInt(m[1], 10) > 0;
}""",
arg=_SEL.FOR_DIG_DISCRETE,
timeout=30_000,
)
logger.debug("[%s] valjdagar data hydrated (url=%s)", state.session_id, page.url)
data = await _extract_parental_leave(page, state)
if state.persist_callback is not None:
@@ -428,6 +449,11 @@ async def _extract_parental_leave(page, state: _SessionState) -> ParentalLeaveFe
if not total_match:
raise ValueError(f"Could not parse total days from: {discrete_text!r}")
total_days = int(total_match.group(1))
if total_days == 0:
raise ValueError(
"FK panel returned total_days=0 — data had not hydrated when read "
"(placeholder state). The hydration wait in _do_extract should prevent this."
)
days_used = total_days - days_left
logger.debug("[%s] total_days=%d days_used=%d", state.session_id, total_days, days_used)
@@ -452,14 +478,19 @@ async def _extract_parental_leave(page, state: _SessionState) -> ParentalLeaveFe
sjukpenning_left, lagsta_niva_left = niva_numbers[0], niva_numbers[1]
logger.debug(
"[%s] sjukpenning_left=%d lagsta_niva_left=%d",
state.session_id, sjukpenning_left, lagsta_niva_left,
state.session_id,
sjukpenning_left,
lagsta_niva_left,
)
# Cross-check: sjukpenning_left + lagsta_niva_left should equal days_left
if sjukpenning_left + lagsta_niva_left != days_left:
logger.warning(
"Tier sum mismatch: sjukpenning=%d + lagstaniva=%d != days_left=%d [id=%s]",
sjukpenning_left, lagsta_niva_left, days_left, state.session_id,
sjukpenning_left,
lagsta_niva_left,
days_left,
state.session_id,
)
# -- dubbeldagar remaining --