Skip to content

dataAvailability field + download/link table #870

Open

Description

@AlexAxthelm

Part of #860 (parallel track). Adds a stored dataAvailability structure describing, per metric,
where and how the underlying data can be obtained, and a table UI to present it. Design refined with
Jacob
(see the model below); example content is still being finalized, so the schema/UI can proceed
against the agreed shape while values land later.

Goal

Replace today's derived data-availability signal with authored, per-metric availability that drives a
table: each row is a metric, showing the segments included in that metric and a single value per
metric per column (granularity / limitations / coverage / …), with an "Overall" summary row; each row
links to a download or the publisher, with format/paywall indicators.

Background — the real-world cases (from Alex)

  1. Timeseries, hosted by us → download available; the data lives in the timeseries metrics JSON and
    we link to it for download (as today, via the generated index src/data/index.gen.ts /
    public/data/index.json, surfaced by availabilityFor in src/utils/searchUtils.ts:178).
  2. Single / small, non-timeseries data → may be downloadable somehow — hosting mechanism still
    TBD
    .
  3. Not hostable → refer to the publisher; indicate whether the metric is available there in
    tables
    or in the body text, and whether the pathway is free or paywalled.

Note: today dataAvailability is derived, not stored (availabilityFor in src/utils/searchUtils.ts);
this ticket introduces the authored, stored form. (The DataAvailability = "Foo" staleness previously
flagged here is already fixed in code — the type is "Download" | "Link" | "Unavailable".)

Table model (refined with Jacob)

