Initial Commit

This commit is contained in:
Nicole Rappe 2025-03-28 21:17:56 -06:00
parent bf88fe7cef
commit fce8b7c911
9 changed files with 761 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
.vs/
Borealis-Workflow-Automation-Tool/

View File

@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="Borealis"
content="Workflow Automation Tool"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>Borealis</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

139
Data/WebUI/src/App.js Normal file
View File

@ -0,0 +1,139 @@
import React from "react";
import FlowEditor from "./components/FlowEditor";
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
import {
AppBar,
Toolbar,
Typography,
Box,
Menu,
MenuItem,
Button,
CssBaseline,
ThemeProvider,
createTheme
} from "@mui/material";
const darkTheme = createTheme({
palette: {
mode: "dark",
background: {
default: "#121212",
paper: "#1e1e1e"
},
text: {
primary: "#ffffff"
}
}
});
export default function App() {
const [workflowsAnchorEl, setWorkflowsAnchorEl] = React.useState(null);
const [aboutAnchorEl, setAboutAnchorEl] = React.useState(null);
const handleWorkflowsMenuOpen = (event) => {
setWorkflowsAnchorEl(event.currentTarget);
};
const handleAboutMenuOpen = (event) => {
setAboutAnchorEl(event.currentTarget);
};
const handleWorkflowsMenuClose = () => {
setWorkflowsAnchorEl(null);
};
const handleAboutMenuClose = () => {
setAboutAnchorEl(null);
};
return (
<ThemeProvider theme={darkTheme}>
<CssBaseline />
{/*
Main container that:
- fills 100% viewport height
- organizes content with flexbox (vertical)
*/}
<Box display="flex" flexDirection="column" height="100vh">
{/* --- TOP BAR --- */}
<AppBar position="static" sx={{ bgcolor: "#092c44" }}>
<Toolbar>
<Typography variant="h6" sx={{ flexGrow: 1 }}>
Borealis - Workflow Automation Tool
</Typography>
{/* Workflows Menu */}
<Button
color="inherit"
onClick={handleWorkflowsMenuOpen}
endIcon={<KeyboardArrowDownIcon />}
>
Workflows
</Button>
<Menu
anchorEl={workflowsAnchorEl}
open={Boolean(workflowsAnchorEl)}
onClose={handleWorkflowsMenuClose}
>
<MenuItem onClick={handleWorkflowsMenuClose}>Save Workflow</MenuItem>
<MenuItem onClick={handleWorkflowsMenuClose}>Load Workflow</MenuItem>
<MenuItem onClick={handleWorkflowsMenuClose}>Close Workflow</MenuItem>
</Menu>
{/* About Menu */}
<Button
color="inherit"
onClick={handleAboutMenuOpen}
endIcon={<KeyboardArrowDownIcon />}
>
About
</Button>
<Menu
anchorEl={aboutAnchorEl}
open={Boolean(aboutAnchorEl)}
onClose={handleAboutMenuClose}
>
<MenuItem onClick={handleAboutMenuClose}>Gitea Project</MenuItem>
<MenuItem onClick={handleAboutMenuClose}>Credits</MenuItem>
</Menu>
</Toolbar>
</AppBar>
{/* --- REACT FLOW EDITOR --- */}
{/*
flexGrow={1} This box expands to fill remaining vertical space
overflow="hidden" No scroll bars, so React Flow does internal panning
mt: 1 Add top margin so the gradient starts closer to the AppBar.
*/}
<Box flexGrow={1} overflow="hidden" sx={{ mt: 0 }}>
<FlowEditor
updateNodeCount={(count) => {
document.getElementById("nodeCount").innerText = count;
}}
/>
</Box>
{/* --- STATUS BAR at BOTTOM --- */}
<Box
component="footer"
sx={{
bgcolor: "#1e1e1e",
color: "white",
px: 2,
py: 1,
textAlign: "left"
}}
>
<b>Nodes</b>: <span id="nodeCount">0</span> | <b>Update Rate</b>: 500ms | <b>Flask API Server:</b>{" "}
<a
href="http://127.0.0.1:5000/api/nodes"
style={{ color: "#3c78b4" }}
>
http://127.0.0.1:5000/data/api/nodes
</a>
</Box>
</Box>
</ThemeProvider>
);
}

View File

@ -0,0 +1,23 @@
/* FlowEditor background container */
.flow-editor-container {
position: relative;
width: 100vw;
height: 100vh;
}
/* Blue Gradient Overlay */
.flow-editor-container::before {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none; /* Ensures grid and nodes remain fully interactive */
background: linear-gradient( to bottom, rgba(9, 44, 68, 0.9) 0%, /* Deep blue at the top */
rgba(30, 30, 30, 0) 45%, /* Fade out towards center */
rgba(30, 30, 30, 0) 75%, /* No gradient in the middle */
rgba(9, 44, 68, 0.7) 100% /* Deep blue at the bottom */
);
z-index: -1; /* Ensures it stays behind the React Flow elements */
}

View File

@ -0,0 +1,68 @@
import React, { useState, useEffect, useCallback } from "react";
import ReactFlow, {
addEdge,
Controls,
Background,
} from "reactflow";
import "reactflow/dist/style.css";
import "./FlowEditor.css";
const fetchNodes = async () => {
const response = await fetch("/api/workflow");
return response.json();
};
const saveWorkflow = async (workflow) => {
await fetch("/api/workflow", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(workflow),
});
};
export default function FlowEditor() {
const [elements, setElements] = useState([]);
useEffect(() => {
fetchNodes().then((data) => {
// Data should contain nodes and edges arrays
const newElements = [...data.nodes, ...data.edges];
setElements(newElements);
});
}, []);
const onConnect = useCallback(
(params) => {
const newEdge = { id: `e${params.source}-${params.target}`, ...params };
setElements((els) => [...els, newEdge]);
// Separate nodes/edges for saving:
const nodes = elements.filter((el) => el.type);
const edges = elements.filter((el) => !el.type);
saveWorkflow({
nodes,
edges: [...edges, newEdge],
});
},
[elements]
);
return (
<div className="flow-editor-container">
<ReactFlow
proOptions={{ hideAttribution: true }} // Remove the React Flow watermark
elements={elements}
onConnect={onConnect}
>
<Controls />
<Background
variant="lines"
gap={100}
size={1}
color="rgba(255, 255, 255, 0.2)" // White grid lines at 20% opacity
/>
</ReactFlow>
</div>
);
}

131
Data/server.py Normal file
View File

