notebook vb

This commit is contained in:
2026-03-20 18:03:44 +01:00
parent f2dc33a05e
commit 1d9c6cca48
10 changed files with 3312 additions and 2923 deletions

View File

@@ -0,0 +1,81 @@
# MedicusWithClaudeSelects SQL dotazy
## Registrovaní pacienti
Přesný select který Medicus používá pro záložku **Registrovaní** (zachycen přes FBScanner, dotaz č. 143).
### Počet registrovaných pacientů
```sql
SELECT COUNT(*) FROM KAR
WHERE (vyrazen = 'N')
AND EXISTS (
SELECT id FROM registr r
JOIN icp i ON r.idicp = i.idicp
WHERE r.idpac = kar.idpac
AND (r.datum <= '2026-03-20')
AND (r.datum_zruseni IS NULL OR r.datum_zruseni >= '2026-03-20')
AND (r.priznak IN ('V','D','A'))
AND (i.icp = '09305001')
AND (i.odb = '001')
)
```
Vrátí: **1618 pacientů** (ověřit na Windows).
### Podmínky registrace vysvětlení
- `vyrazen = 'N'` pacient není vyřazen z kartotéky
- `r.datum <= dnes` registrace již začala
- `r.datum_zruseni IS NULL OR r.datum_zruseni >= dnes` registrace dosud platí
- `r.priznak IN ('V','D','A')` aktivní příznak (ne 'Z' = zrušen, ne 'N')
- `i.icp = '09305001'` IČP naší ordinace
- `i.odb = '001'` odbornost praktický lékař
### Skript pro Python
Viz `count_registrovani.py` v této složce spustit na Windows.
### Plný select Medicusu (seznam pacientů s metadaty)
```sql
SELECT
KAR.DATNAR,
KAR.IDPAC,
KAR.INFORMACE,
KAR.INFORMACE_COL,
KAR.JMENO,
KAR.POHLAVI,
GPP.POJ,
KAR.POZNAMKA,
KAR.PRIJMENI,
KAR.PRIJMENI_UP,
(SELECT DATUM_REGISTRACE FROM SP_GETREGDAT(kar.IDPAC)) AS REGDATUM,
KAR.REGISTROVAL,
(SELECT PRIZNAK FROM SP_GETREGDAT(kar.IDPAC)) AS REGPRIZNAK,
KAR.RODCIS,
KAR.ROZENA,
KAR.TITUL,
KAR.TITULZA,
KAR.TRVOBEC,
KAR.TRVPSC,
KAR.TRVULICE,
KAR.VYRAZEN
FROM KAR
LEFT JOIN GETPACPOJ(KAR.IDPAC, '2026-03-20') GPP ON GPP.IDPAC = KAR.IDPAC
WHERE (vyrazen = 'N')
AND EXISTS (
SELECT id FROM registr r
JOIN icp i ON r.idicp = i.idicp
WHERE r.idpac = kar.idpac
AND (r.datum <= '2026-03-20')
AND (r.datum_zruseni IS NULL OR r.datum_zruseni >= '2026-03-20')
AND (r.priznak IN ('V','D','A'))
AND (i.icp = '09305001')
AND (i.odb = '001')
)
ORDER BY KAR.PRIJMENI_UP ASC, KAR.RODCIS ASC
```
Poznámka: `GETPACPOJ` a `SP_GETREGDAT` jsou uložené procedury Medicusu
fungují v kontextu Firebird připojení přes SYSDBA/masterkey.

View File

@@ -0,0 +1,27 @@
import fdb, datetime
conn = fdb.connect(
dsn=r'localhost:c:\medicus 3\data\medicus.fdb',
user='SYSDBA', password='masterkey', charset='win1250')
cur = conn.cursor()
dnes = datetime.date.today().isoformat()
cur.execute("""
SELECT COUNT(*) FROM KAR
WHERE (vyrazen = 'N')
AND EXISTS (
SELECT id FROM registr r
JOIN icp i ON r.idicp = i.idicp
WHERE r.idpac = kar.idpac
AND (r.datum <= ?)
AND (r.datum_zruseni IS NULL OR r.datum_zruseni >= ?)
AND (r.priznak IN ('V','D','A'))
AND (i.icp = '09305001')
AND (i.odb = '001')
)
""", (dnes, dnes))
pocet = cur.fetchone()[0]
print(f'Registrovaných pacientů: {pocet}')
conn.close()

