@easyrpa/script-creator 1.0.0
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/index.d.ts +32 -0
- package/dist/index.js +460 -0
- package/dist/types.d.ts +87 -0
- package/dist/types.js +19 -0
- package/dist/utils.d.ts +3 -0
- package/dist/utils.js +8 -0
- package/package.json +31 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { ExternalDataNodeType, ExternalDataEdgeType, MetaDataType, VariablesNamesType, CollectionType } from "./types";
|
|
2
|
+
export interface IScriptCreator {
|
|
3
|
+
collections: CollectionType[];
|
|
4
|
+
metaData?: MetaDataType;
|
|
5
|
+
isDebugMode?: boolean;
|
|
6
|
+
isRunMode?: boolean;
|
|
7
|
+
createScript(dirPath: string, nodes: ExternalDataNodeType[], edges: ExternalDataEdgeType[], parentNode?: ExternalDataNodeType): Promise<string>;
|
|
8
|
+
}
|
|
9
|
+
export type ScriptCreatorConfig = {
|
|
10
|
+
logDebugVariablesPath?: string;
|
|
11
|
+
metaData?: MetaDataType;
|
|
12
|
+
isDebugMode?: boolean;
|
|
13
|
+
isRunMode?: boolean;
|
|
14
|
+
variablesNames?: Partial<VariablesNamesType>;
|
|
15
|
+
};
|
|
16
|
+
export declare class ScriptCreator implements IScriptCreator {
|
|
17
|
+
collections: CollectionType[];
|
|
18
|
+
metaData?: MetaDataType;
|
|
19
|
+
isDebugMode?: boolean;
|
|
20
|
+
isRunMode?: boolean;
|
|
21
|
+
variablesNames: VariablesNamesType;
|
|
22
|
+
logDebugVariablesPath?: string;
|
|
23
|
+
constructor(collections: CollectionType[], config: ScriptCreatorConfig);
|
|
24
|
+
createScript(dirPath: string, nodes: ExternalDataNodeType[], edges: ExternalDataEdgeType[], parentNode?: ExternalDataNodeType | undefined): Promise<string>;
|
|
25
|
+
private topologicalSorting;
|
|
26
|
+
private getInputVars;
|
|
27
|
+
private getInputVarsForContainer;
|
|
28
|
+
private createScriptContainers;
|
|
29
|
+
private createScriptSubFlows;
|
|
30
|
+
private createCallNodeFunction;
|
|
31
|
+
private createScriptString;
|
|
32
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
import fsPromises from "fs/promises";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { HandleTypes, NodeTypes, } from "./types";
|
|
4
|
+
import { getSubFlowName, normalize } from "./utils";
|
|
5
|
+
export class ScriptCreator {
|
|
6
|
+
constructor(collections, config) {
|
|
7
|
+
this.logDebugVariablesPath = config.logDebugVariablesPath;
|
|
8
|
+
this.metaData = config.metaData;
|
|
9
|
+
this.isDebugMode = config.isDebugMode;
|
|
10
|
+
this.isRunMode = config.isRunMode;
|
|
11
|
+
this.variablesNames = {
|
|
12
|
+
nodesVariableName: "nodes",
|
|
13
|
+
containerDataName: "container_data",
|
|
14
|
+
nodesJsonVariableName: "nodes_pickle",
|
|
15
|
+
inputDictName: "input_dict",
|
|
16
|
+
inHandlesName: "in_handles",
|
|
17
|
+
defaultScriptName: "script.py",
|
|
18
|
+
...(config.variablesNames || {}),
|
|
19
|
+
};
|
|
20
|
+
this.collections = collections;
|
|
21
|
+
}
|
|
22
|
+
//создание директории скрипта по заданному пути
|
|
23
|
+
async createScript(dirPath, nodes, edges, parentNode = undefined) {
|
|
24
|
+
// проверяем наличие всех необходимых блоков
|
|
25
|
+
const allAvailableNodes = this.collections.reduce((allNodes, collection) => {
|
|
26
|
+
return [
|
|
27
|
+
...allNodes,
|
|
28
|
+
...collection.nodes
|
|
29
|
+
];
|
|
30
|
+
}, []);
|
|
31
|
+
const nodesIsAvailable = nodes.every(node => allAvailableNodes.some(avNode => avNode.collection === node.collection && avNode.name === node.name));
|
|
32
|
+
if (!nodesIsAvailable) {
|
|
33
|
+
return Promise.reject('Some nodes are missing from collections');
|
|
34
|
+
}
|
|
35
|
+
// замена пути к директории блока
|
|
36
|
+
nodes.forEach(node => {
|
|
37
|
+
const availableNode = allAvailableNodes.find(avNode => avNode.collection === node.collection && avNode.name === node.name);
|
|
38
|
+
if (availableNode) {
|
|
39
|
+
node.path = availableNode.path;
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
// сортировка узлов
|
|
43
|
+
const nodesTree = nodes.filter((node) => !(node.parentsId && node.parentsId.length > 0));
|
|
44
|
+
const nodesSort = this.topologicalSorting(nodesTree, edges);
|
|
45
|
+
// копируем все директории узлов Control и Container
|
|
46
|
+
const uniqueNodes = nodes
|
|
47
|
+
.filter((node) => node.type === NodeTypes.CONTROL || node.type === NodeTypes.CONTAINER)
|
|
48
|
+
.filter((node, index, arr) => arr.findIndex((n) => n.name === node.name) === index);
|
|
49
|
+
for (const node of uniqueNodes) {
|
|
50
|
+
if (node.path && node.scriptPath && node.name) {
|
|
51
|
+
await fsPromises.cp(path.join(node.path, path.dirname(normalize(node.scriptPath))), path.join(dirPath, node.name), { recursive: true });
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
return Promise.reject(`Node ${node.name} incorrect: ${JSON.stringify(node)}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
//создаем скрипты для подсхем
|
|
58
|
+
await this.createScriptSubFlows(nodes, dirPath);
|
|
59
|
+
//записываем текст скрипта
|
|
60
|
+
const scriptStr = this.createScriptString(nodes, nodesSort, edges, parentNode);
|
|
61
|
+
const scriptPath = path.join(dirPath, this.variablesNames.defaultScriptName);
|
|
62
|
+
await fsPromises.writeFile(scriptPath, scriptStr);
|
|
63
|
+
return scriptPath;
|
|
64
|
+
}
|
|
65
|
+
//Вспомагательные методы
|
|
66
|
+
//топологическая сортировка узлов одной иерархии
|
|
67
|
+
topologicalSorting(nodes, edges) {
|
|
68
|
+
const nodeTemp = structuredClone(nodes);
|
|
69
|
+
const edgeTemp = structuredClone(edges);
|
|
70
|
+
// Формирование списка опорных узлов, у которых нет входных линий либо входные линии находятся вне его родителя
|
|
71
|
+
const supportNodes = nodeTemp.filter((node) => {
|
|
72
|
+
return !edgeTemp.some((edge) => edge.target === node.id &&
|
|
73
|
+
nodeTemp.some((sourceNode) => sourceNode.id === edge.source));
|
|
74
|
+
});
|
|
75
|
+
// Топологическая сортировка (с помощью обхода в глубину)
|
|
76
|
+
const grayStack = [];
|
|
77
|
+
const blackStack = [];
|
|
78
|
+
supportNodes.forEach((supportNode) => {
|
|
79
|
+
//Помещаем опорный узел в стек Gray
|
|
80
|
+
grayStack.push(supportNode);
|
|
81
|
+
//Поиск в глубину по узлам из стека Gray
|
|
82
|
+
let n = 0;
|
|
83
|
+
while (grayStack.length) {
|
|
84
|
+
const firstNode = grayStack[n];
|
|
85
|
+
if (firstNode) {
|
|
86
|
+
//помещаем соседний узел в стек Gray
|
|
87
|
+
const nextEdge = edgeTemp.find((edge) => !edge.visited && edge.source === firstNode.id);
|
|
88
|
+
if (nextEdge) {
|
|
89
|
+
nextEdge.visited = true;
|
|
90
|
+
const nextNode = nodeTemp.find((node) => node.id === nextEdge.target);
|
|
91
|
+
if (nextNode) {
|
|
92
|
+
if (blackStack.find((node) => node.id === nextNode.id) ||
|
|
93
|
+
grayStack.find((node) => node.id === nextNode.id)) {
|
|
94
|
+
n -= 1; // для перехода к этому же узлу
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
grayStack.push(nextNode);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
n -= 1; // для перехода к этому же узлу
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
// Если это тупик, то помещаем firstNode в стек Black
|
|
106
|
+
blackStack.push(firstNode);
|
|
107
|
+
grayStack.splice(n, 1); // и удаляем firstNode из стека Gray
|
|
108
|
+
n -= 2; // для перехода к предыдущему узлу
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
n++;
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
return blackStack.reverse();
|
|
115
|
+
}
|
|
116
|
+
//получение строки входных параметров функций узлов
|
|
117
|
+
getInputVars(node) {
|
|
118
|
+
const inputVarsArray = [];
|
|
119
|
+
//данные родительских контейнеров
|
|
120
|
+
const isChild = node.parentsId?.length && node.parentsId.length > 0 ? true : false;
|
|
121
|
+
isChild &&
|
|
122
|
+
inputVarsArray.push(`"parent_data": ${this.variablesNames.containerDataName}`);
|
|
123
|
+
//данные из линий, входящих в дескрипторы (переменные), или данные из настроек узла (текстовые константы)
|
|
124
|
+
let inputHandlesVars = [];
|
|
125
|
+
if (node.inputHandles && node.inputHandles.length !== 0) {
|
|
126
|
+
inputHandlesVars = node.inputHandles.reduce((result, handle) => {
|
|
127
|
+
if (handle.hidden) {
|
|
128
|
+
//указание константы из настроек узла
|
|
129
|
+
return [
|
|
130
|
+
...result,
|
|
131
|
+
`"${handle.name}": ${handle.value ? `"${handle.value}"` : "None"}`,
|
|
132
|
+
];
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
if (handle.connectedEdges && handle.connectedEdges.length !== 0) {
|
|
136
|
+
//указание переменной выходного узла связанной линии
|
|
137
|
+
return [
|
|
138
|
+
...result,
|
|
139
|
+
...handle.connectedEdges
|
|
140
|
+
.filter((edge) => !edge.isReplacement)
|
|
141
|
+
.map((edge) => `"${handle.name}": ${this.variablesNames.nodesVariableName}["out_handles_${edge.sourceCid}"]["${edge.sourceHandle}"]`),
|
|
142
|
+
];
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
//указание None
|
|
146
|
+
return [...result, `"${handle.name}": None`];
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}, []);
|
|
150
|
+
}
|
|
151
|
+
inputVarsArray.push(`"${this.variablesNames.inHandlesName}": {${inputHandlesVars?.length > 0 ? inputHandlesVars.join(",") : ""}}`);
|
|
152
|
+
const contentValues = `"content_values": ${node.contentValues && Object.keys(node.contentValues).length > 0
|
|
153
|
+
? JSON.stringify(node.contentValues)
|
|
154
|
+
: "{}"}`;
|
|
155
|
+
contentValues && inputVarsArray.push(contentValues);
|
|
156
|
+
//лямбда функция дочерних блоков контейнеров
|
|
157
|
+
let containerVars = "";
|
|
158
|
+
if (node.type === NodeTypes.CONTAINER) {
|
|
159
|
+
containerVars = this.getInputVarsForContainer(node, isChild);
|
|
160
|
+
}
|
|
161
|
+
containerVars && inputVarsArray.push(containerVars);
|
|
162
|
+
//мета-данные
|
|
163
|
+
if (this.metaData) {
|
|
164
|
+
const metaDataVars = `"meta_data": {${Object.entries(this.metaData)
|
|
165
|
+
.map(([key, value]) => `"${key}": "${value}"`)
|
|
166
|
+
.join(",")}}`;
|
|
167
|
+
inputVarsArray.push(metaDataVars);
|
|
168
|
+
}
|
|
169
|
+
//данные блока
|
|
170
|
+
if (node.cid) {
|
|
171
|
+
const nodeData = {
|
|
172
|
+
id: node.cid,
|
|
173
|
+
};
|
|
174
|
+
const nodeDataVars = `"node_data": {${Object.entries(nodeData)
|
|
175
|
+
.map(([key, value]) => `"${key}": "${value}"`)
|
|
176
|
+
.join(",")}}`;
|
|
177
|
+
inputVarsArray.push(nodeDataVars);
|
|
178
|
+
}
|
|
179
|
+
return `{${inputVarsArray.join(",")}}`;
|
|
180
|
+
}
|
|
181
|
+
//получение строки входных параметров функций контейнеров
|
|
182
|
+
getInputVarsForContainer(container, isChild) {
|
|
183
|
+
const containerData = `{${isChild ? `**${this.variablesNames.containerDataName}, ` : ""}**data}`;
|
|
184
|
+
return `"children": lambda data={}: ${container.cid}_children(${this.variablesNames.nodesVariableName},${containerData})`;
|
|
185
|
+
}
|
|
186
|
+
//создание функции контейнера
|
|
187
|
+
createScriptContainers(nodes, edges, mainScriptArray) {
|
|
188
|
+
nodes
|
|
189
|
+
.filter((node) => node.type === NodeTypes.CONTAINER)
|
|
190
|
+
.forEach((container) => {
|
|
191
|
+
//название функции
|
|
192
|
+
mainScriptArray.push(`def ${container.cid}_children(${this.variablesNames.nodesVariableName}, ${this.variablesNames.containerDataName}):`);
|
|
193
|
+
//входные переменные
|
|
194
|
+
//данные выходных терминалов
|
|
195
|
+
const outputStateTerminals = container.stateTerminals?.filter((terminal) => terminal.handlesType === HandleTypes.OUTPUT) || [];
|
|
196
|
+
if (outputStateTerminals.length > 0) {
|
|
197
|
+
mainScriptArray.push("\t# data output terminals");
|
|
198
|
+
outputStateTerminals.forEach((terminal) => {
|
|
199
|
+
const terminalOutHandles = terminal.outputHandles?.map((handle) => `"${handle.name}": ${this.variablesNames.containerDataName}["${handle.name}"]`) || [];
|
|
200
|
+
mainScriptArray.push(`\t${this.variablesNames.nodesVariableName}["out_handles_${terminal.cid}"] = {${terminalOutHandles.join(",")}}`);
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
//функции дочерних узлов
|
|
204
|
+
if (container.children) {
|
|
205
|
+
mainScriptArray.push("\t# functions children nodes");
|
|
206
|
+
const childrenSort = this.topologicalSorting(container.children, edges);
|
|
207
|
+
childrenSort.forEach((child) => {
|
|
208
|
+
this.createCallNodeFunction(child, nodes, mainScriptArray);
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
//данные входных терминалов
|
|
212
|
+
const inputStateTerminals = container.stateTerminals?.filter((terminal) => terminal.handlesType === HandleTypes.INPUT) || [];
|
|
213
|
+
if (inputStateTerminals.length > 0) {
|
|
214
|
+
mainScriptArray.push("\t# data input terminals");
|
|
215
|
+
inputStateTerminals.forEach((terminal) => {
|
|
216
|
+
let inputHandlesVars = [];
|
|
217
|
+
if (terminal.inputHandles && terminal.inputHandles.length !== 0) {
|
|
218
|
+
inputHandlesVars = terminal.inputHandles.map((handle) => {
|
|
219
|
+
const edge = handle.connectedEdges && handle.connectedEdges.length > 0
|
|
220
|
+
? handle.connectedEdges[0]
|
|
221
|
+
: null;
|
|
222
|
+
return `"${handle.name}": ${edge
|
|
223
|
+
? `${this.variablesNames.nodesVariableName}["out_handles_${edge.sourceCid}"]["${edge.sourceHandle}"]`
|
|
224
|
+
: "None"}`;
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
inputHandlesVars.length > 0 &&
|
|
228
|
+
mainScriptArray.push(`\t${this.variablesNames.nodesVariableName}["out_handles_${terminal.cid}"] = {${inputHandlesVars.join(",")}}`);
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
//выходные переменные
|
|
232
|
+
const dataInputStateTerminals = inputStateTerminals.reduce((result, terminal) => {
|
|
233
|
+
return [
|
|
234
|
+
...result,
|
|
235
|
+
...(terminal.inputHandles?.map((handle) => `"${handle.name}": ${this.variablesNames.nodesVariableName}["out_handles_${terminal.cid}"]["${handle.name}"]`) || []),
|
|
236
|
+
];
|
|
237
|
+
}, []);
|
|
238
|
+
mainScriptArray.push(`\treturn {${dataInputStateTerminals.join(",")}}`);
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
//создание функции подсхемы
|
|
242
|
+
async createScriptSubFlows(nodes, dirPath) {
|
|
243
|
+
const subFlows = nodes.filter((node) => node.type === NodeTypes.SUBFLOW);
|
|
244
|
+
for (const subFlow of subFlows) {
|
|
245
|
+
if (subFlow.dataSubFlow) {
|
|
246
|
+
const dirName = getSubFlowName(subFlow);
|
|
247
|
+
//создаем директорию блока подсхемы
|
|
248
|
+
await fsPromises.mkdir(path.join(dirPath, dirName));
|
|
249
|
+
//создаем скрипт
|
|
250
|
+
await this.createScript(path.join(dirPath, dirName), subFlow.dataSubFlow.nodes, subFlow.dataSubFlow.edges, subFlow);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
//создание строки вызова функции узла
|
|
255
|
+
createCallNodeFunction(node, nodes, resultArray, parentNode = undefined) {
|
|
256
|
+
const nodeName = node.type === NodeTypes.SUBFLOW ? getSubFlowName(node) : node.name;
|
|
257
|
+
let functionCallString = "";
|
|
258
|
+
if (node.type !== NodeTypes.EXTERNALSTATETERMINAL) {
|
|
259
|
+
functionCallString = `\t${this.variablesNames.nodesVariableName}["out_handles_${node.cid}"] = main_${nodeName}(${this.getInputVars(node)})`;
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
//для внешних терминалов входных портов блок схемы
|
|
263
|
+
let handleValues = "";
|
|
264
|
+
if (parentNode) {
|
|
265
|
+
handleValues = [
|
|
266
|
+
...(node.outputHandles?.map((handle) => {
|
|
267
|
+
//поиск порта у родительского узла
|
|
268
|
+
const parentHandle = parentNode.inputHandles?.find((parentHandle) => {
|
|
269
|
+
if (parentHandle.dataHandleSubFlowTerminal) {
|
|
270
|
+
if (handle.nodeСid ===
|
|
271
|
+
parentHandle.dataHandleSubFlowTerminal.nodeСid) {
|
|
272
|
+
return (handle.name ===
|
|
273
|
+
parentHandle.dataHandleSubFlowTerminal.name);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return false;
|
|
277
|
+
});
|
|
278
|
+
if (parentHandle) {
|
|
279
|
+
return `"${handle.name}": ${this.variablesNames.inputDictName}["${this.variablesNames.inHandlesName}"]["${parentHandle.name}"]`;
|
|
280
|
+
}
|
|
281
|
+
return `${handle.name}: None`;
|
|
282
|
+
}) || []),
|
|
283
|
+
...(node.inputHandles?.map((handle) => {
|
|
284
|
+
if (handle.connectedEdges && handle.connectedEdges.length !== 0) {
|
|
285
|
+
//указание переменной выходного узла связанной линии
|
|
286
|
+
const connectedEdge = handle.connectedEdges.find((edge) => !edge.isReplacement);
|
|
287
|
+
if (connectedEdge) {
|
|
288
|
+
return `"${handle.name}": ${this.variablesNames.nodesVariableName}["out_handles_${connectedEdge.sourceCid}"]["${connectedEdge.sourceHandle}"]`;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
return `"${handle.name}": None`;
|
|
292
|
+
}) || []),
|
|
293
|
+
].join(",");
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
handleValues = [
|
|
297
|
+
...(node.outputHandles || []),
|
|
298
|
+
...(node.inputHandles || []),
|
|
299
|
+
]
|
|
300
|
+
.map((handle) => `"${handle.name}": None`)
|
|
301
|
+
.join(",");
|
|
302
|
+
}
|
|
303
|
+
functionCallString = `\t${this.variablesNames.nodesVariableName}["out_handles_${node.cid}"] = { ${handleValues} }`;
|
|
304
|
+
}
|
|
305
|
+
//формирование списка входящих линий
|
|
306
|
+
const inputEdges = [
|
|
307
|
+
...(node.connectedEdges || []),
|
|
308
|
+
...(node.inputHandles || []).reduce((edges, handle) => {
|
|
309
|
+
return [...edges, ...(handle.connectedEdges || [])];
|
|
310
|
+
}, []),
|
|
311
|
+
];
|
|
312
|
+
if (inputEdges.length > 0) {
|
|
313
|
+
const sourceVariables = [];
|
|
314
|
+
for (const edge of inputEdges) {
|
|
315
|
+
const sourceNode = nodes.find((node) => node.id === edge.source);
|
|
316
|
+
if (sourceNode) {
|
|
317
|
+
if (edge.sourceHandle && sourceNode.outputHandles) {
|
|
318
|
+
const sourceHandle = sourceNode.outputHandles.find((handle) => handle.name === edge.sourceHandle);
|
|
319
|
+
if (sourceHandle) {
|
|
320
|
+
sourceVariables.push(`${this.variablesNames.nodesVariableName}["out_handles_${edge.sourceCid}"] and ${this.variablesNames.nodesVariableName}["out_handles_${edge.sourceCid}"]["${edge.sourceHandle}"]`);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
else {
|
|
324
|
+
sourceVariables.push(`${this.variablesNames.nodesVariableName}["out_handles_${edge.sourceCid}"]`);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
if (sourceVariables.length > 0) {
|
|
329
|
+
const outHandlesStopString = node.outputHandles && node.outputHandles.length > 0
|
|
330
|
+
? `{${node.outputHandles
|
|
331
|
+
.map((handle) => `"${handle.name}": StopObject()`)
|
|
332
|
+
.join(",")}}`
|
|
333
|
+
: "StopObject()";
|
|
334
|
+
resultArray.push(`\tif any(isinstance(x, StopObject) for x in [${sourceVariables.join(",")}]):`);
|
|
335
|
+
resultArray.push(`\t\t${this.variablesNames.nodesVariableName}["out_handles_${node.cid}"] = ${outHandlesStopString}`);
|
|
336
|
+
resultArray.push(`\telse:`);
|
|
337
|
+
resultArray.push(`\t${functionCallString}`);
|
|
338
|
+
}
|
|
339
|
+
else {
|
|
340
|
+
resultArray.push(functionCallString);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
else {
|
|
344
|
+
resultArray.push(functionCallString);
|
|
345
|
+
}
|
|
346
|
+
const foundNode = nodes.find((fnode) => fnode.cid === node.cid);
|
|
347
|
+
if (foundNode) {
|
|
348
|
+
foundNode.scriptLineNumber = resultArray.length;
|
|
349
|
+
}
|
|
350
|
+
this.isDebugMode &&
|
|
351
|
+
resultArray.push(`\tlog_debug_variables(${this.variablesNames.nodesVariableName})`);
|
|
352
|
+
}
|
|
353
|
+
//создание текста скрипта
|
|
354
|
+
createScriptString(nodes, nodesSort, edges, parentNode) {
|
|
355
|
+
const resultArray = [];
|
|
356
|
+
//дополнение sys.path
|
|
357
|
+
resultArray.push("# add script directory in sys.path");
|
|
358
|
+
resultArray.push("import sys");
|
|
359
|
+
resultArray.push("import os");
|
|
360
|
+
resultArray.push("sys.path.extend([d[0] for d in list(os.walk(os.path.dirname(__file__)))])");
|
|
361
|
+
resultArray.push("sys.stdout.reconfigure(encoding='utf-8')");
|
|
362
|
+
//импорт для отладки
|
|
363
|
+
if ((this.isDebugMode || this.isRunMode) && this.logDebugVariablesPath) {
|
|
364
|
+
resultArray.push("import jsonpickle");
|
|
365
|
+
resultArray.push("def log_debug_variables (variables):");
|
|
366
|
+
resultArray.push("\tdef fail_safe_func(err):");
|
|
367
|
+
resultArray.push("\t\treturn f'Unknown type'");
|
|
368
|
+
resultArray.push(`\tvariables_pickle = jsonpickle.encode(variables, unpicklable=False, fail_safe=fail_safe_func)`);
|
|
369
|
+
resultArray.push(`\twith open(${JSON.stringify(this.logDebugVariablesPath)}, "w") as file:`);
|
|
370
|
+
resultArray.push("\t\tfile.write(variables_pickle)");
|
|
371
|
+
}
|
|
372
|
+
//импорт внутренних классов для формирования скриптов
|
|
373
|
+
resultArray.push("from easy_module import StopObject");
|
|
374
|
+
//формирование основного тела скрипта
|
|
375
|
+
const nodesAndTerminals = [
|
|
376
|
+
...nodes,
|
|
377
|
+
...nodes
|
|
378
|
+
.filter((node) => node.type === NodeTypes.CONTAINER)
|
|
379
|
+
.reduce((terminals, container) => {
|
|
380
|
+
return [...terminals, ...(container.stateTerminals || [])];
|
|
381
|
+
}, []),
|
|
382
|
+
];
|
|
383
|
+
// Комментарий # import nodes functions
|
|
384
|
+
resultArray.push("# import nodes functions");
|
|
385
|
+
// Импорты скриптов узлов
|
|
386
|
+
const importsNodesFunctions = nodes
|
|
387
|
+
.filter((node) => (node.type === NodeTypes.CONTROL ||
|
|
388
|
+
node.type === NodeTypes.CONTAINER) &&
|
|
389
|
+
node.scriptPath !== undefined &&
|
|
390
|
+
node.name !== undefined)
|
|
391
|
+
.filter((node, index, arr) => arr.findIndex((n) => n.name === node.name) === index)
|
|
392
|
+
.map((node) => `from ${node.name}.${path.basename(normalize(node.scriptPath), ".py")} import main as main_${node.name}`);
|
|
393
|
+
resultArray.push(...importsNodesFunctions);
|
|
394
|
+
//импорт скриптов подсхем
|
|
395
|
+
const importsSubFlowFunctions = nodes
|
|
396
|
+
.filter((node) => node.type === NodeTypes.SUBFLOW)
|
|
397
|
+
.map((subFlow) => `from ${subFlow.name}_${subFlow.cid}.${path.basename(this.variablesNames.defaultScriptName || "script", ".py")} import main as main_${subFlow.name}_${subFlow.cid}`);
|
|
398
|
+
resultArray.push(...importsSubFlowFunctions);
|
|
399
|
+
// Комментарий # container functions
|
|
400
|
+
resultArray.push("# container functions");
|
|
401
|
+
// Описание функций контейнеров с дочерними узлами
|
|
402
|
+
this.createScriptContainers(nodes, edges, resultArray);
|
|
403
|
+
// Комментарий # main function
|
|
404
|
+
resultArray.push("# main function");
|
|
405
|
+
// Описание функции main
|
|
406
|
+
resultArray.push(`def main(${this.variablesNames.inputDictName}=None):`);
|
|
407
|
+
// Комментарий # nodes variables
|
|
408
|
+
resultArray.push("\t# nodes variables");
|
|
409
|
+
// Переменные блоков
|
|
410
|
+
const nodesVariables = nodesAndTerminals.map((node) => `"out_handles_${node.cid}": None`);
|
|
411
|
+
resultArray.push(`\t${this.variablesNames.nodesVariableName} = {${nodesVariables.join(",")}}`);
|
|
412
|
+
// Описание функций узлов первой иерархии
|
|
413
|
+
resultArray.push("\t# functions nodes");
|
|
414
|
+
nodesSort.forEach((node) => {
|
|
415
|
+
//строка функции узла
|
|
416
|
+
this.createCallNodeFunction(node, nodes, resultArray, parentNode);
|
|
417
|
+
});
|
|
418
|
+
//запись файла с переменными для режима Run
|
|
419
|
+
this.isRunMode &&
|
|
420
|
+
resultArray.push(`\tlog_debug_variables(${this.variablesNames.nodesVariableName})`);
|
|
421
|
+
//возврат портов выходных внешних терминалов
|
|
422
|
+
if (parentNode) {
|
|
423
|
+
const returnedHandlesValues = nodes
|
|
424
|
+
.filter((node) => node.type === NodeTypes.EXTERNALSTATETERMINAL)
|
|
425
|
+
.reduce((returnedValues, terminal) => {
|
|
426
|
+
return [
|
|
427
|
+
...returnedValues,
|
|
428
|
+
...(terminal.inputHandles
|
|
429
|
+
?.map((handle) => {
|
|
430
|
+
//поиск порта у родительского узла
|
|
431
|
+
const parentHandle = parentNode.outputHandles?.find((parentHandle) => {
|
|
432
|
+
if (parentHandle.dataHandleSubFlowTerminal) {
|
|
433
|
+
if (handle.nodeСid ===
|
|
434
|
+
parentHandle.dataHandleSubFlowTerminal.nodeСid) {
|
|
435
|
+
return (handle.name ===
|
|
436
|
+
parentHandle.dataHandleSubFlowTerminal.name);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
return false;
|
|
440
|
+
});
|
|
441
|
+
if (parentHandle) {
|
|
442
|
+
return `"${parentHandle.name}": ${this.variablesNames.nodesVariableName}["out_handles_${terminal.cid}"]["${handle.name}"]`;
|
|
443
|
+
}
|
|
444
|
+
return;
|
|
445
|
+
})
|
|
446
|
+
.filter((value) => value !== undefined) || []),
|
|
447
|
+
];
|
|
448
|
+
}, []);
|
|
449
|
+
if (returnedHandlesValues.length > 0) {
|
|
450
|
+
resultArray.push(`\treturn { ${returnedHandlesValues.join(",")} }`);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
// Комментарий # run main function
|
|
454
|
+
resultArray.push("# run main function");
|
|
455
|
+
// Строка запуска функции main
|
|
456
|
+
resultArray.push(`if __name__ == "__main__":`);
|
|
457
|
+
resultArray.push(`\tmain()`);
|
|
458
|
+
return resultArray.join("\n");
|
|
459
|
+
}
|
|
460
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
export type CollectionType = {
|
|
2
|
+
name: string;
|
|
3
|
+
nodes: ExternalDataNodeType[];
|
|
4
|
+
};
|
|
5
|
+
export type ExternalDataNodeType = {
|
|
6
|
+
name: string;
|
|
7
|
+
label: {
|
|
8
|
+
ru: string;
|
|
9
|
+
en: string;
|
|
10
|
+
};
|
|
11
|
+
collection: string;
|
|
12
|
+
cid: string;
|
|
13
|
+
parentsId: string[];
|
|
14
|
+
type: NodeTypes;
|
|
15
|
+
path: string;
|
|
16
|
+
scriptPath: string;
|
|
17
|
+
id: string;
|
|
18
|
+
inputHandles: ExternalDataHandleType[];
|
|
19
|
+
outputHandles: ExternalDataHandleType[];
|
|
20
|
+
stateTerminals: ExternalDataStateTerminalType[];
|
|
21
|
+
connectedEdges: ExternalDataEdgeType[];
|
|
22
|
+
contentValues?: ContentValuesType;
|
|
23
|
+
children?: ExternalDataNodeType[];
|
|
24
|
+
dataSubFlow?: ExternalDataDefaultFlowType;
|
|
25
|
+
scriptLineNumber?: number;
|
|
26
|
+
};
|
|
27
|
+
export type ExternalDataDefaultFlowType = {
|
|
28
|
+
nodes: ExternalDataNodeType[];
|
|
29
|
+
edges: ExternalDataEdgeType[];
|
|
30
|
+
};
|
|
31
|
+
export type ContentValuesType = {
|
|
32
|
+
[key: string]: string | number | {
|
|
33
|
+
[key: string]: string | number;
|
|
34
|
+
} | (string | number)[];
|
|
35
|
+
};
|
|
36
|
+
export type ExternalDataEdgeType = {
|
|
37
|
+
target: string;
|
|
38
|
+
targetCid: string;
|
|
39
|
+
targetHandle?: string;
|
|
40
|
+
source: string;
|
|
41
|
+
sourceCid: string;
|
|
42
|
+
sourceHandle?: string;
|
|
43
|
+
isReplacement: boolean;
|
|
44
|
+
};
|
|
45
|
+
export type ExternalDataStateTerminalType = {
|
|
46
|
+
inputHandles: ExternalDataHandleType[];
|
|
47
|
+
outputHandles: ExternalDataHandleType[];
|
|
48
|
+
handlesType: HandleTypes;
|
|
49
|
+
cid: string;
|
|
50
|
+
};
|
|
51
|
+
export type ExternalDataHandleType = {
|
|
52
|
+
name: string;
|
|
53
|
+
hidden: boolean;
|
|
54
|
+
value: string;
|
|
55
|
+
connectedEdges: ExternalDataEdgeType[];
|
|
56
|
+
dataHandleSubFlowTerminal?: ExternalDataHandleType;
|
|
57
|
+
nodeСid: string;
|
|
58
|
+
};
|
|
59
|
+
export declare enum HandleTypes {
|
|
60
|
+
INPUT = "input",
|
|
61
|
+
OUTPUT = "output"
|
|
62
|
+
}
|
|
63
|
+
export declare enum NodeTypes {
|
|
64
|
+
CONTROL = "control",
|
|
65
|
+
CONTAINER = "container",
|
|
66
|
+
STATETERMINAL = "stateTerminal",
|
|
67
|
+
SUBFLOW = "subFlow",
|
|
68
|
+
EXTERNALSTATETERMINAL = "externalStateTerminal"
|
|
69
|
+
}
|
|
70
|
+
export declare enum LanguageNames {
|
|
71
|
+
RU = "ru",
|
|
72
|
+
EN = "en"
|
|
73
|
+
}
|
|
74
|
+
export type VariablesNamesType = {
|
|
75
|
+
nodesVariableName: string;
|
|
76
|
+
containerDataName: string;
|
|
77
|
+
nodesJsonVariableName: string;
|
|
78
|
+
inputDictName: string;
|
|
79
|
+
inHandlesName: string;
|
|
80
|
+
defaultScriptName: string;
|
|
81
|
+
};
|
|
82
|
+
export type MetaDataType = {
|
|
83
|
+
appLanguage?: LanguageNames;
|
|
84
|
+
};
|
|
85
|
+
export type EdgeSortedType = ExternalDataEdgeType & {
|
|
86
|
+
visited?: boolean;
|
|
87
|
+
};
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export var HandleTypes;
|
|
2
|
+
(function (HandleTypes) {
|
|
3
|
+
HandleTypes["INPUT"] = "input";
|
|
4
|
+
HandleTypes["OUTPUT"] = "output";
|
|
5
|
+
})(HandleTypes || (HandleTypes = {}));
|
|
6
|
+
export var NodeTypes;
|
|
7
|
+
(function (NodeTypes) {
|
|
8
|
+
NodeTypes["CONTROL"] = "control";
|
|
9
|
+
NodeTypes["CONTAINER"] = "container";
|
|
10
|
+
NodeTypes["STATETERMINAL"] = "stateTerminal";
|
|
11
|
+
NodeTypes["SUBFLOW"] = "subFlow";
|
|
12
|
+
NodeTypes["EXTERNALSTATETERMINAL"] = "externalStateTerminal";
|
|
13
|
+
})(NodeTypes || (NodeTypes = {}));
|
|
14
|
+
export var LanguageNames;
|
|
15
|
+
(function (LanguageNames) {
|
|
16
|
+
LanguageNames["RU"] = "ru";
|
|
17
|
+
LanguageNames["EN"] = "en";
|
|
18
|
+
})(LanguageNames || (LanguageNames = {}));
|
|
19
|
+
;
|
package/dist/utils.d.ts
ADDED
package/dist/utils.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@easyrpa/script-creator",
|
|
3
|
+
"description": "Pyton script creator for easy rpa",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
10
|
+
"build": "tsc"
|
|
11
|
+
},
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git+https://github.com/trufak/-easyrpa-script-creator.git"
|
|
15
|
+
},
|
|
16
|
+
"author": "trufak",
|
|
17
|
+
"license": "ISC",
|
|
18
|
+
"bugs": {
|
|
19
|
+
"url": "https://github.com/trufak/-easyrpa-script-creator/issues"
|
|
20
|
+
},
|
|
21
|
+
"homepage": "https://github.com/trufak/-easyrpa-script-creator#readme",
|
|
22
|
+
"private": false,
|
|
23
|
+
"keywords": [
|
|
24
|
+
"easyrpa",
|
|
25
|
+
"script"
|
|
26
|
+
],
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/node": "^25.3.0"
|
|
29
|
+
},
|
|
30
|
+
"files": ["dist", "package.json"]
|
|
31
|
+
}
|