notebookVB
This commit is contained in:
10
.idea/Insurance.iml
generated
Normal file
10
.idea/Insurance.iml
generated
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="PYTHON_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="jdk" jdkName="Python 3.12 (Insurance)" jdkType="Python SDK" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<settings>
|
||||||
|
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||||
|
<version value="1.0" />
|
||||||
|
</settings>
|
||||||
|
</component>
|
||||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/Insurance.iml" filepath="$PROJECT_DIR$/.idea/Insurance.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
123
.idea/workspace.xml
generated
Normal file
123
.idea/workspace.xml
generated
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="AutoImportSettings">
|
||||||
|
<option name="autoReloadType" value="SELECTIVE" />
|
||||||
|
</component>
|
||||||
|
<component name="ChangeListManager">
|
||||||
|
<list default="true" id="c323bf66-f6a5-4ac2-b555-4106254af180" name="Changes" comment="">
|
||||||
|
<change afterPath="$PROJECT_DIR$/.idea/Insurance.iml" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/.idea/inspectionProfiles/profiles_settings.xml" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/.idea/modules.xml" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/.idea/vcs.xml" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/01 testik.py" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/02 testík.py" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/functions.py" afterDir="false" />
|
||||||
|
</list>
|
||||||
|
<option name="SHOW_DIALOG" value="false" />
|
||||||
|
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||||
|
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||||
|
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||||
|
</component>
|
||||||
|
<component name="FileTemplateManagerImpl">
|
||||||
|
<option name="RECENT_TEMPLATES">
|
||||||
|
<list>
|
||||||
|
<option value="Python Script" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
<component name="Git.Settings">
|
||||||
|
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectColorInfo"><![CDATA[{
|
||||||
|
"associatedIndex": 1
|
||||||
|
}]]></component>
|
||||||
|
<component name="ProjectId" id="33FcC2tYI91g8V8fLuzrpFNOGkY" />
|
||||||
|
<component name="ProjectLevelVcsManager" settingsEditedManually="true">
|
||||||
|
<ConfirmationsSetting value="2" id="Add" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectViewState">
|
||||||
|
<option name="hideEmptyMiddlePackages" value="true" />
|
||||||
|
<option name="showLibraryContents" value="true" />
|
||||||
|
</component>
|
||||||
|
<component name="PropertiesComponent"><![CDATA[{
|
||||||
|
"keyToString": {
|
||||||
|
"Python.01 testik.executor": "Run",
|
||||||
|
"Python.02 testík.executor": "Run",
|
||||||
|
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||||
|
"git-widget-placeholder": "master",
|
||||||
|
"ignore.virus.scanning.warn.message": "true",
|
||||||
|
"settings.editor.selected.configurable": "com.jetbrains.python.configuration.PyActiveSdkModuleConfigurable"
|
||||||
|
}
|
||||||
|
}]]></component>
|
||||||
|
<component name="RunManager" selected="Python.01 testik">
|
||||||
|
<configuration name="01 testik" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
|
||||||
|
<module name="Insurance" />
|
||||||
|
<option name="ENV_FILES" value="" />
|
||||||
|
<option name="INTERPRETER_OPTIONS" value="" />
|
||||||
|
<option name="PARENT_ENVS" value="true" />
|
||||||
|
<envs>
|
||||||
|
<env name="PYTHONUNBUFFERED" value="1" />
|
||||||
|
</envs>
|
||||||
|
<option name="SDK_HOME" value="" />
|
||||||
|
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
||||||
|
<option name="IS_MODULE_SDK" value="true" />
|
||||||
|
<option name="ADD_CONTENT_ROOTS" value="true" />
|
||||||
|
<option name="ADD_SOURCE_ROOTS" value="true" />
|
||||||
|
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/01 testik.py" />
|
||||||
|
<option name="PARAMETERS" value="" />
|
||||||
|
<option name="SHOW_COMMAND_LINE" value="false" />
|
||||||
|
<option name="EMULATE_TERMINAL" value="false" />
|
||||||
|
<option name="MODULE_MODE" value="false" />
|
||||||
|
<option name="REDIRECT_INPUT" value="false" />
|
||||||
|
<option name="INPUT_FILE" value="" />
|
||||||
|
<method v="2" />
|
||||||
|
</configuration>
|
||||||
|
<configuration name="02 testík" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
|
||||||
|
<module name="Insurance" />
|
||||||
|
<option name="ENV_FILES" value="" />
|
||||||
|
<option name="INTERPRETER_OPTIONS" value="" />
|
||||||
|
<option name="PARENT_ENVS" value="true" />
|
||||||
|
<envs>
|
||||||
|
<env name="PYTHONUNBUFFERED" value="1" />
|
||||||
|
</envs>
|
||||||
|
<option name="SDK_HOME" value="" />
|
||||||
|
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
||||||
|
<option name="IS_MODULE_SDK" value="true" />
|
||||||
|
<option name="ADD_CONTENT_ROOTS" value="true" />
|
||||||
|
<option name="ADD_SOURCE_ROOTS" value="true" />
|
||||||
|
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/02 testík.py" />
|
||||||
|
<option name="PARAMETERS" value="" />
|
||||||
|
<option name="SHOW_COMMAND_LINE" value="false" />
|
||||||
|
<option name="EMULATE_TERMINAL" value="false" />
|
||||||
|
<option name="MODULE_MODE" value="false" />
|
||||||
|
<option name="REDIRECT_INPUT" value="false" />
|
||||||
|
<option name="INPUT_FILE" value="" />
|
||||||
|
<method v="2" />
|
||||||
|
</configuration>
|
||||||
|
<recent_temporary>
|
||||||
|
<list>
|
||||||
|
<item itemvalue="Python.01 testik" />
|
||||||
|
<item itemvalue="Python.02 testík" />
|
||||||
|
</list>
|
||||||
|
</recent_temporary>
|
||||||
|
</component>
|
||||||
|
<component name="SharedIndexes">
|
||||||
|
<attachedChunks>
|
||||||
|
<set>
|
||||||
|
<option value="bundled-python-sdk-4f4e415b4190-aa17d162503b-com.jetbrains.pycharm.community.sharedIndexes.bundled-PC-243.26053.29" />
|
||||||
|
</set>
|
||||||
|
</attachedChunks>
|
||||||
|
</component>
|
||||||
|
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
|
||||||
|
<component name="TaskManager">
|
||||||
|
<task active="true" id="Default" summary="Default task">
|
||||||
|
<changelist id="c323bf66-f6a5-4ac2-b555-4106254af180" name="Changes" comment="" />
|
||||||
|
<created>1758916129960</created>
|
||||||
|
<option name="number" value="Default" />
|
||||||
|
<option name="presentableId" value="Default" />
|
||||||
|
<updated>1758916129960</updated>
|
||||||
|
</task>
|
||||||
|
<servers />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
131
01 testik.py
Normal file
131
01 testik.py
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
# pip install requests_pkcs12 pymysql
|
||||||
|
from requests_pkcs12 import Pkcs12Adapter
|
||||||
|
import requests
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
from datetime import date
|
||||||
|
import pymysql
|
||||||
|
from pymysql.cursors import DictCursor
|
||||||
|
from pprint import pprint
|
||||||
|
|
||||||
|
# ========== CONFIG ==========
|
||||||
|
PFX_PATH = r"mbcert.pfx"
|
||||||
|
PFX_PASS = "Vlado7309208104++"
|
||||||
|
VERIFY = True
|
||||||
|
|
||||||
|
EP_STAV = "https://prod.b2b.vzp.cz/B2BProxy/HttpProxy/stavPojisteniB2B"
|
||||||
|
SOAP_NS = "http://schemas.xmlsoap.org/soap/envelope/"
|
||||||
|
NS_STAV = "http://xmlns.gemsystem.cz/stavPojisteniB2B"
|
||||||
|
|
||||||
|
# RC provided by you (can contain slash; we normalize before sending/saving)
|
||||||
|
RC = "280616/091"
|
||||||
|
K_DATU = date.today().isoformat()
|
||||||
|
|
||||||
|
# MySQL (table already created as per previous DDL)
|
||||||
|
MYSQL_CFG = dict(
|
||||||
|
host="192.168.1.76",
|
||||||
|
port=3307,
|
||||||
|
user="root",
|
||||||
|
password="Vlado9674+",
|
||||||
|
database="medevio",
|
||||||
|
cursorclass=DictCursor,
|
||||||
|
autocommit=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
# ========== HELPERS ==========
|
||||||
|
def to_int_or_none(val):
|
||||||
|
if val is None:
|
||||||
|
return None
|
||||||
|
s = str(val).strip()
|
||||||
|
return int(s) if s.isdigit() else None
|
||||||
|
|
||||||
|
def normalize_rc(rc: str) -> str:
|
||||||
|
return rc.replace("/", "").strip()
|
||||||
|
|
||||||
|
def post_soap(endpoint: str, body_xml: str) -> str:
|
||||||
|
env = f'''<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<soap:Envelope xmlns:soap="{SOAP_NS}">
|
||||||
|
<soap:Body>{body_xml}</soap:Body>
|
||||||
|
</soap:Envelope>'''
|
||||||
|
s = requests.Session()
|
||||||
|
s.mount("https://", Pkcs12Adapter(pkcs12_filename=PFX_PATH, pkcs12_password=PFX_PASS))
|
||||||
|
r = s.post(
|
||||||
|
endpoint,
|
||||||
|
data=env.encode("utf-8"),
|
||||||
|
headers={"Content-Type": "text/xml; charset=utf-8", "SOAPAction": "process"},
|
||||||
|
timeout=30,
|
||||||
|
verify=VERIFY,
|
||||||
|
)
|
||||||
|
print("HTTP:", r.status_code)
|
||||||
|
return r.text
|
||||||
|
|
||||||
|
def parse_stav(xml_text: str):
|
||||||
|
NS = {"soap": SOAP_NS, "s": NS_STAV}
|
||||||
|
root = ET.fromstring(xml_text)
|
||||||
|
out = {
|
||||||
|
"stav": None,
|
||||||
|
"kodPojistovny": None,
|
||||||
|
"nazevPojistovny": None,
|
||||||
|
"pojisteniKod": None,
|
||||||
|
"stavVyrizeniPozadavku": None,
|
||||||
|
}
|
||||||
|
node = root.find(".//s:stavPojisteni", NS)
|
||||||
|
if node is not None:
|
||||||
|
for tag in ("stav", "kodPojistovny", "nazevPojistovny", "pojisteniKod"):
|
||||||
|
el = node.find(f"s:{tag}", NS)
|
||||||
|
out[tag] = el.text.strip() if el is not None and el.text else None
|
||||||
|
st = root.find(".//s:stavVyrizeniPozadavku", NS)
|
||||||
|
out["stavVyrizeniPozadavku"] = st.text.strip() if st is not None and st.text else None
|
||||||
|
return out
|
||||||
|
|
||||||
|
def save_stav_pojisteni(rc: str, k_datu: str, parsed: dict, response_xml: str) -> int:
|
||||||
|
"""
|
||||||
|
Insert one log row into medevio.vzp_stav_pojisteni and print what is being saved.
|
||||||
|
Assumes the table already exists (no DDL/verification here).
|
||||||
|
"""
|
||||||
|
payload = {
|
||||||
|
"rc": normalize_rc(rc),
|
||||||
|
"k_datu": k_datu,
|
||||||
|
"stav": to_int_or_none(parsed.get("stav")), # handles 'X' -> None
|
||||||
|
"kod_pojistovny": parsed.get("kodPojistovny"),
|
||||||
|
"nazev_pojistovny": parsed.get("nazevPojistovny"),
|
||||||
|
"pojisteni_kod": parsed.get("pojisteniKod"),
|
||||||
|
"stav_vyrizeni": to_int_or_none(parsed.get("stavVyrizeniPozadavku")),
|
||||||
|
"response_xml": response_xml,
|
||||||
|
}
|
||||||
|
|
||||||
|
print("\n=== vzp_stav_pojisteni: will insert ===")
|
||||||
|
pprint(payload)
|
||||||
|
|
||||||
|
sql = """
|
||||||
|
INSERT INTO vzp_stav_pojisteni
|
||||||
|
(rc, k_datu, stav, kod_pojistovny, nazev_pojistovny, pojisteni_kod, stav_vyrizeni, response_xml)
|
||||||
|
VALUES
|
||||||
|
(%(rc)s, %(k_datu)s, %(stav)s, %(kod_pojistovny)s, %(nazev_pojistovny)s, %(pojisteni_kod)s, %(stav_vyrizeni)s, %(response_xml)s)
|
||||||
|
"""
|
||||||
|
|
||||||
|
conn = pymysql.connect(**MYSQL_CFG)
|
||||||
|
try:
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute(sql, payload)
|
||||||
|
conn.commit()
|
||||||
|
finally:
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
print("Inserted 1 row into vzp_stav_pojisteni.")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
# ========== MAIN ==========
|
||||||
|
if __name__ == "__main__":
|
||||||
|
rc_norm = normalize_rc(RC)
|
||||||
|
body = f"""
|
||||||
|
<ns1:stavPojisteniB2B xmlns:ns1="{NS_STAV}">
|
||||||
|
<ns1:cisloPojistence>{rc_norm}</ns1:cisloPojistence>
|
||||||
|
<ns1:kDatu>{K_DATU}</ns1:kDatu>
|
||||||
|
</ns1:stavPojisteniB2B>""".strip()
|
||||||
|
|
||||||
|
xml = post_soap(EP_STAV, body)
|
||||||
|
parsed = parse_stav(xml)
|
||||||
|
print(parsed) # keep your quick print
|
||||||
|
|
||||||
|
# Save to MySQL and print exactly what is saved
|
||||||
|
save_stav_pojisteni(RC, K_DATU, parsed, xml)
|
||||||
203
02 testík.py
Normal file
203
02 testík.py
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Call VZP RegistracePojistencePZSB2B for one patient (001 = VPL),
|
||||||
|
parse the response, upsert rows into medevio.vzp_registrace,
|
||||||
|
and print what is being saved.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# pip install requests_pkcs12 pymysql
|
||||||
|
|
||||||
|
from requests_pkcs12 import Pkcs12Adapter
|
||||||
|
import requests
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
from datetime import date
|
||||||
|
import pymysql
|
||||||
|
from pymysql.cursors import DictCursor
|
||||||
|
from pprint import pprint
|
||||||
|
|
||||||
|
# ------------------- CONFIG -------------------
|
||||||
|
ENDPOINT = "https://prod.b2b.vzp.cz/B2BProxy/HttpProxy/RegistracePojistencePZSB2B" # case-sensitive
|
||||||
|
PFX_PATH = r"mbcert.pfx" # <-- your .pfx path
|
||||||
|
PFX_PASS = "Vlado7309208104++" # <-- your export password
|
||||||
|
VERIFY = True # or path to CA PEM, e.g. r"C:\certs\vzp_ca.pem"
|
||||||
|
|
||||||
|
# Patient + query
|
||||||
|
# RC = "7309208104" # rodné číslo without slash
|
||||||
|
RC = "280616/091" # rodné číslo without slash
|
||||||
|
K_DATU = date.today().isoformat() # YYYY-MM-DD
|
||||||
|
ODBORNOSTI = ["001"] # VPL (adult GP)
|
||||||
|
|
||||||
|
# MySQL
|
||||||
|
MYSQL_CFG = dict(
|
||||||
|
host="192.168.1.76",
|
||||||
|
port=3307,
|
||||||
|
user="root",
|
||||||
|
password="Vlado9674+",
|
||||||
|
database="medevio",
|
||||||
|
cursorclass=DictCursor,
|
||||||
|
autocommit=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Namespaces (from your response/WSDL)
|
||||||
|
NS = {
|
||||||
|
"soap": "http://schemas.xmlsoap.org/soap/envelope/",
|
||||||
|
"rp": "http://xmlns.gemsystem.cz/B2B/RegistracePojistencePZSB2B/1",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------- HELPERS -------------------
|
||||||
|
def normalize_rc(rc: str) -> str:
|
||||||
|
return rc.replace("/", "").strip()
|
||||||
|
|
||||||
|
def build_envelope(rc: str, k_datu: str, odb_list: list[str]) -> str:
|
||||||
|
odb_xml = "".join([f"<ns1:kodOdbornosti>{kod}</ns1:kodOdbornosti>" for kod in odb_list])
|
||||||
|
inner = f"""
|
||||||
|
<ns1:registracePojistencePZSB2B xmlns:ns1="{NS['rp']}">
|
||||||
|
<ns1:cisloPojistence>{rc}</ns1:cisloPojistence>
|
||||||
|
<ns1:kDatu>{k_datu}</ns1:kDatu>
|
||||||
|
<ns1:seznamOdbornosti>
|
||||||
|
{odb_xml}
|
||||||
|
</ns1:seznamOdbornosti>
|
||||||
|
</ns1:registracePojistencePZSB2B>""".strip()
|
||||||
|
|
||||||
|
return f"""<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<soap:Envelope xmlns:soap="{NS['soap']}">
|
||||||
|
<soap:Body>
|
||||||
|
{inner}
|
||||||
|
</soap:Body>
|
||||||
|
</soap:Envelope>"""
|
||||||
|
|
||||||
|
def parse_registrace(xml_text: str):
|
||||||
|
"""
|
||||||
|
Return (rows, stav_vyrizeni) where rows is a list of dicts
|
||||||
|
for each <odbornost> item in the response.
|
||||||
|
"""
|
||||||
|
root = ET.fromstring(xml_text)
|
||||||
|
items = root.findall(".//rp:seznamOdbornosti/rp:odbornost", NS)
|
||||||
|
out = []
|
||||||
|
for it in items:
|
||||||
|
def g(tag, ctx=it):
|
||||||
|
el = ctx.find(f"rp:{tag}", NS)
|
||||||
|
return el.text.strip() if el is not None and el.text else None
|
||||||
|
|
||||||
|
poj = it.find("rp:zdravotniPojistovna", NS)
|
||||||
|
poj_kod = poj.find("rp:kod", NS).text.strip() if (poj is not None and poj.find("rp:kod", NS) is not None) else None
|
||||||
|
poj_zkr = poj.find("rp:zkratka", NS).text.strip() if (poj is not None and poj.find("rp:zkratka", NS) is not None) else None
|
||||||
|
|
||||||
|
spec = it.find("rp:odbornost", NS)
|
||||||
|
odb_kod = spec.find("rp:kod", NS).text.strip() if (spec is not None and spec.find("rp:kod", NS) is not None) else None
|
||||||
|
odb_naz = spec.find("rp:nazev", NS).text.strip() if (spec is not None and spec.find("rp:nazev", NS) is not None) else None
|
||||||
|
|
||||||
|
out.append(dict(
|
||||||
|
icz=g("ICZ"),
|
||||||
|
icp=g("ICP"),
|
||||||
|
nazev_icp=g("nazevICP"),
|
||||||
|
nazev_szz=g("nazevSZZ"),
|
||||||
|
poj_kod=poj_kod,
|
||||||
|
poj_zkratka=poj_zkr,
|
||||||
|
odbornost_kod=odb_kod,
|
||||||
|
odbornost_nazev=odb_naz,
|
||||||
|
datum_registrace=g("datumRegistrace"),
|
||||||
|
datum_zahajeni=g("datumZahajeni"),
|
||||||
|
datum_ukonceni=g("datumUkonceni"),
|
||||||
|
))
|
||||||
|
|
||||||
|
st = root.find(".//rp:stavVyrizeniPozadavku", NS)
|
||||||
|
stav_vyrizeni = st.text.strip() if (st is not None and st.text) else None
|
||||||
|
return out, stav_vyrizeni
|
||||||
|
|
||||||
|
def upsert_rows(rc: str, query_date: str, rows: list[dict], stav_vyrizeni: str, xml_text: str) -> int:
|
||||||
|
"""
|
||||||
|
Insert or update rows into medevio.vzp_registrace and print each payload.
|
||||||
|
Assumes the table already exists (as per your DDL).
|
||||||
|
"""
|
||||||
|
if not rows:
|
||||||
|
print("No <odbornost> rows found; nothing to save.")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
sql = """
|
||||||
|
INSERT INTO vzp_registrace
|
||||||
|
(rc, query_date, odbornost_kod, odbornost_nazev,
|
||||||
|
icz, icp, nazev_icp, nazev_szz,
|
||||||
|
poj_kod, poj_zkratka,
|
||||||
|
datum_registrace, datum_zahajeni, datum_ukonceni,
|
||||||
|
stav_vyrizeni, response_xml)
|
||||||
|
VALUES
|
||||||
|
(%(rc)s, %(query_date)s, %(odbornost_kod)s, %(odbornost_nazev)s,
|
||||||
|
%(icz)s, %(icp)s, %(nazev_icp)s, %(nazev_szz)s,
|
||||||
|
%(poj_kod)s, %(poj_zkratka)s,
|
||||||
|
%(datum_registrace)s, %(datum_zahajeni)s, %(datum_ukonceni)s,
|
||||||
|
%(stav_vyrizeni)s, %(response_xml)s)
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
odbornost_nazev = VALUES(odbornost_nazev),
|
||||||
|
nazev_icp = VALUES(nazev_icp),
|
||||||
|
nazev_szz = VALUES(nazev_szz),
|
||||||
|
poj_kod = VALUES(poj_kod),
|
||||||
|
poj_zkratka = VALUES(poj_zkratka),
|
||||||
|
datum_registrace= VALUES(datum_registrace),
|
||||||
|
datum_zahajeni = VALUES(datum_zahajeni),
|
||||||
|
datum_ukonceni = VALUES(datum_ukonceni),
|
||||||
|
stav_vyrizeni = VALUES(stav_vyrizeni),
|
||||||
|
response_xml = VALUES(response_xml),
|
||||||
|
updated_at = CURRENT_TIMESTAMP
|
||||||
|
"""
|
||||||
|
rc_norm = normalize_rc(rc)
|
||||||
|
qd = query_date or date.today().isoformat()
|
||||||
|
payloads = []
|
||||||
|
for r in rows:
|
||||||
|
payload = {
|
||||||
|
"rc": rc_norm,
|
||||||
|
"query_date": qd,
|
||||||
|
**r,
|
||||||
|
"stav_vyrizeni": stav_vyrizeni,
|
||||||
|
"response_xml": xml_text,
|
||||||
|
}
|
||||||
|
payloads.append(payload)
|
||||||
|
|
||||||
|
# Print what we're going to save
|
||||||
|
print("\n=== Will save the following payload(s) to medevio.vzp_registrace ===")
|
||||||
|
for p in payloads:
|
||||||
|
pprint(p)
|
||||||
|
|
||||||
|
conn = pymysql.connect(**MYSQL_CFG)
|
||||||
|
try:
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.executemany(sql, payloads)
|
||||||
|
conn.commit()
|
||||||
|
finally:
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
print(f"\nUpserted rows: {len(payloads)}")
|
||||||
|
return len(payloads)
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------- MAIN FLOW -------------------
|
||||||
|
def main():
|
||||||
|
# Build SOAP envelope
|
||||||
|
envelope = build_envelope(RC, K_DATU, ODBORNOSTI)
|
||||||
|
|
||||||
|
# mTLS session
|
||||||
|
session = requests.Session()
|
||||||
|
session.mount("https://", Pkcs12Adapter(pkcs12_filename=PFX_PATH, pkcs12_password=PFX_PASS))
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
"Content-Type": "text/xml; charset=utf-8",
|
||||||
|
"SOAPAction": "process", # Oracle composite usually expects this
|
||||||
|
}
|
||||||
|
|
||||||
|
# Call service
|
||||||
|
resp = session.post(ENDPOINT, data=envelope.encode("utf-8"),
|
||||||
|
headers=headers, timeout=30, verify=VERIFY)
|
||||||
|
print("HTTP:", resp.status_code)
|
||||||
|
|
||||||
|
# (Optional) Uncomment to see raw XML
|
||||||
|
# print(resp.text)
|
||||||
|
|
||||||
|
# Parse and save
|
||||||
|
rows, stav = parse_registrace(resp.text)
|
||||||
|
upsert_rows(RC, K_DATU, rows, stav, resp.text)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
5
File3.py
Normal file
5
File3.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
from functions import check_insurance
|
||||||
|
import time
|
||||||
|
import fdb
|
||||||
|
|
||||||
|
|
||||||
138
functions.py
Normal file
138
functions.py
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
# functions.py
|
||||||
|
# pip install requests_pkcs12 pymysql
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
|
from datetime import date
|
||||||
|
from pprint import pprint
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
|
||||||
|
import requests
|
||||||
|
from requests_pkcs12 import Pkcs12Adapter
|
||||||
|
import pymysql
|
||||||
|
from pymysql.cursors import DictCursor
|
||||||
|
|
||||||
|
# ======== CONFIG (env overrides allowed) ========
|
||||||
|
PFX_PATH = os.getenv("VZP_PFX_PATH", r"mbcert.pfx")
|
||||||
|
PFX_PASS = os.getenv("VZP_PFX_PASS", "Vlado7309208104++")
|
||||||
|
VERIFY = os.getenv("VZP_CA_VERIFY", "true").lower() != "false" # set to path or "false" to disable
|
||||||
|
|
||||||
|
EP_STAV = "https://prod.b2b.vzp.cz/B2BProxy/HttpProxy/stavPojisteniB2B"
|
||||||
|
SOAP_NS = "http://schemas.xmlsoap.org/soap/envelope/"
|
||||||
|
NS_STAV = "http://xmlns.gemsystem.cz/stavPojisteniB2B"
|
||||||
|
|
||||||
|
MYSQL_CFG = {
|
||||||
|
"host": os.getenv("MYSQL_HOST", "192.168.1.76"),
|
||||||
|
"port": int(os.getenv("MYSQL_PORT", "3307")),
|
||||||
|
"user": os.getenv("MYSQL_USER", "root"),
|
||||||
|
"password": os.getenv("MYSQL_PASSWORD", "Vlado9674+"),
|
||||||
|
"database": os.getenv("MYSQL_DB", "medevio"),
|
||||||
|
"cursorclass": DictCursor,
|
||||||
|
"autocommit": False,
|
||||||
|
}
|
||||||
|
|
||||||
|
# ======== HELPERS ========
|
||||||
|
def normalize_rc(rc: str) -> str:
|
||||||
|
"""Return rodné číslo without slash/spaces."""
|
||||||
|
return rc.replace("/", "").replace(" ", "").strip()
|
||||||
|
|
||||||
|
def to_int_or_none(val):
|
||||||
|
if val is None:
|
||||||
|
return None
|
||||||
|
s = str(val).strip()
|
||||||
|
return int(s) if s.isdigit() else None
|
||||||
|
|
||||||
|
def _session():
|
||||||
|
s = requests.Session()
|
||||||
|
s.mount("https://", Pkcs12Adapter(pkcs12_filename=PFX_PATH, pkcs12_password=PFX_PASS))
|
||||||
|
return s
|
||||||
|
|
||||||
|
def post_soap(endpoint: str, inner_xml: str) -> str:
|
||||||
|
"""Wrap body in SOAP 1.1 envelope and POST with mTLS."""
|
||||||
|
envelope = f'''<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<soap:Envelope xmlns:soap="{SOAP_NS}">
|
||||||
|
<soap:Body>{inner_xml}</soap:Body>
|
||||||
|
</soap:Envelope>'''
|
||||||
|
r = _session().post(
|
||||||
|
endpoint,
|
||||||
|
data=envelope.encode("utf-8"),
|
||||||
|
headers={"Content-Type": "text/xml; charset=utf-8", "SOAPAction": "process"},
|
||||||
|
timeout=30,
|
||||||
|
verify=VERIFY,
|
||||||
|
)
|
||||||
|
# You may log r.status_code if needed.
|
||||||
|
return r.text
|
||||||
|
|
||||||
|
def parse_stav(xml_text: str) -> dict:
|
||||||
|
"""Parse StavPojisteniB2B response → dict with keys:
|
||||||
|
'stav','kodPojistovny','nazevPojistovny','pojisteniKod','stavVyrizeniPozadavku'."""
|
||||||
|
NS = {"soap": SOAP_NS, "s": NS_STAV}
|
||||||
|
root = ET.fromstring(xml_text)
|
||||||
|
out = {"stav": None, "kodPojistovny": None, "nazevPojistovny": None,
|
||||||
|
"pojisteniKod": None, "stavVyrizeniPozadavku": None}
|
||||||
|
node = root.find(".//s:stavPojisteni", NS)
|
||||||
|
if node is not None:
|
||||||
|
for tag in ("stav", "kodPojistovny", "nazevPojistovny", "pojisteniKod"):
|
||||||
|
el = node.find(f"s:{tag}", NS)
|
||||||
|
out[tag] = el.text.strip() if (el is not None and el.text) else None
|
||||||
|
st = root.find(".//s:stavVyrizeniPozadavku", NS)
|
||||||
|
out["stavVyrizeniPozadavku"] = st.text.strip() if (st is not None and st.text) else None
|
||||||
|
return out
|
||||||
|
|
||||||
|
def save_stav_pojisteni(rc: str, k_datu: str, parsed: dict, response_xml: str) -> None:
|
||||||
|
"""Insert one log row into medevio.vzp_stav_pojisteni and print what is being saved."""
|
||||||
|
payload = {
|
||||||
|
"rc": normalize_rc(rc),
|
||||||
|
"k_datu": k_datu,
|
||||||
|
"stav": to_int_or_none(parsed.get("stav")), # 'X' -> None
|
||||||
|
"kod_pojistovny": parsed.get("kodPojistovny"),
|
||||||
|
"nazev_pojistovny": parsed.get("nazevPojistovny"),
|
||||||
|
"pojisteni_kod": parsed.get("pojisteniKod"),
|
||||||
|
"stav_vyrizeni": to_int_or_none(parsed.get("stavVyrizeniPozadavku")),
|
||||||
|
"response_xml": response_xml,
|
||||||
|
}
|
||||||
|
|
||||||
|
print("\n=== vzp_stav_pojisteni: will insert ===")
|
||||||
|
pprint(payload)
|
||||||
|
|
||||||
|
sql = """
|
||||||
|
INSERT INTO vzp_stav_pojisteni
|
||||||
|
(rc, k_datu, stav, kod_pojistovny, nazev_pojistovny, pojisteni_kod, stav_vyrizeni, response_xml)
|
||||||
|
VALUES
|
||||||
|
(%(rc)s, %(k_datu)s, %(stav)s, %(kod_pojistovny)s, %(nazev_pojistovny)s, %(pojisteni_kod)s, %(stav_vyrizeni)s, %(response_xml)s)
|
||||||
|
"""
|
||||||
|
conn = pymysql.connect(**MYSQL_CFG)
|
||||||
|
try:
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute(sql, payload)
|
||||||
|
conn.commit()
|
||||||
|
finally:
|
||||||
|
conn.close()
|
||||||
|
print("Inserted 1 row into vzp_stav_pojisteni.")
|
||||||
|
|
||||||
|
# ======== PUBLIC API ========
|
||||||
|
def check_insurance(rc: str, mode: str | None = None, k_datu: str | None = None):
|
||||||
|
"""
|
||||||
|
Call VZP 'StavPojisteniB2B' for given RC and date.
|
||||||
|
Returns (is_active, info_dict).
|
||||||
|
- is_active: True iff info_dict['stav'] == '1'
|
||||||
|
- info_dict: {'stav','kodPojistovny','nazevPojistovny','pojisteniKod','stavVyrizeniPozadavku'}
|
||||||
|
If mode == 'nodb' (case-insensitive), DOES NOT write to DB. Otherwise logs into medevio.vzp_stav_pojisteni.
|
||||||
|
"""
|
||||||
|
rc_norm = normalize_rc(rc)
|
||||||
|
kd = k_datu or date.today().isoformat()
|
||||||
|
|
||||||
|
body = f"""
|
||||||
|
<ns1:stavPojisteniB2B xmlns:ns1="{NS_STAV}">
|
||||||
|
<ns1:cisloPojistence>{rc_norm}</ns1:cisloPojistence>
|
||||||
|
<ns1:kDatu>{kd}</ns1:kDatu>
|
||||||
|
</ns1:stavPojisteniB2B>""".strip()
|
||||||
|
|
||||||
|
xml = post_soap(EP_STAV, body)
|
||||||
|
info = parse_stav(xml)
|
||||||
|
is_active = (info.get("stav") == "1")
|
||||||
|
|
||||||
|
if not (mode and mode.lower() == "nodb"):
|
||||||
|
save_stav_pojisteni(rc_norm, kd, info, xml)
|
||||||
|
|
||||||
|
return is_active, info
|
||||||
Reference in New Issue
Block a user