Skip to content

Commit

Permalink
Merge pull request #135 from MichaelPesce/update-output-page
Browse files Browse the repository at this point in the history
Update output page display
  • Loading branch information
MichaelPesce authored Oct 3, 2024
2 parents b5c8bda + 40cd497 commit f218ab4
Show file tree
Hide file tree
Showing 8 changed files with 228 additions and 157 deletions.
14 changes: 12 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,12 @@ jobs:
name: E2E testing (${{ matrix.os }})

runs-on: ${{ matrix.os-version }}

strategy:
fail-fast: false
matrix:
os:
- linux
- win64
# - macos
include:
- os: linux
os-version: ubuntu-latest
Expand All @@ -42,6 +40,11 @@ jobs:
activate-environment: watertap-ui-env
miniforge-version: latest

- name: Set up debug logging
run: |
echo "ACTIONS_RUNNER_DEBUG=true" >> $GITHUB_ENV
echo "ACTIONS_STEP_DEBUG=true" >> $GITHUB_ENV
- name: Add theme to .env file
working-directory: ./electron/ui
run: |
Expand Down Expand Up @@ -127,6 +130,13 @@ jobs:
electron/ui/cypress/screenshots/
electron/ui/cypress/videos/
## post-run conda often fails for weird reasons. this is a potential solution
## see https://github.com/conda-incubator/setup-miniconda/issues/277#issuecomment-1431458277
- name: Rename conda package cache
if: runner.os == 'Windows'
shell: bash
run: mv "${CONDA_PKGS_DIR}" "${CONDA_PKGS_DIR}_do_not_cache"

pytest:
name: pytest (${{ matrix.os }})
runs-on: ${{ matrix.os-version }}
Expand Down
28 changes: 20 additions & 8 deletions electron/ui/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import {getProjectName} from './services/projectName.service';
import MainContent from "./components/MainContent/MainContent";
import WaitForProject from "./components/WaitForProject/WaitForProject";
import {themes} from './theme';
import { ThemeProvider, createTheme } from '@mui/material/styles';


function App() {
let navigate = useNavigate();
Expand All @@ -21,15 +23,22 @@ function App() {
const [checkAgain, setCheckAgain] = useState(1)
const WAIT_TIME = 2

console.log("App hasTheme = ",hasTheme);
// use Material UI theme for styles to be consistent throughout app
const mui_theme = createTheme({
palette: {
primary: {
main: theme?.button.background,
},
},
});

useEffect(() => {
if (hasTheme && checkAgain !== 0)
{
// Get list of flowsheets
getFlowsheetsList()
.then((data) => {
console.log("got flowsheets list")
// console.log("got flowsheets list")
setHasFlowsheetsList(true);
setCheckAgain(0)
}).catch((e) => {
Expand All @@ -45,12 +54,15 @@ function App() {

const subProcState = {value: numberOfSubprocesses, setValue: setNumberOfSubprocesses}
return (
<div className="App">
<MainContent theme={theme} hasTheme={hasTheme} hasFlowsheets={hasFlowsheetsList}
subProcState={subProcState}/>
<WaitForProject hasTheme={hasTheme}></WaitForProject>
<SplashPage theme={theme} hasTheme={hasTheme} hasFlowsheets={hasFlowsheetsList}/>
</div>

<ThemeProvider theme={mui_theme}>
<div className="App">
<MainContent theme={theme} hasTheme={hasTheme} hasFlowsheets={hasFlowsheetsList}
subProcState={subProcState}/>
<WaitForProject hasTheme={hasTheme}></WaitForProject>
<SplashPage theme={theme} hasTheme={hasTheme} hasFlowsheets={hasFlowsheetsList}/>
</div>
</ThemeProvider>
)
}

Expand Down
1 change: 0 additions & 1 deletion electron/ui/src/components/Graph/Graph.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ export default function Graph() {
setGraphImage(URL.createObjectURL(data))
}
else {
console.log("data.size is 0")
if (tryAgain) setTryAgain(false)
}
}).catch((err)=>{
Expand Down
192 changes: 93 additions & 99 deletions electron/ui/src/components/SingleOutput/SingleOutput.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,62 @@
import React, {useState} from "react";
import React, {useState, useEffect, Fragment} from "react";
import {useParams} from "react-router-dom";
// MUI imports
import Accordion from "@mui/material/Accordion";
import AccordionDetails from "@mui/material/AccordionDetails";
import AccordionSummary from "@mui/material/AccordionSummary";
import Box from "@mui/material/Box";
import Button from "@mui/material/Button";
import DownloadIcon from '@mui/icons-material/Download';
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import Grid from "@mui/material/Grid";
import Modal from "@mui/material/Modal";
import SaveIcon from '@mui/icons-material/Save';
import Stack from "@mui/material/Stack";
import TextField from "@mui/material/TextField";
import Toolbar from "@mui/material/Toolbar";
import {
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
} from '@mui/material'
import Paper from '@mui/material/Paper';

export default function SingleOutput(props) {
let params = useParams();
const {outputData, downloadOutput, saveConfig} = props;
const [configName, setConfigName] = useState(outputData.name)
const [openSaveConfig, setOpenSaveConfig] = React.useState(false);
const [saved, setSaved] = React.useState(false);
const [outputTableData, setOutputTableData] = useState({})

const handleOpenSaveConfig = () => setOpenSaveConfig(true);
const handleCloseSaveConfig = () => setOpenSaveConfig(false);

/**
* organize output data into a list of dictionaries formatted for the output table
*/
useEffect(()=> {
let export_variables = {...outputData.outputData.exports}
let rows = {}
for (let key of Object.keys(export_variables)) {
let export_variable = export_variables[key]
let category = export_variable.output_category
if (!category) category = export_variable.input_category
let category_rows
if (Object.keys(rows).includes(category)) category_rows = rows[category]
else {
category_rows = []
rows[category] = category_rows
}
category_rows.push({
key: key,
name: export_variable.name,
value: export_variable.value,
units: export_variable.display_units,
rounding: export_variable.rounding || 2
})
}
setOutputTableData(rows)
}, [outputData])

const modalStyle = {
position: 'absolute',
Expand Down Expand Up @@ -87,101 +119,49 @@ export default function SingleOutput(props) {
});
}

const renderOutputAccordions = () => {
let var_sections = organizeVariables(outputData.outputData.exports)
// console.log("var_sections",var_sections)
return Object.entries(var_sections).map(([key, value]) => {
let gridSize = 4;
let _key = key + Math.floor(Math.random() * 100001);
if (Object.keys(value.output_variables).length > 0) {
return (<Grid item xs={gridSize} key={_key}>
<Accordion expanded={true} style={{border: "1px solid #ddd"}}>
<AccordionSummary expandIcon={<ExpandMoreIcon/>}>
{value.display_name}
</AccordionSummary>
<AccordionDetails>
<Box
component="form"
sx={{
'& > :not(style)': {m: 1},
}}
autoComplete="off"
/**
* generate html for table
* @returns table body component containing table rows
*/
const renderRows = () => {
try {
return (
<TableBody>
{Object.entries(outputTableData).map(([category, rows]) => (
<Fragment key={category}>
<TableRow>
<TableCell
rowSpan={rows.length+1}
sx={{border:"1px solid #ddd"}}
>
{
renderFields(value.output_variables)
}
</Box>
</AccordionDetails>
</Accordion>
</Grid>)
}
else {
return null;
}
})
};

// renders the data in output accordions
const renderFields = (fieldData) => {
// console.log("field data", fieldData)
return Object.keys(fieldData).map((key) => {
let _key = key + Math.floor(Math.random() * 100001);

// handle rounding
let roundedValue
if (fieldData[key].rounding != null) {
if (fieldData[key].rounding > 0) {
roundedValue = parseFloat((fieldData[key].value).toFixed(fieldData[key].rounding))
} else if (fieldData[key].rounding === 0) {
roundedValue = Math.round(fieldData[key].value)
} else // if rounding is negative
{
let factor = 1
let tempRounding = fieldData[key].rounding
console.log('rounding is negative : ', fieldData[key].rounding)
while (tempRounding < 0) {
factor *= 10
tempRounding += 1
}
roundedValue = Math.round((fieldData[key].value / factor)) * factor
console.log("old value is: ", fieldData[key].value)
console.log('new value is: ', roundedValue)
}
} else // if rounding is not provided, just use given value
{
roundedValue = fieldData[key].value
}
return (<div key={_key}>
<span>{fieldData[key].name + " "}</span>
<span
style={{color: "#68c3e4", fontWeight: "bold"}}>{roundedValue}</span>
<span>{" " + fieldData[key].display_units}</span>
</div>)
})
};

const organizeVariables = (bvars) => {
let var_sections = {}
for (const [key, v] of Object.entries(bvars)) {
let catg = v.output_category
let is_input = v.is_input
let is_output = v.is_output
if (catg === null) {
catg = ""
}
if (!Object.hasOwn(var_sections, catg)) {
var_sections[catg] = {
display_name: catg,
variables: {},
input_variables: {},
output_variables: {}
}
}
var_sections[catg]["variables"][key] = v
if (is_input) var_sections[catg]["input_variables"][key] = v;
if (is_output) var_sections[catg]["output_variables"][key] = v
<b>{category}</b>
</TableCell>
</TableRow>
{rows.map((row, idx) => (

<TableRow key={`_${idx}`}>
<TableCell align='right'>
{row.name}
</TableCell>
<TableCell align='center'>
{row.value.toLocaleString('en-US', {maximumFractionDigits:row.rounding})}
</TableCell>
<TableCell align='left'>
{row.units}
</TableCell>
</TableRow>
))}

</Fragment>
))}

</TableBody>
)
} catch(e) {
console.log("unable to render rows: ")
console.log(e)
}
return var_sections

}

return (
Expand Down Expand Up @@ -228,7 +208,21 @@ export default function SingleOutput(props) {
</Grid>
</Modal>
</Grid>
{renderOutputAccordions()}
<Grid item xs={12}>
<Paper>
<Table size="small" sx={{border:"1px solid #ddd"}}>
<TableHead>
<TableRow>
<TableCell>Category</TableCell>
<TableCell align='right'>Variable</TableCell>
<TableCell align='center'>Value</TableCell>
<TableCell align='left'>Units</TableCell>
</TableRow>
</TableHead>
{renderRows()}
</Table>
</Paper>
</Grid>
</>
);
}
2 changes: 0 additions & 2 deletions electron/ui/src/components/SolveDialog/SolveDialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,9 @@ export default function SolveDialog(props) {
const { open, handleSolved, handleError, flowsheetData, id, isSweep } = props;

useEffect(()=>{
console.log("solve dialog use effect")
try {
if(open)
{
console.log("open solve dialog is true")
if(isSweep) {
sweep(id, flowsheetData.inputData)
.then(r => r.json().then(data => ({status: r.status, body: data})))
Expand Down
6 changes: 3 additions & 3 deletions electron/ui/src/theme.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export const themes = {
color: '#FFFFFF', background: '#67C3E4', logoBackground: '#F2F7F8'
},
button: {
background: '#1976d2'
background: '#1976d2',
},
tabs: {
background: '#F1F3F3', color: '#727272'
Expand Down Expand Up @@ -62,7 +62,7 @@ export const themes = {
color: '#000000', background: '#F6F4F4', logoBackground: '#F8F6F6'
},
button: {
background: '#1669B6'
background: '#1669B6',
},
tabs: {
background: '#F6F4F4', color: '#727272'
Expand All @@ -87,7 +87,7 @@ export const themes = {
color: '#FFFFFF', background: '#000000', logoBackground: '#333333' // FIXME?
},
button: {
background: '#1669B6'
background: '#333333',
},
tabs: {
background: '#F1F3F3', color: '#727272'
Expand Down
Loading

0 comments on commit f218ab4

Please sign in to comment.