From 06980bee06681c4f441c7716e06619de62c3f844 Mon Sep 17 00:00:00 2001 From: xmap <16776958+xmap@users.noreply.github.com> Date: Wed, 9 Sep 2026 11:14:30 -0500 Subject: [PATCH] Trim what the descriptor move left stale, and drop the model-tier generator Three follow-ups from moving 93 descriptors to xmap/descriptors, all the same shape: something sized for the old fleet, now sized for one beamline. deployments/aps/site.yaml: - facility.beamlines listed all 10 original APS beamlines; 9 have no descriptor in cora anymore. Trimmed to [2-BM]. - beamlines_note existed twice (a duplicate YAML key, the second silently winning), both versions explaining a multi-maturity spread across beamlines that are now gone. Replaced with one line pointing at xmap/descriptors. - facility.sectors is left alone: it is a physical fact about the real APS ring, true regardless of which beamlines cora has a descriptor for, unlike beamlines/beamlines_note which were specifically about cora's own modeled roster. - added deployments/README.md: nothing in the directory previously explained why only two entries exist. scripts/beamline_pages.py: deleted _render_index, _render_inventory, and _render_beamwalk (191 of the file's 671 lines), the generator for a model-tier beamline's whole reader set. No real descriptor has triggered that path since the move: 2-BM is the only one cora carries, and it is deployment_tier: pilot, not model. The same held for the non-stages "walk" page_layout branch, which no current descriptor uses either. Both were only still reachable through synthetic test fixtures written to keep exercising them. This is the same principle already applied today to the fleet-wide tests, pointed at the generator instead: "CORA's domain model only contains what at least one real deployment forced into it" (docs/deployments/index.md) applies just as well to the code that renders it. Recoverable from git history if a second real deployment ever needs this shape back. scripts/mkdocs_hooks.py: removed the site_by_code lookup built solely to pass facility_label/control_plane into the now-deleted _render_index, and the model_tier=... argument to render_all. apps/api/tests/unit/deployments/test_beamline_descriptor.py: deleted the three tests whose only subject was the removed code (test_stages_layout_dissolves_inventory_into_flat_stage_pages, test_source_ref_renders_as_provenance_link_on_index_not_source, test_walk_layout_keeps_beamline_and_inventory_pages); updated the survivors to drop the now-nonexistent model_tier kwarg. Verified: mkdocs build --strict clean (spot-checked aps/index.md's rendered roster note), `pytest tests/unit tests/architecture` (CI's full fast lane) 51619 passed / 649 skipped / 0 failed. --- .../deployments/test_beamline_descriptor.py | 135 +------ deployments/README.md | 5 + deployments/aps/site.yaml | 5 +- scripts/beamline_pages.py | 341 ++---------------- scripts/mkdocs_hooks.py | 12 - 5 files changed, 45 insertions(+), 453 deletions(-) create mode 100644 deployments/README.md diff --git a/apps/api/tests/unit/deployments/test_beamline_descriptor.py b/apps/api/tests/unit/deployments/test_beamline_descriptor.py index 52067a6f88e..90bd2e2bacb 100644 --- a/apps/api/tests/unit/deployments/test_beamline_descriptor.py +++ b/apps/api/tests/unit/deployments/test_beamline_descriptor.py @@ -210,112 +210,22 @@ def _render_all_pages(slug: str) -> dict[str, str]: slug=slug, catalog_families=frozenset(f.name for f in catalog.families), catalog_models=frozenset(m.name for m in catalog.models), - model_tier=descriptor.beamline.deployment_tier == "model", ) -_SYNTHETIC_STAGES_MODEL = ( - "beamline:\n" - " name: Synthetic\n" - " maturity: model\n" - " evidence: controls_config\n" - " coverage: full\n" - " page_layout: stages\n" - " shape: A synthetic stages-layout model beamline, for pinning the generator.\n" - "source:\n" - " stage: source\n" - " devices:\n" - " - {name: SynMotor, family: LinearStage, pv: 'syn:m1'}\n" - "sample:\n" - " stage: sample\n" - " devices: []\n" - "detector:\n" - " stage: detection\n" - " devices: []\n" -) - - -def test_stages_layout_dissolves_inventory_into_flat_stage_pages(tmp_path: Path) -> None: - # A model-tier beamline with page_layout: stages dissolves Inventory into - # flat source/sample/detector/controls siblings, with no equipment/ folder - # and no beamline.md or inventory.md. Pinned with a synthetic descriptor - # (the real fleet exercising this layout now lives in xmap/descriptors). - path = tmp_path / "beamline.yaml" - path.write_text(_SYNTHETIC_STAGES_MODEL, encoding="utf-8") - descriptor = bd.load(path) - pages = bp.render_all(descriptor, slug="syn", model_tier=True) - assert "deployments/syn/source.md" in pages - assert "deployments/syn/sample.md" in pages - assert "deployments/syn/detector.md" in pages - assert "deployments/syn/controls.md" in pages - assert "deployments/syn/beamline.md" not in pages - assert "deployments/syn/inventory.md" not in pages - assert not any(p.startswith("deployments/syn/equipment/") for p in pages) - # the flat source page is the source stage itself, with no Inventory pointer - # and none of the walk-layout framing (no "walk", no composed-fixture pages, - # and no dangling Operations reference, which is not a page in this layout) - source = pages["deployments/syn/source.md"] - assert source.startswith("# Source") - assert "inventory.md" not in source - assert "walk" not in source.lower() - assert "composed-fixture" not in source - assert "Operations" not in source - assert "[Controls](controls.md)" in source - # Enclosures are a beamline-wide fact: the table moves up to the index and - # off the Source page in the stages layout. - assert "## Enclosures" not in source - # the index presents the stages as first-class sibling pages, not a "Walk the - # beam" spine, and no longer points at Inventory - index = pages["deployments/syn/index.md"] - assert "[Source](source.md)" in index - assert "Walk the beam" not in index - assert "## The beamline" in index - assert "inventory.md" not in index - assert "equipment/" not in index - - def test_pilot_stages_layout_generates_only_flat_source() -> None: - # A pilot on the stages layout generates ONLY its Source page (flat source.md); - # its index, sample, detector, controls, and operational pages are hand-authored. + # 2-BM (the only deployment cora carries) generates ONLY its Source page + # (flat source.md); its index, sample, detector, controls, and operational + # pages are hand-authored. pages = _render_all_pages("2-bm") assert set(pages) == {"deployments/2-bm/source.md"}, sorted(pages) assert pages["deployments/2-bm/source.md"].startswith("# Source") -_SYNTHETIC_SOURCE_REF_MODEL = ( - "beamline:\n" - " name: Synthetic\n" - " maturity: model\n" - " evidence: controls_config\n" - " coverage: full\n" - " page_layout: stages\n" - " shape: A synthetic stages-layout model beamline with a source_ref.\n" - " source_ref:\n" - ' label: "org/repo"\n' - ' url: "https://example.test/org/repo"\n' - "source:\n" - " stage: source\n" - " devices: []\n" -) - - -def test_source_ref_renders_as_provenance_link_on_index_not_source(tmp_path: Path) -> None: - # A model-tier beamline with a source_ref surfaces it in the index's - # generated-from banner as a link, so a reader can trace the facts to the - # public source they came from, and does NOT duplicate it on the Source - # page (that placement is reserved for a pilot, see the test below). - path = tmp_path / "beamline.yaml" - path.write_text(_SYNTHETIC_SOURCE_REF_MODEL, encoding="utf-8") - descriptor = bd.load(path) - pages = bp.render_all(descriptor, slug="syn", model_tier=True) - assert "[org/repo](https://example.test/org/repo)" in pages["deployments/syn/index.md"] - assert "Source: [" not in pages["deployments/syn/source.md"] - - def test_pilot_source_ref_surfaces_on_source_page(tmp_path: Path) -> None: - # A pilot has no generated index, so its Source page is the only place a - # source_ref can land. No real pilot currently carries a source_ref (2-BM - # is live), so render a synthetic pilot to pin the path. + # The Source page is where a source_ref lands, if a descriptor carries one. + # No real descriptor currently does (2-BM is live), so render a synthetic + # one to pin the path. descriptor_yaml = ( "beamline:\n" " name: T\n" @@ -334,7 +244,7 @@ def test_pilot_source_ref_surfaces_on_source_page(tmp_path: Path) -> None: path = tmp_path / "beamline.yaml" path.write_text(descriptor_yaml, encoding="utf-8") descriptor = bd.load(path) - pages = bp.render_all(descriptor, slug="t", model_tier=False) + pages = bp.render_all(descriptor, slug="t") assert "[org/repo](https://example.test/org/repo)" in pages["deployments/t/source.md"] @@ -348,36 +258,6 @@ def test_live_pilot_has_no_source_ref() -> None: assert "Source: [" not in source_page -def test_walk_layout_keeps_beamline_and_inventory_pages(tmp_path: Path) -> None: - # The walk layout (page_layout omitted -> "walk") still emits beamline.md, - # inventory.md, and the equipment/ stage pages with a "Walk the beam" spine. - # 2-BM (the only real deployment left) uses the stages layout, so this pins - # the walk path with a synthetic model-tier descriptor. - descriptor_yaml = ( - "beamline:\n" - " name: W\n" - " maturity: model\n" - " evidence: controls_config\n" - " coverage: full\n" - "source:\n stage: source\n devices: []\n" - "sample:\n stage: sample\n devices: []\n" - "detector:\n stage: detection\n devices: []\n" - ) - path = tmp_path / "beamline.yaml" - path.write_text(descriptor_yaml, encoding="utf-8") - descriptor = bd.load(path) - pages = bp.render_all(descriptor, slug="w", model_tier=True) - assert "deployments/w/beamline.md" in pages - assert "deployments/w/inventory.md" in pages - assert "deployments/w/equipment/sample.md" in pages - assert "deployments/w/equipment/detector.md" in pages - assert "deployments/w/equipment/controls.md" in pages - # the walk layout keeps the "Walk the beam" spine and its Inventory pointer - index = pages["deployments/w/index.md"] - assert "## Walk the beam" in index - assert "[Inventory](inventory.md)" in index - - def test_markers_promoted_from_comments_to_fields() -> None: descriptor = bd.load(_DESCRIPTOR) devices = {d.name: d for _name, group in descriptor.groups for d in group.devices} @@ -562,7 +442,6 @@ def _generated_beamline_pages(deployment: str) -> str: slug=deployment, catalog_families=frozenset(f.name for f in catalog.families), catalog_models=frozenset(m.name for m in catalog.models), - model_tier=descriptor.beamline.deployment_tier == "model", ) return "\n".join(pages.values()) diff --git a/deployments/README.md b/deployments/README.md new file mode 100644 index 00000000000..f81fe0fdc52 --- /dev/null +++ b/deployments/README.md @@ -0,0 +1,5 @@ +# Deployments + +The descriptors for CORA's real deployments: `/beamline.yaml` per beamline, `/site.yaml` per facility. The docs build renders these directly (`scripts/beamline_descriptor.py`, `scripts/site_descriptor.py`, `scripts/mkdocs_hooks.py`), and a handful of `apps/api` fitness tests validate against them. + +Two directories today: `2-bm` (CORA's one real, operational deployment) and `aps` (the Facility record 2-BM belongs to). That is deliberate, not partial: CORA models further beamlines, at APS and elsewhere, from public source, to test that the domain model generalizes, but a beamline CORA has no operational relationship with is not a deployment. That corpus lives in the private [`xmap/descriptors`](https://github.com/xmap/descriptors) repo instead, kept out of both this directory and the public docs site. diff --git a/deployments/aps/site.yaml b/deployments/aps/site.yaml index 36b3d8b2426..65c8d03393f 100644 --- a/deployments/aps/site.yaml +++ b/deployments/aps/site.yaml @@ -23,10 +23,9 @@ facility: kind: Site institution: Argonne control_plane: "EPICS / ophyd" - beamlines_note: "The one Site where CORA spans all three maturities: 2-BM runs live, 7-BM / 19-BM / 32-ID are in design, and the rest are reverse-engineered models, all sharing the APS storage-ring envelope." - beamlines_note: "The pilot 2-BM runs live; 7-BM, 19-BM, and 32-ID are in design; the Sector 4/8/9/12/13 beamlines are reverse-engineered models. All share the APS storage-ring envelope." + beamlines_note: "CORA has also modeled further APS beamlines from public source, to test that the domain model generalizes; those are not deployments and live in the private xmap/descriptors repo, not here." sectors: [Sector 2, Sector 4, Sector 7, Sector 8, Sector 9, Sector 12, Sector 13, Sector 32, Sector 19] - beamlines: [2-BM, 2-ID, 7-BM, 32-ID, 19-BM, 4-ID, 8-ID, 9-ID, 12-ID, 13-ID] + beamlines: [2-BM] # ISA-88 Site Recipes: the facility-adapted form of a catalog Method. A method # that exists in the Catalog renders as a link; a method still pending in the diff --git a/scripts/beamline_pages.py b/scripts/beamline_pages.py index 099137bcae5..7b8527d6f2f 100644 --- a/scripts/beamline_pages.py +++ b/scripts/beamline_pages.py @@ -1,15 +1,26 @@ """Render a beamline descriptor into a docs page. `render_all(descriptor)` returns a {src_uri: markdown} dict (mirroring the -contract scripts/scenarios_pages.render_all used) with a single generated page, -deployments/2-bm/beamline.md: the Source page, a walk along the source-stage -devices (front-end optics to the sample), one section per subsystem with a device -table per group. The cross-cutting controllers and supplies are their own pages -(equipment/controls.md, operations.md); the descriptor still carries them for the -tests and the future seeder. - -The mkdocs on_files hook in scripts/mkdocs_hooks.py injects these as virtual -files at build time; nothing is written to disk. +contract scripts/scenarios_pages.render_all used) with a single generated page: +2-BM's Source-stage walk (front-end optics to the sample), one section per +subsystem with a device table per group. 2-BM hand-authors everything else +(index, sample, detector, controls, operations, ...); this is the one page +generated from the descriptor, so it cannot silently drift from what the +source-stage devices actually are. + +This module used to also generate a beamline's whole reader set (index, +inventory, the sample/detector/controls beam-walk) for "model-tier" beamlines: +CORA's further modeled-but-not-deployed beamlines, reverse-engineered from +public source. That corpus moved to the private xmap/descriptors repo, and the +generator went with the reasoning that moved it: cora's own domain model "only +contains what at least one real deployment forced into it" (docs/deployments/ +index.md), and the same now holds for this generator. 2-BM is the only +descriptor cora carries, and it is not model-tier, so that code had no real +input left to run against. Recoverable from git history if a second real +deployment ever needs it. + +The mkdocs on_files hook in scripts/mkdocs_hooks.py injects this as a virtual +file at build time; nothing is written to disk. """ from __future__ import annotations @@ -71,24 +82,13 @@ def render_all( slug: str = "2-bm", catalog_families: frozenset[str] = frozenset(), catalog_models: frozenset[str] = frozenset(), - facility_label: str | None = None, - control_plane: str | None = None, - model_tier: bool = False, ) -> dict[str, str]: - """Render a beamline's generated docs pages. - - Every beamline gets the Source-stage walk (`beamline.md`). A model-tier - beamline (a reverse-engineered / design scaffold, not one of the richly - hand-authored pilots) additionally gets its whole reader set generated from - the descriptor: the front-door `index.md`, the `inventory.md` reference, and - the Sample / Detector / Controls beam-walk pages. The pilots pass - model_tier=False and keep their hand-authored set; only their Source walk is - generated, as before. - - A model-tier beamline with `page_layout: stages` (the SRX pilot) uses a - flattened set instead: Inventory is dissolved into flat `source.md`, - `sample.md`, `detector.md`, and `controls.md` siblings (no `equipment/` - folder, no `inventory.md`), with `index.md` linking them directly. + """Render the beamline's one generated page: its Source-stage walk. + + `page_layout: stages` (2-BM's layout) generates a flat `source.md` sibling + of the hand-authored pages; `page_layout: walk` generates `beamline.md` + instead, with the walk-layout framing in its intro (no current descriptor + uses it, but nothing here assumes stages is the only valid choice). """ global _KNOWN_FAMILIES, _KNOWN_MODELS _KNOWN_FAMILIES = catalog_families @@ -97,50 +97,22 @@ def render_all( layout = descriptor.beamline.page_layout if layout == "stages": - # Stages layout: the generated Source page sits flat at source.md. A - # model-tier beamline's whole reader set is generated (index + the flat - # stage pages); a pilot hand-authors index / sample / detector / controls - # and their rich operational pages, so only its Source page generates. - pages = { + return { f"deployments/{slug}/source.md": _render_page( descriptor, slug=slug, blob_url=blob_url, link_inventory=False, - include_enclosures=not model_tier, flat=True, - show_source_ref=not model_tier, + show_source_ref=True, ), } - if model_tier: - pages[f"deployments/{slug}/index.md"] = _render_index( - descriptor, - slug=slug, - facility_label=facility_label, - control_plane=control_plane, - page_layout=layout, - ) - pages.update( - _render_beamwalk( - descriptor, slug=slug, control_plane=control_plane, prefix="", depth="../../" - ) - ) - return pages - - pages = { + + return { f"deployments/{slug}/beamline.md": _render_page( - descriptor, slug=slug, blob_url=blob_url, show_source_ref=not model_tier + descriptor, slug=slug, blob_url=blob_url, show_source_ref=True ) } - if model_tier: - pages[f"deployments/{slug}/index.md"] = _render_index( - descriptor, slug=slug, facility_label=facility_label, control_plane=control_plane - ) - pages[f"deployments/{slug}/inventory.md"] = _render_inventory( - descriptor, slug=slug, blob_url=blob_url - ) - pages.update(_render_beamwalk(descriptor, slug=slug, control_plane=control_plane)) - return pages def _esc(text: str) -> str: @@ -418,254 +390,3 @@ def _render_page( blocks.append(_render_group(name, group)) return "\n\n".join(blocks) + "\n" - - -# --------------------------------------------------------------------------- -# Model-tier reader pages, generated from the descriptor. -# -# A model beamline's whole reader set is generated so it cannot drift or carry -# engineering-internal bookkeeping (rule-of-three, loose-family graduation, -# tracking tags). The set is beamline-natural: a front door, then the beam walk -# (source to detector) plus controls. The default "walk" layout keeps the full -# asset tree as a separate Inventory reference; the "stages" layout dissolves -# Inventory into the flat source/sample/detector/controls stage pages. -# --------------------------------------------------------------------------- - -_STAGE_FILE = {"sample": "sample.md", "detection": "detector.md"} -_STAGE_TITLE = {"sample": "Sample", "detection": "Detector"} - - -def _confirm_clause(descriptor: BeamlineDescriptor) -> str: - beamline = descriptor.beamline - if beamline.evidence == "live": - return "" - if beamline.evidence == "controls_config": - return ( - " The device handles are read from the facility's public controls configuration and " - "verified against it; vendor parts, energies, and physical positions are not in it and " - "are carried `confirm` until beamline staff verify them." - ) - if beamline.evidence == "design_report": - return ( - " The values are read from the beamline's design report and carried `confirm` until " - "staff verify them against the built instrument." - ) - return ( - " The device families are inferred from public facility pages and papers; no control " - "handles or vendor models are public, so every value is carried `confirm` until staff " - "verify it." - ) - - -def _render_index( - descriptor: BeamlineDescriptor, - *, - slug: str, - facility_label: str | None, - control_plane: str | None, - page_layout: str = "walk", -) -> str: - beamline = descriptor.beamline - extra = beamline.model_extra or {} - name = beamline.name or slug - summary = beamline.summary or "" - blocks: list[str] = [f"# {name}"] - if summary: - cov = " A deliberately partial first cut." if beamline.coverage == "partial" else "" - blocks.append(f"*{summary}.{cov}*") - - # Facts table: identity + seam, all from the descriptor. - facility_cell = beamline.facility or "" - if facility_label: - facility_cell = f"[{facility_label}](../{beamline.facility}/index.md)" - if control_plane: - facility_cell += f" ({control_plane})" - facts: list[list[str]] = [["Facility", facility_cell]] - if beamline.sector: - facts.append(["Sector", str(beamline.sector)]) - src = str(beamline.source) if beamline.source else "" - if src: - facts.append(["Source", src]) - facts.append( - ["Modelled", f"{_MATURITY[beamline.maturity]}, {_COVERAGE[beamline.coverage]} coverage"] - ) - blocks.append(_table(["Property", "Value"], facts)) - - # The provenance caveat (how trustworthy the facts are, keyed on evidence - # tier) folds into the generated-from banner rather than a near-identical - # "What CORA models" section; the enclosures it used to name are in the - # table below. - banner = ( - f"This page is generated from the descriptor at " - f"[`deployments/{slug}/beamline.yaml`]({_BLOB_BASE}/deployments/{slug}/beamline.yaml). " - "Edit the descriptor, not this page." - ) - caveat = _confirm_clause(descriptor).strip() - ref = beamline.source_ref - if ref is not None: - # Name the specific public source the facts were read from, alongside the - # evidence-tier caveat. - caveat = (caveat + " ").lstrip() + f"Source: [{ref.label}]({ref.url})." - if caveat: - banner += "\n\n" + caveat - blocks.append(_admonition(banner, kind="info", title="Generated from the descriptor")) - - stages = {g.stage for _n, g in descriptor.groups} - flat = page_layout == "stages" - - # Enclosures are a beamline-wide spatial fact (every stage sits in a hutch), - # so the stages layout carries the table on the index rather than the Source - # page. The walk layout keeps it on the Source page (see _render_page). - if flat: - blocks.extend(_enclosures_blocks(descriptor)) - - if flat: - # Stages layout: the stages are first-class sibling pages, so present - # them as a section list (in beam order, controls last) rather than a - # one-line walk sentence. No Inventory (the stage pages are the tree). - # The optional one-line shape leads the section when authored. - lead = beamline.shape.strip() if beamline.shape else "The devices along the beam, area by area." - section = ["## The beamline", lead] - bullets = ["- [Source](source.md): the beam, produced and conditioned before the sample."] - if "sample" in stages: - bullets.append("- [Sample](sample.md): the sample environment and its positioning.") - if "detection" in stages: - bullets.append("- [Detector](detector.md): what records the beam after the sample.") - bullets.append("- [Controls](controls.md): the control plane CORA's edge conducts over.") - section.append("\n".join(bullets)) - blocks.append("\n\n".join(section)) - else: - # Walk layout: the beam walk as a one-line spine, linking the equipment/ - # pages and pointing at the Inventory reference for the full device tree. - walk_links = ["[Source](beamline.md)"] - if "sample" in stages: - walk_links.append("[Sample](equipment/sample.md)") - if "detection" in stages: - walk_links.append("[Detector](equipment/detector.md)") - walk_links.append("[Controls](equipment/controls.md)") - walk_sentence = ( - "The instrument, area by area along the beam: " - + " to ".join(walk_links[:-1]) - + f", driven by {walk_links[-1]}." - + " The full device tree, with families and control handles, is the " - "[Inventory](inventory.md)." - ) - blocks.append("## Walk the beam\n\n" + walk_sentence) - - # Every non-pilot beamline's model page closes by pointing here to explain - # why it has no runbook and no live experiment view. The section was lost - # when these index pages became generated, orphaning that link on 56 pages. - if beamline.maturity != "pilot": - blocks.append("## Not yet documented") - blocks.append( - "This beamline has no Operations runbook and no live Experiment view, " - "because CORA does not drive it yet. Both pages describe what an operator " - "does with a running system, so writing them for a beamline CORA only " - "models would be invention rather than documentation. The pilot at " - "[2-BM](../2-bm/index.md) carries both, and shows the shape they take " - "once a deployment goes live." - ) - - blocks.append("## More") - blocks.append( - "- [Notes](notes.md): techniques, governance, the CORA model index, and " - "open modelling questions." - ) - return "\n\n".join(blocks) + "\n" - - -_MATURITY = {"pilot": "operational pilot", "design": "in design", "model": "reverse-engineered"} -_COVERAGE = {"full": "full", "partial": "partial"} - - -def _render_inventory(descriptor: BeamlineDescriptor, *, slug: str, blob_url: str) -> str: - _set_catalog_depth("../../") # deployments//inventory.md - beamline = descriptor.beamline - name = beamline.name or slug - blocks: list[str] = ["# Inventory"] - blocks.append( - f"*The CORA Asset model for the operational core of {name}: every device by beam-path " - "stage, its Family and control handle, and what still needs confirming.*" - ) - blocks.append( - _admonition( - f"Generated from [`deployments/{slug}/beamline.yaml`]({blob_url}). " - "Edit the descriptor, not this page.", - kind="info", - title="Generated from the descriptor", - ) - ) - if descriptor.enclosures: - rows = [ - [ - f"`{e.name}`", - e.role or "", - f"`{e.facility_code}`" if e.facility_code else "", - _permit_signal_cell(e.permit_signal), - ] - for e in descriptor.enclosures - ] - blocks.append("## Enclosures") - blocks.append(_table(["Enclosure", "Role", "Facility", "Permit signal"], rows)) - - # All device groups, in beam-path order, each as its device table. - for stage in ("source", "sample", "detection"): - for gname, group in descriptor.groups: - if group.stage == stage: - blocks.append(_render_group(gname, group)) - if descriptor.controls is not None: - controls_devices = [ - *descriptor.controls.motion_controllers, - *descriptor.controls.triggering, - ] - if controls_devices: - blocks.append("## Controls") - if descriptor.controls.intro: - blocks.append(descriptor.controls.intro.strip()) - blocks.append(_device_table(controls_devices)) - return "\n\n".join(blocks) + "\n" - - -def _render_beamwalk( - descriptor: BeamlineDescriptor, - *, - slug: str, - control_plane: str | None, - prefix: str = "equipment/", - depth: str = "../../../", -) -> dict[str, str]: - # `prefix`/`depth` place the stage pages: the default "equipment/" + - # "../../../" is the walk layout; the stages layout passes "" + "../../" so - # the pages are flat siblings of index.md. - pages: dict[str, str] = {} - _set_catalog_depth(depth) - # Sample + Detector: one page per stage, its groups rendered. - for stage, filename in _STAGE_FILE.items(): - groups = [(n, g) for n, g in descriptor.groups if g.stage == stage] - if not groups: - continue - blocks = [f"# {_STAGE_TITLE[stage]}"] - # A single group whose name is just the stage would render a `##` heading - # duplicating the page title; render its body inline instead. - if len(groups) == 1 and _humanize(groups[0][0]) == _STAGE_TITLE[stage]: - blocks.append(_render_group_body(groups[0][1])) - else: - for gname, group in groups: - blocks.append(_render_group(gname, group)) - pages[f"deployments/{slug}/{prefix}{filename}"] = "\n\n".join(blocks) + "\n" - - # Controls: the cross-cutting drive electronics + the seam sentence. - controls = descriptor.controls - cblocks = ["# Controls"] - seam = "The control plane the beamline runs on, and the seam CORA's edge conducts over." - if control_plane: - seam += f" Control plane: {control_plane}." - cblocks.append(seam) - if controls is not None: - if controls.intro: - cblocks.append(controls.intro.strip()) - devices = [*controls.motion_controllers, *controls.triggering] - if devices: - cblocks.append(_device_table(devices)) - pages[f"deployments/{slug}/{prefix}controls.md"] = "\n\n".join(cblocks) + "\n" - return pages diff --git a/scripts/mkdocs_hooks.py b/scripts/mkdocs_hooks.py index 4705b954a84..dace7e7e2ed 100644 --- a/scripts/mkdocs_hooks.py +++ b/scripts/mkdocs_hooks.py @@ -261,14 +261,6 @@ def on_files(files: Any, *, config: Any) -> Any: generated: dict[str, str] = {} - # Load every site first so a beamline render can reach its Site's facility - # label + control-plane seam (the generated model-tier index / controls - # pages echo them). code -> (heading, control_plane). - site_by_code: dict[str, tuple[str, str | None]] = {} - for path in sorted(DEPLOYMENTS_DIR.glob("*/site.yaml")): - f = site_descriptor.load(path).facility - site_by_code[f.code] = (f.heading or f.code.upper(), f.control_plane) - # Beamlines: render each, and build the facility -> [BeamlineRef] map the # site pages use for their roster (label, slug, badges, summary), so the # facility page reads the one-liner + badges from the descriptor, not a @@ -278,16 +270,12 @@ def on_files(files: Any, *, config: Any) -> Any: slug = path.parent.name descriptor = beamline_descriptor.load(path) b = descriptor.beamline - facility_label, control_plane = site_by_code.get(b.facility or "", (None, None)) generated.update( beamline_pages.render_all( descriptor, slug=slug, catalog_families=catalog_families, catalog_models=catalog_models, - facility_label=facility_label, - control_plane=control_plane, - model_tier=b.deployment_tier == "model", ) ) if b.facility: