diff --git a/Feasibility/77242113UCO2001/classify_krok_v1.0.py b/Feasibility/77242113UCO2001/classify_krok_v1.0.py
index 4ab3d8e..795ec44 100644
--- a/Feasibility/77242113UCO2001/classify_krok_v1.0.py
+++ b/Feasibility/77242113UCO2001/classify_krok_v1.0.py
@@ -51,7 +51,7 @@ OVERRIDES = {
}
# Ocekavane pocty (dle odsouhlasene klasifikace 09JUN2026) pro kontrolu
-EXPECTED = {K0: 4, K0N: 36, K1: 25, K2: 25, K31: 3, K32: 12, K4: 11, K5: 0, K6: 14, K7: 0}
+EXPECTED = {K0: 4, K0N: 36, K1: 17, K2: 30, K31: 3, K32: 14, K4: 9, K5: 3, K6: 13, K7: 1}
def classify(status: str) -> str:
diff --git a/Feasibility/77242113UCO2001/store_cda_batch_v1.0.py b/Feasibility/77242113UCO2001/store_cda_batch_v1.0.py
index 9251a75..99bad63 100644
--- a/Feasibility/77242113UCO2001/store_cda_batch_v1.0.py
+++ b/Feasibility/77242113UCO2001/store_cda_batch_v1.0.py
@@ -33,26 +33,28 @@ TOWER_USER = "root"
TOWER_PASS = "7309208104"
REMOTE_DIR = "/mnt/user/JNJEMAILS"
TMPDIR = r"u:\Dropbox\!!!Days\Downloads Z230\_cda_tmp"
-STORED_AT = "2026-06-09"
+STORED_AT = "2026-06-10"
# investigator_id -> (msg_filename, attachment_filename, label)
-# DAVKA 2 (09JUN2026): institucionalni CDA, soubor jmenovan primo v STATUS lekare
+# DAVKA 3 (10JUN2026): nove CDA z 10.6. (krok 4 -> 5)
MAPPING = [
- ("6a19832b5fc2213518257950", "FC130007D8A1F0E30000.msg",
- "CZ_CDA Template Master institution_Axon Clinical, s.r.o._fully signed 08Jun2026.pdf",
- "Matous Jan (AXON Clinical, master)"),
- ("6a19832b5fc2213518257958", "FC130007D8A1F0E60000.msg",
- "SK_CDA institution_Gastro LM_fully signed_08Jun2026.pdf",
- "Mihalkanin Lubomir (Gastro LM)"),
- ("6a198b661218c31ab0f5ba4e", "FC130007C1643CA10000.msg",
- "06_CDA-Janssen a FN v Motole_fully executed.pdf",
- "Krizova Viera (FN Motol master)"),
+ ("6a19832b5fc221351825796c", "FC130007DE92C2040000.msg",
+ "CZ_CDA institution_MUDr. GREGAR s.r.o_Jan Gregar_fully signed_09Jun2026.pdf",
+ "Gregar Jan (MUDr. GREGAR s.r.o.)"),
+ ("6a19832b5fc2213518257969", "FC130007DE92C2030000.msg",
+ "SK_CDA PI_Durina_FN Nove Zamky_fully signed 09Jun2026.pdf",
+ "Durina Juraj (FN Nove Zamky)"),
+ ("6a19832b5fc2213518257973", "FC130007DE92C1FE0000.msg",
+ "SK_CDA_Institution_Accout Center s.r.o_09Jun2026.pdf",
+ "Horvath Frantisek (Accout Center)"),
]
-# DAVKA 1 (09JUN2026) - jiz ulozeno, ponechano pro historii:
+# DAVKA 1+2 (09JUN2026) - jiz ulozeno, ponechano pro historii:
# Hlavaty/Cliniq FC1300053049739C, Fedurco/ENDOMED FC1300053049739B,
# Tichy FC13000530495B95, Falc FC130007D8A1F0E6, Pesta FC130007D8A1F0E1,
-# Jungwirthova FC130007D8A1F0E2, Lukac FC130007C9E971FF (store_cda_to_mongo_v1.0)
+# Jungwirthova FC130007D8A1F0E2, Lukac FC130007C9E971FF (store_cda_to_mongo_v1.0),
+# Matous/Axon FC130007D8A1F0E3, Mihalkanin/GastroLM FC130007D8A1F0E6,
+# Krizova/Motol FC130007C1643CA1
def norm(s):
diff --git a/IWRS/Drugs/Trash/import_to_mongo.py b/IWRS/Drugs/Trash/import_to_mongo.py
deleted file mode 100644
index 18123b2..0000000
--- a/IWRS/Drugs/Trash/import_to_mongo.py
+++ /dev/null
@@ -1,253 +0,0 @@
-"""
-Import Drugs dat (shipments, shipment_items, inventory, destruction) z XLSX do MongoDB.
-
-Volá se z IWRS/Drugs/run_all.py po stažení reportů.
-"""
-
-import os
-import sys
-import re
-import glob
-
-import pandas as pd
-
-sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
-from common.mongo_writer import (
- to_str, to_int, to_date,
- ensure_indexes, log_import,
- bulk_upsert_with_snapshot, bulk_upsert_only,
-)
-
-BASE_DIR = os.path.dirname(os.path.abspath(__file__))
-
-
-# ── XLSX parsery (převzaté z run_all.py + úprava na Mongo dokumenty) ─────────
-
-def parse_shipments_report(study):
- path = os.path.join(BASE_DIR, f"xls_shipments_{study}", f"shipments_report_{study}.xlsx")
- if not os.path.exists(path):
- print(f" CHYBI: {path}")
- return []
- raw = pd.read_excel(path, header=None)
- header_row = None
- for i, row in raw.iterrows():
- if "Shipment ID" in [str(v).strip() for v in row]:
- header_row = i
- break
- if header_row is None:
- return []
- df = pd.read_excel(path, header=header_row).dropna(how="all")
- df = df[df["Location"].astype(str).str.contains("Czech", na=False, case=False)]
- col = df.columns.tolist()
- rows = []
- for _, r in df.iterrows():
- sid = to_str(r["Shipment ID"])
- if not sid:
- continue
- rows.append({
- "_id": sid,
- "shipment_id": sid,
- "study": study,
- "status": to_str(r["IRT Shipment Status"]),
- "type": to_str(r["Type"]),
- "ship_from": to_str(r["Shipment From"]),
- "ship_to_site": to_str(r["Ship To:"]),
- "location": to_str(r["Location"]),
- "request_date": to_date(r["Request Date"]),
- "shipped_date": to_date(r["Shipped Date"]),
- "received_date": to_date(r["Received Date"]) if "Received Date" in col else None,
- "received_by": to_str(r["Received by"]) if "Received by" in col else None,
- "delivered_date_utc": to_date(r["Delivered Date [UTC]"]) if "Delivered Date [UTC]" in col else None,
- "delivery_recipient": to_str(r["Delivery Recipient"]) if "Delivery Recipient" in col else None,
- "delivery_details": to_str(r["Delivery Details"]) if "Delivery Details" in col else None,
- "cancelled_date": to_date(r["Cancelled Date"]) if "Cancelled Date" in col else None,
- "total_medication_ids": to_int(r["Total Medication IDs"]) if "Total Medication IDs" in col else None,
- "tracking_no": to_str(r["Tracking #"]) if "Tracking #" in col else None,
- "shipping_category": to_str(r["Shipping Category"]) if "Shipping Category" in col else None,
- "expected_arrival": to_date(r["Expected Arrival"]) if "Expected Arrival" in col else None,
- })
- return rows
-
-
-def parse_shipment_details(study):
- detail_dir = os.path.join(BASE_DIR, f"xls_shipment_details_{study}")
- files = sorted(glob.glob(os.path.join(detail_dir, "shipment_details_*.xlsx")))
- rows = []
- for path in files:
- m = re.search(r"shipment_details_(.+)\.xlsx", os.path.basename(path))
- shipment_id = m.group(1) if m else "UNKNOWN"
- raw = pd.read_excel(path, header=None)
- header_row = None
- for i, row in raw.iterrows():
- if "Medication ID" in [str(v).strip() for v in row]:
- header_row = i
- break
- if header_row is None:
- continue
- df = pd.read_excel(path, header=header_row).dropna(how="all")
- for _, r in df.iterrows():
- med_desc = (to_str(r.get("Medication Description"))
- or to_str(r.get("Medication ID Description")))
- med_type = (to_str(r.get("Medication type"))
- or to_str(r.get("Medication ID type")))
- med_id = to_str(r.get("Medication ID"))
- if not med_id:
- continue
- rows.append({
- "_id": f"{shipment_id}:{med_id}",
- "study": study,
- "shipment_id": shipment_id,
- "destination_location": to_str(r.get("Destination Location")),
- "shipment_status": to_str(r.get("IRT Shipment Status")),
- "shipment_type": to_str(r.get("Type")),
- "destination_site": to_str(r.get("Destination Site")),
- "investigator": to_str(r.get("Investigator")),
- "medication_description": med_desc,
- "medication_type": med_type,
- "medication_id": med_id,
- "packaged_lot_no": to_str(r.get("Packaged Lot number")),
- "packaged_lot_description": to_str(r.get("Packaged Lot description")),
- "container_id": to_str(r.get("Container ID")),
- "quantity": to_int(r.get("Quantity of Medication IDs")),
- "expiration_date": to_date(r.get("Expiration Date")),
- "item_status": to_str(r.get("Status")),
- })
- # dedupe (poslední vyhrává)
- by_id = {r["_id"]: r for r in rows}
- return list(by_id.values())
-
-
-def parse_inventory(study):
- inv_dir = os.path.join(BASE_DIR, f"xls_reports_{study}")
- files = sorted(glob.glob(os.path.join(inv_dir, "onsite_inventory_detail_*.xlsx")))
- rows = []
- for path in files:
- raw = pd.read_excel(path, header=None)
- site = investigator = location = None
- header_row = None
- for i, row in raw.iterrows():
- first = str(row.iloc[0]).strip() if pd.notna(row.iloc[0]) else ""
- if first.startswith("Site:"):
- site = first.replace("Site:", "").strip()
- elif first.startswith("Investigator:"):
- investigator = first.replace("Investigator:", "").strip()
- elif first.startswith("Location:"):
- location = first.replace("Location:", "").strip()
- if first in ("Medication", "Medication ID") and header_row is None:
- header_row = i
- if header_row is None:
- continue
- df = pd.read_excel(path, header=header_row).dropna(how="all")
- df = df.rename(columns={df.columns[0]: "medication_id"})
- for _, r in df.iterrows():
- med_id = to_str(r["medication_id"])
- if not med_id or not site:
- continue
- rows.append({
- "_id": f"{site}:{med_id}",
- "study": study,
- "site": site,
- "investigator": investigator,
- "location": location,
- "medication_id": med_id,
- "packaged_lot_no": to_str(r.get("Packaged Lot number")),
- "original_expiration_date": to_date(r.get("Original Expiration Date when Packaged Lot was Added")),
- "expiration_date": to_date(r.get("Expiration date")),
- "received_date": to_date(r.get("Received Date")),
- "receipt_user": to_str(r.get("Shipment Receipt User")),
- "subject_identifier": to_str(r.get("Subject Identifier")),
- "quantity_assigned": to_int(r.get("Quantity Assigned")),
- "irt_transaction": to_str(r.get("IRT Transaction")),
- "date_assigned": to_date(r.get("Date Assigned")),
- "assignment_user": to_str(r.get("Assignment User")),
- "dispensation_status": to_str(r.get("Dispensation Status")),
- "dispensing_date": to_date(r.get("Dispensing date") or r.get("Dispensing Date")),
- "quantity_dispensed": to_int(r.get("Quantity Dispensed")),
- "dispensing_user": to_str(r.get("Dispensing User")),
- "quantity_returned": to_int(r.get("Quantity Returned")),
- "date_returned": to_date(r.get("Date Returned")),
- "return_user": to_str(r.get("Return User")),
- })
- by_id = {r["_id"]: r for r in rows}
- return list(by_id.values())
-
-
-def parse_destruction_files(study):
- dest_dir = os.path.join(BASE_DIR, f"xls_ip_destruction_{study}")
- files = sorted(glob.glob(os.path.join(dest_dir, "ip_destruction_basket_*.xlsx")))
- rows = []
- for path in files:
- raw = pd.read_excel(path, header=None)
- meta = {}
- header_row = None
- for i, row in raw.iterrows():
- first = str(row.iloc[0]).strip() if pd.notna(row.iloc[0]) else ""
- for key, attr in [
- ("Investigator Name:", "investigator"),
- ("Site ID:", "site_id"),
- ("Location:", "location"),
- ("Basket ID:", "basket_id"),
- ("Drug Destruction Created Date:", "destruction_date"),
- ]:
- if first.startswith(key):
- meta[attr] = first.replace(key, "").strip()
- if first == "Medication ID Description" and header_row is None:
- header_row = i
- if header_row is None:
- continue
- df = pd.read_excel(path, header=header_row).dropna(how="all")
- basket_id = meta.get("basket_id")
- for _, r in df.iterrows():
- med_id = to_str(r.get("Medication ID"))
- if not med_id or not basket_id:
- continue
- rows.append({
- "_id": f"{basket_id}:{med_id}",
- "study": study,
- "site_id": meta.get("site_id"),
- "investigator": meta.get("investigator"),
- "location": meta.get("location"),
- "basket_id": basket_id,
- "destruction_date": to_date(meta.get("destruction_date")),
- "medication_description": to_str(r.get("Medication ID Description")),
- "medication_id": med_id,
- "packaged_lot_description": to_str(r.get("Packaged Lot description")),
- "comments": to_str(r.get("Comments")),
- })
- by_id = {r["_id"]: r for r in rows}
- return list(by_id.values())
-
-
-# ── hlavní import ────────────────────────────────────────────────────────────
-
-def import_study(study):
- print(f"\n [{study}] parsovani XLSX...")
- shipments = parse_shipments_report(study)
- items = parse_shipment_details(study)
- inventory = parse_inventory(study)
- destruct = parse_destruction_files(study)
- print(f" Zasilky: {len(shipments)} | Polozky: {len(items)} | Sklad: {len(inventory)} | Destrukce: {len(destruct)}")
-
- import_id = log_import(study, f"drugs_{study}", "drugs", {
- "shipments": len(shipments),
- "shipment_items": len(items),
- "inventory": len(inventory),
- "destruction": len(destruct),
- })
- print(f" import_id = {import_id}")
-
- bulk_upsert_with_snapshot("iwrs_shipments", "iwrs_shipments_snapshots", shipments, import_id)
- bulk_upsert_with_snapshot("iwrs_shipment_items", "iwrs_shipment_items_snapshots", items, import_id)
- bulk_upsert_with_snapshot("iwrs_inventory", "iwrs_inventory_snapshots", inventory, import_id)
- bulk_upsert_only("iwrs_destruction", destruct, import_id)
-
-
-def run(studies):
- ensure_indexes()
- for s in studies:
- import_study(s)
-
-
-if __name__ == "__main__":
- studies = sys.argv[1:] if len(sys.argv) > 1 else ["77242113UCO3001", "42847922MDD3003"]
- run(studies)
diff --git a/IWRS/Drugs/Trash/run_all.py b/IWRS/Drugs/Trash/run_all.py
deleted file mode 100644
index 136d2f3..0000000
--- a/IWRS/Drugs/Trash/run_all.py
+++ /dev/null
@@ -1,245 +0,0 @@
-"""
-Kompletní pipeline pro Drugs:
- 1. Onsite inventory detail (per site, vždy přepisuje)
- 2. IP destruction (per košík, přeskočí již existující soubory)
- 3. Shipments report (jeden soubor na studii, přepisuje)
- 4. Shipment details (per zásilka CZ, vždy přepisuje)
- 5. Import do MongoDB (studie.iwrs_shipments / iwrs_shipment_items / iwrs_inventory / iwrs_destruction)
-
-Spusť tento skript — zpracuje obě studie automaticky.
-"""
-
-import os
-import glob
-import re
-import datetime
-
-import sys
-import pandas as pd
-from playwright.sync_api import sync_playwright
-
-import import_to_mongo as drugs_mongo
-
-BASE_URL = "https://janssen.4gclinical.com"
-EMAIL = "vbuzalka@its.jnj.com"
-PASSWORD = "Vlado123++-+"
-
-STUDIES = ["77242113UCO3001", "42847922MDD3003"]
-
-SITES = {
- "77242113UCO3001": [
- "DD5-CZ10001", "DD5-CZ10003", "DD5-CZ10006", "DD5-CZ10009",
- "DD5-CZ10010", "DD5-CZ10012", "DD5-CZ10013", "DD5-CZ10015",
- "DD5-CZ10016", "DD5-CZ10020", "DD5-CZ10021", "DD5-CZ10022",
- ],
- "42847922MDD3003": [
- "S10-CZ10002", "S10-CZ10004", "S10-CZ10005",
- "S10-CZ10008", "S10-CZ10011", "S10-CZ10012",
- ],
-}
-
-BASE_DIR = os.path.dirname(os.path.abspath(__file__))
-
-
-
-# ── login ────────────────────────────────────────────────────────────────────
-
-def login(page, study):
- page.goto(BASE_URL)
- page.wait_for_load_state("networkidle")
- page.get_by_label("Email *").fill(EMAIL)
- page.get_by_label("Password *").fill(PASSWORD)
- page.locator("#login__submit").click()
- page.wait_for_load_state("networkidle")
- page.get_by_label("Study *").click()
- page.get_by_role("option", name=study).click()
- page.get_by_role("button", name="SELECT").click()
- page.wait_for_load_state("networkidle")
-
-
-# ── download funkce ──────────────────────────────────────────────────────────
-
-def download_inventory(page, study):
- out_dir = os.path.join(BASE_DIR, f"xls_reports_{study}")
- os.makedirs(out_dir, exist_ok=True)
-
- page.goto(f"{BASE_URL}/report/onsite_inventory_detail")
- page.wait_for_load_state("networkidle", timeout=120000)
-
- for site_id in SITES[study]:
- print(f" [{site_id}] inventory...")
- page.locator('input[placeholder="search"], input[type="text"]').first.click()
- page.get_by_role("option", name=site_id).click()
- page.wait_for_load_state("networkidle", timeout=120000)
-
- filename = os.path.join(out_dir, f"onsite_inventory_detail_{site_id}.xlsx")
- with page.expect_download(timeout=120000) as dl:
- page.get_by_role("button", name="Download XLS").click()
- dl.value.save_as(filename)
-
- page.get_by_role("button", name="Clear").click()
- page.wait_for_load_state("networkidle", timeout=120000)
- print(f" Inventory OK ({len(SITES[study])} center)")
-
-
-def download_destruction(page, study):
- out_dir = os.path.join(BASE_DIR, f"xls_ip_destruction_{study}")
- os.makedirs(out_dir, exist_ok=True)
-
- page.goto(f"{BASE_URL}/report/ip_destruction_form")
- page.wait_for_load_state("networkidle", timeout=120000)
-
- page.locator('input[placeholder="search"], input[type="text"]').first.click()
- page.wait_for_timeout(1000)
- baskets = [b.strip() for b in page.locator("mat-option").all_inner_texts()
- if b.strip() and b.strip() != "No results found"]
- page.keyboard.press("Escape")
- page.wait_for_timeout(500)
-
- if not baskets:
- print(" Žádné destruction košíky")
- return
-
- new_count = 0
- for basket in baskets:
- filename = os.path.join(out_dir, f"ip_destruction_basket_{basket}.xlsx")
- if os.path.exists(filename):
- continue # destrukce se nemění — přeskočit
- print(f" [košík {basket}] stahování...")
- input_field = page.locator('input[placeholder="search"], input[type="text"]').first
- input_field.click()
- input_field.fill(basket)
- page.wait_for_timeout(500)
- page.locator("mat-option").first.dispatch_event("click")
- page.wait_for_load_state("networkidle", timeout=120000)
-
- with page.expect_download(timeout=120000) as dl:
- page.get_by_role("button", name="Download XLS").click()
- dl.value.save_as(filename)
- new_count += 1
-
- page.get_by_role("button", name="Clear").click()
- page.wait_for_load_state("networkidle", timeout=120000)
-
- print(f" Destruction OK ({new_count} nových, {len(baskets) - new_count} přeskočeno)")
-
-
-def download_shipments_report(page, study):
- out_dir = os.path.join(BASE_DIR, f"xls_shipments_{study}")
- os.makedirs(out_dir, exist_ok=True)
-
- page.goto(f"{BASE_URL}/report/shipments_report")
- page.wait_for_load_state("networkidle", timeout=120000)
-
- filename = os.path.join(out_dir, f"shipments_report_{study}.xlsx")
- with page.expect_download(timeout=120000) as dl:
- page.get_by_role("button", name="Download XLS").click()
- dl.value.save_as(filename)
- print(f" Shipments report OK")
-
-
-def download_shipment_details(page, study):
- out_dir = os.path.join(BASE_DIR, f"xls_shipment_details_{study}")
- os.makedirs(out_dir, exist_ok=True)
-
- # načti CZ shipment IDs z právě staženého shipments reportu
- report_path = os.path.join(BASE_DIR, f"xls_shipments_{study}", f"shipments_report_{study}.xlsx")
- raw = pd.read_excel(report_path, header=None)
- header_row = None
- for i, row in raw.iterrows():
- if "Shipment ID" in [str(v).strip() for v in row]:
- header_row = i
- break
- df = pd.read_excel(report_path, header=header_row)
- df = df.dropna(how="all")
- df = df[df["Location"].astype(str).str.contains("Czech", na=False, case=False)]
- cz_shipments = list(zip(
- df["Shipment ID"].astype(str).str.strip(),
- df["IRT Shipment Status"].astype(str).str.strip() if "IRT Shipment Status" in df.columns else [""] * len(df),
- ))
- print(f" CZ zásilek ke stažení: {len(cz_shipments)}")
-
- page.goto(f"{BASE_URL}/report/shipment_details_report")
- page.wait_for_load_state("networkidle", timeout=120000)
-
- skipped = 0
- for shipment, status in cz_shipments:
- filename = os.path.join(out_dir, f"shipment_details_{shipment}.xlsx")
- if os.path.exists(filename) and status.upper() == "RECEIVED":
- skipped += 1
- continue # finální stav, soubor se nemění
- input_field = page.locator('input[placeholder="search"], input[type="text"]').first
- input_field.click()
- input_field.fill(shipment)
- page.wait_for_timeout(500)
- page.locator("mat-option").first.dispatch_event("click")
- page.wait_for_load_state("networkidle", timeout=120000)
-
- with page.expect_download(timeout=120000) as dl:
- page.get_by_role("button", name="Download XLS").click()
- dl.value.save_as(filename)
- print(f" [{shipment}] ({status}) OK")
-
- page.get_by_role("button", name="Clear").click()
- page.wait_for_load_state("networkidle", timeout=120000)
-
- print(f" Přeskočeno (RECEIVED): {skipped}")
-
-
-# ── main ─────────────────────────────────────────────────────────────────────
-
-def main():
- os.chdir(BASE_DIR)
-
- # ── Stahování ────────────────────────────────────────────────────────────
- with sync_playwright() as p:
- for study in STUDIES:
- print(f"\n{'='*60}")
- print(f"[{study}] STAHOVÁNÍ")
- print(f"{'='*60}")
-
- browser = p.chromium.launch(headless=False)
- context = browser.new_context(accept_downloads=True)
- page = context.new_page()
-
- try:
- print(" Přihlášení...")
- login(page, study)
-
- print("\n [1/4] Onsite inventory...")
- download_inventory(page, study)
-
- print("\n [2/4] IP destruction...")
- download_destruction(page, study)
-
- print("\n [3/4] Shipments report...")
- download_shipments_report(page, study)
-
- print("\n [4/4] Shipment details (CZ)...")
- download_shipment_details(page, study)
-
- except Exception as e:
- import traceback
- print(f" CHYBA při stahování: {e}")
- traceback.print_exc()
- finally:
- browser.close()
-
- # ── Import do MongoDB ─────────────────────────────────────────────────────
- print(f"\n{'='*60}")
- print("IMPORT DO MongoDB")
- print(f"{'='*60}")
-
- try:
- drugs_mongo.run(STUDIES)
- except Exception as e:
- import traceback
- print(f" CHYBA při importu: {e}")
- traceback.print_exc()
-
- print(f"\n{'='*60}")
- print("Vše hotovo.")
- print(f"{'='*60}")
-
-
-main()
diff --git a/IWRS/Drugs/Working/_create_tables.py b/IWRS/Drugs/Working/_create_tables.py
deleted file mode 100644
index c364929..0000000
--- a/IWRS/Drugs/Working/_create_tables.py
+++ /dev/null
@@ -1,139 +0,0 @@
-import mysql.connector
-import db_config
-
-conn = mysql.connector.connect(
- host=db_config.DB_HOST, port=db_config.DB_PORT,
- user=db_config.DB_USER, password=db_config.DB_PASSWORD,
- database=db_config.DB_NAME
-)
-c = conn.cursor()
-
-# Přidat report_type do iwrs_import (pokud ještě neexistuje)
-try:
- c.execute("""ALTER TABLE iwrs_import
- ADD COLUMN report_type VARCHAR(20) NOT NULL DEFAULT 'patients'
- AFTER source_file""")
- print("ALTER TABLE iwrs_import OK — report_type přidán")
-except mysql.connector.errors.DatabaseError as e:
- if "Duplicate column" in str(e):
- print("report_type již existuje — přeskočeno")
- else:
- raise
-
-stmts = [
- (
- "iwrs_shipments",
- """CREATE TABLE IF NOT EXISTS iwrs_shipments (
- id INT AUTO_INCREMENT PRIMARY KEY,
- import_id INT NOT NULL,
- study VARCHAR(20) NOT NULL,
- shipment_id VARCHAR(20) NOT NULL,
- status VARCHAR(50),
- type VARCHAR(30),
- ship_from VARCHAR(50),
- ship_to_site VARCHAR(50),
- location VARCHAR(50),
- request_date DATE,
- shipped_date DATE,
- received_date DATE,
- received_by VARCHAR(100),
- delivered_date_utc DATE,
- delivery_recipient VARCHAR(100),
- delivery_details VARCHAR(200),
- cancelled_date DATE,
- total_medication_ids SMALLINT,
- tracking_no VARCHAR(100),
- shipping_category VARCHAR(50),
- expected_arrival DATE,
- FOREIGN KEY (import_id) REFERENCES iwrs_import(import_id),
- INDEX idx_import (import_id),
- INDEX idx_study_shipment (study, shipment_id)
-)"""
- ),
- (
- "iwrs_shipment_items",
- """CREATE TABLE IF NOT EXISTS iwrs_shipment_items (
- id INT AUTO_INCREMENT PRIMARY KEY,
- import_id INT NOT NULL,
- study VARCHAR(20) NOT NULL,
- shipment_id VARCHAR(20) NOT NULL,
- destination_location VARCHAR(50),
- shipment_status VARCHAR(50),
- shipment_type VARCHAR(30),
- destination_site VARCHAR(50),
- investigator VARCHAR(100),
- medication_description VARCHAR(200),
- medication_type VARCHAR(50),
- medication_id VARCHAR(20),
- packaged_lot_no VARCHAR(50),
- packaged_lot_description VARCHAR(100),
- container_id VARCHAR(50),
- quantity SMALLINT,
- expiration_date DATE,
- item_status VARCHAR(50),
- FOREIGN KEY (import_id) REFERENCES iwrs_import(import_id),
- INDEX idx_import (import_id),
- INDEX idx_med_id (medication_id)
-)"""
- ),
- (
- "iwrs_inventory",
- """CREATE TABLE IF NOT EXISTS iwrs_inventory (
- id INT AUTO_INCREMENT PRIMARY KEY,
- import_id INT NOT NULL,
- study VARCHAR(20) NOT NULL,
- site VARCHAR(50),
- investigator VARCHAR(100),
- location VARCHAR(50),
- medication_id VARCHAR(20),
- packaged_lot_no VARCHAR(50),
- original_expiration_date DATE,
- expiration_date DATE,
- received_date DATE,
- receipt_user VARCHAR(100),
- subject_identifier VARCHAR(20),
- quantity_assigned SMALLINT,
- irt_transaction VARCHAR(100),
- date_assigned DATE,
- assignment_user VARCHAR(100),
- dispensation_status VARCHAR(50),
- dispensing_date DATE,
- quantity_dispensed SMALLINT,
- dispensing_user VARCHAR(100),
- quantity_returned SMALLINT,
- date_returned DATE,
- return_user VARCHAR(100),
- FOREIGN KEY (import_id) REFERENCES iwrs_import(import_id),
- INDEX idx_import (import_id),
- INDEX idx_site (study, site)
-)"""
- ),
- (
- "iwrs_destruction",
- """CREATE TABLE IF NOT EXISTS iwrs_destruction (
- id INT AUTO_INCREMENT PRIMARY KEY,
- study VARCHAR(20) NOT NULL,
- site_id VARCHAR(50),
- investigator VARCHAR(100),
- location VARCHAR(50),
- basket_id VARCHAR(20) NOT NULL,
- destruction_date DATE,
- medication_description VARCHAR(200),
- medication_id VARCHAR(20),
- packaged_lot_description VARCHAR(100),
- comments VARCHAR(500),
- imported_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
- UNIQUE KEY uq_destruction (study, basket_id, medication_id),
- INDEX idx_study_basket (study, basket_id)
-)"""
- ),
-]
-
-for name, sql in stmts:
- c.execute(sql)
- print(f"OK: {name}")
-
-conn.commit()
-c.close()
-conn.close()
-print("\nVšechny tabulky připraveny.")
diff --git a/IWRS/Drugs/Working/create_accountability_report.py b/IWRS/Drugs/Working/create_accountability_report.py
deleted file mode 100644
index 5bb5196..0000000
--- a/IWRS/Drugs/Working/create_accountability_report.py
+++ /dev/null
@@ -1,364 +0,0 @@
-import sys
-import os
-import mysql.connector
-import pandas as pd
-from datetime import date
-from pathlib import Path
-from openpyxl import load_workbook
-from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
-from openpyxl.utils import get_column_letter
-
-sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), ".."))
-import db_config
-
-STUDY = "42847922MDD3003"
-# STUDY = "77242113UCO3001"
-
-BASE_DIR = Path(os.path.dirname(os.path.abspath(__file__)))
-OUTPUT_DIR = BASE_DIR / "output"
-OUTPUT_FILE = OUTPUT_DIR / f"{date.today().strftime('%Y-%m-%d')} {STUDY} CZ IWRS overview.xlsx"
-
-DATE_COLUMNS = {
- "Orig Exp Date", "Exp Date", "Rcv Date",
- "Date Asgn", "Disp Date", "Date Ret", "Destroyed", "Max Visit Date",
-}
-
-COLUMN_WIDTHS = {
- "Site": 14,
- "Med ID": 10,
- "Lot No.": 12,
- "Orig Exp Date": 16,
- "Exp Date": 14,
- "Rcv Date": 14,
- "Rcpt User": 22,
- "Subject ID": 14,
- "Qty Asgn": 9,
- "IRT Tx": 8,
- "Date Asgn": 14,
- "Asgn User": 20,
- "Disp Status": 16,
- "Disp Date": 14,
- "Qty Disp": 9,
- "Disp User": 20,
- "Qty Ret": 10,
- "Date Ret": 14,
- "Ret User": 18,
- "Destroyed": 14,
- "Basket No.": 12,
- "Max Visit Date": 16,
-}
-
-# shipments sheet: kolík kde začínají detail sloupce (1-based, pro format_shipment_sheet)
-N_SHIP_COLS = 9
-
-
-# ── DB ────────────────────────────────────────────────────────────────────────
-
-def get_conn():
- return mysql.connector.connect(
- host=db_config.DB_HOST, port=db_config.DB_PORT,
- user=db_config.DB_USER, password=db_config.DB_PASSWORD,
- database=db_config.DB_NAME,
- )
-
-
-def get_latest_import_id(cursor, study):
- cursor.execute(
- "SELECT MAX(import_id) AS mid FROM iwrs_import WHERE study=%s AND report_type='drugs'",
- (study,),
- )
- row = cursor.fetchone()
- mid = row["mid"]
- if mid is None:
- raise RuntimeError(f"Žádná data v MySQL pro studii {study}")
- return mid
-
-
-# ── Načítání dat z MySQL ──────────────────────────────────────────────────────
-
-def load_inventory(cursor, study, import_id):
- """
- Vrátí DataFrame s inventory + destruction join.
- Sloupce jsou rovnou přejmenované pro downstream funkce.
- """
- sql = """
- SELECT
- i.site AS Site,
- i.medication_id AS `Med ID`,
- i.packaged_lot_no AS `Lot No.`,
- i.original_expiration_date AS `Orig Exp Date`,
- i.expiration_date AS `Exp Date`,
- i.received_date AS `Rcv Date`,
- i.receipt_user AS `Rcpt User`,
- i.subject_identifier AS `Subject ID`,
- i.quantity_assigned AS `Qty Asgn`,
- i.irt_transaction AS `IRT Tx`,
- i.date_assigned AS `Date Asgn`,
- i.assignment_user AS `Asgn User`,
- i.dispensation_status AS `Disp Status`,
- i.dispensing_date AS `Disp Date`,
- i.quantity_dispensed AS `Qty Disp`,
- i.dispensing_user AS `Disp User`,
- i.quantity_returned AS `Qty Ret`,
- i.date_returned AS `Date Ret`,
- i.return_user AS `Ret User`,
- d.destruction_date AS Destroyed,
- d.basket_id AS `Basket No.`
- FROM iwrs_inventory i
- LEFT JOIN (
- SELECT medication_id,
- ANY_VALUE(basket_id) AS basket_id,
- ANY_VALUE(destruction_date) AS destruction_date
- FROM iwrs_destruction
- WHERE study = %s
- GROUP BY medication_id
- ) d ON d.medication_id = i.medication_id
- WHERE i.import_id = %s
- AND i.study = %s
- ORDER BY i.site, i.received_date, i.medication_id
- """
- cursor.execute(sql, (study, import_id, study))
- rows = cursor.fetchall()
- df = pd.DataFrame(rows)
- for col in DATE_COLUMNS:
- if col in df.columns:
- df[col] = pd.to_datetime(df[col], errors="coerce")
- print(f" Inventory: {len(df)} kitu")
- return df
-
-
-def load_shipments(cursor, study, import_id):
- """
- Vrátí DataFrame se spojenými shipments + items.
- """
- sql = """
- SELECT
- s.shipment_id AS `Shipment ID`,
- s.status AS `IRT Shipment Status`,
- s.type AS Type,
- s.ship_from AS `Shipment From`,
- s.ship_to_site AS `Ship To:`,
- s.request_date AS `Request Date`,
- s.received_date AS `Received Date`,
- s.received_by AS `Received by`,
- s.expected_arrival AS `Expected Arrival`,
- i.investigator AS Investigator,
- i.medication_description AS `Medication Description`,
- i.medication_id AS `Medication ID`,
- i.packaged_lot_no AS `Packaged Lot number`,
- i.expiration_date AS `Expiration Date`,
- i.item_status AS Status
- FROM iwrs_shipments s
- JOIN iwrs_shipment_items i
- ON i.study = s.study
- AND i.shipment_id = s.shipment_id
- AND i.import_id = %s
- WHERE s.import_id = %s
- AND s.study = %s
- ORDER BY s.ship_to_site, s.shipment_id, i.medication_id
- """
- cursor.execute(sql, (import_id, import_id, study))
- rows = cursor.fetchall()
- df = pd.DataFrame(rows)
- for col in ("Request Date", "Received Date", "Expiration Date", "Expected Arrival"):
- if col in df.columns:
- df[col] = pd.to_datetime(df[col], errors="coerce")
- print(f" Shipments: {df['Shipment ID'].nunique() if len(df) else 0} zásilek, {len(df)} kitu")
- return df
-
-
-# ── Odvozené sheety ───────────────────────────────────────────────────────────
-
-def build_site_summary(shipments_df):
- STATUS_COLS = ["Available", "Assigned", "Dispensed", "Returned by Subject"]
- pivot = shipments_df.groupby("Ship To:")["Status"].value_counts().unstack(fill_value=0)
- for s in STATUS_COLS:
- if s not in pivot.columns:
- pivot[s] = 0
- pivot = (
- pivot[STATUS_COLS]
- .reset_index()
- .rename(columns={"Ship To:": "Site", "Returned by Subject": "Returned"})
- .sort_values("Site")
- .reset_index(drop=True)
- )
- pivot["Total"] = pivot[["Available", "Assigned", "Dispensed", "Returned"]].sum(axis=1)
- print(f" Site Summary: {len(pivot)} center")
- return pivot
-
-
-def build_expired(df):
- today = date.today()
- mask = (
- df["Basket No."].isna() &
- df["Subject ID"].isna() &
- (df["Exp Date"] < pd.Timestamp(today))
- )
- filtered = df[mask].copy().reset_index(drop=True)
- sheet_name = f"Expired as of {today.strftime('%d-%b-%Y')}"
- print(f" Expired: {len(filtered)}")
- return filtered, sheet_name
-
-
-def build_assigned_not_dispensed(df):
- mask = df["Subject ID"].notna() & df["Disp Date"].isna()
- filtered = df[mask].copy().reset_index(drop=True)
- print(f" Assigned not dispensed: {len(filtered)}")
- return filtered
-
-
-def build_not_returned(df):
- no_ret = df[
- df["Date Ret"].isna() &
- df["Subject ID"].notna() &
- (df["Disp Status"].fillna("").str.upper() != "NOT DISPENSED")
- ].copy()
- max_asgn = df.groupby("Subject ID")["Date Asgn"].max().rename("Max Visit Date")
- no_ret = no_ret.join(max_asgn, on="Subject ID")
- filtered = no_ret[no_ret["Date Asgn"] < no_ret["Max Visit Date"]].copy()
- filtered = filtered.drop(columns=["Qty Ret", "Date Ret", "Ret User", "Destroyed", "Basket No."])
- filtered = filtered.reset_index(drop=True)
- print(f" Not returned: {len(filtered)}")
- return filtered
-
-
-def build_kits_for_destruction(df):
- mask = (
- df["Basket No."].isna() &
- (df["Date Ret"].notna() | (df["Disp Status"].fillna("").str.upper() == "NOT DISPENSED"))
- )
- filtered = (
- df[mask]
- .copy()
- .sort_values(["Site", "Date Ret"], ascending=[True, True])
- .drop(columns=["Destroyed", "Basket No."])
- .reset_index(drop=True)
- )
- print(f" Kits for destruction: {len(filtered)}")
- return filtered
-
-
-# ── Formátování ───────────────────────────────────────────────────────────────
-
-def format_sheet(ws, header_color, highlight_col=None, highlight_color=None):
- thin = Side(style="thin", color="000000")
- border = Border(left=thin, right=thin, top=thin, bottom=thin)
- header_fill = PatternFill("solid", start_color=header_color)
- header_font = Font(bold=True, color="FFFFFF", name="Arial", size=10)
- row_font = Font(name="Arial", size=10)
- hi_fill = PatternFill("solid", start_color=highlight_color) if highlight_color else None
-
- headers = [cell.value for cell in ws[1]]
-
- for cell in ws[1]:
- cell.fill = header_fill
- cell.font = header_font
- cell.alignment = Alignment(horizontal="center", vertical="center", wrap_text=False)
- cell.border = border
-
- for row in ws.iter_rows(min_row=2, max_row=ws.max_row):
- for cell in row:
- col_name = headers[cell.column - 1] if cell.column <= len(headers) else None
- cell.font = row_font
- cell.border = border
- cell.alignment = Alignment(horizontal="center")
- if col_name in DATE_COLUMNS:
- cell.number_format = "DD-MMM-YYYY"
- if hi_fill and col_name == highlight_col:
- cell.fill = hi_fill
-
- for cell in ws[1]:
- width = COLUMN_WIDTHS.get(cell.value, 14)
- ws.column_dimensions[get_column_letter(cell.column)].width = width
-
- ws.auto_filter.ref = ws.dimensions
- ws.freeze_panes = "A2"
-
-
-def format_shipment_sheet(ws, header_color_ship, header_color_detail, n_ship_cols):
- thin = Side(style="thin", color="000000")
- border = Border(left=thin, right=thin, top=thin, bottom=thin)
- hfont = Font(bold=True, color="FFFFFF", name="Arial", size=10)
- dfont = Font(name="Arial", size=10)
- fill_ship = PatternFill("solid", start_color=header_color_ship)
- fill_detail = PatternFill("solid", start_color=header_color_detail)
-
- for cell in ws[1]:
- cell.fill = fill_ship if cell.column <= n_ship_cols else fill_detail
- cell.font = hfont
- cell.alignment = Alignment(horizontal="center", vertical="center", wrap_text=True)
- cell.border = border
- ws.column_dimensions[get_column_letter(cell.column)].width = min(
- len(str(cell.value or "")) + 4, 35
- )
- ws.row_dimensions[1].height = 30
-
- for row in ws.iter_rows(min_row=2, max_row=ws.max_row):
- for cell in row:
- cell.font = dfont
- cell.border = border
- cell.alignment = Alignment(horizontal="center", vertical="center")
- if cell.value.__class__.__name__ in ("datetime", "date", "Timestamp"):
- cell.number_format = "DD-MMM-YYYY"
-
- ws.auto_filter.ref = ws.dimensions
- ws.freeze_panes = "A2"
-
-
-# ── Main ──────────────────────────────────────────────────────────────────────
-
-def main():
- OUTPUT_DIR.mkdir(exist_ok=True)
-
- print(f"\nNačítám data z MySQL pro {STUDY}...")
- conn = get_conn()
- cursor = conn.cursor(dictionary=True)
- import_id = get_latest_import_id(cursor, STUDY)
- print(f" import_id = {import_id}")
-
- df = load_inventory(cursor, STUDY, import_id)
- shipments_df = load_shipments(cursor, STUDY, import_id)
-
- cursor.close()
- conn.close()
-
- expired_df, expired_sheet = build_expired(df)
- assigned_df = build_assigned_not_dispensed(df)
- not_returned_df = build_not_returned(df)
- destruction_df = build_kits_for_destruction(df)
- site_summary_df = build_site_summary(shipments_df)
-
- with pd.ExcelWriter(OUTPUT_FILE, engine="openpyxl") as writer:
- df.to_excel( writer, index=False, sheet_name="CountryMedicationOverview")
- expired_df.to_excel( writer, index=False, sheet_name=expired_sheet)
- assigned_df.to_excel( writer, index=False, sheet_name="Assigned not dispensed")
- not_returned_df.to_excel( writer, index=False, sheet_name="Not returned")
- destruction_df.to_excel( writer, index=False, sheet_name="Kits for destruction")
- shipments_df.to_excel( writer, index=False, sheet_name="Shipments")
- site_summary_df.to_excel( writer, index=False, sheet_name="Site Summary")
-
- wb = load_workbook(OUTPUT_FILE)
-
- ws_main = wb["CountryMedicationOverview"]
- format_sheet(ws_main, header_color="1F4E79")
- new_col_fill = PatternFill("solid", start_color="E2EFDA")
- headers_main = [c.value for c in ws_main[1]]
- for row in ws_main.iter_rows(min_row=2, max_row=ws_main.max_row):
- for cell in row:
- col_name = headers_main[cell.column - 1] if cell.column <= len(headers_main) else None
- if col_name in ("Destroyed", "Basket No."):
- cell.fill = new_col_fill
-
- format_sheet(wb[expired_sheet], header_color="C00000", highlight_col="Exp Date", highlight_color="FFE0E0")
- format_sheet(wb["Assigned not dispensed"], header_color="833C00", highlight_col="Subject ID", highlight_color="FFF2CC")
- format_sheet(wb["Not returned"], header_color="375623", highlight_col="Max Visit Date", highlight_color="E2EFDA")
- format_sheet(wb["Kits for destruction"], header_color="595959")
- format_shipment_sheet(wb["Shipments"], "1F4E79", "375623", N_SHIP_COLS)
- format_sheet(wb["Site Summary"], header_color="1F4E79")
-
- wb.save(OUTPUT_FILE)
- print(f"\nUloženo: {OUTPUT_FILE} ({len(df)} řádků, sheety: {wb.sheetnames})")
-
-
-if __name__ == "__main__":
- main()
diff --git a/IWRS/Drugs/Working/create_shipment_report.py b/IWRS/Drugs/Working/create_shipment_report.py
deleted file mode 100644
index 3ac66c8..0000000
--- a/IWRS/Drugs/Working/create_shipment_report.py
+++ /dev/null
@@ -1,205 +0,0 @@
-import sys
-import os
-import mysql.connector
-import openpyxl
-from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
-from openpyxl.utils import get_column_letter
-from datetime import date
-import pandas as pd
-
-# db_config.py je v nadřazeném adresáři (Drugs/)
-sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), ".."))
-import db_config
-
-STUDY = "77242113UCO3001"
-OUTPUT_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "output")
-
-os.makedirs(OUTPUT_DIR, exist_ok=True)
-
-
-def get_conn():
- return mysql.connector.connect(
- host=db_config.DB_HOST, port=db_config.DB_PORT,
- user=db_config.DB_USER, password=db_config.DB_PASSWORD,
- database=db_config.DB_NAME,
- )
-
-
-def load_data(study):
- conn = get_conn()
- cursor = conn.cursor(dictionary=True)
-
- # nejnovější import_id pro danou studii
- cursor.execute(
- "SELECT MAX(import_id) AS mid FROM iwrs_import WHERE study=%s AND report_type='drugs'",
- (study,),
- )
- row = cursor.fetchone()
- import_id = row["mid"]
- if import_id is None:
- raise RuntimeError(f"Žádná data v MySQL pro studii {study}")
- print(f" import_id = {import_id}")
-
- sql = """
- SELECT
- s.shipment_id,
- s.status AS irt_shipment_status,
- s.type,
- s.ship_from AS shipment_from,
- s.ship_to_site AS ship_to,
- s.request_date,
- s.received_date,
- s.received_by,
- s.expected_arrival,
- i.investigator,
- i.medication_description,
- i.medication_id,
- i.packaged_lot_no,
- i.expiration_date,
- i.item_status AS status
- FROM iwrs_shipments s
- JOIN iwrs_shipment_items i
- ON i.study = s.study
- AND i.shipment_id = s.shipment_id
- AND i.import_id = %s
- WHERE s.import_id = %s
- AND s.study = %s
- ORDER BY s.ship_to_site, s.shipment_id, i.medication_id
- """
- cursor.execute(sql, (import_id, import_id, study))
- rows = cursor.fetchall()
- cursor.close()
- conn.close()
- print(f" Načteno řádků: {len(rows)}")
- return rows
-
-
-# shipment sloupce (modrý header) / detail sloupce (zelený header)
-SHIP_COLS = [
- ("shipment_id", "Shipment ID"),
- ("irt_shipment_status","IRT Shipment Status"),
- ("type", "Type"),
- ("shipment_from", "Shipment From"),
- ("ship_to", "Ship To:"),
- ("request_date", "Request Date"),
- ("received_date", "Received Date"),
- ("received_by", "Received by"),
- ("expected_arrival", "Expected Arrival"),
-]
-
-DETAIL_COLS = [
- ("investigator", "Investigator"),
- ("medication_description", "Medication Description"),
- ("medication_id", "Medication ID"),
- ("packaged_lot_no", "Packaged Lot number"),
- ("expiration_date", "Expiration Date"),
- ("status", "Status"),
-]
-
-ALL_COLS = SHIP_COLS + DETAIL_COLS
-N_SHIP_COLS = len(SHIP_COLS)
-
-HEADER_FILL_SHIP = PatternFill("solid", fgColor="1F4E79")
-HEADER_FILL_DETAIL = PatternFill("solid", fgColor="375623")
-HEADER_FONT = Font(name="Arial", bold=True, color="FFFFFF", size=10)
-DATA_FONT = Font(name="Arial", size=10)
-THIN_BORDER = Border(
- left=Side(style="thin", color="BFBFBF"),
- right=Side(style="thin", color="BFBFBF"),
- bottom=Side(style="thin", color="BFBFBF"),
-)
-
-
-def write_shipments_sheet(wb, rows):
- ws = wb.active
- ws.title = "Shipments"
-
- # záhlaví
- for ci, (_, label) in enumerate(ALL_COLS, 1):
- cell = ws.cell(row=1, column=ci, value=label)
- cell.font = HEADER_FONT
- cell.fill = HEADER_FILL_SHIP if ci <= N_SHIP_COLS else HEADER_FILL_DETAIL
- cell.alignment = Alignment(horizontal="center", vertical="center", wrap_text=True)
- cell.border = THIN_BORDER
- ws.row_dimensions[1].height = 30
-
- # data
- for ri, row in enumerate(rows, 2):
- for ci, (key, _) in enumerate(ALL_COLS, 1):
- val = row[key]
- cell = ws.cell(row=ri, column=ci, value=val)
- cell.font = DATA_FONT
- cell.border = THIN_BORDER
- cell.alignment = Alignment(horizontal="center", vertical="center")
- if isinstance(val, date):
- cell.number_format = "DD-MMM-YYYY"
-
- ws.auto_filter.ref = ws.dimensions
- ws.freeze_panes = "A2"
-
- # šířky sloupců
- for ci, (key, label) in enumerate(ALL_COLS, 1):
- vals = [label] + [str(r[key]) for r in rows if r[key] is not None]
- ws.column_dimensions[get_column_letter(ci)].width = min(
- max((len(v) for v in vals), default=10) + 2, 35
- )
-
-
-def write_summary_sheet(wb, rows):
- STATUS_COLS = ["Available", "Assigned", "Dispensed", "Returned by Subject"]
-
- df = pd.DataFrame(rows)
- pivot = df.groupby("ship_to")["status"].value_counts().unstack(fill_value=0)
- for s in STATUS_COLS:
- if s not in pivot.columns:
- pivot[s] = 0
- pivot = (
- pivot[STATUS_COLS]
- .reset_index()
- .rename(columns={"ship_to": "Site", "Returned by Subject": "Returned"})
- .sort_values("Site")
- .reset_index(drop=True)
- )
- pivot["Total"] = pivot[["Available", "Assigned", "Dispensed", "Returned"]].sum(axis=1)
-
- ws = wb.create_sheet("Site Summary")
- s_cols = ["Site", "Available", "Assigned", "Dispensed", "Returned", "Total"]
-
- for ci, col in enumerate(s_cols, 1):
- cell = ws.cell(row=1, column=ci, value=col)
- cell.font = HEADER_FONT
- cell.fill = PatternFill("solid", fgColor="1F4E79")
- cell.alignment = Alignment(horizontal="center", vertical="center")
- cell.border = THIN_BORDER
- ws.row_dimensions[1].height = 25
-
- for ri, (_, row) in enumerate(pivot.iterrows(), 2):
- for ci, col in enumerate(s_cols, 1):
- cell = ws.cell(row=ri, column=ci, value=row[col])
- cell.font = DATA_FONT
- cell.border = THIN_BORDER
- cell.alignment = Alignment(horizontal="center", vertical="center")
-
- for ci, col in enumerate(s_cols, 1):
- vals = [col] + [str(pivot.iloc[r][col]) for r in range(len(pivot))]
- ws.column_dimensions[get_column_letter(ci)].width = min(
- max(len(v) for v in vals) + 4, 35
- )
-
- ws.freeze_panes = "A2"
-
-
-def build_report():
- print(f"\nNačítám data z MySQL pro {STUDY}...")
- rows = load_data(STUDY)
-
- wb = openpyxl.Workbook()
- write_shipments_sheet(wb, rows)
- write_summary_sheet(wb, rows)
-
- outfile = os.path.join(OUTPUT_DIR, f"{date.today()} {STUDY} CZ Shipments.xlsx")
- wb.save(outfile)
- print(f"\nUloženo -> {outfile}")
-
-
-build_report()
diff --git a/IWRS/Drugs/Working/create_studie_report.py b/IWRS/Drugs/Working/create_studie_report.py
deleted file mode 100644
index dfbba66..0000000
--- a/IWRS/Drugs/Working/create_studie_report.py
+++ /dev/null
@@ -1,393 +0,0 @@
-import sys
-import os
-import mysql.connector
-import pandas as pd
-from datetime import date
-from pathlib import Path
-from openpyxl import load_workbook
-from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
-from openpyxl.utils import get_column_letter
-
-sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), ".."))
-import db_config
-
-STUDIES = [
- ("77242113UCO3001", "UCO"),
- ("42847922MDD3003", "MDD"),
-]
-
-BASE_DIR = Path(os.path.dirname(os.path.abspath(__file__)))
-OUTPUT_DIR = BASE_DIR / "output"
-
-DATE_COLUMNS = {
- "Orig Exp Date", "Exp Date", "Rcv Date",
- "Date Asgn", "Disp Date", "Date Ret", "Destroyed", "Max Visit Date",
-}
-
-COLUMN_WIDTHS = {
- "Site": 14,
- "Med ID": 10,
- "Lot No.": 12,
- "Orig Exp Date": 16,
- "Exp Date": 14,
- "Rcv Date": 14,
- "Rcpt User": 22,
- "Subject ID": 14,
- "Qty Asgn": 9,
- "IRT Tx": 8,
- "Date Asgn": 14,
- "Asgn User": 20,
- "Disp Status": 16,
- "Disp Date": 14,
- "Qty Disp": 9,
- "Disp User": 20,
- "Qty Ret": 10,
- "Date Ret": 14,
- "Ret User": 18,
- "Destroyed": 14,
- "Basket No.": 12,
- "Max Visit Date": 16,
-}
-
-N_SHIP_COLS = 9 # počet shipment sloupců (modrý header v Shipments sheetu)
-
-
-# ── DB ────────────────────────────────────────────────────────────────────────
-
-def get_conn():
- return mysql.connector.connect(
- host=db_config.DB_HOST, port=db_config.DB_PORT,
- user=db_config.DB_USER, password=db_config.DB_PASSWORD,
- database=db_config.DB_NAME,
- )
-
-
-def get_latest_import_id(cursor, study):
- cursor.execute(
- "SELECT MAX(import_id) AS mid FROM iwrs_import WHERE study=%s AND report_type='drugs'",
- (study,),
- )
- row = cursor.fetchone()
- mid = row["mid"]
- if mid is None:
- raise RuntimeError(f"Žádná data v MySQL pro studii {study}")
- return mid
-
-
-# ── Načítání dat ──────────────────────────────────────────────────────────────
-
-def load_inventory(cursor, study, import_id):
- sql = """
- SELECT
- i.site AS Site,
- i.medication_id AS `Med ID`,
- i.packaged_lot_no AS `Lot No.`,
- i.original_expiration_date AS `Orig Exp Date`,
- i.expiration_date AS `Exp Date`,
- i.received_date AS `Rcv Date`,
- i.receipt_user AS `Rcpt User`,
- i.subject_identifier AS `Subject ID`,
- i.quantity_assigned AS `Qty Asgn`,
- i.irt_transaction AS `IRT Tx`,
- i.date_assigned AS `Date Asgn`,
- i.assignment_user AS `Asgn User`,
- i.dispensation_status AS `Disp Status`,
- i.dispensing_date AS `Disp Date`,
- i.quantity_dispensed AS `Qty Disp`,
- i.dispensing_user AS `Disp User`,
- i.quantity_returned AS `Qty Ret`,
- i.date_returned AS `Date Ret`,
- i.return_user AS `Ret User`,
- d.destruction_date AS Destroyed,
- d.basket_id AS `Basket No.`
- FROM iwrs_inventory i
- LEFT JOIN (
- SELECT medication_id,
- ANY_VALUE(basket_id) AS basket_id,
- ANY_VALUE(destruction_date) AS destruction_date
- FROM iwrs_destruction
- WHERE study = %s
- GROUP BY medication_id
- ) d ON d.medication_id = i.medication_id
- WHERE i.import_id = %s
- AND i.study = %s
- ORDER BY i.site, i.received_date, i.medication_id
- """
- cursor.execute(sql, (study, import_id, study))
- rows = cursor.fetchall()
- df = pd.DataFrame(rows)
- for col in DATE_COLUMNS:
- if col in df.columns:
- df[col] = pd.to_datetime(df[col], errors="coerce")
- print(f" Inventory: {len(df)} kitu")
- return df
-
-
-def load_shipments(cursor, study, import_id):
- sql = """
- SELECT
- s.shipment_id AS `Shipment ID`,
- s.status AS `IRT Shipment Status`,
- s.type AS Type,
- s.ship_from AS `Shipment From`,
- s.ship_to_site AS `Ship To:`,
- s.request_date AS `Request Date`,
- s.received_date AS `Received Date`,
- s.received_by AS `Received by`,
- s.expected_arrival AS `Expected Arrival`,
- i.investigator AS Investigator,
- i.medication_description AS `Medication Description`,
- i.medication_id AS `Medication ID`,
- i.packaged_lot_no AS `Packaged Lot number`,
- i.expiration_date AS `Expiration Date`,
- i.item_status AS Status
- FROM iwrs_shipments s
- JOIN iwrs_shipment_items i
- ON i.study = s.study
- AND i.shipment_id = s.shipment_id
- AND i.import_id = %s
- WHERE s.import_id = %s
- AND s.study = %s
- ORDER BY s.ship_to_site, s.shipment_id, i.medication_id
- """
- cursor.execute(sql, (import_id, import_id, study))
- rows = cursor.fetchall()
- df = pd.DataFrame(rows)
- for col in ("Request Date", "Received Date", "Expiration Date", "Expected Arrival"):
- if col in df.columns:
- df[col] = pd.to_datetime(df[col], errors="coerce")
- n_ship = df["Shipment ID"].nunique() if len(df) else 0
- print(f" Shipments: {n_ship} zásilek, {len(df)} kitu")
- return df
-
-
-# ── Odvozené sheety ───────────────────────────────────────────────────────────
-
-def build_site_summary(shipments_df):
- STATUS_COLS = ["Available", "Assigned", "Dispensed", "Returned by Subject"]
- pivot = shipments_df.groupby("Ship To:")["Status"].value_counts().unstack(fill_value=0)
- for s in STATUS_COLS:
- if s not in pivot.columns:
- pivot[s] = 0
- pivot = (
- pivot[STATUS_COLS]
- .reset_index()
- .rename(columns={"Ship To:": "Site", "Returned by Subject": "Returned"})
- .sort_values("Site")
- .reset_index(drop=True)
- )
- pivot["Total"] = pivot[["Available", "Assigned", "Dispensed", "Returned"]].sum(axis=1)
- print(f" Site Summary: {len(pivot)} center")
- return pivot
-
-
-def build_expired(df):
- today = date.today()
- mask = (
- df["Basket No."].isna() &
- df["Subject ID"].isna() &
- (df["Exp Date"] < pd.Timestamp(today))
- )
- filtered = df[mask].copy().reset_index(drop=True)
- print(f" Expired: {len(filtered)}")
- return filtered
-
-
-def build_assigned_not_dispensed(df):
- mask = df["Subject ID"].notna() & df["Disp Date"].isna()
- filtered = df[mask].copy().reset_index(drop=True)
- print(f" Assigned not dispensed: {len(filtered)}")
- return filtered
-
-
-def build_not_returned(df):
- no_ret = df[
- df["Date Ret"].isna() &
- df["Subject ID"].notna() &
- (df["Disp Status"].fillna("").str.upper() != "NOT DISPENSED")
- ].copy()
- max_asgn = df.groupby("Subject ID")["Date Asgn"].max().rename("Max Visit Date")
- no_ret = no_ret.join(max_asgn, on="Subject ID")
- filtered = no_ret[no_ret["Date Asgn"] < no_ret["Max Visit Date"]].copy()
- filtered = filtered.drop(columns=["Qty Ret", "Date Ret", "Ret User", "Destroyed", "Basket No."])
- filtered = filtered.reset_index(drop=True)
- print(f" Not returned: {len(filtered)}")
- return filtered
-
-
-def build_kits_for_destruction(df):
- mask = (
- df["Basket No."].isna() &
- (df["Date Ret"].notna() | (df["Disp Status"].fillna("").str.upper() == "NOT DISPENSED"))
- )
- filtered = (
- df[mask]
- .copy()
- .sort_values(["Site", "Date Ret"], ascending=[True, True])
- .drop(columns=["Destroyed", "Basket No."])
- .reset_index(drop=True)
- )
- print(f" Kits for destruction: {len(filtered)}")
- return filtered
-
-
-# ── Formátování ───────────────────────────────────────────────────────────────
-
-def format_sheet(ws, header_color, highlight_col=None, highlight_color=None):
- thin = Side(style="thin", color="000000")
- border = Border(left=thin, right=thin, top=thin, bottom=thin)
- header_fill = PatternFill("solid", start_color=header_color)
- header_font = Font(bold=True, color="FFFFFF", name="Arial", size=10)
- row_font = Font(name="Arial", size=10)
- hi_fill = PatternFill("solid", start_color=highlight_color) if highlight_color else None
-
- headers = [cell.value for cell in ws[1]]
-
- for cell in ws[1]:
- cell.fill = header_fill
- cell.font = header_font
- cell.alignment = Alignment(horizontal="center", vertical="center", wrap_text=False)
- cell.border = border
-
- for row in ws.iter_rows(min_row=2, max_row=ws.max_row):
- for cell in row:
- col_name = headers[cell.column - 1] if cell.column <= len(headers) else None
- cell.font = row_font
- cell.border = border
- cell.alignment = Alignment(horizontal="center")
- if col_name in DATE_COLUMNS:
- cell.number_format = "DD-MMM-YYYY"
- if hi_fill and col_name == highlight_col:
- cell.fill = hi_fill
-
- for cell in ws[1]:
- width = COLUMN_WIDTHS.get(cell.value, 14)
- ws.column_dimensions[get_column_letter(cell.column)].width = width
-
- ws.auto_filter.ref = ws.dimensions
- ws.freeze_panes = "A2"
-
-
-def format_overview_sheet(ws):
- format_sheet(ws, header_color="1F4E79")
- new_col_fill = PatternFill("solid", start_color="E2EFDA")
- headers = [c.value for c in ws[1]]
- for row in ws.iter_rows(min_row=2, max_row=ws.max_row):
- for cell in row:
- col_name = headers[cell.column - 1] if cell.column <= len(headers) else None
- if col_name in ("Destroyed", "Basket No."):
- cell.fill = new_col_fill
-
-
-def format_shipment_sheet(ws):
- thin = Side(style="thin", color="000000")
- border = Border(left=thin, right=thin, top=thin, bottom=thin)
- hfont = Font(bold=True, color="FFFFFF", name="Arial", size=10)
- dfont = Font(name="Arial", size=10)
- fill_ship = PatternFill("solid", start_color="1F4E79")
- fill_detail = PatternFill("solid", start_color="375623")
-
- for cell in ws[1]:
- cell.fill = fill_ship if cell.column <= N_SHIP_COLS else fill_detail
- cell.font = hfont
- cell.alignment = Alignment(horizontal="center", vertical="center", wrap_text=True)
- cell.border = border
- ws.column_dimensions[get_column_letter(cell.column)].width = min(
- len(str(cell.value or "")) + 4, 35
- )
- ws.row_dimensions[1].height = 30
-
- for row in ws.iter_rows(min_row=2, max_row=ws.max_row):
- for cell in row:
- cell.font = dfont
- cell.border = border
- cell.alignment = Alignment(horizontal="center", vertical="center")
- if cell.value.__class__.__name__ in ("datetime", "date", "Timestamp"):
- cell.number_format = "DD-MMM-YYYY"
-
- ws.auto_filter.ref = ws.dimensions
- ws.freeze_panes = "A2"
-
-
-# ── Main ──────────────────────────────────────────────────────────────────────
-
-SHEETS_DEF = [
- ("CountryMedicationOverview", "overview"),
- ("Expired", "expired"),
- ("Assigned not dispensed", "assigned"),
- ("Not returned", "not_returned"),
- ("Kits for destruction", "destruction"),
- ("Shipments", "shipments"),
- ("Site Summary", "site_summary"),
-]
-
-FORMAT_MAP = {
- "overview": lambda ws: format_overview_sheet(ws),
- "expired": lambda ws: format_sheet(ws, "C00000", "Exp Date", "FFE0E0"),
- "assigned": lambda ws: format_sheet(ws, "833C00", "Subject ID", "FFF2CC"),
- "not_returned": lambda ws: format_sheet(ws, "375623", "Max Visit Date", "E2EFDA"),
- "destruction": lambda ws: format_sheet(ws, "595959"),
- "shipments": lambda ws: format_shipment_sheet(ws),
- "site_summary": lambda ws: format_sheet(ws, "1F4E79"),
-}
-
-
-def process_study(cursor, study):
- today = date.today().strftime("%d-%b-%Y")
- import_id = get_latest_import_id(cursor, study)
- print(f" import_id = {import_id}")
-
- df = load_inventory(cursor, study, import_id)
- shipments_df = load_shipments(cursor, study, import_id)
-
- expired_df = build_expired(df)
- assigned_df = build_assigned_not_dispensed(df)
- not_returned_df = build_not_returned(df)
- destruction_df = build_kits_for_destruction(df)
- site_summ_df = build_site_summary(shipments_df)
-
- return [
- df, expired_df, assigned_df, not_returned_df,
- destruction_df, shipments_df, site_summ_df,
- ]
-
-
-def save_study_report(study, data_frames):
- output_file = OUTPUT_DIR / f"{date.today().strftime('%Y-%m-%d')} {study} report.xlsx"
-
- with pd.ExcelWriter(output_file, engine="openpyxl") as writer:
- for (sheet_name, _), df_sheet in zip(SHEETS_DEF, data_frames):
- df_sheet.to_excel(writer, index=False, sheet_name=sheet_name)
-
- wb = load_workbook(output_file)
- for (sheet_name, fmt_key) in SHEETS_DEF:
- FORMAT_MAP[fmt_key](wb[sheet_name])
- wb.save(output_file)
- print(f" Uloženo: {output_file}")
-
-
-def main():
- OUTPUT_DIR.mkdir(exist_ok=True)
-
- conn = get_conn()
- cursor = conn.cursor(dictionary=True)
-
- for study, _ in STUDIES:
- print(f"\n{'='*55}")
- print(f"[{study}]")
- print(f"{'='*55}")
- try:
- data_frames = process_study(cursor, study)
- save_study_report(study, data_frames)
- except Exception as e:
- import traceback
- print(f" CHYBA: {e}")
- traceback.print_exc()
-
- cursor.close()
- conn.close()
- print(f"\nHotovo.")
-
-
-if __name__ == "__main__":
- main()
diff --git a/IWRS/Drugs/Working/download_ip_destruction.py b/IWRS/Drugs/Working/download_ip_destruction.py
deleted file mode 100644
index d139bd2..0000000
--- a/IWRS/Drugs/Working/download_ip_destruction.py
+++ /dev/null
@@ -1,76 +0,0 @@
-from playwright.sync_api import sync_playwright
-import os
-
-# ── CONFIG ──────────────────────────────────────────────────────────────────
-BASE_URL = "https://janssen.4gclinical.com"
-
-EMAIL = "vbuzalka@its.jnj.com"
-PASSWORD = "Vlado123++-+"
-
-# STUDY = "42847922MDD3003"
-STUDY = "77242113UCO3001"
-
-OUTPUT_DIR = f"xls_ip_destruction_{STUDY}"
-# ────────────────────────────────────────────────────────────────────────────
-
-def run(page, study):
- output_dir = f"xls_ip_destruction_{study}"
- os.makedirs(output_dir, exist_ok=True)
-
- page.goto(f"{BASE_URL}/report/ip_destruction_form")
- page.wait_for_load_state("networkidle", timeout=120000)
-
- page.locator('input[placeholder="search"], input[type="text"]').first.click()
- page.wait_for_timeout(1000)
- baskets = [b.strip() for b in page.locator('mat-option').all_inner_texts()
- if b.strip() and b.strip() != "No results found"]
- print(f" Nalezeno {len(baskets)} kosiku: {baskets}")
- page.keyboard.press("Escape")
- page.wait_for_timeout(500)
-
- if not baskets:
- print(" Zadne destruction kosite — preskakuji.")
- return
-
- for basket in baskets:
- filename = os.path.join(output_dir, f"ip_destruction_basket_{basket}.xlsx")
- if os.path.exists(filename):
- print(f" [{basket}] Preskakuji — existuje.")
- continue
- print(f" [{basket}] Stahuji...")
- input_field = page.locator('input[placeholder="search"], input[type="text"]').first
- input_field.click()
- input_field.fill(basket)
- page.wait_for_timeout(500)
- page.locator('mat-option').first.dispatch_event('click')
- page.wait_for_load_state("networkidle", timeout=120000)
-
- with page.expect_download(timeout=120000) as dl:
- page.get_by_role("button", name="Download XLS").click()
- dl.value.save_as(filename)
- print(f" [{basket}] OK")
-
- page.get_by_role("button", name="Clear").click()
- page.wait_for_load_state("networkidle", timeout=120000)
-
- print(" Destruction hotovo.")
-
-
-if __name__ == "__main__":
- from playwright.sync_api import sync_playwright
- with sync_playwright() as p:
- browser = p.chromium.launch(headless=False)
- context = browser.new_context(accept_downloads=True)
- page = context.new_page()
- page.goto(BASE_URL)
- page.wait_for_load_state("networkidle")
- page.get_by_label("Email *").fill(EMAIL)
- page.get_by_label("Password *").fill(PASSWORD)
- page.locator('#login__submit').click()
- page.wait_for_load_state("networkidle")
- page.get_by_label("Study *").click()
- page.get_by_role("option", name=STUDY).click()
- page.get_by_role("button", name="SELECT").click()
- page.wait_for_load_state("networkidle")
- run(page, STUDY)
- browser.close()
diff --git a/IWRS/Drugs/Working/download_reports.py b/IWRS/Drugs/Working/download_reports.py
deleted file mode 100644
index e67955a..0000000
--- a/IWRS/Drugs/Working/download_reports.py
+++ /dev/null
@@ -1,83 +0,0 @@
-from playwright.sync_api import sync_playwright
-import os
-
-# ── CONFIG ──────────────────────────────────────────────────────────────────
-BASE_URL = "https://janssen.4gclinical.com"
-
-EMAIL = "vbuzalka@its.jnj.com"
-PASSWORD = "Vlado123++-+"
-
-# STUDY = "42847922MDD3003"
-STUDY = "77242113UCO3001"
-
-SITES = {
- "42847922MDD3003": [
- "S10-CZ10002",
- "S10-CZ10004",
- "S10-CZ10005",
- "S10-CZ10008",
- "S10-CZ10011",
- "S10-CZ10012",
- ],
- "77242113UCO3001": [
- "DD5-CZ10001",
- "DD5-CZ10003",
- "DD5-CZ10006",
- "DD5-CZ10009",
- "DD5-CZ10010",
- "DD5-CZ10012",
- "DD5-CZ10013",
- "DD5-CZ10015",
- "DD5-CZ10016",
- "DD5-CZ10020",
- "DD5-CZ10021",
- "DD5-CZ10022",
- ],
-}
-
-OUTPUT_DIR = f"xls_reports_{STUDY}"
-# ────────────────────────────────────────────────────────────────────────────
-
-def run(page, study):
- output_dir = f"xls_reports_{study}"
- os.makedirs(output_dir, exist_ok=True)
-
- page.goto(f"{BASE_URL}/report/onsite_inventory_detail")
- page.wait_for_load_state("networkidle", timeout=120000)
-
- for site_id in SITES[study]:
- print(f" [{site_id}] Stahuji...")
- page.locator('input[placeholder="search"], input[type="text"]').first.click()
- page.get_by_role("option", name=site_id).click()
- page.wait_for_load_state("networkidle", timeout=120000)
-
- with page.expect_download(timeout=120000) as dl:
- page.get_by_role("button", name="Download XLS").click()
-
- dl.value.save_as(os.path.join(output_dir, f"onsite_inventory_detail_{site_id}.xlsx"))
- print(f" [{site_id}] OK")
-
- page.get_by_role("button", name="Clear").click()
- page.wait_for_load_state("networkidle", timeout=120000)
-
- print(" Inventory hotovo.")
-
-
-if __name__ == "__main__":
- from playwright.sync_api import sync_playwright
- with sync_playwright() as p:
- browser = p.chromium.launch(headless=False)
- context = browser.new_context(accept_downloads=True)
- page = context.new_page()
- page.goto(BASE_URL)
- page.wait_for_load_state("networkidle")
- page.get_by_label("Email *").fill(EMAIL)
- page.get_by_label("Password *").fill(PASSWORD)
- page.locator('#login__submit').click()
- page.wait_for_load_state("networkidle")
- page.get_by_label("Study *").click()
- page.get_by_role("option", name=STUDY).click()
- page.get_by_role("button", name="SELECT").click()
- page.wait_for_load_state("networkidle")
- run(page, STUDY)
- browser.close()
diff --git a/IWRS/Drugs/Working/download_shipment_details.py b/IWRS/Drugs/Working/download_shipment_details.py
deleted file mode 100644
index d024bb5..0000000
--- a/IWRS/Drugs/Working/download_shipment_details.py
+++ /dev/null
@@ -1,95 +0,0 @@
-from playwright.sync_api import sync_playwright
-import os
-import pandas as pd
-
-# ── CONFIG ──────────────────────────────────────────────────────────────────
-BASE_URL = "https://janssen.4gclinical.com"
-
-EMAIL = "vbuzalka@its.jnj.com"
-PASSWORD = "Vlado123++-+"
-
-STUDY = "42847922MDD3003"
-#STUDY = "77242113UCO3001"
-
-OUTPUT_DIR = f"xls_shipment_details_{STUDY}"
-# ────────────────────────────────────────────────────────────────────────────
-
-def get_cz_shipment_ids(study):
- path = f"xls_shipments_{study}/shipments_report_{study}.xlsx"
- if not os.path.exists(path):
- return None
- df = pd.read_excel(path, header=5)
- df.columns = df.columns.str.strip()
- df = df.dropna(how="all")
- df["Shipment ID"] = df["Shipment ID"].astype(str).str.strip()
- cz = df[df["Location"].str.contains("Czech", na=False, case=False)]
- return cz["Shipment ID"].tolist()
-
-
-def run(page, study):
- output_dir = f"xls_shipment_details_{study}"
- os.makedirs(output_dir, exist_ok=True)
-
- page.goto(f"{BASE_URL}/report/shipment_details_report")
- page.wait_for_load_state("networkidle", timeout=120000)
-
- cz_ids = get_cz_shipment_ids(study)
- if cz_ids is not None:
- shipments = cz_ids
- print(f" Filtrovano ze shipments reportu: {len(shipments)} CZ shipmentu")
- else:
- page.locator('input[placeholder="search"], input[type="text"]').first.click()
- page.wait_for_timeout(1000)
- shipments = [s.strip() for s in page.locator('mat-option').all_inner_texts()
- if s.strip() and s.strip() != "No results found"]
- print(f" Nalezeno {len(shipments)} shipmentu z dropdownu")
- page.keyboard.press("Escape")
- page.wait_for_timeout(500)
-
- if not shipments:
- print(" Zadne shipments — preskakuji.")
- return
-
- for shipment in shipments:
- filename = os.path.join(output_dir, f"shipment_details_{shipment}.xlsx")
- if os.path.exists(filename):
- print(f" [{shipment}] Preskakuji — existuje.")
- continue
- print(f" [{shipment}] Stahuji...")
-
- input_field = page.locator('input[placeholder="search"], input[type="text"]').first
- input_field.click()
- input_field.fill(shipment)
- page.wait_for_timeout(500)
- page.locator('mat-option').first.dispatch_event('click')
- page.wait_for_load_state("networkidle", timeout=120000)
-
- with page.expect_download(timeout=120000) as dl:
- page.get_by_role("button", name="Download XLS").click()
- dl.value.save_as(filename)
- print(f" [{shipment}] OK")
-
- page.get_by_role("button", name="Clear").click()
- page.wait_for_load_state("networkidle", timeout=120000)
-
- print(" Shipment details hotovo.")
-
-
-if __name__ == "__main__":
- from playwright.sync_api import sync_playwright
- with sync_playwright() as p:
- browser = p.chromium.launch(headless=False)
- context = browser.new_context(accept_downloads=True)
- page = context.new_page()
- page.goto(BASE_URL)
- page.wait_for_load_state("networkidle")
- page.get_by_label("Email *").fill(EMAIL)
- page.get_by_label("Password *").fill(PASSWORD)
- page.locator('#login__submit').click()
- page.wait_for_load_state("networkidle")
- page.get_by_label("Study *").click()
- page.get_by_role("option", name=STUDY).click()
- page.get_by_role("button", name="SELECT").click()
- page.wait_for_load_state("networkidle")
- run(page, STUDY)
- browser.close()
diff --git a/IWRS/Drugs/Working/download_shipments_report.py b/IWRS/Drugs/Working/download_shipments_report.py
deleted file mode 100644
index 5644e97..0000000
--- a/IWRS/Drugs/Working/download_shipments_report.py
+++ /dev/null
@@ -1,47 +0,0 @@
-from playwright.sync_api import sync_playwright
-import os
-
-# ── CONFIG ──────────────────────────────────────────────────────────────────
-BASE_URL = "https://janssen.4gclinical.com"
-
-EMAIL = "vbuzalka@its.jnj.com"
-PASSWORD = "Vlado123++-+"
-
-# STUDY = "42847922MDD3003"
-STUDY = "77242113UCO3001"
-
-OUTPUT_DIR = f"xls_shipments_{STUDY}"
-# ────────────────────────────────────────────────────────────────────────────
-
-def run(page, study):
- output_dir = f"xls_shipments_{study}"
- os.makedirs(output_dir, exist_ok=True)
-
- page.goto(f"{BASE_URL}/report/shipments_report")
- page.wait_for_load_state("networkidle", timeout=120000)
-
- filename = os.path.join(output_dir, f"shipments_report_{study}.xlsx")
- with page.expect_download(timeout=120000) as dl:
- page.get_by_role("button", name="Download XLS").click()
- dl.value.save_as(filename)
- print(f" Shipments report OK -> {filename}")
-
-
-if __name__ == "__main__":
- from playwright.sync_api import sync_playwright
- with sync_playwright() as p:
- browser = p.chromium.launch(headless=False)
- context = browser.new_context(accept_downloads=True)
- page = context.new_page()
- page.goto(BASE_URL)
- page.wait_for_load_state("networkidle")
- page.get_by_label("Email *").fill(EMAIL)
- page.get_by_label("Password *").fill(PASSWORD)
- page.locator('#login__submit').click()
- page.wait_for_load_state("networkidle")
- page.get_by_label("Study *").click()
- page.get_by_role("option", name=STUDY).click()
- page.get_by_role("button", name="SELECT").click()
- page.wait_for_load_state("networkidle")
- run(page, STUDY)
- browser.close()
diff --git a/IWRS/Drugs/Working/import_drugs_to_mysql.py b/IWRS/Drugs/Working/import_drugs_to_mysql.py
deleted file mode 100644
index 3ae3ccd..0000000
--- a/IWRS/Drugs/Working/import_drugs_to_mysql.py
+++ /dev/null
@@ -1,441 +0,0 @@
-"""
-Importuje drugs data z IWRS Excel reportů do MySQL.
-
-Tabulky:
- iwrs_shipments — zásilky (jen CZ, verzováno import_id)
- iwrs_shipment_items — obsah zásilek (verzováno import_id)
- iwrs_inventory — lékový sklad na centrech (verzováno import_id)
- iwrs_destruction — destrukce (bez verzování, přeskočí již importované košíky)
-
-Spustit po stažení souborů (nebo přes run_all.py).
-"""
-
-import os
-import glob
-import re
-import datetime
-
-import numpy as np
-import pandas as pd
-import mysql.connector
-
-import db_config
-
-BASE_DIR = os.path.dirname(os.path.abspath(__file__))
-STUDIES = ["77242113UCO3001", "42847922MDD3003"]
-
-SITES = {
- "77242113UCO3001": [
- "DD5-CZ10001", "DD5-CZ10003", "DD5-CZ10006", "DD5-CZ10009",
- "DD5-CZ10010", "DD5-CZ10012", "DD5-CZ10013", "DD5-CZ10015",
- "DD5-CZ10016", "DD5-CZ10020", "DD5-CZ10021", "DD5-CZ10022",
- ],
- "42847922MDD3003": [
- "S10-CZ10002", "S10-CZ10004", "S10-CZ10005",
- "S10-CZ10008", "S10-CZ10011", "S10-CZ10012",
- ],
-}
-
-
-# ── type converters ──────────────────────────────────────────────────────────
-
-def _py(val):
- if isinstance(val, np.generic):
- return val.item()
- return val
-
-def to_date(val):
- val = _py(val)
- if val is None:
- return None
- if isinstance(val, float) and (val != val):
- return None
- try:
- if pd.isna(val):
- return None
- except (TypeError, ValueError):
- pass
- if isinstance(val, pd.Timestamp):
- return None if pd.isna(val) else val.date()
- if isinstance(val, datetime.datetime):
- return val.date()
- if isinstance(val, datetime.date):
- return val
- s = str(val).strip()
- if not s or s.lower() in ("nat", "nan", "none", ""):
- return None
- for fmt in ("%Y-%m-%d", "%d-%b-%Y", "%d-%m-%Y", "%Y-%m-%d %H:%M:%S"):
- try:
- return datetime.datetime.strptime(s, fmt).date()
- except ValueError:
- pass
- return None
-
-def to_int(val):
- val = _py(val)
- try:
- v = float(val)
- return None if (v != v) else int(v)
- except (TypeError, ValueError):
- return None
-
-def to_str(val):
- val = _py(val)
- if val is None:
- return None
- if isinstance(val, float) and (val != val):
- return None
- s = str(val).strip()
- return None if s.lower() in ("nan", "nat", "none", "") else s
-
-
-# ── DB helpers ───────────────────────────────────────────────────────────────
-
-def get_conn():
- return mysql.connector.connect(
- host=db_config.DB_HOST, port=db_config.DB_PORT,
- user=db_config.DB_USER, password=db_config.DB_PASSWORD,
- database=db_config.DB_NAME,
- )
-
-def insert_import(cursor, study, source_label):
- cursor.execute(
- "INSERT INTO iwrs_import (study, imported_at, source_file, report_type) VALUES (%s, %s, %s, %s)",
- (study, datetime.datetime.now(), source_label, "drugs"),
- )
- return cursor.lastrowid
-
-def basket_already_imported(cursor, study, basket_id):
- cursor.execute(
- "SELECT 1 FROM iwrs_destruction WHERE study=%s AND basket_id=%s LIMIT 1",
- (study, str(basket_id)),
- )
- return cursor.fetchone() is not None
-
-
-# ── parsers ──────────────────────────────────────────────────────────────────
-
-def parse_shipments_report(study):
- path = os.path.join(BASE_DIR, f"xls_shipments_{study}", f"shipments_report_{study}.xlsx")
- if not os.path.exists(path):
- print(f" CHYBÍ: {path}")
- return []
-
- raw = pd.read_excel(path, header=None)
- header_row = None
- for i, row in raw.iterrows():
- if "Shipment ID" in [str(v).strip() for v in row]:
- header_row = i
- break
- if header_row is None:
- return []
-
- df = pd.read_excel(path, header=header_row)
- df = df.dropna(how="all")
- # pouze CZ zásilky
- df = df[df["Location"].astype(str).str.contains("Czech", na=False, case=False)]
- col = df.columns.tolist()
-
- rows = []
- for _, r in df.iterrows():
- rows.append({
- "shipment_id": to_str(r["Shipment ID"]),
- "status": to_str(r["IRT Shipment Status"]),
- "type": to_str(r["Type"]),
- "ship_from": to_str(r["Shipment From"]),
- "ship_to_site": to_str(r["Ship To:"]),
- "location": to_str(r["Location"]),
- "request_date": to_date(r["Request Date"]),
- "shipped_date": to_date(r["Shipped Date"]),
- "received_date": to_date(r["Received Date"]) if "Received Date" in col else None,
- "received_by": to_str(r["Received by"]) if "Received by" in col else None,
- "delivered_date_utc": to_date(r["Delivered Date [UTC]"]) if "Delivered Date [UTC]" in col else None,
- "delivery_recipient": to_str(r["Delivery Recipient"]) if "Delivery Recipient" in col else None,
- "delivery_details": to_str(r["Delivery Details"]) if "Delivery Details" in col else None,
- "cancelled_date": to_date(r["Cancelled Date"]) if "Cancelled Date" in col else None,
- "total_medication_ids": to_int(r["Total Medication IDs"]) if "Total Medication IDs" in col else None,
- "tracking_no": to_str(r["Tracking #"]) if "Tracking #" in col else None,
- "shipping_category": to_str(r["Shipping Category"]) if "Shipping Category" in col else None,
- "expected_arrival": to_date(r["Expected Arrival"]) if "Expected Arrival" in col else None,
- })
- return rows
-
-
-def parse_shipment_details(study):
- detail_dir = os.path.join(BASE_DIR, f"xls_shipment_details_{study}")
- files = sorted(glob.glob(os.path.join(detail_dir, "shipment_details_*.xlsx")))
- rows = []
- for path in files:
- # shipment ID z názvu souboru
- m = re.search(r"shipment_details_(.+)\.xlsx", os.path.basename(path))
- shipment_id = m.group(1) if m else "UNKNOWN"
-
- raw = pd.read_excel(path, header=None)
- header_row = None
- for i, row in raw.iterrows():
- if "Medication ID" in [str(v).strip() for v in row]:
- header_row = i
- break
- if header_row is None:
- continue
-
- df = pd.read_excel(path, header=header_row)
- df = df.dropna(how="all")
- col = df.columns.tolist()
-
- for _, r in df.iterrows():
- # normalizace názvů sloupců lišících se mezi studiemi
- med_desc = (to_str(r.get("Medication Description"))
- or to_str(r.get("Medication ID Description")))
- med_type = (to_str(r.get("Medication type"))
- or to_str(r.get("Medication ID type")))
- rows.append({
- "shipment_id": shipment_id,
- "destination_location": to_str(r.get("Destination Location")),
- "shipment_status": to_str(r.get("IRT Shipment Status")),
- "shipment_type": to_str(r.get("Type")),
- "destination_site": to_str(r.get("Destination Site")),
- "investigator": to_str(r.get("Investigator")),
- "medication_description": med_desc,
- "medication_type": med_type,
- "medication_id": to_str(r.get("Medication ID")),
- "packaged_lot_no": to_str(r.get("Packaged Lot number")),
- "packaged_lot_description": to_str(r.get("Packaged Lot description")),
- "container_id": to_str(r.get("Container ID")),
- "quantity": to_int(r.get("Quantity of Medication IDs")),
- "expiration_date": to_date(r.get("Expiration Date")),
- "item_status": to_str(r.get("Status")),
- })
- return rows
-
-
-def parse_inventory(study):
- inv_dir = os.path.join(BASE_DIR, f"xls_reports_{study}")
- files = sorted(glob.glob(os.path.join(inv_dir, "onsite_inventory_detail_*.xlsx")))
- rows = []
- for path in files:
- raw = pd.read_excel(path, header=None)
-
- # extrahuj metadata ze záhlaví
- site = investigator = location = None
- header_row = None
- for i, row in raw.iterrows():
- first = str(row.iloc[0]).strip() if pd.notna(row.iloc[0]) else ""
- if first.startswith("Site:"):
- site = first.replace("Site:", "").strip()
- elif first.startswith("Investigator:"):
- investigator = first.replace("Investigator:", "").strip()
- elif first.startswith("Location:"):
- location = first.replace("Location:", "").strip()
- # hlavička dat — první sloupec je "Medication" nebo "Medication ID"
- if first in ("Medication", "Medication ID") and header_row is None:
- header_row = i
- if header_row is None:
- continue
-
- df = pd.read_excel(path, header=header_row)
- df = df.dropna(how="all")
- # normalizuj první sloupec na "medication_id"
- df = df.rename(columns={df.columns[0]: "medication_id"})
- col = df.columns.tolist()
-
- for _, r in df.iterrows():
- rows.append({
- "site": site,
- "investigator": investigator,
- "location": location,
- "medication_id": to_str(r["medication_id"]),
- "packaged_lot_no": to_str(r.get("Packaged Lot number")),
- "original_expiration_date": to_date(r.get("Original Expiration Date when Packaged Lot was Added")),
- "expiration_date": to_date(r.get("Expiration date")),
- "received_date": to_date(r.get("Received Date")),
- "receipt_user": to_str(r.get("Shipment Receipt User")),
- "subject_identifier": to_str(r.get("Subject Identifier")),
- "quantity_assigned": to_int(r.get("Quantity Assigned")),
- "irt_transaction": to_str(r.get("IRT Transaction")),
- "date_assigned": to_date(r.get("Date Assigned")),
- "assignment_user": to_str(r.get("Assignment User")),
- "dispensation_status": to_str(r.get("Dispensation Status")),
- "dispensing_date": to_date(r.get("Dispensing date") or r.get("Dispensing Date")),
- "quantity_dispensed": to_int(r.get("Quantity Dispensed")),
- "dispensing_user": to_str(r.get("Dispensing User")),
- "quantity_returned": to_int(r.get("Quantity Returned")),
- "date_returned": to_date(r.get("Date Returned")),
- "return_user": to_str(r.get("Return User")),
- })
- return rows
-
-
-def parse_destruction_files(study):
- dest_dir = os.path.join(BASE_DIR, f"xls_ip_destruction_{study}")
- files = sorted(glob.glob(os.path.join(dest_dir, "ip_destruction_basket_*.xlsx")))
- baskets = []
- for path in files:
- raw = pd.read_excel(path, header=None)
-
- # metadata z záhlaví
- meta = {}
- header_row = None
- for i, row in raw.iterrows():
- first = str(row.iloc[0]).strip() if pd.notna(row.iloc[0]) else ""
- for key, attr in [
- ("Investigator Name:", "investigator"),
- ("Site ID:", "site_id"),
- ("Location:", "location"),
- ("Basket ID:", "basket_id"),
- ("Drug Destruction Created Date:", "destruction_date"),
- ]:
- if first.startswith(key):
- meta[attr] = first.replace(key, "").strip()
- if first == "Medication ID Description" and header_row is None:
- header_row = i
-
- if header_row is None:
- continue
-
- df = pd.read_excel(path, header=header_row)
- df = df.dropna(how="all")
-
- items = []
- for _, r in df.iterrows():
- items.append({
- "medication_description": to_str(r.get("Medication ID Description")),
- "medication_id": to_str(r.get("Medication ID")),
- "packaged_lot_description": to_str(r.get("Packaged Lot description")),
- "comments": to_str(r.get("Comments")),
- })
-
- baskets.append({
- "site_id": meta.get("site_id"),
- "investigator": meta.get("investigator"),
- "location": meta.get("location"),
- "basket_id": meta.get("basket_id"),
- "destruction_date": to_date(meta.get("destruction_date")),
- "items": items,
- })
- return baskets
-
-
-# ── inserters ────────────────────────────────────────────────────────────────
-
-def insert_shipments(cursor, import_id, study, rows):
- sql = """INSERT INTO iwrs_shipments
- (import_id, study, shipment_id, status, type, ship_from, ship_to_site,
- location, request_date, shipped_date, received_date, received_by,
- delivered_date_utc, delivery_recipient, delivery_details, cancelled_date,
- total_medication_ids, tracking_no, shipping_category, expected_arrival)
- VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)"""
- for r in rows:
- cursor.execute(sql, (
- import_id, study, r["shipment_id"], r["status"], r["type"],
- r["ship_from"], r["ship_to_site"], r["location"],
- r["request_date"], r["shipped_date"], r["received_date"],
- r["received_by"], r["delivered_date_utc"], r["delivery_recipient"],
- r["delivery_details"], r["cancelled_date"], r["total_medication_ids"],
- r["tracking_no"], r["shipping_category"], r["expected_arrival"],
- ))
-
-
-def insert_shipment_items(cursor, import_id, study, rows):
- sql = """INSERT INTO iwrs_shipment_items
- (import_id, study, shipment_id, destination_location, shipment_status,
- shipment_type, destination_site, investigator, medication_description,
- medication_type, medication_id, packaged_lot_no, packaged_lot_description,
- container_id, quantity, expiration_date, item_status)
- VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)"""
- for r in rows:
- cursor.execute(sql, (
- import_id, study, r["shipment_id"], r["destination_location"],
- r["shipment_status"], r["shipment_type"], r["destination_site"],
- r["investigator"], r["medication_description"], r["medication_type"],
- r["medication_id"], r["packaged_lot_no"], r["packaged_lot_description"],
- r["container_id"], r["quantity"], r["expiration_date"], r["item_status"],
- ))
-
-
-def insert_inventory(cursor, import_id, study, rows):
- sql = """INSERT INTO iwrs_inventory
- (import_id, study, site, investigator, location, medication_id,
- packaged_lot_no, original_expiration_date, expiration_date, received_date,
- receipt_user, subject_identifier, quantity_assigned, irt_transaction,
- date_assigned, assignment_user, dispensation_status, dispensing_date,
- quantity_dispensed, dispensing_user, quantity_returned, date_returned, return_user)
- VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)"""
- for r in rows:
- cursor.execute(sql, (
- import_id, study, r["site"], r["investigator"], r["location"],
- r["medication_id"], r["packaged_lot_no"], r["original_expiration_date"],
- r["expiration_date"], r["received_date"], r["receipt_user"],
- r["subject_identifier"], r["quantity_assigned"], r["irt_transaction"],
- r["date_assigned"], r["assignment_user"], r["dispensation_status"],
- r["dispensing_date"], r["quantity_dispensed"], r["dispensing_user"],
- r["quantity_returned"], r["date_returned"], r["return_user"],
- ))
-
-
-def insert_destruction(cursor, study, baskets):
- sql = """INSERT IGNORE INTO iwrs_destruction
- (study, site_id, investigator, location, basket_id, destruction_date,
- medication_description, medication_id, packaged_lot_description, comments)
- VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)"""
- skipped = 0
- imported = 0
- for b in baskets:
- if basket_already_imported(cursor, study, b["basket_id"]):
- skipped += 1
- continue
- for item in b["items"]:
- cursor.execute(sql, (
- study, b["site_id"], b["investigator"], b["location"],
- b["basket_id"], b["destruction_date"],
- item["medication_description"], item["medication_id"],
- item["packaged_lot_description"], item["comments"],
- ))
- imported += 1
- return imported, skipped
-
-
-# ── main ─────────────────────────────────────────────────────────────────────
-
-def import_study(study):
- print(f"\n Parsování dat pro {study}...")
- shipments = parse_shipments_report(study)
- items = parse_shipment_details(study)
- inventory = parse_inventory(study)
- baskets = parse_destruction_files(study)
-
- print(f" Zásilky: {len(shipments)} | Položky zásilek: {len(items)} | Sklad: {len(inventory)} | Destrukční košíky: {len(baskets)}")
-
- conn = get_conn()
- cursor = conn.cursor()
-
- import_id = insert_import(cursor, study, f"drugs_{study}")
- print(f" import_id = {import_id}")
-
- insert_shipments(cursor, import_id, study, shipments)
- insert_shipment_items(cursor, import_id, study, items)
- insert_inventory(cursor, import_id, study, inventory)
- dest_imported, dest_skipped = insert_destruction(cursor, study, baskets)
-
- conn.commit()
- cursor.close()
- conn.close()
- print(f" Destrukce: {dest_imported} nových | {dest_skipped} košíků přeskočeno (již importováno)")
-
-
-def main():
- for study in STUDIES:
- print(f"\n{'='*60}")
- print(f"[{study}]")
- print(f"{'='*60}")
- try:
- import_study(study)
- print(f" OK")
- except Exception as e:
- import traceback
- print(f" CHYBA: {e}")
- traceback.print_exc()
- print("\nHotovo.")
-
-
-main()
diff --git a/IWRS/Drugs/Working/output/2026-05-05 42847922MDD3003 report.xlsx b/IWRS/Drugs/Working/output/2026-05-05 42847922MDD3003 report.xlsx
deleted file mode 100644
index 1b185f9..0000000
Binary files a/IWRS/Drugs/Working/output/2026-05-05 42847922MDD3003 report.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/Working/output/2026-05-05 77242113UCO3001 report.xlsx b/IWRS/Drugs/Working/output/2026-05-05 77242113UCO3001 report.xlsx
deleted file mode 100644
index e2d221b..0000000
Binary files a/IWRS/Drugs/Working/output/2026-05-05 77242113UCO3001 report.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/Working/run.py b/IWRS/Drugs/Working/run.py
deleted file mode 100644
index 945444c..0000000
--- a/IWRS/Drugs/Working/run.py
+++ /dev/null
@@ -1,85 +0,0 @@
-import sys
-import os
-from playwright.sync_api import sync_playwright
-
-import download_reports
-import download_ip_destruction
-import download_shipments_report
-import download_shipment_details
-import create_accountability_report
-
-BASE_URL = "https://janssen.4gclinical.com"
-EMAIL = "vbuzalka@its.jnj.com"
-PASSWORD = "Vlado123++-+"
-
-STUDIES = {
- "1": "77242113UCO3001",
- "2": "42847922MDD3003",
-}
-
-
-def pick_study():
- print("Vyber studii:")
- for k, v in STUDIES.items():
- print(f" {k}) {v}")
- while True:
- choice = input("Volba (1/2): ").strip()
- if choice in STUDIES:
- return STUDIES[choice]
- print(" Neplatna volba, zkus znovu.")
-
-
-def login_and_select_study(page, study):
- print(f"\n[1/5] Prihlaseni a vyber studie {study}...")
- page.goto(BASE_URL)
- page.wait_for_load_state("networkidle")
- page.get_by_label("Email *").fill(EMAIL)
- page.get_by_label("Password *").fill(PASSWORD)
- page.locator('#login__submit').click()
- page.wait_for_load_state("networkidle")
- page.get_by_label("Study *").click()
- page.get_by_role("option", name=study).click()
- page.get_by_role("button", name="SELECT").click()
- page.wait_for_load_state("networkidle")
- print(" OK")
-
-
-def main():
- os.chdir(os.path.dirname(os.path.abspath(__file__)))
-
- study = pick_study()
-
- with sync_playwright() as p:
- browser = p.chromium.launch(headless=False)
- context = browser.new_context(accept_downloads=True)
- page = context.new_page()
-
- login_and_select_study(page, study)
-
- print(f"\n[2/5] Stahuji inventory reporty...")
- download_reports.run(page, study)
-
- print(f"\n[3/5] Stahuji IP destruction reporty...")
- download_ip_destruction.run(page, study)
-
- print(f"\n[4/5] Stahuji shipments report...")
- download_shipments_report.run(page, study)
-
- print(f"\n[5/5] Stahuji shipment details...")
- download_shipment_details.run(page, study)
-
- browser.close()
-
- print(f"\n[6/6] Generuji accountability report...")
- create_accountability_report.STUDY = study
- create_accountability_report.INVENTORY_DIR = __import__("pathlib").Path(f"xls_reports_{study}")
- create_accountability_report.DESTRUCTION_DIR= __import__("pathlib").Path(f"xls_ip_destruction_{study}")
- create_accountability_report.SHIPMENTS_FILE = __import__("pathlib").Path(f"xls_shipments_{study}/shipments_report_{study}.xlsx")
- create_accountability_report.DETAILS_DIR = __import__("pathlib").Path(f"xls_shipment_details_{study}")
- create_accountability_report.OUTPUT_FILE = create_accountability_report.OUTPUT_DIR / f"{__import__('datetime').date.today().strftime('%Y-%m-%d')} {study} CZ IWRS overview.xlsx"
- create_accountability_report.main()
-
- print("\nVse hotovo!")
-
-
-main()
diff --git a/IWRS/Drugs/create_report.py b/IWRS/Drugs/create_report.py
deleted file mode 100644
index 09eb78b..0000000
--- a/IWRS/Drugs/create_report.py
+++ /dev/null
@@ -1,649 +0,0 @@
-import os
-import sys
-import pandas as pd
-from datetime import date
-from pathlib import Path
-from openpyxl import load_workbook
-from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
-from openpyxl.utils import get_column_letter
-
-sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
-from common.mongo_writer import get_db
-
-STUDIES = ["77242113UCO3001", "42847922MDD3003"]
-
-BASE_DIR = Path(os.path.dirname(os.path.abspath(__file__)))
-OUTPUT_DIR = BASE_DIR / "output"
-
-DATE_COLUMNS = {
- "Orig Exp Date", "Exp Date", "Rcv Date",
- "Date Asgn", "Disp Date", "Date Ret", "Destroyed", "Max Visit Date",
- "Visit Date", "Scheduled Date",
-}
-
-N_SHIP_COLS = 9 # počet shipment sloupců před detail sloupci
-
-
-# ── Načítání dat z MongoDB ────────────────────────────────────────────────────
-
-INVENTORY_COLS = [
- ("site", "Site"),
- ("medication_id", "Med ID"),
- ("packaged_lot_no", "Lot No."),
- ("original_expiration_date", "Orig Exp Date"),
- ("expiration_date", "Exp Date"),
- ("received_date", "Rcv Date"),
- ("receipt_user", "Rcpt User"),
- ("subject_identifier", "Subject ID"),
- ("quantity_assigned", "Qty Asgn"),
- ("irt_transaction", "IRT Tx"),
- ("date_assigned", "Date Asgn"),
- ("assignment_user", "Asgn User"),
- ("dispensation_status", "Disp Status"),
- ("dispensing_date", "Disp Date"),
- ("quantity_dispensed", "Qty Disp"),
- ("dispensing_user", "Disp User"),
- ("quantity_returned", "Qty Ret"),
- ("date_returned", "Date Ret"),
- ("return_user", "Ret User"),
-]
-
-
-def load_inventory(study):
- db = get_db()
- inv = list(db.iwrs_inventory.find({"study": study}))
- destr = list(db.iwrs_destruction.find({"study": study}))
- # map medication_id -> first basket+date
- destr_map = {}
- for d in destr:
- mid = d.get("medication_id")
- if mid and mid not in destr_map:
- destr_map[mid] = (d.get("basket_id"), d.get("destruction_date"))
-
- records = []
- for doc in inv:
- row = {label: doc.get(key) for key, label in INVENTORY_COLS}
- b, dt = destr_map.get(doc.get("medication_id"), (None, None))
- row["Destroyed"] = dt
- row["Basket No."] = b
- records.append(row)
-
- df = pd.DataFrame(records)
- if df.empty:
- print(" Inventory: 0 kitu")
- return df
-
- df = df.sort_values(["Site", "Rcv Date", "Med ID"], na_position="last").reset_index(drop=True)
- for col in DATE_COLUMNS:
- if col in df.columns:
- df[col] = pd.to_datetime(df[col], errors="coerce")
- print(f" Inventory: {len(df)} kitu")
- return df
-
-
-SHIP_COLS = [
- ("shipment_id", "Shipment ID"),
- ("status", "IRT Shipment Status"),
- ("type", "Type"),
- ("ship_from", "Shipment From"),
- ("ship_to_site", "Ship To:"),
- ("request_date", "Request Date"),
- ("received_date", "Received Date"),
- ("received_by", "Received by"),
- ("expected_arrival", "Expected Arrival"),
-]
-
-ITEM_COLS = [
- ("investigator", "Investigator"),
- ("medication_description", "Medication Description"),
- ("medication_id", "Medication ID"),
- ("packaged_lot_no", "Packaged Lot number"),
- ("expiration_date", "Expiration Date"),
- ("item_status", "Status"),
-]
-
-
-def load_shipments(study):
- db = get_db()
- ships = list(db.iwrs_shipments.find({"study": study}))
- items = list(db.iwrs_shipment_items.find({"study": study}))
-
- # index items by shipment_id
- items_by_ship = {}
- for it in items:
- items_by_ship.setdefault(it.get("shipment_id"), []).append(it)
-
- records = []
- for s in ships:
- base = {label: s.get(key) for key, label in SHIP_COLS}
- for it in items_by_ship.get(s.get("shipment_id"), []):
- row = dict(base)
- for key, label in ITEM_COLS:
- row[label] = it.get(key)
- records.append(row)
-
- df = pd.DataFrame(records)
- if df.empty:
- print(" Shipments: 0 zásilek, 0 kitu")
- return df
-
- df = df.sort_values(["Ship To:", "Shipment ID", "Medication ID"], na_position="last").reset_index(drop=True)
- for col in ("Request Date", "Received Date", "Expiration Date", "Expected Arrival"):
- if col in df.columns:
- df[col] = pd.to_datetime(df[col], errors="coerce")
- n_ship = df["Shipment ID"].nunique()
- print(f" Shipments: {n_ship} zásilek, {len(df)} kitu")
- return df
-
-
-def load_visits(study):
- db = get_db()
- cur = db.iwrs_visits.find({
- "study": study,
- "visit_type": "Past",
- "irt_transaction_no": {"$ne": None},
- })
- rows = []
- for v in cur:
- rows.append({
- "Subject": v.get("subject"),
- "Visit Date": v.get("actual_date") or v.get("scheduled_date"),
- "Scheduled Date": v.get("scheduled_date"),
- "IRT Tx No": v.get("irt_transaction_no"),
- "Visit": v.get("irt_transaction_description"),
- "Medication": v.get("medication_assignment"),
- "medication_id": v.get("medication_id"),
- "quantity_assigned": v.get("quantity_assigned"),
- })
- df = pd.DataFrame(rows)
- if df.empty:
- print(" Visits: 0 radku")
- return df
-
- # GROUP BY subject/actual/scheduled/irt_no/desc/medication
- grouped = (
- df.groupby(["Subject", "Visit Date", "Scheduled Date", "IRT Tx No", "Visit", "Medication"],
- dropna=False, as_index=False)
- .agg(**{
- "Med IDs": ("medication_id", lambda s: ", ".join(sorted([str(x) for x in s if pd.notna(x)]))),
- "Qty": ("quantity_assigned", "sum"),
- })
- )
- grouped = grouped.sort_values(["Subject", "Visit Date"]).reset_index(drop=True)
- for col in ("Visit Date", "Scheduled Date"):
- if col in grouped.columns:
- grouped[col] = pd.to_datetime(grouped[col], errors="coerce")
- if study == "77242113UCO3001":
- grouped["Visit"] = grouped["Visit"].replace("Subject Number Creation", "Screening")
- print(f" Visits: {len(grouped)} řádků")
- return grouped
-
-
-# ── Odvozené sheety ───────────────────────────────────────────────────────────
-
-def build_site_summary(shipments_df):
- STATUS_COLS = ["Available", "Assigned", "Dispensed", "Returned by Subject"]
- pivot = shipments_df.groupby("Ship To:")["Status"].value_counts().unstack(fill_value=0)
- for s in STATUS_COLS:
- if s not in pivot.columns:
- pivot[s] = 0
- pivot = (
- pivot[STATUS_COLS]
- .reset_index()
- .rename(columns={"Ship To:": "Site", "Returned by Subject": "Returned"})
- .sort_values("Site")
- .reset_index(drop=True)
- )
- pivot["Total"] = pivot[["Available", "Assigned", "Dispensed", "Returned"]].sum(axis=1)
- print(f" Site Summary: {len(pivot)} center")
- return pivot
-
-
-def build_expired(df):
- today = date.today()
- mask = (
- df["Basket No."].isna() &
- df["Subject ID"].isna() &
- (df["Exp Date"] < pd.Timestamp(today))
- )
- filtered = df[mask].copy().reset_index(drop=True)
- sheet_name = f"Expired as of {today.strftime('%d-%b-%Y')}"
- print(f" Expired: {len(filtered)}")
- return filtered, sheet_name
-
-
-def build_assigned_not_dispensed(df):
- mask = df["Subject ID"].notna() & df["Disp Date"].isna()
- filtered = df[mask].copy().reset_index(drop=True)
- print(f" Assigned not dispensed: {len(filtered)}")
- return filtered
-
-
-def build_not_returned(df):
- no_ret = df[
- df["Date Ret"].isna() &
- df["Subject ID"].notna() &
- (df["Disp Status"].fillna("").str.upper() != "NOT DISPENSED")
- ].copy()
- max_asgn = df.groupby("Subject ID")["Date Asgn"].max().rename("Max Visit Date")
- no_ret = no_ret.join(max_asgn, on="Subject ID")
- filtered = no_ret[no_ret["Date Asgn"] < no_ret["Max Visit Date"]].copy()
- filtered = filtered.drop(columns=["Qty Ret", "Date Ret", "Ret User", "Destroyed", "Basket No."])
- filtered = filtered.reset_index(drop=True)
- print(f" Not returned: {len(filtered)}")
- return filtered
-
-
-def build_kits_for_destruction(df):
- mask = (
- df["Basket No."].isna() &
- (df["Date Ret"].notna() | (df["Disp Status"].fillna("").str.upper() == "NOT DISPENSED"))
- )
- filtered = (
- df[mask]
- .copy()
- .sort_values(["Site", "Date Ret"], ascending=[True, True])
- .drop(columns=["Destroyed", "Basket No."])
- .reset_index(drop=True)
- )
- print(f" Kits for destruction: {len(filtered)}")
- return filtered
-
-
-# ── Formátování ───────────────────────────────────────────────────────────────
-
-STRIPE_GRAY = PatternFill("solid", start_color="F2F2F2")
-STRIPE_WHITE = PatternFill("solid", start_color="FFFFFF")
-
-# pacienti — styly zachovány z create_subject_report.py
-_PAT_HEADER_FILL = PatternFill("solid", start_color="1F4E79")
-_PAT_HEADER_FONT = Font(name="Arial", bold=True, color="FFFFFF", size=10)
-_PAT_NORMAL_FONT = Font(name="Arial", size=10)
-_PAT_BOLD_FONT = Font(name="Arial", bold=True, size=10)
-_PAT_STRIKE_FONT = Font(name="Arial", size=10, strike=True, color="999999")
-_PAT_ADOLESC_FONT = Font(name="Arial", bold=True, size=10)
-_PAT_THIN = Side(style="thin", color="CCCCCC")
-_PAT_BORDER = Border(left=_PAT_THIN, right=_PAT_THIN, top=_PAT_THIN, bottom=_PAT_THIN)
-_PAT_EVEN_FILL = PatternFill("solid", start_color="EBF3FB")
-_PAT_ODD_FILL = PatternFill("solid", start_color="FFFFFF")
-_PAT_CENTER = Alignment(horizontal="center", vertical="center")
-_PAT_LEFT = Alignment(horizontal="left", vertical="center")
-
-
-def _autofit(ws):
- for col_cells in ws.columns:
- max_len = 0
- col_letter = get_column_letter(col_cells[0].column)
- for cell in col_cells:
- if cell.value is None:
- continue
- # datum se zobrazí jako DD-MMM-YYYY = 11 znaků
- if hasattr(cell.value, "strftime") or cell.number_format == "DD-MMM-YYYY":
- length = 11
- else:
- length = len(str(cell.value))
- if length > max_len:
- max_len = length
- ws.column_dimensions[col_letter].width = min(max_len + 3, 50)
-
-
-def format_sheet(ws, header_color, highlight_col=None, highlight_color=None):
- thin = Side(style="thin", color="000000")
- border = Border(left=thin, right=thin, top=thin, bottom=thin)
- header_fill = PatternFill("solid", start_color=header_color)
- header_font = Font(bold=True, color="FFFFFF", name="Arial", size=10)
- row_font = Font(name="Arial", size=10)
- hi_fill = PatternFill("solid", start_color=highlight_color) if highlight_color else None
-
- headers = [cell.value for cell in ws[1]]
-
- for cell in ws[1]:
- cell.fill = header_fill
- cell.font = header_font
- cell.alignment = Alignment(horizontal="center", vertical="center", wrap_text=False)
- cell.border = border
-
- for row in ws.iter_rows(min_row=2, max_row=ws.max_row):
- stripe = STRIPE_GRAY if row[0].row % 2 == 0 else STRIPE_WHITE
- for cell in row:
- col_name = headers[cell.column - 1] if cell.column <= len(headers) else None
- cell.font = row_font
- cell.border = border
- cell.alignment = Alignment(horizontal="center")
- if col_name in DATE_COLUMNS:
- cell.number_format = "DD-MMM-YYYY"
- if hi_fill and col_name == highlight_col:
- cell.fill = hi_fill
- else:
- cell.fill = stripe
-
- _autofit(ws)
- ws.auto_filter.ref = ws.dimensions
- ws.freeze_panes = "A2"
-
-
-def format_shipment_sheet(ws, header_color_ship, header_color_detail, n_ship_cols):
- thin = Side(style="thin", color="000000")
- border = Border(left=thin, right=thin, top=thin, bottom=thin)
- hfont = Font(bold=True, color="FFFFFF", name="Arial", size=10)
- dfont = Font(name="Arial", size=10)
- fill_ship = PatternFill("solid", start_color=header_color_ship)
- fill_detail = PatternFill("solid", start_color=header_color_detail)
-
- for cell in ws[1]:
- cell.fill = fill_ship if cell.column <= n_ship_cols else fill_detail
- cell.font = hfont
- cell.alignment = Alignment(horizontal="center", vertical="center", wrap_text=True)
- cell.border = border
- ws.row_dimensions[1].height = 30
-
- for row in ws.iter_rows(min_row=2, max_row=ws.max_row):
- stripe = STRIPE_GRAY if row[0].row % 2 == 0 else STRIPE_WHITE
- for cell in row:
- cell.font = dfont
- cell.border = border
- cell.alignment = Alignment(horizontal="center", vertical="center")
- cell.fill = stripe
- if cell.value.__class__.__name__ in ("datetime", "date", "Timestamp"):
- cell.number_format = "DD-MMM-YYYY"
-
- _autofit(ws)
- ws.auto_filter.ref = ws.dimensions
- ws.freeze_panes = "A2"
-
-
-# ── Pacienti ─────────────────────────────────────────────────────────────────
-
-def load_patients(study):
- db = get_db()
- docs = list(db.iwrs_subject_summary.find({"study": study}))
- if not docs:
- raise RuntimeError(f"Žádná data v Mongo pro pacienty {study}")
-
- base_cols = [
- ("subject", "Subject"),
- ("investigator", "Investigator"),
- ("age", "Subject's age collection"),
- ("cohort_per_irt", "Cohort per IRT"),
- ("irt_subject_status", "IRT Subject Status"),
- ("last_irt_transaction", "Last Recorded IRT Transaction"),
- ("next_irt_transaction", "Next Expected IRT Transaction"),
- ("next_irt_transaction_date_local", "Next Expected IRT Transaction Date [Local]"),
- ]
- uco_extra = [
- ("rescreened_subject", "Rescreened Subject"),
- ("adt_ir", "ADT-IR"),
- ("three_or_more_advanced_therapies", "3+ Adv. Therapies"),
- ("only_oral_5asa_compounds", "Only 5-ASA"),
- ("ustekinumab", "Ustekinumab"),
- ("isolated_proctitis", "Isolated Proctitis"),
- ]
- cols = list(base_cols)
- if study == "77242113UCO3001":
- cols += uco_extra
-
- rows = [{label: d.get(key) for key, label in cols} for d in docs]
- df = pd.DataFrame(rows).sort_values("Subject").reset_index(drop=True)
-
- if "Next Expected IRT Transaction Date [Local]" in df.columns:
- df["Next Expected IRT Transaction Date [Local]"] = pd.to_datetime(
- df["Next Expected IRT Transaction Date [Local]"], errors="coerce"
- )
- print(f" Pacienti: {len(df)} subjektů")
- return df
-
-
-def _simplify_cohort(val):
- if pd.isna(val):
- return ""
- val = str(val)
- if "dolescent" in val:
- return "Adolescent"
- if val.startswith("Adult"):
- return "Adult"
- return val
-
-
-def _fmt_date(val):
- if pd.isna(val):
- return ""
- if hasattr(val, "strftime"):
- return val.strftime("%Y-%m-%d")
- return str(val)[:10]
-
-
-def _write_prehled(wb, df_raw, study):
- ws = wb.create_sheet("Přehled", 0)
- ws.sheet_view.showGridLines = False
-
- is_uco = (study == "77242113UCO3001")
-
- if is_uco:
- display_headers = ["Subject", "Investigator", "Věk", "Cohort",
- "Rescreened", "ADT-IR", "≥3 Adv.Th.", "5-ASA only",
- "Uste.", "Isol.Proct.",
- "Status", "Last IRT", "Next Visit", "Next Date"]
- col_widths = [14, 22, 6, 12, 11, 8, 11, 10, 8, 12, 14, 12, 12, 13]
- status_col = 11
- flag_cols = set(range(5, 11)) # 1-indexed sloupce s Yes/No hodnotami
- else:
- display_headers = ["Subject", "Investigator", "Věk", "Cohort", "Status", "Last IRT", "Next Visit", "Next Date"]
- col_widths = [14, 22, 6, 12, 14, 12, 12, 13]
- status_col = 5
- flag_cols = set()
-
- last_col = get_column_letter(len(display_headers))
- ws.merge_cells(f"A1:{last_col}1")
- title = ws["A1"]
- title.value = f"Subject Summary — {study} ({date.today().strftime('%d-%b-%Y')})"
- title.font = Font(name="Arial", bold=True, size=12, color="1F4E79")
- title.alignment = Alignment(horizontal="left", vertical="center")
- ws.row_dimensions[1].height = 22
-
- for c, (h, w) in enumerate(zip(display_headers, col_widths), 1):
- cell = ws.cell(row=2, column=c, value=h)
- cell.font = _PAT_HEADER_FONT
- cell.fill = _PAT_HEADER_FILL
- cell.alignment = _PAT_CENTER
- cell.border = _PAT_BORDER
- ws.column_dimensions[get_column_letter(c)].width = w
- ws.row_dimensions[2].height = 18
-
- base = {
- "Subject": df_raw["Subject"].fillna(""),
- "Investigator": df_raw["Investigator"].fillna(""),
- "Věk": df_raw["Subject's age collection"].apply(lambda v: "" if pd.isna(v) else int(v)),
- "Cohort": df_raw["Cohort per IRT"].apply(_simplify_cohort),
- }
- if is_uco:
- base.update({
- "Rescreened": df_raw["Rescreened Subject"].fillna(""),
- "ADT-IR": df_raw["ADT-IR"].fillna(""),
- "≥3 Adv.Th.": df_raw["3+ Adv. Therapies"].fillna(""),
- "5-ASA only": df_raw["Only 5-ASA"].fillna(""),
- "Uste.": df_raw["Ustekinumab"].fillna(""),
- "Isol.Proct.": df_raw["Isolated Proctitis"].fillna(""),
- })
- base.update({
- "Status": df_raw["IRT Subject Status"].fillna(""),
- "Last IRT": df_raw["Last Recorded IRT Transaction"].fillna("—"),
- "Next Visit": df_raw["Next Expected IRT Transaction"].fillna("—"),
- "Next Date": df_raw["Next Expected IRT Transaction Date [Local]"].apply(_fmt_date),
- })
- display = pd.DataFrame(base).sort_values("Subject").reset_index(drop=True)
-
- for r_idx, row in display.iterrows():
- excel_row = r_idx + 3
- status = str(row["Status"])
- is_failed = "Screen Failed" in status or "Discontinued" in status
- is_randomized = "Randomized" in status
- is_adolescent = row["Cohort"] == "Adolescent"
- fill = _PAT_EVEN_FILL if r_idx % 2 == 0 else _PAT_ODD_FILL
-
- for c_idx, val in enumerate(row, 1):
- cell = ws.cell(row=excel_row, column=c_idx, value=val if val != "" else None)
- cell.fill = fill
- cell.border = _PAT_BORDER
- cell.alignment = _PAT_CENTER if (c_idx == 3 or c_idx in flag_cols) else _PAT_LEFT
- if is_failed:
- cell.font = _PAT_STRIKE_FONT
- elif c_idx == status_col and is_randomized:
- cell.font = _PAT_BOLD_FONT
- elif c_idx == 4 and is_adolescent:
- cell.font = _PAT_ADOLESC_FONT
- else:
- cell.font = _PAT_NORMAL_FONT
- ws.row_dimensions[excel_row].height = 16
-
- ws.freeze_panes = "A3"
- ws.auto_filter.ref = f"A2:{last_col}{len(display) + 2}"
-
-
-def _write_next_visits(wb, df_raw, study, visits_df=None):
- ws = wb.create_sheet("Next Visits", 1)
- ws.sheet_view.showGridLines = False
-
- ws.merge_cells("A1:D1")
- title = ws["A1"]
- title.value = f"Next Expected Visits — {study} ({date.today().strftime('%d-%b-%Y')})"
- title.font = Font(name="Arial", bold=True, size=12, color="1F4E79")
- title.alignment = Alignment(horizontal="left", vertical="center")
- ws.row_dimensions[1].height = 22
-
- nv_headers = ["Subject", "Investigator", "Next Visit", "Datum"]
- nv_widths = [14, 22, 26, 13]
- for c, (h, w) in enumerate(zip(nv_headers, nv_widths), 1):
- cell = ws.cell(row=2, column=c, value=h)
- cell.font = _PAT_HEADER_FONT
- cell.fill = _PAT_HEADER_FILL
- cell.alignment = _PAT_CENTER
- cell.border = _PAT_BORDER
- ws.column_dimensions[get_column_letter(c)].width = w
- ws.row_dimensions[2].height = 18
-
- df = pd.DataFrame({
- "Subject": df_raw["Subject"].fillna(""),
- "Investigator": df_raw["Investigator"].fillna(""),
- "Next Visit": df_raw["Next Expected IRT Transaction"].fillna(""),
- "Datum": df_raw["Next Expected IRT Transaction Date [Local]"],
- "Status": df_raw["IRT Subject Status"].fillna(""),
- })
-
- # I-0: datum = screening date + 42 dní
- if visits_df is not None and not visits_df.empty:
- screen = (
- visits_df[visits_df["Visit"].str.contains("Screen", case=False, na=False)]
- .groupby("Subject")["Visit Date"].min()
- .rename("Screening Date")
- )
- df = df.join(screen, on="Subject")
- mask_i0 = df["Next Visit"].str.contains("I-0", na=False)
- df.loc[mask_i0, "Datum"] = df.loc[mask_i0, "Screening Date"] + pd.Timedelta(days=42)
- df = df.drop(columns=["Screening Date"])
-
- df = df[df["Datum"].notna()]
- df = df[~df["Status"].str.contains("Screen Failed|Discontinued", na=False)]
- df = df.sort_values("Datum").reset_index(drop=True)
-
- for r_idx, row in df.iterrows():
- excel_row = r_idx + 3
- fill = _PAT_EVEN_FILL if r_idx % 2 == 0 else _PAT_ODD_FILL
- datum_val = row["Datum"]
- datum_str = datum_val.strftime("%Y-%m-%d") if hasattr(datum_val, "strftime") else str(datum_val)[:10]
- for c_idx, val in enumerate([row["Subject"], row["Investigator"], row["Next Visit"], datum_str], 1):
- cell = ws.cell(row=excel_row, column=c_idx, value=val if val != "" else None)
- cell.fill = fill
- cell.border = _PAT_BORDER
- cell.font = _PAT_NORMAL_FONT
- cell.alignment = _PAT_LEFT
- ws.row_dimensions[excel_row].height = 16
-
- ws.freeze_panes = "A3"
- ws.auto_filter.ref = f"A2:D{len(df) + 2}"
-
-
-# ── Jeden report pro jednu studii ─────────────────────────────────────────────
-
-def create_study_report(study):
- today = date.today()
-
- # číslování: najdi nejvyšší existující verzi pro dnešní datum
- existing = sorted(OUTPUT_DIR.glob(f"{today} {study} CZ IWRS overview v*.xlsx"))
- if existing:
- last = existing[-1].stem # např. "2026-05-12 42847922MDD3003 CZ IWRS overview v3"
- last_ver = int(last.rsplit("v", 1)[-1])
- version = last_ver + 1
- else:
- version = 1
-
- output_file = OUTPUT_DIR / f"{today} {study} CZ IWRS overview v{version}.xlsx"
-
- print(f"\n[{study}] Nacitam z MongoDB...")
- df = load_inventory(study)
- shipments_df = load_shipments(study)
- df_patients = load_patients(study)
- visits_df = load_visits(study)
-
- expired_df, expired_sheet = build_expired(df)
- assigned_df = build_assigned_not_dispensed(df)
- not_returned_df = build_not_returned(df)
- destruction_df = build_kits_for_destruction(df)
- site_summary_df = build_site_summary(shipments_df)
-
- with pd.ExcelWriter(output_file, engine="openpyxl") as writer:
- df.to_excel( writer, index=False, sheet_name="CountryMedicationOverview")
- expired_df.to_excel( writer, index=False, sheet_name=expired_sheet)
- assigned_df.to_excel( writer, index=False, sheet_name="Assigned not dispensed")
- not_returned_df.to_excel( writer, index=False, sheet_name="Not returned")
- destruction_df.to_excel( writer, index=False, sheet_name="Kits for destruction")
- shipments_df.to_excel( writer, index=False, sheet_name="Shipments")
- site_summary_df.to_excel( writer, index=False, sheet_name="Site Summary")
- visits_df.to_excel( writer, index=False, sheet_name="Patient Visits")
-
- wb = load_workbook(output_file)
-
- ws_main = wb["CountryMedicationOverview"]
- format_sheet(ws_main, header_color="1F4E79")
- green_fill = PatternFill("solid", start_color="E2EFDA")
- headers_main = [c.value for c in ws_main[1]]
- for row in ws_main.iter_rows(min_row=2, max_row=ws_main.max_row):
- for cell in row:
- col_name = headers_main[cell.column - 1] if cell.column <= len(headers_main) else None
- if col_name in ("Destroyed", "Basket No."):
- cell.fill = green_fill
-
- format_sheet(wb[expired_sheet], header_color="C00000", highlight_col="Exp Date", highlight_color="FFE0E0")
- format_sheet(wb["Assigned not dispensed"], header_color="833C00", highlight_col="Subject ID", highlight_color="FFF2CC")
- format_sheet(wb["Not returned"], header_color="375623", highlight_col="Max Visit Date", highlight_color="E2EFDA")
- format_sheet(wb["Kits for destruction"], header_color="595959")
- format_shipment_sheet(wb["Shipments"], "1F4E79", "375623", N_SHIP_COLS)
- format_sheet(wb["Site Summary"], header_color="1F4E79")
- format_sheet(wb["Patient Visits"], header_color="1F4E79")
-
- # ── pacienti (Přehled + Next Visits) na začátek ──────────────────────────
- _write_prehled(wb, df_patients, study)
- _write_next_visits(wb, df_patients, study, visits_df)
-
- # ── pořadí listů: Patient Visits jako první ──────────────────────────────
- names = wb.sheetnames
- wb._sheets = [wb["Patient Visits"]] + [wb[s] for s in names if s != "Patient Visits"]
-
- wb.save(output_file)
- print(f" Uloženo: {output_file.name} ({len(df)} řádků)")
-
-
-# ── Main ──────────────────────────────────────────────────────────────────────
-
-def main():
- OUTPUT_DIR.mkdir(exist_ok=True)
- for study in STUDIES:
- try:
- create_study_report(study)
- except Exception as e:
- import traceback
- print(f"\n[{study}] CHYBA: {e}")
- traceback.print_exc()
- print("\nHotovo.")
-
-
-main()
diff --git a/IWRS/Drugs/db_config.py b/IWRS/Drugs/db_config.py
deleted file mode 100644
index bfa5959..0000000
--- a/IWRS/Drugs/db_config.py
+++ /dev/null
@@ -1,5 +0,0 @@
-DB_HOST = "192.168.1.76"
-DB_PORT = 3306
-DB_USER = "root"
-DB_PASSWORD = "Vlado9674+"
-DB_NAME = "studie"
diff --git a/IWRS/Drugs/download_drugs.py b/IWRS/Drugs/download_drugs.py
deleted file mode 100644
index c43cbf0..0000000
--- a/IWRS/Drugs/download_drugs.py
+++ /dev/null
@@ -1,220 +0,0 @@
-"""
-download_drugs.py — stažení Drugs reportů pro jednu studii do IWRS/Incoming/.
-Verze: 1.0 | Datum: 2026-06-10
-
-Volá se z IWRS/run_all_v1.0.py s již přihlášenou Playwright page (login +
-výběr studie zajišťuje common.iwrs_portal.login).
-
- 1. Onsite inventory detail (per site, stahuje se vždy)
- 2. IP destruction (per košík; přeskočí košíky už importované
- v Mongo iwrs_destruction — destrukce se nemění)
- 3. Shipments report (jeden soubor na studii, stahuje se vždy)
- 4. Shipment details (per CZ zásilka; přeskočí zásilky, jejichž
- položky jsou v Mongo iwrs_shipment_items se
- statusem RECEIVED — finální stav)
-
-Názvy souborů (datumované, aby zapadly do Incoming/ flow):
- YYYY-MM-DD {study} Onsite Inventory {site}.xlsx
- YYYY-MM-DD {study} IP Destruction {basket}.xlsx
- YYYY-MM-DD {study} Shipments Report.xlsx
- YYYY-MM-DD {study} Shipment Details {shipment_id}.xlsx
-"""
-
-import os
-import sys
-import datetime
-
-import pandas as pd
-
-BASE_DIR = os.path.dirname(os.path.abspath(__file__))
-IWRS_DIR = os.path.dirname(BASE_DIR)
-for _p in (IWRS_DIR, BASE_DIR):
- if _p not in sys.path:
- sys.path.insert(0, _p)
-
-from common.iwrs_portal import BASE_URL
-from common.paths import INCOMING_DIR, unique_path
-from common.mongo_writer import get_db
-
-SITES = {
- "77242113UCO3001": [
- "DD5-CZ10001", "DD5-CZ10003", "DD5-CZ10006", "DD5-CZ10009",
- "DD5-CZ10010", "DD5-CZ10012", "DD5-CZ10013", "DD5-CZ10015",
- "DD5-CZ10016", "DD5-CZ10020", "DD5-CZ10021", "DD5-CZ10022",
- ],
- "42847922MDD3003": [
- "S10-CZ10002", "S10-CZ10004", "S10-CZ10005",
- "S10-CZ10008", "S10-CZ10011", "S10-CZ10012",
- ],
-}
-
-
-def _today():
- return datetime.date.today().strftime("%Y-%m-%d")
-
-
-# ── skip-logika přes Mongo (náhrada za dřívější "soubor existuje") ───────────
-
-def get_existing_baskets(study):
- """Košíky už importované v iwrs_destruction — destrukce je immutable."""
- try:
- db = get_db()
- return set(db.iwrs_destruction.distinct("basket_id", {"study": study}))
- except Exception as e:
- print(f" UPOZORNĚNÍ: nelze načíst košíky z Mongo ({e}), stahuji vše")
- return set()
-
-
-def get_received_shipments(study):
- """Zásilky, jejichž položky už jsou v Mongo se statusem RECEIVED (finální stav)."""
- try:
- db = get_db()
- return set(db.iwrs_shipment_items.distinct(
- "shipment_id",
- {"study": study, "shipment_status": {"$regex": "^received$", "$options": "i"}},
- ))
- except Exception as e:
- print(f" UPOZORNĚNÍ: nelze načíst zásilky z Mongo ({e}), stahuji vše")
- return set()
-
-
-# ── download funkce ──────────────────────────────────────────────────────────
-
-def download_inventory(page, study):
- today = _today()
- page.goto(f"{BASE_URL}/report/onsite_inventory_detail")
- page.wait_for_load_state("networkidle", timeout=120000)
-
- for site_id in SITES[study]:
- print(f" [{site_id}] inventory...")
- page.locator('input[placeholder="search"], input[type="text"]').first.click()
- page.get_by_role("option", name=site_id).click()
- page.wait_for_load_state("networkidle", timeout=120000)
-
- filename = unique_path(INCOMING_DIR, f"{today} {study} Onsite Inventory {site_id}")
- with page.expect_download(timeout=120000) as dl:
- page.get_by_role("button", name="Download XLS").click()
- dl.value.save_as(filename)
-
- page.get_by_role("button", name="Clear").click()
- page.wait_for_load_state("networkidle", timeout=120000)
- print(f" Inventory OK ({len(SITES[study])} center)")
-
-
-def download_destruction(page, study):
- today = _today()
- page.goto(f"{BASE_URL}/report/ip_destruction_form")
- page.wait_for_load_state("networkidle", timeout=120000)
-
- page.locator('input[placeholder="search"], input[type="text"]').first.click()
- page.wait_for_timeout(1000)
- baskets = [b.strip() for b in page.locator("mat-option").all_inner_texts()
- if b.strip() and b.strip() != "No results found"]
- page.keyboard.press("Escape")
- page.wait_for_timeout(500)
-
- if not baskets:
- print(" Žádné destruction košíky")
- return
-
- existing = get_existing_baskets(study)
- new_count = 0
- for basket in baskets:
- if basket in existing:
- continue # destrukce se nemění — přeskočit
- print(f" [košík {basket}] stahování...")
- input_field = page.locator('input[placeholder="search"], input[type="text"]').first
- input_field.click()
- input_field.fill(basket)
- page.wait_for_timeout(500)
- page.locator("mat-option").first.dispatch_event("click")
- page.wait_for_load_state("networkidle", timeout=120000)
-
- filename = unique_path(INCOMING_DIR, f"{today} {study} IP Destruction {basket}")
- with page.expect_download(timeout=120000) as dl:
- page.get_by_role("button", name="Download XLS").click()
- dl.value.save_as(filename)
- new_count += 1
-
- page.get_by_role("button", name="Clear").click()
- page.wait_for_load_state("networkidle", timeout=120000)
-
- print(f" Destruction OK ({new_count} nových, {len(baskets) - new_count} přeskočeno)")
-
-
-def download_shipments_report(page, study):
- today = _today()
- page.goto(f"{BASE_URL}/report/shipments_report")
- page.wait_for_load_state("networkidle", timeout=120000)
-
- filename = unique_path(INCOMING_DIR, f"{today} {study} Shipments Report")
- with page.expect_download(timeout=120000) as dl:
- page.get_by_role("button", name="Download XLS").click()
- dl.value.save_as(filename)
- print(f" Shipments report OK -> {os.path.basename(filename)}")
- return filename
-
-
-def download_shipment_details(page, study, shipments_report_path):
- today = _today()
-
- # načti CZ shipment IDs z právě staženého shipments reportu
- raw = pd.read_excel(shipments_report_path, header=None)
- header_row = None
- for i, row in raw.iterrows():
- if "Shipment ID" in [str(v).strip() for v in row]:
- header_row = i
- break
- df = pd.read_excel(shipments_report_path, header=header_row)
- df = df.dropna(how="all")
- df = df[df["Location"].astype(str).str.contains("Czech", na=False, case=False)]
- cz_shipments = list(zip(
- df["Shipment ID"].astype(str).str.strip(),
- df["IRT Shipment Status"].astype(str).str.strip() if "IRT Shipment Status" in df.columns else [""] * len(df),
- ))
- print(f" CZ zásilek celkem: {len(cz_shipments)}")
-
- received = get_received_shipments(study)
-
- page.goto(f"{BASE_URL}/report/shipment_details_report")
- page.wait_for_load_state("networkidle", timeout=120000)
-
- skipped = 0
- for shipment, status in cz_shipments:
- if shipment in received:
- skipped += 1
- continue # položky v Mongo už mají finální stav RECEIVED
- input_field = page.locator('input[placeholder="search"], input[type="text"]').first
- input_field.click()
- input_field.fill(shipment)
- page.wait_for_timeout(500)
- page.locator("mat-option").first.dispatch_event("click")
- page.wait_for_load_state("networkidle", timeout=120000)
-
- filename = unique_path(INCOMING_DIR, f"{today} {study} Shipment Details {shipment}")
- with page.expect_download(timeout=120000) as dl:
- page.get_by_role("button", name="Download XLS").click()
- dl.value.save_as(filename)
- print(f" [{shipment}] ({status}) OK")
-
- page.get_by_role("button", name="Clear").click()
- page.wait_for_load_state("networkidle", timeout=120000)
-
- print(f" Přeskočeno (RECEIVED v Mongo): {skipped}")
-
-
-def run(page, study):
- """Stáhne všechny 4 typy Drugs reportů pro studii do IWRS/Incoming/."""
- os.makedirs(INCOMING_DIR, exist_ok=True)
-
- print("\n [1/4] Onsite inventory...")
- download_inventory(page, study)
-
- print("\n [2/4] IP destruction...")
- download_destruction(page, study)
-
- print("\n [3/4] Shipments report...")
- report_path = download_shipments_report(page, study)
-
- print("\n [4/4] Shipment details (CZ)...")
- download_shipment_details(page, study, report_path)
diff --git a/IWRS/Drugs/import_drugs.py b/IWRS/Drugs/import_drugs.py
deleted file mode 100644
index 53474a7..0000000
--- a/IWRS/Drugs/import_drugs.py
+++ /dev/null
@@ -1,306 +0,0 @@
-"""
-import_drugs.py — import Drugs reportů z IWRS/Incoming/ do MongoDB.
-Verze: 1.0 | Datum: 2026-06-10
-
-Nahrazuje Drugs/import_to_mongo.py (ten parsoval pevné adresáře xls_*;
-nyní se parsují datumované soubory z IWRS/Incoming/).
-
-Per studie a běh: jeden import_id. Soubory se zpracují nejstarší napřed,
-při více souborech stejného záznamu vyhrává poslední (poslední stav).
-Po úspěšném zápisu do Monga se zparsované soubory přesunou do
-IWRS/Incoming/Processed/; soubor s chybou parsování zůstává v Incoming/.
-
-Cílové kolekce (db `studie`):
- iwrs_shipments / iwrs_shipment_items / iwrs_inventory (upsert + snapshot)
- iwrs_destruction (upsert only, immutable)
-
-Volá se z IWRS/run_all_v1.0.py (ensure_indexes volá orchestrátor);
-lze spustit i samostatně: python import_drugs.py
-"""
-
-import os
-import re
-import sys
-import glob
-
-import pandas as pd
-
-BASE_DIR = os.path.dirname(os.path.abspath(__file__))
-IWRS_DIR = os.path.dirname(BASE_DIR)
-for _p in (IWRS_DIR, BASE_DIR):
- if _p not in sys.path:
- sys.path.insert(0, _p)
-
-from common.paths import INCOMING_DIR, STUDIES, move_done, sorted_by_mtime
-from common.mongo_writer import (
- to_str, to_int, to_date,
- ensure_indexes, log_import,
- bulk_upsert_with_snapshot, bulk_upsert_only,
-)
-
-
-def _pending(pattern):
- return sorted_by_mtime(glob.glob(os.path.join(INCOMING_DIR, pattern)))
-
-
-def _find_header_row(raw, marker):
- for i, row in raw.iterrows():
- if marker in [str(v).strip() for v in row]:
- return i
- return None
-
-
-# ── XLSX parsery (per soubor) ────────────────────────────────────────────────
-
-def parse_shipments_file(path, study):
- raw = pd.read_excel(path, header=None)
- header_row = _find_header_row(raw, "Shipment ID")
- if header_row is None:
- raise ValueError("hlavičkový řádek 'Shipment ID' nenalezen")
- df = pd.read_excel(path, header=header_row).dropna(how="all")
- df = df[df["Location"].astype(str).str.contains("Czech", na=False, case=False)]
- col = df.columns.tolist()
- rows = []
- for _, r in df.iterrows():
- sid = to_str(r["Shipment ID"])
- if not sid:
- continue
- rows.append({
- "_id": sid,
- "shipment_id": sid,
- "study": study,
- "status": to_str(r["IRT Shipment Status"]),
- "type": to_str(r["Type"]),
- "ship_from": to_str(r["Shipment From"]),
- "ship_to_site": to_str(r["Ship To:"]),
- "location": to_str(r["Location"]),
- "request_date": to_date(r["Request Date"]),
- "shipped_date": to_date(r["Shipped Date"]),
- "received_date": to_date(r["Received Date"]) if "Received Date" in col else None,
- "received_by": to_str(r["Received by"]) if "Received by" in col else None,
- "delivered_date_utc": to_date(r["Delivered Date [UTC]"]) if "Delivered Date [UTC]" in col else None,
- "delivery_recipient": to_str(r["Delivery Recipient"]) if "Delivery Recipient" in col else None,
- "delivery_details": to_str(r["Delivery Details"]) if "Delivery Details" in col else None,
- "cancelled_date": to_date(r["Cancelled Date"]) if "Cancelled Date" in col else None,
- "total_medication_ids": to_int(r["Total Medication IDs"]) if "Total Medication IDs" in col else None,
- "tracking_no": to_str(r["Tracking #"]) if "Tracking #" in col else None,
- "shipping_category": to_str(r["Shipping Category"]) if "Shipping Category" in col else None,
- "expected_arrival": to_date(r["Expected Arrival"]) if "Expected Arrival" in col else None,
- })
- return rows
-
-
-def parse_shipment_details_file(path, study):
- # shipment_id z názvu: "... Shipment Details {id}[ HHMM].xlsx"
- m = re.search(r"Shipment Details (\S+?)(?: \d{4})?\.xlsx$", os.path.basename(path))
- shipment_id = m.group(1) if m else "UNKNOWN"
- raw = pd.read_excel(path, header=None)
- header_row = _find_header_row(raw, "Medication ID")
- if header_row is None:
- raise ValueError("hlavičkový řádek 'Medication ID' nenalezen")
- df = pd.read_excel(path, header=header_row).dropna(how="all")
- rows = []
- for _, r in df.iterrows():
- med_desc = (to_str(r.get("Medication Description"))
- or to_str(r.get("Medication ID Description")))
- med_type = (to_str(r.get("Medication type"))
- or to_str(r.get("Medication ID type")))
- med_id = to_str(r.get("Medication ID"))
- if not med_id:
- continue
- rows.append({
- "_id": f"{shipment_id}:{med_id}",
- "study": study,
- "shipment_id": shipment_id,
- "destination_location": to_str(r.get("Destination Location")),
- "shipment_status": to_str(r.get("IRT Shipment Status")),
- "shipment_type": to_str(r.get("Type")),
- "destination_site": to_str(r.get("Destination Site")),
- "investigator": to_str(r.get("Investigator")),
- "medication_description": med_desc,
- "medication_type": med_type,
- "medication_id": med_id,
- "packaged_lot_no": to_str(r.get("Packaged Lot number")),
- "packaged_lot_description": to_str(r.get("Packaged Lot description")),
- "container_id": to_str(r.get("Container ID")),
- "quantity": to_int(r.get("Quantity of Medication IDs")),
- "expiration_date": to_date(r.get("Expiration Date")),
- "item_status": to_str(r.get("Status")),
- })
- return rows
-
-
-def parse_inventory_file(path, study):
- raw = pd.read_excel(path, header=None)
- site = investigator = location = None
- header_row = None
- for i, row in raw.iterrows():
- first = str(row.iloc[0]).strip() if pd.notna(row.iloc[0]) else ""
- if first.startswith("Site:"):
- site = first.replace("Site:", "").strip()
- elif first.startswith("Investigator:"):
- investigator = first.replace("Investigator:", "").strip()
- elif first.startswith("Location:"):
- location = first.replace("Location:", "").strip()
- if first in ("Medication", "Medication ID") and header_row is None:
- header_row = i
- if header_row is None:
- raise ValueError("hlavičkový řádek 'Medication' nenalezen")
- df = pd.read_excel(path, header=header_row).dropna(how="all")
- df = df.rename(columns={df.columns[0]: "medication_id"})
- rows = []
- for _, r in df.iterrows():
- med_id = to_str(r["medication_id"])
- if not med_id or not site:
- continue
- rows.append({
- "_id": f"{site}:{med_id}",
- "study": study,
- "site": site,
- "investigator": investigator,
- "location": location,
- "medication_id": med_id,
- "packaged_lot_no": to_str(r.get("Packaged Lot number")),
- "original_expiration_date": to_date(r.get("Original Expiration Date when Packaged Lot was Added")),
- "expiration_date": to_date(r.get("Expiration date")),
- "received_date": to_date(r.get("Received Date")),
- "receipt_user": to_str(r.get("Shipment Receipt User")),
- "subject_identifier": to_str(r.get("Subject Identifier")),
- "quantity_assigned": to_int(r.get("Quantity Assigned")),
- "irt_transaction": to_str(r.get("IRT Transaction")),
- "date_assigned": to_date(r.get("Date Assigned")),
- "assignment_user": to_str(r.get("Assignment User")),
- "dispensation_status": to_str(r.get("Dispensation Status")),
- "dispensing_date": to_date(r.get("Dispensing date") or r.get("Dispensing Date")),
- "quantity_dispensed": to_int(r.get("Quantity Dispensed")),
- "dispensing_user": to_str(r.get("Dispensing User")),
- "quantity_returned": to_int(r.get("Quantity Returned")),
- "date_returned": to_date(r.get("Date Returned")),
- "return_user": to_str(r.get("Return User")),
- })
- return rows
-
-
-def parse_destruction_file(path, study):
- raw = pd.read_excel(path, header=None)
- meta = {}
- header_row = None
- for i, row in raw.iterrows():
- first = str(row.iloc[0]).strip() if pd.notna(row.iloc[0]) else ""
- for key, attr in [
- ("Investigator Name:", "investigator"),
- ("Site ID:", "site_id"),
- ("Location:", "location"),
- ("Basket ID:", "basket_id"),
- ("Drug Destruction Created Date:", "destruction_date"),
- ]:
- if first.startswith(key):
- meta[attr] = first.replace(key, "").strip()
- if first == "Medication ID Description" and header_row is None:
- header_row = i
- if header_row is None:
- raise ValueError("hlavičkový řádek 'Medication ID Description' nenalezen")
- df = pd.read_excel(path, header=header_row).dropna(how="all")
- basket_id = meta.get("basket_id")
- rows = []
- for _, r in df.iterrows():
- med_id = to_str(r.get("Medication ID"))
- if not med_id or not basket_id:
- continue
- rows.append({
- "_id": f"{basket_id}:{med_id}",
- "study": study,
- "site_id": meta.get("site_id"),
- "investigator": meta.get("investigator"),
- "location": meta.get("location"),
- "basket_id": basket_id,
- "destruction_date": to_date(meta.get("destruction_date")),
- "medication_description": to_str(r.get("Medication ID Description")),
- "medication_id": med_id,
- "packaged_lot_description": to_str(r.get("Packaged Lot description")),
- "comments": to_str(r.get("Comments")),
- })
- return rows
-
-
-# ── zpracování souborů ───────────────────────────────────────────────────────
-
-def _parse_files(files, parser, study, label):
- """Zparsuje soubory (nejstarší napřed, poslední vyhrává per _id).
-
- Vrací (docs, ok_paths, failed_paths).
- """
- docs, ok, failed = {}, [], []
- for path in files:
- try:
- for d in parser(path, study):
- docs[d["_id"]] = d
- ok.append(path)
- except Exception as e:
- failed.append(path)
- print(f" [{study}] CHYBA parsování {label} {os.path.basename(path)}: {e}")
- return list(docs.values()), ok, failed
-
-
-def import_study(study):
- ship_files = _pending(f"* {study} Shipments Report*.xlsx")
- item_files = _pending(f"* {study} Shipment Details *.xlsx")
- inv_files = _pending(f"* {study} Onsite Inventory *.xlsx")
- dest_files = _pending(f"* {study} IP Destruction *.xlsx")
-
- if not (ship_files or item_files or inv_files or dest_files):
- print(f" [{study}] drugs: nic ke zpracování")
- return
-
- shipments, ok_ship, _ = _parse_files(ship_files, parse_shipments_file, study, "shipments")
- items, ok_item, _ = _parse_files(item_files, parse_shipment_details_file, study, "details")
- inventory, ok_inv, _ = _parse_files(inv_files, parse_inventory_file, study, "inventory")
- destruct, ok_dest, _ = _parse_files(dest_files, parse_destruction_file, study, "destruction")
-
- ok_files = ok_ship + ok_item + ok_inv + ok_dest
- if not ok_files:
- print(f" [{study}] drugs: žádný soubor se nepodařilo zparsovat")
- return
-
- print(f" [{study}] Zásilky: {len(shipments)} | Položky: {len(items)} | "
- f"Sklad: {len(inventory)} | Destrukce: {len(destruct)}")
-
- import_id = log_import(study, f"drugs_{study}", "drugs", {
- "shipments": len(shipments),
- "shipment_items": len(items),
- "inventory": len(inventory),
- "destruction": len(destruct),
- })
- print(f" [{study}] import_id = {import_id}")
-
- bulk_upsert_with_snapshot("iwrs_shipments", "iwrs_shipments_snapshots", shipments, import_id)
- bulk_upsert_with_snapshot("iwrs_shipment_items", "iwrs_shipment_items_snapshots", items, import_id)
- bulk_upsert_with_snapshot("iwrs_inventory", "iwrs_inventory_snapshots", inventory, import_id)
- bulk_upsert_only("iwrs_destruction", destruct, import_id)
-
- # zápis do Monga prošel → archivovat zdrojové soubory
- for path in ok_files:
- move_done(path)
- print(f" [{study}] drugs: {len(ok_files)} soubor(ů) přesunuto do Processed")
-
-
-def run(studies=None):
- studies = studies or STUDIES
- if not os.path.isdir(INCOMING_DIR):
- print(f"Adresář neexistuje: {INCOMING_DIR}")
- return
- print("=" * 60)
- print("Import Drugs (shipments / items / inventory / destruction)")
- print("=" * 60)
- for study in studies:
- try:
- import_study(study)
- except Exception as e:
- import traceback
- print(f" [{study}] CHYBA importu drugs: {e}")
- traceback.print_exc()
-
-
-if __name__ == "__main__":
- ensure_indexes()
- run(sys.argv[1:] or None)
diff --git a/IWRS/Drugs/output/2026-04-21 42847922MDD3003 CZ IWRS overview.xlsx b/IWRS/Drugs/output/2026-04-21 42847922MDD3003 CZ IWRS overview.xlsx
deleted file mode 100644
index a9515da..0000000
Binary files a/IWRS/Drugs/output/2026-04-21 42847922MDD3003 CZ IWRS overview.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/output/2026-04-21 77242113UCO3001 CZ Shipments.xlsx b/IWRS/Drugs/output/2026-04-21 77242113UCO3001 CZ Shipments.xlsx
deleted file mode 100644
index cc31dc5..0000000
Binary files a/IWRS/Drugs/output/2026-04-21 77242113UCO3001 CZ Shipments.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/output/2026-04-21 77242113UCO3001 CZ Shipments_100177.xlsx b/IWRS/Drugs/output/2026-04-21 77242113UCO3001 CZ Shipments_100177.xlsx
deleted file mode 100644
index cfd1874..0000000
Binary files a/IWRS/Drugs/output/2026-04-21 77242113UCO3001 CZ Shipments_100177.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/output/2026-04-27 77242113UCO3001 CZ IWRS overview.xlsx b/IWRS/Drugs/output/2026-04-27 77242113UCO3001 CZ IWRS overview.xlsx
deleted file mode 100644
index 91f0461..0000000
Binary files a/IWRS/Drugs/output/2026-04-27 77242113UCO3001 CZ IWRS overview.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/output/2026-05-12 42847922MDD3003 CZ IWRS overview v1.xlsx b/IWRS/Drugs/output/2026-05-12 42847922MDD3003 CZ IWRS overview v1.xlsx
deleted file mode 100644
index 993c7da..0000000
Binary files a/IWRS/Drugs/output/2026-05-12 42847922MDD3003 CZ IWRS overview v1.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/output/2026-05-12 42847922MDD3003 CZ IWRS overview v2.xlsx b/IWRS/Drugs/output/2026-05-12 42847922MDD3003 CZ IWRS overview v2.xlsx
deleted file mode 100644
index 92607f3..0000000
Binary files a/IWRS/Drugs/output/2026-05-12 42847922MDD3003 CZ IWRS overview v2.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/output/2026-05-12 77242113UCO3001 CZ IWRS overview v1.xlsx b/IWRS/Drugs/output/2026-05-12 77242113UCO3001 CZ IWRS overview v1.xlsx
deleted file mode 100644
index ed4ecad..0000000
Binary files a/IWRS/Drugs/output/2026-05-12 77242113UCO3001 CZ IWRS overview v1.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/output/2026-05-12 77242113UCO3001 CZ IWRS overview v2.xlsx b/IWRS/Drugs/output/2026-05-12 77242113UCO3001 CZ IWRS overview v2.xlsx
deleted file mode 100644
index a8a616b..0000000
Binary files a/IWRS/Drugs/output/2026-05-12 77242113UCO3001 CZ IWRS overview v2.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/output/2026-05-13 42847922MDD3003 CZ IWRS overview v1.xlsx b/IWRS/Drugs/output/2026-05-13 42847922MDD3003 CZ IWRS overview v1.xlsx
deleted file mode 100644
index 30a7849..0000000
Binary files a/IWRS/Drugs/output/2026-05-13 42847922MDD3003 CZ IWRS overview v1.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/output/2026-05-13 77242113UCO3001 CZ IWRS overview v1.xlsx b/IWRS/Drugs/output/2026-05-13 77242113UCO3001 CZ IWRS overview v1.xlsx
deleted file mode 100644
index a8dd09b..0000000
Binary files a/IWRS/Drugs/output/2026-05-13 77242113UCO3001 CZ IWRS overview v1.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/output/2026-05-15 42847922MDD3003 CZ IWRS overview v1.xlsx b/IWRS/Drugs/output/2026-05-15 42847922MDD3003 CZ IWRS overview v1.xlsx
deleted file mode 100644
index e5dedb7..0000000
Binary files a/IWRS/Drugs/output/2026-05-15 42847922MDD3003 CZ IWRS overview v1.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/output/2026-05-15 42847922MDD3003 CZ IWRS overview v2.xlsx b/IWRS/Drugs/output/2026-05-15 42847922MDD3003 CZ IWRS overview v2.xlsx
deleted file mode 100644
index 8d65305..0000000
Binary files a/IWRS/Drugs/output/2026-05-15 42847922MDD3003 CZ IWRS overview v2.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/output/2026-05-15 42847922MDD3003 CZ IWRS overview v3.xlsx b/IWRS/Drugs/output/2026-05-15 42847922MDD3003 CZ IWRS overview v3.xlsx
deleted file mode 100644
index bf8628a..0000000
Binary files a/IWRS/Drugs/output/2026-05-15 42847922MDD3003 CZ IWRS overview v3.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/output/2026-05-15 42847922MDD3003 CZ IWRS overview v4.xlsx b/IWRS/Drugs/output/2026-05-15 42847922MDD3003 CZ IWRS overview v4.xlsx
deleted file mode 100644
index 16d0130..0000000
Binary files a/IWRS/Drugs/output/2026-05-15 42847922MDD3003 CZ IWRS overview v4.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/output/2026-05-15 42847922MDD3003 CZ IWRS overview v5.xlsx b/IWRS/Drugs/output/2026-05-15 42847922MDD3003 CZ IWRS overview v5.xlsx
deleted file mode 100644
index 5c1a8c4..0000000
Binary files a/IWRS/Drugs/output/2026-05-15 42847922MDD3003 CZ IWRS overview v5.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/output/2026-05-15 77242113UCO3001 CZ IWRS overview v1.xlsx b/IWRS/Drugs/output/2026-05-15 77242113UCO3001 CZ IWRS overview v1.xlsx
deleted file mode 100644
index 87cebc1..0000000
Binary files a/IWRS/Drugs/output/2026-05-15 77242113UCO3001 CZ IWRS overview v1.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/output/2026-05-15 77242113UCO3001 CZ IWRS overview v2.xlsx b/IWRS/Drugs/output/2026-05-15 77242113UCO3001 CZ IWRS overview v2.xlsx
deleted file mode 100644
index 8ac7c7d..0000000
Binary files a/IWRS/Drugs/output/2026-05-15 77242113UCO3001 CZ IWRS overview v2.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/output/2026-05-15 77242113UCO3001 CZ IWRS overview v3.xlsx b/IWRS/Drugs/output/2026-05-15 77242113UCO3001 CZ IWRS overview v3.xlsx
deleted file mode 100644
index 2d2afa4..0000000
Binary files a/IWRS/Drugs/output/2026-05-15 77242113UCO3001 CZ IWRS overview v3.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/output/2026-05-15 77242113UCO3001 CZ IWRS overview v4.xlsx b/IWRS/Drugs/output/2026-05-15 77242113UCO3001 CZ IWRS overview v4.xlsx
deleted file mode 100644
index 30f62cd..0000000
Binary files a/IWRS/Drugs/output/2026-05-15 77242113UCO3001 CZ IWRS overview v4.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/output/2026-05-19 42847922MDD3003 CZ IWRS overview v1.xlsx b/IWRS/Drugs/output/2026-05-19 42847922MDD3003 CZ IWRS overview v1.xlsx
deleted file mode 100644
index 548968d..0000000
Binary files a/IWRS/Drugs/output/2026-05-19 42847922MDD3003 CZ IWRS overview v1.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/output/2026-05-19 77242113UCO3001 CZ IWRS overview v1.xlsx b/IWRS/Drugs/output/2026-05-19 77242113UCO3001 CZ IWRS overview v1.xlsx
deleted file mode 100644
index ec01f02..0000000
Binary files a/IWRS/Drugs/output/2026-05-19 77242113UCO3001 CZ IWRS overview v1.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/output/2026-05-20 42847922MDD3003 CZ IWRS overview v1.xlsx b/IWRS/Drugs/output/2026-05-20 42847922MDD3003 CZ IWRS overview v1.xlsx
deleted file mode 100644
index b2ceeaf..0000000
Binary files a/IWRS/Drugs/output/2026-05-20 42847922MDD3003 CZ IWRS overview v1.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/output/2026-05-20 77242113UCO3001 CZ IWRS overview v1.xlsx b/IWRS/Drugs/output/2026-05-20 77242113UCO3001 CZ IWRS overview v1.xlsx
deleted file mode 100644
index c257c0b..0000000
Binary files a/IWRS/Drugs/output/2026-05-20 77242113UCO3001 CZ IWRS overview v1.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/output/2026-05-21 42847922MDD3003 CZ IWRS overview v1.xlsx b/IWRS/Drugs/output/2026-05-21 42847922MDD3003 CZ IWRS overview v1.xlsx
deleted file mode 100644
index 3d2ed0d..0000000
Binary files a/IWRS/Drugs/output/2026-05-21 42847922MDD3003 CZ IWRS overview v1.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/output/2026-05-21 77242113UCO3001 CZ IWRS overview v1.xlsx b/IWRS/Drugs/output/2026-05-21 77242113UCO3001 CZ IWRS overview v1.xlsx
deleted file mode 100644
index f620180..0000000
Binary files a/IWRS/Drugs/output/2026-05-21 77242113UCO3001 CZ IWRS overview v1.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/output/2026-05-25 42847922MDD3003 CZ IWRS overview v1.xlsx b/IWRS/Drugs/output/2026-05-25 42847922MDD3003 CZ IWRS overview v1.xlsx
deleted file mode 100644
index 668d48b..0000000
Binary files a/IWRS/Drugs/output/2026-05-25 42847922MDD3003 CZ IWRS overview v1.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/output/2026-05-25 42847922MDD3003 CZ IWRS overview v2.xlsx b/IWRS/Drugs/output/2026-05-25 42847922MDD3003 CZ IWRS overview v2.xlsx
deleted file mode 100644
index 1ef233d..0000000
Binary files a/IWRS/Drugs/output/2026-05-25 42847922MDD3003 CZ IWRS overview v2.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/output/2026-05-25 42847922MDD3003 CZ IWRS overview v3.xlsx b/IWRS/Drugs/output/2026-05-25 42847922MDD3003 CZ IWRS overview v3.xlsx
deleted file mode 100644
index 0910f2c..0000000
Binary files a/IWRS/Drugs/output/2026-05-25 42847922MDD3003 CZ IWRS overview v3.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/output/2026-05-25 77242113UCO3001 CZ IWRS overview v1.xlsx b/IWRS/Drugs/output/2026-05-25 77242113UCO3001 CZ IWRS overview v1.xlsx
deleted file mode 100644
index 4833832..0000000
Binary files a/IWRS/Drugs/output/2026-05-25 77242113UCO3001 CZ IWRS overview v1.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/output/2026-05-25 77242113UCO3001 CZ IWRS overview v2.xlsx b/IWRS/Drugs/output/2026-05-25 77242113UCO3001 CZ IWRS overview v2.xlsx
deleted file mode 100644
index 1430f62..0000000
Binary files a/IWRS/Drugs/output/2026-05-25 77242113UCO3001 CZ IWRS overview v2.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/output/2026-05-25 77242113UCO3001 CZ IWRS overview v3.xlsx b/IWRS/Drugs/output/2026-05-25 77242113UCO3001 CZ IWRS overview v3.xlsx
deleted file mode 100644
index 9b86d08..0000000
Binary files a/IWRS/Drugs/output/2026-05-25 77242113UCO3001 CZ IWRS overview v3.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/output/2026-05-26 42847922MDD3003 CZ IWRS overview v1.xlsx b/IWRS/Drugs/output/2026-05-26 42847922MDD3003 CZ IWRS overview v1.xlsx
deleted file mode 100644
index c145e10..0000000
Binary files a/IWRS/Drugs/output/2026-05-26 42847922MDD3003 CZ IWRS overview v1.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/output/2026-05-26 77242113UCO3001 CZ IWRS overview v1.xlsx b/IWRS/Drugs/output/2026-05-26 77242113UCO3001 CZ IWRS overview v1.xlsx
deleted file mode 100644
index 82ba4d0..0000000
Binary files a/IWRS/Drugs/output/2026-05-26 77242113UCO3001 CZ IWRS overview v1.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/output/2026-05-27 42847922MDD3003 CZ IWRS overview v1.xlsx b/IWRS/Drugs/output/2026-05-27 42847922MDD3003 CZ IWRS overview v1.xlsx
deleted file mode 100644
index ad1b0bc..0000000
Binary files a/IWRS/Drugs/output/2026-05-27 42847922MDD3003 CZ IWRS overview v1.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/output/2026-05-27 77242113UCO3001 CZ IWRS overview v1.xlsx b/IWRS/Drugs/output/2026-05-27 77242113UCO3001 CZ IWRS overview v1.xlsx
deleted file mode 100644
index 148526a..0000000
Binary files a/IWRS/Drugs/output/2026-05-27 77242113UCO3001 CZ IWRS overview v1.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/output/2026-05-31 42847922MDD3003 CZ IWRS overview v1.xlsx b/IWRS/Drugs/output/2026-05-31 42847922MDD3003 CZ IWRS overview v1.xlsx
deleted file mode 100644
index 751fd35..0000000
Binary files a/IWRS/Drugs/output/2026-05-31 42847922MDD3003 CZ IWRS overview v1.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/output/2026-05-31 77242113UCO3001 CZ IWRS overview v1.xlsx b/IWRS/Drugs/output/2026-05-31 77242113UCO3001 CZ IWRS overview v1.xlsx
deleted file mode 100644
index 3f27a64..0000000
Binary files a/IWRS/Drugs/output/2026-05-31 77242113UCO3001 CZ IWRS overview v1.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/output/2026-06-01 42847922MDD3003 CZ IWRS overview v1.xlsx b/IWRS/Drugs/output/2026-06-01 42847922MDD3003 CZ IWRS overview v1.xlsx
deleted file mode 100644
index 29c9241..0000000
Binary files a/IWRS/Drugs/output/2026-06-01 42847922MDD3003 CZ IWRS overview v1.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/output/2026-06-01 77242113UCO3001 CZ IWRS overview v1.xlsx b/IWRS/Drugs/output/2026-06-01 77242113UCO3001 CZ IWRS overview v1.xlsx
deleted file mode 100644
index 50fb7e8..0000000
Binary files a/IWRS/Drugs/output/2026-06-01 77242113UCO3001 CZ IWRS overview v1.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/output/2026-06-03 42847922MDD3003 CZ IWRS overview v1.xlsx b/IWRS/Drugs/output/2026-06-03 42847922MDD3003 CZ IWRS overview v1.xlsx
deleted file mode 100644
index 992ce3f..0000000
Binary files a/IWRS/Drugs/output/2026-06-03 42847922MDD3003 CZ IWRS overview v1.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/output/2026-06-03 77242113UCO3001 CZ IWRS overview v1.xlsx b/IWRS/Drugs/output/2026-06-03 77242113UCO3001 CZ IWRS overview v1.xlsx
deleted file mode 100644
index b9aed45..0000000
Binary files a/IWRS/Drugs/output/2026-06-03 77242113UCO3001 CZ IWRS overview v1.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/output/2026-06-05 42847922MDD3003 CZ IWRS overview v1.xlsx b/IWRS/Drugs/output/2026-06-05 42847922MDD3003 CZ IWRS overview v1.xlsx
deleted file mode 100644
index 831a7c4..0000000
Binary files a/IWRS/Drugs/output/2026-06-05 42847922MDD3003 CZ IWRS overview v1.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/output/2026-06-05 77242113UCO3001 CZ IWRS overview v1.xlsx b/IWRS/Drugs/output/2026-06-05 77242113UCO3001 CZ IWRS overview v1.xlsx
deleted file mode 100644
index b5d15d8..0000000
Binary files a/IWRS/Drugs/output/2026-06-05 77242113UCO3001 CZ IWRS overview v1.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/output/2026-06-09 42847922MDD3003 CZ IWRS overview v1.xlsx b/IWRS/Drugs/output/2026-06-09 42847922MDD3003 CZ IWRS overview v1.xlsx
deleted file mode 100644
index 450f348..0000000
Binary files a/IWRS/Drugs/output/2026-06-09 42847922MDD3003 CZ IWRS overview v1.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/output/2026-06-09 77242113UCO3001 CZ IWRS overview v1.xlsx b/IWRS/Drugs/output/2026-06-09 77242113UCO3001 CZ IWRS overview v1.xlsx
deleted file mode 100644
index 673f5db..0000000
Binary files a/IWRS/Drugs/output/2026-06-09 77242113UCO3001 CZ IWRS overview v1.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/preview_visits.py b/IWRS/Drugs/preview_visits.py
deleted file mode 100644
index 979f425..0000000
--- a/IWRS/Drugs/preview_visits.py
+++ /dev/null
@@ -1,52 +0,0 @@
-import mysql.connector
-import pandas as pd
-import db_config
-
-conn = mysql.connector.connect(
- host=db_config.DB_HOST, port=db_config.DB_PORT,
- user=db_config.DB_USER, password=db_config.DB_PASSWORD,
- database=db_config.DB_NAME,
-)
-cursor = conn.cursor(dictionary=True)
-
-# Vezmi nejnovější import_id pro každou studii
-for study in ["77242113UCO3001", "42847922MDD3003"]:
- cursor.execute(
- "SELECT MAX(import_id) AS mid FROM iwrs_import WHERE study=%s AND report_type='patients'",
- (study,),
- )
- row = cursor.fetchone()
- mid = row["mid"]
- print(f"\n=== {study} (import_id={mid}) ===")
-
- cursor.execute("""
- SELECT
- v.subject,
- v.actual_date,
- v.scheduled_date,
- v.irt_transaction_no,
- v.irt_transaction_description,
- v.medication_assignment,
- GROUP_CONCAT(v.medication_id ORDER BY v.medication_id SEPARATOR ', ') AS medication_ids,
- SUM(v.quantity_assigned) AS quantity_assigned
- FROM iwrs_subject_visits v
- WHERE v.import_id = %s AND v.study = %s AND v.visit_type = 'Past'
- AND v.irt_transaction_no IS NOT NULL
- GROUP BY v.subject, v.actual_date, v.scheduled_date, v.irt_transaction_no,
- v.irt_transaction_description, v.medication_assignment
- ORDER BY v.subject, v.actual_date
- LIMIT 20
- """, (mid, study))
-
- rows = cursor.fetchall()
- df = pd.DataFrame(rows)
- if df.empty:
- print(" Žádná data.")
- else:
- pd.set_option("display.max_columns", None)
- pd.set_option("display.width", 200)
- pd.set_option("display.max_colwidth", 30)
- print(df.to_string(index=False))
-
-cursor.close()
-conn.close()
diff --git a/IWRS/Drugs/xls_ip_destruction_42847922MDD3003/ip_destruction_basket_194.xlsx b/IWRS/Drugs/xls_ip_destruction_42847922MDD3003/ip_destruction_basket_194.xlsx
deleted file mode 100644
index f7c1e0a..0000000
Binary files a/IWRS/Drugs/xls_ip_destruction_42847922MDD3003/ip_destruction_basket_194.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_ip_destruction_42847922MDD3003/ip_destruction_basket_202.xlsx b/IWRS/Drugs/xls_ip_destruction_42847922MDD3003/ip_destruction_basket_202.xlsx
deleted file mode 100644
index a890c9e..0000000
Binary files a/IWRS/Drugs/xls_ip_destruction_42847922MDD3003/ip_destruction_basket_202.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_ip_destruction_42847922MDD3003/ip_destruction_basket_248.xlsx b/IWRS/Drugs/xls_ip_destruction_42847922MDD3003/ip_destruction_basket_248.xlsx
deleted file mode 100644
index 46fad8f..0000000
Binary files a/IWRS/Drugs/xls_ip_destruction_42847922MDD3003/ip_destruction_basket_248.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_ip_destruction_42847922MDD3003/ip_destruction_basket_269.xlsx b/IWRS/Drugs/xls_ip_destruction_42847922MDD3003/ip_destruction_basket_269.xlsx
deleted file mode 100644
index 0e590b5..0000000
Binary files a/IWRS/Drugs/xls_ip_destruction_42847922MDD3003/ip_destruction_basket_269.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_ip_destruction_42847922MDD3003/ip_destruction_basket_273.xlsx b/IWRS/Drugs/xls_ip_destruction_42847922MDD3003/ip_destruction_basket_273.xlsx
deleted file mode 100644
index 86ffc69..0000000
Binary files a/IWRS/Drugs/xls_ip_destruction_42847922MDD3003/ip_destruction_basket_273.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_ip_destruction_42847922MDD3003/ip_destruction_basket_276.xlsx b/IWRS/Drugs/xls_ip_destruction_42847922MDD3003/ip_destruction_basket_276.xlsx
deleted file mode 100644
index d193ae7..0000000
Binary files a/IWRS/Drugs/xls_ip_destruction_42847922MDD3003/ip_destruction_basket_276.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_ip_destruction_42847922MDD3003/ip_destruction_basket_286.xlsx b/IWRS/Drugs/xls_ip_destruction_42847922MDD3003/ip_destruction_basket_286.xlsx
deleted file mode 100644
index 6e29d3a..0000000
Binary files a/IWRS/Drugs/xls_ip_destruction_42847922MDD3003/ip_destruction_basket_286.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_ip_destruction_42847922MDD3003/ip_destruction_basket_289.xlsx b/IWRS/Drugs/xls_ip_destruction_42847922MDD3003/ip_destruction_basket_289.xlsx
deleted file mode 100644
index ce22899..0000000
Binary files a/IWRS/Drugs/xls_ip_destruction_42847922MDD3003/ip_destruction_basket_289.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_ip_destruction_42847922MDD3003/ip_destruction_basket_301.xlsx b/IWRS/Drugs/xls_ip_destruction_42847922MDD3003/ip_destruction_basket_301.xlsx
deleted file mode 100644
index eb84fc5..0000000
Binary files a/IWRS/Drugs/xls_ip_destruction_42847922MDD3003/ip_destruction_basket_301.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_ip_destruction_42847922MDD3003/ip_destruction_basket_313.xlsx b/IWRS/Drugs/xls_ip_destruction_42847922MDD3003/ip_destruction_basket_313.xlsx
deleted file mode 100644
index a855cf2..0000000
Binary files a/IWRS/Drugs/xls_ip_destruction_42847922MDD3003/ip_destruction_basket_313.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_ip_destruction_42847922MDD3003/ip_destruction_basket_326.xlsx b/IWRS/Drugs/xls_ip_destruction_42847922MDD3003/ip_destruction_basket_326.xlsx
deleted file mode 100644
index 3397dc6..0000000
Binary files a/IWRS/Drugs/xls_ip_destruction_42847922MDD3003/ip_destruction_basket_326.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_ip_destruction_42847922MDD3003/ip_destruction_basket_343.xlsx b/IWRS/Drugs/xls_ip_destruction_42847922MDD3003/ip_destruction_basket_343.xlsx
deleted file mode 100644
index 400e438..0000000
Binary files a/IWRS/Drugs/xls_ip_destruction_42847922MDD3003/ip_destruction_basket_343.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_ip_destruction_42847922MDD3003/ip_destruction_basket_358.xlsx b/IWRS/Drugs/xls_ip_destruction_42847922MDD3003/ip_destruction_basket_358.xlsx
deleted file mode 100644
index 8642e53..0000000
Binary files a/IWRS/Drugs/xls_ip_destruction_42847922MDD3003/ip_destruction_basket_358.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_reports_42847922MDD3003/onsite_inventory_detail_S10-CZ10002.xlsx b/IWRS/Drugs/xls_reports_42847922MDD3003/onsite_inventory_detail_S10-CZ10002.xlsx
deleted file mode 100644
index 970b463..0000000
Binary files a/IWRS/Drugs/xls_reports_42847922MDD3003/onsite_inventory_detail_S10-CZ10002.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_reports_42847922MDD3003/onsite_inventory_detail_S10-CZ10004.xlsx b/IWRS/Drugs/xls_reports_42847922MDD3003/onsite_inventory_detail_S10-CZ10004.xlsx
deleted file mode 100644
index 126414f..0000000
Binary files a/IWRS/Drugs/xls_reports_42847922MDD3003/onsite_inventory_detail_S10-CZ10004.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_reports_42847922MDD3003/onsite_inventory_detail_S10-CZ10005.xlsx b/IWRS/Drugs/xls_reports_42847922MDD3003/onsite_inventory_detail_S10-CZ10005.xlsx
deleted file mode 100644
index 5580549..0000000
Binary files a/IWRS/Drugs/xls_reports_42847922MDD3003/onsite_inventory_detail_S10-CZ10005.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_reports_42847922MDD3003/onsite_inventory_detail_S10-CZ10008.xlsx b/IWRS/Drugs/xls_reports_42847922MDD3003/onsite_inventory_detail_S10-CZ10008.xlsx
deleted file mode 100644
index 72bd572..0000000
Binary files a/IWRS/Drugs/xls_reports_42847922MDD3003/onsite_inventory_detail_S10-CZ10008.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_reports_42847922MDD3003/onsite_inventory_detail_S10-CZ10011.xlsx b/IWRS/Drugs/xls_reports_42847922MDD3003/onsite_inventory_detail_S10-CZ10011.xlsx
deleted file mode 100644
index 627f772..0000000
Binary files a/IWRS/Drugs/xls_reports_42847922MDD3003/onsite_inventory_detail_S10-CZ10011.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_reports_42847922MDD3003/onsite_inventory_detail_S10-CZ10012.xlsx b/IWRS/Drugs/xls_reports_42847922MDD3003/onsite_inventory_detail_S10-CZ10012.xlsx
deleted file mode 100644
index 80279df..0000000
Binary files a/IWRS/Drugs/xls_reports_42847922MDD3003/onsite_inventory_detail_S10-CZ10012.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_reports_77242113UCO3001/onsite_inventory_detail_DD5-CZ10001.xlsx b/IWRS/Drugs/xls_reports_77242113UCO3001/onsite_inventory_detail_DD5-CZ10001.xlsx
deleted file mode 100644
index 3e9a340..0000000
Binary files a/IWRS/Drugs/xls_reports_77242113UCO3001/onsite_inventory_detail_DD5-CZ10001.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_reports_77242113UCO3001/onsite_inventory_detail_DD5-CZ10003.xlsx b/IWRS/Drugs/xls_reports_77242113UCO3001/onsite_inventory_detail_DD5-CZ10003.xlsx
deleted file mode 100644
index 5d5eee7..0000000
Binary files a/IWRS/Drugs/xls_reports_77242113UCO3001/onsite_inventory_detail_DD5-CZ10003.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_reports_77242113UCO3001/onsite_inventory_detail_DD5-CZ10006.xlsx b/IWRS/Drugs/xls_reports_77242113UCO3001/onsite_inventory_detail_DD5-CZ10006.xlsx
deleted file mode 100644
index 7533a8b..0000000
Binary files a/IWRS/Drugs/xls_reports_77242113UCO3001/onsite_inventory_detail_DD5-CZ10006.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_reports_77242113UCO3001/onsite_inventory_detail_DD5-CZ10009.xlsx b/IWRS/Drugs/xls_reports_77242113UCO3001/onsite_inventory_detail_DD5-CZ10009.xlsx
deleted file mode 100644
index 5365856..0000000
Binary files a/IWRS/Drugs/xls_reports_77242113UCO3001/onsite_inventory_detail_DD5-CZ10009.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_reports_77242113UCO3001/onsite_inventory_detail_DD5-CZ10010.xlsx b/IWRS/Drugs/xls_reports_77242113UCO3001/onsite_inventory_detail_DD5-CZ10010.xlsx
deleted file mode 100644
index a677935..0000000
Binary files a/IWRS/Drugs/xls_reports_77242113UCO3001/onsite_inventory_detail_DD5-CZ10010.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_reports_77242113UCO3001/onsite_inventory_detail_DD5-CZ10012.xlsx b/IWRS/Drugs/xls_reports_77242113UCO3001/onsite_inventory_detail_DD5-CZ10012.xlsx
deleted file mode 100644
index 866e5fc..0000000
Binary files a/IWRS/Drugs/xls_reports_77242113UCO3001/onsite_inventory_detail_DD5-CZ10012.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_reports_77242113UCO3001/onsite_inventory_detail_DD5-CZ10013.xlsx b/IWRS/Drugs/xls_reports_77242113UCO3001/onsite_inventory_detail_DD5-CZ10013.xlsx
deleted file mode 100644
index 41c70c8..0000000
Binary files a/IWRS/Drugs/xls_reports_77242113UCO3001/onsite_inventory_detail_DD5-CZ10013.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_reports_77242113UCO3001/onsite_inventory_detail_DD5-CZ10015.xlsx b/IWRS/Drugs/xls_reports_77242113UCO3001/onsite_inventory_detail_DD5-CZ10015.xlsx
deleted file mode 100644
index f645880..0000000
Binary files a/IWRS/Drugs/xls_reports_77242113UCO3001/onsite_inventory_detail_DD5-CZ10015.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_reports_77242113UCO3001/onsite_inventory_detail_DD5-CZ10016.xlsx b/IWRS/Drugs/xls_reports_77242113UCO3001/onsite_inventory_detail_DD5-CZ10016.xlsx
deleted file mode 100644
index a1b0376..0000000
Binary files a/IWRS/Drugs/xls_reports_77242113UCO3001/onsite_inventory_detail_DD5-CZ10016.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_reports_77242113UCO3001/onsite_inventory_detail_DD5-CZ10020.xlsx b/IWRS/Drugs/xls_reports_77242113UCO3001/onsite_inventory_detail_DD5-CZ10020.xlsx
deleted file mode 100644
index e07c3e2..0000000
Binary files a/IWRS/Drugs/xls_reports_77242113UCO3001/onsite_inventory_detail_DD5-CZ10020.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_reports_77242113UCO3001/onsite_inventory_detail_DD5-CZ10021.xlsx b/IWRS/Drugs/xls_reports_77242113UCO3001/onsite_inventory_detail_DD5-CZ10021.xlsx
deleted file mode 100644
index ee7ae1c..0000000
Binary files a/IWRS/Drugs/xls_reports_77242113UCO3001/onsite_inventory_detail_DD5-CZ10021.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_reports_77242113UCO3001/onsite_inventory_detail_DD5-CZ10022.xlsx b/IWRS/Drugs/xls_reports_77242113UCO3001/onsite_inventory_detail_DD5-CZ10022.xlsx
deleted file mode 100644
index b23e88d..0000000
Binary files a/IWRS/Drugs/xls_reports_77242113UCO3001/onsite_inventory_detail_DD5-CZ10022.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_100873.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_100873.xlsx
deleted file mode 100644
index 2f7fa54..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_100873.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_100874.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_100874.xlsx
deleted file mode 100644
index 258c572..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_100874.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_100880.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_100880.xlsx
deleted file mode 100644
index 5b6ccc1..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_100880.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_100881.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_100881.xlsx
deleted file mode 100644
index f02f476..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_100881.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_100895.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_100895.xlsx
deleted file mode 100644
index 4200081..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_100895.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_100905.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_100905.xlsx
deleted file mode 100644
index ef9904d..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_100905.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_100946.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_100946.xlsx
deleted file mode 100644
index 72d2c9c..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_100946.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_100971.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_100971.xlsx
deleted file mode 100644
index 0fc1dfe..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_100971.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_100980.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_100980.xlsx
deleted file mode 100644
index f18a7c0..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_100980.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_100986.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_100986.xlsx
deleted file mode 100644
index 079ad78..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_100986.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_100994.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_100994.xlsx
deleted file mode 100644
index 5dfdae7..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_100994.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101085.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101085.xlsx
deleted file mode 100644
index 8c81ed6..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101085.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101092.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101092.xlsx
deleted file mode 100644
index 4b516c3..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101092.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101102.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101102.xlsx
deleted file mode 100644
index 2efc8ec..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101102.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101110.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101110.xlsx
deleted file mode 100644
index a534d23..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101110.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101117.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101117.xlsx
deleted file mode 100644
index 2fd361e..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101117.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101118.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101118.xlsx
deleted file mode 100644
index 4e2e32a..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101118.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101119.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101119.xlsx
deleted file mode 100644
index 08d8da7..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101119.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101139.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101139.xlsx
deleted file mode 100644
index 33bd95c..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101139.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101246.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101246.xlsx
deleted file mode 100644
index b6818ac..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101246.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101270.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101270.xlsx
deleted file mode 100644
index fa69ca4..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101270.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101274.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101274.xlsx
deleted file mode 100644
index 96769ef..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101274.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101293.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101293.xlsx
deleted file mode 100644
index bd3d5c9..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101293.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101300.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101300.xlsx
deleted file mode 100644
index b2b8248..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101300.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101322.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101322.xlsx
deleted file mode 100644
index e8823a8..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101322.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101327.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101327.xlsx
deleted file mode 100644
index 0925291..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101327.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101357.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101357.xlsx
deleted file mode 100644
index 4884854..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101357.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101378.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101378.xlsx
deleted file mode 100644
index 94534aa..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101378.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101385.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101385.xlsx
deleted file mode 100644
index b16cc01..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101385.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101418.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101418.xlsx
deleted file mode 100644
index f67b7f5..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101418.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101444.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101444.xlsx
deleted file mode 100644
index 8be9ff9..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101444.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101487.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101487.xlsx
deleted file mode 100644
index 6de6ab8..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101487.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101508.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101508.xlsx
deleted file mode 100644
index 3218104..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101508.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101524.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101524.xlsx
deleted file mode 100644
index 90184b7..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101524.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101530.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101530.xlsx
deleted file mode 100644
index 12f82a0..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101530.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101531.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101531.xlsx
deleted file mode 100644
index 6bda309..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101531.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101555.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101555.xlsx
deleted file mode 100644
index 35372ba..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101555.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101589.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101589.xlsx
deleted file mode 100644
index 6949101..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101589.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101662.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101662.xlsx
deleted file mode 100644
index b56e198..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101662.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101688.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101688.xlsx
deleted file mode 100644
index bc8adc3..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101688.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101700.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101700.xlsx
deleted file mode 100644
index c7755da..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101700.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101720.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101720.xlsx
deleted file mode 100644
index 3162abb..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101720.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101732.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101732.xlsx
deleted file mode 100644
index f659594..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101732.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101738.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101738.xlsx
deleted file mode 100644
index a3a2165..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101738.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101750.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101750.xlsx
deleted file mode 100644
index 40a86e9..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101750.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101751.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101751.xlsx
deleted file mode 100644
index c968f07..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101751.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101784.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101784.xlsx
deleted file mode 100644
index 0d0521a..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101784.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101785.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101785.xlsx
deleted file mode 100644
index 9eb04d1..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101785.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101827.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101827.xlsx
deleted file mode 100644
index a173fe5..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101827.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101858.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101858.xlsx
deleted file mode 100644
index 61d9755..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101858.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101910.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101910.xlsx
deleted file mode 100644
index b1fbaf4..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101910.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101919.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101919.xlsx
deleted file mode 100644
index b09acd7..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101919.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101925.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101925.xlsx
deleted file mode 100644
index 063ec16..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101925.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101962.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101962.xlsx
deleted file mode 100644
index 75ea307..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101962.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101963.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101963.xlsx
deleted file mode 100644
index eafb3a0..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101963.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101964.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101964.xlsx
deleted file mode 100644
index b9982d8..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101964.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101965.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101965.xlsx
deleted file mode 100644
index 81ad3ae..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101965.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101966.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101966.xlsx
deleted file mode 100644
index d59ba91..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101966.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101967.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101967.xlsx
deleted file mode 100644
index ac045f4..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_101967.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102071.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102071.xlsx
deleted file mode 100644
index 46a38d2..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102071.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102075.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102075.xlsx
deleted file mode 100644
index 394516a..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102075.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102094.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102094.xlsx
deleted file mode 100644
index c5cb372..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102094.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102108.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102108.xlsx
deleted file mode 100644
index 6ad52c0..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102108.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102136.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102136.xlsx
deleted file mode 100644
index 41d1c5f..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102136.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102137.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102137.xlsx
deleted file mode 100644
index db40fe0..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102137.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102160.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102160.xlsx
deleted file mode 100644
index 992a07f..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102160.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102193.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102193.xlsx
deleted file mode 100644
index 93e239a..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102193.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102199.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102199.xlsx
deleted file mode 100644
index d233738..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102199.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102247.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102247.xlsx
deleted file mode 100644
index 3a8f04f..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102247.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102256.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102256.xlsx
deleted file mode 100644
index a1e7bd6..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102256.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102275.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102275.xlsx
deleted file mode 100644
index ead0f33..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102275.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102295.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102295.xlsx
deleted file mode 100644
index baa5a4e..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102295.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102322.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102322.xlsx
deleted file mode 100644
index f2a1d6b..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102322.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102341.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102341.xlsx
deleted file mode 100644
index 71a421f..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102341.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102403.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102403.xlsx
deleted file mode 100644
index 5c7bddf..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102403.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102418.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102418.xlsx
deleted file mode 100644
index ac31e27..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102418.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102439.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102439.xlsx
deleted file mode 100644
index dd6627e..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102439.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102455.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102455.xlsx
deleted file mode 100644
index db4a44b..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102455.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102497.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102497.xlsx
deleted file mode 100644
index 08b64b4..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102497.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102538.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102538.xlsx
deleted file mode 100644
index 61eb222..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102538.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102550.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102550.xlsx
deleted file mode 100644
index 219c81c..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102550.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102596.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102596.xlsx
deleted file mode 100644
index 67de467..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102596.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102602.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102602.xlsx
deleted file mode 100644
index 72dd112..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102602.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102640.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102640.xlsx
deleted file mode 100644
index 81e9679..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102640.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102641.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102641.xlsx
deleted file mode 100644
index 2206827..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102641.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102758.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102758.xlsx
deleted file mode 100644
index df43764..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102758.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102784.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102784.xlsx
deleted file mode 100644
index 265d30b..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102784.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102814.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102814.xlsx
deleted file mode 100644
index d53eaac..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102814.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102839.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102839.xlsx
deleted file mode 100644
index 914560d..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102839.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102840.xlsx b/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102840.xlsx
deleted file mode 100644
index 77b74a7..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_42847922MDD3003/shipment_details_102840.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100177.xlsx b/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100177.xlsx
deleted file mode 100644
index 8d40975..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100177.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100222.xlsx b/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100222.xlsx
deleted file mode 100644
index e5d0347..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100222.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100354.xlsx b/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100354.xlsx
deleted file mode 100644
index f943d52..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100354.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100382.xlsx b/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100382.xlsx
deleted file mode 100644
index eaf5976..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100382.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100411.xlsx b/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100411.xlsx
deleted file mode 100644
index bb5fa9e..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100411.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100421.xlsx b/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100421.xlsx
deleted file mode 100644
index 6dc3f08..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100421.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100498.xlsx b/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100498.xlsx
deleted file mode 100644
index e16b7ce..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100498.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100510.xlsx b/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100510.xlsx
deleted file mode 100644
index 36e1676..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100510.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100587.xlsx b/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100587.xlsx
deleted file mode 100644
index 8b4b60e..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100587.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100593.xlsx b/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100593.xlsx
deleted file mode 100644
index b4d740c..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100593.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100616.xlsx b/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100616.xlsx
deleted file mode 100644
index 557406f..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100616.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100678.xlsx b/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100678.xlsx
deleted file mode 100644
index d121f04..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100678.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100717.xlsx b/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100717.xlsx
deleted file mode 100644
index 97f5958..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100717.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100728.xlsx b/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100728.xlsx
deleted file mode 100644
index 1100be4..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100728.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100733.xlsx b/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100733.xlsx
deleted file mode 100644
index 972ff9a..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100733.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100740.xlsx b/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100740.xlsx
deleted file mode 100644
index bd3c538..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100740.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100776.xlsx b/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100776.xlsx
deleted file mode 100644
index 1436168..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100776.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100843.xlsx b/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100843.xlsx
deleted file mode 100644
index 3841e30..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100843.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100845.xlsx b/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100845.xlsx
deleted file mode 100644
index 7dd92ad..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100845.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100956.xlsx b/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100956.xlsx
deleted file mode 100644
index 2a4700c..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100956.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100974.xlsx b/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100974.xlsx
deleted file mode 100644
index 1d141e0..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100974.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100991.xlsx b/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100991.xlsx
deleted file mode 100644
index 68a67b5..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_100991.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_101039.xlsx b/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_101039.xlsx
deleted file mode 100644
index 0dadc82..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_101039.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_101060.xlsx b/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_101060.xlsx
deleted file mode 100644
index 32ec8c9..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_101060.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_101061.xlsx b/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_101061.xlsx
deleted file mode 100644
index 7fb9f95..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_101061.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_101087.xlsx b/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_101087.xlsx
deleted file mode 100644
index 422a853..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_101087.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_101104.xlsx b/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_101104.xlsx
deleted file mode 100644
index 3486dfa..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_101104.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_101132.xlsx b/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_101132.xlsx
deleted file mode 100644
index 30cfcfc..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_101132.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_101135.xlsx b/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_101135.xlsx
deleted file mode 100644
index d6847eb..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_101135.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_101185.xlsx b/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_101185.xlsx
deleted file mode 100644
index 6443df2..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_101185.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_101204.xlsx b/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_101204.xlsx
deleted file mode 100644
index 9548536..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_101204.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_101210.xlsx b/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_101210.xlsx
deleted file mode 100644
index e64a94f..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_101210.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_101245.xlsx b/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_101245.xlsx
deleted file mode 100644
index 632aa3f..0000000
Binary files a/IWRS/Drugs/xls_shipment_details_77242113UCO3001/shipment_details_101245.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipments_42847922MDD3003/shipments_report_42847922MDD3003.xlsx b/IWRS/Drugs/xls_shipments_42847922MDD3003/shipments_report_42847922MDD3003.xlsx
deleted file mode 100644
index a2193d2..0000000
Binary files a/IWRS/Drugs/xls_shipments_42847922MDD3003/shipments_report_42847922MDD3003.xlsx and /dev/null differ
diff --git a/IWRS/Drugs/xls_shipments_77242113UCO3001/shipments_report_77242113UCO3001.xlsx b/IWRS/Drugs/xls_shipments_77242113UCO3001/shipments_report_77242113UCO3001.xlsx
deleted file mode 100644
index 5a34722..0000000
Binary files a/IWRS/Drugs/xls_shipments_77242113UCO3001/shipments_report_77242113UCO3001.xlsx and /dev/null differ
diff --git a/IWRS/Patients/CONTEXT.md b/IWRS/Patients/CONTEXT.md
deleted file mode 100644
index 8e0a7d5..0000000
--- a/IWRS/Patients/CONTEXT.md
+++ /dev/null
@@ -1,56 +0,0 @@
-# Kontext práce — IWRS Notifications Pipeline
-## Datum: 2026-06-01
-
-## Co bylo uděláno
-
-### Nové soubory
-- `download_subject_notifications.py` — standalone skript pro stažení notifikací (referenční, nepoužívaný v pipeline)
-- `test_notifications.py` — testovací skript pro jednoho pacienta (CZ100222003 / UCO3001)
-- `create_iwrs_tables.py` — jednorázový skript pro vytvoření MySQL tabulek
-
-### Upravené soubory
-- `download_subject_details.py` — přidáno stahování notifikací (PDF + JSON) pro každý subjekt přímo v loopě
-- `import_to_mysql.py` — přidána funkce `import_notifications()` která importuje JSON+PDF do DB a přesouvá do `Zpracováno/`
-- `create_iwrs_tables.sql` — přidána tabulka `iwrs_notifications`
-- `run_all.py` — krok 2 nyní volá `dsd.run()` z `download_subject_details.py`
-
-## Jak to funguje
-
-### Stahování notifikací (v `download_subject_details.py`)
-1. Při výběru subjektu se zachytí `table_1` API response (obsahuje notifikace s `pk`, `et_title`, `label`, `body`, `actual_date_raw`)
-2. Porovná `pk` s DB (`iwrs_notifications`) — stahuje jen nové
-3. Stáhne PDF přes `page.request.get()` s Bearer tokenem (JWT se načítá čerstvě před každým requestem)
-4. Uloží PDF + JSON do `IncomingSourceReportsDetails/{study}/`
-5. Název souboru: `{actual_date_raw}_{label_s_podtržítky}.pdf` (při kolizi přidá `_pk{pk}`)
-
-### API endpointy
-- **Notifikace data**: `POST /_/p/{instance_id}/api/v1/reports_api/report_data?path=patient_detail_report&id={subject}&key=table_1&unblinded=false`
-- **PDF download**: `GET /_/p/{instance_id}/api/v1/meta_api/pdfnotification?pk={pk}&title={et_title}&html=true`
-- **app_instances** (pro zjištění instance_id): `GET /_/api/dispatch/app_instances/`
-- Headers: `Authorization: Bearer {JWT}`, `lang: en`, `prancer_study: {study_code}`
-
-### Instance ID mapping
-- `77242113UCO3001` → `/_/p/106`
-- `42847922MDD3003` → `/_/p/70`
-- `77242113CRD3001` → `/_/p/103`
-
-### Import (`import_to_mysql.py`)
-- Čte všechny `.json` soubory z `IncomingSourceReportsDetails/{study}/`
-- Načte příslušné `.pdf` jako binární data
-- Uloží do tabulky `iwrs_notifications` (UNIQUE KEY na `pk` — bez duplikátů)
-- Přesune soubory do `IncomingSourceReportsDetails/{study}/Zpracováno/`
-
-## MySQL tabulka `iwrs_notifications`
-```sql
-id, study, subject, pk (UNIQUE), title, label, event, actual_date, text (TEXT), pdf (MEDIUMBLOB), source_file, imported_at
-```
-
-## Aktuální stav
-- UCO3001: ~76 notifikací importováno
-- MDD3003: ~119 notifikací importováno (část 403 chyb — JWT expiroval, opraveno načítáním JWT čerstvě)
-- MDD3003 notifikace s 403 čekají na příští `run_all.py` (soubory nejsou v `Zpracováno`, takže se znovu stáhnou)
-
-## Co zbývá / možná vylepšení
-- Ověřit že MDD3003 403 chyby jsou opraveny (JWT refresh)
-- `CZ100132003` UCO3001 — timeout při stahování XLS (subjekt přeskočen, zkusit znovu)
-- Případně přidat retry logiku pro timeout
diff --git a/IWRS/Patients/CreatedReports/2026-05-04 42847922MDD3003 Subject Summary 1306.xlsx b/IWRS/Patients/CreatedReports/2026-05-04 42847922MDD3003 Subject Summary 1306.xlsx
deleted file mode 100644
index 8e81c20..0000000
Binary files a/IWRS/Patients/CreatedReports/2026-05-04 42847922MDD3003 Subject Summary 1306.xlsx and /dev/null differ
diff --git a/IWRS/Patients/CreatedReports/2026-05-04 42847922MDD3003 Subject Summary.xlsx b/IWRS/Patients/CreatedReports/2026-05-04 42847922MDD3003 Subject Summary.xlsx
deleted file mode 100644
index 6971da4..0000000
Binary files a/IWRS/Patients/CreatedReports/2026-05-04 42847922MDD3003 Subject Summary.xlsx and /dev/null differ
diff --git a/IWRS/Patients/CreatedReports/2026-05-04 77242113UCO3001 Subject Summary 1306.xlsx b/IWRS/Patients/CreatedReports/2026-05-04 77242113UCO3001 Subject Summary 1306.xlsx
deleted file mode 100644
index 31d24d0..0000000
Binary files a/IWRS/Patients/CreatedReports/2026-05-04 77242113UCO3001 Subject Summary 1306.xlsx and /dev/null differ
diff --git a/IWRS/Patients/CreatedReports/2026-05-04 77242113UCO3001 Subject Summary.xlsx b/IWRS/Patients/CreatedReports/2026-05-04 77242113UCO3001 Subject Summary.xlsx
deleted file mode 100644
index b315716..0000000
Binary files a/IWRS/Patients/CreatedReports/2026-05-04 77242113UCO3001 Subject Summary.xlsx and /dev/null differ
diff --git a/IWRS/Patients/CreatedReports/2026-05-12 42847922MDD3003 Subject Summary.xlsx b/IWRS/Patients/CreatedReports/2026-05-12 42847922MDD3003 Subject Summary.xlsx
deleted file mode 100644
index d0c6c06..0000000
Binary files a/IWRS/Patients/CreatedReports/2026-05-12 42847922MDD3003 Subject Summary.xlsx and /dev/null differ
diff --git a/IWRS/Patients/CreatedReports/2026-05-12 77242113UCO3001 Subject Summary.xlsx b/IWRS/Patients/CreatedReports/2026-05-12 77242113UCO3001 Subject Summary.xlsx
deleted file mode 100644
index 430c0e3..0000000
Binary files a/IWRS/Patients/CreatedReports/2026-05-12 77242113UCO3001 Subject Summary.xlsx and /dev/null differ
diff --git a/IWRS/Patients/Trash/create_iwrs_tables.py b/IWRS/Patients/Trash/create_iwrs_tables.py
deleted file mode 100644
index 1f80b74..0000000
--- a/IWRS/Patients/Trash/create_iwrs_tables.py
+++ /dev/null
@@ -1,39 +0,0 @@
-"""
-Jednorázový skript — vytvoří/aktualizuje tabulky v MySQL.
-Spusť jednou: python create_iwrs_tables.py
-"""
-import os
-import mysql.connector
-import db_config
-
-BASE_DIR = os.path.dirname(os.path.abspath(__file__))
-SQL_FILE = os.path.join(BASE_DIR, "create_iwrs_tables.sql")
-
-conn = mysql.connector.connect(
- host=db_config.DB_HOST,
- port=db_config.DB_PORT,
- user=db_config.DB_USER,
- password=db_config.DB_PASSWORD,
- database=db_config.DB_NAME,
-)
-cursor = conn.cursor()
-
-sql = open(SQL_FILE, encoding="utf-8").read()
-# Odstraň komentáře a rozdělíme na příkazy
-stmts = [s.strip() for s in sql.split(";")]
-for stmt in stmts:
- # Odstraň řádkové komentáře
- lines = [l for l in stmt.splitlines() if not l.strip().startswith("--")]
- stmt = "\n".join(lines).strip()
- if not stmt or stmt.upper().startswith("USE"):
- continue
- try:
- cursor.execute(stmt)
- print(f"OK: {stmt[:80]}")
- except Exception as e:
- print(f"SKIP: {e}")
-
-conn.commit()
-cursor.close()
-conn.close()
-print("\nHotovo.")
diff --git a/IWRS/Patients/Trash/create_iwrs_tables.sql b/IWRS/Patients/Trash/create_iwrs_tables.sql
deleted file mode 100644
index b022bc7..0000000
--- a/IWRS/Patients/Trash/create_iwrs_tables.sql
+++ /dev/null
@@ -1,128 +0,0 @@
--- IWRS tabulky pro databázi studie
--- Spustit jednou: mysql -h 192.168.1.76 -u root -p studie < create_iwrs_tables.sql
-
-USE studie;
-
--- ── Import log ───────────────────────────────────────────────────────────────
-CREATE TABLE IF NOT EXISTS iwrs_import (
- import_id INT AUTO_INCREMENT PRIMARY KEY,
- study VARCHAR(20) NOT NULL,
- imported_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
- source_file VARCHAR(500) NOT NULL,
- INDEX idx_study (study)
-);
-
--- ── UCO3001 subject summary ───────────────────────────────────────────────────
-CREATE TABLE IF NOT EXISTS iwrs_uco3001_subject_summary (
- id INT AUTO_INCREMENT PRIMARY KEY,
- import_id INT NOT NULL,
- subject VARCHAR(20) NOT NULL,
- prior_subject_identifier VARCHAR(20),
- site VARCHAR(50),
- investigator VARCHAR(100),
- location VARCHAR(50),
- cohort_per_irt VARCHAR(100),
- informed_consent_date DATE,
- adolescent_assent_date DATE,
- age SMALLINT,
- weight DECIMAL(5,1),
- rescreened_subject VARCHAR(10),
- adt_ir VARCHAR(10),
- three_or_more_advanced_therapies VARCHAR(10),
- only_oral_5asa_compounds VARCHAR(10),
- ustekinumab VARCHAR(10),
- isolated_proctitis VARCHAR(10),
- clinical_responder_status_i12_m0 VARCHAR(100),
- irt_subject_status VARCHAR(50),
- i0_rand_date_local DATE,
- last_irt_transaction VARCHAR(100),
- last_irt_transaction_date_local DATE,
- last_irt_transaction_date_utc DATE,
- next_irt_transaction VARCHAR(100),
- next_irt_transaction_date_local DATE,
- most_recent_med_assignment_date DATE,
- days_since_last_med_assignment SMALLINT,
- patient_forecast_status VARCHAR(50),
- patient_forecast_status_changed_date DATE,
- FOREIGN KEY (import_id) REFERENCES iwrs_import(import_id),
- INDEX idx_import (import_id),
- INDEX idx_subject (subject)
-);
-
--- ── MDD3003 subject summary ───────────────────────────────────────────────────
-CREATE TABLE IF NOT EXISTS iwrs_mdd3003_subject_summary (
- id INT AUTO_INCREMENT PRIMARY KEY,
- import_id INT NOT NULL,
- subject VARCHAR(20) NOT NULL,
- prior_subject_identifier VARCHAR(20),
- site VARCHAR(50),
- investigator VARCHAR(100),
- location VARCHAR(50),
- cohort_per_irt VARCHAR(50),
- madrs_criteria_integrated VARCHAR(50),
- informed_consent_date DATE,
- age SMALLINT,
- madrs_criteria_v15 VARCHAR(10),
- madrs_criteria_v16 VARCHAR(10),
- madrs_criteria_v17 VARCHAR(10),
- stratification_country VARCHAR(10),
- age_group VARCHAR(20),
- stable_remitters VARCHAR(50),
- irt_subject_status VARCHAR(100),
- last_irt_transaction VARCHAR(100),
- last_irt_transaction_date_local DATE,
- last_irt_transaction_date_utc DATE,
- next_irt_transaction VARCHAR(100),
- next_irt_transaction_date_local DATE,
- date_screened DATE,
- date_screen_failed DATE,
- date_randomized_part1 DATE,
- date_early_withdraw_randomized_part1 DATE,
- date_open_label_induction DATE,
- date_early_withdraw_open_label_induction DATE,
- date_randomized_part2 DATE,
- date_early_withdraw_randomized_part2 DATE,
- date_completed DATE,
- date_unblinded DATE,
- FOREIGN KEY (import_id) REFERENCES iwrs_import(import_id),
- INDEX idx_import (import_id),
- INDEX idx_subject (subject)
-);
-
--- ── Notifications ────────────────────────────────────────────────────────────
-CREATE TABLE IF NOT EXISTS iwrs_notifications (
- id INT AUTO_INCREMENT PRIMARY KEY,
- study VARCHAR(20) NOT NULL,
- subject VARCHAR(20) NOT NULL,
- pk INT NOT NULL,
- title VARCHAR(100),
- label VARCHAR(500),
- event VARCHAR(50),
- actual_date DATE,
- text TEXT,
- pdf MEDIUMBLOB,
- source_file VARCHAR(500),
- imported_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
- UNIQUE KEY uq_pk (pk),
- INDEX idx_study_subject (study, subject)
-);
-
--- ── Subject visits / transactions (obě studie) ───────────────────────────────
-CREATE TABLE IF NOT EXISTS iwrs_subject_visits (
- id INT AUTO_INCREMENT PRIMARY KEY,
- import_id INT NOT NULL,
- study VARCHAR(20) NOT NULL,
- subject VARCHAR(20) NOT NULL,
- visit_type ENUM('Past','Upcoming') NOT NULL,
- scheduled_date DATE,
- window_days VARCHAR(20),
- actual_date DATE,
- irt_transaction_no SMALLINT,
- irt_transaction_description VARCHAR(200),
- medication_assignment VARCHAR(200),
- quantity_assigned SMALLINT,
- medication_id VARCHAR(20),
- FOREIGN KEY (import_id) REFERENCES iwrs_import(import_id),
- INDEX idx_import (import_id),
- INDEX idx_study_subject (study, subject)
-);
diff --git a/IWRS/Patients/Trash/download_all.py b/IWRS/Patients/Trash/download_all.py
deleted file mode 100644
index 72376af..0000000
--- a/IWRS/Patients/Trash/download_all.py
+++ /dev/null
@@ -1,90 +0,0 @@
-"""
-Stažení reportů z IWRS portálu — vše do jednoho adresáře `Incoming/`.
-
- 1. Subject Summary Report (per studie)
- 2. Subject Detail Reports + notifikace (per subjekt)
-
-Import se spouští samostatně skriptem `import_all.py`.
-"""
-
-import os
-import datetime
-
-from playwright.sync_api import sync_playwright
-
-import download_subject_details as dsd
-
-# ── CONFIG ───────────────────────────────────────────────────────────────────
-BASE_URL = "https://janssen.4gclinical.com"
-EMAIL = "vbuzalka@its.jnj.com"
-PASSWORD = "Vlado123++-+"
-
-STUDIES = ["77242113UCO3001", "42847922MDD3003"]
-
-BASE_DIR = os.path.dirname(os.path.abspath(__file__))
-INCOMING_DIR = os.path.join(BASE_DIR, "Incoming")
-
-
-def unique_path(directory, stem, ext=".xlsx"):
- path = os.path.join(directory, f"{stem}{ext}")
- if not os.path.exists(path):
- return path
- time_tag = datetime.datetime.now().strftime("%H%M")
- return os.path.join(directory, f"{stem} {time_tag}{ext}")
-
-
-def login(page, study):
- page.goto(BASE_URL)
- page.wait_for_load_state("networkidle")
- page.get_by_label("Email *").fill(EMAIL)
- page.get_by_label("Password *").fill(PASSWORD)
- page.locator("#login__submit").click()
- page.wait_for_load_state("networkidle")
- page.get_by_label("Study *").click()
- page.get_by_role("option", name=study).click()
- page.get_by_role("button", name="SELECT").click()
- page.wait_for_load_state("networkidle")
-
-
-def download_summary(page, study, today):
- print(f" [{study}] Stahuji Subject Summary Report...")
- page.goto(f"{BASE_URL}/report/patient_summary_report")
- page.wait_for_load_state("networkidle", timeout=120000)
- filename = unique_path(INCOMING_DIR, f"{today} {study} Subject Summary Report")
- with page.expect_download(timeout=120000) as dl:
- page.get_by_role("button", name="Download XLS").click()
- dl.value.save_as(filename)
- print(f" [{study}] Summary OK -> {os.path.basename(filename)}")
- return filename
-
-
-def main():
- today = datetime.date.today().strftime("%Y-%m-%d")
- os.makedirs(INCOMING_DIR, exist_ok=True)
-
- with sync_playwright() as p:
- for study in STUDIES:
- print("\n" + "=" * 60)
- print(f"[{study}] Stažení reportů")
- print("=" * 60)
- browser = p.chromium.launch(headless=False)
- context = browser.new_context(accept_downloads=True)
- page = context.new_page()
- try:
- login(page, study)
- download_summary(page, study, today)
- # detail XLSX + notifikace přímo do Incoming/
- dsd.run(page, study, out_dir=INCOMING_DIR, subjects_source_dir=INCOMING_DIR)
- except Exception as e:
- print(f" [{study}] CHYBA: {e}")
- finally:
- browser.close()
-
- print("\n" + "=" * 60)
- print(f"Stahování hotovo. Soubory v: {INCOMING_DIR}")
- print("Pro import spusť: python import_all.py")
- print("=" * 60)
-
-
-if __name__ == "__main__":
- main()
diff --git a/IWRS/Patients/Trash/download_subject_notifications.py b/IWRS/Patients/Trash/download_subject_notifications.py
deleted file mode 100644
index 029a3e1..0000000
--- a/IWRS/Patients/Trash/download_subject_notifications.py
+++ /dev/null
@@ -1,201 +0,0 @@
-from playwright.sync_api import sync_playwright
-import os
-import glob
-import datetime
-import requests
-
-import pandas as pd
-
-# ── CONFIG ──────────────────────────────────────────────────────────────────
-BASE_URL = "https://janssen.4gclinical.com"
-EMAIL = "vbuzalka@its.jnj.com"
-PASSWORD = "Vlado123++-+"
-
-STUDIES = ["77242113UCO3001", "42847922MDD3003"]
-
-BASE_DIR = os.path.dirname(os.path.abspath(__file__))
-INCOMING_DIR = os.path.join(BASE_DIR, "IncomingSourceReports")
-DETAILS_DIR = os.path.join(BASE_DIR, "IncomingSourceReportsDetails")
-# ────────────────────────────────────────────────────────────────────────────
-
-
-def get_subjects(study):
- pattern = os.path.join(INCOMING_DIR, f"* {study} Subject Summary Report.xlsx")
- files = sorted(
- [f for f in glob.glob(pattern) if not os.path.basename(f).startswith("~$")],
- key=os.path.getmtime,
- reverse=True,
- )
- if not files:
- raise FileNotFoundError(f"Nenalezen Subject Summary Report pro {study}")
- today = datetime.date.today().strftime("%Y-%m-%d")
- if not os.path.basename(files[0]).startswith(today):
- raise FileNotFoundError(
- f"Dnešní Subject Summary Report pro {study} neexistuje — spusť nejdříve download_subject_summary.py"
- )
- path = files[0]
- print(f" Čtu subjekty z: {os.path.basename(path)}")
-
- raw = pd.read_excel(path, header=None)
- header_row = None
- for i, row in raw.iterrows():
- if "Subject" in [str(v).strip() for v in row]:
- header_row = i
- break
- if header_row is None:
- raise ValueError("Hlavičkový řádek nenalezen")
-
- df = pd.read_excel(path, header=header_row)
- subjects = df["Subject"].dropna().astype(str).str.strip().tolist()
- return subjects
-
-
-def get_jwt_and_api_base(page, study):
- """Získá JWT token a api_base_url pro danou studii."""
- jwt = page.evaluate("localStorage.getItem('JWT.access')")
- if not jwt:
- raise ValueError("JWT token nenalezen v localStorage")
-
- instances = page.evaluate("""async (jwt) => {
- const res = await fetch('/_/api/dispatch/app_instances/', {
- headers: { 'Authorization': `Bearer ${jwt}` }
- });
- return res.json();
- }""", jwt)
-
- instance = next(
- (i for i in instances if study in i.get("label", "")),
- None
- )
- if not instance:
- raise ValueError(f"app_instance pro studii {study} nenalezena")
-
- return jwt, instance["api_base_url"]
-
-
-def get_notifications(jwt, api_base, study, subject):
- """Načte seznam notifikací pro daného subjekta přes report_data API."""
- url = f"{BASE_URL}{api_base}/api/v1/reports_api/report_data"
- params = {
- "path": "patient_detail_report",
- "id": subject,
- "key": "table_1",
- "unblinded": "false",
- }
- payload = {
- "path": "patient_detail_report",
- "study": study,
- "id": subject,
- "key": "table_1",
- "fields": {},
- "filters": [{"tableId": "table_1", "tableFilters": {}}],
- "pagination_details": {"order": "type", "reverseOrder": False, "page": 1, "limit": 500},
- "cache_key": f"py_{subject}_{datetime.datetime.now().timestamp()}",
- }
- headers = {
- "Authorization": f"Bearer {jwt}",
- "Content-Type": "application/json",
- "lang": "en",
- }
- resp = requests.post(url, params=params, json=payload, headers=headers)
- resp.raise_for_status()
- data = resp.json()
-
- notifications = []
- for row in data.get("data", []):
- for notif in row.get("notification", []):
- item = notif.get("item", {})
- pk = item.get("pk")
- title = item.get("et_title")
- if pk and title:
- notifications.append({"pk": pk, "title": title, "event": row.get("event_event_id", "")})
- return notifications
-
-
-def download_pdf(jwt, api_base, pk, title, out_path):
- """Stáhne PDF notifikaci a uloží ji."""
- url = f"{BASE_URL}{api_base}/api/v1/meta_api/pdfnotification"
- params = {"pk": pk, "title": title, "html": "true"}
- headers = {
- "Authorization": f"Bearer {jwt}",
- "lang": "en",
- "Accept": "*/*",
- }
- resp = requests.get(url, params=params, headers=headers)
- resp.raise_for_status()
- with open(out_path, "wb") as f:
- f.write(resp.content)
-
-
-def run(page, study):
- out_dir = os.path.join(DETAILS_DIR, study)
- os.makedirs(out_dir, exist_ok=True)
-
- subjects = get_subjects(study)
- print(f" Nalezeno {len(subjects)} subjektů")
- today = datetime.date.today().strftime("%Y-%m-%d")
-
- # Načteme stránku aby byl platný session kontext
- page.goto(f"{BASE_URL}/report/patient_detail_report")
- page.wait_for_load_state("networkidle", timeout=120000)
-
- jwt, api_base = get_jwt_and_api_base(page, study)
- print(f" API base: {api_base}")
-
- for subject in subjects:
- print(f" [{subject}] Stahuji notifikace...")
- try:
- notifications = get_notifications(jwt, api_base, study, subject)
- if not notifications:
- print(f" [{subject}] Žádné notifikace")
- continue
-
- for notif in notifications:
- pk = notif["pk"]
- title = notif["title"]
- filename = os.path.join(out_dir, f"{today} {study} {subject} Notification {title} pk{pk}.pdf")
- if os.path.exists(filename):
- print(f" [{subject}] {title} (pk={pk}) — již existuje, přeskakuji")
- continue
- download_pdf(jwt, api_base, pk, title, filename)
- print(f" [{subject}] {title} (pk={pk}) OK")
-
- except Exception as e:
- print(f" [{subject}] CHYBA při notifikacích: {e}")
-
- print(f" [{study}] Notifikace hotovo.")
-
-
-def main():
- os.makedirs(DETAILS_DIR, exist_ok=True)
-
- with sync_playwright() as p:
- for study in STUDIES:
- print(f"\n[{study}] Přihlášení...")
- browser = p.chromium.launch(headless=False)
- context = browser.new_context(accept_downloads=True)
- page = context.new_page()
-
- page.goto(BASE_URL)
- page.wait_for_load_state("networkidle")
- page.get_by_label("Email *").fill(EMAIL)
- page.get_by_label("Password *").fill(PASSWORD)
- page.locator("#login__submit").click()
- page.wait_for_load_state("networkidle")
-
- page.get_by_label("Study *").click()
- page.get_by_role("option", name=study).click()
- page.get_by_role("button", name="SELECT").click()
- page.wait_for_load_state("networkidle")
-
- try:
- run(page, study)
- except Exception as e:
- print(f" [{study}] CHYBA: {e}")
-
- browser.close()
-
- print("\nVše hotovo.")
-
-
-main()
diff --git a/IWRS/Patients/Trash/download_subject_summary.py b/IWRS/Patients/Trash/download_subject_summary.py
deleted file mode 100644
index 517f2bc..0000000
--- a/IWRS/Patients/Trash/download_subject_summary.py
+++ /dev/null
@@ -1,76 +0,0 @@
-from playwright.sync_api import sync_playwright
-import os
-import datetime
-
-# ── CONFIG ──────────────────────────────────────────────────────────────────
-BASE_URL = "https://janssen.4gclinical.com"
-EMAIL = "vbuzalka@its.jnj.com"
-PASSWORD = "Vlado123++-+"
-
-STUDIES = ["77242113UCO3001", "42847922MDD3003"]
-
-BASE_DIR = os.path.dirname(os.path.abspath(__file__))
-INCOMING_DIR = os.path.join(BASE_DIR, "IncomingSourceReports")
-CREATED_DIR = os.path.join(BASE_DIR, "CreatedReports")
-# ────────────────────────────────────────────────────────────────────────────
-
-
-def unique_path(directory, stem):
- path = os.path.join(directory, f"{stem}.xlsx")
- if not os.path.exists(path):
- return path
- time_tag = datetime.datetime.now().strftime("%H%M")
- return os.path.join(directory, f"{stem} {time_tag}.xlsx")
-
-
-def download_study(page, study, today):
- print(f"\n[{study}] Prihlaseni...")
- page.goto(BASE_URL)
- page.wait_for_load_state("networkidle")
- page.get_by_label("Email *").fill(EMAIL)
- page.get_by_label("Password *").fill(PASSWORD)
- page.locator("#login__submit").click()
- page.wait_for_load_state("networkidle")
-
- print(f"[{study}] Vyber studie...")
- page.get_by_label("Study *").click()
- page.get_by_role("option", name=study).click()
- page.get_by_role("button", name="SELECT").click()
- page.wait_for_load_state("networkidle")
-
- print(f"[{study}] Stahuji Subject Summary Report...")
- page.goto(f"{BASE_URL}/report/patient_summary_report")
- page.wait_for_load_state("networkidle", timeout=120000)
-
- filename = unique_path(INCOMING_DIR, f"{today} {study} Subject Summary Report")
- with page.expect_download(timeout=120000) as dl:
- page.get_by_role("button", name="Download XLS").click()
- dl.value.save_as(filename)
- print(f"[{study}] OK -> {filename}")
- return filename
-
-
-def main():
- today = datetime.date.today().strftime("%Y-%m-%d")
- os.makedirs(INCOMING_DIR, exist_ok=True)
- os.makedirs(CREATED_DIR, exist_ok=True)
-
- downloaded = []
-
- with sync_playwright() as p:
- for study in STUDIES:
- browser = p.chromium.launch(headless=False)
- context = browser.new_context(accept_downloads=True)
- page = context.new_page()
-
- filename = download_study(page, study, today)
- downloaded.append((study, filename))
-
- browser.close()
-
- print("\nVse stazeno:")
- for study, path in downloaded:
- print(f" {study}: {path}")
-
-
-main()
diff --git a/IWRS/Patients/Trash/import_all.py b/IWRS/Patients/Trash/import_all.py
deleted file mode 100644
index fd9ed8f..0000000
--- a/IWRS/Patients/Trash/import_all.py
+++ /dev/null
@@ -1,107 +0,0 @@
-"""
-Import všech čekajících reportů z `Incoming/` do MongoDB.
-
-Pořadí zpracování per typ + studie: nejstarší soubor podle mtime první
-(důležité pro chronologickou správnost snapshotů).
-
-Po úspěšném importu se soubor přesune do `Incoming/Zpracováno/`.
-Při chybě zůstane soubor v `Incoming/`.
-"""
-
-import os
-import sys
-import glob
-import shutil
-
-sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
-from common.mongo_writer import ensure_indexes
-
-import import_to_mongo
-import import_notifications_to_mongo
-
-BASE_DIR = os.path.dirname(os.path.abspath(__file__))
-INCOMING_DIR = os.path.join(BASE_DIR, "Incoming")
-DONE_DIR = os.path.join(INCOMING_DIR, "Zpracováno")
-
-STUDIES = ["77242113UCO3001", "42847922MDD3003"]
-
-
-def _move_done(path):
- os.makedirs(DONE_DIR, exist_ok=True)
- dst = os.path.join(DONE_DIR, os.path.basename(path))
- # kolize → přepiš (Mongo už má aktuální data, soubor je jen archiv)
- if os.path.exists(dst):
- os.remove(dst)
- shutil.move(path, dst)
-
-
-def _sorted_by_mtime(paths):
- """Nejstarší první."""
- return sorted(
- (p for p in paths if not os.path.basename(p).startswith("~$")),
- key=os.path.getmtime,
- )
-
-
-def import_summaries(study):
- pattern = os.path.join(INCOMING_DIR, f"* {study} Subject Summary Report*.xlsx")
- files = _sorted_by_mtime(glob.glob(pattern))
- if not files:
- print(f" [{study}] summary: nic ke zpracování")
- return
- print(f" [{study}] summary: {len(files)} soubor(ů) (oldest first)")
- for path in files:
- try:
- import_to_mongo.import_subject_summary(study, path)
- _move_done(path)
- except Exception as e:
- print(f" [{study}] CHYBA summary {os.path.basename(path)}: {e}")
-
-
-def import_details(study):
- pattern = os.path.join(INCOMING_DIR, f"* {study} * Subject Detail.xlsx")
- files = _sorted_by_mtime(glob.glob(pattern))
- if not files:
- print(f" [{study}] detail: nic ke zpracování")
- return
- print(f" [{study}] detail: {len(files)} soubor(ů) (oldest first)")
- for path in files:
- parsed = import_to_mongo.parse_detail_filename(path)
- if not parsed:
- print(f" [{study}] PŘESKAKUJI (nelze parsovat název): {os.path.basename(path)}")
- continue
- _, parsed_study, subject = parsed
- if parsed_study != study:
- continue # patří jiné studii
- try:
- import_to_mongo.import_visits_single_file(study, subject, path)
- _move_done(path)
- except Exception as e:
- print(f" [{study}] CHYBA detail {os.path.basename(path)}: {e}")
-
-
-def main():
- if not os.path.isdir(INCOMING_DIR):
- print(f"Adresář neexistuje: {INCOMING_DIR}")
- return
- ensure_indexes()
-
- print("=" * 60)
- print("Import Subject Summary + Visits")
- print("=" * 60)
- for study in STUDIES:
- import_summaries(study)
- import_details(study)
-
- print("\n" + "=" * 60)
- print("Import notifikací")
- print("=" * 60)
- import_notifications_to_mongo.import_from_dir(INCOMING_DIR, DONE_DIR, STUDIES)
-
- print("\n" + "=" * 60)
- print(f"Hotovo. Zpracované soubory: {DONE_DIR}")
- print("=" * 60)
-
-
-if __name__ == "__main__":
- main()
diff --git a/IWRS/Patients/Trash/import_to_mysql.py b/IWRS/Patients/Trash/import_to_mysql.py
deleted file mode 100644
index d8ffe6b..0000000
--- a/IWRS/Patients/Trash/import_to_mysql.py
+++ /dev/null
@@ -1,453 +0,0 @@
-"""
-Importuje data z IWRS Excel reportů do MySQL (databáze studie).
-
-Pořadí spuštění:
- 1. download_subject_summary.py
- 2. download_subject_details.py
- 3. tento skript
-
-Každé spuštění vytvoří nový import_id v iwrs_import.
-Reportovací skripty pracují vždy s MAX(import_id) pro danou studii.
-"""
-
-import os
-import glob
-import datetime
-import re
-
-import numpy as np
-import pandas as pd
-import mysql.connector
-
-import db_config
-
-BASE_DIR = os.path.dirname(os.path.abspath(__file__))
-INCOMING_DIR = os.path.join(BASE_DIR, "IncomingSourceReports")
-DETAILS_DIR = os.path.join(BASE_DIR, "IncomingSourceReportsDetails")
-
-STUDIES = ["77242113UCO3001", "42847922MDD3003"]
-
-
-# ── helpers ──────────────────────────────────────────────────────────────────
-
-def get_conn():
- return mysql.connector.connect(
- host=db_config.DB_HOST,
- port=db_config.DB_PORT,
- user=db_config.DB_USER,
- password=db_config.DB_PASSWORD,
- database=db_config.DB_NAME,
- )
-
-
-def _py(val):
- """Převede numpy skalár na Python nativní typ."""
- if isinstance(val, np.generic):
- return val.item()
- return val
-
-
-def to_date(val):
- """Převede pandas Timestamp / string / NaT / NaN na date nebo None."""
- val = _py(val)
- if val is None or (isinstance(val, float) and (val != val)): # NaN check
- return None
- try:
- if pd.isna(val):
- return None
- except (TypeError, ValueError):
- pass
- if isinstance(val, pd.Timestamp):
- return None if pd.isna(val) else val.date()
- if isinstance(val, datetime.datetime):
- return val.date()
- if isinstance(val, datetime.date):
- return val
- s = str(val).strip()
- if not s or s.lower() in ("nat", "nan", "none", ""):
- return None
- for fmt in ("%Y-%m-%d", "%d-%b-%Y", "%d-%m-%Y", "%Y-%m-%d %H:%M:%S"):
- try:
- return datetime.datetime.strptime(s, fmt).date()
- except ValueError:
- pass
- return None
-
-
-def to_int(val):
- val = _py(val)
- try:
- v = float(val)
- return None if (v != v) else int(v) # v != v je True jen pro NaN
- except (TypeError, ValueError):
- return None
-
-
-def to_float(val):
- val = _py(val)
- try:
- v = float(val)
- return None if (v != v) else float(v)
- except (TypeError, ValueError):
- return None
-
-
-def to_str(val):
- val = _py(val)
- if val is None:
- return None
- if isinstance(val, float) and (val != val): # NaN
- return None
- s = str(val).strip()
- return None if s.lower() in ("nan", "nat", "none", "") else s
-
-
-def find_summary_file(study):
- today = datetime.date.today().strftime("%Y-%m-%d")
- pattern = os.path.join(INCOMING_DIR, f"* {study} Subject Summary Report.xlsx")
- files = sorted(
- [f for f in glob.glob(pattern) if not os.path.basename(f).startswith("~$")],
- key=os.path.getmtime,
- reverse=True,
- )
- if not files:
- raise FileNotFoundError(f"Nenalezen Subject Summary Report pro {study}")
- if not os.path.basename(files[0]).startswith(today):
- print(f" UPOZORNĚNÍ: nejnovější Summary Report pro {study} není z dnešního dne ({os.path.basename(files[0])[:10]})")
- return files[0]
-
-
-def read_summary_df(path):
- """Přečte Summary xlsx, vrátí DataFrame od řádku s hlavičkou."""
- raw = pd.read_excel(path, header=None)
- header_row = None
- for i, row in raw.iterrows():
- if "Subject" in [str(v).strip() for v in row]:
- header_row = i
- break
- if header_row is None:
- raise ValueError(f"Hlavičkový řádek nenalezen v {path}")
- return pd.read_excel(path, header=header_row)
-
-
-def find_detail_files(study):
- out_dir = os.path.join(DETAILS_DIR, study)
- # Vezme soubory ze stejného dne jako nejnovější Summary Report
- summary_path = find_summary_file(study)
- file_date = os.path.basename(summary_path)[:10] # "YYYY-MM-DD"
- pattern = os.path.join(out_dir, f"{file_date} {study} * Subject Detail.xlsx")
- files = [f for f in glob.glob(pattern) if not os.path.basename(f).startswith("~$")]
- return sorted(files)
-
-
-def parse_detail_visits(path):
- """
- Vrátí list slovníků s daty visitů z Detail xlsx.
- Každý řádek tabulky (od řádku s hlavičkou Visit Type) je jedna transakce.
- """
- df = pd.read_excel(path, sheet_name="patient_detail_report", header=None)
-
- header_row = None
- for i, row in df.iterrows():
- if "Visit Type" in [str(v).strip() for v in row]:
- header_row = i
- break
- if header_row is None:
- return []
-
- visits_df = df.iloc[header_row + 1:].copy()
- visits_df.columns = range(visits_df.shape[1])
-
- rows = []
- for _, r in visits_df.iterrows():
- visit_type = to_str(r.get(0))
- if visit_type not in ("Past", "Upcoming"):
- continue
- rows.append({
- "visit_type": visit_type,
- "scheduled_date": to_date(r.get(1)),
- "window_days": to_str(r.get(2)),
- "actual_date": to_date(r.get(3)),
- "irt_transaction_no": to_int(r.get(4)),
- "irt_transaction_description": to_str(r.get(5)),
- "medication_assignment": to_str(r.get(6)),
- "quantity_assigned": to_int(r.get(7)),
- "medication_id": to_str(r.get(8)),
- })
- return rows
-
-
-# ── insert helpers ────────────────────────────────────────────────────────────
-
-def insert_import(cursor, study, source_file):
- cursor.execute(
- "INSERT INTO iwrs_import (study, imported_at, source_file) VALUES (%s, %s, %s)",
- (study, datetime.datetime.now(), os.path.basename(source_file)),
- )
- return cursor.lastrowid
-
-
-def insert_uco3001_summary(cursor, import_id, df):
- sql = """
- INSERT INTO iwrs_uco3001_subject_summary (
- import_id, subject, prior_subject_identifier, site, investigator, location,
- cohort_per_irt, informed_consent_date, adolescent_assent_date, age, weight,
- rescreened_subject, adt_ir, three_or_more_advanced_therapies,
- only_oral_5asa_compounds, ustekinumab, isolated_proctitis,
- clinical_responder_status_i12_m0, irt_subject_status,
- i0_rand_date_local, last_irt_transaction,
- last_irt_transaction_date_local, last_irt_transaction_date_utc,
- next_irt_transaction, next_irt_transaction_date_local,
- most_recent_med_assignment_date, days_since_last_med_assignment,
- patient_forecast_status, patient_forecast_status_changed_date
- ) VALUES (
- %s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s
- )
- """
- col = df.columns.tolist()
-
- def c(name):
- return col.index(name) if name in col else None
-
- for _, r in df.iterrows():
- cursor.execute(sql, (
- import_id,
- to_str(r["Subject"]),
- to_str(r["Prior Subject Identifier"]) if "Prior Subject Identifier" in col else None,
- to_str(r["Site"]),
- to_str(r["Investigator"]),
- to_str(r["Location"]),
- to_str(r["Cohort per IRT"]),
- to_date(r["Informed Consent Date"]),
- to_date(r["Adolescent Assent Date"]) if "Adolescent Assent Date" in col else None,
- to_int(r["Subject's age collection"]),
- to_float(r["Subject's weight collection"]) if "Subject's weight collection" in col else None,
- to_str(r["Rescreened Subject"]) if "Rescreened Subject" in col else None,
- to_str(r["ADT-IR"]) if "ADT-IR" in col else None,
- to_str(r["3 or More Advanced Therapies"]) if "3 or More Advanced Therapies" in col else None,
- to_str(r["Only Oral 5-ASA Compounds"]) if "Only Oral 5-ASA Compounds" in col else None,
- to_str(r["Ustekinumab"]) if "Ustekinumab" in col else None,
- to_str(r["Isolated Proctitis"]) if "Isolated Proctitis" in col else None,
- to_str(r["Clinical Responder Status at I-12 / M-0"]) if "Clinical Responder Status at I-12 / M-0" in col else None,
- to_str(r["IRT Subject Status"]),
- to_date(r["I0_RAND_TIMESTAMP_LOCAL [Local]"]) if "I0_RAND_TIMESTAMP_LOCAL [Local]" in col else None,
- to_str(r["Last Recorded IRT Transaction"]),
- to_date(r["Last Recorded IRT Transaction Date [Local]"]),
- to_date(r["Last Recorded IRT Transaction Date (UTC)"]),
- to_str(r["Next Expected IRT Transaction"]),
- to_date(r["Next Expected IRT Transaction Date [Local]"]),
- to_date(r["Most Recent Medication Assignment Transaction [Local]"]) if "Most Recent Medication Assignment Transaction [Local]" in col else None,
- to_int(r["Days Since Last Medication Assignment Transaction"]) if "Days Since Last Medication Assignment Transaction" in col else None,
- to_str(r["Patient Forecast Status"]) if "Patient Forecast Status" in col else None,
- to_date(r["Patient Forecast Status Changed Date (UTC)"]) if "Patient Forecast Status Changed Date (UTC)" in col else None,
- ))
-
-
-def insert_mdd3003_summary(cursor, import_id, df):
- sql = """
- INSERT INTO iwrs_mdd3003_subject_summary (
- import_id, subject, prior_subject_identifier, site, investigator, location,
- cohort_per_irt, madrs_criteria_integrated, informed_consent_date, age,
- madrs_criteria_v15, madrs_criteria_v16, madrs_criteria_v17,
- stratification_country, age_group, stable_remitters, irt_subject_status,
- last_irt_transaction, last_irt_transaction_date_local,
- last_irt_transaction_date_utc, next_irt_transaction,
- next_irt_transaction_date_local, date_screened, date_screen_failed,
- date_randomized_part1, date_early_withdraw_randomized_part1,
- date_open_label_induction, date_early_withdraw_open_label_induction,
- date_randomized_part2, date_early_withdraw_randomized_part2,
- date_completed, date_unblinded
- ) VALUES (
- %s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s
- )
- """
- col = df.columns.tolist()
-
- for _, r in df.iterrows():
- cursor.execute(sql, (
- import_id,
- to_str(r["Subject"]),
- to_str(r["Prior Subject Identifier"]) if "Prior Subject Identifier" in col else None,
- to_str(r["Site"]),
- to_str(r["Investigator"]),
- to_str(r["Location"]),
- to_str(r["Cohort per IRT"]),
- to_str(r["MADRS response criteria integrated or manually entered"]) if "MADRS response criteria integrated or manually entered" in col else None,
- to_date(r["Informed Consent Date"]),
- to_int(r["Subject's age collection"]),
- to_str(r["MADRS response criteria v1.5 from RAVE"]) if "MADRS response criteria v1.5 from RAVE" in col else None,
- to_str(r["MADRS response criteria v1.6 from RAVE"]) if "MADRS response criteria v1.6 from RAVE" in col else None,
- to_str(r["MADRS response criteria v1.7 from RAVE"]) if "MADRS response criteria v1.7 from RAVE" in col else None,
- to_str(r["Stratification Country"]) if "Stratification Country" in col else None,
- to_str(r["Age Group"]) if "Age Group" in col else None,
- to_str(r["Stable Remitters vs. Non Stable Remitters"]) if "Stable Remitters vs. Non Stable Remitters" in col else None,
- to_str(r["IRT Subject Status"]),
- to_str(r["Last Recorded IRT Transaction"]),
- to_date(r["Last Recorded IRT Transaction Date [Local]"]),
- to_date(r["Last Recorded IRT Transaction Date (UTC)"]),
- to_str(r["Next Expected IRT Transaction"]),
- to_date(r["Next Expected IRT Transaction Date [Local]"]),
- to_date(r["Date Screened [Local]"]) if "Date Screened [Local]" in col else None,
- to_date(r["Date Screen Failed [Local]"]) if "Date Screen Failed [Local]" in col else None,
- to_date(r["Date Randomized Part 1 [Local]"]) if "Date Randomized Part 1 [Local]" in col else None,
- to_date(r["Date Early Withdraw Randomized Part 1 [Local]"]) if "Date Early Withdraw Randomized Part 1 [Local]" in col else None,
- to_date(r["Date Open Label Induction [Local]"]) if "Date Open Label Induction [Local]" in col else None,
- to_date(r["Date Early Withdraw Open Label Induction [Local]"]) if "Date Early Withdraw Open Label Induction [Local]" in col else None,
- to_date(r["Date Randomized Part 2 [Local]"]) if "Date Randomized Part 2 [Local]" in col else None,
- to_date(r["Date Early Withdraw Randomized Part 2 [Local]"]) if "Date Early Withdraw Randomized Part 2 [Local]" in col else None,
- to_date(r["Date Completed [Local]"]) if "Date Completed [Local]" in col else None,
- to_date(r["Date Unblinded [Local]"]) if "Date Unblinded [Local]" in col else None,
- ))
-
-
-def insert_visits(cursor, import_id, study, subject, visits):
- if not visits:
- return
- sql = """
- INSERT INTO iwrs_subject_visits (
- import_id, study, subject, visit_type, scheduled_date, window_days,
- actual_date, irt_transaction_no, irt_transaction_description,
- medication_assignment, quantity_assigned, medication_id
- ) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)
- """
- for v in visits:
- cursor.execute(sql, (
- import_id, study, subject,
- v["visit_type"], v["scheduled_date"], v["window_days"],
- v["actual_date"], v["irt_transaction_no"],
- v["irt_transaction_description"], v["medication_assignment"],
- v["quantity_assigned"], v["medication_id"],
- ))
-
-
-# ── notifications ─────────────────────────────────────────────────────────────
-
-def find_notification_json_files(study):
- """Najde všechny .json soubory notifikací pro danou studii."""
- out_dir = os.path.join(DETAILS_DIR, study)
- return sorted(glob.glob(os.path.join(out_dir, "*.json")))
-
-
-def import_notifications(conn, study):
- import json as json_lib
- json_files = find_notification_json_files(study)
- if not json_files:
- print(f" Žádné notifikace k importu pro {study}")
- return 0
-
- sql = """
- INSERT INTO iwrs_notifications
- (study, subject, pk, title, label, event, actual_date, text, pdf, source_file)
- VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
- ON DUPLICATE KEY UPDATE
- label = VALUES(label),
- text = VALUES(text),
- pdf = VALUES(pdf),
- source_file = VALUES(source_file)
- """
-
- done_dir = os.path.join(os.path.join(DETAILS_DIR, study), "Zpracováno")
- os.makedirs(done_dir, exist_ok=True)
-
- cursor = conn.cursor()
- count = 0
- for json_path in json_files:
- try:
- with open(json_path, "r", encoding="utf-8") as f:
- meta = json_lib.load(f)
-
- pdf_path = json_path.replace(".json", ".pdf")
- pdf_data = None
- if os.path.exists(pdf_path):
- with open(pdf_path, "rb") as f:
- pdf_data = f.read()
-
- cursor.execute(sql, (
- meta.get("study", study),
- meta.get("subject"),
- meta.get("pk"),
- meta.get("title"),
- meta.get("label"),
- meta.get("event"),
- to_date(meta.get("actual_date")),
- meta.get("text"),
- pdf_data,
- os.path.basename(json_path),
- ))
- count += 1
-
- # Přesun do Zpracováno
- import shutil
- shutil.move(json_path, os.path.join(done_dir, os.path.basename(json_path)))
- if os.path.exists(pdf_path):
- shutil.move(pdf_path, os.path.join(done_dir, os.path.basename(pdf_path)))
-
- except Exception as e:
- print(f" CHYBA při importu {os.path.basename(json_path)}: {e}")
-
- conn.commit()
- cursor.close()
- print(f" Notifikací uloženo/přesunuto: {count}")
- return count
-
-
-# ── main ──────────────────────────────────────────────────────────────────────
-
-def import_study(conn, study):
- summary_path = find_summary_file(study)
- print(f" Summary: {os.path.basename(summary_path)}")
-
- df_summary = read_summary_df(summary_path)
- df_summary = df_summary.dropna(how="all")
-
- detail_files = find_detail_files(study)
- print(f" Detail souborů: {len(detail_files)}")
-
- cursor = conn.cursor()
- import_id = insert_import(cursor, study, summary_path)
- print(f" import_id = {import_id}")
-
- if study == "77242113UCO3001":
- insert_uco3001_summary(cursor, import_id, df_summary)
- else:
- insert_mdd3003_summary(cursor, import_id, df_summary)
- print(f" Summary řádků: {len(df_summary)}")
-
- visited = 0
- for path in detail_files:
- fname = os.path.basename(path)
- # název: "2026-05-04 77242113UCO3001 CZ100012001 Subject Detail.xlsx"
- m = re.search(r"\d{4}-\d{2}-\d{2} \S+ (\S+) Subject Detail\.xlsx", fname)
- subject = m.group(1) if m else "UNKNOWN"
- visits = parse_detail_visits(path)
- insert_visits(cursor, import_id, study, subject, visits)
- visited += len(visits)
-
- conn.commit()
- cursor.close()
- print(f" Transakce uloženo: {visited}")
- return import_id
-
-
-def main():
- conn = get_conn()
- print("Připojeno k MySQL.\n")
-
- for study in STUDIES:
- print(f"[{study}]")
- try:
- import_id = import_study(conn, study)
- print(f" OK — import_id {import_id}")
- except Exception as e:
- print(f" CHYBA: {e}")
- try:
- import_notifications(conn, study)
- except Exception as e:
- print(f" CHYBA notifikace: {e}")
- print()
-
- conn.close()
- print("Hotovo.")
-
-
-main()
diff --git a/IWRS/Patients/Trash/run_all.py b/IWRS/Patients/Trash/run_all.py
deleted file mode 100644
index 8ea266a..0000000
--- a/IWRS/Patients/Trash/run_all.py
+++ /dev/null
@@ -1,175 +0,0 @@
-"""
-Kompletní pipeline:
- 1. Stažení Subject Summary Reportů (obě studie)
- 2. Stažení Subject Detail Reportů + notifikací (obě studie)
- 3. Import do MongoDB (subject_summary + visits + notifications)
-
-Spusť tento skript místo samostatných skriptů.
-"""
-
-import os
-import sys
-import datetime
-import glob
-
-from playwright.sync_api import sync_playwright
-
-import download_subject_details as dsd
-import import_to_mongo
-import import_notifications_to_mongo
-
-# ── CONFIG ───────────────────────────────────────────────────────────────────
-BASE_URL = "https://janssen.4gclinical.com"
-EMAIL = "vbuzalka@its.jnj.com"
-PASSWORD = "Vlado123++-+"
-
-STUDIES = ["77242113UCO3001", "42847922MDD3003"]
-
-BASE_DIR = os.path.dirname(os.path.abspath(__file__))
-INCOMING_DIR = os.path.join(BASE_DIR, "IncomingSourceReports")
-DETAILS_DIR = os.path.join(BASE_DIR, "IncomingSourceReportsDetails")
-
-
-# ── helpers ───────────────────────────────────────────────────────────────────
-
-def unique_path(directory, stem):
- path = os.path.join(directory, f"{stem}.xlsx")
- if not os.path.exists(path):
- return path
- time_tag = datetime.datetime.now().strftime("%H%M")
- return os.path.join(directory, f"{stem} {time_tag}.xlsx")
-
-
-def login(page, study):
- page.goto(BASE_URL)
- page.wait_for_load_state("networkidle")
- page.get_by_label("Email *").fill(EMAIL)
- page.get_by_label("Password *").fill(PASSWORD)
- page.locator("#login__submit").click()
- page.wait_for_load_state("networkidle")
- page.get_by_label("Study *").click()
- page.get_by_role("option", name=study).click()
- page.get_by_role("button", name="SELECT").click()
- page.wait_for_load_state("networkidle")
-
-
-# ── KROK 1: Subject Summary ───────────────────────────────────────────────────
-
-def download_summary(page, study, today):
- print(f" [{study}] Stahuji Subject Summary Report...")
- page.goto(f"{BASE_URL}/report/patient_summary_report")
- page.wait_for_load_state("networkidle", timeout=120000)
- filename = unique_path(INCOMING_DIR, f"{today} {study} Subject Summary Report")
- with page.expect_download(timeout=120000) as dl:
- page.get_by_role("button", name="Download XLS").click()
- dl.value.save_as(filename)
- print(f" [{study}] Summary OK -> {os.path.basename(filename)}")
- return filename
-
-
-# ── KROK 2: Subject Details ───────────────────────────────────────────────────
-
-def get_subjects_from_summary(summary_path):
- import pandas as pd
- raw = pd.read_excel(summary_path, header=None)
- header_row = None
- for i, row in raw.iterrows():
- if "Subject" in [str(v).strip() for v in row]:
- header_row = i
- break
- if header_row is None:
- raise ValueError("Hlavičkový řádek nenalezen")
- df = pd.read_excel(summary_path, header=header_row)
- return df["Subject"].dropna().astype(str).str.strip().tolist()
-
-
-def download_details(page, study, summary_path, today):
- out_dir = os.path.join(DETAILS_DIR, study)
- os.makedirs(out_dir, exist_ok=True)
-
- subjects = get_subjects_from_summary(summary_path)
- print(f" [{study}] Subjektů k stažení: {len(subjects)}")
-
- page.goto(f"{BASE_URL}/report/patient_detail_report")
- page.wait_for_load_state("networkidle", timeout=120000)
-
- for subject in subjects:
- filename = os.path.join(out_dir, f"{today} {study} {subject} Subject Detail.xlsx")
- input_field = page.locator('input[placeholder="search"], input[type="text"]').first
- input_field.click()
- input_field.fill(subject)
- page.wait_for_timeout(500)
- page.locator("mat-option").first.dispatch_event("click")
- page.wait_for_load_state("networkidle", timeout=120000)
-
- with page.expect_download(timeout=120000) as dl:
- page.get_by_role("button", name="Download XLS").click()
- dl.value.save_as(filename)
- print(f" [{study}] Detail {subject} OK")
-
- page.get_by_role("button", name="Clear").click()
- page.wait_for_load_state("networkidle", timeout=120000)
-
-
-# ── KROK 3: Import do MongoDB ────────────────────────────────────────────────
-
-def main():
- today = datetime.date.today().strftime("%Y-%m-%d")
- os.makedirs(INCOMING_DIR, exist_ok=True)
- os.makedirs(DETAILS_DIR, exist_ok=True)
-
- summary_paths = {}
-
- # Krok 1 + 2: stahování (Playwright, každá studie zvlášť kvůli session)
- with sync_playwright() as p:
- for study in STUDIES:
- print("\n" + "=" * 60)
- print(f"[{study}] KROK 1: Subject Summary Report")
- print("=" * 60)
- browser = p.chromium.launch(headless=False)
- context = browser.new_context(accept_downloads=True)
- page = context.new_page()
-
- try:
- login(page, study)
- summary_path = download_summary(page, study, today)
- summary_paths[study] = summary_path
-
- print(f"\n[{study}] KROK 2: Subject Detail Reports + notifikace")
- dsd.run(page, study)
-
- except Exception as e:
- print(f" [{study}] CHYBA při stahování: {e}")
- summary_paths[study] = None
- finally:
- browser.close()
-
- # Krok 3: import do MongoDB
- print("\n" + "=" * 60)
- print("KROK 3: Import do MongoDB")
- print("=" * 60)
-
- for study in STUDIES:
- summary_path = summary_paths.get(study)
- if not summary_path:
- print(f" [{study}] PŘESKOČENO — stahování selhalo")
- continue
-
- try:
- import_to_mongo.run(study, summary_path, DETAILS_DIR, today)
- except Exception as e:
- print(f" [{study}] CHYBA při importu summary/visits: {e}")
-
- # Notifikace: PDF/JSON z disku rovnou do Mongo iwrs_notifications
- print("\n [notifikace] import PDF/JSON do Mongo...")
- try:
- import_notifications_to_mongo.main(STUDIES)
- except Exception as e:
- print(f" CHYBA při importu notifikací: {e}")
-
- print("\n" + "=" * 60)
- print("Vše hotovo.")
- print("=" * 60)
-
-
-main()
diff --git a/IWRS/Patients/Trash/test_notifications.py b/IWRS/Patients/Trash/test_notifications.py
deleted file mode 100644
index ae2c5d3..0000000
--- a/IWRS/Patients/Trash/test_notifications.py
+++ /dev/null
@@ -1,172 +0,0 @@
-from playwright.sync_api import sync_playwright
-import re
-import os
-import datetime
-import mysql.connector
-import db_config
-
-
-def get_existing_pks(study):
- """Vrátí set pk notifikací které už jsou v DB pro danou studii."""
- try:
- conn = mysql.connector.connect(
- host=db_config.DB_HOST, port=db_config.DB_PORT,
- user=db_config.DB_USER, password=db_config.DB_PASSWORD,
- database=db_config.DB_NAME,
- )
- cursor = conn.cursor()
- cursor.execute("SELECT pk FROM iwrs_notifications WHERE study = %s", (study,))
- pks = {row[0] for row in cursor.fetchall()}
- cursor.close()
- conn.close()
- return pks
- except Exception as e:
- print(f" UPOZORNĚNÍ: nelze načíst existující pk z DB ({e}), stahuji vše")
- return set()
-
-BASE_URL = "https://janssen.4gclinical.com"
-EMAIL = "vbuzalka@its.jnj.com"
-PASSWORD = "Vlado123++-+"
-
-STUDY = "77242113UCO3001"
-SUBJECT = "CZ100222003"
-
-BASE_DIR = os.path.dirname(os.path.abspath(__file__))
-DETAILS_DIR = os.path.join(BASE_DIR, "IncomingSourceReportsDetails")
-
-
-def strip_html(html):
- text = re.sub(r"
", "\n", html, flags=re.IGNORECASE)
- text = re.sub(r"<[^>]+>", "", text)
- text = re.sub(r"\n{3,}", "\n\n", text)
- return text.strip()
-
-
-def main():
- existing_pks = get_existing_pks(STUDY)
- print(f"V DB již existuje {len(existing_pks)} notifikací pro {STUDY}")
-
- with sync_playwright() as p:
- browser = p.chromium.launch(headless=False, args=["--start-maximized"])
- context = browser.new_context(no_viewport=True)
- page = context.new_page()
-
- print("Přihlašuji se...")
- page.goto(BASE_URL)
- page.wait_for_load_state("networkidle")
- page.get_by_label("Email *").fill(EMAIL)
- page.get_by_label("Password *").fill(PASSWORD)
- page.locator("#login__submit").click()
- page.wait_for_load_state("networkidle")
-
- page.get_by_label("Study *").click()
- page.get_by_role("option", name=STUDY).click()
- page.get_by_role("button", name="SELECT").click()
- page.wait_for_load_state("networkidle")
-
- page.goto(f"{BASE_URL}/report/patient_detail_report")
- page.wait_for_load_state("networkidle", timeout=60000)
-
- # JWT + api_base
- jwt = page.evaluate("localStorage.getItem('JWT.access')")
- print(f"JWT: {jwt[:30]}...")
- instances = page.evaluate("""async (jwt) => {
- const res = await fetch('/_/api/dispatch/app_instances/', {
- headers: { 'Authorization': `Bearer ${jwt}` }
- });
- return res.json();
- }""", jwt)
- instance = next((i for i in instances if STUDY in i.get("label", "")), None)
- if not instance:
- raise ValueError(f"Instance pro {STUDY} nenalezena")
- api_base = instance["api_base_url"]
- print(f"API base: {api_base}")
-
- # Vyber subjekt a zachyť table_1 response přímo
- print(f"Vybírám subjekt {SUBJECT}...")
- input_field = page.locator('input[placeholder="search"], input[type="text"]').first
- input_field.click()
- input_field.fill(SUBJECT)
- page.wait_for_timeout(1000)
-
- captured = {}
- with page.expect_response(
- lambda r: "report_data" in r.url and "table_1" in r.url,
- timeout=60000
- ) as resp_info:
- page.locator("mat-option").first.dispatch_event("click")
-
- response = resp_info.value
- data = response.json()
-
- out_dir = os.path.join(DETAILS_DIR, STUDY)
- os.makedirs(out_dir, exist_ok=True)
- today = datetime.date.today().strftime("%Y-%m-%d")
-
- print(f"\n{'='*60}")
- print(f"Subjekt: {SUBJECT} | Studie: {STUDY}")
- print(f"{'='*60}")
-
- count = 0
- for row in data.get("data", []):
- for notif in (row.get("notification") or []):
- item = notif.get("item", {})
- pk = item.get("pk")
- title = item.get("et_title")
- label = (notif.get("label") or title or "").strip()
- # Celý label, mezery → podtržítka, nepovolené znaky pryč
- safe_label = re.sub(r'[\\/*?:"<>|]', "", label).replace(" ", "_")
- body = item.get("body", "")
- text = strip_html(body)
- count += 1
- print(f"\n--- Notifikace #{count}: {safe_label} (pk={pk}) | event: {row.get('event_event_id')} ---")
- print(text)
-
- if pk in existing_pks:
- print(f" → pk={pk} již v DB, přeskakuji")
- continue
-
- actual_date = row.get("actual_date_raw", "0000-00-00")
- pdf_filename = os.path.join(out_dir, f"{actual_date}_{safe_label}.pdf")
- if os.path.exists(pdf_filename):
- pdf_filename = os.path.join(out_dir, f"{actual_date}_{safe_label}_pk{pk}.pdf")
-
- pdf_url = f"{BASE_URL}{api_base}/api/v1/meta_api/pdfnotification?pk={pk}&title={title}&html=true"
- pdf_resp = page.request.get(pdf_url, headers={
- "Authorization": f"Bearer {jwt}",
- "lang": "en",
- "prancer_study": STUDY,
- "Accept": "application/json, text/plain, */*",
- })
- if pdf_resp.ok:
- with open(pdf_filename, "wb") as f:
- f.write(pdf_resp.body())
- print(f" → PDF uloženo: {os.path.basename(pdf_filename)}")
- json_filename = pdf_filename.replace(".pdf", ".json")
- import json
- with open(json_filename, "w", encoding="utf-8") as f:
- json.dump({
- "pk": pk,
- "title": title,
- "label": label,
- "event": row.get("event_event_id"),
- "actual_date": actual_date,
- "subject": SUBJECT,
- "study": STUDY,
- "text": text,
- }, f, ensure_ascii=False, indent=2)
- print(f" → JSON uloženo: {os.path.basename(json_filename)}")
- else:
- print(f" → PDF chyba: {pdf_resp.status}")
- page.wait_for_timeout(300)
-
- if count == 0:
- print("Žádné notifikace nalezeny.")
- else:
- print(f"\n{'='*60}")
- print(f"Celkem notifikací: {count}")
-
- browser.close()
-
-
-main()
diff --git a/IWRS/Patients/create_subject_report.py b/IWRS/Patients/create_subject_report.py
deleted file mode 100644
index 2b5af71..0000000
--- a/IWRS/Patients/create_subject_report.py
+++ /dev/null
@@ -1,310 +0,0 @@
-import os
-import glob
-import datetime
-import pandas as pd
-from openpyxl import Workbook
-from openpyxl.styles import (
- Font, PatternFill, Alignment, Border, Side, GradientFill
-)
-from openpyxl.utils import get_column_letter
-
-BASE_DIR = os.path.dirname(os.path.abspath(__file__))
-INCOMING_DIR = os.path.join(BASE_DIR, "IncomingSourceReports")
-CREATED_DIR = os.path.join(BASE_DIR, "CreatedReports")
-
-STUDIES = ["77242113UCO3001", "42847922MDD3003"]
-
-SOURCE_COLS = [
- "Subject",
- "Investigator",
- "Subject's age collection",
- "Cohort per IRT",
- "IRT Subject Status",
- "Last Recorded IRT Transaction",
- "Next Expected IRT Transaction",
- "Next Expected IRT Transaction Date [Local]",
-]
-
-DISPLAY_HEADERS = [
- "Subject",
- "Investigator",
- "Věk",
- "Cohort",
- "Status",
- "Last IRT",
- "Next Visit",
- "Next Date",
-]
-
-COL_WIDTHS = [14, 22, 6, 12, 14, 12, 12, 13]
-
-# ── Styles ───────────────────────────────────────────────────────────────────
-HEADER_FILL = PatternFill("solid", fgColor="1F4E79")
-HEADER_FONT = Font(name="Arial", bold=True, color="FFFFFF", size=10)
-NORMAL_FONT = Font(name="Arial", size=10)
-BOLD_FONT = Font(name="Arial", bold=True, size=10)
-STRIKE_FONT = Font(name="Arial", size=10, strike=True, color="999999")
-ADOLESC_FONT = Font(name="Arial", bold=True, size=10)
-
-THIN = Side(style="thin", color="CCCCCC")
-BORDER = Border(left=THIN, right=THIN, top=THIN, bottom=THIN)
-
-EVEN_FILL = PatternFill("solid", fgColor="EBF3FB")
-ODD_FILL = PatternFill("solid", fgColor="FFFFFF")
-
-CENTER = Alignment(horizontal="center", vertical="center", wrap_text=False)
-LEFT = Alignment(horizontal="left", vertical="center", wrap_text=False)
-
-
-def unique_path(directory, stem):
- path = os.path.join(directory, f"{stem}.xlsx")
- if not os.path.exists(path):
- return path
- time_tag = datetime.datetime.now().strftime("%H%M")
- return os.path.join(directory, f"{stem} {time_tag}.xlsx")
-
-
-def find_latest_source(study):
- pattern = os.path.join(INCOMING_DIR, f"* {study} Subject Summary Report.xlsx")
- files = sorted(
- [f for f in glob.glob(pattern) if not os.path.basename(f).startswith("~$")],
- key=os.path.getmtime,
- reverse=True,
- )
- if not files:
- raise FileNotFoundError(f"Nenalezen zdrojový soubor pro {study} v {INCOMING_DIR}")
- return files[0]
-
-
-def load_source(path):
- raw = pd.read_excel(path, header=None)
- # find header row (row with "Subject" in first cell)
- header_row = None
- for i, row in raw.iterrows():
- if "Subject" in [str(v).strip() for v in row]:
- header_row = i
- break
- if header_row is None:
- raise ValueError("Hlavičkový řádek nenalezen")
- df = pd.read_excel(path, header=header_row)
- return df
-
-
-def simplify_cohort(val):
- if pd.isna(val):
- return ""
- val = str(val)
- if "dolescent" in val:
- return "Adolescent"
- if val.startswith("Adult"):
- return "Adult"
- # MDD3003: "Part 1", "Part 2" — keep as-is
- return val
-
-
-def format_date(val):
- if pd.isna(val):
- return ""
- if hasattr(val, "strftime"):
- return val.strftime("%Y-%m-%d")
- return str(val)[:10]
-
-
-def write_zdroj(wb, df_raw, source_path):
- mtime = datetime.datetime.fromtimestamp(os.path.getmtime(source_path))
- sheet_name = f"ZDROJ ({mtime.strftime('%d%b%Y').upper()})"
- ws = wb.create_sheet(sheet_name)
- ws.sheet_view.showGridLines = True
-
- # write raw headers + data as plain table
- headers = list(df_raw.columns)
- for c, h in enumerate(headers, 1):
- cell = ws.cell(row=1, column=c, value=h)
- cell.font = Font(name="Arial", bold=True, size=9, color="FFFFFF")
- cell.fill = PatternFill("solid", fgColor="404040")
- cell.alignment = LEFT
- cell.border = BORDER
- ws.column_dimensions[get_column_letter(c)].width = 20
-
- for r, (_, row) in enumerate(df_raw.iterrows(), 2):
- fill = EVEN_FILL if r % 2 == 0 else ODD_FILL
- for c, col in enumerate(headers, 1):
- val = row[col]
- if pd.isna(val):
- val = ""
- elif hasattr(val, "strftime"):
- val = val.strftime("%Y-%m-%d")
- cell = ws.cell(row=r, column=c, value=val)
- cell.font = Font(name="Arial", size=9)
- cell.fill = fill
- cell.border = BORDER
- cell.alignment = LEFT
-
- ws.freeze_panes = "A2"
- ws.auto_filter.ref = f"A1:{get_column_letter(len(headers))}1"
-
-
-def write_prehled(wb, df_raw, study):
- ws = wb.create_sheet("Přehled")
- ws.sheet_view.showGridLines = False
- ws.sheet_view.showRowColHeaders = True
-
- # ── title row ────────────────────────────────────────────────────────────
- ws.merge_cells("A1:H1")
- title = ws["A1"]
- title.value = f"Subject Summary — {study} ({datetime.date.today().strftime('%d-%b-%Y')})"
- title.font = Font(name="Arial", bold=True, size=12, color="1F4E79")
- title.alignment = Alignment(horizontal="left", vertical="center")
- ws.row_dimensions[1].height = 22
-
- # ── header row ───────────────────────────────────────────────────────────
- for c, (h, w) in enumerate(zip(DISPLAY_HEADERS, COL_WIDTHS), 1):
- cell = ws.cell(row=2, column=c, value=h)
- cell.font = HEADER_FONT
- cell.fill = HEADER_FILL
- cell.alignment = CENTER
- cell.border = BORDER
- ws.column_dimensions[get_column_letter(c)].width = w
- ws.row_dimensions[2].height = 18
-
- # ── build display dataframe ───────────────────────────────────────────────
- display = pd.DataFrame()
- display["Subject"] = df_raw["Subject"].fillna("")
- display["Investigator"]= df_raw["Investigator"].fillna("")
- display["Věk"] = df_raw["Subject's age collection"].apply(
- lambda v: "" if pd.isna(v) else int(v))
- display["Cohort"] = df_raw["Cohort per IRT"].apply(simplify_cohort)
- display["Status"] = df_raw["IRT Subject Status"].fillna("")
- display["Last IRT"] = df_raw["Last Recorded IRT Transaction"].fillna("—")
- display["Next Visit"] = df_raw["Next Expected IRT Transaction"].fillna("—")
- display["Next Date"] = df_raw["Next Expected IRT Transaction Date [Local]"].apply(format_date)
-
- display = display.sort_values("Subject").reset_index(drop=True)
-
- # ── data rows ────────────────────────────────────────────────────────────
- for r_idx, row in display.iterrows():
- excel_row = r_idx + 3 # row 1=title, row 2=header
- status = str(row["Status"])
- is_failed = "Screen Failed" in status or "Discontinued" in status
- is_randomized = "Randomized" in status
- is_adolescent = row["Cohort"] == "Adolescent"
- fill = EVEN_FILL if r_idx % 2 == 0 else ODD_FILL
-
- values = [
- row["Subject"], row["Investigator"], row["Věk"],
- row["Cohort"], row["Status"], row["Last IRT"],
- row["Next Visit"], row["Next Date"],
- ]
-
- for c_idx, val in enumerate(values, 1):
- cell = ws.cell(row=excel_row, column=c_idx, value=val if val != "" else None)
- cell.fill = fill
- cell.border = BORDER
-
- # alignment
- cell.alignment = CENTER if c_idx in (3,) else LEFT
-
- # font logic
- if is_failed:
- cell.font = STRIKE_FONT
- elif c_idx == 5 and is_randomized:
- cell.font = BOLD_FONT
- elif c_idx == 4 and is_adolescent:
- cell.font = ADOLESC_FONT
- else:
- cell.font = NORMAL_FONT
-
- ws.row_dimensions[excel_row].height = 16
-
- ws.freeze_panes = "A3"
- last_data_row = len(display) + 2
- ws.auto_filter.ref = f"A2:H{last_data_row}"
-
-
-def write_next_visits(wb, df_raw, study):
- ws = wb.create_sheet("Next Visits")
- ws.sheet_view.showGridLines = False
-
- # title
- ws.merge_cells("A1:D1")
- title = ws["A1"]
- title.value = f"Next Expected Visits — {study} ({datetime.date.today().strftime('%d-%b-%Y')})"
- title.font = Font(name="Arial", bold=True, size=12, color="1F4E79")
- title.alignment = Alignment(horizontal="left", vertical="center")
- ws.row_dimensions[1].height = 22
-
- # headers
- nv_headers = ["Subject", "Investigator", "Next Visit", "Datum"]
- nv_widths = [14, 22, 26, 13]
- for c, (h, w) in enumerate(zip(nv_headers, nv_widths), 1):
- cell = ws.cell(row=2, column=c, value=h)
- cell.font = HEADER_FONT
- cell.fill = HEADER_FILL
- cell.alignment = CENTER
- cell.border = BORDER
- ws.column_dimensions[get_column_letter(c)].width = w
- ws.row_dimensions[2].height = 18
-
- # data — only rows with a Next Date, exclude Screen Failed / Discontinued
- df = pd.DataFrame()
- df["Subject"] = df_raw["Subject"].fillna("")
- df["Investigator"]= df_raw["Investigator"].fillna("")
- df["Next Visit"] = df_raw["Next Expected IRT Transaction"].fillna("")
- df["Datum"] = df_raw["Next Expected IRT Transaction Date [Local]"]
- df["Status"] = df_raw["IRT Subject Status"].fillna("")
-
- df = df[df["Datum"].notna()]
- df = df[~df["Status"].str.contains("Screen Failed|Discontinued", na=False)]
- df = df.sort_values("Datum").reset_index(drop=True)
-
- for r_idx, row in df.iterrows():
- excel_row = r_idx + 3
- fill = EVEN_FILL if r_idx % 2 == 0 else ODD_FILL
- datum_val = row["Datum"]
- datum_str = datum_val.strftime("%Y-%m-%d") if hasattr(datum_val, "strftime") else str(datum_val)[:10]
-
- values = [row["Subject"], row["Investigator"], row["Next Visit"], datum_str]
- for c_idx, val in enumerate(values, 1):
- cell = ws.cell(row=excel_row, column=c_idx, value=val if val != "" else None)
- cell.fill = fill
- cell.border = BORDER
- cell.font = NORMAL_FONT
- cell.alignment = LEFT
- ws.row_dimensions[excel_row].height = 16
-
- ws.freeze_panes = "A3"
- last_data_row = len(df) + 2
- ws.auto_filter.ref = f"A2:D{last_data_row}"
-
-
-def create_report(study):
- source_path = find_latest_source(study)
- print(f"[{study}] Čtu: {os.path.basename(source_path)}")
-
- df_raw = load_source(source_path)
-
- wb = Workbook()
- wb.remove(wb.active) # remove default sheet
-
- write_prehled(wb, df_raw, study)
- write_next_visits(wb, df_raw, study)
- write_zdroj(wb, df_raw, source_path)
-
- today = datetime.date.today().strftime("%Y-%m-%d")
- out_path = unique_path(CREATED_DIR, f"{today} {study} Subject Summary")
- wb.save(out_path)
- print(f"[{study}] Uloženo: {out_path}")
- return out_path
-
-
-def main():
- os.makedirs(CREATED_DIR, exist_ok=True)
- for study in STUDIES:
- try:
- create_report(study)
- except FileNotFoundError as e:
- print(f"[{study}] PŘESKOČENO: {e}")
- print("\nHotovo.")
-
-
-main()
diff --git a/IWRS/Patients/db_config.py b/IWRS/Patients/db_config.py
deleted file mode 100644
index bfa5959..0000000
--- a/IWRS/Patients/db_config.py
+++ /dev/null
@@ -1,5 +0,0 @@
-DB_HOST = "192.168.1.76"
-DB_PORT = 3306
-DB_USER = "root"
-DB_PASSWORD = "Vlado9674+"
-DB_NAME = "studie"
diff --git a/IWRS/Patients/download_patients.py b/IWRS/Patients/download_patients.py
deleted file mode 100644
index 57e2b7a..0000000
--- a/IWRS/Patients/download_patients.py
+++ /dev/null
@@ -1,48 +0,0 @@
-"""
-download_patients.py — stažení pacientských reportů pro jednu studii.
-Verze: 1.0 | Datum: 2026-06-10
-
-Volá se z IWRS/run_all_v1.0.py s již přihlášenou Playwright page (login +
-výběr studie zajišťuje common.iwrs_portal.login).
-
- 1. Subject Summary Report
- 2. Subject Detail Reports + notifikace PDF+JSON (per subjekt, jen nové dle pk v Mongo)
-
-Vše se ukládá ploše do IWRS/Incoming/ s datumovanými názvy.
-"""
-
-import os
-import sys
-import datetime
-
-BASE_DIR = os.path.dirname(os.path.abspath(__file__))
-IWRS_DIR = os.path.dirname(BASE_DIR)
-for _p in (IWRS_DIR, BASE_DIR):
- if _p not in sys.path:
- sys.path.insert(0, _p)
-
-from common.iwrs_portal import BASE_URL
-from common.paths import INCOMING_DIR, unique_path
-
-import download_subject_details as dsd
-
-
-def download_summary(page, study, today):
- print(f" [{study}] Stahuji Subject Summary Report...")
- page.goto(f"{BASE_URL}/report/patient_summary_report")
- page.wait_for_load_state("networkidle", timeout=120000)
- filename = unique_path(INCOMING_DIR, f"{today} {study} Subject Summary Report")
- with page.expect_download(timeout=120000) as dl:
- page.get_by_role("button", name="Download XLS").click()
- dl.value.save_as(filename)
- print(f" [{study}] Summary OK -> {os.path.basename(filename)}")
- return filename
-
-
-def run(page, study):
- """Stáhne summary + detaily + notifikace pro studii do IWRS/Incoming/."""
- os.makedirs(INCOMING_DIR, exist_ok=True)
- today = datetime.date.today().strftime("%Y-%m-%d")
- download_summary(page, study, today)
- # detail XLSX + notifikace přímo do Incoming/ (flat názvy se study+subject)
- dsd.run(page, study, out_dir=INCOMING_DIR, subjects_source_dir=INCOMING_DIR)
diff --git a/IWRS/Patients/import_patients.py b/IWRS/Patients/import_patients.py
deleted file mode 100644
index cd814c5..0000000
--- a/IWRS/Patients/import_patients.py
+++ /dev/null
@@ -1,90 +0,0 @@
-"""
-import_patients.py — import pacientských reportů z IWRS/Incoming/ do MongoDB.
-Verze: 1.0 | Datum: 2026-06-10
-
-Pořadí zpracování per typ + studie: nejstarší soubor podle mtime první
-(důležité pro chronologickou správnost snapshotů).
-
-Po úspěšném importu se soubor přesune do IWRS/Incoming/Processed/.
-Při chybě zůstane soubor v Incoming/.
-
-Volá se z IWRS/run_all_v1.0.py (ensure_indexes volá orchestrátor);
-lze spustit i samostatně: python import_patients.py
-"""
-
-import os
-import sys
-import glob
-
-BASE_DIR = os.path.dirname(os.path.abspath(__file__))
-IWRS_DIR = os.path.dirname(BASE_DIR)
-for _p in (IWRS_DIR, BASE_DIR):
- if _p not in sys.path:
- sys.path.insert(0, _p)
-
-from common.paths import INCOMING_DIR, PROCESSED_DIR, STUDIES, move_done, sorted_by_mtime
-from common.mongo_writer import ensure_indexes
-
-import import_to_mongo
-import import_notifications_to_mongo
-
-
-def import_summaries(study):
- pattern = os.path.join(INCOMING_DIR, f"* {study} Subject Summary Report*.xlsx")
- files = sorted_by_mtime(glob.glob(pattern))
- if not files:
- print(f" [{study}] summary: nic ke zpracování")
- return
- print(f" [{study}] summary: {len(files)} soubor(ů) (oldest first)")
- for path in files:
- try:
- import_to_mongo.import_subject_summary(study, path)
- move_done(path)
- except Exception as e:
- print(f" [{study}] CHYBA summary {os.path.basename(path)}: {e}")
-
-
-def import_details(study):
- pattern = os.path.join(INCOMING_DIR, f"* {study} * Subject Detail.xlsx")
- files = sorted_by_mtime(glob.glob(pattern))
- if not files:
- print(f" [{study}] detail: nic ke zpracování")
- return
- print(f" [{study}] detail: {len(files)} soubor(ů) (oldest first)")
- for path in files:
- parsed = import_to_mongo.parse_detail_filename(path)
- if not parsed:
- print(f" [{study}] PŘESKAKUJI (nelze parsovat název): {os.path.basename(path)}")
- continue
- _, parsed_study, subject = parsed
- if parsed_study != study:
- continue # patří jiné studii
- try:
- import_to_mongo.import_visits_single_file(study, subject, path)
- move_done(path)
- except Exception as e:
- print(f" [{study}] CHYBA detail {os.path.basename(path)}: {e}")
-
-
-def run(studies=None):
- studies = studies or STUDIES
- if not os.path.isdir(INCOMING_DIR):
- print(f"Adresář neexistuje: {INCOMING_DIR}")
- return
-
- print("=" * 60)
- print("Import Subject Summary + Visits")
- print("=" * 60)
- for study in studies:
- import_summaries(study)
- import_details(study)
-
- print("\n" + "=" * 60)
- print("Import notifikací")
- print("=" * 60)
- import_notifications_to_mongo.import_from_dir(INCOMING_DIR, PROCESSED_DIR, studies)
-
-
-if __name__ == "__main__":
- ensure_indexes()
- run()
diff --git a/IWRS/Patients/parse_notifications_to_mongo.py b/IWRS/Patients/parse_notifications_to_mongo.py
deleted file mode 100644
index 0f2fc3b..0000000
--- a/IWRS/Patients/parse_notifications_to_mongo.py
+++ /dev/null
@@ -1,246 +0,0 @@
-"""
-Parsuje texty IWRS notifikací z MySQL (iwrs_notifications) a ukládá strukturovaná
-data do MongoDB (databáze 'studie', kolekce 'iwrs').
-
-Idempotentní: upsert podle pk (unikátní identifikátor notifikace v IWRS).
-"""
-
-import re
-import datetime
-import sys
-
-import mysql.connector
-from pymongo import MongoClient, ASCENDING
-
-import db_config
-
-MONGO_URI = "mongodb://192.168.1.76:27017"
-MONGO_DB = "studie"
-MONGO_COLL = "iwrs_notifications"
-
-
-# ── parsery ──────────────────────────────────────────────────────────────────
-
-def parse_kv_lines(text):
- """Vytáhne všechny řádky typu 'Klíč: Hodnota' do dictu.
- Když je hodnota za dvojtečkou prázdná, vezme se první neprázdný následující řádek."""
- out = {}
- lines = [l.strip() for l in text.splitlines()]
- pending_key = None
- for line in lines:
- # čekáme na hodnotu pro klíč z předchozího řádku
- if pending_key is not None:
- if not line:
- continue
- if ":" not in line:
- out.setdefault(pending_key, line)
- pending_key = None
- continue
- # další řádek je sám "Klíč: Hodnota" → zahodíme pending a zpracujeme normálně
- pending_key = None
-
- if not line or ":" not in line:
- continue
- if line.lower().startswith("http"):
- continue
- key, _, val = line.partition(":")
- key = key.strip()
- val = val.strip()
- if not key or (" " in key and len(key.split()) > 8):
- continue
- if not val:
- pending_key = key
- continue
- out.setdefault(key, val)
- return out
-
-
-DATE_RE = re.compile(r"^\d{2}-[A-Z][a-z]{2}-\d{4}$")
-DATETIME_RE = re.compile(r"^(\d{2}-[A-Z][a-z]{2}-\d{4})\s+(\d{2}:\d{2}:\d{2})$")
-
-
-def to_date(s):
- if not s:
- return None
- s = s.strip()
- if DATE_RE.match(s):
- try:
- return datetime.datetime.strptime(s, "%d-%b-%Y")
- except ValueError:
- return None
- return None
-
-
-def to_datetime(s):
- if not s:
- return None
- s = re.sub(r"\s+", " ", s.strip())
- m = DATETIME_RE.match(s)
- if m:
- try:
- return datetime.datetime.strptime(f"{m.group(1)} {m.group(2)}", "%d-%b-%Y %H:%M:%S")
- except ValueError:
- return None
- return None
-
-
-MED_ROW_RE = re.compile(
- r"(?P\d{7})\s*[\s\n]*"
- r"(?P[A-Za-z][A-Za-z0-9 /+\-]+?)\s*[\s\n]*"
- r"(?P[A-Z0-9]{5,10})\s*[\s\n]*"
- r"(?P\d{2}-[A-Z][a-z]{2}-\d{4})"
-)
-
-
-def parse_medication_table(text):
- """Najde záznamy medikace (med_no, med_type, lot, expirace) v textu.
- Pracuje s oběma formáty (UCO3001 multiline i MDD3003 concatenated)."""
- rows = []
- # zkomprimuj whitespace pro snadnější regex
- compact = re.sub(r"\s+", " ", text)
- for m in MED_ROW_RE.finditer(compact):
- med_type = m.group("type").strip()
- # uřízni nadbytečné koncové fragmenty
- med_type = re.sub(r"\s+(Packaged|Lot|Expiration|No|Date|Medication).*$", "", med_type).strip()
- rows.append({
- "medication_no": m.group("no"),
- "medication_type": med_type,
- "lot_no": m.group("lot"),
- "expiration_date": to_date(m.group("exp")),
- })
- # dedupe
- seen = set()
- unique = []
- for r in rows:
- key = (r["medication_no"], r["lot_no"])
- if key in seen:
- continue
- seen.add(key)
- unique.append(r)
- return unique
-
-
-# fields, které chceme v dokumentu vyloučit z kv (ošklivé / nepotřebné)
-KV_BLACKLIST = {
- "If you have questions about this notification, please contact 4G Clinical Support at",
-}
-
-
-def build_document(row):
- pk, study, subject, title, label, event, actual_date, text = row
-
- kv = parse_kv_lines(text)
- meds = parse_medication_table(text)
-
- # převod známých datumových/datetime polí
- dt_site = to_datetime(kv.get("Transaction Date/Time (site local)"))
- dt_sys = to_datetime(kv.get("Transaction Date/Time (system local)"))
-
- date_fields = [
- "Informed Consent Date",
- "Informed Consent Date at Screening",
- "Informed Consent Date at Subject Creation",
- "Date of Subject Creation in IRT",
- "Date of Screening in IRT",
- "Screenfail Date",
- "Discontinuation date",
- "Dispensation date",
- "Returned Date",
- ]
- parsed_dates = {}
- for f in date_fields:
- if f in kv:
- d = to_date(kv[f])
- if d:
- parsed_dates[f] = d
-
- doc = {
- "_id": pk, # použij IWRS pk jako _id (idempotence)
- "pk": pk,
- "study": study,
- "subject": subject,
- "title": title,
- "label": label,
- "event": event,
- "actual_date": (
- datetime.datetime.combine(actual_date, datetime.time())
- if isinstance(actual_date, datetime.date) and not isinstance(actual_date, datetime.datetime)
- else actual_date
- ),
- "site": kv.get("Site"),
- "investigator": kv.get("Investigator"),
- "location": kv.get("Location"),
- "cohort": kv.get("Cohort"),
- "irt_subject_status": kv.get("IRT Subject Status"),
- "transaction_site_local": dt_site,
- "transaction_system_local": dt_sys,
- "transaction_by": kv.get("Transaction performed by"),
- "medications": meds,
- "fields": {k: v for k, v in kv.items() if k not in {
- "Site", "Investigator", "Location", "Cohort", "IRT Subject Status",
- "Subject",
- "Transaction Date/Time (site local)",
- "Transaction Date/Time (system local)",
- "Transaction performed by",
- }},
- "parsed_dates": parsed_dates,
- "raw_text": text,
- }
- return doc
-
-
-# ── main ─────────────────────────────────────────────────────────────────────
-
-def main(studies=None):
- conn = mysql.connector.connect(
- host=db_config.DB_HOST, port=db_config.DB_PORT,
- user=db_config.DB_USER, password=db_config.DB_PASSWORD,
- database=db_config.DB_NAME,
- )
- cur = conn.cursor()
-
- if studies:
- placeholders = ",".join(["%s"] * len(studies))
- cur.execute(
- f"SELECT pk, study, subject, title, label, event, actual_date, text "
- f"FROM iwrs_notifications WHERE study IN ({placeholders})",
- studies,
- )
- else:
- cur.execute(
- "SELECT pk, study, subject, title, label, event, actual_date, text "
- "FROM iwrs_notifications"
- )
- rows = cur.fetchall()
- cur.close()
- conn.close()
- print(f" Nacteno {len(rows)} notifikaci z MySQL")
-
- mc = MongoClient(MONGO_URI, serverSelectionTimeoutMS=5000)
- coll = mc[MONGO_DB][MONGO_COLL]
-
- # indexy
- coll.create_index([("study", ASCENDING), ("subject", ASCENDING)])
- coll.create_index([("study", ASCENDING), ("title", ASCENDING)])
- coll.create_index([("actual_date", ASCENDING)])
-
- upserts = 0
- for row in rows:
- doc = build_document(row)
- coll.replace_one({"_id": doc["_id"]}, doc, upsert=True)
- upserts += 1
-
- print(f" Upsert {upserts} dokumentu do {MONGO_DB}.{MONGO_COLL}")
-
- # stats
- print("\n Statistika v Mongo:")
- for r in coll.aggregate([
- {"$group": {"_id": {"study": "$study", "title": "$title"}, "count": {"$sum": 1}}},
- {"$sort": {"_id.study": 1, "_id.title": 1}},
- ]):
- print(f" {r['_id']['study']} | {r['_id']['title']:30s} | {r['count']}")
-
-
-if __name__ == "__main__":
- studies = sys.argv[1:] if len(sys.argv) > 1 else None
- main(studies)
diff --git a/IWRS/Testing/format_accountability.py b/IWRS/Testing/format_accountability.py
deleted file mode 100644
index a7ef956..0000000
--- a/IWRS/Testing/format_accountability.py
+++ /dev/null
@@ -1,118 +0,0 @@
-import pandas as pd
-from openpyxl import load_workbook
-from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
-from openpyxl.utils import get_column_letter
-
-INPUT_FILE = "accountability_combined.xlsx"
-OUTPUT_FILE = "accountability_formatted.xlsx"
-SHEET_NAME = "CountryMedicationOverview"
-
-COLUMN_RENAMES = {
- "Site": "Site",
- "Medication ID": "Med ID",
- "Packaged Lot number": "Lot No.",
- "Original Expiration Date when Packaged Lot was Added": "Orig Exp Date",
- "Expiration date": "Exp Date",
- "Received Date": "Rcv Date",
- "Shipment Receipt User": "Rcpt User",
- "Subject Identifier": "Subject ID",
- "Quantity Assigned": "Qty Asgn",
- "IRT Transaction": "IRT Tx",
- "Date Assigned": "Date Asgn",
- "Assignment User": "Asgn User",
- "Dispensation Status": "Disp Status",
- "Dispensing Date": "Disp Date",
- "Quantity Dispensed": "Qty Disp",
- "Dispensing User": "Disp User",
- "Quantity Returned": "Qty Ret",
- "Date Returned": "Date Ret",
- "Return User": "Ret User",
- "DestroyedOn": "Destroyed",
- "Basket number": "Basket No.",
-}
-
-DATE_COLUMNS = {
- "Orig Exp Date", "Exp Date", "Rcv Date",
- "Date Asgn", "Disp Date", "Date Ret", "Destroyed",
-}
-
-COLUMN_WIDTHS = {
- "Site": 14,
- "Med ID": 10,
- "Lot No.": 12,
- "Orig Exp Date": 16,
- "Exp Date": 14,
- "Rcv Date": 14,
- "Rcpt User": 22,
- "Subject ID": 14,
- "Qty Asgn": 9,
- "IRT Tx": 8,
- "Date Asgn": 14,
- "Asgn User": 20,
- "Disp Status": 16,
- "Disp Date": 14,
- "Qty Disp": 9,
- "Disp User": 20,
- "Qty Ret": 10,
- "Date Ret": 14,
- "Ret User": 18,
- "Destroyed": 14,
- "Basket No.": 12,
-}
-
-# ── 1. Load with pandas and convert date columns ─────────────────────────────
-df = pd.read_excel(INPUT_FILE)
-df.rename(columns=COLUMN_RENAMES, inplace=True)
-
-for col in DATE_COLUMNS:
- if col in df.columns:
- df[col] = pd.to_datetime(df[col], dayfirst=True, errors="coerce")
-
-df.sort_values(["Site", "Rcv Date", "Med ID"], inplace=True, ignore_index=True)
-df.to_excel(OUTPUT_FILE, index=False, sheet_name=SHEET_NAME)
-
-# ── 2. Format with openpyxl ───────────────────────────────────────────────────
-wb = load_workbook(OUTPUT_FILE)
-ws = wb[SHEET_NAME]
-
-header_fill = PatternFill("solid", start_color="1F4E79")
-header_font = Font(bold=True, color="FFFFFF", name="Arial", size=10)
-new_col_fill = PatternFill("solid", start_color="E2EFDA")
-row_font = Font(name="Arial", size=10)
-
-thin = Side(style="thin", color="000000")
-border = Border(left=thin, right=thin, top=thin, bottom=thin)
-
-headers = [cell.value for cell in ws[1]]
-new_cols = {"Destroyed", "Basket No."}
-
-# Header row
-for cell in ws[1]:
- cell.fill = header_fill
- cell.font = header_font
- cell.alignment = Alignment(horizontal="center", vertical="center", wrap_text=False)
- cell.border = border
-
-# Data rows
-max_row = ws.max_row
-for row in ws.iter_rows(min_row=2, max_row=max_row):
- for cell in row:
- col_name = headers[cell.column - 1] if cell.column <= len(headers) else None
- cell.font = row_font
- cell.border = border
- cell.alignment = Alignment(horizontal="center")
- if col_name in DATE_COLUMNS:
- cell.number_format = "DD-MMM-YYYY"
- if col_name in new_cols:
- cell.fill = new_col_fill
-
-# Column widths
-for cell in ws[1]:
- width = COLUMN_WIDTHS.get(cell.value, 14)
- ws.column_dimensions[get_column_letter(cell.column)].width = width
-
-ws.auto_filter.ref = ws.dimensions
-ws.freeze_panes = "A2"
-
-wb.save(OUTPUT_FILE)
-print(f"Saved: {OUTPUT_FILE} ({max_row - 1} rows, sheet '{SHEET_NAME}')")
diff --git a/IWRS/Testing/list_reports.py b/IWRS/Testing/list_reports.py
deleted file mode 100644
index ab0e876..0000000
--- a/IWRS/Testing/list_reports.py
+++ /dev/null
@@ -1,74 +0,0 @@
-from playwright.sync_api import sync_playwright
-import json
-
-# ── CONFIG ──────────────────────────────────────────────────────────────────
-BASE_URL = "https://janssen.4gclinical.com"
-STUDY = "42847922MDD3003"
-
-EMAIL = "vbuzalka@its.jnj.com"
-PASSWORD = "Vlado123++-" # doplň heslo
-# ────────────────────────────────────────────────────────────────────────────
-
-
-def list_reports():
- with sync_playwright() as p:
- browser = p.chromium.launch(headless=False)
- page = browser.new_page()
-
- # Přihlášení
- page.goto(BASE_URL)
- page.wait_for_load_state("networkidle")
-
- page.get_by_label("Email *").fill(EMAIL)
- page.get_by_label("Password *").fill(PASSWORD)
- page.locator('#login__submit').click()
- page.wait_for_load_state("networkidle")
-
- # Výběr studie — klikni na dropdown, vyber studii, klikni SELECT
- page.get_by_label("Study *").click()
- page.get_by_role("option", name=STUDY).click()
- page.get_by_role("button", name="SELECT").click()
- page.wait_for_load_state("networkidle")
-
- # Přejdi na seznam reportů
- page.goto(f"{BASE_URL}/reports")
- page.wait_for_load_state("networkidle")
- page.wait_for_selector('[role="gridcell"] a', timeout=15000)
-
- # Získej názvy reportů
- names = page.evaluate("""
- () => Array.from(document.querySelectorAll('[role="gridcell"] a'))
- .map(a => a.innerText.trim())
- .filter(n => n)
- """)
- print(f"\nNalezeno {len(names)} reportů, zjišťuji URL...\n")
-
- # Pro každý report klikni, zaznamenej URL a vrať se zpět
- reports = []
- for name in names:
- with page.expect_navigation(timeout=15000):
- page.locator('[role="gridcell"] a').filter(has_text=name).click()
- page.wait_for_load_state("networkidle")
- page.wait_for_timeout(2000)
- path = page.url.replace(BASE_URL, "")
- reports.append({"name": name, "href": path})
- print(f" {name:50s} {path}")
- # Průběžné uložení po každém reportu
- with open("reports.json", "w", encoding="utf-8") as f:
- json.dump(reports, f, ensure_ascii=False, indent=2)
- if page.url != f"{BASE_URL}/reports":
- page.goto(f"{BASE_URL}/reports")
- page.wait_for_load_state("networkidle")
- page.wait_for_timeout(2000)
- page.wait_for_selector('[role="gridcell"] a', timeout=30000)
-
- browser.close()
-
- with open("reports.json", "w", encoding="utf-8") as f:
- json.dump(reports, f, ensure_ascii=False, indent=2)
- print(f"\nUloženo do reports.json")
-
- return reports
-
-
-list_reports()
diff --git a/IWRS/Testing/sheet_assigned_not_dispensed.py b/IWRS/Testing/sheet_assigned_not_dispensed.py
deleted file mode 100644
index 3b97d1a..0000000
--- a/IWRS/Testing/sheet_assigned_not_dispensed.py
+++ /dev/null
@@ -1,92 +0,0 @@
-import pandas as pd
-from openpyxl import load_workbook
-from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
-from openpyxl.utils import get_column_letter
-
-SOURCE_FILE = "accountability_combined.xlsx"
-OUTPUT_FILE = "sheet_assigned_not_dispensed.xlsx"
-SHEET_NAME = "Assigned not dispensed"
-
-DATE_COLUMNS = {
- "Orig Exp Date", "Exp Date", "Rcv Date",
- "Date Asgn", "Disp Date", "Date Ret", "Destroyed",
-}
-
-COLUMN_WIDTHS = {
- "Site": 14,
- "Med ID": 10,
- "Lot No.": 12,
- "Orig Exp Date": 16,
- "Exp Date": 14,
- "Rcv Date": 14,
- "Rcpt User": 22,
- "Subject ID": 14,
- "Qty Asgn": 9,
- "IRT Tx": 8,
- "Date Asgn": 14,
- "Asgn User": 20,
- "Disp Status": 16,
- "Disp Date": 14,
- "Qty Disp": 9,
- "Disp User": 20,
- "Qty Ret": 10,
- "Date Ret": 14,
- "Ret User": 18,
- "Destroyed": 14,
- "Basket No.": 12,
-}
-
-df = pd.read_excel(SOURCE_FILE)
-
-for col in DATE_COLUMNS:
- if col in df.columns:
- df[col] = pd.to_datetime(df[col], errors="coerce")
-
-# Filter: Subject ID present AND Disp Date missing
-mask = df["Subject ID"].notna() & df["Disp Date"].isna()
-filtered = df[mask].copy().reset_index(drop=True)
-
-print(f"Assigned not dispensed: {len(filtered)}")
-
-filtered.to_excel(OUTPUT_FILE, index=False, sheet_name=SHEET_NAME)
-
-# Formatting
-wb = load_workbook(OUTPUT_FILE)
-ws = wb[SHEET_NAME]
-
-header_fill = PatternFill("solid", start_color="833C00") # dark orange
-header_font = Font(bold=True, color="FFFFFF", name="Arial", size=10)
-row_font = Font(name="Arial", size=10)
-subj_fill = PatternFill("solid", start_color="FFF2CC") # light yellow highlight for Subject ID
-
-thin = Side(style="thin", color="000000")
-border = Border(left=thin, right=thin, top=thin, bottom=thin)
-
-headers = [cell.value for cell in ws[1]]
-
-for cell in ws[1]:
- cell.fill = header_fill
- cell.font = header_font
- cell.alignment = Alignment(horizontal="center", vertical="center", wrap_text=False)
- cell.border = border
-
-for row in ws.iter_rows(min_row=2, max_row=ws.max_row):
- for cell in row:
- col_name = headers[cell.column - 1] if cell.column <= len(headers) else None
- cell.font = row_font
- cell.border = border
- cell.alignment = Alignment(horizontal="center")
- if col_name in DATE_COLUMNS:
- cell.number_format = "DD-MMM-YYYY"
- if col_name == "Subject ID":
- cell.fill = subj_fill
-
-for cell in ws[1]:
- width = COLUMN_WIDTHS.get(cell.value, 14)
- ws.column_dimensions[get_column_letter(cell.column)].width = width
-
-ws.auto_filter.ref = ws.dimensions
-ws.freeze_panes = "A2"
-
-wb.save(OUTPUT_FILE)
-print(f"Saved: {OUTPUT_FILE} (sheet: '{SHEET_NAME}')")
diff --git a/IWRS/Testing/sheet_expired.py b/IWRS/Testing/sheet_expired.py
deleted file mode 100644
index b41d353..0000000
--- a/IWRS/Testing/sheet_expired.py
+++ /dev/null
@@ -1,97 +0,0 @@
-import pandas as pd
-from datetime import date
-from openpyxl import load_workbook
-from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
-from openpyxl.utils import get_column_letter
-
-SOURCE_FILE = "accountability_combined.xlsx"
-OUTPUT_FILE = "sheet_expired.xlsx"
-
-DATE_COLUMNS = {
- "Orig Exp Date", "Exp Date", "Rcv Date",
- "Date Asgn", "Disp Date", "Date Ret", "Destroyed",
-}
-
-COLUMN_WIDTHS = {
- "Site": 14,
- "Med ID": 10,
- "Lot No.": 12,
- "Orig Exp Date": 16,
- "Exp Date": 14,
- "Rcv Date": 14,
- "Rcpt User": 22,
- "Subject ID": 14,
- "Qty Asgn": 9,
- "IRT Tx": 8,
- "Date Asgn": 14,
- "Asgn User": 20,
- "Disp Status": 16,
- "Disp Date": 14,
- "Qty Disp": 9,
- "Disp User": 20,
- "Qty Ret": 10,
- "Date Ret": 14,
- "Ret User": 18,
- "Destroyed": 14,
- "Basket No.": 12,
-}
-
-today = date.today()
-sheet_name = f"Expired as of {today.strftime('%d-%b-%Y')}"
-
-# Load source
-df = pd.read_excel(SOURCE_FILE)
-
-# Convert date columns
-for col in DATE_COLUMNS:
- if col in df.columns:
- df[col] = pd.to_datetime(df[col], errors="coerce")
-
-# Filter: not in basket AND not assigned to patient AND Exp Date < today
-mask = df["Basket No."].isna() & df["Subject ID"].isna() & (df["Exp Date"] < pd.Timestamp(today))
-filtered = df[mask].copy().reset_index(drop=True)
-
-print(f"Expired kits not in basket: {len(filtered)}")
-
-filtered.to_excel(OUTPUT_FILE, index=False, sheet_name=sheet_name)
-
-# Formatting
-wb = load_workbook(OUTPUT_FILE)
-ws = wb[sheet_name]
-
-header_fill = PatternFill("solid", start_color="C00000") # dark red
-header_font = Font(bold=True, color="FFFFFF", name="Arial", size=10)
-row_font = Font(name="Arial", size=10)
-exp_fill = PatternFill("solid", start_color="FFE0E0") # light red highlight for Exp Date
-
-thin = Side(style="thin", color="000000")
-border = Border(left=thin, right=thin, top=thin, bottom=thin)
-
-headers = [cell.value for cell in ws[1]]
-
-for cell in ws[1]:
- cell.fill = header_fill
- cell.font = header_font
- cell.alignment = Alignment(horizontal="center", vertical="center", wrap_text=False)
- cell.border = border
-
-for row in ws.iter_rows(min_row=2, max_row=ws.max_row):
- for cell in row:
- col_name = headers[cell.column - 1] if cell.column <= len(headers) else None
- cell.font = row_font
- cell.border = border
- cell.alignment = Alignment(horizontal="center")
- if col_name in DATE_COLUMNS:
- cell.number_format = "DD-MMM-YYYY"
- if col_name == "Exp Date":
- cell.fill = exp_fill
-
-for cell in ws[1]:
- width = COLUMN_WIDTHS.get(cell.value, 14)
- ws.column_dimensions[get_column_letter(cell.column)].width = width
-
-ws.auto_filter.ref = ws.dimensions
-ws.freeze_panes = "A2"
-
-wb.save(OUTPUT_FILE)
-print(f"Saved: {OUTPUT_FILE} (sheet: '{sheet_name}')")
diff --git a/IWRS/Testing/sheet_kits_for_destruction.py b/IWRS/Testing/sheet_kits_for_destruction.py
deleted file mode 100644
index a6b6dd8..0000000
--- a/IWRS/Testing/sheet_kits_for_destruction.py
+++ /dev/null
@@ -1,99 +0,0 @@
-import pandas as pd
-from openpyxl import load_workbook
-from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
-from openpyxl.utils import get_column_letter
-
-SOURCE_FILE = "accountability_combined.xlsx"
-OUTPUT_FILE = "sheet_kits_for_destruction.xlsx"
-SHEET_NAME = "Kits for destruction"
-
-DATE_COLUMNS = {
- "Orig Exp Date", "Exp Date", "Rcv Date",
- "Date Asgn", "Disp Date", "Date Ret", "Destroyed",
-}
-
-COLUMN_WIDTHS = {
- "Site": 14,
- "Med ID": 10,
- "Lot No.": 12,
- "Orig Exp Date": 16,
- "Exp Date": 14,
- "Rcv Date": 14,
- "Rcpt User": 22,
- "Subject ID": 14,
- "Qty Asgn": 9,
- "IRT Tx": 8,
- "Date Asgn": 14,
- "Asgn User": 20,
- "Disp Status": 16,
- "Disp Date": 14,
- "Qty Disp": 9,
- "Disp User": 20,
- "Qty Ret": 10,
- "Date Ret": 14,
- "Ret User": 18,
- "Destroyed": 14,
- "Basket No.": 12,
-}
-
-df = pd.read_excel(SOURCE_FILE)
-
-for col in DATE_COLUMNS:
- if col in df.columns:
- df[col] = pd.to_datetime(df[col], errors="coerce")
-
-# Filter: no basket AND (Date Ret filled OR Disp Status == NOT DISPENSED)
-mask = (
- df["Basket No."].isna() &
- (
- df["Date Ret"].notna() |
- (df["Disp Status"].str.upper() == "NOT DISPENSED")
- )
-)
-filtered = df[mask].copy().sort_values(["Site", "Date Ret"], ascending=[True, True])
-filtered = filtered.drop(columns=["Destroyed", "Basket No."]).reset_index(drop=True)
-
-print(f"Kits for destruction: {len(filtered)}")
-
-filtered.to_excel(OUTPUT_FILE, index=False, sheet_name=SHEET_NAME)
-
-# Formatting
-wb = load_workbook(OUTPUT_FILE)
-ws = wb[SHEET_NAME]
-
-header_fill = PatternFill("solid", start_color="595959") # dark grey
-header_font = Font(bold=True, color="FFFFFF", name="Arial", size=10)
-row_font = Font(name="Arial", size=10)
-basket_fill = PatternFill("solid", start_color="FFE0E0") # light red for empty Basket No.
-
-thin = Side(style="thin", color="000000")
-border = Border(left=thin, right=thin, top=thin, bottom=thin)
-
-headers = [cell.value for cell in ws[1]]
-
-for cell in ws[1]:
- cell.fill = header_fill
- cell.font = header_font
- cell.alignment = Alignment(horizontal="center", vertical="center", wrap_text=False)
- cell.border = border
-
-for row in ws.iter_rows(min_row=2, max_row=ws.max_row):
- for cell in row:
- col_name = headers[cell.column - 1] if cell.column <= len(headers) else None
- cell.font = row_font
- cell.border = border
- cell.alignment = Alignment(horizontal="center")
- if col_name in DATE_COLUMNS:
- cell.number_format = "DD-MMM-YYYY"
- if col_name == "Basket No.":
- cell.fill = basket_fill
-
-for cell in ws[1]:
- width = COLUMN_WIDTHS.get(cell.value, 14)
- ws.column_dimensions[get_column_letter(cell.column)].width = width
-
-ws.auto_filter.ref = ws.dimensions
-ws.freeze_panes = "A2"
-
-wb.save(OUTPUT_FILE)
-print(f"Saved: {OUTPUT_FILE} (sheet: '{SHEET_NAME}')")
diff --git a/IWRS/Testing/sheet_not_returned.py b/IWRS/Testing/sheet_not_returned.py
deleted file mode 100644
index 79f68e2..0000000
--- a/IWRS/Testing/sheet_not_returned.py
+++ /dev/null
@@ -1,102 +0,0 @@
-import pandas as pd
-from openpyxl import load_workbook
-from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
-from openpyxl.utils import get_column_letter
-
-SOURCE_FILE = "accountability_combined.xlsx"
-OUTPUT_FILE = "sheet_not_returned.xlsx"
-SHEET_NAME = "Not returned"
-
-DATE_COLUMNS = {
- "Orig Exp Date", "Exp Date", "Rcv Date",
- "Date Asgn", "Disp Date", "Max Visit Date",
-}
-
-COLUMN_WIDTHS = {
- "Site": 14,
- "Med ID": 10,
- "Lot No.": 12,
- "Orig Exp Date": 16,
- "Exp Date": 14,
- "Rcv Date": 14,
- "Rcpt User": 22,
- "Subject ID": 14,
- "Qty Asgn": 9,
- "IRT Tx": 8,
- "Date Asgn": 14,
- "Asgn User": 20,
- "Disp Status": 16,
- "Disp Date": 14,
- "Qty Disp": 9,
- "Disp User": 20,
- "Max Visit Date": 16,
-}
-
-df = pd.read_excel(SOURCE_FILE)
-
-for col in DATE_COLUMNS:
- if col in df.columns:
- df[col] = pd.to_datetime(df[col], errors="coerce")
-
-# Kits with no return date, assigned to a patient, and not "NOT DISPENSED"
-no_ret = df[
- df["Date Ret"].isna() &
- df["Subject ID"].notna() &
- (df["Disp Status"].str.upper() != "NOT DISPENSED")
-].copy()
-
-# Max Date Asgn per patient (from full dataset)
-max_asgn = df.groupby("Subject ID")["Date Asgn"].max().rename("Max Visit Date")
-no_ret = no_ret.join(max_asgn, on="Subject ID")
-
-# Keep only kits where Date Asgn is NOT the latest for that patient
-filtered = no_ret[no_ret["Date Asgn"] < no_ret["Max Visit Date"]].copy()
-
-# Drop columns Q-U and keep Max Visit Date
-filtered = filtered.drop(columns=["Qty Ret", "Date Ret", "Ret User", "Destroyed", "Basket No."])
-filtered = filtered.reset_index(drop=True)
-
-print(f"Not returned kits: {len(filtered)}")
-
-filtered.to_excel(OUTPUT_FILE, index=False, sheet_name=SHEET_NAME)
-
-# Formatting
-wb = load_workbook(OUTPUT_FILE)
-ws = wb[SHEET_NAME]
-
-header_fill = PatternFill("solid", start_color="375623") # dark green
-header_font = Font(bold=True, color="FFFFFF", name="Arial", size=10)
-row_font = Font(name="Arial", size=10)
-ret_fill = PatternFill("solid", start_color="E2EFDA") # light green highlight for Date Ret
-
-thin = Side(style="thin", color="000000")
-border = Border(left=thin, right=thin, top=thin, bottom=thin)
-
-headers = [cell.value for cell in ws[1]]
-
-for cell in ws[1]:
- cell.fill = header_fill
- cell.font = header_font
- cell.alignment = Alignment(horizontal="center", vertical="center", wrap_text=False)
- cell.border = border
-
-for row in ws.iter_rows(min_row=2, max_row=ws.max_row):
- for cell in row:
- col_name = headers[cell.column - 1] if cell.column <= len(headers) else None
- cell.font = row_font
- cell.border = border
- cell.alignment = Alignment(horizontal="center")
- if col_name in DATE_COLUMNS:
- cell.number_format = "DD-MMM-YYYY"
- if col_name == "Max Visit Date":
- cell.fill = ret_fill
-
-for cell in ws[1]:
- width = COLUMN_WIDTHS.get(cell.value, 14)
- ws.column_dimensions[get_column_letter(cell.column)].width = width
-
-ws.auto_filter.ref = ws.dimensions
-ws.freeze_panes = "A2"
-
-wb.save(OUTPUT_FILE)
-print(f"Saved: {OUTPUT_FILE} (sheet: '{SHEET_NAME}')")
diff --git a/IWRS/backfill_mysql_to_mongo.py b/IWRS/backfill_mysql_to_mongo.py
deleted file mode 100644
index cc52b2e..0000000
--- a/IWRS/backfill_mysql_to_mongo.py
+++ /dev/null
@@ -1,272 +0,0 @@
-"""
-Jednorázový backfill historických dat z MySQL do MongoDB.
-
-Pro každou snapshotovanou tabulku:
- - všechny řádky všech import_id → snapshot kolekce
- - řádky z MAX(import_id) per studie → hlavní kolekce (replace_one upsert)
-
-Pro idempotentní tabulky (notifications, destruction):
- - všechno → hlavní kolekce (replace_one upsert)
-
-Notifikace jsou už v Mongo z parse_notifications_to_mongo.py — přeskočí se.
-"""
-
-import os
-import sys
-import datetime
-
-import mysql.connector
-from pymongo import ReplaceOne
-
-sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
-from common.mongo_writer import get_db, ensure_indexes, MONGO_DB
-
-sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "Patients"))
-import db_config
-
-
-def conn():
- return mysql.connector.connect(
- host=db_config.DB_HOST, port=db_config.DB_PORT,
- user=db_config.DB_USER, password=db_config.DB_PASSWORD,
- database=db_config.DB_NAME,
- )
-
-
-def dict_rows(cursor):
- cols = [d[0] for d in cursor.description]
- for row in cursor:
- yield dict(zip(cols, row))
-
-
-def to_mongo_date(v):
- if isinstance(v, datetime.datetime):
- return v
- if isinstance(v, datetime.date):
- return datetime.datetime(v.year, v.month, v.day)
- return v
-
-
-def normalize(doc):
- return {k: to_mongo_date(v) for k, v in doc.items() if v is not None}
-
-
-# ── 1. iwrs_imports → iwrs_imports ───────────────────────────────────────────
-
-def backfill_imports():
- print("[iwrs_imports]")
- c = conn(); cur = c.cursor()
- cur.execute("SELECT import_id, study, imported_at, source_file, report_type FROM iwrs_import")
- db = get_db()
- ops = []
- for r in dict_rows(cur):
- d = normalize(r)
- d["_id"] = d["import_id"]
- ops.append(ReplaceOne({"_id": d["_id"]}, d, upsert=True))
- if ops:
- db.iwrs_imports.bulk_write(ops, ordered=False)
- print(f" -> {len(ops)} import logu")
- cur.close(); c.close()
-
-
-# ── 2. subject_summary (UCO + MDD sjednoceno) ────────────────────────────────
-
-UCO_TABLE = "iwrs_uco3001_subject_summary"
-MDD_TABLE = "iwrs_mdd3003_subject_summary"
-
-
-def backfill_subject_summary():
- print("[iwrs_subject_summary]")
- db = get_db()
- # zjisti import_id → study mapování
- c = conn(); cur = c.cursor()
- cur.execute("SELECT import_id, study, imported_at FROM iwrs_import")
- import_meta = {r[0]: {"study": r[1], "imported_at": r[2]} for r in cur.fetchall()}
- cur.close(); c.close()
-
- total_snap = 0
- total_main = 0
-
- for table, study in [(UCO_TABLE, "77242113UCO3001"), (MDD_TABLE, "42847922MDD3003")]:
- c = conn(); cur = c.cursor()
- cur.execute(f"SELECT * FROM {table}")
- all_rows = list(dict_rows(cur))
- cur.close(); c.close()
-
- # MAX import_id per studie (pro hlavní kolekci)
- import_ids = [r["import_id"] for r in all_rows if r.get("import_id") is not None]
- if not import_ids:
- continue
- max_import = max(import_ids)
-
- # snapshoty: každý řádek → iwrs_subject_summary_snapshots
- snap_docs = []
- main_ops = []
- for r in all_rows:
- doc = normalize(r)
- doc.pop("id", None) # MySQL autoincrement nezachováváme
- doc["study"] = study
- subject = doc.get("subject")
- if not subject:
- continue
- natural = f"{study}:{subject}"
-
- snap = dict(doc)
- snap["natural_id"] = natural
- meta = import_meta.get(doc.get("import_id"), {})
- snap["imported_at"] = meta.get("imported_at")
- snap_docs.append(snap)
-
- if doc["import_id"] == max_import:
- main = dict(doc)
- main["_id"] = natural
- main["last_import_id"] = max_import
- main["last_imported_at"] = meta.get("imported_at")
- main_ops.append(ReplaceOne({"_id": natural}, main, upsert=True))
-
- if snap_docs:
- db.iwrs_subject_summary_snapshots.insert_many(snap_docs, ordered=False)
- total_snap += len(snap_docs)
- if main_ops:
- db.iwrs_subject_summary.bulk_write(main_ops, ordered=False)
- total_main += len(main_ops)
- print(f" {study}: snap={len(snap_docs)} main={len(main_ops)}")
-
- print(f" TOTAL snap={total_snap} main={total_main}")
-
-
-# ── 3. visits, shipments, items, inventory (per import_id) ───────────────────
-
-def backfill_per_import(mysql_table, main_coll, snap_coll, id_fn,
- drop_cols=("id",)):
- print(f"[{mysql_table} -> {main_coll}/{snap_coll}]")
- db = get_db()
- c = conn(); cur = c.cursor()
-
- # import_id metadata
- cur.execute("SELECT import_id, imported_at FROM iwrs_import")
- import_meta = {r[0]: r[1] for r in cur.fetchall()}
-
- # MAX import_id per studie
- cur.execute(f"SELECT study, MAX(import_id) FROM {mysql_table} GROUP BY study")
- max_per_study = {r[0]: r[1] for r in cur.fetchall()}
-
- cur.execute(f"SELECT * FROM {mysql_table}")
- all_rows = list(dict_rows(cur))
- cur.close(); c.close()
-
- snap_docs = []
- main_ops = []
- seen_main = set()
- for r in all_rows:
- doc = normalize(r)
- for col in drop_cols:
- doc.pop(col, None)
- natural = id_fn(doc)
- if not natural:
- continue
- imp_at = import_meta.get(doc.get("import_id"))
-
- snap = dict(doc)
- snap["natural_id"] = natural
- snap["imported_at"] = imp_at
- snap_docs.append(snap)
-
- study = doc.get("study")
- if study and doc.get("import_id") == max_per_study.get(study):
- if natural in seen_main:
- continue
- seen_main.add(natural)
- main = dict(doc)
- main["_id"] = natural
- main["last_import_id"] = doc["import_id"]
- main["last_imported_at"] = imp_at
- main_ops.append(ReplaceOne({"_id": natural}, main, upsert=True))
-
- if snap_docs:
- db[snap_coll].insert_many(snap_docs, ordered=False)
- if main_ops:
- db[main_coll].bulk_write(main_ops, ordered=False)
- print(f" snap={len(snap_docs)} main={len(main_ops)}")
-
-
-def visit_id(doc):
- s, sub = doc.get("study"), doc.get("subject")
- if not s or not sub:
- return None
- key = doc.get("irt_transaction_no")
- if key is None:
- sd = doc.get("scheduled_date")
- key = sd.strftime("%Y%m%d") if sd else "noidx"
- desc = (doc.get("irt_transaction_description") or "").replace(" ", "_")[:30]
- return f"{s}:{sub}:{key}:{desc}"
-
-
-def shipment_id_(doc):
- return doc.get("shipment_id")
-
-
-def shipment_item_id(doc):
- s, m = doc.get("shipment_id"), doc.get("medication_id")
- return f"{s}:{m}" if s and m else None
-
-
-def inventory_id(doc):
- s, m = doc.get("site"), doc.get("medication_id")
- return f"{s}:{m}" if s and m else None
-
-
-# ── 4. destruction (idempotentní, jen do main) ───────────────────────────────
-
-def backfill_destruction():
- print("[iwrs_destruction]")
- db = get_db()
- c = conn(); cur = c.cursor()
- cur.execute("SELECT * FROM iwrs_destruction")
- rows = list(dict_rows(cur))
- cur.close(); c.close()
- ops = []
- seen = set()
- for r in rows:
- doc = normalize(r)
- doc.pop("id", None)
- basket, med = doc.get("basket_id"), doc.get("medication_id")
- if not basket or not med:
- continue
- nid = f"{basket}:{med}"
- if nid in seen:
- continue
- seen.add(nid)
- doc["_id"] = nid
- ops.append(ReplaceOne({"_id": nid}, doc, upsert=True))
- if ops:
- db.iwrs_destruction.bulk_write(ops, ordered=False)
- print(f" -> {len(ops)} destrukci")
-
-
-# ── main ─────────────────────────────────────────────────────────────────────
-
-def main():
- print(f"Cilova DB: {MONGO_DB}")
- ensure_indexes()
- backfill_imports()
- backfill_subject_summary()
- backfill_per_import("iwrs_subject_visits", "iwrs_visits", "iwrs_visits_snapshots", visit_id)
- backfill_per_import("iwrs_shipments", "iwrs_shipments", "iwrs_shipments_snapshots", shipment_id_)
- backfill_per_import("iwrs_shipment_items", "iwrs_shipment_items", "iwrs_shipment_items_snapshots", shipment_item_id)
- backfill_per_import("iwrs_inventory", "iwrs_inventory", "iwrs_inventory_snapshots", inventory_id)
- backfill_destruction()
-
- # finalni statistika
- db = get_db()
- print("\nFINALNI STAV V MONGO:")
- for coll in ["iwrs_imports","iwrs_subject_summary","iwrs_visits","iwrs_notifications",
- "iwrs_shipments","iwrs_shipment_items","iwrs_inventory","iwrs_destruction",
- "iwrs_subject_summary_snapshots","iwrs_visits_snapshots",
- "iwrs_shipments_snapshots","iwrs_shipment_items_snapshots","iwrs_inventory_snapshots"]:
- n = db[coll].count_documents({})
- print(f" {coll:42s} {n}")
-
-
-if __name__ == "__main__":
- main()
diff --git a/IWRS/Patients/download_subject_details.py b/IWRS/download_subject_details.py
similarity index 99%
rename from IWRS/Patients/download_subject_details.py
rename to IWRS/download_subject_details.py
index a156af0..410b857 100644
--- a/IWRS/Patients/download_subject_details.py
+++ b/IWRS/download_subject_details.py
@@ -7,7 +7,7 @@ import json
import sys
import pandas as pd
-sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from common.mongo_writer import get_db
# ── CONFIG ──────────────────────────────────────────────────────────────────
diff --git a/IWRS/Patients/import_notifications_to_mongo.py b/IWRS/import_notifications_to_mongo.py
similarity index 97%
rename from IWRS/Patients/import_notifications_to_mongo.py
rename to IWRS/import_notifications_to_mongo.py
index 651dabe..85c92ba 100644
--- a/IWRS/Patients/import_notifications_to_mongo.py
+++ b/IWRS/import_notifications_to_mongo.py
@@ -20,11 +20,10 @@ import datetime
from bson.binary import Binary
-sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from common.mongo_writer import get_db, to_date
-# parsery z původního skriptu
-from parse_notifications_to_mongo import (
+from notification_parsers import (
parse_kv_lines, parse_medication_table, to_date as parse_to_date,
to_datetime as parse_to_datetime,
)
diff --git a/IWRS/Patients/import_to_mongo.py b/IWRS/import_patients_mongo.py
similarity index 97%
rename from IWRS/Patients/import_to_mongo.py
rename to IWRS/import_patients_mongo.py
index a93d542..e070f5c 100644
--- a/IWRS/Patients/import_to_mongo.py
+++ b/IWRS/import_patients_mongo.py
@@ -1,7 +1,8 @@
"""
-Import Patients dat (subject_summary, visits) z XLSX do MongoDB.
+import_patients_mongo.py — import Patients dat (subject_summary, visits) z XLSX do MongoDB.
-Volá se z IWRS/Patients/run_all.py po stažení reportů. Hlavní kolekce + snapshoty.
+Dříve Patients/import_to_mongo.py. Volá se z import_patients.py
+(orchestrátor IWRS/run_all). Hlavní kolekce + snapshoty.
"""
import os
@@ -12,7 +13,7 @@ import datetime
import pandas as pd
-sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from common.mongo_writer import (
to_str, to_int, to_float, to_date,
ensure_indexes, log_import, bulk_upsert_with_snapshot,
diff --git a/IWRS/notification_parsers.py b/IWRS/notification_parsers.py
new file mode 100644
index 0000000..97d5d4b
--- /dev/null
+++ b/IWRS/notification_parsers.py
@@ -0,0 +1,110 @@
+"""
+notification_parsers.py — parsery textů IWRS notifikací.
+Verze: 1.0 | Datum: 2026-06-10
+
+Vyčleněno z bývalého Patients/parse_notifications_to_mongo.py (MySQL→Mongo
+legacy skript) — zůstaly jen čisté parsovací funkce, které používá
+import_notifications_to_mongo.py. Žádná závislost na MySQL.
+"""
+
+import re
+import datetime
+
+
+def parse_kv_lines(text):
+ """Vytáhne všechny řádky typu 'Klíč: Hodnota' do dictu.
+ Když je hodnota za dvojtečkou prázdná, vezme se první neprázdný následující řádek."""
+ out = {}
+ lines = [l.strip() for l in text.splitlines()]
+ pending_key = None
+ for line in lines:
+ # čekáme na hodnotu pro klíč z předchozího řádku
+ if pending_key is not None:
+ if not line:
+ continue
+ if ":" not in line:
+ out.setdefault(pending_key, line)
+ pending_key = None
+ continue
+ # další řádek je sám "Klíč: Hodnota" → zahodíme pending a zpracujeme normálně
+ pending_key = None
+
+ if not line or ":" not in line:
+ continue
+ if line.lower().startswith("http"):
+ continue
+ key, _, val = line.partition(":")
+ key = key.strip()
+ val = val.strip()
+ if not key or (" " in key and len(key.split()) > 8):
+ continue
+ if not val:
+ pending_key = key
+ continue
+ out.setdefault(key, val)
+ return out
+
+
+DATE_RE = re.compile(r"^\d{2}-[A-Z][a-z]{2}-\d{4}$")
+DATETIME_RE = re.compile(r"^(\d{2}-[A-Z][a-z]{2}-\d{4})\s+(\d{2}:\d{2}:\d{2})$")
+
+
+def to_date(s):
+ if not s:
+ return None
+ s = s.strip()
+ if DATE_RE.match(s):
+ try:
+ return datetime.datetime.strptime(s, "%d-%b-%Y")
+ except ValueError:
+ return None
+ return None
+
+
+def to_datetime(s):
+ if not s:
+ return None
+ s = re.sub(r"\s+", " ", s.strip())
+ m = DATETIME_RE.match(s)
+ if m:
+ try:
+ return datetime.datetime.strptime(f"{m.group(1)} {m.group(2)}", "%d-%b-%Y %H:%M:%S")
+ except ValueError:
+ return None
+ return None
+
+
+MED_ROW_RE = re.compile(
+ r"(?P\d{7})\s*[\s\n]*"
+ r"(?P[A-Za-z][A-Za-z0-9 /+\-]+?)\s*[\s\n]*"
+ r"(?P[A-Z0-9]{5,10})\s*[\s\n]*"
+ r"(?P\d{2}-[A-Z][a-z]{2}-\d{4})"
+)
+
+
+def parse_medication_table(text):
+ """Najde záznamy medikace (med_no, med_type, lot, expirace) v textu.
+ Pracuje s oběma formáty (UCO3001 multiline i MDD3003 concatenated)."""
+ rows = []
+ # zkomprimuj whitespace pro snadnější regex
+ compact = re.sub(r"\s+", " ", text)
+ for m in MED_ROW_RE.finditer(compact):
+ med_type = m.group("type").strip()
+ # uřízni nadbytečné koncové fragmenty
+ med_type = re.sub(r"\s+(Packaged|Lot|Expiration|No|Date|Medication).*$", "", med_type).strip()
+ rows.append({
+ "medication_no": m.group("no"),
+ "medication_type": med_type,
+ "lot_no": m.group("lot"),
+ "expiration_date": to_date(m.group("exp")),
+ })
+ # dedupe
+ seen = set()
+ unique = []
+ for r in rows:
+ key = (r["medication_no"], r["lot_no"])
+ if key in seen:
+ continue
+ seen.add(key)
+ unique.append(r)
+ return unique
diff --git a/IWRS/reports.json b/IWRS/reports.json
deleted file mode 100644
index c66e6eb..0000000
--- a/IWRS/reports.json
+++ /dev/null
@@ -1,23 +0,0 @@
-[
- {"name": "Drug Accountability Form - Multiple Subjects", "href": "/report/drug_accountability_form_multiple_subjects"},
- {"name": "Drug Accountability Form - Single Subject", "href": "/report/drug_accountability_form_single_subject"},
- {"name": "Janssen Pharmaceuticals IP Destruction Form", "href": "/report/ip_destruction_form"},
- {"name": "On-Site Drug Inventory and Accountability Details Form", "href": "/report/onsite_inventory_detail"},
- {"name": "On-Site Drug Inventory Form", "href": "/report/onsite_drug_inventory_form"},
- {"name": "Location Summary Report", "href": "/report/country_summary_report"},
- {"name": "Site Detail Report", "href": "/report/site_detail_report"},
- {"name": "Study Sites Report", "href": "/report/study_sites_report"},
- {"name": "Site Inventory Detail Report", "href": "/report/site_inventory_detail"},
- {"name": "Site Inventory Summary Report", "href": "/report/site_inventory_summary"},
- {"name": "Subject Data Changes Report", "href": "/report/patient_data_changes_report"},
- {"name": "Subject Detail Report", "href": "/report/patient_detail_report"},
- {"name": "Subject Summary Report", "href": "/report/patient_summary_report"},
- {"name": "Subject Visit Summary Report", "href": "/report/patient_visit_summary"},
- {"name": "Shipment Details Report", "href": "/report/shipment_details_report"},
- {"name": "Shipments Report", "href": "/report/shipments_report"},
- {"name": "Cohort History Report", "href": "/report/cohort_history_report"},
- {"name": "Cohort Summary Report", "href": "/report/cohort_summary_report"},
- {"name": "Site Activations Report", "href": "/report/site_activation_pivot"},
- {"name": "User Login History", "href": "/report/user_logins"},
- {"name": "Users List", "href": "/report/users"}
-]
diff --git a/IWRS/run_all_v1.0.md b/IWRS/run_all_v1.0.md
deleted file mode 100644
index da699b4..0000000
--- a/IWRS/run_all_v1.0.md
+++ /dev/null
@@ -1,110 +0,0 @@
-# run_all_v1.0.py — IWRS: kompletní pipeline Pacienti + Léky
-
-**Verze:** 1.0 | **Datum:** 2026-06-10
-
-Jeden vstupní skript na úrovni `IWRS/`, který stáhne z janssen.4gclinical.com
-a naimportuje do MongoDB (db `studie`) data pacientů i léků pro obě studie
-(77242113UCO3001, 42847922MDD3003). Nahrazuje dřívější `Drugs/run_all.py`
-a `Patients/download_all.py` + `Patients/import_all.py` (přesunuty do `Trash/`).
-
-## Tok souborů
-
-```
-IWRS/Incoming/ ← sem padá vše stažené (pacienti i léky, datumované názvy)
-IWRS/Incoming/Processed/ ← sem se přesouvá po úspěšném importu
-```
-
-- Při chybě importu soubor **zůstává v Incoming/** a zpracuje se při příštím běhu.
-- Import jde vždy **nejstarší soubor napřed** (mtime) — chronologická správnost snapshotů.
-- Kolize jména v Processed/ → přepíše se (Mongo už data má, soubor je jen archiv).
-- Adresář `IWRS/Incoming/` je v `.gitignore` (stejně jako dříve `Patients/Incoming/`).
-- Původní adresáře `Drugs/xls_*` zůstávají zmrazené na místě jako archiv — nový kód je nepoužívá.
-
-## Názvy souborů v Incoming/
-
-| Typ | Vzor |
-|---|---|
-| Subject Summary | `YYYY-MM-DD {study} Subject Summary Report.xlsx` |
-| Subject Detail | `YYYY-MM-DD {study} {subject} Subject Detail.xlsx` |
-| Notifikace | `{datum}_{study}_{subject}_{label}.pdf` + `.json` |
-| Onsite Inventory | `YYYY-MM-DD {study} Onsite Inventory {site}.xlsx` |
-| IP Destruction | `YYYY-MM-DD {study} IP Destruction {basket}.xlsx` |
-| Shipments Report | `YYYY-MM-DD {study} Shipments Report.xlsx` |
-| Shipment Details | `YYYY-MM-DD {study} Shipment Details {shipment_id}.xlsx` |
-
-Při kolizi (druhý běh ve stejný den) se před příponu přidá ` HHMM`.
-Metadata (site, basket, study) se při importu čtou primárně z **obsahu** souboru;
-z názvu se bere jen `shipment_id` u Shipment Details.
-
-## Průběh
-
-### Fáze 1 — stahování (2 přihlášení, per studie jedna browser session)
-
-1. Login + výběr studie (`common/iwrs_portal.py`)
-2. **Pacienti** (`Patients/download_patients.py`):
- - Subject Summary Report
- - per subjekt: Subject Detail XLSX + notifikace PDF+JSON (stahují se jen
- notifikace, jejichž `pk` ještě není v Mongo `iwrs_notifications`)
-3. **Léky** (`Drugs/download_drugs.py`):
- - Onsite Inventory — všechna centra, vždy znovu
- - IP Destruction — přeskočí košíky už importované v `iwrs_destruction`
- (destrukce je immutable); dříve se přeskakovalo podle existence souboru
- - Shipments Report — vždy znovu
- - Shipment Details — jen CZ zásilky; přeskočí zásilky, jejichž položky
- jsou v `iwrs_shipment_items` se statusem RECEIVED (finální stav);
- dříve „soubor existuje a status RECEIVED“. CANCELLED zásilky se stahují
- při každém běhu (záměrně zachováno z původní verze).
-
-### Fáze 2 — import (po stažení obou studií)
-
-1. `ensure_indexes()` (jednou)
-2. **Pacienti** (`Patients/import_patients.py`): summary → detaily → notifikace;
- per soubor, po úspěchu přesun do Processed/
-3. **Léky** (`Drugs/import_drugs.py`): jeden `import_id` per studie a běh;
- parsuje všechny čekající soubory (nejstarší napřed, poslední vyhrává per `_id`),
- pak hromadný zápis:
- - `iwrs_shipments`, `iwrs_shipment_items`, `iwrs_inventory` — upsert + snapshot
- - `iwrs_destruction` — upsert bez snapshotu
- Po úspěšném zápisu se zparsované soubory přesunou do Processed/;
- soubor s chybou parsování zůstává v Incoming/.
-
-## Použití
-
-```
-python run_all_v1.0.py # vše (download + import, obě studie)
-python run_all_v1.0.py --download-only # jen stažení do Incoming/
-python run_all_v1.0.py --import-only # jen import čekajících souborů
-python run_all_v1.0.py --only-patients # jen pacientská část
-python run_all_v1.0.py --only-drugs # jen léková část
-python run_all_v1.0.py --study 42847922MDD3003 # jen jedna studie
-```
-
-Prohlížeč běží s `headless=False` (viditelné okno) jako dosud.
-Moduly `import_patients.py` a `import_drugs.py` lze spustit i samostatně.
-
-## Mapa modulů
-
-```
-IWRS/
- run_all_v1.0.py ← vstupní skript (CLI, orchestrace)
- common/
- iwrs_portal.py ← BASE_URL, credentials, login(page, study)
- paths.py ← INCOMING/PROCESSED, unique_path, move_done, sorted_by_mtime
- mongo_writer.py ← beze změny (konvertory, upserty, snapshoty, import log)
- Patients/
- download_patients.py ← summary + delegace na download_subject_details.run()
- import_patients.py ← logika z bývalého import_all.py, nové cesty
- download_subject_details.py, import_to_mongo.py,
- import_notifications_to_mongo.py, parse_notifications_to_mongo.py ← beze změny
- Trash/download_all.py, Trash/import_all.py ← nahrazeno
- Drugs/
- download_drugs.py ← 4 typy reportů → Incoming/, skip-logika přes Mongo
- import_drugs.py ← parsery z bývalého import_to_mongo.py, čte Incoming/
- Trash/run_all.py, Trash/import_to_mongo.py ← nahrazeno
-```
-
-## Jednorázová migrace (provedeno 2026-06-10)
-
-- `Patients/Incoming/Zpracováno/` (1343 souborů) → `IWRS/Incoming/Processed/`
-- `.gitignore`: `IWRS/Patients/Incoming/` → `IWRS/Incoming/`
-- Staré vstupní skripty → `Trash/` (viz mapa výše)
diff --git a/IWRS/run_all_v1.0.py b/IWRS/run_all_v1.0.py
deleted file mode 100644
index c17a3ce..0000000
--- a/IWRS/run_all_v1.0.py
+++ /dev/null
@@ -1,147 +0,0 @@
-"""
-================================================================================
- run_all_v1.0.py — IWRS: kompletní pipeline Pacienti + Léky (obě studie)
- Verze: 1.0
- Datum: 2026-06-10
-================================================================================
-
-Stáhne z janssen.4gclinical.com a naimportuje do MongoDB (db `studie`):
-
- Pacienti: Subject Summary, Subject Details, notifikace (PDF+JSON)
- Léky: Onsite Inventory, IP Destruction, Shipments Report, Shipment Details
-
-Tok souborů: vše se stahuje do IWRS/Incoming/, po úspěšném importu se přesouvá
-do IWRS/Incoming/Processed/. Při chybě soubor zůstává v Incoming/ a zpracuje
-se při příštím běhu.
-
-Přihlášení: 2× (jednou per studie) — studie se vybírá až po přihlášení, takže
-jedna browser session stáhne pacienty i léky pro jednu studii.
-
-Použití:
- python run_all_v1.0.py # vše (download + import, obě studie)
- python run_all_v1.0.py --download-only # jen stažení do Incoming/
- python run_all_v1.0.py --import-only # jen import čekajících souborů
- python run_all_v1.0.py --only-patients # jen pacientská část
- python run_all_v1.0.py --only-drugs # jen léková část
- python run_all_v1.0.py --study 42847922MDD3003 # jen jedna studie
-
-Detaily v run_all_v1.0.md.
-"""
-
-import os
-import sys
-import argparse
-import traceback
-
-BASE_DIR = os.path.dirname(os.path.abspath(__file__))
-for _p in (os.path.join(BASE_DIR, "Drugs"), os.path.join(BASE_DIR, "Patients"), BASE_DIR):
- if _p not in sys.path:
- sys.path.insert(0, _p)
-
-from playwright.sync_api import sync_playwright
-
-from common.iwrs_portal import login
-from common.paths import STUDIES, INCOMING_DIR, PROCESSED_DIR, ensure_dirs
-from common.mongo_writer import ensure_indexes
-
-import download_patients
-import import_patients
-import download_drugs
-import import_drugs
-
-
-def download_phase(studies, do_patients, do_drugs):
- with sync_playwright() as p:
- for study in studies:
- print(f"\n{'='*60}")
- print(f"[{study}] STAHOVÁNÍ")
- print(f"{'='*60}")
-
- browser = p.chromium.launch(headless=False)
- context = browser.new_context(accept_downloads=True)
- page = context.new_page()
-
- try:
- print(" Přihlášení...")
- login(page, study)
-
- if do_patients:
- print(f"\n ── PACIENTI [{study}] ──")
- try:
- download_patients.run(page, study)
- except Exception as e:
- print(f" CHYBA při stahování pacientů: {e}")
- traceback.print_exc()
-
- if do_drugs:
- print(f"\n ── LÉKY [{study}] ──")
- try:
- download_drugs.run(page, study)
- except Exception as e:
- print(f" CHYBA při stahování léků: {e}")
- traceback.print_exc()
-
- except Exception as e:
- print(f" CHYBA (login/session): {e}")
- traceback.print_exc()
- finally:
- browser.close()
-
-
-def import_phase(studies, do_patients, do_drugs):
- print(f"\n{'='*60}")
- print("IMPORT DO MongoDB")
- print(f"{'='*60}")
- ensure_indexes()
-
- if do_patients:
- try:
- import_patients.run(studies)
- except Exception as e:
- print(f" CHYBA při importu pacientů: {e}")
- traceback.print_exc()
-
- if do_drugs:
- try:
- import_drugs.run(studies)
- except Exception as e:
- print(f" CHYBA při importu léků: {e}")
- traceback.print_exc()
-
-
-def main():
- ap = argparse.ArgumentParser(
- description="IWRS pipeline: stažení + import pacientů a léků (obě studie)")
- ap.add_argument("--download-only", action="store_true", help="jen stažení do Incoming/")
- ap.add_argument("--import-only", action="store_true", help="jen import čekajících souborů")
- ap.add_argument("--only-patients", action="store_true", help="jen pacientská část")
- ap.add_argument("--only-drugs", action="store_true", help="jen léková část")
- ap.add_argument("--study", choices=STUDIES, help="jen jedna studie")
- args = ap.parse_args()
-
- if args.download_only and args.import_only:
- ap.error("--download-only a --import-only nelze kombinovat")
- if args.only_patients and args.only_drugs:
- ap.error("--only-patients a --only-drugs nelze kombinovat")
-
- studies = [args.study] if args.study else STUDIES
- do_patients = not args.only_drugs
- do_drugs = not args.only_patients
-
- ensure_dirs()
-
- if not args.import_only:
- download_phase(studies, do_patients, do_drugs)
-
- if not args.download_only:
- import_phase(studies, do_patients, do_drugs)
-
- print(f"\n{'='*60}")
- print("Vše hotovo.")
- print(f" Incoming: {INCOMING_DIR}")
- print(f" Processed: {PROCESSED_DIR}")
- print(f"{'='*60}")
-
-
-if __name__ == "__main__":
- main()