tool_export
Atoms-based export of a section's records to a flat table (CSV / TSV / ODS / XLSX / HTML / preview / media), streamed over the export_tabulator NDJSON protocol.
What it does / why & when to use it
tool_export turns the whole current selection of a section into a spreadsheet-style flat table — the user picks which components become columns, in what order, and how relations and hierarchies are flattened. Because Dédalo stores highly structured data (multi-language values, relations to lists and thesauri, hierarchies, dataframes), exporting is never a plain dump: the tool lets a person shape the output for the job at hand.
Concrete heritage scenario: a numismatics cataloguer has filtered the Coins section down to the issues of one mint and wants a report for a colleague. They open the tool on that filtered list, drag the Inventory number, Weight, Diameter and the related Mint → name into the active columns, choose Breakdown / rows so each linked type lands on its own row, tick parents on the type column to also get the term's ancestor chain, run the export, and download an XLSX. The same configuration can be saved as a named preset for the next batch.
A second, machine-facing use: the Dédalo (Raw) format produces cells wrapped as {"dedalo_data":…} that the CSV import tool unwraps and conforms like typed input — an edit-and-reload format, NOT a backup and not a way to move data between installations: component_text_area markup is rewritten, component_geolocation item ids are dropped, empty cells clear values, no media bytes travel, and a locator's section_id is checked for shape only. The complete, verified copy of a section set is the archive door.
Use it when: someone needs section data as a spreadsheet/report, a raw CSV to bulk-edit and load back, or the media files referenced by a record set. Do not use it for single-record edits or for tabular editing — it is read-only export.
How it works (server + client)
The export pipeline resolves each cell through the list-mode leaf-value contract: get_export_grid (tools/tool_export/server/{index,tool_export}.ts) delegates to exportGridUnified (src/diffusion/export/index.ts), whose column resolution (src/diffusion/export/atoms.ts) calls resolveCellValue (src/core/resolve/relation_list.ts) — the same accessor the relation-list panel uses — rather than a dedicated per-component export method. This reaches full parity across value/grid_value/dedalo_raw data formats, all three breakdown modes, ndjson_stream, multi-hop export paths, and media cells; the coverage is byte-parity gated.
Server:
get_export_gridis the single API entry point, declaratively gatedpermission: 'section', minLevel: 1onsection_tipo. Records are then resolved through the standard search assembler (buildSearchSql) with the caller'sprincipal— for non-admins this applies the same project-scoped ACL (Layer-2) every other search-backed read goes through.- Options are normalized against fixed allowlists (
data_format→value|grid_value|dedalo_raw, falling back tovalue;breakdown→default|rows|columns) and the SQO is forced to the whole filtered selection (limit: null/ALL,offset: 0) — the export deliberately serializes the entire filtered selection; subsetting is done via the SQO filter, not a page limit. - The protocol lines (
meta,col*,row*,end) are built from one shared resolution path for both the streamed (ndjson_stream:true) and whole-grid response shapes.
Client (tools/tool_export/js/): tool_export.js is the instance; render_tool_export.js builds the three-pane UI (left = available components, center = active columns, right = config + preview); drag_tool_export.js is the drag-and-drop column model; flat_table.js accumulates the NDJSON lines into the live preview and produces the downloads; export_user_presets.js manages per-user presets (ontology section dd1781). The client fetches via data_manager.request_fetch_stream with ndjson_stream:true and resolves as soon as the meta line arrives, then keeps filling rows with a progress bar. The tool opens in its own window (properties.open_as = "window"). None of this client code changed for the TS rewrite.
Actions & options
apiActions = { get_export_grid: { permission: 'section', minLevel: 1, handler: toolExportGetExportGrid } } — a single, declaratively gated action. The handler accepts options.section_tipo ?? options.tipo as a fallback for legacy callers still sending tipo.
| Action | Permission gate | Background | Reads from options |
|---|---|---|---|
get_export_grid |
declarative: permission: 'section', minLevel: 1 on section_tipo; every SQO section is additionally covered by the standard project-scoped search ACL applied when building the record query |
no | see below |
components_with_parent |
declarative: permission: 'section', minLevel: 1 on section_tipo |
no | components: [{tipo, section_tipo}] → { [tipo]: boolean } — whether each relation component points at a section with a component_relation_parent (targets resolve through the datalist request-config builder). Powers the client's per-column parents-checkbox visibility (WC-049). Ontology-only; no record data. |
Key options read by get_export_grid / setup:
| Option | Type | Meaning |
|---|---|---|
section_tipo |
string (req.) | Target section to export. Falls back to tipo for legacy callers. Read-gated. |
model |
string | Element model; defaults to 'section'. |
data_format |
string | value (default, one flat cell per column) | grid_value (breakdown) | dedalo_raw (the wrapped edit-and-reload form). Unknown values fall back to value. |
breakdown |
string | Relation explosion for grid_value: default | rows | columns. Defaults to default. |
fill_the_gaps |
bool | Repeat spanning (record-level) values on each exploded row. Default true. |
value_with_parents (per ddo) |
bool | PER-DDO ONLY (WC-049): set value_with_parents: true on an ar_ddo_to_export entry to emit that column's locator-target ancestor chains (getParentsRecursive × term resolver, ' > ' nearest-first, self excluded) as a sibling #parents column. grid_value format only; a request-global options.value_with_parents is ignored. Targets without hierarchy emit nothing. |
ar_ddo_to_export |
array (req.) | The chosen columns, in output order (= the order of the Active elements list / the user's drag order). Stored internally as ar_ddo_map. |
sqo |
object (req.) | Search query object = the selection to export. Server forces limit='ALL', offset=0. |
ndjson_stream |
bool | true → stream the NDJSON flat-table protocol and exit(); false/absent → return the whole grid in response->result. |
Response (non-stream): { result: {meta, columns, rows, end} | false, msg }.
How it is registered & surfaced
tools/tool_export/register.json is a column-keyed dump (string/relation/misc/… keyed by component tipo — a seeded matrix-row snapshot, not a hand-authored file); importTools() passes it through as-is (see register.json reference). The essentials it carries:
dd1326name =tool_export;dd1327version (2.0.3);dd1328minimum Dédalo version (6.0.0);dd1644developer.dd1330affected_models =["section"]→ the tool attaches to sections.dd1331show_in_inspector =falseanddd1332show_in_component =false(it is a section-toolbar tool, not an inspector/inline-component button).dd1335properties ={ "open_as": "window", "windowFeatures": null }→ opens in its own window.dd1372labels supply the localized UI strings for thefill_the_gaps,show_tipo_in_labelandvalue_with_parentsoptions across project languages.
Surfacing (in getElementTools, src/core/tools/registry.ts): because affected_models is ["section"], the Export button appears on sections in list mode. There is no rule restricting the time-machine section (dd15) to tool_export alone: registry.ts's hardcoded NO_TOOLS_MODELS set only covers component_section_id/component_info, and no dd15-specific rule exists anywhere in the section/tool-filter path. On a TS-served install, dd15 shows whatever tools its affected_models/affected_tipos normally match, the same as any other section.
Examples
Client-side tool_request (built by tool_export.js::get_export_grid, sent through dd_tools_api):
const rqo = {
dd_api : 'dd_tools_api',
action : 'tool_request',
source : create_source(self, 'get_export_grid'), // → tool_export::get_export_grid
prevent_lock : true,
options : {
section_tipo : 'rsc167', // the section being exported
model : 'section',
data_format : 'grid_value', // breakdown
breakdown : 'rows', // one row per related item
fill_the_gaps : true,
// parents chains are PER-COLUMN: value_with_parents rides each
// ar_ddo_to_export entry (WC-049), not the request options
ar_ddo_to_export : [ /* chosen columns, in output order */ ],
sqo : self.sqo, // the current filtered selection
ndjson_stream : true // stream the NDJSON protocol
}
}
const stream = await data_manager.request_fetch_stream({ body: rqo })
The server emits NDJSON, one JSON object per line, discriminated by t:
{"t":"meta","section_tipo":"rsc167","total":128,"data_format":"grid_value","breakdown":"rows", ...}
{"t":"col","i":0,"key":"...","label":"Inventory number","cell_type":"text", ...}
{"t":"row","rec":12,"sub":0,"c":{"0":"NM-0001","1":"7.21"}}
{"t":"end","columns":[0,1,2],"rows":340,"records":128}
Raw cell shape (data_format:'dedalo_raw'), the form the CSV import tool unwraps — always the component's stored value wrapped exactly once:
{"dedalo_data":[{"value":"Hello","lang":"lg-eng","id":1}]}
A component with dataframe slots grows one extra column per slot, headed by the dataframe component's tipo and carrying its own frame locators (WC-2026-08-09-export-raw-dataframe-own-column).
Related
- Exporting data — the end-user + developer guide for this tool (UI walkthrough, formats, breakdown, presets, NDJSON protocol, component
get_export_valuecontract). - tool_import_dedalo_csv — consumes the
dedalo_rawexport (loading edited cells back, not a restore); see Importing data. - Creating new tools · Server contract — the tool model,
apiActions, gates and lifecycle this page builds on. - Source:
tools/tool_export/server/{index,tool_export}.ts,tools/tool_export/register.json,tools/tool_export/js/{tool_export,render_tool_export,flat_table,drag_tool_export,export_user_presets}.js; the reused resolution core:src/core/resolve/relation_list.ts.