Skip to content

Commit 54dabd0

Browse files
author
John Major
committed
X
1 parent 6b22db3 commit 54dabd0

39 files changed

+3397
-0
lines changed

docs/CYTOSCAPE_CODE_REVIEW.md

Lines changed: 620 additions & 0 deletions
Large diffs are not rendered by default.

static/js/dag-explorer/.hold

Whitespace-only changes.

static/js/dag-explorer/api.js

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
/**
2+
* DAG Explorer API Module
3+
*
4+
* Handles all backend API calls with proper error handling and debouncing.
5+
*/
6+
7+
const DAGAPI = (function() {
8+
'use strict';
9+
10+
// Track pending requests for cancellation
11+
let pendingRequests = [];
12+
13+
/**
14+
* Make an AJAX request with error handling
15+
* @param {Object} options - jQuery AJAX options
16+
* @returns {Promise} Promise resolving to response data
17+
*/
18+
function request(options) {
19+
return new Promise((resolve, reject) => {
20+
const defaults = {
21+
dataType: 'json',
22+
timeout: 30000 // 30 second timeout
23+
};
24+
25+
const ajaxOptions = Object.assign({}, defaults, options, {
26+
success: function(response) {
27+
resolve(response);
28+
},
29+
error: function(xhr, status, error) {
30+
console.error(`API Error [${options.url}]:`, status, error);
31+
reject({
32+
status: xhr.status,
33+
statusText: status,
34+
error: error,
35+
response: xhr.responseText
36+
});
37+
}
38+
});
39+
40+
const xhr = $.ajax(ajaxOptions);
41+
pendingRequests.push(xhr);
42+
43+
// Clean up completed request
44+
xhr.always(function() {
45+
const index = pendingRequests.indexOf(xhr);
46+
if (index > -1) {
47+
pendingRequests.splice(index, 1);
48+
}
49+
});
50+
});
51+
}
52+
53+
/**
54+
* Cancel all pending requests
55+
*/
56+
function cancelPendingRequests() {
57+
pendingRequests.forEach(xhr => xhr.abort());
58+
pendingRequests = [];
59+
}
60+
61+
/**
62+
* Fetch DAG data for a node at specified depth
63+
* @param {string} euid - Starting node EUID
64+
* @param {number} depth - Traversal depth
65+
* @returns {Promise} Promise resolving to graph data
66+
*/
67+
function fetchDAGData(euid, depth) {
68+
return request({
69+
url: '/get_dagv2',
70+
method: 'GET',
71+
data: { euid: euid, depth: depth }
72+
});
73+
}
74+
75+
// Debounced version of fetchDAGData
76+
let debouncedFetchTimeout = null;
77+
function debouncedFetchDAGData(euid, depth, callback) {
78+
if (debouncedFetchTimeout) {
79+
clearTimeout(debouncedFetchTimeout);
80+
}
81+
debouncedFetchTimeout = setTimeout(function() {
82+
fetchDAGData(euid, depth)
83+
.then(callback)
84+
.catch(function(error) {
85+
console.error('Failed to fetch DAG data:', error);
86+
});
87+
}, DAGConfig.TIMING.DEBOUNCE_DELAY);
88+
}
89+
90+
/**
91+
* Get node information
92+
* @param {string} euid - Node EUID
93+
* @returns {Promise} Promise resolving to node info
94+
*/
95+
function getNodeInfo(euid) {
96+
return request({
97+
url: '/get_node_info',
98+
method: 'GET',
99+
data: { euid: euid }
100+
});
101+
}
102+
103+
/**
104+
* Get a specific property of a node
105+
* @param {string} euid - Node EUID
106+
* @param {string} key - Property key
107+
* @returns {Promise} Promise resolving to property value
108+
*/
109+
function getNodeProperty(euid, key) {
110+
return request({
111+
url: '/get_node_property',
112+
method: 'GET',
113+
data: { euid: euid, key: key }
114+
});
115+
}
116+
117+
/**
118+
* Calculate COGS for parent nodes
119+
* @param {string} euid - Node EUID
120+
* @returns {Promise} Promise resolving to COGS value
121+
*/
122+
function calculateCogsParents(euid) {
123+
return request({
124+
url: '/calculate_cogs_parents',
125+
method: 'GET',
126+
data: { euid: euid }
127+
}).then(function(response) {
128+
// Handle response that might be a string
129+
if (typeof response === 'string') {
130+
return DAGUtils.safeJSONParse(response, { cogs_value: 'N/A' });
131+
}
132+
return response;
133+
});
134+
}
135+
136+
/**
137+
* Calculate COGS for child nodes
138+
* @param {string} euid - Node EUID
139+
* @returns {Promise} Promise resolving to COGS value
140+
*/
141+
function calculateCogsChildren(euid) {
142+
return request({
143+
url: '/calculate_cogs_children',
144+
method: 'GET',
145+
data: { euid: euid }
146+
}).then(function(response) {
147+
if (typeof response === 'string') {
148+
return DAGUtils.safeJSONParse(response, { cogs_value: 'N/A' });
149+
}
150+
return response;
151+
});
152+
}
153+
154+
/**
155+
* Add a new edge between nodes
156+
* @param {string} parentUuid - Parent node UUID
157+
* @param {string} childUuid - Child node UUID
158+
* @returns {Promise} Promise resolving to new edge data
159+
*/
160+
function addNewEdge(parentUuid, childUuid) {
161+
return request({
162+
url: '/add_new_edge',
163+
method: 'POST',
164+
contentType: 'application/json',
165+
data: JSON.stringify({ parent_uuid: parentUuid, child_uuid: childUuid })
166+
});
167+
}
168+
169+
/**
170+
* Delete an object (node or edge)
171+
* @param {string} euid - Object EUID
172+
* @returns {Promise} Promise resolving to deletion result
173+
*/
174+
function deleteObject(euid) {
175+
return request({
176+
url: '/delete_object',
177+
method: 'POST',
178+
contentType: 'application/json',
179+
data: JSON.stringify({ euid: euid })
180+
});
181+
}
182+
183+
/**
184+
* Update the DAG with new graph data
185+
* @param {Object} graphData - Cytoscape graph JSON
186+
* @returns {Promise} Promise resolving to update result
187+
*/
188+
function updateDAG(graphData) {
189+
return request({
190+
url: '/update_dag',
191+
method: 'POST',
192+
contentType: 'application/json',
193+
data: JSON.stringify(graphData)
194+
});
195+
}
196+
197+
/**
198+
* Add a new node
199+
* @returns {Promise} Promise resolving to new node data
200+
*/
201+
function addNewNode() {
202+
return request({
203+
url: '/add_new_node',
204+
method: 'GET'
205+
});
206+
}
207+
208+
// Public API
209+
return {
210+
request: request,
211+
cancelPendingRequests: cancelPendingRequests,
212+
fetchDAGData: fetchDAGData,
213+
debouncedFetchDAGData: debouncedFetchDAGData,
214+
getNodeInfo: getNodeInfo,
215+
getNodeProperty: getNodeProperty,
216+
calculateCogsParents: calculateCogsParents,
217+
calculateCogsChildren: calculateCogsChildren,
218+
addNewEdge: addNewEdge,
219+
deleteObject: deleteObject,
220+
updateDAG: updateDAG,
221+
addNewNode: addNewNode
222+
};
223+
})();
224+
225+
// Export for use in other modules
226+
if (typeof module !== 'undefined' && module.exports) {
227+
module.exports = DAGAPI;
228+
}
229+

