Skip to content

Tables tab: bulk delete and bulk dataset add/remove (#2565) - #2591

Open
jh-RLI wants to merge 2 commits into
developfrom
feature-2565-bulk-delete-datasets
Open

jh-RLI wants to merge 2 commits into
developfrom
feature-2565-bulk-delete-datasets

Conversation

@jh-RLI

@jh-RLI jh-RLI commented Oct 3, 2026 •

Copy link
Copy Markdown
Contributor

Closes #2565. Slice 13 of spec #2551.

What it does

The bulk bar gains Add to dataset…, Remove from dataset… and Delete… (red, last; the Organization actions of #2568 go before it). All three run through the table action service and the one action dialog from #2564. The selection still travels as one comma-joined tables field, and the bulk preflight is still a POST (login:table-action-check).

Bulk delete

  • The dialog lists every Table in a scrollable, keyboard-focusable list and marks the published ones.
  • What deleting breaks is counted, not repeated per Table: how many are published, reviewed or in review, and under an active embargo; which Datasets lose how many of them (other people's named with their owner); and the knowledge-graph links that stop resolving.
  • _delete_consequences now returns per-Dataset counts and review counts, in 3 queries instead of 5, constant in the batch size (tested at 2 and 20).
  • The typed count (any published Table, or more than TYPED_COUNT_ABOVE) and the failed drops named in a lasting warning were already the service's (Tables tab: delete a Table from the row, including published ones #2562). They are now tested through the bulk route.

Bulk Dataset add/remove

  • Which Tables are already in the chosen Dataset (add) or not in it (remove) depends on the choice. Choosing therefore asks the bulk preflight again, with the whole selection (selection).
  • The re-check swaps only the preview, the footer and a status line ("3 of 6 tables will be added to “X”."), never the select. The select keeps focus.
  • A closed select fires change on each arrow key, so the request waits until the choice has rested (delay:400ms), and a newer request replaces an older one (hx-sync).
  • The preview carries the Dataset it was checked against (previewed). A confirmation sent inside that delay names another Dataset: it writes nothing and gets the dialog checked against the Dataset sent.
  • A 409 or 400 re-run is checked against the whole selection again, so the Tables left out before stay named.
  • One transaction, all or none.

Ceilings (CEILINGS, reasoning beside the constant, stated in every bulk dialog before they are reached and again when exceeded):

metadata preflight add remove
6 KB 0.1-0.3 ms 2.5-3.0 ms 0.8-1.2 ms
60 KB 0.4-0.7 ms 2.7-3.3 ms 1.1-1.4 ms
500 KB 3.0-3.5 ms 5.0-6.8 ms 3.3-3.9 ms

At the worst 6.8 ms, 2,500 Tables take about 17 s against production's 300 s (Timeout 300 / socket-timeout=300): a safety factor of about 17. 2,500 rather than publish's 1,000 is a choice: it makes "select all" on the largest account (2,068 Tables) followed by "Add to dataset" a single request.

Ceiling messages now name the action in words ("Adding to a dataset takes at most …"), not by its identifier.

No migration, no deploy step.

Checked

  • login + base.tests.test_views + modelview + api.tests.test_datasets_api + api.tests.test_table_actions_api: 719 green.
  • Full suite: 1,508 tests, the only failure the known LightImportTest, which cannot find the isolated-settings module in its subprocess. Run on a private OEDB.
  • vitest: 202 green. No client module changed.
  • 28 new tests in login/tests/test_tables_bulk_delete_datasets.py.
  • Headless Chrome on a throwaway database, 16 Tables, two of them Data-editor-only:
    • Bulk add with three Tables already in the chosen Dataset: the re-check named them and posted only the other three.
    • Arrow keys through the closed select: focus stayed on it throughout, and three quick presses sent one request (server log).
    • Bulk delete of all 16: the two editor-only Tables were left out with the reason; consequences counted; a wrong count kept the dialog open with the error; the right count deleted 14 in one request and the toast offered "Show tables".
    • At 390 px the "Actions ▾" menu holds all five actions with Delete in red, and the page has no horizontal scroll.

For the maintainer

  1. Dataset ceiling 2,500 rather than 1,000 (see above). Easy to lower if you prefer one number for every non-delete action.
  2. The delete dialog names Datasets by name (ds_wind), as Tables tab: delete a Table from the row, including published ones #2562's row dialog does, while the Dataset chooser shows titles. Switching the consequences to titles is a small follow-up if wanted.
  3. others_datasets has two shapes: (owner, name, count) for delete, (owner, name) for unpublish. Unpublish was left as it was. Unifying them would touch Tables tab: selection, bulk bar and bulk publish/unpublish #2564's tests.
  4. The bulk dialog's title counts the names sent ("Delete 16 tables") while the list says "Will be deleted (14)". That is the existing subject of Tables tab: selection, bulk bar and bulk publish/unpublish #2564.

🤖 Generated with Claude Code

jh-RLI and others added 2 commits October 3, 2026 02:10
The bulk bar gains "Add to dataset…", "Remove from dataset…" and "Delete…"
(red, last), all through the table action service and the one action
dialog #2564 built.

Bulk delete lists every Table in a scrollable, focusable list with the
published ones marked, and counts what deleting breaks instead of
repeating it per Table: how many are published, reviewed or in review,
under an active embargo, which Datasets lose how many of them (other
people's by owner), and the knowledge-graph links that stop resolving.
_delete_consequences now returns the per-Dataset counts and the review
counts, in three queries instead of five. The typed count and the failed
drops named in a lasting warning were already the service's; they are now
tested through the bulk preflight and the joined `tables` field.

Bulk Dataset actions: which Tables are already in the chosen Dataset (add)
or not in it (remove) depends on the choice, so choosing re-asks the bulk
preflight with the whole selection (`selection`) and swaps only the
preview, the footer and a status line, never the select. A closed select
fires `change` on each arrow key, so the request waits until the choice
has rested (delay 400 ms) and a newer one replaces an older one; checked
in headless Chrome: focus stays on the select, three quick arrow presses
send one request. A confirmation sent before the re-check came back is
refused whole and the dialog re-run, as before.

Ceilings: adding to and removing from a Dataset take at most 2,500 Tables,
measured with the new benchmarks/tables_tab/dataset_cost.py (worst 6.8 ms
per Table at 500 KB of metadata, about 17 s for 2,500 against the host's
300 s); every measured account (2,068 Tables at most) fits one request.
The ceiling message names actions in words ("Adding to a dataset takes at
most ...").

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
From review. The Dataset re-check waits 400 ms before it is sent, and
Confirm is only disabled once it is, so a confirmation in that window
posted names checked against the previous choice. The preview now carries
the Dataset it was checked against (`previewed`); a confirmation naming
another one writes nothing and gets the dialog checked against the one
sent, with a notice.

A dialog re-run after a refusal (409) or an unusable parameter (400) is
checked against the whole selection again, and the 400 keeps the chosen
Dataset, so the Tables left out before are still named.

A bulk dialog states its action's ceiling before it is reached ("Delete
takes at most 50 tables at a time."), as the issue asks, not only once it
is exceeded. Preflight.dataset_title replaces three copies of the same
template expression.

Tests: no more assertions on classes or attributes; the log's batch field
is matched by name; `text()` moved to modelview/tests/html.py beside
`element_markup`; the joined-field helpers no longer shadow the
repeated-field ones. benchmarks/tables_tab/README.md documents
dataset_cost.py.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

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.

Tables tab: bulk delete and bulk dataset add/remove

1 participant