Session logi zostaja w docs/sessions/ (decyzja z etapu 1). Dodany wylacznie blok frontmattera: type: session-log, visibility: private, status: active, updated = data ostatniego commita pliku. Tresc nietknieta — kazdy plik to +9/-0 linii. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
5.9 KiB
| okf | type | visibility | status | updated | links |
|---|---|---|---|---|---|
| 0.1 | session-log | private | active | 2026-06-24 |
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=1gzaaplikowany po reboocie (wymagałcgroup_enable=memoryw cmdline +docker-compose up --force-recreate).- Status:
healthy, port5433.
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/<name>/
Jednorazowe i periodyczne joby (nie serwisy) żyją w jobs/<name>/.
| Katalog | Co tu trafia |
|---|---|
packages/<lib>/ |
reużywalne biblioteki Python (nie deployowane samodzielnie) |
services/<svc>/ |
długo żyjące serwisy Docker |
jobs/<job>/ |
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[]:
{
"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
# 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:<pw>@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:<pw>@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
rsyncTakeout SOLARIA → PIHA po Tailscale (27 GB mbox na NVMe PIHA).gmail-bulk-import ... --dry-run— weryfikacja liczby wiadomości.gmail-bulk-import ... --limit 200— próbka, weryfikacja jakości.- Pełny wlew pod
ionice -c 3 nice -n 19. - 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_DSNdefaultuje dolocalhost:5433/kb— poprawne dla PIHA, ale warto sprawdzić wszystkie occurrencessolaria:5433w 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=1dla PIHA (firmware/cmdline — poza GitOps, udokumentować whosts/piha/host.yamllub 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> 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
docs/kb/kb-00-overview.md (etap 2 gotowy, konwencja jobs/)
docs/kb/kb-01-email-design.md (§8 krok 2 = kod gotowy)
docs/sessions/2026-06-24-kb-gmail-importer.md