notebook vb

This commit is contained in:
2026-03-18 07:13:47 +01:00
parent a599b6741b
commit f2dc33a05e
23 changed files with 9102 additions and 13 deletions

View File

@@ -4,6 +4,8 @@
- 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.
## Bezpečnost
- Pracujeme na **místní kopii** poškození DB nevadí, obnova = 5 minut
@@ -21,6 +23,9 @@ conn = fdb.connect(
)
```
- 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
@@ -157,6 +162,186 @@ conn.commit()
- `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, nespuštěno
- `test_import_single.py` připraveno, nespuštěno
### TODO integrace do s03soubory.py
- Přidat `najdi_posledni_dekurs_dnes()` do s03soubory.py
- Přidat `pridat_do_sekce_prilohy()` a `merge_rtf_prepend()` do s03soubory.py
- Nahradit přímý INSERT DEKURS rozhodovací logikou (3 případy)
- Pozn: s03soubory.py má starý `prvnibookmark=True` blok odstranit (relikt)
## Další postup (nápady)
- Napsat `rtf_to_text()` pro extrakci čistého textu z dekurzů
- Prozkoumat tabulky: LECH/LECD (léky?), POU (poukazy?), AMBULEKY (výkony?)

View File

@@ -0,0 +1,37 @@
@echo off
:: Firebird SQL trace pro Medicus
:: Zachytí všechny SQL příkazy při exportu souborů do externích DB
::
:: INSTRUKCE:
:: 1. Spusť tento soubor jako Administrator (pravé tlačítko → Spustit jako správce)
:: 2. Nechej okno otevřené
:: 3. V Medicusu proveď export souborů do externích DB
:: 4. Vrať se sem a stiskni Ctrl+C pro zastavení
:: 5. Log najdeš v medicus_trace_output.txt
echo.
echo === MEDICUS SQL TRACE ===
echo Hledam Firebird fbtracemgr...
echo.
set FB_BIN=C:\Program Files\Firebird\Firebird_2_5_CGM\bin
if not exist "%FB_BIN%\fbtracemgr.exe" (
echo CHYBA: fbtracemgr.exe nenalezen v %FB_BIN%
echo Zkontroluj cestu a uprav tento soubor.
pause
exit /b 1
)
echo Nalezen: %FB_BIN%\fbtracemgr.exe
echo.
echo POZOR: Nyni PROVEĎ export v Medicusu, pak se vrat a stiskni Ctrl+C
echo Výstup se zapisuje do: medicus_trace_output.txt
echo.
powershell -Command "& '%FB_BIN%\fbtracemgr.exe' -se localhost:service_mgr -user SYSDBA -password masterkey -start -config '%~dp0medicus_trace.conf' | Tee-Object -FilePath '%~dp0medicus_trace_output.txt'"
echo.
echo === TRACE ZASTAVEN ===
echo Výsledky jsou v medicus_trace_output.txt
pause

View File

@@ -0,0 +1,42 @@
"""check_last_dekurs.py zobrazí posledních 5 záznamů v DEKURS + FILES pro pacienta 9742"""
import fdb
conn = fdb.connect(
dsn=r'localhost:c:\medicus 3\data\medicus.fdb',
user='SYSDBA', password='masterkey', charset='WIN1250'
)
cur = conn.cursor()
print("=== Posledních 5 záznamů DEKURS pro Buzalka (idpac=9742) ===")
cur.execute("""
SELECT id, datum, cas, OCTET_LENGTH(dekurs) as delka
FROM dekurs WHERE idpac = 9742
ORDER BY id DESC ROWS 5
""")
for row in cur.fetchall():
print(f" ID={row[0]} datum={row[1]} cas={row[2]} délka={row[3]} B")
print()
print("=== Obsah posledního DEKURS záznamu ===")
cur.execute("""
SELECT dekurs FROM dekurs WHERE idpac = 9742
ORDER BY id DESC ROWS 1
""")
row = cur.fetchone()
if row:
text = row[0]
if isinstance(text, bytes):
text = text.decode('cp1250', errors='replace')
print(text)
print()
print("=== Posledních 5 záznamů FILES pro Buzalka (idpac=9742) ===")
cur.execute("""
SELECT id, filename, datum, OCTET_LENGTH(body) as bodylen
FROM files WHERE idpac = 9742
ORDER BY id DESC ROWS 5
""")
for row in cur.fetchall():
print(f" ID={row[0]} filename={row[1]} datum={row[2]} body={row[3]} B")
conn.close()

View File

@@ -0,0 +1,70 @@
"""
Pomocný skript pro Clauda spusť na Windows: python db_query.py
Výsledky zapíše do db_query_result.txt
"""
import fdb
import json
import sys
conn = fdb.connect(
dsn=r'localhost:c:\medicus 3\data\medicus.fdb',
user='SYSDBA',
password='masterkey',
charset='win1250'
)
cur = conn.cursor()
results = {}
# 1. Tabulky s klíčovými slovy (soubory, dokumenty)
cur.execute("""
SELECT RDB$RELATION_NAME
FROM RDB$RELATIONS
WHERE RDB$SYSTEM_FLAG = 0 AND RDB$VIEW_BLR IS NULL
ORDER BY RDB$RELATION_NAME
""")
all_tables = [row[0].strip() for row in cur.fetchall()]
keywords = ['FILE', 'SOUB', 'DOC', 'SCAN', 'OBRAZ', 'IMG', 'ATTACH',
'EXTERN', 'PRILOHA', 'ZPRAV', 'ARCH', 'PRIL', 'FOTO']
matches = [t for t in all_tables if any(k in t.upper() for k in keywords)]
results['tables_matching'] = matches
results['total_tables'] = len(all_tables)
# 2. Podrobnosti o nalezených tabulkách (sloupce + počet řádků)
table_details = {}
for t in matches:
try:
cur.execute(f"SELECT COUNT(*) FROM {t}")
count = cur.fetchone()[0]
except:
count = "error"
cur.execute("""
SELECT r.RDB$FIELD_NAME, f.RDB$FIELD_TYPE, f.RDB$FIELD_LENGTH
FROM RDB$RELATION_FIELDS r
JOIN RDB$FIELDS f ON f.RDB$FIELD_NAME = r.RDB$FIELD_SOURCE
WHERE r.RDB$RELATION_NAME = ?
ORDER BY r.RDB$FIELD_POSITION
""", (t,))
cols = [(row[0].strip(), row[1], row[2]) for row in cur.fetchall()]
table_details[t] = {"count": count, "columns": cols}
results['table_details'] = table_details
# 3. Uložit výsledky
output = []
output.append(f"=== VÝSLEDKY DB PRŮZKUMU ===\n")
output.append(f"Celkem tabulek v DB: {results['total_tables']}\n")
output.append(f"\nTabulky obsahující klíčová slova (soubory/dokumenty):\n")
for t in results['tables_matching']:
d = results['table_details'].get(t, {})
output.append(f"\n ** {t} ** (řádků: {d.get('count', '?')})")
for col in d.get('columns', []):
output.append(f" - {col[0]} (type={col[1]}, len={col[2]})")
with open('db_query_result.txt', 'w', encoding='utf-8') as f:
f.write('\n'.join(output))
print("Hotovo! Výsledky jsou v db_query_result.txt")
conn.close()

View File

@@ -0,0 +1,75 @@
"""
Podrobný průzkum FILES a EXTERNI_DB spusť na Windows: python db_query2.py
"""
import fdb
conn = fdb.connect(
dsn=r'localhost:c:\medicus 3\data\medicus.fdb',
user='SYSDBA',
password='masterkey',
charset='win1250'
)
cur = conn.cursor()
out = []
# 1. Obsah EXTERNI_DB
out.append("=== EXTERNI_DB (konfigurace externích databází) ===")
cur.execute("SELECT DBNAME, PATH, SERVER, HESLO FROM EXTERNI_DB")
for row in cur.fetchall():
out.append(f" DBNAME={row[0]} PATH={row[1]} SERVER={row[2]} HESLO={row[3]}")
# 2. FILES přehled po rocích
out.append("\n=== FILES počty záznamů po rocích ===")
cur.execute("""
SELECT EXTRACT(YEAR FROM DATUM) as ROK, COUNT(*) as POCET,
SUM(OCTET_LENGTH(BODY)) as CELKEM_BYTU
FROM FILES
GROUP BY EXTRACT(YEAR FROM DATUM)
ORDER BY ROK
""")
total_bytes = 0
for row in cur.fetchall():
rok, pocet, bytu = row
mb = (bytu or 0) / 1024 / 1024
total_bytes += (bytu or 0)
out.append(f" Rok {int(rok) if rok else '?'}: {pocet} souborů, {mb:.1f} MB")
out.append(f" CELKEM: {total_bytes/1024/1024:.1f} MB v BLOB datech FILES")
# 3. FILES ukázka typů souborů
out.append("\n=== FILES typy souborů (dle přípony FILENAME) ===")
cur.execute("""
SELECT LOWER(SUBSTRING(FILENAME FROM CHAR_LENGTH(FILENAME) - 3)) as EXT,
COUNT(*) as POCET
FROM FILES
WHERE FILENAME IS NOT NULL
GROUP BY 1
ORDER BY 2 DESC
""")
for row in cur.fetchall():
out.append(f" {row[0]} : {row[1]} souborů")
# 4. FILES ukázka prvních 5 záznamů (bez BODY)
out.append("\n=== FILES ukázka 5 záznamů (bez binárních dat) ===")
cur.execute("""
SELECT ID, IDPAC, FILENAME, DATUM, OCTET_LENGTH(BODY) as VELIKOST,
POZNAMKA, IDDOCTYP
FROM FILES
ORDER BY DATUM DESC
ROWS 5
""")
for row in cur.fetchall():
out.append(f" ID={row[0]} IDPAC={row[1]} soubor={row[2]} datum={row[3]}"
f" velikost={row[4]}B pozn={row[5]} typ={row[6]}")
# 5. DOCTYP typy dokumentů
out.append("\n=== DOCTYP číselník typů dokumentů ===")
cur.execute("SELECT ID, NAZEV, SYSID FROM DOCTYP ORDER BY ID")
for row in cur.fetchall():
out.append(f" ID={row[0]} nazev={row[1]} sysid={row[2]}")
result = '\n'.join(out)
print(result)
with open('db_query2_result.txt', 'w', encoding='utf-8') as f:
f.write(result)
print("\nUloženo do db_query2_result.txt")
conn.close()

View File

