notebook
This commit is contained in:
@@ -0,0 +1,143 @@
|
||||
# Zmeny v1.1 (2026-06-10)
|
||||
|
||||
- Bugfix: NON_MAILBOX_COLLECTIONS += jnj_messages, jnj_sync_state (phantom kolekce JNJ folder trackingu zpusobovaly Graph 404 -> FAIL kroku 1b).
|
||||
|
||||
# 1b_parse_emails_graph_delta_v1.0.py
|
||||
|
||||
**Inkrementalní sync přes Microsoft Graph delta query.** Sourozenec [`1_parse_emails_graph_v1.4.py`](1_parse_emails_graph_v1.4.md) — každý řeší jiný use case:
|
||||
|
||||
| Skript | Použití |
|
||||
|---|---|
|
||||
| `1_parse_emails_graph_v1.4.py` | **První plný import** schránky (vše od začátku) |
|
||||
| `1b_parse_emails_graph_delta_v1.0.py` | **Pravidelný sync** — jen co se od minula změnilo |
|
||||
|
||||
## Jak funguje
|
||||
|
||||
Graph API vystavuje `messages/delta` endpoint, který si pamatuje **záložku** (`deltaLink` s tokenem). Při dalším volání s touto záložkou vrátí jen:
|
||||
|
||||
- **nové zprávy**
|
||||
- **změny** existujících (`isRead`, vlajka, přesun do jiné složky, kategorie)
|
||||
- **smazané** zprávy (`@removed`)
|
||||
|
||||
Delta běží **per složka**. Skript drží stav v Mongo kolekci `emaily.sync_state`:
|
||||
|
||||
```json
|
||||
{
|
||||
"_id": "ordinace@buzalkova.cz|<folder_id>",
|
||||
"mailbox": "ordinace@buzalkova.cz",
|
||||
"folder_id": "AAA...",
|
||||
"folder_path": "Inbox",
|
||||
"delta_link": "https://graph.microsoft.com/.../delta?$deltatoken=...",
|
||||
"last_run_at": "2026-06-04T10:00:00Z",
|
||||
"cumulative_new": 1234, "cumulative_sync": 5678, "cumulative_removed": 12, "run_count": 42
|
||||
}
|
||||
```
|
||||
|
||||
První běh = fresh delta (Graph vrátí všechno + dá `deltaLink`). Každý další = jen změny od poslední záložky.
|
||||
|
||||
## Co se stane se smazanými zprávami
|
||||
|
||||
Když delta vrátí `@removed` pro zprávu, skript ji **nemaže** z Mongo. Pouze nastaví:
|
||||
|
||||
```json
|
||||
{ "permanently_deleted": true, "permanently_deleted_at": "2026-06-04T10:00:00Z" }
|
||||
```
|
||||
|
||||
Dohledatelné: `col.find({"permanently_deleted": true})`.
|
||||
|
||||
**`@removed` přijde jen pro definitivně smazané** zprávy (uživatel vysypal koš / Shift+Del). Mail v `Deleted Items` je pořád normální zpráva, jen má `folder_path = "Deleted Items"`.
|
||||
|
||||
## Extrakce zprávy
|
||||
|
||||
Funkce `extract_message` a `extract_sync_fields` se načítají přímo z modulu `1_parse_emails_graph_v1.4.py` (přes `importlib`) — extrakční logika je jediná na celý projekt, nemůže se rozejít.
|
||||
|
||||
## Nové vs změněné — jak skript pozná
|
||||
|
||||
Pro každou položku z delta odpovědi:
|
||||
|
||||
1. **Má `@removed`?** → označit `permanently_deleted` v Mongo, hotovo.
|
||||
2. **`graph_id` už je v Mongo?** → existující změna — pošle se jen `extract_sync_fields` (is_read, flag, folder, …) přes `$set`.
|
||||
3. **`graph_id` v Mongo není?** → nová zpráva — udělá se druhý GET `/messages/{id}?$expand=attachments` (delta nepodporuje `$expand`), aby přišla těla, hlavičky i přílohy, a uloží se přes `extract_message` jako klasický nový dokument.
|
||||
|
||||
## Argumenty
|
||||
|
||||
| Argument | Povinný | Hodnoty | Default | Popis |
|
||||
|---|---|---|---|---|
|
||||
| `--mailbox` | **ne** | e-mail | (všechny) | Schránka = kolekce v Mongo. **Bez argumentu projede všechny** kolekce v `emaily` mimo `SKIP_MAILBOXES` a systémové (`attachments_index`, `sync_state`) |
|
||||
| `--folder` | ne | substring | (všechny) | Filtr složek (např. `Inbox` zahrne i `Inbox/Archive`) |
|
||||
| `--limit N` | ne | int | 0 (bez limitu) | Max položek na složku (test) |
|
||||
| `--reset` | ne | flag | false | Smaže všechny `deltaLink`y pro vybrané schránky → další běh začne od fresh delta |
|
||||
| `--dry-run` | ne | flag | false | Nic neuloží do Mongo, jen vypíše co by se stalo |
|
||||
|
||||
## SKIP_MAILBOXES (hardcoded ve skriptu)
|
||||
|
||||
| Schránka | Důvod |
|
||||
|---|---|
|
||||
| `vbuzalka@its.jnj.com` | JNJ tenant, nemáme Graph API přístup. Pro tuto schránku je nutný samostatný skript (lokální `.msg` parser nebo jiný zdroj). |
|
||||
|
||||
Při `--mailbox vbuzalka@its.jnj.com` skript skončí s exit kódem 2. Při běhu bez `--mailbox` se schránka tiše přeskočí s hlášením `[skip]`.
|
||||
|
||||
## Varianty volání
|
||||
|
||||
```bash
|
||||
# VŠECHNY schránky najednou (mimo SKIP_MAILBOXES) — pro cron / pravidelný sync:
|
||||
docker exec -it python-runner python /scripts/1b_parse_emails_graph_delta_v1.0.py
|
||||
|
||||
# Jedna schránka — první běh (fresh delta — projde všechno, uloží deltaLinky):
|
||||
docker exec -it python-runner python /scripts/1b_parse_emails_graph_delta_v1.0.py --mailbox ordinace@buzalkova.cz
|
||||
|
||||
# Pravidelný sync jedné schránky (jen změny od minulého běhu):
|
||||
docker exec -it python-runner python /scripts/1b_parse_emails_graph_delta_v1.0.py --mailbox ordinace@buzalkova.cz
|
||||
|
||||
# Dry-run — uvidíš co by se stalo, nic se neuloží:
|
||||
docker exec -it python-runner python /scripts/1b_parse_emails_graph_delta_v1.0.py --mailbox ordinace@buzalkova.cz --dry-run
|
||||
|
||||
# Test jen na složce Inbox, max 20 položek:
|
||||
docker exec -it python-runner python /scripts/1b_parse_emails_graph_delta_v1.0.py --mailbox ordinace@buzalkova.cz --folder Inbox --limit 20
|
||||
|
||||
# Reset — zahodí deltaLinky a najede znova od plné delta:
|
||||
docker exec -it python-runner python /scripts/1b_parse_emails_graph_delta_v1.0.py --mailbox ordinace@buzalkova.cz --reset
|
||||
|
||||
# Cron / na pozadí (každých 5 min):
|
||||
docker exec -d python-runner bash -c "python /scripts/1b_parse_emails_graph_delta_v1.0.py --mailbox ordinace@buzalkova.cz > /scripts/delta_sync.log 2>&1"
|
||||
```
|
||||
|
||||
## Co dělat na začátek
|
||||
|
||||
1. **První import** schránky pořád přes `1_parse_emails_graph_v1.4.py` (existující data zůstanou).
|
||||
2. **První běh** `1b_…delta_v1.0.py` — fresh delta projde znovu všechny zprávy a hlavně uloží `deltaLink`y do `sync_state`. To může chvíli trvat (podobně jako `--mode new-only` na v1.4).
|
||||
3. **Další běhy** = už jen rychlé, vrací 0-X změn za interval.
|
||||
|
||||
## Otevřené body k otestování
|
||||
|
||||
- Jak rychle běží první (fresh) delta na velké schránce (`vladimir.buzalka@buzalka.cz` ~80k mailů)
|
||||
- Co Graph vrátí pro nově vytvořené složky (mělo by fungovat — appendnou se do `folders` při dalším `get_all_folders`)
|
||||
- Chování při `--limit` (drží se starý deltaLink → pristi beh dokonci zbytek)
|
||||
|
||||
## HTTP 410 — expirovaný deltaLink
|
||||
|
||||
DeltaLinky drží Graph cca 30 dní. Pokud nebudeš schránku syncovat měsíc, skript dostane 410, **smaže starý state** a sám zopakuje běh jako fresh delta. Žádný manuální zásah není potřeba.
|
||||
|
||||
## Závislosti
|
||||
|
||||
Stejné jako `1_parse_emails_graph_v1.4.py` (msal, requests, pymongo, dateutil) — žádné nové.
|
||||
|
||||
## Sledování průběhu
|
||||
|
||||
```bash
|
||||
docker exec -it python-runner tail -f /scripts/delta_sync.log
|
||||
docker exec -it python-runner tail -f /scripts/delta_errors.log
|
||||
```
|
||||
|
||||
## Stav sync_state v Mongo
|
||||
|
||||
```python
|
||||
# Přehled posledních synců:
|
||||
db.sync_state.find().sort("last_run_at", -1)
|
||||
|
||||
# Zahodit deltaLinky pro jednu schránku (= efekt --reset):
|
||||
db.sync_state.delete_many({"mailbox": "ordinace@buzalkova.cz"})
|
||||
|
||||
# Najít všechny permanentně smazané v jedné schránce:
|
||||
db["ordinace@buzalkova.cz"].find({"permanently_deleted": true}, {"subject": 1, "permanently_deleted_at": 1})
|
||||
```
|
||||
Reference in New Issue
Block a user