Files
janssen/Covance_UCO3001/Trash/download_test_results_v1.3.md
T
2026-05-29 14:07:57 +02:00

10 KiB
Raw Blame History

download_test_results_v1.3.py — dokumentace

Verze: 1.3 · Datum: 2026-05-29 Umístění: U:\PythonProject\Janssen\Covance_UCO3001\download_test_results_v1.3.py

Změny v1.3 oproti 1.2: robustní login + okno se při chybě nezavře.

  • login()nečeká na networkidle (login SPA labcorp/OKTA jí nikdy nedosáhne → dřív to vedlo k timeoutu a pádu procesu / „zmizelému" oknu). Místo toho čeká přímo na pole Email a Password přes wait_for(state="visible").
  • Celý běh je obalen try/except a na konci je input()konzolové okno shardu se při chybě nezavře, takže je vidět chybový log.
  • Launcher povýšen na run_test_results_parallel_v1.1.py (cílí na v1.3, STAGGER_S = 8).

Změny v1.2 oproti 1.1: paralelní běh přes sharding (--shard N --of M). Změny v1.1 oproti 1.0: druhá studie 35472 (MDD) + report Microbiology.


1. Účel

Automatické stažení reportů Test Results z portálu Labcorp Sponsor Portal (xsp.labcorp.com). Skript projde 2 studie × jejich centra × 2 typy reportu, u každého vyexportuje grid do CSV a uloží ho timestampovaný do Source/.

Studie Interní ID Význam Počet center
UC 36940 77242113UCO3001 12
MDD 35472 druhá studie 5

Typy reportu: Standard (/standard-test-results) a Microbiology (/microbiology).

Celkem: (12 + 5) × 2 = 34 reportů (prázdná centra se přeskakují, viz 6.5).


2. Spuštění

2a) Paralelně (doporučeno — rychlejší)

Přes launcher, který rozjede 4 procesy (každý ve vlastním okně):

U:\PythonProject\Janssen\.venv\Scripts\python.exe ^
  U:\PythonProject\Janssen\Covance_UCO3001\run_test_results_parallel_v1.1.py

2b) Serialně (jeden proces, jako dřív)

U:\PythonProject\Janssen\.venv\Scripts\python.exe ^
  U:\PythonProject\Janssen\Covance_UCO3001\download_test_results_v1.3.py

2c) Jeden konkrétní shard ručně

…\python.exe download_test_results_v1.3.py --shard 2 --of 4
  • Prohlížeč běží viditelně (headless=False), maximalizovaný.
  • Persistent profile — session přežívá (viz sekce 4).
  • Po dokončení (i po chybě) okno čeká na Enter — schválně, ať je vidět log.

3. Paralelní běh (sharding) — jak funguje

3.1 Rozdělení práce

Argumenty --shard N --of M rozkrojí seznam všech 34 reportů:

REPORTS = ALL_REPORTS[SHARD - 1::OF]

Tj. shard 1 z 4 vezme reporty na indexech 0, 4, 8, … ; shard 2 indexy 1, 5, 9, … Rovnoměrné rozdělení, žádný report neudělají dva shardy. Bez argumentů (--of 1) běží serialně přes všech 34.

3.2 Proč nestačily 4 taby v jednom procesu

Playwright sync API je blokujícíwait_for_* drží jediné vlákno celou dobu čekání, takže 4 taby v jednom sync skriptu by se stejně střídaly sériově. Skutečná paralelnost vyžaduje buď async API, nebo — jak je zvoleno zde — 4 nezávislé procesy, kde souběh zajišťuje OS.

3.3 Profil per shard (povinné)

Chrome zamyká adresář profilu → dvě běžící instance nemohou sdílet jeden profil. Proto:

Běh Profil
serialní (--of 1) browser_profile/ (původní)
shard N (--of M>1) browser_profile_{N}/

3.4 Login (varianta B) — POZOR na fragilitu

Session se mezi profily nesdílí. Každý shard se proto při prvním spuštění přihlásí sám (login je jen heslem, žádné MFA). Po prvním běhu už session zůstane uložená v browser_profile_{N}/, takže další běhy login přeskočí.