View File

@@ -0,0 +1,78 @@
"""db_bridge_vm.py VM strana souborového mostu k Medicusu.
Použití z Linuxu:
from db_bridge_vm import query
rows, columns = query("SELECT COUNT(*) FROM KAR")
print(columns, rows)
"""
import json, time, os, uuid
BRIDGE_DIR = os.path.dirname(os.path.abspath(__file__))
REQUEST = os.path.join(BRIDGE_DIR, 'query_request.json')
RESPONSE = os.path.join(BRIDGE_DIR, 'query_response.json')
TIMEOUT_SEC = 30
POLL_SEC = 0.3
def query(sql, params=None, timeout=TIMEOUT_SEC):
"""Pošle SQL dotaz přes souborový most a vrátí (rows, columns).
Raises:
TimeoutError watchdog neodpověděl do timeout sekund
RuntimeError Firebird vrátil chybu
"""
# Smaž případnou starou response
if os.path.exists(RESPONSE):
os.remove(RESPONSE)
req_id = uuid.uuid4().hex
req = {'id': req_id, 'sql': sql, 'params': params or []}
with open(REQUEST, 'w', encoding='utf-8') as f:
json.dump(req, f, ensure_ascii=False)
# Čekej na odpověď
waited = 0.0
while waited < timeout:
time.sleep(POLL_SEC)
waited += POLL_SEC
if os.path.exists(RESPONSE):
with open(RESPONSE, 'r', encoding='utf-8') as f:
resp = json.load(f)
os.remove(RESPONSE)
if resp.get('status') == 'error':
raise RuntimeError(f"DB chyba: {resp.get('error')}")
return resp.get('rows', []), resp.get('columns', [])
# Timeout smaž request aby watchdog nezpracoval zastaralý dotaz
if os.path.exists(REQUEST):
os.remove(REQUEST)
raise TimeoutError(f'Watchdog neodpověděl do {timeout}s běží db_bridge_windows.py?')
def query_print(sql, params=None):
"""Spustí dotaz a vypíše výsledek přehledně."""
rows, cols = query(sql, params)
if not cols:
print('(žádné sloupce)')
return rows, cols
col_w = [max(len(str(c)), max((len(str(r[i])) for r in rows), default=0))
for i, c in enumerate(cols)]
sep = '+' + '+'.join('-' * (w + 2) for w in col_w) + '+'
fmt = '|' + '|'.join(f' {{:<{w}}} ' for w in col_w) + '|'
print(sep)
print(fmt.format(*cols))
print(sep)
for row in rows:
print(fmt.format(*[str(v) if v is not None else 'NULL' for v in row]))
print(sep)
print(f'{len(rows)} řádků')
return rows, cols
if __name__ == '__main__':
# Rychlý test
print('Testuji spojení...')
rows, cols = query('SELECT COUNT(*) AS POCET FROM KAR')
print(f'OK pacientů v KAR: {rows[0][0]}')

View File

