Skip to content

Add column groups support - #345

Open
AndreLobatoSeequent wants to merge 8 commits into
SeequentEvo:mainfrom
AndreLobatoSeequent:add-column-groups-support
Open

Add column groups support#345
AndreLobatoSeequent wants to merge 8 commits into
SeequentEvo:mainfrom
AndreLobatoSeequent:add-column-groups-support

Conversation

@AndreLobatoSeequent

@AndreLobatoSeequent AndreLobatoSeequent commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Description

Adds full column-group support to the high-level BlockModelAPIClient for the block model service. Previously the SDK could only read groups off a version; callers had to drop down to the generated endpoint layer to create, assign, or modify them. This change exposes the complete group lifecycle — create, assign, read, update and delete — through the public client and data model.

Column groups are a preview feature: the client must be constructed with preview=True to use them, consistent with column tags. The design goal is for the SDK to mirror the service: where the service addresses columns by their qualified (group▸title) title, the caller supplies that same qualified title. The SDK does not invent a plain-title abstraction, rename your data, or perform hidden version lookups — this avoids ambiguity when the same title is reused across groups.

Write path

  • Create / update / delete groups — new BlockModelAPIClient.update_groups(bm_id, new=, update=, delete=, comment=). Create groups with GroupDefinition (title, parent group, missing-column policy, tags, hidden flag); rename, re-parent, change policy, replace tags or toggle visibility with GroupMetadataUpdate; delete by title. Any combination can be applied in a single call.
  • Group a new column — the column_groups parameter on add_new_columns, add_new_subblocked_columns, update_block_model_columns and update_subblocked_columns maps a new column's qualified title (its key in data, e.g. Assays▸Cu) → the group it belongs to (Assays). The SDK derives the title by stripping the group prefix.
  • Move or ungroup an existing column — update_column_metadata with ColumnMetadataUpdate(group=...). A column's group is metadata, so moving it needs no data upload. Address the column by its current qualified title and set group to the new group's qualified title (or "" to ungroup).

Column titles

The caller keys data by each column's title: a plain title for an ungrouped column, or the qualified group▸title for a grouped one. The SDK uploads the table verbatim and never renames columns. Two opt-in helpers are provided to build these titles from plain-titled data:

  • get_qualified_title(group, title) — builds a single qualified title.
  • qualify_column_titles(data, {title: group}) — renames a plain-titled table and returns (renamed_table, column_groups) ready to pass straight to the client.

Read path

  • Version / ListingVersion gain bridge helpers so a caller who wrote a group by title can find it again on the returned version without hand-rolling a lookup: group_by_uuid, group_for_column, qualified_group_title and group_by_qualified_title.

Public API

  • New exports from evo.blockmodels.data:
    • GroupDefinition
    • GroupMetadataUpdate
    • MissingColumnPolicy
    • QUALIFIED_TITLE_SEPARATOR
    • get_qualified_title and qualify_column_titles
  • get_qualified_title, qualify_column_titles and QUALIFIED_TITLE_SEPARATOR are also re-exported from the evo.blockmodels package root.

Notes

  • New parameters are appended to existing method signatures to preserve positional-argument compatibility.
  • Existing columns (in update_columns / delete_columns) are addressed by the exact title the service currently stores them under: the qualified group▸title if grouped, or the plain title if not. There is no hidden lookup — the caller states the current reference.
  • Moving or ungrouping an existing column is a metadata-only operation (no data upload) via update_column_metadata; ColumnMetadataUpdate accepts a group field ("" ungroups). The column_groups parameter on the column methods only groups new columns.
  • Groups cannot be referenced during create_block_model (none exist yet); the documented flow is create the model → update_groups(new=...) → add columns with column_groups.
  • Input models (GroupDefinition, GroupMetadataUpdate, ColumnMetadataUpdate) reject unknown fields to catch typos early.
  • Adds test coverage for grouping new columns, metadata-only moving/ungrouping of existing columns, data-only updates of grouped columns, group create/update/delete, the get_qualified_title / qualify_column_titles helpers, preview-header propagation, input validation, and all bridge helpers.

Checklist

  • I have read the contributing guide and the code of conduct

feature; the client must be constructed with ``preview=True`` to use them.
:param column_groups: A dictionary mapping column names within `data` to the qualified title of the group the
column should be placed in. Column groups are a preview feature; the client must be constructed with
``preview=True`` to use them.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Column groups are a preview feature; the client must be constructed with
preview=True to use them

We are aiming to remove api-preview flag very soon, perhaps we could avoid this sentence.

Comment thread packages/evo-blockmodels/src/evo/blockmodels/client.py
AndreLobatoSeequent and others added 2 commits September 1, 2026 11:37
Make the block model column-group API reflect the service instead of
adding a bare-title abstraction layer:

- Callers key `data` by each column's exact upload heading (bare for
  ungrouped, qualified `group▸leaf` for grouped); the SDK uploads the
  table verbatim and never renames columns.
- `column_groups` maps a new column's qualified heading → group, or an
  existing column's current qualified title → new group (`""` ungroups).
- `update_columns` / `delete_columns` use the column's current stored
  title (qualified if grouped, bare otherwise). Removed the hidden
  latest-version fetch that translated bare → qualified titles.
