diff --git a/insomnia/Insomnia_2024-10-02.json b/insomnia/Insomnia_2024-10-02.json
new file mode 100644
index 0000000..8f7eb79
--- /dev/null
+++ b/insomnia/Insomnia_2024-10-02.json
@@ -0,0 +1,113 @@
+{
+ "_type": "export",
+ "__export_format": 4,
+ "__export_date": "2024-10-02T18:34:55.495Z",
+ "__export_source": "insomnia.desktop.app:v10.0.0",
+ "resources": [
+ {
+ "_id": "req_55b9623b1d824b52b34d13279176c354",
+ "parentId": "wrk_b6f7ea92d2f443b2bf923a99dc6fca88",
+ "modified": 1727867887432,
+ "created": 1727867720980,
+ "url": "http://localhost:4321/randomize",
+ "name": "randomize",
+ "description": "",
+ "method": "POST",
+ "body": {},
+ "parameters": [],
+ "headers": [
+ {
+ "name": "User-Agent",
+ "value": "insomnia/10.0.0"
+ }
+ ],
+ "authentication": {},
+ "metaSortKey": -1727867720980,
+ "isPrivate": false,
+ "pathParameters": [],
+ "settingStoreCookies": true,
+ "settingSendCookies": true,
+ "settingDisableRenderRequestBody": false,
+ "settingEncodeUrl": true,
+ "settingRebuildPath": true,
+ "settingFollowRedirects": "global",
+ "_type": "request"
+ },
+ {
+ "_id": "wrk_b6f7ea92d2f443b2bf923a99dc6fca88",
+ "parentId": null,
+ "modified": 1727867693579,
+ "created": 1727867693579,
+ "name": "Compo-Service-Log-Project",
+ "description": "",
+ "scope": "collection",
+ "_type": "workspace"
+ },
+ {
+ "_id": "req_cc7e8367ddb843da91ef50d9d0df6ed2",
+ "parentId": "wrk_b6f7ea92d2f443b2bf923a99dc6fca88",
+ "modified": 1727885012157,
+ "created": 1727867896038,
+ "url": "http://localhost:4321/randomize/nudger",
+ "name": "randomize/nudger",
+ "description": "",
+ "method": "POST",
+ "body": {
+ "mimeType": "application/xml",
+ "text": "\n\n \n \n \n \n \n \n \n \n \n \n string length(?)=13 and starts with(?, \"3\") and not matches(?, \"^3[89]\")\n \n \n \"France-Monaco\"\n \n \n \n \n matches(?, \"^46\\d{11}$\")\n \n \n \"Russia\"\n \n \n \n \n string length(?)=13 and starts with(?, \"560\")\n \n \n \"Portugal\"\n \n \n \n \n \nmatches(?, \"^9[0-1]{1}\\d{10}$\")\n \n \n \"AT\"\n \n \n \n \n matches(?, \"^9[0-1]{1}\\d{10}$\")\n \n \n \"Austria\"\n \n \n \n \n\n"
+ },
+ "parameters": [
+ {
+ "id": "pair_d55b3a61f8fc4ad89303e5b70fc19df6",
+ "name": "size",
+ "value": "1000",
+ "description": "",
+ "disabled": true
+ }
+ ],
+ "headers": [
+ {
+ "name": "Content-Type",
+ "value": "application/xml"
+ },
+ {
+ "name": "User-Agent",
+ "value": "insomnia/10.0.0"
+ }
+ ],
+ "authentication": {},
+ "metaSortKey": -1727867720930,
+ "isPrivate": false,
+ "pathParameters": [],
+ "settingStoreCookies": true,
+ "settingSendCookies": true,
+ "settingDisableRenderRequestBody": false,
+ "settingEncodeUrl": true,
+ "settingRebuildPath": true,
+ "settingFollowRedirects": "global",
+ "_type": "request"
+ },
+ {
+ "_id": "env_025a059466ea4c5d1a14c9fd8028c0bb99039906",
+ "parentId": "wrk_b6f7ea92d2f443b2bf923a99dc6fca88",
+ "modified": 1727867693581,
+ "created": 1727867693581,
+ "name": "Base Environment",
+ "data": {},
+ "dataPropertyOrder": null,
+ "color": null,
+ "isPrivate": false,
+ "metaSortKey": 1727867693581,
+ "_type": "environment"
+ },
+ {
+ "_id": "jar_025a059466ea4c5d1a14c9fd8028c0bb99039906",
+ "parentId": "wrk_b6f7ea92d2f443b2bf923a99dc6fca88",
+ "modified": 1727867693582,
+ "created": 1727867693582,
+ "name": "Default Jar",
+ "cookies": [],
+ "_type": "cookie_jar"
+ }
+ ]
+}
diff --git a/package-lock.json b/package-lock.json
index e1d4604..08938c5 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,9 +10,12 @@
"license": "ISC",
"dependencies": {
"axios": "^1.7.7",
+ "body-parser": "^1.20.3",
"csvtojson": "^2.0.10",
+ "dmn-moddle": "^10.0.0",
"dotenv": "^16.4.5",
"express": "^4.21.0",
+ "JSONStream": "^1.3.5",
"node-stream-zip": "^1.15.0",
"tar-stream": "^3.1.7",
"unzipper": "^0.12.3"
@@ -506,6 +509,17 @@
"node": ">=0.3.1"
}
},
+ "node_modules/dmn-moddle": {
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/dmn-moddle/-/dmn-moddle-10.0.0.tgz",
+ "integrity": "sha512-JD08qH2VqA7O54qCQFrGruwGyVovow+9g2O5/ww0KpC/n0mvuua117dz2CONiUWexJxq/dOAaMjrQwkWnK+oEA==",
+ "license": "MIT",
+ "dependencies": {
+ "min-dash": "^3.0.0",
+ "moddle": "^5.0.1",
+ "moddle-xml": "^9.0.5"
+ }
+ },
"node_modules/dotenv": {
"version": "16.4.5",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
@@ -871,6 +885,31 @@
"graceful-fs": "^4.1.6"
}
},
+ "node_modules/jsonparse": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz",
+ "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==",
+ "engines": [
+ "node >= 0.2.0"
+ ],
+ "license": "MIT"
+ },
+ "node_modules/JSONStream": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz",
+ "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==",
+ "license": "(MIT OR Apache-2.0)",
+ "dependencies": {
+ "jsonparse": "^1.2.0",
+ "through": ">=2.2.7 <3"
+ },
+ "bin": {
+ "JSONStream": "bin.js"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
@@ -944,6 +983,32 @@
"node": ">= 0.6"
}
},
+ "node_modules/min-dash": {
+ "version": "3.8.1",
+ "resolved": "https://registry.npmjs.org/min-dash/-/min-dash-3.8.1.tgz",
+ "integrity": "sha512-evumdlmIlg9mbRVPbC4F5FuRhNmcMS5pvuBUbqb1G9v09Ro0ImPEgz5n3khir83lFok1inKqVDjnKEg3GpDxQg==",
+ "license": "MIT"
+ },
+ "node_modules/moddle": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/moddle/-/moddle-5.0.4.tgz",
+ "integrity": "sha512-Kjb+hjuzO+YlojNGxEUXvdhLYTHTtAABDlDcJTtTcn5MbJF9Zkv4I1Fyvp3Ypmfgg1EfHDZ3PsCQTuML9JD6wg==",
+ "license": "MIT",
+ "dependencies": {
+ "min-dash": "^3.0.0"
+ }
+ },
+ "node_modules/moddle-xml": {
+ "version": "9.0.6",
+ "resolved": "https://registry.npmjs.org/moddle-xml/-/moddle-xml-9.0.6.tgz",
+ "integrity": "sha512-tl0reHpsY/aKlLGhXeFlQWlYAQHFxTkFqC8tq8jXRYpQSnLVw13T6swMaourLd7EXqHdWsc+5ggsB+fEep6xZQ==",
+ "license": "MIT",
+ "dependencies": {
+ "min-dash": "^3.5.2",
+ "moddle": "^5.0.2",
+ "saxen": "^8.1.2"
+ }
+ },
"node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
@@ -1150,6 +1215,12 @@
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"license": "MIT"
},
+ "node_modules/saxen": {
+ "version": "8.1.2",
+ "resolved": "https://registry.npmjs.org/saxen/-/saxen-8.1.2.tgz",
+ "integrity": "sha512-xUOiiFbc3Ow7p8KMxwsGICPx46ZQvy3+qfNVhrkwfz3Vvq45eGt98Ft5IQaA1R/7Tb5B5MKh9fUR9x3c3nDTxw==",
+ "license": "MIT"
+ },
"node_modules/send": {
"version": "0.19.0",
"resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz",
@@ -1315,6 +1386,12 @@
"b4a": "^1.6.4"
}
},
+ "node_modules/through": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
+ "license": "MIT"
+ },
"node_modules/toidentifier": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
diff --git a/package.json b/package.json
index 0f2ca4b..596d1d8 100644
--- a/package.json
+++ b/package.json
@@ -16,9 +16,12 @@
"description": "",
"dependencies": {
"axios": "^1.7.7",
+ "body-parser": "^1.20.3",
"csvtojson": "^2.0.10",
+ "dmn-moddle": "^10.0.0",
"dotenv": "^16.4.5",
"express": "^4.21.0",
+ "JSONStream": "^1.3.5",
"node-stream-zip": "^1.15.0",
"tar-stream": "^3.1.7",
"unzipper": "^0.12.3"
diff --git a/src/Server.ts b/src/Server.ts
index ea43844..f44f4a5 100644
--- a/src/Server.ts
+++ b/src/Server.ts
@@ -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() {
diff --git a/src/index.ts b/src/index.ts
index 8ef04a7..661f796 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -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);
diff --git a/src/middlewares/index.ts b/src/middlewares/index.ts
index 4412251..a4deecf 100644
--- a/src/middlewares/index.ts
+++ b/src/middlewares/index.ts
@@ -1 +1,2 @@
export { default as logger } from "./logger";
+export { default as xmlBodyParser } from "./xmlBodyParser";
diff --git a/src/middlewares/xmlBodyParser.ts b/src/middlewares/xmlBodyParser.ts
new file mode 100644
index 0000000..b9f4f6e
--- /dev/null
+++ b/src/middlewares/xmlBodyParser.ts
@@ -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();
+ }
+}
diff --git a/src/routes/data/nudger.ts b/src/routes/data/nudger.ts
deleted file mode 100644
index 16cf46d..0000000
--- a/src/routes/data/nudger.ts
+++ /dev/null
@@ -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;
diff --git a/src/routes/data/openfoodfacts.ts b/src/routes/data/openfoodfacts.ts
deleted file mode 100644
index 937a976..0000000
--- a/src/routes/data/openfoodfacts.ts
+++ /dev/null
@@ -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;
diff --git a/src/routes/index.ts b/src/routes/index.ts
index 23efeb9..517ec97 100644
--- a/src/routes/index.ts
+++ b/src/routes/index.ts
@@ -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;
diff --git a/src/routes/randomize.ts b/src/routes/randomize.ts
index eda1001..857e0f3 100644
--- a/src/routes/randomize.ts
+++ b/src/routes/randomize.ts
@@ -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;
diff --git a/src/services/CacheService.ts b/src/services/CacheService.ts
index 447f150..5117f73 100644
--- a/src/services/CacheService.ts
+++ b/src/services/CacheService.ts
@@ -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";
diff --git a/src/services/DMN.ts b/src/services/DMN.ts
deleted file mode 100644
index 18830dc..0000000
--- a/src/services/DMN.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { Dataset } from "./dataset";
-import { Data } from "./data";
-
-class DMN {
- readonly xmlPath: string;
- constructor(xmlPath: string, dataset: Dataset) {
- this.xmlPath = xmlPath;
- }
- public parseXml() {}
-}
diff --git a/src/services/FileService.ts b/src/services/FileService.ts
index 052bf20..947d4f2 100644
--- a/src/services/FileService.ts
+++ b/src/services/FileService.ts
@@ -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
diff --git a/src/services/archive/ArchiveFactory.ts b/src/services/archive/ArchiveFactory.ts
index 336a129..d46261d 100644
--- a/src/services/archive/ArchiveFactory.ts
+++ b/src/services/archive/ArchiveFactory.ts
@@ -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");
}
diff --git a/src/services/archive/GzipArchive.ts b/src/services/archive/GzipArchive.ts
index 2c56bbe..ce8ecdc 100644
--- a/src/services/archive/GzipArchive.ts
+++ b/src/services/archive/GzipArchive.ts
@@ -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;
diff --git a/src/services/archive/index.ts b/src/services/archive/index.ts
index 7e1491b..f6562c9 100644
--- a/src/services/archive/index.ts
+++ b/src/services/archive/index.ts
@@ -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";
diff --git a/src/services/data/NudgerData.ts b/src/services/data/NudgerData.ts
index 7fdfaf9..c13f0e4 100644
--- a/src/services/data/NudgerData.ts
+++ b/src/services/data/NudgerData.ts
@@ -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;
}
}
diff --git a/src/services/data/index.ts b/src/services/data/index.ts
index f497037..12c9b86 100644
--- a/src/services/data/index.ts
+++ b/src/services/data/index.ts
@@ -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";
diff --git a/src/services/dataset/Dataset.ts b/src/services/dataset/Dataset.ts
index 443936b..30e26be 100644
--- a/src/services/dataset/Dataset.ts
+++ b/src/services/dataset/Dataset.ts
@@ -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 {
-
+class Dataset {
+ 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 {
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 {
+ 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);
+ });
+ });
}
}
diff --git a/src/services/dataset/DatasetCollection.ts b/src/services/dataset/DatasetCollection.ts
index 38223fc..d83a7d7 100644
--- a/src/services/dataset/DatasetCollection.ts
+++ b/src/services/dataset/DatasetCollection.ts
@@ -3,12 +3,14 @@ import { ArchiveType } from "../archive";
import { Dataset, DatasetType } from "./";
class DatasetCollection {
- static datasets: Dataset[] = [
+ public static datasets: Dataset[] = [
new Dataset(
+ 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
),
];
diff --git a/src/services/dataset/DatasetType.ts b/src/services/dataset/DatasetType.ts
index 1c8c19f..19a7ecc 100644
--- a/src/services/dataset/DatasetType.ts
+++ b/src/services/dataset/DatasetType.ts
@@ -1,10 +1,5 @@
enum DatasetType {
CSV = ".csv",
- // TSV = ".tsv",
- // PARQUET = ".parquet",
- // JSONL = ".jsonl",
- // XML = ".xml",
- // RDF = ".rdf",
}
export default DatasetType;
diff --git a/src/services/dataset/index.ts b/src/services/dataset/index.ts
index 1c35651..f300450 100644
--- a/src/services/dataset/index.ts
+++ b/src/services/dataset/index.ts
@@ -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";
diff --git a/src/services/dmn/DMN.ts b/src/services/dmn/DMN.ts
new file mode 100644
index 0000000..391baba
--- /dev/null
+++ b/src/services/dmn/DMN.ts
@@ -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);
+ // }
+ // }
+}
diff --git a/src/services/dmn/error/DmnError.ts b/src/services/dmn/error/DmnError.ts
new file mode 100644
index 0000000..954bad8
--- /dev/null
+++ b/src/services/dmn/error/DmnError.ts
@@ -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 =
+ "Inconsistent DMN diagram";
+ static readonly Invalid_data_format = "Invalid data format";
+ static readonly Invalid_drop_mode = "Invalid drop mode";
+ static readonly Invalid_JSON = "Invalid JSON";
+ static readonly No_business_logic = "No business logic";
+ static readonly No_possible_randomization =
+ "No possible randomization";
+ static readonly No_possible_visualization =
+ "No possible visualization";
+ static readonly Not_trained = "Not trained";
+ static readonly Separator = " ⤳ ";
+ static readonly TensorFlow_js = "TensorFlow.js";
+ static readonly Undefined_DMN_type = "Undefined DMN type";
+
+ constructor(
+ readonly me: ModdleElement,
+ ...messages: Array
+ ) {
+ super(
+ Name_of_ModdleElement(me) +
+ DmnError.Separator +
+ messages.join(DmnError.Separator)
+ );
+ }
+}
diff --git a/src/services/dmn/interfaces/DMN_AuthorityRequirement.ts b/src/services/dmn/interfaces/DMN_AuthorityRequirement.ts
new file mode 100644
index 0000000..f366a47
--- /dev/null
+++ b/src/services/dmn/interfaces/DMN_AuthorityRequirement.ts
@@ -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;
+}
diff --git a/src/services/dmn/interfaces/DMN_BusinessKnoledgeModel.ts b/src/services/dmn/interfaces/DMN_BusinessKnoledgeModel.ts
new file mode 100644
index 0000000..ace2653
--- /dev/null
+++ b/src/services/dmn/interfaces/DMN_BusinessKnoledgeModel.ts
@@ -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;
+}
diff --git a/src/services/dmn/interfaces/DMN_Context.ts b/src/services/dmn/interfaces/DMN_Context.ts
new file mode 100644
index 0000000..e6bd186
--- /dev/null
+++ b/src/services/dmn/interfaces/DMN_Context.ts
@@ -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;
+ 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
+ );
+}
diff --git a/src/services/dmn/interfaces/DMN_ContextEntry.ts b/src/services/dmn/interfaces/DMN_ContextEntry.ts
new file mode 100644
index 0000000..85a6585
--- /dev/null
+++ b/src/services/dmn/interfaces/DMN_ContextEntry.ts
@@ -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;
+}
diff --git a/src/services/dmn/interfaces/DMN_DMNElementReference.ts b/src/services/dmn/interfaces/DMN_DMNElementReference.ts
new file mode 100644
index 0000000..34c1480
--- /dev/null
+++ b/src/services/dmn/interfaces/DMN_DMNElementReference.ts
@@ -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"
+}
diff --git a/src/services/dmn/interfaces/DMN_Decision.ts b/src/services/dmn/interfaces/DMN_Decision.ts
new file mode 100644
index 0000000..34dcd90
--- /dev/null
+++ b/src/services/dmn/interfaces/DMN_Decision.ts
@@ -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; // Knowledge source(s)
+ decisionLogic: DMN_Context | DMN_DecisionTable | DMN_LiteralExpression;
+ description?: string;
+ informationRequirement?: Array; // Input data
+ knowledgeRequirement?: Array; // 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;
+}
diff --git a/src/services/dmn/interfaces/DMN_DecisionRule.ts b/src/services/dmn/interfaces/DMN_DecisionRule.ts
new file mode 100644
index 0000000..7935e95
--- /dev/null
+++ b/src/services/dmn/interfaces/DMN_DecisionRule.ts
@@ -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;
+ outputEntry: Array;
+}
diff --git a/src/services/dmn/interfaces/DMN_DecisionTable.ts b/src/services/dmn/interfaces/DMN_DecisionTable.ts
new file mode 100644
index 0000000..138593d
--- /dev/null
+++ b/src/services/dmn/interfaces/DMN_DecisionTable.ts
@@ -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;
+ hitPolicy?: Hit_policy;
+ input?: Array;
+ output?: Array;
+ outputLabel?: string;
+ rule?: Array;
+}
+
+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
+ );
+}
diff --git a/src/services/dmn/interfaces/DMN_Definitions.ts b/src/services/dmn/interfaces/DMN_Definitions.ts
new file mode 100644
index 0000000..f307ddf
--- /dev/null
+++ b/src/services/dmn/interfaces/DMN_Definitions.ts
@@ -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; // 'dmn:Association' | 'dmn:TextAnnotation'
+ // dmnDI: DMNDI_DMNDI;
+ drgElement: Array<
+ | DMN_BusinessKnowledgeModel
+ | DMN_Decision
+ | DMN_InputData
+ | DMN_KnowledgeSource
+ >;
+ itemDefinition: Array;
+}
+
+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;
+}
diff --git a/src/services/dmn/interfaces/DMN_InformationItem.ts b/src/services/dmn/interfaces/DMN_InformationItem.ts
new file mode 100644
index 0000000..26746b1
--- /dev/null
+++ b/src/services/dmn/interfaces/DMN_InformationItem.ts
@@ -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_;
+}
diff --git a/src/services/dmn/interfaces/DMN_InformationRequirement.ts b/src/services/dmn/interfaces/DMN_InformationRequirement.ts
new file mode 100644
index 0000000..7337d02
--- /dev/null
+++ b/src/services/dmn/interfaces/DMN_InformationRequirement.ts
@@ -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
+ );
+}
diff --git a/src/services/dmn/interfaces/DMN_InputClause.ts b/src/services/dmn/interfaces/DMN_InputClause.ts
new file mode 100644
index 0000000..6bffaef
--- /dev/null
+++ b/src/services/dmn/interfaces/DMN_InputClause.ts
@@ -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 | 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)
+ );
+}
diff --git a/src/services/dmn/interfaces/DMN_InputData.ts b/src/services/dmn/interfaces/DMN_InputData.ts
new file mode 100644
index 0000000..094c122
--- /dev/null
+++ b/src/services/dmn/interfaces/DMN_InputData.ts
@@ -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;
+}
diff --git a/src/services/dmn/interfaces/DMN_ItemDefinition.ts b/src/services/dmn/interfaces/DMN_ItemDefinition.ts
new file mode 100644
index 0000000..f069fba
--- /dev/null
+++ b/src/services/dmn/interfaces/DMN_ItemDefinition.ts
@@ -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;
+ label: string;
+ name: string;
+ typeRef?: DMN_type_reference_;
+}
diff --git a/src/services/dmn/interfaces/DMN_KnowledgSource.ts b/src/services/dmn/interfaces/DMN_KnowledgSource.ts
new file mode 100644
index 0000000..4647685
--- /dev/null
+++ b/src/services/dmn/interfaces/DMN_KnowledgSource.ts
@@ -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;
+}
diff --git a/src/services/dmn/interfaces/DMN_KnowledgeRequirement.ts b/src/services/dmn/interfaces/DMN_KnowledgeRequirement.ts
new file mode 100644
index 0000000..3d877a6
--- /dev/null
+++ b/src/services/dmn/interfaces/DMN_KnowledgeRequirement.ts
@@ -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;
+}
diff --git a/src/services/dmn/interfaces/DMN_LiteralExpression.ts b/src/services/dmn/interfaces/DMN_LiteralExpression.ts
new file mode 100644
index 0000000..77764dd
--- /dev/null
+++ b/src/services/dmn/interfaces/DMN_LiteralExpression.ts
@@ -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
+ );
+}
diff --git a/src/services/dmn/interfaces/DMN_OutputClause.ts b/src/services/dmn/interfaces/DMN_OutputClause.ts
new file mode 100644
index 0000000..e6eec24
--- /dev/null
+++ b/src/services/dmn/interfaces/DMN_OutputClause.ts
@@ -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 | 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)
+ );
+}
diff --git a/src/services/dmn/interfaces/DMN_RuleAnnotationClause.ts b/src/services/dmn/interfaces/DMN_RuleAnnotationClause.ts
new file mode 100644
index 0000000..7728736
--- /dev/null
+++ b/src/services/dmn/interfaces/DMN_RuleAnnotationClause.ts
@@ -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;
+}
diff --git a/src/services/dmn/interfaces/DMN_UnaryTests.ts b/src/services/dmn/interfaces/DMN_UnaryTests.ts
new file mode 100644
index 0000000..3528424
--- /dev/null
+++ b/src/services/dmn/interfaces/DMN_UnaryTests.ts
@@ -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;
+}
diff --git a/src/services/dmn/interfaces/DMN_enums.ts b/src/services/dmn/interfaces/DMN_enums.ts
new file mode 100644
index 0000000..ac08d25
--- /dev/null
+++ b/src/services/dmn/interfaces/DMN_enums.ts
@@ -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 | DMN_type_reference>;
+export type TensorFlow_data = Array;
+
+/**
+ * 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;
+// }
+
+// export interface DMNDI_DMNDiagram extends ModdleElement {
+// $type: 'dmndi:DMNDiagram';
+// diagramElements: Array; // 'dmndi:DMNEdge' | 'dmndi:DMNShape'
+// }
+
+export function _Extract_enumeration_values(
+ enumeration: string
+): Array | null;
+export function _Extract_enumeration_values(
+ enumeration: string,
+ type_reference: DMN_type_reference_
+): Array | null;
+export function _Extract_enumeration_values(
+ enumeration: string,
+ type_reference?: DMN_type_reference_
+): Array | Array | 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, ""));
+}
diff --git a/src/services/dmn/interfaces/Data.ts b/src/services/dmn/interfaces/Data.ts
new file mode 100644
index 0000000..93ab504
--- /dev/null
+++ b/src/services/dmn/interfaces/Data.ts
@@ -0,0 +1,20 @@
+import { Drop_mode, Status_mode } from "./DMN_enums";
+
+export interface Data {
+ action: Drop_mode | Status_mode;
+ data: Array