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

16 KiB
Raw Permalink Blame History

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

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

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í YYYYMMDBNAME = '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 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