Skip to content

Commit f284541

Browse files
author
mahsa.forghani
committed
Cardinality in outputs
1 parent 1f4af1c commit f284541

File tree

1 file changed

+84
-10
lines changed

1 file changed

+84
-10
lines changed

Boxology-Interface/src/plugin/GoJSBoxologyValidation.ts

Lines changed: 84 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
import * as go from "gojs";
32

43
// --- Pattern and connection rules ---
@@ -133,17 +132,27 @@ export function setupDiagramValidation(diagram: go.Diagram) {
133132
const fromName = getNodeName(link.fromNode);
134133
const toName = getNodeName(link.toNode);
135134

136-
// Prevent invalid connection
135+
// Prevent invalid connection by rule table
137136
if (!validNext[fromName] || !validNext[fromName].includes(toName)) {
138-
// Remove the link immediately
139137
diagram.model.startTransaction("remove invalid link");
140138
(diagram.model as go.GraphLinksModel).removeLinkData(link.data);
141139
diagram.model.commitTransaction("remove invalid link");
142-
// Optionally, show a warning (you can use a toast or alert)
143-
alert("❌ Invalid connection! Edge will be removed.");
140+
alert("Invalid connection. Edge removed.");
144141
return;
145142
}
146143

144+
// Enforce process output cardinality: exactly one of symbol/model/data
145+
if (PROCESS_NODES.has(fromName) && OUTPUT_TARGETS.has(toName)) {
146+
const outCount = countProcessOutputs(link.fromNode);
147+
if (outCount > 1) {
148+
diagram.model.startTransaction("enforce process output cardinality");
149+
(diagram.model as go.GraphLinksModel).removeLinkData(link.data);
150+
diagram.model.commitTransaction("enforce process output cardinality");
151+
alert("A process can have exactly one output (symbol, model, or data).");
152+
return;
153+
}
154+
}
155+
147156
// 🎯 FIXED: Only merge nodes if they have SAME NAME AND SAME LABEL
148157
const fromLabel = link.fromNode.data.label || "";
149158
const toLabel = link.toNode.data.label || "";
@@ -171,6 +180,22 @@ export function validateGoJSDiagram(diagram: go.Diagram): string {
171180
else if (part instanceof go.Link && part.fromNode && part.toNode) links.push(part);
172181
});
173182