@@ -0,0 +1,532 @@
=== VÝSLEDKY DB PRŮZKUMU ===
Celkem tabulek v DB: 993
Tabulky obsahující klíčová slova (soubory/dokumenty):
** CHAT_ZPRAVY ** (řádků: 0)
- ID (type=8, len=4)
- IDADRESAR (type=8, len=4)
- ODCHOZI (type=14, len=1)
- ODESLANO (type=35, len=8)
- TEXT (type=261, len=8)
- OTHER_ID (type=8, len=4)
- PRECTENO (type=35, len=8)
- SENT (type=35, len=8)
** CLICKDOC ** (řádků: 2)
- KEY (type=37, len=100)
- VALUE (type=37, len=100)
** CLICKDOC_CINNOSTI ** (řádků: 0)
- IDCLICKDOC (type=8, len=4)
- IDCINNOSTI (type=8, len=4)
** CLICKDOC_KAR ** (řádků: 0)
- IDCLICKDOC (type=8, len=4)
- IDPAC (type=8, len=4)
** CLICKDOC_OBJ ** (řádků: 0)
- IDCLICKDOC (type=8, len=4)
- IDOBJ (type=8, len=4)
- REMOTECREATE (type=14, len=1)
- REMOTECHANGE (type=14, len=1)
- APPROVAL (type=7, len=2)
- SEND_REMINDER_SMS (type=14, len=1)
** CLICKDOC_UZIVATEL ** (řádků: 0)
- IDCLICKDOC (type=8, len=4)
- IDUZI (type=8, len=4)
** DEKIMG ** (řádků: 8)
- ID (type=8, len=4)
- HASH (type=37, len=32)
- REFCOUNT (type=8, len=4)
- DATA (type=261, len=8)
** DEKIMGREF ** (řádků: 7)
- ID (type=8, len=4)
- IDPAC (type=8, len=4)
- IDDEKURS (type=8, len=4)
- IDDEKIMG (type=8, len=4)
** DOCLIST ** (řádků: 461429)
- ID (type=8, len=4)
- DATUM (type=12, len=4)
- TABULKA (type=37, len=20)
- IDREC (type=8, len=4)
- TYP (type=7, len=2)
- POPIS (type=37, len=40)
- IDPAC (type=8, len=4)
- IDODDEL (type=8, len=4)
** DOCTYP ** (řádků: 27)
- ID (type=8, len=4)
- NAZEV (type=37, len=32)
- SYSID (type=8, len=4)
** DOMPECEARCHIV ** (řádků: 0)
- ID (type=8, len=4)
- CREATED (type=35, len=8)
- DATA (type=261, len=8)
** ED_BOOKOFSUBMISSIONATTACH ** (řádků: 0)
- ID (type=8, len=4)
- BOOK_OF_SUBMISSION_ID (type=8, len=4)
- ORDER (type=8, len=4)
- HAS_ALREADY_BEEN_SENT (type=14, len=1)
- NAME (type=37, len=255)
- CONTENT (type=261, len=8)
** EET_ZPRAVARECORD ** (řádků: 0)
- ID (type=8, len=4)
- UUID_ZPRAVY (type=14, len=16)
- ID_TRZBA (type=8, len=4)
- GUID_TRZBA (type=14, len=16)
- DAT_ODESL (type=35, len=8)
- CERTTHUMBPRINT (type=37, len=64)
- REQUEST (type=261, len=8)
- RESPONSE (type=261, len=8)
- RESP_TYPE (type=8, len=4)
- RESP_UUID_ZPRAVY (type=37, len=50)
- RESP_DAT_PRIJ (type=35, len=8)
- RESP_DAT_ODMIT (type=35, len=8)
- RESP_BKP (type=37, len=44)
- RESP_FIK (type=37, len=39)
- RESP_KOD (type=8, len=4)
- RESP_CHYBA (type=37, len=100)
- PRVNI_ZASLANI (type=7, len=2)
- RESP_TEST (type=7, len=2)
- RESP_EXCEPTIONMESSAGE (type=261, len=8)
** ES_KONZULTACE_FILE ** (řádků: 0)
- ID (type=8, len=4)
- ID_KONZULTACE (type=8, len=4)
- CAS (type=35, len=8)
- NAME (type=37, len=256)
- CONTENT (type=261, len=8)
- MIME (type=37, len=100)
- PATIENT_DOC (type=14, len=1)
- PORADI (type=8, len=4)
** EXTERNI_DB ** (řádků: 1)
- DBNAME (type=37, len=12)
- PATH (type=37, len=120)
- SERVER (type=37, len=32)
- HESLO (type=37, len=64)
** FILES ** (řádků: 10305)
- ID (type=8, len=4)
- IDPAC (type=8, len=4)
- DOCID (type=8, len=4)
- TYP (type=8, len=4)
- FILENAME (type=37, len=254)
- BODY (type=261, len=8)
- DATUM (type=12, len=4)
- IDDOCTYP (type=8, len=4)
- IDPRAC (type=8, len=4)
- IDUZI (type=8, len=4)
- POZNAMKA (type=37, len=100)
- DATSOUBORU (type=35, len=8)
- ID_EDOKUMENT (type=8, len=4)
- EXT_ID (type=37, len=36)
** FNUSA_ATTACHMENT ** (řádků: 0)
- ID (type=8, len=4)
- DATA (type=261, len=8)
- ATTACH_INT_TYP (type=37, len=1)
- ATTACH_INT_ID (type=8, len=4)
- LAST_UPDATED (type=35, len=8)
** GRARCH ** (řádků: 0)
- ID (type=8, len=4)
- NAME (type=37, len=16)
- PATH (type=37, len=150)
** GRDOC ** (řádků: 0)
- ID (type=8, len=4)
- IDNEM (type=37, len=60)
- IDPAC (type=8, len=4)
- SKUPINA (type=8, len=4)
- DATUM (type=12, len=4)
- PREVIEW (type=261, len=8)
- PICT (type=261, len=8)
- LOCATION (type=37, len=16)
** HISTDOC ** (řádků: 32250)
- ID (type=8, len=4)
- TYP (type=37, len=7)
- DATUM (type=12, len=4)
- DATA (type=261, len=8)
- CENA (type=16, len=8)
- REPPOZN (type=37, len=60)
- REPDATUM (type=12, len=4)
- IDPACI (type=8, len=4)
- IDZAR (type=8, len=4)
- IDODD (type=8, len=4)
- IDPRAC (type=8, len=4)
- IDUZIV (type=8, len=4)
- IDLEK (type=8, len=4)
- IDZARPR (type=8, len=4)
- IDODDPR (type=8, len=4)
- IDPRACPR (type=8, len=4)
- IDLEKPR (type=8, len=4)
- STAV (type=14, len=1)
- REPSTAV (type=14, len=1)
- IDSABLONPRAC (type=37, len=55)
- REPTEXT (type=261, len=8)
- TYPCENY (type=14, len=1)
- CENAPACIENT (type=16, len=8)
- IDDOKLAD (type=8, len=4)
- POZNAMKA (type=37, len=100)
- PRINTED (type=14, len=1)
- PRIJALJINE (type=37, len=150)
- BMKDATA (type=261, len=8)
- PORCISLO (type=8, len=4)
- DRUHPOJ (type=8, len=4)
- IDLEKZPR (type=8, len=4)
- IDHLAV (type=8, len=4)
- CREATED (type=35, len=8)
- IDSOUHLASPACSABL (type=8, len=4)
- REPIDUZIVINS (type=8, len=4)
- REPIDUZIVUPD (type=8, len=4)
- CGMNUMBER_ODESILATEL (type=37, len=12)
- CGMNUMBER_PRIJEMCE (type=37, len=12)
- CLICKBOX_ATT_ID (type=8, len=4)
- CLICKBOX_MAILBOX (type=37, len=255)
- ID_EDOKUMENT (type=8, len=4)
** HISTDOCLAB ** (řádků: 27185)
- ID (type=8, len=4)
- IDHISTDOC (type=8, len=4)
- IDMETOD (type=8, len=4)
- POZN (type=261, len=8)
- IDPLADET (type=8, len=4)
** HISTDOCLABPARAMS ** (řádků: 0)
- ID (type=8, len=4)
- IDHISTDOC (type=8, len=4)
- IDHISTDOCLAB (type=8, len=4)
- IDPARAM (type=8, len=4)
- TYP (type=14, len=1)
- TEXT (type=14, len=8)
** HISTDOCSABL ** (řádků: 7)
- ID (type=8, len=4)
- TYP (type=37, len=7)
- DATA (type=261, len=8)
- IDZAR (type=8, len=4)
- IDODD (type=8, len=4)
- IDPRAC (type=8, len=4)
- IDUZIV (type=8, len=4)
- IDLEK (type=8, len=4)
- IDZARPR (type=8, len=4)
- IDODDPR (type=8, len=4)
- IDPRACPR (type=8, len=4)
- IDLEKPR (type=8, len=4)
- STAV (type=14, len=1)
- IDSABLONPRAC (type=37, len=55)
- NAZEV (type=37, len=48)
- REPSTAV (type=14, len=1)
- CENA (type=37, len=20)
- DOPLATEK (type=37, len=20)
- USEPRAC (type=8, len=4)
- USEUZIV (type=8, len=4)
** HISTDOCSABLLAB ** (řádků: 19)
- ID (type=8, len=4)
- IDHISTDOC (type=8, len=4)
- IDMETOD (type=8, len=4)
** HISTDOCVYK ** (řádků: 1868)
- IDHDVYK (type=8, len=4)
- ID (type=8, len=4)
- KOD (type=14, len=7)
- POCET (type=16, len=8)
- CENA (type=16, len=8)
- CENADOPL (type=16, len=8)
- POJ (type=14, len=3)
- ICZ (type=14, len=8)
- ICP (type=14, len=8)
- DATUM (type=12, len=4)
- REV (type=14, len=1)
- TYP (type=7, len=2)
- NOT_CENA_PRUMER (type=14, len=1)
- VARSYM (type=37, len=20)
- SKUPINA (type=14, len=2)
** HISTDOC_DEF ** (řádků: 0)
- ID (type=8, len=4)
- KOD (type=37, len=5)
- NAZEV (type=37, len=40)
- IDUZI (type=8, len=4)
- DATA (type=261, len=8)
- SKUPINA (type=8, len=4)
** HISTDOC_EPOUKAZ ** (řádků: 849)
- IDHISTDOC (type=8, len=4)
- ID_DOKLADU (type=14, len=9)
- IDUZI (type=8, len=4)
- IDPRAC (type=8, len=4)
- ODESLANO (type=35, len=8)
- CHYBA (type=14, len=1)
- SCHVALENI (type=14, len=1)
- MODIFIED (type=14, len=1)
- ODESLANO_LAST (type=35, len=8)
- ID_ZP (type=37, len=36)
- ID_DOKLADU_VYDEJ (type=37, len=9)
- ID_ZP_VYDEJ (type=37, len=36)
- VYDANO (type=35, len=8)
** HISTDOC_EPOUKAZ_ZMENY ** (řádků: 3)
- ID_LEKAR (type=37, len=36)
- LAST_DATE (type=12, len=4)
** HISTDOC_EPRILOHY ** (řádků: 17)
- ID (type=8, len=4)
- IDHISTDOC (type=8, len=4)
- NAZEV (type=37, len=255)
- POPIS (type=37, len=255)
- TYP (type=37, len=7)
- SOUBOR (type=261, len=8)
- ID_PRILOHY (type=37, len=36)
** HISTDOC_EZADANKA ** (řádků: 2195)
- ID (type=8, len=4)
- IDHISTDOC (type=8, len=4)
- TYP (type=37, len=10)
- DATUMVYS (type=12, len=4)
- CASVYS (type=13, len=4)
- CISLO (type=37, len=30)
- POZNAMKA (type=261, len=8)
- HOTOVO (type=14, len=1)
- IDZAD (type=8, len=4)
- VYSTEXT (type=261, len=8)
- DATUMEXP (type=12, len=4)
- CASEXP (type=13, len=4)
- SABLONY (type=261, len=8)
- PRINTED (type=35, len=8)
- SENDED (type=35, len=8)
- TEMPDAT (type=35, len=8)
- SEZNAMMETOD (type=261, len=8)
- SEZNAMSABLON (type=261, len=8)
- DIAG1 (type=37, len=10)
- DIAG2 (type=37, len=10)
- DIAG3 (type=37, len=10)
- STATIM (type=14, len=1)
- ODEBRANO (type=35, len=8)
- BEZODBERU (type=14, len=1)
- EXPORTOVAT (type=35, len=8)
- NAH1 (type=37, len=10)
- NAH2 (type=37, len=10)
- ODEBRAL (type=37, len=30)
- POCETZKUM (type=8, len=4)
- OS_NAZEV (type=37, len=100)
- OS_ID (type=37, len=20)
- PARAMETRY (type=261, len=8)
- POZNAMKY (type=261, len=8)
- STRINGPARAMETRY (type=261, len=8)
- VZOREK1 (type=37, len=25)
- VZOREK2 (type=37, len=25)
- VZOREK3 (type=37, len=25)
- VZOREK4 (type=37, len=25)
- PLATCE (type=37, len=30)
- OSVOBOZENDPH (type=14, len=1)
- PLATCEPOZNAMKA (type=37, len=100)
- KATALOGALIAS (type=37, len=20)
- KATALOGNAZEV (type=37, len=100)
- PRIMARNIVZORKY (type=37, len=256)
** HISTDOC_SCHVALENI ** (řádků: 0)
- ID (type=8, len=4)
- IDHISTDOC (type=8, len=4)
- IDVZPARC (type=8, len=4)
- PRILOHA (type=261, len=8)
- ODESLANO (type=14, len=1)
** HOSP_ZPRAVY ** (řádků: 0)
- ID (type=8, len=4)
- DATUM (type=12, len=4)
- CAS (type=13, len=4)
- ZPRAVA (type=261, len=8)
- IDUZI (type=8, len=4)
- IDPAC (type=8, len=4)
- ID_EDOKUMENT (type=8, len=4)
- IDHOSP (type=8, len=4)
** IZIPDOC ** (řádků: 0)
- ID (type=8, len=4)
- IDPAC (type=8, len=4)
- DATUM (type=12, len=4)
- STATUS (type=8, len=4)
- IDENT (type=8, len=4)
- ZAPIS (type=261, len=8)
- DEKID (type=8, len=4)
- DGN (type=14, len=5)
- CAS (type=13, len=4)
- IDZAR (type=8, len=4)
- IDODD (type=8, len=4)
- IDPRAC (type=8, len=4)
- AUTOR (type=8, len=4)
- LOCKED (type=14, len=1)
- TYP (type=14, len=1)
- VDGN1 (type=14, len=5)
- VDGN2 (type=14, len=5)
- VDGN3 (type=14, len=5)
- VDGN4 (type=14, len=5)
** IZIPFILES ** (řádků: 0)
- ID (type=8, len=4)
- DOCID (type=8, len=4)
- TYP (type=8, len=4)
- FILENAME (type=37, len=50)
- BODY (type=261, len=8)
- DATUM (type=12, len=4)
** LEKZPRAVY ** (řádků: 13748)
- ID (type=8, len=4)
- IDPAC (type=8, len=4)
- DEKURSID (type=8, len=4)
- DATUM (type=12, len=4)
- TEXT (type=261, len=8)
- IDUZI (type=8, len=4)
- IDNOSVYK (type=8, len=4)
- TYP (type=14, len=1)
- ODESILATEL (type=37, len=254)
- DEKURS_COPYED (type=35, len=8)
- EISPUBLIC (type=14, len=1)
- PRIJEMCE (type=37, len=254)
- IDPRAC (type=8, len=4)
- SIGNATURE (type=261, len=8)
- SIGNATURE_INFO (type=261, len=8)
- IDCERTIFICATE (type=8, len=4)
- TST (type=261, len=8)
- NAZEV (type=37, len=80)
- ID_EDOKUMENT (type=8, len=4)
- PRIJATO (type=14, len=1)
- JMENOLEK (type=37, len=50)
- ICP (type=14, len=8)
- ES_STAV (type=37, len=1)
- PR_TYP (type=14, len=1)
- PR_ID (type=8, len=4)
- STORNO (type=14, len=1)
- UID (type=37, len=16)
- PUVODNI (type=8, len=4)
- CAS (type=13, len=4)
- DATAKT (type=35, len=8)
- ODALIAS (type=37, len=8)
- PDF (type=261, len=8)
- PREPIS (type=8, len=4)
- OZNACENI_O (type=37, len=50)
- STAV (type=14, len=1)
- CLICKBOX_ATT_ID (type=8, len=4)
- CGMNUMBER_ODESILATEL (type=37, len=12)
- CLICKBOX_MAILBOX (type=37, len=255)
- CGMNUMBER_PRIJEMCE (type=37, len=12)
- CGMNUMBER (type=37, len=12)
** LEKZPRAVY_PR ** (řádků: 134)
- ID (type=8, len=4)
- LEKZPRID (type=8, len=4)
- PRID (type=8, len=4)
- IDUZI (type=8, len=4)
- TYP (type=14, len=1)
- EXT (type=14, len=4)
** LEKZPRAVY_SEND ** (řádků: 0)
- LEKZPRID (type=8, len=4)
- KDY (type=35, len=8)
- KOMU (type=37, len=80)
** MEDINETIN_ATTACHMENT ** (řádků: 120)
- ID (type=8, len=4)
- ID_MEDINETIN (type=8, len=4)
- FILE_NAME (type=37, len=254)
- FILE_EXT (type=37, len=5)
- FILE_TYPE (type=8, len=4)
- DATA (type=261, len=8)
- ID_FILES (type=8, len=4)
- ZPRACOVANO (type=14, len=1)
** MEDINETOUT_ATTACHMENT ** (řádků: 2236)
- ID (type=8, len=4)
- ID_MEDINETOUT (type=8, len=4)
- FILE_ID (type=37, len=36)
- FILE_NAME (type=37, len=254)
- FILE_EXT (type=37, len=5)
- FILE_TYPE (type=8, len=4)
- DATA (type=261, len=8)
- ATTACH_INT_TYP (type=37, len=1)
- ATTACH_INT_ID (type=8, len=4)
** OSE_ZPRAVY ** (řádků: 0)
- ID (type=8, len=4)
- IDPAC (type=8, len=4)
- DATUM (type=35, len=8)
- UZI (type=37, len=3)
- DATA (type=261, len=8)
- SIGNATURE (type=261, len=8)
- SIGNATURE_INFO (type=261, len=8)
- IDCERTIFICATE (type=8, len=4)
- STORNO (type=35, len=8)
- STORNOBY (type=14, len=3)
** SOC_FINPRIJEM_HISTDOC ** (řádků: 0)
- ID_SFH (type=8, len=4)
- ID_SFP (type=8, len=4)
- ID_HDC (type=8, len=4)
** ZPRAVY ** (řádků: 26)
- ID (type=8, len=4)
- IDZPRAVY (type=8, len=4)
- IDOD (type=8, len=4)
- IDKOMUZAR (type=8, len=4)
- IDKOMUODD (type=8, len=4)
- IDKOMUPRAC (type=8, len=4)
- IDKOMUUZIV (type=8, len=4)
- PREDMET (type=37, len=100)
- TEXT (type=261, len=8)
- DATUM (type=12, len=4)
- CAS (type=13, len=4)
- URGENTNI (type=8, len=4)
- PRECTENA (type=8, len=4)
- DATUMPRECT (type=12, len=4)
- CASPRECT (type=13, len=4)
- SMAZANA (type=8, len=4)
- OZNAMOPRECT (type=8, len=4)
- TYP (type=8, len=4)
- KOMU (type=261, len=8)
- PLATNOST (type=35, len=8)
- LZE_ODPOVED (type=14, len=1)
- ID_EXT (type=8, len=4)
** ZPRAVY_ANKETY ** (řádků: 0)
- ID (type=8, len=4)
- IDUZI (type=8, len=4)
- IDPRIJEMCE (type=8, len=4)
- ID_EXT (type=8, len=4)
- DATA (type=261, len=8)
- DATUM (type=12, len=4)
- ODESLANO (type=14, len=1)
** ZPRAVY_EXT ** (řádků: 18)
- ID (type=8, len=4)
- ID_EXT (type=8, len=4)
- ID_ODESILATEL (type=8, len=4)
- TYP (type=37, len=1)
- PRIORITA (type=37, len=1)
- DATUM (type=35, len=8)
- PREDMET (type=37, len=254)
- DATA (type=261, len=8)
- STAV (type=37, len=1)
- AKCE (type=37, len=20)
- PRIJEMCE (type=37, len=3)
- ODESLAL (type=37, len=20)
- ODB (type=37, len=3)
** ZPRAVY_LOCK ** (řádků: 1)
- IDUZI (type=8, len=4)
- BEGINTIME (type=35, len=8)
- ENDTIME (type=35, len=8)

