Skip to content

feat(reports): add Reports tab — PDF diagnostics + Capture - #44

Open
Kimkhoi3010 wants to merge 2 commits into
mainfrom
feat/reports-db-panel
Open

Kimkhoi3010 wants to merge 2 commits into
mainfrom
feat/reports-db-panel

Conversation

@Kimkhoi3010

@Kimkhoi3010 Kimkhoi3010 commented Sep 22, 2026

Copy link
Copy Markdown
Contributor

Depend on

  • https://github.com/trobz/odoo-db/pull/34

Summary

Adds a Reports tab to the database pane — diagnostics + a capture tool for PDF report generation failures (wkhtmltopdf), built around a real incident: foodcoop18 staging, 2026-07-06/07, a Stock Valuation Report PDF that generated with no error but no CSS at all, only diagnosable by hand-finding and re-running the actual wkhtmltopdf command on the host.

Diagnostics

  • New Reports tab runs odoo-db's new wkhtmltopdf <db> command (trobz/odoo-db#<PR_NUMBER>) — report.url/report.delay, report_wkhtmltopdf_param (OCA reporting-engine) install state + its per-paperformat overrides if installed — rendered as its own set of tables like Mail/Neutralization, since it answers one nested object, not a flat row list.
  • Two host-level facts odoo-db can't see, read directly from the host: the actual wkhtmltopdf --version running (probes.wkhtmltopdf_version), and PDF-related pip packages in the instance's own venv (probes.pdf_pip_packages, reusing the existing VIRTUAL_ENV-resolution precedent _resolve_argv0 already uses).
  • Enable Capture is offered up front, before the odoo-db round trip — confirmed live against a database odoo-db can't reach at all (database "test" does not exist): Capture doesn't need odoo-db, so the button has to survive a Diagnostics fetch failing.

Capture

  • Enable Capture arms a watcher for every wkhtmltopdf process Odoo runs on that instance (not just the highlighted database — a ps row has no way to name which database on a multi-tenant instance triggered it). A dedicated 2s-interval timer, zero-cost when nothing is armed.
  • Each one caught gets its temp files (cookie jar, header/footer/body HTML) backed up under ~/.oa-reports/ before Odoo deletes them — usually within a second or two of the process exiting, so the backup has to happen the moment the process is seen, not after it exits.
  • Once anything is armed or caught, the tab switches from Diagnostics to the captured-report list — a normal row table (self._dbtab.rows), so it gets / search and Enter-to-open-raw-json ("Inspect": the full command, parsed <base href>/CSS asset links) for free, same as any other db tab.
  • Two new row-scoped keybindings: C copies the selected report's exact reproduce command to the clipboard, X runs it and shows the real result inline — the actual point of Capture, getting wkhtmltopdf's real error instead of guessing from Odoo's generic failure message.
  • Disable Capture stops the watcher without clearing what's already listed, or its backups on disk.
  • State (armed instances, captured list) lives at the app level, not the tab's — survives a tab switch or navigating to a different instance, which nothing pane-scoped does today (even the Logs tab's tail dies on every switch).

Result

Verified live against a real local Postgres database (seeded with realistic data — report.url deliberately stale, report_wkhtmltopdf_param installed with per-paperformat overrides) and a real running process standing in for an Odoo instance (a script matching _looks_like_odoo's detection, spawning a fake wkhtmltopdf child with real temp files on real trigger) — not mocked, not the automated test suite.

1. Diagnostics — full data

Screenshot from 2026-09-22 11-15-50

report.url/report.delay set (deliberately stale, the foodcoop18 shape), report_wkhtmltopdf_param shown installed, per-paperformat overrides table (A4, US Letter) rendering correctly.

2. Capture armed, nothing caught yet

Screenshot from 2026-09-22 11-15-58

Enable Capture pressed — confirm toast, tab switches to the (empty) captured-report table, action button flips to Disable Capture.

3. A real report caught

Screenshot from 2026-09-22 11-16-06

A real wkhtmltopdf process spawned on the fake instance — caught within one poll tick, listed as row #1 with pid, captured time, full command, and backup directory.

4. Inspect — raw json

Screenshot from 2026-09-22 11-16-11

Enter on the row — the generic raw-json drill-in every db tab already has, showing the full captured command plus base_href/css_links parsed out of the backed-up body HTML.

5. C — copy reproduce command

Screenshot from 2026-09-22 11-16-21

Confirms the exact captured command is on the clipboard, ready to paste into a real shell.

6. X — run reproduce command

Screenshot from 2026-09-22 11-16-43

Re-runs the captured command for real and shows the actual failure inline (HostNotFoundError on the stale report.url's asset URLs — mimicking exactly the foodcoop18 case) instead of Odoo's generic "report failed" message.

Backup directory on disk after step 3, confirmed to hold all four temp files (cookie jar, header, footer, body HTML):

$ ls ~/.oa-reports/local_test/162528-1790050563/
report.body.tmp.abc123.html    report.footer.tmp.abc123.html
report.cookie_jar.tmp.abc123.txt  report.header.tmp.abc123.html

Test plan

  • make check (ruff lint/format, ty)
  • make test (full suite, 210 passing)
  • New unit tests in tests/test_probes.py: parse_wkhtmltopdf_argv (temp files + positionals extracted, missing flags absent not crashing), parse_report_html (<base href> + known CSS links found, empty body handled), backup_wkhtmltopdf_capture (files saved under their basenames, unreadable file skipped without raising), wkhtmltopdf_version, pdf_pip_packages
  • New integration tests in tests/test_tui.py (real Textual pilot, arm_capture/_do_poll_capture/selected_db_row driven directly): full capture flow (baseline poll catches nothing, a genuinely new pid gets backed up and appears live with no manual refresh, Disable keeps the list), Diagnostics-before-anything-armed, C/X act on the row under the cursor
  • Manual, live, no mocking (see Result above): Diagnostics against a real seeded local database and against 7 real foodcoop18 staging databases; Capture against a real running process, real temp files, real backup directory; C/X against a real captured row
  • site-docs/docs/keybindings.md updated — new C/X bindings, Reports/Capture section

odoo-db's diagnostics command is now named reports (trobz/odoo-db#34),
so the tab name and the odoo-db command match by default — no override
needed, same as Mail today.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant