# jnj_mailbox_sync v1.5.0 **Soubor:** `jnj_mailbox_sync_v1.5.py` **Datum:** 2026-06-16 **Autor:** vladimir.buzalka **Běží:** JNJ stroj (Outlook MAPI), Python z Thonny. ## Co to je Synchronizace JNJ Outlooku (MAPI) → osobní schránka (přes msgreceiver) + bookkeeping v SQLite (`C:\Users\vbuzalka\SQLITE\jnjemails.db`). Sleduje přesuny e-mailů mezi složkami a příznak „už není ve schránce" — bez opětovného přenosu těla. Skenované složky: **Inbox + Sent Items + Deleted Items + Archive** (vč. podsložek). ## Novinka v1.5 — provenance verze skriptu na úrovni entry Do tabulky `messages` přidány dva sloupce (jen pro náhled, **Tower je nezpracovává** — nejsou v mirroru do `jnj_messages`): | Sloupec | Význam | |---|---| | `captured_by_version` | verze skriptu, která entry **poprvé zachytila/odeslala** (set při INSERT) | | `last_upload_version` | verze, která naposledy **re-uploadla tělo** (set při INSERT i při re-uploadu) | Smysl: kdykoliv se podívat (`jnjemails` SQL), kterou verzí byl daný e-mail přenesen. **Pravidlo:** při jakékoliv změně skriptu vždy bumpni verzi (`SCRIPT_VERSION`) — jinak tahle stopa ztrácí smysl. Migrace přes `ALTER TABLE` (staré řádky = NULL). ## Novinka v1.4 — skenování složky Archive (primární schránka) Přidána složka **Archive** (jednoklikové archivování v Outlooku) v **primární** schránce. Archive **není** default folder, takže se hledá podle jména `"Archive"` pod kořenem primární schránky (`Inbox.Parent`) a přidává se do `scanned_roots` (aby se její položky nehodnotily jako „opustilo schránku"). **Online Archive** (samostatný store) se i nadále **neskenuje**. Řeší případy, kdy odeslaná kopie skončila v Archive (jinak chyběla domácímu přehledu i párování dvojčat). ## Novinka v1.3 — detekce změny obsahu (re-upload změněného e-mailu) **Problém:** e-mail **bez Message-ID** (typicky **NEODESLANÝ** Sent kvůli `SendAsDenied`, nebo čerstvě odeslaný, kde Exchange ještě nedoplnil Message-ID) má **stabilní EntryID**. Když do něj Outlook **po zachycení** dopíše chybu odeslání, obsah se změní, ale identita (`entryid:`) zůstane → starý sync to vyhodnotil jako „známé, beze změny" a aktualizovaný (chybový) e-mail už domů **nepřenesl**. Naproti tomu úspěšně odeslaný e-mail dostane **nové EntryID + Message-ID**, takže se zachytil jako nový. Vznikla asymetrie: failed-update se ztrácel. **Řešení:** identita zůstává (Message-ID / `entryid:`), ale navíc se sleduje **verzní otisk** = `PR_LAST_MODIFICATION_TIME` (`0x30080040`). U **známé položky bez Message-ID** (`mid` začíná `entryid:`) se otisk porovná; když se posunul, e-mail se znovu uloží (`SaveAs`) a nahraje s `overwrite=1` → server přepíše původní `.msg` na místě → Tower ho přeparsuje → dokument v Mongu se aktualizuje (vč. těla s chybou). - Hlídání je **levné** — druhé čtení property jen u známých no-ID položek (desítky kusů); položky s Message-ID jsou finalizované a nesledují se. - Re-upload běží jen v režimech, které smějí nahrávat (**capture, full-update**), a posílá se s `folder=""` → server **nedělá** Graph re-import (žádný duplikát v Graph zrcadle). - **Vyžaduje msgreceiver app.py ≥ v2.4** (overwrite na `/upload`). Bez něj se re-upload chová jako starý skip (nepřepíše, ale nic nerozbije) — pořadí nasazení server → JNJ bez výpadku. ## Nové sloupce SQLite - `messages.last_mod_time` — PR_LAST_MODIFICATION_TIME při posledním zachycení (otisk). - `messages.content_uploads` — kolikrát se tělo nahrálo (1 = jen první zachycení). - `runs.content_updated` — kolik e-mailů se v běhu re-uploadlo kvůli změně obsahu. (Migrace přes stávající `ALTER TABLE` smyčku — staré `jnjemails.db` se doplní automaticky.) ## Argumenty `--mode {capture,update-paths,full-update}` (default capture), `--days N` (0 = celé), `--dry-run`, `--limit N`, `--no-db-upload`. ## Spouštění (JNJ stroj, plné cesty) ``` "C:\Users\vbuzalka\AppData\Local\Programs\Thonny\python.exe" "c:\Users\vbuzalka\OneDrive - JNJ\##JNJPrenos\Python\jnj_mailbox_sync_v1.5.py" --mode full-update --days 60 ``` `full-update --days 60` = dorovná chybějící + **re-uploadne změněné** (chybové) Sent položky za poslední 60 dní. To je doporučený běh pro „aktualizovat i neodeslané". ## Revert Stará verze: `Trash/jnj_mailbox_sync_v1.4.py` (bez provenance sloupců), `…_v1.3.py` (bez skenování Archive), `…_v1.2.py` (bez detekce změny). Server v2.4 zůstává zpětně kompatibilní (overwrite je opt-in), takže revert na JNJ straně nevyžaduje zásah na serveru. ## Historie - **1.0.0** — režimy capture/update-paths/full-update, sledování přesunů, updated_at. - **1.1.0** — + Deleted Items do skenovaných složek. - **1.2.0** — upload SQLite komprimován (lzma/xz max) + šifrován (Fernet) → `.db.xz.enc`. - **1.3.0** — + detekce změny obsahu přes `PR_LAST_MODIFICATION_TIME`: známé no-ID položky, které se po zachycení změnily (např. dopsaná chyba `SendAsDenied`), se znovu nahrávají s `overwrite=1`. Nové sloupce `last_mod_time`, `content_uploads`, `runs.content_updated`. Vyžaduje app.py ≥ v2.4. - **1.4.0** — + skenování složky **Archive** v primární schránce (hledá se podle jména pod kořenem schránky, ne přes default folder; Online Archive se neskenuje). - **1.5.0** — + provenance verze na úrovni entry: sloupce `captured_by_version` a `last_upload_version` (jen náhled, Tower nezpracovává).