The table is organised per metric, not as a flat field array:

  • Rows = metrics. For each metric, show which segments are included in that metric, then a
    single value per metric per column for the descriptive axes (granularity / limitations /
    coverage / time resolution / format / …). One value per cell — not a list.
  • "Children" are included. The more-granular geography/sector values under a metric appear in the
    table. There is no need to show every granular value at once when the user is viewing the widest
    scope (ties to the display simplification in Frontend: scoped values + scope/fallback badges #859).
  • "Overall" row/section. A summary describing the current full-timeseries data file, plus anything
    that doesn't fit the per-metric structure.
  • "No information" is shown at the correct granularity where a metric/segment has none (consistent
    with the v2 schema + data codemod #858/Frontend: scoped values + scope/fallback badges #859 sentinel), rather than an empty cell.

Underlying per-metric fields (from the redesign doc; enum vocabularies to be confirmed against the
finalized example data):

Field Type Notes
metricName enum sector-conditional; shares vocabulary with metric
sector enum existing sector enum
sectorSegment enum sector-conditional (e.g. Power: Power generation / Storage / T&D / …)
geographyCoverage enum global / regional / country
timeResolution enum 2050 point / medium-term / 10-yr / 5-yr / 1-yr / 5–10-yr / other / unavailable
dataFormat enum in tool / tabular-in-pub (free | paywall) / text-in-pub (free | paywall)
granularity string[] | null metric-conditional (e.g. Power capacity: Coal/Gas/Wind/Solar/…)
scopeLimitations string | null free text

dataFormat = "in tool" rows link to the existing timeseries download. Case (2) above (small,
non-timeseries downloadable data) becomes a new dataFormat value once the hosting mechanism is
decided — that hosting decision is tracked as its own ticket (see README), not blocked here.

Scope

  • Add dataAvailability to the schema as a v2 point release (pathwayMetadata.v2.x) +
    regenerate types. Does not block the initial v2 cut in v2 schema + data codemod #858 — lands after this design settles
    and the example values are final.
  • Author dataAvailability for existing pathways (content work; values pending from Jacob).
  • Build the per-metric table UI (rows = metrics → segments → single value per column, plus the
    "Overall" summary and "children"); wire "in tool" rows to the timeseries download, others to the
    publisher; show free/paywall + tables/text indicators; render "No information" at the right
    granularity.
  • The table lives on the detail page and is scoped by the sticky Geo/sector filter bar (Details page updates for fine grained pathway details #872).
  • Decide the fate of the derived availabilityFor facet (retire vs. keep as fallback) now that
    availability is authored.

Acceptance criteria

  • Detail page renders the per-metric table (segments + single value per column + "Overall") from
    the stored dataAvailability, respecting the Details page updates for fine grained pathway details #872 filter selection.
  • "In tool" rows download the timeseries; publisher rows link out with free/paywall + tables/text
    indicators; "No information" shown at the correct granularity.
  • dataAvailability validates under the v2.x schema; types regenerated; vitest green.

Notes / links

Activity

  1. self-assigned this
    on Aug 26, 2026
  2. jacobvjk commented on Sep 11, 2026

    @jacobvjk
    Collaborator

    Status after #936 and #946.

    • The table is built (src/components/DataAvailabilityTable.tsx, Feat/UI tabs #936) and is
      now filtered by the detail page's scope selection (Feat/detail scope filters #946), as the schema
      comment on byMetric[].geography anticipated.
    • Feat/detail scope filters #946 also fixed a latent bug: rows were keyed by (metricName, sectorSegment,
      geography), omitting sector from the uniqueness tuple the schema enforces,
      so a multi-sector pathway reporting the same metric twice rendered one row
      twice. Sector and geography scope are now visible as conditional columns.

    Blocker on observing any of it: no pathway in src/data/ has
    byMetric populated.
    Only testdata/valid/pathwayMetadata_v2_full.json and
    test fixtures exercise the table, so this ships invisible in the running app.
    Authoring it for one real pathway is probably worth its own task.

    Vocabulary change to be aware of (#936): sectorSegment renamed Storage
    → Energy storage, and a new Fuel extraction and processing segment was
    added. Schema enum, generated types and fixtures updated; no authored data
    needed migrating because none used a named segment.

  3. jacobvjk commented on Sep 24, 2026

    @jacobvjk
    Collaborator

    This PR splits dataFormat into two separate columns: dataFromat and access: https://github.com/RMI/tpr/pull/898/changes#diff-0dc51340471f147cebec4629b65693571cc18b10b34e328fb7ddac1e5f1e011b

    The rational is that one describes the actual format that the data is available in and the other describes how users can access that information. That is a good idea. However, we only host data and pathways that are available for free at this point anyway, and the "free" value in the access variable does not really convey the details that may come with data licenses anyway. Likewise, there is no point in mixing information on data format/access within this tool (value: "in tool") with source data format information. So "in tool" should be dropped. The final version of this variable should instead be the following:

    Field Type Notes
    metricName enum sector-conditional; shares vocabulary with metric
    sector enum existing sector enum
    sectorSegment enum sector-conditional (e.g. Power: Power generation / Energy storage / T&D / …)
    geographyCoverage enum global / regional / country
    timeResolution enum 2050 point / medium-term / 10-yr / 5-yr / 1-yr / 5–10-yr / other / unavailable
    dataFormat enum tabular / text / figure
    granularity string[] | null metric-conditional (e.g. Power capacity: Coal/Gas/Wind/Solar/…)
    scopeLimitations string | null free text
  4. jacobvjk commented on Sep 24, 2026

    @jacobvjk
    Collaborator

    Following up on my comment above — dataFormat is now authored in the cookbook, so the vocabulary is settled. RMI/tpr-tpc#20.

    The allowed values

    dataFormat is a single-valued enum with exactly four members, spelled as follows:

    Value Meaning
    Tabular Values are in a table or a machine-readable data file, readable directly
    Text Values appear only in prose
    Figure Values appear only in a chart, plot or map, and must be read off the graphic
    Not covered The sector-metric combination is not covered

    Spelling matters — these are the literal strings. Two differences from what's in dataAvailability.v1.json on the #898 branch:

    • Not Tabular in publication / Text in publication. With In tool gone, every value is in-publication by definition, so the qualifier carries nothing.
    • Capitalised first letter only. My earlier comment wrote tabular / text / figure as shorthand; the cookbook's house style for allowed values is Tabular, Text, Figure, matching Not covered, Medium-term and the rest.

    What needs to change on this side

    1. Drop access entirely, including the validateScopedEntries biconditional that ties it to In tool. There's nothing left for it to hold.
    2. Drop In tool. dataFormat describes the source publication only. Whether we host a file is something the repository knows about itself, not a classification judgement about the pathway — that was the whole point of separating the two. If the "we serve this / go to the publisher" signal is still wanted in the UI, it should be derived from the timeseries index as it is today, or become its own field. It should not come back as a dataFormat member.
    3. Add Not covered. This one isn't in Feat/metadata v2 schema #898 at all and is easy to miss. The cookbook requires a row for every allowable (sector, metric) pair in a covered sector, even when the pathway reports nothing for it — an omitted row is a completeness defect, not an absence. So Not covered is a real value that will appear in authored data, not a null.
    4. There is deliberately no Unspecified, unlike timeResolution. A metric whose values you can see at all has a visible form.

    One rule that affects authoring

    When a metric appears in more than one form, the most extractable wins: Tabular > Text > Figure. This comes up immediately — AGF Laos justifies eight rows with "Technology-level reporting in tables/figures", so a publication that prints a chart and ships the underlying spreadsheet is Tabular.

    Related: judge where the metric's values are, not where it's mentioned. JETP CIPP's Power technology cost is Figure — the LCOE numbers are in Figure 5.2-10 — even though surrounding prose references cost baselines.

    Provenance

    Cookbook: docs/cookbook/split/data_availability/data_format.md, cookbook version tpr_cookbook_20260924. Reasoning and the rejected alternatives are in decision record 0021. The classification template gained Data format and Data format_Justification.

    Worth knowing for planning: the classification side has the columns and the validator enforces the enum, but no pathway has authored Data format values yet — the gold workbooks carry NULL pending an authoring pass. So this unblocks the schema and UI work, but real values arrive separately, same as the byMetric situation flagged in the #936/#946 status above.

  5. jacobvjk commented on Sep 25, 2026

    @jacobvjk
    Collaborator

    The vocabulary is in, but the table can't be filled in yet

    The schema and UI now match cookbook tpr_cookbook_20260924 — dataFormat is
    Tabular / Text / Figure / Not covered, access and In tool are gone, the time
    resolutions use the cookbook's exact wording, geography coverage is the list of
    geographies rather than a Global/Regional/Country label, and Unspecified vs
    Not covered replaces blank cells everywhere. Pushed to #898, #936, #946 and #954.

    Before anyone starts an authoring pass, though, there's a gap worth knowing about. It
    isn't a problem with this ticket's design — it's a collision with something #860 parked.

    The tool only knows 5 metrics; the cookbook's table needs up to 13

    The Data Availability section has its own metric list — the cookbook calls it
    "Metrics – Extended" and is explicit that it's a different thing from the pathway-level
    Metric field: it's what defines a row in the table.

    The tool currently accepts five metrics anywhere: Emissions Intensity, Capacity,
    Generation, Technology Mix, Absolute Emissions. The cookbook's table wants:

    Sector Metrics the table needs We can record
    Power 9 5
    Steel 10 2
    Aviation 13 0

    (The Steel count is generous: it assumes our single "Emissions Intensity" stands in for
    the cookbook's three — total, primary and secondary — which it can't distinguish.)

    Missing across every sector: Technology cost, Investment requirement, Asset
    lifetime
    . Plus Transmission lines for Power; Scrap share, Steel production by
    technology
    and the split Emissions intensity (total / primary / secondary) for
    Steel; and all the Aviation demand metrics.

    In practice: a row for one of those is rejected when the file is saved. So today you
    could fill in about half of a Power pathway's rows and none of the Steel- or
    Aviation-specific ones.

    A second, less obvious blocker

    The tool also currently requires every Data Availability row to name a metric the
    pathway already lists in its own top-level "Metrics" field. The cookbook says the
    opposite: a covered sector gets a row for every metric in the extended list,
    whether or not the pathway reports it — an unreported one is recorded as Not covered.

    So that rule has to go as well. While it stands, Not covered rows are impossible to
    write, which is exactly the completeness rule the section is built around.

    Why this wasn't just fixed alongside the rest

    Two reasons, and both need a decision rather than code.

    It's user-facing. The metric list isn't only used here — it's the Metrics filter
    dropdown on the search page
    . Adding nine-plus values changes what every visitor sees,
    so it isn't a quiet internal change.

    The cookbook hasn't settled it either. Its own register (T1) flags that the Data
    Availability metric list has Transmission lines where the pathway-level list has
    Storage capacity, notes the two disagree, and records it as unresolved.

    This is the "sector-aware restructuring of the metric field" that #860 lists under
    Out of scope ("separate future release; structure TBD"). So it belongs in its own
    ticket rather than being smuggled in here.

    Suggestion

    Raise it as a ticket now and sequence it before any authoring pass, since the
    cookbook's "a row for every allowable pair" rule can't be met until it lands. Two things
    to decide:

    1. One metric list or two? Is Data Availability's list a superset of the
      pathway-level one, or a genuinely separate vocabulary that happens to overlap? That
      determines whether the search dropdown grows.
    2. Transmission lines or Storage capacity for Power — the cookbook's two lists
      disagree, and we'd be picking a side.

    What did land

    Everything else in the section. granularity also gained the emissions-scope values
    (Scope 1, Scope 1 & 2, …) that Absolute Emissions and Emissions Intensity need,
    since those two metrics do exist. The rest of the cookbook's per-metric granularity
    vocabularies — the Steel production routes, the Aviation propulsion types — are waiting
    on the same decision, because they hang off metrics the tool can't name yet.

    Worth noting alongside the byMetric situation already flagged above: no pathway in
    src/data has any of this authored, so nothing in the running app changes yet.

  6. jacobvjk commented on Oct 2, 2026

    @jacobvjk
    Collaborator

    Correction, and the table now has real data

    Correction to my earlier comment. I said the extra metrics (Transmission lines, Technology cost, Investment requirement, Asset lifetime, and the Steel and Aviation lists) were blocked behind #860's deferred restructure of the metric field, because adding them would change the search filter. That was wrong. Cookbook register item D16 says the pathway-level Metric list and the Data Availability Metrics – Extended list are meant to differ. So the availability table got its own metric list, and the search filter is unchanged. Nothing was blocked. This is done on #898.

    Where things stand. The import on #935 now fills the table from the draft_data_availability sheet: 171 rows across 24 pathways. You can see it on the #937 preview, for example ACE-ATS-2024 → Scope & Granularity.

    Held back, and why. 45 of the sheet's 216 rows are not imported yet, each named in the import report:

    Rows Pathways What needs authoring
    35 all six Singapore pathways Data Format is blank. It's required, and I didn't want to guess it. Until it's filled in, Singapore shows only its uncovered metrics, which reads as if it covers almost nothing.
    10 IEA CPS / STEPS / NZE 2025 (2 each), the four Laos pathways (1 each) Data Format says "Not covered" while the rest of the row describes a covered metric. In the cookbook, "Not covered" means the sector–metric pair isn't covered at all, so these rows contradict themselves. Probably a real format (Tabular / Text / Figure) was meant.

    One more to check: for Singapore BAS SG, the metadata lists Capacity as a reported metric, but its availability row says Capacity is "Not covered".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions