Files
medicus/MedicusWithClaude/CLAUDE_NOTES.md
2026-03-20 18:03:44 +01:00

364 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# MedicusWithClaude poznámky pro Clauda
## Stav projektu
- Zahájeno: 2026-03-13
- Cíl: průzkum Firebird DB Medicus → analýzy a reporty
- Zatím: úspěšně čteme i zapisujeme do DB, rozumíme RTF formátu
- **2026-03-17**: Obnovena session Claude si přečetl poznámky, připraven pokračovat
- **2026-03-18**: Obnovena session Claude si přečetl poznámky, připraven pokračovat. Průběžně zapisuje do tohoto souboru.
- **2026-03-20**: Obnovena session merge logika z `test_import_single.py` integrována do `s03soubory.py`. Import pipeline kompletní.
## Bezpečnost
- Pracujeme na **místní kopii** poškození DB nevadí, obnova = 5 minut
- Lze bez obav experimentovat, zapisovat testovací data atd.
- Testovací záznamy nemusíme mazat
## Připojení k DB
```python
import fdb
conn = fdb.connect(
dsn=r'localhost:c:\medicus 3\data\medicus.fdb',
user='SYSDBA',
password='masterkey',
charset='win1250'
)
```
- fdb verze 2.0.4, Python na PATH (prostý `python` příkaz)
- Firebird instalace: `C:\Program Files\Firebird\Firebird_2_5_CGM\bin\`
- Jedná se o **HQbird** (IBSurgeon enhanced Firebird) port **3070** (ne standardní 3050)
- fbtracemgr trace nefungoval spolehlivě doporučeno použít **FBScanner** (stejný výrobce)
## O Medicusu
- Lékařský software pro praktického lékaře
- Vyvíjen od ~roku 2000, organicky rostl přidáváním funkcí
- Firebird 2.5 nikdy nepřejdou na nový (desetitisíce instalací, 993 tabulek)
- Žádné zabezpečení DB vše čitelné přes SYSDBA/masterkey
- Charset: win1250
## DB fakta
- 993 tabulek celkem
- Charset: win1250
## Klíčové tabulky
### KAR Kartotéka (základní kámen Medicusu)
- 6319 pacientů všichni kdo kdy prošli ordinací
- Primární klíč: **IDPAC** (interní ID, používá se jako FK v ostatních tabulkách)
- Identifikace pacienta v ČR/pojišťovna: **RODCIS** (rodné číslo, bez lomítka, např. 7309208104)
- **PRIJMENI**, **JMENO**, TITUL, TITULZA, DATNAR, POHLAVI
- **POJ** pojišťovna (3 znaky, např. 111 = VZP)
- Adresy: TRV* (trvalé), PRE* (přechodné)
- **VYRAZEN** příznak vyřazeného pacienta
- HLAVDGN hlavní diagnóza
### DEKURS záznamy z návštěv
- 172 068 záznamů
- **IDPAC** → vazba na KAR
- **DATUM** (date), **CAS** (time) kdy
- **DEKURS** text záznamu (BLOB, RTF nebo holý text)
- **DGN1** hlavní diagnóza (5 znaků, např. 'Z000 '), VDGN1-4 vedlejší
- **IDPRAC** který pracovník
- IDODD, IDUZI oddělení/uživatel (u Buzalky = 2, 2, 6)
- IDSKUPINA, IDTYP volitelné (NULL OK)
## Základní schéma
```
KAR (kartotéka) ←IDPAC→ DEKURS (záznamy z návštěv)
```
## Testovací pacient (uživatel)
- **IDPAC = 9742** Buzalka Vladimír, RC 7309208104, nar. 20.9.1973
- IDODD=2, IDPRAC=2, IDUZI=6 (použít při INSERT do DEKURS)
## RTF formát v DEKURS
### Základní struktura
```
{\rtf1\ansi\ansicpg1250\uc1\deff0\deflang1029
{\info{\bookmarks "popis","Typ:ID",číslo}} ← metadata Medicusu
{\fonttbl{\f0\fnil\fcharset238 Arial;}{\f2...Courier New;}{\f5...Symbol;}}
{\colortbl ;\red0\green0\blue255;\red255\green255\blue0;\red0\green128\blue0;\red192\green192\blue192;}
{\stylesheet{styly...}}
\uc1\pard\s10\plain... ← tělo záznamu
}
```
### Barvy (\cfX)
- `\cf1` = modrá (odkazy)
- `\cf2` = žlutá (highlight)
- `\cf3` = zelená
- `\cf4` = šedá (pozadí buněk tabulky `\clcbpat4`)
### Velikost písma (\fsXX = XX/2 bodů)
- `\fs18` = 9pt (tabulky)
- `\fs20` = 10pt (normální)
- `\fs24` = 12pt atd.
### Řezy a formátování
- `\b` = tučné, `\i` = kurzíva, `\ul` = podtržené
- `\ql` = vlevo, `\qr` = vpravo, `\qc` = střed
### Styly (stylesheet)
- `cs15/cs16` = Normální
- `cs20` = Vkládaný text
- `cs21` = Záhlaví (italic)
- `cs22/cs23` = Odkaz (underline, modrá)
- `s8/s10` = Vlevo (paragraph styl)
### Záložky (bookmarks v {\info})
- Klikatelné odkazy v Medicusu
- Formát: `"popis","Typ:ID",číslo`
- Typy: `Files:ID`, `MEDLAB:ID`, `Lab:ID`, `Ock:ID`, `VykA:ID`
### České znaky
- RTF hex escape v win1250: `\'e1`=á, `\'9e`=ž, `\'fd`=ý atd.
### Tabulky
- Plně funkční RTF tabulky (`\trowd`, `\cell`, `\cellx...`)
- Používají se např. pro laboratorní výsledky (viz ID=243082, 50KB)
### Holý text (starší záznamy)
- Některé starší záznamy jsou prostý text bez RTF obálky
- Nutno detekovat: začíná na `{\\rtf` = RTF, jinak plain text
### Příklad minimálního RTF záznamu
```
{\rtf1\ansi\ansicpg1250\uc1\deff0\deflang1029
{\fonttbl{\f0\fnil\fcharset238 Arial;}}
{\colortbl ;\red0\green0\blue255;\red0\green128\blue0;\red0\green0\blue0;}
\pard\plain\f0\fs20 Text záznamu zde\par
}
```
## INSERT do DEKURS ověřený postup
```python
import fdb, datetime
conn = fdb.connect(...)
cur = conn.cursor()
cur.execute('SELECT MAX(ID) FROM DEKURS')
next_id = cur.fetchone()[0] + 1
text = r'{\rtf1\ansi\ansicpg1250...\par' + '\n}' # raw string pro RTF!
cur.execute("""
INSERT INTO DEKURS (ID, IDPAC, IDODD, IDPRAC, IDUZI, DATUM, CAS, DEKURS, DGN1)
VALUES (?, ?, 2, 2, 6, ?, ?, ?, ?)
""", (next_id, idpac, datum, cas, text, dgn1))
conn.commit()
```
- RTF string musí být **raw string** (r'...') kvůli zpětným lomítkům
- DGN1 = 5 znaků s mezerou: `'Z000 '`
- DATUM = datetime.date(...), CAS = datetime.time(...)
## Soubory v projektu
- `explore_db.py` výpis všech tabulek a jejich sloupců
- `inspect_table.py` detailní info o konkrétní tabulce (arg: název tabulky)
- `sample_dekurs.py` ukázky nejstarších a nejnovějších záznamů
- `sample_dekurs2.py` celý text jednoho záznamu
- `sample_rtf.py` nejdelší záznamy pacienta (dle délky RTF)
- `analyze_rtf.py` analýza RTF tagů v záznamu
- `insert_test.py` testovací INSERT do DEKURS
- `CLAUDE_NOTES.md` tento soubor
## Export FILES do externích DB (zjištěno 2026-03-17 přes FBScanner)
### Mechanismus přesunu
Medicus vytváří **měsíční** externí Firebird databáze ve formátu `MEDICUS_FILES_YYYYMM.fdb`.
**Postup exportu:**
1. SELECT dat z FILES po 10 záznamech: `select first 10 ID, BODY, DATUM from FILES where ID > X ORDER BY ID ASC`
2. Binární data (skeny) se zapíší do externího `.fdb` souboru
3. V hlavní DB se BODY přepíše referenčním číslem (ID v externím souboru): `update FILES set BODY = 379 where ID = 10009`
4. Do EXTERNI_DB se zapíše lokace: `insert into EXTERNI_DB (DBNAME, SERVER, PATH, HESLO) values ('DB202501', 'localhost/3053', 'u:\MEDICUS_FILES_202501.fdb', '<encrypted>')`
### Výsledek
- Hlavní DB: FILES.BODY obsahuje jen malé číslo (referenci) místo MB blobu
- Skenované soubory jsou fyzicky v `u:\MEDICUS_FILES_YYYYMM.fdb`
- Medicus načítá soubory přes EXTERNI_DB tabulku (SERVER + PATH + HESLO)
- Hesla v EXTERNI_DB jsou šifrovaná (base64 AES)
### FBScanner
- Nainstalován, funguje jako proxy: Medicus → [3050] FBScanner → [3053] Firebird
- Community Edition 2.5 stačí pro monitoring
- Export proveden na záloze (ne produkční DB)
## Přímý zápis do externí DB dokončeno (2026-03-17)
### Nový soubor: `funkce_ext.py`
Náhrada za `funkce.zapis_file()`. Ukládá PDF přímo do měsíční externí FDB
místo do hlavní medicus.fdb.
**Funkce:** `zapis_file_ext(vstupconnection, idpac, cesta, souborname, prvnizavorka, soubordate, souborfiledate, poznamka, ext_base_path=r'u:\\')`
- Parametry shodné s `funkce.zapis_file()` → jen prohodit import
- Vrací `fileid` stejně jako původní funkce
**Co dělá:**
1. Z `soubordate` odvodí `YYYYMM``DBNAME = 'DB202603'`
2. Načte PDF jako bytes
3. Vyhledá EXTERNI_DB pro daný měsíc:
- Nalezeno → připojí se k existující FDB (masterkey)
- Nenalezeno → vytvoří novou `MEDICUS_FILES_YYYYMM.fdb`, zaregistruje do EXTERNI_DB
4. Vygeneruje `UID = uuid.uuid4().hex` (32 znaků)
5. Zapíše do DATA tabulky: `UID, DATA (blob), DATASIZE, CHUNK=0`
6. Sestaví 48bajtovou BODY referenci
7. Získá `fileid` z Gen_Files
8. Vloží do FILES s BODY = reference (ne blob)
### Úprava `s03soubory.py`
- Přidán import `funkce_ext`
- Volání `funkce.zapis_file(...)` nahrazeno `funkce_ext.zapis_file_ext(...)`
### BODY reference binární formát (48 B)
```
magic 4 B = b'\xee\xbb\xaa\x0b'
uid 32 B = UUID4 hex (ASCII, 32 znaků)
dblen 4 B = len(DBNAME) jako little-endian uint32
dbname N B = DBNAME ASCII (typicky 'DB202603', 8 B)
```
Celkem typicky: 4 + 32 + 4 + 8 = **48 bajtů**
### Struktura DATA tabulky (externe DB)
```
UID VARCHAR(32) NOT NULL
DATA BLOB SUB_TYPE 0
DATASIZE INTEGER
CHUNK INTEGER
```
Generator: `GEN_UID` (Medicus ho asi nepoužívá přímo, UID je UUID string)
### EXTERNI_DB tabulka (hlavní DB)
```
DBNAME např. 'DB202603'
SERVER např. 'localhost/3053' (FBScanner proxy)
PATH např. 'u:\MEDICUS_FILES_202603.fdb'
HESLO šifrované heslo (Medicus AES) zkopírujeme z existující položky
```
Při vytváření nové DB: HESLO zkopírujeme z libovolné existující EXTERNI_DB položky
(všechny používají masterkey, jen jinak zašifrované ale stejný klíč).
### Poznámka k HESLO šifrování
Hesla v EXTERNI_DB jsou šifrovaná Medicusem. Při vytváření nové DB proto
zkopírujeme HESLO z existující položky (stejné heslo = stejný šifrovaný text).
Medicus pak dokáže novou DB otevřít standardně. Pokud by to nefungovalo,
lze heslo zadat manuálně přes správce Medicusu.
## Stav 2026-03-18 nastudováno po obnově session
### Co bylo dokončeno (včera večer)
- `funkce_ext.zapis_file_ext()` je **otestována a funkční**
- `s03soubory.py` volá `zapis_file_ext` místo původní `funkce.zapis_file`
- Celý import pipeline funguje end-to-end
### Celý import pipeline (s03soubory.py)
1. Scanuje složku `u:\NextcloudOrdinace\Dokumentace_ke_zpracování`
2. Ověří formát názvu souboru: `RC YYYY-MM-DD Jmeno, Jmeno. [prvnizavorka] [druhazavorka].pdf`
3. Ověří rodné číslo v KAR tabulce
4. Seskupí soubory podle RC (jeden pacient = jeden dekurs)
5. Pro každý PDF zavolá `funkce_ext.zapis_file_ext()` → zapíše do externí DB
6. Přesune soubor do `u:\NextcloudOrdinace\Dokumentace_zpracovaná`
7. Sestaví RTF se záložkami (klikatelné odkazy na soubory v Medicusu)
8. Zapíše dekurs do DEKURS tabulky
### Klíčové soubory
- `funkce_ext.py` zápis PDF do měsíční externí FDB (náhrada za funkce.zapis_file)
- `funkce.py` původní funkce (zapis_file, zapis_dekurs, get_files_id, get_idpac, convert_to1250)
- `s03soubory.py` hlavní import script (spouštět na Windows)
- `s04_presun_externi_db.py` utility: přesun ext DB souborů do u:\externi\ a update PATH v EXTERNI_DB
- `test_ext_db4.py` ověření BODY bajty z FILES tabulky (hex dump)
### Formát názvu souboru pro import
```
RC YYYY-MM-DD Prijmeni, Jmeno. [prvnizavorka] [druhazavorka].pdf
např: 7309208104 2026-03-17 Buzalka, Vladimír. [EKG] [normální nález].pdf
```
## Oprava RTF klikacích odkazů (2026-03-18)
### Problém
`s03soubory.py` generoval RTF s `\cs22` pro styl odkazu, ale Medicus vyžaduje `\cs32`.
Druhý+ bookmark neměl link styling vůbec. `poradi` se nikdy neinkrementoval.
### Zjištěno analýzou Medicusem vytvořeného DEKURS (ID=263480, AhojClaude.pdf)
Správný RTF formát klikacího odkazu:
```
{\*\cs32\f0\ul\fs20\cf1 Odkaz;} ← stylesheet: cs32, NE cs22
\uc1\pard\s10\plain\cs20\f0\i\fs20 Přílohy: {\*\bkmkstart 0}\plain\cs32\f0\ul\fs20\cf1 TEXT{\*\bkmkend 0}\par
```
- "Přílohy:" je inline s prvním odkazem na stejném `\pard` řádku
- Každý další odkaz: `\pard\s10{\*\bkmkstart N}\plain\cs32\f0\ul\fs20\cf1 TEXT{\*\bkmkend N}\par`
### Opraveno v s03soubory.py + test_import_3files.py
1. `\cs22``\cs32` v stylesheet i těle RTF
2. Header `Vložené přílohy:` je na **samostatném** `\pard` řádku (RTF: `Vlo\'9een\'e9 p\'f8\'edlohy:`)
3. Všechny bookmarky mají stejný formát: `\pard\s10{\*\bkmkstart N}\plain\cs32\f0\ul\fs20\cf1 TEXT{\*\bkmkend N}\par`
4. Přidáno `poradi += 1` (bug: poradi se nikdy neinkrementoval)
5. Odstraněna proměnná `prvnibookmark` již nepotřebná
### RTF kódování českých znaků (win1250)
- ž = `\'9e`, é = `\'e9`, ř = `\'f8`, í = `\'ed`, á = `\'e1`
- "Vložené přílohy:" → `Vlo\'9een\'e9 p\'f8\'edlohy:`
## Merge do existujícího dekurzu (2026-03-18)
### Rozhodovací logika (3 případy)
1. Poslední dekurs pacienta je z dnešního dne A má `Vložené přílohy:`
**přidat soubor DO existující sekce** (`pridat_do_sekce_prilohy`)
2. Poslední dekurs je z dnešního dne, ale sekci nemá
**prepend nové sekce na začátek** (`merge_rtf_prepend`)
3. Poslední dekurs je z jiného dne / neexistuje
**nový dekurs pro dnešek**
- Funkce: `najdi_posledni_dekurs_dnes(conn, idpac, datum_vlozeni)`
### pridat_do_sekce_prilohy (přidání do existující sekce)
1. Spočítat `"Files:"` v `{\info{\bookmarks}}` = N → nový bkmkstart = N
2. Posunout všechny bkmkstart/bkmkend ≥ N o +1 (uvolnit index N)
3. Vložit nový `\pard` před `\pard\s10\plain\cs15\f0\fs20 \par` (konec sekce)
4. Vložit bookmark na pozici N do `{\info{\bookmarks}}`
### merge_rtf_prepend (prepend nové sekce)
1. Posunout existující `\bkmkstart N` / `\bkmkend N` o počet nových souborů
2. Přidat naše bookmarky NA ZAČÁTEK `{\info{\bookmarks ...}}`
3. Vložit naše `\pard` tělo PŘED první `\uc1\pard` existujícího těla
### Detekce sekce
- Přítomnost: `PRILOHY_HEADER = r"Vlo\'9een\'e9 p\'f8\'edlohy:"` hledáme v RTF
- Konec sekce: `PRILOHY_CLOSING = r'\pard\s10\plain\cs15\f0\fs20 \par'`
### Skripty
- `test_import_merge.py` 3 soubory → merge do dnešního dekurzu (bez sekce přílohy)
- `test_import_single.py` 1 soubor → merge DO existující sekce přílohy
- `test_import_3files.py` 3 soubory → vždy nový dekurs (bez merge logiky, starší)
### Stav testování (2026-03-18)
- `test_import_3files.py` ✅ ověřeno klikací odkazy fungují, "Vložené přílohy:" správně
- `test_import_merge.py` ✅ připraveno merge 3 souborů do dnešního dekurzu (bez sekce)
- `test_import_single.py` ✅ připraveno přidání 1 souboru DO existující sekce přílohy
### Stav s03soubory.py (2026-03-20) KOMPLETNÍ ✅
- Merge logika plně integrována z `test_import_single.py`
- Přidány konstanty `PRILOHY_HEADER`, `PRILOHY_CLOSING`
- Přidány funkce: `najdi_posledni_dekurs_dnes()`, `ma_sekci_prilohy()`, `pridat_do_sekce_prilohy()`, `merge_rtf_prepend()`
- RTF šablona přesunuta do konstanty `RTF_TEMPLATE`
- Odstraněn dead code: `prvnibookmark`, staré šablony, nepoužívané `convert_to1250`
- `pridat_do_sekce_prilohy()` podporuje **více souborů najednou** (oproti test_import_single.py, kde byl jen 1)
- `idpac` se bere z prvního záznamu skupiny (čistěji než z `row` po skončení loopu)
### Rozhodovací logika s03soubory.py 3 případy
1. Dnešní dekurs **má** sekci `Vložené přílohy` → soubory přidány **dovnitř** sekce (`pridat_do_sekce_prilohy`)
2. Dnešní dekurs **nemá** sekci příloh → nová sekce vložena **na začátek** (`merge_rtf_prepend`)
3. Žádný dnešní dekurs → **nový** záznam (INSERT)
### Stav testovacích skriptů
- `test_import_3files.py` ✅ ověřeno klikací odkazy fungují
- `test_import_merge.py` ✅ otestováno merge 3 souborů (jen 2 případy, bez detekce sekce)
- `test_import_single.py` ✅ otestováno všechny 3 případy, základ pro s03soubory.py
## Další postup (nápady)
- Otestovat `s03soubory.py` na Windows se skutečnými soubory (všechny 3 případy)
- Napsat `rtf_to_text()` pro extrakci čistého textu z dekurzů
- Prozkoumat tabulky: LECH/LECD (léky?), POU (poukazy?), AMBULEKY (výkony?)
- První report domluvit s uživatelem co chce vidět