160 lines
6.5 KiB
Python
160 lines
6.5 KiB
Python
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
"""
|
||
sipiq_reminder_v1.1.py
|
||
======================
|
||
Verze: 1.1
|
||
Datum: 2026-06-18
|
||
Autor: Claude Code (pro MUDr. Vladimíra Buzalku)
|
||
|
||
Změna proti v1.0
|
||
----------------
|
||
- BEZ hlavičky From — do .eml se NEdává odesílací účet; JNJ Outlook doplní odesílatele
|
||
z GAL a odešle přes JNJ Exchange (jediná možnost). Pravidlo 18JUN2026. v1.0 v TRASH.
|
||
|
||
Popis
|
||
-----
|
||
Vygeneruje .eml draft PŘIPOMÍNKY vyplnění SIPIQ (studie 77242113UCO3002 / DAWN) pro centra
|
||
na KROK 6 (SIPIQ odeslán), která dlouho nereagovala. Vřelá prosba o vyplnění co nejdříve
|
||
+ odkaz na dotazník specifický pro centrum (sipiq.link z investigators) + bezpečnostní věta
|
||
„pokud jste již vyplnil(a), ignorujte".
|
||
|
||
To = email + email2 z investigators (+ EXTRA_TO ručně, dd-proxy vynechány), Cc = koordinátorky,
|
||
from = vbuzalka@its.jnj.com, tělo BASE64 (Outlook čistě). Draft ≠ odesláno — STATUS „připomínka
|
||
SIPIQ odeslána" psát až po ověření odeslání.
|
||
|
||
Použití:
|
||
python sipiq_reminder_v1.1.py --names "Matous,Kojecky,Jungwirthova,Krížová,Mudr" # dry-run
|
||
python sipiq_reminder_v1.1.py --names "Matous,Kojecky,Jungwirthova,Krížová,Mudr" --apply
|
||
Mongo 192.168.1.76:27017, bez auth, pymongo.
|
||
"""
|
||
import argparse
|
||
import os
|
||
import re
|
||
import unicodedata
|
||
from datetime import date
|
||
from email.message import EmailMessage
|
||
|
||
from pymongo import MongoClient
|
||
|
||
OUT_DIR = r"u:\Dropbox\!!!Days\Downloads Z230\UploadToJNJ"
|
||
CC = "AKocourk@ITS.JNJ.com, EBartoso@its.jnj.com"
|
||
FROM = "vbuzalka@its.jnj.com"
|
||
SIG = ("MUDr. Vladimír BUZALKA<br>"
|
||
"ICON plc / Performing Local Trial Management Services for Janssen – Cilag s.r.o. "
|
||
"/ Global Clinical Operations<br>"
|
||
"Mobile: +420 775 735 276 / Fax: +420 227 012 284<br>"
|
||
"E-mail: vbuzalka@its.jnj.com, vladimir.buzalka@iconplc.com")
|
||
|
||
# Ručně doplněné adresy (z reply-checku / STATUS) — komu navíc poslat
|
||
EXTRA_TO = {
|
||
"Jungwirthova": ["a.jungwirthova@liberascientia.cz"],
|
||
"Krížová": ["Viera.Krizova@fnmh.cz"],
|
||
"Mudr": ["petr.pekny@nmskb.cz"], # dr. Pěkný komunikuje za dr. Mudra (dle STATUS)
|
||
}
|
||
|
||
CZ_MON = {1: "ledna", 2: "února", 3: "března", 4: "dubna", 5: "května", 6: "června",
|
||
7: "července", 8: "srpna", 9: "září", 10: "října", 11: "listopadu", 12: "prosince"}
|
||
MON = {m: i + 1 for i, m in enumerate(
|
||
["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"])}
|
||
|
||
|
||
def ascii_slug(s):
|
||
return "".join(c for c in unicodedata.normalize("NFKD", str(s or ""))
|
||
if not unicodedata.combining(c)).lower().replace(" ", "")
|
||
|
||
|
||
def sent_date_cz(status):
|
||
dates = []
|
||
for ln in (status or "").splitlines():
|
||
if "SIPIQ odesl" in ln:
|
||
m = re.match(r"(\d{2})([A-Za-z]{3})(\d{4})", ln.strip())
|
||
if m and m.group(2).upper() in MON:
|
||
dates.append(date(int(m.group(3)), MON[m.group(2).upper()], int(m.group(1))))
|
||
if not dates:
|
||
return None
|
||
d = max(dates)
|
||
return f"{d.day}. {CZ_MON[d.month]} {d.year}"
|
||
|
||
|
||
def is_female(prijmeni):
|
||
p = (prijmeni or "").lower()
|
||
return p.endswith("ová") or p.endswith("ova") or p.endswith("á")
|
||
|
||
|
||
def build_body(jmeno, prijmeni, sent_cz, link):
|
||
f = is_female(prijmeni)
|
||
greet = "Vážená paní doktorko," if f else "Vážený pane doktore,"
|
||
vyplnil = "vyplnila" if f else "vyplnil"
|
||
sent_part = f" dne <b>{sent_cz}</b>" if sent_cz else ""
|
||
return (
|
||
f'<p>{greet}</p>'
|
||
f'<p>dovoluji si zdvořile připomenout feasibility dotazník <b>SIPIQ</b> ke studii '
|
||
f'<b>77242113UCO3002 (DAWN)</b> (přípravek icotrokinra, ulcerózní kolitida), '
|
||
f'který jsme Vám zaslali{sent_part}.</p>'
|
||
f'<p><b>Velmi bychom ocenili jeho vyplnění co nejdříve</b> — je pro nás klíčový pro '
|
||
f'posouzení Vašeho centra a další postup. Níže najdete odkaz na dotazník specifický '
|
||
f'pro Vaše centrum:</p>'
|
||
f'<p><a href="{link}">{link}</a></p>'
|
||
f'<p>Pokud jste dotazník již {vyplnil}, přijměte prosím poděkování a tento e-mail '
|
||
f'ignorujte.</p>'
|
||
f'<p>V případě jakýchkoli dotazů (např. potíže s odkazem) jsem Vám plně k dispozici. '
|
||
f'Předem děkuji za Váš čas a vstřícnost.</p>'
|
||
f'<p>S pozdravem,<br>{SIG}</p>'
|
||
)
|
||
|
||
|
||
def main():
|
||
ap = argparse.ArgumentParser()
|
||
ap.add_argument("--names", required=True, help="příjmení čárkou (pi v investigators)")
|
||
ap.add_argument("--apply", action="store_true")
|
||
args = ap.parse_args()
|
||
|
||
db = MongoClient("mongodb://192.168.1.76:27017", serverSelectionTimeoutMS=8000).feasibility
|
||
names = [n.strip() for n in args.names.split(",") if n.strip()]
|
||
docs = list(db.investigators.find({"prijmeni": {"$in": names}}))
|
||
found = {d["prijmeni"] for d in docs}
|
||
for n in names:
|
||
if n not in found:
|
||
print(f" ! nenalezeno: {n}")
|
||
|
||
subject = "[Připomínka] 77242113UCO3002 (DAWN) — feasibility dotazník SIPIQ (prosba o vyplnění)"
|
||
done = []
|
||
for d in docs:
|
||
link = (d.get("sipiq") or {}).get("link")
|
||
if not link:
|
||
print(f" ! {d['prijmeni']}: chybí sipiq.link — přeskakuji"); continue
|
||
tos = [e for e in [d.get("email"), d.get("email2")] if e]
|
||
tos += EXTRA_TO.get(d["prijmeni"], [])
|
||
tos = [e for e in dict.fromkeys(tos) if "dd-proxy" not in e.lower()] # dedup + bez proxy
|
||
to = ", ".join(tos)
|
||
sent_cz = sent_date_cz(d.get("STATUS"))
|
||
body = build_body(d.get("jmeno"), d["prijmeni"], sent_cz, link)
|
||
print(f" {d.get('jmeno')} {d['prijmeni']:14} | To: {to}")
|
||
|
||
if not args.apply:
|
||
continue
|
||
msg = EmailMessage()
|
||
# ŽÁDNÝ From — JNJ Outlook doplní odesílatele z GAL (odešle přes JNJ Exchange)
|
||
msg["To"] = to
|
||
msg["Cc"] = CC
|
||
msg["Subject"] = subject
|
||
msg["X-Unsent"] = "1"
|
||
html = (f'<html><body style="font-family:Calibri,Arial,sans-serif;font-size:11pt">'
|
||
f'{body}</body></html>')
|
||
msg.set_content(html, subtype="html", charset="utf-8", cte="base64")
|
||
os.makedirs(OUT_DIR, exist_ok=True)
|
||
fn = f"pripominka_sipiq_{ascii_slug(d['prijmeni'])}_18JUN2026.eml"
|
||
with open(os.path.join(OUT_DIR, fn), "wb") as fh:
|
||
fh.write(bytes(msg))
|
||
done.append(fn)
|
||
|
||
if not args.apply:
|
||
print(f"\n[DRY-RUN] {len(docs)} center, nic nezapsáno. Ostrý: --apply")
|
||
else:
|
||
print(f"\n[APPLY] Vygenerováno {len(done)} připomínek do {OUT_DIR}")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|