recon_gen.common.l2.coverage

coverage_for() — per-L2-primitive presence in <prefix>_transactions.

The Studio diagram’s coverage-tint chrome (X.4.c.5) needs a per-entity “do you have data” answer keyed off the same node IDs the topology emit produces. This module is the data fetcher: three GROUP BY queries on <prefix>_transactions plus a derived chain-edge map, all keyed to the topology IDs role__X / rail__X / tmpl__X / chain__<parent>__<child>.

Why a separate module:

  • The query shape is the same across all four apps (it doesn’t ride on any one app’s dataset SQL); coupling it to either apps/ or common/html/ would force one consumer to import the other.

  • Studio’s diagram chrome (common/html/_studio_routes.py) is the initial consumer, but the same fetcher is what the eventual ETL-engineer uncovered_rails scope (X.4.h) will reuse to decide what to fill — keeping it in common/l2/ matches that future symmetry.

Severability: this module imports only common/db.py (async pool protocol), common/l2/primitives.py (the L2 model), and common/l2/topology.py (the node-id helpers). It does NOT import common/html/ — the coverage data flows the other way.

Functions

chain_edge_id(parent, child)

Stable ID for a chain edge in the topology coverage map.

coverage_for(pool, prefix, instance, *, dialect)

Compute per-L2-primitive row presence in <prefix>_transactions.

metadata_coverage_per_template(pool, prefix, ...)

For each TransferTemplate, count rows landing its required metadata keys.

Classes

CoverageEntry(present, count)

One L2 primitive's data presence + row count.

CoverageMap(by_node_id, by_chain_edge_id)

Per-topology-element coverage from a single demo-DB snapshot.

TemplateMetadataCoverage(template_name, ...)

Per-template required-metadata-key landing tally.

class recon_gen.common.l2.coverage.CoverageEntry(present, count)[source]

Bases: object

One L2 primitive’s data presence + row count.

count is the absolute row count behind present so the chrome’s hover tooltip can say “12,304 rows” instead of just “yes”.

Parameters:
  • present (bool)

  • count (int)

count: int
present: bool
class recon_gen.common.l2.coverage.CoverageMap(by_node_id, by_chain_edge_id)[source]

Bases: object

Per-topology-element coverage from a single demo-DB snapshot.

Two maps:

  • by_node_id — keyed by the topology node IDs that topology.py’s _role_id / _rail_id / _template_id helpers emit. One entry per L2-declared role / rail / template — even an empty entity gets a present=False, count=0 record so the chrome can render absence as well as presence.

  • by_chain_edge_id — keyed by chain__<parent>__<child> (the discriminator scheme matches the node-id prefix convention). A chain edge is “covered” when both endpoints (rail or template) have at least one transaction. Derived; no extra DB query.

Parameters:
by_chain_edge_id: Mapping[str, CoverageEntry]
by_node_id: Mapping[str, CoverageEntry]
class recon_gen.common.l2.coverage.TemplateMetadataCoverage(template_name, row_count, per_key)[source]

Bases: object

Per-template required-metadata-key landing tally.

For one TransferTemplate, row_count is the total rows tagged with that template_name; per_key maps each required key (from TransferTemplate.transfer_key + each leg-rail’s metadata_keys) to the count of rows whose metadata JSON contains the key. BT.3’s coverage card reads per_key[k] == row_count to paint ✓-all-rows-have-it, per_key[k] == 0 for ✗-missing, partial counts for the “12/14 rows have it” middle.

Parameters:
  • template_name (str)

  • row_count (int)

  • per_key (Mapping[str, int])

per_key: Mapping[str, int]
row_count: int
template_name: str
recon_gen.common.l2.coverage.chain_edge_id(parent, child)[source]

Stable ID for a chain edge in the topology coverage map.

Matches the role__ / rail__ / tmpl__ discriminator scheme so the SVG post-processor can key off the prefix in the chrome’s coverage paint.

Return type:

str

Parameters:
  • parent (str)

  • child (str)

async recon_gen.common.l2.coverage.coverage_for(pool, prefix, instance, *, dialect)[source]

Compute per-L2-primitive row presence in <prefix>_transactions.

Three GROUP BY queries hit the DB once per call:

  • account_role → role coverage

  • rail_name → rail coverage

  • template_name → template coverage (NULL filtered, since most transfers are standalone-rail and carry NULL template_name)

Chain edge coverage is derived: an edge is “covered” iff both parent + child names appear in either rail or template counts.

Iteration is over the L2-declared primitives (not the DB result set) so an L2-declared-but-unfed entity returns present=False, count=0 instead of being absent from the map entirely. The chrome surface needs to render absence too — that’s the integrator’s “is my ETL hooked up” signal.

Computed on-demand (no caching). Studio’s audience is one user iterating, not a hot path; a 50-rail L2 against the demo DB returns in single-digit ms.

Parameters:
  • pool (AsyncConnectionPool) – AsyncConnectionPool against the demo DB.

  • prefix (str) – L2 instance prefix (e.g. "sasquatch_pr"); used as the <prefix>_transactions schema prefix.

  • instance (L2Instance) – The L2 model whose declared primitives drive the iteration set (so absence = present=False, count=0).

  • dialect (Dialect) – SQL dialect; drives column-name case-folding via column_name(...).

Return type:

CoverageMap

Returns:

A CoverageMap with by_node_id covering every declared role / rail / template + by_chain_edge_id covering every declared chain entry.

async recon_gen.common.l2.coverage.metadata_coverage_per_template(pool, prefix, instance, *, dialect)[source]

For each TransferTemplate, count rows landing its required metadata keys.

The required-keys set unions the template’s own transfer_key plus each leg-rail’s metadata_keys. Counts use the same JSON-text quoted-key heuristic probe.evaluate_predicate uses ("key" substring scan) — robust enough for the BT.3 coverage card; BT.4’s Triage will do proper JSON_VALUE / JSONPath extraction when the gap shape needs it.

Iteration is over the L2’s declared templates so an empty-data instance still gets a complete result (every template returns a row_count=0 entry, per_key empty for each required key).

One query per template (per-template + per-key count is folded into a single SELECT with N conditional SUMs); studio’s audience is one user iterating, so the per-call cost is negligible.

Return type:

Mapping[str, TemplateMetadataCoverage]

Parameters: