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

Begin alignment with new API #471

Merged
merged 11 commits into from
Dec 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions alfalfa_web/components.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
components:
schemas:
runId:
type: string
pointId:
type: string
format: uuid
pointName:
type: string
default: Outdoor Air Temperature Sensor
pointType:
type: string
enum:
- "input"
- "output"
- "bidirectional"
runMetadata:
type: object
properties:
id:
$ref: "#/components/schemas/runId"
pointMetadata:
type: object
properties:
id:
$ref: "#/components/schemas/pointId"
name:
$ref: "#/components/schemas/pointName"
description:
type: string
default: An outdoor air temperature sensor for an air handling unit
readOnly: true
unit:
type: string
default: °F
readOnly: true
min:
type: number
readOnly: true
max:
type: number
type:
$ref: "#/components/schemas/pointType"
required:
- id
- type
pointValue:
type: object
properties:
id:
$ref: "#/components/schemas/pointId"
value:
type: number
default: 24.3
pointData:
allOf:
- $ref: "#/components/schemas/pointMetadata"
- $ref: "#/components/schemas/pointValue"
parameters:
runId:
in: path
name: runId
schema:
$ref: "#/components/schemas/runId"
required: true
description: UUID of run
pointId:
in: path
name: pointId
schema:
$ref: "#/components/schemas/pointId"
required: true
description: UUID of point
6 changes: 3 additions & 3 deletions alfalfa_web/components/Sites/ErrorDialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,20 @@ import React from "react";
import { Close } from "@mui/icons-material";
import { Dialog, DialogContent, DialogTitle, Grid, IconButton } from "@mui/material";

