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

@@ -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