@ -0,0 +1,131 @@
from flask import Flask, send_from_directory, jsonify, request, abort
import os
import importlib
import inspect
import uuid
from OdenGraphQt import BaseNode
# Determine the absolute path for the React build folder
build_folder = os.path.join(os.getcwd(), "web-interface", "build")
if not os.path.exists(build_folder):
print("WARNING: web-interface build folder not found. Please build your React app.")
app = Flask(__name__, static_folder=build_folder, static_url_path="/")
# Directory where nodes are stored
NODES_PACKAGE = "Nodes"
# In-memory workflow storage
workflow_data = {
"nodes": [],
"edges": [] # Store connections separately
}
def import_nodes_from_folder(package_name):
"""Dynamically import node classes from the given package and list them."""
nodes_by_category = {}
package = importlib.import_module(package_name)
package_path = package.__path__[0]
for root, _, files in os.walk(package_path):
rel_path = os.path.relpath(root, package_path).replace(os.sep, ".")
module_prefix = f"{package_name}.{rel_path}" if rel_path != "." else package_name
category_name = os.path.basename(root)
for file in files:
if file.endswith(".py") and file != "__init__.py":
module_name = f"{module_prefix}.{file[:-3]}"
try:
module = importlib.import_module(module_name)
for name, obj in inspect.getmembers(module, inspect.isclass):
if issubclass(obj, BaseNode) and obj.__module__ == module.__name__:
if category_name not in nodes_by_category:
nodes_by_category[category_name] = []
nodes_by_category[category_name].append(obj.NODE_NAME)
except Exception as e:
print(f"Failed to import {module_name}: {e}")
return nodes_by_category
@app.route("/")
def serve_frontend():
"""Serve the React app."""
index_path = os.path.join(build_folder, "index.html")
if os.path.exists(index_path):
return send_from_directory(app.static_folder, "index.html")
return "<h1>Borealis React App Code Not Found</h1><p>Please re-deploy Borealis Workflow Automation Tool</p>", 404
@app.route("/api/nodes", methods=["GET"])
def get_available_nodes():
"""Return available node types."""
nodes = import_nodes_from_folder(NODES_PACKAGE)
return jsonify(nodes)
@app.route("/api/workflow", methods=["GET", "POST"])
def handle_workflow():
"""Retrieve or update the workflow."""
global workflow_data
if request.method == "GET":
return jsonify(workflow_data)
elif request.method == "POST":
data = request.get_json()
if not data:
abort(400, "Invalid workflow data")
workflow_data = data
return jsonify({"status": "success", "workflow": workflow_data})
@app.route("/api/node", methods=["POST"])
def create_node():
"""Create a new node with a unique UUID."""
data = request.get_json()
if not data or "nodeType" not in data:
abort(400, "Invalid node data")
node_id = str(uuid.uuid4()) # Generate a unique ID
node = {
"id": node_id,
"type": data["nodeType"],
"position": data.get("position", {"x": 100, "y": 100}),
"properties": data.get("properties", {})
}
workflow_data["nodes"].append(node)
return jsonify({"status": "success", "node": node})
@app.route("/api/node/<string:node_id>", methods=["PUT", "DELETE"])
def modify_node(node_id):
"""Update or delete a node."""
global workflow_data
if request.method == "PUT":
data = request.get_json()
for node in workflow_data["nodes"]:
if node["id"] == node_id:
node["position"] = data.get("position", node["position"])
node["properties"] = data.get("properties", node["properties"])
return jsonify({"status": "success", "node": node})
abort(404, "Node not found")
elif request.method == "DELETE":
workflow_data["nodes"] = [n for n in workflow_data["nodes"] if n["id"] != node_id]
return jsonify({"status": "success", "deletedNode": node_id})
@app.route("/api/edge", methods=["POST"])
def create_edge():
"""Create a new connection (edge) between nodes."""
data = request.get_json()
if not data or "source" not in data or "target" not in data:
abort(400, "Invalid edge data")
edge_id = str(uuid.uuid4())
edge = {"id": edge_id, "source": data["source"], "target": data["target"]}
workflow_data["edges"].append(edge)
return jsonify({"status": "success", "edge": edge})
@app.route("/api/edge/<string:edge_id>", methods=["DELETE"])
def delete_edge(edge_id):
"""Delete an edge by ID."""
global workflow_data
workflow_data["edges"] = [e for e in workflow_data["edges"] if e["id"] != edge_id]
return jsonify({"status": "success", "deletedEdge": edge_id})
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000, debug=False)

149
Launch-Borealis.ps1 Normal file
View File

