Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GUI: add task control for int, float, string and structure #295

Merged
merged 11 commits into from
Nov 12, 2024
Merged
16 changes: 13 additions & 3 deletions aiida_workgraph/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -696,14 +696,20 @@ def process_properties(task: Dict) -> Dict:
for name, prop in task["properties"].items():
identifier = prop["identifier"]
value = prop.get("value")
result[name] = get_raw_value(identifier, value)
result[name] = {
"identifier": identifier,
"value": get_raw_value(identifier, value),
}
#
for name, input in task["inputs"].items():
if input["property"] is not None:
prop = input["property"]
identifier = prop["identifier"]
value = prop.get("value")
result[name] = get_raw_value(identifier, value)
result[name] = {
"identifier": identifier,
"value": get_raw_value(identifier, value),
}

return result

Expand All @@ -722,7 +728,11 @@ def workgraph_to_short_json(
#
for name, task in wgdata["tasks"].items():
# Add required inputs to nodes
inputs = [{"name": name} for name in task["inputs"] if name in task["args"]]
inputs = [
{"name": name, "identifier": input["identifier"]}
for name, input in task["inputs"].items()
if name in task["args"]
]
properties = process_properties(task)
wgdata_short["nodes"][name] = {
"label": task["name"],
Expand Down
30 changes: 15 additions & 15 deletions aiida_workgraph/web/frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion aiida_workgraph/web/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"three": "^0.156.1",
"typescript": "^4.9.5",
"vis-timeline": "^7.7.3",
"weas": "^0.1.6",
"weas": "^0.1.31",
"web-vitals": "^2.1.4",
"web-worker": "^1.2.0"
},
Expand Down
47 changes: 38 additions & 9 deletions aiida_workgraph/web/frontend/src/components/WorkGraphItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { useState, useEffect, useRef, useMemo} from 'react';
import { useParams } from 'react-router-dom';
import '../App.css';
import '../rete.css';
import { createEditor } from '../rete/default';
import { createEditor, addControls, removeControls } from '../rete/default';
import { Button, Switch } from "antd";
import WorkGraphIndicator from './WorkGraphIndicator'; // Import the WorkGraphIndicator component
import WorkGraphSummary from './WorkGraphSummary';
Expand Down Expand Up @@ -81,6 +81,7 @@ function WorkGraph() {
const [workgraphHierarchy, setWorkGraphHierarchy] = useState([]);
const [selectedView, setSelectedView] = useState('Editor');
const [realtimeSwitch, setRealtimeSwitch] = useState(false); // State to manage the realtime switch
const [detailNodeViewSwitch, setDetailNodeViewSwitch] = useState(false); // State to manage the realtime switch

// Fetch state data from the backend
const fetchStateData = async () => {
Expand Down Expand Up @@ -149,7 +150,25 @@ function WorkGraph() {
clearInterval(intervalId); // Clear the interval when the component unmounts or the switch is turned off
}
};
}, [realtimeSwitch, pk, editor]); // Depend on the realtimeSwitch, pk, and editor
}, [realtimeSwitch]); // Depend on the realtimeSwitch, pk, and editor

// Setup interval for fetching real-time data when the switch is turned on
useEffect(() => {
if (editor) {
if (detailNodeViewSwitch) {
console.log('Adding controls');
addControls(editor.editor, editor.area, workgraphData);
}
else {
console.log('Removing controls');
removeControls(editor.editor, editor.area, workgraphData);
}
// need to call layout to update the view
editor?.layout(true);
}
return () => {
};
}, [detailNodeViewSwitch]); // Depend on the realtimeSwitch, pk, and editor


