This commit is contained in:
2026-05-21 17:12:18 +02:00
parent 15eed6b4c3
commit c434f270b0
30 changed files with 2823 additions and 21 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"sel_status": "Aktivní", "sel_status": "Aktivní",
"sel_proto": "42847922MDD3003", "sel_proto": "77242113UCO3001",
"sel_role": [ "sel_role": [
"Principal Investigator", "Principal Investigator",
"Sub-Investigator", "Sub-Investigator",
+65 -19
View File
@@ -355,12 +355,21 @@ def load_patients(cursor, study):
mid = cursor.fetchone()["mid"] mid = cursor.fetchone()["mid"]
if mid is None: if mid is None:
raise RuntimeError(f"Žádná data v MySQL pro pacienty {study}") raise RuntimeError(f"Žádná data v MySQL pro pacienty {study}")
extra_cols = ""
if study == "77242113UCO3001":
extra_cols = """
rescreened_subject AS `Rescreened Subject`,
adt_ir AS `ADT-IR`,
three_or_more_advanced_therapies AS `3+ Adv. Therapies`,
only_oral_5asa_compounds AS `Only 5-ASA`,
ustekinumab AS `Ustekinumab`,
isolated_proctitis AS `Isolated Proctitis`,"""
sql = f""" sql = f"""
SELECT SELECT
subject AS `Subject`, subject AS `Subject`,
investigator AS `Investigator`, investigator AS `Investigator`,
age AS `Subject's age collection`, age AS `Subject's age collection`,
cohort_per_irt AS `Cohort per IRT`, cohort_per_irt AS `Cohort per IRT`,{extra_cols}
irt_subject_status AS `IRT Subject Status`, irt_subject_status AS `IRT Subject Status`,
last_irt_transaction AS `Last Recorded IRT Transaction`, last_irt_transaction AS `Last Recorded IRT Transaction`,
next_irt_transaction AS `Next Expected IRT Transaction`, next_irt_transaction AS `Next Expected IRT Transaction`,
@@ -403,15 +412,30 @@ def _write_prehled(wb, df_raw, study):
ws = wb.create_sheet("Přehled", 0) ws = wb.create_sheet("Přehled", 0)
ws.sheet_view.showGridLines = False ws.sheet_view.showGridLines = False
ws.merge_cells("A1:H1") 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 = ws["A1"]
title.value = f"Subject Summary — {study} ({date.today().strftime('%d-%b-%Y')})" title.value = f"Subject Summary — {study} ({date.today().strftime('%d-%b-%Y')})"
title.font = Font(name="Arial", bold=True, size=12, color="1F4E79") title.font = Font(name="Arial", bold=True, size=12, color="1F4E79")
title.alignment = Alignment(horizontal="left", vertical="center") title.alignment = Alignment(horizontal="left", vertical="center")
ws.row_dimensions[1].height = 22 ws.row_dimensions[1].height = 22
display_headers = ["Subject", "Investigator", "Věk", "Cohort", "Status", "Last IRT", "Next Visit", "Next Date"]
col_widths = [14, 22, 6, 12, 14, 12, 12, 13]
for c, (h, w) in enumerate(zip(display_headers, col_widths), 1): for c, (h, w) in enumerate(zip(display_headers, col_widths), 1):
cell = ws.cell(row=2, column=c, value=h) cell = ws.cell(row=2, column=c, value=h)
cell.font = _PAT_HEADER_FONT cell.font = _PAT_HEADER_FONT
@@ -421,16 +445,28 @@ def _write_prehled(wb, df_raw, study):
ws.column_dimensions[get_column_letter(c)].width = w ws.column_dimensions[get_column_letter(c)].width = w
ws.row_dimensions[2].height = 18 ws.row_dimensions[2].height = 18
display = pd.DataFrame({ base = {
"Subject": df_raw["Subject"].fillna(""), "Subject": df_raw["Subject"].fillna(""),
"Investigator": df_raw["Investigator"].fillna(""), "Investigator": df_raw["Investigator"].fillna(""),
"Věk": df_raw["Subject's age collection"].apply(lambda v: "" if pd.isna(v) else int(v)), "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), "Cohort": df_raw["Cohort per IRT"].apply(_simplify_cohort),
"Status": df_raw["IRT Subject Status"].fillna(""), }
"Last IRT": df_raw["Last Recorded IRT Transaction"].fillna(""), if is_uco:
"Next Visit": df_raw["Next Expected IRT Transaction"].fillna(""), base.update({
"Next Date": df_raw["Next Expected IRT Transaction Date [Local]"].apply(_fmt_date), "Rescreened": df_raw["Rescreened Subject"].fillna(""),
}).sort_values("Subject").reset_index(drop=True) "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(): for r_idx, row in display.iterrows():
excel_row = r_idx + 3 excel_row = r_idx + 3
@@ -440,17 +476,14 @@ def _write_prehled(wb, df_raw, study):
is_adolescent = row["Cohort"] == "Adolescent" is_adolescent = row["Cohort"] == "Adolescent"
fill = _PAT_EVEN_FILL if r_idx % 2 == 0 else _PAT_ODD_FILL fill = _PAT_EVEN_FILL if r_idx % 2 == 0 else _PAT_ODD_FILL
values = [row["Subject"], row["Investigator"], row["Věk"], for c_idx, val in enumerate(row, 1):
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 = ws.cell(row=excel_row, column=c_idx, value=val if val != "" else None)
cell.fill = fill cell.fill = fill
cell.border = _PAT_BORDER cell.border = _PAT_BORDER
cell.alignment = _PAT_CENTER if c_idx == 3 else _PAT_LEFT cell.alignment = _PAT_CENTER if (c_idx == 3 or c_idx in flag_cols) else _PAT_LEFT
if is_failed: if is_failed:
cell.font = _PAT_STRIKE_FONT cell.font = _PAT_STRIKE_FONT
elif c_idx == 5 and is_randomized: elif c_idx == status_col and is_randomized:
cell.font = _PAT_BOLD_FONT cell.font = _PAT_BOLD_FONT
elif c_idx == 4 and is_adolescent: elif c_idx == 4 and is_adolescent:
cell.font = _PAT_ADOLESC_FONT cell.font = _PAT_ADOLESC_FONT
@@ -459,10 +492,10 @@ def _write_prehled(wb, df_raw, study):
ws.row_dimensions[excel_row].height = 16 ws.row_dimensions[excel_row].height = 16
ws.freeze_panes = "A3" ws.freeze_panes = "A3"
ws.auto_filter.ref = f"A2:H{len(display) + 2}" ws.auto_filter.ref = f"A2:{last_col}{len(display) + 2}"
def _write_next_visits(wb, df_raw, study): def _write_next_visits(wb, df_raw, study, visits_df=None):
ws = wb.create_sheet("Next Visits", 1) ws = wb.create_sheet("Next Visits", 1)
ws.sheet_view.showGridLines = False ws.sheet_view.showGridLines = False
@@ -491,6 +524,19 @@ def _write_next_visits(wb, df_raw, study):
"Datum": df_raw["Next Expected IRT Transaction Date [Local]"], "Datum": df_raw["Next Expected IRT Transaction Date [Local]"],
"Status": df_raw["IRT Subject Status"].fillna(""), "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["Datum"].notna()]
df = df[~df["Status"].str.contains("Screen Failed|Discontinued", na=False)] df = df[~df["Status"].str.contains("Screen Failed|Discontinued", na=False)]
df = df.sort_values("Datum").reset_index(drop=True) df = df.sort_values("Datum").reset_index(drop=True)
@@ -579,7 +625,7 @@ def create_study_report(study):
# ── pacienti (Přehled + Next Visits) na začátek ────────────────────────── # ── pacienti (Přehled + Next Visits) na začátek ──────────────────────────
_write_prehled(wb, df_patients, study) _write_prehled(wb, df_patients, study)
_write_next_visits(wb, df_patients, study) _write_next_visits(wb, df_patients, study, visits_df)
# ── pořadí listů: Patient Visits jako první ────────────────────────────── # ── pořadí listů: Patient Visits jako první ──────────────────────────────
names = wb.sheetnames names = wb.sheetnames
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff