232 lines
9.1 KiB
Markdown
232 lines
9.1 KiB
Markdown
# download_test_results_v1.1.py — dokumentace
|
||
|
||
**Verze:** 1.1 · **Datum:** 2026-05-29
|
||
**Umístění:** `U:\PythonProject\Janssen\Covance_UCO3001\download_test_results_v1.1.py`
|
||
|
||
> **Změny v1.1 oproti 1.0:** přidána **druhá studie 35472 (MDD)** a druhý typ
|
||
> reportu **Microbiology**. Dříve jen 36940 + Standard.
|
||
|
||
---
|
||
|
||
## 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 (záložka v URL): **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í
|
||
|
||
```bat
|
||
U:\PythonProject\Janssen\.venv\Scripts\python.exe ^
|
||
U:\PythonProject\Janssen\Covance_UCO3001\download_test_results_v1.1.py
|
||
```
|
||
|
||
- Prohlížeč běží **viditelně** (`headless=False`), maximalizovaný.
|
||
- **Persistent profile** (`browser_profile/` vedle skriptu) — session přežívá.
|
||
|
||
### Dočasný test (1 centrum / studie)
|
||
|
||
Pro rychlé ověření stačí dočasně zúžit `STUDIES`, např.:
|
||
|
||
```python
|
||
STUDIES = [
|
||
{"study": "36940", "sites": ["930557"]}, # UC — centrum s daty
|
||
{"study": "35472", "sites": ["898745"]}, # MDD — první centrum
|
||
]
|
||
```
|
||
|
||
→ proběhnou 4 reporty (2 centra × 2 typy). Po testu vrátit plný seznam.
|
||
|
||
---
|
||
|
||
## 3. Konfigurace (konstanty v hlavičce skriptu)
|
||
|
||
| Konstanta | Hodnota / význam |
|
||
|---|---|
|
||
| `EMAIL` | `vbuzalka@its.jnj.com` (login přes iMedidata/OKTA na xsp.covance.com) |
|
||
| `PASSWORD` | heslo k účtu (uložené přímo v kódu) |
|
||
| `LOGIN_URL` | `https://xsp.covance.com/` — po přihlášení redirect na xsp.labcorp.com |
|
||
| `OUT_DIR` | `U:\PythonProject\Janssen\Covance_UCO3001\Source` |
|
||
| `PROFILE_DIR` | `browser_profile/` vedle skriptu (persistent Chromium profil) |
|
||
| `STUDIES` | seznam studií, každá má `study` (interní ID) + `sites` (interní ID center) |
|
||
| `REPORT_TYPES` | `standard-test-results`/`standard` a `microbiology`/`microbiology` |
|
||
|
||
### Interní čísla center
|
||
|
||
```
|
||
36940 (UC): 930551, 930556, 930525, 930549, 930543, 930547,
|
||
930555, 930557, 930539, 930536, 930553, 930531
|
||
35472 (MDD): 898745, 898739, 898733, 898744, 898727
|
||
```
|
||
|
||
> **Pozor:** jsou to **interní ID** Labcorpu, **nikoli** čísla center typu
|
||
> CZ10001. V URL test-results se používá právě toto interní ID:
|
||
> `…/test-results/{SITE_ID}/{standard-test-results|microbiology}`.
|
||
> (Pozn.: 36940 zdroj = `download_equeries_report_v1.1.py SITES`;
|
||
> 35472 zdroj = ručně dodaná čísla center.)
|
||
|
||
### Generování REPORTS (DRY)
|
||
|
||
`REPORTS` se sestaví automaticky jako **kartézský součin**
|
||
`STUDIES × sites × REPORT_TYPES`. Přidání/odebrání centra, studie nebo typu
|
||
reportu = úprava příslušného seznamu, vzor URL i názvu se píše jen jednou.
|
||
|
||
---
|
||
|
||
## 4. Výstupní soubory
|
||
|
||
Formát názvu (timestamp + popisný název):
|
||
|
||
```
|
||
{YYYY-MM-DD_HHMMSS} sponsor-study-{STUDY}-test-results-{SITE_ID}-{TYP}.csv
|
||
```
|
||
|
||
`{TYP}` = `standard` nebo `microbiology`. Příklady:
|
||
```
|
||
2026-05-29_131121 sponsor-study-36940-test-results-930557-standard.csv
|
||
2026-05-29_131500 sponsor-study-36940-test-results-930557-microbiology.csv
|
||
2026-05-29_131800 sponsor-study-35472-test-results-898745-standard.csv
|
||
```
|
||
|
||
- Timestamp se generuje pro **každý** report zvlášť (v okamžiku exportu).
|
||
- Staré soubory se **nikdy nemažou** (verzování přes timestamp).
|
||
- `expect_download` zachytí download event, `save_as()` uloží pod naším názvem.
|
||
|
||
---
|
||
|
||
## 5. Průběh skriptu (kroky + logging)
|
||
|
||
Každý krok se loguje s časem (`[HH:MM:SS]`, `flush=True`).
|
||
|
||
| Fáze | Co dělá |
|
||
|---|---|
|
||
| START | spustí prohlížeč |
|
||
| LOGIN | otevře login; **pokud je session aktivní, přihlášení přeskočí** |
|
||
| Pro každý report (studie × centrum × typ): | |
|
||
| KROK 1/5 | navigace na report URL |
|
||
| KROK 2/5 | čeká na řádky gridu `.ag-row` **nebo** prázdný grid („No Data"); pak stabilizace počtu |
|
||
| KROK 3/5 | klik na viditelné tři tečky (`more_horiz`) → otevře menu |
|
||
| KROK 4/5 | klik na viditelné „Export to CSV" → zachytí download |
|
||
| KROK 5/5 | uloží soubor do `OUT_DIR` |
|
||
| KONEC | souhrn `hotovo X/34` + seznam selhaných (`site/typ`) |
|
||
|
||
Log u každého reportu ukazuje i typ: `=== Centrum 930557 / microbiology (studie 36940) ===`.
|
||
|
||
---
|
||
|
||
## 6. Klíčové technické poznatky (PROČ to tak je) — ověřeno přes Chrome MCP
|
||
|
||
Stránka test-results je **Angular SPA** s knihovnou **MDL** a tabulkou **AG Grid**.
|
||
Microbiology záložka má **stejnou** strukturu jako Standard → vše níže platí pro oba.
|
||
|
||
### 6.1 Čekání na data = řádky AG Gridu
|
||
- Data jsou načtena, jakmile se objeví řádky **`div.ag-row`** (count 0 → N).
|
||
- **Řádky jsou `position-absolute`** (virtuální render) → Playwright je
|
||
**nepovažuje za „visible"**. Proto:
|
||
- ❌ `wait_for_selector("div.ag-row")` (default visible) **timeoutuje**.
|
||
- ✅ `wait_for_function("() => document.querySelectorAll('div.ag-row').length > 0")`.
|
||
- Stabilizace: počet řádků se čte co 2 s, dokud se 2× po sobě neshodne.
|
||
- **POZN.: počet `.ag-row` se „zastropuje"** (typicky ~153) kvůli virtuálnímu
|
||
renderu — NENÍ to skutečný počet záznamů. Slouží jen jako signál „data jsou".
|
||
**Export do CSV ale vyexportuje VŠECHNY řádky** (ověřeno: různé velikosti CSV).
|
||
|
||
### 6.2 „Fetching Data" / spinner — NEPLATÍ pro tuto stránku
|
||
- Na test-results **NENÍ** text „Fetching Data" (ten je na *samples* reportu).
|
||
- `<loading-bar>` jen problikne při route-loadu → nespoléhat. Signál = `.ag-row`.
|
||
|
||
### 6.3 Tři tečky (export) — na stránce jsou DVA `<ag-export>`
|
||
- 2× `<ag-export>` (1 skrytý), 3× ikona `more_horiz`. Proto filtr na viditelnost:
|
||
✅ `page.locator("ag-export button:visible", has_text="more_horiz").first.click()`
|
||
|
||
### 6.4 „Export to CSV" — v DOM jsou DVĚ položky
|
||
- 2× `mdl-menu-item` „Export to CSV" (1 skrytá). Proto:
|
||
✅ `page.locator("mdl-menu-item:visible", has_text="Export to CSV").first.click()`
|
||
- ❌ `get_by_text("Export to CSV")` → strict mode violation (2 elementy).
|
||
|
||
### 6.5 Prázdné centrum (ověřeno přes Chrome MCP na centru 930551)
|
||
- AG Grid při 0 záznamech zobrazí no-rows overlay s textem **„No Data"**.
|
||
- Struktura: `.ag-overlay` → `.ag-overlay-panel` →
|
||
**`.ag-overlay-no-rows-wrapper`** → `<span>No Data</span>`.
|
||
- ⚠️ Třída **NENÍ** `.ag-overlay-no-rows-center` (chybný předpoklad, nikdy
|
||
nezabral) — správně je **`.ag-overlay-no-rows-wrapper`**.
|
||
- ⚠️ Text „No Data" **NENÍ** v `.ag-body-viewport` (ten je prázdný, `height: 1px`).
|
||
- ⚠️ Na stránce jsou **2 overlaye** (1 skrytý, 1 viditelný) → kontrola
|
||
viditelnosti `offsetParent !== null`.
|
||
- Detekce (KROK 2):
|
||
```js
|
||
() => {
|
||
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 necheká
|
||
zbytečně 120 s a export se přeskočí.
|
||
|
||
---
|
||
|
||
## 7. Login logika (sdílená napříč Covance skripty)
|
||
|
||
```python
|
||
page.goto(LOGIN_URL)
|
||
page.wait_for_load_state("networkidle")
|
||
if not page.get_by_label("Email").is_visible():
|
||
return # session aktivní → login přeskočit
|
||
# jinak: Email → Next → Password → Verify → wait redirect (code= zmizí z URL)
|
||
```
|
||
|
||
- **Proč `is_visible()` a ne kontrola URL:** po `goto(LOGIN_URL)` zůstane URL
|
||
`xsp.covance.com` i po redirectu, takže URL test je nespolehlivý.
|
||
- `is_visible()` neháže výjimku — když pole není, vrátí `False`.
|
||
|
||
---
|
||
|
||
## 8. Chrome flagy proti „Restore pages" / broken session
|
||
|
||
```
|
||
--disable-restore-session-state # neobnovovat předchozí session
|
||
--disable-session-crashed-bubble # potlačit "Chromium didn't shut down correctly"
|
||
```
|
||
|
||
---
|
||
|
||
## 9. Robustnost smyčky
|
||
|
||
- Každý report je v `try/except` → **chyba u jednoho nezastaví zbytek**.
|
||
- Na konci souhrn: `hotovo X/34` + seznam `SELHALA centra: site/typ, …`.
|
||
|
||
---
|
||
|
||
## 10. Možná budoucí rozšíření
|
||
|
||
- **Další studie / centra**: přidat položku do `STUDIES`.
|
||
- **Další typ reportu**: přidat položku do `REPORT_TYPES` (slug + suffix).
|
||
- **Ověření počtu**: počet `.ag-row` se loguje — POZOR, je zastropený virtuálním
|
||
renderem, takže ho nelze porovnávat s počtem řádků v CSV (CSV má všechny).
|
||
|
||
---
|
||
|
||
## 11. Příbuzné skripty (stejná složka / portál)
|
||
|
||
| Skript | Co stahuje |
|
||
|---|---|
|
||
| `download_samples_report_v1.1.py` | All Samples (sampletracking, čeká na „Fetching Data") |
|
||
| `download_kit_inventory_v2.1.py` | Kit inventory (on-hand expiration) |
|
||
| `download_equeries_report_v1.1.py` | eQuery reporty (zdroj SITE_IDS pro 36940) |
|
||
| `download_test_results_v1.1.py` | **tento** — Test Results (Standard + Microbiology, 2 studie) |
|
||
|
||
Všechny sdílejí: persistent profile, login s `is_visible()` checkem,
|
||
`ag-export` + „Export to CSV" pattern.
|