@ -0,0 +1,149 @@
# Start_Windows - WebServer.ps1
# Run this script with:
# Set-ExecutionPolicy Unrestricted -Scope Process; .\Start_Windows -WebServer.ps1
# ---------------------- Initialization & Visuals ----------------------
$symbols = @{
Success = [char]0x2705
Running = [char]0x23F3
Fail = [char]0x274C
Info = [char]0x2139
}
function Write-ProgressStep {
param (
[string]$Message,
[string]$Status = $symbols["Info"] # Ensure proper lookup
)
Write-Host "`r$Status $Message... " -NoNewline
}
function Run-Step {
param (
[string]$Message,
[scriptblock]$Script
)
Write-ProgressStep -Message $Message -Status "$($symbols.Running)"
try {
& $Script
if ($LASTEXITCODE -eq 0 -or $?) {
Write-Host "`r$($symbols.Success) $Message " # Fix symbol lookup
} else {
throw "Non-zero exit code"
}
} catch {
Write-Host "`r$($symbols.Fail) $Message - Failed: $_ " -ForegroundColor Red
exit 1
}
}
Clear-Host
Write-Host "Deploying Borealis - Workflow Automation Tool..." -ForegroundColor Green
Write-Host "===================================================================================="
# ---------------------- Node.js Check ----------------------
if (-not (Get-Command node -ErrorAction SilentlyContinue)) {
Write-Host "`r$($symbols.Fail) Node.js is not installed. Please install Node.js and try again." -ForegroundColor Red
exit 1
}
# ---------------------- Path Definitions ----------------------
$venvFolder = "Borealis-Workflow-Automation-Tool"
$dataSource = "Data"
$dataDestination = "$venvFolder\Borealis"
$customUIPath = "$dataSource\WebUI"
$webUIDestination = "$venvFolder\web-interface"
# ---------------------- Create Python Virtual Environment ----------------------
Run-Step "Create Virtual Python Environment" {
if (!(Test-Path "$venvFolder\Scripts\Activate")) {
python -m venv $venvFolder | Out-Null
}
}
# ---------------------- Copy Server Data ----------------------
Run-Step "Copy Borealis Server Data into Virtual Python Environment" {
if (Test-Path $dataSource) {
if (Test-Path $dataDestination) {
Remove-Item -Recurse -Force $dataDestination | Out-Null
}
New-Item -Path $dataDestination -ItemType Directory -Force | Out-Null
Copy-Item -Path "$dataSource\*" -Destination $dataDestination -Recurse
} else {
Write-Host "`r$($symbols.Info) Warning: Data folder not found, skipping copy." -ForegroundColor Yellow
}
}
# ---------------------- React UI Deployment ----------------------
Run-Step "Create a new ReactJS App in $webUIDestination" {
if (-not (Test-Path $webUIDestination)) {
npx create-react-app $webUIDestination | Out-Null
}
}
Run-Step "Overwrite ReactJS App Files with Borealis ReactJS Files" {
if (Test-Path $customUIPath) {
Copy-Item -Path "$customUIPath\*" -Destination $webUIDestination -Recurse -Force
} else {
Write-Host "`r$($symbols.Info) No custom UI found, using default React app." -ForegroundColor Yellow
}
}
Run-Step "Remove Existing ReactJS Build Folder (If Exists)" {
if (Test-Path "$webUIDestination\build") {
Remove-Item -Path "$webUIDestination\build" -Recurse -Force
}
}
# ---------------------- Activate Python Virtual Environment ----------------------
Run-Step "Activate Virtual Python Environment" {
. "$venvFolder\Scripts\Activate"
}
# ---------------------- Install Python Dependencies ----------------------
Run-Step "Install Python Dependencies into Virtual Python Environment" {
if (Test-Path "requirements.txt") {
pip install -q -r requirements.txt 2>&1 | Out-Null
} else {
Write-Host "`r$($symbols.Info) No requirements.txt found, skipping Python packages." -ForegroundColor Yellow
}
}
# ---------------------- Build React App ----------------------
Run-Step "Install NPM into ReactJS App" {
$packageJsonPath = Join-Path $webUIDestination "package.json"
if (Test-Path $packageJsonPath) {
Push-Location $webUIDestination
$env:npm_config_loglevel = "silent"
npm install --silent --no-fund --audit=false 2>&1 | Out-Null
Pop-Location
}
}
Run-Step "Install React Flow into ReactJS App" {
Push-Location $webUIDestination
npm install reactflow --no-fund --audit=false | Out-Null
Pop-Location
}
Run-Step "Install Material UI Libraries into ReactJS App" {
Push-Location $webUIDestination
$env:npm_config_loglevel = "silent" # Force NPM to be completely silent
npm install --silent @mui/material @mui/icons-material @emotion/react @emotion/styled --no-fund --audit=false 2>&1 | Out-Null
Pop-Location
}
Run-Step "Build ReactJS App" {
Push-Location $webUIDestination
npm run build | Out-Null
Pop-Location
}
# ---------------------- Launch Flask Server ----------------------
Push-Location $venvFolder
Write-Host "`nLaunching Borealis..." -ForegroundColor Green
Write-Host "===================================================================================="
Write-Host "$($symbols.Running) Starting the Python Flask server..." -NoNewline
python "Borealis\server.py"
Write-Host "`r$($symbols.Success) Borealis Launched Successfully!"
Pop-Location