export const ErrorDialog = ({ onClose, site }) => {
export const ErrorDialog = ({ onClose, run }) => {
return (
<div>
<Dialog fullWidth={true} maxWidth="lg" open={true} onClose={onClose}>
<DialogTitle>
<Grid container justifyContent="space-between" alignItems="center">
<span>{`${site.name} Error Log`}</span>
<span>{`${run.name} Error Log`}</span>
<IconButton onClick={onClose}>
<Close />
</IconButton>
</Grid>
</DialogTitle>
<DialogContent>
<pre style={{ whiteSpace: "pre-wrap" }}>{site.errorLog}</pre>
<pre style={{ whiteSpace: "pre-wrap" }}>{run.errorLog}</pre>
</DialogContent>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing from site to run

</Dialog>
</div>
Expand Down
20 changes: 15 additions & 5 deletions alfalfa_web/components/Sites/PointDialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,23 @@ import {
} from "@mui/material";
import ky from "ky";

export const PointDialog = ({ onClose, site }) => {
export const PointDialog = ({ onClose, run }) => {
const [expanded, setExpanded] = useState(false);
const [points, setPoints] = useState();

useEffect(async () => {
const { data: points } = await ky(`/api/v2/sites/${site.id}/points`).json();
setPoints(points);
const { payload: points } = await ky(`/api/v2/runs/${run.id}/points`).json();
await ky(`/api/v2/runs/${run.id}/points/values`)
.json()
.then(({ payload: values }) => {
for (const i in points) {
const point = points[i];
if (point.id in values) {
point.value = values[point.id];
}
}
setPoints(points);
});
}, []);

const handleChange = (pointId) => (event, expanded) => {
Expand All @@ -48,7 +58,7 @@ export const PointDialog = ({ onClose, site }) => {
return (
<div style={{ paddingTop: "2px" }}>
{!points.length ? (
<Typography align="center">— No points associated with site —</Typography>
<Typography align="center">— No points associated with run —</Typography>
) : (
points.sort(sortPoints).map((point, i) => {
return (
Expand Down Expand Up @@ -90,7 +100,7 @@ export const PointDialog = ({ onClose, site }) => {
<Dialog fullWidth={true} maxWidth="lg" open={true} onClose={onClose}>
<DialogTitle>
<Grid container justifyContent="space-between" alignItems="center">
<span>{`${site.name} Points`}</span>
<span>{`${run.name} Points`}</span>
<IconButton onClick={onClose}>
<Close />
</IconButton>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changing from site to run

Expand Down
102 changes: 49 additions & 53 deletions alfalfa_web/components/Sites/Sites.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,72 +9,72 @@ import { StartDialog } from "./StartDialog";
export const Sites = () => {
const [loading, setLoading] = useState(true);
const [selected, setSelected] = useState([]);
const [sites, setSites] = useState([]);
const [runs, setRuns] = useState([]);
const [showErrorDialog, setShowErrorDialog] = useState(null);
const [showPointDialog, setShowPointDialog] = useState(null);
const [showStartDialog, setShowStartDialog] = useState(null);

const validStates = {
start: ["ready"],
stop: ["preprocessing", "starting", "started", "running", "stopping"],
remove: ["ready", "complete", "error"],
download: ["ready", "complete", "error"]
start: ["READY"],
stop: ["PREPROCESSING", "STARTING", "STARTED", "RUNNING", "STOPPING"],
remove: ["READY", "COMPLETE", "ERROR"],
download: ["READY", "COMPLETE", "ERROR"]
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I got rid of the code which was lowercasing all of the statuses when they went through the api

};

const fetchSites = async () => {
const { data: sites } = await ky("/api/v2/sites").json();
setSites(sites);
const fetchRuns = async () => {
const { payload: runs } = await ky("/api/v2/runs").json();
setRuns(runs);
setLoading(false);
};

useEffect(() => {
fetchSites();
const id = setInterval(fetchSites, 1000);
fetchRuns();
const id = setInterval(fetchRuns, 1000);

return () => clearInterval(id);
}, []);

const isSelected = (siteRef) => selected.includes(siteRef);
const isSelected = (runId) => selected.includes(runId);

const selectedSites = () => sites.filter(({ id }) => selected.includes(id));
const selectedRuns = () => runs.filter(({ id }) => selected.includes(id));

const handleRowClick = (event, siteRef) => {
const newSelected = selected.includes(siteRef) ? selected.filter((id) => id !== siteRef) : [...selected, siteRef];
const handleRowClick = (event, runId) => {
const newSelected = selected.includes(runId) ? selected.filter((id) => id !== runId) : [...selected, runId];
setSelected(newSelected);
};

const isStartButtonDisabled = () => {
return !selectedSites().some(({ status }) => validStates.start.includes(status));
return !selectedRuns().some(({ status }) => validStates.start.includes(status));
};

const isStopButtonDisabled = () => {
return !selectedSites().some(({ status }) => validStates.stop.includes(status));
return !selectedRuns().some(({ status }) => validStates.stop.includes(status));
};

const isRemoveButtonDisabled = () => {
return !selectedSites().some(({ status }) => validStates.remove.includes(status));
return !selectedRuns().some(({ status }) => validStates.remove.includes(status));
};

const isDownloadButtonDisabled = () => {
return !selectedSites().some(({ status }) => validStates.download.includes(status));
return !selectedRuns().some(({ status }) => validStates.download.includes(status));
};

const handleOpenErrorDialog = (event, site) => {
const handleOpenErrorDialog = (event, run) => {
event.stopPropagation();
setShowErrorDialog(site);
setShowErrorDialog(run);
};

const handleOpenPointDialog = (event, site) => {
const handleOpenPointDialog = (event, run) => {
event.stopPropagation();
setShowPointDialog(site);
setShowPointDialog(run);
};

const handleStartSimulation = (startDatetime, endDatetime, timescale, realtime, externalClock) => {
selectedSites()
selectedRuns()
.filter(({ status }) => validStates.start.includes(status))
.map(async ({ id }) => {
await ky
.post(`/api/v2/sites/${id}/start`, {
.post(`/api/v2/runs/${id}/start`, {
json: {
startDatetime,
endDatetime,
Expand All @@ -88,28 +88,28 @@ export const Sites = () => {
};

const handleStopSimulation = () => {
selectedSites()
selectedRuns()
.filter(({ status }) => validStates.stop.includes(status))
.map(async ({ id }) => {
await ky.post(`/api/v2/sites/${id}/stop`).json();
await ky.post(`/api/v2/runs/${id}/stop`).json();
});
};

const handleRemoveSite = () => {
selectedSites()
const handleRemoveRun = () => {
selectedRuns()
.filter(({ status }) => validStates.remove.includes(status))
.map(async ({ id }) => {
await ky.delete(`/api/v2/sites/${id}`).json();
await ky.delete(`/api/v2/runs/${id}`).json();
});
};

const handleDownloadSite = async () => {
const ids = selectedSites()
const handleDownloadRun = async () => {
const ids = selectedRuns()
.filter(({ status }) => validStates.download.includes(status))
.map(({ id }) => id);

for (const id of ids) {
location.href = `/api/v2/sites/${id}/download`;
location.href = `/api/v2/runs/${id}/download`;
await new Promise((resolve) => setTimeout(resolve, 500));
}
};
Expand All @@ -118,8 +118,8 @@ export const Sites = () => {

return (
<Grid container direction="column">
{showErrorDialog && <ErrorDialog site={showErrorDialog} onClose={() => setShowErrorDialog(null)} />}
{showPointDialog && <PointDialog site={showPointDialog} onClose={() => setShowPointDialog(null)} />}
{showErrorDialog && <ErrorDialog run={showErrorDialog} onClose={() => setShowErrorDialog(null)} />}
{showPointDialog && <PointDialog run={showPointDialog} onClose={() => setShowPointDialog(null)} />}
{showStartDialog && (
<StartDialog onStartSimulation={handleStartSimulation} onClose={() => setShowStartDialog(null)} />
)}
Expand All @@ -136,33 +136,33 @@ export const Sites = () => {
</TableRow>
</TableHead>
<TableBody>
{sites.map((site) => {
{runs.map((run) => {
return (
<TableRow
key={site.id}
key={run.id}
selected={false}
style={{ cursor: "default" }}
onClick={(event) => handleRowClick(event, site.id)}>
onClick={(event) => handleRowClick(event, run.id)}>
<TableCell padding="checkbox">
<Checkbox checked={isSelected(site.id)} />
<Checkbox checked={isSelected(run.id)} />
</TableCell>
<TableCell padding="none">{site.name}</TableCell>
<TableCell>{site.id}</TableCell>
<TableCell padding="none">{run.name}</TableCell>
<TableCell>{run.id}</TableCell>
<TableCell>
{site.status === "error" && site.errorLog ? (
{run.status === "error" && run.errorLog ? (
<Button
variant="text"
style={{ marginLeft: -9 }}
onClick={(event) => handleOpenErrorDialog(event, site)}>
{site.status.toUpperCase()}
onClick={(event) => handleOpenErrorDialog(event, run)}>
{run.status.toUpperCase()}
</Button>
) : (
site.status.toUpperCase()
run.status.toUpperCase()
)}
</TableCell>
<TableCell>{site.datetime}</TableCell>
<TableCell>{run.datetime}</TableCell>
<TableCell>
<IconButton onClick={(event) => handleOpenPointDialog(event, site)}>
<IconButton onClick={(event) => handleOpenPointDialog(event, run)}>
<MoreVert />
</IconButton>
</TableCell>
Expand All @@ -189,17 +189,13 @@ export const Sites = () => {
</Button>
</Grid>
<Grid item>
<Button variant="contained" disabled={isRemoveButtonDisabled()} onClick={handleRemoveSite} sx={{ m: 1 }}>
<Button variant="contained" disabled={isRemoveButtonDisabled()} onClick={handleRemoveRun} sx={{ m: 1 }}>
Remove Test Case
</Button>
</Grid>
<Grid item>
<Button
variant="contained"
disabled={isDownloadButtonDisabled()}
onClick={handleDownloadSite}
sx={{ m: 1 }}>
Download Site
<Button variant="contained" disabled={isDownloadButtonDisabled()} onClick={handleDownloadRun} sx={{ m: 1 }}>
Download Run
</Button>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rest of file is migrating from sites to runs

</Grid>
</Grid>
Expand Down
8 changes: 6 additions & 2 deletions alfalfa_web/generate-docs.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const { version } = require("./package.json");
const serverType = process.env.NODE_ENV === "production" ? "Production" : "Development";

const openapiSpecification = swaggerJsdoc({
apis: ["./server/api-v2.js", "./server/api-haystack.js"],
apis: ["./server/api-v2.js", "./server/api-haystack.js", "./components.yml"],
definition: {
openapi: "3.1.0",
info: {
Expand Down Expand Up @@ -47,6 +47,10 @@ const openapiSpecification = swaggerJsdoc({
name: "Site",
description: "Manage sites"
},
{
name: "Run",
description: "Manage Runs"
},
{
name: "Haystack",
description:
Expand All @@ -56,7 +60,7 @@ const openapiSpecification = swaggerJsdoc({
"x-tagGroups": [
{
name: "Alfalfa API",
tags: ["About", "Alias", "Model", "Simulation", "Site"]
tags: ["About", "Alias", "Model", "Simulation", "Site", "Run"]
},
{
name: "Project Haystack API",
Expand Down
2 changes: 1 addition & 1 deletion alfalfa_web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
"@babel/polyfill": "^7.12.1",
"@babel/preset-env": "^7.20.2",
"@babel/preset-react": "^7.18.6",
"@redocly/cli": "^1.0.0-beta.124",
"@redocly/cli": "^1.5.0",
"babel-loader": "^9.1.2",
"babel-preset-minify": "^0.5.2",
"before-build-webpack": "^0.2.13",
Expand Down
Loading
Loading