notebookvb

This commit is contained in:
Vladimir Buzalka
2026-04-29 06:51:47 +02:00
parent a1b9c93506
commit a9c143ba24
141 changed files with 30711 additions and 0 deletions
@@ -0,0 +1,143 @@
# MedicusWithClaudePosudek poznámky pro Clauda
## O co jde
Lékařské posudky vystavované MUDr. Buzalkovou. Prozatím řešíme posudky k řízení motorových vozidel.
Nový zákon ukládá povinnost odesílat posudky k řízení do **centrálního registru** tuto funkci Medicus přidal v aktualizaci z konce března 2026.
---
## Tabulky
### HISTDOC hlavní tabulka pro všechny posudky
Všechny posudky jsou záznamy v `HISTDOC`, lišící se hodnotou sloupce `TYP`.
Klíčové sloupce:
| Sloupec | Popis |
|---|---|
| `ID` | primární klíč |
| `TYP` | typ dokumentu (viz níže) |
| `DATUM` | datum vystavení posudku |
| `IDPACI` | FK → KAR.IDPAC (pacient) |
| `DATA` | obsah posudku text ve formátu key=value (viz níže) |
| `PORCISLO` | pořadové číslo posudku (= PorCislo v DATA) |
| `STAV` | stav záznamu (Z = zavřeno) |
| `PRINTED` | T/F byl vytištěn |
| `IDUZIV` | FK → UZIVATEL.IDUZI kdo vystavil (4 = MUDr. Buzalková) |
| `CREATED` | timestamp vytvoření záznamu |
**Vazba:** žádná přímá vazba na jiné tabulky (vyšetření, dekurz apod.) posudek je svébytný dokument.
### TYP hodnoty relevantní pro posudky řidičů
| TYP | Popis | Počet (k 2026-03-31) |
|---|---|---|
| `MOTORVO` | ruční posudek k řízení motorových vozidel | 1530 |
| `EPOSMRO` | elektronické podání posudku do centrálního registru | 2 |
Ostatní typy posudků v HISTDOC (pro referenci):
- `ZBROJPR`, `ZBROJP2` zbrojní průkaz
- `ZPUPRN` způsobilost pro práci
- `ZDRSTA3``ZDRSTA5`, `ZDRSTAV`, `ZDRINF` zdravotní stav (různé varianty)
- ... (celkem desítky typů)
### HISTDOC_EPOSUDEK evidence odeslání do registru
Doplňková tabulka k EPOSMRO záznamům v HISTDOC.
| Sloupec | Popis |
|---|---|
| `ID_HISTDOC` | FK → HISTDOC.ID (záznam EPOSMRO) |
| `ID_PODANI` | UUID přidělené centrálním registrem |
| `ODESLANO` | timestamp odeslání |
| `STATUS` | O = odesláno |
| `VERZE` | verze záznamu (base64 interní hodnota) |
### VS_POSUDKY prázdná, zatím nepoužívaná
Sloupce: ID, IDPAC, DATA (BLOB), DATUM, POSTYPE. Pravděpodobně připravena pro budoucí využití.
---
## Workflow: ruční posudek → elektronické podání
1. Lékař v Medicusu vyplní posudek → vznikne `HISTDOC` TYP=`MOTORVO`
2. Medicus automaticky odešle do centrálního registru → vznikne `HISTDOC` TYP=`EPOSMRO` + záznam v `HISTDOC_EPOSUDEK`
3. Oba záznamy mají stejné `IDPACI` + `DATUM` → podle toho je párujeme
Příklad (pacient Vráček, 30.3.2026):
- HISTDOC ID=34743, TYP=MOTORVO, CREATED=13:12
- HISTDOC ID=34746, TYP=EPOSMRO, CREATED=13:21
- HISTDOC_EPOSUDEK: STATUS=O, ODESLANO=13:21
---
## Formát DATA (key=value) MOTORVO
```
JmenoPac=Radomil Vráček
DatNar=D:27.03.1956
Prukaz=207069669 ← číslo řidičského průkazu
DatKonec=D:30.03.2028 ← platnost posudku do
DatumVyd=D:30.03.2026 ← datum vydání
Bydliste=K Šafránce 507/16, 19000 Praha 9-Střížkov
DruhProh=periodická ← druh prohlídky
Posouzeni=T ← T = způsobilý (F = nezpůsobilý?)
Posouzeni2=F ← T = nezpůsobilý (druhá volba)
ZpusobJe=B:0 ← skupiny bez podmínky
ZpusobPodminka=B:1 ← B:1 = má podmínku
SkupinaPodminka=sk. B brýle
PorCislo=2600037
KonecDleZakona=D
DatumPrevzeti=D:30.03.2026
```
**Výsledek posouzení** (kombinace Posouzeni + Posouzeni2 + ZpusobPodminka):
- `Posouzeni=T` + `Posouzeni2=F` + `ZpusobPodminka=B:0` → způsobilý
- `Posouzeni=T` + `Posouzeni2=F` + `ZpusobPodminka=B:1` → způsobilý s podmínkou
- `Posouzeni=T` + `Posouzeni2=T` → nezpůsobilý
## Formát DATA (key=value) EPOSMRO
```
Lekar=MUDr. Michaela Buzalková
KRZPID=130153584 ← ID lékaře v registru
ICO=68366370
ICP=09305001
Pacient=Radomil Vráček
RID=8705636888 ← číslo řidičáku
DatumNarozeni=D:27.03.1956
StavPosudkuKodVerze=zneplatneny|1.0.0
StavPosudkuNazev=Zneplatněný ← stav posudku v registru
TypAkceNazev=vytvoření
TypAkceKodVerze=akce_ro_1|1.0.0
DruhProhlidkyNazev=pravidelná
DruhProhlidkyKodVerze=Pravidelna|1.0.0
DruhPosudkuNazev=řidičské oprávnění pro seniory
DruhPosudkuKodVerze=SenioriRo|1.0.0
SkupinaZadatelRidicNazev=skupina 1
SkupinyRidicskehoOpravneniSeznam=B
HarmonizovaneNarodniKody=$:~HNK1:011:01.01 Brýle5:01.012:HK1:B0: ← kódy omezení (brýle)
VysledekKodVerze=ZpusobilySPodminkou|1.0.0
VysledekNazev=způsobilý s podmínkou
DatumVystaveni=D:30.03.2026
PlatnostDo=D:30.03.2028
```
**StavPosudku = "Zneplatněný"** neznamená chybu jde o akci, kdy lékař odvolá způsobilost pacienta (např. po mrtvici, epileptickém záchvatu apod.). Medicus pak odešle do registru zneplatnění existujícího posudku.
---
## Soubory v projektu
- `posudky_report.py` generuje Excel s listy MOTORVO a EPOSMRO
- `CLAUDE_NOTES.md` tento soubor
## Report (posudky_report.py)
- Výstup: `u:\Dropbox\!!!Days\Downloads Z230\YYYY-MM-DD_HH-MM-SS_Přehled posudků řidičák.xlsx`
- Maže předchozí verzi před zápisem nové
- List MOTORVO: 1530 záznamů, sloupec `ePosudek` = ANO (zeleně) / NE podle toho, zda byl odeslán ePosudek (párování IDPACI + DATUM)
- List EPOSMRO: 2 záznamy, detail elektronického podání
@@ -0,0 +1,237 @@
import fdb
import openpyxl
from openpyxl.styles import Font, PatternFill, Alignment
from openpyxl.utils import get_column_letter
from datetime import datetime
import os
import sys
# --- Připojení ---
conn = fdb.connect(
dsn=r'localhost:c:\medicus 3\data\medicus.fdb',
user='SYSDBA', password='masterkey', charset='win1250'
)
cur = conn.cursor()
# --- Výstupní soubor ---
output_dir = r'u:\Dropbox\!!!Days\Downloads Z230'
now = datetime.now()
filename = now.strftime('%Y-%m-%d_%H-%M-%S') + '_Přehled posudků řidičák.xlsx'
output_path = os.path.join(output_dir, filename)
# --- Smazání předchozích verzí ---
for f in os.listdir(output_dir):
if f.endswith('_Přehled posudků řidičák.xlsx'):
os.remove(os.path.join(output_dir, f))
wb = openpyxl.Workbook()
# =====================
# Pomocné funkce
# =====================
HEADER_FILL = PatternFill('solid', fgColor='2F5496')
HEADER_FONT = Font(bold=True, color='FFFFFF')
ZEBRA_FILL = PatternFill('solid', fgColor='DCE6F1')
GREEN_FILL = PatternFill('solid', fgColor='C6EFCE')
GREEN_FONT = Font(bold=True, color='276221')
def style_header(ws):
for cell in ws[1]:
cell.fill = HEADER_FILL
cell.font = HEADER_FONT
cell.alignment = Alignment(horizontal='center')
def autofit(ws):
for col in ws.columns:
max_len = max((len(str(cell.value)) if cell.value is not None else 0) for cell in col)
ws.column_dimensions[get_column_letter(col[0].column)].width = min(max_len + 2, 50)
def fmt(val):
if val is None:
return ''
return val
def parse_data(data_str):
"""Parsuje key=value text z HISTDOC.DATA do slovníku."""
result = {}
if not data_str:
return result
for line in data_str.splitlines():
if '=' in line:
key, _, val = line.partition('=')
result[key.strip()] = val.strip()
return result
def parse_date(val):
"""Převede 'D:DD.MM.YYYY' na datetime.date, nebo vrátí původní hodnotu."""
if val and val.startswith('D:'):
try:
return datetime.strptime(val[2:], '%d.%m.%Y').date()
except ValueError:
return val
return val
# =====================
# List 1 MOTORVO (ruční posudky k řízení)
# =====================
ws1 = wb.active
ws1.title = 'MOTORVO'
# Množina (IDPACI, DATUM) kde existuje EPOSMRO
cur.execute("""
SELECT IDPACI, DATUM FROM HISTDOC WHERE TYP = 'EPOSMRO'
""")
eposmro_keys = set((r[0], r[1]) for r in cur.fetchall())
cur.execute("""
SELECT h.ID, h.DATUM, h.IDPACI,
k.PRIJMENI, k.JMENO, k.RODCIS,
h.DATA, h.PORCISLO, h.STAV, h.PRINTED, h.IDUZIV, h.CREATED
FROM HISTDOC h
JOIN KAR k ON k.IDPAC = h.IDPACI
WHERE h.TYP = 'MOTORVO'
ORDER BY h.ID DESC
""")
raw_rows = cur.fetchall()
headers = [
'ID', 'DATUM', 'IDPACI', 'PRIJMENI', 'JMENO', 'RODCIS',
'PorCislo', 'DatumVyd', 'DatKonec', 'DruhProh',
'Posouzeni', 'ZpusobPodminka', 'SkupinaPodminka',
'Skupiny',
'ePosudek',
'STAV', 'PRINTED', 'IDUZIV', 'CREATED'
]
ws1.append(headers)
for i, row in enumerate(raw_rows, start=2):
(hid, datum, idpac, prijmeni, jmeno, rodcis,
data_blob, porcislo, stav, printed, iduziv, created) = row
data = parse_data(data_blob)
posouzeni = ''
if data.get('Posouzeni') == 'T':
if data.get('Posouzeni2') == 'T':
posouzeni = 'nezpůsobilý'
elif data.get('ZpusobPodminka') == 'B:1':
posouzeni = 'způsobilý s podmínkou'
else:
posouzeni = 'způsobilý'
skupiny = data.get('SkupinyRidicskehoOpravneniSeznam', '')
if not skupiny:
# MOTORVO nemá SkupinyRidicskehoOpravneniSeznam, zkusíme ZpusobJe
skupiny = data.get('ZpusobJe', '')
ws1.append([
hid,
fmt(datum),
idpac,
fmt(prijmeni),
fmt(jmeno),
fmt(rodcis),
fmt(porcislo or data.get('PorCislo', '')),
parse_date(data.get('DatumVyd', '')),
parse_date(data.get('DatKonec', '')),
fmt(data.get('DruhProh', '')),
posouzeni,
fmt(data.get('ZpusobPodminka', '')),
fmt(data.get('SkupinaPodminka', '')),
fmt(skupiny),
'ANO' if (idpac, datum) in eposmro_keys else 'NE',
fmt(stav),
fmt(printed),
fmt(iduziv),
fmt(created),
])
if i % 2 == 0:
for cell in ws1[i]:
cell.fill = ZEBRA_FILL
# Sloupec ePosudek zvýraznit ANO zeleně
epos_col = headers.index('ePosudek') + 1
cell = ws1.cell(row=i, column=epos_col)
if cell.value == 'ANO':
cell.fill = GREEN_FILL
cell.font = GREEN_FONT
style_header(ws1)
ws1.freeze_panes = 'A2'
autofit(ws1)
# =====================
# List 2 EPOSMRO (elektronická podání do registru)
# =====================
ws2 = wb.create_sheet('EPOSMRO')
cur.execute("""
SELECT h.ID, h.DATUM, h.IDPACI,
k.PRIJMENI, k.JMENO, k.RODCIS,
h.DATA, h.STAV, h.CREATED,
e.ID_PODANI, e.ODESLANO, e.STATUS
FROM HISTDOC h
JOIN KAR k ON k.IDPAC = h.IDPACI
LEFT JOIN HISTDOC_EPOSUDEK e ON e.ID_HISTDOC = h.ID
WHERE h.TYP = 'EPOSMRO'
ORDER BY h.ID DESC
""")
epos_rows = cur.fetchall()
headers2 = [
'ID', 'DATUM', 'IDPACI', 'PRIJMENI', 'JMENO', 'RODCIS',
'DatumVyd', 'DatKonec', 'DruhProhlidky', 'DruhPosudku',
'Vysledek', 'StavPosudku', 'TypAkce',
'STAV', 'CREATED',
'ID_PODANI', 'ODESLANO', 'STATUS_ODESL'
]
ws2.append(headers2)
for i, row in enumerate(epos_rows, start=2):
(hid, datum, idpac, prijmeni, jmeno, rodcis,
data_blob, stav, created,
id_podani, odeslano, status_odesl) = row
data = parse_data(data_blob)
ws2.append([
hid,
fmt(datum),
idpac,
fmt(prijmeni),
fmt(jmeno),
fmt(rodcis),
parse_date(data.get('DatumVystaveni', '')),
parse_date(data.get('PlatnostDo', '')),
fmt(data.get('DruhProhlidkyNazev', '')),
fmt(data.get('DruhPosudkuNazev', '')),
fmt(data.get('VysledekNazev', '')),
fmt(data.get('StavPosudkuNazev', '')),
fmt(data.get('TypAkceNazev', '')),
fmt(stav),
fmt(created),
fmt(id_podani),
fmt(odeslano),
fmt(status_odesl),
])
if i % 2 == 0:
for cell in ws2[i]:
cell.fill = ZEBRA_FILL
style_header(ws2)
ws2.freeze_panes = 'A2'
autofit(ws2)
# =====================
# Uložení
# =====================
conn.close()
wb.save(output_path)
sys.stdout.buffer.write(f'Ulozeno: {output_path}\n'.encode('utf-8'))
sys.stdout.buffer.write(f'MOTORVO: {len(raw_rows)} radku, EPOSMRO: {len(epos_rows)} radku\n'.encode('utf-8'))