# PanoramaContacts — CLAUDE.md

## Účel adresáře

Import kontaktů středisek (site contacts) z exportů systému PANORAMA (CTMS) do MySQL a jejich zobrazení ve Streamlit web reportu.
Filtruje pouze záznamy pro **Czechia**. Aktuálně pokryté protokoly:

| Protocol ID | TA |
|---|---|
| `77242113UCO3001` | Immunology |
| `42847922MDD3003` | Neuroscience |

---

## Soubory

| Soubor | Účel |
|---|---|
| `import_CZ_contacts.py` | Import xlsx → MySQL |
| `webreport.py` | Streamlit web report |
| `run_webreport.py` | PyCharm launcher (`streamlit run webreport.py`) |
| `sql/create_CTMS_contacts.sql` | DDL tabulky `CTMS_contacts` |
| `SourceData/*.xlsx` | PANORAMA Dashboard exporty (zdrojová data) |
| `filter_state.json` | Automaticky ukládaný stav filtrů (generuje app) |

---

## MySQL

- **Host:** 192.168.1.76:3306 · **DB:** `studie` · **Tabulka:** `CTMS_contacts`
- **Sheet v xlsx:** `Site Contacts`, header na řádku 6 (0-based index 5)

### Klíčové sloupce tabulky

| Sloupec | Typ | Poznámka |
|---|---|---|
| `file_date` | DATE | Z `dcterms:created` v docProps/core.xml xlsx |
| `imported_at` | DATETIME | Auto timestamp importu |
| `protocol_id` | VARCHAR(20) | Identifikátor studie |
| `site_id` | VARCHAR(15) | Středisko (např. `DD5-CZ10006`) |
| `contact_role` | VARCHAR(50) | Role kontaktu (PI, Study Coordinator, …) |
| `contact_start_date` | DATE | Začátek platnosti kontaktu |
| `contact_end_date` | DATE | Konec platnosti — NULL = stále aktivní |
| `email` | VARCHAR(100) | Hlavní e-mail |

---

## import_CZ_contacts.py

- Zpracuje všechny `*.xlsx` v `SourceData/`
- Přeskočí soubory, jejichž `file_date` ≠ dnešní datum (UTC)
- Přepis: DELETE + INSERT podle `(file_date, protocol_id, country_name)`
- `clean_value()` převede NaN / NaT / Timestamp na typy přijatelné MySQL driverem

---

## webreport.py — Streamlit app

### Filtry (sidebar)

| Filtr | Widget | Logika options |
|---|---|---|
| **Střediska** | radio | Aktivní / Neaktivní / Všechna |
| **Protokol** | selectbox | Z celé DB |
| **Role** | multiselect | Filtrováno dle protokolu + aktivní/neaktivní |
| **Site** | multiselect | Filtrováno dle protokolu + aktivní/neaktivní |
| **Hledání** | text_input | Fulltext přes všechny sloupce řádku |

### Logika filtru Střediska

| Hodnota | Site podmínka | End Date podmínka |
|---|---|---|
| **Aktivní** | `site_id` v `ACTIVE_SITES` | `contact_end_date IS NULL` |
| **Neaktivní** | `site_id` NOT v `ACTIVE_SITES` | bez omezení |
| **Všechna** | bez omezení | bez omezení |

### Aktivní střediska (ACTIVE_SITES)

```python
"77242113UCO3001": {
    "DD5-CZ10001", "DD5-CZ10003", "DD5-CZ10006", "DD5-CZ10009",
    "DD5-CZ10010", "DD5-CZ10012", "DD5-CZ10013", "DD5-CZ10015",
    "DD5-CZ10016", "DD5-CZ10020", "DD5-CZ10021", "DD5-CZ10022",
}
"42847922MDD3003": {
    "S10-CZ10004", "S10-CZ10008", "S10-CZ10011", "S10-CZ10012",
}
```

### Perzistence filtrů

- Stav se ukládá do `filter_state.json` při každé změně filtru (`on_change=save_filter_state`)
- Načítá se jednou za session přes flag `filters_initialized` v `st.session_state`
- Při načítání se hodnoty validují vůči aktuálním options (ochrana před zastaralými daty)

### Clipboard tlačítko

- Knihovna `pyperclip` — kopíruje přímo do Windows clipboardu ze serverové strany
- Formát: `Jméno Příjmení <email@domain.cz>; …`
- Reaguje na aktuálně zobrazené (filtrované) záznamy

### Cache

- `@st.cache_data(ttl=300)` — data se drží 5 minut
- Tlačítko 🔄 Obnovit data volá `st.cache_data.clear()` + `st.rerun()`

---

## Závislosti (venv)

```
mysql-connector-python
pandas
openpyxl
streamlit
pyperclip
```