@@ -0,0 +1,90 @@
"""db_bridge_windows.py Windows watchdog pro dotazy z Linux VM.
Spusť jednou na Windows:
python db_bridge_windows.py
Skript sleduje soubor query_request.json ve stejné složce.
Jakmile ho najde, spustí SQL dotaz proti Medicusu a zapíše výsledek
do query_response.json. Pak čeká na další dotaz.
Ukonči: Ctrl+C
"""
import fdb, json, time, os, traceback, datetime
# ── Konfigurace ───────────────────────────────────────────────────────────────
DSN = r'localhost:c:\medicus 3\data\medicus.fdb'
USER = 'SYSDBA'
PASSWORD = 'masterkey'
CHARSET = 'win1250'
BRIDGE_DIR = os.path.dirname(os.path.abspath(__file__))
REQUEST = os.path.join(BRIDGE_DIR, 'query_request.json')
RESPONSE = os.path.join(BRIDGE_DIR, 'query_response.json')
POLL_SEC = 0.5
# ── Pomocné funkce ────────────────────────────────────────────────────────────
def serialize(val):
"""Převede Python hodnoty na JSON-serializovatelné typy."""
if isinstance(val, (datetime.date, datetime.datetime)):
return val.isoformat()
if isinstance(val, datetime.time):
return val.isoformat()
if isinstance(val, bytes):
return f'<bytes len={len(val)}>'
return val
def run_query(sql, params=None):
conn = fdb.connect(dsn=DSN, user=USER, password=PASSWORD, charset=CHARSET)
try:
cur = conn.cursor()
cur.execute(sql, params or [])
columns = [d[0] for d in cur.description] if cur.description else []
rows = [[serialize(v) for v in row] for row in cur.fetchall()]
return {'status': 'ok', 'columns': columns, 'rows': rows, 'error': None}
except Exception as e:
return {'status': 'error', 'columns': [], 'rows': [], 'error': str(e)}
finally:
conn.close()
# ── Hlavní smyčka ─────────────────────────────────────────────────────────────
print(f'DB Bridge spuštěn. Sleduju: {REQUEST}')
print('Ukončení: Ctrl+C\n')
while True:
try:
if os.path.exists(REQUEST):
print(f'[{datetime.datetime.now().strftime("%H:%M:%S")}] Přijat dotaz...')
with open(REQUEST, 'r', encoding='utf-8') as f:
req = json.load(f)
os.remove(REQUEST)
sql = req.get('sql', '')
params = req.get('params', [])
req_id = req.get('id', '')
result = run_query(sql, params)
result['id'] = req_id
result['sql'] = sql
with open(RESPONSE, 'w', encoding='utf-8') as f:
json.dump(result, f, ensure_ascii=False, indent=2)
if result['status'] == 'ok':
print(f' → OK, {len(result["rows"])} řádků')
else:
print(f' → CHYBA: {result["error"]}')
time.sleep(POLL_SEC)
except KeyboardInterrupt:
print('\nDB Bridge ukončen.')
break
except Exception as e:
print(f'Neočekávaná chyba: {e}')
traceback.print_exc()
time.sleep(2)

View File

@@ -0,0 +1,30 @@
"""get_kar_sortby_idlist.py přečte definici stored procedure KAR_SORTBY_IDLIST z Firebirdu.
Spustit 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()
cur.execute("""
SELECT RDB$PROCEDURE_SOURCE FROM RDB$PROCEDURES
WHERE RDB$PROCEDURE_NAME = 'KAR_SORTBY_IDLIST'
""")
row = cur.fetchone()
if row and row[0]:
print(row[0])
else:
# Možná je to funkce (FUNCTION), ne procedure
cur.execute("""
SELECT RDB$FUNCTION_SOURCE FROM RDB$FUNCTIONS
WHERE RDB$FUNCTION_NAME = 'KAR_SORTBY_IDLIST'
""")
row = cur.fetchone()
if row and row[0]:
print(row[0])
else:
print("Nenalezeno ani jako PROCEDURE ani jako FUNCTION.")
conn.close()

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,74 @@
{
"status": "ok",
"columns": [
"ID",
"DATUM",
"CAS",
"UZIVATEL",
"IDPAC",
"TABULKA",
"IDREC",
"AKCE",
"DETAIL"
],
"rows": [
[
2882053,
"2026-03-20",
"17:43:50.523000",
"VBU ",
3234,
10000,
3234,
"V",
null
],
[
2882052,
"2026-03-20",
"17:43:46.435000",
"VBU ",
3234,
10000,
3234,
"V",
null
],
[
2882051,
"2026-03-20",
"17:24:19.777000",
"VBU ",
4757,
10000,
4757,
"V",
null
],
[
2882050,
"2026-03-20",
"17:24:15.866000",
"VBU ",
3234,
10000,
3234,
"V",
null
],
[
2882049,
"2026-03-20",
"07:11:41.434000",
"VBU ",
4757,
10000,
4757,
"V",
null
]
],
"error": null,
"id": "11859d529a784bedb653030ba60131de",
"sql": "SELECT FIRST 5 * FROM LOG ORDER BY ID DESC"
}