@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
|
@@ -21,9 +21,11 @@ import type {
|
|
|
21
21
|
Card,
|
|
22
22
|
ResourceFolderType,
|
|
23
23
|
} from '../interfaces/project-interfaces.js';
|
|
24
|
+
import type { Logger } from 'pino';
|
|
24
25
|
import { type Project, ResourcesFrom } from '../containers/project.js';
|
|
25
26
|
import type { ResourceContent } from '../interfaces/resource-interfaces.js';
|
|
26
27
|
import type { ResourceName } from '../utils/resource-utils.js';
|
|
28
|
+
import { getChildLogger } from '../utils/log-utils.js';
|
|
27
29
|
|
|
28
30
|
// Possible operations to perform when doing "update"
|
|
29
31
|
export type UpdateOperations = 'add' | 'change' | 'rank' | 'remove';
|
|
@@ -44,6 +46,7 @@ export type AddOperation<T> = BaseOperation<T> & {
|
|
|
44
46
|
export type ChangeOperation<T> = BaseOperation<T> & {
|
|
45
47
|
name: 'change';
|
|
46
48
|
to: T;
|
|
49
|
+
mappingTable?: { stateMapping: Record<string, string> }; // Optional state mapping for workflow changes
|
|
47
50
|
};
|
|
48
51
|
|
|
49
52
|
// Move item in an array to new position.
|
|
@@ -81,6 +84,9 @@ export abstract class AbstractResource {
|
|
|
81
84
|
protected abstract usage(cards?: Card[]): Promise<string[]>; // list of card keys or resource names where this resource is used in
|
|
82
85
|
protected abstract validate(content?: object): Promise<void>; // validate the content
|
|
83
86
|
protected abstract write(): Promise<void>; // write content to disk
|
|
87
|
+
// Abstract getters
|
|
88
|
+
protected abstract get getType(): string;
|
|
89
|
+
protected abstract getLogger(loggerName: string): Logger;
|
|
84
90
|
}
|
|
85
91
|
|
|
86
92
|
/**
|
|
@@ -110,6 +116,14 @@ export class ResourceObject extends AbstractResource {
|
|
|
110
116
|
protected async show(): Promise<ResourceContent> {
|
|
111
117
|
return {} as ResourceContent;
|
|
112
118
|
}
|
|
119
|
+
protected get getType(): string {
|
|
120
|
+
return this.type;
|
|
121
|
+
}
|
|
122
|
+
protected getLogger(loggerName: string): Logger {
|
|
123
|
+
return getChildLogger({
|
|
124
|
+
module: loggerName,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
113
127
|
protected async update<Type>(_key: string, _op: Operation<Type>) {}
|
|
114
128
|
protected async usage(_cards?: Card[]): Promise<string[]> {
|
|
115
129
|
return [];
|
|
@@ -197,7 +211,11 @@ export class ResourceObject extends AbstractResource {
|
|
|
197
211
|
);
|
|
198
212
|
}
|
|
199
213
|
|
|
200
|
-
const filename = join(
|
|
214
|
+
const filename = join(
|
|
215
|
+
calculation.path,
|
|
216
|
+
basename(calculation.name) + '.lp',
|
|
217
|
+
);
|
|
218
|
+
|
|
201
219
|
try {
|
|
202
220
|
const content = await readFile(filename, 'utf-8');
|
|
203
221
|
const updatedContent = content.replaceAll(from, to);
|
|
@@ -146,7 +146,7 @@ export class TemplateResource extends FolderResource {
|
|
|
146
146
|
|
|
147
147
|
await super.update(key, op);
|
|
148
148
|
|
|
149
|
-
const content =
|
|
149
|
+
const content = structuredClone(this.content) as TemplateMetadata;
|
|
150
150
|
|
|
151
151
|
if (key === 'name') {
|
|
152
152
|
content.name = super.handleScalar(op) as string;
|
|
@@ -82,12 +82,8 @@ export class WorkflowResource extends FileResource {
|
|
|
82
82
|
|
|
83
83
|
// Handle change of workflow state.
|
|
84
84
|
private async handleStateChange(op: ChangeOperation<WorkflowState>) {
|
|
85
|
-
const content =
|
|
86
|
-
const stateName = (
|
|
87
|
-
(op.target as WorkflowState).name
|
|
88
|
-
? (op.target as WorkflowState).name
|
|
89
|
-
: op.target
|
|
90
|
-
) as string;
|
|
85
|
+
const content = structuredClone(this.content) as Workflow;
|
|
86
|
+
const stateName = this.targetName(op) as string;
|
|
91
87
|
// Check that state can be changed to
|
|
92
88
|
content.transitions = content.transitions.filter(
|
|
93
89
|
(t) => t.toState !== stateName,
|
|
@@ -111,12 +107,8 @@ export class WorkflowResource extends FileResource {
|
|
|
111
107
|
// Handle removal of workflow state.
|
|
112
108
|
// State can be removed with or without replacement.
|
|
113
109
|
private async handleStateRemoval(op: RemoveOperation<WorkflowState>) {
|
|
114
|
-
const content =
|
|
115
|
-
const stateName = (
|
|
116
|
-
(op.target as WorkflowState).name
|
|
117
|
-
? (op.target as WorkflowState).name
|
|
118
|
-
: op.target
|
|
119
|
-
) as string;
|
|
110
|
+
const content = structuredClone(this.content) as Workflow;
|
|
111
|
+
const stateName = this.targetName(op) as string;
|
|
120
112
|
|
|
121
113
|
// If there is no replacement value, remove all transitions "to" and "from" this state.
|
|
122
114
|
if (!op.replacementValue) {
|
|
@@ -154,6 +146,42 @@ export class WorkflowResource extends FileResource {
|
|
|
154
146
|
}
|
|
155
147
|
}
|
|
156
148
|
|
|
149
|
+
// Returns target name irregardless of the type
|
|
150
|
+
private targetName(op: Operation<WorkflowState | WorkflowTransition>) {
|
|
151
|
+
const name = op.target.name ? op.target.name : op.target;
|
|
152
|
+
return name;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Potentially updates the changed transition with current properties.
|
|
156
|
+
private async transitionObject(op: ChangeOperation<WorkflowTransition>) {
|
|
157
|
+
const content = structuredClone(this.content) as Workflow;
|
|
158
|
+
const targetTransitionName = this.targetName(op);
|
|
159
|
+
const currentTransition = content.transitions.filter(
|
|
160
|
+
(item) => item.name === targetTransitionName,
|
|
161
|
+
)[0];
|
|
162
|
+
|
|
163
|
+
if (currentTransition) {
|
|
164
|
+
op.to.fromState =
|
|
165
|
+
op.to.fromState.length === 0
|
|
166
|
+
? currentTransition.fromState
|
|
167
|
+
: op.to.fromState;
|
|
168
|
+
op.to.toState = op.to.toState ?? currentTransition.toState;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (
|
|
172
|
+
op.to.name === undefined ||
|
|
173
|
+
op.to.toState === undefined ||
|
|
174
|
+
op.to.fromState == undefined ||
|
|
175
|
+
op.to.fromState.length === 0
|
|
176
|
+
) {
|
|
177
|
+
throw new Error(
|
|
178
|
+
`Cannot change transition '${targetTransitionName}' for workflow '${this.content.name}'.
|
|
179
|
+
Updated transition must have 'name', 'toState' and 'fromState' properties.`,
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
return op.to;
|
|
183
|
+
}
|
|
184
|
+
|
|
157
185
|
// Check if operation is a string operation.
|
|
158
186
|
private isStringOperation(op: Operation<unknown>): op is Operation<string> {
|
|
159
187
|
return typeof op.target === 'string';
|
|
@@ -250,7 +278,7 @@ export class WorkflowResource extends FileResource {
|
|
|
250
278
|
|
|
251
279
|
await super.update(key, op);
|
|
252
280
|
|
|
253
|
-
const content =
|
|
281
|
+
const content = structuredClone(this.content) as Workflow;
|
|
254
282
|
|
|
255
283
|
if (key === 'name') {
|
|
256
284
|
content.name = super.handleScalar(op) as string;
|
|
@@ -270,6 +298,33 @@ export class WorkflowResource extends FileResource {
|
|
|
270
298
|
throw new Error(`Unknown property '${key}' for Workflow`);
|
|
271
299
|
}
|
|
272
300
|
|
|
301
|
+
// If workflow transition is removed, then above call to 'handleArray' is all that is needed.
|
|
302
|
+
|
|
303
|
+
if (key === 'transitions' && op.name === 'change') {
|
|
304
|
+
// If workflow transition is changed, update to full object and change the content.
|
|
305
|
+
let changeOp: ChangeOperation<WorkflowTransition>;
|
|
306
|
+
if (this.isStringOperation(op)) {
|
|
307
|
+
const targetTransition = (this.content as Workflow).transitions.find(
|
|
308
|
+
(transition) => transition.name === op.target,
|
|
309
|
+
)!;
|
|
310
|
+
changeOp = {
|
|
311
|
+
name: 'change',
|
|
312
|
+
target: targetTransition as WorkflowTransition,
|
|
313
|
+
to: {
|
|
314
|
+
name: op.to,
|
|
315
|
+
toState: targetTransition.toState,
|
|
316
|
+
fromState: targetTransition.fromState,
|
|
317
|
+
},
|
|
318
|
+
};
|
|
319
|
+
} else {
|
|
320
|
+
changeOp = op as ChangeOperation<WorkflowTransition>;
|
|
321
|
+
}
|
|
322
|
+
const newTransition = await this.transitionObject(changeOp);
|
|
323
|
+
content.transitions = content.transitions.map((item) =>
|
|
324
|
+
item.name == newTransition.name ? newTransition : item,
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
|
|
273
328
|
if (key === 'states' && op.name === 'remove') {
|
|
274
329
|
// If workflow state is removed, remove all transitions "to" and "from" this state.
|
|
275
330
|
let removeOp: RemoveOperation<WorkflowState>;
|
package/src/svg/index.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
Cyberismo
|
|
3
|
+
Copyright © Cyberismo Ltd and contributors 2025
|
|
4
|
+
|
|
5
|
+
This program is free software: you can redistribute it and/or modify it under
|
|
6
|
+
the terms of the GNU Affero General Public License version 3 as published by
|
|
7
|
+
the Free Software Foundation. This program is distributed in the hope that it
|
|
8
|
+
will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
|
9
|
+
of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
10
|
+
See the GNU Affero General Public License for more details.
|
|
11
|
+
You should have received a copy of the GNU Affero General Public
|
|
12
|
+
License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
13
|
+
*/
|
|
14
|
+
export * from './scoreCard.js';
|
|
15
|
+
export * from './percentage.js';
|
package/src/svg/lib.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
Cyberismo
|
|
3
|
+
Copyright © Cyberismo Ltd and contributors 2025
|
|
4
|
+
|
|
5
|
+
This program is free software: you can redistribute it and/or modify it under
|
|
6
|
+
the terms of the GNU Affero General Public License version 3 as published by
|
|
7
|
+
the Free Software Foundation. This program is distributed in the hope that it
|
|
8
|
+
will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
|
9
|
+
of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
10
|
+
See the GNU Affero General Public License for more details.
|
|
11
|
+
You should have received a copy of the GNU Affero General Public
|
|
12
|
+
License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
13
|
+
*/
|
|
14
|
+
import pixelWidth from 'string-pixel-width';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Measures the width of a text string
|
|
18
|
+
* @param text - The text to measure
|
|
19
|
+
* @param font - The font to use
|
|
20
|
+
* @param size - The size of the font
|
|
21
|
+
* @param bold - Whether the text is bold
|
|
22
|
+
* @returns The width of the text
|
|
23
|
+
*/
|
|
24
|
+
export function measureTextWidth(
|
|
25
|
+
text: string,
|
|
26
|
+
font: string,
|
|
27
|
+
size: number,
|
|
28
|
+
bold = false,
|
|
29
|
+
): number {
|
|
30
|
+
return pixelWidth(text, { font, size, bold });
|
|
31
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
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
|
+
interface PercentageOptions {
|
|
15
|
+
title: string;
|
|
16
|
+
value: number;
|
|
17
|
+
legend: string;
|
|
18
|
+
colour?: 'blue' | 'green' | 'yellow' | 'red' | 'orange' | 'purple';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const SIZE = 160;
|
|
22
|
+
const STROKE = 18;
|
|
23
|
+
const R = (SIZE - STROKE) / 2;
|
|
24
|
+
const CIRC = 2 * Math.PI * R;
|
|
25
|
+
const TITLE_HEIGHT = 70; // Space for the title above the donut
|
|
26
|
+
const EXTRA_WIDTH = 80; // Extra width for the title area
|
|
27
|
+
const SVG_WIDTH = SIZE + EXTRA_WIDTH;
|
|
28
|
+
// SVG_HEIGHT will be calculated dynamically below
|
|
29
|
+
const TITLE_FONT_SIZE = 22;
|
|
30
|
+
const VALUE_FONT_SIZE = 32;
|
|
31
|
+
const LEGEND_FONT_SIZE = 18;
|
|
32
|
+
const DONUT_COLOR_RED = '#b22217';
|
|
33
|
+
const TITLE_Y = 36;
|
|
34
|
+
const LINE_SPACING = 1.2;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Splits a string into lines of up to maxLen characters, breaking at spaces.
|
|
38
|
+
*/
|
|
39
|
+
function wrapText(text: string, maxLen: number): string[] {
|
|
40
|
+
const words = text.split(' ');
|
|
41
|
+
const lines: string[] = [];
|
|
42
|
+
let current = '';
|
|
43
|
+
for (const word of words) {
|
|
44
|
+
if ((current + (current ? ' ' : '') + word).length > maxLen) {
|
|
45
|
+
if (current) lines.push(current);
|
|
46
|
+
current = word;
|
|
47
|
+
} else {
|
|
48
|
+
current += (current ? ' ' : '') + word;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (current) lines.push(current);
|
|
52
|
+
return lines;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Creates a percentage widget as an SVG
|
|
57
|
+
* @param options - The options for the percentage
|
|
58
|
+
* @returns The widget as an SVG
|
|
59
|
+
*/
|
|
60
|
+
export function percentage(options: PercentageOptions): string {
|
|
61
|
+
const { title, value, legend, colour = 'blue' } = options;
|
|
62
|
+
const offset = CIRC * (1 - value / 100);
|
|
63
|
+
const DONUT_COLOR = colour === 'red' ? DONUT_COLOR_RED : colour;
|
|
64
|
+
const titleLines = wrapText(title, 24);
|
|
65
|
+
const donutYOffset =
|
|
66
|
+
TITLE_Y +
|
|
67
|
+
(titleLines.length - 1) * TITLE_FONT_SIZE * LINE_SPACING +
|
|
68
|
+
TITLE_FONT_SIZE +
|
|
69
|
+
24;
|
|
70
|
+
const donutCenterY = donutYOffset + SIZE / 2 - TITLE_HEIGHT / 2;
|
|
71
|
+
const dynamicSVGHeight = donutYOffset + SIZE / 2 + R + 20;
|
|
72
|
+
return `
|
|
73
|
+
<svg width="${SVG_WIDTH}" height="${dynamicSVGHeight}" viewBox="0 0 ${SVG_WIDTH} ${dynamicSVGHeight}" xmlns="http://www.w3.org/2000/svg">
|
|
74
|
+
<title>${title}</title>
|
|
75
|
+
|
|
76
|
+
<!-- Visible Title (wrapped) -->
|
|
77
|
+
<text x="${SVG_WIDTH / 2}" y="${TITLE_Y}" text-anchor="middle" font-size="${TITLE_FONT_SIZE}" font-weight="bold">
|
|
78
|
+
${titleLines.map((line, i) => `<tspan x='${SVG_WIDTH / 2}' dy='${i === 0 ? 0 : LINE_SPACING}em'>${line}</tspan>`).join('')}
|
|
79
|
+
</text>
|
|
80
|
+
|
|
81
|
+
<!-- Background track -->
|
|
82
|
+
<circle cx="${SVG_WIDTH / 2}" cy="${donutCenterY}" r="${R}"
|
|
83
|
+
fill="none" stroke="#eee" stroke-width="${STROKE}" />
|
|
84
|
+
|
|
85
|
+
<!-- Progress arc -->
|
|
86
|
+
<circle cx="${SVG_WIDTH / 2}" cy="${donutCenterY}" r="${R}"
|
|
87
|
+
fill="none" stroke="${DONUT_COLOR}" stroke-width="${STROKE}"
|
|
88
|
+
stroke-dasharray="${CIRC}" stroke-dashoffset="${offset}"
|
|
89
|
+
stroke-linecap="butt"
|
|
90
|
+
transform="rotate(-90 ${SVG_WIDTH / 2} ${donutCenterY})" />
|
|
91
|
+
|
|
92
|
+
<!-- Numbers -->
|
|
93
|
+
<text x="${SVG_WIDTH / 2}" y="${donutCenterY - 8}" text-anchor="middle" font-size="${VALUE_FONT_SIZE}" font-weight="bold">${value}%</text>
|
|
94
|
+
<text x="${SVG_WIDTH / 2}" y="${donutCenterY + 20}" text-anchor="middle" font-size="${LEGEND_FONT_SIZE}">${legend}</text>
|
|
95
|
+
</svg>
|
|
96
|
+
`;
|
|
97
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
Cyberismo
|
|
3
|
+
Copyright © Cyberismo Ltd and contributors 2025
|
|
4
|
+
|
|
5
|
+
This program is free software: you can redistribute it and/or modify it under
|
|
6
|
+
the terms of the GNU Affero General Public License version 3 as published by
|
|
7
|
+
the Free Software Foundation. This program is distributed in the hope that it
|
|
8
|
+
will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
|
9
|
+
of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
10
|
+
See the GNU Affero General Public License for more details.
|
|
11
|
+
You should have received a copy of the GNU Affero General Public
|
|
12
|
+
License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
13
|
+
*/
|
|
14
|
+
import { measureTextWidth } from './lib.js';
|
|
15
|
+
|
|
16
|
+
// Padding on y-axis
|
|
17
|
+
const PADDING = 24;
|
|
18
|
+
// font used to estimate text width
|
|
19
|
+
const FONT_FAMILY = 'Helvetica';
|
|
20
|
+
const TITLE_SIZE = 16;
|
|
21
|
+
const VALUE_SIZE = 48;
|
|
22
|
+
const UNIT_SIZE = 24;
|
|
23
|
+
const CAPTION_SIZE = 14;
|
|
24
|
+
const UNIT_OFFSET = 3;
|
|
25
|
+
const LINE_GAP_TITLE = 16;
|
|
26
|
+
const LINE_GAP_CAPTION = 16;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Options for the score card
|
|
30
|
+
* @param title - The title of the score card
|
|
31
|
+
* @param value - The value of the score card
|
|
32
|
+
* @param unit - The unit of the score card
|
|
33
|
+
* @param legend - The legend of the score card
|
|
34
|
+
*/
|
|
35
|
+
export interface ScoreCardOptions {
|
|
36
|
+
value: number;
|
|
37
|
+
title?: string;
|
|
38
|
+
legend?: string;
|
|
39
|
+
unit?: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Creates an SVG score card
|
|
44
|
+
* @param options - The options for the score card
|
|
45
|
+
* @returns The SVG score card
|
|
46
|
+
*/
|
|
47
|
+
export function scoreCard(options: ScoreCardOptions): string {
|
|
48
|
+
const { title = '', value, unit = '', legend = '' } = options;
|
|
49
|
+
|
|
50
|
+
const titleWidth = measureTextWidth(title, FONT_FAMILY, TITLE_SIZE, false);
|
|
51
|
+
const valueWidth = measureTextWidth(
|
|
52
|
+
String(value),
|
|
53
|
+
FONT_FAMILY,
|
|
54
|
+
VALUE_SIZE,
|
|
55
|
+
true,
|
|
56
|
+
);
|
|
57
|
+
const unitWidth = measureTextWidth(unit, FONT_FAMILY, UNIT_SIZE, false);
|
|
58
|
+
const captionWidth = measureTextWidth(
|
|
59
|
+
legend,
|
|
60
|
+
FONT_FAMILY,
|
|
61
|
+
CAPTION_SIZE,
|
|
62
|
+
false,
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const valueLineWidth = valueWidth + unitWidth + UNIT_OFFSET;
|
|
66
|
+
const maxTextWidth = Math.max(titleWidth, valueLineWidth, captionWidth);
|
|
67
|
+
|
|
68
|
+
const width = Math.ceil(maxTextWidth + PADDING * 2);
|
|
69
|
+
|
|
70
|
+
const titleHeight = title.length > 0 ? TITLE_SIZE + LINE_GAP_TITLE : 0;
|
|
71
|
+
const valueHeight = VALUE_SIZE;
|
|
72
|
+
const captionHeight = legend.length > 0 ? CAPTION_SIZE + LINE_GAP_CAPTION : 0;
|
|
73
|
+
|
|
74
|
+
const textBlockHeight = titleHeight + valueHeight + captionHeight;
|
|
75
|
+
|
|
76
|
+
const height = Math.ceil(textBlockHeight + PADDING * 2);
|
|
77
|
+
|
|
78
|
+
const svgContent = `<svg class="card" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}" xmlns="http://www.w3.org/2000/svg">
|
|
79
|
+
<rect rx="8" ry="8" fill="#fff" stroke="#dfe4ea" stroke-width="3" width="${width}" height="${height}"/>
|
|
80
|
+
<g text-anchor="middle">
|
|
81
|
+
<text class="title" x="${width / 2}" y="${TITLE_SIZE / 2 + PADDING}" font-size="${TITLE_SIZE}" font-weight="400" fill="#001829" dominant-baseline="middle">${title}</text>
|
|
82
|
+
<text class="value" x="${width / 2}" y="${titleHeight + VALUE_SIZE / 2 + PADDING}" font-size="${VALUE_SIZE}" font-weight="700" fill="#333" dominant-baseline="middle">${value}<tspan class="unit" font-size="${UNIT_SIZE}" font-weight="400" dx="${UNIT_OFFSET}">${unit}</tspan></text>
|
|
83
|
+
<text class="caption" x="${width / 2}" y="${titleHeight + valueHeight + LINE_GAP_CAPTION + CAPTION_SIZE / 2 + PADDING}" font-size="${CAPTION_SIZE}" font-weight="400" fill="#999" dominant-baseline="middle">${legend}</text>
|
|
84
|
+
</g>
|
|
85
|
+
</svg>`;
|
|
86
|
+
|
|
87
|
+
return svgContent;
|
|
88
|
+
}
|
package/src/types/queries.ts
CHANGED
|
@@ -61,11 +61,6 @@ export interface DeniedOperationCollection {
|
|
|
61
61
|
|
|
62
62
|
export interface BaseResult extends Record<string, unknown> {
|
|
63
63
|
key: string;
|
|
64
|
-
labels: string[];
|
|
65
|
-
links: CalculationLink[];
|
|
66
|
-
notifications: Notification[];
|
|
67
|
-
policyChecks: PolicyCheckCollection;
|
|
68
|
-
deniedOperations: DeniedOperationCollection;
|
|
69
64
|
}
|
|
70
65
|
|
|
71
66
|
export interface ParseResult<T extends BaseResult> {
|
|
@@ -97,9 +92,15 @@ interface CardQueryResult extends BaseResult {
|
|
|
97
92
|
rank: string;
|
|
98
93
|
title: string;
|
|
99
94
|
cardType: string;
|
|
95
|
+
cardTypeDisplayName: string;
|
|
100
96
|
workflowState: string;
|
|
101
97
|
lastUpdated: string;
|
|
102
|
-
fields
|
|
98
|
+
fields: CardQueryField[];
|
|
99
|
+
labels: string[];
|
|
100
|
+
links: CalculationLink[];
|
|
101
|
+
notifications: Notification[];
|
|
102
|
+
policyChecks: PolicyCheckCollection;
|
|
103
|
+
deniedOperations: DeniedOperationCollection;
|
|
103
104
|
}
|
|
104
105
|
interface FieldsToUpdateQueryResult extends BaseResult {
|
|
105
106
|
updateFields: UpdateField[];
|
|
@@ -135,7 +136,7 @@ interface CardQueryField extends BaseResult {
|
|
|
135
136
|
visibility: 'always' | 'optional';
|
|
136
137
|
index: number;
|
|
137
138
|
fieldDisplayName: string;
|
|
138
|
-
|
|
139
|
+
fieldDescription: string;
|
|
139
140
|
dataType: DataType;
|
|
140
141
|
isCalculated: boolean;
|
|
141
142
|
value: string | number | boolean | null | EnumValue | ListValueItem[];
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
Cyberismo
|
|
3
|
+
Copyright © Cyberismo Ltd and contributors 2025
|
|
4
|
+
|
|
5
|
+
This program is free software: you can redistribute it and/or modify it under
|
|
6
|
+
the terms of the GNU Affero General Public License version 3 as published by
|
|
7
|
+
the Free Software Foundation. This program is distributed in the hope that it
|
|
8
|
+
will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
|
9
|
+
of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
10
|
+
See the GNU Affero General Public License for more details.
|
|
11
|
+
You should have received a copy of the GNU Affero General Public
|
|
12
|
+
License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
13
|
+
*/
|
|
14
|
+
declare module 'string-pixel-width' {
|
|
15
|
+
interface PixelWidthOptions {
|
|
16
|
+
font?: string;
|
|
17
|
+
size?: number;
|
|
18
|
+
bold?: boolean;
|
|
19
|
+
italic?: boolean;
|
|
20
|
+
}
|
|
21
|
+
function pixelWidth(text: string, options?: PixelWidthOptions): number;
|
|
22
|
+
export default pixelWidth;
|
|
23
|
+
}
|
package/src/utils/card-utils.ts
CHANGED
|
@@ -81,3 +81,16 @@ export const sortCards = (a: string, b: string) => {
|
|
|
81
81
|
if (aParts[1] < bParts[1]) return -1;
|
|
82
82
|
return 0;
|
|
83
83
|
};
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Returns module name from card key
|
|
87
|
+
* @param cardKey card key
|
|
88
|
+
* @returns module name
|
|
89
|
+
*/
|
|
90
|
+
export const moduleNameFromCardKey = (cardKey: string) => {
|
|
91
|
+
const parts = cardKey.split('_');
|
|
92
|
+
if (parts.length !== 2) {
|
|
93
|
+
throw new Error(`Invalid card key: ${cardKey}`);
|
|
94
|
+
}
|
|
95
|
+
return parts[0];
|
|
96
|
+
};
|
|
@@ -16,7 +16,11 @@ import {
|
|
|
16
16
|
type AllowedClingoType,
|
|
17
17
|
ClingoFactBuilder,
|
|
18
18
|
} from './clingo-fact-builder.js';
|
|
19
|
-
import type {
|
|
19
|
+
import type {
|
|
20
|
+
Card,
|
|
21
|
+
Context,
|
|
22
|
+
ModuleContent,
|
|
23
|
+
} from '../interfaces/project-interfaces.js';
|
|
20
24
|
import type {
|
|
21
25
|
CardType,
|
|
22
26
|
FieldType,
|
|
@@ -101,6 +105,23 @@ export const createWorkflowFacts = (workflow: Workflow) => {
|
|
|
101
105
|
Facts.Workflow.WORKFLOW,
|
|
102
106
|
workflow.name,
|
|
103
107
|
);
|
|
108
|
+
|
|
109
|
+
if (workflow.displayName)
|
|
110
|
+
builder.addFact(
|
|
111
|
+
Facts.Common.FIELD,
|
|
112
|
+
workflow.name,
|
|
113
|
+
'displayName',
|
|
114
|
+
workflow.displayName,
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
if (workflow.description)
|
|
118
|
+
builder.addFact(
|
|
119
|
+
Facts.Common.FIELD,
|
|
120
|
+
workflow.name,
|
|
121
|
+
'description',
|
|
122
|
+
workflow.description,
|
|
123
|
+
);
|
|
124
|
+
|
|
104
125
|
// add states
|
|
105
126
|
for (const state of workflow.states) {
|
|
106
127
|
if (state.category) {
|
|
@@ -340,6 +361,22 @@ export const createCardTypeFacts = (cardType: CardType) => {
|
|
|
340
361
|
|
|
341
362
|
builder.addFact(Facts.CardType.CARD_TYPE, cardType.name);
|
|
342
363
|
|
|
364
|
+
if (cardType.displayName)
|
|
365
|
+
builder.addFact(
|
|
366
|
+
Facts.Common.FIELD,
|
|
367
|
+
cardType.name,
|
|
368
|
+
'displayName',
|
|
369
|
+
cardType.displayName,
|
|
370
|
+
);
|
|
371
|
+
|
|
372
|
+
if (cardType.description)
|
|
373
|
+
builder.addFact(
|
|
374
|
+
Facts.Common.FIELD,
|
|
375
|
+
cardType.name,
|
|
376
|
+
'description',
|
|
377
|
+
cardType.description,
|
|
378
|
+
);
|
|
379
|
+
|
|
343
380
|
builder.addFact(
|
|
344
381
|
Facts.Common.FIELD,
|
|
345
382
|
cardType.name,
|
|
@@ -415,14 +452,39 @@ export const createCardTypeFacts = (cardType: CardType) => {
|
|
|
415
452
|
return builder.buildAll();
|
|
416
453
|
};
|
|
417
454
|
|
|
455
|
+
export const createContextFacts = (context: Context) => {
|
|
456
|
+
const builder = new ClingoProgramBuilder();
|
|
457
|
+
builder.addFact(context);
|
|
458
|
+
return builder.buildAll();
|
|
459
|
+
};
|
|
460
|
+
|
|
418
461
|
/**
|
|
419
462
|
* Creates Clingo facts for a link type.
|
|
420
463
|
* @param linkType Link type metadata
|
|
421
464
|
* @returns clingo facts as a string
|
|
422
465
|
*/
|
|
423
466
|
export const createLinkTypeFacts = (linkType: LinkType) => {
|
|
424
|
-
const builder = new ClingoProgramBuilder()
|
|
425
|
-
|
|
467
|
+
const builder = new ClingoProgramBuilder().addFact(
|
|
468
|
+
Facts.LinkType.LINK_TYPE,
|
|
469
|
+
linkType.name,
|
|
470
|
+
);
|
|
471
|
+
|
|
472
|
+
if (linkType.displayName)
|
|
473
|
+
builder.addFact(
|
|
474
|
+
Facts.Common.FIELD,
|
|
475
|
+
linkType.name,
|
|
476
|
+
'displayName',
|
|
477
|
+
linkType.displayName,
|
|
478
|
+
);
|
|
479
|
+
|
|
480
|
+
if (linkType.description)
|
|
481
|
+
builder.addFact(
|
|
482
|
+
Facts.Common.FIELD,
|
|
483
|
+
linkType.name,
|
|
484
|
+
'description',
|
|
485
|
+
linkType.description,
|
|
486
|
+
);
|
|
487
|
+
builder
|
|
426
488
|
.addFact(
|
|
427
489
|
Facts.Common.FIELD,
|
|
428
490
|
linkType.name,
|