Files
janssen/EmailsImport/parse_emails_tower_v1.3.md

10 KiB
Raw Permalink Blame History

parse_emails_tower_v1.3

Spuštění

První spuštění:

docker exec -d python-runner bash -c \
  "python /scripts/parse_emails_tower_v1.3.py > /scripts/parse_emails_tower.log 2>&1"

Pokračování po přerušení (přeskočí už importované):

docker exec -d python-runner bash -c \
  "python /scripts/parse_emails_tower_v1.3.py --skip-existing > /scripts/parse_emails_tower.log 2>&1"

Stav importu

Sledování průběhu (live log):

docker exec -it python-runner tail -f /scripts/parse_emails_tower.log

Počet emailů v MongoDB:

docker exec -it python-runner python -c \
  "from pymongo import MongoClient; c=MongoClient('mongodb://192.168.1.76:27017'); print(c['emaily']['vbuzalka@its.jnj.com'].count_documents({}))"

Název: parse_emails_tower_v1.3.py
Verze: 1.3
Datum: 2026-06-08
Autor: vladimir.buzalka


Účel

Import všech .msg souborů do MongoDB. Z každého souboru extrahuje všechny dostupné vlastnosti — podobně jako EXIF u fotek.

  • DB: emaily
  • Kolekce: vbuzalka@its.jnj.com
  • _id = Internet Message-ID (nebo filename:<stem> jako fallback)
  • Bezpečné přerušit a opakovat — upsert podle _id

Prostředí

Běží v Docker containeru python-runner na Unraid Tower.

Komponenta Umístění
Container python-runner (Docker na Unraid Tower)
.msg soubory /mnt/user/JNJEMAILS/mnt/JNJEMAILS uvnitř containeru
Skripty /mnt/user/Scripts/scripts uvnitř containeru
MongoDB 192.168.1.76:27017 (externí, mimo container)

Spouštění (z Unraid terminálu)

Test na 50 emailech:

docker exec -it python-runner python /scripts/parse_emails_tower_v1.3.py --limit 50 --no-indexes

Kompletní import na pozadí (log do souboru):

docker exec -d python-runner bash -c \
  "python /scripts/parse_emails_tower_v1.3.py > /scripts/parse_emails_tower.log 2>&1"

Pokračování po přerušení:

docker exec -d python-runner bash -c \
  "python /scripts/parse_emails_tower_v1.3.py --skip-existing > /scripts/parse_emails_tower.log 2>&1"

Sledování průběhu (Ctrl+C ukončí sledování, import běží dál):

docker exec -it python-runner tail -f /scripts/parse_emails_tower.log

Všechny parametry

Parametr Popis
--skip-existing Načte seznam hotových souborů z MongoDB a přeskočí je. Použij pro pokračování po přerušení.
--limit N Zpracuje jen prvních N souborů. Vhodné pro test.
--no-indexes Nevytváří indexy na konci. Použij pokud přerušíš uprostřed — indexy vytvoř ručně až je vše hotové.
--msgs-dir PATH Přepíše výchozí cestu k .msg souborům (výchozí: /mnt/JNJEMAILS).

Průběh na konzoli

Každý email na jednom řádku:

       1/69371  OK    RE: Protocol deviation CZ10022                    jan.novak@its.jnj.com
       2/69371  OK    UCO3001: Draft FUL pro DD5-CZ10022                monitor@4gclinical.com
       3/69371  ERR   ?                                                  ?

Každých 500 emailů oddělovač s průběhem:

  ────────────────────────────────────────────────────────────────────────────────
  Průběh: ok=498  err=2  0.4 msg/s  ETA 47h12m
  ────────────────────────────────────────────────────────────────────────────────

Na konci souhrn:

====================================================
Vysledek:  ok=69300  |  skip=0  |  err=71
Celkovy cas: 47h 23m 10s
Dokumentu v kolekci: 69300

Zdroje dat z každého .msg

Pole Popis
Předmět, normalized subject
Odesílatel email, jméno, SMTP adresa
Příjemci To/CC/BCC strukturovaně [{type, email, name}]
Čas doručení a odeslání UTC
Tělo plaintext + HTML (max 2 MB)
Přílohy metadata: jméno, velikost, MIME typ, inline flag
Internet headers X-Originating-IP, Received, DKIM, X-Mailer, ...
MAPI důležitost, citlivost, příznak, konverzační vlákno, kategorie
In-Reply-To, References pro rekonstrukci vlákna
Raw MAPI properties {0xXXXX: value}

Hodnotové kódy

Pole Hodnota Význam
importance 0 Nízká
1 Normální
2 Vysoká
sensitivity 0 Normální
1 Osobní
2 Soukromé
3 Důvěrné
flag_status 0 Bez příznaku
1 Označeno (follow up)
2 Dokončeno

MongoDB indexy

Automaticky vytvořeny na konci importu (--no-indexes přeskočí):

Index Pole
Chronologický received_at, sent_at
Odesílatel sender.email
Soubor filename (unique)
Konverzace conversation_topic
Filtry has_attachments, categories, importance, flag_status
Full-text subject + body_text + to + cc (text index text_search)

Ukázkové dotazy (MongoDB shell / MCP)

Emaily o UCO3001 s přílohou:

db["vbuzalka@its.jnj.com"].find({
  $text: { $search: "UCO3001" },
  has_attachments: true
}).sort({ received_at: -1 })

Emaily od konkrétního odesílatele:

db["vbuzalka@its.jnj.com"].find({
  "sender.email": /covance/i
}).sort({ received_at: -1 })

Celé konverzační vlákno:

db["vbuzalka@its.jnj.com"].find({
  conversation_topic: "Protocol deviation CZ10022"
}).sort({ received_at: 1 })

Statistiky podle odesílatele (top 20):

db["vbuzalka@its.jnj.com"].aggregate([
  { $group: { _id: "$sender.email", count: { $sum: 1 } } },
  { $sort: { count: -1 } },
  { $limit: 20 }
])

Chybový log

Soubory které selhaly jsou zalogovány do samostatného parse_emails_tower_errors.log vedle skriptu (tj. /scripts/parse_emails_tower_errors.log\\tower\Scripts\parse_emails_tower_errors.log). Tento log je oddělený od Graph importu, aby v něm nebyl bordel:

2026-06-08 12:40:33 | open failed [7A3F...0000.msg]: <důvod>
2026-06-08 12:41:02 | per-dokument selhal [_id=<...>]: <důvod>

Stdout (průběh) jde do parse_emails_tower.log — rovněž samostatný.


Záchrana problémových .msg (v1.3)

Některé .msg defaultní extract_msg neumí otevřít a celý soubor zahodí, i když email je naprosto v pořádku (jde otevřít v Outlooku). Tři příčiny a jejich řešení:

Příčina Příklad Řešení
Vadná příloha bez PR_ATTACH_METHOD „Attachment method missing" errorBehavior=SUPPRESS_ALL — vadnou přílohu přeskočí, zbytek (tělo, ostatní přílohy) načte
Tělo deklaruje codepage 1200 (UTF-16), ale bajty jsou cp1250/gb2312 české místo diakritiky raw-OLE čtení + kaskádové dekódování
Vnořený email (Outlook item) „not an MSG file", extract_msg vrátí prázdno raw-OLE čtení klíčových MAPI streamů

Jak to funguje:

  1. open_message() — kaskádové otevření: normalSUPPRESS_ALL+overrideEncoding (dle codepage property).
  2. raw-OLE fallback — když extract_msg vrátí prázdno/ nebo musel hádat kódování, klíčová pole (subject, sender, body, html) se dočtou přímo z OLE streamů (__substg1.0_0037/0C1A/5D01/1000/1013) s kaskádovým dekódováním:
    utf-8 (strict) → kódování dle CPID → cp1250 → cp1252 → gb2312 → latin-1
    
    Hlavičkám o kódování se nevěří (často si protiřečí); bere se první kódování, které projde striktně bez chyby. utf-8 strict je silný rozlišovač.

Nová pole v dokumentu:

Pole Význam
parse_mode normal / suppress_all / override:<enc> — jak byl soubor otevřen
parse_degraded true = byl potřeba fallback (vadná příloha nebo hádané kódování)

Ověřeno: všech 126 dříve selhaných souborů z běhu 8.6. se obnoví čistě (74× suppress_all, 52× override:cp1250), 0 prázdných, 0 s .

Dohledání degradovaných:

db["vbuzalka@its.jnj.com"].find({ parse_degraded: true })

Výkon

Parametr Hodnota
Počet souborů ~69 000
Rychlost ~0.4 msg/s (htmlBody dekódování)
Odhadovaný čas 48 hodin
Batch size 200 dokumentů / bulk_write
Odhadovaná velikost DB 25 GB

Závislosti (v Docker image python-runner)

extract-msg==0.55.0
olefile
pymongo
python-dateutil

Image sestaven z Dockerfile v /mnt/user/Scripts/python-runner/.


Historie verzí

Verze Datum Změna
1.0 2026-06-01 Iniciální verze
1.1 2026-06-02 Nasazení na Unraid Tower v Docker containeru python-runner; MSGS_DIR změněno z SMB share (\\tower\JNJEMAILS) na lokální mount (/mnt/JNJEMAILS); aktualizován popis spouštění pro docker exec
1.2 2026-06-08 Oprava to_bson: int mimo rozsah int64 (BSON umí jen 8-byte ints) se převede na string — dřív celý bulk_write spadl na MongoDB can only handle up to 8-byte ints a zahodil celou dávku 200 dokumentů (běh v1.1 z 8.6. neuložil nic). flush() má fallback per-dokument (vadný záznam zahodí sám, ne celou dávku). bool() testován před int(). Samostatné logy parse_emails_tower.log + parse_emails_tower_errors.log.
1.3 2026-06-08 Záchrana dříve selhaných .msg (cca 126 z běhu 8.6.): open_message() kaskádové otevření (normalSUPPRESS_ALL+overrideEncoding) řeší vadné přílohy i „not an MSG file"; raw-OLE fallback dočítá subject/sender/body/html přímo z OLE streamů s kaskádovým dekódováním (utf-8 strict→CPID→cp1250…), když extract_msg vrátí prázdno/. Nová pole parse_mode, parse_degraded. Nová závislost olefile. Ověřeno: 126/126 obnoveno čistě.