domain/converters/loop.converter.ts
domain/converters/loop.converter.ts is a file in Bane's Lab Site. 174 lines of code and 20 definitions.
import {
AXIS_LABEL,
CONTRACTS_LABEL,
FIELD_SEPARATOR,
FLOW_ARROW,
GROUNDS_LABEL,
LAYER_LABEL,
MATH_TYPE_LABEL,
} from "#configuration/strings/ontology.strings";
import { EDGE_DOTTED, EDGE_LABEL_OPEN, EDGE_SOLID } from "#configuration/constants/diagram.constants";
import { FACE_SEPARATOR, REASON_FACE } from "@govlab/constants";
import {
GATE_LABEL,
LOOP_CAPTION,
LOOP_INTRO,
LOOP_SECTION_TITLE,
MODEL_INTRO,
MODEL_TITLE,
ON_FAIL_LABEL,
ON_PASS_LABEL,
QUESTION_LABEL,
RECURSION_LABEL,
SEQUENCE_TITLE,
SUBSTRATE_CAPTION,
SUBSTRATE_INTRO,
SUBSTRATE_SECTION_TITLE,
TRANSITIONS_TITLE,
TRANSITION_LABEL,
} from "#configuration/strings/reason.strings";
import type { LoopStageView, LoopTransitionView, SubstrateNodeView } from "#types/loop.types";
import { MODEL_SECTION_ID, REASON_ANCHOR, STAGE_ANCHOR, SUBSTRATE_SECTION_ID } from "#core/ids/ontology.ids";
import type { ModelView, ReasonView } from "#types/reason.types";
import type { Section, Subsection } from "#types/document.types";
import { chips, fields, glossary, labeled, linkOf, linkRow, linked, row } from "#domain/converters/ontology.converter";
import { diagram, edgeLine, node } from "#domain/converters/ontology.graph.converter";
import { REASON_SECTION_ICON } from "#configuration/icons/ontology.icons";
import type { ResolutionView } from "#types/ontology.types";
import { checkBlocks } from "#domain/converters/check.converter";
import { reasonRecord } from "#domain/converters/reason.fragment.converter";
const HEADER = "flowchart TB";
const HEADER_WIDE = "flowchart LR";
const GATE_KIND = "gates";
const REFUTE_KIND = "refutes-back";
const transitionLine = function transitionLine(transition: LoopTransitionView): string {
if (transition.kind === REFUTE_KIND) {
return edgeLine(transition.from.label, EDGE_DOTTED, transition.to.label);
}
if (transition.kind === GATE_KIND && transition.gate !== null) {
return edgeLine(transition.from.label, EDGE_LABEL_OPEN + transition.gate + EDGE_SOLID, transition.to.label);
}
return edgeLine(transition.from.label, EDGE_SOLID, transition.to.label);
};
const loopDiagram = function loopDiagram(reason: ReasonView): string {
const { derivationLoop } = reason;
const nodes = derivationLoop.stages.map((stage) => node(stage.id, stage.id + FIELD_SEPARATOR + stage.axis.label));
return diagram(HEADER, [...nodes, ...derivationLoop.transitions.map(transitionLine)]);
};
const stageSubsection = function stageSubsection(stage: LoopStageView, resolution: ResolutionView): Subsection {
return {
blocks: [
chips([linked(AXIS_LABEL, stage.axis)]),
glossary([...linkRow(CONTRACTS_LABEL, stage.contracts), ...linkRow(GROUNDS_LABEL, stage.edges)]),
...checkBlocks(resolution, REASON_FACE + FACE_SEPARATOR + stage.anchor),
],
id: STAGE_ANCHOR + stage.id,
title: stage.id,
};
};
const transitionRow = function transitionRow(transition: LoopTransitionView): {
readonly description: string;
readonly term: string;
} {
return row(
transition.from.label + FLOW_ARROW + transition.to.label,
fields([
linkOf(transition.from) + FLOW_ARROW + linkOf(transition.to),
labeled(TRANSITION_LABEL, transition.kind),
labeled(GATE_LABEL, transition.gate),
labeled(ON_PASS_LABEL, transition.onPass),
labeled(ON_FAIL_LABEL, transition.onFail),
]),
);
};
export const loopSection = function loopSection(reason: ReasonView, resolution: ResolutionView): Section {
const { derivationLoop } = reason;
return {
blocks: [
{ caption: LOOP_CAPTION, kind: "mermaid", text: loopDiagram(reason) },
...checkBlocks(resolution, REASON_FACE + FACE_SEPARATOR + derivationLoop.anchor),
],
icon: REASON_SECTION_ICON,
id: REASON_ANCHOR + derivationLoop.anchor,
intro: LOOP_INTRO,
subsections: [
...derivationLoop.stages.map((stage) => stageSubsection(stage, resolution)),
{ blocks: [glossary(derivationLoop.transitions.map(transitionRow))], title: TRANSITIONS_TITLE },
],
title: LOOP_SECTION_TITLE,
};
};
const substrateDiagram = function substrateDiagram(reason: ReasonView): string {
const { substrate } = reason;
const nodes = substrate.cycle.map((stage) => node(stage.label, stage.label));
const edges = substrate.cycle.flatMap((stage, index) => {
const next = substrate.cycle[index + 1];
return next === undefined ? [] : [edgeLine(stage.label, EDGE_SOLID, next.label)];
});
return diagram(HEADER_WIDE, [
...nodes,
...edges,
edgeLine(substrate.recursionFrom.label, EDGE_DOTTED, substrate.recursionTo.label),
]);
};
const substrateSubsection = function substrateSubsection(
entry: SubstrateNodeView,
resolution: ResolutionView,
): Subsection {
return {
blocks: [
chips([linked(LAYER_LABEL, entry.layer), linked(MATH_TYPE_LABEL, entry.mathType)]),
...checkBlocks(resolution, REASON_FACE + FACE_SEPARATOR + entry.anchor),
],
id: REASON_ANCHOR + entry.anchor,
title: entry.name,
};
};
const modelSubsection = function modelSubsection(model: ModelView, resolution: ResolutionView): Subsection {
const sequence = [...model.sequence.map(linkOf), ...model.steps].join(FLOW_ARROW);
return reasonRecord(
model.anchor,
model.id,
[
chips([
labeled(QUESTION_LABEL, model.question),
model.recursionFrom === null || model.recursionTo === null
? null
: labeled(RECURSION_LABEL, model.recursionFrom + FLOW_ARROW + model.recursionTo),
]),
glossary([row(SEQUENCE_TITLE, sequence)]),
],
resolution,
);
};
export const modelSection = function modelSection(reason: ReasonView, resolution: ResolutionView): Section {
return {
icon: REASON_SECTION_ICON,
id: MODEL_SECTION_ID,
intro: MODEL_INTRO,
subsections: reason.models.map((model) => modelSubsection(model, resolution)),
title: MODEL_TITLE,
};
};
export const substrateSection = function substrateSection(reason: ReasonView, resolution: ResolutionView): Section {
const { substrate } = reason;
return {
blocks: [
{ caption: SUBSTRATE_CAPTION, kind: "mermaid", text: substrateDiagram(reason) },
{
kind: "text",
text:
RECURSION_LABEL +
FIELD_SEPARATOR +
linkOf(substrate.recursionFrom) +
FLOW_ARROW +
linkOf(substrate.recursionTo),
},
],
icon: REASON_SECTION_ICON,
id: SUBSTRATE_SECTION_ID,
intro: SUBSTRATE_INTRO,
subsections: substrate.nodes.map((entry) => substrateSubsection(entry, resolution)),
title: SUBSTRATE_SECTION_TITLE,
};
};