- Add opt-in `qualified_heading` and `qualify_headings` helpers (and a
  `Table.rename_columns` protocol method) to build qualified headings
  from bare-titled data, re-exported from the package root.
- Update docstrings and rewrite/extend tests for the new contract.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@AndreLobatoSeequent
AndreLobatoSeequent marked this pull request as ready for review September 1, 2026 02:03
@AndreLobatoSeequent
AndreLobatoSeequent requested a review from a team as a code owner September 1, 2026 02:03

@rohancrookbain-seequent rohancrookbain-seequent left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Partially reviewed; will have a more complete look soon!

Comment thread packages/evo-blockmodels/src/evo/blockmodels/client.py Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds column-group lifecycle and column assignment/move support to the evo.blockmodels high-level client, along with new public data models and helpers for qualified column headings (preview-gated via API-Preview: opt-in).

Changes:

  • Adds group CRUD/update support via BlockModelAPIClient.update_groups(...) and wires groups into the update payload.
  • Adds column_groups support for new columns and for moving/ungrouping existing columns during data re-upload.
  • Introduces qualified-title utilities and Version group-lookup helpers, plus expanded test coverage.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/evo-blockmodels/tests/test_update.py Adds tests for grouped column creation, group moves/ungrouping via re-upload, and update_groups behavior.
packages/evo-blockmodels/tests/test_group_helpers.py New tests covering Version group helper methods and extra-field validation and heading qualification helpers.
packages/evo-blockmodels/src/evo/blockmodels/data.py Adds group input models, qualified-title helpers, and Version group bridge helpers.
packages/evo-blockmodels/src/evo/blockmodels/client.py Implements group update endpoint support and column group assignment/move logic with validation and payload shaping.
packages/evo-blockmodels/src/evo/blockmodels/_types.py Extends Table protocol with rename_columns(...) to support heading qualification helper.
packages/evo-blockmodels/src/evo/blockmodels/init.py Re-exports qualified-title helpers and separator at package root.
packages/evo-blockmodels/pyproject.toml Bumps package version to 0.7.0.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread packages/evo-blockmodels/src/evo/blockmodels/data.py Outdated
Comment thread packages/evo-blockmodels/src/evo/blockmodels/data.py

@rohancrookbain-seequent rohancrookbain-seequent left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good :)

Comment on lines +154 to +157


if __name__ == "__main__":
unittest.main()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: Shouldn't this be at the end of the file?

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Preview-only group functionality is documented but not consistently enforced in the client (and one test currently exercises group updates without preview opt-in), which can lead to runtime API failures and contract mismatches.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (2)

packages/evo-blockmodels/src/evo/blockmodels/client.py:912

  • Same as _add_new_columns(): _update_columns() can receive a non-empty column_groups mapping when preview=False, but will send the payload without the preview opt-in header. Guard early to match the documented preview contract and avoid hard-to-debug API errors.
        if column_groups is None:
            column_groups = {}
        data_type_map = {name: data_type for name, data_type in zip(schema.names, schema.types)}

packages/evo-blockmodels/tests/test_update.py:967

  • After switching to the preview client for ungrouping, this assertion should also include the preview opt-in header; otherwise the test will accept behavior that contradicts the preview-only contract for column groups.
            method=RequestMethod.PATCH,
            path=f"{self.base_path}/block-models/{BM_UUID}/blocks",
            body=expected_update_body.model_dump(mode="json", exclude_unset=True),
            headers=DEFAULT_EXPECTED_HEADERS,
  • Files reviewed: 7/7 changed files
  • Comments generated: 5
  • Review effort level: Lite

Comment on lines +808 to +821
if column_groups is None:
column_groups = {}
new_column_names = {name for name in schema.names if name not in _GEOMETRY_COLUMNS}
unknown_unit_columns = set(units) - new_column_names
if unknown_unit_columns:
raise MissingColumnInTable(f"units reference columns that are not being added: {unknown_unit_columns}")
unknown_tag_columns = set(tags) - new_column_names
if unknown_tag_columns:
raise MissingColumnInTable(f"tags reference columns that are not being added: {unknown_tag_columns}")
unknown_group_columns = set(column_groups) - new_column_names
if unknown_group_columns:
raise MissingColumnInTable(
f"column_groups reference columns that are not being added: {unknown_group_columns}"
)
Comment on lines +1122 to 1126
A column's group is metadata, so it can be moved (or ungrouped) here without re-uploading its
data. Address the column by the title the service currently stores it under: its qualified title
(``group▸title``) if it is currently grouped, or its plain title if it is not. Set
``ColumnMetadataUpdate(group=...)`` to the target group's qualified title (or ``""`` to ungroup).

Comment on lines +1190 to +1191
if not new and not update and not delete:
raise ValueError("At least one of 'new', 'update' or 'delete' must be provided.")
job_response=JobResponse(job_status=JobStatus.COMPLETE, payload=UPDATED_VERSION),
)
)
await self.bms_client_without_cache.update_column_metadata(
Comment on lines +156 to +158
if __name__ == "__main__":
unittest.main()

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.

3 participants