# -*- coding: utf-8 -*-
# =============================================================================
# Nazev: forward_offer_outlook_v1.1.py
# Verze: 1.1
# Datum: 2026-06-16
# Popis: JNJ-native skript (MAPI / pywin32). Pro daneho lekare najde PUVODNI
# uvodni nabidku ve slozce Odeslane a vytvori jeji skutecny Outlook
# FORWARD (zachova original vcetne data 31.05.2026, formatu i hlavicky).
# PRIMARNE hleda zpravu PRIMO podle jednoznacneho EntryID (MAPI) pres
# GetItemFromID -> nalezeni "na prvni dobrou", nulova nejednoznacnost.
# FALLBACK (kdyz EntryID nesedne) = subjekt + datum + prijemce To.
# Forward predvyplni To (lekar) + CC (Kocourkova, Bartosova),
# volitelne prida kratky uvod a OTEVRE okno k rucni kontrole/odeslani.
# Pouziti: Spustit v JNJ Pythonu s nakonfigurovanym Outlookem (JNJ schranka).
# pip install pywin32 ; python forward_offer_outlook_v1.1.py
# Bezpecnost: ACTION="display" => jen otevre Forward, NEODESILA.
# Zmeny v1.1: primarni hledani podle EntryID (GetItemFromID) ziskaneho z JNJ
# SQLite (tabulka messages.entry_id). v1.0 hledalo jen heuristicky.
# =============================================================================
import sys
import datetime
import win32com.client # pywin32
# ----------------------------- KONFIGURACE -----------------------------------
SENDER_SMTP = "vbuzalka@its.jnj.com"
# Cile. entry_id = jednoznacny MAPI EntryID puvodni nabidky (z JNJ SQLite,
# tabulka messages.entry_id). Kdyz entry_id chybi/nesedne, pouzije se fallback
# podle subjektu+data+prijemce.
TARGETS = [
{
"email": "rastislav.hustak@fntt.sk",
"entry_id": "000000008431528824F96740840A72BAD506477D070092544C32292E3A46AC27E91F5A4CDB1100000007A91B00005BFD391558BBC54FA9172E1614A2FC13000530495B210000",
},
# {"email": "voska@nemocnice-horovice.cz", "entry_id": ""},
# {"email": "sercl@seznam.cz", "entry_id": ""},
# {"email": "petra.minarikova@uvn.cz", "entry_id": ""},
]
CC_RECIPIENTS = ["AKocourk@ITS.JNJ.com", "EBartoso@ITS.JNJ.com"]
# Fallback kriteria (kdyz EntryID nesedne):
SUBJECT_STARTSWITH = "nabidka spoluprace na klinickem hodnoceni"
ORIG_DATE = datetime.date(2026, 5, 31)
ADD_INTRO = True
INTRO_HTML = (
"
Dobry den,
"
"dovoluji si Vam znovu preposlat nize uvedenou nabidku ze dne "
"31. kvetna 2026. Velmi bych ocenil Vase vyjadreni — a to "
"i v pripade, ze o ucast nemate zajem. Dekuji.
"
"S pozdravem
MUDr. Vladimir Buzalka
"
"
"
)
ACTION = "display" # "display" | "draft" | "send"
# -----------------------------------------------------------------------------
OL_FOLDER_SENT = 5
OL_TO, OL_CC = 1, 2
PR_SMTP = "http://schemas.microsoft.com/mapi/proptag/0x39FE001E"
def norm(s):
import unicodedata
s = s or ""
s = unicodedata.normalize("NFKD", s)
s = "".join(c for c in s if not unicodedata.combining(c))
return " ".join(s.lower().split())
def smtp_of(recipient):
try:
return (recipient.PropertyAccessor.GetProperty(PR_SMTP) or "").lower()
except Exception:
try:
return (recipient.Address or "").lower()
except Exception:
return ""
def get_sent_folder(ns):
try:
for acct in ns.Accounts:
if (acct.SmtpAddress or "").lower() == SENDER_SMTP.lower():
return acct.DeliveryStore.GetDefaultFolder(OL_FOLDER_SENT)
except Exception:
pass
return ns.GetDefaultFolder(OL_FOLDER_SENT)
def get_by_entryid(ns, entry_id, store_id):
"""Nacte zpravu PRIMO podle EntryID. Vrati MailItem nebo None."""
if not entry_id:
return None
for args in ((entry_id, store_id), (entry_id,)):
try:
it = ns.GetItemFromID(*args)
if it is not None:
return it
except Exception:
continue
return None
def find_original_fallback(items, target_email):
"""Fallback: subjekt + datum 31.05.2026 + prijemce To."""
tgt = target_email.lower()
out = []
for it in items:
try:
if it.Class != 43:
continue
if norm(it.Subject)[: len(SUBJECT_STARTSWITH)] != SUBJECT_STARTSWITH:
continue
s = it.SentOn
if s is None or s.date() != ORIG_DATE:
continue
for r in it.Recipients:
if r.Type == OL_TO and smtp_of(r) == tgt:
out.append(it)
break
except Exception:
continue
return out
def make_forward(orig, email):
fwd = orig.Forward()
fwd.Recipients.Add(email).Type = OL_TO
for cc in CC_RECIPIENTS:
fwd.Recipients.Add(cc).Type = OL_CC
fwd.Recipients.ResolveAll()
if ADD_INTRO:
try:
fwd.HTMLBody = INTRO_HTML + fwd.HTMLBody
except Exception:
pass
return fwd
def main():
outlook = win32com.client.Dispatch("Outlook.Application")
ns = outlook.GetNamespace("MAPI")
sent = get_sent_folder(ns)
store_id = sent.Store.StoreID
items = None # lazy, jen kdyz bude potreba fallback
print("Slozka Odeslane:", sent.FolderPath)
print("Rezim ACTION :", ACTION)
print("=" * 60)
for t in TARGETS:
email = t["email"]
orig = get_by_entryid(ns, t.get("entry_id", ""), store_id)
how = "EntryID"
if orig is None:
# fallback heuristika
if items is None:
items = sent.Items
items.Sort("[SentOn]", True)
found = find_original_fallback(items, email)
if not found:
print(f"[!] {email}: NENALEZENO (EntryID nesedl ani fallback). Preskakuji.")
continue
if len(found) > 1:
print(f"[!] {email}: fallback nasel {len(found)} shod — nejednoznacne, preskakuji.")
continue
orig = found[0]
how = "fallback(subjekt+datum+prijemce)"
# kontrola, ze to je opravdu ta nabidka tomu lekari
try:
print(f" nalezeno pres {how}: \"{orig.Subject}\" | odeslano {orig.SentOn}")
except Exception:
pass
fwd = make_forward(orig, email)
if ACTION == "send":
fwd.Send()
print(f"[ODESLANO] {email}")
elif ACTION == "draft":
fwd.Save()
print(f"[KONCEPT ] {email}")
else:
fwd.Display()
print(f"[OTEVRENO] {email} — zkontroluj a posli rucne")
print("=" * 60)
print("Hotovo.")
if __name__ == "__main__":
try:
main()
except Exception as e:
print("CHYBA:", e)
sys.exit(1)