Files
ordinaceprojekt/Knihovny/medicus_db.py
T
Vladimir Buzalka 79216dfbdb notebookvb
2026-06-15 18:07:32 +02:00

187 lines
6.0 KiB
Python

# medicus_db.py — aktuální k 2026-05-26
# Připojení k Firebird databázi Medicus (medicus.fdb). Volí DSN podle názvu počítače.
# Obsahuje třídu MedicusDB s metodami pro dotazy na pacienty, registrace a faktury.
import os
import socket
import fdb
def get_medicus_connection():
"""
Připojí se k Firebird medicus.fdb. DSN se vybere takto:
1) env MEDICUS_FDB_DSN (má přednost — nutné v dockeru, kde hostname = ID kontejneru),
2) podle názvu počítače (dsn_map),
3) default.
Vrátí fdb.Connection nebo vyhodí RuntimeError.
"""
computer_name = socket.gethostname().upper()
dsn_map = {
"LEKAR": r"localhost:M:\medicus\data\medicus.fdb",
"SESTRA": r"192.168.1.10:m:\medicus\data\medicus.fdb",
"LENOVO": r"192.168.1.10:m:\medicus\data\medicus.fdb",
"NTBVBHP470G10": r"192.168.1.76:/firebird/data/medicus.fdb", # přepnuto z reporteru na tower 2026-06-14
"Z230": r"192.168.1.76:/firebird/data/medicus.fdb", # přepnuto z reporteru na tower 2026-06-14
"TOWER": r"192.168.1.76:/firebird/data/medicus.fdb", # Firebird 2.5 docker kontejner na toweru
}
dsn = (os.environ.get("MEDICUS_FDB_DSN")
or dsn_map.get(computer_name, r"localhost:c:\medicus 3\data\medicus.fdb"))
import sys
print(f"[medicus_db] Pripojuji se jako {computer_name} -> {dsn}", file=sys.stderr, flush=True)
return fdb.connect(dsn=dsn, user="SYSDBA", password="masterkey", charset="win1250")
class ReconnectingConnection:
"""Obal nad fdb.Connection, který se sám znovu připojí, když spojení umře.
Dlouho běžící procesy (např. MCP server) drží jedno spojení od startu.
To se rozpadne, když:
* se notebook uspí/hibernuje (TCP socket odumře), nebo
* na serveru proběhne denní gbak restore (Firebird zabije stará spojení).
Výsledkem je `SQLCODE -902 Error writing data to the connection`.
Tato třída ověří před každým použitím, že spojení žije, a když ne,
tiše ho znovu naváže. Volající kód používá .cursor()/.commit() beze změny.
"""
def __init__(self, connect_fn=get_medicus_connection):
self._connect = connect_fn
self._conn = None
def _alive(self):
if self._conn is None:
return False
try:
cur = self._conn.cursor()
cur.execute("SELECT 1 FROM RDB$DATABASE")
cur.fetchone()
return True
except Exception:
return False
def _ensure(self):
if not self._alive():
if self._conn is not None:
try:
self._conn.close()
except Exception:
pass
self._conn = None
self._conn = self._connect()
return self._conn
def cursor(self):
return self._ensure().cursor()
def commit(self):
return self._ensure().commit()
def rollback(self):
if self._conn is not None:
return self._conn.rollback()
def close(self):
if self._conn is not None:
try:
self._conn.close()
finally:
self._conn = None
def __getattr__(self, name):
# ostatní atributy/metody proxy na živé spojení
return getattr(self._ensure(), name)
def get_medicus_connection_reconnecting():
"""Vrátí spojení s automatickým reconnectem (vhodné pro dlouho běžící procesy)."""
return ReconnectingConnection(get_medicus_connection)
def get_medicus_db():
"""Vrátí MedicusDB instanci s připojením podle názvu počítače."""
conn = get_medicus_connection()
instance = object.__new__(MedicusDB)
instance.conn = conn
instance.cur = conn.cursor()
return instance
class MedicusDB:
def __init__(self, host, db_path, user="SYSDBA", password="masterkey", charset="WIN1250"):
self.conn = fdb.connect(
host=host,
database=db_path,
user=user,
password=password,
charset=charset
)
self.cur = self.conn.cursor()
def get_fak_kapitace(self, as_dict=False):
sql = """
SELECT
fak.id,
fak.cisfak,
fak.poj,
fak.kapdetail
FROM fak
WHERE fak.kapdetail IS NOT NULL
AND fak.kapdetail <> ''
"""
if as_dict:
return self.query_dict(sql)
return self.query(sql)
def query(self, sql, params=None):
self.cur.execute(sql, params or ())
return self.cur.fetchall()
def query_dict(self, sql, params=None):
self.cur.execute(sql, params or ())
cols = [d[0].strip().lower() for d in self.cur.description]
return [dict(zip(cols, row)) for row in self.cur.fetchall()]
def get_active_registered_patients(self, as_dict=False):
sql = """
SELECT
kar.rodcis,
kar.prijmeni,
kar.jmeno,
kar.poj
FROM kar
WHERE kar.vyrazen = 'N'
AND kar.rodcis IS NOT NULL
AND kar.rodcis <> ''
AND EXISTS (
SELECT r.id FROM registr r
JOIN icp i ON r.idicp = i.idicp
WHERE r.idpac = kar.idpac
AND r.datum <= CURRENT_DATE
AND (r.datum_zruseni IS NULL OR r.datum_zruseni >= CURRENT_DATE)
AND r.priznak IN ('V','D','A')
AND i.icp = '09305001'
AND i.odb = '001'
)
ORDER BY kar.prijmeni, kar.rodcis
"""
if as_dict:
return self.query_dict(sql)
return self.query(sql)
def get_all_patients(self, as_dict=False):
sql = """
SELECT
kar.rodcis,
kar.prijmeni,
kar.jmeno,
kar.poj
FROM kar
"""
if as_dict:
return self.query_dict(sql)
return self.query(sql)
def close(self):
self.conn.close()