static/js/dag-explorer/config.js

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
/**
2+
* DAG Explorer Configuration
3+
*
4+
* Centralized configuration for the Cytoscape.js DAG visualization.
5+
* This file contains all constants, colors, timing values, and defaults.
6+
*/
7+
8+
const DAGConfig = (function() {
9+
'use strict';
10+
11+
// Timing constants (in milliseconds)
12+
const TIMING = {
13+
CLICK_RESET_DELAY: 500, // Time window for multi-click detection
14+
NODE_FLASH_DURATION: 500, // Duration of node flash effect
15+
EDGE_HIGHLIGHT_DURATION: 2000, // Duration of edge highlight effect
16+
NEIGHBORHOOD_HIGHLIGHT: 1700, // Duration of neighborhood highlight
17+
ANIMATION_DURATION: 200, // Default animation duration
18+
DEBOUNCE_DELAY: 300, // Debounce delay for API calls
19+
LAYOUT_TIMEOUT: 500, // Timeout for layout completion
20+
DOUBLE_CLICK_THRESHOLD: 500 // Time window for double-click detection
21+
};
22+
23+
// Node colors by super_type
24+
const NODE_COLORS = {
25+
container: '#8B00FF', // Purple
26+
content: '#00BFFF', // Deep Sky Blue
27+
workflow: '#00FF7F', // Spring Green
28+
workflow_step: '#ADFF2F', // Green Yellow
29+
equipment: '#FF4500', // Orange Red
30+
data: '#FFD700', // Gold
31+
actor: '#FF69B4', // Hot Pink
32+
default: 'pink' // Fallback color
33+
};
34+
35+
// Sub-type specific color overrides
36+
const SUB_TYPE_COLORS = {
37+
well: '#70658c',
38+
file_set: '#228080',
39+
plate: '#9932CC',
40+
tube: '#8B008B'
41+
};
42+
43+
// Edge colors by relationship type
44+
const EDGE_COLORS = {
45+
generic: '#ADD8E6', // Light Blue
46+
index: '#4CAF50', // Green
47+
parent_child: '#357b95', // Default teal
48+
default: 'lightgreen'
49+
};
50+
51+
// Default values
52+
const DEFAULTS = {
53+
filterLevel: 4,
54+
zoom: 4,
55+
startNode: 'AY1',
56+
layout: 'dagre',
57+
maxLabels: 10, // Maximum COGS labels on screen
58+
edgeThreshold: 1 // Default edge filter threshold
59+
};
60+
61+
// Cytoscape style definitions
62+
const STYLES = {
63+
node: {
64+
'background-color': function(ele) {
65+
return ele.data('color') || NODE_COLORS.default;
66+
},
67+
'label': 'data(id)',
68+
'text-valign': 'center',
69+
'color': '#fff',
70+
'text-outline-width': 2,
71+
'text-outline-color': '#666',
72+
'width': '60px',
73+
'height': '60px',
74+
'border-color': '#000',
75+
'border-width': '2px',
76+
'font-size': '20px'
77+
},
78+
nodeSelected: {
79+
'background-color': '#030f26',
80+
'border-color': '#f339f6',
81+
'border-width': '9px',
82+
'width': '90px',
83+
'height': '90px',
84+
'font-size': '70px'
85+
},
86+
nodeTransparent: {
87+
'opacity': 0.1
88+
},
89+
edge: {
90+
'width': 3,
91+
'line-color': function(ele) {
92+
return ele.data('color') || '#357b95';
93+
},
94+
'source-arrow-color': '#357b95',
95+
'target-arrow-color': '#357b95',
96+
'source-arrow-shape': 'triangle',
97+
'curve-style': 'bezier',
98+
'control-point-step-size': 40
99+
},
100+
edgeSelected: {
101+
'line-color': '#22b8f0',
102+
'source-arrow-color': '#357b95',
103+
'target-arrow-color': '#357b95',
104+
'width': '6px'
105+
},
106+
edgeTransparent: {
107+
'opacity': 0.1
108+
},
109+
highlighted: {
110+
'background-color': '#61bffc',
111+
'line-color': '#61bffc',
112+
'target-arrow-color': '#61bffc',
113+
'transition-property': 'background-color, line-color, target-arrow-color',
114+
'transition-duration': '0.5s'
115+
}
116+
};
117+
118+
// Valid keyboard shortcuts
119+
const VALID_KEYS = ['p', 'c', 'n', 'l'];
120+
121+
// Available layout options
122+
const LAYOUTS = ['dagre', 'cose', 'breadthfirst', 'grid', 'circle', 'random', 'concentric'];
123+
124+
// Public API
125+
return {
126+
TIMING: TIMING,
127+
NODE_COLORS: NODE_COLORS,
128+
SUB_TYPE_COLORS: SUB_TYPE_COLORS,
129+
EDGE_COLORS: EDGE_COLORS,
130+
DEFAULTS: DEFAULTS,
131+
STYLES: STYLES,
132+
VALID_KEYS: VALID_KEYS,
133+
LAYOUTS: LAYOUTS,
134+
135+
// Helper function to get node color
136+
getNodeColor: function(superType, subType) {
137+
if (subType && SUB_TYPE_COLORS[subType]) {
138+
return SUB_TYPE_COLORS[subType];
139+
}
140+
return NODE_COLORS[superType] || NODE_COLORS.default;
141+
},
142+
143+
// Helper function to get edge color
144+
getEdgeColor: function(relationshipType) {
145+
return EDGE_COLORS[relationshipType] || EDGE_COLORS.default;
146+
}
147+
};
148+
})();
149+
150+
// Export for use in other modules
151+
if (typeof module !== 'undefined' && module.exports) {
152+
module.exports = DAGConfig;
153+
}
154+

0 commit comments

Comments
 (0)