Files
medicus/MedicusWithClaude/funkce_ext.py
2026-03-18 07:13:47 +01:00

185 lines
6.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""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