--- okf: "0.1" type: session-log visibility: private status: active updated: 2026-06-24 links: [] --- # Sesja 2026-06-24 — KB etap 2: importer Gmail (kod gotowy) ## Cel Zbudowanie jednorazowego bulk importera Gmail Takeout (`mbox → archiwum .eml + envelope DB`). Import nie uruchomiony w tej sesji — Takeout na SOLARIA, transfer+wlew = następny krok. --- ## Stan infrastruktury po sesji ### kb-postgres na PIHA (korekta z etapu 1) Baza **przeniesiona na PIHA**, nie na SOLARIA jak zakładał pierwotny plan. - `Memory: hard=1g` zaaplikowany po reboocie (wymagał `cgroup_enable=memory` w cmdline + `docker-compose up --force-recreate`). - Status: `healthy`, port `5433`. ### Google Takeout (Gmail „All Mail") - Pobrany: **~15 GB zip**, po rozpakowaniu **~27 GB mbox**, jeden plik. - Lokalizacja: `SOLARIA ~/Downloads` — jeszcze nie rozpakowany, nie zaimportowany. --- ## Etap 2 — ZBUDOWANY (kod gotowy) ### Nowa konwencja: `jobs//` Jednorazowe i periodyczne joby (nie serwisy) żyją w `jobs//`. | Katalog | Co tu trafia | |---------|-------------| | `packages//` | reużywalne biblioteki Python (nie deployowane samodzielnie) | | `services//` | długo żyjące serwisy Docker | | `jobs//` | one-shot i periodyczne joby CLI | Instalacja lokalnie: `pip install -e packages/kb-mail/ && pip install -e jobs/gmail-bulk-import/` Bez Dockera — odpalany bezpośrednio na PIHA pod `nice`/`ionice`. ### jobs/gmail-bulk-import Wejście: plik `.mbox` z Google Takeout. Wyjście: `.eml` w archiwum (append-only, PIHA NVMe) + wiersze `envelope` w kb-postgres. Kluczowe decyzje implementacyjne: | Kwestia | Decyzja | |---------|---------| | ID wiadomości | `Message-ID` header (stripped `<>`); fallback: `sha256-<32hex>` treści | | Timestamp | `Date` header → UTC; fallback epoch 1970-01-01 + licznik `epoch_fallback` | | Źródło | `source=gmail` | | Idempotencja | `FileExistsError` z archiwum → skip; `ON CONFLICT DO NOTHING` w DB | | Wznawialność | mbox iterowany od początku; już zarchiwizowane = skip; already in DB = noop | | Batch inserty | `executemany` co 500 wpisów (+ flush na końcu); `_eml_ref` rekonstruuje `raw_ref` dla skipped | | Docker | **brak** — CLI bez konteneryzacji | ### Załączniki → entities[] W tym etapie bajty załączników **zostają w .eml** — nie są ekstrahowane ani OCR-owane. Importer zapisuje **manifest** do `Envelope.entities[]`: ```json { "type": "attachment", "filename": "faktura.pdf", "content_type": "application/pdf", "size": 42387, "sha256": "a3f4..." } ``` Powiązanie mail↔załącznik w bazie od dnia zero. Ekstrakcja/OCR = **faza 2** (możliwe przekierowanie faktur/umów do Paperless, filar dokumentów). ### CLI ```bash # Dry run — tylko liczenie i parsowanie, bez zapisów: gmail-bulk-import --mbox ~/takeout/allmail.mbox --archive /data/kb/archive --dry-run # Próbka 200 wiadomości (przed pełnym wlewem): gmail-bulk-import --mbox ~/takeout/allmail.mbox --archive /data/kb/archive \ --dsn postgresql://kb:@localhost:5433/kb --limit 200 # Pełny wlew na PIHA pod nice/ionice: ionice -c 3 nice -n 19 gmail-bulk-import \ --mbox ~/takeout/allmail.mbox \ --archive /data/kb/archive \ --dsn postgresql://kb:@localhost:5433/kb ``` ### Statystyki zwracane przez importer ``` processed, imported, skipped, errors, epoch_fallback, ← maile bez parsowalnej daty msgs_with_attachments, ← sizing fazy 2 total_attachments, total_attachment_bytes ``` ### Testy - 24 testy jednostkowe (bez DB, bez sieci) — wszystkie zielone. - Pokrycie: `_message_id`, `_parse_date`, `_parse_attachments`, `run_import` (dry-run, archiwum, idempotencja, --limit, epoch_fallback, statystyki załączników, batch). --- ## GOTCHA tej sesji Pierwsza iteracja importera (commit 57a27af) celowała w `solaria:5433` zamiast PIHA, pominęła `entities[]` załączników, `--limit`, batch inserty i `epoch_fallback`. Złapane w review, poprawione w osobnym commicie. **Lekcja**: prompt musi explicite podać host bazy = PIHA (nie SOLARIA). --- ## Następny krok 1. `rsync` Takeout SOLARIA → PIHA po Tailscale (27 GB mbox na NVMe PIHA). 2. `gmail-bulk-import ... --dry-run` — weryfikacja liczby wiadomości. 3. `gmail-bulk-import ... --limit 200` — próbka, weryfikacja jakości. 4. Pełny wlew pod `ionice -c 3 nice -n 19`. 5. Weryfikacja: `ls archive/gmail/ | wc` ≈ `SELECT count(*) FROM envelope WHERE source='gmail'` ≈ liczba wiadomości w mboxie. --- ## Backlog (osobne zadania) - `packages/kb-mail/tests/test_db.py`: `KB_TEST_DSN` defaultuje do `localhost:5433/kb` — poprawne dla PIHA, ale warto sprawdzić wszystkie occurrences `solaria:5433` w testach. - Etap 3: Fastmail JMAP live ingest → `jobs/fastmail-poller/`. - Etap 4: Gmail IMAP live sync → `jobs/gmail-imap-poller/`. - Zapis deklaratywny `cgroup_enable=memory + swapaccount=1` dla PIHA (firmware/cmdline — poza GitOps, udokumentować w `hosts/piha/host.yaml` lub README). --- ## Higiena git - Praca w worktree `task/kb-gmail-import`; master deploy-only, czysty przez cały czas. - 4 commity na branchu po zamknięciu sesji. --- ## Commits ``` 57a27af feat(kb-mail): etap 2 — jednorazowy bulk importer Gmail (mbox → archiwum) f0e4d90 refactor(kb-mail): importer Gmail — entities załączników, --limit, batch, bez Dockera, DSN→PIHA c5dd8f3 fix(piha): capabilities — realny RAM/NVMe + gitignore build dirs docs(kb): sesja 2026-06-24 — importer Gmail gotowy + konwencja jobs/ ``` ## Files changed (etap 2) ``` jobs/gmail-bulk-import/src/gmail_bulk_import/__init__.py jobs/gmail-bulk-import/src/gmail_bulk_import/importer.py jobs/gmail-bulk-import/pyproject.toml jobs/gmail-bulk-import/tests/test_importer.py hosts/piha/capabilities.yaml .gitignore kb/subsystems/kb-overview.md (etap 2 gotowy, konwencja jobs/) kb/subsystems/kb-mail-pillar.md (§8 krok 2 = kod gotowy) docs/sessions/2026-06-24-kb-gmail-importer.md ```