114
MedicusWithClaude/funkce.py Normal file
View File

@@ -0,0 +1,114 @@
import os,fdb,datetime
# conn = fdb.connect(
# dsn=r'localhost:u:\medicus 3\data\medicus.fdb', # Database path
# user='SYSDBA', # Username
# password="masterkey", # Password,
# charset="win1250")
# cur = conn.cursor()
def zapis_file(vstupconnection,idpac,cesta,souborname,prvnizavorka,soubordate,souborfiledate,poznamka):
import funkce
cur=vstupconnection.cursor()
fileid = funkce.get_files_id(vstupconnection)
with open(os.path.join(cesta,souborname), 'rb') as f:
daticka = f.read()
query = "insert into files (id,iduzi,iddoctyp,typ,idpac,filename,body,datum,datsouboru,poznamka) values(?,?,?,?,?,?,?,?,?,?)"
cur.execute(query,(fileid,6,1,1,idpac,prvnizavorka+".pdf",daticka,soubordate,souborfiledate,poznamka[:99]))
vstupconnection.commit()
return fileid
def zapis_dekurs(vstupconnection, idpac, idodd, iduzi, idprac, idfile, filename, text, datumzpravy,
datumsouboru):
import funkce
dekursid = funkce.get_dekurs_id(vstupconnection)
cur = vstupconnection.cursor()
print("Funkce zapis_dekurs hlasí OK")
print("idpac", idpac)
print("idodd", idodd)
print("iduzi", iduzi)
print("idfile", idfile)
print("filename", filename)
print("text", text)
print("datumzpravy", datumzpravy)
print("datumsouboru", datumsouboru)
print("dekursid", dekursid)
# rtf = r"""{\rtf1\ansi\ansicpg1250\uc1\deff0\deflang1029{\info{\bookmarks "BOOKMARKNAME","Files:FILEID",9}}{\fonttbl{\f0\fnil\fcharset238 Arial;}{\f5\fnil\fcharset238 Symbol;}}
# {\colortbl ;\red0\green0\blue255;\red0\green128\blue0;\red0\green0\blue0;}
# {\stylesheet{\s0\fi0\li0\ql\ri0\sb0\sa0 Norm\'e1ln\'ed;}{\*\cs15\f0\fs20 Norm\'e1ln\'ed;}{\*\cs20\f0\i\fs20 Z\'e1hlav\'ed;}{\*\cs32\f0\ul\fs20\cf1 Odkaz;}}
# \uc1\pard\s0\plain\cs20\f0\i\fs20 P\'f8\'edlohy: {\*\bkmkstart 0}\plain\cs32\f0\ul\fs20\cf1 BOOKMARKNAME{\*\bkmkend 0}\par
# \pard\s0\plain\cs15\f0\fs20 \par
# }
# """
rtf = r"""{\rtf1\ansi\ansicpg1250\uc1\deff0\deflang1029{\info{\bookmarks "BOOKMARKNAME","Files:FILEID",9}}{\fonttbl{\f0\fnil\fcharset238 Arial;}{\f5\fnil\fcharset238 Symbol;}}
{\colortbl ;\red0\green0\blue255;\red0\green128\blue0;\red0\green0\blue0;}
{\stylesheet{\s10\fi0\li0\ql\ri0\sb0\sa0 Vlevo;}{\*\cs15\f0\fs20 Norm\'e1ln\'ed;}{\*\cs20\f0\i\fs20 Z\'e1hlav\'ed;}{\*\cs22\f0\ul\fs20\cf1 Odkaz;}}
\uc1\pard\s10\plain\cs20\f0\i\fs20 P\'f8\'edlohy:\par
\pard\s10{\*\bkmkstart 0}\plain\cs22\f0\ul\fs20\cf1 BOOKMARKNAME{\*\bkmkend 0}\par
\pard\s10\plain\cs15\f0\fs20 \par
}
"""
# id idpac filename body docid typ datum iddoctyp poznamka idpac=2 iduzi=2 datsouboru id_edokument ext_id
encodedbookmark = funkce.convert_to1250(filename)
print("Encodedbookmark", encodedbookmark)
rtf = rtf.replace("BOOKMARKNAME", encodedbookmark)
rtf = rtf.replace("FILEID", str(idfile))
rtf = rtf.replace("TEXTENTER", text)
datumzapisu = datetime.datetime.now().date()
caszapisu = datetime.datetime.now().time()
print("Datumzapisu", datumzapisu)
print("Caszapisu", caszapisu)
print("RTF", rtf)
cur.execute("insert into dekurs (id,idpac,idodd,iduzi,idprac,datum,cas,dekurs) values(?,?,?,?,?,?,?,?)",
(dekursid, idpac, idodd, iduzi, idprac, datumzapisu, caszapisu, rtf))
vstupconnection.commit()
def convert_to1250(retezec):
retezec=retezec.encode("cp1250")
retezec=str(retezec)[2:]
retezec = retezec[:-1]
retezec=retezec.replace(r"\x",r"\'")
return retezec
# x=convert_to1250("Příloha")
# print(x,len(x))
def get_dekurs_id(connection):
try:
query = "SELECT GEN_ID(Gen_Dekurs, 1) FROM RDB$DATABASE"
cur = connection.cursor()
cur.execute(query)
newid=cur.fetchone()[0]
print("Funkce GET_DEKURS_ID přiřadila nové ID:",newid)
return(newid)
except:
print("Funkce GET_DEKURS_ID nepřiřadila nové ID")
return(None)
def get_files_id(connection):
try:
query = "SELECT GEN_ID(Gen_Files, 1) FROM RDB$DATABASE"
cur=connection.cursor()
cur.execute(query)
newid=cur.fetchone()[0]
print(newid)
return(newid)
except:
return(None)
def get_idpac(rodnecislo,connection):
try:
query = "SELECT idpac,prijmeni FROM kar where rodcis=?"
cur = connection.cursor()
cur.execute(query,(rodnecislo,))
tmp_nacteno=cur.fetchone()
tmp_id = tmp_nacteno[0]
tmp_jmeno=tmp_nacteno[1]
print(f"Pacient s rodným číslem {rodnecislo} má ID {tmp_id} a jméno {tmp_jmeno}")
return (tmp_id)
except:
return(None)

