From d361e6e80bcb982ac472be9e2eaa4d6093e641b3 Mon Sep 17 00:00:00 2001 From: Austin Orr Date: Fri, 18 Aug 2023 14:27:26 -0700 Subject: [PATCH 1/7] fix scenario layout closes #286 --- .../scenario-module/scenario-all-map.jsx | 2 +- .../scenario-module/scenario-page.jsx | 65 ++++++++++++------- 2 files changed, 44 insertions(+), 23 deletions(-) diff --git a/stormpiper/stormpiper/spa/src/components/scenario-module/scenario-all-map.jsx b/stormpiper/stormpiper/spa/src/components/scenario-module/scenario-all-map.jsx index 315ef8bf..e46fc58c 100644 --- a/stormpiper/stormpiper/spa/src/components/scenario-module/scenario-all-map.jsx +++ b/stormpiper/stormpiper/spa/src/components/scenario-module/scenario-all-map.jsx @@ -119,7 +119,7 @@ export default function AllScenariosMap({ - + - - View All Scenarios - - - Use the table below to find an existing scenario or to create a - new one. - + + View All Scenarios + + + - - + + - + - + + + + Use the table below to find an existing scenario or to create a + new one. + + + Date: Fri, 18 Aug 2023 14:39:22 -0700 Subject: [PATCH 2/7] fix subbasin lineweight closes #294 --- stormpiper/stormpiper/spa/src/assets/geojson/coreLayers.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/stormpiper/stormpiper/spa/src/assets/geojson/coreLayers.js b/stormpiper/stormpiper/spa/src/assets/geojson/coreLayers.js index d8e4b534..9d5fbf77 100644 --- a/stormpiper/stormpiper/spa/src/assets/geojson/coreLayers.js +++ b/stormpiper/stormpiper/spa/src/assets/geojson/coreLayers.js @@ -407,9 +407,10 @@ export const subbasins = { "polygons-stroke": { type: StrokedPathLayer, getPath: (d) => d, - getWidth: 4, + getWidth: 1, + widthUnits: "pixels", getColor: colorToList("black", 1), - getOutlineWidth: 12, + getOutlineWidth: 3, getOutlineColor: colorToList("orange", 1), }, }, From 53764f0f99b736599a7a5486829e347199e89fc9 Mon Sep 17 00:00:00 2001 From: Austin Orr Date: Fri, 18 Aug 2023 15:02:14 -0700 Subject: [PATCH 3/7] add manholes to map explorer closes #290 --- .../spa/src/assets/geojson/coreLayers.js | 39 +++++++++++++++++-- .../stormpiper/spa/src/components/map.jsx | 4 ++ 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/stormpiper/stormpiper/spa/src/assets/geojson/coreLayers.js b/stormpiper/stormpiper/spa/src/assets/geojson/coreLayers.js index 9d5fbf77..fea4eb52 100644 --- a/stormpiper/stormpiper/spa/src/assets/geojson/coreLayers.js +++ b/stormpiper/stormpiper/spa/src/assets/geojson/coreLayers.js @@ -213,7 +213,7 @@ export const swInlet = { }, }; -export const swManHole = { +export const swManHoleModel = { layer: GeoJsonLayer, props: { data: collatePaginatedQuery({ @@ -231,13 +231,12 @@ export const swManHole = { }, }, }, - id: "swManHole", + id: "swManHoleModel", featurePKField: "OBJECTID", label: "Regional Facility Model: Manholes", featureType: "points", minZoom: 14, zorder: 10, - pointType: "circle", pointRadiusUnits: "pixels", defaultFillColor: colorToList("grey"), @@ -253,6 +252,38 @@ export const swManHole = { }, }; +export const swManHole = { + layer: GeoJsonLayer, + props: { + data: collatePaginatedQuery({ + url: "https://gis.cityoftacoma.org/arcgis/rest/services/ES/SurfacewaterNetwork/MapServer/23", + fields: ["FACILITYID", "ALTID"], + }), + loadOptions: { + fetch: { + headers: { + Authorization: Authorization, + }, + }, + }, + id: "swManHole", + featurePKField: "OBJECTID", + label: "Manholes", + featureType: "points", + minZoom: 14, + zorder: 10, + pointType: "circle", + pointRadiusUnits: "pixels", + defaultFillColor: colorToList("grey"), + getFillColor: colorToList("grey"), + getPointRadius: 3, + getLineWidth: 1, + lineWidthUnits: "pixels", + pickable: true, + onByDefault: false, + }, +}; + export const delineations = { layer: GeoJsonLayer, props: { @@ -659,7 +690,7 @@ export const layerDict = { Delineations: [delineations, subbasins], }, "Base Layers": { - Conveyance: [swTrunk, swMain, swCBLead, swInlet, swManHole], + Conveyance: [swTrunk, swMain, swCBLead, swInlet, swManHole, swManHoleModel], Landuse: [ landCoverRaster, imperviousnessRaster, diff --git a/stormpiper/stormpiper/spa/src/components/map.jsx b/stormpiper/stormpiper/spa/src/components/map.jsx index d3430e73..d1d26d71 100644 --- a/stormpiper/stormpiper/spa/src/components/map.jsx +++ b/stormpiper/stormpiper/spa/src/components/map.jsx @@ -41,6 +41,10 @@ const tooltipFieldDict = { swCBLead: [{ id: "ALTID", label: "Lead ID" }], swInlet: [{ id: "ALTID", label: "Inlet ID" }], swManHole: [ + { id: "FACILITYID", label: "Facility ID" }, + { id: "ALTID", label: "ALTID" }, + ], + swManHoleModel: [ { id: "MH_DrainageArea_ALTID", label: "Manhole ID" }, { id: "MH_DrainageArea_UPST_IMPVS", label: "Upstream Impervious Acres" }, ], From fb1297bb56ac361975ceaa614ea289c675daf42b Mon Sep 17 00:00:00 2001 From: Austin Orr Date: Fri, 18 Aug 2023 15:05:27 -0700 Subject: [PATCH 4/7] fix scenario designer menu closes #285 --- .../spa/src/components/scenario-module/scenario-create-map.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/stormpiper/stormpiper/spa/src/components/scenario-module/scenario-create-map.jsx b/stormpiper/stormpiper/spa/src/components/scenario-module/scenario-create-map.jsx index 2a985e8a..146738eb 100644 --- a/stormpiper/stormpiper/spa/src/components/scenario-module/scenario-create-map.jsx +++ b/stormpiper/stormpiper/spa/src/components/scenario-module/scenario-create-map.jsx @@ -347,7 +347,6 @@ export default function ScenarioCreateMap({ position: "relative", display: "flex", flexDirection: "column", - pointerEvents: "none", }} > Date: Fri, 18 Aug 2023 16:48:49 -0700 Subject: [PATCH 5/7] fix ddt attr submission closes #283 --- .../0042-4c54e753599b_add_ddt_to_tmnt_attr.py | 30 +++++++++++++++++++ .../stormpiper/database/schemas/tmnt.py | 1 + stormpiper/stormpiper/models/tmnt_attr.py | 1 + 3 files changed, 32 insertions(+) create mode 100644 stormpiper/alembic/versions/0042-4c54e753599b_add_ddt_to_tmnt_attr.py diff --git a/stormpiper/alembic/versions/0042-4c54e753599b_add_ddt_to_tmnt_attr.py b/stormpiper/alembic/versions/0042-4c54e753599b_add_ddt_to_tmnt_attr.py new file mode 100644 index 00000000..7dd2f19f --- /dev/null +++ b/stormpiper/alembic/versions/0042-4c54e753599b_add_ddt_to_tmnt_attr.py @@ -0,0 +1,30 @@ +"""add ddt to tmnt attr + +Revision ID: 4c54e753599b +Revises: 452744bc099f +Create Date: 2023-08-18 22:57:02.394645 + +""" +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision = "4c54e753599b" +down_revision = "452744bc099f" +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column( + "tmnt_facility_attribute", + sa.Column("treatment_drawdown_time_hr", sa.Float(), nullable=True), + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column("tmnt_facility_attribute", "treatment_drawdown_time_hr") + # ### end Alembic commands ### diff --git a/stormpiper/stormpiper/database/schemas/tmnt.py b/stormpiper/stormpiper/database/schemas/tmnt.py index 66e45c88..75a03331 100644 --- a/stormpiper/stormpiper/database/schemas/tmnt.py +++ b/stormpiper/stormpiper/database/schemas/tmnt.py @@ -85,6 +85,7 @@ class TMNTFacilityAttr(Base, MutableTrackedTable): minimum_retention_pct_override = Column(Float) treatment_rate_cfs = Column(Float) depth_ft = Column(Float) + treatment_drawdown_time_hr = Column(Float) # simplified attrs captured_pct = Column(Float) diff --git a/stormpiper/stormpiper/models/tmnt_attr.py b/stormpiper/stormpiper/models/tmnt_attr.py index 318d522d..fcc2b756 100644 --- a/stormpiper/stormpiper/models/tmnt_attr.py +++ b/stormpiper/stormpiper/models/tmnt_attr.py @@ -23,6 +23,7 @@ class TMNTFacilityAttrBase(BaseModel): minimum_retention_pct_override: None | float = None treatment_rate_cfs: None | float = None depth_ft: None | float = None + treatment_drawdown_time_hr: None | float = None # simplified attrs captured_pct: None | float = None From 3d191f7ebcfb8f13a80a82d68c0615de9a4a1cdc Mon Sep 17 00:00:00 2001 From: Austin Orr Date: Sat, 19 Aug 2023 15:06:20 -0700 Subject: [PATCH 6/7] fix subbasin lineweight again --- .../stormpiper/spa/src/assets/geojson/coreLayers.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/stormpiper/stormpiper/spa/src/assets/geojson/coreLayers.js b/stormpiper/stormpiper/spa/src/assets/geojson/coreLayers.js index fea4eb52..d0fc50a2 100644 --- a/stormpiper/stormpiper/spa/src/assets/geojson/coreLayers.js +++ b/stormpiper/stormpiper/spa/src/assets/geojson/coreLayers.js @@ -438,11 +438,11 @@ export const subbasins = { "polygons-stroke": { type: StrokedPathLayer, getPath: (d) => d, - getWidth: 1, + getWidth: 0.5, widthUnits: "pixels", - getColor: colorToList("black", 1), - getOutlineWidth: 3, - getOutlineColor: colorToList("orange", 1), + getColor: colorToList("midgrey", 1), + getOutlineWidth: 2, + getOutlineColor: colorToList("orange", 0.25), }, }, }, From bb7145309f2da9f29bd4a0e53908f35a7681f1fb Mon Sep 17 00:00:00 2001 From: Austin Orr Date: Sat, 19 Aug 2023 15:25:32 -0700 Subject: [PATCH 7/7] fix server side validation error on scenario page closes #289 --- Dockerfile | 3 +- stormpiper/requirements.txt | 2 +- stormpiper/requirements_unpinned.txt | 2 +- .../bmp-detail-page/bmp-detail-form.jsx | 5 +- .../scenario-bmp-detail-form.jsx | 30 ++-- .../scenario-module/scenario-detail-page.jsx | 131 +++++++++++++----- 6 files changed, 121 insertions(+), 52 deletions(-) diff --git a/Dockerfile b/Dockerfile index 9c819dcd..11a69b7f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -43,7 +43,8 @@ COPY ./stormpiper/requirements.txt /requirements.txt RUN mkdir /core \ && pip wheel \ --wheel-dir=/core \ - -r /requirements.txt + -r /requirements.txt \ + hatchling RUN mkdir /gunicorn \ && pip wheel \ --wheel-dir=/gunicorn \ diff --git a/stormpiper/requirements.txt b/stormpiper/requirements.txt index 7dcceae4..9c0c9e1f 100644 --- a/stormpiper/requirements.txt +++ b/stormpiper/requirements.txt @@ -65,7 +65,7 @@ Mako==1.2.4 MarkupSafe==2.1.3 matplotlib==3.7.2 multidict==6.0.4 -nereid @ https://github.com/Geosyntec/nereid/archive/v0.8.0.zip +nereid-engine @ https://github.com/Geosyntec/nereid/archive/v0.9.1.zip networkx==3.1 numpy==1.25.1 orjson==3.9.2 diff --git a/stormpiper/requirements_unpinned.txt b/stormpiper/requirements_unpinned.txt index 8eae1df4..21e1d23a 100644 --- a/stormpiper/requirements_unpinned.txt +++ b/stormpiper/requirements_unpinned.txt @@ -52,7 +52,7 @@ watchfiles asgi-ratelimit pint -nereid @ https://github.com/Geosyntec/nereid/archive/v0.8.0.zip +nereid-engine @ https://github.com/Geosyntec/nereid/archive/v0.9.1.zip setuptools wheel diff --git a/stormpiper/stormpiper/spa/src/components/bmp-detail-page/bmp-detail-form.jsx b/stormpiper/stormpiper/spa/src/components/bmp-detail-page/bmp-detail-form.jsx index 1379a0c8..fca5c5fc 100644 --- a/stormpiper/stormpiper/spa/src/components/bmp-detail-page/bmp-detail-form.jsx +++ b/stormpiper/stormpiper/spa/src/components/bmp-detail-page/bmp-detail-form.jsx @@ -152,7 +152,6 @@ export function BMPDetailForm() { msg = msg.replaceAll(beginningText, ""); let err = msg.match(/([\w\s.;=_]*)\([\w.=;\s]+\)/g); - console.log("Found errors:", err); if (err) { err.map((e) => { errorList.push(e.replace(/\([\w.=;\s]+\)/g, "")); //remove the error type in parantheses @@ -208,9 +207,9 @@ export function BMPDetailForm() { {errorMsg && _renderErrorHeader(errorMsg)} - {_getErrorList(errorMsg).map((msg) => { + {_getErrorList(errorMsg).map((msg, i) => { return ( - + {msg} ); diff --git a/stormpiper/stormpiper/spa/src/components/scenario-module/scenario-bmp-detail-form.jsx b/stormpiper/stormpiper/spa/src/components/scenario-module/scenario-bmp-detail-form.jsx index 300e76c3..26312d41 100644 --- a/stormpiper/stormpiper/spa/src/components/scenario-module/scenario-bmp-detail-form.jsx +++ b/stormpiper/stormpiper/spa/src/components/scenario-module/scenario-bmp-detail-form.jsx @@ -7,7 +7,14 @@ import { } from "react"; import { api_fetch } from "../../utils/utils"; import { BMPForm } from "../bmpForm"; -import { Box, Dialog, DialogActions, Typography, Button } from "@mui/material"; +import { + Box, + Dialog, + DialogActions, + Typography, + Button, + List, +} from "@mui/material"; export const ScenarioBMPForm = forwardRef(function ScenarioBMPForm( { facilitySetter, facility, formDisabled, showHelperText }, @@ -120,7 +127,7 @@ export const ScenarioBMPForm = forwardRef(function ScenarioBMPForm( if (header) { return header[0]; } else { - return header; + return msg; } } @@ -216,20 +223,23 @@ export const ScenarioBMPForm = forwardRef(function ScenarioBMPForm( setResultError(false)}> - + Error Saving Scenario - Please try again - - {_renderErrorHeader(errorMsg)} + + {errorMsg && _renderErrorHeader(errorMsg)} -
    - {_getErrorList(errorMsg).map((msg) => { - return
  • {msg}
  • ; + + {_getErrorList(errorMsg).map((msg, i) => { + return ( + + {msg} + + ); })} -
+
); diff --git a/stormpiper/stormpiper/spa/src/components/scenario-module/scenario-detail-page.jsx b/stormpiper/stormpiper/spa/src/components/scenario-module/scenario-detail-page.jsx index b62125dc..a3894550 100644 --- a/stormpiper/stormpiper/spa/src/components/scenario-module/scenario-detail-page.jsx +++ b/stormpiper/stormpiper/spa/src/components/scenario-module/scenario-detail-page.jsx @@ -4,6 +4,9 @@ import { useParams } from "react-router-dom"; import { Box, Card, + Dialog, + List, + ListItem, Typography, Button, Snackbar, @@ -41,6 +44,8 @@ async function getDataByID(id) { export default function ScenarioDetailPage({ setDrawerButtonList }) { const params = useParams(); const [scenarioObject, setScenarioObject] = useState(null); + const [submissionError, setSubmissionError] = useState(false); + const [submissionErrorMsg, setSubmissionErrorMsg] = useState("error!"); const [resultsPollInterval, setResultsPollInterval] = useState(null); const [resultsSuccessDisplay, setResultsSuccessDisplay] = useState({ status: false, @@ -216,35 +221,10 @@ export default function ScenarioDetailPage({ setDrawerButtonList }) { }); } else { submitScenario(); - setScenarioEditMode(false); } setMapMode("default"); } - function submitScenario() { - const scenarioToSubmit = { - name: scenarioObject.name, - info: scenarioObject.info, - input: scenarioObject.input, - }; - api_fetch(`/api/rest/scenario/${params.id}`, { - credentials: "same-origin", - headers: { - accept: "application/json", - "Content-type": "application/json", - }, - method: "PATCH", - body: JSON.stringify(scenarioToSubmit), - }).then((res) => { - if (res.status === 200) { - setResultsSuccessDisplay({ - status: true, - msg: "Scenario Updated Successfully", - }); - } - }); - } - async function initiateScenarioSolve() { setResultsLoadingDisplay({ status: true, @@ -293,13 +273,13 @@ export default function ScenarioDetailPage({ setDrawerButtonList }) { setResultsPollInterval(resultsPoll); } - function submitScenario() { + async function submitScenario() { const scenarioToSubmit = { name: scenarioObject.name, info: scenarioObject.info, input: scenarioObject.input, }; - api_fetch(`/api/rest/scenario/${params.id}`, { + const response = await api_fetch(`/api/rest/scenario/${params.id}`, { credentials: "same-origin", headers: { accept: "application/json", @@ -307,14 +287,65 @@ export default function ScenarioDetailPage({ setDrawerButtonList }) { }, method: "PATCH", body: JSON.stringify(scenarioToSubmit), - }).then((res) => { - if (res.status === 200) { - setResultsSuccessDisplay({ - status: true, - msg: "Scenario Updated Successfully", - }); - } - }); + }) + .then((resp) => { + if (resp.status >= 400) { + setSubmissionError(true); + } else { + setResultsSuccessDisplay({ + status: true, + msg: "Scenario Updated Successfully", + }); + setScenarioEditMode(false); + } + return resp.json(); + }) + .then((r) => { + //assume that only error responses have a detail object + if (r.detail) { + setSubmissionErrorMsg(r.detail); + } + }) + .catch((err) => { + setSubmissionError(true); + setSubmissionErrorMsg(err.message); + setScenarioEditMode(true); + console.log(err); + }); + return response; + } + + function _renderErrorHeader(msg) { + let beginningText = /[0-9]*\svalidation (error[s]*)/g; + let header = msg.match(beginningText); + if (header) { + return header[0]; + } else { + return msg; + } + } + + function _getErrorList(msg) { + let errorList = []; + + //Find the number of errors so that we know + let errorNum = 0; + let nums = msg.match(/[0-9]*/g); + if (nums) { + errorNum = parseInt(nums[0]); + } + + //Isolate just the list of errors + let beginningText = /[0-9]*\svalidation (error[s]*\sfor\s\w*\s)/g; + msg = msg.replaceAll(beginningText, ""); + + let err = msg.match(/([\w\s.;=_]*)\([\w.=;\s]+\)/g); + if (err) { + err.map((e) => { + errorList.push(e.replace(/\([\w.=;\s]+\)/g, "")); //remove the error type in parantheses + }); + } + return errorList; } function createBMPResultsCSV() { @@ -513,7 +544,13 @@ export default function ScenarioDetailPage({ setDrawerButtonList }) { )}

- + Delineation Details {scenarioEditMode && (