data-pipelines: DAG shown at both levels — collapsed modules and expanded tables - #265
Conversation
…ded tables) The regenerated figure shows tables, so "nodes represent Python modules" had become inaccurate. The section now pairs a collapsed module-level view (nodes are schemas/modules; edges bundle foreign key references and the import dependency) with the expanded table-level view (nodes are tables; each edge is an individual foreign key constraint).
scan→imaging now carries two foreign keys (Scan→ScanQuality, ScanInfo→MotionCorrection) that collapse into one heavier bundled edge; reference supplies both scan and imaging. Prose points at the bundling in both views.
…llapse Subject moves into lab; Session references User directly; Scan and ScanInfo move into session. Twelve tables collapse to four nodes, with two boundaries (lab→session, session→imaging) each bundling two foreign keys into one heavier edge.
The canonical four-module pipeline (lab, reference, session, imaging) now serves every section: the two-level DAG figures, the schema- organization file tree (lab.py/reference.py/session.py/imaging.py), a drill-in of the imaging module (master-part: Segmentation.Roi, Fluorescence.Trace), the OAS storage-tier snippet (ScanInfo), and the workflow steps name the example's tables. The unrelated scan-schema example (AcquisitionSoftware/Field/Channel) is dropped from this page.
…s the imaging drill-in)
…pped brand notation - Stored dj.Diagram SVG outputs in 16 notebooks recolored from the interim #1534 hues to the shipped #1544 tier palette (deterministic hex mapping, light and dark values). - Stored make_mermaid text output updated to the palette the library now emits. - Legends and notation prose corrected (calcium-imaging, both ephys tutorials, relational-workflow-model, diagram spec): orange computed tier, rounded boxes, entity clusters for master-part, underline = new dimension; the spec's tier table now carries the exact palette. - rwm-pipeline.svg recolored; rwm-legend.svg already current.
MilagrosMarin
left a comment
There was a problem hiding this comment.
The prose/figure mismatch is genuinely fixed, and I verified the new text against the generated SVGs — 14 tables → 4 nodes is exact, the collapsed edge set matches (reference feeding both session and imaging), the session → imaging pair (Scan → ScanQuality, ScanInfo → MotionCorrection) is correct, and imaging-schema.svg has all three master-part edges including Segmentation.Roi → Fluorescence.Trace. The diagram.md hex triples match _DIAGRAM_THEMES post-#1544 verbatim.
One correction, and the reason I'm marking this rather than commenting: the lab → session pair is given as "Subject → Session and Session → User" — the second is reversed. The generated SVG has User -> Session, and Lab -> User places User in the lab schema.
It's a one-word fix, but it's load-bearing in context:
- the collapsed figure directly above has no
session → labedge, which it would need ifSession → Userwere real; - the same paragraph argues that collapsing every module's tables "must itself yield a DAG" —
Session → Useralongsidelab → sessionwould put a cycle in exactly the claim being made.
The preceding session → imaging sentence gets parent→child right, so it reads as a slip rather than a different convention. Should be Subject → Session and User → Session.
Two notes, neither blocking:
The description covers essentially two files while the PR touches 25 — 14 regenerated notebooks (SVG output only, no re-execution — I checked) plus the spec-table rewrite and the example unification. All of it is coherent work, but worth reflecting in the body since that becomes the squash message.
The figures and the new spec table encode post-#1534/#1544 notation, and the latest release (v2.3.2, 2026-07-21) predates both merges — so this documents colors the released library doesn't produce. Explicitly not introduced here: #255/#256/#259/#263 already landed figures from the unreleased renderer. But it's now at spec depth rather than just figures, so worth confirming the release-time re-verification plan along the #210 lines.
The table-level SVG has `User -> Session` (and `Lab -> User`, placing `User` in the `lab` schema). The reversed form contradicted the collapsed figure above, which has no `session → lab` edge, and would have put a cycle in the DAG claim the same paragraph makes.
|
Thanks — you were right to block on this rather than leave it as a comment, and the reasoning is the part I want to keep. The reversed edge — fixed in 4458ae7. Scope in the body — done. You're right that the description covered two files while the change touches 25, and that the body becomes the squash message. Rewritten to name the example unification ( Release-time re-verification — recorded in #246 (#246 (comment)). Your distinction is the right one: figures from the unreleased renderer already landed in #255/#256/#259/#263, but this PR is the first to put that notation at spec depth, where a reader is entitled to treat the table as normative for the version they installed. #246 now stays open until the renderer changes ship, with two closing steps — re-verify the edge-style rows and theme hex triples against Ready for another look when you have a moment. |
MilagrosMarin
left a comment
There was a problem hiding this comment.
All three verified. The fix is one line and correct — User → Session, no stragglers, and the sentence now reads parent→child consistently across both pairs.
The rewritten description covers the real scope, and I'd single out the notebook line ("SVG output only … banners untouched, so the guard stays green") — that's the detail a future reader needs to know they can skip re-verifying 14 files.
#246 is the right home for the release gating, and the closing condition is sharper than what I raised: two concrete steps, gated on the renderer shipping, once per major.minor rather than per patch. Keeping the figures-vs-spec-depth distinction in the issue itself is what makes it actionable later.
Approving.
The three figures on the data-pipelines and relational-workflow-model pages were committed SVGs with no recoverable source: the 14-table pipeline behind them existed nowhere in the repo, so 'regenerate against a new release' meant reconstructing the schemas by reading the picture. scripts/pipeline_example/ defines that pipeline as four modules, one schema each -- the correspondence the page describes -- and scripts/gen_pipeline_diagrams.py renders the three figures from it. --check reports any committed figure that differs and exits non-zero. Verified to reproduce the committed figures' nodes, tiers, shapes, edge weights and styles, tooltips, clusters and labels. Three caveats are documented in the module docstring: the generator needs an empty database, tooltip padding entities vary by pydot version, and the collapsed lab -> session edge is traversal-order dependent in the renderer.
|
Folded the diagram generator into this PR (bce9e26), and building it turned up something on this page's prose that needs a call before merge. The generator
This was worth doing because the figures had no recoverable source. The pipeline behind them was nowhere in the repo, so the #246 release-time step was going to mean re-deriving 14 table definitions from the picture. Now it's What it exposedThe prose claim about edge weight is wrong, and it isn't wrong because of anything in this PR.
if not new_graph.has_edge(new_src, new_dest):
new_graph.add_edge(new_src, new_dest, **data)The collapsed edge takes the attributes of whichever foreign key in the bundle is visited first. There is no aggregation over the bundle. Two consequences:
Note this is the same edge as your blocking correction, from the other direction: you caught the prose misdescribing its direction, and the figure turns out to be unstable in how it draws it. What I'd like your read onThree options, and I don't want to pick unilaterally since it changes what the page claims:
I lean 1 — the sentence is load-bearing for a reader checking the figure, which is exactly the failure mode you caught in the direction slip, and the renderer fix shouldn't gate a docs correction. Also two smaller things from the docstring: the generator needs an empty database (unprefixed schema names are required for the cluster labels, so it silently absorbs foreign tables if a |
On PostgreSQL a dj.Schema is a schema within a database, so DJ_DATABASE_NAME is enough to keep the example's unprefixed schema names from colliding -- separate databases can hold same-named schemas. The previous note asked for a throwaway server, which is stronger than necessary.
MilagrosMarin
left a comment
There was a problem hiding this comment.
Verified all of it. diagram.py:1387 is first-wins with no aggregation, and the figure bears out the consequence: lab → session and session → imaging both bundle exactly two foreign keys and render 0.75 and 2 respectively. So "a heavier edge carries a larger bundle" is contradicted by the figure directly beneath it. Per-FK styles confirm your reading — Scan → ScanQuality and ScanInfo → MotionCorrection are both 1:1, Subject → Session and User → Session both multi.
On the dash instability: narrower than it might read. Only lab → session has a mixed bundle. session → imaging's two members agree, and both reference edges are single-FK bundles (ScannerModel → Scan, SegmentationMethod → Segmentation). One of four edges is order-dependent — still real, still worth fixing, but the blast radius on --check is one edge.
Agreed on option 1, and I'd rule out 3 explicitly: shipping a sentence you know the figure contradicts is worse than the direction slip was, because that one was accidental. Option 2 gates a docs correction on a renderer behavior change that needs its own design call — what should a mixed bundle render as? — and widens the gap #246 already tracks.
One refinement on how to fix the prose. Dropping the bundle-size reading isn't quite enough: softening to "weight encodes cardinality" would still mislead, since on a collapsed edge it's the cardinality of one arbitrary bundle member. I'd not attribute meaning to weight or dash in the collapsed figure at all — describe it as one edge per schema pair, and keep the weight/dash semantics with the table-level figure where they're well defined. Otherwise the wrong claim becomes a vaguer claim that's still unsupported.
For the follow-up: worth including the dash instability alongside the weight semantics, since it means the committed figure isn't byte-reproducible for reasons unrelated to pydot padding — which lands on #246's "regenerate the committed SVGs" step.
The generator is a real improvement regardless. Re-deriving 14 table definitions from a picture at release time was the alternative.
The page claimed 'a heavier edge carries a larger bundle'. The figure directly beneath contradicts it: lab -> session and session -> imaging both bundle exactly two foreign keys and render 0.75 and 2. A collapsed edge inherits the attributes of one arbitrary bundle member, so weight there is neither bundle size nor a property of the bundle. Softening to 'weight encodes cardinality' would still mislead for the same reason, so the collapsed view now attributes no meaning to weight or style: it is one edge per schema pair, recording that a bundle exists. The weight and style semantics stay with the table-level figure, where they are well defined, now pointing at the Diagram spec.
|
Option 1 it is, and your refinement was the right call — I'd drafted exactly the softening you warned against ("weight encodes cardinality") before rereading your point that on a collapsed edge it's the cardinality of one arbitrary member. Vaguer and still unsupported. Prose fixed in 4be3067. The collapsed view now attributes no meaning to weight or style: one edge per schema pair, recording that a bundle exists. Weight and style semantics moved to the table-level paragraph, which now points at On option 2 — the design call has been made, so it's no longer a blocker; it's just a separate PR. datajoint/datajoint-python#1545. The rule: an edge between two collapsed nodes claims none of the per-foreign-key properties, and every bundle edge renders identically (solid, penwidth 2). Two points that sharpened it while implementing:
I chose uniform styling over aggregating the bundle (thick only if every member is 1:1, dashed only if all secondary). Aggregation is well-defined and order-independent too, but it gives the same visual cue a second meaning that holds only at the collapsed level. One meaning per cue. The order-dependence reproduces cleanly, which answers your reproducibility point: identical structure, opposite declaration order, Not regenerating the committed SVGs in this PR. The fix isn't merged or released, and the figures shouldn't move ahead of the renderer that docs builds against. When it does land, |
|
Correcting something I told you two comments up: I said the release-time re-verification was "recorded in #246, which stays open until the renderer changes ship." Wrong place. #246's edge-weight half shipped in #247 back on Aug 13 — the commit referenced the issue without closing it, so it has been sitting open with that part done. Its remaining half is the one #247 explicitly deferred: tier palette, entity groups, and the left-to-right/no-arrowhead direction. That is the The recurring re-verification is a maintenance step, not #246's ask, and hanging it there would have kept the issue open indefinitely past the work it describes. It now lives in #266, with your two refinements built in: the pydot padding diff called out as a non-signal, and the collapsed-edge order-dependence recorded separately from it, since that one genuinely broke byte-reproducibility until datajoint-python#1545. Worth flagging for your review of the spec table here: the hex triples #246 proposed are not what shipped. It suggested Manual |
MilagrosMarin
left a comment
There was a problem hiding this comment.
All three landed, and the prose fix is the version I was hoping for — "a collapsed edge records only that the bundle exists … read at the table level, not here" attributes nothing rather than attributing something vaguer, and the table-level paragraph now carries the full notation explicitly. Zero residue of the bundle-size claim, User → Session correct, body covers all 25 files, and #246/#266 are split the right way: #247 did ship the edge-weight half back on Aug 13, so closing #246 here is right, and the recurring step belongs in its own issue.
The generator is the part that will keep paying off — --check instead of re-deriving 14 table definitions from a picture is exactly the difference between a release step someone does and one they skip.
One thing to fold in before datajoint-python#1545 ships: that PR gives bundle edges penwidth 2 solid, which is what § Edge Styles here assigns to "1:1 dependency" + "primary foreign key," and the section states "line weight encodes cardinality, and only cardinality — it is binary." That becomes untrue in collapsed views, and the table has no mention of bundles. Since #1545 carries no docs, this table is the only normative home for it — a row or a sentence would do. Cheap to add here while the PR is open, or fold into #266 if you'd rather keep this one closed.
Approving — this doesn't block, since the spec only goes stale when #1545 ships.
Follow-up to #263. The restyle replaced the module-level illustration with a generated table-level diagram, which made the prose inaccurate — it still said "nodes represent Python modules" while the figure's nodes are tables.
The DAG at both levels
src/explanation/data-pipelines.mdnow shows the same pipeline at both levels, usingdj.Diagram'scollapse():pipeline-modules-collapsed.svg, one node per schema/module with its table count; each edge represents a bundle: the foreign key references between the two schemas' tables plus the Python import dependency between their modules.pipeline-modules.svg; nodes are tables, dashed clusters group each module, and each edge is an individual foreign key constraint.Closes the description gap and demonstrates the collapse feature in the docs. Both figures are generated from the same schema set, adaptive light/dark.
Also in this PR
The change touches 25 files; the rest is the work that makes the two figures consistent with the surrounding docs:
imaging-schema.svgreplacesscan-schema.svg, so the schema set behind the two new figures is the one the surrounding pages already use.relational-workflow-model.md,index.md, andrwm-pipeline.svgfollow.reference/specs/diagram.md§ Visual Encoding: the notation and theme hex triples brought in line with_DIAGRAM_THEMES. This is the half of Diagram notation: edge-weight rule is stated incorrectly; document the modernized style #246 that docs(#246): correct the diagram edge-weight rule #247 deferred (tier palette, entity groups, direction), documenting what #1544 actually shipped rather than the hex values Diagram notation: edge-weight rule is stated incorrectly; document the modernized style #246 proposed.check-notebook-versionsguard stays green on 2.3.Diagram generator
The three figures on these pages were committed SVGs with no recoverable source — the 14-table pipeline behind them existed nowhere in the repo, so re-verifying them against a future release meant reconstructing the schemas by reading the picture.
scripts/pipeline_example/— that pipeline, as four modules with one schema each, which is the module↔schema correspondence the page describes.scripts/gen_pipeline_diagrams.py— renders the three figures from it.--checkreports any committed figure that differs and exits non-zero, so Diagram notation: edge-weight rule is stated incorrectly; document the modernized style #246's release-time step becomes a command rather than a manual audit.Verified to reproduce the committed figures' nodes, tiers, shapes, edge weights and styles, tooltips, clusters, and labels. The committed SVGs are unchanged by this PR — the generator is added alongside them, not used to overwrite them.
Three caveats are documented in the module docstring, two of which are findings worth their own follow-up:
sessionschema the diagram silently picks up the foreign tables. This bit during development. from whichever pydot produced the committed figures, literal spaces from 4.0.1. Whole-file diff, no visual change. Nothing pins pydot.lab → sessionedge is traversal-order dependent.Diagram._collapse_graphgives a collapsed edge the attributes of whichever foreign key in its bundle is visited first, with no aggregation. Where a bundle mixes a primary and a secondary foreign key — aslab → sessiondoes, bundlingSubject → SessionandUser → Session— it renders solid or dashed on order alone. The committed figure is solid; the generator produces dashed. Both come out of the same renderer.That last one has a consequence for the prose on this page, flagged for review rather than silently changed — see the review thread.
Verification
referencefeeds bothsessionandimaging; the twosession → imagingforeign keys areScan → ScanQualityandScanInfo → MotionCorrection; the twolab → sessionforeign keys areSubject → SessionandUser → Session.imaging-schema.svgcarries all three master-part edges, includingSegmentation.Roi → Fluorescence.Trace.diagram.mdhex triples match_DIAGRAM_THEMESpost-#1544.Release-time re-verification
The figures and the spec table encode notation from datajoint-python #1534/#1544, which the latest release (v2.3.2) predates, and datajoint-python#1545 will change the collapsed figure again. That recurring re-verification is tracked separately in #266 — once per
major.minor, following #210.Closes #246.