Files
2026-09-25 18:11:14 +02:00

4.9 KiB

SmartTime 9 UI integration

Observed read-only against the internal instance on 2026-09-25 using a separate headless Playwright browser. No stamps, corrections or approvals were submitted. The dashboard and September month row both showed 09:29, not exactly 09:30.

Changes from 8.6

  • Login redirects to /login; the user input is now input[name="username"], not useraccount. The password input remains input[name="password"].
  • The application is Vaadin Flow with web components and shadow DOM, not the old GWT dashboard. Setting location.hash = 'dashboard_tab' is obsolete.
  • Sidebar items are vaadin-side-nav-item[path="..."]. Dashboard has an empty path, month view has MonthlyOV; the selected item has a current attribute.
  • The personal card still has a Letzte Buchung label followed by an HH:MM label. It is inside .shadow-xs. Its vaadin-icon[icon="far:building"] uses the status color. The phone icon is not the presence indicator.
  • There is no Aktueller Status value. The status legend contains all four labels regardless of actual presence. Matching the first Anwesend in body text is unsafe. Match the personal building icon's computed fill to each legend swatch's computed background color; unknown or ambiguous colors fail closed.

Loading and recovery

Login, navigation and monthly selections finish asynchronously. Wait for the expected DOM and the Vaadin Flow clients' isActive() queues to become idle. Do not use networkidle: the app maintains background communication. Menu options remain in the DOM after closing; locate visible top-level menu buttons separately.

smarttime_browser.py launches one isolated worker per read. A whole-job watchdog also covers cases in which a Playwright DOM operation or browser shutdown hangs. On Windows it terminates only that job's process tree (taskkill /PID <job> /T /F) before retrying with a new browser. There is no browser profile lock or poisoned thread queue to reuse after a failure. The old profile is left untouched on disk. The subprocess boundary also resolves Streamlit's Windows SelectorEventLoop issue.

UI waits are bounded (normally 40 seconds); the hard job budgets are 75 seconds for status and 180 seconds for export, with at most three read attempts. Exhaustion is an unknown/error result, never proof that a booking failed.

Monthly extraction

  1. Click vaadin-side-nav-item[path="MonthlyOV"] and wait for current.
  2. Read the visible month/year vaadin-menu-bar-button labels. Select the year and month by their exact menu-item names; wait for the new labels and idle server queue.
  3. Read vaadin-grid.Customers-grid. Its shadow root contains #header, #items and the scrolling #table. Each cell's <slot> resolves to light-DOM content via assignedElements(). This preserves empty columns and multiple bookings.
  4. Use the grid's scrollToIndex() to render successive portions of the month. The grid's size must equal the calendar's number of days. Collect by day, compare overlapping rows, and require every date before writing the export.

Observed columns, in order: Datum, an unlabeled notes/icon column, Kommen / Gehen, Soll, Ist, TSaldo, Saldo, Pause, Abwesenheit. The unlabeled column is retained in raw_cells. Booking strings are kept in display order; absence credits are not turned into fictitious bookings.

CLI and diagnostics

scripts/smarttime_cli.py uses exactly the same reader/recovery code as the app. --month YYYY-MM exports JSON; --artifacts DIRECTORY saves a screenshot and sanitized HTML for the dashboard and multiple month scroll positions. The HTML includes declarative shadow-root snapshots; ordinary page.content() omits them. Input value attributes are stripped, but exported time data remains personal.

scripts/smarttime_probe.py is the separate interactive exploration tool. It uses a fresh headless browser, accepts JSON commands on stdin, and saves local snapshots under .local/smarttime/<timestamp>. Example commands:

{"action":"snapshot"}
{"action":"login","user":"input[name=username]","password":"input[name=password]"}
{"action":"selector","selector":"a[href=MonthlyOV]"}
{"action":"snapshot"}
{"action":"quit"}

The probe loads credentials from settings without printing them. Its generic selectors are intended for developer inspection; use only navigation/read controls. The supported CLI exposes only read operations.

Validation

Live dashboard reads, the app's verification API, the Streamlit Jetzt prüfen button, and full August/September 2026 and September 2025 exports were exercised. The Streamlit check used the live backend with the auto-stamp scheduler mocked out. Offline tests cover the real DOM-reading JavaScript, status colors, delayed rendering, unknown/invalid values, slot extraction, full calendar validation, and retry guards. A separate test launches real Chromium with an intercepted navigation that never responds, then verifies watchdog cleanup.