domain/converters/principle.converter.ts
domain/converters/principle.converter.ts is a file in Bane's Lab Site. 152 lines of code and 14 definitions.
import {
AFTER_TITLE,
ALIASES_LABEL,
BEFORE_TITLE,
CANON_LABEL,
CATEGORY_CAPTION,
CATEGORY_INTRO,
CATEGORY_LABEL,
CONFLICTS_WITH_LABEL,
CONTRACTS_LABEL,
DETECTED_BY_LABEL,
DISTINCT_FROM_LABEL,
ENABLES_LABEL,
ENFORCED_BY_LABEL,
EXPRESSED_BY_LABEL,
FORMED_BY_LABEL,
KIND_LABEL,
LABEL_SEPARATOR,
LAYER_LABEL,
MANDATORY_FOR_LABEL,
MEASURED_BY_LABEL,
REFACTORED_BY_LABEL,
REFERENCED_BY_LABEL,
REINFORCES_LABEL,
REQUIRES_LABEL,
SCOPE_LABEL,
SEVERITY_LABEL,
TENSIONS_LABEL,
TENSIONS_WITH_LABEL,
TERM_LABEL,
VIOLATED_BY_LABEL,
} from "#configuration/strings/ontology.strings";
import { ARCH_ANCHOR, ARCH_CATEGORY_ANCHOR } from "#core/ids/ontology.ids";
import { ARCH_FACE, FACE_SEPARATOR } from "@govlab/constants";
import { EDGE_DOTTED, EDGE_SOLID } from "#configuration/constants/diagram.constants";
import type { PrincipleCategoryView, PrincipleView, ResolutionView } from "#types/ontology.types";
import type { Section, Subsection } from "#types/document.types";
import { canonOf, checkBlocks } from "#domain/converters/check.converter";
import {
chips,
exemplarBlocks,
glossary,
labeled,
linkList,
linkOf,
linkRow,
linked,
plainList,
row,
wideRow,
} from "#domain/converters/ontology.converter";
import { diagram, edgeLine, localId, node } from "#domain/converters/ontology.graph.converter";
import type { Block } from "#types/block.types";
import { CATEGORY_SECTION_ICON } from "#configuration/icons/ontology.icons";
import { DEFINITION_LABEL } from "#configuration/strings/lexicon.strings";
import type { EdgeRef } from "#types/link.types";
import { groupTitleOf } from "#configuration/strings/catalog.strings";
const HEADER = "flowchart LR";
const NEGATIVE = " --x ";
const facts = function facts(principle: PrincipleView): Block {
return chips([
linked(KIND_LABEL, principle.kind),
linked(CATEGORY_LABEL, principle.category),
linked(SEVERITY_LABEL, principle.severity),
labeled(MANDATORY_FOR_LABEL, principle.mandatoryFor),
principle.scope.length === 0 ? null : labeled(SCOPE_LABEL, linkList(principle.scope)),
principle.aliases.length === 0 ? null : labeled(ALIASES_LABEL, plainList(principle.aliases)),
linked(LAYER_LABEL, principle.layer),
]);
};
const relations = function relations(principle: PrincipleView): Block {
return glossary([
row(REQUIRES_LABEL, linkList(principle.edges.requires)),
row(REINFORCES_LABEL, linkList(principle.edges.reinforces)),
row(ENABLES_LABEL, linkList(principle.edges.enables)),
row(TENSIONS_WITH_LABEL, linkList(principle.edges.tensionsWith)),
row(CONFLICTS_WITH_LABEL, linkList(principle.edges.conflictsWith)),
...linkRow(REFERENCED_BY_LABEL, principle.referencedBy),
...linkRow(TERM_LABEL, principle.term === null ? [] : [principle.term]),
...linkRow(CONTRACTS_LABEL, principle.contracts),
...linkRow(TENSIONS_LABEL, principle.tensions),
...linkRow(EXPRESSED_BY_LABEL, principle.expressedBy),
...principle.distinctFrom.map((entry) =>
row(DISTINCT_FROM_LABEL, linkOf(entry.record) + LABEL_SEPARATOR + entry.reason),
),
]);
};
const descriptors = function descriptors(principle: PrincipleView): Block {
return glossary([
principle.formedBy === null
? row(VIOLATED_BY_LABEL, linkList(principle.violatedBy))
: row(FORMED_BY_LABEL, principle.formedBy),
row(DETECTED_BY_LABEL, linkList(principle.detectedBy)),
row(MEASURED_BY_LABEL, linkList(principle.measuredBy)),
row(REFACTORED_BY_LABEL, linkList(principle.refactoredBy)),
row(ENFORCED_BY_LABEL, linkList(principle.enforcedBy)),
...(principle.canon.length === 0 ? [] : [row(CANON_LABEL, canonOf(principle.canon))]),
]);
};
const principleSubsection = function principleSubsection(
principle: PrincipleView,
resolution: ResolutionView,
): Subsection {
return {
blocks: [
facts(principle),
glossary([wideRow(DEFINITION_LABEL, principle.definition)]),
relations(principle),
descriptors(principle),
...exemplarBlocks(principle.exemplar, BEFORE_TITLE, AFTER_TITLE),
...checkBlocks(resolution, ARCH_FACE + FACE_SEPARATOR + principle.id),
],
id: ARCH_ANCHOR + principle.id,
title: principle.name,
};
};
const categoryDiagram = function categoryDiagram(group: PrincipleCategoryView): string {
const members = new Set(group.principles.map((principle) => principle.id));
const nodes = group.principles.map((principle) => node(principle.id, principle.name));
const edges = group.principles.flatMap((principle) => {
const arrows: [readonly EdgeRef[], string][] = [
[principle.edges.requires, EDGE_SOLID],
[principle.edges.reinforces, EDGE_SOLID],
[principle.edges.enables, EDGE_SOLID],
[principle.edges.tensionsWith, EDGE_DOTTED],
[principle.edges.conflictsWith, NEGATIVE],
];
return arrows.flatMap(([targets, arrow]) =>
targets.flatMap((target) => {
const id = localId(target, ARCH_FACE, members);
return id === null || id === principle.id ? [] : [edgeLine(principle.id, arrow, id)];
}),
);
});
return diagram(HEADER, [...nodes, ...edges]);
};
const categorySection = function categorySection(group: PrincipleCategoryView, resolution: ResolutionView): Section {
return {
blocks: [{ caption: CATEGORY_CAPTION, kind: "mermaid", text: categoryDiagram(group) }],
icon: CATEGORY_SECTION_ICON,
id: ARCH_CATEGORY_ANCHOR + group.id,
intro: CATEGORY_INTRO,
subsections: group.principles.map((principle) => principleSubsection(principle, resolution)),
title: groupTitleOf(group.id, group.category),
};
};
export const principleSections = function principleSections(
groups: readonly PrincipleCategoryView[],
resolution: ResolutionView,
): readonly Section[] {
return groups.map((group) => categorySection(group, resolution));
};