feat(saved): saved, shared, starred, scheduled and widget analyses, export, SQL handoff #10

Merged
KofTwentyTwo merged 14 commits from feature/saved into develop 2026-10-14 12:10:02 +00:00
KofTwentyTwo commented 2026-10-14 02:23:29 +00:00 (Migrated from github.com)

Summary

Phase (f) of docs/DATA-ANALYSIS.md (§7, §8, §9.3). Stacked on #9 → #8 → #6 → #5. Draft until RC.6.

Extension 1.4.0, with 21 operations under /qqq/v1/qbit/data-analysis:

  • Saved analyses, any of the 7 kinds, as typed definition_json validated on save.
    • POST /saved/{id}/run re-runs as the viewer: it needs READ on the saved row plus <table>.analyze, and fields the viewer can't analyze are dropped with a note.
    • GET /saved lists the viewer's visible analyses, with the starred flag.
  • Shared READ through core record locks. A WRITE lock covers owner_user_id. A READ lock covers a hidden read_owner_user_id projection, set by core customizers to the owner for private rows and to null for shared rows.
    • Owner-only writes and the admin all-access key are unchanged; no custom query filter is involved.
    • A database CHECK (shared OR read_owner_user_id IS NOT NULL) makes a row written outside QQQ fail closed.
    • The README has the migration for existing hosts. Collaboration's sharing contract (#1136) will replace the flag.
  • Stars: PUT / DELETE /saved/{id}/star. They move to collaboration_favorite later.
  • Schedules:
    • the hidden process data_analysis_run_schedules runs every 5 minutes and computes the next run from frequency, day, time and time zone (or cron);
    • each run happens as the owner, and mail goes only to recipients who hold READ, .analyze and FULL field access; others are skipped and listed in last_status;
    • mail is an HTML summary plus a CSV/XLSX attachment through core messaging;
    • failures raise data.analysis.schedule.failed;
    • "Send now" record action.
    • An attachment-capable messaging provider is required. Stock core EMAIL is refused explicitly in last_status, never silently.
  • Export: GET /saved/{id}/export?format=csv|xlsx and POST /export (file specs).
  • SQL Workbench handoff: POST /tables/{t}/sql, available only when qbit-sql-workbench is installed and permitted. It returns the backend's statement, with a note that QQQ applies security filters at run time.
  • Widgets: a DATA_ANALYSIS_SAVED widget that renders a saved analysis as the viewer and links back. The Analyses home is now standard widgets: a statistics row, starred analyses, firing watches and my recent runs, all real counts with honest empty states.
  • Every endpoint has assistant-oriented descriptions, and the operations appear in UiApiInvoker.listOperations().

Test plan

  • ./mvnw verify with Docker: green on memory, H2 and Postgres (~300 tests). JaCoCo ~97% instructions / 100% classes.
  • Save and re-run as another viewer (shared, unshared, dropped fields); definition validation; star toggle and list
  • Schedules: due computation (time zones, weekly and monthly), run as owner, recipient filtering, email through a capturing provider with attachment content, the failure alert
  • CSV/XLSX content; SQL handoff (H2 available, memory not); widget data; home widgets with real rows and empty states; HTTP endpoints; DDL CHECK

🤖 Generated with Claude Code

## Summary Phase (f) of `docs/DATA-ANALYSIS.md` (§7, §8, §9.3). **Stacked on #9 → #8 → #6 → #5.** Draft until RC.6. Extension 1.4.0, with 21 operations under `/qqq/v1/qbit/data-analysis`: - **Saved analyses**, any of the 7 kinds, as typed `definition_json` validated on save. - `POST /saved/{id}/run` re-runs as the **viewer**: it needs READ on the saved row plus `<table>.analyze`, and fields the viewer can't analyze are dropped with a note. - `GET /saved` lists the viewer's visible analyses, with the starred flag. - **Shared READ through core record locks.** A WRITE lock covers `owner_user_id`. A READ lock covers a hidden `read_owner_user_id` projection, set by core customizers to the owner for private rows and to null for shared rows. - Owner-only writes and the admin all-access key are unchanged; no custom query filter is involved. - A database `CHECK (shared OR read_owner_user_id IS NOT NULL)` makes a row written outside QQQ fail closed. - The README has the migration for existing hosts. Collaboration's sharing contract (#1136) will replace the flag. - **Stars:** `PUT` / `DELETE /saved/{id}/star`. They move to `collaboration_favorite` later. - **Schedules:** - the hidden process `data_analysis_run_schedules` runs every 5 minutes and computes the next run from frequency, day, time and time zone (or cron); - each run happens **as the owner**, and mail goes only to recipients who hold READ, `.analyze` and FULL field access; others are skipped and listed in `last_status`; - mail is an HTML summary plus a CSV/XLSX attachment through core messaging; - failures raise `data.analysis.schedule.failed`; - "Send now" record action. - An attachment-capable messaging provider is required. Stock core EMAIL is refused explicitly in `last_status`, never silently. - **Export:** `GET /saved/{id}/export?format=csv|xlsx` and `POST /export` (file specs). - **SQL Workbench handoff:** `POST /tables/{t}/sql`, available only when qbit-sql-workbench is installed and permitted. It returns the backend's statement, with a note that QQQ applies security filters at run time. - **Widgets:** a `DATA_ANALYSIS_SAVED` widget that renders a saved analysis as the viewer and links back. The **Analyses home** is now standard widgets: a statistics row, starred analyses, firing watches and my recent runs, all real counts with honest empty states. - Every endpoint has assistant-oriented descriptions, and the operations appear in `UiApiInvoker.listOperations()`. ## Test plan - [x] `./mvnw verify` with Docker: green on memory, H2 and Postgres (~300 tests). JaCoCo ~97% instructions / 100% classes. - [x] Save and re-run as another viewer (shared, unshared, dropped fields); definition validation; star toggle and list - [x] Schedules: due computation (time zones, weekly and monthly), run as owner, recipient filtering, email through a capturing provider with attachment content, the failure alert - [x] CSV/XLSX content; SQL handoff (H2 available, memory not); widget data; home widgets with real rows and empty states; HTTP endpoints; DDL CHECK 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign in to join this conversation.