@cyberismo/data-handler 0.0.8 → 0.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/command-handler.d.ts +11 -2
- package/dist/command-handler.js +61 -18
- package/dist/command-handler.js.map +1 -1
- package/dist/command-manager.js +8 -8
- package/dist/command-manager.js.map +1 -1
- package/dist/commands/calculate.d.ts +7 -44
- package/dist/commands/calculate.js +8 -389
- package/dist/commands/calculate.js.map +1 -1
- package/dist/commands/create.d.ts +6 -3
- package/dist/commands/create.js +27 -5
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/edit.d.ts +15 -3
- package/dist/commands/edit.js +52 -9
- package/dist/commands/edit.js.map +1 -1
- package/dist/commands/export.d.ts +11 -9
- package/dist/commands/export.js +80 -28
- package/dist/commands/export.js.map +1 -1
- package/dist/commands/import.d.ts +7 -0
- package/dist/commands/import.js +13 -0
- package/dist/commands/import.js.map +1 -1
- package/dist/commands/move.d.ts +11 -12
- package/dist/commands/move.js +12 -13
- package/dist/commands/move.js.map +1 -1
- package/dist/commands/remove.d.ts +1 -3
- package/dist/commands/remove.js +4 -6
- package/dist/commands/remove.js.map +1 -1
- package/dist/commands/rename.d.ts +1 -3
- package/dist/commands/rename.js +3 -6
- package/dist/commands/rename.js.map +1 -1
- package/dist/commands/show.d.ts +37 -5
- package/dist/commands/show.js +89 -8
- package/dist/commands/show.js.map +1 -1
- package/dist/commands/transition.d.ts +1 -3
- package/dist/commands/transition.js +3 -5
- package/dist/commands/transition.js.map +1 -1
- package/dist/commands/update.d.ts +5 -1
- package/dist/commands/update.js +7 -1
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/validate.d.ts +7 -0
- package/dist/commands/validate.js +31 -4
- package/dist/commands/validate.js.map +1 -1
- package/dist/containers/card-container.d.ts +6 -0
- package/dist/containers/card-container.js +61 -0
- package/dist/containers/card-container.js.map +1 -1
- package/dist/containers/project/calculation-engine.d.ts +90 -0
- package/dist/containers/project/calculation-engine.js +402 -0
- package/dist/containers/project/calculation-engine.js.map +1 -0
- package/dist/containers/project/resource-collector.js +9 -13
- package/dist/containers/project/resource-collector.js.map +1 -1
- package/dist/containers/project.d.ts +18 -1
- package/dist/containers/project.js +28 -55
- package/dist/containers/project.js.map +1 -1
- package/dist/containers/template.d.ts +5 -0
- package/dist/containers/template.js +9 -0
- package/dist/containers/template.js.map +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/interfaces/macros.d.ts +4 -2
- package/dist/interfaces/project-interfaces.d.ts +21 -0
- package/dist/interfaces/project-interfaces.js +4 -0
- package/dist/interfaces/project-interfaces.js.map +1 -1
- package/dist/interfaces/resource-interfaces.d.ts +4 -2
- package/dist/interfaces/resource-interfaces.js.map +1 -1
- package/dist/macros/base-macro.d.ts +2 -1
- package/dist/macros/base-macro.js +14 -2
- package/dist/macros/base-macro.js.map +1 -1
- package/dist/macros/common.d.ts +16 -9
- package/dist/macros/common.js +22 -9
- package/dist/macros/common.js.map +1 -1
- package/dist/macros/createCards/index.d.ts +2 -2
- package/dist/macros/createCards/index.js +4 -0
- package/dist/macros/createCards/index.js.map +1 -1
- package/dist/macros/graph/index.d.ts +1 -3
- package/dist/macros/graph/index.js +1 -6
- package/dist/macros/graph/index.js.map +1 -1
- package/dist/macros/image/index.d.ts +39 -0
- package/dist/macros/image/index.js +82 -0
- package/dist/macros/image/index.js.map +1 -0
- package/dist/macros/image/metadata.d.ts +18 -0
- package/dist/macros/image/metadata.js +22 -0
- package/dist/macros/image/metadata.js.map +1 -0
- package/dist/macros/include/index.d.ts +31 -0
- package/dist/macros/include/index.js +94 -0
- package/dist/macros/include/index.js.map +1 -0
- package/dist/macros/include/metadata.d.ts +15 -0
- package/dist/macros/include/metadata.js +19 -0
- package/dist/macros/include/metadata.js.map +1 -0
- package/dist/macros/index.d.ts +33 -31
- package/dist/macros/index.js +142 -71
- package/dist/macros/index.js.map +1 -1
- package/dist/macros/percentage/index.d.ts +28 -0
- package/dist/macros/percentage/index.js +33 -0
- package/dist/macros/percentage/index.js.map +1 -0
- package/dist/macros/percentage/metadata.d.ts +15 -0
- package/dist/macros/percentage/metadata.js +19 -0
- package/dist/macros/percentage/metadata.js.map +1 -0
- package/dist/macros/report/index.d.ts +2 -6
- package/dist/macros/report/index.js +3 -7
- package/dist/macros/report/index.js.map +1 -1
- package/dist/macros/scoreCard/index.d.ts +14 -15
- package/dist/macros/scoreCard/index.js +14 -18
- package/dist/macros/scoreCard/index.js.map +1 -1
- package/dist/macros/vega/index.d.ts +28 -0
- package/dist/macros/vega/index.js +27 -0
- package/dist/macros/vega/index.js.map +1 -0
- package/dist/macros/vega/metadata.d.ts +15 -0
- package/dist/macros/vega/metadata.js +7 -0
- package/dist/macros/vega/metadata.js.map +1 -0
- package/dist/macros/vegalite/index.d.ts +26 -0
- package/dist/macros/vegalite/index.js +24 -0
- package/dist/macros/vegalite/index.js.map +1 -0
- package/dist/macros/vegalite/metadata.d.ts +15 -0
- package/dist/macros/vegalite/metadata.js +7 -0
- package/dist/macros/vegalite/metadata.js.map +1 -0
- package/dist/macros/xref/index.d.ts +26 -0
- package/dist/macros/xref/index.js +53 -0
- package/dist/macros/xref/index.js.map +1 -0
- package/dist/macros/xref/metadata.d.ts +15 -0
- package/dist/macros/xref/metadata.js +19 -0
- package/dist/macros/xref/metadata.js.map +1 -0
- package/dist/module-manager.d.ts +1 -0
- package/dist/module-manager.js +14 -4
- package/dist/module-manager.js.map +1 -1
- package/dist/permissions/action-guard.d.ts +2 -2
- package/dist/permissions/action-guard.js +1 -1
- package/dist/permissions/action-guard.js.map +1 -1
- package/dist/resources/card-type-resource.d.ts +2 -0
- package/dist/resources/card-type-resource.js +63 -0
- package/dist/resources/card-type-resource.js.map +1 -1
- package/dist/resources/file-resource.d.ts +2 -0
- package/dist/resources/file-resource.js +12 -4
- package/dist/resources/file-resource.js.map +1 -1
- package/dist/resources/folder-resource.d.ts +19 -0
- package/dist/resources/folder-resource.js +51 -2
- package/dist/resources/folder-resource.js.map +1 -1
- package/dist/resources/graph-model-resource.js +1 -1
- package/dist/resources/graph-model-resource.js.map +1 -1
- package/dist/resources/graph-view-resource.js +1 -1
- package/dist/resources/graph-view-resource.js.map +1 -1
- package/dist/resources/report-resource.js +1 -1
- package/dist/resources/report-resource.js.map +1 -1
- package/dist/resources/resource-object.d.ts +8 -0
- package/dist/resources/resource-object.js +10 -1
- package/dist/resources/resource-object.js.map +1 -1
- package/dist/resources/template-resource.js +1 -1
- package/dist/resources/template-resource.js.map +1 -1
- package/dist/resources/workflow-resource.d.ts +2 -0
- package/dist/resources/workflow-resource.js +53 -9
- package/dist/resources/workflow-resource.js.map +1 -1
- package/dist/svg/index.d.ts +15 -0
- package/dist/svg/index.js +16 -0
- package/dist/svg/index.js.map +1 -0
- package/dist/svg/lib.d.ts +9 -0
- package/dist/svg/lib.js +26 -0
- package/dist/svg/lib.js.map +1 -0
- package/dist/svg/percentage.d.ts +25 -0
- package/dist/svg/percentage.js +90 -0
- package/dist/svg/percentage.js.map +1 -0
- package/dist/svg/scoreCard.d.ts +19 -0
- package/dist/svg/scoreCard.js +55 -0
- package/dist/svg/scoreCard.js.map +1 -0
- package/dist/types/queries.d.ts +8 -7
- package/dist/types/queries.js.map +1 -1
- package/dist/utils/card-utils.d.ts +6 -0
- package/dist/utils/card-utils.js +12 -0
- package/dist/utils/card-utils.js.map +1 -1
- package/dist/utils/clingo-facts.d.ts +2 -1
- package/dist/utils/clingo-facts.js +19 -2
- package/dist/utils/clingo-facts.js.map +1 -1
- package/dist/utils/clingo-parser.d.ts +1 -0
- package/dist/utils/clingo-parser.js +22 -100
- package/dist/utils/clingo-parser.js.map +1 -1
- package/dist/utils/constants.d.ts +7 -0
- package/dist/utils/constants.js +14 -0
- package/dist/utils/constants.js.map +1 -1
- package/dist/utils/csv.js.map +1 -1
- package/dist/utils/report.d.ts +17 -3
- package/dist/utils/report.js +38 -2
- package/dist/utils/report.js.map +1 -1
- package/dist/utils/resource-utils.d.ts +2 -1
- package/dist/utils/resource-utils.js +12 -3
- package/dist/utils/resource-utils.js.map +1 -1
- package/dist/utils/user-preferences.d.ts +1 -11
- package/dist/utils/user-preferences.js +30 -13
- package/dist/utils/user-preferences.js.map +1 -1
- package/dist/utils/validate.d.ts +2 -3
- package/dist/utils/validate.js +2 -2
- package/dist/utils/validate.js.map +1 -1
- package/package.json +8 -6
- package/src/command-handler.ts +96 -17
- package/src/command-manager.ts +8 -8
- package/src/commands/calculate.ts +11 -525
- package/src/commands/create.ts +35 -6
- package/src/commands/edit.ts +94 -11
- package/src/commands/export.ts +104 -31
- package/src/commands/import.ts +16 -0
- package/src/commands/move.ts +12 -15
- package/src/commands/remove.ts +4 -8
- package/src/commands/rename.ts +3 -12
- package/src/commands/show.ts +126 -9
- package/src/commands/transition.ts +3 -7
- package/src/commands/update.ts +6 -0
- package/src/commands/validate.ts +41 -13
- package/src/containers/card-container.ts +74 -0
- package/src/containers/project/calculation-engine.ts +535 -0
- package/src/containers/project/resource-collector.ts +13 -15
- package/src/containers/project.ts +30 -66
- package/src/containers/template.ts +16 -0
- package/src/index.ts +13 -2
- package/src/interfaces/macros.ts +4 -1
- package/src/interfaces/project-interfaces.ts +27 -0
- package/src/interfaces/resource-interfaces.ts +5 -2
- package/src/macros/base-macro.ts +19 -4
- package/src/macros/common.ts +22 -9
- package/src/macros/createCards/index.ts +6 -2
- package/src/macros/graph/index.ts +6 -10
- package/src/macros/image/index.ts +128 -0
- package/src/macros/image/metadata.ts +25 -0
- package/src/macros/include/index.ts +143 -0
- package/src/macros/include/metadata.ts +22 -0
- package/src/macros/index.ts +150 -98
- package/src/macros/percentage/index.ts +50 -0
- package/src/macros/percentage/metadata.ts +22 -0
- package/src/macros/report/index.ts +4 -12
- package/src/macros/scoreCard/index.ts +21 -25
- package/src/macros/vega/index.ts +55 -0
- package/src/macros/vega/metadata.ts +21 -0
- package/src/macros/vegalite/index.ts +46 -0
- package/src/macros/vegalite/metadata.ts +21 -0
- package/src/macros/xref/index.ts +73 -0
- package/src/macros/xref/metadata.ts +22 -0
- package/src/module-manager.ts +15 -5
- package/src/permissions/action-guard.ts +3 -3
- package/src/resources/card-type-resource.ts +100 -0
- package/src/resources/file-resource.ts +16 -4
- package/src/resources/folder-resource.ts +59 -2
- package/src/resources/graph-model-resource.ts +1 -1
- package/src/resources/graph-view-resource.ts +1 -1
- package/src/resources/report-resource.ts +1 -1
- package/src/resources/resource-object.ts +19 -1
- package/src/resources/template-resource.ts +1 -1
- package/src/resources/workflow-resource.ts +68 -13
- package/src/svg/index.ts +15 -0
- package/src/svg/lib.ts +31 -0
- package/src/svg/percentage.ts +97 -0
- package/src/svg/scoreCard.ts +88 -0
- package/src/types/queries.ts +8 -7
- package/src/types/string-pixel-width.d.ts +23 -0
- package/src/utils/card-utils.ts +13 -0
- package/src/utils/clingo-facts.ts +65 -3
- package/src/utils/clingo-parser.ts +31 -144
- package/src/utils/constants.ts +16 -0
- package/src/utils/csv.ts +1 -1
- package/src/utils/report.ts +45 -4
- package/src/utils/resource-utils.ts +12 -2
- package/src/utils/user-preferences.ts +32 -14
- package/src/utils/validate.ts +3 -3
|
@@ -0,0 +1,535 @@
|
|
|
1
|
+
/**
|
|
2
|
+
Cyberismo
|
|
3
|
+
Copyright © Cyberismo Ltd and contributors 2025
|
|
4
|
+
This program is free software: you can redistribute it and/or modify it under
|
|
5
|
+
the terms of the GNU Affero General Public License version 3 as published by
|
|
6
|
+
the Free Software Foundation. This program is distributed in the hope that it
|
|
7
|
+
will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
|
8
|
+
of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
9
|
+
See the GNU Affero General Public License for more details.
|
|
10
|
+
You should have received a copy of the GNU Affero General Public
|
|
11
|
+
License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
// node
|
|
15
|
+
import { basename, join, resolve } from 'node:path';
|
|
16
|
+
import { readFile } from 'node:fs/promises';
|
|
17
|
+
|
|
18
|
+
import { sanitizeSvgBase64 } from '../../utils/sanitize-svg.js';
|
|
19
|
+
import { instance } from '@viz-js/viz';
|
|
20
|
+
|
|
21
|
+
import type {
|
|
22
|
+
BaseResult,
|
|
23
|
+
ParseResult,
|
|
24
|
+
QueryName,
|
|
25
|
+
QueryResult,
|
|
26
|
+
} from '../../types/queries.js';
|
|
27
|
+
import type { Card, Context } from '../../interfaces/project-interfaces.js';
|
|
28
|
+
import ClingoParser from '../../utils/clingo-parser.js';
|
|
29
|
+
import { pathExists } from '../../utils/file-utils.js';
|
|
30
|
+
import { Mutex } from 'async-mutex';
|
|
31
|
+
import Handlebars from 'handlebars';
|
|
32
|
+
import { type Project, ResourcesFrom } from '../../containers/project.js';
|
|
33
|
+
import { getChildLogger } from '../../utils/log-utils.js';
|
|
34
|
+
import {
|
|
35
|
+
createCardFacts,
|
|
36
|
+
createCardTypeFacts,
|
|
37
|
+
createContextFacts,
|
|
38
|
+
createFieldTypeFacts,
|
|
39
|
+
createLinkTypeFacts,
|
|
40
|
+
createModuleFacts,
|
|
41
|
+
createProjectFacts,
|
|
42
|
+
createReportFacts,
|
|
43
|
+
createTemplateFacts,
|
|
44
|
+
createWorkflowFacts,
|
|
45
|
+
} from '../../utils/clingo-facts.js';
|
|
46
|
+
import { CardMetadataUpdater } from '../../card-metadata-updater.js';
|
|
47
|
+
import type {
|
|
48
|
+
CardType,
|
|
49
|
+
FieldType,
|
|
50
|
+
LinkType,
|
|
51
|
+
ReportMetadata,
|
|
52
|
+
TemplateMetadata,
|
|
53
|
+
Workflow,
|
|
54
|
+
} from '../../interfaces/resource-interfaces.js';
|
|
55
|
+
import {
|
|
56
|
+
removeAllPrograms,
|
|
57
|
+
solve,
|
|
58
|
+
setProgram,
|
|
59
|
+
removeProgram,
|
|
60
|
+
} from '@cyberismo/node-clingo';
|
|
61
|
+
import { generateReportContent } from '../../utils/report.js';
|
|
62
|
+
import { lpFiles, graphvizReport } from '@cyberismo/assets';
|
|
63
|
+
import {
|
|
64
|
+
type ResourceName,
|
|
65
|
+
resourceNameToString,
|
|
66
|
+
} from '../../utils/resource-utils.js';
|
|
67
|
+
|
|
68
|
+
// Define the all category that will be used for all programs
|
|
69
|
+
const ALL_CATEGORY = 'all';
|
|
70
|
+
|
|
71
|
+
export class CalculationEngine {
|
|
72
|
+
constructor(private project: Project) {}
|
|
73
|
+
|
|
74
|
+
private static mutex = new Mutex();
|
|
75
|
+
|
|
76
|
+
private get logger() {
|
|
77
|
+
return getChildLogger({
|
|
78
|
+
module: 'calculate',
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Storage for in-memory program content
|
|
83
|
+
private modules: string = '';
|
|
84
|
+
|
|
85
|
+
private modulesInitialized = false;
|
|
86
|
+
|
|
87
|
+
// Initialize modules during construction
|
|
88
|
+
private async initializeModules() {
|
|
89
|
+
try {
|
|
90
|
+
// Collect all available calculations at initialization time
|
|
91
|
+
this.modules = await this.generateModules();
|
|
92
|
+
this.modulesInitialized = true;
|
|
93
|
+
} catch (error) {
|
|
94
|
+
this.logger.error(error, 'Failed to initialize modules');
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Gets the logic program content for a specific card
|
|
100
|
+
* @param cardKey The key of the card
|
|
101
|
+
* @returns The logic program content for the card
|
|
102
|
+
*/
|
|
103
|
+
public async cardLogicProgram(cardKey: string): Promise<string> {
|
|
104
|
+
const card = await this.project.findSpecificCard(cardKey, {
|
|
105
|
+
metadata: true,
|
|
106
|
+
});
|
|
107
|
+
if (!card) {
|
|
108
|
+
throw new Error(`Card '${cardKey}' does not exist in the project`);
|
|
109
|
+
}
|
|
110
|
+
return createCardFacts(card, this.project);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// // Wrapper to run onCreation query.
|
|
114
|
+
private async creationQuery(cardKeys: string[], context: Context) {
|
|
115
|
+
if (!cardKeys) return undefined;
|
|
116
|
+
return this.runQuery('onCreation', context, {
|
|
117
|
+
cardKeys,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Generate card tree content
|
|
122
|
+
private async setCardTreeContent() {
|
|
123
|
+
const cards = await this.getCards(undefined);
|
|
124
|
+
|
|
125
|
+
for (const card of cards) {
|
|
126
|
+
await this.setCardContent(card);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private async setCardContent(card: Card) {
|
|
131
|
+
const cardContent = await createCardFacts(card, this.project);
|
|
132
|
+
setProgram(card.key, cardContent, [ALL_CATEGORY]);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Generates logic programs related to modules (and project itself).
|
|
136
|
+
private async generateModules() {
|
|
137
|
+
const modules = await this.project.modules();
|
|
138
|
+
let content = '';
|
|
139
|
+
for (const module of await Promise.all(
|
|
140
|
+
modules.map((mod) => this.project.module(mod.name)),
|
|
141
|
+
)) {
|
|
142
|
+
if (!module) continue;
|
|
143
|
+
const moduleContent = createModuleFacts(module);
|
|
144
|
+
content = content.concat(moduleContent);
|
|
145
|
+
}
|
|
146
|
+
const projectContent = createProjectFacts(this.project.projectPrefix);
|
|
147
|
+
content = content.concat(projectContent);
|
|
148
|
+
return content;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Sets individual CardType programs
|
|
152
|
+
private async setCardTypesPrograms() {
|
|
153
|
+
const cardTypes = await this.project.cardTypes();
|
|
154
|
+
|
|
155
|
+
for (const cardType of await Promise.all(
|
|
156
|
+
cardTypes.map((c) => this.project.resource<CardType>(c.name)),
|
|
157
|
+
)) {
|
|
158
|
+
if (!cardType) continue;
|
|
159
|
+
|
|
160
|
+
const cardTypeContent = createCardTypeFacts(cardType);
|
|
161
|
+
setProgram(cardType.name, cardTypeContent, [ALL_CATEGORY]);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Sets individual FieldType programs
|
|
166
|
+
private async setFieldTypesPrograms() {
|
|
167
|
+
const fieldTypes = await this.project.fieldTypes();
|
|
168
|
+
|
|
169
|
+
for (const fieldType of await Promise.all(
|
|
170
|
+
fieldTypes.map((m) => this.project.resource<FieldType>(m.name)),
|
|
171
|
+
)) {
|
|
172
|
+
if (!fieldType) continue;
|
|
173
|
+
|
|
174
|
+
const fieldTypeContent = createFieldTypeFacts(fieldType);
|
|
175
|
+
setProgram(fieldType.name, fieldTypeContent, [ALL_CATEGORY]);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Sets individual LinkType programs
|
|
180
|
+
private async setLinkTypesPrograms() {
|
|
181
|
+
const linkTypes = await this.project.linkTypes();
|
|
182
|
+
|
|
183
|
+
for (const linkType of await Promise.all(
|
|
184
|
+
linkTypes.map((c) => this.project.resource<LinkType>(c.name)),
|
|
185
|
+
)) {
|
|
186
|
+
if (!linkType) continue;
|
|
187
|
+
|
|
188
|
+
const linkTypeContent = createLinkTypeFacts(linkType);
|
|
189
|
+
setProgram(linkType.name, linkTypeContent, [ALL_CATEGORY]);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Sets individual Workflow programs
|
|
194
|
+
private async setWorkflowsPrograms() {
|
|
195
|
+
const workflows = await this.project.workflows();
|
|
196
|
+
|
|
197
|
+
for (const workflow of await Promise.all(
|
|
198
|
+
workflows.map((m) => this.project.resource<Workflow>(m.name)),
|
|
199
|
+
)) {
|
|
200
|
+
if (!workflow) continue;
|
|
201
|
+
|
|
202
|
+
const workflowContent = createWorkflowFacts(workflow);
|
|
203
|
+
setProgram(workflow.name, workflowContent, [ALL_CATEGORY]);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Sets individual Report programs
|
|
208
|
+
private async setReportsPrograms() {
|
|
209
|
+
const reports = await this.project.reports();
|
|
210
|
+
|
|
211
|
+
for (const report of await Promise.all(
|
|
212
|
+
reports.map((r) => this.project.resource<ReportMetadata>(r.name)),
|
|
213
|
+
)) {
|
|
214
|
+
if (!report) continue;
|
|
215
|
+
|
|
216
|
+
const reportContent = createReportFacts(report);
|
|
217
|
+
setProgram(report.name, reportContent, [ALL_CATEGORY]);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Sets individual Template programs
|
|
222
|
+
private async setTemplatesPrograms() {
|
|
223
|
+
const templates = await this.project.templates();
|
|
224
|
+
|
|
225
|
+
for (const template of await Promise.all(
|
|
226
|
+
templates.map((r) => this.project.resource<TemplateMetadata>(r.name)),
|
|
227
|
+
)) {
|
|
228
|
+
if (!template) continue;
|
|
229
|
+
|
|
230
|
+
const templateContent = createTemplateFacts(template);
|
|
231
|
+
const cards = await this.getCards(template.name);
|
|
232
|
+
for (const card of cards) {
|
|
233
|
+
const cardContent = await createCardFacts(card, this.project);
|
|
234
|
+
setProgram(card.key, cardContent, [ALL_CATEGORY]);
|
|
235
|
+
}
|
|
236
|
+
setProgram(template.name, templateContent, [ALL_CATEGORY]);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Sets individual Calculation programs
|
|
241
|
+
private async setCalculationsPrograms() {
|
|
242
|
+
const calculations = await this.project.calculations(ResourcesFrom.all);
|
|
243
|
+
|
|
244
|
+
for (const calculationFile of calculations) {
|
|
245
|
+
if (calculationFile.path) {
|
|
246
|
+
const moduleLogicFile = resolve(
|
|
247
|
+
join(calculationFile.path, basename(calculationFile.name)),
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
const filePath = moduleLogicFile.endsWith('.lp')
|
|
251
|
+
? moduleLogicFile
|
|
252
|
+
: moduleLogicFile + '.lp';
|
|
253
|
+
|
|
254
|
+
if (pathExists(filePath)) {
|
|
255
|
+
try {
|
|
256
|
+
const moduleContent = await readFile(filePath, 'utf-8');
|
|
257
|
+
setProgram(calculationFile.name, moduleContent, [ALL_CATEGORY]);
|
|
258
|
+
} catch (error) {
|
|
259
|
+
this.logger.warn(
|
|
260
|
+
error,
|
|
261
|
+
`Failed to read calculation ${calculationFile.name}`,
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Gets either all the cards (no parent), or a subtree.
|
|
270
|
+
private async getCards(templateName?: string): Promise<Card[]> {
|
|
271
|
+
return templateName
|
|
272
|
+
? this.project.templateCards(templateName)
|
|
273
|
+
: this.project.cards();
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Checks that Clingo successfully returned result.
|
|
277
|
+
private async parseClingoResult(
|
|
278
|
+
data: string[],
|
|
279
|
+
): Promise<ParseResult<BaseResult>> {
|
|
280
|
+
const parser = new ClingoParser();
|
|
281
|
+
return parser.parseInput(data.join('\n'));
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
private async run(query: string, context: Context): Promise<string[]> {
|
|
285
|
+
try {
|
|
286
|
+
const res = await CalculationEngine.mutex.runExclusive(async () => {
|
|
287
|
+
// Use the main category to include all programs
|
|
288
|
+
const basePrograms = [ALL_CATEGORY];
|
|
289
|
+
|
|
290
|
+
this.logger.trace(
|
|
291
|
+
{
|
|
292
|
+
clingo: true,
|
|
293
|
+
},
|
|
294
|
+
'Solving',
|
|
295
|
+
);
|
|
296
|
+
|
|
297
|
+
const contextFacts = createContextFacts(context);
|
|
298
|
+
setProgram('context', contextFacts, [ALL_CATEGORY]);
|
|
299
|
+
// Then solve with the program - need to pass the program as parameter
|
|
300
|
+
return solve(query, basePrograms);
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
if (res && res.answers && res.answers.length > 0) {
|
|
304
|
+
return res.answers;
|
|
305
|
+
}
|
|
306
|
+
throw new Error('Failed to run Clingo solve. No answers returned.');
|
|
307
|
+
} catch (error) {
|
|
308
|
+
this.logger.error(
|
|
309
|
+
{
|
|
310
|
+
error,
|
|
311
|
+
query,
|
|
312
|
+
},
|
|
313
|
+
'Clingo solve failed',
|
|
314
|
+
);
|
|
315
|
+
throw error;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Generates a logic program.
|
|
321
|
+
*/
|
|
322
|
+
public async generate() {
|
|
323
|
+
await CalculationEngine.mutex.runExclusive(async () => {
|
|
324
|
+
this.logger.trace(
|
|
325
|
+
{
|
|
326
|
+
clingo: true,
|
|
327
|
+
},
|
|
328
|
+
'Generating logic program',
|
|
329
|
+
);
|
|
330
|
+
removeAllPrograms();
|
|
331
|
+
|
|
332
|
+
if (!this.modulesInitialized) {
|
|
333
|
+
await this.initializeModules();
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Set base common programs with main category
|
|
337
|
+
setProgram('base', lpFiles.common.base, [ALL_CATEGORY]);
|
|
338
|
+
setProgram('queryLanguage', lpFiles.common.queryLanguage, [ALL_CATEGORY]);
|
|
339
|
+
setProgram('utils', lpFiles.common.utils, [ALL_CATEGORY]);
|
|
340
|
+
setProgram('modules', this.modules, [ALL_CATEGORY]);
|
|
341
|
+
|
|
342
|
+
// Set individual resource type programs
|
|
343
|
+
await this.setCardTreeContent();
|
|
344
|
+
await this.setCardTypesPrograms();
|
|
345
|
+
await this.setFieldTypesPrograms();
|
|
346
|
+
await this.setLinkTypesPrograms();
|
|
347
|
+
await this.setWorkflowsPrograms();
|
|
348
|
+
await this.setReportsPrograms();
|
|
349
|
+
await this.setTemplatesPrograms();
|
|
350
|
+
await this.setCalculationsPrograms();
|
|
351
|
+
|
|
352
|
+
this.logger.trace(
|
|
353
|
+
{
|
|
354
|
+
clingo: true,
|
|
355
|
+
},
|
|
356
|
+
'Logic program set',
|
|
357
|
+
);
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* When card changes, update the card specific calculations.
|
|
363
|
+
* @param changedCard Card that was changed.
|
|
364
|
+
*/
|
|
365
|
+
public async handleCardChanged(changedCard: Card) {
|
|
366
|
+
await CalculationEngine.mutex.runExclusive(async () => {
|
|
367
|
+
await this.setCardContent(changedCard);
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* When cards are removed, automatically remove card-specific calculations.
|
|
373
|
+
* @param deletedCard Card that is to be removed.
|
|
374
|
+
*/
|
|
375
|
+
public async handleDeleteCard(deletedCard: Card) {
|
|
376
|
+
if (!deletedCard) {
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
try {
|
|
380
|
+
await CalculationEngine.mutex.runExclusive(async () => {
|
|
381
|
+
if (!removeProgram(deletedCard.key)) {
|
|
382
|
+
this.logger.warn(
|
|
383
|
+
{
|
|
384
|
+
cardKey: deletedCard.key,
|
|
385
|
+
},
|
|
386
|
+
'Tried to remove card program that does not exist',
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
} catch {
|
|
391
|
+
this.logger.warn('Removing program failed');
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* When new cards are added, automatically calculate card-specific values.
|
|
397
|
+
* @param cards Added cards.
|
|
398
|
+
*/
|
|
399
|
+
public async handleNewCards(cards: Card[]) {
|
|
400
|
+
if (!cards) {
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
await CalculationEngine.mutex.runExclusive(async () => {
|
|
404
|
+
for (const card of cards) {
|
|
405
|
+
await this.setCardContent(card);
|
|
406
|
+
}
|
|
407
|
+
});
|
|
408
|
+
const cardKeys = cards.map((item) => item.key);
|
|
409
|
+
const queryResult = await this.creationQuery(cardKeys, 'localApp');
|
|
410
|
+
if (
|
|
411
|
+
!queryResult ||
|
|
412
|
+
queryResult.at(0) === undefined ||
|
|
413
|
+
queryResult.at(0)?.updateFields === undefined
|
|
414
|
+
) {
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
const fieldsToUpdate = queryResult.at(0)!.updateFields;
|
|
418
|
+
await CardMetadataUpdater.apply(this.project, fieldsToUpdate);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Gets the logic program content for a specific resource
|
|
423
|
+
* @param resourceName The name of the resource
|
|
424
|
+
* @returns The logic program content for the resource
|
|
425
|
+
*/
|
|
426
|
+
public async resourceLogicProgram(
|
|
427
|
+
resourceName: ResourceName,
|
|
428
|
+
): Promise<string> {
|
|
429
|
+
const resource = await this.project.resource(
|
|
430
|
+
resourceNameToString(resourceName),
|
|
431
|
+
);
|
|
432
|
+
if (!resource) {
|
|
433
|
+
throw new Error(
|
|
434
|
+
`Resource '${resourceNameToString(resourceName)}' does not exist in the project`,
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
switch (resourceName.type) {
|
|
439
|
+
case 'cardTypes':
|
|
440
|
+
return createCardTypeFacts(resource as CardType);
|
|
441
|
+
case 'fieldTypes':
|
|
442
|
+
return createFieldTypeFacts(resource as FieldType);
|
|
443
|
+
case 'linkTypes':
|
|
444
|
+
return createLinkTypeFacts(resource as LinkType);
|
|
445
|
+
case 'workflows':
|
|
446
|
+
return createWorkflowFacts(resource as Workflow);
|
|
447
|
+
case 'reports':
|
|
448
|
+
return createReportFacts(resource as ReportMetadata);
|
|
449
|
+
case 'templates':
|
|
450
|
+
return createTemplateFacts(resource as TemplateMetadata);
|
|
451
|
+
default:
|
|
452
|
+
throw new Error(
|
|
453
|
+
`Resource ${resourceNameToString(resourceName)} does not have a logic program`,
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* Runs given logic program and creates a graph using clingraph
|
|
460
|
+
* @param data Provide a query or/and a file which can be given to clingraph
|
|
461
|
+
* @param timeout Maximum amount of milliseconds clingraph is allowed to run
|
|
462
|
+
* @returns a base64 encoded image as a string
|
|
463
|
+
*/
|
|
464
|
+
public async runGraph(model: string, view: string, context: Context) {
|
|
465
|
+
this.logger.trace(
|
|
466
|
+
{
|
|
467
|
+
model,
|
|
468
|
+
view,
|
|
469
|
+
},
|
|
470
|
+
'Running graph',
|
|
471
|
+
);
|
|
472
|
+
|
|
473
|
+
const result = await generateReportContent({
|
|
474
|
+
calculate: this,
|
|
475
|
+
contentTemplate: graphvizReport.content,
|
|
476
|
+
queryTemplate: graphvizReport.query,
|
|
477
|
+
options: {
|
|
478
|
+
model: model,
|
|
479
|
+
view: view,
|
|
480
|
+
},
|
|
481
|
+
graph: true,
|
|
482
|
+
context,
|
|
483
|
+
});
|
|
484
|
+
let graph = (await instance()).renderString(result, {
|
|
485
|
+
format: 'svg',
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
// asciidoctor-pdf will error on the a elements with xtitle attribute
|
|
489
|
+
// because of the unescaped <font> tags.
|
|
490
|
+
if (context === 'exportedDocument') {
|
|
491
|
+
graph = graph.replace(/xlink:title="[^"]*"/g, '');
|
|
492
|
+
}
|
|
493
|
+
return sanitizeSvgBase64(graph);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* Runs a logic program using clingo.
|
|
498
|
+
* @param query Logic program to be run
|
|
499
|
+
* @returns parsed program output
|
|
500
|
+
*/
|
|
501
|
+
public async runLogicProgram(query: string, context: Context = 'localApp') {
|
|
502
|
+
const clingoOutput = await this.run(query, context);
|
|
503
|
+
return this.parseClingoResult(clingoOutput);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
* Runs a pre-defined query.
|
|
508
|
+
* @param queryName Name of the query file without extension
|
|
509
|
+
* @param options Any object that contains state for handlebars
|
|
510
|
+
* @returns parsed program output
|
|
511
|
+
*/
|
|
512
|
+
public async runQuery<T extends QueryName>(
|
|
513
|
+
queryName: T,
|
|
514
|
+
context: Context = 'localApp',
|
|
515
|
+
options?: unknown,
|
|
516
|
+
): Promise<QueryResult<T>[]> {
|
|
517
|
+
let content = lpFiles.queries[queryName];
|
|
518
|
+
const handlebars = Handlebars.create();
|
|
519
|
+
const compiled = handlebars.compile(content);
|
|
520
|
+
content = compiled(options || {});
|
|
521
|
+
if (!content) {
|
|
522
|
+
throw new Error(`Query file ${queryName} not found`);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
this.logger.trace({ queryName }, 'Running query');
|
|
526
|
+
const clingoOutput = await this.run(content, context);
|
|
527
|
+
|
|
528
|
+
const result = await this.parseClingoResult(clingoOutput);
|
|
529
|
+
|
|
530
|
+
if (result.error) {
|
|
531
|
+
throw new Error(result.error);
|
|
532
|
+
}
|
|
533
|
+
return result.results as QueryResult<T>[];
|
|
534
|
+
}
|
|
535
|
+
}
|
|
@@ -14,9 +14,8 @@
|
|
|
14
14
|
|
|
15
15
|
import { type Dirent, readdirSync } from 'node:fs';
|
|
16
16
|
import { readdir } from 'node:fs/promises';
|
|
17
|
-
import { join } from 'node:path';
|
|
17
|
+
import { extname, join } from 'node:path';
|
|
18
18
|
|
|
19
|
-
import { CardContainer } from '../card-container.js';
|
|
20
19
|
import type { Project } from '../project.js';
|
|
21
20
|
import type { ProjectPaths } from './project-paths.js';
|
|
22
21
|
import type {
|
|
@@ -38,6 +37,9 @@ export enum ResourcesFrom {
|
|
|
38
37
|
localOnly = 'local',
|
|
39
38
|
}
|
|
40
39
|
|
|
40
|
+
// This class collects resources that have these types of files.
|
|
41
|
+
const allowedExtensions = ['.lp', '.json'];
|
|
42
|
+
|
|
41
43
|
// Helper class to contain collected resources.
|
|
42
44
|
class ResourceCollection {
|
|
43
45
|
public calculations: Resource[] = [];
|
|
@@ -109,9 +111,7 @@ export class ResourceCollector {
|
|
|
109
111
|
}
|
|
110
112
|
|
|
111
113
|
const isValidFile = (item: Dirent): boolean =>
|
|
112
|
-
item.isFile() &&
|
|
113
|
-
item.name !== CardContainer.schemaContentFile &&
|
|
114
|
-
item.name !== '.gitkeep';
|
|
114
|
+
item.isFile() && allowedExtensions.includes(extname(item.name));
|
|
115
115
|
|
|
116
116
|
const processResource = async (resource: Dirent): Promise<Resource[]> => {
|
|
117
117
|
const resourcePath = join(
|
|
@@ -231,17 +231,12 @@ export class ResourceCollector {
|
|
|
231
231
|
}
|
|
232
232
|
resources.push(
|
|
233
233
|
...entries
|
|
234
|
-
.filter(
|
|
235
|
-
|
|
236
|
-
entry.isFile() &&
|
|
237
|
-
|
|
238
|
-
entry.name !== CardContainer.schemaContentFile
|
|
239
|
-
);
|
|
240
|
-
})
|
|
234
|
+
.filter(
|
|
235
|
+
(entry) =>
|
|
236
|
+
entry.isFile() && allowedExtensions.includes(extname(entry.name)),
|
|
237
|
+
)
|
|
241
238
|
.map((entry) => {
|
|
242
|
-
|
|
243
|
-
entry.name = stripExtension(entry.name);
|
|
244
|
-
}
|
|
239
|
+
entry.name = stripExtension(entry.name);
|
|
245
240
|
return {
|
|
246
241
|
name: `${this.project.projectPrefix}/${type}/${entry.name}`,
|
|
247
242
|
path: entry.parentPath,
|
|
@@ -303,6 +298,9 @@ export class ResourceCollector {
|
|
|
303
298
|
|
|
304
299
|
const { type } = resourceName(resource.name);
|
|
305
300
|
switch (type) {
|
|
301
|
+
case 'calculations':
|
|
302
|
+
addItem(this.local.calculations, resource);
|
|
303
|
+
break;
|
|
306
304
|
case 'cardTypes':
|
|
307
305
|
addItem(this.local.cardTypes, resource);
|
|
308
306
|
break;
|