View File

@@ -0,0 +1,184 @@
"""funkce_ext.py zápis PDF souborů přímo do měsíční externí Firebird DB
místo do hlavní medicus.fdb spouštět na Windows
Náhrada za funkce.zapis_file() v s03soubory.py.
Binární data souboru se uloží do MEDICUS_FILES_YYYYMM.fdb (DATA tabulka),
do hlavní FILES.BODY se vloží 48bajtová reference.
Formát FILES.BODY reference (48 B):
magic 4 B = b'\\xee\\xbb\\xaa\\x0b'
uid 32 B = UUID4 hex (ASCII, 32 znaků)
dblen 4 B = délka DBNAME jako little-endian uint32
dbname N B = DBNAME ASCII (např. 'DB202603', 8 B)
"""
import os
import struct
import uuid
import fdb
# Magic bajty identifikující referenci na externí DB
MAGIC = b'\xee\xbb\xaa\x0b'
# ─── Pomocné funkce ──────────────────────────────────────────────────────────
def make_body_ref(uid: str, dbname: str) -> bytes:
"""Sestaví 48bajtovou binární referenci pro FILES.BODY."""
uid_bytes = uid.encode('ascii') # 32 B
dbname_bytes = dbname.encode('ascii') # typicky 8 B ('DB202603')
return MAGIC + uid_bytes + struct.pack('<I', len(dbname_bytes)) + dbname_bytes
def _get_ext_server(vstupconnection):
"""Zjistí SERVER z existující EXTERNI_DB (např. 'localhost/3053')."""
cur = vstupconnection.cursor()
cur.execute(
"SELECT SERVER FROM EXTERNI_DB WHERE DBNAME LIKE 'DB%' ORDER BY DBNAME DESC ROWS 1"
)
row = cur.fetchone()
return row[0] if row else 'localhost/3053'
def _get_ext_heslo(vstupconnection):
"""Přečte šifrované heslo z existující EXTERNI_DB.
Všechny externí DB mají stejné heslo (masterkey, jen zašifrované Medicusem),
takže stačí vzít heslo z libovolné existující položky."""
cur = vstupconnection.cursor()
cur.execute(
"SELECT HESLO FROM EXTERNI_DB WHERE DBNAME LIKE 'DB%' ORDER BY DBNAME DESC ROWS 1"
)
row = cur.fetchone()
return row[0] if row else 'masterkey'
def _vytvor_externi_db(ext_path):
"""Vytvoří novou prázdnou externí Firebird DB s tabulkou DATA a generátorem GEN_UID.
Schéma odpovídá tomu, co vytváří Medicus sám při prvním exportu daného měsíce."""
print(f" Vytvářím novou ext DB: {ext_path}")
conn = fdb.create_database(
dsn=f'localhost:{ext_path}',
user='SYSDBA',
password='masterkey',
charset='WIN1250',
page_size=16384
)
cur = conn.cursor()
cur.execute("""
CREATE TABLE DATA (
UID VARCHAR(32) NOT NULL,
DATA BLOB SUB_TYPE 0,
DATASIZE INTEGER,
CHUNK INTEGER
)
""")
cur.execute("CREATE GENERATOR GEN_UID")
conn.commit()
print(f" Nová ext DB vytvořena: {ext_path}")
return conn
# ─── Hlavní funkce ───────────────────────────────────────────────────────────
def zapis_file_ext(vstupconnection, idpac, cesta, souborname, prvnizavorka,
soubordate, souborfiledate, poznamka,
ext_base_path=r'u:\externi'):
"""Zapíše PDF soubor do měsíční externí DB a vrátí fileid.
Parametry jsou shodné s funkce.zapis_file() stačí prohodit import.
Postup:
1. Určí měsíc z soubordate → DBNAME (např. 'DB202603')
2. Načte binární data PDF
3. Najde nebo vytvoří měsíční MEDICUS_FILES_YYYYMM.fdb
4. Zapíše data do DATA tabulky pod novým UID (UUID4 hex)
5. Sestaví 48bajtovou BODY referenci
6. Získá nové FILES ID z Gen_Files
7. Vloží záznam do hlavní FILES tabulky (BODY = reference, ne BLOB)
8. Vrátí fileid (shodné s funkce.zapis_file())
"""
import funkce # get_files_id ze stávajícího funkce.py
# 1. Měsíc a DBNAME
yyyymm = soubordate.strftime('%Y%m')
dbname = f'DB{yyyymm}'
print(f" DBNAME: {dbname}")
# 2. Binární data PDF
soubor_cesta = os.path.join(cesta, souborname)
with open(soubor_cesta, 'rb') as f:
data = f.read()
print(f" Načten soubor: {souborname} ({len(data):,} B)")
# 3. Připojení k externí DB
cur = vstupconnection.cursor()
cur.execute(
"SELECT SERVER, PATH FROM EXTERNI_DB WHERE DBNAME = ?", (dbname,)
)
row = cur.fetchone()
if row:
# Existující měsíční DB připojíme se
ext_server_raw, ext_path = row[0], row[1]
# SERVER je "localhost/3053" (FBScanner proxy) bereme jen hostname
ext_server = ext_server_raw.split('/')[0]
print(f" Existující ext DB: {dbname}{ext_path}")
conn_ext = fdb.connect(
dsn=f'{ext_server}:{ext_path}',
user='SYSDBA',
password='masterkey',
charset='WIN1250'
)
else:
# Nová měsíční DB vytvoříme a zaregistrujeme
ext_filename = f'MEDICUS_FILES_{yyyymm}.fdb'
ext_path = os.path.join(ext_base_path, ext_filename)
conn_ext = _vytvor_externi_db(ext_path)
# Zaregistrovat do EXTERNI_DB (heslo zkopírujeme z existující položky)
ext_server = _get_ext_server(vstupconnection)
heslo = _get_ext_heslo(vstupconnection)
cur.execute(
"INSERT INTO EXTERNI_DB (DBNAME, SERVER, PATH, HESLO) VALUES (?, ?, ?, ?)",
(dbname, ext_server, ext_path, heslo)
)
vstupconnection.commit()
print(f" Registrována v EXTERNI_DB: {dbname}, server={ext_server}")
# 4. Zápis do DATA tabulky
uid = uuid.uuid4().hex # 32 hex znaků, vždy unikátní
cur_ext = conn_ext.cursor()
cur_ext.execute(
"INSERT INTO DATA (UID, DATA, DATASIZE, CHUNK) VALUES (?, ?, ?, ?)",
(uid, data, len(data), 0)
)
conn_ext.commit()
conn_ext.close()
print(f" Zapsáno do ext DB: UID={uid}")
# 5. BODY reference (48 B)
body_ref = make_body_ref(uid, dbname)
print(f" BODY ref: {body_ref.hex()}")
# 6. Nové FILES ID
fileid = funkce.get_files_id(vstupconnection)
if fileid is None:
raise RuntimeError("Nepodařilo se získat nové FILES ID (Gen_Files selhal)!")
# 7. INSERT do hlavní FILES tabulky
cur.execute(
"INSERT INTO FILES "
"(id, iduzi, iddoctyp, typ, idpac, filename, body, datum, datsouboru, poznamka) "
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
(fileid, 6, 1, 1, idpac,
prvnizavorka + '.pdf',
body_ref,
soubordate, souborfiledate,
poznamka[:99])
)
vstupconnection.commit()
print(f" FILES záznam vytvořen: ID={fileid}, idpac={idpac}, "
f"soubor={prvnizavorka}.pdf")
return fileid

View File

