# -*- 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)