Initial commit — clean history (removed large test files, browser profiles, Medidata/Clario downloads)
This commit is contained in:
@@ -0,0 +1,233 @@
|
||||
# download_test_results_v1.2.py — dokumentace
|
||||
|
||||
**Verze:** 1.2 · **Datum:** 2026-05-29
|
||||
**Umístění:** `U:\PythonProject\Janssen\Covance_UCO3001\download_test_results_v1.2.py`
|
||||
|
||||
> **Změny v1.2 oproti 1.1:** přidán **paralelní běh přes sharding**
|
||||
> (`--shard N --of M`). Skript lze spustit ve více procesech současně, každý
|
||||
> zpracuje svůj podíl reportů a používá vlastní profil. Spouští se přes launcher
|
||||
> `run_test_results_parallel_v1.0.py`.
|
||||
> **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ě):
|
||||
|
||||
```bat
|
||||
U:\PythonProject\Janssen\.venv\Scripts\python.exe ^
|
||||
U:\PythonProject\Janssen\Covance_UCO3001\run_test_results_parallel_v1.0.py
|
||||
```
|
||||
|
||||
### 2b) Serialně (jeden proces, jako dřív)
|
||||
|
||||
```bat
|
||||
U:\PythonProject\Janssen\.venv\Scripts\python.exe ^
|
||||
U:\PythonProject\Janssen\Covance_UCO3001\download_test_results_v1.2.py
|
||||
```
|
||||
|
||||
### 2c) Jeden konkrétní shard ručně
|
||||
|
||||
```bat
|
||||
…\python.exe download_test_results_v1.2.py --shard 2 --of 4
|
||||
```
|
||||
|
||||
- Prohlížeč běží **viditelně** (`headless=False`), maximalizovaný.
|
||||
- **Persistent profile** — session přežívá (viz sekce 4).
|
||||
|
||||
---
|
||||
|
||||
## 3. Paralelní běh (sharding) — jak funguje
|
||||
|
||||
### 3.1 Rozdělení práce
|
||||
Argumenty `--shard N --of M` rozkrojí seznam všech 34 reportů:
|
||||
|
||||
```python
|
||||
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ě
|
||||
(čekání by se sčítala, ne překrývala). Skutečná paralelnost vyžaduje buď async
|
||||
API (přepis logiky), 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)
|
||||
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čí
|
||||
(`is_visible()` check). Launcher startuje procesy s **rozestupem** (`STAGGER_S`),
|
||||
aby se OKTA login nezahltil 4 současnými požadavky.
|
||||
|
||||
### 3.5 Launcher `run_test_results_parallel_v1.0.py`
|
||||
- `N_SHARDS = 4` — počet souběžných procesů (oken).
|
||||
- `STAGGER_S = 4` — rozestup mezi starty (s).
|
||||
- Každý proces se spustí 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á, až všechny dobíhnou, a vypíše souhrn (které shardy selhaly
|
||||
dle `returncode`).
|
||||
|
||||
### 3.6 Reálné zrychlení
|
||||
Ne přesně 4× — export běží na serveru a velká centra generují hodně řádků,
|
||||
takže reálně spíš 2–3×. Server může souběžné exporty throttlovat.
|
||||
|
||||
---
|
||||
|
||||
## 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`. Všechny shardy ukládají do **stejného**
|
||||
adresáře `Source/`; timestamp + unikátní názvy (site/typ) zaručují, že nedojde ke
|
||||
kolizi. Staré soubory se **nikdy nemažou** (verzování přes timestamp).
|
||||
|
||||
---
|
||||
|
||||
## 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 | otevře login; **pokud je session aktivní, přihlášení 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 |
|
||||
|
||||
Log u každého reportu: `=== Centrum 930557 / microbiology (studie 36940) ===`,
|
||||
prefixovaný `[S{shard}/{of}]` při paralelním běhu.
|
||||
|
||||
---
|
||||
|
||||
## 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** (ověřeno: různé
|
||||
velikosti CSV).
|
||||
|
||||
### 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()`
|
||||
(❌ `get_by_text` → strict mode violation.)
|
||||
|
||||
### 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):
|
||||
```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 nečeká 120 s.
|
||||
|
||||
---
|
||||
|
||||
## 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)
|
||||
```
|
||||
|
||||
`is_visible()` (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.
|
||||
- Souhrn na konci: `hotovo X/Y (shard N/M)` + `SELHALA centra: site/typ, …`.
|
||||
- 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.2.py` | **tento** — Test Results (Standard + Microbiology, 2 studie, sharding) |
|
||||
| `run_test_results_parallel_v1.0.py` | launcher 4 paralelních shardů test-results |
|
||||
Reference in New Issue
Block a user