Skip to content

Choose per file column whether to add, fill or skip on metadata import - #134

Merged
AStaroverov merged 5 commits into
mainfrom
feat/metadata-import-remap
Sep 10, 2026
Merged

AStaroverov merged 5 commits into
mainfrom
feat/metadata-import-remap

Conversation

@AStaroverov

@AStaroverov AStaroverov commented Sep 10, 2026 •

Copy link
Copy Markdown
Contributor

Why

A project created from a template gets its datasets and metadata columns without any values, so after applying a template the user re-imports a metadata file. Until now the import matched file columns to project columns by name only: a column whose header differed from the project's column silently became a second, new column, and there was no way to say "this column of my file is the project's Tissue".

What changed

Every column of the file now carries its own action in the import dialog:

  • Add as a new column — create a metadata column from the header.
  • Fill "" — write into an existing column of the project, keeping that column's identity. Only type-compatible columns are offered.
  • Do not import — leave the column out.

Columns whose name and type match an existing column are pre-set to fill it, and each project column is auto-claimed at most once, so an import nobody touches behaves exactly as before.

A mode switch above the list picks between Keep existing columns and Replace all metadata. Replace deletes every existing column and rebuilds the metadata from the file, with a warning that blocks downstream lose the columns they were pointed at and the project has to be re-checked.

Three warnings guard the mapping:

  • two file columns writing into one project column (the rightmost wins);
  • a new column created under a name the project already uses — the workflow exports metadata PColumns with the label in the domain, so two columns with one name collide;
  • project columns the file leaves untouched.

Values are now checked against the target column's type on write: numbers are stringified for String columns, strings no longer land in numeric columns. This also affects the samplesheet import, which shares the same helpers.

Notes

Filling an existing column reuses its column object rather than renaming anything, which keeps the pl7.app/columnId domain stable and leaves downstream block selections valid.

Testing

ui/src/utils/metadata.test.ts covers the default mapping, the three actions, a target column deleted mid-dialog, two columns filling one, replace mode and the type-checked writes. pnpm check, pnpm test and pnpm build pass in the ui package.

🤖 Generated with Claude Code

Greptile Summary

This PR adds explicit per-file-column metadata import actions, preserves existing column identities when filling them, supports replacing all metadata, prevents normalized-label collisions, and enforces value-type compatibility during writes. It also adds focused utility tests and refreshes the shared Platforma build and SDK dependencies.

Important touched terms

  • ImportMode — Determines whether an import keeps existing metadata columns or deletes and rebuilds them. This PR introduces keep and replace modes and exposes them in the dialog.
  • ColumnTarget — Describes one imported column’s action. This PR defines new, existing, and ignore targets.
  • MetadataColumnMapping — Maps each source-file column index to its ColumnTarget. This PR adds the mapping as dialog state and passes it into column resolution.
  • defaultColumnMapping — Produces untouched-import defaults. It now auto-fills the first compatible, normalized-name match, avoids claiming an existing column twice, and marks skipped columns as ignored.
  • resolveMetadataColumns — Converts mappings into existing or newly created model columns. It now preserves selected column identities, recovers from deleted targets, and assigns collision-free labels.
  • isColumnTypeCompatible — Defines allowable source-to-target type relationships. String targets accept numeric sources, Double targets accept Double or Long, and Long targets accept only Long.
  • populateMetadataFromRow — Writes imported values into resolved metadata columns. It now stringifies numeric values for String columns and rejects strings for numeric columns.
  • Normalized metadata label — A case-insensitive, trimmed label used for matching and collision detection. This PR centralizes normalization and suffixes colliding new labels.
  • Unfilled existing column — A project metadata column not targeted by any imported file column. This PR identifies these columns and informs users that their current values remain unchanged.

Confidence Score: 5/5

The PR appears safe to merge, with no outstanding actionable failures identified in the current changes.

The current implementation consistently resolves each source column to an ignored, new, or compatible existing target, prevents normalized-label collisions, and preserves existing column identity when requested. Both previous findings were manually resolved and therefore do not remain outstanding.

Important Files Changed

Filename Overview
ui/src/dialogs/ImportMetadataDialog.vue Adds keep/replace modes, per-column target selection, mapping warnings, and import execution based on resolved columns.
ui/src/utils/metadata.ts Adds mapping and type-compatibility primitives, collision-free column resolution, and type-checked metadata writes.
ui/src/utils/metadata.test.ts Covers default mappings, all target actions, stale targets, duplicate fills, replacement, label collisions, and typed writes.
pnpm-workspace.yaml Refreshes shared Platforma SDK and TypeScript build-tool catalog versions.
pnpm-lock.yaml Regenerates dependency resolutions for the updated workspace catalog.
.changeset/metadata-import-column-actions.md Records minor releases for the UI and block packages and describes the new import behavior.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Imported file column] --> B{Sample-name column?}
    B -- Yes --> C[Ignore for metadata]
    B -- No --> D{Import mode}
    D -- Replace --> E[Create collision-free new column]
    D -- Keep --> F{Configured target}
    F -- Ignore --> C
    F -- New --> E
    F -- Existing --> G{Target still exists?}
    G -- Yes --> H[Reuse existing column identity]
    G -- No --> E
    E --> I[Write type-checked values]
    H --> I
Loading

Reviews (2): Last reviewed commit: "Never create a metadata column under a n..." | Re-trigger Greptile

AStaroverov and others added 2 commits September 10, 2026 15:52
A project seeded from a template carries its metadata columns with no
values, so the import has to be able to point a file column at an
existing column rather than only match it by name. Every column of the
file now gets its own action: become a new metadata column, fill an
existing column of the project, or stay out. Name and type matches are
pre-selected, so an import nobody touches behaves as it did.

A second mode replaces the project's metadata wholesale, warning that
blocks downstream lose the columns they were pointed at.

Values are checked against the target column's type on write: numbers
are stringified for String columns, strings no longer land in numeric
ones.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comment thread ui/src/utils/metadata.ts
Comment thread ui/src/dialogs/ImportMetadataDialog.vue Outdated
A global metadata column is exported with its label in the PColumn
domain, so two columns sharing a label are exported as one and lose
each other's values. Two paths could produce that pair: a file column
whose name matches a project column it cannot fill for want of a
compatible type, and two columns of one file whose headers differ only
in case or padding.

Column creation now settles the clash by numbering the newcomer, so
neither the metadata dialog nor the samplesheet import can build a
colliding pair. The dialog reports the columns it had to rename and
points at filling the existing column instead.

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

Copy link
Copy Markdown
Contributor Author

@greptileai

AStaroverov and others added 2 commits September 10, 2026 16:44
The `d`-flag indices array now types every entry as possibly absent, so
destructuring one without a check stopped compiling under the TypeScript
that came with the dependency update. Both reads are of entries that are
always there: the whole match, and a group the caller has already found
in the match. Asserting them keeps the parser as it was and gets the UI
package type checking again.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A non-null assertion states the expectation without checking it. Both
reads of the `d`-flag positions now check and throw when what they
expect is absent: the indices array itself, the entry for a capture
group the caller has already found in the match, and the entry for the
whole match. The parser behaves as before wherever the flag is set,
which is everywhere this module builds a pattern.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@AStaroverov
AStaroverov added this pull request to the merge queue Sep 10, 2026
Merged via the queue into main with commit cd509b1 Sep 10, 2026
10 checks passed
@AStaroverov
AStaroverov deleted the feat/metadata-import-remap branch September 10, 2026 15:06
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