185
Launch-Borealis.sh Normal file
View File

@ -0,0 +1,185 @@
#!/usr/bin/env bash
# --------------------------------------------------------------------
# Deploying Borealis - Workflow Automation Tool
#
# This script deploys the Borealis Workflow Automation Tool by:
# - Detecting the Linux distro and installing required system dependencies.
# - Creating a Python virtual environment.
# - Copying server data.
# - Setting up a React UI application.
# - Installing Python and Node dependencies.
# - Building the React app.
# - Launching the Flask server.
#
# Usage:
# chmod +x deploy_borealis.sh
# ./deploy_borealis.sh
# --------------------------------------------------------------------
# ---------------------- Initialization & Visuals ----------------------
GREEN="\033[0;32m"
YELLOW="\033[1;33m"
RED="\033[0;31m"
RESET="\033[0m"
CHECKMARK="✅"
HOURGLASS="⏳"
CROSSMARK="❌"
INFO=""
# Function to run a step with progress visuals and error checking
run_step() {
local message="$1"
shift
echo -ne "${HOURGLASS} ${message}... "
if "$@"; then
echo -e "\r${CHECKMARK} ${message}"
else
echo -e "\r${CROSSMARK} ${message} - Failed${RESET}"
exit 1
fi
}
echo -e "${GREEN}Deploying Borealis - Workflow Automation Tool...${RESET}"
echo "===================================================================================="
# ---------------------- Detect Linux Distribution ----------------------
detect_distro() {
# This function detects the Linux distribution by sourcing /etc/os-release.
if [ -f /etc/os-release ]; then
. /etc/os-release
DISTRO_ID=$ID
else
DISTRO_ID="unknown"
fi
echo -e "${INFO} Detected OS: ${DISTRO_ID}"
}
detect_distro
# ---------------------- Install System Dependencies ----------------------
install_core_dependencies() {
# Install required packages based on detected Linux distribution.
case "$DISTRO_ID" in
ubuntu|debian)
sudo apt update -qq
sudo apt install -y python3 python3-venv python3-pip nodejs npm git curl
;;
rhel|centos|fedora|rocky)
# For Fedora and similar distributions, the venv module is built-in so we omit python3-venv.
sudo dnf install -y python3 python3-pip nodejs npm git curl
;;
arch)
sudo pacman -Sy --noconfirm python python-venv python-pip nodejs npm git curl
;;
*)
echo -e "${RED}${CROSSMARK} Unsupported Linux distribution: ${DISTRO_ID}${RESET}"
exit 1
;;
esac
}
run_step "Install System Dependencies" install_core_dependencies
# ---------------------- Path Setup ----------------------
# Variables and path definitions
venvFolder="Borealis-Workflow-Automation-Tool"
dataSource="Data"
dataDestination="${venvFolder}/Borealis"
customUIPath="${dataSource}/WebUI"
webUIDestination="${venvFolder}/web-interface"
# ---------------------- Create Python Virtual Environment ----------------------
run_step "Create Virtual Python Environment" bash -c "
# Check if virtual environment already exists; if not, create one.
if [ ! -f '${venvFolder}/bin/activate' ]; then
python3 -m venv '${venvFolder}'
fi
"
# ---------------------- Copy Borealis Data ----------------------
run_step "Copy Borealis Server Data into Virtual Python Environment" bash -c "
# If the Data folder exists, remove any existing server data folder and copy fresh data.
if [ -d \"$dataSource\" ]; then
rm -rf \"$dataDestination\"
mkdir -p \"$dataDestination\"
cp -r \"$dataSource/\"* \"$dataDestination\"
else
echo -e \"\r${INFO} Warning: Data folder not found, skipping copy.${RESET}\"
fi
true
"
# ---------------------- React UI Setup ----------------------
run_step "Create a new ReactJS App in ${webUIDestination}" bash -c "
# Create a React app if the destination folder does not exist.
if [ ! -d \"$webUIDestination\" ]; then
# Set CI=true and add --loglevel=error to suppress funding and audit messages
CI=true npx create-react-app \"$webUIDestination\" --silent --use-npm --loglevel=error
fi
"
run_step "Overwrite React App with Custom Files" bash -c "
# If custom UI files exist, copy them into the React app folder.
if [ -d \"$customUIPath\" ]; then
cp -r \"$customUIPath/\"* \"$webUIDestination\"
else
echo -e \"\r${INFO} No custom UI found, using default React app.${RESET}\"
fi
true
"
run_step "Remove Existing React Build (if any)" bash -c "
# Remove the build folder if it exists to ensure a fresh build.
if [ -d \"$webUIDestination/build\" ]; then
rm -rf \"$webUIDestination/build\"
fi
true
"
# ---------------------- Activate Python Virtual Environment ----------------------
# Activate the Python virtual environment for subsequent commands.
source "${venvFolder}/bin/activate"
# ---------------------- Install Python Dependencies ----------------------
run_step "Install Python Dependencies into Virtual Python Environment" bash -c "
# Install Python packages if a requirements.txt file is present.
if [ -f \"requirements.txt\" ]; then
pip install -q -r requirements.txt
else
echo -e \"\r${INFO} No requirements.txt found, skipping Python packages.${RESET}\"
fi
true
"
# ---------------------- Install Node Dependencies & Build React UI ----------------------
run_step "Install React App Dependencies" bash -c "
# Install npm dependencies if package.json exists.
if [ -f \"$webUIDestination/package.json\" ]; then
cd \"$webUIDestination\"
# Add --loglevel=error to suppress npm's funding and audit messages
npm install --silent --no-fund --audit=false --loglevel=error
cd -
fi
"
run_step "Install React Flow and UI Libraries" bash -c "
# Install additional React libraries.
cd \"$webUIDestination\"
npm install reactflow --silent --no-fund --audit=false --loglevel=error
npm install --silent @mui/material @mui/icons-material @emotion/react @emotion/styled --no-fund --audit=false --loglevel=error
cd -
"
run_step "Build React App" bash -c "
# Build the React app to create production-ready files.
cd \"$webUIDestination\"
npm run build --silent --loglevel=error
cd -
"
# ---------------------- Launch Flask Server ----------------------
cd "${venvFolder}"
echo -e "\n${GREEN}Launching Borealis...${RESET}"
echo "===================================================================================="
echo -ne "${HOURGLASS} Starting Flask server... "
python3 Borealis/server.py
echo -e "\r${CHECKMARK} Borealis Launched Successfully!"

21
requirements.txt Normal file
View File

@ -0,0 +1,21 @@
# PyTorch and related deep learning libraries (GPU Supported Functionality)
torch --index-url https://download.pytorch.org/whl/cu121
torchvision --index-url https://download.pytorch.org/whl/cu121
torchaudio --index-url https://download.pytorch.org/whl/cu121
# Flask for API handling
Flask
requests
# GUI-related dependencies (Qt for GUI components)
Qt.py
qtpy
OdenGraphQt
PyQt5
# Computer Vision & OCR dependencies
numpy # Numerical operations
opencv-python # Computer vision processing
pytesseract # OCR engine
easyocr # Deep-learning-based OCR
Pillow # Image processing