@@ -0,0 +1,16 @@
# Firebird trace konfigurace zachytí SQL příkazy Medicusu
# Soubor: medicus_trace.conf
<database c:\\medicus 3\\data\\medicus.fdb>
enabled true
log_connections false
log_transactions false
log_statement_prepare false
log_statement_start false
log_statement_finish true
log_procedure_finish false
print_plan false
print_perf false
time_threshold 0
max_sql_length 32768
</database>

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,22 @@
"""read_last_dekurs.py jednorázový skript: vypíše obsah posledního DEKURS záznamu pro Buzalka"""
import fdb
conn = fdb.connect(
dsn=r'localhost:c:\medicus 3\data\medicus.fdb',
user='SYSDBA', password='masterkey', charset='win1250'
)
cur = conn.cursor()
cur.execute("""
SELECT FIRST 1 ID, DATUM, CAS, CHAR_LENGTH(DEKURS), DEKURS
FROM DEKURS
WHERE IDPAC = 9742
ORDER BY ID DESC
""")
row = cur.fetchone()
idek, datum, cas, delka, obsah = row
print(f"ID={idek} datum={datum} cas={cas} délka={delka} B")
print("=" * 60)
print(obsah)
conn.close()

View File

@@ -1,5 +1,5 @@
import os,shutil,fdb,time
import re,datetime,funkce
import re,datetime,funkce,funkce_ext
# Connect to the Firebird database
conn = fdb.connect(
@@ -148,12 +148,12 @@ BOOKMARKSTEXT
}"""
for key in skupiny.keys():
rtf = r"""{\rtf1\ansi\ansicpg1250\uc1\deff0\deflang1029{\info{\bookmarks BOOKMARKNAMES }}{\fonttbl{\f0\fnil\fcharset238 Arial;}{\f5\fnil\fcharset238 Symbol;}}
rtf = r"""{\rtf1\ansi\ansicpg1250\uc1\deff0\deflang1029{\info{\bookmarks BOOKMARKNAMES}}{\fonttbl{\f0\fnil\fcharset238 Arial;}{\f5\fnil\fcharset238 Symbol;}}
{\colortbl ;\red0\green0\blue255;\red0\green128\blue0;\red0\green0\blue0;}
{\stylesheet{\s10\fi0\li0\ql\ri0\sb0\sa0 Vlevo;}{\*\cs15\f0\fs20 Norm\'e1ln\'ed;}{\*\cs20\f0\i\fs20 Z\'e1hlav\'ed;}{\*\cs22\f0\ul\fs20\cf1 Odkaz;}}
\uc1\pard\s10\plain\cs20\f0\i\fs20 Vlo\'9eena skenovan\'e1 dokumentace:\par
{\stylesheet{\s10\fi0\li0\ql\ri0\sb0\sa0 Vlevo;}{\*\cs15\f0\fs20 Norm\'e1ln\'ed;}{\*\cs20\f0\i\fs20 Z\'e1hlav\'ed;}{\*\cs32\f0\ul\fs20\cf1 Odkaz;}}
\uc1\pard\s10\plain\cs20\f0\i\fs20 Vlo\'9een\'e9 p\'f8\'edlohy:\par
BOOKMARKSTEXT
\pard\s10\plain\cs15\f0\fs20\par
\pard\s10\plain\cs15\f0\fs20 \par
}"""
@@ -169,9 +169,9 @@ BOOKMARKSTEXT
# print(row)
pacid=row[1]
filename=row[6]
fileid= funkce.zapis_file(vstupconnection=conn, idpac=row[1],
cesta=cesta, souborname=row[6], prvnizavorka=row[4],
soubordate=row[2], souborfiledate=row[7], poznamka=row[5])
fileid= funkce_ext.zapis_file_ext(vstupconnection=conn, idpac=row[1],
cesta=cesta, souborname=row[6], prvnizavorka=row[4],
soubordate=row[2], souborfiledate=row[7], poznamka=row[5])
for attempt in range(3):
try:
@@ -202,11 +202,8 @@ BOOKMARKSTEXT
bookmark=bookmark+'"'+filenameforbookmark+'","Files:'+str(fileid)+'",'+str(cislo)+";"
cislo+=7
# print(bookmark)
if prvnibookmark:
bookmarks=bookmarks+r'\pard\s10{\*\bkmkstart '+str(poradi)+r"}\plain\cs22\f0\ul\fs20\cf1 "+filenameforbookmark+r"{\*\bkmkend "+str(poradi)+r"}\par"
prvnibookmark=False
else:
bookmarks=bookmarks+r'\pard\s10{\*\bkmkstart '+str(poradi)+r"}" + filenameforbookmark + r"{\*\bkmkend " + str(poradi) + r"}\par"
bookmarks += r'\pard\s10{\*\bkmkstart ' + str(poradi) + r'}\plain\cs32\f0\ul\fs20\cf1 ' + filenameforbookmark + r'{\*\bkmkend ' + str(poradi) + r'}\par'
poradi += 1
bookmark=bookmark[:-1]
# bookmarks=bookmarks[:-2]
print(bookmark)

View File

@@ -0,0 +1,108 @@
"""s04_presun_externi_db.py Přesun externích DB souborů z u:\ do u:\externi\
Spustit na Windows s ZAVŘENÝM Medicusem!
Co dělá:
1. Připojí se k hlavní medicus.fdb
2. Načte všechny záznamy z EXTERNI_DB
3. Pro každý záznam zkopíruje FDB soubor z původní lokace do u:\externi\
4. Aktualizuje EXTERNI_DB.PATH na novou lokaci
5. Vytiskne přehled výsledků
Po spuštění zkus v Medicusu otevřít přílohu pacienta mělo by to fungovat.
Pokud ne, zálohuj si databázi a spusť znovu (skript je idempotentní).
"""
import os
import shutil
import fdb
# ── Konfigurace ──────────────────────────────────────────────────────────────
MAIN_DB = r'c:\medicus 3\data\medicus.fdb'
CÍL_SLOŽKA = r'u:\externi'
# ─────────────────────────────────────────────────────────────────────────────
def main():
os.makedirs(CÍL_SLOŽKA, exist_ok=True)
print(f"Cílová složka: {CÍL_SLOŽKA}")
print()
print("Připojuji se k hlavní DB...")
conn = fdb.connect(
dsn=f'localhost:{MAIN_DB}',
user='SYSDBA',
password='masterkey',
charset='WIN1250'
)
cur = conn.cursor()
# Načti všechny záznamy z EXTERNI_DB
cur.execute("SELECT DBNAME, SERVER, PATH, HESLO FROM EXTERNI_DB ORDER BY DBNAME")
zaznamy = cur.fetchall()
print(f"Nalezeno {len(zaznamy)} záznamů v EXTERNI_DB:")
print()
ok = 0
preskoceno = 0
chyba = 0
for dbname, server, path_puvodni, heslo in zaznamy:
print(f" [{dbname}] {path_puvodni}")
# Název souboru (jen basename, bez cesty)
basename = os.path.basename(path_puvodni)
# Normalize: Medicus někdy ukládá s malým .fdb, někdy velkým .FDB
path_cil = os.path.join(CÍL_SLOŽKA, basename)
# Zkontroluj, jestli zdrojový soubor existuje
# (zkus obě varianty přípony)
path_src = None
for kandidat in [path_puvodni,
path_puvodni.replace('.fdb', '.FDB'),
path_puvodni.replace('.FDB', '.fdb')]:
if os.path.isfile(kandidat):
path_src = kandidat
break
if path_src is None:
print(f" ⚠ ZDROJOVÝ SOUBOR NENALEZEN: {path_puvodni} přeskakuji")
chyba += 1
continue
# Pokud je soubor už v cíli, přeskoč kopírování
if os.path.isfile(path_cil):
velikost = os.path.getsize(path_cil)
print(f" → Soubor již existuje v cíli ({velikost:,} B), přeskakuji kopírování")
else:
velikost = os.path.getsize(path_src)
print(f" → Kopíruji ({velikost:,} B)...", end=' ', flush=True)
shutil.copy2(path_src, path_cil)
print("OK")
# Aktualizuj PATH v EXTERNI_DB pokud se liší
if path_puvodni != path_cil:
cur.execute(
"UPDATE EXTERNI_DB SET PATH = ? WHERE DBNAME = ?",
(path_cil, dbname)
)
print(f" → EXTERNI_DB.PATH aktualizováno: {path_cil}")
else:
print(f" → PATH již ukazuje na cíl, není třeba měnit")
preskoceno += 1
ok += 1
conn.commit()
conn.close()
print()
print("=" * 60)
print(f"Hotovo! Zpracováno: {ok}, přeskočeno: {preskoceno}, chyb: {chyba}")
print()
if chyba > 0:
print("⚠ Některé soubory nebyly nalezeny viz výpis výše.")
print(" Pokud jsou to staré DB které již neexistují, nevadí to.")
print("Nyní spusť Medicus a zkus otevřít přílohu pacienta.")
if __name__ == '__main__':
main()

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,53 @@
"""Test připojení k externí DB a prozkoumání její struktury - spusť na Windows"""
import fdb, os, glob
# Najde první existující externí FDB soubor
soubory = glob.glob(r'u:\MEDICUS_FILES_*.fdb')
if not soubory:
soubory = glob.glob(r'u:\externi\MEDICUS_FILES_*.fdb')
if not soubory:
print("Žádné externí FDB soubory nenalezeny!")
exit()
soubor = sorted(soubory)[-1] # vezme nejnovější
print(f"Zkouším: {soubor}")
# Test 1: masterkey
try:
conn = fdb.connect(dsn=f'localhost:{soubor}', user='SYSDBA', password='masterkey', charset='win1250')
print("✓ Připojení s masterkey FUNGUJE!")
cur = conn.cursor()
# Jaké tabulky jsou v externí DB?
cur.execute("SELECT RDB$RELATION_NAME FROM RDB$RELATIONS WHERE RDB$SYSTEM_FLAG=0 AND RDB$VIEW_BLR IS NULL")
tabulky = [r[0].strip() for r in cur.fetchall()]
print(f"Tabulky v externí DB: {tabulky}")
# Kolik záznamů v FILES?
if 'FILES' in tabulky:
cur.execute("SELECT COUNT(*), MIN(ID), MAX(ID) FROM FILES")
row = cur.fetchone()
print(f"FILES: {row[0]} záznamů, ID od {row[1]} do {row[2]}")
cur.execute("SELECT ID, IDPAC, FILENAME, DATUM, OCTET_LENGTH(BODY) FROM FILES ORDER BY ID ROWS 3")
for r in cur.fetchall():
print(f" ID={r[0]} IDPAC={r[1]} FILE={r[2]} DATUM={r[3]} BODY={r[4]}B")
# Zjistit generátor
cur.execute("SELECT RDB$GENERATOR_NAME, RDB$GENERATOR_ID FROM RDB$GENERATORS WHERE RDB$SYSTEM_FLAG=0")
for r in cur.fetchall():
print(f"Generátor: {r[0].strip()} = {r[1]}")
conn.close()
except Exception as e:
print(f"✗ masterkey NEFUNGUJE: {e}")
# Test s jiným heslem - zkus prázdné
try:
conn = fdb.connect(dsn=f'localhost:{soubor}', user='SYSDBA', password='', charset='win1250')
print("✓ Připojení s prázdným heslem FUNGUJE!")
conn.close()
except Exception as e2:
print(f"✗ Prázdné heslo také nefunguje: {e2}")

View File

@@ -0,0 +1,46 @@
"""Prozkoumá strukturu tabulky DATA v externí DB - spusť na Windows"""
import fdb, glob
soubory = glob.glob(r'u:\MEDICUS_FILES_*.fdb') + glob.glob(r'u:\MEDICUS_FILES_*.FDB')
if not soubory:
soubory = glob.glob(r'u:\externi\MEDICUS_FILES_*.fdb') + glob.glob(r'u:\externi\MEDICUS_FILES_*.FDB')
soubor = sorted(soubory)[-1]
print(f"Soubor: {soubor}\n")
conn = fdb.connect(dsn=f'localhost:{soubor}', user='SYSDBA', password='masterkey', charset='win1250')
cur = conn.cursor()
# Sloupce tabulky DATA
print("=== Sloupce tabulky DATA ===")
cur.execute("""
SELECT r.RDB$FIELD_NAME, f.RDB$FIELD_TYPE, f.RDB$FIELD_LENGTH, f.RDB$SEGMENT_LENGTH
FROM RDB$RELATION_FIELDS r
JOIN RDB$FIELDS f ON f.RDB$FIELD_NAME = r.RDB$FIELD_SOURCE
WHERE r.RDB$RELATION_NAME = 'DATA'
ORDER BY r.RDB$FIELD_POSITION
""")
for row in cur.fetchall():
typ = {7:'SMALLINT',8:'INTEGER',12:'DATE',13:'TIME',14:'CHAR',16:'INT64',35:'TIMESTAMP',37:'VARCHAR',261:'BLOB'}.get(row[1], str(row[1]))
print(f" {row[0].strip():30} {typ} len={row[2]}")
# Generátory
print("\n=== Generátory ===")
cur.execute("SELECT RDB$GENERATOR_NAME, RDB$GENERATOR_ID FROM RDB$GENERATORS WHERE RDB$SYSTEM_FLAG=0")
for row in cur.fetchall():
print(f" {row[0].strip()} = {row[1]}")
# Počet záznamů a ukázka
print("\n=== Počet záznamů ===")
cur.execute("SELECT COUNT(*) FROM DATA")
print(f" {cur.fetchone()[0]} záznamů")
print("\n=== Ukázka 3 záznamů (bez BLOB) ===")
cur.execute("""
SELECT ID, OCTET_LENGTH(BODY) as VELIKOST
FROM DATA ORDER BY ID ROWS 3
""")
for row in cur.fetchall():
print(f" ID={row[0]} BODY={row[1]}B")
conn.close()

View File

@@ -0,0 +1,47 @@
"""Zjistí propojení mezi hlavní DB a externí DB - spusť na Windows"""
import fdb, glob
# Hlavní DB
conn_main = fdb.connect(dsn=r'localhost:c:\medicus 3\data\medicus.fdb',
user='SYSDBA', password='masterkey', charset='win1250')
cur = conn_main.cursor()
# Najít exportované záznamy v FILES (BODY není velký BLOB)
print("=== FILES záznamy po exportu (malý BODY = reference na externí DB) ===")
cur.execute("""
SELECT ID, IDPAC, FILENAME, DATUM, EXT_ID, DOCID,
OCTET_LENGTH(BODY) as BODY_LEN,
CAST(BODY AS VARCHAR(100)) as BODY_TEXT
FROM FILES
WHERE OCTET_LENGTH(BODY) < 100
ORDER BY ID DESC ROWS 5
""")
for r in cur.fetchall():
print(f" ID={r[0]} IDPAC={r[1]} FILE={r[2]}")
print(f" DATUM={r[3]} EXT_ID={r[4]} DOCID={r[5]}")
print(f" BODY_LEN={r[6]} BODY_TEXT='{r[7]}'")
# Ukázka záznamu s plným BODY (ještě ne exportovaný)
print("\n=== FILES záznamy PŘED exportem (velký BODY = binary data) ===")
cur.execute("""
SELECT ID, FILENAME, DATUM, EXT_ID, OCTET_LENGTH(BODY) as BODY_LEN
FROM FILES
WHERE OCTET_LENGTH(BODY) > 1000
ORDER BY ID DESC ROWS 3
""")
for r in cur.fetchall():
print(f" ID={r[0]} FILE={r[1]} DATUM={r[2]} EXT_ID={r[3]} BODY={r[4]}B")
conn_main.close()
# Porovnat s externí DB
print("\n=== DATA záznamy v externí DB ===")
soubory = glob.glob(r'u:\MEDICUS_FILES_*.fdb') + glob.glob(r'u:\MEDICUS_FILES_*.FDB')
soubor = sorted(soubory)[0] # vezmeme nejstarší
print(f"Soubor: {soubor}")
conn_ext = fdb.connect(dsn=f'localhost:{soubor}', user='SYSDBA', password='masterkey', charset='win1250')
cur_ext = conn_ext.cursor()
cur_ext.execute("SELECT UID, DATASIZE, CHUNK FROM DATA ORDER BY CHUNK ROWS 5")
for r in cur_ext.fetchall():
print(f" UID='{r[0]}' DATASIZE={r[1]} CHUNK={r[2]}")
conn_ext.close()

View File

@@ -0,0 +1,20 @@
"""Čte přesné bajty BODY z exportovaného záznamu FILES - spusť na Windows"""
import fdb
conn = fdb.connect(dsn=r'localhost:c:\medicus 3\data\medicus.fdb',
user='SYSDBA', password='masterkey', charset='win1250')
cur = conn.cursor()
# Vezmi jeden exportovaný záznam
cur.execute("SELECT ID, BODY FROM FILES WHERE ID = 10487")
row = cur.fetchone()
fileid = row[0]
body_bytes = row[1] # fdb vrací BLOB jako bytes přímo
print(f"FILE ID: {fileid}")
print(f"BODY délka: {len(body_bytes)} bajtů")
print(f"BODY hex: {body_bytes.hex()}")
print(f"BODY repr: {repr(body_bytes)}")
print(f"BODY jako string (latin1): {body_bytes.decode('latin1')}")
conn.close()

View File

@@ -0,0 +1,108 @@
"""test_import_3files.py jednorázový test: 3 soubory jednoho pacienta → 1 dekurs s klikacími odkazy
Testuje opravenou RTF logiku (cs32, Přílohy: inline, poradi++)
Spustit na Windows.
"""
import datetime
import os
import fdb
import funkce
import funkce_ext
CESTA = r'u:\\'
IDPAC = 9742 # Buzalka Vladimír, RC 7309208104
SOUBORY = [
{
'souborname': '7309208104 2026-03-18 Buzalka, Vladimír [vyšetření] [ahoj Claude copy1].pdf',
'prvnizavorka': 'vyšetření',
'druhazavorka': 'ahoj Claude copy1',
'datum': datetime.date(2026, 3, 18),
},
{
'souborname': '7309208104 2026-03-18 Buzalka, Vladimír [vyšetření] [ahoj Claude copy2].pdf',
'prvnizavorka': 'vyšetření',
'druhazavorka': 'ahoj Claude copy2',
'datum': datetime.date(2026, 3, 18),
},
{
'souborname': '7309208104 2026-03-18 Buzalka, Vladimír [vyšetření] [ahoj Claude].pdf',
'prvnizavorka': 'vyšetření',
'druhazavorka': 'ahoj Claude',
'datum': datetime.date(2026, 3, 18),
},
]
# ── Připojení ─────────────────────────────────────────────────────────────────
conn = fdb.connect(
dsn=r'localhost:c:\medicus 3\data\medicus.fdb',
user='SYSDBA', password='masterkey', charset='WIN1250'
)
# ── Krok 1: vložit každý soubor do ext DB ─────────────────────────────────────
bookmark = ''
bookmarks = ''
cislo = 9
poradi = 0
for s in SOUBORY:
cesta_souboru = os.path.join(CESTA, s['souborname'])
datumsouboru = datetime.datetime.fromtimestamp(os.path.getmtime(cesta_souboru))
print(f"\n>>> Zpracovávám: {s['souborname']}")
fileid = funkce_ext.zapis_file_ext(
vstupconnection = conn,
idpac = IDPAC,
cesta = CESTA,
souborname = s['souborname'],
prvnizavorka = s['prvnizavorka'],
soubordate = s['datum'],
souborfiledate = datumsouboru,
poznamka = s['druhazavorka'],
)
print(f" → FILES.ID = {fileid}")
# {\info{\bookmarks ...}} sekce
filenameforbookmark = s['datum'].strftime('%Y-%m-%d') + ' ' + s['prvnizavorka'] + ': ' + s['druhazavorka']
bookmark += '"' + filenameforbookmark + '","Files:' + str(fileid) + '",' + str(cislo) + ';'
cislo += 7
# tělo RTF klikatelné záložky (cs32, všechny stejný formát)
bookmarks += (r'\pard\s10{\*\bkmkstart ' + str(poradi) + r'}'
r'\plain\cs32\f0\ul\fs20\cf1 ' + filenameforbookmark +
r'{\*\bkmkend ' + str(poradi) + r'}\par')
poradi += 1
bookmark = bookmark[:-1] # odstranit poslední ;
# ── Krok 2: sestavit RTF ──────────────────────────────────────────────────────
rtf = r"""{\rtf1\ansi\ansicpg1250\uc1\deff0\deflang1029{\info{\bookmarks BOOKMARKNAMES}}{\fonttbl{\f0\fnil\fcharset238 Arial;}{\f5\fnil\fcharset238 Symbol;}}
{\colortbl ;\red0\green0\blue255;\red0\green128\blue0;\red0\green0\blue0;}
{\stylesheet{\s10\fi0\li0\ql\ri0\sb0\sa0 Vlevo;}{\*\cs15\f0\fs20 Norm\'e1ln\'ed;}{\*\cs20\f0\i\fs20 Z\'e1hlav\'ed;}{\*\cs32\f0\ul\fs20\cf1 Odkaz;}}
\uc1\pard\s10\plain\cs20\f0\i\fs20 Vlo\'9een\'e9 p\'f8\'edlohy:\par
BOOKMARKSTEXT
\pard\s10\plain\cs15\f0\fs20 \par
}"""
rtf = rtf.replace('BOOKMARKNAMES', bookmark)
rtf = rtf.replace('BOOKMARKSTEXT', bookmarks)
print('\n=== Výsledný RTF ===')
print(rtf)
# ── Krok 3: zapsat dekurs ─────────────────────────────────────────────────────
dekursid = funkce.get_dekurs_id(conn)
datumzapisu = datetime.datetime.now().date()
caszapisu = datetime.datetime.now().time()
cur = conn.cursor()
cur.execute(
"INSERT INTO DEKURS (id, iduzi, idprac, idodd, idpac, datum, cas, dekurs)"
" VALUES (?,?,?,?,?,?,?,?)",
(dekursid, 6, 2, 2, IDPAC, datumzapisu, caszapisu, rtf)
)
conn.commit()
conn.close()
print(f'\n=== HOTOVO ===')
print(f'DEKURS.ID = {dekursid}')
print('Otevři Medicus → karta Buzalka Vladimír → najdi dnešní záznam → klikej na odkazy!')

View File

@@ -0,0 +1,47 @@
"""test_import_april2026.py jednorázový testovací import do DB202604
Spustit na Windows (Medicus může být spuštěný, jen ne na kartě tohoto pacienta).
"""
import datetime
import os
import fdb
import funkce_ext
SOUBOR_CESTA = r'u:\\'
SOUBOR_NAZEV = '7309208104 Buzalka, Vladimír 2026-04-01 [testovaci zprava] [všechno OK].pdf'
IDPAC = 9742 # Buzalka Vladimír (RC 7309208104)
PRVNI_ZAVORKA = 'testovaci zprava'
POZNAMKA = 'všechno OK'
DATUM_ZPRAVY = datetime.date(2026, 4, 1)
DATUM_SOUBORU = datetime.datetime.fromtimestamp(
os.path.getmtime(os.path.join(SOUBOR_CESTA, SOUBOR_NAZEV)))
print(f"Soubor: {os.path.join(SOUBOR_CESTA, SOUBOR_NAZEV)}")
print(f"Datum zprávy: {DATUM_ZPRAVY}")
print(f"Datum souboru:{DATUM_SOUBORU}")
print()
conn = fdb.connect(
dsn=r'localhost:c:\medicus 3\data\medicus.fdb',
user='SYSDBA',
password='masterkey',
charset='WIN1250'
)
fileid = funkce_ext.zapis_file_ext(
vstupconnection=conn,
idpac=IDPAC,
cesta=SOUBOR_CESTA,
souborname=SOUBOR_NAZEV,
prvnizavorka=PRVNI_ZAVORKA,
soubordate=DATUM_ZPRAVY,
souborfiledate=DATUM_SOUBORU,
poznamka=POZNAMKA,
)
conn.close()
print()
print(f"Hotovo! FILES.ID = {fileid}")
print("Otevři Medicus, přejdi na kartu Buzalka Vladimír a zkontroluj záložku Soubory.")

View File

@@ -0,0 +1,182 @@
"""test_import_merge.py vloží přílohy do dekurzu daného dne.
Pokud dekurs pro daný den existuje → vloží přílohy NAHORU před stávající text.
Pokud neexistuje → vytvoří nový dekurs.
Spustit na Windows.
"""
import datetime, os, re, fdb
import funkce, funkce_ext
CESTA = r'u:\\'
IDPAC = 9742 # Buzalka Vladimír
DATUM = datetime.date(2026, 3, 18)
SOUBORY = [
{'souborname': '7309208104 2026-03-18 Buzalka, Vladimír [vyšetření] [ahoj Claude copy1].pdf',
'prvnizavorka': 'vyšetření', 'druhazavorka': 'ahoj Claude copy1', 'datum': DATUM},
{'souborname': '7309208104 2026-03-18 Buzalka, Vladimír [vyšetření] [ahoj Claude copy2].pdf',
'prvnizavorka': 'vyšetření', 'druhazavorka': 'ahoj Claude copy2', 'datum': DATUM},
{'souborname': '7309208104 2026-03-18 Buzalka, Vladimír [vyšetření] [ahoj Claude].pdf',
'prvnizavorka': 'vyšetření', 'druhazavorka': 'ahoj Claude', 'datum': DATUM},
]
# ─────────────────────────────────────────────────────────────────────────────
def najdi_posledni_dekurs_dnes(conn, idpac, datum_vlozeni):
"""Najde POSLEDNÍ dekurs pacienta (jakýkoli datum) a vrátí (id, rtf)
pouze pokud je ze stejného dne jako datum_vlozeni. Jinak vrátí None.
Logika: zajímá nás jen poslední zápis pokud je z dnešního dne,
vložíme přílohy do něj. Pokud je starší, zakládáme nový dnešní záznam.
"""
cur = conn.cursor()
cur.execute("""
SELECT FIRST 1 ID, DATUM, DEKURS FROM DEKURS
WHERE IDPAC = ?
ORDER BY ID DESC
""", (idpac,))
row = cur.fetchone()
if row is None:
return None
dekurs_id, dekurs_datum, dekurs_rtf = row
print(f" Poslední dekurs: ID={dekurs_id}, datum={dekurs_datum}")
if dekurs_datum == datum_vlozeni:
print(f" → shoduje se s dneškem ({datum_vlozeni}), budeme mergovat")
return (dekurs_id, dekurs_rtf)
else:
print(f" → jiný den ({dekurs_datum}{datum_vlozeni}), vytvoříme nový")
return None
def merge_rtf_prepend(existing_rtf, new_bkm_list, new_body_pards, n_new):
"""Vloží nový obsah (přílohy) na ZAČÁTEK stávajícího dekurzu.
existing_rtf stávající RTF string z DB
new_bkm_list list stringů typu '"popis","Files:123",9'
new_body_pards RTF string: header \par + \pard řádky s odkazy
n_new počet nových bkmkstart indexů (= počet souborů)
"""
rtf = existing_rtf
# 1. Posunout stávající bkmkstart/bkmkend indexy o n_new,
# aby nedošlo ke kolizi s našimi novými (0, 1, 2 …)
rtf = re.sub(r'\\bkmkstart (\d+)',
lambda m: '\\bkmkstart ' + str(int(m.group(1)) + n_new), rtf)
rtf = re.sub(r'\\bkmkend (\d+)',
lambda m: '\\bkmkend ' + str(int(m.group(1)) + n_new), rtf)
# 2. Přidat naše bookmarky na ZAČÁTEK {\info{\bookmarks ...}}
new_bkm_str = ';'.join(new_bkm_list)
def merge_bkm(m):
existing = m.group(1).strip()
combined = new_bkm_str + (';' + existing if existing else '')
return '{\\info{\\bookmarks ' + combined + '}}'
if re.search(r'\{\\info\{\\bookmarks', rtf):
rtf = re.sub(r'\{\\info\{\\bookmarks ([^}]*)\}\}', merge_bkm, rtf)
else:
# žádný {\info} blok vložíme za \deflang...
rtf = re.sub(r'(\\deflang\d+)',
r'\1{\\info{\\bookmarks ' + new_bkm_str + '}}', rtf, count=1)
# 3. Vložit naše tělo před první \uc1\pard těla stávajícího dekurzu
match = re.search(r'\\uc1\\pard', rtf)
if match:
pos = rtf.index(r'\uc1\pard', match.start())
rtf = rtf[:pos] + new_body_pards + '\n' + rtf[pos:]
else:
# fallback připojit před poslední }
rtf = rtf.rstrip()
if rtf.endswith('}'):
rtf = rtf[:-1] + new_body_pards + '\n}'
return rtf
# ─────────────────────────────────────────────────────────────────────────────
conn = fdb.connect(dsn=r'localhost:c:\medicus 3\data\medicus.fdb',
user='SYSDBA', password='masterkey', charset='WIN1250')
# ── Krok 1: vložit soubory do ext DB ─────────────────────────────────────────
bookmark_list = []
bookmarks_body = ''
cislo = 9
poradi = 0
for s in SOUBORY:
cesta_souboru = os.path.join(CESTA, s['souborname'])
datumsouboru = datetime.datetime.fromtimestamp(os.path.getmtime(cesta_souboru))
print(f"\n>>> Zpracovávám: {s['souborname']}")
fileid = funkce_ext.zapis_file_ext(
vstupconnection=conn, idpac=IDPAC,
cesta=CESTA, souborname=s['souborname'],
prvnizavorka=s['prvnizavorka'],
soubordate=s['datum'], souborfiledate=datumsouboru,
poznamka=s['druhazavorka'],
)
print(f" → FILES.ID = {fileid}")
filenameforbookmark = (s['datum'].strftime('%Y-%m-%d') + ' '
+ s['prvnizavorka'] + ': ' + s['druhazavorka'])
bookmark_list.append('"' + filenameforbookmark + '","Files:' + str(fileid) + '",' + str(cislo))
cislo += 7
bookmarks_body += (r'\pard\s10{\*\bkmkstart ' + str(poradi) + r'}'
r'\plain\cs32\f0\ul\fs20\cf1 ' + filenameforbookmark
+ r'{\*\bkmkend ' + str(poradi) + r'}\par')
poradi += 1
# Tělo přílohy (záhlaví + odkaz řádky + prázdný řádek)
new_body = (r'\uc1\pard\s10\plain\cs20\f0\i\fs20 Vlo\'9een\'e9 p\'f8\'edlohy:\par' + '\n'
+ bookmarks_body + '\n'
+ r'\pard\s10\plain\cs15\f0\fs20 \par')
# ── Krok 2: existující dekurs pro tento den? ──────────────────────────────────
print(f"\n>>> Hledám poslední dekurs pro IDPAC={IDPAC}...")
existujici = najdi_posledni_dekurs_dnes(conn, IDPAC, DATUM)
cur = conn.cursor()
now = datetime.datetime.now()
if existujici:
dekurs_id, existing_rtf = existujici
print(f"\n>>> Nalezen existující dekurs pro {DATUM}: ID={dekurs_id}")
print(">>> Vkládám přílohy na začátek...")
merged_rtf = merge_rtf_prepend(existing_rtf, bookmark_list, new_body, len(SOUBORY))
print("\n=== Výsledný RTF ===")
print(merged_rtf)
cur.execute("UPDATE DEKURS SET DEKURS = ? WHERE ID = ?", (merged_rtf, dekurs_id))
conn.commit()
print(f"\n>>> UPDATE DEKURS ID={dekurs_id} hotovo!")
else:
print(f"\n>>> Žádný dekurs pro {DATUM} nenalezen vytvářím nový...")
bookmark_str = ';'.join(bookmark_list)
rtf = r"""{\rtf1\ansi\ansicpg1250\uc1\deff0\deflang1029{\info{\bookmarks BOOKMARKNAMES}}{\fonttbl{\f0\fnil\fcharset238 Arial;}{\f5\fnil\fcharset238 Symbol;}}
{\colortbl ;\red0\green0\blue255;\red0\green128\blue0;\red0\green0\blue0;}
{\stylesheet{\s10\fi0\li0\ql\ri0\sb0\sa0 Vlevo;}{\*\cs15\f0\fs20 Norm\'e1ln\'ed;}{\*\cs20\f0\i\fs20 Z\'e1hlav\'ed;}{\*\cs32\f0\ul\fs20\cf1 Odkaz;}}
BOOKMARKSTEXT
\pard\s10\plain\cs15\f0\fs20 \par
}"""
rtf = rtf.replace('BOOKMARKNAMES', bookmark_str)
rtf = rtf.replace('BOOKMARKSTEXT', new_body)
print("\n=== Výsledný RTF ===")
print(rtf)
dekursid = funkce.get_dekurs_id(conn)
cur.execute(
"INSERT INTO DEKURS (id, iduzi, idprac, idodd, idpac, datum, cas, dekurs)"
" VALUES (?,?,?,?,?,?,?,?)",
(dekursid, 6, 2, 2, IDPAC, now.date(), now.time(), rtf)
)
conn.commit()
print(f"\n>>> Nový DEKURS ID={dekursid}")
conn.close()
print("\n=== HOTOVO ===")
print("Otevři Medicus → karta Buzalka → zkontroluj dekurs!")

View File

@@ -0,0 +1,238 @@
"""test_import_single.py vloží 1 soubor do dekurzu s rozšířenou logikou:
1. Poslední dekurs pacienta je z dnešního dne A má sekci 'Vložené přílohy'
→ soubor se přidá DO té sekce (ne nová sekce)
2. Poslední dekurs je z dnešního dne, ale sekci 'Vložené přílohy' nemá
→ prepend nové sekce na začátek
3. Poslední dekurs je z jiného dne / neexistuje
→ nový dekurs pro dnešek
Spustit na Windows.
"""
import datetime, os, re, fdb
import funkce, funkce_ext
CESTA = r'u:\\'
IDPAC = 9742
DATUM = datetime.date(2026, 3, 18)
SOUBORY = [
{'souborname': '7309208104 2026-03-18 Buzalka, Vladimír [vyšetření] [ahoj Claude - zařazení].pdf',
'prvnizavorka': 'vyšetření',
'druhazavorka': 'ahoj Claude - zařazení',
'datum': DATUM},
]
# Vzor pro detekci sekce Vložené přílohy (RTF kódování win1250)
PRILOHY_HEADER = r"Vlo\'9een\'e9 p\'f8\'edlohy:"
PRILOHY_CLOSING = r'\pard\s10\plain\cs15\f0\fs20 \par'
# ─────────────────────────────────────────────────────────────────────────────
def najdi_posledni_dekurs_dnes(conn, idpac, datum_vlozeni):
"""Vrátí (id, rtf) posledního dekurzu pacienta pokud je z dnešního dne."""
cur = conn.cursor()
cur.execute("""
SELECT FIRST 1 ID, DATUM, DEKURS FROM DEKURS
WHERE IDPAC = ?
ORDER BY ID DESC
""", (idpac,))
row = cur.fetchone()
if row is None:
return None
dekurs_id, dekurs_datum, dekurs_rtf = row
print(f" Poslední dekurs: ID={dekurs_id}, datum={dekurs_datum}")
if dekurs_datum == datum_vlozeni:
print(f" → dnešní den ({datum_vlozeni}) ✓")
return (dekurs_id, dekurs_rtf)
else:
print(f" → jiný den ({dekurs_datum}{datum_vlozeni}), vytvoříme nový")
return None
def ma_sekci_prilohy(rtf):
return PRILOHY_HEADER in rtf
def pridat_do_sekce_prilohy(rtf, new_bkm_entry, filenameforbookmark):
"""Přidá soubor do EXISTUJÍCÍ sekce 'Vložené přílohy'.
Postup:
1. Spočítá počet Files: odkazů = N → nový index = N
2. Posune bkmkstart/bkmkend >= N o +1 (uvolní místo pro nový)
3. Vloží nový \pard před uzavírací prázdný řádek sekce
4. Vloží bookmark na pozici N do {\info{\bookmarks ...}}
"""
# 1. Počet existujících Files: odkazů v bookmarks listu
bkm_match = re.search(r'\{\\info\{\\bookmarks ([^}]*)\}\}', rtf)
if bkm_match:
bkm_entries = [e for e in bkm_match.group(1).split(';') if e.strip()]
n_files = sum(1 for e in bkm_entries if '"Files:' in e)
else:
bkm_entries = []
n_files = 0
new_idx = n_files
print(f" Počet existujících Files odkazů: {n_files} → nový bkmkstart={new_idx}")
# 2. Posunout bkmkstart/bkmkend >= n_files o +1
rtf = re.sub(r'\\bkmkstart (\d+)',
lambda m: '\\bkmkstart ' + (
str(int(m.group(1)) + 1) if int(m.group(1)) >= n_files
else m.group(1)),
rtf)
rtf = re.sub(r'\\bkmkend (\d+)',
lambda m: '\\bkmkend ' + (
str(int(m.group(1)) + 1) if int(m.group(1)) >= n_files
else m.group(1)),
rtf)
# 3. Najít uzavírající prázdný řádek sekce (první výskyt po hlavičce)
prilohy_pos = rtf.find(PRILOHY_HEADER)
closing_pos = rtf.find(PRILOHY_CLOSING, prilohy_pos)
if closing_pos == -1:
raise RuntimeError("Nenalezen uzavírací řádek sekce Vložené přílohy!")
new_pard = (r'\pard\s10{\*\bkmkstart ' + str(new_idx) + r'}'
r'\plain\cs32\f0\ul\fs20\cf1 ' + filenameforbookmark
+ r'{\*\bkmkend ' + str(new_idx) + r'}\par')
rtf = rtf[:closing_pos] + new_pard + '\n' + rtf[closing_pos:]
# 4. Vložit bookmark na pozici n_files do {\info{\bookmarks}}
def insert_bookmark(m):
entries = [e for e in m.group(1).split(';') if e.strip()]
entries.insert(n_files, new_bkm_entry)
return '{\\info{\\bookmarks ' + ';'.join(entries) + '}}'
rtf = re.sub(r'\{\\info\{\\bookmarks ([^}]*)\}\}', insert_bookmark, rtf)
return rtf
def merge_rtf_prepend(existing_rtf, new_bkm_list, new_body_pards, n_new):
"""Vloží nový obsah na ZAČÁTEK stávajícího dekurzu (žádná existující sekce)."""
rtf = existing_rtf
rtf = re.sub(r'\\bkmkstart (\d+)',
lambda m: '\\bkmkstart ' + str(int(m.group(1)) + n_new), rtf)
rtf = re.sub(r'\\bkmkend (\d+)',
lambda m: '\\bkmkend ' + str(int(m.group(1)) + n_new), rtf)
new_bkm_str = ';'.join(new_bkm_list)
def merge_bkm(m):
existing = m.group(1).strip()
combined = new_bkm_str + (';' + existing if existing else '')
return '{\\info{\\bookmarks ' + combined + '}}'
if re.search(r'\{\\info\{\\bookmarks', rtf):
rtf = re.sub(r'\{\\info\{\\bookmarks ([^}]*)\}\}', merge_bkm, rtf)
else:
rtf = re.sub(r'(\\deflang\d+)',
r'\1{\\info{\\bookmarks ' + new_bkm_str + '}}', rtf, count=1)
match = re.search(r'\\uc1\\pard', rtf)
if match:
pos = match.start()
rtf = rtf[:pos] + new_body_pards + '\n' + rtf[pos:]
return rtf
# ─────────────────────────────────────────────────────────────────────────────
conn = fdb.connect(dsn=r'localhost:c:\medicus 3\data\medicus.fdb',
user='SYSDBA', password='masterkey', charset='WIN1250')
# ── Krok 1: vložit soubory do ext DB ─────────────────────────────────────────
bookmark_list = []
bookmarks_body = ''
cislo = 9
poradi = 0
for s in SOUBORY:
cesta_souboru = os.path.join(CESTA, s['souborname'])
datumsouboru = datetime.datetime.fromtimestamp(os.path.getmtime(cesta_souboru))
print(f"\n>>> Zpracovávám: {s['souborname']}")
fileid = funkce_ext.zapis_file_ext(
vstupconnection=conn, idpac=IDPAC,
cesta=CESTA, souborname=s['souborname'],
prvnizavorka=s['prvnizavorka'],
soubordate=s['datum'], souborfiledate=datumsouboru,
poznamka=s['druhazavorka'],
)
print(f" → FILES.ID = {fileid}")
filenameforbookmark = (s['datum'].strftime('%Y-%m-%d') + ' '
+ s['prvnizavorka'] + ': ' + s['druhazavorka'])
bookmark_list.append('"' + filenameforbookmark + '","Files:' + str(fileid) + '",' + str(cislo))
cislo += 7
bookmarks_body += (r'\pard\s10{\*\bkmkstart ' + str(poradi) + r'}'
r'\plain\cs32\f0\ul\fs20\cf1 ' + filenameforbookmark
+ r'{\*\bkmkend ' + str(poradi) + r'}\par')
poradi += 1
new_body = (r'\uc1\pard\s10\plain\cs20\f0\i\fs20 Vlo\'9een\'e9 p\'f8\'edlohy:\par' + '\n'
+ bookmarks_body + '\n'
+ r'\pard\s10\plain\cs15\f0\fs20 \par')
# ── Krok 2: rozhodovací logika ────────────────────────────────────────────────
print(f"\n>>> Hledám poslední dekurs pro IDPAC={IDPAC}...")
existujici = najdi_posledni_dekurs_dnes(conn, IDPAC, DATUM)
cur = conn.cursor()
now = datetime.datetime.now()
if existujici:
dekurs_id, existing_rtf = existujici
if ma_sekci_prilohy(existing_rtf):
# ── Případ 1: dnešní dekurs s existující sekcí → přidáme do ní ──────
print(f"\n>>> Sekce 'Vložené přílohy' nalezena v DEKURS ID={dekurs_id}")
print(">>> Přidávám soubor DO existující sekce...")
filenameforbookmark = (SOUBORY[0]['datum'].strftime('%Y-%m-%d') + ' '
+ SOUBORY[0]['prvnizavorka'] + ': ' + SOUBORY[0]['druhazavorka'])
merged_rtf = pridat_do_sekce_prilohy(
existing_rtf,
bookmark_list[0],
filenameforbookmark
)
else:
# ── Případ 2: dnešní dekurs bez sekce → prepend ───────────────────────
print(f"\n>>> DEKURS ID={dekurs_id} nemá sekci příloh → prepend nové sekce")
merged_rtf = merge_rtf_prepend(existing_rtf, bookmark_list, new_body, len(SOUBORY))
print("\n=== Výsledný RTF ===")
print(merged_rtf)
cur.execute("UPDATE DEKURS SET DEKURS = ? WHERE ID = ?", (merged_rtf, dekurs_id))
conn.commit()
print(f"\n>>> UPDATE DEKURS ID={dekurs_id} hotovo!")
else:
# ── Případ 3: žádný dnešní dekurs → nový ─────────────────────────────────
print(f"\n>>> Žádný dekurs pro {DATUM} → vytvářím nový...")
bookmark_str = ';'.join(bookmark_list)
rtf = r"""{\rtf1\ansi\ansicpg1250\uc1\deff0\deflang1029{\info{\bookmarks BOOKMARKNAMES}}{\fonttbl{\f0\fnil\fcharset238 Arial;}{\f5\fnil\fcharset238 Symbol;}}
{\colortbl ;\red0\green0\blue255;\red0\green128\blue0;\red0\green0\blue0;}
{\stylesheet{\s10\fi0\li0\ql\ri0\sb0\sa0 Vlevo;}{\*\cs15\f0\fs20 Norm\'e1ln\'ed;}{\*\cs20\f0\i\fs20 Z\'e1hlav\'ed;}{\*\cs32\f0\ul\fs20\cf1 Odkaz;}}
BOOKMARKSTEXT
\pard\s10\plain\cs15\f0\fs20 \par
}"""
rtf = rtf.replace('BOOKMARKNAMES', bookmark_str)
rtf = rtf.replace('BOOKMARKSTEXT', new_body)
print("\n=== Výsledný RTF ===")
print(rtf)
dekursid = funkce.get_dekurs_id(conn)
cur.execute(
"INSERT INTO DEKURS (id, iduzi, idprac, idodd, idpac, datum, cas, dekurs)"
" VALUES (?,?,?,?,?,?,?,?)",
(dekursid, 6, 2, 2, IDPAC, now.date(), now.time(), rtf)
)
conn.commit()
print(f"\n>>> Nový DEKURS ID={dekursid}")
conn.close()
print("\n=== HOTOVO ===")
print("Otevři Medicus → karta Buzalka → zkontroluj dekurs!")