z230
This commit is contained in:
@@ -0,0 +1,218 @@
|
||||
# download_test_results_v1.4.py — dokumentace
|
||||
|
||||
**Verze:** 1.4 · **Datum:** 2026-05-29
|
||||
**Umístění:** `U:\PythonProject\Janssen\Covance\download_test_results_v1.4.py`
|
||||
|
||||
> **Změny v1.4 oproti 1.3:** **retry na úrovni reportu.**
|
||||
> Při paralelním běhu server občas vrátí timeout (zejm. `page.goto` 30 s,
|
||||
> nebo `wait_for_function` 120 s na grid). Dřív byl takový report rovnou
|
||||
> zapsán jako selhaný. Teď se každý report zkusí až **2× (MAX_ATTEMPTS=2)**,
|
||||
> mezi pokusy 5 s pauza a `goto("about:blank")` jako reset. Většina
|
||||
> přechodných timeoutů projde napodruhé.
|
||||
> Launcher povýšen na `run_test_results_parallel_v1.2.py` (cílí na v1.4).
|
||||
>
|
||||
> **Změny v1.3 oproti 1.2:** robustní login (NEčekat na `networkidle`,
|
||||
> čekat přímo na pole Email/Password) + okno se při pádu nezavře
|
||||
> (try/except + input()).
|
||||
> **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ší)
|
||||
|
||||
```bat
|
||||
U:\PythonProject\Janssen\.venv\Scripts\python.exe ^
|
||||
U:\PythonProject\Janssen\Covance\run_test_results_parallel_v1.2.py
|
||||
```
|
||||
|
||||
### 2b) Serialně (jeden proces)
|
||||
|
||||
```bat
|
||||
U:\PythonProject\Janssen\.venv\Scripts\python.exe ^
|
||||
U:\PythonProject\Janssen\Covance\download_test_results_v1.4.py
|
||||
```
|
||||
|
||||
### 2c) Jeden konkrétní shard ručně
|
||||
|
||||
```bat
|
||||
…\python.exe download_test_results_v1.4.py --shard 2 --of 4
|
||||
```
|
||||
|
||||
- Prohlížeč běží **viditelně** (`headless=False`), maximalizovaný.
|
||||
- Po dokončení (i po chybě) okno **čeká na Enter** — ať je vidět log.
|
||||
|
||||
---
|
||||
|
||||
## 3. Retry (v1.4)
|
||||
|
||||
### 3.1 Proč
|
||||
Z reálného běhu 4 shardů (29. 5. 2026) ze 34 reportů selhaly 2:
|
||||
- `930539/standard` — `page.goto` timeout 30 s (server vůbec neodpověděl)
|
||||
- `930547/standard` — `wait_for_function` timeout 120 s (grid se nedotáhl)
|
||||
|
||||
Obojí jsou **přechodné serverové timeouty** (sharding ho zatěžuje). Stačí
|
||||
počkat a zkusit znovu — propadne to.
|
||||
|
||||
### 3.2 Jak
|
||||
Konstanty:
|
||||
```python
|
||||
MAX_ATTEMPTS = 2 # 1. pokus + 1 retry
|
||||
RETRY_BACKOFF_S = 5 # pauza pred opakovanym pokusem
|
||||
```
|
||||
|
||||
`download_report(page, report)` je wrapper, který volá
|
||||
`download_report_once(...)` v cyklu 1..MAX_ATTEMPTS. Při výjimce zaloguje
|
||||
`POKUS X/N SELHAL: …`, počká `RETRY_BACKOFF_S`, navštíví `about:blank`
|
||||
(reset gridu), a zkusí znovu. Když selže poslední pokus, výjimka se propustí
|
||||
ven a vnější smyčka v `main()` to zapíše do `failed`.
|
||||
|
||||
### 3.3 Co se nezopakuje
|
||||
Login — ten je mimo retry. Pokud spadne login, shard skončí přes vnější
|
||||
`try/except` v `__main__` s `FATAL: ...` a okno čeká na Enter.
|
||||
|
||||
---
|
||||
|
||||
## 4. Paralelní běh (sharding) — beze změny vůči v1.3
|
||||
|
||||
### 4.1 Rozdělení práce
|
||||
```python
|
||||
REPORTS = ALL_REPORTS[SHARD - 1::OF]
|
||||
```
|
||||
Shard 1 z 4 vezme indexy 0, 4, 8, … atd. Bez argumentů = serial.
|
||||
|
||||
### 4.2 Profil per shard
|
||||
| Běh | Profil |
|
||||
|---|---|
|
||||
| serialní (`--of 1`) | `browser_profile/` |
|
||||
| shard N (`--of M>1`) | `browser_profile_{N}/` |
|
||||
|
||||
Chrome zamyká adresář profilu → 2 instance nesdílí 1 profil.
|
||||
|
||||
### 4.3 Login (varianta B)
|
||||
Každý shard se přihlásí sám (jen heslo, žádné MFA). Session se uloží do
|
||||
`browser_profile_{N}/` → další běh login přeskočí. Launcher startuje
|
||||
s rozestupem `STAGGER_S = 8 s`, aby se OKTA nezahltil.
|
||||
|
||||
### 4.4 Launcher `run_test_results_parallel_v1.2.py`
|
||||
- `N_SHARDS = 4`, `STAGGER_S = 8 s`.
|
||||
- Každý proces v novém okně (`CREATE_NEW_CONSOLE`) → logy se neprolínají.
|
||||
- Prefix logu: `[S{shard}/{of}]`.
|
||||
|
||||
### 4.5 Reálné zrychlení
|
||||
~2–3× (server throttluje exporty, ne přesně 4×).
|
||||
|
||||
---
|
||||
|
||||
## 5. Výstupní soubory
|
||||
|
||||
```
|
||||
{YYYY-MM-DD_HHMMSS} sponsor-study-{STUDY}-test-results-{SITE_ID}-{TYP}.csv
|
||||
```
|
||||
|
||||
Všechny shardy ukládají do `Source/`. Staré se **nikdy nemažou**.
|
||||
|
||||
---
|
||||
|
||||
## 6. Klíčové technické poznatky — ověřeno přes Chrome MCP
|
||||
|
||||
### 6.1 Čekání na data = `.ag-row`
|
||||
- ❌ `wait_for_selector("div.ag-row")` (default visible) timeoutuje
|
||||
(řádky jsou `position-absolute`).
|
||||
- ✅ `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. Export do
|
||||
CSV ale vyexportuje VŠECHNY řádky.
|
||||
|
||||
### 6.2 „Fetching Data" / spinner — NEPLATÍ pro tuto stránku
|
||||
Je jen na *samples* reportu. Signál = `.ag-row`.
|
||||
|
||||
### 6.3 Tři tečky (export) — DVA `<ag-export>`
|
||||
✅ `page.locator("ag-export button:visible", has_text="more_horiz").first.click()`
|
||||
|
||||
### 6.4 „Export to CSV" — DVĚ položky v DOM
|
||||
✅ `page.locator("mdl-menu-item:visible", has_text="Export to CSV").first.click()`
|
||||
|
||||
### 6.5 Prázdné centrum
|
||||
- No-rows overlay `.ag-overlay-no-rows-wrapper` (NE `-no-rows-center`).
|
||||
- 2 overlaye, 1 skrytý → kontrola `offsetParent !== null`.
|
||||
- KROK 2 čeká na `.ag-row` **NEBO** prázdný overlay → nečeká 120 s zbytečně.
|
||||
|
||||
---
|
||||
|
||||
## 7. Login logika (v1.3+) — beze změny ve v1.4
|
||||
|
||||
```python
|
||||
page.goto(LOGIN_URL)
|
||||
try:
|
||||
page.get_by_label("Email").wait_for(state="visible", timeout=12000)
|
||||
except Exception:
|
||||
return # session aktivni → login přeskočit
|
||||
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)
|
||||
```
|
||||
|
||||
**NE** `networkidle`. **NE** kontrola URL (po redirectu zůstává `xsp.covance.com`).
|
||||
|
||||
---
|
||||
|
||||
## 8. Chrome flagy
|
||||
|
||||
```
|
||||
--disable-restore-session-state
|
||||
--disable-session-crashed-bubble
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. Robustnost
|
||||
|
||||
- **Retry per report** (`MAX_ATTEMPTS = 2`) → přechodné timeouty propadnou.
|
||||
- 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 pokusů:** zvednout `MAX_ATTEMPTS` (3+) — pro hodně přetížený server.
|
||||
- **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
|
||||
|
||||
| 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.4.py` | **tento** — Test Results (Standard + Microbiology, 2 studie, sharding, retry) |
|
||||
| `run_test_results_parallel_v1.2.py` | launcher 4 paralelních shardů test-results |
|
||||
Reference in New Issue
Block a user