This commit is contained in:
2026-06-16 14:32:28 +02:00
parent b825e4ee7c
commit f385d7bf0b
95 changed files with 43120 additions and 0 deletions
@@ -0,0 +1,48 @@
# forward_offer_outlook_v1.0.py
**Verze:** 1.0 · **Datum:** 2026-06-16
JNJ-native skript (pywin32 / MAPI). V odeslané poště Outlooku najde **původní
úvodní nabídku** odeslanou konkrétnímu lékaři dne **31.05.2026** a vytvoří její
**skutečný Outlook Forward** — zachová originál včetně data, formátování i
hlavičky (tj. to, co `vbcz-email` `.eml` udělat nedokáže).
## Spuštění (na JNJ stroji s Outlookem)
```
pip install pywin32 # jednorázově
python forward_offer_outlook_v1.0.py
```
## Co dělá
1. Otevře MAPI namespace, najde složku **Odeslané** účtu `vbuzalka@its.jnj.com`.
2. Pro každého lékaře v `TARGETS` najde původní e-mail podle:
- subjekt začíná „Nabídka spolupráce na klinickém hodnocení…" (odliší od
připomínek `[2. připomínka]` a odpovědí `RE:`),
- datum odeslání = **31.05.2026**,
- příjemce **To** = e-mail lékaře.
3. Zavolá `.Forward()` → předvyplní **To** (lékař) + **CC** (Kocourková,
Bartošová), volitelně přidá krátký úvod a podle `ACTION`:
- `display` (default) — jen **otevře okno** Forwardu, NEODesílá,
- `draft` — uloží do Konceptů,
- `send` — odešle.
## Konfigurace (nahoře ve skriptu)
- `TARGETS` — seznam adres. **Defaultně jen Hušták** (odladění); ostatní
(Voska, Šerclová, Mináříková) odkomentovat až po ověření.
- `CC_RECIPIENTS` — Kocourková + Bartošová.
- `ADD_INTRO` / `INTRO_HTML` — krátký úvod nad přeposlaným originálem
(`False` = čisté přeposlání bez textu navíc).
- `ACTION``display` / `draft` / `send`.
- `SUBJECT_STARTSWITH`, `ORIG_DATE` — kritéria pro nalezení originálu.
## Pozn.
- Porovnání subjektu je bez diakritiky a malými písmeny (robustní vůči
„prípravku"/„přípravku").
- Když nenajde právě jednu shodu, lékaře **přeskočí** a vypíše varování
(nehádá).
- `display` nevyvolává Outlookový „program se snaží odeslat" dialog —
odeslání je vždy na tobě.
- Pokud by JNJ Outlook měl JNJ schránku jako jiný než výchozí účet, skript
si složku Odeslané najde podle `SENDER_SMTP`.
```
```
@@ -0,0 +1,175 @@
# -*- coding: utf-8 -*-
# =============================================================================
# Nazev: forward_offer_outlook_v1.0.py
# Verze: 1.0
# Datum: 2026-06-16
# Popis: JNJ-native skript. Pres MAPI (Outlook, pywin32) najde v odeslane
# poste PUVODNI uvodni nabidku ("Nabidka spoluprace na klinickem
# hodnoceni pripravku icotrokinra...") odeslanou konkretnimu lekari
# dne 31.05.2026 a vytvori jeji FORWARD (skutecny Outlook Forward,
# tj. zachova original vcetne data, formatovani i hlavicky).
# Forward predvyplni prijemce (lekar) + CC (Kocourkova, Bartosova),
# volitelne prida kratky uvod a OTEVRE okno k rucni kontrole/odeslani.
# Pouziti: Spustit v JNJ Pythonu, kde je nakonfigurovany Outlook s JNJ schrankou.
# Vyzaduje pywin32: pip install pywin32
# python forward_offer_outlook_v1.0.py
# Bezpecnost: ACTION = "display" => jen otevre Forward, NEODESILA.
# "draft" => ulozi do Konceptu. "send" => odesle (uvazene zapnout).
# =============================================================================
import sys
import datetime
import win32com.client # pywin32
# ----------------------------- KONFIGURACE -----------------------------------
# JNJ schranka (odesilatel puvodnich nabidek). Pouzije se jeji slozka Odeslane.
SENDER_SMTP = "vbuzalka@its.jnj.com"
# Komu forwardovat. Pro odladeni zatim JEN Hustak; ostatni odkomentuj az to klapne.
TARGETS = [
"rastislav.hustak@fntt.sk",
# "voska@nemocnice-horovice.cz",
# "sercl@seznam.cz",
# "petra.minarikova@uvn.cz",
]
# CC na kazdy forward (nas lokalni tym).
CC_RECIPIENTS = ["AKocourk@ITS.JNJ.com", "EBartoso@ITS.JNJ.com"]
# Identifikace puvodniho e-mailu:
# - subjekt zacina na (po ocisteni) tento text (odlisi nabidku od pripominek/RE)
SUBJECT_STARTSWITH = "nabidka spoluprace na klinickem hodnoceni"
# - datum odeslani originalu
ORIG_DATE = datetime.date(2026, 5, 31)
# Volitelny kratky uvod nad forwardovanym originalem.
# ADD_INTRO = False => ciste preposlani bez jakehokoli textu navic.
ADD_INTRO = True
INTRO_HTML = (
"<p>Dobry den,</p>"
"<p>dovoluji si Vam znovu preposlat nize uvedenou nabidku ze dne "
"31.&nbsp;kvetna&nbsp;2026. Velmi bych ocenil Vase vyjadreni &mdash; a to "
"i v pripade, ze o ucast nemate zajem. Dekuji.</p>"
"<p>S pozdravem<br>MUDr. Vladimir Buzalka</p>"
"<hr>"
)
# Co s vytvorenym forwardem: "display" | "draft" | "send"
ACTION = "display"
# -----------------------------------------------------------------------------
OL_FOLDER_SENT = 5 # olFolderSentMail
OL_TO, OL_CC = 1, 2 # olTo, olCC
PR_SMTP = "http://schemas.microsoft.com/mapi/proptag/0x39FE001E"
def norm(s):
"""male pismena bez diakritiky pro porovnani subjektu"""
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):
"""Slozka Odeslane prislusneho uctu (dle SENDER_SMTP), fallback default."""
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 find_original(items, target_email):
"""Najde puvodni nabidku: subjekt + datum 31.05.2026 + prijemce To."""
tgt = target_email.lower()
matches = []
for it in items:
try:
if it.Class != 43: # olMail
continue
if norm(it.Subject)[: len(SUBJECT_STARTSWITH)] != SUBJECT_STARTSWITH:
continue
sent = it.SentOn
if sent is None or sent.date() != ORIG_DATE:
continue
for r in it.Recipients:
if r.Type == OL_TO and smtp_of(r) == tgt:
matches.append(it)
break
except Exception:
continue
return matches
def main():
outlook = win32com.client.Dispatch("Outlook.Application")
ns = outlook.GetNamespace("MAPI")
sent = get_sent_folder(ns)
items = sent.Items
items.Sort("[SentOn]", True) # nejnovejsi prvni
print("Slozka Odeslane:", sent.FolderPath)
print("Rezim ACTION :", ACTION)
print("=" * 60)
for email in TARGETS:
found = find_original(items, email)
if not found:
print(f"[!] {email}: PUVODNI NABIDKA NENALEZENA (subjekt/datum/prijemce). Preskakuji.")
continue
if len(found) > 1:
print(f"[!] {email}: nalezeno {len(found)} shod — nejednoznacne, preskakuji (over rucne).")
continue
orig = found[0]
fwd = orig.Forward() # SKUTECNY Outlook Forward (zachova original + datum)
# prijemce
fwd.Recipients.Add(email).Type = OL_TO
for cc in CC_RECIPIENTS:
fwd.Recipients.Add(cc).Type = OL_CC
fwd.Recipients.ResolveAll()
# volitelny uvod nad forward blokem
if ADD_INTRO:
try:
fwd.HTMLBody = INTRO_HTML + fwd.HTMLBody
except Exception:
pass
if ACTION == "send":
fwd.Send()
print(f"[ODESLANO] {email} (subjekt: {fwd.Subject})")
elif ACTION == "draft":
fwd.Save()
print(f"[KONCEPT ] {email} (subjekt: {fwd.Subject})")
else: # display
fwd.Display()
print(f"[OTEVRENO] {email} (subjekt: {fwd.Subject}) — zkontroluj a posli rucne")
print("=" * 60)
print("Hotovo.")
if __name__ == "__main__":
try:
main()
except Exception as e:
print("CHYBA:", e)
sys.exit(1)