// Fetch workgraph data from the API
Expand Down Expand Up @@ -263,13 +282,23 @@ function WorkGraph() {
<WorkGraphIndicator parentWorkGraphs={workgraphHierarchy} />
<EditorContainer>
<LayoutAction>
<div>
<Switch
checked={realtimeSwitch}
onChange={(checked) => setRealtimeSwitch(checked)}
style={{ marginRight: '10px' }}
/>
<label>Real-time state</label>
<div style={{ display: 'flex', alignItems: 'center' }}>
<div style={{ marginRight: '20px' }}>
<Switch
checked={realtimeSwitch}
onChange={(checked) => setRealtimeSwitch(checked)}
style={{ marginRight: '10px' }}
className="realtime-switch"
/>Real-time state
</div>
<div>
<Switch
checked={detailNodeViewSwitch}
onChange={(checked) => setDetailNodeViewSwitch(checked)}
style={{ marginRight: '10px' }}
className="detail-switch"
/>Detail node view
</div>
</div>
<div>
<ToastContainer />
Expand Down
127 changes: 127 additions & 0 deletions aiida_workgraph/web/frontend/src/rete/AtomsItem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import React, { useEffect, useRef } from 'react';
import { Atoms, WEAS } from 'weas';

// Define the useNoDrag hook
function useNoDrag(ref, disabled = false) {
useEffect(() => {
const handleClick = (e) => {
if (disabled) return;

e.stopPropagation(); // Stop the event from bubbling up to prevent dragging
};
const el = ref.current;
el?.addEventListener('pointerdown', handleClick);

return () => {
el?.removeEventListener('pointerdown', handleClick);
};
}, [ref, disabled]); // Re-run when ref or disabled changes
}

function AtomsItem({ data }) {
const weasContainerRef = useRef(null);
useNoDrag(weasContainerRef); // Apply the useNoDrag hook


// Convert AiiDA structure data to the format expected by Atoms
function structureToAtomsData(inputData) {
const data = {
cell: inputData.cell,
pbc: [inputData.pbc1, inputData.pbc2, inputData.pbc3],
species: {},
symbols: [],
positions: []
};

// Process kinds to fill species information
inputData.kinds.forEach((kind, index) => {
data.species[kind.name] = kind.symbols[0]; // Assuming atomic number is the index + 1 for simplicity
});

// Process sites to fill positions and symbols
inputData.sites.forEach(site => {
data.symbols.push(site.kind_name); // Using index + 1 as a stand-in for atomic number
data.positions.push(site.position);
});

return data;
}


// Convert AiiDA structure data to the format expected by Atoms
function aseAtomsToAtomsData(inputData) {
const data = {
cell: inputData.cell,
pbc: inputData.pbc,
symbols: inputData.symbols,
positions: inputData.positions
};

return data;
}

useEffect(() => {

// console.log("atoms control data: ", data)
data = data.data;
let atomsData = {};
if (data.node_type === 'data.core.structure.StructureData.') {
atomsData = structureToAtomsData(data)
} else if (data.node_type === 'data.workgraph.ase.atoms.Atoms.AtomsData.') {
atomsData = aseAtomsToAtomsData(data)
}
// console.log("atoms control data: ", atomsData);
const atoms = new Atoms(atomsData);

if (weasContainerRef.current) {
const defaultGuiConfig = {
controls: {
enabled: false,
atomsControl: false,
colorControl: false,
cameraControls: false,
},
buttons: {
enabled: true,
fullscreen: true,
// undo: false,
// redo: false,
download: false,
// measurement: false,
}
};

function preventEventPropagation(element) {
const stopPropagation = (e) => e.stopPropagation();
["click", "keydown", "keyup", "keypress"].forEach((eventType) => {
element.addEventListener(eventType, stopPropagation, false);
});
}

let domElement = document.createElement("div");
domElement.style.cssText = "position: relative; width: 195px; height: 195px; border: 1px solid black;";
weasContainerRef.current.appendChild(domElement);

// Create an instance of AtomsViewer and pass the Atoms object to it
preventEventPropagation(domElement);
const editor = new WEAS({domElement: domElement, guiConfig: defaultGuiConfig});

editor.avr.atoms = atoms;
editor.render();

// Cleanup function to be called when the component unmounts
return () => {
// viewer.destroy();
};
}
}, [data]); // Include data in the dependency array

return (
<div>
{/* <h1>Atoms Viewer</h1> */}
<div ref={weasContainerRef} style={{ position: "relative", width: '200px', height: '200px' }}></div>
</div>
);
}

export default AtomsItem;
Loading
Loading