Task: Land CSV/XML import-export as a qt-profile template capability
Table of Contents
This page documents a task in the Commission: currency story. It captures the goal, current status, acceptance, and any notes or results.
Goal
Generalise currency's hand-rolled CSV export and XML import/export
(CurrencyMdiWindow::exportToCSV/exportToXML/importFromXML,
ImportCurrencyDialog) into an opt-in qt-profile mdi_window template
capability, with currency as the first real consumer. Step 2 of the
regenerate plan (task 6E0CD16E) — must land before "Sync Qt codegen
for currency" regenerates CurrencyMdiWindow.
Status
| Field | Value |
|---|---|
| State | DONE |
| Parent story | Commission: currency |
| Now | Nothing. |
| Waiting on | Nothing. |
| Next | Nothing. |
| Last touched | 2026-07-06 |
Acceptance
- CSV export and XML import/export are opt-in mdi_window template capabilities, not currency-specific code.
- Regenerating currency's mdi_window with the capability enabled reproduces equivalent behaviour to the current hand-written code.
- Build passes.
Plan
Current state (investigated 2026-07-06)
Currency is the only entity with CSV/XML import/export anywhere in the codebase — there is no existing generic pattern to templatize against:
ores.refdata.api/csv/exporter.hpphas exactly one method,export_currency_config; no other entity has a CSV exporter.ore::xml::exporter/importer::export_currency_config/import_currency_configare likewise currency-only.ImportCurrencyDialog(preview table, checkboxes, progress bar, cancel) is a hand-written, currency-specificQDialog.cpp_qt_mdi_window.{hpp,cpp}.mustachehave zero hooks for CSV/XML/import/export today.
So this is new capability design, not a "lift existing pattern into mustache" job.
Scope decision
Split into two independently-landable pieces, since they have different generality:
- CSV/XML domain-layer exporter/importer — per-entity, hand-written
(like today), NOT templated. The record shape differs too much
entity-to-entity to make a generic serializer worth the codegen
complexity for two formats. Codegen's job is only to know an entity
has these and wire the Qt calls to them by convention
(
export_<entity>_config,import_<entity>_config), same as it already does for e.g.generate_fictional_currencies-style generator hooks. - Qt wiring — templated. A new opt-in mdi_window flag,
has_csv_xml_io: true, gated the same wayhas_flag_icongates the flag-image mechanism today. When set, the generated*MdiWindowgets:- toolbar actions: Import XML, Export CSV, Export XML
exportToCSV()=/=exportToXML()=/=importFromXML()methods calling the entity'scsv::exporter=/=xml::exporter=/=xml::importerby the naming convention above- a generic
ImportEntityDialog<Entity>(header-only template inores.qt/api, same shape asDynamicComboSetup.hpp'spopulateDynamicCombo<Entity>) replacing the currency-specificImportCurrencyDialog, parametrized by:code_of(Entity)/ preview column accessors (reuse the mustache field list already used for the entity's list/detail views instead of a second field enumeration)- the import call
- the
has_csv_xml_ioknob also implies the entity type is copy/move-constructible and has the two free functions above; codegen should fail fast at generation time if either is missing rather than emit code that won't compile.
Steps
- Add
ImportEntityDialog<Entity>template toores.qt/api, modelled onImportCurrencyDialogbut parametrized per the above. - Migrate currency's
ImportCurrencyDialogcall site to it; deleteImportCurrencyDialog.{hpp,cpp}once the migration is verified behaviourally equivalent. - Add the
has_csv_xml_ioschema flag to the qt entity model (Python side) and the corresponding mustache sections incpp_qt_mdi_window.{hpp,cpp}.mustache. - Flip the flag on for currency's model; regenerate; diff against the migrated hand-written version from step 2 — should match modulo naming.
- Build passes; acceptance met.
Implementation notes
- Reusing the full
qt.columnslist for the import preview table was wrong: fields likeversion=/=modified_by=/=recorded_atare meaningless before a record is imported. First cut added a derivedimport_preview_columns(org_loader.py) filteringcolumnsdown to non-computed, non-timestamp, non-audit fields — an opt-out exclude-list. PR review caught that this still over-included fields (11 columns for currency vs the 5 the hand-migrated dialog actually shows), and that the--diffverification below should have caught the mismatch and didn't. Replaced with an opt-in:import_preview: truecolumn property instead, so an entity author explicitly marks which columns matter for the import preview — no exclude-list to silently under/over-fire. - The naive
get{{entity_pascal}}s()method name mispluralised ("getCurrencys"). Used the existingentity_pascal_short_pluralproperty (already used elsewhere, e.g.on{{entity_pascal_short_plural}}Loaded) instead of inventing a new per-entity property. - Verified by running
codegen entity generate currency --address ores.cpp.qt --diffand comparing the has_csv_xml_io-gated sections against the hand-migrated code from step 2 — match modulo formatting/comments. Did not adopt the full regenerated file set: currency's Qt layer has substantial pre-existing drift unrelated to this capability (badges, image cache, generator button, NATS notifications, …) that the broader "Sync Qt codegen for currency" story/regenerate plan still needs to reconcile separately.
Notes
PRs
| PR | Title |
|---|---|
| #1445 | [codegen,qt] Land CSV/XML import-export as a qt-profile capability |
Review
| # | Comment summary | File | Decision | Notes |
|---|---|---|---|---|
| 1 | import_preview_columns exclude-list over-includes fields (11 vs 5 hand-migrated) | org_loader.py | Fixed | Replaced with opt-in :import_preview: column flag, 263d90206 |
| 2 | No unit test for import_preview_columns derivation in org_loader.py | org_loader.py | Declined | No existing test file for org_loader.py at all; bigger lift than this fix, follow-up |
Result
Shipped ImportEntityDialog (ores.qt/api), a generic, non-templated
preview-and-import dialog (Qt moc can't process class templates with
Q_OBJECT, so it's driven by type-erased rows and label_of=/
=import_one callbacks), replacing the currency-specific
ImportCurrencyDialog. Added the opt-in has_csv_xml_io qt-profile
flag to the mdi_window/client_model mustache templates: toolbar
actions (Import XML, Export CSV, Export XML), generated
exportToCSV=/=exportToXML=/=importFromXML wired to the entity's
csv/xml exporter/importer by naming convention, and a bulk
get<Plural>() accessor on the client model. The import preview
column list is opt-in per column (:import_preview: true), not
derived from an exclude-list — a PR review round caught the original
exclude-list approach over-including fields (11 vs 5 for currency)
and it was corrected. Currency is the first consumer
(generateAction_ wiring aside, this task covers CSV/XML only).
Verified via codegen entity generate currency --address ores.cpp.qt
--diff: the has_csv_xml_io-gated sections match the hand-migrated
code, including the corrected 5-column import preview. All three
acceptance criteria met. PR #1445.