feat: Get json dataset from stream, convert into NudgerData, start to implement DMN concepts

This commit is contained in:
Lucàs
2024-10-02 20:37:44 +02:00
parent ffc1ad3e84
commit 4c92b856c0
53 changed files with 1234 additions and 73 deletions
+2 -2
View File
@@ -1,7 +1,7 @@
import express from "express";
import routes from "./routes";
import { createServer } from "node:http";
import { logger } from "./middlewares";
import { logger, xmlBodyParser } from "./middlewares";
export default class Server {
private readonly app: express.Application;
@@ -9,7 +9,7 @@ export default class Server {
constructor() {
this.app = express();
this.app.use(logger, routes);
this.app.use(express.json(), xmlBodyParser, logger, routes);
}
public start() {
+3 -3
View File
@@ -5,6 +5,6 @@ import { DatasetCollection } from "./services/dataset";
dotenv.config();
DatasetCollection.loadAll()
.then(() => console.log("All datasets are loaded"))
.then(() => new Server().start())
.catch(console.error);
.then(() => console.log("All datasets are loaded"))
.then(() => new Server().start())
.catch(console.error);
+1
View File
@@ -1 +1,2 @@
export { default as logger } from "./logger";
export { default as xmlBodyParser } from "./xmlBodyParser";
+17
View File
@@ -0,0 +1,17 @@
import { NextFunction, Request, Response } from "express";
export default function (req: Request, res: Response, next: NextFunction) {
if (req.is("application/xml")) {
let data = "";
req.setEncoding("utf8");
req.on("data", (chunk: any) => {
data += chunk;
});
req.on("end", () => {
req.body = data;
next();
});
} else {
next();
}
}
-9
View File
@@ -1,9 +0,0 @@
import { Router, Request, Response } from "express";
const router = Router();
router.get("/data/nudger", (req: Request, res: Response) => {
res.status(501).send("Not yet implemented");
});
export default router;
-9
View File
@@ -1,9 +0,0 @@
import { Router, Request, Response } from "express";
const router = Router();
router.get("/data/openfoodfacts", (req: Request, res: Response) => {
res.status(501).send("Not yet implemented");
});
export default router;
+2 -4
View File
@@ -1,5 +1,3 @@
import randomizeRouter from "./randomize";
import nudgerRouter from "./data/nudger";
import openDataRouter from "./data/openfoodfacts";
import randomize from "./randomize";
export default [randomizeRouter, nudgerRouter, openDataRouter];
export default randomize;
+26 -5
View File
@@ -1,14 +1,35 @@
import { Router, Request, Response } from "express";
import { Dataset, DatasetCollection } from "../services/dataset";
import { Data } from "../services/data";
import DmnModdle from "dmn-moddle";
import { DMN } from "../services/dmn/DMN";
const router = Router();
router.post("/randomize", (req: Request, res: Response) => {
// TODO: Implement randomize route
// TODO: Parse the DMN file
// TODO:
res.status(200).json({ status: "RANDOMIZED", data: [{}] });
});
router.post("/randomize/:id", async (req: Request, res: Response) => {
const { id } = req.params;
const size: number | undefined = req.query.size
? parseInt(req.query.size as string)
: undefined;
const dataset = DatasetCollection.datasets.find(
(dataset) => dataset.id === id
);
if (!dataset) return res.status(404).json({ status: "NOT_FOUND" });
const a = await DMN.parse(req.body);
const { rootElement, warnings } = await new DmnModdle().fromXML(req.body);
console.log(rootElement);
const data = await dataset.get(size);
return res.status(200).json({ status: "RANDOMIZED", data, a });
});
export default router;
-1
View File
@@ -2,7 +2,6 @@ import { createHash } from "node:crypto";
import { join } from "node:path";
import { existsSync, mkdirSync } from "node:fs";
class CacheService {
public static readonly CACHE_DIR: string = "./cache";
-10
View File
@@ -1,10 +0,0 @@
import { Dataset } from "./dataset";
import { Data } from "./data";
class DMN {
readonly xmlPath: string;
constructor(xmlPath: string, dataset: Dataset<Data>) {
this.xmlPath = xmlPath;
}
public parseXml() {}
}
-1
View File
@@ -4,7 +4,6 @@ import * as fs from "node:fs";
import { WriteStream } from "node:fs";
class FileService {
/**
* Get the compressed file stream from a given url
* @param url - The url of the file
+2 -1
View File
@@ -3,7 +3,8 @@ import { Archive, ZipArchive, ArchiveType, GzipArchive } from "./";
class ArchiveFactory {
static getArchive(archiveType: ArchiveType): Archive {
if (archiveType === ArchiveType.ZIP) return ZipArchive.instance;
if ([ArchiveType.GZIP, ArchiveType.GZ].includes(archiveType)) return GzipArchive.instance;
// if ([ArchiveType.GZIP, ArchiveType.GZ].includes(archiveType))
// return GzipArchive.instance;
throw new Error("Unsupported archive type");
}
+4 -4
View File
@@ -3,10 +3,10 @@ import { Duplex } from "node:stream";
import { Archive } from "./";
class GzipArchive implements Archive {
public static instance: Archive = new GzipArchive();
public static instance: Archive = new GzipArchive();
public extract(source: string): Duplex {
return createGunzip();
}
public extract(source: string): Duplex {
return createGunzip();
}
}
export default GzipArchive;
+1 -1
View File
@@ -1,7 +1,7 @@
export { default as ArchiveType } from "./ArchiveType";
export { default as ArchiveFactory } from "./ArchiveFactory";
export { default as Archive } from "./Archive";
export { default as ArchiveFactory } from "./ArchiveFactory";
export { default as ZipArchive } from "./ZipArchive";
export { default as GzipArchive } from "./GzipArchive";
-2
View File
@@ -17,11 +17,9 @@ type RawNudgerData = {
class NudgerData implements Data {
barcode: string;
country: string;
constructor(rawData: RawNudgerData) {
this.barcode = rawData.code;
this.country = rawData.gs1_country;
}
}
+2 -2
View File
@@ -1,3 +1,3 @@
export {default as Data} from "./Data";
export { default as Data } from "./Data";
export {default as NudgerData} from "./NudgerData";
export { default as NudgerData } from "./NudgerData";
+48 -8
View File
@@ -5,31 +5,39 @@ import FileService from "../FileService";
import { ArchiveFactory, ArchiveType } from "../archive";
import { ParserFactory } from "../parser";
import { DatasetType } from "./";
import * as fs from "node:fs";
import * as readline from "node:readline";
/**
* Represents a dataset that can be loaded and queried
*/
class Dataset<Data> {
class Dataset<D> {
readonly id: string;
readonly url: string;
readonly sourceFile: string;
readonly archiveType: ArchiveType;
readonly datasetType: DatasetType;
readonly cachePath: string;
private dConstructor: { new (rawData: any): D };
/**
* Create a new dataset instance
* @param id - The unique identifier of the dataset
* @param url - The URL of the dataset
* @param sourceFile - The file name of the dataset in the archive
* @param archiveType - The type of the archive
* @param datasetType - The type of the dataset
*/
constructor(
dConstructor: new (rawData: any) => D,
id: string,
url: string,
sourceFile: string,
archiveType: ArchiveType,
datasetType: DatasetType,
datasetType: DatasetType
) {
this.dConstructor = dConstructor;
this.id = id;
this.url = url;
this.sourceFile = sourceFile;
this.archiveType = archiveType;
@@ -59,17 +67,49 @@ class Dataset<Data> {
await FileService.getFileStream(this.url),
archive.extract(this.sourceFile),
parser.parse(),
FileService.createWriteStream(this.cachePath),
FileService.createWriteStream(this.cachePath)
);
}
/**
* Get a number of data entries from the dataset
* @param count - The number of data entries to get (default: 10)
* @param length - The number of data entries to get (default: 10)
*/
public get(count: number = 10): Data[] {
// TODO: Implement the get method
return [];
public get(length: number = 10): Promise<D[]> {
return new Promise((resolve, reject) => {
let count: number = 0;
const results: D[] = [];
const stream = fs.createReadStream(this.cachePath, { encoding: "utf8" });
const rl = readline.createInterface({
input: stream,
crlfDelay: Infinity,
});
rl.on("line", (line) => {
if (count < length) {
try {
const obj = JSON.parse(line);
results.push(new this.dConstructor(obj));
count++;
} catch (err) {
console.error("Erreur lors du parsing de la ligne:", err);
}
} else {
rl.close(); // Fermer le flux si on a atteint les n objets
}
});
// Quand le flux est terminé ou a été fermé
rl.on("close", () => {
resolve(results); // Renvoie les n objets lus
});
// Gérer les erreurs du flux de lecture
rl.on("error", (err) => {
reject(err);
});
});
}
}
+4 -2
View File
@@ -3,12 +3,14 @@ import { ArchiveType } from "../archive";
import { Dataset, DatasetType } from "./";
class DatasetCollection {
static datasets: Dataset<Data>[] = [
public static datasets: Dataset<Data>[] = [
new Dataset<NudgerData>(
NudgerData,
"nudger",
"https://files.opendatarchives.fr/data.cquest.org/open4goods/gtin-open-data.zip",
"open4goods-full-gtin-dataset.csv",
ArchiveType.ZIP,
DatasetType.CSV,
DatasetType.CSV
),
];
-5
View File
@@ -1,10 +1,5 @@
enum DatasetType {
CSV = ".csv",
// TSV = ".tsv",
// PARQUET = ".parquet",
// JSONL = ".jsonl",
// XML = ".xml",
// RDF = ".rdf",
}
export default DatasetType;
+4 -3
View File
@@ -1,3 +1,4 @@
export {default as DatasetCollection} from "./DatasetCollection";
export {default as DatasetType} from "./DatasetType";
export {default as Dataset} from "./Dataset";
export { default as DatasetType } from "./DatasetType";
export { default as Dataset } from "./Dataset";
export { default as DatasetCollection } from "./DatasetCollection";
+78
View File
@@ -0,0 +1,78 @@
import { Dataset } from "../dataset";
import { Data } from "../data";
import DmnModdle from "dmn-moddle";
import { DMN_Decision, Is_DMN_Decision } from "./interfaces/DMN_Decision";
import {
DMN_DecisionTable,
Is_DMN_DecisionTable,
} from "./interfaces/DMN_DecisionTable";
import {
DMN_InputClause,
Name_of_DMN_InputClause,
} from "./interfaces/DMN_InputClause";
import { Name_of_DMN_OutputClause } from "./interfaces/DMN_OutputClause";
import { ModdleElement } from "./interfaces/ModdleElement";
import { DmnError } from "./error/DmnError";
export class DMN {
static async parse(xml: string) {
const { rootElement, warnings } = await new DmnModdle().fromXML(xml);
if (warnings.length !== 0)
console.warn(warnings.map((warning: any) => warning.message).join(" * "));
return rootElement;
}
// static async getFilter(xml: string) {
// const rootElement = await DMN.parse(xml);
//
// const filterFunction = (me: ModdleElement) =>
// Is_DMN_Decision(me) && Is_DMN_DecisionTable(me.decisionLogic);
// const everyFunction = (decision: DMN_Decision) => {
// try {
// const decision_table: DMN_DecisionTable =
// decision.decisionLogic as DMN_DecisionTable;
// let features: string[] = decision_table.input!.map(
// (input_clause: DMN_InputClause) =>
// Name_of_DMN_InputClause(input_clause)
// );
// const index: number = features
// .map((feature: string): string => feature.toUpperCase())
// .indexOf(DMN.URL);
// if (index === -1) return false;
// // Si la zone 'text' est égale à "" alors prendre 'features[0]' :
// const data_source = decision_table.input[
// index
// ].inputExpression.text.replaceAll('"', ""); // ES2021
// features = features.concat(
// decision_table.output!.map((output_clause) =>
// Name_of_DMN_OutputClause(output_clause)
// )
// );
// // A changer, il y a autant de 'Randomizer' objects que de tables de décision :
// DMN._Randomizer = new Randomizer(
// data_source,
// features /* Number of features whose type is "output", default is '1' */
// );
//
// // decision_table.rule!.forEach((rule) => {
// // features.forEach((feature, feature_index) => {
// // const column = rule.inputEntry[feature_index];
// // // A priori, nothing here since "rules" are ignored...
// // y});
// // });
// } catch (error: unknown) {
// throw new DmnError(decision, DmnError.Invalid_JSON);
// }
// };
//
// const a: boolean = rootElement.drgElement
// .filter(filterFunction)
// .every(everyFunction);
//
// try {
// if (a === false) return Promise.resolve(undefined); // DMN processing causes trouble(s)...
// } catch (error: unknown) {
// console.error(error);
// }
// }
}
+30
View File
@@ -0,0 +1,30 @@
import { ModdleElement } from "../interfaces/ModdleElement";
import { Name_of_ModdleElement } from "../interfaces/DMN_enums";
export class DmnError extends Error {
static readonly Inconsistent_DMN_diagram =
"<strong>Inconsistent DMN diagram</strong>";
static readonly Invalid_data_format = "<strong>Invalid data format</strong>";
static readonly Invalid_drop_mode = "<strong>Invalid drop mode</strong>";
static readonly Invalid_JSON = "<strong>Invalid JSON</strong>";
static readonly No_business_logic = "<strong>No business logic</strong>";
static readonly No_possible_randomization =
"<strong>No possible randomization</strong>";
static readonly No_possible_visualization =
"<strong>No possible visualization</strong>";
static readonly Not_trained = "<strong>Not trained</strong>";
static readonly Separator = " &rarrc; ";
static readonly TensorFlow_js = "<strong>TensorFlow.js</strong>";
static readonly Undefined_DMN_type = "<strong>Undefined DMN type</strong>";
constructor(
readonly me: ModdleElement,
...messages: Array<string>
) {
super(
Name_of_ModdleElement(me) +
DmnError.Separator +
messages.join(DmnError.Separator)
);
}
}
@@ -0,0 +1,11 @@
import { ModdleElement } from "./ModdleElement";
import { DMN_DMNElementReference } from "./DMN_DMNElementReference";
export const _DMN_AuthorityRequirement: "dmn:AuthorityRequirement" =
"dmn:AuthorityRequirement";
export interface DMN_AuthorityRequirement extends ModdleElement {
$type: typeof _DMN_AuthorityRequirement;
requiredAuthority?: DMN_DMNElementReference;
requiredDecision?: DMN_DMNElementReference;
}
@@ -0,0 +1,8 @@
import { ModdleElement } from "./ModdleElement";
export const _DMN_BusinessKnowledgeModel: "dmn:BusinessKnowledgeModel" =
"dmn:BusinessKnowledgeModel";
export interface DMN_BusinessKnowledgeModel extends ModdleElement {
$type: typeof _DMN_BusinessKnowledgeModel;
}
@@ -0,0 +1,20 @@
import { ModdleElement } from "./ModdleElement";
import { DMN_ContextEntry } from "./DMN_ContextEntry";
import { DMN_type_reference_ } from "./DMN_enums";
const _DMN_Context: "dmn:Context" = "dmn:Context";
export interface DMN_Context extends ModdleElement {
$type: typeof _DMN_Context;
contextEntry: Array<DMN_ContextEntry>;
typeRef: DMN_type_reference_;
}
export function Is_DMN_Context(me: ModdleElement): me is DMN_Context {
return (
"$type" in me &&
me.$type === _DMN_Context &&
"contextEntry" in me &&
"typeRef" in me
);
}
@@ -0,0 +1,11 @@
import { ModdleElement } from "./ModdleElement";
import { DMN_LiteralExpression } from "./DMN_LiteralExpression";
import { DMN_InformationItem } from "./DMN_InformationItem";
export const _DMN_ContextEntry: "dmn:ContextEntry" = "dmn:ContextEntry";
export interface DMN_ContextEntry extends ModdleElement {
$type: typeof _DMN_ContextEntry;
value: DMN_LiteralExpression;
variable: DMN_InformationItem;
}
@@ -0,0 +1,8 @@
import { ModdleElement } from "./ModdleElement";
export const _DMN_DMNElementReference: "dmn:DMNElementReference" =
"dmn:DMNElementReference";
export interface DMN_DMNElementReference extends ModdleElement {
$type: typeof _DMN_DMNElementReference;
href: string; // Example: "#temperature_id"
}
@@ -0,0 +1,32 @@
import { ModdleElement } from "./ModdleElement";
import { DMN_AuthorityRequirement } from "./DMN_AuthorityRequirement";
import { DMN_Context } from "./DMN_Context";
import { DMN_DecisionTable } from "./DMN_DecisionTable";
import { DMN_Definitions } from "./DMN_Definitions";
import { DMN_LiteralExpression } from "./DMN_LiteralExpression";
import { DMN_InformationRequirement } from "./DMN_InformationRequirement";
import { DMN_KnowledgeRequirement } from "./DMN_KnowledgeRequirement";
import { DMN_InformationItem } from "./DMN_InformationItem";
export const _DMN_Decision: "dmn:Decision" = "dmn:Decision";
export interface DMN_Decision extends ModdleElement {
$parent: DMN_Definitions;
$type: typeof _DMN_Decision;
allowedAnswers?: string;
authorityRequirement?: Array<DMN_AuthorityRequirement>; // Knowledge source(s)
decisionLogic: DMN_Context | DMN_DecisionTable | DMN_LiteralExpression;
description?: string;
informationRequirement?: Array<DMN_InformationRequirement>; // Input data
knowledgeRequirement?: Array<DMN_KnowledgeRequirement>; // Knowledge model(s)
question?: string;
variable?: DMN_InformationItem;
}
export function Is_DMN_Decision(me: ModdleElement): me is DMN_Decision {
return "$type" in me && me.$type === _DMN_Decision && "decisionLogic" in me;
}
export function Name_of_DMN_Decision(decision: DMN_Decision): string {
return "name" in decision ? decision.name! : decision.id;
}
@@ -0,0 +1,11 @@
import { ModdleElement } from "./ModdleElement";
import { DMN_UnaryTests } from "./DMN_UnaryTests";
import { DMN_LiteralExpression } from "./DMN_LiteralExpression";
export const _DMN_DecisionRule: "dmn:DecisionRule" = "dmn:DecisionRule";
export interface DMN_DecisionRule extends ModdleElement {
$type: typeof _DMN_DecisionRule;
description: string;
inputEntry: Array<DMN_UnaryTests>;
outputEntry: Array<DMN_LiteralExpression>;
}
@@ -0,0 +1,32 @@
import { ModdleElement } from "./ModdleElement";
import { DMN_Decision } from "./DMN_Decision";
import { DMN_DecisionRule } from "./DMN_DecisionRule";
import { DMN_RuleAnnotationClause } from "./DMN_RuleAnnotationClause";
import { Hit_policy } from "./DMN_enums";
import { DMN_InputClause } from "./DMN_InputClause";
import { DMN_OutputClause } from "./DMN_OutputClause";
export const _DMN_DecisionTable: "dmn:DecisionTable" = "dmn:DecisionTable";
export interface DMN_DecisionTable extends ModdleElement {
$parent: DMN_Decision; // Overriding...
$type: typeof _DMN_DecisionTable;
annotation?: Array<DMN_RuleAnnotationClause>;
hitPolicy?: Hit_policy;
input?: Array<DMN_InputClause>;
output?: Array<DMN_OutputClause>;
outputLabel?: string;
rule?: Array<DMN_DecisionRule>;
}
export function Is_DMN_DecisionTable(
me: ModdleElement
): me is DMN_DecisionTable {
return (
"$type" in me &&
me.$type === _DMN_DecisionTable &&
"input" in me &&
"output" in me &&
"rule" in me
);
}
@@ -0,0 +1,42 @@
import { ModdleElement } from "./ModdleElement";
import { DMN_BusinessKnowledgeModel } from "./DMN_BusinessKnoledgeModel";
import { DMN_Decision } from "./DMN_Decision";
import { DMN_InputData } from "./DMN_InputData";
import { DMN_KnowledgeSource } from "./DMN_KnowledgSource";
import { DMN_ItemDefinition } from "./DMN_ItemDefinition";
import { DMN_type_reference_, Trace } from "./DMN_enums";
export const _DMN_Definitions: "dmn:Definitions" = "dmn:Definitions";
export interface DMN_Definitions extends ModdleElement {
$type: typeof _DMN_Definitions;
// artifact?: Array<ModdleElement>; // 'dmn:Association' | 'dmn:TextAnnotation'
// dmnDI: DMNDI_DMNDI;
drgElement: Array<
| DMN_BusinessKnowledgeModel
| DMN_Decision
| DMN_InputData
| DMN_KnowledgeSource
>;
itemDefinition: Array<DMN_ItemDefinition>;
}
export function _Get_type_reference_from_DMN_Definitions(
me: DMN_Definitions,
type_name: string | undefined
): DMN_type_reference_ | undefined {
if (Trace)
console.assert(
Is_DMN_Definitions(me),
"'_Get_type_reference_from_DMN_Definitions' >> 'Is_DMN_Definitions(me)', untrue"
);
if (type_name === undefined) return undefined;
const index = me.itemDefinition.findIndex(
(item: DMN_ItemDefinition) => item.name === type_name
);
return index !== -1 ? me.itemDefinition[index].typeRef : undefined;
}
export function Is_DMN_Definitions(me: ModdleElement): me is DMN_Definitions {
return "$type" in me && me.$type === _DMN_Definitions && "drgElement" in me;
}
@@ -0,0 +1,10 @@
import { ModdleElement } from "./ModdleElement";
import { DMN_type_reference_ } from "./DMN_enums";
export const _DMN_InformationItem: "dmn:InformationItem" =
"dmn:InformationItem";
export interface DMN_InformationItem extends ModdleElement {
$type: typeof _DMN_InformationItem;
typeRef: DMN_type_reference_;
}
@@ -0,0 +1,21 @@
import { ModdleElement } from "./ModdleElement";
import { DMN_DMNElementReference } from "./DMN_DMNElementReference";
const _DMN_InformationRequirement: "dmn:InformationRequirement" =
"dmn:InformationRequirement";
export interface DMN_InformationRequirement extends ModdleElement {
$type: typeof _DMN_InformationRequirement;
requiredDecision?: DMN_DMNElementReference;
requiredInput?: DMN_DMNElementReference;
}
export function Is_DMN_InformationRequirement(
me: ModdleElement
): me is DMN_InformationRequirement {
return (
"$type" in me &&
me.$type === _DMN_InformationRequirement &&
"requiredInput" in me
);
}
@@ -0,0 +1,129 @@
import { ModdleElement } from "./ModdleElement";
import { DmnError } from "../error/DmnError";
import { DMN_DecisionTable } from "./DMN_DecisionTable";
import {
_Get_type_reference_from_DMN_Definitions,
DMN_Definitions,
} from "./DMN_Definitions";
import { DMN_Decision } from "./DMN_Decision";
import { DMN_LiteralExpression } from "./DMN_LiteralExpression";
import { DMN_UnaryTests } from "./DMN_UnaryTests";
import {
_Extract_enumeration_values,
DMN_type_reference_,
Is_DMN_type_reference_,
} from "./DMN_enums";
export const _DMN_InputClause: "dmn:InputClause" = "dmn:InputClause";
export interface DMN_InputClause extends ModdleElement {
$parent: DMN_DecisionTable; // Overriding...
$type: typeof _DMN_InputClause;
inputExpression?: DMN_LiteralExpression;
inputValues?: DMN_UnaryTests;
label?: string;
typeRef?: DMN_type_reference_;
width?: number;
}
export function Get_enumeration_from_DMN_InputClause(
me: DMN_InputClause
): Array<any> | never {
// if (Trace)
// console.assert(_Is_DMN_InputClause_enumeration_(me), "Get_enumeration_from_DMN_InputClause >> '_Is_DMN_InputClause_enumeration_(me)', untrue");
let type_reference =
"inputExpression" in me ? me.inputExpression!.typeRef : undefined;
if (Is_DMN_type_reference_(type_reference) === false) {
type_reference = _Get_type_reference_from_DMN_Definitions(
me.$parent.$parent.$parent as DMN_Definitions,
type_reference
);
if (type_reference === undefined)
throw new DmnError(
me,
DmnError.Undefined_DMN_type,
Name_of_DMN_InputClause(me)
);
}
if (type_reference === DMN_type_reference_.BOOLEAN) return [false, true];
else if (type_reference === DMN_type_reference_.STRING) {
const extraction = _Extract_enumeration_values(me.inputValues!.text);
if (extraction === null)
throw new DmnError(
me,
DmnError.Undefined_DMN_type,
Name_of_DMN_InputClause(me)
);
return extraction;
} else {
const extraction = _Extract_enumeration_values(
me.inputValues!.text,
type_reference
);
if (extraction === null)
throw new DmnError(
me,
DmnError.Undefined_DMN_type,
Name_of_DMN_InputClause(me)
);
return extraction;
}
}
function _Is_DMN_InputClause_enumeration_(me: DMN_InputClause): boolean {
return (
"inputExpression" in me &&
"typeRef" in me.inputExpression! &&
("inputValues" in me ||
me.inputExpression.typeRef === DMN_type_reference_.BOOLEAN)
);
}
export function Name_of_DMN_InputClause(me: DMN_InputClause): string {
return "label" in me
? me.label!
: "name" in me
? me.name!
: "inputExpression" in me && "name" in me.inputExpression!
? me.inputExpression.name!
: me.id;
}
export function Type_of_DMN_InputClause(
me: DMN_InputClause,
decision: DMN_Decision
): DMN_type_reference_ | never {
if (_Is_DMN_InputClause_enumeration_(me))
return DMN_type_reference_.ENUMERATION;
else if ("typeRef" in me) {
if (Is_DMN_type_reference_(me.typeRef!)) return me.typeRef;
else {
const base_type = _Get_type_reference_from_DMN_Definitions(
decision.$parent,
me.typeRef! as string
);
return base_type && Is_DMN_type_reference_(base_type)
? base_type
: DMN_type_reference_.STRING;
}
} else {
if ("inputExpression" in me && "typeRef" in me.inputExpression!) {
if (Is_DMN_type_reference_(me.inputExpression.typeRef))
return me.inputExpression.typeRef;
else {
const base_type = _Get_type_reference_from_DMN_Definitions(
decision.$parent,
me.inputExpression!.typeRef as string
);
return base_type && Is_DMN_type_reference_(base_type)
? base_type
: DMN_type_reference_.STRING;
}
}
}
throw new DmnError(
me,
DmnError.Undefined_DMN_type,
Name_of_DMN_InputClause(me)
);
}
@@ -0,0 +1,14 @@
import { ModdleElement } from "./ModdleElement";
import { DMN_InformationItem } from "./DMN_InformationItem";
export const _DMN_InputData: "dmn:InputData" = "dmn:InputData";
export interface DMN_InputData extends ModdleElement {
$type: typeof _DMN_InputData;
name: string;
variable?: DMN_InformationItem;
}
export function Is_DMN_InputData(me: ModdleElement): me is DMN_InputData {
return "$type" in me && me.$type === _DMN_InputData;
}
@@ -0,0 +1,13 @@
import { DMN_UnaryTests } from "./DMN_UnaryTests";
import { DMN_type_reference_ } from "./DMN_enums";
const _DMN_ItemDefinition: "dmn:ItemDefinition" = "dmn:ItemDefinition";
export interface DMN_ItemDefinition {
$type: typeof _DMN_ItemDefinition;
allowedValues?: DMN_UnaryTests;
itemComponent?: Array<DMN_ItemDefinition>;
label: string;
name: string;
typeRef?: DMN_type_reference_;
}
@@ -0,0 +1,10 @@
import { ModdleElement } from "./ModdleElement";
import { DMN_AuthorityRequirement } from "./DMN_AuthorityRequirement";
export const _DMN_KnowledgeSource: "dmn:KnowledgeSource" =
"dmn:KnowledgeSource";
export interface DMN_KnowledgeSource extends ModdleElement {
$type: typeof _DMN_KnowledgeSource;
authorityRequirement?: Array<DMN_AuthorityRequirement>;
}
@@ -0,0 +1,10 @@
import { ModdleElement } from "./ModdleElement";
import { DMN_DMNElementReference } from "./DMN_DMNElementReference";
const _DMN_KnowledgeRequirement: "dmn:KnowledgeRequirement" =
"dmn:KnowledgeRequirement";
export interface DMN_KnowledgeRequirement extends ModdleElement {
$type: typeof _DMN_KnowledgeRequirement;
requiredKnowledge: DMN_DMNElementReference;
}
@@ -0,0 +1,22 @@
import { ModdleElement } from "./ModdleElement";
import { DMN_type_reference_ } from "./DMN_enums";
export const _DMN_LiteralExpression: "dmn:LiteralExpression" =
"dmn:LiteralExpression";
export interface DMN_LiteralExpression extends ModdleElement {
$type: typeof _DMN_LiteralExpression;
text: string;
typeRef: DMN_type_reference_;
}
export function Is_DMN_LiteralExpression(
me: ModdleElement
): me is DMN_LiteralExpression {
return (
"$type" in me &&
me.$type === _DMN_LiteralExpression &&
"text" in me &&
"typeRef" in me
);
}
@@ -0,0 +1,112 @@
import { ModdleElement } from "./ModdleElement";
import { DMN_DecisionTable } from "./DMN_DecisionTable";
import { DMN_Decision } from "./DMN_Decision";
import { DmnError } from "../error/DmnError";
import {
_Get_type_reference_from_DMN_Definitions,
DMN_Definitions,
} from "./DMN_Definitions";
import { DMN_UnaryTests } from "./DMN_UnaryTests";
import {
_Extract_enumeration_values,
DMN_type_reference_,
Is_DMN_type_reference_,
} from "./DMN_enums";
const _DMN_OutputClause: "dmn:OutputClause" = "dmn:OutputClause";
export interface DMN_OutputClause extends ModdleElement {
$parent: DMN_DecisionTable; // Overriding...
$type: typeof _DMN_OutputClause;
label?: string;
outputValues?: DMN_UnaryTests;
typeRef?: DMN_type_reference_;
}
export function Get_enumeration_from_DMN_OutputClause(
me: DMN_OutputClause
): Array<any> | never {
// if (Trace)
// console.assert(_Is_DMN_OutputClause_enumeration_(me), "Get_enumeration_from_DMN_OutputClause >> '_Is_DMN_OutputClause_enumeration_(me)', untrue");
let type_reference = me.typeRef;
if (Is_DMN_type_reference_(type_reference) === false) {
type_reference = _Get_type_reference_from_DMN_Definitions(
me.$parent.$parent.$parent as DMN_Definitions,
type_reference
);
if (type_reference === undefined)
throw new DmnError(
me,
DmnError.Undefined_DMN_type,
Name_of_DMN_OutputClause(me)
);
}
if (type_reference === DMN_type_reference_.BOOLEAN) return [false, true];
else if (type_reference === DMN_type_reference_.STRING) {
const extraction = _Extract_enumeration_values(me.outputValues!.text);
if (extraction === null)
throw new DmnError(
me,
DmnError.Undefined_DMN_type,
Name_of_DMN_OutputClause(me)
);
return extraction;
} else {
const extraction = _Extract_enumeration_values(
me.outputValues!.text,
type_reference
);
if (extraction === null)
throw new DmnError(
me,
DmnError.Undefined_DMN_type,
Name_of_DMN_OutputClause(me)
);
return extraction;
}
}
export function _Is_DMN_OutputClause_enumeration_(
me: DMN_OutputClause
): boolean {
// 'typeRef' may be missing even though 'outputValues' is present -> "enumeration" anyway...
return /*'typeRef' in me &&*/ "outputValues" in me;
}
export function Name_of_DMN_OutputClause(me: DMN_OutputClause): string {
return "label" in me
? me.label!
: "name" in me
? me.name!
: "outputLabel" in me.$parent
? me.$parent.outputLabel!
: "name" in me.$parent.$parent
? me.$parent.$parent.name!
: me.id;
}
export function Type_of_DMN_OutputClause(
me: DMN_OutputClause,
decision: DMN_Decision,
primitive_type = false
): DMN_type_reference_ | never {
if (primitive_type === false && _Is_DMN_OutputClause_enumeration_(me))
return DMN_type_reference_.ENUMERATION;
else if ("typeRef" in me)
if (Is_DMN_type_reference_(me.typeRef!)) return me.typeRef;
else {
const base_type = _Get_type_reference_from_DMN_Definitions(
decision.$parent,
me.typeRef! as string
);
return base_type && Is_DMN_type_reference_(base_type)
? base_type
: DMN_type_reference_.STRING;
}
throw new DmnError(
me,
DmnError.Undefined_DMN_type,
Name_of_DMN_OutputClause(me)
);
}
@@ -0,0 +1,8 @@
import { ModdleElement } from "./ModdleElement";
const _DMN_RuleAnnotationClause: "dmn:RuleAnnotationClause" =
"dmn:RuleAnnotationClause";
export interface DMN_RuleAnnotationClause extends ModdleElement {
$type: typeof _DMN_RuleAnnotationClause;
}
@@ -0,0 +1,12 @@
import { ModdleElement } from "./ModdleElement";
const _DMN_UnaryTests: "dmn:UnaryTests" = "dmn:UnaryTests";
export interface DMN_UnaryTests extends ModdleElement {
$type: typeof _DMN_UnaryTests;
text: string;
}
export function Is_DMN_UnaryTests(me: ModdleElement): me is DMN_UnaryTests {
return "$type" in me && me.$type === _DMN_UnaryTests && "text" in me;
}
+122
View File
@@ -0,0 +1,122 @@
import { ModdleElement } from "./ModdleElement";
export const _DMiNer_ = "_DMiNer_";
export const FEEL_range = /^[[(\]]\d{1,}\.\.\d{1,}[[)\]]$/;
export const Trace = true; // 'false' in production mode...
export enum Drop_mode {
FEEL = "FEEL",
PREDICT = "PREDICT",
TRAIN = "TRAIN",
}
export enum State_mode {
MENU = "MENU",
RANDOMIZE = "RANDOMIZE",
}
export enum Status_mode {
FELT = "FELT",
PREDICTED = "PREDICTED",
RANDOMIZED = "RANDOMIZED",
}
export enum Hit_policy { // DMN 1.3
ANY = "ANY",
COLLECT = "COLLECT",
FIRST = "FIRST",
OUTPUT_ORDER = "OUTPUT ORDER",
PRIORITY = "PRIORITY",
RULE_ORDER = "RULE ORDER",
UNIQUE = "UNIQUE",
}
export function Name_of_ModdleElement(me: ModdleElement): string {
return "name" in me ? me.name! : me.$type + me.id;
}
// https://docs.camunda.org/manual/7.18/user-guide/dmn-engine/data-types/#supported-data-types
export enum DMN_type_reference_ {
BOOLEAN = "boolean",
DATE = "date",
DOUBLE = "double",
ENUMERATION = "enum",
INTEGER = "integer",
LONG = "long",
NUMBER = "number",
STRING = "string",
}
export function Is_DMN_type_reference_(
type_reference: string | undefined
): type_reference is DMN_type_reference_ {
if (type_reference === undefined) return false;
return Object.values(DMN_type_reference_).includes(
type_reference.toLowerCase() as DMN_type_reference_
);
}
export type DMN_type_reference = boolean | Date | number | string;
export type TensorFlow_datum = Array<Array<0 | 1> | DMN_type_reference>;
export type TensorFlow_data = Array<TensorFlow_datum>;
/**
* A decision service exposes one or more
* decisions from a decision model as a reusable element, a service, which might be consumed (for example) internally by
* another decision in the decision model, or externally by a task in a BPMN process model.
*/
const _DMN_DecisionService: "dmn:DecisionService" = "dmn:DecisionService";
const _DMN_Invocation: "dmn:Invocation" = "dmn:Invocation"; // Alternative to decision table and literal expression
// export interface DMNDI_DMNDI extends ModdleElement {
// $type: 'dmndi:DMNDI';
// diagrams: Array<DMNDI_DMNDiagram>;
// }
// export interface DMNDI_DMNDiagram extends ModdleElement {
// $type: 'dmndi:DMNDiagram';
// diagramElements: Array<ModdleElement>; // 'dmndi:DMNEdge' | 'dmndi:DMNShape'
// }
export function _Extract_enumeration_values(
enumeration: string
): Array<string> | null;
export function _Extract_enumeration_values(
enumeration: string,
type_reference: DMN_type_reference_
): Array<number> | null;
export function _Extract_enumeration_values(
enumeration: string,
type_reference?: DMN_type_reference_
): Array<string> | Array<number> | null {
if (type_reference) {
if (FEEL_range.test(enumeration)) {
const values = enumeration.match(/\d+/g)!.map((value) => parseInt(value));
const start = /^[\(\]]/.test(enumeration)
? Math.min(...values) + 1
: Math.min(...values);
const end = /[\)\[]$/.test(enumeration)
? Math.max(...values) - 1
: Math.max(...values);
values.length = 0;
for (let i = start; i <= end; i++) values.push(i);
return values;
} else if (
type_reference === DMN_type_reference_.INTEGER ||
type_reference === DMN_type_reference_.LONG
)
return enumeration.split(",").map((value) => parseInt(value));
else if (
type_reference === DMN_type_reference_.DOUBLE ||
type_reference === DMN_type_reference_.NUMBER
)
return enumeration.split(",").map((value) => parseFloat(value));
return null;
}
const values = enumeration.match(/"\w+( \w+)*"/g);
return values === null
? values
: values.map((value) => value.replace(/^"/g, "").replace(/"$/g, ""));
}
+20
View File
@@ -0,0 +1,20 @@
import { Drop_mode, Status_mode } from "./DMN_enums";
export interface Data {
action: Drop_mode | Status_mode;
data: Array<Object>;
}
export function Is_Data(data: Data): data is Data {
return (
"action" in data &&
(Object.values(Drop_mode).includes(
(data.action as string).toUpperCase() as Drop_mode
) ||
Object.values(Status_mode).includes(
(data.action as string).toUpperCase() as Status_mode
)) &&
"data" in data &&
Array.isArray(data.data)
);
}
@@ -0,0 +1,7 @@
export interface ModdleElement {
// $attrs: Object; // Unused...
id: string;
name?: string;
$parent: ModdleElement | undefined;
$type: string;
}
+1
View File
@@ -0,0 +1 @@
declare module "JSONStream";
+1
View File
@@ -0,0 +1 @@
declare module "dmn-moddle";