# jnj_tower_ingest v1.0.0

**Soubor:** `jnj_tower_ingest_v1.0.py`
**Datum:** 2026-06-10
**Autor:** vladimir.buzalka
**Běží:** Docker kontejner `python-runner` na Unraid Tower (192.168.1.76), u MongoDB.

## Co to je

Sjednocený **Tower-side ingest** JNJ e-mailů — spojuje dvě dříve oddělené poloviny
do jednoho běhu:

| Fáze | Dříve samostatně | Co dělá |
|---|---|---|
| **1. PARSE** | `parse_emails_tower_v1.3.py` | `.msg` z `/mnt/JNJEMAILS` → bohatý dokument v Mongo `emaily."vbuzalka@its.jnj.com"` (tělo, přílohy, hlavičky, MAPI props). `_id` = Internet Message-ID. |
| **2. SYNC** | `sync_jnj_state_v1.0.py` | nejnovější `/mnt/JNJEMAILS/db/jnjemails_*.db` (SQLite, **jen čtení** `mode=ro`) → zrcadlo do `jnj_messages` + doplnění `jnj_folder`/stavu do `emaily`. |

**Pořadí: parse BĚŽÍ PŘED sync.** Tím čerstvě naparsované maily dostanou cestu hned ve
stejném běhu (dřív: když sync předběhl parse, nový mail neměl co matchnout — sync
nezakládá stuby). Spojovací klíč všude = **Internet Message-ID = Mongo `_id`**.

## Inkrementálnost (vhodné pro cron každých 5 min)

- **PARSE** — parsuje jen `.msg` s `mtime` novějším než watermark
  (`jnj_sync_state` / `_id="parse_state"` → `last_parse_mtime`).
  - **První běh = seed:** watermark chybí → kandidáti = soubory, jejichž `filename`
    ještě není v Mongu (jednorázový `distinct("filename")`); poté se watermark
    nastaví na nejnovější soubor.
  - **Další běhy = incremental:** jen `mtime > watermark`. Žádný sken Monga.
  - `--full` reparsuje vše (upsert, idempotentní).
  - **Indexy** se vytvářejí jen při `full`/`seed`/`--reindex` (v incremental už existují).
- **SYNC** — watermark `updated_at` (`jnj_sync_state` / `_id="watermark"`) + zkratka
  `last_db` (stejná SQLite jako minule → okamžitý no-op, nesahá na Mongo data).

Dvě nezávislé události (nová `.msg` / nová `.db`) → skript udělá jen tu fázi, co má
práci; jinak levný no-op.

## Argumenty

| Argument | Význam |
|---|---|
| `--dry-run` | nic nezapíše, jen plán obou fází |
| `--full` | parse: reparsuj vše; sync: ignoruj watermark |
| `--limit N` | max N souborů (parse) / řádků (sync) — test |
| `--reindex` | vynutí indexy po parse fázi |
| `--force` | sync: ignoruj zkratku `last_db` |
| `--parse-only` | jen fáze PARSE |
| `--sync-only` | jen fáze SYNC |

## Spouštění

```bash
# Test:
docker exec -it python-runner python3 /scripts/jnj_tower_ingest_v1.0.py --dry-run
# Ostrý inkrementální běh (volá ho cron):
docker exec python-runner python3 /scripts/jnj_tower_ingest_v1.0.py
# Plný reparse + reindex:
docker exec -it python-runner python3 /scripts/jnj_tower_ingest_v1.0.py --full --reindex
```

## Plánování (HOTOVO)

Unraid User Scripts úloha `jnj_state_sync` (cron `*/5 * * * *`) — wrapper s `flock`
volá `docker exec python-runner python3 /scripts/jnj_tower_ingest_v1.0.py`.
Loguje jen reálnou práci/chyby do `/mnt/user/Scripts/logs/jnj_tower_ingest.log`
(grep `Zapisuji|PARSE hotovo|SYNC hotovo|CHYBA|Traceback`). Cron řádek/rozvrh se při
přepnutí ze `sync_jnj_state` neměnil — jen obsah wrapperu.

## Revert

Staré skripty `parse_emails_tower_v1.3.py` a `sync_jnj_state_v1.0.py` zůstávají v
`/scripts/` jako pojistka. Návrat = přepsat wrapper zpět na `sync_jnj_state_v1.0.py`.

## Závislosti

`extract-msg==0.55.0`, `olefile`, `pymongo`, `python-dateutil`, `sqlite3` (stdlib).
Python 3.10+.

## Historie verzí

- **1.0.0** 2026-06-10 — sjednocení `parse_emails_tower_v1.3` + `sync_jnj_state_v1.0`;
  parse zinkrementálněn přes mtime watermark; indexy jen při full/seed/`--reindex`;
  pořadí parse→sync.