Klíčová oprava ve v1.3: login stránka labcorp/OKTA je SPA, která nikdy nedosáhne stavu networkidle (běží tam analytika/polling). Původní page.wait_for_load_state("networkidle") se proto zasekl až do timeoutu a shard spadl ještě před vyplněním přihlášení (okno „zmizelo"). Nové login():

page.goto(LOGIN_URL)
try:
    page.get_by_label("Email").wait_for(state="visible", timeout=12000)
except Exception:
    return                       # Email se neobjevil -> session aktivni
page.get_by_label("Email").fill(EMAIL)
page.get_by_role("button", name="Next").click()
page.get_by_label("Password").wait_for(state="visible", timeout=30000)
page.get_by_label("Password").fill(PASSWORD)
page.get_by_role("button", name="Verify").click()
page.wait_for_url(lambda url: "code=" not in url or "xsp." in url, timeout=60000)

Launcher startuje procesy s rozestupem (STAGGER_S = 8 s), aby se OKTA login nezahltil souběžnými požadavky.

3.5 Okno se při chybě nezavře

__main__ je obalen try/except + finally: input(...). Když shard spadne, vypíše FATAL: ... + traceback a čeká na Enter místo okamžitého zavření okna. Tím je vždy vidět, co se pokazilo. (Důsledek: launcher reportuje „HOTOVO" až po zavření všech oken Enterem.)

3.6 Launcher run_test_results_parallel_v1.1.py

  • N_SHARDS = 4 — počet souběžných procesů (oken).
  • STAGGER_S = 8 — rozestup mezi starty (s).
  • Každý proces v novém konzolovém okně (CREATE_NEW_CONSOLE) → logy se neprolínají; navíc každý log má prefix [S{shard}/{of}].
  • Launcher počká na všechny a vypíše souhrn (které shardy selhaly dle returncode).

3.7 Reálné zrychlení

Ne přesně 4× — export běží na serveru, velká centra generují hodně řádků → reálně spíš 23×. Server může souběžné exporty throttlovat.


4. Výstupní soubory

{YYYY-MM-DD_HHMMSS} sponsor-study-{STUDY}-test-results-{SITE_ID}-{TYP}.csv

{TYP} = standard nebo microbiology. Všechny shardy ukládají do stejného adresáře Source/; timestamp + unikátní názvy (site/typ) zaručí, že nedojde ke kolizi. Staré soubory se nikdy nemažou.


5. Průběh jednoho reportu (kroky + logging)

Fáze Co dělá
START vypíše shard/of, profil, počet reportů; spustí prohlížeč
LOGIN čeká na pole Email; pokud není → session aktivní, login přeskočí
KROK 1/5 navigace na report URL
KROK 2/5 čeká na .ag-row nebo prázdný grid („No Data"); pak stabilizace počtu
KROK 3/5 klik na viditelné tři tečky (more_horiz) → menu
KROK 4/5 klik na viditelné „Export to CSV" → zachytí download
KROK 5/5 uloží soubor do OUT_DIR
KONEC souhrn hotovo X/Y (shard N/M) + seznam selhaných; pak čeká na Enter

6. Klíčové technické poznatky (PROČ to tak je) — ověřeno přes Chrome MCP

Stránka test-results je Angular SPA s MDL a tabulkou AG Grid. Microbiology záložka má stejnou strukturu jako Standard.

6.1 Čekání na data = řádky AG Gridu

  • Data načtena, jakmile se objeví div.ag-row (count 0 → N).
  • Řádky jsou position-absolute → Playwright je nepovažuje za „visible":
    • wait_for_selector("div.ag-row") (default visible) timeoutuje.
    • wait_for_function("() => document.querySelectorAll('div.ag-row').length > 0").
  • Stabilizace: počet se čte co 2 s, dokud se 2× neshodne.
  • Počet .ag-row je zastropený (~153) virtuálním renderem — NENÍ to skutečný počet záznamů. Export do CSV ale vyexportuje VŠECHNY řádky.

6.2 „Fetching Data" / spinner — NEPLATÍ pro tuto stránku

Na test-results NENÍ text „Fetching Data" (je jen na samples reportu). <loading-bar> jen problikne → nespoléhat. Signál = .ag-row.

6.3 Tři tečky (export) — DVA <ag-export>

2× <ag-export> (1 skrytý) → filtr na viditelnost: page.locator("ag-export button:visible", has_text="more_horiz").first.click()

6.4 „Export to CSV" — DVĚ položky v DOM

2× mdl-menu-item (1 skrytá) → page.locator("mdl-menu-item:visible", has_text="Export to CSV").first.click()

6.5 Prázdné centrum (ověřeno přes Chrome MCP na centru 930551)

  • No-rows overlay s textem „No Data" ve .ag-overlay-no-rows-wrapper.
  • ⚠️ Třída NENÍ .ag-overlay-no-rows-center.
  • ⚠️ Text NENÍ v .ag-body-viewport (ten je prázdný, height: 1px).
  • ⚠️ Na stránce 2 overlaye (1 skrytý) → kontrola offsetParent !== null.
  • Detekce (KROK 2):
    () => {
      if (document.querySelectorAll('div.ag-row').length > 0) return false;
      return [...document.querySelectorAll('.ag-overlay-no-rows-wrapper')]
          .some(e => e.offsetParent !== null);
    }
    
  • KROK 2 čeká na .ag-row NEBO tuto detekci → prázdné centrum nečeká 120 s.

7. Login logika (sdílená napříč Covance skripty) — v1.3

page.goto(LOGIN_URL)
# NEcekat na networkidle (login SPA ji nikdy nedosahne)
try:
    page.get_by_label("Email").wait_for(state="visible", timeout=12000)
except Exception:
    return                       # session aktivni → login přeskočit
# jinak: Email → Next → cekej na Password → Password → Verify → wait redirect

wait_for(state="visible") na pole (NE networkidle, NE kontrola URL — po redirectu zůstává xsp.covance.com).


8. Chrome flagy proti „Restore pages" / broken session

--disable-restore-session-state
--disable-session-crashed-bubble

9. Robustnost

  • Každý report v try/except → chyba u jednoho nezastaví zbytek shardu.
  • Celý běh shardu v try/except + finally: input() → okno se při pádu nezavře.
  • Souhrn na konci: hotovo X/Y (shard N/M) + SELHALA centra: ….
  • Launcher hlídá returncode každého shardu.

10. Možná budoucí rozšíření

  • Více/méně procesů: změnit N_SHARDS v launcheru (profily _1.._N).
  • Další studie / centra / typ reportu: přidat do STUDIES / REPORT_TYPES.
  • Vyčistit profily: smazat browser_profile_*/ (vynutí nový login).

11. Příbuzné skripty (stejná složka / portál)

Skript Co stahuje
download_samples_report_v1.1.py All Samples (sampletracking)
download_kit_inventory_v2.1.py Kit inventory
download_equeries_report_v1.1.py eQuery reporty (zdroj SITE_IDS pro 36940)
download_test_results_v1.3.py tento — Test Results (Standard + Microbiology, 2 studie, sharding)
run_test_results_parallel_v1.1.py launcher 4 paralelních shardů test-results