183+
// Cardinality issues within the selection
184+
const tooManyOutputs: string[] = [];
185+
const missingOutputs: string[] = [];
186+
187+
nodes.forEach(node => {
188+
const name = getNodeName(node);
189+
if (!PROCESS_NODES.has(name)) return;
190+
const outSel = links.filter(l =>
191+
l.fromNode === node &&
192+
l.toNode &&
193+
OUTPUT_TARGETS.has(getNodeName(l.toNode))
194+
).length;
195+
if (outSel > 1) tooManyOutputs.push(name);
196+
if (outSel === 0) missingOutputs.push(name);
197+
});
198+
174199
// Group nodes by name (logical nodes)
175200
const nodesByName: { [logicalName: string]: go.Node[] } = {};
176201
nodes.forEach(node => {
@@ -255,21 +280,29 @@ export function validateGoJSDiagram(diagram: go.Diagram): string {
255280
matchedPatterns.length > 0 &&
256281
unmatchedLogicalNodes.length === 0 &&
257282
isolatedLogicalNodes.length === 0 &&
258-
disconnectedNodes.length === 0
283+
disconnectedNodes.length === 0 &&
284+
tooManyOutputs.length === 0 &&
285+
missingOutputs.length === 0
259286
) {
260287
let summary = "✅ Valid pattern(s) detected:\n\n";
261-
for (const [pattern, logicalNodeSet] of Object.entries(matchedNodesByPattern)) {
288+
for (const [pattern] of Object.entries(matchedNodesByPattern)) {
262289
summary += `• ${pattern}\n`;
263290
}
264291
return summary;
265292
} else {
266293
let summary = "❌ Invalid pattern: Issues detected.\n\n";
267294
if (matchedPatterns.length > 0) {
268295
summary += "✅ Partial matches found:\n";
269-
for (const [pattern, logicalNodeSet] of Object.entries(matchedNodesByPattern)) {
296+
for (const [pattern] of Object.entries(matchedNodesByPattern)) {
270297
summary += ` • ${pattern}\n`;
271298
}
272299
}
300+
if (tooManyOutputs.length > 0) {
301+
summary += `\n⚠️ Processes with more than one output (symbol/model/data): ${Array.from(new Set(tooManyOutputs)).join(", ")}`;
302+
}
303+
if (missingOutputs.length > 0) {
304+
summary += `\n⚠️ Processes with no output (symbol/model/data): ${Array.from(new Set(missingOutputs)).join(", ")}`;
305+
}
273306
if (unmatchedLogicalNodes.length > 0) {
274307
summary += `\n⚠️ Unmatched logical nodes: ${unmatchedLogicalNodes.join(", ")}`;
275308
}
@@ -340,8 +373,6 @@ export function validateEntireDiagram(diagram: go.Diagram): string {
340373
getNodeName(link.toNode!)
341374
]);
342375

343-
console.log('🔗 Connections found:', edgeNameList);
344-
345376
// Check for invalid connections first
346377
const invalidConnections: string[] = [];
347378
edgeNameList.forEach(([from, to]) => {
@@ -420,6 +451,22 @@ export function validateEntireDiagram(diagram: go.Diagram): string {
420451
return node.findLinksConnected().count === 0;
421452
});
422453

454+
// Cardinality checks for whole diagram
455+
const tooManyOutputs: string[] = [];
456+
const missingOutputs: string[] = [];
457+
458+
nodes.forEach(node => {
459+
const name = getNodeName(node);
460+
if (!PROCESS_NODES.has(name)) return;
461+
let out = 0;
462+
node.findLinksOutOf().each(l => {
463+
const t = l.toNode ? getNodeName(l.toNode) : '';
464+
if (OUTPUT_TARGETS.has(t)) out++;
465+
});
466+
if (out > 1) tooManyOutputs.push(`${name} (key ${node.data.key})`);
467+
if (out === 0) missingOutputs.push(`${name} (key ${node.data.key})`);
468+
});
469+
423470
console.log('📊 Validation Results:', {
424471
matchedPatterns: matchedPatterns.length,
425472
unmatchedLogicalNodes: unmatchedLogicalNodes.length,
@@ -496,6 +543,20 @@ export function validateEntireDiagram(diagram: go.Diagram): string {
496543
summary += "\n";
497544
}
498545

546+
// Cardinality issues
547+
const hasCardinalityIssues = tooManyOutputs.length > 0 || missingOutputs.length > 0;
548+
549+
if (hasCardinalityIssues) {
550+
summary += `⚠️ CARDINALITY ISSUES:\n`;
551+
if (tooManyOutputs.length > 0) {
552+
summary += ` • Processes with more than one output: ${tooManyOutputs.join(", ")}\n`;
553+
}
554+
if (missingOutputs.length > 0) {
555+
summary += ` • Processes with no output: ${missingOutputs.join(", ")}\n`;
556+
}
557+
summary += "\n";
558+
}
559+
499560
// Recommendations
500561
summary += `💡 RECOMMENDATIONS:\n`;
501562
if (status === "VALID") {
@@ -515,4 +576,17 @@ export function validateEntireDiagram(diagram: go.Diagram): string {
515576

516577
console.log(`🏁 Final status: ${status}`);
517578
return summary;
579+
}
580+
581+
const PROCESS_NODES = new Set(['training', 'engineering', 'transform', 'deduce']);
582+
const OUTPUT_TARGETS = new Set(['symbol', 'model', 'data']);
583+
584+
function countProcessOutputs(node: go.Node): number {
585+
if (!node) return 0;
586+
let n = 0;
587+
node.findLinksOutOf().each(l => {
588+
const tgt = l.toNode ? getNodeName(l.toNode) : '';
589+
if (OUTPUT_TARGETS.has(tgt)) n++;
590+
});
591+
return n;
518592
}

0 commit comments

Comments
 (0)