185 lines
6.7 KiB
Python
185 lines
6.7 KiB
Python
"""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
|