From 99ba81fce26bbd99340990d0207761463558d4a7 Mon Sep 17 00:00:00 2001 From: hatim dinia Date: Thu, 2 Mar 2023 16:14:29 +0100 Subject: [PATCH 01/70] fix(ui-common): add matrices float handling --- webapp/src/components/common/EditableMatrix/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/src/components/common/EditableMatrix/index.tsx b/webapp/src/components/common/EditableMatrix/index.tsx index e31191838a..11977349fe 100644 --- a/webapp/src/components/common/EditableMatrix/index.tsx +++ b/webapp/src/components/common/EditableMatrix/index.tsx @@ -69,7 +69,7 @@ function EditableMatrix(props: PropTypes) { const handleSlice = (change: CellChange[], source: string) => { const isChanged = change.map((item) => { - if (parseInt(item[2], 10) === parseInt(item[3], 10)) { + if (parseFloat(item[2]) === parseFloat(item[3])) { return; } return item; From 56641d7ad995d8b7dd6755b13f1689b32b6296d8 Mon Sep 17 00:00:00 2001 From: hatim dinia Date: Tue, 28 Feb 2023 17:43:46 +0100 Subject: [PATCH 02/70] fix(ui-hydro): update hydro matrices columns --- .../explore/Modelization/Areas/Hydro/utils.ts | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/utils.ts b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/utils.ts index 23038fe4e1..f1753e20e7 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/utils.ts +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/utils.ts @@ -66,6 +66,7 @@ export const MATRICES: Matrices = { [MatrixType.Dailypower]: { title: "Daily power", url: "input/hydro/common/capacity/creditmodulations_{areaId}", + cols: generateColumns(), stats: MatrixStats.NOCOL, }, [MatrixType.EnergyCredits]: { @@ -82,13 +83,14 @@ export const MATRICES: Matrices = { [MatrixType.ReservoirLevels]: { title: "Reservoir levels", url: "input/hydro/common/capacity/reservoir_{areaId}", - cols: ["Lev Low(%)", "Lev Avg(%)", "Lev High(%)"], + cols: ["Lev Low (p.u)", "Lev Avg (p.u)", "Lev High (p.u)"], stats: MatrixStats.NOCOL, }, [MatrixType.WaterValues]: { title: "Water values", url: "input/hydro/common/capacity/waterValues_{areaId}", - stats: MatrixStats.TOTAL, + cols: generateColumns("%"), + stats: MatrixStats.NOCOL, }, [MatrixType.HydroStorage]: { title: "Hydro storage", @@ -101,3 +103,20 @@ export const MATRICES: Matrices = { stats: MatrixStats.STATS, }, }; + +//////////////////////////////////////////////////////////////// +// Functions +//////////////////////////////////////////////////////////////// + +/** + * Generates an array of column names from 0 to 100, optionally with a suffix. + * @param columnSuffix The suffix to append to the column names. + * @returns An array of strings representing column names from 0 to 100. + */ +function generateColumns(columnSuffix = ""): string[] { + const columns: string[] = []; + for (let i = 0; i <= 100; i += 1) { + columns.push(`${i}${columnSuffix}`); + } + return columns; +} From a466e3459e25ece8f2d80c8eb501ba05c717d5fa Mon Sep 17 00:00:00 2001 From: hatim dinia Date: Wed, 8 Mar 2023 10:47:58 +0100 Subject: [PATCH 03/70] feat(ui-hydro): add inflow structure tab --- .../Areas/Hydro/InflowStructure.tsx | 14 ++++++++++ .../Areas/Hydro/ManagementOptions/index.tsx | 27 ++++++++----------- .../Modelization/Areas/Hydro/index.tsx | 7 +++++ .../explore/Modelization/Areas/Hydro/utils.ts | 20 ++++++++++++++ webapp/src/components/App/index.tsx | 5 ++++ 5 files changed, 57 insertions(+), 16 deletions(-) create mode 100644 webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/InflowStructure.tsx diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/InflowStructure.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/InflowStructure.tsx new file mode 100644 index 0000000000..b1aee32d4b --- /dev/null +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/InflowStructure.tsx @@ -0,0 +1,14 @@ +import HydroMatrix from "./HydroMatrix"; +import { Root } from "./style"; +import { MatrixType } from "./utils"; + +function InflowStructure() { + return ( + + + + + ); +} + +export default InflowStructure; diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/ManagementOptions/index.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/ManagementOptions/index.tsx index 9769d83b2b..088c3bcefc 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/ManagementOptions/index.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/ManagementOptions/index.tsx @@ -2,10 +2,8 @@ import { useOutletContext } from "react-router"; import { StudyMetadata } from "../../../../../../../../common/types"; import useAppSelector from "../../../../../../../../redux/hooks/useAppSelector"; import { getCurrentAreaId } from "../../../../../../../../redux/selectors"; -import DocLink from "../../../../../../../common/DocLink"; import Form from "../../../../../../../common/Form"; import { SubmitHandlerPlus } from "../../../../../../../common/Form/types"; -import { ACTIVE_WINDOWS_DOC_PATH } from "../../../BindingConstraints/BindingConstView/utils"; import Fields from "./Fields"; import { getManagementOptionsFormFields, @@ -32,20 +30,17 @@ function ManagementOptions() { //////////////////////////////////////////////////////////////// return ( - <> - -
- getManagementOptionsFormFields(studyId, areaId), - }} - onSubmit={handleSubmit} - autoSubmit - > - - - +
+ getManagementOptionsFormFields(studyId, areaId), + }} + onSubmit={handleSubmit} + autoSubmit + > + + ); } diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/index.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/index.tsx index 7ff71df67f..a23151a380 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/index.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/index.tsx @@ -1,7 +1,9 @@ import { useMemo } from "react"; import { useOutletContext } from "react-router"; import { StudyMetadata } from "../../../../../../../common/types"; +import DocLink from "../../../../../../common/DocLink"; import TabWrapper from "../../../TabWrapper"; +import { ACTIVE_WINDOWS_DOC_PATH } from "../../BindingConstraints/BindingConstView/utils"; import { Root } from "./style"; function Hydro() { @@ -13,6 +15,10 @@ function Hydro() { label: "Management options", path: `/studies/${study?.id}/explore/modelization/area/hydro/management`, }, + { + label: "Inflow structure", + path: `/studies/${study?.id}/explore/modelization/area/hydro/inflowstructure`, + }, { label: "Daily Power", path: `/studies/${study?.id}/explore/modelization/area/hydro/dailypower`, @@ -47,6 +53,7 @@ function Hydro() { return ( + ); diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/utils.ts b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/utils.ts index f1753e20e7..52d1e195a9 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/utils.ts +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/utils.ts @@ -11,6 +11,8 @@ export enum MatrixType { WaterValues, HydroStorage, RunOfRiver, + InflowPattern, + OverallMonthlyHydro, } //////////////////////////////////////////////////////////////// @@ -102,6 +104,24 @@ export const MATRICES: Matrices = { url: "input/hydro/series/{areaId}/ror", stats: MatrixStats.STATS, }, + [MatrixType.InflowPattern]: { + title: "Inflow pattern", + url: "input/hydro/common/capacity/inflowPattern_{areaId}", + cols: ["Inflow Pattern (X)"], + stats: MatrixStats.NOCOL, + }, + [MatrixType.OverallMonthlyHydro]: { + title: "Overall monthly hydro", + url: "input/hydro/prepro/{areaId}/energy", + cols: [ + "Expectation (MWh)", + "Std Deviation (MWh)", + "Min. (MWh)", + "Max. (MWh)", + "ROR Share", + ], + stats: MatrixStats.NOCOL, + }, }; //////////////////////////////////////////////////////////////// diff --git a/webapp/src/components/App/index.tsx b/webapp/src/components/App/index.tsx index 7af36f9ab1..073b450c15 100644 --- a/webapp/src/components/App/index.tsx +++ b/webapp/src/components/App/index.tsx @@ -50,6 +50,7 @@ import { import HydroMatrix from "./Singlestudy/explore/Modelization/Areas/Hydro/HydroMatrix"; import Layers from "./Singlestudy/explore/Modelization/Map/MapConfig/Layers"; import Districts from "./Singlestudy/explore/Modelization/Map/MapConfig/Districts"; +import InflowStructure from "./Singlestudy/explore/Modelization/Areas/Hydro/InflowStructure"; function App() { return ( @@ -87,6 +88,10 @@ function App() { path="management" element={} /> + } + /> {HYDRO_ROUTES.map((route: HydroRoute) => ( Date: Wed, 8 Mar 2023 19:45:01 +0100 Subject: [PATCH 04/70] feat(ui-hydro): add row names --- .../Modelization/Areas/Hydro/HydroMatrix.tsx | 1 + .../Areas/Hydro/InflowStructure.tsx | 18 +++++++++++++----- .../explore/Modelization/Areas/Hydro/utils.ts | 16 ++++++++++++++++ .../components/common/EditableMatrix/index.tsx | 8 ++++++++ .../components/common/MatrixInput/index.tsx | 11 ++++------- 5 files changed, 42 insertions(+), 12 deletions(-) diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/HydroMatrix.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/HydroMatrix.tsx index 77f07fa2cf..8498bfa384 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/HydroMatrix.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/HydroMatrix.tsx @@ -25,6 +25,7 @@ function HydroMatrix({ type }: Props) { - - - + } + right={} + sx={{ + ".SplitLayoutView__Left": { + width: "50%", + }, + ".SplitLayoutView__Right": { + height: "100%", + }, + }} + /> ); } diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/utils.ts b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/utils.ts index 52d1e195a9..6f41f863cc 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/utils.ts +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/utils.ts @@ -23,6 +23,7 @@ interface HydroMatrixProps { title: string; url: string; cols?: string[]; + rows?: string[]; stats: MatrixStats; } @@ -69,6 +70,7 @@ export const MATRICES: Matrices = { title: "Daily power", url: "input/hydro/common/capacity/creditmodulations_{areaId}", cols: generateColumns(), + rows: ["Generating Power", "Pumping Power"], stats: MatrixStats.NOCOL, }, [MatrixType.EnergyCredits]: { @@ -120,6 +122,20 @@ export const MATRICES: Matrices = { "Max. (MWh)", "ROR Share", ], + rows: [ + "January", + "Febuary", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December", + ], stats: MatrixStats.NOCOL, }, }; diff --git a/webapp/src/components/common/EditableMatrix/index.tsx b/webapp/src/components/common/EditableMatrix/index.tsx index 11977349fe..a8d79504bf 100644 --- a/webapp/src/components/common/EditableMatrix/index.tsx +++ b/webapp/src/components/common/EditableMatrix/index.tsx @@ -26,6 +26,7 @@ interface PropTypes { toggleView?: boolean; onUpdate?: (change: MatrixEditDTO[], source: string) => void; columnsNames?: string[]; + rowNames?: string[]; computStats?: MatrixStats; } @@ -53,6 +54,7 @@ function EditableMatrix(props: PropTypes) { toggleView, onUpdate, columnsNames, + rowNames, computStats, } = props; const { data = [], columns = [], index = [] } = matrix; @@ -171,6 +173,12 @@ function EditableMatrix(props: PropTypes) { : _.fill(Array(formatedColumns.length), 100) } columns={formatedColumns} + rowHeaders={rowNames} + modifyRowHeaderWidth={(rowNames) => { + if (rowNames) { + return 150; + } + }} manualColumnResize /> ) : ( diff --git a/webapp/src/components/common/MatrixInput/index.tsx b/webapp/src/components/common/MatrixInput/index.tsx index 6c4e3bf093..cd573b1fd8 100644 --- a/webapp/src/components/common/MatrixInput/index.tsx +++ b/webapp/src/components/common/MatrixInput/index.tsx @@ -40,12 +40,13 @@ interface PropsType { study: StudyMetadata; url: string; columnsNames?: string[]; + rowNames?: string[]; title?: string; computStats: MatrixStats; } function MatrixInput(props: PropsType) { - const { study, url, columnsNames, title, computStats } = props; + const { study, url, columnsNames, rowNames, title, computStats } = props; const { enqueueSnackbar } = useSnackbar(); const enqueueErrorSnackbar = useEnqueueErrorSnackbar(); const [t] = useTranslation(); @@ -194,9 +195,10 @@ function MatrixInput(props: PropsType) { {!isLoading && data?.columns?.length >= 1 ? ( Date: Thu, 9 Mar 2023 14:19:22 +0100 Subject: [PATCH 05/70] feat(ui-matrix): update "Time" column and add index row headers fix #1385 --- .../common/EditableMatrix/index.tsx | 26 +++++-------------- .../components/common/EditableMatrix/utils.ts | 16 +++++++----- 2 files changed, 16 insertions(+), 26 deletions(-) diff --git a/webapp/src/components/common/EditableMatrix/index.tsx b/webapp/src/components/common/EditableMatrix/index.tsx index a8d79504bf..0c9249f417 100644 --- a/webapp/src/components/common/EditableMatrix/index.tsx +++ b/webapp/src/components/common/EditableMatrix/index.tsx @@ -1,8 +1,8 @@ import { useEffect, useState, useRef } from "react"; -import _ from "lodash"; import debug from "debug"; import HotTable from "@handsontable/react"; import { CellChange } from "handsontable/common"; +import { ColumnSettings } from "handsontable/settings"; import { MatrixIndex, MatrixEditDTO, @@ -31,7 +31,6 @@ interface PropTypes { } type CellType = Array; -type ColumnsType = { title: string; readOnly: boolean }; const formatColumnName = (col: string) => { try { @@ -60,9 +59,7 @@ function EditableMatrix(props: PropTypes) { const { data = [], columns = [], index = [] } = matrix; const prependIndex = index.length > 0 && matrixTime; const [grid, setGrid] = useState>([]); - const [formatedColumns, setFormatedColumns] = useState>( - [] - ); + const [formatedColumns, setFormatedColumns] = useState([]); const hotTableComponent = useRef(null); //////////////////////////////////////////////////////////////// @@ -103,7 +100,7 @@ function EditableMatrix(props: PropTypes) { useEffect(() => { setFormatedColumns([ - ...(prependIndex ? [{ title: "Time", readOnly: true }] : []), + ...(prependIndex ? [{ title: "Time", readOnly: true, width: 130 }] : []), ...columns.map((col, index) => ({ title: columnsNames?.[index] || formatColumnName(col), readOnly, @@ -124,9 +121,7 @@ function EditableMatrix(props: PropTypes) { let tmpRow = row as (string | number)[]; if (prependIndex) { if (matrixIndex) { - tmpRow = [ - createDateFromIndex(i, matrixIndex.start_date, matrixIndex.level), - ].concat(row); + tmpRow = [createDateFromIndex(i, matrixIndex)].concat(row); } } if (computStats) { @@ -163,22 +158,13 @@ function EditableMatrix(props: PropTypes) { stretchH="all" className="editableMatrix" colHeaders + rowHeaderWidth={rowNames ? 150 : undefined} afterChange={(change, source) => onUpdate && handleSlice(change || [], source) } beforeKeyDown={(e) => handleKeyDown(e)} - colWidths={ - prependIndex - ? [220].concat(_.fill(Array(formatedColumns.length), 100)) - : _.fill(Array(formatedColumns.length), 100) - } columns={formatedColumns} - rowHeaders={rowNames} - modifyRowHeaderWidth={(rowNames) => { - if (rowNames) { - return 150; - } - }} + rowHeaders={rowNames || true} manualColumnResize /> ) : ( diff --git a/webapp/src/components/common/EditableMatrix/utils.ts b/webapp/src/components/common/EditableMatrix/utils.ts index 47b5425a99..f988ce19d0 100644 --- a/webapp/src/components/common/EditableMatrix/utils.ts +++ b/webapp/src/components/common/EditableMatrix/utils.ts @@ -3,6 +3,7 @@ import moment, { DurationInputArg2 } from "moment"; import { CellChange } from "handsontable/common"; import { MatrixEditDTO, + MatrixIndex, MatrixStats, Operator, StudyOutputDownloadLevelDTO, @@ -68,14 +69,17 @@ const convertLevelDate = ( export const createDateFromIndex = ( indexDate: string | number, - startDate: string, - levelDate: StudyOutputDownloadLevelDTO + matrixIndex: MatrixIndex ): string | number => { const date = moment - .utc(startDate) - .add(indexDate, convertLevelDate(levelDate)) - .format("(ww) - ddd DD MMM HH:mm"); - return `${indexDate.toString().padStart(4, "0")} ${date}`.toUpperCase(); + .utc(matrixIndex.start_date) + .add(indexDate, convertLevelDate(matrixIndex.level)) + .format( + matrixIndex.level === StudyOutputDownloadLevelDTO.HOURLY + ? "ddd DD MMM HH:mm" + : "ddd DD MMM" + ); + return date; }; export const slice = (tab: CellChange[]): MatrixEditDTO[] => { From e529a799071e9c5485e2cba35eb5a7c2c18c25e7 Mon Sep 17 00:00:00 2001 From: Samir Kamal <1954121+skamril@users.noreply.github.com> Date: Thu, 9 Mar 2023 13:44:26 +0100 Subject: [PATCH 06/70] fix(ui-hydro): correct column names --- .../Singlestudy/explore/Modelization/Areas/Hydro/utils.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/utils.ts b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/utils.ts index 6f41f863cc..0a08517da2 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/utils.ts +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/utils.ts @@ -77,10 +77,10 @@ export const MATRICES: Matrices = { title: "Standard credit", url: "input/hydro/common/capacity/maxpower_{areaId}", cols: [ - "Generating Max Power(MW)", - "Generating Max Energy(Hours at Pmax)", - "Pumping Max Power(MW)", - "Pumping Max Energy(Hours at Pmax)", + "Generating Max Power (MW)", + "Generating Max Energy (Hours at Pmax)", + "Pumping Max Power (MW)", + "Pumping Max Energy (Hours at Pmax)", ], stats: MatrixStats.NOCOL, }, From da0013190d7e31e1afe9d8f5c3b03c378ca41507 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Omn=C3=A8s?= Date: Fri, 10 Mar 2023 09:32:47 +0100 Subject: [PATCH 07/70] fix(ui): fix typo on error page (#1390) --- webapp/src/components/common/loaders/GlobalPageLoadingError.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/src/components/common/loaders/GlobalPageLoadingError.tsx b/webapp/src/components/common/loaders/GlobalPageLoadingError.tsx index 3a0efb50ec..b1229f07ab 100644 --- a/webapp/src/components/common/loaders/GlobalPageLoadingError.tsx +++ b/webapp/src/components/common/loaders/GlobalPageLoadingError.tsx @@ -47,7 +47,7 @@ function GlobalPageLoadingError() { boxSizing="border-box" > - Oups, an unexpected error happened. + Oops, an unexpected error happened.
Please try to refresh the page.
From 26444169b9ab60f54e8ee7a2d16fb10dbc4d537e Mon Sep 17 00:00:00 2001 From: Samir Kamal <1954121+skamril@users.noreply.github.com> Date: Fri, 10 Mar 2023 15:49:15 +0100 Subject: [PATCH 08/70] fix(matrix): correct frequency some matrices (#1384) Co-authored-by: Laurent LAPORTE --- .../filesystem/matrix/date_serializer.py | 2 +- .../filesystem/matrix/input_series_matrix.py | 27 ++--- .../model/filesystem/matrix/matrix.py | 64 +++++++---- .../filesystem/matrix/output_series_matrix.py | 29 ++--- .../input/hydro/common/capacity/capacity.py | 69 ++++++++--- .../root/input/hydro/series/area/area.py | 29 +++-- .../output/simulation/mode/common/area.py | 15 +-- .../simulation/mode/common/binding_const.py | 23 ++-- .../output/simulation/mode/common/link.py | 21 ++-- .../output/simulation/mode/common/links.py | 1 - .../root/output/simulation/mode/common/set.py | 23 ++-- .../matrix/output_series_matrix_test.py | 35 +++--- .../filesystem/matrix/test_matrix_node.py | 94 ++++++++------- .../repository/filesystem/root/__init__.py | 0 .../filesystem/root/input/__init__.py | 0 .../filesystem/root/input/hydro/__init__.py | 0 .../root/input/hydro/common/__init__.py | 0 .../input/hydro/common/capacity/__init__.py | 0 .../hydro/common/capacity/test_capacity.py | 106 +++++++++++++++++ .../root/input/hydro/series/__init__.py | 0 .../root/input/hydro/series/area/__init__.py | 0 .../root/input/hydro/series/area/test_area.py | 100 ++++++++++++++++ .../filesystem/root/output/__init__.py | 0 .../root/output/simulation/__init__.py | 0 .../root/output/simulation/mode/__init__.py | 0 .../output/simulation/mode/common/__init__.py | 0 .../simulation/mode/common/test_area.py | 107 ++++++++++++++++++ .../mode/common/test_binding_const.py | 70 ++++++++++++ .../simulation/mode/common/test_link.py | 88 ++++++++++++++ .../output/simulation/mode/common/test_set.py | 87 ++++++++++++++ 30 files changed, 810 insertions(+), 180 deletions(-) create mode 100644 tests/storage/repository/filesystem/root/__init__.py create mode 100644 tests/storage/repository/filesystem/root/input/__init__.py create mode 100644 tests/storage/repository/filesystem/root/input/hydro/__init__.py create mode 100644 tests/storage/repository/filesystem/root/input/hydro/common/__init__.py create mode 100644 tests/storage/repository/filesystem/root/input/hydro/common/capacity/__init__.py create mode 100644 tests/storage/repository/filesystem/root/input/hydro/common/capacity/test_capacity.py create mode 100644 tests/storage/repository/filesystem/root/input/hydro/series/__init__.py create mode 100644 tests/storage/repository/filesystem/root/input/hydro/series/area/__init__.py create mode 100644 tests/storage/repository/filesystem/root/input/hydro/series/area/test_area.py create mode 100644 tests/storage/repository/filesystem/root/output/__init__.py create mode 100644 tests/storage/repository/filesystem/root/output/simulation/__init__.py create mode 100644 tests/storage/repository/filesystem/root/output/simulation/mode/__init__.py create mode 100644 tests/storage/repository/filesystem/root/output/simulation/mode/common/__init__.py create mode 100644 tests/storage/repository/filesystem/root/output/simulation/mode/common/test_area.py create mode 100644 tests/storage/repository/filesystem/root/output/simulation/mode/common/test_binding_const.py create mode 100644 tests/storage/repository/filesystem/root/output/simulation/mode/common/test_link.py create mode 100644 tests/storage/repository/filesystem/root/output/simulation/mode/common/test_set.py diff --git a/antarest/study/storage/rawstudy/model/filesystem/matrix/date_serializer.py b/antarest/study/storage/rawstudy/model/filesystem/matrix/date_serializer.py index 528185b56f..224c1c4d4f 100644 --- a/antarest/study/storage/rawstudy/model/filesystem/matrix/date_serializer.py +++ b/antarest/study/storage/rawstudy/model/filesystem/matrix/date_serializer.py @@ -1,6 +1,6 @@ import re from abc import ABC, abstractmethod -from typing import Tuple, List +from typing import List, Tuple import pandas as pd # type: ignore from pandas import DataFrame diff --git a/antarest/study/storage/rawstudy/model/filesystem/matrix/input_series_matrix.py b/antarest/study/storage/rawstudy/model/filesystem/matrix/input_series_matrix.py index f28d0cc66b..00f496fd4e 100644 --- a/antarest/study/storage/rawstudy/model/filesystem/matrix/input_series_matrix.py +++ b/antarest/study/storage/rawstudy/model/filesystem/matrix/input_series_matrix.py @@ -1,6 +1,6 @@ import logging from pathlib import Path -from typing import List, Optional, Any, Union, cast, Dict +from typing import Any, Dict, List, Optional, Union, cast import pandas as pd # type: ignore from pandas.errors import EmptyDataError # type: ignore @@ -14,6 +14,7 @@ ContextServer, ) from antarest.study.storage.rawstudy.model.filesystem.matrix.matrix import ( + MatrixFrequency, MatrixNode, ) @@ -29,8 +30,8 @@ def __init__( self, context: ContextServer, config: FileStudyTreeConfig, + freq: MatrixFrequency = MatrixFrequency.HOURLY, nb_columns: Optional[int] = None, - freq: str = "hourly", default_empty: Optional[List[List[float]]] = None, ): super().__init__(context=context, config=config, freq=freq) @@ -45,6 +46,7 @@ def parse( ) -> Union[JSON, pd.DataFrame]: file_path = file_path or self.config.path try: + # sourcery skip: extract-method stopwatch = StopWatch() if self.get_link_path().exists(): link = self.get_link_path().read_text() @@ -79,22 +81,7 @@ def parse( except EmptyDataError: logger.warning(f"Empty file found when parsing {file_path}") default = self._format_default_matrix() - if return_dataframe: - return pd.DataFrame(default) - return default - - def _dump_json(self, data: JSON) -> None: - df = pd.DataFrame(**data) - if not df.empty: - df.to_csv( - self.config.path, - sep="\t", - header=False, - index=False, - float_format="%.6f", - ) - else: - self.config.path.write_bytes(b"") + return pd.DataFrame(default) if return_dataframe else default def check_errors( self, @@ -123,8 +110,8 @@ def _format_default_matrix(self) -> Dict[str, Any]: if column_count > 0: logger.info("Using preset default matrix") return { - "index": list(range(0, index_count)), - "columns": list(range(0, column_count)), + "index": list(range(index_count)), + "columns": list(range(column_count)), "data": self.default_empty, } return {} diff --git a/antarest/study/storage/rawstudy/model/filesystem/matrix/matrix.py b/antarest/study/storage/rawstudy/model/filesystem/matrix/matrix.py index 914d8a4464..77326ac3ad 100644 --- a/antarest/study/storage/rawstudy/model/filesystem/matrix/matrix.py +++ b/antarest/study/storage/rawstudy/model/filesystem/matrix/matrix.py @@ -1,7 +1,8 @@ import logging from abc import ABC, abstractmethod +from enum import Enum from pathlib import Path -from typing import List, Optional, Union, Any +from typing import Any, List, Optional, Union import pandas as pd # type: ignore @@ -15,19 +16,31 @@ from antarest.study.storage.rawstudy.model.filesystem.exceptions import ( DenormalizationException, ) -from antarest.study.storage.rawstudy.model.filesystem.lazy_node import ( - LazyNode, -) +from antarest.study.storage.rawstudy.model.filesystem.lazy_node import LazyNode logger = logging.getLogger(__name__) +class MatrixFrequency(str, Enum): + """ + An enumeration of matrix frequencies. + + Each frequency corresponds to a specific time interval for a matrix's data. + """ + + ANNUAL = "annual" + MONTHLY = "monthly" + WEEKLY = "weekly" + DAILY = "daily" + HOURLY = "hourly" + + class MatrixNode(LazyNode[Union[bytes, JSON], Union[bytes, JSON], JSON], ABC): def __init__( self, context: ContextServer, config: FileStudyTreeConfig, - freq: str, + freq: MatrixFrequency, ) -> None: LazyNode.__init__(self, context, config) self.freq = freq @@ -99,26 +112,35 @@ def parse( """ raise NotImplementedError() - @abstractmethod - def _dump_json(self, data: JSON) -> None: + def dump( + self, + data: Union[bytes, JSON], + url: Optional[List[str]] = None, + ) -> None: """ - Store data on tree. - - Args: - data: new data to save - url: data path to change + Write matrix data to file. - Returns: + If the input data is of type bytes, write the data to file as is. + Otherwise, convert the data to a Pandas DataFrame and write it to file as a tab-separated CSV. + If the resulting DataFrame is empty, write an empty bytes object to file. + Args: + data: The data to write to file. If data is bytes, it will be written directly to file, + otherwise it will be converted to a Pandas DataFrame and then written to file. + url: node URL (not used here). """ - - raise NotImplementedError() - - def dump( - self, data: Union[bytes, JSON], url: Optional[List[str]] = None - ) -> None: + self.config.path.parent.mkdir(exist_ok=True, parents=True) if isinstance(data, bytes): - self.config.path.parent.mkdir(exist_ok=True, parents=True) self.config.path.write_bytes(data) else: - self._dump_json(data) + df = pd.DataFrame(**data) + if df.empty: + self.config.path.write_bytes(b"") + else: + df.to_csv( + self.config.path, + sep="\t", + header=False, + index=False, + float_format="%.6f", + ) diff --git a/antarest/study/storage/rawstudy/model/filesystem/matrix/output_series_matrix.py b/antarest/study/storage/rawstudy/model/filesystem/matrix/output_series_matrix.py index c9f420ef88..0c0750b702 100644 --- a/antarest/study/storage/rawstudy/model/filesystem/matrix/output_series_matrix.py +++ b/antarest/study/storage/rawstudy/model/filesystem/matrix/output_series_matrix.py @@ -1,6 +1,6 @@ import logging from pathlib import Path -from typing import List, Optional, cast, Union, Any +from typing import Any, List, Optional, Union, cast import pandas as pd # type: ignore from pandas import DataFrame @@ -17,14 +17,17 @@ ) from antarest.study.storage.rawstudy.model.filesystem.lazy_node import LazyNode from antarest.study.storage.rawstudy.model.filesystem.matrix.date_serializer import ( - IDateMatrixSerializer, FactoryDateSerializer, + IDateMatrixSerializer, rename_unnamed, ) from antarest.study.storage.rawstudy.model.filesystem.matrix.head_writer import ( + AreaHeadWriter, HeadWriter, LinkHeadWriter, - AreaHeadWriter, +) +from antarest.study.storage.rawstudy.model.filesystem.matrix.matrix import ( + MatrixFrequency, ) logger = logging.getLogger(__name__) @@ -42,9 +45,9 @@ def __init__( self, context: ContextServer, config: FileStudyTreeConfig, + freq: MatrixFrequency, date_serializer: IDateMatrixSerializer, head_writer: HeadWriter, - freq: str, ): super().__init__(context=context, config=config) self.date_serializer = date_serializer @@ -152,12 +155,12 @@ def load( return b"" if not file_path.exists(): - raise KeyError + raise FileNotFoundError(file_path) return self.parse(file_path, tmp_dir) - except KeyError: + except FileNotFoundError as e: raise ChildNotFoundError( f"Output file {self.config.path.name} not found in study {self.config.study_id}" - ) + ) from e def dump( self, data: Union[bytes, JSON], url: Optional[List[str]] = None @@ -180,16 +183,16 @@ def __init__( self, context: ContextServer, config: FileStudyTreeConfig, - freq: str, + freq: MatrixFrequency, src: str, dest: str, ): super(LinkOutputSeriesMatrix, self).__init__( context=context, config=config, + freq=freq, date_serializer=FactoryDateSerializer.create(freq, src), head_writer=LinkHeadWriter(src, dest, freq), - freq=freq, ) @@ -198,15 +201,15 @@ def __init__( self, context: ContextServer, config: FileStudyTreeConfig, - freq: str, + freq: MatrixFrequency, area: str, ): super(AreaOutputSeriesMatrix, self).__init__( context, config=config, + freq=freq, date_serializer=FactoryDateSerializer.create(freq, area), head_writer=AreaHeadWriter(area, config.path.name[:2], freq), - freq=freq, ) @@ -215,12 +218,12 @@ def __init__( self, context: ContextServer, config: FileStudyTreeConfig, - freq: str, + freq: MatrixFrequency, ): super(BindingConstraintOutputSeriesMatrix, self).__init__( context, config=config, + freq=freq, date_serializer=FactoryDateSerializer.create(freq, "system"), head_writer=AreaHeadWriter("system", config.path.name[:2], freq), - freq=freq, ) diff --git a/antarest/study/storage/rawstudy/model/filesystem/root/input/hydro/common/capacity/capacity.py b/antarest/study/storage/rawstudy/model/filesystem/root/input/hydro/common/capacity/capacity.py index d5e872192a..b671c20d38 100644 --- a/antarest/study/storage/rawstudy/model/filesystem/root/input/hydro/common/capacity/capacity.py +++ b/antarest/study/storage/rawstudy/model/filesystem/root/input/hydro/common/capacity/capacity.py @@ -1,6 +1,5 @@ -from antarest.study.storage.rawstudy.model.filesystem.config.model import ( - FileStudyTreeConfig, -) +from typing import List, TypedDict + from antarest.study.storage.rawstudy.model.filesystem.folder_node import ( FolderNode, ) @@ -8,23 +7,57 @@ from antarest.study.storage.rawstudy.model.filesystem.matrix.input_series_matrix import ( InputSeriesMatrix, ) +from antarest.study.storage.rawstudy.model.filesystem.matrix.matrix import ( + MatrixFrequency, +) + + +class MatrixInfo(TypedDict, total=False): + name: str + freq: MatrixFrequency + start_version: int + + +# noinspection SpellCheckingInspection +MATRICES_INFO: List[MatrixInfo] = [ + { + "name": "maxpower", + "freq": MatrixFrequency.HOURLY, + "start_version": 0, + }, + { + "name": "reservoir", + "freq": MatrixFrequency.DAILY, + "start_version": 0, + }, + { + "name": "inflowPattern", + "freq": MatrixFrequency.HOURLY, + "start_version": 650, + }, + { + "name": "creditmodulations", + "freq": MatrixFrequency.HOURLY, + "start_version": 650, + }, + { + "name": "waterValues", + "freq": MatrixFrequency.DAILY, + "start_version": 650, + }, +] class InputHydroCommonCapacity(FolderNode): def build(self) -> TREE: - children: TREE = dict() - for area in self.config.area_names(): - config_filenames = [ - "maxpower", - "reservoir", - ] - if self.config.version >= 650: - config_filenames.append("inflowPattern") - config_filenames.append("creditmodulations") - config_filenames.append("waterValues") - for file in config_filenames: - name = f"{file}_{area}" - children[name] = InputSeriesMatrix( - self.context, self.config.next_file(f"{name}.txt") - ) + children: TREE = {} + for info in MATRICES_INFO: + if self.config.version >= info["start_version"]: + for area in self.config.area_names(): + name = f"{info['name']}_{area}" + children[name] = InputSeriesMatrix( + self.context, + self.config.next_file(f"{name}.txt"), + freq=info["freq"], + ) return children diff --git a/antarest/study/storage/rawstudy/model/filesystem/root/input/hydro/series/area/area.py b/antarest/study/storage/rawstudy/model/filesystem/root/input/hydro/series/area/area.py index 1dad2ba5c3..00710cff1b 100644 --- a/antarest/study/storage/rawstudy/model/filesystem/root/input/hydro/series/area/area.py +++ b/antarest/study/storage/rawstudy/model/filesystem/root/input/hydro/series/area/area.py @@ -1,35 +1,44 @@ -from antarest.study.storage.rawstudy.model.filesystem.config.model import ( - FileStudyTreeConfig, -) from antarest.study.storage.rawstudy.model.filesystem.folder_node import ( FolderNode, ) from antarest.study.storage.rawstudy.model.filesystem.inode import TREE from antarest.study.storage.rawstudy.model.filesystem.matrix.constants import ( - default_scenario_hourly, default_scenario_daily, + default_scenario_hourly, default_scenario_monthly, ) from antarest.study.storage.rawstudy.model.filesystem.matrix.input_series_matrix import ( InputSeriesMatrix, ) +from antarest.study.storage.rawstudy.model.filesystem.matrix.matrix import ( + MatrixFrequency, +) class InputHydroSeriesArea(FolderNode): def build(self) -> TREE: - children: TREE = { - # TODO mod is monthly on version < 650, then daily afterward + freq = ( + MatrixFrequency.DAILY + if self.config.version >= 650 + else MatrixFrequency.MONTHLY + ) + default_empty = ( + default_scenario_daily + if self.config.version >= 650 + else default_scenario_monthly + ) + return { "mod": InputSeriesMatrix( self.context, self.config.next_file("mod.txt"), - default_empty=default_scenario_daily - if self.config.version >= 650 - else default_scenario_monthly, + freq=freq, + default_empty=default_empty, ), + # Run of River "ror": InputSeriesMatrix( self.context, self.config.next_file("ror.txt"), + freq=MatrixFrequency.HOURLY, default_empty=default_scenario_hourly, ), } - return children diff --git a/antarest/study/storage/rawstudy/model/filesystem/root/output/simulation/mode/common/area.py b/antarest/study/storage/rawstudy/model/filesystem/root/output/simulation/mode/common/area.py index 65dd649249..149ebf38c9 100644 --- a/antarest/study/storage/rawstudy/model/filesystem/root/output/simulation/mode/common/area.py +++ b/antarest/study/storage/rawstudy/model/filesystem/root/output/simulation/mode/common/area.py @@ -1,8 +1,5 @@ -from typing import cast - from antarest.study.storage.rawstudy.model.filesystem.config.model import ( FileStudyTreeConfig, - ENR_MODELLING, ) from antarest.study.storage.rawstudy.model.filesystem.context import ( ContextServer, @@ -11,6 +8,9 @@ FolderNode, ) from antarest.study.storage.rawstudy.model.filesystem.inode import TREE +from antarest.study.storage.rawstudy.model.filesystem.matrix.matrix import ( + MatrixFrequency, +) from antarest.study.storage.rawstudy.model.filesystem.matrix.output_series_matrix import ( AreaOutputSeriesMatrix, ) @@ -29,13 +29,13 @@ def __init__( self.mc_all = mc_all def build(self) -> TREE: - children: TREE = dict() + children: TREE = {} # filters = self.config.get_filters_synthesis(self.area) # todo get the config related to this output (now this may fail if input has changed since the launch) - filters = ["hourly", "daily", "weekly", "monthly", "annual"] - for freq in filters: + freq: MatrixFrequency + for freq in MatrixFrequency: if self.mc_all: children[f"id-{freq}"] = AreaOutputSeriesMatrix( self.context, @@ -63,7 +63,8 @@ def build(self) -> TREE: self.area, ) - # has_enr_clusters = self.config.enr_modelling == ENR_MODELLING.CLUSTERS.value and len(self.config.get_renewable_names(self.area, only_enabled=True)) > 0 + # has_enr_clusters = self.config.enr_modelling == ENR_MODELLING.CLUSTERS.value and + # len(self.config.get_renewable_names(self.area, only_enabled=True)) > 0 # todo get the config related to this output (now this may fail if input has changed since the launch) has_enr_clusters = True diff --git a/antarest/study/storage/rawstudy/model/filesystem/root/output/simulation/mode/common/binding_const.py b/antarest/study/storage/rawstudy/model/filesystem/root/output/simulation/mode/common/binding_const.py index 6eeec6ea5e..5bf1498e80 100644 --- a/antarest/study/storage/rawstudy/model/filesystem/root/output/simulation/mode/common/binding_const.py +++ b/antarest/study/storage/rawstudy/model/filesystem/root/output/simulation/mode/common/binding_const.py @@ -1,32 +1,29 @@ -from typing import cast - from antarest.study.storage.rawstudy.model.filesystem.folder_node import ( FolderNode, ) from antarest.study.storage.rawstudy.model.filesystem.inode import TREE +from antarest.study.storage.rawstudy.model.filesystem.matrix.matrix import ( + MatrixFrequency, +) from antarest.study.storage.rawstudy.model.filesystem.matrix.output_series_matrix import ( - LinkOutputSeriesMatrix, BindingConstraintOutputSeriesMatrix, ) class OutputSimulationBindingConstraintItem(FolderNode): def build(self) -> TREE: - children: TREE = {} - # filters = self.config.get_filters_synthesis(self.area, self.link) # todo get the config related to this output (now this may fail if input has changed since the launch) - filters = ["hourly", "daily", "weekly", "monthly", "annual"] - for timing in filters: - children[ - f"binding-constraints-{timing}" - ] = BindingConstraintOutputSeriesMatrix( + freq: MatrixFrequency + children: TREE = { + f"binding-constraints-{freq}": BindingConstraintOutputSeriesMatrix( self.context, - self.config.next_file(f"binding-constraints-{timing}.txt"), - timing, + self.config.next_file(f"binding-constraints-{freq}.txt"), + freq, ) - + for freq in MatrixFrequency + } return { child: children[child] for child in children diff --git a/antarest/study/storage/rawstudy/model/filesystem/root/output/simulation/mode/common/link.py b/antarest/study/storage/rawstudy/model/filesystem/root/output/simulation/mode/common/link.py index 64b691163b..55c50d079f 100644 --- a/antarest/study/storage/rawstudy/model/filesystem/root/output/simulation/mode/common/link.py +++ b/antarest/study/storage/rawstudy/model/filesystem/root/output/simulation/mode/common/link.py @@ -1,5 +1,3 @@ -from typing import cast - from antarest.study.storage.rawstudy.model.filesystem.config.model import ( FileStudyTreeConfig, ) @@ -10,6 +8,9 @@ FolderNode, ) from antarest.study.storage.rawstudy.model.filesystem.inode import TREE +from antarest.study.storage.rawstudy.model.filesystem.matrix.matrix import ( + MatrixFrequency, +) from antarest.study.storage.rawstudy.model.filesystem.matrix.output_series_matrix import ( LinkOutputSeriesMatrix, ) @@ -34,21 +35,21 @@ def build(self) -> TREE: # filters = self.config.get_filters_synthesis(self.area, self.link) # todo get the config related to this output (now this may fail if input has changed since the launch) - filters = ["hourly", "daily", "weekly", "monthly", "annual"] - for timing in filters: - children[f"values-{timing}"] = LinkOutputSeriesMatrix( + freq: MatrixFrequency + for freq in MatrixFrequency: + children[f"values-{freq}"] = LinkOutputSeriesMatrix( self.context, - self.config.next_file(f"values-{timing}.txt"), - timing, + self.config.next_file(f"values-{freq}.txt"), + freq, self.area, self.link, ) if self.mc_all: - children[f"id-{timing}"] = LinkOutputSeriesMatrix( + children[f"id-{freq}"] = LinkOutputSeriesMatrix( self.context, - self.config.next_file(f"id-{timing}.txt"), - timing, + self.config.next_file(f"id-{freq}.txt"), + freq, self.area, self.link, ) diff --git a/antarest/study/storage/rawstudy/model/filesystem/root/output/simulation/mode/common/links.py b/antarest/study/storage/rawstudy/model/filesystem/root/output/simulation/mode/common/links.py index ddf11fde9c..3f459b4d1a 100644 --- a/antarest/study/storage/rawstudy/model/filesystem/root/output/simulation/mode/common/links.py +++ b/antarest/study/storage/rawstudy/model/filesystem/root/output/simulation/mode/common/links.py @@ -8,7 +8,6 @@ FolderNode, ) from antarest.study.storage.rawstudy.model.filesystem.inode import TREE - from antarest.study.storage.rawstudy.model.filesystem.root.output.simulation.mode.common.link import ( OutputSimulationLinkItem, ) diff --git a/antarest/study/storage/rawstudy/model/filesystem/root/output/simulation/mode/common/set.py b/antarest/study/storage/rawstudy/model/filesystem/root/output/simulation/mode/common/set.py index 7decb9d1da..4070031963 100644 --- a/antarest/study/storage/rawstudy/model/filesystem/root/output/simulation/mode/common/set.py +++ b/antarest/study/storage/rawstudy/model/filesystem/root/output/simulation/mode/common/set.py @@ -1,5 +1,3 @@ -from typing import cast - from antarest.study.storage.rawstudy.model.filesystem.config.model import ( FileStudyTreeConfig, ) @@ -10,6 +8,9 @@ FolderNode, ) from antarest.study.storage.rawstudy.model.filesystem.inode import TREE +from antarest.study.storage.rawstudy.model.filesystem.matrix.matrix import ( + MatrixFrequency, +) from antarest.study.storage.rawstudy.model.filesystem.matrix.output_series_matrix import ( AreaOutputSeriesMatrix, ) @@ -28,25 +29,25 @@ def __init__( self.mc_all = mc_all def build(self) -> TREE: - children: TREE = dict() + children: TREE = {} # filters = self.config.get_filters_synthesis(self.set) # todo get the config related to this output (now this may fail if input has changed since the launch) - filters = ["hourly", "daily", "weekly", "monthly", "annual"] - for timing in filters: + freq: MatrixFrequency + for freq in MatrixFrequency: if self.mc_all: - children[f"id-{timing}"] = AreaOutputSeriesMatrix( + children[f"id-{freq.value}"] = AreaOutputSeriesMatrix( self.context, - self.config.next_file(f"id-{timing}.txt"), - timing, + self.config.next_file(f"id-{freq.value}.txt"), + freq, self.set, ) - children[f"values-{timing}"] = AreaOutputSeriesMatrix( + children[f"values-{freq.value}"] = AreaOutputSeriesMatrix( self.context, - self.config.next_file(f"values-{timing}.txt"), - timing, + self.config.next_file(f"values-{freq.value}.txt"), + freq, self.set, ) diff --git a/tests/storage/repository/filesystem/matrix/output_series_matrix_test.py b/tests/storage/repository/filesystem/matrix/output_series_matrix_test.py index cf4c521458..ccf7c2bb82 100644 --- a/tests/storage/repository/filesystem/matrix/output_series_matrix_test.py +++ b/tests/storage/repository/filesystem/matrix/output_series_matrix_test.py @@ -9,10 +9,25 @@ from antarest.study.storage.rawstudy.model.filesystem.matrix.head_writer import ( AreaHeadWriter, ) +from antarest.study.storage.rawstudy.model.filesystem.matrix.matrix import ( + MatrixFrequency, +) from antarest.study.storage.rawstudy.model.filesystem.matrix.output_series_matrix import ( OutputSeriesMatrix, ) +MATRIX_DAILY_DATA = """\ +DE area va hourly +\tVARIABLES\tBEGIN\tEND +\t2\t1\t2 + +DE\thourly\t\t\t\t01_solar\t02_wind_on +\t\t\t\t\tMWh\tMWh +\tindex\tday\tmonth\thourly\tEXP\tEXP +\t1\t1\tJAN\t00:00\t27000\t600 +\t2\t1\tJAN\t01:00\t48000\t34400 +""" + def test_get(tmp_path: Path): file = tmp_path / "matrix-daily.txt" @@ -45,9 +60,9 @@ def test_get(tmp_path: Path): node = OutputSeriesMatrix( context=Mock(), config=config, + freq=MatrixFrequency.DAILY, date_serializer=serializer, head_writer=AreaHeadWriter(area="", data_type="", freq=""), - freq="", ) assert node.load() == matrix.to_dict(orient="split") @@ -72,9 +87,9 @@ def test_save(tmp_path: Path): node = OutputSeriesMatrix( context=Mock(), config=config, + freq=MatrixFrequency.DAILY, date_serializer=serializer, head_writer=AreaHeadWriter(area="de", data_type="va", freq="hourly"), - freq="", ) matrix = pd.DataFrame( @@ -86,17 +101,5 @@ def test_save(tmp_path: Path): ) node.dump(matrix.to_dict(orient="split")) - print(file.read_text()) - assert ( - file.read_text() - == """DE area va hourly - VARIABLES BEGIN END - 2 1 2 - -DE hourly 01_solar 02_wind_on - MWh MWh - index day month hourly EXP EXP - 1 1 JAN 00:00 27000 600 - 2 1 JAN 01:00 48000 34400 -""" - ) + actual = file.read_text() + assert actual == MATRIX_DAILY_DATA diff --git a/tests/storage/repository/filesystem/matrix/test_matrix_node.py b/tests/storage/repository/filesystem/matrix/test_matrix_node.py index 2849a114bd..35aa1d3dc8 100644 --- a/tests/storage/repository/filesystem/matrix/test_matrix_node.py +++ b/tests/storage/repository/filesystem/matrix/test_matrix_node.py @@ -1,9 +1,10 @@ -import json from pathlib import Path from tempfile import TemporaryDirectory -from typing import Optional, List +from typing import List, Optional from unittest.mock import Mock +import pandas as pd # type: ignore + from antarest.core.model import JSON from antarest.study.storage.rawstudy.model.filesystem.config.model import ( FileStudyTreeConfig, @@ -12,6 +13,7 @@ ContextServer, ) from antarest.study.storage.rawstudy.model.filesystem.matrix.matrix import ( + MatrixFrequency, MatrixNode, ) @@ -21,7 +23,6 @@ "data": [[1, 2], [3, 4]], } - MOCK_MATRIX_DTO = [[1, 2], [3, 4]] @@ -29,7 +30,11 @@ class MockMatrixNode(MatrixNode): def __init__( self, context: ContextServer, config: FileStudyTreeConfig ) -> None: - super().__init__(config=config, context=context, freq="annual") + super().__init__( + config=config, + context=context, + freq=MatrixFrequency.ANNUAL, + ) def parse( self, @@ -39,8 +44,14 @@ def parse( ) -> JSON: return MOCK_MATRIX_JSON - def _dump_json(self, data: JSON) -> None: - json.dump(data, self.config.path.open("w")) + # def dump( + # self, data: Union[bytes, JSON], url: Optional[List[str]] = None + # ) -> None: + # """Dump the matrix data in JSON format to simplify the tests""" + # self.config.path.parent.mkdir(exist_ok=True, parents=True) + # self.config.path.write_text( + # json.dumps(data, indent=2), encoding="utf-8" + # ) def check_errors( self, data: str, url: Optional[List[str]] = None, raising: bool = False @@ -48,46 +59,51 @@ def check_errors( pass # not used -def test_normalize(tmp_path: Path): - file = tmp_path / "matrix.txt" - file.touch() +class TestMatrixNode: + def test_normalize(self, tmp_path: Path): + file = tmp_path / "matrix.json" + file.touch() + + matrix_service = Mock() + matrix_service.create.return_value = "my-id" - matrix_service = Mock() - matrix_service.create.return_value = "my-id" + resolver = Mock() + resolver.build_matrix_uri.return_value = "matrix://my-id" - resolver = Mock() - resolver.build_matrix_uri.return_value = "matrix://my-id" + node = MockMatrixNode( + context=ContextServer(matrix=matrix_service, resolver=resolver), + config=FileStudyTreeConfig( + study_path=file, path=file, study_id="mi-id", version=-1 + ), + ) - node = MockMatrixNode( - context=ContextServer(matrix=matrix_service, resolver=resolver), - config=FileStudyTreeConfig( - study_path=file, path=file, study_id="mi-id", version=-1 - ), - ) + node.normalize() - node.normalize() - assert node.get_link_path().read_text() == "matrix://my-id" - assert not file.exists() - matrix_service.create.assert_called_once_with(MOCK_MATRIX_DTO) - resolver.build_matrix_uri.assert_called_once_with("my-id") + # check the result + assert node.get_link_path().read_text() == "matrix://my-id" + assert not file.exists() + matrix_service.create.assert_called_once_with(MOCK_MATRIX_DTO) + resolver.build_matrix_uri.assert_called_once_with("my-id") + def test_denormalize(self, tmp_path: Path): + file = tmp_path / "matrix.json" -def test_denormalize(tmp_path: Path): - file = tmp_path / "matrix.txt" + link = file.parent / f"{file.name}.link" + link.write_text("my-id") - link = file.parent / f"{file.name}.link" - link.write_text("my-id") + resolver = Mock() + resolver.resolve.return_value = MOCK_MATRIX_JSON - resolver = Mock() - resolver.resolve.return_value = MOCK_MATRIX_JSON + node = MockMatrixNode( + context=ContextServer(matrix=Mock(), resolver=resolver), + config=FileStudyTreeConfig( + study_path=file, path=file, study_id="mi-id", version=-1 + ), + ) - node = MockMatrixNode( - context=ContextServer(matrix=Mock(), resolver=resolver), - config=FileStudyTreeConfig( - study_path=file, path=file, study_id="mi-id", version=-1 - ), - ) + node.denormalize() - node.denormalize() - assert not link.exists() - assert json.loads(file.read_text()) == MOCK_MATRIX_JSON + # check the result + assert not link.exists() + actual = pd.read_csv(file, sep="\t", header=None) + assert actual.values.tolist() == MOCK_MATRIX_JSON["data"] diff --git a/tests/storage/repository/filesystem/root/__init__.py b/tests/storage/repository/filesystem/root/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/storage/repository/filesystem/root/input/__init__.py b/tests/storage/repository/filesystem/root/input/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/storage/repository/filesystem/root/input/hydro/__init__.py b/tests/storage/repository/filesystem/root/input/hydro/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/storage/repository/filesystem/root/input/hydro/common/__init__.py b/tests/storage/repository/filesystem/root/input/hydro/common/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/storage/repository/filesystem/root/input/hydro/common/capacity/__init__.py b/tests/storage/repository/filesystem/root/input/hydro/common/capacity/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/storage/repository/filesystem/root/input/hydro/common/capacity/test_capacity.py b/tests/storage/repository/filesystem/root/input/hydro/common/capacity/test_capacity.py new file mode 100644 index 0000000000..ed55f0c005 --- /dev/null +++ b/tests/storage/repository/filesystem/root/input/hydro/common/capacity/test_capacity.py @@ -0,0 +1,106 @@ +import uuid +from pathlib import Path +from unittest.mock import Mock + +import pytest + +from antarest.matrixstore.service import ISimpleMatrixService +from antarest.matrixstore.uri_resolver_service import UriResolverService +from antarest.study.storage.rawstudy.model.filesystem.config.model import ( + Area, + FileStudyTreeConfig, +) +from antarest.study.storage.rawstudy.model.filesystem.context import ( + ContextServer, +) +from antarest.study.storage.rawstudy.model.filesystem.matrix.input_series_matrix import ( + InputSeriesMatrix, +) +from antarest.study.storage.rawstudy.model.filesystem.matrix.matrix import ( + MatrixFrequency, +) +from antarest.study.storage.rawstudy.model.filesystem.root.input.hydro.common.capacity import ( + capacity, +) + + +# noinspection SpellCheckingInspection +BEFORE_650 = { + # fmt: off + "maxpower_en": {"default_empty": None, "freq": MatrixFrequency.HOURLY, "nb_columns": None}, + "maxpower_fr": {"default_empty": None, "freq": MatrixFrequency.HOURLY, "nb_columns": None}, + "reservoir_en": {"default_empty": None, "freq": MatrixFrequency.DAILY, "nb_columns": None}, + "reservoir_fr": {"default_empty": None, "freq": MatrixFrequency.DAILY, "nb_columns": None}, + # fmt: on +} + +# noinspection SpellCheckingInspection +AFTER_650 = { + # fmt: off + "creditmodulations_en": {"default_empty": None, "freq": MatrixFrequency.HOURLY, "nb_columns": None}, + "creditmodulations_fr": {"default_empty": None, "freq": MatrixFrequency.HOURLY, "nb_columns": None}, + "inflowPattern_en": {"default_empty": None, "freq": MatrixFrequency.HOURLY, "nb_columns": None}, + "inflowPattern_fr": {"default_empty": None, "freq": MatrixFrequency.HOURLY, "nb_columns": None}, + "maxpower_en": {"default_empty": None, "freq": MatrixFrequency.HOURLY, "nb_columns": None}, + "maxpower_fr": {"default_empty": None, "freq": MatrixFrequency.HOURLY, "nb_columns": None}, + "reservoir_en": {"default_empty": None, "freq": MatrixFrequency.DAILY, "nb_columns": None}, + "reservoir_fr": {"default_empty": None, "freq": MatrixFrequency.DAILY, "nb_columns": None}, + "waterValues_en": {"default_empty": None, "freq": MatrixFrequency.DAILY, "nb_columns": None}, + "waterValues_fr": {"default_empty": None, "freq": MatrixFrequency.DAILY, "nb_columns": None}, + # fmt: on +} + + +class TestInputHydroCommonCapacity: + @pytest.mark.parametrize( + "version, expected", + [ + pytest.param("000", BEFORE_650, id="before-650"), + pytest.param("650", AFTER_650, id="after-650"), + ], + ) + def test_build_input_hydro_common_capacity( + self, + version: str, + expected: dict, + ): + matrix = Mock(spec=ISimpleMatrixService) + resolver = Mock(spec=UriResolverService) + context = ContextServer(matrix=matrix, resolver=resolver) + study_id = str(uuid.uuid4()) + config = FileStudyTreeConfig( + study_path=Path("path/to/study"), + path=Path("path/to/study"), + study_id=study_id, + version=int(version), # will become a `str` in the future + areas={ + name: Area( + name=name.upper(), + links={}, + thermals=[], + renewables=[], + filters_synthesis=[], + filters_year=[], + ) + for name in ["fr", "en"] + }, + ) + + node = capacity.InputHydroCommonCapacity( + context=context, + config=config, + children_glob_exceptions=None, + ) + actual = node.build() + + # check the result + value: InputSeriesMatrix + actual_obj = { + key: { + "default_empty": value.default_empty, + "freq": value.freq, + "nb_columns": value.nb_columns, + } + for key, value in actual.items() + } + assert actual_obj == expected diff --git a/tests/storage/repository/filesystem/root/input/hydro/series/__init__.py b/tests/storage/repository/filesystem/root/input/hydro/series/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/storage/repository/filesystem/root/input/hydro/series/area/__init__.py b/tests/storage/repository/filesystem/root/input/hydro/series/area/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/storage/repository/filesystem/root/input/hydro/series/area/test_area.py b/tests/storage/repository/filesystem/root/input/hydro/series/area/test_area.py new file mode 100644 index 0000000000..a774dae29f --- /dev/null +++ b/tests/storage/repository/filesystem/root/input/hydro/series/area/test_area.py @@ -0,0 +1,100 @@ +import uuid +from pathlib import Path +from unittest.mock import Mock + +import pytest + +from antarest.matrixstore.service import ISimpleMatrixService +from antarest.matrixstore.uri_resolver_service import UriResolverService +from antarest.study.storage.rawstudy.model.filesystem.config.model import ( + FileStudyTreeConfig, +) +from antarest.study.storage.rawstudy.model.filesystem.context import ( + ContextServer, +) +from antarest.study.storage.rawstudy.model.filesystem.matrix.constants import ( + default_scenario_monthly, + default_scenario_hourly, + default_scenario_daily, +) +from antarest.study.storage.rawstudy.model.filesystem.matrix.input_series_matrix import ( + InputSeriesMatrix, +) +from antarest.study.storage.rawstudy.model.filesystem.matrix.matrix import ( + MatrixFrequency, +) +from antarest.study.storage.rawstudy.model.filesystem.root.input.hydro.series.area import ( + area, +) + + +BEFORE_650 = { + "mod": { + "default_empty": default_scenario_monthly, + "freq": MatrixFrequency.MONTHLY, + "nb_columns": None, + }, + "ror": { + "default_empty": default_scenario_hourly, + "freq": MatrixFrequency.HOURLY, + "nb_columns": None, + }, +} + +AFTER_650 = { + "mod": { + "default_empty": default_scenario_daily, + "freq": MatrixFrequency.DAILY, + "nb_columns": None, + }, + "ror": { + "default_empty": default_scenario_hourly, + "freq": MatrixFrequency.HOURLY, + "nb_columns": None, + }, +} + + +class TestInputHydroSeriesArea: + @pytest.mark.parametrize( + "version, expected", + [ + pytest.param("000", BEFORE_650, id="before-650"), + pytest.param("650", AFTER_650, id="after-650"), + ], + ) + def test_build_input_hydro_series_area( + self, + version: str, + expected: dict, + ): + matrix = Mock(spec=ISimpleMatrixService) + resolver = Mock(spec=UriResolverService) + context = ContextServer(matrix=matrix, resolver=resolver) + study_id = str(uuid.uuid4()) + config = FileStudyTreeConfig( + study_path=Path("path/to/study"), + path=Path("path/to/study"), + study_id=study_id, + version=int(version), # will become a `str` in the future + areas={}, + ) + + node = area.InputHydroSeriesArea( + context=context, + config=config, + children_glob_exceptions=None, + ) + actual = node.build() + + # check the result + value: InputSeriesMatrix + actual_obj = { + key: { + "default_empty": value.default_empty, + "freq": value.freq, + "nb_columns": value.nb_columns, + } + for key, value in actual.items() + } + assert actual_obj == expected diff --git a/tests/storage/repository/filesystem/root/output/__init__.py b/tests/storage/repository/filesystem/root/output/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/storage/repository/filesystem/root/output/simulation/__init__.py b/tests/storage/repository/filesystem/root/output/simulation/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/storage/repository/filesystem/root/output/simulation/mode/__init__.py b/tests/storage/repository/filesystem/root/output/simulation/mode/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/storage/repository/filesystem/root/output/simulation/mode/common/__init__.py b/tests/storage/repository/filesystem/root/output/simulation/mode/common/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/storage/repository/filesystem/root/output/simulation/mode/common/test_area.py b/tests/storage/repository/filesystem/root/output/simulation/mode/common/test_area.py new file mode 100644 index 0000000000..e2e9783e40 --- /dev/null +++ b/tests/storage/repository/filesystem/root/output/simulation/mode/common/test_area.py @@ -0,0 +1,107 @@ +import uuid +from pathlib import Path +from unittest.mock import Mock + +import pytest + +from antarest.matrixstore.service import ISimpleMatrixService +from antarest.matrixstore.uri_resolver_service import UriResolverService +from antarest.study.storage.rawstudy.model.filesystem.config.model import ( + FileStudyTreeConfig, +) +from antarest.study.storage.rawstudy.model.filesystem.context import ( + ContextServer, +) +from antarest.study.storage.rawstudy.model.filesystem.matrix.matrix import ( + MatrixFrequency, +) +from antarest.study.storage.rawstudy.model.filesystem.matrix.output_series_matrix import ( + AreaOutputSeriesMatrix, +) +from antarest.study.storage.rawstudy.model.filesystem.root.output.simulation.mode.common import ( + area, +) + +# noinspection SpellCheckingInspection +MC_ALL_TRUE = { + "details-annual": {"freq": MatrixFrequency.ANNUAL}, + "details-daily": {"freq": MatrixFrequency.DAILY}, + "details-hourly": {"freq": MatrixFrequency.HOURLY}, + "details-monthly": {"freq": MatrixFrequency.MONTHLY}, + "details-res-annual": {"freq": MatrixFrequency.ANNUAL}, + "details-res-daily": {"freq": MatrixFrequency.DAILY}, + "details-res-hourly": {"freq": MatrixFrequency.HOURLY}, + "details-res-monthly": {"freq": MatrixFrequency.MONTHLY}, + "details-res-weekly": {"freq": MatrixFrequency.WEEKLY}, + "details-weekly": {"freq": MatrixFrequency.WEEKLY}, + "id-annual": {"freq": MatrixFrequency.ANNUAL}, + "id-daily": {"freq": MatrixFrequency.DAILY}, + "id-hourly": {"freq": MatrixFrequency.HOURLY}, + "id-monthly": {"freq": MatrixFrequency.MONTHLY}, + "id-weekly": {"freq": MatrixFrequency.WEEKLY}, + "values-annual": {"freq": MatrixFrequency.ANNUAL}, + "values-daily": {"freq": MatrixFrequency.DAILY}, + "values-hourly": {"freq": MatrixFrequency.HOURLY}, + "values-monthly": {"freq": MatrixFrequency.MONTHLY}, + "values-weekly": {"freq": MatrixFrequency.WEEKLY}, +} + +# noinspection SpellCheckingInspection +MC_ALL_FALSE = { + "details-annual": {"freq": MatrixFrequency.ANNUAL}, + "details-daily": {"freq": MatrixFrequency.DAILY}, + "details-hourly": {"freq": MatrixFrequency.HOURLY}, + "details-monthly": {"freq": MatrixFrequency.MONTHLY}, + "details-res-annual": {"freq": MatrixFrequency.ANNUAL}, + "details-res-daily": {"freq": MatrixFrequency.DAILY}, + "details-res-hourly": {"freq": MatrixFrequency.HOURLY}, + "details-res-monthly": {"freq": MatrixFrequency.MONTHLY}, + "details-res-weekly": {"freq": MatrixFrequency.WEEKLY}, + "details-weekly": {"freq": MatrixFrequency.WEEKLY}, + "values-annual": {"freq": MatrixFrequency.ANNUAL}, + "values-daily": {"freq": MatrixFrequency.DAILY}, + "values-hourly": {"freq": MatrixFrequency.HOURLY}, + "values-monthly": {"freq": MatrixFrequency.MONTHLY}, + "values-weekly": {"freq": MatrixFrequency.WEEKLY}, +} + + +class TestOutputSimulationAreaItem: + @pytest.mark.parametrize( + "mc_all, expected", + [ + pytest.param(True, MC_ALL_TRUE, id="mc-all-True"), + pytest.param(False, MC_ALL_FALSE, id="mc-all-False"), + ], + ) + def test_build_output_simulation_area_item( + self, + mc_all: bool, + expected: dict, + ): + matrix = Mock(spec=ISimpleMatrixService) + resolver = Mock(spec=UriResolverService) + context = ContextServer(matrix=matrix, resolver=resolver) + study_id = str(uuid.uuid4()) + config = FileStudyTreeConfig( + study_path=Path("path/to/study"), + path=Path("path/to/study"), + study_id=study_id, + version=850, # will become a `str` in the future + areas={}, + ) + + node = area.OutputSimulationAreaItem( + context=context, + config=config, + area="fr", + mc_all=mc_all, + ) + actual = node.build() + + # check the result + value: AreaOutputSeriesMatrix + actual_obj = { + key: {"freq": value.freq} for key, value in actual.items() + } + assert actual_obj == expected diff --git a/tests/storage/repository/filesystem/root/output/simulation/mode/common/test_binding_const.py b/tests/storage/repository/filesystem/root/output/simulation/mode/common/test_binding_const.py new file mode 100644 index 0000000000..17ec74d716 --- /dev/null +++ b/tests/storage/repository/filesystem/root/output/simulation/mode/common/test_binding_const.py @@ -0,0 +1,70 @@ +import uuid +from pathlib import Path +from unittest.mock import Mock + +import pytest + +from antarest.matrixstore.service import ISimpleMatrixService +from antarest.matrixstore.uri_resolver_service import UriResolverService +from antarest.study.storage.rawstudy.model.filesystem.config.model import ( + FileStudyTreeConfig, +) +from antarest.study.storage.rawstudy.model.filesystem.context import ( + ContextServer, +) +from antarest.study.storage.rawstudy.model.filesystem.matrix.matrix import ( + MatrixFrequency, +) +from antarest.study.storage.rawstudy.model.filesystem.matrix.output_series_matrix import ( + BindingConstraintOutputSeriesMatrix, +) +from antarest.study.storage.rawstudy.model.filesystem.root.output.simulation.mode.common import ( + binding_const, +) + +# noinspection SpellCheckingInspection +NOMINAL_CASE = { + "binding-constraints-annual": {"freq": MatrixFrequency.ANNUAL}, + "binding-constraints-daily": {"freq": MatrixFrequency.DAILY}, + "binding-constraints-hourly": {"freq": MatrixFrequency.HOURLY}, + "binding-constraints-monthly": {"freq": MatrixFrequency.MONTHLY}, + "binding-constraints-weekly": {"freq": MatrixFrequency.WEEKLY}, +} + + +class TestOutputSimulationBindingConstraintItem: + @pytest.mark.parametrize( + "expected", + [ + pytest.param(NOMINAL_CASE, id="nominal-case-True"), + ], + ) + def test_build_output_simulation_binding_constraint_item( + self, + expected: dict, + ): + matrix = Mock(spec=ISimpleMatrixService) + resolver = Mock(spec=UriResolverService) + context = ContextServer(matrix=matrix, resolver=resolver) + study_id = str(uuid.uuid4()) + config = FileStudyTreeConfig( + study_path=Path("path/to/study"), + path=Path("path/to/study"), + study_id=study_id, + version=850, # will become a `str` in the future + areas={}, + ) + + node = binding_const.OutputSimulationBindingConstraintItem( + context=context, + config=config, + children_glob_exceptions=None, + ) + actual = node.build() + + # check the result + value: BindingConstraintOutputSeriesMatrix + actual_obj = { + key: {"freq": value.freq} for key, value in actual.items() + } + assert actual_obj == expected diff --git a/tests/storage/repository/filesystem/root/output/simulation/mode/common/test_link.py b/tests/storage/repository/filesystem/root/output/simulation/mode/common/test_link.py new file mode 100644 index 0000000000..6063d23d11 --- /dev/null +++ b/tests/storage/repository/filesystem/root/output/simulation/mode/common/test_link.py @@ -0,0 +1,88 @@ +import uuid +from pathlib import Path +from unittest.mock import Mock + +import pytest + +from antarest.matrixstore.service import ISimpleMatrixService +from antarest.matrixstore.uri_resolver_service import UriResolverService +from antarest.study.storage.rawstudy.model.filesystem.config.model import ( + FileStudyTreeConfig, +) +from antarest.study.storage.rawstudy.model.filesystem.context import ( + ContextServer, +) +from antarest.study.storage.rawstudy.model.filesystem.matrix.matrix import ( + MatrixFrequency, +) +from antarest.study.storage.rawstudy.model.filesystem.matrix.output_series_matrix import ( + LinkOutputSeriesMatrix, +) +from antarest.study.storage.rawstudy.model.filesystem.root.output.simulation.mode.common import ( + link, +) + +# noinspection SpellCheckingInspection +MC_ALL_TRUE = { + "id-annual": {"freq": MatrixFrequency.ANNUAL}, + "id-daily": {"freq": MatrixFrequency.DAILY}, + "id-hourly": {"freq": MatrixFrequency.HOURLY}, + "id-monthly": {"freq": MatrixFrequency.MONTHLY}, + "id-weekly": {"freq": MatrixFrequency.WEEKLY}, + "values-annual": {"freq": MatrixFrequency.ANNUAL}, + "values-daily": {"freq": MatrixFrequency.DAILY}, + "values-hourly": {"freq": MatrixFrequency.HOURLY}, + "values-monthly": {"freq": MatrixFrequency.MONTHLY}, + "values-weekly": {"freq": MatrixFrequency.WEEKLY}, +} + +# noinspection SpellCheckingInspection +MC_ALL_FALSE = { + "values-annual": {"freq": MatrixFrequency.ANNUAL}, + "values-daily": {"freq": MatrixFrequency.DAILY}, + "values-hourly": {"freq": MatrixFrequency.HOURLY}, + "values-monthly": {"freq": MatrixFrequency.MONTHLY}, + "values-weekly": {"freq": MatrixFrequency.WEEKLY}, +} + + +class TestOutputSimulationLinkItem: + @pytest.mark.parametrize( + "mc_all, expected", + [ + pytest.param(True, MC_ALL_TRUE, id="mc-all-True"), + pytest.param(False, MC_ALL_FALSE, id="mc-all-False"), + ], + ) + def test_build_output_simulation_link_item( + self, + mc_all: bool, + expected: dict, + ): + matrix = Mock(spec=ISimpleMatrixService) + resolver = Mock(spec=UriResolverService) + context = ContextServer(matrix=matrix, resolver=resolver) + study_id = str(uuid.uuid4()) + config = FileStudyTreeConfig( + study_path=Path("path/to/study"), + path=Path("path/to/study"), + study_id=study_id, + version=850, # will become a `str` in the future + areas={}, + ) + + node = link.OutputSimulationLinkItem( + context=context, + config=config, + area="fr", + link="fr -> de", + mc_all=mc_all, + ) + actual = node.build() + + # check the result + value: LinkOutputSeriesMatrix + actual_obj = { + key: {"freq": value.freq} for key, value in actual.items() + } + assert actual_obj == expected diff --git a/tests/storage/repository/filesystem/root/output/simulation/mode/common/test_set.py b/tests/storage/repository/filesystem/root/output/simulation/mode/common/test_set.py new file mode 100644 index 0000000000..03adcbe76d --- /dev/null +++ b/tests/storage/repository/filesystem/root/output/simulation/mode/common/test_set.py @@ -0,0 +1,87 @@ +import uuid +from pathlib import Path +from unittest.mock import Mock + +import pytest + +from antarest.matrixstore.service import ISimpleMatrixService +from antarest.matrixstore.uri_resolver_service import UriResolverService +from antarest.study.storage.rawstudy.model.filesystem.config.model import ( + FileStudyTreeConfig, +) +from antarest.study.storage.rawstudy.model.filesystem.context import ( + ContextServer, +) +from antarest.study.storage.rawstudy.model.filesystem.matrix.matrix import ( + MatrixFrequency, +) +from antarest.study.storage.rawstudy.model.filesystem.matrix.output_series_matrix import ( + AreaOutputSeriesMatrix, +) +from antarest.study.storage.rawstudy.model.filesystem.root.output.simulation.mode.common import ( + set, +) + +# noinspection SpellCheckingInspection +MC_ALL_TRUE = { + "id-annual": {"freq": MatrixFrequency.ANNUAL}, + "id-daily": {"freq": MatrixFrequency.DAILY}, + "id-hourly": {"freq": MatrixFrequency.HOURLY}, + "id-monthly": {"freq": MatrixFrequency.MONTHLY}, + "id-weekly": {"freq": MatrixFrequency.WEEKLY}, + "values-annual": {"freq": MatrixFrequency.ANNUAL}, + "values-daily": {"freq": MatrixFrequency.DAILY}, + "values-hourly": {"freq": MatrixFrequency.HOURLY}, + "values-monthly": {"freq": MatrixFrequency.MONTHLY}, + "values-weekly": {"freq": MatrixFrequency.WEEKLY}, +} + +# noinspection SpellCheckingInspection +MC_ALL_FALSE = { + "values-annual": {"freq": MatrixFrequency.ANNUAL}, + "values-daily": {"freq": MatrixFrequency.DAILY}, + "values-hourly": {"freq": MatrixFrequency.HOURLY}, + "values-monthly": {"freq": MatrixFrequency.MONTHLY}, + "values-weekly": {"freq": MatrixFrequency.WEEKLY}, +} + + +class TestOutputSimulationSet: + @pytest.mark.parametrize( + "mc_all, expected", + [ + pytest.param(True, MC_ALL_TRUE, id="mc-all-True"), + pytest.param(False, MC_ALL_FALSE, id="mc-all-False"), + ], + ) + def test_output_simulation_set( + self, + mc_all: bool, + expected: dict, + ): + matrix = Mock(spec=ISimpleMatrixService) + resolver = Mock(spec=UriResolverService) + context = ContextServer(matrix=matrix, resolver=resolver) + study_id = str(uuid.uuid4()) + config = FileStudyTreeConfig( + study_path=Path("path/to/study"), + path=Path("path/to/study"), + study_id=study_id, + version=850, # will become a `str` in the future + areas={}, + ) + + node = set.OutputSimulationSet( + context=context, + config=config, + set="foo", + mc_all=mc_all, + ) + actual = node.build() + + # check the result + value: AreaOutputSeriesMatrix + actual_obj = { + key: {"freq": value.freq} for key, value in actual.items() + } + assert actual_obj == expected From aa5e3e87d95b8b5061030025e89443e1fc71823d Mon Sep 17 00:00:00 2001 From: Samir Kamal <1954121+skamril@users.noreply.github.com> Date: Tue, 21 Mar 2023 15:49:27 +0100 Subject: [PATCH 09/70] feat(ui): add @total-typescript/ts-reset lib and tsUtils (#1408) --- webapp/package-lock.json | 6 ++++++ webapp/package.json | 3 ++- webapp/src/components/App/Settings/index.tsx | 15 +++++++-------- webapp/src/types/reset.d.ts | 7 +++++++ webapp/src/utils/tsUtils.ts | 3 +++ 5 files changed, 25 insertions(+), 9 deletions(-) create mode 100644 webapp/src/types/reset.d.ts create mode 100644 webapp/src/utils/tsUtils.ts diff --git a/webapp/package-lock.json b/webapp/package-lock.json index 180c77d5f1..d2ed1d5749 100644 --- a/webapp/package-lock.json +++ b/webapp/package-lock.json @@ -2648,6 +2648,12 @@ "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==" }, + "@total-typescript/ts-reset": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@total-typescript/ts-reset/-/ts-reset-0.4.2.tgz", + "integrity": "sha512-vqd7ZUDSrXFVT1n8b2kc3LnklncDQFPvR58yUS1kEP23/nHPAO9l1lMjUfnPrXYYk4Hj54rrLKMW5ipwk7k09A==", + "dev": true + }, "@trysound/sax": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", diff --git a/webapp/package.json b/webapp/package.json index acb70e12b3..78053e0415 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -48,7 +48,7 @@ "ramda-adjunct": "3.4.0", "react": "18.2.0", "react-beautiful-dnd": "13.1.1", - "react-color": "^2.19.3", + "react-color": "2.19.3", "react-d3-graph": "2.6.0", "react-dom": "18.2.0", "react-dropzone": "14.2.3", @@ -96,6 +96,7 @@ "proxy": "http://localhost:8080", "homepage": "/", "devDependencies": { + "@total-typescript/ts-reset": "0.4.2", "@types/debug": "4.1.7", "@types/js-cookie": "3.0.3", "@types/lodash": "4.14.191", diff --git a/webapp/src/components/App/Settings/index.tsx b/webapp/src/components/App/Settings/index.tsx index d460de40e5..462a3ad428 100644 --- a/webapp/src/components/App/Settings/index.tsx +++ b/webapp/src/components/App/Settings/index.tsx @@ -13,6 +13,7 @@ import { isAuthUserAdmin, isAuthUserInGroupAdmin, } from "../../../redux/selectors"; +import { tuple } from "../../../utils/tsUtils"; /** * Component @@ -26,14 +27,12 @@ function Settings() { const tabList = useMemo(() => { return [ - isUserAdmin && [t("global.users"), () => ], - (isUserAdmin || isUserInGroupAdmin) && [ - t("global.group"), - () => , - ], - [t("global.tokens"), () => ], - isUserAdmin && [t("global.maintenance"), () => ], - ].filter(Boolean) as Array<[string, () => JSX.Element]>; + isUserAdmin && tuple(t("global.users"), () => ), + (isUserAdmin || isUserInGroupAdmin) && + tuple(t("global.group"), () => ), + tuple(t("global.tokens"), () => ), + isUserAdmin && tuple(t("global.maintenance"), () => ), + ].filter(Boolean); }, [isUserAdmin, isUserInGroupAdmin, t]); //////////////////////////////////////////////////////////////// diff --git a/webapp/src/types/reset.d.ts b/webapp/src/types/reset.d.ts new file mode 100644 index 0000000000..3054362ff5 --- /dev/null +++ b/webapp/src/types/reset.d.ts @@ -0,0 +1,7 @@ +import "@total-typescript/ts-reset/dist/array-includes"; +import "@total-typescript/ts-reset/dist/array-index-of"; +import "@total-typescript/ts-reset/dist/filter-boolean"; +// TODO After the resolution of this bug https://github.com/total-typescript/ts-reset/pull/56 +// import "@total-typescript/ts-reset/dist/is-array"; +import "@total-typescript/ts-reset/dist/set-has"; +import "@total-typescript/ts-reset/dist/map-has"; diff --git a/webapp/src/utils/tsUtils.ts b/webapp/src/utils/tsUtils.ts new file mode 100644 index 0000000000..eb60713aa8 --- /dev/null +++ b/webapp/src/utils/tsUtils.ts @@ -0,0 +1,3 @@ +export function tuple(...items: T): T { + return items; +} From dfa1b2729ddb3e46f3b7f65a4a0079211da2c69c Mon Sep 17 00:00:00 2001 From: Samir Kamal <1954121+skamril@users.noreply.github.com> Date: Thu, 23 Mar 2023 11:12:18 +0100 Subject: [PATCH 10/70] feat(api): update optimization form endpoint and add adequacy patch form endpoint --- .../business/adequacy_patch_management.py | 144 ++++++++++++++++++ .../study/business/optimization_management.py | 98 +----------- antarest/study/service.py | 6 + antarest/study/web/study_data_blueprint.py | 56 ++++++- tests/integration/test_integration.py | 87 ++++++++--- 5 files changed, 273 insertions(+), 118 deletions(-) create mode 100644 antarest/study/business/adequacy_patch_management.py diff --git a/antarest/study/business/adequacy_patch_management.py b/antarest/study/business/adequacy_patch_management.py new file mode 100644 index 0000000000..54ea098057 --- /dev/null +++ b/antarest/study/business/adequacy_patch_management.py @@ -0,0 +1,144 @@ +from enum import Enum +from typing import Optional, List, Any, Dict + +from pydantic.types import StrictBool, StrictFloat, StrictInt + +from antarest.study.business.utils import ( + FormFieldsBaseModel, + execute_or_add_commands, + FieldInfo, + GENERAL_DATA_PATH, +) +from antarest.study.model import Study +from antarest.study.storage.storage_service import StudyStorageService +from antarest.study.storage.variantstudy.model.command.update_config import ( + UpdateConfig, +) + + +class PriceTakingOrder(str, Enum): + DENS = "DENS" + LOAD = "Load" + + +class AdequacyPatchFormFields(FormFieldsBaseModel): + # version 830 + enable_adequacy_patch: Optional[StrictBool] + ntc_from_physical_areas_out_to_physical_areas_in_adequacy_patch: Optional[ + StrictBool + ] + ntc_between_physical_areas_out_adequacy_patch: Optional[StrictBool] + # version 850 + price_taking_order: Optional[PriceTakingOrder] + include_hurdle_cost_csr: Optional[StrictBool] + check_csr_cost_function: Optional[StrictBool] + threshold_initiate_curtailment_sharing_rule: Optional[StrictFloat] + threshold_display_local_matching_rule_violations: Optional[StrictFloat] + threshold_csr_variable_bounds_relaxation: Optional[StrictInt] + + +ADEQUACY_PATCH_PATH = f"{GENERAL_DATA_PATH}/adequacy patch" + + +FIELDS_INFO: Dict[str, FieldInfo] = { + "enable_adequacy_patch": { + "path": f"{ADEQUACY_PATCH_PATH}/include-adq-patch", + "default_value": False, + "start_version": 830, + }, + "ntc_from_physical_areas_out_to_physical_areas_in_adequacy_patch": { + "path": f"{ADEQUACY_PATCH_PATH}/set-to-null-ntc-from-physical-out-to-physical-in-for-first-step", + "default_value": True, + "start_version": 830, + }, + "ntc_between_physical_areas_out_adequacy_patch": { + "path": f"{ADEQUACY_PATCH_PATH}/set-to-null-ntc-between-physical-out-for-first-step", + "default_value": True, + "start_version": 830, + }, + "price_taking_order": { + "path": f"{ADEQUACY_PATCH_PATH}/price-taking-order", + "default_value": PriceTakingOrder.DENS.value, + "start_version": 850, + }, + "include_hurdle_cost_csr": { + "path": f"{ADEQUACY_PATCH_PATH}/include-hurdle-cost-csr", + "default_value": False, + "start_version": 850, + }, + "check_csr_cost_function": { + "path": f"{ADEQUACY_PATCH_PATH}/check-csr-cost-function", + "default_value": False, + "start_version": 850, + }, + "threshold_initiate_curtailment_sharing_rule": { + "path": f"{ADEQUACY_PATCH_PATH}/threshold-initiate-curtailment-sharing-rule", + "default_value": 0.0, + "start_version": 850, + }, + "threshold_display_local_matching_rule_violations": { + "path": f"{ADEQUACY_PATCH_PATH}/threshold-display-local-matching-rule-violations", + "default_value": 0.0, + "start_version": 850, + }, + "threshold_csr_variable_bounds_relaxation": { + "path": f"{ADEQUACY_PATCH_PATH}/threshold-csr-variable-bounds-relaxation", + "default_value": 3, + "start_version": 850, + }, +} + + +class AdequacyPatchManager: + def __init__(self, storage_service: StudyStorageService) -> None: + self.storage_service = storage_service + + def get_field_values(self, study: Study) -> AdequacyPatchFormFields: + """ + Get adequacy patch field values for the webapp form + """ + file_study = self.storage_service.get_storage(study).get_raw(study) + general_data = file_study.tree.get(GENERAL_DATA_PATH.split("/")) + parent = general_data.get("adequacy patch", {}) + + def get_value(field_info: FieldInfo) -> Any: + path = field_info["path"] + start_version = field_info.get("start_version", -1) + target_name = path.split("/")[-1] + is_in_version = file_study.config.version >= start_version # type: ignore + + return ( + parent.get(target_name, field_info["default_value"]) + if is_in_version + else None + ) + + return AdequacyPatchFormFields.construct( + **{name: get_value(info) for name, info in FIELDS_INFO.items()} + ) + + def set_field_values( + self, study: Study, field_values: AdequacyPatchFormFields + ) -> None: + """ + Set adequacy patch config from the webapp form + """ + commands: List[UpdateConfig] = [] + + for field_name, value in field_values.__iter__(): + if value is not None: + info = FIELDS_INFO[field_name] + + commands.append( + UpdateConfig( + target=info["path"], + data=value, + command_context=self.storage_service.variant_study_service.command_factory.command_context, + ) + ) + + if commands: + file_study = self.storage_service.get_storage(study).get_raw(study) + execute_or_add_commands( + study, file_study, commands, self.storage_service + ) diff --git a/antarest/study/business/optimization_management.py b/antarest/study/business/optimization_management.py index 6c6daa85bf..401aed6497 100644 --- a/antarest/study/business/optimization_management.py +++ b/antarest/study/business/optimization_management.py @@ -1,7 +1,7 @@ from enum import Enum from typing import Optional, Union, List, Any, Dict -from pydantic.types import StrictBool, StrictFloat, StrictInt +from pydantic.types import StrictBool from antarest.study.business.utils import ( FormFieldsBaseModel, @@ -16,11 +16,6 @@ ) -class LinkType(str, Enum): - LOCAL = "local" - AC = "ac" - - class LegacyTransmissionCapacities(str, Enum): INFINITE = "infinite" @@ -45,11 +40,6 @@ class SimplexOptimizationRange(str, Enum): WEEK = "week" -class PriceTakingOrder(str, Enum): - DENS = "DENS" - LOAD = "Load" - - class OptimizationFormFields(FormFieldsBaseModel): binding_constraints: Optional[StrictBool] hurdle_costs: Optional[StrictBool] @@ -59,7 +49,6 @@ class OptimizationFormFields(FormFieldsBaseModel): Union[LegacyTransmissionCapacities, TransmissionCapacities], ] ] - link_type: Optional[LinkType] thermal_clusters_min_stable_power: Optional[StrictBool] thermal_clusters_min_ud_time: Optional[StrictBool] day_ahead_reserve: Optional[StrictBool] @@ -69,24 +58,9 @@ class OptimizationFormFields(FormFieldsBaseModel): export_mps: Optional[Union[bool, str]] unfeasible_problem_behavior: Optional[UnfeasibleProblemBehavior] simplex_optimization_range: Optional[SimplexOptimizationRange] - # version 830 - split_exported_mps: Optional[StrictBool] - enable_adequacy_patch: Optional[StrictBool] - ntc_from_physical_areas_out_to_physical_areas_in_adequacy_patch: Optional[ - StrictBool - ] - ntc_between_physical_areas_out_adequacy_patch: Optional[StrictBool] - # version 850 - price_taking_order: Optional[PriceTakingOrder] - include_hurdle_cost_csr: Optional[StrictBool] - check_csr_cost_function: Optional[StrictBool] - threshold_initiate_curtailment_sharing_rule: Optional[StrictFloat] - threshold_display_local_matching_rule_violations: Optional[StrictFloat] - threshold_csr_variable_bounds_relaxation: Optional[StrictInt] OPTIMIZATION_PATH = f"{GENERAL_DATA_PATH}/optimization" -ADEQUACY_PATCH_PATH = f"{GENERAL_DATA_PATH}/adequacy patch" FIELDS_INFO: Dict[str, FieldInfo] = { @@ -102,10 +76,6 @@ class OptimizationFormFields(FormFieldsBaseModel): "path": f"{OPTIMIZATION_PATH}/transmission-capacities", "default_value": True, }, - "link_type": { - "path": f"{OPTIMIZATION_PATH}/link-type", - "default_value": LinkType.LOCAL, - }, "thermal_clusters_min_stable_power": { "path": f"{OPTIMIZATION_PATH}/include-tc-minstablepower", "default_value": True, @@ -136,62 +106,11 @@ class OptimizationFormFields(FormFieldsBaseModel): }, "unfeasible_problem_behavior": { "path": f"{OPTIMIZATION_PATH}/include-unfeasible-problem-behavior", - "default_value": UnfeasibleProblemBehavior.ERROR_VERBOSE, + "default_value": UnfeasibleProblemBehavior.ERROR_VERBOSE.value, }, "simplex_optimization_range": { "path": f"{OPTIMIZATION_PATH}/simplex-range", - "default_value": SimplexOptimizationRange.WEEK, - }, - "split_exported_mps": { - "path": f"{OPTIMIZATION_PATH}/include-split-exported-mps", - "default_value": False, - "start_version": 830, - "end_version": 840, - }, - "enable_adequacy_patch": { - "path": f"{ADEQUACY_PATCH_PATH}/include-adq-patch", - "default_value": False, - "start_version": 830, - }, - "ntc_from_physical_areas_out_to_physical_areas_in_adequacy_patch": { - "path": f"{ADEQUACY_PATCH_PATH}/set-to-null-ntc-from-physical-out-to-physical-in-for-first-step", - "default_value": True, - "start_version": 830, - }, - "ntc_between_physical_areas_out_adequacy_patch": { - "path": f"{ADEQUACY_PATCH_PATH}/set-to-null-ntc-between-physical-out-for-first-step", - "default_value": True, - "start_version": 830, - }, - "price_taking_order": { - "path": f"{ADEQUACY_PATCH_PATH}/price-taking-order", - "default_value": "DENS", - "start_version": 850, - }, - "include_hurdle_cost_csr": { - "path": f"{ADEQUACY_PATCH_PATH}/include-hurdle-cost-csr", - "default_value": False, - "start_version": 850, - }, - "check_csr_cost_function": { - "path": f"{ADEQUACY_PATCH_PATH}/check-csr-cost-function", - "default_value": False, - "start_version": 850, - }, - "threshold_initiate_curtailment_sharing_rule": { - "path": f"{ADEQUACY_PATCH_PATH}/threshold-initiate-curtailment-sharing-rule", - "default_value": 0.0, - "start_version": 850, - }, - "threshold_display_local_matching_rule_violations": { - "path": f"{ADEQUACY_PATCH_PATH}/threshold-display-local-matching-rule-violations", - "default_value": 0.0, - "start_version": 850, - }, - "threshold_csr_variable_bounds_relaxation": { - "path": f"{ADEQUACY_PATCH_PATH}/threshold-csr-variable-bounds-relaxation", - "default_value": 3, - "start_version": 850, + "default_value": SimplexOptimizationRange.WEEK.value, }, } @@ -202,21 +121,18 @@ def __init__(self, storage_service: StudyStorageService) -> None: def get_field_values(self, study: Study) -> OptimizationFormFields: """ - Get Optimization field values for the webapp form + Get optimization field values for the webapp form """ file_study = self.storage_service.get_storage(study).get_raw(study) general_data = file_study.tree.get(GENERAL_DATA_PATH.split("/")) - optimization = general_data.get("optimization", {}) - adequacy_patch = general_data.get("adequacy patch", {}) + parent = general_data.get("optimization", {}) def get_value(field_info: FieldInfo) -> Any: path = field_info["path"] start_version = field_info.get("start_version", -1) target_name = path.split("/")[-1] is_in_version = file_study.config.version >= start_version # type: ignore - parent = ( - optimization if OPTIMIZATION_PATH in path else adequacy_patch - ) + return ( parent.get(target_name, field_info["default_value"]) if is_in_version @@ -231,7 +147,7 @@ def set_field_values( self, study: Study, field_values: OptimizationFormFields ) -> None: """ - Set Optimization config from the webapp form + Set optimization config from the webapp form """ commands: List[UpdateConfig] = [] diff --git a/antarest/study/service.py b/antarest/study/service.py index d4bf5144cf..aa0aad2dce 100644 --- a/antarest/study/service.py +++ b/antarest/study/service.py @@ -53,6 +53,9 @@ MatrixEditInstructionDTO, ) from antarest.matrixstore.utils import parse_tsv_matrix +from antarest.study.business.adequacy_patch_management import ( + AdequacyPatchManager, +) from antarest.study.business.advanced_parameters_management import ( AdvancedParamsManager, ) @@ -303,6 +306,9 @@ def __init__( self.storage_service ) self.optimization_manager = OptimizationManager(self.storage_service) + self.adequacy_patch_manager = AdequacyPatchManager( + self.storage_service + ) self.advanced_parameters_manager = AdvancedParamsManager( self.storage_service ) diff --git a/antarest/study/web/study_data_blueprint.py b/antarest/study/web/study_data_blueprint.py index 83ce875852..2c9f2e3035 100644 --- a/antarest/study/web/study_data_blueprint.py +++ b/antarest/study/web/study_data_blueprint.py @@ -12,6 +12,9 @@ from antarest.matrixstore.business.matrix_editor import ( MatrixEditInstructionDTO, ) +from antarest.study.business.adequacy_patch_management import ( + AdequacyPatchFormFields, +) from antarest.study.business.advanced_parameters_management import ( AdvancedParamsFormFields, ) @@ -683,7 +686,7 @@ def set_general_form_values( @bp.get( path="/studies/{uuid}/config/optimization/form", tags=[APITag.study_data], - summary="Get Optimization config values for form", + summary="Get optimization config values for form", response_model=OptimizationFormFields, response_model_exclude_none=True, ) @@ -692,7 +695,7 @@ def get_optimization_form_values( current_user: JWTUser = Depends(auth.get_current_user), ) -> OptimizationFormFields: logger.info( - msg=f"Getting Optimization management config for study {uuid}", + msg=f"Getting optimization config for study {uuid}", extra={"user": current_user.id}, ) params = RequestParameters(user=current_user) @@ -705,7 +708,7 @@ def get_optimization_form_values( @bp.put( path="/studies/{uuid}/config/optimization/form", tags=[APITag.study_data], - summary="Set Optimization config with values from form", + summary="Set optimization config with values from form", ) def set_optimization_form_values( uuid: str, @@ -713,7 +716,7 @@ def set_optimization_form_values( current_user: JWTUser = Depends(auth.get_current_user), ) -> None: logger.info( - f"Updating Optimization management config for study {uuid}", + f"Updating optimization config for study {uuid}", extra={"user": current_user.id}, ) params = RequestParameters(user=current_user) @@ -725,6 +728,51 @@ def set_optimization_form_values( study, field_values ) + @bp.get( + path="/studies/{uuid}/config/adequacypatch/form", + tags=[APITag.study_data], + summary="Get adequacy patch config values for form", + response_model=AdequacyPatchFormFields, + response_model_exclude_none=True, + ) + def get_adequacy_patch_form_values( + uuid: str, + current_user: JWTUser = Depends(auth.get_current_user), + ) -> AdequacyPatchFormFields: + logger.info( + msg=f"Getting adequacy patch config for study {uuid}", + extra={"user": current_user.id}, + ) + params = RequestParameters(user=current_user) + study = study_service.check_study_access( + uuid, StudyPermissionType.READ, params + ) + + return study_service.adequacy_patch_manager.get_field_values(study) + + @bp.put( + path="/studies/{uuid}/config/adequacypatch/form", + tags=[APITag.study_data], + summary="Set adequacy patch config with values from form", + ) + def set_adequacy_patch_form_values( + uuid: str, + field_values: AdequacyPatchFormFields, + current_user: JWTUser = Depends(auth.get_current_user), + ) -> None: + logger.info( + f"Updating adequacy patch config for study {uuid}", + extra={"user": current_user.id}, + ) + params = RequestParameters(user=current_user) + study = study_service.check_study_access( + uuid, StudyPermissionType.WRITE, params + ) + + study_service.adequacy_patch_manager.set_field_values( + study, field_values + ) + @bp.get( path="/studies/{uuid}/config/timeseries/form", tags=[APITag.study_data], diff --git a/tests/integration/test_integration.py b/tests/integration/test_integration.py index f0fc467202..2b6ffc56a2 100644 --- a/tests/integration/test_integration.py +++ b/tests/integration/test_integration.py @@ -5,8 +5,14 @@ from unittest.mock import ANY from antarest.core.tasks.model import TaskDTO, TaskStatus +from antarest.study.business.adequacy_patch_management import PriceTakingOrder from antarest.study.business.area_management import AreaType, LayerInfoDTO from antarest.study.business.general_management import Mode +from antarest.study.business.optimization_management import ( + TransmissionCapacities, + UnfeasibleProblemBehavior, + SimplexOptimizationRange, +) from antarest.study.business.table_mode_management import ( FIELDS_INFO_BY_TYPE, AdequacyPatchMode, @@ -992,6 +998,8 @@ def test_area_management(app: FastAPI): ) assert res.status_code == 200 + # Optimization form + res_optimization_config = client.get( f"/v1/studies/{study_id}/config/optimization/form", headers={ @@ -1002,8 +1010,7 @@ def test_area_management(app: FastAPI): assert res_optimization_config_json == { "bindingConstraints": True, "hurdleCosts": True, - "transmissionCapacities": "local-values", - "linkType": "local", + "transmissionCapacities": TransmissionCapacities.LOCAL_VALUES.value, "thermalClustersMinStablePower": True, "thermalClustersMinUdTime": True, "dayAheadReserve": True, @@ -1011,18 +1018,8 @@ def test_area_management(app: FastAPI): "strategicReserve": True, "spinningReserve": True, "exportMps": False, - "unfeasibleProblemBehavior": "error-verbose", - "simplexOptimizationRange": "week", - "splitExportedMps": False, - "enableAdequacyPatch": False, - "ntcFromPhysicalAreasOutToPhysicalAreasInAdequacyPatch": True, - "ntcBetweenPhysicalAreasOutAdequacyPatch": True, - "checkCsrCostFunction": False, - "includeHurdleCostCsr": False, - "priceTakingOrder": "DENS", - "thresholdInitiateCurtailmentSharingRule": 0.0, - "thresholdDisplayLocalMatchingRuleViolations": 0.0, - "thresholdCsrVariableBoundsRelaxation": 3, + "unfeasibleProblemBehavior": UnfeasibleProblemBehavior.ERROR_VERBOSE.value, + "simplexOptimizationRange": SimplexOptimizationRange.WEEK.value, } client.put( @@ -1032,8 +1029,8 @@ def test_area_management(app: FastAPI): }, json={ "strategicReserve": False, - "unfeasibleProblemBehavior": "warning-verbose", - "ntcBetweenPhysicalAreasOutAdequacyPatch": False, + "unfeasibleProblemBehavior": UnfeasibleProblemBehavior.WARNING_VERBOSE.value, + "simplexOptimizationRange": SimplexOptimizationRange.DAY.value, }, ) res_optimization_config = client.get( @@ -1046,8 +1043,7 @@ def test_area_management(app: FastAPI): assert res_optimization_config_json == { "bindingConstraints": True, "hurdleCosts": True, - "transmissionCapacities": "local-values", - "linkType": "local", + "transmissionCapacities": TransmissionCapacities.LOCAL_VALUES.value, "thermalClustersMinStablePower": True, "thermalClustersMinUdTime": True, "dayAheadReserve": True, @@ -1055,20 +1051,63 @@ def test_area_management(app: FastAPI): "strategicReserve": False, "spinningReserve": True, "exportMps": False, - "unfeasibleProblemBehavior": "warning-verbose", - "simplexOptimizationRange": "week", - "splitExportedMps": False, + "unfeasibleProblemBehavior": UnfeasibleProblemBehavior.WARNING_VERBOSE.value, + "simplexOptimizationRange": SimplexOptimizationRange.DAY.value, + } + + # Adequacy patch form + + res_adequacy_patch_config = client.get( + f"/v1/studies/{study_id}/config/adequacypatch/form", + headers={ + "Authorization": f'Bearer {admin_credentials["access_token"]}' + }, + ) + res_adequacy_patch_config_json = res_adequacy_patch_config.json() + assert res_adequacy_patch_config_json == { "enableAdequacyPatch": False, "ntcFromPhysicalAreasOutToPhysicalAreasInAdequacyPatch": True, - "ntcBetweenPhysicalAreasOutAdequacyPatch": False, + "ntcBetweenPhysicalAreasOutAdequacyPatch": True, "checkCsrCostFunction": False, "includeHurdleCostCsr": False, - "priceTakingOrder": "DENS", + "priceTakingOrder": PriceTakingOrder.DENS.value, "thresholdInitiateCurtailmentSharingRule": 0.0, "thresholdDisplayLocalMatchingRuleViolations": 0.0, "thresholdCsrVariableBoundsRelaxation": 3, } + client.put( + f"/v1/studies/{study_id}/config/adequacypatch/form", + headers={ + "Authorization": f'Bearer {admin_credentials["access_token"]}' + }, + json={ + "ntcBetweenPhysicalAreasOutAdequacyPatch": False, + "priceTakingOrder": PriceTakingOrder.LOAD.value, + "thresholdDisplayLocalMatchingRuleViolations": 1.1, + }, + ) + res_adequacy_patch_config = client.get( + f"/v1/studies/{study_id}/config/adequacypatch/form", + headers={ + "Authorization": f'Bearer {admin_credentials["access_token"]}' + }, + ) + res_adequacy_patch_config_json = res_adequacy_patch_config.json() + assert res_adequacy_patch_config_json == { + "enableAdequacyPatch": False, + "ntcFromPhysicalAreasOutToPhysicalAreasInAdequacyPatch": True, + "ntcBetweenPhysicalAreasOutAdequacyPatch": False, + "checkCsrCostFunction": False, + "includeHurdleCostCsr": False, + "priceTakingOrder": PriceTakingOrder.LOAD.value, + "thresholdInitiateCurtailmentSharingRule": 0.0, + "thresholdDisplayLocalMatchingRuleViolations": 1.1, + "thresholdCsrVariableBoundsRelaxation": 3, + } + + # General form + res_general_config = client.get( f"/v1/studies/{study_id}/config/general/form", headers={ @@ -1132,6 +1171,8 @@ def test_area_management(app: FastAPI): "thematicTrimming": False, } + # Thematic trimming form + res_thematic_trimming_config = client.get( f"/v1/studies/{study_id}/config/thematictrimming/form", headers={ From f68c54b9b846d32e65d32c14c8931c625a6bd498 Mon Sep 17 00:00:00 2001 From: Samir Kamal <1954121+skamril@users.noreply.github.com> Date: Wed, 22 Mar 2023 16:52:15 +0100 Subject: [PATCH 11/70] feat(ui-config): update optimization form and add adequacy patch form --- webapp/public/locales/en/main.json | 35 +++--- webapp/public/locales/fr/main.json | 39 ++++--- .../Configuration/AdequacyPatch/Fields.tsx | 98 +++++++++++++++++ .../Configuration/AdequacyPatch/index.tsx | 43 ++++++++ .../Configuration/AdequacyPatch/utils.ts | 57 ++++++++++ .../explore/Configuration/General/Fields.tsx | 8 +- .../Configuration/Optimization/Fields.tsx | 100 ++++++------------ .../Configuration/Optimization/utils.ts | 23 ++-- .../explore/Configuration/index.tsx | 26 +++-- webapp/src/components/common/Fieldset.tsx | 6 +- 10 files changed, 311 insertions(+), 124 deletions(-) create mode 100644 webapp/src/components/App/Singlestudy/explore/Configuration/AdequacyPatch/Fields.tsx create mode 100644 webapp/src/components/App/Singlestudy/explore/Configuration/AdequacyPatch/index.tsx create mode 100644 webapp/src/components/App/Singlestudy/explore/Configuration/AdequacyPatch/utils.ts diff --git a/webapp/public/locales/en/main.json b/webapp/public/locales/en/main.json index 6c7bdc460c..e56c9b5108 100644 --- a/webapp/public/locales/en/main.json +++ b/webapp/public/locales/en/main.json @@ -265,21 +265,20 @@ "study.modelization.tableMode.dialog.add.title": "Add table", "study.modelization.tableMode.dialog.edit.title": "Edit table", "study.modelization.tableMode.dialog.delete.text": "Are you sure you want to delete '{{0}}' table?", - "study.configuration.general.simulation": "Simulation", + "study.configuration.general.legend.simulation": "Simulation", + "study.configuration.general.legend.calendar": "Calendar", + "study.configuration.general.legend.monteCarloScenarios": "Monte-Carlo Scenarios", + "study.configuration.general.legend.outputProfile": "Output profile", "study.configuration.general.mode": "Mode", "study.configuration.general.firstDay": "First day", "study.configuration.general.lastDay": "Last day", - "study.configuration.general.calendar": "Calendar", "study.configuration.general.horizon": "Horizon", "study.configuration.general.year": "Year", "study.configuration.general.week": "Week", "study.configuration.general.firstDayOfYear": "1st January", "study.configuration.general.leapYear": "Leap year", - "study.configuration.general.adequacyPatch": "Adequacy patch", - "study.configuration.general.monteCarloScenarios": "Monte-Carlo Scenarios", "study.configuration.general.buildingMode": "Building mode", "study.configuration.general.selectionMode": "Selection mode", - "study.configuration.general.outputProfile": "Output profile", "study.configuration.general.simulationSynthesis": "Simulation synthesis", "study.configuration.general.yearByYear": "Year-by-year", "study.configuration.general.mcScenario": "MC Scenario", @@ -310,25 +309,35 @@ "study.configuration.general.geographicTrimming": "Geographic trimming", "study.configuration.general.thematicTrimming": "Thematic trimming", "study.configuration.general.filtering": "Filtering", - "study.configuration.optimization.optimization": "Optimization", + "study.configuration.optimization.legend.general": "General", + "study.configuration.optimization.legend.links": "Links", + "study.configuration.optimization.legend.thermalClusters": "Thermal Clusters", + "study.configuration.optimization.legend.reserve": "Reserve", "study.configuration.optimization.bindingConstraints": "Binding constraints", "study.configuration.optimization.hurdleCosts": "Hurdle costs", "study.configuration.optimization.transmissionCapacities": "Transmission capacities", - "study.configuration.optimization.linkType": "Link type", "study.configuration.optimization.thermalClustersMinStablePower": "Thermal clusters min stable power", "study.configuration.optimization.thermalClustersMinUdTime": "Thermal clusters min UD time", "study.configuration.optimization.dayAheadReserve": "Day ahead reserve", "study.configuration.optimization.primaryReserve": "Primary reserve", "study.configuration.optimization.strategicReserve": "Strategic reserve", "study.configuration.optimization.spinningReserve": "Spinning reserve", - "study.configuration.optimization.exportMps": "Export mps", - "study.configuration.optimization.splitExportedMps": "Split exported mps", + "study.configuration.optimization.exportMps": "Export MPS", "study.configuration.optimization.unfeasibleProblemBehavior": "Unfeasible problem behavior", "study.configuration.optimization.simplexOptimizationRange": "Simplex optimization range", - "study.configuration.optimization.adequacyPatch": "Adequacy patch", - "study.configuration.optimization.enableAdequacyPatch": "Enable adequacy patch", - "study.configuration.optimization.ntcFromPhysicalAreasOutToPhysicalAreasInAdequacyPatch": "NTC from physical areas out to physical areas in adequacy patch", - "study.configuration.optimization.ntcBetweenPhysicalAreasOutAdequacyPatch": "NTC between physical areas out adequacy patch", + "study.configuration.adequacyPatch.legend.general": "General", + "study.configuration.adequacyPatch.legend.localMatchingRule": "Local matching rule", + "study.configuration.adequacyPatch.legend.curtailmentSharing": "Curtailment sharing", + "study.configuration.adequacyPatch.legend.advanced": "Advanced", + "study.configuration.adequacyPatch.enableAdequacyPatch": "Enable adequacy patch", + "study.configuration.adequacyPatch.ntcFromPhysicalAreasOutToPhysicalAreasInAdequacyPatch": "NTC from physical areas out to physical areas in adequacy patch", + "study.configuration.adequacyPatch.ntcBetweenPhysicalAreasOutAdequacyPatch": "NTC between physical areas out adequacy patch", + "study.configuration.adequacyPatch.priceTakingOrder": "Price taking order", + "study.configuration.adequacyPatch.includeHurdleCostCsr": "Include hurdle cost CSR", + "study.configuration.adequacyPatch.thresholdInitiateCurtailmentSharingRule": "Threshold initiate curtailment sharing rule", + "study.configuration.adequacyPatch.thresholdDisplayLocalMatchingRuleViolations": "Threshold display local matching rule violations", + "study.configuration.adequacyPatch.thresholdCsrVariableBoundsRelaxation": "Threshold CSR variable bounds relaxation", + "study.configuration.adequacyPatch.checkCsrCostFunction": "Check CSR cost function", "study.configuration.advancedParameters.seedsForRandomNumbers": "Seeds for random numbers", "study.configuration.advancedParameters.spatialTimeSeriesCorrelation": "Spatial time-series correlation", "study.configuration.advancedParameters.otherPreferences": "Other preferences", diff --git a/webapp/public/locales/fr/main.json b/webapp/public/locales/fr/main.json index e0d3293c73..2f89b69003 100644 --- a/webapp/public/locales/fr/main.json +++ b/webapp/public/locales/fr/main.json @@ -265,21 +265,20 @@ "study.modelization.tableMode.dialog.add.title": "Ajouter une table", "study.modelization.tableMode.dialog.edit.title": "Modifier une table", "study.modelization.tableMode.dialog.delete.text": "Êtes-vous sûr de vouloir supprimer la table '{{0}}' ?", - "study.configuration.general.simulation": "Simulation", + "study.configuration.general.legend.simulation": "Simulation", + "study.configuration.general.legend.calendar": "Calendrier", + "study.configuration.general.legend.monteCarloScenarios": "Scénarios Monte-Carlo", + "study.configuration.general.legend.outputProfile": "Profil de sortie", "study.configuration.general.mode": "Mode", "study.configuration.general.firstDay": "Premier jour", "study.configuration.general.lastDay": "Dernier jour", - "study.configuration.general.calendar": "Calendrier", "study.configuration.general.horizon": "Horizon", "study.configuration.general.year": "Année", "study.configuration.general.week": "Semaine", "study.configuration.general.firstDayOfYear": "1er Janvier", "study.configuration.general.leapYear": "Année bissextile", - "study.configuration.general.adequacyPatch": "Adequacy patch", - "study.configuration.general.monteCarloScenarios": "Monte-Carlo Scenarios", "study.configuration.general.buildingMode": "Building mode", "study.configuration.general.selectionMode": "Selection mode", - "study.configuration.general.outputProfile": "Output profile", "study.configuration.general.simulationSynthesis": "Simulation synthesis", "study.configuration.general.yearByYear": "Year-by-year", "study.configuration.general.mcScenario": "MC Scenario", @@ -310,25 +309,35 @@ "study.configuration.general.geographicTrimming": "Geographic trimming", "study.configuration.general.thematicTrimming": "Thematic trimming", "study.configuration.general.filtering": "Filtering", - "study.configuration.optimization.optimization": "Optimization", + "study.configuration.optimization.legend.general": "Générale", + "study.configuration.optimization.legend.links": "Liens", + "study.configuration.optimization.legend.thermalClusters": "Cluster thermiques", + "study.configuration.optimization.legend.reserve": "Réserve", "study.configuration.optimization.bindingConstraints": "Binding constraints", "study.configuration.optimization.hurdleCosts": "Hurdle costs", "study.configuration.optimization.transmissionCapacities": "Transmission capacities", - "study.configuration.optimization.linkType": "Link type", "study.configuration.optimization.thermalClustersMinStablePower": "Thermal clusters min stable power", "study.configuration.optimization.thermalClustersMinUdTime": "Thermal clusters min UD time", "study.configuration.optimization.dayAheadReserve": "Day ahead reserve", "study.configuration.optimization.primaryReserve": "Primary reserve", "study.configuration.optimization.strategicReserve": "Strategic reserve", "study.configuration.optimization.spinningReserve": "Spinning reserve", - "study.configuration.optimization.exportMps": "Export mps", - "study.configuration.optimization.splitExportedMps": "Split exported mps", + "study.configuration.optimization.exportMps": "Export MPS", "study.configuration.optimization.unfeasibleProblemBehavior": "Unfeasible problem behavior", "study.configuration.optimization.simplexOptimizationRange": "Simplex optimization range", - "study.configuration.optimization.adequacyPatch": "Adequacy patch", - "study.configuration.optimization.enableAdequacyPatch": "Activer l'adequacy patch", - "study.configuration.optimization.ntcFromPhysicalAreasOutToPhysicalAreasInAdequacyPatch": "NTC from physical areas out to physical areas in adequacy patch", - "study.configuration.optimization.ntcBetweenPhysicalAreasOutAdequacyPatch": "NTC between physical areas out adequacy patch", + "study.configuration.adequacyPatch.legend.general": "Générale", + "study.configuration.adequacyPatch.legend.localMatchingRule": "Règle de correspondance locale", + "study.configuration.adequacyPatch.legend.curtailmentSharing": "Partage de réduction", + "study.configuration.adequacyPatch.legend.advanced": "Avancée", + "study.configuration.adequacyPatch.enableAdequacyPatch": "Enable adequacy patch", + "study.configuration.adequacyPatch.ntcFromPhysicalAreasOutToPhysicalAreasInAdequacyPatch": "NTC from physical areas out to physical areas in adequacy patch", + "study.configuration.adequacyPatch.ntcBetweenPhysicalAreasOutAdequacyPatch": "NTC between physical areas out adequacy patch", + "study.configuration.adequacyPatch.priceTakingOrder": "Prix de la prise en charge", + "study.configuration.adequacyPatch.includeHurdleCostCsr": "Inclure le coût de la hausse de la CSR", + "study.configuration.adequacyPatch.thresholdInitiateCurtailmentSharingRule": "Seuil de déclenchement de la règle de partage des réductions", + "study.configuration.adequacyPatch.thresholdDisplayLocalMatchingRuleViolations": "Seuil d'affichage des violations de la règle d'appariement local", + "study.configuration.adequacyPatch.thresholdCsrVariableBoundsRelaxation": "Seuil d'assouplissement des limites variables de la CSR", + "study.configuration.adequacyPatch.checkCsrCostFunction": "Vérifier la fonction de coût CSR", "study.configuration.advancedParameters.seedsForRandomNumbers": "Seeds for random numbers", "study.configuration.advancedParameters.spatialTimeSeriesCorrelation": "Spatial time-series correlation", "study.configuration.advancedParameters.otherPreferences": "Autres préférences", @@ -372,9 +381,9 @@ "study.modelization.map.layers.add": "Ajouter un layer", "study.modelization.map.layers.edit": "Modifier un layer", "study.modelization.map.layers.delete.confirm": "Êtes-vous sûr de vouloir supprimer le layer '{{0}}' ?", + "study.modelization.map.districts": "Districts", "study.modelization.map.districts.field.comments": "Commentaires", "study.modelization.map.districts.field.outputs": "Sorties", - "study.modelization.map.districts": "Districts", "study.modelization.map.districts.add": "Ajouter un district", "study.modelization.map.districts.edit": "Modifier un district", "study.modelization.map.districts.delete.confirm": "Êtes-vous sûr de vouloir supprimer le district '{{0}}' ?", @@ -479,6 +488,8 @@ "study.error.modifiedStudy": "Erreur lors de la modification de l'étude {{studyname}}", "study.error.launchLoad": "Échec lors de la récupération de la charge du cluster", "study.error.upgrade": "Échec lors de la mise à jour de votre étude", + "study.error.createDistrict": "Failed to add district", + "study.error.createLayer": "Failed to add layer", "study.success.commentsSaved": "Commentaires enregistrés avec succès", "study.success.studyIdCopy": "Identifiant de l'étude copié !", "study.success.jobIdCopy": "Identifiant de la tâche copié !", diff --git a/webapp/src/components/App/Singlestudy/explore/Configuration/AdequacyPatch/Fields.tsx b/webapp/src/components/App/Singlestudy/explore/Configuration/AdequacyPatch/Fields.tsx new file mode 100644 index 0000000000..9faf800cf2 --- /dev/null +++ b/webapp/src/components/App/Singlestudy/explore/Configuration/AdequacyPatch/Fields.tsx @@ -0,0 +1,98 @@ +import { Box } from "@mui/material"; +import { useTranslation } from "react-i18next"; +import NumberFE from "../../../../../common/fieldEditors/NumberFE"; +import SelectFE from "../../../../../common/fieldEditors/SelectFE"; +import SwitchFE from "../../../../../common/fieldEditors/SwitchFE"; +import Fieldset from "../../../../../common/Fieldset"; +import { useFormContextPlus } from "../../../../../common/Form"; +import { AdequacyPatchFormFields, PRICE_TAKING_ORDER_OPTIONS } from "./utils"; + +function Fields() { + const { t } = useTranslation(); + const { control } = useFormContextPlus(); + + return ( + +
+ +
+
+ + +
+
+ + +
+
+ + + + + +
+
+ ); +} + +export default Fields; diff --git a/webapp/src/components/App/Singlestudy/explore/Configuration/AdequacyPatch/index.tsx b/webapp/src/components/App/Singlestudy/explore/Configuration/AdequacyPatch/index.tsx new file mode 100644 index 0000000000..82052798ce --- /dev/null +++ b/webapp/src/components/App/Singlestudy/explore/Configuration/AdequacyPatch/index.tsx @@ -0,0 +1,43 @@ +import { useOutletContext } from "react-router"; +import { StudyMetadata } from "../../../../../../common/types"; +import Form from "../../../../../common/Form"; +import { SubmitHandlerPlus } from "../../../../../common/Form/types"; +import Fields from "./Fields"; +import { + AdequacyPatchFormFields, + getAdequacyPatchFormFields, + setAdequacyPatchFormFields, +} from "./utils"; + +function AdequacyPatch() { + const { study } = useOutletContext<{ study: StudyMetadata }>(); + + //////////////////////////////////////////////////////////////// + // Event Handlers + //////////////////////////////////////////////////////////////// + + const handleSubmit = async ( + data: SubmitHandlerPlus + ) => { + return setAdequacyPatchFormFields(study.id, data.dirtyValues); + }; + + //////////////////////////////////////////////////////////////// + // JSX + //////////////////////////////////////////////////////////////// + + return ( +
getAdequacyPatchFormFields(study.id), + }} + onSubmit={handleSubmit} + autoSubmit + > + + + ); +} + +export default AdequacyPatch; diff --git a/webapp/src/components/App/Singlestudy/explore/Configuration/AdequacyPatch/utils.ts b/webapp/src/components/App/Singlestudy/explore/Configuration/AdequacyPatch/utils.ts new file mode 100644 index 0000000000..914d0e1b10 --- /dev/null +++ b/webapp/src/components/App/Singlestudy/explore/Configuration/AdequacyPatch/utils.ts @@ -0,0 +1,57 @@ +import { StudyMetadata } from "../../../../../../common/types"; +import client from "../../../../../../services/api/client"; + +//////////////////////////////////////////////////////////////// +// Enums +//////////////////////////////////////////////////////////////// + +enum PriceTakingOrder { + DENS = "DENS", + Load = "Load", +} + +//////////////////////////////////////////////////////////////// +// Types +//////////////////////////////////////////////////////////////// + +export interface AdequacyPatchFormFields { + // Version 830 + enableAdequacyPatch: boolean; + ntcFromPhysicalAreasOutToPhysicalAreasInAdequacyPatch: boolean; + ntcBetweenPhysicalAreasOutAdequacyPatch: boolean; + // Version 850 + priceTakingOrder: PriceTakingOrder; + includeHurdleCostCsr: boolean; + checkCsrCostFunction: boolean; + thresholdInitiateCurtailmentSharingRule: number; + thresholdDisplayLocalMatchingRuleViolations: number; + thresholdCsrVariableBoundsRelaxation: number; +} + +//////////////////////////////////////////////////////////////// +// Constants +//////////////////////////////////////////////////////////////// + +export const PRICE_TAKING_ORDER_OPTIONS = Object.values(PriceTakingOrder); + +//////////////////////////////////////////////////////////////// +// Functions +//////////////////////////////////////////////////////////////// + +function makeRequestURL(studyId: StudyMetadata["id"]): string { + return `v1/studies/${studyId}/config/adequacypatch/form`; +} + +export async function getAdequacyPatchFormFields( + studyId: StudyMetadata["id"] +): Promise { + const res = await client.get(makeRequestURL(studyId)); + return res.data; +} + +export function setAdequacyPatchFormFields( + studyId: StudyMetadata["id"], + values: Partial +): Promise { + return client.put(makeRequestURL(studyId), values); +} diff --git a/webapp/src/components/App/Singlestudy/explore/Configuration/General/Fields.tsx b/webapp/src/components/App/Singlestudy/explore/Configuration/General/Fields.tsx index 5aaa8141dc..34787777d1 100644 --- a/webapp/src/components/App/Singlestudy/explore/Configuration/General/Fields.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Configuration/General/Fields.tsx @@ -108,7 +108,7 @@ function Fields(props: Props) { return ( <> -
+
-
+
(); const version = Number(study.version); - const isVer830OrAbove = version >= 830; - const isVer840OrAbove = version >= 840; return ( -
+
+ + + + +
+
= 840 ? TRANSMISSION_CAPACITIES_OPTIONS : LEGACY_TRANSMISSION_CAPACITIES_OPTIONS } name="transmissionCapacities" control={control} - rules={{ - setValueAs: R.cond([ - [R.equals("true"), R.T], - [R.equals("false"), R.F], - [R.T, R.identity], - ]), - }} - /> - +
+
+
+
- - {isVer830OrAbove && ( - - )} - -
- {isVer830OrAbove && ( -
- - - -
- )} ); } diff --git a/webapp/src/components/App/Singlestudy/explore/Configuration/Optimization/utils.ts b/webapp/src/components/App/Singlestudy/explore/Configuration/Optimization/utils.ts index cb291572ef..2b83ea58c5 100644 --- a/webapp/src/components/App/Singlestudy/explore/Configuration/Optimization/utils.ts +++ b/webapp/src/components/App/Singlestudy/explore/Configuration/Optimization/utils.ts @@ -1,3 +1,5 @@ +import * as R from "ramda"; + //////////////////////////////////////////////////////////////// // Enums //////////////////////////////////////////////////////////////// @@ -5,11 +7,6 @@ import { StudyMetadata } from "../../../../../../common/types"; import client from "../../../../../../services/api/client"; -enum LinkType { - Local = "local", - AC = "ac", -} - enum UnfeasibleProblemBehavior { WarningDry = "warning-dry", WarningVerbose = "warning-verbose", @@ -47,7 +44,6 @@ export interface OptimizationFormFields { | boolean | LegacyTransmissionCapacities.Infinite | TransmissionCapacities; - linkType: LinkType; thermalClustersMinStablePower: boolean; thermalClustersMinUdTime: boolean; dayAheadReserve: boolean; @@ -57,21 +53,12 @@ export interface OptimizationFormFields { exportMps: boolean; unfeasibleProblemBehavior: UnfeasibleProblemBehavior; simplexOptimizationRange: SimplexOptimizationRange; - // version 830 - splitExportedMps?: boolean; - enableAdequacyPatch?: boolean; - ntcFromPhysicalAreasOutToPhysicalAreasInAdequacyPatch?: boolean; - ntcBetweenPhysicalAreasOutAdequacyPatch?: boolean; } //////////////////////////////////////////////////////////////// // Constants //////////////////////////////////////////////////////////////// -export const LINK_TYPE_OPTIONS = [ - { label: "Local", value: LinkType.Local }, - { label: "AC", value: LinkType.AC }, -]; export const UNFEASIBLE_PROBLEM_BEHAVIOR_OPTIONS = Object.values( UnfeasibleProblemBehavior ); @@ -106,3 +93,9 @@ export function setOptimizationFormFields( ): Promise { return client.put(makeRequestURL(studyId), values); } + +export const toBooleanIfNeeded = R.cond([ + [R.equals("true"), R.T], + [R.equals("false"), R.F], + [R.T, R.identity], +]); diff --git a/webapp/src/components/App/Singlestudy/explore/Configuration/index.tsx b/webapp/src/components/App/Singlestudy/explore/Configuration/index.tsx index 594040c534..7913623af2 100644 --- a/webapp/src/components/App/Singlestudy/explore/Configuration/index.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Configuration/index.tsx @@ -2,10 +2,13 @@ import { Paper } from "@mui/material"; import * as R from "ramda"; import { useMemo, useState } from "react"; +import { useOutletContext } from "react-router"; +import { StudyMetadata } from "../../../../../common/types"; import UnderConstruction from "../../../../common/page/UnderConstruction"; import PropertiesView from "../../../../common/PropertiesView"; import SplitLayoutView from "../../../../common/SplitLayoutView"; import ListElement from "../common/ListElement"; +import AdequacyPatch from "./AdequacyPatch"; import AdvancedParameters from "./AdvancedParameters"; import General from "./General"; import Optimization from "./Optimization"; @@ -13,17 +16,21 @@ import RegionalDistricts from "./RegionalDistricts"; import TimeSeriesManagement from "./TimeSeriesManagement"; function Configuration() { + const { study } = useOutletContext<{ study: StudyMetadata }>(); const [currentElementIndex, setCurrentElementIndex] = useState(0); + // TODO i18n const listElement = useMemo( - () => [ - { name: "General" }, - { name: "Time-series management" }, - { name: "Regional districts" }, - { name: "Optimization preferences" }, - { name: "Advanced parameters" }, - ], - [] + () => + [ + { name: "General" }, + { name: "Time-series management" }, + { name: "Regional districts" }, + { name: "Optimization preferences" }, + Number(study.version) >= 830 && { name: "Adequacy Patch" }, + { name: "Advanced parameters" }, + ].filter(Boolean), + [study.version] ); return ( @@ -49,7 +56,8 @@ function Configuration() { [R.equals(1), () => ], [R.equals(2), () => ], [R.equals(3), () => ], - [R.equals(4), () => ], + [R.equals(4), () => ], + [R.equals(5), () => ], ])(currentElementIndex)} } diff --git a/webapp/src/components/common/Fieldset.tsx b/webapp/src/components/common/Fieldset.tsx index ed3dd3fdea..7c38752cc4 100644 --- a/webapp/src/components/common/Fieldset.tsx +++ b/webapp/src/components/common/Fieldset.tsx @@ -34,7 +34,7 @@ function Fieldset(props: FieldsetProps) { flexWrap: "wrap", gap: 2, ".MuiFormControl-root": { - width: fullFieldWidth ? "100%" : 220, + width: fullFieldWidth ? 1 : 220, }, }, }, @@ -63,4 +63,8 @@ function Fieldset(props: FieldsetProps) { ); } +Fieldset.Break = function Break() { + return ; +}; + export default Fieldset; From 5e5e4e7efcfc93e4682825a9c514417679fba89b Mon Sep 17 00:00:00 2001 From: Samir Kamal <1954121+skamril@users.noreply.github.com> Date: Thu, 23 Mar 2023 13:59:36 +0100 Subject: [PATCH 12/70] style(tablemode): fix typo --- antarest/study/business/table_mode_management.py | 16 ++++++++-------- antarest/study/web/study_data_blueprint.py | 6 +++--- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/antarest/study/business/table_mode_management.py b/antarest/study/business/table_mode_management.py index 1f7bf3a348..41e217e39d 100644 --- a/antarest/study/business/table_mode_management.py +++ b/antarest/study/business/table_mode_management.py @@ -379,7 +379,7 @@ class PathVars(TypedDict, total=False): }, } -COLUMN_MODELS_BY_TYPE = { +COLUMNS_MODELS_BY_TYPE = { TableTemplateType.AREA: AreaColumns, TableTemplateType.LINK: LinkColumns, TableTemplateType.CLUSTER: ClusterColumns, @@ -387,7 +387,7 @@ class PathVars(TypedDict, total=False): TableTemplateType.BINDING_CONSTRAINT: BindingConstraintColumns, } -ColumnModelTypes = Union[ +ColumnsModelTypes = Union[ AreaColumns, LinkColumns, ClusterColumns, @@ -405,9 +405,9 @@ def get_table_data( study: RawStudy, table_type: TableTemplateType, columns: List[str], - ) -> Dict[str, ColumnModelTypes]: + ) -> Dict[str, ColumnsModelTypes]: file_study = self.storage_service.get_storage(study).get_raw(study) - column_model = COLUMN_MODELS_BY_TYPE[table_type] + columns_model = COLUMNS_MODELS_BY_TYPE[table_type] fields_info = FIELDS_INFO_BY_TYPE[table_type] glob_object = TableModeManager.__get_glob_object( file_study, table_type @@ -426,7 +426,7 @@ def get_column_value(col: str, data: Dict[str, Any]) -> Any: if table_type == TableTemplateType.AREA: return { - area_id: column_model.construct( + area_id: columns_model.construct( **{col: get_column_value(col, data) for col in columns} ) # type: ignore for area_id, data in glob_object.items() @@ -434,7 +434,7 @@ def get_column_value(col: str, data: Dict[str, Any]) -> Any: if table_type == TableTemplateType.BINDING_CONSTRAINT: return { - data["id"]: column_model.construct( + data["id"]: columns_model.construct( **{col: get_column_value(col, data) for col in columns} ) # type: ignore for data in glob_object.values() @@ -443,7 +443,7 @@ def get_column_value(col: str, data: Dict[str, Any]) -> Any: obj: Dict[str, Any] = {} for id_1, value_1 in glob_object.items(): for id_2, value_2 in value_1.items(): - obj[f"{id_1} / {id_2}"] = column_model.construct( + obj[f"{id_1} / {id_2}"] = columns_model.construct( **{col: get_column_value(col, value_2) for col in columns} ) @@ -453,7 +453,7 @@ def set_table_data( self, study: RawStudy, table_type: TableTemplateType, - data: Dict[str, ColumnModelTypes], + data: Dict[str, ColumnsModelTypes], ) -> None: commands: List[ICommand] = [] bindings_by_id = None diff --git a/antarest/study/web/study_data_blueprint.py b/antarest/study/web/study_data_blueprint.py index 2c9f2e3035..f7111e6a16 100644 --- a/antarest/study/web/study_data_blueprint.py +++ b/antarest/study/web/study_data_blueprint.py @@ -45,7 +45,7 @@ from antarest.study.business.playlist_management import PlaylistColumns from antarest.study.business.renewable_management import RenewableFormFields from antarest.study.business.table_mode_management import ( - ColumnModelTypes, + ColumnsModelTypes, TableTemplateType, ) from antarest.study.business.thematic_trimming_management import ( @@ -829,7 +829,7 @@ def get_table_data( table_type: TableTemplateType, columns: str, current_user: JWTUser = Depends(auth.get_current_user), - ) -> Dict[str, ColumnModelTypes]: + ) -> Dict[str, ColumnsModelTypes]: logger.info( f"Getting template table data for study {uuid}", extra={"user": current_user.id}, @@ -851,7 +851,7 @@ def get_table_data( def set_table_data( uuid: str, table_type: TableTemplateType, - data: Dict[str, ColumnModelTypes], + data: Dict[str, ColumnsModelTypes], current_user: JWTUser = Depends(auth.get_current_user), ) -> None: logger.info( From ad9f9c055713ef81a94b8c7bb01caae783ab8de9 Mon Sep 17 00:00:00 2001 From: Samir Kamal <1954121+skamril@users.noreply.github.com> Date: Thu, 23 Mar 2023 14:00:33 +0100 Subject: [PATCH 13/70] style(ui): fix filename --- webapp/src/components/common/Form/index.tsx | 2 +- .../Form/{useDefaultValuesPlus.ts => useAsyncDefaultValues.ts} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename webapp/src/components/common/Form/{useDefaultValuesPlus.ts => useAsyncDefaultValues.ts} (100%) diff --git a/webapp/src/components/common/Form/index.tsx b/webapp/src/components/common/Form/index.tsx index 943ae21dfc..eafc7b90e3 100644 --- a/webapp/src/components/common/Form/index.tsx +++ b/webapp/src/components/common/Form/index.tsx @@ -39,7 +39,7 @@ import { getDirtyValues, stringToPath, toAutoSubmitConfig } from "./utils"; import useDebouncedState from "../../../hooks/useDebouncedState"; import usePrompt from "../../../hooks/usePrompt"; import { mergeSxProp } from "../../../utils/muiUtils"; -import useAsyncDefaultValues from "./useDefaultValuesPlus"; +import useAsyncDefaultValues from "./useAsyncDefaultValues"; import { ControlPlus, SubmitHandlerPlus, diff --git a/webapp/src/components/common/Form/useDefaultValuesPlus.ts b/webapp/src/components/common/Form/useAsyncDefaultValues.ts similarity index 100% rename from webapp/src/components/common/Form/useDefaultValuesPlus.ts rename to webapp/src/components/common/Form/useAsyncDefaultValues.ts From f63edda65345bf9848fb44a8a067a885ca5fbd83 Mon Sep 17 00:00:00 2001 From: Samir Kamal <1954121+skamril@users.noreply.github.com> Date: Thu, 23 Mar 2023 14:02:49 +0100 Subject: [PATCH 14/70] fix(ui): size issue with HandsonTable --- .../ScenarioBuilderDialog/tabs/Table.tsx | 1 - .../dialogs/ScenarioPlaylistDialog/index.tsx | 2 -- .../Map/MapConfig/Districts/index.tsx | 27 +++++++------------ .../Map/MapConfig/Layers/index.tsx | 27 +++++++------------ .../explore/Modelization/TableMode/index.tsx | 6 +---- .../src/components/common/FormTable/index.tsx | 10 ++++++- 6 files changed, 30 insertions(+), 43 deletions(-) diff --git a/webapp/src/components/App/Singlestudy/explore/Configuration/General/dialogs/ScenarioBuilderDialog/tabs/Table.tsx b/webapp/src/components/App/Singlestudy/explore/Configuration/General/dialogs/ScenarioBuilderDialog/tabs/Table.tsx index e510258cc2..2e5bdaeeac 100644 --- a/webapp/src/components/App/Singlestudy/explore/Configuration/General/dialogs/ScenarioBuilderDialog/tabs/Table.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Configuration/General/dialogs/ScenarioBuilderDialog/tabs/Table.tsx @@ -132,7 +132,6 @@ function Table(props: Props) { `${t("study.configuration.general.mcScenarioBuilder.year")} ${ index + 1 }`, - height: "100%", stretchH: "all", className: "htCenter", }} diff --git a/webapp/src/components/App/Singlestudy/explore/Configuration/General/dialogs/ScenarioPlaylistDialog/index.tsx b/webapp/src/components/App/Singlestudy/explore/Configuration/General/dialogs/ScenarioPlaylistDialog/index.tsx index 6aa5f47dc7..7ca0d31935 100644 --- a/webapp/src/components/App/Singlestudy/explore/Configuration/General/dialogs/ScenarioPlaylistDialog/index.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Configuration/General/dialogs/ScenarioPlaylistDialog/index.tsx @@ -122,8 +122,6 @@ function ScenarioPlaylistDialog(props: Props) { tableProps={{ rowHeaders: (row) => `MC Year ${row.id}`, tableRef, - height: "100%", - width: 500, stretchH: "all", className: "htCenter", cells: handleCellsRender, diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Map/MapConfig/Districts/index.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Map/MapConfig/Districts/index.tsx index 40139784e0..23edfd8dd8 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Map/MapConfig/Districts/index.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Map/MapConfig/Districts/index.tsx @@ -1,5 +1,4 @@ import { Box, Button } from "@mui/material"; -import AutoSizer from "react-virtualized-auto-sizer"; import { useMemo, useState } from "react"; import { useOutletContext } from "react-router"; import { Add, Edit } from "@mui/icons-material"; @@ -121,22 +120,16 @@ function Districts() { {columns.length > 0 && ( - - {({ height, width }) => ( - - districtsById[colName].name, - selectionMode: "single", - }} - onSubmit={handleSubmit} - /> - - )} - + districtsById[colName].name, + selectionMode: "single", + }} + onSubmit={handleSubmit} + /> )} diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Map/MapConfig/Layers/index.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Map/MapConfig/Layers/index.tsx index 0f04dfeee0..34c7b7474c 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Map/MapConfig/Layers/index.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Map/MapConfig/Layers/index.tsx @@ -1,5 +1,4 @@ import { Box, Button } from "@mui/material"; -import AutoSizer from "react-virtualized-auto-sizer"; import { useMemo, useState } from "react"; import { useOutletContext } from "react-router"; import { Add, Edit } from "@mui/icons-material"; @@ -122,22 +121,16 @@ function Layers() { {columns.length > 0 && ( - - {({ height, width }) => ( - - layersById[colName].name, - selectionMode: "single", - }} - onSubmit={handleSubmit} - /> - - )} - + layersById[colName].name, + selectionMode: "single", + }} + onSubmit={handleSubmit} + /> )} {createLayerDialogOpen && ( diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/TableMode/index.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/TableMode/index.tsx index 7da88bd1b2..8658e81bdb 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/TableMode/index.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/TableMode/index.tsx @@ -151,11 +151,7 @@ function TableMode() { )} /> diff --git a/webapp/src/components/common/FormTable/index.tsx b/webapp/src/components/common/FormTable/index.tsx index 28db8dc7c1..a61e28f895 100644 --- a/webapp/src/components/common/FormTable/index.tsx +++ b/webapp/src/components/common/FormTable/index.tsx @@ -85,7 +85,15 @@ function FormTable( onSubmit={onSubmit} onSubmitError={onSubmitError} autoSubmit - sx={mergeSxProp({ width: 1, height: 1, pt: 0 }, sx)} + sx={mergeSxProp( + { + width: 1, + height: 1, + pt: 0, + overflow: "hidden", // https://handsontable.com/docs/12.0/grid-size/#define-the-size-in-css-styles + }, + sx + )} apiRef={formApiRef} > Date: Wed, 5 Apr 2023 15:30:28 +0200 Subject: [PATCH 15/70] fix(common): field array change doesn't trigger on auto submit (#1439) --- webapp/src/components/common/Form/index.tsx | 42 ++++++++++++++++----- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/webapp/src/components/common/Form/index.tsx b/webapp/src/components/common/Form/index.tsx index eafc7b90e3..84b810fcc5 100644 --- a/webapp/src/components/common/Form/index.tsx +++ b/webapp/src/components/common/Form/index.tsx @@ -8,6 +8,7 @@ import { useState, } from "react"; import { + BatchFieldArrayUpdate, DeepPartial, FieldPath, FieldValues, @@ -47,6 +48,7 @@ import { UseFormRegisterPlus, UseFormReturnPlus, } from "./types"; +import useAutoUpdateRef from "../../../hooks/useAutoUpdateRef"; export type AutoSubmitConfig = { enable: boolean; wait?: number }; @@ -134,7 +136,8 @@ function Form( // In case we have invalid default value for example. const isSubmitAllowed = isDirty && !isSubmitting; // To use it in wrapper functions without need to add the value in `useCallback`'s deps - const isSubmittingRef = useRef(isSubmitting); + const isSubmittingRef = useAutoUpdateRef(isSubmitting); + const isAutoSubmitEnabledRef = useAutoUpdateRef(autoSubmitConfig.enable); useAsyncDefaultValues(asyncDefaultValues, (values) => { reset(values); @@ -146,8 +149,6 @@ function Form( if (isSubmitting) { setShowLoader.flush(); } - - isSubmittingRef.current = isSubmitting; }, [isSubmitting]); useUpdateEffect( @@ -266,7 +267,7 @@ function Form( ...options, onChange: (event: unknown) => { options?.onChange?.(event); - if (autoSubmitConfig.enable) { + if (isAutoSubmitEnabledRef.current) { if ( isSubmittingRef.current && !fieldsChangeDuringAutoSubmitting.current.includes(name) @@ -281,7 +282,7 @@ function Form( return register(name, newOptions); }, - [autoSubmitConfig.enable, register, requestSubmit] + [isAutoSubmitEnabledRef, isSubmittingRef, register, requestSubmit] ); const unregisterWrapper = useCallback>( @@ -300,11 +301,11 @@ function Form( const setValueWrapper = useCallback>( (name, value, options) => { const newOptions: typeof options = { - shouldDirty: autoSubmitConfig.enable, // Option false by default + shouldDirty: isAutoSubmitEnabledRef.current, // Option false by default ...options, }; - if (autoSubmitConfig.enable && newOptions.shouldDirty) { + if (isAutoSubmitEnabledRef.current && newOptions.shouldDirty) { if (isSubmittingRef.current) { fieldsChangeDuringAutoSubmitting.current.push(name); } @@ -316,7 +317,13 @@ function Form( setValue(name, value, newOptions); }, - [autoSubmitConfig.enable, setValue, getValues, requestSubmit] + [ + getValues, + isAutoSubmitEnabledRef, + isSubmittingRef, + requestSubmit, + setValue, + ] ); const controlWrapper = useMemo>(() => { @@ -326,8 +333,25 @@ function Form( controlPlus.unregister = unregisterWrapper; controlPlus._showSkeleton = showSkeleton; + const updateFieldArrayOriginal = control._updateFieldArray.bind(control); + const updateFieldArrayWrapper: BatchFieldArrayUpdate = (...args) => { + updateFieldArrayOriginal(...args); + if (isAutoSubmitEnabledRef.current) { + requestSubmit(); + } + }; + // Used by `useFieldArray` hook's methods (`append`, `remove`...) + controlPlus._updateFieldArray = updateFieldArrayWrapper; + return controlPlus; - }, [control, registerWrapper, unregisterWrapper, showSkeleton]); + }, [ + control, + isAutoSubmitEnabledRef, + registerWrapper, + requestSubmit, + showSkeleton, + unregisterWrapper, + ]); //////////////////////////////////////////////////////////////// // Form API Plus From 2d03befe999e558c989e1cce1f51186beff5502b Mon Sep 17 00:00:00 2001 From: Laurent LAPORTE <43534797+laurent-laporte-pro@users.noreply.github.com> Date: Wed, 5 Apr 2023 16:30:03 +0200 Subject: [PATCH 16/70] docs(study-upgrade): add the "How to upgrade a study?" topic in the documentation (#1400) --- .../how-to/studies-upgrade-dialog_box.png | Bin 0 -> 18599 bytes .../media/how-to/studies-upgrade-done.png | Bin 0 -> 32253 bytes .../how-to/studies-upgrade-menu_open.png | Bin 0 -> 39212 bytes .../how-to/sudies-upgrade-menu_version.png | Bin 0 -> 223325 bytes docs/how-to/studies-upgrade.md | 60 ++++++++++++++++++ mkdocs.yml | 22 ++++--- 6 files changed, 72 insertions(+), 10 deletions(-) create mode 100755 docs/assets/media/how-to/studies-upgrade-dialog_box.png create mode 100755 docs/assets/media/how-to/studies-upgrade-done.png create mode 100755 docs/assets/media/how-to/studies-upgrade-menu_open.png create mode 100755 docs/assets/media/how-to/sudies-upgrade-menu_version.png create mode 100644 docs/how-to/studies-upgrade.md diff --git a/docs/assets/media/how-to/studies-upgrade-dialog_box.png b/docs/assets/media/how-to/studies-upgrade-dialog_box.png new file mode 100755 index 0000000000000000000000000000000000000000..c623c6daff93f7bd5f725b12ad34a2f4b62cda1e GIT binary patch literal 18599 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 VunCox{mJ8$6qOSx|77s>{{XqO$z1>d literal 0 HcmV?d00001 diff --git a/docs/assets/media/how-to/studies-upgrade-done.png b/docs/assets/media/how-to/studies-upgrade-done.png new file mode 100755 index 0000000000000000000000000000000000000000..dd00aff4f8d19e49430def3269d80aa78a3af799 GIT binary patch literal 32253 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 literal 0 HcmV?d00001 diff --git a/docs/assets/media/how-to/studies-upgrade-menu_open.png b/docs/assets/media/how-to/studies-upgrade-menu_open.png new file mode 100755 index 0000000000000000000000000000000000000000..120678c9ae24d1b67d14f1f6a54cc9d6a6b1ac2d GIT binary patch literal 39212 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 Fb#%8xDE<)*^T=Z9kr>(;vvCN?%*q&4B;}a| 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 z*+ZWN0000000000>8VPzMz98eSP-89000000001V^G?Rkwln{GfnW^)0000000000 b&_4eGg&np(YuWOo00000NkvXXu0mjfX5=OB literal 0 HcmV?d00001 diff --git a/docs/how-to/studies-upgrade.md b/docs/how-to/studies-upgrade.md new file mode 100644 index 0000000000..6c538fc59b --- /dev/null +++ b/docs/how-to/studies-upgrade.md @@ -0,0 +1,60 @@ +--- +title: How to upgrade a study? +author: Laurent LAPORTE +date: 2023-03-10 +tags: + +- upgrade +- version + +--- + +# Introduction + +Upgrading versioned studies is an important step to ensure compatibility of your studies with the latest versions of +Antares Web and Antares Simulator. This upgrade is necessary because some earlier versions may be deprecated and no +longer supported. + +The upgrade process involves updating your study to the latest available version, using an automated process that +ensures consistency of results across versions. This upgrade may include minor or major changes to the structure of the +study.Generally, the changes are related to the configuration, the creation of new folders to take into account new +functionalities (adequacy patch, xpansion, etc.) but also the upgrade of matrices. + +> Please note that upgrading your study to the latest version of Antares Simulator does not automatically enable new +> features, and may require manual adjustments to your study to take full advantage of the new capabilities. + +We strongly recommend upgrading your studies to the latest version to take advantage of all the new features, and +improvements in Antares Web and Antares Simulator. If you encounter any difficulties during the upgrade, please do not +hesitate to contact our support team for assistance. + +# Upgrading + +To upgrade your study to the latest version of Antares Web and Antares Simulator, you can follow these steps: + +On the main page of the study, you can find the version number at the top of the menu bar: + +![](../assets/media/how-to/sudies-upgrade-menu_version.png) + +To upgrade the study, click on the nemu and select "Upgrade Study". + +![studies-upgrade-menu_open.png](../assets/media/how-to/studies-upgrade-menu_open.png) + +The confirmation dialog box will appear, click "Yes" to start the upgrade: + +![studies-upgrade-menu_open.png](../assets/media/how-to/studies-upgrade-dialog_box.png) + +The upgrade task is launched in the task manager. + +> **NOTE:** Most of the upgrades are instantaneous but some can take time, especially when there are matrix +> transformations. + +When the upgrade is done, you can see the version number updated: + +![](../assets/media/how-to/studies-upgrade-done.png) + +Once the upgrade is complete, you can open your study and perform the manual upgrade in the configuration. + +# See also + +- Create a new study in the latest version +- Run a study in the latest version diff --git a/mkdocs.yml b/mkdocs.yml index a1918c08c1..b46a354a8f 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -30,18 +30,20 @@ theme: nav: - Home: index.md - 'User guide': - - 'Introduction': 'user-guide/0-introduction.md' - - 'User interface': 'user-guide/1-interface.md' - - 'Variant manager': 'user-guide/2-variant_manager.md' + - 'Introduction': 'user-guide/0-introduction.md' + - 'User interface': 'user-guide/1-interface.md' + - 'Variant manager': 'user-guide/2-variant_manager.md' + - 'How to': + - 'Upgrade a study': 'how-to/studies-upgrade.md' - 'Build': - - 'Introduction': 'install/0-INSTALL.md' - - 'Configuration': 'install/1-CONFIG.md' - - 'Deployment': 'install/2-DEPLOY.md' + - 'Introduction': 'install/0-INSTALL.md' + - 'Configuration': 'install/1-CONFIG.md' + - 'Deployment': 'install/2-DEPLOY.md' - 'Develop': - - 'Introduction': 'architecture/0-introduction.md' - - 'Database management': 'architecture/1-database.md' - - 'Roadmap': 'architecture/5-roadmap.md' - - 'Antares ecosystem' : 'https://antares-doc.readthedocs.io' + - 'Introduction': 'architecture/0-introduction.md' + - 'Database management': 'architecture/1-database.md' + - 'Roadmap': 'architecture/5-roadmap.md' + - 'Antares ecosystem': 'https://antares-doc.readthedocs.io' - 'Changelog': 'CHANGELOG.md' extra: From 1d129d9d6bac97deee9ebc98d3334117fe837444 Mon Sep 17 00:00:00 2001 From: Samir Kamal <1954121+skamril@users.noreply.github.com> Date: Fri, 7 Apr 2023 18:07:08 +0200 Subject: [PATCH 17/70] feat(ui): update react-hook-form lib and use the new API (#1444) --- webapp/package-lock.json | 6 +-- webapp/package.json | 2 +- .../dialog/GroupFormDialog/GroupForm.tsx | 3 +- .../Configuration/AdequacyPatch/index.tsx | 2 +- .../AdvancedParameters/index.tsx | 2 +- .../explore/Configuration/General/Fields.tsx | 46 +++++++++++-------- .../dialogs/ThematicTrimmingDialog/index.tsx | 2 +- .../explore/Configuration/General/index.tsx | 2 +- .../Configuration/Optimization/index.tsx | 2 +- .../TimeSeriesManagement/index.tsx | 2 +- .../Areas/Hydro/ManagementOptions/index.tsx | 3 +- .../Modelization/Areas/Properties/index.tsx | 2 +- .../Modelization/Areas/Renewables/index.tsx | 2 +- .../Modelization/Areas/Thermal/index.tsx | 2 +- .../Modelization/Links/LinkView/LinkForm.tsx | 5 +- .../App/Studies/MoveStudyDialog.tsx | 17 +++---- webapp/src/components/common/Form/index.tsx | 38 +++++++-------- webapp/src/components/common/Form/types.ts | 27 ++++------- .../common/Form/useAsyncDefaultValues.ts | 20 -------- .../components/common/FormGenerator/index.tsx | 9 ++-- .../src/components/common/FormTable/index.tsx | 4 +- webapp/src/hoc/reactHookFormSupport.tsx | 19 ++++++-- 22 files changed, 104 insertions(+), 113 deletions(-) delete mode 100644 webapp/src/components/common/Form/useAsyncDefaultValues.ts diff --git a/webapp/package-lock.json b/webapp/package-lock.json index d2ed1d5749..73553538f1 100644 --- a/webapp/package-lock.json +++ b/webapp/package-lock.json @@ -13581,9 +13581,9 @@ "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" }, "react-hook-form": { - "version": "7.34.0", - "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.34.0.tgz", - "integrity": "sha512-s0/TJ09NVlEk2JPp3yit1WnMuPNBXFmUKEQPulgDi9pYBw/ZmmAFHe6AXWq73Y+kp8ye4OcMf0Jv+i/qLPektg==" + "version": "7.43.9", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.43.9.tgz", + "integrity": "sha512-AUDN3Pz2NSeoxQ7Hs6OhQhDr6gtF9YRuutGDwPQqhSUAHJSgGl2VeY3qN19MG0SucpjgDiuMJ4iC5T5uB+eaNQ==" }, "react-i18next": { "version": "12.1.5", diff --git a/webapp/package.json b/webapp/package.json index 78053e0415..9e1cd78852 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -52,7 +52,7 @@ "react-d3-graph": "2.6.0", "react-dom": "18.2.0", "react-dropzone": "14.2.3", - "react-hook-form": "7.34.0", + "react-hook-form": "7.43.9", "react-i18next": "12.1.5", "react-json-view": "1.21.3", "react-plotly.js": "2.6.0", diff --git a/webapp/src/components/App/Settings/Groups/dialog/GroupFormDialog/GroupForm.tsx b/webapp/src/components/App/Settings/Groups/dialog/GroupFormDialog/GroupForm.tsx index 43501b71ac..69c3e849e9 100644 --- a/webapp/src/components/App/Settings/Groups/dialog/GroupFormDialog/GroupForm.tsx +++ b/webapp/src/components/App/Settings/Groups/dialog/GroupFormDialog/GroupForm.tsx @@ -41,8 +41,7 @@ function GroupForm(props: UseFormReturnPlus) { control, register, getValues, - formState: { errors }, - defaultValues, + formState: { errors, defaultValues }, } = props; const userLabelId = useRef(uuidv4()).current; diff --git a/webapp/src/components/App/Singlestudy/explore/Configuration/AdequacyPatch/index.tsx b/webapp/src/components/App/Singlestudy/explore/Configuration/AdequacyPatch/index.tsx index 82052798ce..1577bf8ac2 100644 --- a/webapp/src/components/App/Singlestudy/explore/Configuration/AdequacyPatch/index.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Configuration/AdequacyPatch/index.tsx @@ -30,7 +30,7 @@ function AdequacyPatch() {
getAdequacyPatchFormFields(study.id), + defaultValues: () => getAdequacyPatchFormFields(study.id), }} onSubmit={handleSubmit} autoSubmit diff --git a/webapp/src/components/App/Singlestudy/explore/Configuration/AdvancedParameters/index.tsx b/webapp/src/components/App/Singlestudy/explore/Configuration/AdvancedParameters/index.tsx index 714d3d22a7..a2d11f31e7 100644 --- a/webapp/src/components/App/Singlestudy/explore/Configuration/AdvancedParameters/index.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Configuration/AdvancedParameters/index.tsx @@ -51,7 +51,7 @@ function AdvancedParameters() { getAdvancedParamsFormFields(study.id), + defaultValues: () => getAdvancedParamsFormFields(study.id), }} onSubmit={handleSubmit} autoSubmit diff --git a/webapp/src/components/App/Singlestudy/explore/Configuration/General/Fields.tsx b/webapp/src/components/App/Singlestudy/explore/Configuration/General/Fields.tsx index 34787777d1..16e025935d 100644 --- a/webapp/src/components/App/Singlestudy/explore/Configuration/General/Fields.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Configuration/General/Fields.tsx @@ -3,6 +3,7 @@ import { useTranslation } from "react-i18next"; import SettingsIcon from "@mui/icons-material/Settings"; import { useEffect } from "react"; import * as RA from "ramda-adjunct"; +import { Validate } from "react-hook-form"; import SelectFE from "../../../../../common/fieldEditors/SelectFE"; import SwitchFE from "../../../../../common/fieldEditors/SwitchFE"; import { @@ -30,8 +31,7 @@ interface Props { function Fields(props: Props) { const { setDialog } = props; const [t] = useTranslation(); - const { control, setValue, watch, getValues } = - useFormContextPlus(); + const { control, setValue, watch } = useFormContextPlus(); const [ buildingMode, selectionMode, @@ -79,17 +79,35 @@ function Fields(props: Props) { // Event Handlers //////////////////////////////////////////////////////////////// - const handleDayValidation = (v: number) => { - if (v < 1 || Number.isNaN(v)) { + const handleDayValidation: Validate = ( + value, + formValues + ) => { + if (value < 1 || Number.isNaN(value)) { return "Minimum is 1"; } - if (getValues("firstDay") > getValues("lastDay")) { + if (formValues.firstDay > formValues.lastDay) { return false; } - if (getValues("leapYear")) { - return v <= 366 ? true : "Maximum is 366 for a leap year"; + if (formValues.leapYear) { + return value <= 366 ? true : "Maximum is 366 for a leap year"; } - return v <= 365 ? true : "Maximum is 365 for a non-leap year"; + return value <= 365 ? true : "Maximum is 365 for a non-leap year"; + }; + + const handleNbYearsValidation: Validate = ( + value, + formValues + ) => { + if (formValues.buildingMode === BuildingMode.Derated) { + return value === 1 + ? true + : "Value must be 1 when building mode is derated"; + } + if (value < 1) { + return "Minimum is 1"; + } + return value <= 50000 ? true : "Maximum is 50000"; }; //////////////////////////////////////////////////////////////// @@ -187,17 +205,7 @@ function Fields(props: Props) { variant="filled" control={control} rules={{ - validate: (v) => { - if (buildingMode === BuildingMode.Derated) { - return v === 1 - ? true - : "Value must be 1 when building mode is derated"; - } - if (v < 1) { - return "Minimum is 1"; - } - return v <= 50000 ? true : "Maximum is 50000"; - }, + validate: handleNbYearsValidation, }} /> diff --git a/webapp/src/components/App/Singlestudy/explore/Configuration/General/dialogs/ThematicTrimmingDialog/index.tsx b/webapp/src/components/App/Singlestudy/explore/Configuration/General/dialogs/ThematicTrimmingDialog/index.tsx index 4d5ad9b94a..845e8a19c3 100644 --- a/webapp/src/components/App/Singlestudy/explore/Configuration/General/dialogs/ThematicTrimmingDialog/index.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Configuration/General/dialogs/ThematicTrimmingDialog/index.tsx @@ -57,7 +57,7 @@ function ThematicTrimmingDialog(props: Props) { open={open} title="Thematic Trimming" config={{ - asyncDefaultValues: () => getThematicTrimmingFormFields(study.id), + defaultValues: () => getThematicTrimmingFormFields(study.id), }} autoSubmit onSubmit={handleSubmit} diff --git a/webapp/src/components/App/Singlestudy/explore/Configuration/General/index.tsx b/webapp/src/components/App/Singlestudy/explore/Configuration/General/index.tsx index 2c0ac3a427..b3c7e3303c 100644 --- a/webapp/src/components/App/Singlestudy/explore/Configuration/General/index.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Configuration/General/index.tsx @@ -43,7 +43,7 @@ function GeneralParameters() { <> getGeneralFormFields(study.id) }} + config={{ defaultValues: () => getGeneralFormFields(study.id) }} onSubmit={handleSubmit} autoSubmit apiRef={apiRef} diff --git a/webapp/src/components/App/Singlestudy/explore/Configuration/Optimization/index.tsx b/webapp/src/components/App/Singlestudy/explore/Configuration/Optimization/index.tsx index cac4bf873a..77a92e8c98 100644 --- a/webapp/src/components/App/Singlestudy/explore/Configuration/Optimization/index.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Configuration/Optimization/index.tsx @@ -29,7 +29,7 @@ function Optimization() { return ( getOptimizationFormFields(study.id) }} + config={{ defaultValues: () => getOptimizationFormFields(study.id) }} onSubmit={handleSubmit} autoSubmit > diff --git a/webapp/src/components/App/Singlestudy/explore/Configuration/TimeSeriesManagement/index.tsx b/webapp/src/components/App/Singlestudy/explore/Configuration/TimeSeriesManagement/index.tsx index f3850f05e0..f08f29d972 100644 --- a/webapp/src/components/App/Singlestudy/explore/Configuration/TimeSeriesManagement/index.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Configuration/TimeSeriesManagement/index.tsx @@ -27,7 +27,7 @@ function TimeSeriesManagement() { return ( getTimeSeriesFormFields(study.id) }} + config={{ defaultValues: () => getTimeSeriesFormFields(study.id) }} onSubmit={handleSubmit} autoSubmit > diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/ManagementOptions/index.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/ManagementOptions/index.tsx index 088c3bcefc..cfc05ae208 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/ManagementOptions/index.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/ManagementOptions/index.tsx @@ -33,8 +33,7 @@ function ManagementOptions() { - getManagementOptionsFormFields(studyId, areaId), + defaultValues: () => getManagementOptionsFormFields(studyId, areaId), }} onSubmit={handleSubmit} autoSubmit diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Properties/index.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Properties/index.tsx index 7fb3926323..970e67c9e3 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Properties/index.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Properties/index.tsx @@ -18,7 +18,7 @@ function Properties() { getDefaultValues(study.id, currentArea, t), + defaultValues: () => getDefaultValues(study.id, currentArea, t), }} autoSubmit > diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Renewables/index.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Renewables/index.tsx index 21f6816430..c07f8157df 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Renewables/index.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Renewables/index.tsx @@ -60,7 +60,7 @@ function Renewables() { { + defaultValues: () => { return getRenewableFormFields(study.id, area, cluster); }, }} diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Thermal/index.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Thermal/index.tsx index 61d5d00968..21e9546ef8 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Thermal/index.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Thermal/index.tsx @@ -48,7 +48,7 @@ function Thermal() { { + defaultValues: () => { return getThermalFormFields(study.id, area, cluster); }, }} diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Links/LinkView/LinkForm.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Links/LinkView/LinkForm.tsx index 23b4dfa1d9..1a235f99e2 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Links/LinkView/LinkForm.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Links/LinkView/LinkForm.tsx @@ -46,7 +46,10 @@ function LinkForm(props: Props) { return getLinkPath(area1, area2); }, [area1, area2]); - const { control, defaultValues } = useFormContextPlus(); + const { + control, + formState: { defaultValues }, + } = useFormContextPlus(); const optionTransCap = ["infinite", "ignore", "enabled"].map((item) => ({ label: t(`study.modelization.links.transmissionCapa.${item}`), diff --git a/webapp/src/components/App/Studies/MoveStudyDialog.tsx b/webapp/src/components/App/Studies/MoveStudyDialog.tsx index c50984eca4..1659231154 100644 --- a/webapp/src/components/App/Studies/MoveStudyDialog.tsx +++ b/webapp/src/components/App/Studies/MoveStudyDialog.tsx @@ -1,7 +1,7 @@ import { DialogProps } from "@mui/material"; import TextField from "@mui/material/TextField"; import { useSnackbar } from "notistack"; -import { dropLast, join, split } from "ramda"; +import * as R from "ramda"; import { useTranslation } from "react-i18next"; import { usePromise } from "react-use"; import { StudyMetadata } from "../../../common/types"; @@ -22,12 +22,17 @@ function MoveStudyDialog(props: Props) { const mounted = usePromise(); const { enqueueSnackbar } = useSnackbar(); const enqueueErrorSnackbar = useEnqueueErrorSnackbar(); + const defaultValues = { + folder: R.join("/", R.dropLast(1, R.split("/", study.folder || ""))), + }; //////////////////////////////////////////////////////////////// // Event Handlers //////////////////////////////////////////////////////////////// - const handleSubmit = async (data: SubmitHandlerPlus) => { + const handleSubmit = async ( + data: SubmitHandlerPlus + ) => { const { folder } = data.values; try { await mounted(moveStudy(study.id, folder)); @@ -53,11 +58,7 @@ function MoveStudyDialog(props: Props) { return ( @@ -71,7 +72,7 @@ function MoveStudyDialog(props: Props) { placeholder={t("studies.movefolderplaceholder") as string} InputLabelProps={ // Allow to show placeholder when field is empty - formObj.defaultValues?.folder ? { shrink: true } : {} + formObj.formState.defaultValues?.folder ? { shrink: true } : {} } fullWidth {...formObj.register("folder", { diff --git a/webapp/src/components/common/Form/index.tsx b/webapp/src/components/common/Form/index.tsx index 84b810fcc5..17ab16decb 100644 --- a/webapp/src/components/common/Form/index.tsx +++ b/webapp/src/components/common/Form/index.tsx @@ -1,12 +1,5 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { - FormEvent, - useCallback, - useEffect, - useMemo, - useRef, - useState, -} from "react"; +import { FormEvent, useCallback, useEffect, useMemo, useRef } from "react"; import { BatchFieldArrayUpdate, DeepPartial, @@ -18,6 +11,7 @@ import { SubmitErrorHandler, useForm, useFormContext as useFormContextOriginal, + UseFormProps, UseFormSetValue, UseFormUnregister, } from "react-hook-form"; @@ -40,11 +34,10 @@ import { getDirtyValues, stringToPath, toAutoSubmitConfig } from "./utils"; import useDebouncedState from "../../../hooks/useDebouncedState"; import usePrompt from "../../../hooks/usePrompt"; import { mergeSxProp } from "../../../utils/muiUtils"; -import useAsyncDefaultValues from "./useAsyncDefaultValues"; import { ControlPlus, + DefaultValuesFix, SubmitHandlerPlus, - UseFormPropsPlus, UseFormRegisterPlus, UseFormReturnPlus, } from "./types"; @@ -56,7 +49,14 @@ export interface FormProps< TFieldValues extends FieldValues = FieldValues, TContext = any > extends Omit, "onSubmit" | "children"> { - config?: UseFormPropsPlus; + config?: Omit, "defaultValues"> & { + // Workaround to fix issue related to the use of an async function for the default values: + // without that all props that use `TFieldValues` will have a wrong type, + // `TFieldValues` will be equal to `() => Promise` + defaultValues?: + | DefaultValuesFix + | (() => Promise); + }; onSubmit?: ( data: SubmitHandlerPlus, event?: React.BaseSyntheticEvent @@ -110,13 +110,16 @@ function Form( const fieldsChangeDuringAutoSubmitting = useRef[]>( [] ); - const { asyncDefaultValues, ...restConfig } = config || {}; - const [showSkeleton, setShowSkeleton] = useState(!!asyncDefaultValues); const formApi = useForm({ mode: "onChange", delayError: 750, - ...restConfig, + ...config, + // TS issue caused by the used of `DefaultValuesFix` instead of original `DefaultValues` + defaultValues: config?.defaultValues as UseFormProps< + TFieldValues, + TContext + >["defaultValues"], }); const { @@ -139,11 +142,6 @@ function Form( const isSubmittingRef = useAutoUpdateRef(isSubmitting); const isAutoSubmitEnabledRef = useAutoUpdateRef(autoSubmitConfig.enable); - useAsyncDefaultValues(asyncDefaultValues, (values) => { - reset(values); - setShowSkeleton(false); - }); - useUpdateEffect(() => { setShowLoader(isSubmitting); if (isSubmitting) { @@ -331,7 +329,6 @@ function Form( const controlPlus = control as ControlPlus; controlPlus.register = registerWrapper; controlPlus.unregister = unregisterWrapper; - controlPlus._showSkeleton = showSkeleton; const updateFieldArrayOriginal = control._updateFieldArray.bind(control); const updateFieldArrayWrapper: BatchFieldArrayUpdate = (...args) => { @@ -349,7 +346,6 @@ function Form( isAutoSubmitEnabledRef, registerWrapper, requestSubmit, - showSkeleton, unregisterWrapper, ]); diff --git a/webapp/src/components/common/Form/types.ts b/webapp/src/components/common/Form/types.ts index a92879b864..288acba1fb 100644 --- a/webapp/src/components/common/Form/types.ts +++ b/webapp/src/components/common/Form/types.ts @@ -2,17 +2,17 @@ import { Control, DeepPartial, - DefaultValues, FieldPath, FieldPathValue, FieldValues, RegisterOptions, - UseFormProps, UseFormRegisterReturn, UseFormReturn, } from "react-hook-form"; +import { O } from "ts-toolbelt"; export interface SubmitHandlerPlus< + // TODO Make parameter required TFieldValues extends FieldValues = FieldValues > { values: TFieldValues; @@ -24,19 +24,12 @@ export type AutoSubmitHandler< TFieldName extends FieldPath = FieldPath > = (value: FieldPathValue) => any | Promise; -export interface RegisterOptionsPlus< +export type RegisterOptionsPlus< TFieldValues extends FieldValues = FieldValues, TFieldName extends FieldPath = FieldPath -> extends RegisterOptions { +> = RegisterOptions & { onAutoSubmit?: AutoSubmitHandler; -} - -export interface UseFormPropsPlus< - TFieldValues extends FieldValues = FieldValues, - TContext = any -> extends UseFormProps { - asyncDefaultValues?: () => Promise>; -} +}; export type UseFormRegisterPlus< TFieldValues extends FieldValues = FieldValues @@ -50,7 +43,6 @@ export interface ControlPlus< TContext = any > extends Control { register: UseFormRegisterPlus; - _showSkeleton: boolean; } export interface UseFormReturnPlus< @@ -59,8 +51,9 @@ export interface UseFormReturnPlus< > extends UseFormReturn { register: UseFormRegisterPlus; control: ControlPlus; - /** - * @deprecated - */ - defaultValues?: UseFormProps["defaultValues"]; } + +export type DefaultValuesFix = O.Partial< + TFieldValues, + "deep" +>; diff --git a/webapp/src/components/common/Form/useAsyncDefaultValues.ts b/webapp/src/components/common/Form/useAsyncDefaultValues.ts deleted file mode 100644 index 985da7637c..0000000000 --- a/webapp/src/components/common/Form/useAsyncDefaultValues.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { DefaultValues, FieldValues } from "react-hook-form"; -import usePromiseWithSnackbarError from "../../../hooks/usePromiseWithSnackbarError"; -import { UseFormPropsPlus } from "./types"; - -function useAsyncDefaultValues( - fn: UseFormPropsPlus["asyncDefaultValues"], - onResolve: (defaultValues: DefaultValues) => void -): void { - usePromiseWithSnackbarError( - async () => { - if (fn) { - const data = await fn(); - onResolve(data); - } - }, - { errorMessage: "Cannot get form fields" } - ); -} - -export default useAsyncDefaultValues; diff --git a/webapp/src/components/common/FormGenerator/index.tsx b/webapp/src/components/common/FormGenerator/index.tsx index 21713db545..c48d6394a0 100644 --- a/webapp/src/components/common/FormGenerator/index.tsx +++ b/webapp/src/components/common/FormGenerator/index.tsx @@ -1,7 +1,7 @@ import * as R from "ramda"; import * as RA from "ramda-adjunct"; import { v4 as uuidv4 } from "uuid"; -import { DeepPartial, FieldValues, Path } from "react-hook-form"; +import { FieldValues, FormState, Path } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { SxProps, Theme } from "@mui/material"; import { Fragment, ReactNode, useMemo } from "react"; @@ -32,7 +32,7 @@ export interface IGeneratorField { name: IGeneratorField["name"], path: string, required?: boolean | string, - defaultValues?: DeepPartial + defaultValues?: FormState["defaultValues"] ) => | Omit< RegisterOptionsPlus & (string | undefined)>, @@ -82,7 +82,10 @@ export default function FormGenerator( [jsonTemplate] ); const [t] = useTranslation(); - const { control, defaultValues } = useFormContextPlus(); + const { + control, + formState: { defaultValues }, + } = useFormContextPlus(); return ( <> diff --git a/webapp/src/components/common/FormTable/index.tsx b/webapp/src/components/common/FormTable/index.tsx index a61e28f895..3dc5d7eabb 100644 --- a/webapp/src/components/common/FormTable/index.tsx +++ b/webapp/src/components/common/FormTable/index.tsx @@ -1,7 +1,6 @@ import * as RA from "ramda-adjunct"; import { ColumnSettings } from "handsontable/settings"; import { startCase } from "lodash"; -import { DefaultValues } from "react-hook-form"; import * as R from "ramda"; import type { SxProps } from "@mui/material"; import type { Theme } from "@mui/system"; @@ -12,6 +11,7 @@ import Table, { TableProps } from "./Table"; import { getCellType } from "./utils"; import { mergeSxProp } from "../../../utils/muiUtils"; import useMemoLocked from "../../../hooks/useMemoLocked"; +import { DefaultValuesFix } from "../Form/types"; type TableFieldValuesByRow = Record< IdType, @@ -21,7 +21,7 @@ type TableFieldValuesByRow = Record< export interface FormTableProps< TFieldValues extends TableFieldValuesByRow = TableFieldValuesByRow > { - defaultValues: DefaultValues; + defaultValues: DefaultValuesFix; onSubmit?: FormProps["onSubmit"]; onSubmitError?: FormProps["onSubmitError"]; formApiRef?: FormProps["apiRef"]; diff --git a/webapp/src/hoc/reactHookFormSupport.tsx b/webapp/src/hoc/reactHookFormSupport.tsx index 86948d87bd..511efbe1d4 100644 --- a/webapp/src/hoc/reactHookFormSupport.tsx +++ b/webapp/src/hoc/reactHookFormSupport.tsx @@ -22,7 +22,7 @@ import { interface ReactHookFormSupport { defaultValue?: NonNullable | ((props: any) => NonNullable); setValueAs?: (value: any) => any; - preValidate?: (value: any) => boolean; + preValidate?: (value: any, formValues: any) => boolean; } // `...args: any` allows to be compatible with all field editors @@ -140,14 +140,23 @@ function reactHookFormSupport( >(() => { if (preValidate) { if (RA.isFunction(validate)) { - return (v) => preValidate?.(v) && validate(v); + return (value, formValues) => { + return ( + preValidate?.(value, formValues) && validate(value, formValues) + ); + }; } if (RA.isPlainObj(validate)) { return Object.keys(validate).reduce((acc, key) => { - acc[key] = (v) => preValidate?.(v) && validate[key](v); + acc[key] = (value, formValues) => { + return ( + preValidate?.(value, formValues) && + validate[key](value, formValues) + ); + }; return acc; - }, {} as Record>>); + }, {} as Record, TFieldValues>>); } return preValidate; @@ -194,7 +203,7 @@ function reactHookFormSupport( /> ); - return control._showSkeleton ? ( + return control._formState.isLoading ? ( {field} From 5dbb85fdc733731c5fc16a258666869486b5cddf Mon Sep 17 00:00:00 2001 From: hatim dinia Date: Tue, 28 Mar 2023 17:16:43 +0200 Subject: [PATCH 18/70] feat(ui-hydro): add allocation form --- webapp/public/locales/en/main.json | 4 + webapp/public/locales/fr/main.json | 4 + .../Hydro/Allocation/AllocationField.tsx | 72 +++++++++ .../Hydro/Allocation/AllocationSelect.tsx | 50 ++++++ .../Areas/Hydro/Allocation/Fields.tsx | 153 ++++++++++++++++++ .../Areas/Hydro/Allocation/index.tsx | 57 +++++++ .../Areas/Hydro/Allocation/utils.ts | 41 +++++ .../Modelization/Areas/Hydro/index.tsx | 4 + .../Map/MapConfig/Layers/index.tsx | 2 + webapp/src/components/App/index.tsx | 5 + .../components/common/MatrixInput/index.tsx | 1 + 11 files changed, 393 insertions(+) create mode 100644 webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/AllocationField.tsx create mode 100644 webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/AllocationSelect.tsx create mode 100644 webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/Fields.tsx create mode 100644 webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/index.tsx create mode 100644 webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/utils.ts diff --git a/webapp/public/locales/en/main.json b/webapp/public/locales/en/main.json index e56c9b5108..45ddbc9080 100644 --- a/webapp/public/locales/en/main.json +++ b/webapp/public/locales/en/main.json @@ -393,6 +393,10 @@ "study.modelization.load": "Load", "study.modelization.thermal": "Thermal Clus.", "study.modelization.hydro": "Hydro", + "study.modelization.hydro.allocation.areaId": "Allocation for the {{areaId}} area", + "study.modelization.hydro.allocation.select": "Select an area", + "study.modelization.hydro.allocation.error.field.delete": "Error when deleting the allocation", + "study.modelization.hydro.allocation.error.field.delete.currentArea": "Error when deleting the allocation for the current area", "study.modelization.wind": "Wind", "study.modelization.solar": "Solar", "study.modelization.renewables": "Renewables Clus.", diff --git a/webapp/public/locales/fr/main.json b/webapp/public/locales/fr/main.json index 2f89b69003..17acbfdaac 100644 --- a/webapp/public/locales/fr/main.json +++ b/webapp/public/locales/fr/main.json @@ -393,6 +393,10 @@ "study.modelization.load": "Conso", "study.modelization.thermal": "Clus. Thermiques", "study.modelization.hydro": "Hydro", + "study.modelization.hydro.allocation.areaId": "Allocation pour la zone {{areaId}}", + "study.modelization.hydro.allocation.select": "Choisir une zone", + "study.modelization.hydro.allocation.error.field.delete": "Erreur lors de la suppression de l'allocation", + "study.modelization.hydro.allocation.error.field.delete.currentArea": "Erreur lors de la suppression de l'allocation pour la zone actuelle", "study.modelization.wind": "Éolien", "study.modelization.solar": "Solaire", "study.modelization.renewables": "Clus. Renouvelables", diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/AllocationField.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/AllocationField.tsx new file mode 100644 index 0000000000..3a7616d48b --- /dev/null +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/AllocationField.tsx @@ -0,0 +1,72 @@ +import { Paper, Typography, IconButton } from "@mui/material"; +import DeleteIcon from "@mui/icons-material/Delete"; +import { memo } from "react"; +import NumberFE from "../../../../../../../common/fieldEditors/NumberFE"; +import { useFormContextPlus } from "../../../../../../../common/Form"; +import { AllocationFormFields } from "./utils"; + +interface Props { + field: { + id: string; + areaId: string; + coefficient: number; + }; + index: number; + removeField: (index: number) => void; + getAreaLabel: (areaId: string) => string | undefined; +} + +export default memo(function AllocationField({ + field, + index, + removeField, + getAreaLabel, +}: Props) { + const { control } = useFormContextPlus(); + + //////////////////////////////////////////////////////////////// + // JSX + //////////////////////////////////////////////////////////////// + + return ( + + + {getAreaLabel(field.areaId)} + + { + if (value < 0) { + return false; + } + }, + }, + }} + /> + removeField(index)}> + + + + ); +}); diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/AllocationSelect.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/AllocationSelect.tsx new file mode 100644 index 0000000000..ba173f278d --- /dev/null +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/AllocationSelect.tsx @@ -0,0 +1,50 @@ +import { Box } from "@mui/material"; +import { t } from "i18next"; +import { memo, useMemo } from "react"; +import { Area } from "../../../../../../../../common/types"; +import SelectFE from "../../../../../../../common/fieldEditors/SelectFE"; + +interface Props { + filteredAreas: Array; + appendField: (obj: { areaId: string; coefficient: number }) => void; +} + +export default memo(function AllocationSelect({ + filteredAreas, + appendField, +}: Props) { + const options = useMemo( + () => + filteredAreas.map((area) => ({ + label: area.name, + value: area.id, + })), + [filteredAreas] + ); + + //////////////////////////////////////////////////////////////// + // JSX + //////////////////////////////////////////////////////////////// + + return ( + + {filteredAreas.length > 0 && ( + { + appendField({ areaId: e.target.value as string, coefficient: 0 }); + }} + size="small" + sx={{ minWidth: 180 }} + /> + )} + + ); +}); diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/Fields.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/Fields.tsx new file mode 100644 index 0000000000..2beba1d77d --- /dev/null +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/Fields.tsx @@ -0,0 +1,153 @@ +import { Box } from "@mui/material"; +import { useFieldArray } from "react-hook-form"; +import { useOutletContext } from "react-router"; +import { useCallback, useMemo } from "react"; +import { AxiosError } from "axios"; +import { t } from "i18next"; +import { useSnackbar } from "notistack"; +import Fieldset from "../../../../../../../common/Fieldset"; +import { useFormContextPlus } from "../../../../../../../common/Form"; +import useAppSelector from "../../../../../../../../redux/hooks/useAppSelector"; +import { + getAreas, + getCurrentAreaId, + getStudySynthesis, +} from "../../../../../../../../redux/selectors"; +import { StudyMetadata } from "../../../../../../../../common/types"; +import { AllocationFormFields, setAllocationFormFields } from "./utils"; +import AllocationSelect from "./AllocationSelect"; +import AllocationField from "./AllocationField"; +import useEnqueueErrorSnackbar from "../../../../../../../../hooks/useEnqueueErrorSnackbar"; + +export default function Fields() { + const { + study: { id: studyId }, + } = useOutletContext<{ study: StudyMetadata }>(); + const { enqueueSnackbar } = useSnackbar(); + const enqueueErrorSnackbar = useEnqueueErrorSnackbar(); + const areaId = useAppSelector(getCurrentAreaId); + const areas = useAppSelector((state) => getAreas(state, studyId)); + const areasById = useAppSelector( + (state) => getStudySynthesis(state, studyId)?.areas + ); + const { control, setValue } = useFormContextPlus(); + const { fields, append } = useFieldArray({ + control, + name: "allocation", + }); + + // Get a list of all the area IDs that are allocated + const allocatedAreaIds = useMemo( + () => fields.map((field) => field.areaId), + [fields] + ); + + // Filter the list of areas to only include those that are not allocated + const filteredAreas = useMemo( + () => areas.filter((area) => !allocatedAreaIds.includes(area.id)), + [areas, allocatedAreaIds] + ); + + //////////////////////////////////////////////////////////////// + // Utils + //////////////////////////////////////////////////////////////// + + // Get the name of an area based on its ID. + const getAreaLabel = useCallback( + (areaId: string) => { + return areasById?.[areaId]?.name ?? ""; + }, + [areasById] + ); + + // Append a new allocation field to the form values. + const appendField = useCallback( + (obj: { areaId: string; coefficient: number }) => { + append(obj); + }, + [append] + ); + + // Remove an allocation field at the specified index from the form values and the server. + const removeField = useCallback( + async (index: number) => { + // Make sure that the current area is not deleted. + const currentArea = areas.find((area) => area.id === areaId); + const deletedAreaId = fields[index].areaId; + + if (currentArea?.id === deletedAreaId) { + enqueueSnackbar( + t( + "study.modelization.hydro.allocation.error.field.delete.currentArea" + ), + { + variant: "warning", + autoHideDuration: 1800, + } + ); + return; + } + + const updatedFields = fields + .filter((_, currentIndex) => currentIndex !== index) + .map(({ id, ...fields }) => fields); + + try { + // TODO: implement and use deleteAllocationFormFields once it's supported by the API. + await setAllocationFormFields(studyId, areaId, { + allocation: updatedFields, + }); + setValue("allocation", updatedFields); + } catch (e) { + enqueueErrorSnackbar( + t("study.modelization.hydro.allocation.error.field.delete"), + e as AxiosError + ); + } + }, + [ + areaId, + areas, + enqueueErrorSnackbar, + enqueueSnackbar, + fields, + setValue, + studyId, + ] + ); + + //////////////////////////////////////////////////////////////// + // JSX + //////////////////////////////////////////////////////////////// + + return ( +

+ + + + {fields.map((field, index) => ( + + ))} + + +
+ ); +} diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/index.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/index.tsx new file mode 100644 index 0000000000..99a8647a6b --- /dev/null +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/index.tsx @@ -0,0 +1,57 @@ +import { Box } from "@mui/material"; +import { useOutletContext } from "react-router"; +import Form from "../../../../../../../common/Form"; +import Fields from "./Fields"; +import { StudyMetadata } from "../../../../../../../../common/types"; +import useAppSelector from "../../../../../../../../redux/hooks/useAppSelector"; +import { getCurrentAreaId } from "../../../../../../../../redux/selectors"; +import { + AllocationFormFields, + getAllocationFormFields, + setAllocationFormFields, +} from "./utils"; +import { SubmitHandlerPlus } from "../../../../../../../common/Form/types"; + +function Allocation() { + const { + study: { id: studyId }, + } = useOutletContext<{ study: StudyMetadata }>(); + const areaId = useAppSelector(getCurrentAreaId); + + //////////////////////////////////////////////////////////////// + // Event handlers + //////////////////////////////////////////////////////////////// + + const handleSubmit = (data: SubmitHandlerPlus) => { + setAllocationFormFields(studyId, areaId, { + allocation: data.values.allocation, + }); + }; + + //////////////////////////////////////////////////////////////// + // JSX + //////////////////////////////////////////////////////////////// + + return ( + + getAllocationFormFields(studyId, areaId), + }} + onSubmit={handleSubmit} + autoSubmit + > + + + + ); +} + +export default Allocation; diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/utils.ts b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/utils.ts new file mode 100644 index 0000000000..2192ae61ba --- /dev/null +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/utils.ts @@ -0,0 +1,41 @@ +import { StudyMetadata, Area } from "../../../../../../../../common/types"; +import client from "../../../../../../../../services/api/client"; + +//////////////////////////////////////////////////////////////// +// Types +//////////////////////////////////////////////////////////////// + +export interface AllocationField { + areaId: string; + coefficient: number; +} +export interface AllocationFormFields { + allocation: AllocationField[]; +} + +//////////////////////////////////////////////////////////////// +// Utils +//////////////////////////////////////////////////////////////// + +function makeRequestURL( + studyId: StudyMetadata["id"], + areaId: Area["name"] +): string { + return `v1/studies/${studyId}/areas/${areaId}/hydro/allocation`; +} + +export async function getAllocationFormFields( + studyId: StudyMetadata["id"], + areaId: Area["name"] +): Promise { + const res = await client.get(makeRequestURL(studyId, areaId)); + return res.data; +} + +export function setAllocationFormFields( + studyId: StudyMetadata["id"], + areaId: Area["name"], + values: Partial +): Promise { + return client.put(makeRequestURL(studyId, areaId), values); +} diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/index.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/index.tsx index a23151a380..ed92c9cbd4 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/index.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/index.tsx @@ -19,6 +19,10 @@ function Hydro() { label: "Inflow structure", path: `/studies/${study?.id}/explore/modelization/area/hydro/inflowstructure`, }, + { + label: "Allocation", + path: `/studies/${study?.id}/explore/modelization/area/hydro/allocation`, + }, { label: "Daily Power", path: `/studies/${study?.id}/explore/modelization/area/hydro/dailypower`, diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Map/MapConfig/Layers/index.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Map/MapConfig/Layers/index.tsx index 34c7b7474c..3b23c1f30a 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Map/MapConfig/Layers/index.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Map/MapConfig/Layers/index.tsx @@ -50,6 +50,8 @@ function Layers() { [columns.length] ); + console.log("defaultValues", defaultValues); + //////////////////////////////////////////////////////////////// // Event handlers //////////////////////////////////////////////////////////////// diff --git a/webapp/src/components/App/index.tsx b/webapp/src/components/App/index.tsx index 073b450c15..0231421459 100644 --- a/webapp/src/components/App/index.tsx +++ b/webapp/src/components/App/index.tsx @@ -51,6 +51,7 @@ import HydroMatrix from "./Singlestudy/explore/Modelization/Areas/Hydro/HydroMat import Layers from "./Singlestudy/explore/Modelization/Map/MapConfig/Layers"; import Districts from "./Singlestudy/explore/Modelization/Map/MapConfig/Districts"; import InflowStructure from "./Singlestudy/explore/Modelization/Areas/Hydro/InflowStructure"; +import Allocation from "./Singlestudy/explore/Modelization/Areas/Hydro/Allocation"; function App() { return ( @@ -92,6 +93,10 @@ function App() { path="inflowstructure" element={} /> + } + /> {HYDRO_ROUTES.map((route: HydroRoute) => ( { + console.log("change", change); if (source !== "loadData" && source !== "updateData") { try { if (change.length > 0) { From b2bee0ed8e9817da2ed642474504fb25a95a8360 Mon Sep 17 00:00:00 2001 From: hatim dinia Date: Tue, 28 Mar 2023 17:17:04 +0200 Subject: [PATCH 19/70] feat(api): add allocation form endpoints --- antarest/core/exceptions.py | 14 ++- .../study/business/allocation_management.py | 92 +++++++++++++++ antarest/study/service.py | 2 + antarest/study/web/study_data_blueprint.py | 45 ++++++++ webapp/public/locales/en/main.json | 3 +- webapp/public/locales/fr/main.json | 1 - .../Hydro/Allocation/AllocationField.tsx | 37 +++--- .../Hydro/Allocation/AllocationSelect.tsx | 15 ++- .../Areas/Hydro/Allocation/Fields.tsx | 107 +++--------------- .../Areas/Hydro/Allocation/index.tsx | 19 +++- .../Areas/Hydro/Allocation/utils.ts | 1 + .../Map/MapConfig/Layers/index.tsx | 2 - .../components/common/MatrixInput/index.tsx | 1 - 13 files changed, 212 insertions(+), 127 deletions(-) create mode 100644 antarest/study/business/allocation_management.py diff --git a/antarest/core/exceptions.py b/antarest/core/exceptions.py index 5a0476751f..86e243af5b 100644 --- a/antarest/core/exceptions.py +++ b/antarest/core/exceptions.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Optional +from typing import Optional, Set from fastapi import HTTPException @@ -213,6 +213,18 @@ def __init__(self, *district_ids: str): super().__init__(HTTPStatus.CONFLICT, msg) +class AllocationDataNotFound(HTTPException): + def __init__(self, area_id: str): + msg = f"No allocation data found for area {area_id}" + super().__init__(HTTPStatus.NOT_FOUND, msg) + + +class InvalidAllocationData(HTTPException): + def __init__(self, invalid_ids: Set[str]): + msg = f"Invalid areas: {', '.join(invalid_ids)}" + super().__init__(HTTPStatus.BAD_REQUEST, msg) + + class BadEditInstructionException(HTTPException): def __init__(self, message: str) -> None: super().__init__(HTTPStatus.BAD_REQUEST, message) diff --git a/antarest/study/business/allocation_management.py b/antarest/study/business/allocation_management.py new file mode 100644 index 0000000000..1e870abfbe --- /dev/null +++ b/antarest/study/business/allocation_management.py @@ -0,0 +1,92 @@ +from typing import List + +from pydantic import NonNegativeFloat +from starlette.responses import JSONResponse + +from antarest.core.exceptions import AllocationDataNotFound, InvalidAllocationData +from antarest.study.business.area_management import AreaInfoDTO +from antarest.study.business.utils import ( + FormFieldsBaseModel, + execute_or_add_commands, +) +from antarest.study.model import Study +from antarest.study.storage.storage_service import StudyStorageService +from antarest.study.storage.variantstudy.model.command.update_config import ( + UpdateConfig, +) + + +class AllocationField(FormFieldsBaseModel): + area_id: str + coefficient: NonNegativeFloat + + +class AllocationFormFields(FormFieldsBaseModel): + allocation: List[AllocationField] + + +class AllocationManager: + """ + Provides functionality for getting and updating allocation fields for a given area + """ + + def __init__(self, storage_service: StudyStorageService) -> None: + self.storage_service = storage_service + + def get_field_values( + self, study: Study, area_id: str, all_areas + ) -> AllocationFormFields: + file_study = self.storage_service.get_storage(study).get_raw(study) + allocation_data = file_study.tree.get( + f"input/hydro/allocation/{area_id}".split("/"), 2 + ) + + areas_ids = [area.id for area in all_areas] + + if not allocation_data: + raise AllocationDataNotFound(area_id) + + area, values = allocation_data.popitem() + + allocation_fields = AllocationFormFields( + allocation=[ + AllocationField.construct(areaId=area, coefficient=value) + for area, value in values.items() + if area in areas_ids # filter invalid areas + ] + ) + + return allocation_fields + + def set_field_values( + self, + study: Study, + area_id: str, + data: AllocationFormFields, + all_areas: List[AreaInfoDTO], + ) -> None: + """ + Sets allocation fields for a given area + """ + areas_ids = [area.id for area in all_areas] + allocation_ids = [field.area_id for field in data.allocation] + + if invalid_ids := set(allocation_ids) - set(areas_ids): + return InvalidAllocationData(invalid_ids) + + command_context = ( + self.storage_service.variant_study_service.command_factory.command_context + ) + command = UpdateConfig( + target=f"input/hydro/allocation/{area_id}/[allocation]", + data={ + field.area_id: float(field.coefficient) + for field in data.allocation + }, + command_context=command_context, + ) + + file_study = self.storage_service.get_storage(study).get_raw(study) + execute_or_add_commands( + study, file_study, [command], self.storage_service + ) diff --git a/antarest/study/service.py b/antarest/study/service.py index aa0aad2dce..147e6c75a8 100644 --- a/antarest/study/service.py +++ b/antarest/study/service.py @@ -59,6 +59,7 @@ from antarest.study.business.advanced_parameters_management import ( AdvancedParamsManager, ) +from antarest.study.business.allocation_management import AllocationManager from antarest.study.business.area_management import ( AreaCreationDTO, AreaInfoDTO, @@ -313,6 +314,7 @@ def __init__( self.storage_service ) self.hydro_manager = HydroManager(self.storage_service) + self.allocation_manager = AllocationManager(self.storage_service) self.renewable_manager = RenewableManager(self.storage_service) self.thermal_manager = ThermalManager(self.storage_service) self.ts_config_manager = TimeSeriesConfigManager(self.storage_service) diff --git a/antarest/study/web/study_data_blueprint.py b/antarest/study/web/study_data_blueprint.py index f7111e6a16..3e7c22da19 100644 --- a/antarest/study/web/study_data_blueprint.py +++ b/antarest/study/web/study_data_blueprint.py @@ -18,6 +18,7 @@ from antarest.study.business.advanced_parameters_management import ( AdvancedParamsFormFields, ) +from antarest.study.business.allocation_management import AllocationFormFields from antarest.study.business.area_management import ( AreaCreationDTO, AreaInfoDTO, @@ -1020,6 +1021,50 @@ def remove_constraint_term( study, binding_constraint_id, term_id ) + @bp.get( + path="/studies/{uuid}/areas/{area_id}/hydro/allocation", + tags=[APITag.study_data], + summary="Get allocation options for a given area", + response_model=AllocationFormFields, + ) + def get_allocation_form_values( + uuid: str, + area_id: str, + current_user: JWTUser = Depends(auth.get_current_user), + ) -> AllocationFormFields: + params = RequestParameters(user=current_user) + study = study_service.check_study_access( + uuid, StudyPermissionType.READ, params + ) + all_areas = study_service.get_all_areas( + uuid, area_type=AreaType.AREA, ui=False, params=params + ) + return study_service.allocation_manager.get_field_values( + study, area_id, all_areas + ) + + @bp.put( + path="/studies/{uuid}/areas/{area_id}/hydro/allocation", + tags=[APITag.study_data], + summary="Set allocation options for a given area", + ) + def set_allocation_form_values( + uuid: str, + area_id: str, + data: AllocationFormFields, + current_user: JWTUser = Depends(auth.get_current_user), + ) -> None: + params = RequestParameters(user=current_user) + study = study_service.check_study_access( + uuid, StudyPermissionType.WRITE, params + ) + all_areas = study_service.get_all_areas( + uuid, area_type=AreaType.AREA, ui=False, params=params + ) + return study_service.allocation_manager.set_field_values( + study, area_id, data, all_areas + ) + @bp.get( path="/studies/{uuid}/config/advancedparameters/form", tags=[APITag.study_data], diff --git a/webapp/public/locales/en/main.json b/webapp/public/locales/en/main.json index 45ddbc9080..5611f05dda 100644 --- a/webapp/public/locales/en/main.json +++ b/webapp/public/locales/en/main.json @@ -395,8 +395,7 @@ "study.modelization.hydro": "Hydro", "study.modelization.hydro.allocation.areaId": "Allocation for the {{areaId}} area", "study.modelization.hydro.allocation.select": "Select an area", - "study.modelization.hydro.allocation.error.field.delete": "Error when deleting the allocation", - "study.modelization.hydro.allocation.error.field.delete.currentArea": "Error when deleting the allocation for the current area", + "study.modelization.hydro.allocation.error.field.delete": "Error when deleting the allocation", "study.modelization.wind": "Wind", "study.modelization.solar": "Solar", "study.modelization.renewables": "Renewables Clus.", diff --git a/webapp/public/locales/fr/main.json b/webapp/public/locales/fr/main.json index 17acbfdaac..ed827c97da 100644 --- a/webapp/public/locales/fr/main.json +++ b/webapp/public/locales/fr/main.json @@ -396,7 +396,6 @@ "study.modelization.hydro.allocation.areaId": "Allocation pour la zone {{areaId}}", "study.modelization.hydro.allocation.select": "Choisir une zone", "study.modelization.hydro.allocation.error.field.delete": "Erreur lors de la suppression de l'allocation", - "study.modelization.hydro.allocation.error.field.delete.currentArea": "Erreur lors de la suppression de l'allocation pour la zone actuelle", "study.modelization.wind": "Éolien", "study.modelization.solar": "Solaire", "study.modelization.renewables": "Clus. Renouvelables", diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/AllocationField.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/AllocationField.tsx index 3a7616d48b..e2dfdf6ab6 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/AllocationField.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/AllocationField.tsx @@ -1,9 +1,10 @@ import { Paper, Typography, IconButton } from "@mui/material"; import DeleteIcon from "@mui/icons-material/Delete"; -import { memo } from "react"; import NumberFE from "../../../../../../../common/fieldEditors/NumberFE"; import { useFormContextPlus } from "../../../../../../../common/Form"; import { AllocationFormFields } from "./utils"; +import { getCurrentAreaId } from "../../../../../../../../redux/selectors"; +import useAppSelector from "../../../../../../../../redux/hooks/useAppSelector"; interface Props { field: { @@ -12,17 +13,13 @@ interface Props { coefficient: number; }; index: number; - removeField: (index: number) => void; - getAreaLabel: (areaId: string) => string | undefined; + label: string; + remove: (index: number) => void; } -export default memo(function AllocationField({ - field, - index, - removeField, - getAreaLabel, -}: Props) { +function AllocationField({ field, index, label, remove }: Props) { const { control } = useFormContextPlus(); + const currentAreaId = useAppSelector(getCurrentAreaId); //////////////////////////////////////////////////////////////// // JSX @@ -35,19 +32,14 @@ export default memo(function AllocationField({ display: "flex", alignContent: "center", alignItems: "center", + justifyContent: "strech", px: 2, borderRadius: 0, backgroundImage: "linear-gradient(rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0.05))", }} > - - {getAreaLabel(field.areaId)} - + {label} - removeField(index)}> + + remove(index)} + disabled={field.areaId === currentAreaId} + style={{ + visibility: field.areaId === currentAreaId ? "hidden" : "visible", + }} + > ); -}); +} + +export default AllocationField; diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/AllocationSelect.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/AllocationSelect.tsx index ba173f278d..72cb1dc564 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/AllocationSelect.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/AllocationSelect.tsx @@ -1,18 +1,15 @@ import { Box } from "@mui/material"; import { t } from "i18next"; -import { memo, useMemo } from "react"; +import { useMemo } from "react"; import { Area } from "../../../../../../../../common/types"; import SelectFE from "../../../../../../../common/fieldEditors/SelectFE"; interface Props { filteredAreas: Array; - appendField: (obj: { areaId: string; coefficient: number }) => void; + append: (obj: { areaId: string; coefficient: number }) => void; } -export default memo(function AllocationSelect({ - filteredAreas, - appendField, -}: Props) { +function AllocationSelect({ filteredAreas, append }: Props) { const options = useMemo( () => filteredAreas.map((area) => ({ @@ -39,7 +36,7 @@ export default memo(function AllocationSelect({ label={t("study.modelization.hydro.allocation.select")} options={options} onChange={(e) => { - appendField({ areaId: e.target.value as string, coefficient: 0 }); + append({ areaId: e.target.value as string, coefficient: 0 }); }} size="small" sx={{ minWidth: 180 }} @@ -47,4 +44,6 @@ export default memo(function AllocationSelect({ )} ); -}); +} + +export default AllocationSelect; diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/Fields.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/Fields.tsx index 2beba1d77d..5d13215526 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/Fields.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/Fields.tsx @@ -1,10 +1,8 @@ import { Box } from "@mui/material"; import { useFieldArray } from "react-hook-form"; import { useOutletContext } from "react-router"; -import { useCallback, useMemo } from "react"; -import { AxiosError } from "axios"; +import { useMemo } from "react"; import { t } from "i18next"; -import { useSnackbar } from "notistack"; import Fieldset from "../../../../../../../common/Fieldset"; import { useFormContextPlus } from "../../../../../../../common/Form"; import useAppSelector from "../../../../../../../../redux/hooks/useAppSelector"; @@ -14,107 +12,37 @@ import { getStudySynthesis, } from "../../../../../../../../redux/selectors"; import { StudyMetadata } from "../../../../../../../../common/types"; -import { AllocationFormFields, setAllocationFormFields } from "./utils"; +import { AllocationFormFields } from "./utils"; import AllocationSelect from "./AllocationSelect"; import AllocationField from "./AllocationField"; -import useEnqueueErrorSnackbar from "../../../../../../../../hooks/useEnqueueErrorSnackbar"; -export default function Fields() { +function Fields() { const { study: { id: studyId }, } = useOutletContext<{ study: StudyMetadata }>(); - const { enqueueSnackbar } = useSnackbar(); - const enqueueErrorSnackbar = useEnqueueErrorSnackbar(); const areaId = useAppSelector(getCurrentAreaId); const areas = useAppSelector((state) => getAreas(state, studyId)); const areasById = useAppSelector( (state) => getStudySynthesis(state, studyId)?.areas ); - const { control, setValue } = useFormContextPlus(); - const { fields, append } = useFieldArray({ + const { control } = useFormContextPlus(); + const { fields, append, remove } = useFieldArray({ control, name: "allocation", }); - // Get a list of all the area IDs that are allocated - const allocatedAreaIds = useMemo( - () => fields.map((field) => field.areaId), - [fields] - ); - - // Filter the list of areas to only include those that are not allocated - const filteredAreas = useMemo( - () => areas.filter((area) => !allocatedAreaIds.includes(area.id)), - [areas, allocatedAreaIds] - ); + const filteredAreas = useMemo(() => { + const allocatedAreaIds = fields.map((field) => field.areaId); + return areas.filter((area) => !allocatedAreaIds.includes(area.id)); + }, [areas, fields]); //////////////////////////////////////////////////////////////// // Utils //////////////////////////////////////////////////////////////// - // Get the name of an area based on its ID. - const getAreaLabel = useCallback( - (areaId: string) => { - return areasById?.[areaId]?.name ?? ""; - }, - [areasById] - ); - - // Append a new allocation field to the form values. - const appendField = useCallback( - (obj: { areaId: string; coefficient: number }) => { - append(obj); - }, - [append] - ); - - // Remove an allocation field at the specified index from the form values and the server. - const removeField = useCallback( - async (index: number) => { - // Make sure that the current area is not deleted. - const currentArea = areas.find((area) => area.id === areaId); - const deletedAreaId = fields[index].areaId; - - if (currentArea?.id === deletedAreaId) { - enqueueSnackbar( - t( - "study.modelization.hydro.allocation.error.field.delete.currentArea" - ), - { - variant: "warning", - autoHideDuration: 1800, - } - ); - return; - } - - const updatedFields = fields - .filter((_, currentIndex) => currentIndex !== index) - .map(({ id, ...fields }) => fields); - - try { - // TODO: implement and use deleteAllocationFormFields once it's supported by the API. - await setAllocationFormFields(studyId, areaId, { - allocation: updatedFields, - }); - setValue("allocation", updatedFields); - } catch (e) { - enqueueErrorSnackbar( - t("study.modelization.hydro.allocation.error.field.delete"), - e as AxiosError - ); - } - }, - [ - areaId, - areas, - enqueueErrorSnackbar, - enqueueSnackbar, - fields, - setValue, - studyId, - ] - ); + const getAreaLabel = (areaId: string) => { + return areasById?.[areaId]?.name ?? ""; + }; //////////////////////////////////////////////////////////////// // JSX @@ -132,18 +60,15 @@ export default function Fields() { gap: 2, }} > - + {fields.map((field, index) => ( ))} @@ -151,3 +76,5 @@ export default function Fields() { ); } + +export default Fields; diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/index.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/index.tsx index 99a8647a6b..586c9ef404 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/index.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/index.tsx @@ -1,5 +1,7 @@ import { Box } from "@mui/material"; import { useOutletContext } from "react-router"; +import { t } from "i18next"; +import { AxiosError } from "axios"; import Form from "../../../../../../../common/Form"; import Fields from "./Fields"; import { StudyMetadata } from "../../../../../../../../common/types"; @@ -11,21 +13,30 @@ import { setAllocationFormFields, } from "./utils"; import { SubmitHandlerPlus } from "../../../../../../../common/Form/types"; +import useEnqueueErrorSnackbar from "../../../../../../../../hooks/useEnqueueErrorSnackbar"; function Allocation() { const { study: { id: studyId }, } = useOutletContext<{ study: StudyMetadata }>(); const areaId = useAppSelector(getCurrentAreaId); + const enqueueErrorSnackbar = useEnqueueErrorSnackbar(); //////////////////////////////////////////////////////////////// // Event handlers //////////////////////////////////////////////////////////////// const handleSubmit = (data: SubmitHandlerPlus) => { - setAllocationFormFields(studyId, areaId, { - allocation: data.values.allocation, - }); + try { + setAllocationFormFields(studyId, areaId, { + allocation: data.values.allocation, + }); + } catch (e) { + enqueueErrorSnackbar( + t("study.modelization.hydro.allocation.error.field.delete"), + e as AxiosError + ); + } }; //////////////////////////////////////////////////////////////// @@ -38,6 +49,7 @@ function Allocation() { width: 1, height: 1, py: 1, + overflowX: "auto", }} >
getAllocationFormFields(studyId, areaId), }} onSubmit={handleSubmit} - autoSubmit > diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/utils.ts b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/utils.ts index 2192ae61ba..500f38eca0 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/utils.ts +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/utils.ts @@ -9,6 +9,7 @@ export interface AllocationField { areaId: string; coefficient: number; } + export interface AllocationFormFields { allocation: AllocationField[]; } diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Map/MapConfig/Layers/index.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Map/MapConfig/Layers/index.tsx index 3b23c1f30a..34c7b7474c 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Map/MapConfig/Layers/index.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Map/MapConfig/Layers/index.tsx @@ -50,8 +50,6 @@ function Layers() { [columns.length] ); - console.log("defaultValues", defaultValues); - //////////////////////////////////////////////////////////////// // Event handlers //////////////////////////////////////////////////////////////// diff --git a/webapp/src/components/common/MatrixInput/index.tsx b/webapp/src/components/common/MatrixInput/index.tsx index 492901cfd9..cd573b1fd8 100644 --- a/webapp/src/components/common/MatrixInput/index.tsx +++ b/webapp/src/components/common/MatrixInput/index.tsx @@ -88,7 +88,6 @@ function MatrixInput(props: PropsType) { //////////////////////////////////////////////////////////////// const handleUpdate = async (change: MatrixEditDTO[], source: string) => { - console.log("change", change); if (source !== "loadData" && source !== "updateData") { try { if (change.length > 0) { From 08680af4344b7dd9aa365267a0deb8d9094f0294 Mon Sep 17 00:00:00 2001 From: Laurent LAPORTE Date: Fri, 31 Mar 2023 13:38:23 +0200 Subject: [PATCH 20/70] docs(api): add API documentation for the hydraulic allocation (and fix minor awkwardness) --- antarest/core/exceptions.py | 19 +++- .../study/business/allocation_management.py | 90 +++++++++++++------ antarest/study/web/study_data_blueprint.py | 70 ++++++++++++--- 3 files changed, 133 insertions(+), 46 deletions(-) diff --git a/antarest/core/exceptions.py b/antarest/core/exceptions.py index 86e243af5b..c62d1a54aa 100644 --- a/antarest/core/exceptions.py +++ b/antarest/core/exceptions.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Optional, Set +from typing import Iterable, Optional from fastapi import HTTPException @@ -214,14 +214,25 @@ def __init__(self, *district_ids: str): class AllocationDataNotFound(HTTPException): + """ + Exception raised if no hydraulic allocation + is defined for the given production area (the `.ini` file may be missing). + """ + def __init__(self, area_id: str): - msg = f"No allocation data found for area {area_id}" + msg = f"No allocation data found for area '{area_id}'" super().__init__(HTTPStatus.NOT_FOUND, msg) class InvalidAllocationData(HTTPException): - def __init__(self, invalid_ids: Set[str]): - msg = f"Invalid areas: {', '.join(invalid_ids)}" + """ + Exception raised if at least one area + of the hydraulic allocation table is not an existing area. + """ + + def __init__(self, invalid_ids: Iterable[str]): + areas = ", ".join([f"'{a}'" for a in invalid_ids]) + msg = f"Invalid areas: {areas}" super().__init__(HTTPStatus.BAD_REQUEST, msg) diff --git a/antarest/study/business/allocation_management.py b/antarest/study/business/allocation_management.py index 1e870abfbe..49789e5c5e 100644 --- a/antarest/study/business/allocation_management.py +++ b/antarest/study/business/allocation_management.py @@ -1,9 +1,9 @@ from typing import List -from pydantic import NonNegativeFloat -from starlette.responses import JSONResponse - -from antarest.core.exceptions import AllocationDataNotFound, InvalidAllocationData +from antarest.core.exceptions import ( + AllocationDataNotFound, + InvalidAllocationData, +) from antarest.study.business.area_management import AreaInfoDTO from antarest.study.business.utils import ( FormFieldsBaseModel, @@ -14,41 +14,69 @@ from antarest.study.storage.variantstudy.model.command.update_config import ( UpdateConfig, ) +from pydantic import NonNegativeFloat class AllocationField(FormFieldsBaseModel): + """Consumption coefficient of a given area.""" + area_id: str coefficient: NonNegativeFloat class AllocationFormFields(FormFieldsBaseModel): + """ + Hydraulic allocation of a production area: + electrical energy consumption coefficients to consider for each area. + """ + allocation: List[AllocationField] class AllocationManager: """ - Provides functionality for getting and updating allocation fields for a given area + Provides functionality for getting and updating hydraulic allocation + coefficients for a given production area. """ def __init__(self, storage_service: StudyStorageService) -> None: self.storage_service = storage_service def get_field_values( - self, study: Study, area_id: str, all_areas + self, all_areas: List[AreaInfoDTO], study: Study, area_id: str ) -> AllocationFormFields: + """ + Get the hydraulic allocation table of the given production area. + + Get, for a given production area, the electrical energy consumption + coefficients to consider for the other areas. + Those values are used to fill in the allocation table form. + + Args: + study: the current study + area_id: Production area ID from which we want to retrieve the allocation table. + all_areas: The complete list of study areas. + + Returns: + Hydraulic allocation of the production area: + The list of electrical energy consumption coefficients + to consider for each area. + + Raises: + AllocationDataNotFound: exception raised if no hydraulic allocation + is defined for the given production area (the `.ini` file may be missing). + """ file_study = self.storage_service.get_storage(study).get_raw(study) allocation_data = file_study.tree.get( - f"input/hydro/allocation/{area_id}".split("/"), 2 + f"input/hydro/allocation/{area_id}".split("/"), depth=2 ) - - areas_ids = [area.id for area in all_areas] - if not allocation_data: raise AllocationDataNotFound(area_id) + areas_ids = {area.id for area in all_areas} area, values = allocation_data.popitem() - allocation_fields = AllocationFormFields( + return AllocationFormFields.construct( allocation=[ AllocationField.construct(areaId=area, coefficient=value) for area, value in values.items() @@ -56,33 +84,39 @@ def get_field_values( ] ) - return allocation_fields - def set_field_values( - self, - study: Study, - area_id: str, - data: AllocationFormFields, - all_areas: List[AreaInfoDTO], + self, + all_areas: List[AreaInfoDTO], + study: Study, + area_id: str, + data: AllocationFormFields, ) -> None: """ - Sets allocation fields for a given area + Update the hydraulic allocation table of the given production area. + + Args: + study: the current study + area_id: Production area ID from which we want to retrieve the allocation table. + all_areas: The complete list of study areas. + data: Hydraulic allocation of the production area: + The list of electrical energy consumption coefficients + to consider for each area. + + Raises: + InvalidAllocationData: exception raised if at least one area + of the hydraulic allocation table is not an existing area. """ - areas_ids = [area.id for area in all_areas] - allocation_ids = [field.area_id for field in data.allocation] - - if invalid_ids := set(allocation_ids) - set(areas_ids): - return InvalidAllocationData(invalid_ids) + allocation_ids = {field.area_id for field in data.allocation} + areas_ids = {area.id for area in all_areas} + if invalid_ids := allocation_ids - areas_ids: + raise InvalidAllocationData(invalid_ids) command_context = ( self.storage_service.variant_study_service.command_factory.command_context ) command = UpdateConfig( target=f"input/hydro/allocation/{area_id}/[allocation]", - data={ - field.area_id: float(field.coefficient) - for field in data.allocation - }, + data={f.area_id: f.coefficient for f in data.allocation}, command_context=command_context, ) diff --git a/antarest/study/web/study_data_blueprint.py b/antarest/study/web/study_data_blueprint.py index 3e7c22da19..72cb3875d5 100644 --- a/antarest/study/web/study_data_blueprint.py +++ b/antarest/study/web/study_data_blueprint.py @@ -1,7 +1,6 @@ import logging -from typing import Any, Dict, List, Optional, Union - -from fastapi import APIRouter, Body, Depends +from http import HTTPStatus +from typing import Any, Dict, List, Optional, Union, cast from antarest.core.config import Config from antarest.core.jwt import JWTUser @@ -56,6 +55,8 @@ from antarest.study.business.timeseries_config_management import TSFormFields from antarest.study.model import PatchArea, PatchCluster from antarest.study.service import StudyService +from fastapi import APIRouter, Body, Depends +from fastapi.params import Body logger = logging.getLogger(__name__) @@ -65,12 +66,13 @@ def create_study_data_routes( ) -> APIRouter: """ Endpoint implementation for studies area management + Args: study_service: study service facade to handle request config: main server configuration Returns: - + The FastAPI route for Study data management """ bp = APIRouter(prefix="/v1") auth = Auth(config) @@ -1024,7 +1026,7 @@ def remove_constraint_term( @bp.get( path="/studies/{uuid}/areas/{area_id}/hydro/allocation", tags=[APITag.study_data], - summary="Get allocation options for a given area", + summary="Get the hydraulic allocation of a given area", response_model=AllocationFormFields, ) def get_allocation_form_values( @@ -1032,37 +1034,77 @@ def get_allocation_form_values( area_id: str, current_user: JWTUser = Depends(auth.get_current_user), ) -> AllocationFormFields: + """ + Get, for a given production area, the electrical energy consumption + coefficients to consider for the other areas. + + Parameters: + + - `uuid`: Study UUID, e.g.: '7cdc506c-808e-4bd5-8a6a-493e1f028c3b' + - `area_id`: Production area ID, e.g.: 'EAST' + + Returns form fields used for the allocation table: + The list of electrical energy consumption coefficients + to consider for each area. + """ params = RequestParameters(user=current_user) study = study_service.check_study_access( uuid, StudyPermissionType.READ, params ) - all_areas = study_service.get_all_areas( - uuid, area_type=AreaType.AREA, ui=False, params=params + all_areas = cast( + List[AreaInfoDTO], # because `ui=False` + study_service.get_all_areas( + uuid, area_type=AreaType.AREA, ui=False, params=params + ), ) return study_service.allocation_manager.get_field_values( - study, area_id, all_areas + all_areas, study, area_id ) @bp.put( path="/studies/{uuid}/areas/{area_id}/hydro/allocation", tags=[APITag.study_data], - summary="Set allocation options for a given area", + summary="Update the hydraulic allocation of a given area", + status_code=HTTPStatus.NO_CONTENT, ) def set_allocation_form_values( uuid: str, area_id: str, - data: AllocationFormFields, + data: AllocationFormFields = Body( + ..., + example=AllocationFormFields( + allocation=[ + {"areaId": "EAST", "coefficient": 1}, + {"areaId": "NORTH", "coefficient": 0.20}, + ] + ), + ), current_user: JWTUser = Depends(auth.get_current_user), ) -> None: + """ + Update, for a given production area, the electrical energy consumption + coefficients to consider for the other areas. + + Parameters: + + - `uuid`: Study UUID, e.g.: '7cdc506c-808e-4bd5-8a6a-493e1f028c3b' + - `area_id`: Production area ID, e.g.: 'EAST' + - `data`: Hydraulic allocation of the production area: + The list of electrical energy consumption coefficients + to consider for each area. + """ params = RequestParameters(user=current_user) study = study_service.check_study_access( uuid, StudyPermissionType.WRITE, params ) - all_areas = study_service.get_all_areas( - uuid, area_type=AreaType.AREA, ui=False, params=params + all_areas = cast( + List[AreaInfoDTO], # because `ui=False` + study_service.get_all_areas( + uuid, area_type=AreaType.AREA, ui=False, params=params + ), ) - return study_service.allocation_manager.set_field_values( - study, area_id, data, all_areas + study_service.allocation_manager.set_field_values( + all_areas, study, area_id, data ) @bp.get( From 0df0b2121e761a91946452874d70bc80dbe07647 Mon Sep 17 00:00:00 2001 From: hatim dinia Date: Fri, 31 Mar 2023 15:29:57 +0200 Subject: [PATCH 21/70] feat(ui-hydro): display area name instead of id --- webapp/public/locales/en/main.json | 2 +- webapp/public/locales/fr/main.json | 2 +- .../Modelization/Areas/Hydro/Allocation/Fields.tsx | 13 +++++++------ 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/webapp/public/locales/en/main.json b/webapp/public/locales/en/main.json index 5611f05dda..91413db3c1 100644 --- a/webapp/public/locales/en/main.json +++ b/webapp/public/locales/en/main.json @@ -393,7 +393,7 @@ "study.modelization.load": "Load", "study.modelization.thermal": "Thermal Clus.", "study.modelization.hydro": "Hydro", - "study.modelization.hydro.allocation.areaId": "Allocation for the {{areaId}} area", + "study.modelization.hydro.allocation.areaName": "Allocation for the {{areaName}} area", "study.modelization.hydro.allocation.select": "Select an area", "study.modelization.hydro.allocation.error.field.delete": "Error when deleting the allocation", "study.modelization.wind": "Wind", diff --git a/webapp/public/locales/fr/main.json b/webapp/public/locales/fr/main.json index ed827c97da..f86fc13e03 100644 --- a/webapp/public/locales/fr/main.json +++ b/webapp/public/locales/fr/main.json @@ -393,7 +393,7 @@ "study.modelization.load": "Conso", "study.modelization.thermal": "Clus. Thermiques", "study.modelization.hydro": "Hydro", - "study.modelization.hydro.allocation.areaId": "Allocation pour la zone {{areaId}}", + "study.modelization.hydro.allocation.areaName": "Allocation pour la zone {{areaName}}", "study.modelization.hydro.allocation.select": "Choisir une zone", "study.modelization.hydro.allocation.error.field.delete": "Erreur lors de la suppression de l'allocation", "study.modelization.wind": "Éolien", diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/Fields.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/Fields.tsx index 5d13215526..18cdb277ec 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/Fields.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/Fields.tsx @@ -20,16 +20,17 @@ function Fields() { const { study: { id: studyId }, } = useOutletContext<{ study: StudyMetadata }>(); - const areaId = useAppSelector(getCurrentAreaId); - const areas = useAppSelector((state) => getAreas(state, studyId)); - const areasById = useAppSelector( - (state) => getStudySynthesis(state, studyId)?.areas - ); const { control } = useFormContextPlus(); const { fields, append, remove } = useFieldArray({ control, name: "allocation", }); + const areaId = useAppSelector(getCurrentAreaId); + const areas = useAppSelector((state) => getAreas(state, studyId)); + const areasById = useAppSelector( + (state) => getStudySynthesis(state, studyId)?.areas + ); + const areaName = areasById?.[areaId]?.name ?? ""; const filteredAreas = useMemo(() => { const allocatedAreaIds = fields.map((field) => field.areaId); @@ -50,7 +51,7 @@ function Fields() { return (
Date: Fri, 31 Mar 2023 17:16:06 +0200 Subject: [PATCH 22/70] feat(ui-hydro): update allocation form styles --- webapp/public/locales/en/main.json | 3 +- webapp/public/locales/fr/main.json | 3 +- .../Hydro/Allocation/AllocationField.tsx | 29 +++++---- .../Hydro/Allocation/AllocationSelect.tsx | 10 +--- .../Areas/Hydro/Allocation/Fields.tsx | 59 ++++++++++--------- .../Areas/Hydro/Allocation/index.tsx | 2 +- 6 files changed, 52 insertions(+), 54 deletions(-) diff --git a/webapp/public/locales/en/main.json b/webapp/public/locales/en/main.json index 91413db3c1..86b81b9006 100644 --- a/webapp/public/locales/en/main.json +++ b/webapp/public/locales/en/main.json @@ -393,8 +393,7 @@ "study.modelization.load": "Load", "study.modelization.thermal": "Thermal Clus.", "study.modelization.hydro": "Hydro", - "study.modelization.hydro.allocation.areaName": "Allocation for the {{areaName}} area", - "study.modelization.hydro.allocation.select": "Select an area", + "study.modelization.hydro.allocation.select": "Add an area", "study.modelization.hydro.allocation.error.field.delete": "Error when deleting the allocation", "study.modelization.wind": "Wind", "study.modelization.solar": "Solar", diff --git a/webapp/public/locales/fr/main.json b/webapp/public/locales/fr/main.json index f86fc13e03..90856a0405 100644 --- a/webapp/public/locales/fr/main.json +++ b/webapp/public/locales/fr/main.json @@ -393,8 +393,7 @@ "study.modelization.load": "Conso", "study.modelization.thermal": "Clus. Thermiques", "study.modelization.hydro": "Hydro", - "study.modelization.hydro.allocation.areaName": "Allocation pour la zone {{areaName}}", - "study.modelization.hydro.allocation.select": "Choisir une zone", + "study.modelization.hydro.allocation.select": "Ajouter une zone", "study.modelization.hydro.allocation.error.field.delete": "Erreur lors de la suppression de l'allocation", "study.modelization.wind": "Éolien", "study.modelization.solar": "Solaire", diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/AllocationField.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/AllocationField.tsx index e2dfdf6ab6..8b78c1dc6c 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/AllocationField.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/AllocationField.tsx @@ -1,5 +1,5 @@ -import { Paper, Typography, IconButton } from "@mui/material"; -import DeleteIcon from "@mui/icons-material/Delete"; +import { Typography, IconButton, Box } from "@mui/material"; +import RemoveCircleOutlineIcon from "@mui/icons-material/RemoveCircleOutline"; import NumberFE from "../../../../../../../common/fieldEditors/NumberFE"; import { useFormContextPlus } from "../../../../../../../common/Form"; import { AllocationFormFields } from "./utils"; @@ -26,26 +26,30 @@ function AllocationField({ field, index, label, remove }: Props) { //////////////////////////////////////////////////////////////// return ( - - {label} + + {label} + { @@ -56,7 +60,6 @@ function AllocationField({ field, index, label, remove }: Props) { }, }} /> - remove(index)} disabled={field.areaId === currentAreaId} @@ -64,9 +67,9 @@ function AllocationField({ field, index, label, remove }: Props) { visibility: field.areaId === currentAreaId ? "hidden" : "visible", }} > - + - + ); } diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/AllocationSelect.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/AllocationSelect.tsx index 72cb1dc564..57056b44c4 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/AllocationSelect.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/AllocationSelect.tsx @@ -24,13 +24,7 @@ function AllocationSelect({ filteredAreas, append }: Props) { //////////////////////////////////////////////////////////////// return ( - + {filteredAreas.length > 0 && ( )} diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/Fields.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/Fields.tsx index 18cdb277ec..f09a778a9c 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/Fields.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/Fields.tsx @@ -1,14 +1,12 @@ -import { Box } from "@mui/material"; +import { Box, Divider, Paper } from "@mui/material"; import { useFieldArray } from "react-hook-form"; import { useOutletContext } from "react-router"; import { useMemo } from "react"; -import { t } from "i18next"; import Fieldset from "../../../../../../../common/Fieldset"; import { useFormContextPlus } from "../../../../../../../common/Form"; import useAppSelector from "../../../../../../../../redux/hooks/useAppSelector"; import { getAreas, - getCurrentAreaId, getStudySynthesis, } from "../../../../../../../../redux/selectors"; import { StudyMetadata } from "../../../../../../../../common/types"; @@ -25,12 +23,10 @@ function Fields() { control, name: "allocation", }); - const areaId = useAppSelector(getCurrentAreaId); const areas = useAppSelector((state) => getAreas(state, studyId)); const areasById = useAppSelector( (state) => getStudySynthesis(state, studyId)?.areas ); - const areaName = areasById?.[areaId]?.name ?? ""; const filteredAreas = useMemo(() => { const allocatedAreaIds = fields.map((field) => field.areaId); @@ -50,29 +46,36 @@ function Fields() { //////////////////////////////////////////////////////////////// return ( -
- - - - {fields.map((field, index) => ( - - ))} - +
+ + + + {fields.map((field, index) => ( + + ))} + + + +
); diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/index.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/index.tsx index 586c9ef404..4d76a85195 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/index.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/index.tsx @@ -48,7 +48,7 @@ function Allocation() { sx={{ width: 1, height: 1, - py: 1, + p: 1, overflowX: "auto", }} > From 22fd1962ab2b37ea9f2b32dc0448922a5e2db121 Mon Sep 17 00:00:00 2001 From: Laurent LAPORTE Date: Fri, 31 Mar 2023 23:16:12 +0200 Subject: [PATCH 23/70] test(api): add unit tests and improve API to return allocation matrix --- antarest/core/exceptions.py | 12 - .../study/business/allocation_management.py | 100 ++++++-- antarest/study/web/study_data_blueprint.py | 33 ++- tests/integration/conftest.py | 2 +- .../study_data_blueprint/__init__.py | 0 .../study_data_blueprint/conftest.py | 57 +++++ .../test_hydro_allocation.py | 237 ++++++++++++++++++ 7 files changed, 398 insertions(+), 43 deletions(-) create mode 100644 tests/integration/study_data_blueprint/__init__.py create mode 100644 tests/integration/study_data_blueprint/conftest.py create mode 100644 tests/integration/study_data_blueprint/test_hydro_allocation.py diff --git a/antarest/core/exceptions.py b/antarest/core/exceptions.py index c62d1a54aa..2971752651 100644 --- a/antarest/core/exceptions.py +++ b/antarest/core/exceptions.py @@ -224,18 +224,6 @@ def __init__(self, area_id: str): super().__init__(HTTPStatus.NOT_FOUND, msg) -class InvalidAllocationData(HTTPException): - """ - Exception raised if at least one area - of the hydraulic allocation table is not an existing area. - """ - - def __init__(self, invalid_ids: Iterable[str]): - areas = ", ".join([f"'{a}'" for a in invalid_ids]) - msg = f"Invalid areas: {areas}" - super().__init__(HTTPStatus.BAD_REQUEST, msg) - - class BadEditInstructionException(HTTPException): def __init__(self, message: str) -> None: super().__init__(HTTPStatus.BAD_REQUEST, message) diff --git a/antarest/study/business/allocation_management.py b/antarest/study/business/allocation_management.py index 49789e5c5e..d8fc962477 100644 --- a/antarest/study/business/allocation_management.py +++ b/antarest/study/business/allocation_management.py @@ -1,9 +1,8 @@ -from typing import List +from typing import List, Union -from antarest.core.exceptions import ( - AllocationDataNotFound, - InvalidAllocationData, -) +import numpy +import numpy as np +from antarest.core.exceptions import AllocationDataNotFound, AreaNotFound from antarest.study.business.area_management import AreaInfoDTO from antarest.study.business.utils import ( FormFieldsBaseModel, @@ -14,7 +13,7 @@ from antarest.study.storage.variantstudy.model.command.update_config import ( UpdateConfig, ) -from pydantic import NonNegativeFloat +from pydantic import NonNegativeFloat, validator, BaseModel class AllocationField(FormFieldsBaseModel): @@ -32,6 +31,37 @@ class AllocationFormFields(FormFieldsBaseModel): allocation: List[AllocationField] + # noinspection PyMethodParameters + @validator("allocation") + def validate_hydro_allocation_table(cls, fields: List[AllocationField]): + """ + Validate hydraulic allocation table. + + Args: + fields: A list of validated allocation fields containing + the coefficients to be validated. + + Raises: + ValueError: + If the coefficients array is empty or has no non-null values. + + Returns: + The allocation fields. + """ + array = np.array([f.coefficient for f in fields]) + if array.size == 0: + raise ValueError("coefficients array is empty") + elif np.any(array == 0): + raise ValueError("coefficients array has no non-null values") + else: + return fields + + +class AllocationMatrix(BaseModel): + index: List[str] + columns: List[str] + data: List[List[NonNegativeFloat]] + class AllocationManager: """ @@ -44,17 +74,26 @@ def __init__(self, storage_service: StudyStorageService) -> None: def get_field_values( self, all_areas: List[AreaInfoDTO], study: Study, area_id: str - ) -> AllocationFormFields: + ) -> Union[AllocationFormFields, AllocationMatrix]: """ - Get the hydraulic allocation table of the given production area. + Get the hydraulic allocation table of the given production area (or areas). Get, for a given production area, the electrical energy consumption coefficients to consider for the other areas. Those values are used to fill in the allocation table form. + If several areas are selected (with a comma-separated list of area IDs), + it returns the hydraulic allocation matrix of the selected areas. + + If the star symbol "*" is used, it returns the hydraulic allocation + matrix of the all the areas. + Args: study: the current study - area_id: Production area ID from which we want to retrieve the allocation table. + area_id: + A production area ID (e.g.: 'EAST'), + a comma-separated list of production area IDs (e.g.: 'EAST,SOUTH'), or + the star symbol '*' (all areas). all_areas: The complete list of study areas. Returns: @@ -62,6 +101,8 @@ def get_field_values( The list of electrical energy consumption coefficients to consider for each area. + Or, returns the hydraulic allocation matrix of several areas + Raises: AllocationDataNotFound: exception raised if no hydraulic allocation is defined for the given production area (the `.ini` file may be missing). @@ -72,17 +113,31 @@ def get_field_values( ) if not allocation_data: raise AllocationDataNotFound(area_id) - - areas_ids = {area.id for area in all_areas} - area, values = allocation_data.popitem() - - return AllocationFormFields.construct( - allocation=[ - AllocationField.construct(areaId=area, coefficient=value) - for area, value in values.items() - if area in areas_ids # filter invalid areas - ] - ) + if len(allocation_data) == 1: + # single-column allocation table + areas_ids = {area.id for area in all_areas} + allocations = allocation_data["[allocation]"] + return AllocationFormFields.construct( + allocation=[ + AllocationField.construct(areaId=area, coefficient=value) + for area, value in allocations.items() + if area in areas_ids # filter invalid areas + ] + ) + else: + # allocation matrix + rows = sorted(area.id for area in all_areas) + columns = sorted(allocation_data) + array = numpy.zeros((len(rows), len(columns)), dtype=numpy.float) + for prod_area, allocation_dict in allocation_data.items(): + allocations = allocation_dict["[allocation]"] + for cons_area, coefficient in allocations.items(): + row_idx = rows.index(cons_area) + col_idx = columns.index(prod_area) + array[row_idx][col_idx] = coefficient + return AllocationMatrix.construct( + index=rows, columns=columns, data=array.tolist() + ) def set_field_values( self, @@ -103,13 +158,14 @@ def set_field_values( to consider for each area. Raises: - InvalidAllocationData: exception raised if at least one area + AreaNotFound: exception raised if at least one area of the hydraulic allocation table is not an existing area. """ allocation_ids = {field.area_id for field in data.allocation} areas_ids = {area.id for area in all_areas} if invalid_ids := allocation_ids - areas_ids: - raise InvalidAllocationData(invalid_ids) + # sorting is mandatory for unit tests + raise AreaNotFound(*sorted(invalid_ids)) command_context = ( self.storage_service.variant_study_service.command_factory.command_context diff --git a/antarest/study/web/study_data_blueprint.py b/antarest/study/web/study_data_blueprint.py index 72cb3875d5..e0d394b915 100644 --- a/antarest/study/web/study_data_blueprint.py +++ b/antarest/study/web/study_data_blueprint.py @@ -17,7 +17,10 @@ from antarest.study.business.advanced_parameters_management import ( AdvancedParamsFormFields, ) -from antarest.study.business.allocation_management import AllocationFormFields +from antarest.study.business.allocation_management import ( + AllocationFormFields, + AllocationMatrix, +) from antarest.study.business.area_management import ( AreaCreationDTO, AreaInfoDTO, @@ -1026,26 +1029,40 @@ def remove_constraint_term( @bp.get( path="/studies/{uuid}/areas/{area_id}/hydro/allocation", tags=[APITag.study_data], - summary="Get the hydraulic allocation of a given area", - response_model=AllocationFormFields, + summary=( + "Get the hydraulic allocation of a given area," + " or the allocation matrix of several areas" + ), + response_model=Union[AllocationFormFields, AllocationMatrix], ) def get_allocation_form_values( uuid: str, area_id: str, current_user: JWTUser = Depends(auth.get_current_user), - ) -> AllocationFormFields: + ) -> Union[AllocationFormFields, AllocationMatrix]: """ Get, for a given production area, the electrical energy consumption coefficients to consider for the other areas. + If several areas are selected (with a comma-separated list of area IDs), + it returns the hydraulic allocation matrix of the selected areas. + + If the star symbol "*" is used, it returns the hydraulic allocation + matrix of the all the areas. + Parameters: - - `uuid`: Study UUID, e.g.: '7cdc506c-808e-4bd5-8a6a-493e1f028c3b' - - `area_id`: Production area ID, e.g.: 'EAST' + - `uuid`: Study UUID (e.g.: '7cdc506c-808e-4bd5-8a6a-493e1f028c3b') + - `area_id`: + A production area ID (e.g.: 'EAST'), + a comma-separated list of production area IDs (e.g.: 'EAST,SOUTH'), or + the star symbol '*' (all areas). Returns form fields used for the allocation table: The list of electrical energy consumption coefficients to consider for each area. + + Or, returns the hydraulic allocation matrix of several areas """ params = RequestParameters(user=current_user) study = study_service.check_study_access( @@ -1087,8 +1104,8 @@ def set_allocation_form_values( Parameters: - - `uuid`: Study UUID, e.g.: '7cdc506c-808e-4bd5-8a6a-493e1f028c3b' - - `area_id`: Production area ID, e.g.: 'EAST' + - `uuid`: Study UUID (e.g.: '7cdc506c-808e-4bd5-8a6a-493e1f028c3b') + - `area_id`: Production area ID (e.g.: 'EAST') - `data`: Hydraulic allocation of the production area: The list of electrical energy consumption coefficients to consider for each area. diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index a38448973d..bf555afa74 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -24,7 +24,7 @@ def sta_mini_zip_path(project_path: Path) -> Path: @pytest.fixture def app(tmp_path: str, sta_mini_zip_path: Path, project_path: Path): - engine = create_engine("sqlite:///:memory:", echo=True) + engine = create_engine("sqlite:///:memory:", echo=False) Base.metadata.create_all(engine) DBSessionMiddleware( Mock(), diff --git a/tests/integration/study_data_blueprint/__init__.py b/tests/integration/study_data_blueprint/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/integration/study_data_blueprint/conftest.py b/tests/integration/study_data_blueprint/conftest.py new file mode 100644 index 0000000000..c0dbe9ffac --- /dev/null +++ b/tests/integration/study_data_blueprint/conftest.py @@ -0,0 +1,57 @@ +import pytest +from fastapi import FastAPI +from starlette.testclient import TestClient + + +@pytest.fixture(name="client") +def fixture_client(app: FastAPI) -> TestClient: + """Get the webservice client used for unit testing""" + return TestClient(app, raise_server_exceptions=False) + + +@pytest.fixture(name="admin_access_token") +def fixture_admin_access_token(client: TestClient) -> str: + """Get the admin user access token used for authentication""" + res = client.post( + "/v1/login", + json={"username": "admin", "password": "admin"}, + ) + assert res.status_code == 200 + credentials = res.json() + return credentials["access_token"] + + +@pytest.fixture(name="user_access_token") +def fixture_user_access_token( + client: TestClient, + admin_access_token: str, +) -> str: + """Get a classic user access token used for authentication""" + res = client.post( + "/v1/users", + headers={"Authorization": f"Bearer {admin_access_token}"}, + json={"name": "George", "password": "mypass"}, + ) + assert res.status_code == 200 + res = client.post( + "/v1/login", + json={"username": "George", "password": "mypass"}, + ) + assert res.status_code == 200 + credentials = res.json() + return credentials["access_token"] + + +@pytest.fixture(name="study_id") +def fixture_study_id( + client: TestClient, + user_access_token: str, +) -> str: + """Get the ID of the study to upgrade""" + res = client.get( + "/v1/studies", + headers={"Authorization": f"Bearer {user_access_token}"}, + ) + assert res.status_code == 200 + study_ids = res.json() + return next(iter(study_ids)) diff --git a/tests/integration/study_data_blueprint/test_hydro_allocation.py b/tests/integration/study_data_blueprint/test_hydro_allocation.py new file mode 100644 index 0000000000..f23fa0e031 --- /dev/null +++ b/tests/integration/study_data_blueprint/test_hydro_allocation.py @@ -0,0 +1,237 @@ +from typing import List + +import pytest +from http import HTTPStatus +from starlette.testclient import TestClient + + +@pytest.mark.unit_test +class TestHydroAllocation: + """ + Test the end points related to hydraulic allocation. + + Those tests use the "examples/studies/STA-mini.zip" Study, + which contains the following areas: ["de", "es", "fr", "it"]. + """ + + def test_get_allocation_form_values( + self, + client: TestClient, + user_access_token: str, + study_id: str, + ): + area_id = "de" + res = client.get( + f"/v1/studies/{study_id}/areas/{area_id}/hydro/allocation", + headers={"Authorization": f"Bearer {user_access_token}"}, + ) + assert res.status_code == HTTPStatus.OK + actual = res.json() + expected = {"allocation": [{"areaId": "de", "coefficient": 1.0}]} + assert actual == expected + + @pytest.mark.parametrize( + "area_id, expected", + [ + pytest.param( + "*", + { + "columns": ["de", "es", "fr", "it"], + "data": [ + [1.0, 0.0, 0.0, 0.0], + [0.0, 1.0, 0.0, 0.0], + [0.0, 0.0, 1.0, 0.0], + [0.0, 0.0, 0.0, 1.0], + ], + "index": ["de", "es", "fr", "it"], + }, + id="all-areas", + ), + pytest.param( + "fr,de", + { + "columns": ["de", "fr"], + "data": [ + [1.0, 0.0], + [0.0, 0.0], + [0.0, 1.0], + [0.0, 0.0], + ], + "index": ["de", "es", "fr", "it"], + }, + id="some-areas", + ), + ], + ) + def test_get_allocation_matrix( + self, + client: TestClient, + user_access_token: str, + study_id: str, + area_id: str, + expected: List[List[float]], + ): + res = client.get( + f"/v1/studies/{study_id}/areas/{area_id}/hydro/allocation", + headers={"Authorization": f"Bearer {user_access_token}"}, + ) + assert res.status_code == HTTPStatus.OK + actual = res.json() + assert actual == expected + + def test_set_allocation_form_values( + self, + client: TestClient, + user_access_token: str, + study_id: str, + ): + area_id = "de" + obj = { + "allocation": [ + {"areaId": "de", "coefficient": 3}, + {"areaId": "es", "coefficient": 1.0}, + ] + } + res = client.put( + f"/v1/studies/{study_id}/areas/{area_id}/hydro/allocation", + headers={"Authorization": f"Bearer {user_access_token}"}, + json=obj, + ) + assert res.status_code == HTTPStatus.NO_CONTENT, res.json() + actual = res.json() + assert not actual + + # check that the values are updated + res = client.get( + f"/v1/studies/{study_id}/raw?path=input/hydro/allocation&depth=3", + headers={"Authorization": f"Bearer {user_access_token}"}, + json=obj, + ) + assert res.status_code == 200, res.json() + actual = res.json() + expected = { + "de": {"[allocation]": {"de": 3.0, "es": 1.0}}, + "es": {"[allocation]": {"es": 1}}, + "fr": {"[allocation]": {"fr": 1}}, + "it": {"[allocation]": {"it": 1}}, + } + assert actual == expected + + @pytest.mark.parametrize( + "obj, detail", + [ + pytest.param( + {"allocation": []}, + [ + { + "loc": ["body", "allocation"], + "msg": "coefficients array is empty", + "type": "value_error", + } + ], + id="at-least-1-area", + ), + pytest.param( + {"allocation": [{"areaId": "de", "coefficient": -5}]}, + [ + { + "loc": ["body", "allocation", 0, "coefficient"], + "msg": "ensure this value is greater than or equal to 0", + "type": "value_error.number.not_ge", + "ctx": {"limit_value": 0}, + } + ], + id="non-positive-number", + ), + pytest.param( + {"allocation": [{"areaId": "de", "coefficient": 0}]}, + [ + { + "loc": ["body", "allocation"], + "msg": "coefficients array has no non-null values", + "type": "value_error", + } + ], + id="nul-allocation-table1", + ), + pytest.param( + { + "allocation": [ + {"areaId": "de", "coefficient": 0}, + {"areaId": "fr", "coefficient": 0}, + ] + }, + [ + { + "loc": ["body", "allocation"], + "msg": "coefficients array has no non-null values", + "type": "value_error", + } + ], + id="nul-allocation-table2", + ), + # fmt: on + ], + ) + def test_update_unprocessable_entity( + self, + client: TestClient, + user_access_token: str, + study_id: str, + obj: dict, + detail: list, + ): + area_id = "de" + res = client.put( + f"/v1/studies/{study_id}/areas/{area_id}/hydro/allocation", + headers={"Authorization": f"Bearer {user_access_token}"}, + json=obj, + ) + actual = res.json() + assert res.status_code == HTTPStatus.UNPROCESSABLE_ENTITY, actual + assert actual == {"detail": detail} + + @pytest.mark.parametrize( + "obj, expected", + [ + pytest.param( + {"allocation": [{"areaId": "MISSING", "coefficient": 3}]}, + { + "description": "1 area is not found: 'MISSING'", + "exception": "AreaNotFound", + }, + id="UNKNOWN-area", + ), + pytest.param( + { + "allocation": [ + {"areaId": "MISSING1", "coefficient": 1}, + {"areaId": "MISSING2", "coefficient": 2}, + {"areaId": "fr", "coefficient": 3}, + ] + }, + { + "description": "2 areas are not found: 'MISSING1', 'MISSING2'", + "exception": "AreaNotFound", + }, + id="UNKNOWN-areas", + ), + ], + ) + def test_update_not_found( + self, + client: TestClient, + user_access_token: str, + study_id: str, + obj: dict, + expected: dict, + ): + area_id = "de" + res = client.put( + f"/v1/studies/{study_id}/areas/{area_id}/hydro/allocation", + headers={"Authorization": f"Bearer {user_access_token}"}, + json=obj, + ) + actual = res.json() + assert res.status_code == HTTPStatus.NOT_FOUND, actual + assert actual == expected From f7ccd56153a67fbe4a1a651497aa115280253ed8 Mon Sep 17 00:00:00 2001 From: Laurent LAPORTE Date: Sat, 1 Apr 2023 08:36:43 +0200 Subject: [PATCH 24/70] feat(api): create a specific end point for allocation matrices --- antarest/core/exceptions.py | 31 ++++++- .../study/business/allocation_management.py | 88 +++++++++++-------- antarest/study/web/study_data_blueprint.py | 70 ++++++++++----- .../test_hydro_allocation.py | 2 +- 4 files changed, 130 insertions(+), 61 deletions(-) diff --git a/antarest/core/exceptions.py b/antarest/core/exceptions.py index 2971752651..7b25ab7686 100644 --- a/antarest/core/exceptions.py +++ b/antarest/core/exceptions.py @@ -215,15 +215,38 @@ def __init__(self, *district_ids: str): class AllocationDataNotFound(HTTPException): """ - Exception raised if no hydraulic allocation - is defined for the given production area (the `.ini` file may be missing). + Exception raised if no hydraulic allocation is defined for the given + production area (the `.ini` file may be missing). """ - def __init__(self, area_id: str): - msg = f"No allocation data found for area '{area_id}'" + def __init__(self, *area_ids: str) -> None: + count = len(area_ids) + ids = ", ".join(f"'{a}'" for a in area_ids) + msg = { + 0: "Hydraulic allocation found", + 1: f"{count} hydraulic allocation is not found: {ids}", + 2: f"{count} hydraulic allocations are not found: {ids}", + }[min(count, 2)] super().__init__(HTTPStatus.NOT_FOUND, msg) +class MultipleAllocationDataFound(HTTPException): + """ + Exception raised if several hydraulic allocation are requested when only + one can be processed (several production area). + """ + + def __init__(self, *area_ids: str) -> None: + count = len(area_ids) + ids = ", ".join(f"'{a}'" for a in area_ids) + msg = { + 0: "No hydraulic allocation found", + 1: f"{count} hydraulic allocation is found: {ids}", + 2: f"{count} hydraulic allocations are found: {ids}", + }[min(count, 2)] + super().__init__(HTTPStatus.CONFLICT, msg) + + class BadEditInstructionException(HTTPException): def __init__(self, message: str) -> None: super().__init__(HTTPStatus.BAD_REQUEST, message) diff --git a/antarest/study/business/allocation_management.py b/antarest/study/business/allocation_management.py index d8fc962477..3684b29343 100644 --- a/antarest/study/business/allocation_management.py +++ b/antarest/study/business/allocation_management.py @@ -1,8 +1,8 @@ -from typing import List, Union +from typing import List import numpy import numpy as np -from antarest.core.exceptions import AllocationDataNotFound, AreaNotFound +from antarest.core.exceptions import AllocationDataNotFound, AreaNotFound, MultipleAllocationDataFound from antarest.study.business.area_management import AreaInfoDTO from antarest.study.business.utils import ( FormFieldsBaseModel, @@ -72,36 +72,27 @@ class AllocationManager: def __init__(self, storage_service: StudyStorageService) -> None: self.storage_service = storage_service - def get_field_values( + def get_allocation_matrix( self, all_areas: List[AreaInfoDTO], study: Study, area_id: str - ) -> Union[AllocationFormFields, AllocationMatrix]: + ) -> AllocationMatrix: """ - Get the hydraulic allocation table of the given production area (or areas). - - Get, for a given production area, the electrical energy consumption - coefficients to consider for the other areas. - Those values are used to fill in the allocation table form. - - If several areas are selected (with a comma-separated list of area IDs), - it returns the hydraulic allocation matrix of the selected areas. - - If the star symbol "*" is used, it returns the hydraulic allocation - matrix of the all the areas. + Get the electrical energy consumption matrix for a given production area, + a selected list of production areas or all areas. Args: + all_areas: The complete list of study areas. study: the current study area_id: A production area ID (e.g.: 'EAST'), - a comma-separated list of production area IDs (e.g.: 'EAST,SOUTH'), or - the star symbol '*' (all areas). - all_areas: The complete list of study areas. + a comma-separated list of production area IDs (e.g.: 'EAST,SOUTH'), + or all areas using the star symbol (e.g.: '*'). Returns: - Hydraulic allocation of the production area: - The list of electrical energy consumption coefficients - to consider for each area. + Returns the data frame matrix, where: - Or, returns the hydraulic allocation matrix of several areas + - `columns`: is the list of selected production areas (given by `area_id`), + - `index`: is the list of all study areas, + - `data`: is the 2D-array matrix of consumption coefficients. Raises: AllocationDataNotFound: exception raised if no hydraulic allocation @@ -113,6 +104,44 @@ def get_field_values( ) if not allocation_data: raise AllocationDataNotFound(area_id) + rows = sorted(area.id for area in all_areas) + columns = sorted(allocation_data) + array = numpy.zeros((len(rows), len(columns)), dtype=numpy.float) + for prod_area, allocation_dict in allocation_data.items(): + allocations = allocation_dict["[allocation]"] + for cons_area, coefficient in allocations.items(): + row_idx = rows.index(cons_area) + col_idx = columns.index(prod_area) + array[row_idx][col_idx] = coefficient + return AllocationMatrix.construct( + index=rows, columns=columns, data=array.tolist() + ) + + def get_field_values( + self, all_areas: List[AreaInfoDTO], study: Study, area_id: str + ) -> AllocationFormFields: + """ + Get the hydraulic allocation table of the given production area (or areas). + + Args: + all_areas: The complete list of study areas. + study: the current study + area_id: A production area ID (e.g.: 'EAST'). + + Returns: + Hydraulic allocation of the production area: + The list of electrical energy consumption coefficients + to consider for each area. + + Raises: + MultipleAllocationDataFound: exception raised if no hydraulic allocation + is defined for the given production area (the `.ini` file may be missing), + or if several production areas are requested. + """ + file_study = self.storage_service.get_storage(study).get_raw(study) + allocation_data = file_study.tree.get( + f"input/hydro/allocation/{area_id}".split("/"), depth=2 + ) if len(allocation_data) == 1: # single-column allocation table areas_ids = {area.id for area in all_areas} @@ -124,20 +153,7 @@ def get_field_values( if area in areas_ids # filter invalid areas ] ) - else: - # allocation matrix - rows = sorted(area.id for area in all_areas) - columns = sorted(allocation_data) - array = numpy.zeros((len(rows), len(columns)), dtype=numpy.float) - for prod_area, allocation_dict in allocation_data.items(): - allocations = allocation_dict["[allocation]"] - for cons_area, coefficient in allocations.items(): - row_idx = rows.index(cons_area) - col_idx = columns.index(prod_area) - array[row_idx][col_idx] = coefficient - return AllocationMatrix.construct( - index=rows, columns=columns, data=array.tolist() - ) + raise MultipleAllocationDataFound(*allocation_data) def set_field_values( self, diff --git a/antarest/study/web/study_data_blueprint.py b/antarest/study/web/study_data_blueprint.py index e0d394b915..f03087b420 100644 --- a/antarest/study/web/study_data_blueprint.py +++ b/antarest/study/web/study_data_blueprint.py @@ -2,6 +2,9 @@ from http import HTTPStatus from typing import Any, Dict, List, Optional, Union, cast +from fastapi import APIRouter, Body, Depends +from fastapi.params import Body + from antarest.core.config import Config from antarest.core.jwt import JWTUser from antarest.core.model import StudyPermissionType @@ -58,8 +61,6 @@ from antarest.study.business.timeseries_config_management import TSFormFields from antarest.study.model import PatchArea, PatchCluster from antarest.study.service import StudyService -from fastapi import APIRouter, Body, Depends -from fastapi.params import Body logger = logging.getLogger(__name__) @@ -1026,43 +1027,72 @@ def remove_constraint_term( study, binding_constraint_id, term_id ) + @bp.get( + path="/studies/{uuid}/areas/{area_id}/hydro/allocation.df", + tags=[APITag.study_data], + summary="Get the hydraulic allocation matrix of a given area", + response_model=AllocationMatrix, + ) + def get_allocation_matrix( + uuid: str, + area_id: str, + current_user: JWTUser = Depends(auth.get_current_user), + ) -> AllocationMatrix: + """ + Get the electrical energy consumption matrix for a given production area, + a selected list of production areas or all areas. + + Parameters: + + - `uuid`: Study UUID (e.g.: '7cdc506c-808e-4bd5-8a6a-493e1f028c3b') + - `area_id`: + A production area ID (e.g.: 'EAST'), + a comma-separated list of production area IDs (e.g.: 'EAST,SOUTH'), + or all areas using the star symbol (e.g.: '*'). + + Returns the data frame matrix, where: + + - `columns`: is the list of selected production areas (given by `area_id`), + - `index`: is the list of all study areas, + - `data`: is the 2D-array matrix of consumption coefficients. + """ + params = RequestParameters(user=current_user) + study = study_service.check_study_access( + uuid, StudyPermissionType.READ, params + ) + all_areas = cast( + List[AreaInfoDTO], # because `ui=False` + study_service.get_all_areas( + uuid, area_type=AreaType.AREA, ui=False, params=params + ), + ) + return study_service.allocation_manager.get_allocation_matrix( + all_areas, study, area_id + ) + @bp.get( path="/studies/{uuid}/areas/{area_id}/hydro/allocation", tags=[APITag.study_data], - summary=( - "Get the hydraulic allocation of a given area," - " or the allocation matrix of several areas" - ), - response_model=Union[AllocationFormFields, AllocationMatrix], + summary="Get the hydraulic allocation of a given area", + response_model=AllocationFormFields, ) def get_allocation_form_values( uuid: str, area_id: str, current_user: JWTUser = Depends(auth.get_current_user), - ) -> Union[AllocationFormFields, AllocationMatrix]: + ) -> AllocationFormFields: """ Get, for a given production area, the electrical energy consumption coefficients to consider for the other areas. - If several areas are selected (with a comma-separated list of area IDs), - it returns the hydraulic allocation matrix of the selected areas. - - If the star symbol "*" is used, it returns the hydraulic allocation - matrix of the all the areas. - Parameters: - `uuid`: Study UUID (e.g.: '7cdc506c-808e-4bd5-8a6a-493e1f028c3b') - - `area_id`: - A production area ID (e.g.: 'EAST'), - a comma-separated list of production area IDs (e.g.: 'EAST,SOUTH'), or - the star symbol '*' (all areas). + - `area_id`: A production area ID (e.g.: 'EAST'). Returns form fields used for the allocation table: The list of electrical energy consumption coefficients to consider for each area. - - Or, returns the hydraulic allocation matrix of several areas """ params = RequestParameters(user=current_user) study = study_service.check_study_access( diff --git a/tests/integration/study_data_blueprint/test_hydro_allocation.py b/tests/integration/study_data_blueprint/test_hydro_allocation.py index f23fa0e031..871a1bf65a 100644 --- a/tests/integration/study_data_blueprint/test_hydro_allocation.py +++ b/tests/integration/study_data_blueprint/test_hydro_allocation.py @@ -72,7 +72,7 @@ def test_get_allocation_matrix( expected: List[List[float]], ): res = client.get( - f"/v1/studies/{study_id}/areas/{area_id}/hydro/allocation", + f"/v1/studies/{study_id}/areas/{area_id}/hydro/allocation.df", headers={"Authorization": f"Bearer {user_access_token}"}, ) assert res.status_code == HTTPStatus.OK From cd7715dc18424761b8151780443ca92999b3ffda Mon Sep 17 00:00:00 2001 From: Laurent LAPORTE Date: Mon, 3 Apr 2023 11:52:01 +0200 Subject: [PATCH 25/70] feat(api): add validation functions (and unit tests) --- .../study/business/allocation_management.py | 119 ++++- .../test_hydro_allocation.py | 136 +----- .../study/business/test_allocation_manager.py | 434 ++++++++++++++++++ 3 files changed, 549 insertions(+), 140 deletions(-) create mode 100644 tests/study/business/test_allocation_manager.py diff --git a/antarest/study/business/allocation_management.py b/antarest/study/business/allocation_management.py index 3684b29343..dde7af9324 100644 --- a/antarest/study/business/allocation_management.py +++ b/antarest/study/business/allocation_management.py @@ -1,8 +1,11 @@ -from typing import List +from typing import List, Dict -import numpy import numpy as np -from antarest.core.exceptions import AllocationDataNotFound, AreaNotFound, MultipleAllocationDataFound +from antarest.core.exceptions import ( + AllocationDataNotFound, + AreaNotFound, + MultipleAllocationDataFound, +) from antarest.study.business.area_management import AreaInfoDTO from antarest.study.business.utils import ( FormFieldsBaseModel, @@ -13,12 +16,16 @@ from antarest.study.storage.variantstudy.model.command.update_config import ( UpdateConfig, ) -from pydantic import NonNegativeFloat, validator, BaseModel +from pydantic import NonNegativeFloat, validator, BaseModel, conlist class AllocationField(FormFieldsBaseModel): """Consumption coefficient of a given area.""" + class Config: + allow_population_by_field_name = True + extra = "forbid" + area_id: str coefficient: NonNegativeFloat @@ -29,13 +36,18 @@ class AllocationFormFields(FormFieldsBaseModel): electrical energy consumption coefficients to consider for each area. """ + class Config: + extra = "forbid" + allocation: List[AllocationField] # noinspection PyMethodParameters @validator("allocation") - def validate_hydro_allocation_table(cls, fields: List[AllocationField]): + def validate_hydro_allocation_column( + cls, fields: List[AllocationField] + ) -> List[AllocationField]: """ - Validate hydraulic allocation table. + Validate hydraulic allocation column. Args: fields: A list of validated allocation fields containing @@ -50,17 +62,73 @@ def validate_hydro_allocation_table(cls, fields: List[AllocationField]): """ array = np.array([f.coefficient for f in fields]) if array.size == 0: - raise ValueError("coefficients array is empty") + raise ValueError("ensure coefficients colum is non-empty") elif np.any(array == 0): - raise ValueError("coefficients array has no non-null values") + raise ValueError("ensure coefficients column is not nul") else: return fields class AllocationMatrix(BaseModel): - index: List[str] - columns: List[str] - data: List[List[NonNegativeFloat]] + """ + Hydraulic allocation matrix. + + Data frame matrix, where: + + - `columns`: is the list of selected production areas (given by `area_id`), + - `index`: is the list of all study areas, + - `data`: is the 2D-array matrix of consumption coefficients. + """ + + class Config: + allow_population_by_field_name = True + extra = "forbid" + + index: conlist(str, min_items=1) # type: ignore + columns: conlist(str, min_items=1) # type: ignore + data: List[List[float]] # NonNegativeFloat not necessary + + # noinspection PyMethodParameters + @validator("data") + def validate_hydro_allocation_table( + cls, data: List[List[float]], values: Dict[str, List[str]] + ) -> List[List[float]]: + """ + Validate hydraulic allocation table. + + Args: + data: Hydraulic allocation matrix containing + the coefficients to be validated. + values: a dict containing the name-to-value mapping + of the `index` and `columns` fields (if they are validated). + values: dictionary that maps names to corresponding fields. + If the `index` and `columns` fields are validated, + they are included in this dictionary. + + Raises: + ValueError: + If the coefficients columns are empty or has no non-null values. + + Returns: + The allocation fields. + """ + array = np.array(data) + rows = len(values["index"]) if "index" in values else 0 + cols = len(values["columns"]) if "columns" in values else 0 + if array.size == 0: + raise ValueError("ensure coefficients matrix is a non-empty array") + elif array.shape != (rows, cols): + raise ValueError( + f"ensure coefficients matrix is an array of shape {rows}×{cols}" + ) + elif np.any(array < 0): + raise ValueError( + "ensure coefficients matrix has positive or nul coefficients" + ) + elif np.any(array.sum(axis=0) == 0): + raise ValueError("ensure coefficients matrix has non-nul columns") + else: + return data class AllocationManager: @@ -99,15 +167,19 @@ def get_allocation_matrix( is defined for the given production area (the `.ini` file may be missing). """ file_study = self.storage_service.get_storage(study).get_raw(study) - allocation_data = file_study.tree.get( + allocation_cfg = file_study.tree.get( f"input/hydro/allocation/{area_id}".split("/"), depth=2 ) - if not allocation_data: + if not allocation_cfg: raise AllocationDataNotFound(area_id) - rows = sorted(area.id for area in all_areas) - columns = sorted(allocation_data) - array = numpy.zeros((len(rows), len(columns)), dtype=numpy.float) - for prod_area, allocation_dict in allocation_data.items(): + elif len(allocation_cfg) == 1: + allocation_cfg = {area_id: allocation_cfg} + # Preserve the order of `all_areas` + rows = [area.id for area in all_areas] + # IMPORTANT: keep the same order for columns + columns = [area.id for area in all_areas if area.id in allocation_cfg] + array = np.zeros((len(rows), len(columns)), dtype=np.float64) + for prod_area, allocation_dict in allocation_cfg.items(): allocations = allocation_dict["[allocation]"] for cons_area, coefficient in allocations.items(): row_idx = rows.index(cons_area) @@ -144,11 +216,14 @@ def get_field_values( ) if len(allocation_data) == 1: # single-column allocation table - areas_ids = {area.id for area in all_areas} allocations = allocation_data["[allocation]"] + areas_ids = {area.id for area in all_areas} return AllocationFormFields.construct( allocation=[ - AllocationField.construct(areaId=area, coefficient=value) + # IMPORTANT: use snake_case args because validation is not called, + # so field names aliasing is not done. + # NOTE: the conversion to JSON uses camelCase names (aliases). + AllocationField.construct(area_id=area, coefficient=value) for area, value in allocations.items() if area in areas_ids # filter invalid areas ] @@ -167,7 +242,7 @@ def set_field_values( Args: study: the current study - area_id: Production area ID from which we want to retrieve the allocation table. + area_id: Production area ID from which we want to update the allocation table. all_areas: The complete list of study areas. data: Hydraulic allocation of the production area: The list of electrical energy consumption coefficients @@ -181,7 +256,9 @@ def set_field_values( areas_ids = {area.id for area in all_areas} if invalid_ids := allocation_ids - areas_ids: # sorting is mandatory for unit tests - raise AreaNotFound(*sorted(invalid_ids)) + raise AreaNotFound(*invalid_ids) + elif area_id not in areas_ids: + raise AreaNotFound(area_id) command_context = ( self.storage_service.variant_study_service.command_factory.command_context diff --git a/tests/integration/study_data_blueprint/test_hydro_allocation.py b/tests/integration/study_data_blueprint/test_hydro_allocation.py index 871a1bf65a..2c8bb798b1 100644 --- a/tests/integration/study_data_blueprint/test_hydro_allocation.py +++ b/tests/integration/study_data_blueprint/test_hydro_allocation.py @@ -20,6 +20,7 @@ def test_get_allocation_form_values( user_access_token: str, study_id: str, ): + """Check `get_allocation_form_values` end point""" area_id = "de" res = client.get( f"/v1/studies/{study_id}/areas/{area_id}/hydro/allocation", @@ -61,6 +62,20 @@ def test_get_allocation_form_values( }, id="some-areas", ), + pytest.param( + "fr", + { + "columns": ["fr"], + "data": [ + [0.0], + [0.0], + [1.0], + [0.0], + ], + "index": ["de", "es", "fr", "it"], + }, + id="one-area", + ), ], ) def test_get_allocation_matrix( @@ -71,6 +86,7 @@ def test_get_allocation_matrix( area_id: str, expected: List[List[float]], ): + """Check `get_allocation_matrix` end point""" res = client.get( f"/v1/studies/{study_id}/areas/{area_id}/hydro/allocation.df", headers={"Authorization": f"Bearer {user_access_token}"}, @@ -85,6 +101,7 @@ def test_set_allocation_form_values( user_access_token: str, study_id: str, ): + """Check `set_allocation_form_values` end point""" area_id = "de" obj = { "allocation": [ @@ -116,122 +133,3 @@ def test_set_allocation_form_values( "it": {"[allocation]": {"it": 1}}, } assert actual == expected - - @pytest.mark.parametrize( - "obj, detail", - [ - pytest.param( - {"allocation": []}, - [ - { - "loc": ["body", "allocation"], - "msg": "coefficients array is empty", - "type": "value_error", - } - ], - id="at-least-1-area", - ), - pytest.param( - {"allocation": [{"areaId": "de", "coefficient": -5}]}, - [ - { - "loc": ["body", "allocation", 0, "coefficient"], - "msg": "ensure this value is greater than or equal to 0", - "type": "value_error.number.not_ge", - "ctx": {"limit_value": 0}, - } - ], - id="non-positive-number", - ), - pytest.param( - {"allocation": [{"areaId": "de", "coefficient": 0}]}, - [ - { - "loc": ["body", "allocation"], - "msg": "coefficients array has no non-null values", - "type": "value_error", - } - ], - id="nul-allocation-table1", - ), - pytest.param( - { - "allocation": [ - {"areaId": "de", "coefficient": 0}, - {"areaId": "fr", "coefficient": 0}, - ] - }, - [ - { - "loc": ["body", "allocation"], - "msg": "coefficients array has no non-null values", - "type": "value_error", - } - ], - id="nul-allocation-table2", - ), - # fmt: on - ], - ) - def test_update_unprocessable_entity( - self, - client: TestClient, - user_access_token: str, - study_id: str, - obj: dict, - detail: list, - ): - area_id = "de" - res = client.put( - f"/v1/studies/{study_id}/areas/{area_id}/hydro/allocation", - headers={"Authorization": f"Bearer {user_access_token}"}, - json=obj, - ) - actual = res.json() - assert res.status_code == HTTPStatus.UNPROCESSABLE_ENTITY, actual - assert actual == {"detail": detail} - - @pytest.mark.parametrize( - "obj, expected", - [ - pytest.param( - {"allocation": [{"areaId": "MISSING", "coefficient": 3}]}, - { - "description": "1 area is not found: 'MISSING'", - "exception": "AreaNotFound", - }, - id="UNKNOWN-area", - ), - pytest.param( - { - "allocation": [ - {"areaId": "MISSING1", "coefficient": 1}, - {"areaId": "MISSING2", "coefficient": 2}, - {"areaId": "fr", "coefficient": 3}, - ] - }, - { - "description": "2 areas are not found: 'MISSING1', 'MISSING2'", - "exception": "AreaNotFound", - }, - id="UNKNOWN-areas", - ), - ], - ) - def test_update_not_found( - self, - client: TestClient, - user_access_token: str, - study_id: str, - obj: dict, - expected: dict, - ): - area_id = "de" - res = client.put( - f"/v1/studies/{study_id}/areas/{area_id}/hydro/allocation", - headers={"Authorization": f"Bearer {user_access_token}"}, - json=obj, - ) - actual = res.json() - assert res.status_code == HTTPStatus.NOT_FOUND, actual - assert actual == expected diff --git a/tests/study/business/test_allocation_manager.py b/tests/study/business/test_allocation_manager.py new file mode 100644 index 0000000000..59cd3645e8 --- /dev/null +++ b/tests/study/business/test_allocation_manager.py @@ -0,0 +1,434 @@ +import contextlib +import datetime +import uuid +from unittest.mock import Mock, patch, ANY, call + +import pytest +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker + +from antarest.core.exceptions import ( + AllocationDataNotFound, + AreaNotFound, + MultipleAllocationDataFound, +) +from antarest.core.model import PublicMode +from antarest.dbmodel import Base +from antarest.login.model import User, Group +from antarest.study.business.allocation_management import ( + AllocationField, + AllocationFormFields, + AllocationMatrix, + AllocationManager, +) +from antarest.study.business.area_management import AreaInfoDTO, AreaType +from antarest.study.model import Study, StudyContentStatus, RawStudy +from antarest.study.storage.rawstudy.model.filesystem.factory import FileStudy +from antarest.study.storage.rawstudy.model.filesystem.root.filestudytree import ( + FileStudyTree, +) +from antarest.study.storage.rawstudy.raw_study_service import RawStudyService +from antarest.study.storage.storage_service import StudyStorageService +from antarest.study.storage.variantstudy.command_factory import CommandFactory +from antarest.study.storage.variantstudy.model.command.common import ( + CommandName, +) +from antarest.study.storage.variantstudy.model.command.update_config import ( + UpdateConfig, +) +from antarest.study.storage.variantstudy.model.command_context import ( + CommandContext, +) +from antarest.study.storage.variantstudy.variant_study_service import ( + VariantStudyService, +) + + +class TestAllocationField: + def test_init__nominal_case(self): + field = AllocationField(area_id="NORTH", coefficient=1) + assert field.area_id == "NORTH" + assert field.coefficient == 1 + + def test_init__camel_case_args(self): + field = AllocationField(areaId="NORTH", coefficient=1) + assert field.area_id == "NORTH" + assert field.coefficient == 1 + + def test_validation__ge_0(self): + """Ensure `coefficient` is greater than or equal to 0""" + with pytest.raises(ValueError, match="greater than or equal to 0"): + AllocationField(areaId="NORTH", coefficient=-1) + + +class TestAllocationFormFields: + def test_init__nominal_case(self): + fields = AllocationFormFields( + allocation=[ + {"area_id": "NORTH", "coefficient": 0.75}, + {"area_id": "SOUTH", "coefficient": 0.25}, + ] + ) + assert fields.allocation == [ + AllocationField(area_id="NORTH", coefficient=0.75), + AllocationField(area_id="SOUTH", coefficient=0.25), + ] + + def test_validation__coefficients_column_non_empty(self): + """Check that the coefficients colum is non-empty""" + with pytest.raises(ValueError, match="non-empty|not empty"): + AllocationFormFields(allocation=[]) + + def test_validation__coefficients_column_not_nul(self): + """Check that the coefficients column is not nul""" + with pytest.raises(ValueError, match="non-nul|not nul"): + AllocationFormFields( + allocation=[ + {"area_id": "NORTH", "coefficient": 0}, + ] + ) + + +class TestAllocationMatrix: + def test_init__nominal_case(self): + field = AllocationMatrix( + index=["fr", "de"], + columns=["fr"], + data=[ + [1.0], + [0.2], + ], + ) + assert field.index == ["fr", "de"] + assert field.columns == ["fr"] + assert field.data == [ + [1.0], + [0.2], + ] + + def test_validation__coefficients_matrix_non_empty_array(self): + """Check that the coefficients matrix is a non-empty array""" + # fmt: off + with pytest.raises(ValueError, match="(?:not empty|non[ -]empty) array"): + AllocationMatrix( + index=[], + columns=[], + data=[], + ) + # fmt: off + + def test_validation__coefficients_matrix_array_shape(self): + """Check that the coefficients matrix is an array of shape 2×1""" + with pytest.raises(ValueError, match=r"array of shape \d+×\d+"): + AllocationMatrix( + index=["fr", "de"], + columns=["fr"], + data=[[1, 2], [3, 4]], + ) + + def test_validation__coefficients_matrix_positive_or_nul(self): + """Check that all coefficients matrix has positive or nul coefficients""" + # fmt: off + with pytest.raises(ValueError, match="positive or nul|greater than or equal to 0"): + AllocationMatrix( + index=["fr", "de"], + columns=["fr", "de"], + data=[ + [1.0, -1], + [0.2, 0], + ], + ) + # fmt: on + + def test_validation__coefficients_array_has_no_non_null_values(self): + """Check that the coefficients matrix have non-nul columns""" + with pytest.raises(ValueError, match="(?:not nul|non-nul) columns"): + AllocationMatrix( + index=["fr", "de"], + columns=["fr", "de"], + data=[ + [0, 8], + [0, 0], + ], + ) + + +@pytest.fixture(scope="function", name="db_engine") +def db_engine_fixture(): + engine = create_engine("sqlite:///:memory:") + Base.metadata.create_all(engine) + yield engine + engine.dispose() + + +@pytest.fixture(scope="function", name="db_session") +def db_session_fixture(db_engine): + make_session = sessionmaker(bind=db_engine) + with contextlib.closing(make_session()) as session: + yield session + + +# noinspection SpellCheckingInspection +EXECUTE_OR_ADD_COMMANDS = ( + "antarest.study.business.allocation_management.execute_or_add_commands" +) + + +class TestAllocationManager: + @pytest.fixture(name="study_storage_service") + def study_storage_service(self) -> StudyStorageService: + """Return a mocked StudyStorageService.""" + return Mock( + spec=StudyStorageService, + variant_study_service=Mock( + spec=VariantStudyService, + command_factory=Mock( + spec=CommandFactory, + command_context=Mock(spec=CommandContext), + ), + ), + get_storage=Mock( + return_value=Mock( + spec=RawStudyService, get_raw=Mock(spec=FileStudy) + ) + ), + ) + + # noinspection PyArgumentList + @pytest.fixture(name="study_uuid") + def study_uuid_fixture(self, db_session) -> str: + user = User(id=0, name="admin") + group = Group(id="my-group", name="group") + raw_study = RawStudy( + id=str(uuid.uuid4()), + name="Dummy", + version="850", + author="John Smith", + created_at=datetime.datetime.now(datetime.timezone.utc), + updated_at=datetime.datetime.now(datetime.timezone.utc), + public_mode=PublicMode.FULL, + owner=user, + groups=[group], + workspace="default", + path="/path/to/study", + content_status=StudyContentStatus.WARNING, + ) + db_session.add(raw_study) + db_session.commit() + return raw_study.id + + def test_get_allocation_matrix__nominal_case( + self, db_session, study_storage_service, study_uuid + ): + # The study must be fetched from the database + study: RawStudy = db_session.query(Study).get(study_uuid) + + # Prepare the mocks + allocation_cfg = { + "n": {"[allocation]": {"n": 1}}, + "e": {"[allocation]": {"e": 3, "s": 1}}, + "s": {"[allocation]": {"s": 0.1, "n": 0.2, "w": 0.6}}, + "w": {"[allocation]": {"w": 1}}, + } + storage = study_storage_service.get_storage(study) + file_study = storage.get_raw(study) + file_study.tree = Mock( + spec=FileStudyTree, + get=Mock(return_value=allocation_cfg), + ) + + # Given the following arguments + all_areas = [ + AreaInfoDTO(id="n", name="North", type=AreaType.AREA), + AreaInfoDTO(id="e", name="East", type=AreaType.AREA), + AreaInfoDTO(id="s", name="South", type=AreaType.AREA), + AreaInfoDTO(id="w", name="West", type=AreaType.AREA), + ] + area_id = "*" # all areas + manager = AllocationManager(study_storage_service) + + # run + matrix = manager.get_allocation_matrix( + all_areas=all_areas, study=study, area_id=area_id + ) + + # Check + assert matrix == AllocationMatrix( + index=["n", "e", "s", "w"], + columns=["n", "e", "s", "w"], + data=[ + [1.0, 0.0, 0.2, 0.0], + [0.0, 3.0, 0.0, 0.0], + [0.0, 1.0, 0.1, 0.0], + [0.0, 0.0, 0.6, 1.0], + ], + ) + + def test_get_allocation_matrix__allocation_data_not_found( + self, db_session, study_storage_service, study_uuid + ): + # The study must be fetched from the database + study: RawStudy = db_session.query(Study).get(study_uuid) + + # Prepare the mocks with an empty allocation config + allocation_cfg = {} + storage = study_storage_service.get_storage(study) + file_study = storage.get_raw(study) + file_study.tree = Mock( + spec=FileStudyTree, + get=Mock(return_value=allocation_cfg), + ) + + # Given the following arguments + all_areas = [ + AreaInfoDTO(id="n", name="North", type=AreaType.AREA), + AreaInfoDTO(id="e", name="East", type=AreaType.AREA), + AreaInfoDTO(id="s", name="South", type=AreaType.AREA), + AreaInfoDTO(id="w", name="West", type=AreaType.AREA), + ] + area_id = "*" # all areas + manager = AllocationManager(study_storage_service) + with pytest.raises(AllocationDataNotFound): + manager.get_allocation_matrix( + all_areas=all_areas, study=study, area_id=area_id + ) + + def test_get_field_values__nominal_case( + self, db_session, study_storage_service, study_uuid + ): + # The study must be fetched from the database + study: RawStudy = db_session.query(Study).get(study_uuid) + + # Prepare the mocks + allocation_data = {"[allocation]": {"s": 0.1, "n": 0.2, "w": 0.6}} + storage = study_storage_service.get_storage(study) + file_study = storage.get_raw(study) + file_study.tree = Mock( + spec=FileStudyTree, + get=Mock(return_value=allocation_data), + ) + + # Given the following arguments + all_areas = [ + AreaInfoDTO(id="n", name="North", type=AreaType.AREA), + AreaInfoDTO(id="e", name="East", type=AreaType.AREA), + AreaInfoDTO(id="s", name="South", type=AreaType.AREA), + AreaInfoDTO(id="w", name="West", type=AreaType.AREA), + ] + area_id = "s" # South + manager = AllocationManager(study_storage_service) + fields = manager.get_field_values( + all_areas=all_areas, study=study, area_id=area_id + ) + assert fields == AllocationFormFields( + allocation=[ + AllocationField(areaId="s", coefficient=0.1), + AllocationField(areaId="n", coefficient=0.2), + AllocationField(areaId="w", coefficient=0.6), + ] + ) + + def test_get_field_values__multiple_allocation_data_found( + self, db_session, study_storage_service, study_uuid + ): + # The study must be fetched from the database + study: RawStudy = db_session.query(Study).get(study_uuid) + + # Prepare the mocks: North + South + allocation_cfg = { + "n": {"[allocation]": {"n": 1}}, + "s": {"[allocation]": {"s": 0.1, "n": 0.2, "w": 0.6}}, + } + storage = study_storage_service.get_storage(study) + file_study = storage.get_raw(study) + file_study.tree = Mock( + spec=FileStudyTree, + get=Mock(return_value=allocation_cfg), + ) + + # Given the following arguments + all_areas = [ + AreaInfoDTO(id="n", name="North", type=AreaType.AREA), + AreaInfoDTO(id="e", name="East", type=AreaType.AREA), + AreaInfoDTO(id="s", name="South", type=AreaType.AREA), + AreaInfoDTO(id="w", name="West", type=AreaType.AREA), + ] + area_id = "n,s" # North + South + manager = AllocationManager(study_storage_service) + with pytest.raises(MultipleAllocationDataFound): + manager.get_field_values( + all_areas=all_areas, study=study, area_id=area_id + ) + + def test_set_field_values__nominal_case( + self, db_session, study_storage_service, study_uuid + ): + # The study must be fetched from the database + study: RawStudy = db_session.query(Study).get(study_uuid) + + # Given the following arguments + all_areas = [ + AreaInfoDTO(id="n", name="North", type=AreaType.AREA), + AreaInfoDTO(id="e", name="East", type=AreaType.AREA), + AreaInfoDTO(id="s", name="South", type=AreaType.AREA), + AreaInfoDTO(id="w", name="West", type=AreaType.AREA), + ] + area_id = "s" # South + manager = AllocationManager(study_storage_service) + with patch(EXECUTE_OR_ADD_COMMANDS) as exe: + manager.set_field_values( + all_areas=all_areas, + study=study, + area_id=area_id, + data=AllocationFormFields( + allocation=[ + AllocationField(area_id="s", coefficient=2), + AllocationField(area_id="e", coefficient=3), + AllocationField(area_id="n", coefficient=4), + ] + ), + ) + + # check update + assert exe.call_count == 1 + mock_call = exe.mock_calls[0] + # signature: execute_or_add_commands(study, file_study, commands, storage_service) + actual_study, _, actual_cmds, _ = mock_call.args + assert actual_study == study + assert len(actual_cmds) == 1 + cmd: UpdateConfig = actual_cmds[0] + assert cmd.command_name == CommandName.UPDATE_CONFIG + assert cmd.target == f"input/hydro/allocation/{area_id}/[allocation]" + assert cmd.data == {"s": 2.0, "e": 3.0, "n": 4.0} + + def test_set_field_values__area_not_found( + self, db_session, study_storage_service, study_uuid + ): + # The study must be fetched from the database + study: RawStudy = db_session.query(Study).get(study_uuid) + + # Given the following arguments + all_areas = [ + AreaInfoDTO(id="n", name="North", type=AreaType.AREA), + AreaInfoDTO(id="e", name="East", type=AreaType.AREA), + AreaInfoDTO(id="s", name="South", type=AreaType.AREA), + AreaInfoDTO(id="w", name="West", type=AreaType.AREA), + ] + area_id = "n" # South + manager = AllocationManager(study_storage_service) + + with patch(EXECUTE_OR_ADD_COMMANDS) as exe: + with pytest.raises(AreaNotFound): + manager.set_field_values( + all_areas=all_areas, + study=study, + area_id=area_id, + data=AllocationFormFields( + allocation=[ + AllocationField( + area_id="UNKNOWN", coefficient=3.14 + ), + ] + ), + ) + exe.assert_not_called() From fc392dc68c4458b2e36e669f5b540c72344d7890 Mon Sep 17 00:00:00 2001 From: Laurent LAPORTE Date: Mon, 3 Apr 2023 19:21:40 +0200 Subject: [PATCH 26/70] test(api): check the error message when an exception is raised --- antarest/core/exceptions.py | 4 ++-- tests/study/business/test_allocation_manager.py | 12 ++++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/antarest/core/exceptions.py b/antarest/core/exceptions.py index 7b25ab7686..223db3c4db 100644 --- a/antarest/core/exceptions.py +++ b/antarest/core/exceptions.py @@ -1,7 +1,7 @@ from http import HTTPStatus -from typing import Iterable, Optional +from typing import Optional -from fastapi import HTTPException +from fastapi.exceptions import HTTPException class ShouldNotHappenException(Exception): diff --git a/tests/study/business/test_allocation_manager.py b/tests/study/business/test_allocation_manager.py index 59cd3645e8..aafb787312 100644 --- a/tests/study/business/test_allocation_manager.py +++ b/tests/study/business/test_allocation_manager.py @@ -1,7 +1,7 @@ import contextlib import datetime import uuid -from unittest.mock import Mock, patch, ANY, call +from unittest.mock import Mock, patch import pytest from sqlalchemy import create_engine @@ -288,10 +288,11 @@ def test_get_allocation_matrix__allocation_data_not_found( ] area_id = "*" # all areas manager = AllocationManager(study_storage_service) - with pytest.raises(AllocationDataNotFound): + with pytest.raises(AllocationDataNotFound) as ctx: manager.get_allocation_matrix( all_areas=all_areas, study=study, area_id=area_id ) + assert "'*'" in ctx.value.detail def test_get_field_values__nominal_case( self, db_session, study_storage_service, study_uuid @@ -355,10 +356,12 @@ def test_get_field_values__multiple_allocation_data_found( ] area_id = "n,s" # North + South manager = AllocationManager(study_storage_service) - with pytest.raises(MultipleAllocationDataFound): + with pytest.raises(MultipleAllocationDataFound) as ctx: manager.get_field_values( all_areas=all_areas, study=study, area_id=area_id ) + assert "'n'" in ctx.value.detail + assert "'s'" in ctx.value.detail def test_set_field_values__nominal_case( self, db_session, study_storage_service, study_uuid @@ -418,7 +421,7 @@ def test_set_field_values__area_not_found( manager = AllocationManager(study_storage_service) with patch(EXECUTE_OR_ADD_COMMANDS) as exe: - with pytest.raises(AreaNotFound): + with pytest.raises(AreaNotFound) as ctx: manager.set_field_values( all_areas=all_areas, study=study, @@ -431,4 +434,5 @@ def test_set_field_values__area_not_found( ] ), ) + assert "'UNKNOWN'" in ctx.value.detail exe.assert_not_called() From f6fa3ce9107f7c82fb1a590a13f59b912cb9aa99 Mon Sep 17 00:00:00 2001 From: Laurent LAPORTE Date: Tue, 4 Apr 2023 12:26:28 +0200 Subject: [PATCH 27/70] feat(api): update the allocation matrix when an area is added or removed --- .../study/business/allocation_management.py | 2 + .../variantstudy/model/command/remove_area.py | 247 ++++++------------ .../test_hydro_allocation.py | 97 ++++++- 3 files changed, 177 insertions(+), 169 deletions(-) diff --git a/antarest/study/business/allocation_management.py b/antarest/study/business/allocation_management.py index dde7af9324..decfb4ea3c 100644 --- a/antarest/study/business/allocation_management.py +++ b/antarest/study/business/allocation_management.py @@ -173,6 +173,8 @@ def get_allocation_matrix( if not allocation_cfg: raise AllocationDataNotFound(area_id) elif len(allocation_cfg) == 1: + # IMPORTANT: when there is only one element left the function returns + # the allocation of the element in place of the dictionary by zone allocation_cfg = {area_id: allocation_cfg} # Preserve the order of `all_areas` rows = [area.id for area in all_areas] diff --git a/antarest/study/storage/variantstudy/model/command/remove_area.py b/antarest/study/storage/variantstudy/model/command/remove_area.py index eda840d148..d02c17c639 100644 --- a/antarest/study/storage/variantstudy/model/command/remove_area.py +++ b/antarest/study/storage/variantstudy/model/command/remove_area.py @@ -1,5 +1,6 @@ +import contextlib import logging -from typing import Any, List, Tuple, Dict +from typing import Any, Dict, List, Tuple from antarest.core.model import JSON from antarest.study.storage.rawstudy.model.filesystem.config.model import ( @@ -13,12 +14,12 @@ remove_area_cluster_from_binding_constraints, ) from antarest.study.storage.variantstudy.model.command.common import ( - CommandOutput, CommandName, + CommandOutput, ) from antarest.study.storage.variantstudy.model.command.icommand import ( - ICommand, MATCH_SIGNATURE_SEPARATOR, + ICommand, ) from antarest.study.storage.variantstudy.model.model import CommandDTO @@ -36,11 +37,12 @@ def __init__(self, **data: Any) -> None: def _remove_area_from_links_in_config( self, study_data_config: FileStudyTreeConfig ) -> None: - link_to_remove = [] - for area_name, area in study_data_config.areas.items(): - for link in area.links.keys(): - if link == self.id: - link_to_remove.append((area_name, link)) + link_to_remove = [ + (area_name, link) + for area_name, area in study_data_config.areas.items() + for link in area.links + if link == self.id + ] for area_name, link in link_to_remove: del study_data_config.areas[area_name].links[link] @@ -49,11 +51,9 @@ def _remove_area_from_sets_in_config( ) -> None: for id, set in study_data_config.sets.items(): if set.areas and self.id in set.areas: - try: + with contextlib.suppress(ValueError): set.areas.remove(self.id) study_data_config.sets[id] = set - except ValueError: - pass def _apply_config( self, study_data_config: FileStudyTreeConfig @@ -69,98 +69,87 @@ def _apply_config( return ( CommandOutput(status=True, message=f"Area '{self.id}' deleted"), - dict(), + {}, ) def _remove_area_from_links(self, study_data: FileStudy) -> None: for area_name, area in study_data.config.areas.items(): - for link in area.links.keys(): + for link in area.links: if link == self.id: study_data.tree.delete( ["input", "links", area_name, "properties", self.id] ) try: + # fmt: off if study_data.config.version < 820: - study_data.tree.delete( - ["input", "links", area_name, self.id] - ) + study_data.tree.delete(["input", "links", area_name, self.id]) else: - study_data.tree.delete( - [ - "input", - "links", - area_name, - f"{self.id}_parameters", - ] - ) - study_data.tree.delete( - [ - "input", - "links", - area_name, - "capacities", - f"{self.id}_indirect", - ] - ) - study_data.tree.delete( - [ - "input", - "links", - area_name, - "capacities", - f"{self.id}_direct", - ] - ) + study_data.tree.delete(["input", "links", area_name, f"{self.id}_parameters"]) + study_data.tree.delete(["input", "links", area_name, "capacities", f"{self.id}_indirect"]) + study_data.tree.delete(["input", "links", area_name, "capacities", f"{self.id}_direct"]) + # fmt: on except ChildNotFoundError as e: logger.warning( - f"Failed to clean link data when deleting area {self.id} in study {study_data.config.study_id}", + f"Failed to clean link data when deleting area {self.id}" + f" in study {study_data.config.study_id}", exc_info=e, ) def _remove_area_from_binding_constraints( self, study_data: FileStudy ) -> None: - binding_constraints = study_data.tree.get( - ["input", "bindingconstraints", "bindingconstraints"] - ) - - id_to_remove = set() - - for id, bc in binding_constraints.items(): - for key in bc.keys(): - if self.id in key: - id_to_remove.add(id) + # fmt: off + binding_constraints = study_data.tree.get(["input", "bindingconstraints", "bindingconstraints"]) + + id_to_remove = { + bc_id + for bc_id, bc in binding_constraints.items() + for key in bc + if self.id in key + } - for id in id_to_remove: + for bc_id in id_to_remove: study_data.tree.delete( - [ - "input", - "bindingconstraints", - binding_constraints[id]["id"], - ] + ["input", "bindingconstraints", binding_constraints[bc_id]["id"]] ) - del binding_constraints[id] + del binding_constraints[bc_id] + + study_data.tree.save(binding_constraints, ["input", "bindingconstraints", "bindingconstraints"]) + # fmt: on - study_data.tree.save( - binding_constraints, - ["input", "bindingconstraints", "bindingconstraints"], + def _remove_area_from_hydro_allocation( + self, study_data: FileStudy + ) -> None: + """ + Delete the column for the hydraulic production area + and updates the rows for the other areas. + + Args: + study_data: file study + """ + study_data.tree.delete(["input", "hydro", "allocation", self.id]) + allocation_cfg = study_data.tree.get( + ["input", "hydro", "allocation", "*"] ) + if len(allocation_cfg) == 1: + # IMPORTANT: when there is only one element left the function returns + # the allocation of the element in place of the dictionary by zone + allocation_cfg = {self.id: allocation_cfg} + allocation_cfg.pop(self.id, None) # ensure allocation is removed + for prod_area, allocation_dict in allocation_cfg.items(): + for name, allocations in allocation_dict.items(): + allocations.pop(self.id, None) + study_data.tree.save(allocation_cfg, ["input", "hydro", "allocation"]) def _remove_area_from_districts(self, study_data: FileStudy) -> None: districts = study_data.tree.get(["input", "areas", "sets"]) - for id, district in districts.items(): + for district in districts.values(): if district.get("+", None): - try: + with contextlib.suppress(ValueError): district["+"].remove(self.id) - except ValueError: - pass elif district.get("-", None): - try: + with contextlib.suppress(ValueError): district["-"].remove(self.id) - except ValueError: - pass - - districts[id] = district study_data.tree.save(districts, ["input", "areas", "sets"]) @@ -170,113 +159,45 @@ def _remove_area_from_cluster(self, study_data: FileStudy) -> None: def _remove_area_from_time_series(self, study_data: FileStudy) -> None: study_data.tree.delete(["input", "thermal", "series", self.id]) + # noinspection SpellCheckingInspection def _apply(self, study_data: FileStudy) -> CommandOutput: + # fmt: off study_data.tree.delete(["input", "areas", self.id]) - - study_data.tree.delete(["input", "hydro", "allocation", self.id]) - study_data.tree.delete( - ["input", "hydro", "common", "capacity", f"maxpower_{self.id}"] - ) - study_data.tree.delete( - ["input", "hydro", "common", "capacity", f"reservoir_{self.id}"] - ) + study_data.tree.delete(["input", "hydro", "common", "capacity", f"maxpower_{self.id}"]) + study_data.tree.delete(["input", "hydro", "common", "capacity", f"reservoir_{self.id}"]) study_data.tree.delete(["input", "hydro", "prepro", self.id]) study_data.tree.delete(["input", "hydro", "series", self.id]) - study_data.tree.delete( - ["input", "hydro", "hydro", "inter-daily-breakdown", self.id] - ) - study_data.tree.delete( - ["input", "hydro", "hydro", "intra-daily-modulation", self.id] - ) - study_data.tree.delete( - ["input", "hydro", "hydro", "inter-monthly-breakdown", self.id] - ) + study_data.tree.delete(["input", "hydro", "hydro", "inter-daily-breakdown", self.id]) + study_data.tree.delete(["input", "hydro", "hydro", "intra-daily-modulation", self.id]) + study_data.tree.delete(["input", "hydro", "hydro", "inter-monthly-breakdown", self.id]) study_data.tree.delete(["input", "load", "prepro", self.id]) study_data.tree.delete(["input", "load", "series", f"load_{self.id}"]) study_data.tree.delete(["input", "misc-gen", f"miscgen-{self.id}"]) study_data.tree.delete(["input", "reserves", self.id]) study_data.tree.delete(["input", "solar", "prepro", self.id]) - study_data.tree.delete( - ["input", "solar", "series", f"solar_{self.id}"] - ) + study_data.tree.delete(["input", "solar", "series", f"solar_{self.id}"]) study_data.tree.delete(["input", "thermal", "clusters", self.id]) - study_data.tree.delete( - ["input", "thermal", "areas", "unserverdenergycost", self.id] - ) - study_data.tree.delete( - ["input", "thermal", "areas", "spilledenergycost", self.id] - ) + study_data.tree.delete(["input", "thermal", "areas", "unserverdenergycost", self.id]) + study_data.tree.delete(["input", "thermal", "areas", "spilledenergycost", self.id]) study_data.tree.delete(["input", "wind", "prepro", self.id]) study_data.tree.delete(["input", "wind", "series", f"wind_{self.id}"]) study_data.tree.delete(["input", "links", self.id]) + # fmt: on if study_data.config.version > 650: - study_data.tree.delete( - [ - "input", - "hydro", - "hydro", - "initialize reservoir date", - self.id, - ] - ) - study_data.tree.delete( - [ - "input", - "hydro", - "hydro", - "leeway low", - self.id, - ] - ) - study_data.tree.delete( - [ - "input", - "hydro", - "hydro", - "leeway up", - self.id, - ] - ) - study_data.tree.delete( - [ - "input", - "hydro", - "hydro", - "pumping efficiency", - self.id, - ] - ) - study_data.tree.delete( - [ - "input", - "hydro", - "common", - "capacity", - f"creditmodulations_{self.id}", - ] - ) - study_data.tree.delete( - [ - "input", - "hydro", - "common", - "capacity", - f"inflowPattern_{self.id}", - ] - ) - study_data.tree.delete( - [ - "input", - "hydro", - "common", - "capacity", - f"waterValues_{self.id}", - ] - ) + # fmt: off + study_data.tree.delete(["input", "hydro", "hydro", "initialize reservoir date", self.id]) + study_data.tree.delete(["input", "hydro", "hydro", "leeway low", self.id]) + study_data.tree.delete(["input", "hydro", "hydro", "leeway up", self.id]) + study_data.tree.delete(["input", "hydro", "hydro", "pumping efficiency", self.id]) + study_data.tree.delete(["input", "hydro", "common", "capacity", f"creditmodulations_{self.id}"]) + study_data.tree.delete(["input", "hydro", "common", "capacity", f"inflowPattern_{self.id}"]) + study_data.tree.delete(["input", "hydro", "common", "capacity", f"waterValues_{self.id}"]) + # fmt: on self._remove_area_from_links(study_data) self._remove_area_from_binding_constraints(study_data) + self._remove_area_from_hydro_allocation(study_data) self._remove_area_from_districts(study_data) self._remove_area_from_cluster(study_data) self._remove_area_from_time_series(study_data) @@ -310,9 +231,7 @@ def match_signature(self) -> str: ) def match(self, other: ICommand, equal: bool = False) -> bool: - if not isinstance(other, RemoveArea): - return False - return self.id == other.id + return isinstance(other, RemoveArea) and self.id == other.id def _create_diff(self, other: "ICommand") -> List["ICommand"]: return [] diff --git a/tests/integration/study_data_blueprint/test_hydro_allocation.py b/tests/integration/study_data_blueprint/test_hydro_allocation.py index 2c8bb798b1..ac7a8285b5 100644 --- a/tests/integration/study_data_blueprint/test_hydro_allocation.py +++ b/tests/integration/study_data_blueprint/test_hydro_allocation.py @@ -1,7 +1,8 @@ +from http import HTTPStatus from typing import List import pytest -from http import HTTPStatus +from antarest.study.business.area_management import AreaInfoDTO from starlette.testclient import TestClient @@ -26,7 +27,7 @@ def test_get_allocation_form_values( f"/v1/studies/{study_id}/areas/{area_id}/hydro/allocation", headers={"Authorization": f"Bearer {user_access_token}"}, ) - assert res.status_code == HTTPStatus.OK + assert res.status_code == HTTPStatus.OK, res.json() actual = res.json() expected = {"allocation": [{"areaId": "de", "coefficient": 1.0}]} assert actual == expected @@ -91,7 +92,7 @@ def test_get_allocation_matrix( f"/v1/studies/{study_id}/areas/{area_id}/hydro/allocation.df", headers={"Authorization": f"Bearer {user_access_token}"}, ) - assert res.status_code == HTTPStatus.OK + assert res.status_code == HTTPStatus.OK, res.json() actual = res.json() assert actual == expected @@ -122,9 +123,8 @@ def test_set_allocation_form_values( res = client.get( f"/v1/studies/{study_id}/raw?path=input/hydro/allocation&depth=3", headers={"Authorization": f"Bearer {user_access_token}"}, - json=obj, ) - assert res.status_code == 200, res.json() + assert res.status_code == HTTPStatus.OK, res.json() actual = res.json() expected = { "de": {"[allocation]": {"de": 3.0, "es": 1.0}}, @@ -133,3 +133,90 @@ def test_set_allocation_form_values( "it": {"[allocation]": {"it": 1}}, } assert actual == expected + + def test_create_area( + self, client: TestClient, user_access_token: str, study_id: str + ): + """ + Given a study, when an area is created, the hydraulic allocation + column for this area must be updated with the following values: + - the coefficient == 1 for this area, + - the coefficient == 0 for the other areas. + Other columns must not be changed. + """ + area_info = AreaInfoDTO(id="north", name="NORTH", type="AREA") + res = client.post( + f"/v1/studies/{study_id}/areas", + headers={"Authorization": f"Bearer {user_access_token}"}, + data=area_info.json(), + ) + assert res.status_code == HTTPStatus.OK, res.json() + + res = client.get( + f"/v1/studies/{study_id}/areas/*/hydro/allocation.df", + headers={"Authorization": f"Bearer {user_access_token}"}, + ) + assert res.status_code == HTTPStatus.OK + actual = res.json() + expected = { + "columns": ["de", "es", "fr", "it", "north"], + "data": [ + [1.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 1.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 1.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 1.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 1.0], + ], + "index": ["de", "es", "fr", "it", "north"], + } + assert actual == expected + + def test_delete_area( + self, client: TestClient, user_access_token: str, study_id: str + ): + """ + Given a study, when an area is deleted, the hydraulic allocation + column for this area must be removed. + Other columns must be updated to reflect the area deletion. + """ + # First change the coefficients to avoid zero values (which are defaults). + obj = { + "de": {"[allocation]": {"de": 10, "es": 20, "fr": 30, "it": 40}}, + "es": {"[allocation]": {"de": 11, "es": 21, "fr": 31, "it": 41}}, + "fr": {"[allocation]": {"de": 12, "es": 22, "fr": 32, "it": 42}}, + "it": {"[allocation]": {"de": 13, "es": 23, "fr": 33, "it": 43}}, + } + for prod_area, allocation_cfg in obj.items(): + res = client.post( + f"/v1/studies/{study_id}/raw?path=input/hydro/allocation/{prod_area}", + headers={"Authorization": f"Bearer {user_access_token}"}, + json=allocation_cfg, + ) + assert res.status_code == HTTPStatus.NO_CONTENT, res.json() + + # Then we remove the "fr" zone. + # The deletion should update the allocation matrix of all other zones. + res = client.delete( + f"/v1/studies/{study_id}/areas/fr", + headers={"Authorization": f"Bearer {user_access_token}"}, + ) + assert res.status_code == HTTPStatus.OK, res.json() + + # Check that the "fr" column is removed from the hydraulic allocation matrix. + # The row corresponding to "fr" must also be deleted. + res = client.get( + f"/v1/studies/{study_id}/areas/*/hydro/allocation.df", + headers={"Authorization": f"Bearer {user_access_token}"}, + ) + assert res.status_code == HTTPStatus.OK, res.json() + actual = res.json() + expected = { + "columns": ["de", "es", "it"], + "data": [ + [10.0, 11.0, 13.0], + [20.0, 21.0, 23.0], + [40.0, 41.0, 43.0], + ], + "index": ["de", "es", "it"], + } + assert actual == expected From b94ca7c2a2aef02bbc1260392074ebadf2057820 Mon Sep 17 00:00:00 2001 From: hatim dinia Date: Tue, 11 Apr 2023 14:27:33 +0200 Subject: [PATCH 28/70] feat(api,ui-hydro): update form styles + api improvements --- antarest/core/exceptions.py | 40 +- antarest/main.py | 18 + .../study/business/allocation_management.py | 320 +++++++-------- antarest/study/web/study_data_blueprint.py | 64 ++- .../test_hydro_allocation.py | 18 +- .../study/business/test_allocation_manager.py | 370 ++++++++++-------- .../Hydro/Allocation/AllocationField.tsx | 92 ++--- .../Hydro/Allocation/AllocationSelect.tsx | 32 +- .../Areas/Hydro/Allocation/Fields.tsx | 53 +-- .../Areas/Hydro/Allocation/index.tsx | 45 +-- .../Areas/Hydro/Allocation/utils.ts | 11 +- 11 files changed, 539 insertions(+), 524 deletions(-) diff --git a/antarest/core/exceptions.py b/antarest/core/exceptions.py index 223db3c4db..324c71ccf4 100644 --- a/antarest/core/exceptions.py +++ b/antarest/core/exceptions.py @@ -177,6 +177,12 @@ class StudyOutputNotFoundError(Exception): pass +class AllocationDataNotFound(HTTPException): + def __init__(self, area_id: str) -> None: + msg = f"Allocation data for area: {area_id} is not found" + super().__init__(HTTPStatus.NOT_FOUND, msg) + + class AreaNotFound(HTTPException): def __init__(self, *area_ids: str) -> None: count = len(area_ids) @@ -213,40 +219,6 @@ def __init__(self, *district_ids: str): super().__init__(HTTPStatus.CONFLICT, msg) -class AllocationDataNotFound(HTTPException): - """ - Exception raised if no hydraulic allocation is defined for the given - production area (the `.ini` file may be missing). - """ - - def __init__(self, *area_ids: str) -> None: - count = len(area_ids) - ids = ", ".join(f"'{a}'" for a in area_ids) - msg = { - 0: "Hydraulic allocation found", - 1: f"{count} hydraulic allocation is not found: {ids}", - 2: f"{count} hydraulic allocations are not found: {ids}", - }[min(count, 2)] - super().__init__(HTTPStatus.NOT_FOUND, msg) - - -class MultipleAllocationDataFound(HTTPException): - """ - Exception raised if several hydraulic allocation are requested when only - one can be processed (several production area). - """ - - def __init__(self, *area_ids: str) -> None: - count = len(area_ids) - ids = ", ".join(f"'{a}'" for a in area_ids) - msg = { - 0: "No hydraulic allocation found", - 1: f"{count} hydraulic allocation is found: {ids}", - 2: f"{count} hydraulic allocations are found: {ids}", - }[min(count, 2)] - super().__init__(HTTPStatus.CONFLICT, msg) - - class BadEditInstructionException(HTTPException): def __init__(self, message: str) -> None: super().__init__(HTTPStatus.BAD_REQUEST, message) diff --git a/antarest/main.py b/antarest/main.py index 217022f162..34b5bcbfc4 100644 --- a/antarest/main.py +++ b/antarest/main.py @@ -7,6 +7,8 @@ import sqlalchemy.ext.baked # type: ignore import uvicorn # type: ignore from fastapi import FastAPI, HTTPException +from fastapi.encoders import jsonable_encoder +from fastapi.exceptions import RequestValidationError from fastapi_jwt_auth import AuthJWT # type: ignore from ratelimit import RateLimitMiddleware # type: ignore from ratelimit.backends.redis import RedisBackend # type: ignore @@ -221,6 +223,22 @@ def handle_http_exception(request: Request, exc: HTTPException) -> Any: status_code=exc.status_code, ) + @application.exception_handler(RequestValidationError) + async def handle_validation_exception( + request: Request, exc: RequestValidationError + ) -> Any: + error_message = exc.errors()[0]["msg"] + return JSONResponse( + status_code=422, + content=jsonable_encoder( + { + "description": error_message, + "exception": "RequestValidationError", + "body": exc.body, + } + ), + ) + @application.exception_handler(Exception) def handle_all_exception(request: Request, exc: Exception) -> Any: """Return JSON instead of HTML for HTTP errors.""" diff --git a/antarest/study/business/allocation_management.py b/antarest/study/business/allocation_management.py index decfb4ea3c..ebe2f2bd44 100644 --- a/antarest/study/business/allocation_management.py +++ b/antarest/study/business/allocation_management.py @@ -1,10 +1,13 @@ from typing import List, Dict +import numpy import numpy as np +from pydantic import root_validator +from pydantic import validator, conlist + from antarest.core.exceptions import ( AllocationDataNotFound, AreaNotFound, - MultipleAllocationDataFound, ) from antarest.study.business.area_management import AreaInfoDTO from antarest.study.business.utils import ( @@ -16,73 +19,56 @@ from antarest.study.storage.variantstudy.model.command.update_config import ( UpdateConfig, ) -from pydantic import NonNegativeFloat, validator, BaseModel, conlist class AllocationField(FormFieldsBaseModel): - """Consumption coefficient of a given area.""" - - class Config: - allow_population_by_field_name = True - extra = "forbid" + """Model for consumption coefficients of a given area.""" area_id: str - coefficient: NonNegativeFloat + coefficient: float class AllocationFormFields(FormFieldsBaseModel): - """ - Hydraulic allocation of a production area: - electrical energy consumption coefficients to consider for each area. - """ - - class Config: - extra = "forbid" + """Model for a list of consumption coefficients for each area.""" allocation: List[AllocationField] - # noinspection PyMethodParameters - @validator("allocation") - def validate_hydro_allocation_column( - cls, fields: List[AllocationField] - ) -> List[AllocationField]: - """ - Validate hydraulic allocation column. + @root_validator + def check_allocation( + cls, values: Dict[str, List[AllocationField]] + ) -> Dict[str, List[AllocationField]]: + allocation = values.get("allocation", []) - Args: - fields: A list of validated allocation fields containing - the coefficients to be validated. + if not allocation: + raise ValueError("allocation must not be empty") - Raises: - ValueError: - If the coefficients array is empty or has no non-null values. + if len(allocation) != len(set(a.area_id for a in allocation)): + raise ValueError("allocation must not contain duplicate area IDs") - Returns: - The allocation fields. - """ - array = np.array([f.coefficient for f in fields]) - if array.size == 0: - raise ValueError("ensure coefficients colum is non-empty") - elif np.any(array == 0): - raise ValueError("ensure coefficients column is not nul") - else: - return fields + for a in allocation: + if a.coefficient < 0: + raise ValueError( + "allocation must not contain negative coefficients" + ) + if numpy.isnan(a.coefficient): + raise ValueError( + "allocation must not contain NaN coefficients" + ) -class AllocationMatrix(BaseModel): - """ - Hydraulic allocation matrix. + if sum(a.coefficient for a in allocation) <= 0: + raise ValueError("sum of allocation coefficients must be positive") - Data frame matrix, where: + return values - - `columns`: is the list of selected production areas (given by `area_id`), - - `index`: is the list of all study areas, - - `data`: is the 2D-array matrix of consumption coefficients. - """ - class Config: - allow_population_by_field_name = True - extra = "forbid" +class AllocationMatrix(FormFieldsBaseModel): + """ + Hydraulic allocation matrix. + index: List of all study areas + columns: List of selected production areas + data: 2D-array matrix of consumption coefficients + """ index: conlist(str, min_items=1) # type: ignore columns: conlist(str, min_items=1) # type: ignore @@ -90,188 +76,208 @@ class Config: # noinspection PyMethodParameters @validator("data") - def validate_hydro_allocation_table( + def validate_hydro_allocation_matrix( cls, data: List[List[float]], values: Dict[str, List[str]] ) -> List[List[float]]: """ - Validate hydraulic allocation table. - + Validate the hydraulic allocation matrix. Args: - data: Hydraulic allocation matrix containing - the coefficients to be validated. - values: a dict containing the name-to-value mapping - of the `index` and `columns` fields (if they are validated). - values: dictionary that maps names to corresponding fields. - If the `index` and `columns` fields are validated, - they are included in this dictionary. - + data: the allocation matrix to validate. + values: the allocation matrix fields. Raises: ValueError: If the coefficients columns are empty or has no non-null values. - Returns: The allocation fields. """ + array = np.array(data) - rows = len(values["index"]) if "index" in values else 0 - cols = len(values["columns"]) if "columns" in values else 0 + rows = len(values.get("index", [])) + cols = len(values.get("columns", [])) + if array.size == 0: - raise ValueError("ensure coefficients matrix is a non-empty array") - elif array.shape != (rows, cols): + raise ValueError("allocation matrix must not be empty") + if array.shape != (rows, cols): + raise ValueError(f"allocation matrix must have square shape") + if np.any(array < 0): + raise ValueError( + "allocation matrix must not contain negative coefficients" + ) + if np.any(array.sum(axis=0) == 0): raise ValueError( - f"ensure coefficients matrix is an array of shape {rows}×{cols}" + "allocation matrix must not contain empty columns" ) - elif np.any(array < 0): + if np.any(np.isnan(array)): raise ValueError( - "ensure coefficients matrix has positive or nul coefficients" + "allocation matrix must not contain NaN coefficients" + ) + if np.all(array == 0): + raise ValueError( + "allocation matrix must not contain only null values" ) - elif np.any(array.sum(axis=0) == 0): - raise ValueError("ensure coefficients matrix has non-nul columns") - else: - return data + + return data class AllocationManager: """ - Provides functionality for getting and updating hydraulic allocation - coefficients for a given production area. + Manage hydraulic allocation coefficients. """ def __init__(self, storage_service: StudyStorageService) -> None: self.storage_service = storage_service - def get_allocation_matrix( - self, all_areas: List[AreaInfoDTO], study: Study, area_id: str - ) -> AllocationMatrix: + def get_allocation_data( + self, study: Study, area_id: str + ) -> Dict[str, List[AllocationField]]: """ - Get the electrical energy consumption matrix for a given production area, - a selected list of production areas or all areas. + Get hydraulic allocation data. Args: - all_areas: The complete list of study areas. - study: the current study - area_id: - A production area ID (e.g.: 'EAST'), - a comma-separated list of production area IDs (e.g.: 'EAST,SOUTH'), - or all areas using the star symbol (e.g.: '*'). + study: study to get the allocation data from + area_id: area to get the allocation data from Returns: - Returns the data frame matrix, where: - - - `columns`: is the list of selected production areas (given by `area_id`), - - `index`: is the list of all study areas, - - `data`: is the 2D-array matrix of consumption coefficients. + The allocation data. Raises: - AllocationDataNotFound: exception raised if no hydraulic allocation - is defined for the given production area (the `.ini` file may be missing). + AllocationDataNotFound: if the allocation data is not found. """ + file_study = self.storage_service.get_storage(study).get_raw(study) - allocation_cfg = file_study.tree.get( + allocation_data = file_study.tree.get( f"input/hydro/allocation/{area_id}".split("/"), depth=2 ) - if not allocation_cfg: + + if not allocation_data: raise AllocationDataNotFound(area_id) - elif len(allocation_cfg) == 1: - # IMPORTANT: when there is only one element left the function returns - # the allocation of the element in place of the dictionary by zone - allocation_cfg = {area_id: allocation_cfg} - # Preserve the order of `all_areas` - rows = [area.id for area in all_areas] - # IMPORTANT: keep the same order for columns - columns = [area.id for area in all_areas if area.id in allocation_cfg] - array = np.zeros((len(rows), len(columns)), dtype=np.float64) - for prod_area, allocation_dict in allocation_cfg.items(): - allocations = allocation_dict["[allocation]"] - for cons_area, coefficient in allocations.items(): - row_idx = rows.index(cons_area) - col_idx = columns.index(prod_area) - array[row_idx][col_idx] = coefficient - return AllocationMatrix.construct( - index=rows, columns=columns, data=array.tolist() - ) - def get_field_values( + return allocation_data.get("[allocation]", {}) + + def get_allocation_form_fields( self, all_areas: List[AreaInfoDTO], study: Study, area_id: str ) -> AllocationFormFields: """ - Get the hydraulic allocation table of the given production area (or areas). + Get hydraulic allocation coefficients. Args: - all_areas: The complete list of study areas. - study: the current study - area_id: A production area ID (e.g.: 'EAST'). + all_areas: list of all areas in the study + study: study to get the allocation coefficients from + area_id: area to get the allocation coefficients from Returns: - Hydraulic allocation of the production area: - The list of electrical energy consumption coefficients - to consider for each area. + The allocation coefficients. Raises: - MultipleAllocationDataFound: exception raised if no hydraulic allocation - is defined for the given production area (the `.ini` file may be missing), - or if several production areas are requested. + AllocationDataNotFound: if the allocation data is not found. """ - file_study = self.storage_service.get_storage(study).get_raw(study) - allocation_data = file_study.tree.get( - f"input/hydro/allocation/{area_id}".split("/"), depth=2 + + areas_ids = {area.id for area in all_areas} + allocations = self.get_allocation_data(study, area_id) + + return AllocationFormFields.construct( + allocation=[ + AllocationField.construct(area_id=area, coefficient=value) + for area, value in allocations.items() + if area in areas_ids # filter invalid areas + ] ) - if len(allocation_data) == 1: - # single-column allocation table - allocations = allocation_data["[allocation]"] - areas_ids = {area.id for area in all_areas} - return AllocationFormFields.construct( - allocation=[ - # IMPORTANT: use snake_case args because validation is not called, - # so field names aliasing is not done. - # NOTE: the conversion to JSON uses camelCase names (aliases). - AllocationField.construct(area_id=area, coefficient=value) - for area, value in allocations.items() - if area in areas_ids # filter invalid areas - ] - ) - raise MultipleAllocationDataFound(*allocation_data) - def set_field_values( + def set_allocation_form_fields( self, all_areas: List[AreaInfoDTO], study: Study, area_id: str, data: AllocationFormFields, - ) -> None: + ) -> AllocationFormFields: """ - Update the hydraulic allocation table of the given production area. + Set hydraulic allocation coefficients. Args: - study: the current study - area_id: Production area ID from which we want to update the allocation table. - all_areas: The complete list of study areas. - data: Hydraulic allocation of the production area: - The list of electrical energy consumption coefficients - to consider for each area. + all_areas: list of all areas in the study + study: study to set the allocation coefficients to + area_id: area to set the allocation coefficients to + data: allocation coefficients to set Raises: - AreaNotFound: exception raised if at least one area - of the hydraulic allocation table is not an existing area. + AreaNotFound: if the area is not found. """ + allocation_ids = {field.area_id for field in data.allocation} areas_ids = {area.id for area in all_areas} + if invalid_ids := allocation_ids - areas_ids: - # sorting is mandatory for unit tests - raise AreaNotFound(*invalid_ids) - elif area_id not in areas_ids: - raise AreaNotFound(area_id) + # sort for deterministic error message and testing + raise AreaNotFound(*sorted(invalid_ids)) + + filtered_allocations = [ + f + for f in data.allocation + if f.coefficient > 0 and f.area_id in areas_ids + ] command_context = ( self.storage_service.variant_study_service.command_factory.command_context ) command = UpdateConfig( target=f"input/hydro/allocation/{area_id}/[allocation]", - data={f.area_id: f.coefficient for f in data.allocation}, + data={f.area_id: f.coefficient for f in filtered_allocations}, command_context=command_context, ) file_study = self.storage_service.get_storage(study).get_raw(study) + execute_or_add_commands( study, file_study, [command], self.storage_service ) + + updated_allocations = self.get_allocation_data(study, area_id) + + return AllocationFormFields.construct( + allocation=[ + AllocationField.construct(area_id=area, coefficient=value) + for area, value in updated_allocations.items() + ] + ) + + def get_allocation_matrix( + self, all_areas: List[AreaInfoDTO], study: Study, area_id: str + ) -> AllocationMatrix: + """ + Get the hydraulic allocation matrix. + + Args: + all_areas: list of all areas in the study + study: study to get the allocation matrix from + area_id: area to get the allocation matrix from + + Returns: + The allocation matrix. + + Raises: + AllocationDataNotFound: if the allocation data is not found. + """ + file_study = self.storage_service.get_storage(study).get_raw(study) + allocation_cfg = file_study.tree.get( + f"input/hydro/allocation/{area_id}".split("/"), depth=2 + ) + if not allocation_cfg: + raise AllocationDataNotFound(area_id) + elif len(allocation_cfg) == 1: + # IMPORTANT: when there is only one element left the function returns + # the allocation of the element in place of the dictionary by zone + allocation_cfg = {area_id: allocation_cfg} + # Preserve the order of `all_areas` + rows = [area.id for area in all_areas] + # IMPORTANT: keep the same order for columns + columns = [area.id for area in all_areas if area.id in allocation_cfg] + array = np.zeros((len(rows), len(columns)), dtype=np.float64) + for prod_area, allocation_dict in allocation_cfg.items(): + allocations = allocation_dict["[allocation]"] + for cons_area, coefficient in allocations.items(): + row_idx = rows.index(cons_area) + col_idx = columns.index(prod_area) + array[row_idx][col_idx] = coefficient + return AllocationMatrix.construct( + index=rows, columns=columns, data=array.tolist() + ) diff --git a/antarest/study/web/study_data_blueprint.py b/antarest/study/web/study_data_blueprint.py index f03087b420..538f2c7f07 100644 --- a/antarest/study/web/study_data_blueprint.py +++ b/antarest/study/web/study_data_blueprint.py @@ -1028,7 +1028,7 @@ def remove_constraint_term( ) @bp.get( - path="/studies/{uuid}/areas/{area_id}/hydro/allocation.df", + path="/studies/{uuid}/areas/{area_id}/hydro/allocation/matrix", tags=[APITag.study_data], summary="Get the hydraulic allocation matrix of a given area", response_model=AllocationMatrix, @@ -1039,22 +1039,16 @@ def get_allocation_matrix( current_user: JWTUser = Depends(auth.get_current_user), ) -> AllocationMatrix: """ - Get the electrical energy consumption matrix for a given production area, - a selected list of production areas or all areas. + Get the hydraulic allocation matrix of a given area. Parameters: - - - `uuid`: Study UUID (e.g.: '7cdc506c-808e-4bd5-8a6a-493e1f028c3b') - - `area_id`: - A production area ID (e.g.: 'EAST'), - a comma-separated list of production area IDs (e.g.: 'EAST,SOUTH'), - or all areas using the star symbol (e.g.: '*'). + - `uuid`: the study UUID, + - `area_id`: the area ID. Returns the data frame matrix, where: - - - `columns`: is the list of selected production areas (given by `area_id`), - - `index`: is the list of all study areas, - - `data`: is the 2D-array matrix of consumption coefficients. + - the rows are the areas, + - the columns are the hydraulic structures, + - the values are the allocation factors. """ params = RequestParameters(user=current_user) study = study_service.check_study_access( @@ -1071,28 +1065,24 @@ def get_allocation_matrix( ) @bp.get( - path="/studies/{uuid}/areas/{area_id}/hydro/allocation", + path="/studies/{uuid}/areas/{area_id}/hydro/allocation/form", tags=[APITag.study_data], - summary="Get the hydraulic allocation of a given area", + summary="Get the form fields used for the allocation form", response_model=AllocationFormFields, ) - def get_allocation_form_values( + def get_allocation_form_fields( uuid: str, area_id: str, current_user: JWTUser = Depends(auth.get_current_user), ) -> AllocationFormFields: """ - Get, for a given production area, the electrical energy consumption - coefficients to consider for the other areas. + Get the form fields used for the allocation form. Parameters: + - `uuid`: the study UUID, + - `area_id`: the area ID. - - `uuid`: Study UUID (e.g.: '7cdc506c-808e-4bd5-8a6a-493e1f028c3b') - - `area_id`: A production area ID (e.g.: 'EAST'). - - Returns form fields used for the allocation table: - The list of electrical energy consumption coefficients - to consider for each area. + Returns the allocation form fields. """ params = RequestParameters(user=current_user) study = study_service.check_study_access( @@ -1104,17 +1094,18 @@ def get_allocation_form_values( uuid, area_type=AreaType.AREA, ui=False, params=params ), ) - return study_service.allocation_manager.get_field_values( + return study_service.allocation_manager.get_allocation_form_fields( all_areas, study, area_id ) @bp.put( - path="/studies/{uuid}/areas/{area_id}/hydro/allocation", + path="/studies/{uuid}/areas/{area_id}/hydro/allocation/form", tags=[APITag.study_data], - summary="Update the hydraulic allocation of a given area", - status_code=HTTPStatus.NO_CONTENT, + summary="Update the form fields used for the allocation form", + status_code=HTTPStatus.OK, + response_model=AllocationFormFields, ) - def set_allocation_form_values( + def set_allocation_form_fields( uuid: str, area_id: str, data: AllocationFormFields = Body( @@ -1127,18 +1118,15 @@ def set_allocation_form_values( ), ), current_user: JWTUser = Depends(auth.get_current_user), - ) -> None: + ) -> AllocationFormFields: """ - Update, for a given production area, the electrical energy consumption - coefficients to consider for the other areas. + Update the hydraulic allocation of a given area. Parameters: + - `uuid`: the study UUID, + - `area_id`: the area ID. - - `uuid`: Study UUID (e.g.: '7cdc506c-808e-4bd5-8a6a-493e1f028c3b') - - `area_id`: Production area ID (e.g.: 'EAST') - - `data`: Hydraulic allocation of the production area: - The list of electrical energy consumption coefficients - to consider for each area. + Returns the updated allocation form fields. """ params = RequestParameters(user=current_user) study = study_service.check_study_access( @@ -1150,7 +1138,7 @@ def set_allocation_form_values( uuid, area_type=AreaType.AREA, ui=False, params=params ), ) - study_service.allocation_manager.set_field_values( + return study_service.allocation_manager.set_allocation_form_fields( all_areas, study, area_id, data ) diff --git a/tests/integration/study_data_blueprint/test_hydro_allocation.py b/tests/integration/study_data_blueprint/test_hydro_allocation.py index ac7a8285b5..e9694e5f42 100644 --- a/tests/integration/study_data_blueprint/test_hydro_allocation.py +++ b/tests/integration/study_data_blueprint/test_hydro_allocation.py @@ -24,7 +24,7 @@ def test_get_allocation_form_values( """Check `get_allocation_form_values` end point""" area_id = "de" res = client.get( - f"/v1/studies/{study_id}/areas/{area_id}/hydro/allocation", + f"/v1/studies/{study_id}/areas/{area_id}/hydro/allocation/form", headers={"Authorization": f"Bearer {user_access_token}"}, ) assert res.status_code == HTTPStatus.OK, res.json() @@ -89,7 +89,7 @@ def test_get_allocation_matrix( ): """Check `get_allocation_matrix` end point""" res = client.get( - f"/v1/studies/{study_id}/areas/{area_id}/hydro/allocation.df", + f"/v1/studies/{study_id}/areas/{area_id}/hydro/allocation/matrix", headers={"Authorization": f"Bearer {user_access_token}"}, ) assert res.status_code == HTTPStatus.OK, res.json() @@ -104,20 +104,20 @@ def test_set_allocation_form_values( ): """Check `set_allocation_form_values` end point""" area_id = "de" - obj = { + expected = { "allocation": [ {"areaId": "de", "coefficient": 3}, {"areaId": "es", "coefficient": 1.0}, ] } res = client.put( - f"/v1/studies/{study_id}/areas/{area_id}/hydro/allocation", + f"/v1/studies/{study_id}/areas/{area_id}/hydro/allocation/form", headers={"Authorization": f"Bearer {user_access_token}"}, - json=obj, + json=expected, ) - assert res.status_code == HTTPStatus.NO_CONTENT, res.json() + assert res.status_code == HTTPStatus.OK, res.json() actual = res.json() - assert not actual + assert actual == expected # check that the values are updated res = client.get( @@ -153,7 +153,7 @@ def test_create_area( assert res.status_code == HTTPStatus.OK, res.json() res = client.get( - f"/v1/studies/{study_id}/areas/*/hydro/allocation.df", + f"/v1/studies/{study_id}/areas/*/hydro/allocation/matrix", headers={"Authorization": f"Bearer {user_access_token}"}, ) assert res.status_code == HTTPStatus.OK @@ -205,7 +205,7 @@ def test_delete_area( # Check that the "fr" column is removed from the hydraulic allocation matrix. # The row corresponding to "fr" must also be deleted. res = client.get( - f"/v1/studies/{study_id}/areas/*/hydro/allocation.df", + f"/v1/studies/{study_id}/areas/*/hydro/allocation/matrix", headers={"Authorization": f"Bearer {user_access_token}"}, ) assert res.status_code == HTTPStatus.OK, res.json() diff --git a/tests/study/business/test_allocation_manager.py b/tests/study/business/test_allocation_manager.py index aafb787312..939d8320b7 100644 --- a/tests/study/business/test_allocation_manager.py +++ b/tests/study/business/test_allocation_manager.py @@ -7,11 +7,7 @@ from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker -from antarest.core.exceptions import ( - AllocationDataNotFound, - AreaNotFound, - MultipleAllocationDataFound, -) +from antarest.core.exceptions import AllocationDataNotFound, AreaNotFound from antarest.core.model import PublicMode from antarest.dbmodel import Base from antarest.login.model import User, Group @@ -43,113 +39,142 @@ VariantStudyService, ) +from antarest.study.business.allocation_management import ( + AllocationManager, + AllocationField, + AllocationFormFields, +) -class TestAllocationField: - def test_init__nominal_case(self): - field = AllocationField(area_id="NORTH", coefficient=1) - assert field.area_id == "NORTH" - assert field.coefficient == 1 - def test_init__camel_case_args(self): +class TestAllocationField: + def test_base(self): field = AllocationField(areaId="NORTH", coefficient=1) assert field.area_id == "NORTH" assert field.coefficient == 1 - def test_validation__ge_0(self): - """Ensure `coefficient` is greater than or equal to 0""" - with pytest.raises(ValueError, match="greater than or equal to 0"): - AllocationField(areaId="NORTH", coefficient=-1) + def test_camel_case(self): + field = AllocationField(areaId="NORTH", coefficient=1) + assert field.dict(by_alias=True) == { + "areaId": "NORTH", + "coefficient": 1, + } class TestAllocationFormFields: - def test_init__nominal_case(self): + def test_base_case(self): fields = AllocationFormFields( allocation=[ - {"area_id": "NORTH", "coefficient": 0.75}, - {"area_id": "SOUTH", "coefficient": 0.25}, + {"areaId": "NORTH", "coefficient": 0.75}, + {"areaId": "SOUTH", "coefficient": 0.25}, ] ) assert fields.allocation == [ - AllocationField(area_id="NORTH", coefficient=0.75), - AllocationField(area_id="SOUTH", coefficient=0.25), + AllocationField(areaId="NORTH", coefficient=0.75), + AllocationField(areaId="SOUTH", coefficient=0.25), ] - def test_validation__coefficients_column_non_empty(self): - """Check that the coefficients colum is non-empty""" - with pytest.raises(ValueError, match="non-empty|not empty"): - AllocationFormFields(allocation=[]) + def test_fields_not_empty(self): + """Check that the coefficients column is not empty""" + with pytest.raises(ValueError, match="empty"): + AllocationFormFields( + allocation=[], + ) + + def test_validation_fields_no_duplicate_area_id(self): + """Check that the coefficients column does not contain duplicate area IDs""" + with pytest.raises(ValueError, match="duplicate"): + AllocationFormFields( + allocation=[ + {"areaId": "NORTH", "coefficient": 0.75}, + {"areaId": "NORTH", "coefficient": 0.25}, + ], + ) + + def test_validation_fields_no_negative_coefficient(self): + """Check that the coefficients column does not contain negative coefficients""" + with pytest.raises(ValueError, match="negative"): + AllocationFormFields( + allocation=[ + {"areaId": "NORTH", "coefficient": 0.75}, + {"areaId": "SOUTH", "coefficient": -0.25}, + ], + ) + + def test_validation_fields_no_negative_sum_coefficient(self): + """Check that the coefficients values does not sum to negative""" + with pytest.raises(ValueError, match="negative"): + AllocationFormFields( + allocation=[ + {"areaId": "NORTH", "coefficient": -0.75}, + {"areaId": "SOUTH", "coefficient": -0.25}, + ], + ) - def test_validation__coefficients_column_not_nul(self): - """Check that the coefficients column is not nul""" - with pytest.raises(ValueError, match="non-nul|not nul"): + def test_validation_fields_no_nan_coefficient(self): + """Check that the coefficients values does not contain NaN coefficients""" + with pytest.raises(ValueError, match="NaN"): AllocationFormFields( allocation=[ - {"area_id": "NORTH", "coefficient": 0}, - ] + {"areaId": "NORTH", "coefficient": 0.75}, + {"areaId": "SOUTH", "coefficient": float("nan")}, + ], ) class TestAllocationMatrix: - def test_init__nominal_case(self): + def test_base_case(self): field = AllocationMatrix( - index=["fr", "de"], - columns=["fr"], - data=[ - [1.0], - [0.2], - ], + index=["NORTH", "SOUTH"], + columns=["NORTH", "SOUTH"], + data=[[0.75, 0.25], [0.25, 0.75]], ) - assert field.index == ["fr", "de"] - assert field.columns == ["fr"] - assert field.data == [ - [1.0], - [0.2], - ] + assert field.index == ["NORTH", "SOUTH"] + assert field.columns == ["NORTH", "SOUTH"] + assert field.data == [[0.75, 0.25], [0.25, 0.75]] - def test_validation__coefficients_matrix_non_empty_array(self): - """Check that the coefficients matrix is a non-empty array""" - # fmt: off - with pytest.raises(ValueError, match="(?:not empty|non[ -]empty) array"): + def test_validation_coefficients_not_empty(self): + """Check that the coefficients matrix is not empty""" + with pytest.raises(ValueError, match="empty"): AllocationMatrix( index=[], columns=[], data=[], ) - # fmt: off - def test_validation__coefficients_matrix_array_shape(self): - """Check that the coefficients matrix is an array of shape 2×1""" - with pytest.raises(ValueError, match=r"array of shape \d+×\d+"): + def test_validation_matrix_shape(self): + """Check that the coefficients matrix is square""" + with pytest.raises(ValueError, match="square"): AllocationMatrix( - index=["fr", "de"], - columns=["fr"], - data=[[1, 2], [3, 4]], + index=["NORTH", "SOUTH"], + columns=["NORTH"], + data=[[0.75, 0.25], [0.25, 0.75]], ) - def test_validation__coefficients_matrix_positive_or_nul(self): - """Check that all coefficients matrix has positive or nul coefficients""" - # fmt: off - with pytest.raises(ValueError, match="positive or nul|greater than or equal to 0"): + def test_validation_matrix_sum_positive(self): + """Check that the coefficients matrix sum to positive""" + with pytest.raises(ValueError, match="negative"): AllocationMatrix( - index=["fr", "de"], - columns=["fr", "de"], - data=[ - [1.0, -1], - [0.2, 0], - ], + index=["NORTH", "SOUTH"], + columns=["NORTH", "SOUTH"], + data=[[0.75, -0.25], [-0.25, 0.75]], ) - # fmt: on - def test_validation__coefficients_array_has_no_non_null_values(self): - """Check that the coefficients matrix have non-nul columns""" - with pytest.raises(ValueError, match="(?:not nul|non-nul) columns"): + def test_validation_matrix_no_nan(self): + """Check that the coefficients matrix does not contain NaN values""" + with pytest.raises(ValueError, match="NaN"): AllocationMatrix( - index=["fr", "de"], - columns=["fr", "de"], - data=[ - [0, 8], - [0, 0], - ], + index=["NORTH", "SOUTH"], + columns=["NORTH", "SOUTH"], + data=[[0.75, 0.25], [0.25, float("nan")]], + ) + + def test_validation_matrix_no_non_null_values(self): + """Check that the coefficients matrix does not contain only null values""" + with pytest.raises(ValueError, match="(?:all|zero)"): + AllocationMatrix( + index=["NORTH", "SOUTH"], + columns=["NORTH", "SOUTH"], + data=[[0, 0], [0, 0]], ) @@ -264,13 +289,13 @@ def test_get_allocation_matrix__nominal_case( ], ) - def test_get_allocation_matrix__allocation_data_not_found( + def test_get_allocation_matrix__no_allocation( self, db_session, study_storage_service, study_uuid ): # The study must be fetched from the database study: RawStudy = db_session.query(Study).get(study_uuid) - # Prepare the mocks with an empty allocation config + # Prepare the mocks allocation_cfg = {} storage = study_storage_service.get_storage(study) file_study = storage.get_raw(study) @@ -286,153 +311,192 @@ def test_get_allocation_matrix__allocation_data_not_found( AreaInfoDTO(id="s", name="South", type=AreaType.AREA), AreaInfoDTO(id="w", name="West", type=AreaType.AREA), ] - area_id = "*" # all areas + area_id = "*" manager = AllocationManager(study_storage_service) + with pytest.raises(AllocationDataNotFound) as ctx: manager.get_allocation_matrix( all_areas=all_areas, study=study, area_id=area_id ) - assert "'*'" in ctx.value.detail + assert "*" in ctx.value.detail - def test_get_field_values__nominal_case( + def test_get_allocation_form_fields__nominal_case( self, db_session, study_storage_service, study_uuid ): - # The study must be fetched from the database study: RawStudy = db_session.query(Study).get(study_uuid) - - # Prepare the mocks - allocation_data = {"[allocation]": {"s": 0.1, "n": 0.2, "w": 0.6}} + allocation_cfg = { + "n": {"[allocation]": {"n": 1}}, + "e": {"[allocation]": {"e": 3, "s": 1}}, + "s": {"[allocation]": {"s": 0.1, "n": 0.2, "w": 0.6}}, + "w": {"[allocation]": {"w": 1}}, + } storage = study_storage_service.get_storage(study) file_study = storage.get_raw(study) file_study.tree = Mock( spec=FileStudyTree, - get=Mock(return_value=allocation_data), + get=Mock(return_value=allocation_cfg["n"]), ) - # Given the following arguments all_areas = [ AreaInfoDTO(id="n", name="North", type=AreaType.AREA), AreaInfoDTO(id="e", name="East", type=AreaType.AREA), AreaInfoDTO(id="s", name="South", type=AreaType.AREA), AreaInfoDTO(id="w", name="West", type=AreaType.AREA), ] - area_id = "s" # South + + area_id = "n" manager = AllocationManager(study_storage_service) - fields = manager.get_field_values( + + fields = manager.get_allocation_form_fields( all_areas=all_areas, study=study, area_id=area_id ) - assert fields == AllocationFormFields( - allocation=[ - AllocationField(areaId="s", coefficient=0.1), - AllocationField(areaId="n", coefficient=0.2), - AllocationField(areaId="w", coefficient=0.6), - ] - ) - def test_get_field_values__multiple_allocation_data_found( + expected_allocation = [ + AllocationField.construct(area_id=area, coefficient=value) + for area, value in allocation_cfg[area_id]["[allocation]"].items() + ] + assert fields.allocation == expected_allocation + + def test_get_allocation_form_fields__no_allocation_data( self, db_session, study_storage_service, study_uuid ): - # The study must be fetched from the database study: RawStudy = db_session.query(Study).get(study_uuid) - - # Prepare the mocks: North + South - allocation_cfg = { - "n": {"[allocation]": {"n": 1}}, - "s": {"[allocation]": {"s": 0.1, "n": 0.2, "w": 0.6}}, - } + allocation_cfg = {"n": {}} storage = study_storage_service.get_storage(study) file_study = storage.get_raw(study) file_study.tree = Mock( spec=FileStudyTree, - get=Mock(return_value=allocation_cfg), + get=Mock(return_value=allocation_cfg["n"]), ) - # Given the following arguments all_areas = [ AreaInfoDTO(id="n", name="North", type=AreaType.AREA), - AreaInfoDTO(id="e", name="East", type=AreaType.AREA), - AreaInfoDTO(id="s", name="South", type=AreaType.AREA), - AreaInfoDTO(id="w", name="West", type=AreaType.AREA), ] - area_id = "n,s" # North + South + + area_id = "n" manager = AllocationManager(study_storage_service) - with pytest.raises(MultipleAllocationDataFound) as ctx: - manager.get_field_values( + + with pytest.raises(AllocationDataNotFound) as ctx: + manager.get_allocation_form_fields( all_areas=all_areas, study=study, area_id=area_id ) - assert "'n'" in ctx.value.detail - assert "'s'" in ctx.value.detail + assert "n" in ctx.value.detail - def test_set_field_values__nominal_case( + def test_set_allocation_form_fields__nominal_case( self, db_session, study_storage_service, study_uuid ): - # The study must be fetched from the database study: RawStudy = db_session.query(Study).get(study_uuid) - - # Given the following arguments all_areas = [ AreaInfoDTO(id="n", name="North", type=AreaType.AREA), AreaInfoDTO(id="e", name="East", type=AreaType.AREA), AreaInfoDTO(id="s", name="South", type=AreaType.AREA), AreaInfoDTO(id="w", name="West", type=AreaType.AREA), ] - area_id = "s" # South + area_id = "n" manager = AllocationManager(study_storage_service) with patch(EXECUTE_OR_ADD_COMMANDS) as exe: - manager.set_field_values( - all_areas=all_areas, - study=study, - area_id=area_id, - data=AllocationFormFields( - allocation=[ - AllocationField(area_id="s", coefficient=2), - AllocationField(area_id="e", coefficient=3), - AllocationField(area_id="n", coefficient=4), - ] - ), - ) + with patch( + "antarest.study.business.allocation_management.AllocationManager.get_allocation_data", + return_value={"e": 0.5, "s": 0.25, "w": 0.25}, + ): + manager.set_allocation_form_fields( + all_areas=all_areas, + study=study, + area_id=area_id, + data=AllocationFormFields.construct( + allocation=[ + AllocationField.construct( + area_id="e", coefficient=0.5 + ), + AllocationField.construct( + area_id="s", coefficient=0.25 + ), + AllocationField.construct( + area_id="w", coefficient=0.25 + ), + ], + ), + ) - # check update assert exe.call_count == 1 mock_call = exe.mock_calls[0] - # signature: execute_or_add_commands(study, file_study, commands, storage_service) - actual_study, _, actual_cmds, _ = mock_call.args + actual_study, _, actual_commands, _ = mock_call.args assert actual_study == study - assert len(actual_cmds) == 1 - cmd: UpdateConfig = actual_cmds[0] + assert len(actual_commands) == 1 + cmd: UpdateConfig = actual_commands[0] assert cmd.command_name == CommandName.UPDATE_CONFIG assert cmd.target == f"input/hydro/allocation/{area_id}/[allocation]" - assert cmd.data == {"s": 2.0, "e": 3.0, "n": 4.0} + assert cmd.data == {"e": 0.5, "s": 0.25, "w": 0.25} - def test_set_field_values__area_not_found( + def test_set_allocation_form_fields__no_allocation_data( self, db_session, study_storage_service, study_uuid ): - # The study must be fetched from the database study: RawStudy = db_session.query(Study).get(study_uuid) - # Given the following arguments all_areas = [ AreaInfoDTO(id="n", name="North", type=AreaType.AREA), AreaInfoDTO(id="e", name="East", type=AreaType.AREA), AreaInfoDTO(id="s", name="South", type=AreaType.AREA), AreaInfoDTO(id="w", name="West", type=AreaType.AREA), ] - area_id = "n" # South + + area_id = "n" manager = AllocationManager(study_storage_service) with patch(EXECUTE_OR_ADD_COMMANDS) as exe: - with pytest.raises(AreaNotFound) as ctx: - manager.set_field_values( - all_areas=all_areas, - study=study, - area_id=area_id, - data=AllocationFormFields( - allocation=[ - AllocationField( - area_id="UNKNOWN", coefficient=3.14 - ), - ] - ), - ) - assert "'UNKNOWN'" in ctx.value.detail - exe.assert_not_called() + with patch( + "antarest.study.business.allocation_management.AllocationManager.get_allocation_data", + side_effect=AllocationDataNotFound(area_id), + ): + with pytest.raises(AllocationDataNotFound) as ctx: + manager.set_allocation_form_fields( + all_areas=all_areas, + study=study, + area_id=area_id, + data=AllocationFormFields.construct( + allocation=[ + AllocationField.construct( + area_id="e", coefficient=0.5 + ), + AllocationField.construct( + area_id="s", coefficient=0.25 + ), + AllocationField.construct( + area_id="w", coefficient=0.25 + ), + ], + ), + ) + assert "n" in ctx.value.detail + + def test_set_allocation_form_fields__invalid_area_ids( + self, db_session, study_storage_service, study_uuid + ): + study: RawStudy = db_session.query(Study).get(study_uuid) + + all_areas = [ + AreaInfoDTO(id="n", name="North", type=AreaType.AREA), + AreaInfoDTO(id="e", name="East", type=AreaType.AREA), + AreaInfoDTO(id="s", name="South", type=AreaType.AREA), + AreaInfoDTO(id="w", name="West", type=AreaType.AREA), + ] + + area_id = "n" + manager = AllocationManager(study_storage_service) + + data = AllocationFormFields.construct( + allocation=[ + AllocationField.construct(area_id="e", coefficient=0.5), + AllocationField.construct(area_id="s", coefficient=0.25), + AllocationField.construct( + area_id="invalid_area", coefficient=0.25 + ), + ] + ) + + with pytest.raises(AreaNotFound) as ctx: + manager.set_allocation_form_fields( + all_areas=all_areas, study=study, area_id=area_id, data=data + ) + + assert "invalid_area" in ctx.value.detail diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/AllocationField.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/AllocationField.tsx index 8b78c1dc6c..c6b46d5da9 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/AllocationField.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/AllocationField.tsx @@ -1,75 +1,59 @@ -import { Typography, IconButton, Box } from "@mui/material"; +import { Typography, IconButton, Grid } from "@mui/material"; import RemoveCircleOutlineIcon from "@mui/icons-material/RemoveCircleOutline"; +import { t } from "i18next"; +import { FieldArrayWithId, UseFieldArrayRemove } from "react-hook-form"; import NumberFE from "../../../../../../../common/fieldEditors/NumberFE"; import { useFormContextPlus } from "../../../../../../../common/Form"; import { AllocationFormFields } from "./utils"; -import { getCurrentAreaId } from "../../../../../../../../redux/selectors"; -import useAppSelector from "../../../../../../../../redux/hooks/useAppSelector"; interface Props { - field: { - id: string; - areaId: string; - coefficient: number; - }; + field: FieldArrayWithId; index: number; label: string; - remove: (index: number) => void; + remove: UseFieldArrayRemove; + fieldsLength: number; } -function AllocationField({ field, index, label, remove }: Props) { +function AllocationField({ field, index, label, remove, fieldsLength }: Props) { const { control } = useFormContextPlus(); - const currentAreaId = useAppSelector(getCurrentAreaId); //////////////////////////////////////////////////////////////// // JSX //////////////////////////////////////////////////////////////// return ( - - - {label} - - { - if (value < 0) { - return false; - } + + + + {label} + + + + - remove(index)} - disabled={field.areaId === currentAreaId} - style={{ - visibility: field.areaId === currentAreaId ? "hidden" : "visible", - }} - > - - - + }} + /> + + + remove(index)} disabled={fieldsLength === 1}> + + + + ); } diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/AllocationSelect.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/AllocationSelect.tsx index 57056b44c4..971ac5be2f 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/AllocationSelect.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/AllocationSelect.tsx @@ -1,12 +1,13 @@ -import { Box } from "@mui/material"; import { t } from "i18next"; import { useMemo } from "react"; +import { UseFieldArrayAppend } from "react-hook-form"; import { Area } from "../../../../../../../../common/types"; import SelectFE from "../../../../../../../common/fieldEditors/SelectFE"; +import { AllocationFormFields } from "./utils"; interface Props { filteredAreas: Array; - append: (obj: { areaId: string; coefficient: number }) => void; + append: UseFieldArrayAppend; } function AllocationSelect({ filteredAreas, append }: Props) { @@ -23,21 +24,18 @@ function AllocationSelect({ filteredAreas, append }: Props) { // JSX //////////////////////////////////////////////////////////////// - return ( - - {filteredAreas.length > 0 && ( - { - append({ areaId: e.target.value as string, coefficient: 0 }); - }} - size="small" - variant="outlined" - /> - )} - - ); + return filteredAreas.length > 0 ? ( + { + append({ areaId: e.target.value as string, coefficient: 0 }); + }} + size="small" + variant="outlined" + sx={{ width: 200 }} + /> + ) : null; } export default AllocationSelect; diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/Fields.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/Fields.tsx index f09a778a9c..8a0a154621 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/Fields.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/Fields.tsx @@ -1,8 +1,7 @@ -import { Box, Divider, Paper } from "@mui/material"; +import { Divider, Grid } from "@mui/material"; import { useFieldArray } from "react-hook-form"; import { useOutletContext } from "react-router"; import { useMemo } from "react"; -import Fieldset from "../../../../../../../common/Fieldset"; import { useFormContextPlus } from "../../../../../../../common/Form"; import useAppSelector from "../../../../../../../../redux/hooks/useAppSelector"; import { @@ -46,38 +45,26 @@ function Fields() { //////////////////////////////////////////////////////////////// return ( -
- - - - {fields.map((field, index) => ( - - ))} - - + + {fields.map((field, index) => ( + - - - -
+ ))} + + + + + + + + ); } diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/index.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/index.tsx index 4d76a85195..48820c1529 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/index.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/index.tsx @@ -1,7 +1,5 @@ -import { Box } from "@mui/material"; +import { Box, Paper } from "@mui/material"; import { useOutletContext } from "react-router"; -import { t } from "i18next"; -import { AxiosError } from "axios"; import Form from "../../../../../../../common/Form"; import Fields from "./Fields"; import { StudyMetadata } from "../../../../../../../../common/types"; @@ -13,30 +11,21 @@ import { setAllocationFormFields, } from "./utils"; import { SubmitHandlerPlus } from "../../../../../../../common/Form/types"; -import useEnqueueErrorSnackbar from "../../../../../../../../hooks/useEnqueueErrorSnackbar"; function Allocation() { const { study: { id: studyId }, } = useOutletContext<{ study: StudyMetadata }>(); const areaId = useAppSelector(getCurrentAreaId); - const enqueueErrorSnackbar = useEnqueueErrorSnackbar(); //////////////////////////////////////////////////////////////// // Event handlers //////////////////////////////////////////////////////////////// const handleSubmit = (data: SubmitHandlerPlus) => { - try { - setAllocationFormFields(studyId, areaId, { - allocation: data.values.allocation, - }); - } catch (e) { - enqueueErrorSnackbar( - t("study.modelization.hydro.allocation.error.field.delete"), - e as AxiosError - ); - } + return setAllocationFormFields(studyId, areaId, { + allocation: data.values.allocation, + }); }; //////////////////////////////////////////////////////////////// @@ -48,19 +37,27 @@ function Allocation() { sx={{ width: 1, height: 1, - p: 1, - overflowX: "auto", + p: 2, + overflow: "auto", }} > -
getAllocationFormFields(studyId, areaId), + - - +
getAllocationFormFields(studyId, areaId), + }} + onSubmit={handleSubmit} + sx={{ p: 3 }} + > + + +
); } diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/utils.ts b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/utils.ts index 500f38eca0..729b7d3660 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/utils.ts +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/utils.ts @@ -22,7 +22,7 @@ function makeRequestURL( studyId: StudyMetadata["id"], areaId: Area["name"] ): string { - return `v1/studies/${studyId}/areas/${areaId}/hydro/allocation`; + return `v1/studies/${studyId}/areas/${areaId}/hydro/allocation/form`; } export async function getAllocationFormFields( @@ -33,10 +33,11 @@ export async function getAllocationFormFields( return res.data; } -export function setAllocationFormFields( +export async function setAllocationFormFields( studyId: StudyMetadata["id"], areaId: Area["name"], - values: Partial -): Promise { - return client.put(makeRequestURL(studyId, areaId), values); + values: AllocationFormFields +): Promise { + const res = await client.put(makeRequestURL(studyId, areaId), values); + return res.data; } From 4b30780af02f29711af184ca997dfdbba2cd0fab Mon Sep 17 00:00:00 2001 From: Laurent LAPORTE <43534797+laurent-laporte-pro@users.noreply.github.com> Date: Thu, 13 Apr 2023 23:34:01 +0200 Subject: [PATCH 29/70] feat(api): spacial correlation matrix (#1445) --- antarest/core/exceptions.py | 4 +- .../study/business/correlation_management.py | 369 ++++++++++++++++ .../variantstudy/model/command/create_area.py | 45 +- .../variantstudy/model/command/remove_area.py | 27 ++ antarest/study/web/study_data_blueprint.py | 195 ++++++++- examples/studies/STA-mini.zip | Bin 445596 -> 481559 bytes .../test_hydro_correlation.py | 253 +++++++++++ .../storage/business/test_arealink_manager.py | 35 +- .../business/test_correlation_manager.py | 397 ++++++++++++++++++ tests/variantstudy/conftest.py | 6 +- .../model/command/test_remove_area.py | 199 +++++---- 11 files changed, 1378 insertions(+), 152 deletions(-) create mode 100644 antarest/study/business/correlation_management.py create mode 100644 tests/integration/study_data_blueprint/test_hydro_correlation.py create mode 100644 tests/study/business/test_correlation_manager.py diff --git a/antarest/core/exceptions.py b/antarest/core/exceptions.py index 324c71ccf4..857ea1462f 100644 --- a/antarest/core/exceptions.py +++ b/antarest/core/exceptions.py @@ -189,8 +189,8 @@ def __init__(self, *area_ids: str) -> None: ids = ", ".join(f"'{a}'" for a in area_ids) msg = { 0: "All areas are found", - 1: f"{count} area is not found: {ids}", - 2: f"{count} areas are not found: {ids}", + 1: f"Area is not found: {ids}", + 2: f"Areas are not found: {ids}", }[min(count, 2)] super().__init__(HTTPStatus.NOT_FOUND, msg) diff --git a/antarest/study/business/correlation_management.py b/antarest/study/business/correlation_management.py new file mode 100644 index 0000000000..763dbdc12d --- /dev/null +++ b/antarest/study/business/correlation_management.py @@ -0,0 +1,369 @@ +""" +Management of spatial correlations between the different generators. +The generators are of the same category and can be hydraulic, wind, load or solar. +""" +import collections +from typing import Dict, List, Sequence + +import numpy as np +import numpy.typing as npt +from antarest.core.exceptions import AreaNotFound +from antarest.study.business.area_management import AreaInfoDTO +from antarest.study.business.utils import ( + FormFieldsBaseModel, + execute_or_add_commands, +) +from antarest.study.model import Study +from antarest.study.storage.rawstudy.model.filesystem.factory import FileStudy +from antarest.study.storage.storage_service import StudyStorageService +from antarest.study.storage.variantstudy.model.command.update_config import ( + UpdateConfig, +) +from pydantic import conlist, validator + + +class CorrelationField(FormFieldsBaseModel): + """ + Model for correlation coefficients of a given area. + + Attributes: + area_id: Area identifier. + coefficient: correlation coefficients in percentage (-100 <= coefficient <= 100). + """ + + class Config: + allow_population_by_field_name = True + + area_id: str + coefficient: float + + +class CorrelationFormFields(FormFieldsBaseModel): + """ + Model for a list of consumption coefficients for each area. + + Attributes: + correlation: A list of non-null correlation coefficients in percentage. + """ + + correlation: List[CorrelationField] + + # noinspection PyMethodParameters + @validator("correlation") + def check_correlation( + cls, correlation: List[CorrelationField] + ) -> List[CorrelationField]: + if not correlation: + raise ValueError("correlation must not be empty") + counter = collections.Counter(field.area_id for field in correlation) + if duplicates := {id_ for id_, count in counter.items() if count > 1}: + raise ValueError( + f"correlation must not contain duplicate area IDs: {duplicates}" + ) + # fmt: off + array = np.array([a.coefficient for a in correlation], dtype=np.float64) + if np.any((array < -100) | np.any(array > 100)): + raise ValueError("percentage must be between -100 and 100") + if np.any(np.isnan(array)): + raise ValueError("correlation matrix must not contain NaN coefficients") + # fmt: on + return correlation + + +class CorrelationMatrix(FormFieldsBaseModel): + """ + Correlation matrix for hydraulic, wind, load, or solar generators. + + Attributes: + index: A list of all study areas. + columns: A list of selected production areas. + data: A 2D-array matrix of correlation coefficients. + """ + + index: conlist(str, min_items=1) # type: ignore + columns: conlist(str, min_items=1) # type: ignore + data: List[List[float]] # NonNegativeFloat not necessary + + # noinspection PyMethodParameters + @validator("data") + def validate_correlation_matrix( + cls, data: List[List[float]], values: Dict[str, List[str]] + ) -> List[List[float]]: + """ + Validates the correlation matrix by checking its shape and range of coefficients. + + Args: + cls: The `CorrelationMatrix` class. + data: The correlation matrix to validate. + values: A dictionary containing the values of `index` and `columns`. + + Returns: + List[List[float]]: The validated correlation matrix. + + Raises: + ValueError: + If the correlation matrix is empty, + has an incorrect shape, + is squared but not symmetric, + or contains coefficients outside the range of -1 to 1 + or NaN coefficients. + """ + + array = np.array(data) + rows = len(values.get("index", [])) + cols = len(values.get("columns", [])) + + # fmt: off + if array.size == 0: + raise ValueError("correlation matrix must not be empty") + if array.shape != (rows, cols): + raise ValueError(f"correlation matrix must have shape ({rows}×{cols})") + if np.any((array < -1) | np.any(array > 1)): + raise ValueError("coefficients must be between -1 and 1") + if np.any(np.isnan(array)): + raise ValueError("correlation matrix must not contain NaN coefficients") + if ( + array.shape[0] == array.shape[1] + and not np.array_equal(array, array.T) + ): + raise ValueError("correlation matrix is not symmetric") + # fmt: on + return data + + class Config: + schema_extra = { + "example": { + "columns": ["north", "east", "south", "west"], + "data": [ + [0.0, 0.0, 0.25, 0.0], + [0.0, 0.0, 0.75, 0.12], + [0.25, 0.75, 0.0, 0.75], + [0.0, 0.12, 0.75, 0.0], + ], + "index": ["north", "east", "south", "west"], + } + } + + +def _config_to_array( + area_ids: Sequence[str], + correlation_cfg: Dict[str, str], +) -> npt.NDArray[np.float64]: + array = np.identity(len(area_ids), dtype=np.float64) + for key, value in correlation_cfg.items(): + a1, a2 = key.split("%") + i = area_ids.index(a1) + j = area_ids.index(a2) + if i == j: + # ignored: values from the diagonal are always == 1.0 + continue + coefficient = value + array[i][j] = coefficient + array[j][i] = coefficient + return array + + +def _array_to_config( + area_ids: Sequence[str], + array: npt.NDArray[np.float64], +) -> Dict[str, str]: + correlation_cfg: Dict[str, str] = {} + count = len(area_ids) + for i in range(count): + # not saved: values from the diagonal are always == 1.0 + for j in range(i + 1, count): + coefficient = array[i][j] + if not coefficient: + # null values are not saved + continue + a1 = area_ids[i] + a2 = area_ids[j] + correlation_cfg[f"{a1}%{a2}"] = coefficient + return correlation_cfg + + +class CorrelationManager: + """ + This manager allows you to read and write the hydraulic, wind, load or solar + correlation matrices of a raw study or a variant. + """ + + # Today, only the 'hydro' category is fully supported, but + # we could also manage the 'load' 'solar' and 'wind' + # categories but the usage is deprecated. + url = ["input", "hydro", "prepro", "correlation", "annual"] + + def __init__(self, storage_service: StudyStorageService) -> None: + self.storage_service = storage_service + + def _get_array( + self, + file_study: FileStudy, + area_ids: Sequence[str], + ) -> npt.NDArray[np.float64]: + correlation_cfg = file_study.tree.get(self.url, depth=3) + return _config_to_array(area_ids, correlation_cfg) + + def _set_array( + self, + study: Study, + file_study: FileStudy, + area_ids: Sequence[str], + array: npt.NDArray[np.float64], + ) -> None: + correlation_cfg = _array_to_config(area_ids, array) + command_context = ( + self.storage_service.variant_study_service.command_factory.command_context + ) + command = UpdateConfig( + target="/".join(self.url), + data=correlation_cfg, + command_context=command_context, + ) + execute_or_add_commands( + study, file_study, [command], self.storage_service + ) + + def get_correlation_form_fields( + self, all_areas: List[AreaInfoDTO], study: Study, area_id: str + ) -> CorrelationFormFields: + """ + Get the correlation form fields (percentage values) for a given area. + + Args: + all_areas: list of all areas in the study. + study: study to get the correlation coefficients from. + area_id: area to get the correlation coefficients from. + + Returns: + The correlation coefficients. + """ + file_study = self.storage_service.get_storage(study).get_raw(study) + area_ids = [area.id for area in all_areas] + array = self._get_array(file_study, area_ids) + column = array[:, area_ids.index(area_id)] * 100 + return CorrelationFormFields.construct( + correlation=[ + CorrelationField.construct(area_id=a, coefficient=c) + for a, c in zip(area_ids, column) + if c + ] + ) + + def set_correlation_form_fields( + self, + all_areas: List[AreaInfoDTO], + study: Study, + area_id: str, + data: CorrelationFormFields, + ) -> CorrelationFormFields: + """ + Set the correlation coefficients of a given area from the form fields (percentage values). + + Args: + all_areas: list of all areas in the study. + study: study to set the correlation coefficients to. + area_id: area to set the correlation coefficients to. + data: correlation coefficients to set. + + Raises: + AreaNotFound: if the area is not found or invalid. + + Returns: + The updated correlation coefficients. + """ + correlation_ids = {field.area_id for field in data.correlation} + area_ids = [area.id for area in all_areas] + if invalid_ids := correlation_ids - set(area_ids): + # sort for deterministic error message and testing + raise AreaNotFound(*sorted(invalid_ids)) + + file_study = self.storage_service.get_storage(study).get_raw(study) + array = self._get_array(file_study, area_ids) + for field in data.correlation: + i = area_ids.index(field.area_id) + j = area_ids.index(area_id) + array[i][j] = field.coefficient / 100 + self._set_array(study, file_study, area_ids, array) + + column = array[:, area_ids.index(area_id)] * 100 + return CorrelationFormFields.construct( + correlation=[ + CorrelationField.construct(area_id=a, coefficient=c) + for a, c in zip(area_ids, column) + if c + ] + ) + + def get_correlation_matrix( + self, all_areas: List[AreaInfoDTO], study: Study, columns: List[str] + ) -> CorrelationMatrix: + """ + Read the correlation coefficients and get the correlation matrix (values in the range -1 to 1). + + Args: + all_areas: list of all areas in the study. + study: study to get the correlation matrix from. + columns: areas to get the correlation matrix from. + + Returns: + The correlation matrix. + """ + file_study = self.storage_service.get_storage(study).get_raw(study) + area_ids = [area.id for area in all_areas] + columns = ( + [a for a in area_ids if a in columns] if columns else area_ids + ) + array = self._get_array(file_study, area_ids) + # noinspection PyTypeChecker + data = [ + [c for i, c in enumerate(row) if area_ids[i] in columns] + for row in array.tolist() + ] + + return CorrelationMatrix.construct( + index=area_ids, columns=columns, data=data + ) + + def set_correlation_matrix( + self, + all_areas: List[AreaInfoDTO], + study: Study, + matrix: CorrelationMatrix, + ) -> CorrelationMatrix: + """ + Set the correlation coefficients from the coefficient matrix (values in the range -1 to 1). + + Args: + all_areas: list of all areas in the study. + study: study to get the correlation matrix from. + matrix: correlation matrix to update + + Returns: + The updated correlation matrix. + """ + file_study = self.storage_service.get_storage(study).get_raw(study) + area_ids = [area.id for area in all_areas] + + array = self._get_array(file_study, area_ids) + + for row, a1 in zip(matrix.data, matrix.index): + for coefficient, a2 in zip(row, matrix.columns): + if missing := {a1, a2} - set(area_ids): + raise AreaNotFound(*missing) + i = area_ids.index(a1) + j = area_ids.index(a2) + array[i][j] = coefficient + array[j][i] = coefficient + + self._set_array(study, file_study, area_ids, array) + + # noinspection PyTypeChecker + data = [ + [c for i, c in enumerate(row) if area_ids[i] in matrix.columns] + for row in array.tolist() + ] + + return CorrelationMatrix.construct( + index=area_ids, columns=matrix.columns, data=data + ) diff --git a/antarest/study/storage/variantstudy/model/command/create_area.py b/antarest/study/storage/variantstudy/model/command/create_area.py index 2cc832c612..cd029cbed1 100644 --- a/antarest/study/storage/variantstudy/model/command/create_area.py +++ b/antarest/study/storage/variantstudy/model/command/create_area.py @@ -91,16 +91,12 @@ def _apply(self, study_data: FileStudy) -> CommandOutput: area_id = data["area_id"] version = study_data.config.version + # fmt: off hydro_config = study_data.tree.get(["input", "hydro", "hydro"]) - get_or_create_section(hydro_config, "inter-daily-breakdown")[ - area_id - ] = 1 - get_or_create_section(hydro_config, "intra-daily-modulation")[ - area_id - ] = 24 - get_or_create_section(hydro_config, "inter-monthly-breakdown")[ - area_id - ] = 1 + get_or_create_section(hydro_config, "inter-daily-breakdown")[area_id] = 1 + get_or_create_section(hydro_config, "intra-daily-modulation")[area_id] = 24 + get_or_create_section(hydro_config, "inter-monthly-breakdown")[area_id] = 1 + # fmt: on new_area_data: JSON = { "input": { @@ -229,30 +225,22 @@ def _apply(self, study_data: FileStudy) -> CommandOutput: } if version > 650: - get_or_create_section(hydro_config, "initialize reservoir date")[ - area_id - ] = 0 + # fmt: off + get_or_create_section(hydro_config, "initialize reservoir date")[area_id] = 0 get_or_create_section(hydro_config, "leeway low")[area_id] = 1 get_or_create_section(hydro_config, "leeway up")[area_id] = 1 - get_or_create_section(hydro_config, "pumping efficiency")[ - area_id - ] = 1 + get_or_create_section(hydro_config, "pumping efficiency")[area_id] = 1 - new_area_data["input"]["hydro"]["common"]["capacity"][ - f"creditmodulations_{area_id}" - ] = ( + new_area_data["input"]["hydro"]["common"]["capacity"][f"creditmodulations_{area_id}"] = ( self.command_context.generator_matrix_constants.get_hydro_credit_modulations() ) - new_area_data["input"]["hydro"]["common"]["capacity"][ - f"inflowPattern_{area_id}" - ] = ( + new_area_data["input"]["hydro"]["common"]["capacity"][f"inflowPattern_{area_id}"] = ( self.command_context.generator_matrix_constants.get_hydro_inflow_pattern() ) - new_area_data["input"]["hydro"]["common"]["capacity"][ - f"waterValues_{area_id}" - ] = ( + new_area_data["input"]["hydro"]["common"]["capacity"][f"waterValues_{area_id}"] = ( self.command_context.generator_matrix_constants.get_null_matrix() ) + # fmt: on if version >= 830: new_area_data["input"]["areas"][area_id]["adequacy_patch"] = { @@ -261,6 +249,15 @@ def _apply(self, study_data: FileStudy) -> CommandOutput: new_area_data["input"]["hydro"]["hydro"] = hydro_config + # NOTE regarding the following configurations: + # - ["input", "hydro", "prepro", "correlation"] + # - ["input", "load", "prepro", "correlation"] + # - ["input", "solar", "prepro", "correlation"] + # - ["input", "wind", "prepro", "correlation"] + # When creating a new area, we should not add a new correlation + # value to the configuration because it does not store the values + # of the diagonal (always equal to 1). + study_data.tree.save(new_area_data) return output diff --git a/antarest/study/storage/variantstudy/model/command/remove_area.py b/antarest/study/storage/variantstudy/model/command/remove_area.py index d02c17c639..ca633b0d2e 100644 --- a/antarest/study/storage/variantstudy/model/command/remove_area.py +++ b/antarest/study/storage/variantstudy/model/command/remove_area.py @@ -141,6 +141,32 @@ def _remove_area_from_hydro_allocation( allocations.pop(self.id, None) study_data.tree.save(allocation_cfg, ["input", "hydro", "allocation"]) + def _remove_area_from_correlation_matrices( + self, study_data: FileStudy + ) -> None: + """ + Removes the values from the correlation matrix that match the current area. + + This function can update the following configurations: + - ["input", "hydro", "prepro", "correlation"] + + Args: + study_data:File Study to update. + """ + # Today, only the 'hydro' category is fully supported, but + # we could also manage the 'load' 'solar' and 'wind' + # categories but the usage is deprecated. + url = ["input", "hydro", "prepro", "correlation"] + correlation_cfg = study_data.tree.get(url) + for section, correlation in correlation_cfg.items(): + if section == "general": + continue + for key in list(correlation): + a1, a2 = key.split("%") + if a1 == self.id or a2 == self.id: + del correlation[key] + study_data.tree.save(correlation_cfg, url) + def _remove_area_from_districts(self, study_data: FileStudy) -> None: districts = study_data.tree.get(["input", "areas", "sets"]) for district in districts.values(): @@ -197,6 +223,7 @@ def _apply(self, study_data: FileStudy) -> CommandOutput: self._remove_area_from_links(study_data) self._remove_area_from_binding_constraints(study_data) + self._remove_area_from_correlation_matrices(study_data) self._remove_area_from_hydro_allocation(study_data) self._remove_area_from_districts(study_data) self._remove_area_from_cluster(study_data) diff --git a/antarest/study/web/study_data_blueprint.py b/antarest/study/web/study_data_blueprint.py index 538f2c7f07..8b82df0d40 100644 --- a/antarest/study/web/study_data_blueprint.py +++ b/antarest/study/web/study_data_blueprint.py @@ -2,9 +2,6 @@ from http import HTTPStatus from typing import Any, Dict, List, Optional, Union, cast -from fastapi import APIRouter, Body, Depends -from fastapi.params import Body - from antarest.core.config import Config from antarest.core.jwt import JWTUser from antarest.core.model import StudyPermissionType @@ -35,6 +32,11 @@ ConstraintTermDTO, UpdateBindingConstProps, ) +from antarest.study.business.correlation_management import ( + CorrelationFormFields, + CorrelationManager, + CorrelationMatrix, +) from antarest.study.business.district_manager import ( DistrictCreationDTO, DistrictInfoDTO, @@ -61,6 +63,8 @@ from antarest.study.business.timeseries_config_management import TSFormFields from antarest.study.model import PatchArea, PatchCluster from antarest.study.service import StudyService +from fastapi import APIRouter, Body, Depends +from fastapi.params import Body, Query logger = logging.getLogger(__name__) @@ -1142,6 +1146,191 @@ def set_allocation_form_fields( all_areas, study, area_id, data ) + @bp.get( + path="/studies/{uuid}/areas/hydro/correlation/matrix", + tags=[APITag.study_data], + summary="Get the hydraulic/load/solar/wind correlation matrix of a study", + response_model=CorrelationMatrix, + ) + def get_correlation_matrix( + uuid: str, + columns: Optional[str] = Query( + None, + examples={ + "all areas": { + "description": "get the correlation matrix for all areas (by default)", + "value": "", + }, + "single area": { + "description": "get the correlation column for a single area", + "value": "north", + }, + "selected areas": { + "description": "get the correlation columns for a selected list of areas", + "value": "north,east", + }, + }, + ), # type: ignore + current_user: JWTUser = Depends(auth.get_current_user), + ) -> CorrelationMatrix: + """ + Get the hydraulic/load/solar/wind correlation matrix of a study. + + Parameters: + - `uuid`: The UUID of the study. + - `columns`: A filter on the area identifiers: + - Use no parameter to select all areas. + - Use an area identifier to select a single area. + - Use a comma-separated list of areas to select those areas. + + Returns the hydraulic/load/solar/wind correlation matrix with the following attributes: + - `index`: A list of all study areas. + - `columns`: A list of selected production areas. + - `data`: A 2D-array matrix of correlation coefficients with values in the range of -1 to 1. + """ + params = RequestParameters(user=current_user) + study = study_service.check_study_access( + uuid, StudyPermissionType.READ, params + ) + all_areas = cast( + List[AreaInfoDTO], # because `ui=False` + study_service.get_all_areas( + uuid, area_type=AreaType.AREA, ui=False, params=params + ), + ) + manager = CorrelationManager(study_service.storage_service) + return manager.get_correlation_matrix( + all_areas, + study, + columns.split(",") if columns else [], + ) + + @bp.put( + path="/studies/{uuid}/areas/hydro/correlation/matrix", + tags=[APITag.study_data], + summary="Set the hydraulic/load/solar/wind correlation matrix of a study", + status_code=HTTPStatus.OK, + response_model=CorrelationMatrix, + ) + def set_correlation_matrix( + uuid: str, + matrix: CorrelationMatrix = Body( + ..., + example={ + "columns": ["north", "east", "south", "west"], + "data": [ + [0.0, 0.0, 0.25, 0.0], + [0.0, 0.0, 0.75, 0.12], + [0.25, 0.75, 0.0, 0.75], + [0.0, 0.12, 0.75, 0.0], + ], + "index": ["north", "east", "south", "west"], + }, + ), + current_user: JWTUser = Depends(auth.get_current_user), + ) -> CorrelationMatrix: + """ + Set the hydraulic/load/solar/wind correlation matrix of a study. + + Parameters: + - `uuid`: The UUID of the study. + - `index`: A list of all study areas. + - `columns`: A list of selected production areas. + - `data`: A 2D-array matrix of correlation coefficients with values in the range of -1 to 1. + + Returns the hydraulic/load/solar/wind correlation matrix updated + """ + params = RequestParameters(user=current_user) + study = study_service.check_study_access( + uuid, StudyPermissionType.WRITE, params + ) + all_areas = cast( + List[AreaInfoDTO], # because `ui=False` + study_service.get_all_areas( + uuid, area_type=AreaType.AREA, ui=False, params=params + ), + ) + manager = CorrelationManager(study_service.storage_service) + return manager.set_correlation_matrix(all_areas, study, matrix) + + @bp.get( + path="/studies/{uuid}/areas/{area_id}/hydro/correlation/form", + tags=[APITag.study_data], + summary="Get the form fields used for the correlation form", + response_model=CorrelationFormFields, + ) + def get_correlation_form_fields( + uuid: str, + area_id: str, + current_user: JWTUser = Depends(auth.get_current_user), + ) -> CorrelationFormFields: + """ + Get the form fields used for the correlation form. + + Parameters: + - `uuid`: The UUID of the study. + - `area_id`: the area ID. + + Returns the correlation form fields in percentage. + """ + params = RequestParameters(user=current_user) + study = study_service.check_study_access( + uuid, StudyPermissionType.READ, params + ) + all_areas = cast( + List[AreaInfoDTO], # because `ui=False` + study_service.get_all_areas( + uuid, area_type=AreaType.AREA, ui=False, params=params + ), + ) + manager = CorrelationManager(study_service.storage_service) + return manager.get_correlation_form_fields(all_areas, study, area_id) + + @bp.put( + path="/studies/{uuid}/areas/{area_id}/hydro/correlation/form", + tags=[APITag.study_data], + summary="Set the form fields used for the correlation form", + status_code=HTTPStatus.OK, + response_model=CorrelationFormFields, + ) + def set_correlation_form_fields( + uuid: str, + area_id: str, + data: CorrelationFormFields = Body( + ..., + example=CorrelationFormFields( + correlation=[ + {"areaId": "east", "coefficient": 80}, + {"areaId": "north", "coefficient": 20}, + ] + ), + ), + current_user: JWTUser = Depends(auth.get_current_user), + ) -> CorrelationFormFields: + """ + Update the hydraulic/load/solar/wind correlation of a given area. + + Parameters: + - `uuid`: The UUID of the study. + - `area_id`: the area ID. + + Returns the correlation form fields in percentage. + """ + params = RequestParameters(user=current_user) + study = study_service.check_study_access( + uuid, StudyPermissionType.WRITE, params + ) + all_areas = cast( + List[AreaInfoDTO], # because `ui=False` + study_service.get_all_areas( + uuid, area_type=AreaType.AREA, ui=False, params=params + ), + ) + manager = CorrelationManager(study_service.storage_service) + return manager.set_correlation_form_fields( + all_areas, study, area_id, data + ) + @bp.get( path="/studies/{uuid}/config/advancedparameters/form", tags=[APITag.study_data], diff --git a/examples/studies/STA-mini.zip b/examples/studies/STA-mini.zip index c033602313c66c3398425c3ce90d39487a8065ad..0de2e32809f07d3a8dd9258873b87ea207dd51ba 100644 GIT binary patch literal 481559 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

yG~s>9M- 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 FileStudy: +def empty_study(tmp_path: Path) -> FileStudy: cur_dir: Path = Path(__file__).parent - study_path = Path(tmpdir / str(uuid.uuid4())) - os.mkdir(study_path) + study_path = tmp_path.joinpath(str(uuid.uuid4())) + study_path.mkdir() with ZipFile(cur_dir / "assets" / "empty_study_810.zip") as zip_output: zip_output.extractall(path=study_path) config = build(study_path, "1") @@ -72,9 +67,9 @@ def empty_study(tmpdir: Path) -> FileStudy: @pytest.fixture -def matrix_service(tmpdir: Path) -> ISimpleMatrixService: - matrix_path = Path(tmpdir / "matrix_store") - os.mkdir(matrix_path) +def matrix_service(tmp_path: Path) -> ISimpleMatrixService: + matrix_path = tmp_path.joinpath("matrix_store") + matrix_path.mkdir() return SimpleMatrixService(matrix_path) @@ -94,6 +89,7 @@ def test_area_crud( raw_study_service, variant_study_service ) ) + # noinspection PyArgumentList study = RawStudy( id="1", path=empty_study.config.study_path, @@ -143,6 +139,7 @@ def test_area_crud( area_manager.delete_area(study, "test2") assert len(empty_study.config.areas.keys()) == 0 + # noinspection PyArgumentList study = VariantStudy( id="2", path=empty_study.config.study_path, @@ -421,8 +418,6 @@ def test_get_all_area(): {"area1": "a2", "area2": "a3", "ui": None}, ] == [link.dict() for link in links] - pass - def test_update_area(): raw_study_service = Mock(spec=RawStudyService) @@ -523,4 +518,4 @@ def test_update_clusters(): ) assert len(new_area_info.thermals) == 1 assert new_area_info.thermals[0].type == "a" - assert new_area_info.thermals[0].code_oi == None + assert new_area_info.thermals[0].code_oi is None diff --git a/tests/study/business/test_correlation_manager.py b/tests/study/business/test_correlation_manager.py new file mode 100644 index 0000000000..29c07c393a --- /dev/null +++ b/tests/study/business/test_correlation_manager.py @@ -0,0 +1,397 @@ +import contextlib +import datetime +import uuid +from unittest.mock import Mock, patch + +import numpy as np +import pytest +from antarest.core.exceptions import AreaNotFound +from antarest.core.model import PublicMode +from antarest.dbmodel import Base +from antarest.login.model import Group, User +from antarest.study.business.area_management import AreaInfoDTO, AreaType +from antarest.study.business.correlation_management import ( + CorrelationField, + CorrelationFormFields, + CorrelationManager, + CorrelationMatrix, +) +from antarest.study.model import RawStudy, Study, StudyContentStatus +from antarest.study.storage.rawstudy.model.filesystem.factory import FileStudy +from antarest.study.storage.rawstudy.model.filesystem.root.filestudytree import ( + FileStudyTree, +) +from antarest.study.storage.rawstudy.raw_study_service import RawStudyService +from antarest.study.storage.storage_service import StudyStorageService +from antarest.study.storage.variantstudy.command_factory import CommandFactory +from antarest.study.storage.variantstudy.model.command.common import ( + CommandName, +) +from antarest.study.storage.variantstudy.model.command.update_config import ( + UpdateConfig, +) +from antarest.study.storage.variantstudy.model.command_context import ( + CommandContext, +) +from antarest.study.storage.variantstudy.variant_study_service import ( + VariantStudyService, +) +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker + + +class TestCorrelationField: + def test_init__nominal_case(self): + field = CorrelationField(area_id="NORTH", coefficient=100) + assert field.area_id == "NORTH" + assert field.coefficient == 100 + + def test_init__camel_case_args(self): + field = CorrelationField(areaId="NORTH", coefficient=100) + assert field.area_id == "NORTH" + assert field.coefficient == 100 + + +class TestCorrelationFormFields: + def test_init__nominal_case(self): + fields = CorrelationFormFields( + correlation=[ + {"area_id": "NORTH", "coefficient": 75}, + {"area_id": "SOUTH", "coefficient": 25}, + ] + ) + assert fields.correlation == [ + CorrelationField(area_id="NORTH", coefficient=75), + CorrelationField(area_id="SOUTH", coefficient=25), + ] + + def test_validation__coefficients_not_empty(self): + """correlation must not be empty""" + with pytest.raises(ValueError, match="must not be empty"): + CorrelationFormFields(correlation=[]) + + def test_validation__coefficients_no_duplicates(self): + """correlation must not contain duplicate area IDs:""" + with pytest.raises(ValueError, match="duplicate area IDs") as ctx: + CorrelationFormFields( + correlation=[ + {"area_id": "NORTH", "coefficient": 50}, + {"area_id": "NORTH", "coefficient": 25}, + {"area_id": "SOUTH", "coefficient": 25}, + ] + ) + assert "NORTH" in str(ctx.value) # duplicates + + @pytest.mark.parametrize("coefficient", [-101, 101, np.nan]) + def test_validation__coefficients_invalid_values(self, coefficient): + """coefficients must be between -100 and 100""" + with pytest.raises( + ValueError, match="between -100 and 100|must not contain NaN" + ): + CorrelationFormFields( + correlation=[ + {"area_id": "NORTH", "coefficient": coefficient}, + ] + ) + + +class TestCorrelationMatrix: + def test_init__nominal_case(self): + field = CorrelationMatrix( + index=["fr", "de"], + columns=["fr"], + data=[ + [1.0], + [0.2], + ], + ) + assert field.index == ["fr", "de"] + assert field.columns == ["fr"] + assert field.data == [ + [1.0], + [0.2], + ] + + def test_validation__coefficients_non_empty_array(self): + """Check that the coefficients matrix is a non-empty array""" + # fmt: off + with pytest.raises(ValueError, match="must not be empty"): + CorrelationMatrix( + index=[], + columns=[], + data=[], + ) + # fmt: off + + def test_validation__coefficients_array_shape(self): + """Check that the coefficients matrix is an array of shape 2×1""" + with pytest.raises(ValueError, match=r"must have shape \(\d+×\d+\)"): + CorrelationMatrix( + index=["fr", "de"], + columns=["fr"], + data=[[1, 2], [3, 4]], + ) + + @pytest.mark.parametrize("coefficient", [-1.1, 1.1, np.nan]) + def test_validation__coefficients_invalid_value(self, coefficient): + """Check that all coefficients matrix has positive or nul coefficients""" + # fmt: off + with pytest.raises(ValueError, match="between -1 and 1|must not contain NaN"): + CorrelationMatrix( + index=["fr", "de"], + columns=["fr", "de"], + data=[ + [1.0, coefficient], + [0.2, 0], + ], + ) + # fmt: on + + def test_validation__matrix_not_symmetric(self): + """Check that the correlation matrix is not symmetric""" + with pytest.raises(ValueError, match=r"not symmetric"): + CorrelationMatrix( + index=["fr", "de"], + columns=["fr", "de"], + data=[[0.1, 0.2], [0.3, 0.4]], + ) + + +@pytest.fixture(scope="function", name="db_engine") +def db_engine_fixture(): + engine = create_engine("sqlite:///:memory:") + Base.metadata.create_all(engine) + yield engine + engine.dispose() + + +@pytest.fixture(scope="function", name="db_session") +def db_session_fixture(db_engine): + make_session = sessionmaker(bind=db_engine) + with contextlib.closing(make_session()) as session: + yield session + + +# noinspection SpellCheckingInspection +EXECUTE_OR_ADD_COMMANDS = ( + "antarest.study.business.correlation_management.execute_or_add_commands" +) + + +class TestCorrelationManager: + @pytest.fixture(name="study_storage_service") + def study_storage_service(self) -> StudyStorageService: + """Return a mocked StudyStorageService.""" + return Mock( + spec=StudyStorageService, + variant_study_service=Mock( + spec=VariantStudyService, + command_factory=Mock( + spec=CommandFactory, + command_context=Mock(spec=CommandContext), + ), + ), + get_storage=Mock( + return_value=Mock( + spec=RawStudyService, get_raw=Mock(spec=FileStudy) + ) + ), + ) + + # noinspection PyArgumentList + @pytest.fixture(name="study_uuid") + def study_uuid_fixture(self, db_session) -> str: + user = User(id=0, name="admin") + group = Group(id="my-group", name="group") + raw_study = RawStudy( + id=str(uuid.uuid4()), + name="Dummy", + version="850", + author="John Smith", + created_at=datetime.datetime.now(datetime.timezone.utc), + updated_at=datetime.datetime.now(datetime.timezone.utc), + public_mode=PublicMode.FULL, + owner=user, + groups=[group], + workspace="default", + path="/path/to/study", + content_status=StudyContentStatus.WARNING, + ) + db_session.add(raw_study) + db_session.commit() + return raw_study.id + + def test_get_correlation_matrix__nominal_case( + self, db_session, study_storage_service, study_uuid + ): + # The study must be fetched from the database + study: RawStudy = db_session.query(Study).get(study_uuid) + + # Prepare the mocks + correlation_cfg = { + "n%n": 0.1, + "e%e": 0.3, + "s%s": 0.1, + "s%n": 0.2, + "s%w": 0.6, + "w%w": 0.1, + } + storage = study_storage_service.get_storage(study) + file_study = storage.get_raw(study) + file_study.tree = Mock( + spec=FileStudyTree, + get=Mock(return_value=correlation_cfg), + ) + + # Given the following arguments + all_areas = [ + AreaInfoDTO(id="n", name="North", type=AreaType.AREA), + AreaInfoDTO(id="e", name="East", type=AreaType.AREA), + AreaInfoDTO(id="s", name="South", type=AreaType.AREA), + AreaInfoDTO(id="w", name="West", type=AreaType.AREA), + ] + manager = CorrelationManager(study_storage_service) + + # run + matrix = manager.get_correlation_matrix( + all_areas=all_areas, study=study, columns=[] + ) + + # Check + assert matrix == CorrelationMatrix( + index=["n", "e", "s", "w"], + columns=["n", "e", "s", "w"], + data=[ + [1.0, 0.0, 0.2, 0.0], + [0.0, 1.0, 0.0, 0.0], + [0.2, 0.0, 1.0, 0.6], + [0.0, 0.0, 0.6, 1.0], + ], + ) + + def test_get_field_values__nominal_case( + self, db_session, study_storage_service, study_uuid + ): + # The study must be fetched from the database + study: RawStudy = db_session.query(Study).get(study_uuid) + + # Prepare the mocks + # NOTE: "s%s" value is ignored + correlation_cfg = {"s%s": 0.1, "n%s": 0.2, "w%n": 0.6} + storage = study_storage_service.get_storage(study) + file_study = storage.get_raw(study) + file_study.tree = Mock( + spec=FileStudyTree, + get=Mock(return_value=correlation_cfg), + ) + + # Given the following arguments + all_areas = [ + AreaInfoDTO(id="n", name="North", type=AreaType.AREA), + AreaInfoDTO(id="e", name="East", type=AreaType.AREA), + AreaInfoDTO(id="s", name="South", type=AreaType.AREA), + AreaInfoDTO(id="w", name="West", type=AreaType.AREA), + ] + area_id = "s" # South + manager = CorrelationManager(study_storage_service) + fields = manager.get_correlation_form_fields( + all_areas=all_areas, study=study, area_id=area_id + ) + assert fields == CorrelationFormFields( + correlation=[ + CorrelationField(area_id="n", coefficient=20.0), + CorrelationField(area_id="s", coefficient=100.0), + ] + ) + + def test_set_field_values__nominal_case( + self, db_session, study_storage_service, study_uuid + ): + # The study must be fetched from the database + study: RawStudy = db_session.query(Study).get(study_uuid) + + # Prepare the mocks: North + South + correlation_cfg = {} + storage = study_storage_service.get_storage(study) + file_study = storage.get_raw(study) + file_study.tree = Mock( + spec=FileStudyTree, + get=Mock(return_value=correlation_cfg), + ) + + # Given the following arguments + all_areas = [ + AreaInfoDTO(id="n", name="North", type=AreaType.AREA), + AreaInfoDTO(id="e", name="East", type=AreaType.AREA), + AreaInfoDTO(id="s", name="South", type=AreaType.AREA), + AreaInfoDTO(id="w", name="West", type=AreaType.AREA), + ] + area_id = "s" # South + manager = CorrelationManager(study_storage_service) + with patch(EXECUTE_OR_ADD_COMMANDS) as exe: + manager.set_correlation_form_fields( + all_areas=all_areas, + study=study, + area_id=area_id, + data=CorrelationFormFields( + correlation=[ + CorrelationField(area_id="s", coefficient=100), + CorrelationField(area_id="e", coefficient=30), + CorrelationField(area_id="n", coefficient=40), + ] + ), + ) + + # check update + assert exe.call_count == 1 + mock_call = exe.mock_calls[0] + # signature: execute_or_add_commands(study, file_study, commands, storage_service) + actual_study, _, actual_cmds, _ = mock_call.args + assert actual_study == study + assert len(actual_cmds) == 1 + cmd: UpdateConfig = actual_cmds[0] + assert cmd.command_name == CommandName.UPDATE_CONFIG + assert cmd.target == "input/hydro/prepro/correlation/annual" + assert cmd.data == {"e%s": 0.3, "n%s": 0.4} + + def test_set_field_values__area_not_found( + self, db_session, study_storage_service, study_uuid + ): + # The study must be fetched from the database + study: RawStudy = db_session.query(Study).get(study_uuid) + + # Prepare the mocks: North + South + correlation_cfg = {} + storage = study_storage_service.get_storage(study) + file_study = storage.get_raw(study) + file_study.tree = Mock( + spec=FileStudyTree, + get=Mock(return_value=correlation_cfg), + ) + + # Given the following arguments + all_areas = [ + AreaInfoDTO(id="n", name="North", type=AreaType.AREA), + AreaInfoDTO(id="e", name="East", type=AreaType.AREA), + AreaInfoDTO(id="s", name="South", type=AreaType.AREA), + AreaInfoDTO(id="w", name="West", type=AreaType.AREA), + ] + area_id = "n" # South + manager = CorrelationManager(study_storage_service) + + with patch(EXECUTE_OR_ADD_COMMANDS) as exe: + with pytest.raises(AreaNotFound) as ctx: + manager.set_correlation_form_fields( + all_areas=all_areas, + study=study, + area_id=area_id, + data=CorrelationFormFields( + correlation=[ + CorrelationField( + area_id="UNKNOWN", coefficient=3.14 + ), + ] + ), + ) + assert "'UNKNOWN'" in ctx.value.detail + exe.assert_not_called() diff --git a/tests/variantstudy/conftest.py b/tests/variantstudy/conftest.py index 77fe89bdf4..e9ad73b7d7 100644 --- a/tests/variantstudy/conftest.py +++ b/tests/variantstudy/conftest.py @@ -51,6 +51,7 @@ def matrix_service() -> MatrixService: @pytest.fixture def command_context(matrix_service: MatrixService) -> CommandContext: + # sourcery skip: inline-immediately-returned-variable command_context = CommandContext( generator_matrix_constants=GeneratorMatrixConstants( matrix_service=matrix_service @@ -75,10 +76,10 @@ def command_factory(matrix_service: MatrixService) -> CommandFactory: @pytest.fixture -def empty_study(tmp_path: str, matrix_service: MatrixService) -> FileStudy: +def empty_study(tmp_path: Path, matrix_service: MatrixService) -> FileStudy: project_dir: Path = Path(__file__).parent.parent.parent empty_study_path: Path = project_dir / "resources" / "empty_study_720.zip" - empty_study_destination_path = Path(tmp_path) / "empty-study" + empty_study_destination_path = tmp_path.joinpath("empty-study") with zipfile.ZipFile(empty_study_path, "r") as zip_empty_study: zip_empty_study.extractall(empty_study_destination_path) @@ -90,6 +91,7 @@ def empty_study(tmp_path: str, matrix_service: MatrixService) -> FileStudy: areas={}, sets={}, ) + # sourcery skip: inline-immediately-returned-variable file_study = FileStudy( config=config, tree=FileStudyTree( diff --git a/tests/variantstudy/model/command/test_remove_area.py b/tests/variantstudy/model/command/test_remove_area.py index 6fd8e646fb..b1f61f0bd9 100644 --- a/tests/variantstudy/model/command/test_remove_area.py +++ b/tests/variantstudy/model/command/test_remove_area.py @@ -1,10 +1,10 @@ -from checksumdir import dirhash +import pytest -from antarest.study.storage.rawstudy.io.reader import IniReader from antarest.study.storage.rawstudy.model.filesystem.config.model import ( transform_name_to_id, ) from antarest.study.storage.rawstudy.model.filesystem.factory import FileStudy +from antarest.study.storage.study_upgrader import upgrade_study from antarest.study.storage.variantstudy.model.command.common import ( TimeStep, BindingConstraintOperator, @@ -41,25 +41,14 @@ class TestRemoveArea: - def test_validation(self, empty_study: FileStudy): - pass - + @pytest.mark.parametrize("version", [810, 840]) def test_apply( self, empty_study: FileStudy, command_context: CommandContext, + version: int, ): - bd_config = IniReader().read( - empty_study.config.study_path - / "input" - / "bindingconstraints" - / "bindingconstraints.ini" - ) - - area_name = "Area" - area_id = transform_name_to_id(area_name) - area_name2 = "Area2" - area_id2 = transform_name_to_id(area_name2) + # noinspection SpellCheckingInspection empty_study.tree.save( { "input": { @@ -84,6 +73,8 @@ def test_apply( } ) + area_name = "Area" + area_id = transform_name_to_id(area_name) create_area_command: ICommand = CreateArea.parse_obj( { "area_name": area_name, @@ -93,14 +84,6 @@ def test_apply( output = create_area_command.apply(study_data=empty_study) assert output.status - parameters = { - "group": "Other", - "unitcount": "1", - "nominalcapacity": "1000000", - "marginal-cost": "30", - "market-bid-cost": "30", - } - create_district_command = CreateDistrict( name="foo", base_filter=DistrictBaseFilter.add_all, @@ -112,85 +95,99 @@ def test_apply( ######################################################################################## - empty_study_hash = dirhash(empty_study.config.study_path, "md5") + upgrade_study(empty_study.config.study_path, str(version)) - for version in [810, 840]: - empty_study.config.version = version - create_area_command: ICommand = CreateArea.parse_obj( - { - "area_name": area_name2, - "command_context": command_context, - } - ) - output = create_area_command.apply(study_data=empty_study) - assert output.status - - create_link_command: ICommand = CreateLink( - area1=area_id, - area2=area_id2, - parameters={}, - command_context=command_context, - series=[[0]], - ) - output = create_link_command.apply(study_data=empty_study) - assert output.status - - create_cluster_command = CreateCluster.parse_obj( - { - "area_id": area_id2, - "cluster_name": "cluster", - "parameters": parameters, - "prepro": [[0]], - "modulation": [[0]], - "command_context": command_context, - } - ) - output = create_cluster_command.apply(study_data=empty_study) - assert output.status - - bind1_cmd = CreateBindingConstraint( - name="BD 2", - time_step=TimeStep.HOURLY, - operator=BindingConstraintOperator.LESS, - coeffs={ - f"{area_id}%{area_id2}": [400, 30], - f"{area_id2}.cluster": [400, 30], + empty_study_cfg = empty_study.tree.get(depth=999) + if version >= 830: + empty_study_cfg["input"]["areas"][area_id]["adequacy_patch"] = { + "adequacy-patch": {"adequacy-patch-mode": "outside"} + } + empty_study_cfg["input"]["links"][area_id]["capacities"] = {} + + area_name2 = "Area2" + area_id2 = transform_name_to_id(area_name2) + + empty_study.config.version = version + create_area_command: ICommand = CreateArea.parse_obj( + { + "area_name": area_name2, + "command_context": command_context, + } + ) + output = create_area_command.apply(study_data=empty_study) + assert output.status + + create_link_command: ICommand = CreateLink( + area1=area_id, + area2=area_id2, + parameters={}, + command_context=command_context, + series=[[0]], + ) + output = create_link_command.apply(study_data=empty_study) + assert output.status + + # noinspection SpellCheckingInspection + create_cluster_command = CreateCluster.parse_obj( + { + "area_id": area_id2, + "cluster_name": "cluster", + "parameters": { + "group": "Other", + "unitcount": "1", + "nominalcapacity": "1000000", + "marginal-cost": "30", + "market-bid-cost": "30", }, - comments="Hello", - command_context=command_context, - ) - output = bind1_cmd.apply(study_data=empty_study) - assert output.status - - remove_district_command = RemoveDistrict( - id="foo", - command_context=command_context, - ) - output = remove_district_command.apply(study_data=empty_study) - assert output.status - - create_district_command = CreateDistrict( - name="foo", - base_filter=DistrictBaseFilter.add_all, - filter_items=[area_id, area_id2], - command_context=command_context, - ) - output = create_district_command.apply(study_data=empty_study) - assert output.status - - remove_area_command: ICommand = RemoveArea.parse_obj( - { - "id": transform_name_to_id(area_name2), - "command_context": command_context, - } - ) - output = remove_area_command.apply(study_data=empty_study) - assert output.status - - assert ( - dirhash(empty_study.config.study_path, "md5") - == empty_study_hash - ) + "prepro": [[0]], + "modulation": [[0]], + "command_context": command_context, + } + ) + output = create_cluster_command.apply(study_data=empty_study) + assert output.status + + bind1_cmd = CreateBindingConstraint( + name="BD 2", + time_step=TimeStep.HOURLY, + operator=BindingConstraintOperator.LESS, + coeffs={ + f"{area_id}%{area_id2}": [400, 30], + f"{area_id2}.cluster": [400, 30], + }, + comments="Hello", + command_context=command_context, + ) + output = bind1_cmd.apply(study_data=empty_study) + assert output.status + + remove_district_command = RemoveDistrict( + id="foo", + command_context=command_context, + ) + output = remove_district_command.apply(study_data=empty_study) + assert output.status + + create_district_command = CreateDistrict( + name="foo", + base_filter=DistrictBaseFilter.add_all, + filter_items=[area_id, area_id2], + command_context=command_context, + ) + output = create_district_command.apply(study_data=empty_study) + assert output.status + + remove_area_command: ICommand = RemoveArea.parse_obj( + { + "id": transform_name_to_id(area_name2), + "command_context": command_context, + } + ) + output = remove_area_command.apply(study_data=empty_study) + assert output.status + + actual_cfg = empty_study.tree.get(depth=999) + assert actual_cfg == empty_study_cfg def test_match(command_context: CommandContext): From 18f054c27eef2dcf319a28ffd331cc9f180bf212 Mon Sep 17 00:00:00 2001 From: Laurent LAPORTE <43534797+laurent-laporte-pro@users.noreply.github.com> Date: Fri, 14 Apr 2023 12:40:14 +0200 Subject: [PATCH 30/70] fix(api): correct the spatial correlation form update (#1449) --- .../study/business/correlation_management.py | 16 +++-- .../test_hydro_correlation.py | 58 ++++++++++++++++++- 2 files changed, 66 insertions(+), 8 deletions(-) diff --git a/antarest/study/business/correlation_management.py b/antarest/study/business/correlation_management.py index 763dbdc12d..db779fe103 100644 --- a/antarest/study/business/correlation_management.py +++ b/antarest/study/business/correlation_management.py @@ -272,18 +272,22 @@ def set_correlation_form_fields( Returns: The updated correlation coefficients. """ - correlation_ids = {field.area_id for field in data.correlation} area_ids = [area.id for area in all_areas] - if invalid_ids := correlation_ids - set(area_ids): + correlation_values = collections.OrderedDict.fromkeys(area_ids, 0.0) + correlation_values.update( + {field.area_id: field.coefficient for field in data.correlation} + ) + + if invalid_ids := set(correlation_values) - set(area_ids): # sort for deterministic error message and testing raise AreaNotFound(*sorted(invalid_ids)) file_study = self.storage_service.get_storage(study).get_raw(study) array = self._get_array(file_study, area_ids) - for field in data.correlation: - i = area_ids.index(field.area_id) - j = area_ids.index(area_id) - array[i][j] = field.coefficient / 100 + j = area_ids.index(area_id) + for i, coefficient in enumerate(correlation_values.values()): + array[i][j] = coefficient / 100 + array[j][i] = coefficient / 100 self._set_array(study, file_study, area_ids, array) column = array[:, area_ids.index(area_id)] * 100 diff --git a/tests/integration/study_data_blueprint/test_hydro_correlation.py b/tests/integration/study_data_blueprint/test_hydro_correlation.py index 83aac59495..a2542e8c82 100644 --- a/tests/integration/study_data_blueprint/test_hydro_correlation.py +++ b/tests/integration/study_data_blueprint/test_hydro_correlation.py @@ -50,8 +50,9 @@ def test_set_correlation_form_values( obj = { "correlation": [ {"areaId": "de", "coefficient": 20}, - {"areaId": "es", "coefficient": -80}, + {"areaId": "es", "coefficient": -82.8}, {"areaId": "it", "coefficient": 0}, + {"areaId": "fr", "coefficient": 100.0}, ] } res = client.put( @@ -64,12 +65,47 @@ def test_set_correlation_form_values( expected = { "correlation": [ {"areaId": "de", "coefficient": 20.0}, - {"areaId": "es", "coefficient": -80.0}, + {"areaId": "es", "coefficient": -82.8}, {"areaId": "fr", "coefficient": 100.0}, ] } assert actual == expected + # check that the form is updated correctly + res = client.get( + f"/v1/studies/{study_id}/areas/{area_id}/hydro/correlation/form", + headers={"Authorization": f"Bearer {user_access_token}"}, + ) + assert res.status_code == HTTPStatus.OK, res.json() + actual = res.json() + expected = { + "correlation": [ + {"areaId": "de", "coefficient": 20.0}, + {"areaId": "es", "coefficient": -82.8}, + {"areaId": "fr", "coefficient": 100.0}, + ] + } + assert actual == expected + + # check that the matrix is symmetric + res = client.get( + f"/v1/studies/{study_id}/areas/hydro/correlation/matrix", + headers={"Authorization": f"Bearer {user_access_token}"}, + ) + assert res.status_code == HTTPStatus.OK, res.json() + actual = res.json() + expected = { + "columns": ["de", "es", "fr", "it"], + "data": [ + [1.0, 0.0, 0.2, 0.0], + [0.0, 1.0, -0.828, 0.12], + [0.2, -0.828, 1.0, 0.0], + [0.0, 0.12, 0.0, 1.0], + ], + "index": ["de", "es", "fr", "it"], + } + assert actual == expected + @pytest.mark.parametrize( "columns, expected", [ @@ -162,6 +198,24 @@ def test_set_correlation_matrix( expected = obj assert actual == expected + res = client.get( + f"/v1/studies/{study_id}/areas/hydro/correlation/matrix", + headers={"Authorization": f"Bearer {user_access_token}"}, + ) + assert res.status_code == HTTPStatus.OK, res.json() + actual = res.json() + expected = { + "columns": ["de", "es", "fr", "it"], + "data": [ + [1.0, 0.0, -0.79332875, -0.96830414], + [0.0, 1.0, -0.23220568, -0.158783], + [-0.79332875, -0.23220568, 1.0, 0.82], + [-0.96830414, -0.158783, 0.82, 1.0], + ], + "index": ["de", "es", "fr", "it"], + } + assert actual == expected + def test_create_area( self, client: TestClient, user_access_token: str, study_id: str ): From 1dc830499dc494a49550887500ee391787f9af63 Mon Sep 17 00:00:00 2001 From: Laurent LAPORTE <43534797+laurent-laporte-pro@users.noreply.github.com> Date: Fri, 14 Apr 2023 16:26:40 +0200 Subject: [PATCH 31/70] fix(log): reduce size of Antares Web Worker log file (#1343) --- antarest/core/logging/utils.py | 65 ++++++++++++++-- antarest/worker/archive_worker_service.py | 55 ++++++++----- tests/worker/test_archive_worker_service.py | 86 +++++++++++++++++++++ 3 files changed, 177 insertions(+), 29 deletions(-) create mode 100644 tests/worker/test_archive_worker_service.py diff --git a/antarest/core/logging/utils.py b/antarest/core/logging/utils.py index 56a6e00fc3..b1e295f072 100644 --- a/antarest/core/logging/utils.py +++ b/antarest/core/logging/utils.py @@ -22,7 +22,27 @@ class CustomDefaultFormatter(logging.Formatter): + """ + A custom logging formatter that ensures all fields specified + in the format string are available in the log record. + + This formatter uses a regular expression pattern to extract + field names from the format string, and adds any missing + fields to the log record with a value of `None`. + """ + def format(self, record: logging.LogRecord) -> str: + """ + Formats the specified log record using the custom formatter, + ensuring all fields specified in the format string are available + in the record. Returns the formatted string. + + Args: + record: The logging record to format. + + Returns: + The formatted message. + """ arg_pattern = re.compile(r"%\((\w+)\)") arg_names = [x.group(1) for x in arg_pattern.finditer(self._fmt or "")] for field in arg_names: @@ -31,7 +51,17 @@ def format(self, record: logging.LogRecord) -> str: return super().format(record) -def configure_logger(config: Config) -> None: +def configure_logger( + config: Config, handler_cls: str = "logging.FileHandler" +) -> None: + """ + Set up the logging configuration based on the input `config` object + and an optional `handler_cls` argument. + + Args: + config: A `Config` object that contains the logging configuration parameters. + handler_cls: A string representing the class of the logging handler. + """ logging_config: Dict[str, Any] = { "version": 1, "disable_existing_loggers": False, @@ -91,13 +121,32 @@ def configure_logger(config: Config) -> None: }, } if config.logging.logfile is not None: - logging_config["handlers"]["default"] = { - "class": "logging.FileHandler", - "formatter": "console", - "level": "INFO", - "filename": config.logging.logfile, - "filters": ["context"], - } + if handler_cls == "logging.FileHandler": + logging_config["handlers"]["default"] = { + "class": handler_cls, + "formatter": "console", + "level": "INFO", + "filename": config.logging.logfile, + "filters": ["context"], + } + elif handler_cls == "logging.handlers.TimedRotatingFileHandler": + logging_config["handlers"]["default"] = { + "class": handler_cls, + "filename": config.logging.logfile, + "when": "D", # D = day + "interval": 90, # 90 days = 3 months + "backupCount": 1, # keep only 1 backup (0 means keep all) + "encoding": "utf-8", + "delay": False, + "utc": False, + "atTime": None, + "formatter": "console", + "level": "INFO", + "filters": ["context"], + } + else: # pragma: no cover + raise NotImplementedError(handler_cls) + if config.logging.level is not None and config.logging.level in [ "INFO", "WARNING", diff --git a/antarest/worker/archive_worker_service.py b/antarest/worker/archive_worker_service.py index fd044239e7..197bbd235e 100644 --- a/antarest/worker/archive_worker_service.py +++ b/antarest/worker/archive_worker_service.py @@ -1,30 +1,36 @@ import argparse -import sys +import logging from pathlib import Path +from typing import Optional, Sequence +from antarest import __version__ from antarest.core.config import Config from antarest.core.logging.utils import configure_logger from antarest.core.utils.utils import get_local_path -from antarest import __version__ from antarest.utils import create_archive_worker +# use the real module name instead of `__name__` (because `__name__ == "__main__"`) +logger = logging.getLogger("antarest.worker.archive_worker_service") + +ArgsType = Optional[Sequence[str]] -def parse_arguments() -> argparse.Namespace: + +def parse_arguments(args: ArgsType = None) -> argparse.Namespace: + version = f"%(prog)s {__version__}" parser = argparse.ArgumentParser() + parser.add_argument( + "-v", + "--version", + help="Display worker version and exit", + action="version", + version=version, + ) parser.add_argument( "-c", "--config", dest="config_file", help="path to the config file", ) - parser.add_argument( - "-v", - "--version", - dest="version", - help="Worker version", - action="store_true", - required=False, - ) parser.add_argument( "-w", "--workspace", @@ -34,24 +40,31 @@ def parse_arguments() -> argparse.Namespace: ) parser.add_argument( "-l", + "--local-root", "--local_root", dest="local_root", help="Define the local root path", required=False, ) - return parser.parse_args() + return parser.parse_args(args) -if __name__ == "__main__": +def run_archive_worker(args: ArgsType = None) -> None: res = get_local_path() / "resources" - args = parse_arguments() - if args.version: - print(__version__) - sys.exit() - config_file = Path(args.config_file) - local_root = Path(args.local_root or "/") - workspace = args.workspace + namespace = parse_arguments(args) + config_file = Path(namespace.config_file) + local_root = Path(namespace.local_root or "/") + workspace = namespace.workspace config = Config.from_yaml_file(res=res, file=config_file) - configure_logger(config) + # Handler for logging to a file, rotating the log file at certain timed intervals. + configure_logger( + config, handler_cls="logging.handlers.TimedRotatingFileHandler" + ) + logger.info(f"Starting Archive Worker for {namespace}...") worker = create_archive_worker(config, workspace, Path(local_root)) worker.start(threaded=False) + logger.info("Archive Worker task is done, bye.") + + +if __name__ == "__main__": + run_archive_worker() diff --git a/tests/worker/test_archive_worker_service.py b/tests/worker/test_archive_worker_service.py new file mode 100644 index 0000000000..b8fd4d2e33 --- /dev/null +++ b/tests/worker/test_archive_worker_service.py @@ -0,0 +1,86 @@ +from unittest.mock import Mock, patch + +import pytest +import yaml + +from antarest import __version__ +from antarest.worker.archive_worker import ArchiveWorker +from antarest.worker.archive_worker_service import run_archive_worker + + +def test_run_archive_worker__version(capsys): + with pytest.raises(SystemExit) as ctx: + run_archive_worker(["--version"]) + assert int(ctx.value.args[0]) == 0 + out, err = capsys.readouterr() + assert __version__ in out + + +def test_run_archive_worker__help(capsys): + with pytest.raises(SystemExit) as ctx: + run_archive_worker(["--help"]) + assert int(ctx.value.args[0]) == 0 + out, err = capsys.readouterr() + assert "CONFIG_FILE" in out + assert "WORKSPACE" in out + assert "LOCAL_ROOT" in out + + +WORKER_YAML = """\ +storage: + tmp_dir: /antarest_tmp_dir + archive_dir: /studies/archives + matrixstore: /matrixstore + matrix_gc_dry_run: true + workspaces: + default: + path: /studies/internal + common_space: + path: /mounts/common_spaces + +logging: + logfile: /path/to/worker.log + json: false + level: INFO + +redis: + host: redis-server + port: 6379 + password: '*****' +""" + + +def test_run_archive_worker__logging_setup(tmp_path): + """ + The purpose of this unit test is to check that the logging is set up correctly. + """ + # create a `worker.yaml` with the right log path + log_path = tmp_path.joinpath("worker.log") + obj = yaml.safe_load(WORKER_YAML) + obj["logging"]["logfile"] = str(log_path) + config_path = tmp_path.joinpath("worker.yaml") + with config_path.open(mode="w", encoding="utf-8") as fd: + yaml.dump(obj, fd) + + # do not start the worker: use a Mock instead + create_archive_worker = Mock() + create_archive_worker.return_value = Mock(spec=ArchiveWorker) + + # noinspection SpellCheckingInspection + with patch( + "antarest.worker.archive_worker_service.create_archive_worker", + new=create_archive_worker, + ): + run_archive_worker( + [ + f"--config={config_path}", + "--workspace=foo", + "--local-root=/path/to/local/root", + ] + ) + + # check: log file is generated with 2 messages + assert log_path.is_file() + lines = log_path.read_text(encoding="utf-8").splitlines() + assert "Starting Archive Worker" in lines[0] + assert "Archive Worker task is done" in lines[1] From 10715c3bfe00c4d1354a6d1d8679d908b9f00b8b Mon Sep 17 00:00:00 2001 From: hatim dinia Date: Mon, 17 Apr 2023 15:16:24 +0200 Subject: [PATCH 32/70] fix(ui-thermal): remove duplicate fields --- .../explore/Modelization/Areas/Thermal/Fields.tsx | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Thermal/Fields.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Thermal/Fields.tsx index 031cd8f285..a93d607298 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Thermal/Fields.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Thermal/Fields.tsx @@ -68,16 +68,6 @@ function Fields() { name="nominalCapacity" control={control} /> - - Date: Thu, 20 Apr 2023 09:36:18 +0200 Subject: [PATCH 33/70] test(hydro): improve hydro allocation unit tests (#1467) --- tests/integration/__init__.py | 0 .../test_hydro_allocation.py | 60 ++++++++++++++++++- tests/integration/test_integration.py | 26 +++----- tests/integration/utils.py | 15 +++++ 4 files changed, 82 insertions(+), 19 deletions(-) create mode 100644 tests/integration/__init__.py create mode 100644 tests/integration/utils.py diff --git a/tests/integration/__init__.py b/tests/integration/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/integration/study_data_blueprint/test_hydro_allocation.py b/tests/integration/study_data_blueprint/test_hydro_allocation.py index e9694e5f42..fa3d1a428f 100644 --- a/tests/integration/study_data_blueprint/test_hydro_allocation.py +++ b/tests/integration/study_data_blueprint/test_hydro_allocation.py @@ -2,9 +2,11 @@ from typing import List import pytest -from antarest.study.business.area_management import AreaInfoDTO from starlette.testclient import TestClient +from antarest.study.business.area_management import AreaInfoDTO +from tests.integration.utils import wait_for + @pytest.mark.unit_test class TestHydroAllocation: @@ -32,6 +34,62 @@ def test_get_allocation_form_values( expected = {"allocation": [{"areaId": "de", "coefficient": 1.0}]} assert actual == expected + def test_get_allocation_form_values__variant( + self, + client: TestClient, + user_access_token: str, + study_id: str, + ): + """ + The purpose of this test is to check that we can get the form parameters from a study variant. + To prepare this test, we start from a RAW study, copy it to the managed study workspace + and then create a variant from this managed workspace. + """ + # Execute the job to copy the study to the workspace + res = client.post( + f"/v1/studies/{study_id}/copy?dest=Clone&with_outputs=false", + headers={"Authorization": f"Bearer {user_access_token}"}, + ) + res.raise_for_status() + task_id = res.json() + + # wait for the job to finish + def copy_task_done() -> bool: + r = client.get( + f"/v1/tasks/{task_id}", + headers={"Authorization": f"Bearer {user_access_token}"}, + ) + return r.json()["status"] == 3 + + wait_for(copy_task_done, sleep_time=0.2) + + # Get the job result to retrieve the study ID + res = client.get( + f"/v1/tasks/{task_id}", + headers={"Authorization": f"Bearer {user_access_token}"}, + ) + res.raise_for_status() + managed_id = res.json()["result"]["return_value"] + + # create a variant study from the managed study + res = client.post( + f"/v1/studies/{managed_id}/variants?name=foo", + headers={"Authorization": f"Bearer {user_access_token}"}, + ) + res.raise_for_status() + variant_id = res.json() + + # get allocation form + area_id = "de" + res = client.get( + f"/v1/studies/{variant_id}/areas/{area_id}/hydro/allocation/form", + headers={"Authorization": f"Bearer {user_access_token}"}, + ) + res.raise_for_status() + actual = res.json() + expected = {"allocation": [{"areaId": "de", "coefficient": 1.0}]} + assert actual == expected + @pytest.mark.parametrize( "area_id, expected", [ diff --git a/tests/integration/test_integration.py b/tests/integration/test_integration.py index 2b6ffc56a2..217490775c 100644 --- a/tests/integration/test_integration.py +++ b/tests/integration/test_integration.py @@ -1,46 +1,36 @@ -import contextlib import time from pathlib import Path -from typing import Callable from unittest.mock import ANY +from fastapi import FastAPI +from starlette.testclient import TestClient + from antarest.core.tasks.model import TaskDTO, TaskStatus from antarest.study.business.adequacy_patch_management import PriceTakingOrder from antarest.study.business.area_management import AreaType, LayerInfoDTO from antarest.study.business.general_management import Mode from antarest.study.business.optimization_management import ( + SimplexOptimizationRange, TransmissionCapacities, UnfeasibleProblemBehavior, - SimplexOptimizationRange, ) from antarest.study.business.table_mode_management import ( FIELDS_INFO_BY_TYPE, AdequacyPatchMode, AssetType, + BindingConstraintOperator, + BindingConstraintType, LawOption, TableTemplateType, TimeSeriesGenerationOption, - TransmissionCapacity, TimeSeriesInterpretation, - BindingConstraintType, - BindingConstraintOperator, + TransmissionCapacity, ) from antarest.study.model import MatrixIndex, StudyDownloadLevelDTO from antarest.study.storage.variantstudy.model.command.common import ( CommandName, ) -from fastapi import FastAPI -from starlette.testclient import TestClient - - -def wait_for(predicate: Callable[[], bool], timeout=10): - end = time.time() + timeout - while time.time() < end: - with contextlib.suppress(Exception): - if predicate(): - return - time.sleep(1) - raise TimeoutError() +from tests.integration.utils import wait_for def init_test(app: FastAPI): diff --git a/tests/integration/utils.py b/tests/integration/utils.py new file mode 100644 index 0000000000..86d7759285 --- /dev/null +++ b/tests/integration/utils.py @@ -0,0 +1,15 @@ +import contextlib +import time +from typing import Callable + + +def wait_for( + predicate: Callable[[], bool], timeout: float = 10, sleep_time: float = 1 +) -> None: + end = time.time() + timeout + while time.time() < end: + with contextlib.suppress(Exception): + if predicate(): + return + time.sleep(sleep_time) + raise TimeoutError(f"task is still in progress after {timeout} seconds") From c824e1f6d73679c0469cae254a8eca1796e5a724 Mon Sep 17 00:00:00 2001 From: Hatim Dinia Date: Thu, 20 Apr 2023 13:17:31 +0200 Subject: [PATCH 34/70] feat(ui-hydro): add correlation form (#1464) --- webapp/public/locales/en/main.json | 2 +- webapp/public/locales/fr/main.json | 2 +- .../Hydro/Allocation/AllocationField.tsx | 18 ++--- .../Hydro/Allocation/AllocationSelect.tsx | 41 ----------- .../Areas/Hydro/Allocation/Fields.tsx | 72 +++++++------------ .../Areas/Hydro/Allocation/utils.ts | 8 +-- .../Hydro/Correlation/CorrelationField.tsx | 58 +++++++++++++++ .../Areas/Hydro/Correlation/Fields.tsx | 53 ++++++++++++++ .../Areas/Hydro/Correlation/index.tsx | 65 +++++++++++++++++ .../Areas/Hydro/Correlation/utils.ts | 39 ++++++++++ .../Areas/Hydro/hooks/useAreasOptions.ts | 29 ++++++++ .../Modelization/Areas/Hydro/index.tsx | 4 ++ .../explore/Modelization/Areas/Hydro/utils.ts | 5 ++ webapp/src/components/App/index.tsx | 5 ++ webapp/src/components/common/DynamicList.tsx | 69 ++++++++++++++++++ webapp/src/redux/selectors.ts | 10 +++ 16 files changed, 373 insertions(+), 107 deletions(-) delete mode 100644 webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/AllocationSelect.tsx create mode 100644 webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Correlation/CorrelationField.tsx create mode 100644 webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Correlation/Fields.tsx create mode 100644 webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Correlation/index.tsx create mode 100644 webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Correlation/utils.ts create mode 100644 webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/hooks/useAreasOptions.ts create mode 100644 webapp/src/components/common/DynamicList.tsx diff --git a/webapp/public/locales/en/main.json b/webapp/public/locales/en/main.json index 86b81b9006..b1b98f126d 100644 --- a/webapp/public/locales/en/main.json +++ b/webapp/public/locales/en/main.json @@ -72,6 +72,7 @@ "global.error.failedtoretrievejobs": "Failed to retrieve job information", "global.error.failedtoretrievelogs": "Failed to retrieve job logs", "global.error.failedtoretrievedownloads": "Failed to retrieve downloads list", + "global.area.add": "Add an area", "login.error": "Failed to authenticate", "tasks.title": "Tasks", "api.title": "API", @@ -393,7 +394,6 @@ "study.modelization.load": "Load", "study.modelization.thermal": "Thermal Clus.", "study.modelization.hydro": "Hydro", - "study.modelization.hydro.allocation.select": "Add an area", "study.modelization.hydro.allocation.error.field.delete": "Error when deleting the allocation", "study.modelization.wind": "Wind", "study.modelization.solar": "Solar", diff --git a/webapp/public/locales/fr/main.json b/webapp/public/locales/fr/main.json index 90856a0405..8a38cd2ba0 100644 --- a/webapp/public/locales/fr/main.json +++ b/webapp/public/locales/fr/main.json @@ -72,6 +72,7 @@ "global.error.failedtoretrievejobs": "Échec de la récupération des tâches", "global.error.failedtoretrievelogs": "Échec de la récupération des logs", "global.error.failedtoretrievedownloads": "Échec de la récupération des exports", + "global.area.add": "Ajouter une zone", "login.error": "Échec de l'authentification", "tasks.title": "Tâches", "api.title": "API", @@ -393,7 +394,6 @@ "study.modelization.load": "Conso", "study.modelization.thermal": "Clus. Thermiques", "study.modelization.hydro": "Hydro", - "study.modelization.hydro.allocation.select": "Ajouter une zone", "study.modelization.hydro.allocation.error.field.delete": "Erreur lors de la suppression de l'allocation", "study.modelization.wind": "Éolien", "study.modelization.solar": "Solaire", diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/AllocationField.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/AllocationField.tsx index c6b46d5da9..3894aa07ff 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/AllocationField.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/AllocationField.tsx @@ -1,7 +1,6 @@ -import { Typography, IconButton, Grid } from "@mui/material"; -import RemoveCircleOutlineIcon from "@mui/icons-material/RemoveCircleOutline"; +import { Typography, Grid } from "@mui/material"; import { t } from "i18next"; -import { FieldArrayWithId, UseFieldArrayRemove } from "react-hook-form"; +import { FieldArrayWithId } from "react-hook-form"; import NumberFE from "../../../../../../../common/fieldEditors/NumberFE"; import { useFormContextPlus } from "../../../../../../../common/Form"; import { AllocationFormFields } from "./utils"; @@ -10,11 +9,9 @@ interface Props { field: FieldArrayWithId; index: number; label: string; - remove: UseFieldArrayRemove; - fieldsLength: number; } -function AllocationField({ field, index, label, remove, fieldsLength }: Props) { +function AllocationField({ field, index, label }: Props) { const { control } = useFormContextPlus(); //////////////////////////////////////////////////////////////// @@ -22,7 +19,7 @@ function AllocationField({ field, index, label, remove, fieldsLength }: Props) { //////////////////////////////////////////////////////////////// return ( - + <> - - remove(index)} disabled={fieldsLength === 1}> - - - - + ); } diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/AllocationSelect.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/AllocationSelect.tsx deleted file mode 100644 index 971ac5be2f..0000000000 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/AllocationSelect.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { t } from "i18next"; -import { useMemo } from "react"; -import { UseFieldArrayAppend } from "react-hook-form"; -import { Area } from "../../../../../../../../common/types"; -import SelectFE from "../../../../../../../common/fieldEditors/SelectFE"; -import { AllocationFormFields } from "./utils"; - -interface Props { - filteredAreas: Array; - append: UseFieldArrayAppend; -} - -function AllocationSelect({ filteredAreas, append }: Props) { - const options = useMemo( - () => - filteredAreas.map((area) => ({ - label: area.name, - value: area.id, - })), - [filteredAreas] - ); - - //////////////////////////////////////////////////////////////// - // JSX - //////////////////////////////////////////////////////////////// - - return filteredAreas.length > 0 ? ( - { - append({ areaId: e.target.value as string, coefficient: 0 }); - }} - size="small" - variant="outlined" - sx={{ width: 200 }} - /> - ) : null; -} - -export default AllocationSelect; diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/Fields.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/Fields.tsx index 8a0a154621..e6ed81c9f6 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/Fields.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/Fields.tsx @@ -1,70 +1,52 @@ -import { Divider, Grid } from "@mui/material"; import { useFieldArray } from "react-hook-form"; import { useOutletContext } from "react-router"; -import { useMemo } from "react"; import { useFormContextPlus } from "../../../../../../../common/Form"; -import useAppSelector from "../../../../../../../../redux/hooks/useAppSelector"; -import { - getAreas, - getStudySynthesis, -} from "../../../../../../../../redux/selectors"; -import { StudyMetadata } from "../../../../../../../../common/types"; import { AllocationFormFields } from "./utils"; -import AllocationSelect from "./AllocationSelect"; import AllocationField from "./AllocationField"; +import DynamicList from "../../../../../../../common/DynamicList"; +import useAppSelector from "../../../../../../../../redux/hooks/useAppSelector"; +import { getAreasById } from "../../../../../../../../redux/selectors"; +import { StudyMetadata } from "../../../../../../../../common/types"; +import { useAreasOptions } from "../hooks/useAreasOptions"; function Fields() { const { study: { id: studyId }, } = useOutletContext<{ study: StudyMetadata }>(); + const areasById = useAppSelector((state) => getAreasById(state, studyId)); const { control } = useFormContextPlus(); const { fields, append, remove } = useFieldArray({ control, name: "allocation", }); - const areas = useAppSelector((state) => getAreas(state, studyId)); - const areasById = useAppSelector( - (state) => getStudySynthesis(state, studyId)?.areas - ); - - const filteredAreas = useMemo(() => { - const allocatedAreaIds = fields.map((field) => field.areaId); - return areas.filter((area) => !allocatedAreaIds.includes(area.id)); - }, [areas, fields]); - - //////////////////////////////////////////////////////////////// - // Utils - //////////////////////////////////////////////////////////////// - const getAreaLabel = (areaId: string) => { - return areasById?.[areaId]?.name ?? ""; - }; + const options = useAreasOptions(fields); //////////////////////////////////////////////////////////////// // JSX //////////////////////////////////////////////////////////////// return ( - - - {fields.map((field, index) => ( - - ))} - - - - - - - - + ( + + )} + options={options} + onAdd={(value) => + append({ + areaId: value, + coefficient: 0, + }) + } + onDelete={remove} + allowEmpty={false} + /> ); } diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/utils.ts b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/utils.ts index 729b7d3660..60a8ffcebb 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/utils.ts +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/utils.ts @@ -1,17 +1,13 @@ import { StudyMetadata, Area } from "../../../../../../../../common/types"; import client from "../../../../../../../../services/api/client"; +import { AreaCoefficientItem } from "../utils"; //////////////////////////////////////////////////////////////// // Types //////////////////////////////////////////////////////////////// -export interface AllocationField { - areaId: string; - coefficient: number; -} - export interface AllocationFormFields { - allocation: AllocationField[]; + allocation: AreaCoefficientItem[]; } //////////////////////////////////////////////////////////////// diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Correlation/CorrelationField.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Correlation/CorrelationField.tsx new file mode 100644 index 0000000000..5c6b88258c --- /dev/null +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Correlation/CorrelationField.tsx @@ -0,0 +1,58 @@ +import { Typography, Grid } from "@mui/material"; +import { FieldArrayWithId } from "react-hook-form"; +import { useTranslation } from "react-i18next"; +import NumberFE from "../../../../../../../common/fieldEditors/NumberFE"; +import { CorrelationFormFields } from "./utils"; +import { useFormContextPlus } from "../../../../../../../common/Form"; + +interface Props { + field: FieldArrayWithId; + index: number; + label: string; +} + +// TODO merge with AllocationField +function CorrelationField({ field, index, label }: Props) { + const { control } = useFormContextPlus(); + const { t } = useTranslation(); + + //////////////////////////////////////////////////////////////// + // JSX + //////////////////////////////////////////////////////////////// + + return ( + <> + + + {label} + + + + + + + ); +} + +export default CorrelationField; diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Correlation/Fields.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Correlation/Fields.tsx new file mode 100644 index 0000000000..1c252a8e70 --- /dev/null +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Correlation/Fields.tsx @@ -0,0 +1,53 @@ +import { useFieldArray } from "react-hook-form"; +import { useOutletContext } from "react-router"; +import { StudyMetadata } from "../../../../../../../../common/types"; +import useAppSelector from "../../../../../../../../redux/hooks/useAppSelector"; +import { getAreasById } from "../../../../../../../../redux/selectors"; +import DynamicList from "../../../../../../../common/DynamicList"; +import { useFormContextPlus } from "../../../../../../../common/Form"; +import { useAreasOptions } from "../hooks/useAreasOptions"; +import CorrelationField from "./CorrelationField"; +import { CorrelationFormFields } from "./utils"; + +function Fields() { + const { + study: { id: studyId }, + } = useOutletContext<{ study: StudyMetadata }>(); + const areasById = useAppSelector((state) => getAreasById(state, studyId)); + const { control } = useFormContextPlus(); + const { fields, append, remove } = useFieldArray({ + control, + name: "correlation", + }); + + const options = useAreasOptions(fields); + + //////////////////////////////////////////////////////////////// + // JSX + //////////////////////////////////////////////////////////////// + + return ( + ( + + )} + options={options} + onAdd={(value: string) => + append({ + areaId: value, + coefficient: 0, + }) + } + onDelete={remove} + allowEmpty={false} + /> + ); +} + +export default Fields; diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Correlation/index.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Correlation/index.tsx new file mode 100644 index 0000000000..3edbfdad6d --- /dev/null +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Correlation/index.tsx @@ -0,0 +1,65 @@ +import { Box, Paper } from "@mui/material"; +import { useOutletContext } from "react-router"; +import Form from "../../../../../../../common/Form"; +import { StudyMetadata } from "../../../../../../../../common/types"; +import useAppSelector from "../../../../../../../../redux/hooks/useAppSelector"; +import { getCurrentAreaId } from "../../../../../../../../redux/selectors"; +import { SubmitHandlerPlus } from "../../../../../../../common/Form/types"; +import { + CorrelationFormFields, + getCorrelationFormFields, + setCorrelationFormFields, +} from "./utils"; +import Fields from "./Fields"; + +function Correlation() { + const { + study: { id: studyId }, + } = useOutletContext<{ study: StudyMetadata }>(); + const areaId = useAppSelector(getCurrentAreaId); + + //////////////////////////////////////////////////////////////// + // Event handlers + //////////////////////////////////////////////////////////////// + + const handleSubmit = (data: SubmitHandlerPlus) => { + return setCorrelationFormFields(studyId, areaId, { + correlation: data.values.correlation, + }); + }; + + //////////////////////////////////////////////////////////////// + // JSX + //////////////////////////////////////////////////////////////// + + return ( + + +
getCorrelationFormFields(studyId, areaId), + }} + onSubmit={handleSubmit} + sx={{ p: 3 }} + > + + +
+
+ ); +} + +export default Correlation; diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Correlation/utils.ts b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Correlation/utils.ts new file mode 100644 index 0000000000..6c9a945961 --- /dev/null +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Correlation/utils.ts @@ -0,0 +1,39 @@ +import { StudyMetadata, Area } from "../../../../../../../../common/types"; +import client from "../../../../../../../../services/api/client"; +import { AreaCoefficientItem } from "../utils"; + +//////////////////////////////////////////////////////////////// +// Types +//////////////////////////////////////////////////////////////// + +export interface CorrelationFormFields { + correlation: AreaCoefficientItem[]; +} + +//////////////////////////////////////////////////////////////// +// Utils +//////////////////////////////////////////////////////////////// + +function makeRequestURL( + studyId: StudyMetadata["id"], + areaId: Area["name"] +): string { + return `v1/studies/${studyId}/areas/${areaId}/hydro/correlation/form`; +} + +export async function getCorrelationFormFields( + studyId: StudyMetadata["id"], + areaId: Area["name"] +): Promise { + const res = await client.get(makeRequestURL(studyId, areaId)); + return res.data; +} + +export async function setCorrelationFormFields( + studyId: StudyMetadata["id"], + areaId: Area["name"], + values: CorrelationFormFields +): Promise { + const res = await client.put(makeRequestURL(studyId, areaId), values); + return res.data; +} diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/hooks/useAreasOptions.ts b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/hooks/useAreasOptions.ts new file mode 100644 index 0000000000..c3edefe0ad --- /dev/null +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/hooks/useAreasOptions.ts @@ -0,0 +1,29 @@ +import { useMemo } from "react"; +import { useOutletContext } from "react-router"; +import { StudyMetadata } from "../../../../../../../../common/types"; +import useAppSelector from "../../../../../../../../redux/hooks/useAppSelector"; +import { getAreas } from "../../../../../../../../redux/selectors"; +import { DynamicListProps } from "../../../../../../../common/DynamicList"; +import { AreaCoefficientItem } from "../utils"; + +export function useAreasOptions( + fields: AreaCoefficientItem[] +): DynamicListProps["options"] { + const { + study: { id: studyId }, + } = useOutletContext<{ study: StudyMetadata }>(); + + const areas = useAppSelector((state) => getAreas(state, studyId)); + + const options = useMemo(() => { + const areaIds = fields.map((field) => field.areaId); + return areas + .filter((area) => !areaIds.includes(area.id)) + .map((area) => ({ + label: area.name, + value: area.id, + })); + }, [areas, fields]); + + return options; +} diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/index.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/index.tsx index ed92c9cbd4..973b5aa1c6 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/index.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/index.tsx @@ -23,6 +23,10 @@ function Hydro() { label: "Allocation", path: `/studies/${study?.id}/explore/modelization/area/hydro/allocation`, }, + { + label: "Correlation", + path: `/studies/${study?.id}/explore/modelization/area/hydro/correlation`, + }, { label: "Daily Power", path: `/studies/${study?.id}/explore/modelization/area/hydro/dailypower`, diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/utils.ts b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/utils.ts index 0a08517da2..2cc92c520e 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/utils.ts +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/utils.ts @@ -34,6 +34,11 @@ export interface HydroRoute { type: number; } +export interface AreaCoefficientItem { + areaId: string; + coefficient: number; +} + //////////////////////////////////////////////////////////////// // Constants //////////////////////////////////////////////////////////////// diff --git a/webapp/src/components/App/index.tsx b/webapp/src/components/App/index.tsx index 0231421459..58e56f58ee 100644 --- a/webapp/src/components/App/index.tsx +++ b/webapp/src/components/App/index.tsx @@ -52,6 +52,7 @@ import Layers from "./Singlestudy/explore/Modelization/Map/MapConfig/Layers"; import Districts from "./Singlestudy/explore/Modelization/Map/MapConfig/Districts"; import InflowStructure from "./Singlestudy/explore/Modelization/Areas/Hydro/InflowStructure"; import Allocation from "./Singlestudy/explore/Modelization/Areas/Hydro/Allocation"; +import Correlation from "./Singlestudy/explore/Modelization/Areas/Hydro/Correlation"; function App() { return ( @@ -97,6 +98,10 @@ function App() { path="allocation" element={} /> + } + /> {HYDRO_ROUTES.map((route: HydroRoute) => ( { + items: T[]; + renderItem: (item: T, index: number) => React.ReactNode; + options: ListOption[]; + onAdd: (value: string) => void; + onDelete: (index: number) => void; + allowEmpty?: boolean; +} + +function DynamicList({ + items, + renderItem, + options, + onAdd, + onDelete, + allowEmpty = true, +}: DynamicListProps) { + const disableDelete = items.length === 1 && !allowEmpty; + + return ( + + + {items.map((item, index) => ( + + + {renderItem(item, index)} + + onDelete(index)} + disabled={disableDelete} + > + + + + + + ))} + + + + + + {options.length > 0 && ( + onAdd(e.target.value as string)} + size="small" + variant="outlined" + sx={{ width: 200, mb: 2 }} + /> + )} + + + ); +} + +export default DynamicList; diff --git a/webapp/src/redux/selectors.ts b/webapp/src/redux/selectors.ts index 268c9f0be6..d7fa749a0a 100644 --- a/webapp/src/redux/selectors.ts +++ b/webapp/src/redux/selectors.ts @@ -214,6 +214,16 @@ export const getAreas = createSelector(getStudySynthesis, (synthesis) => { return []; }); +export const getAreasById = createSelector(getStudySynthesis, (synthesis) => { + if (synthesis) { + return Object.keys(synthesis.areas).reduce((acc, id) => { + acc[id] = { ...synthesis.areas[id], id }; + return acc; + }, {} as Record); + } + return {}; +}); + export const getArea = createSelector( getStudySynthesis, (state: AppState, studyId: StudyMetadata["id"], areaId: string) => areaId, From 25af0ff4b30c2ea8514332bae7b77293ba6ec497 Mon Sep 17 00:00:00 2001 From: Samir Kamal <1954121+skamril@users.noreply.github.com> Date: Thu, 27 Apr 2023 12:36:14 +0200 Subject: [PATCH 35/70] fix(api): add validation for day fields in general form --- antarest/study/business/general_management.py | 33 +++++++++++++++++-- tests/integration/test_integration.py | 3 +- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/antarest/study/business/general_management.py b/antarest/study/business/general_management.py index a7c2797f82..46f0dfbb64 100644 --- a/antarest/study/business/general_management.py +++ b/antarest/study/business/general_management.py @@ -1,7 +1,7 @@ from enum import Enum -from typing import Optional, Dict, Any, List +from typing import Optional, Dict, Any, List, cast -from pydantic import StrictBool, conint, PositiveInt +from pydantic import StrictBool, conint, PositiveInt, root_validator from antarest.study.business.utils import ( FormFieldsBaseModel, @@ -57,7 +57,7 @@ class BuildingMode(str, Enum): DERATED = "Derated" -DayNumberType = conint(ge=1, le=365) +DayNumberType = conint(ge=1, le=366) class GeneralFormFields(FormFieldsBaseModel): @@ -82,6 +82,33 @@ class GeneralFormFields(FormFieldsBaseModel): geographic_trimming: Optional[StrictBool] thematic_trimming: Optional[StrictBool] + @root_validator + def day_fields_validation(cls, values: Dict[str, Any]) -> Dict[str, Any]: + first_day = values.get("first_day") + last_day = values.get("last_day") + leap_year = values.get("leap_year") + + if any(v is None for v in [first_day, last_day, leap_year]): + raise ValueError( + "First day, last day and leap year fields must be defined together" + ) + + first_day = cast(int, first_day) + last_day = cast(int, last_day) + leap_year = cast(bool, leap_year) + num_days_in_year = 366 if leap_year else 365 + + if first_day > last_day: + raise ValueError( + "Last day must be greater than or equal to the first day" + ) + if last_day > num_days_in_year: + raise ValueError( + f"Last day cannot be greater than {num_days_in_year}" + ) + + return values + GENERAL = "general" OUTPUT = "output" diff --git a/tests/integration/test_integration.py b/tests/integration/test_integration.py index 217490775c..f0bc853921 100644 --- a/tests/integration/test_integration.py +++ b/tests/integration/test_integration.py @@ -1131,6 +1131,7 @@ def test_area_management(app: FastAPI): }, json={ "mode": Mode.ADEQUACY.value, + "firstDay": 2, "lastDay": 299, "leapYear": True, }, @@ -1144,7 +1145,7 @@ def test_area_management(app: FastAPI): res_general_config_json = res_general_config.json() assert res_general_config_json == { "mode": Mode.ADEQUACY.value, - "firstDay": 1, + "firstDay": 2, "lastDay": 299, "horizon": "", "firstMonth": "january", From 1c63d2e8242a38dbd2ed399ab0e2cfa2a8a4f0f7 Mon Sep 17 00:00:00 2001 From: Samir Kamal <1954121+skamril@users.noreply.github.com> Date: Wed, 19 Apr 2023 19:18:30 +0200 Subject: [PATCH 36/70] fix(config): update day fields validation in general form --- .../explore/Configuration/General/Fields.tsx | 17 +++++++++-------- .../explore/Configuration/General/index.tsx | 19 ++++++++++++++----- .../explore/Configuration/General/utils.ts | 13 +++++++++++++ 3 files changed, 36 insertions(+), 13 deletions(-) diff --git a/webapp/src/components/App/Singlestudy/explore/Configuration/General/Fields.tsx b/webapp/src/components/App/Singlestudy/explore/Configuration/General/Fields.tsx index 16e025935d..9c3b822014 100644 --- a/webapp/src/components/App/Singlestudy/explore/Configuration/General/Fields.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Configuration/General/Fields.tsx @@ -18,7 +18,6 @@ import { } from "./utils"; import BooleanFE from "../../../../../common/fieldEditors/BooleanFE"; import { useFormContextPlus } from "../../../../../common/Form"; -import useDebouncedEffect from "../../../../../../hooks/useDebouncedEffect"; import StringFE from "../../../../../common/fieldEditors/StringFE"; import NumberFE from "../../../../../common/fieldEditors/NumberFE"; import Fieldset from "../../../../../common/Fieldset"; @@ -57,22 +56,24 @@ function Fields(props: Props) { } }, [buildingMode, setValue]); - useDebouncedEffect( + useEffect( () => { - if (firstDay > 0 && firstDay > lastDay) { + if (firstDay > 0 && firstDay <= 366 && firstDay > lastDay) { setValue("lastDay", firstDay); } }, - { wait: 500, deps: [firstDay] } + // eslint-disable-next-line react-hooks/exhaustive-deps + [firstDay] ); - useDebouncedEffect( + useEffect( () => { - if (lastDay > 0 && lastDay < firstDay) { + if (lastDay > 0 && lastDay <= 366 && lastDay < firstDay) { setValue("firstDay", lastDay); } }, - { wait: 500, deps: [lastDay] } + // eslint-disable-next-line react-hooks/exhaustive-deps + [lastDay] ); //////////////////////////////////////////////////////////////// @@ -84,7 +85,7 @@ function Fields(props: Props) { formValues ) => { if (value < 1 || Number.isNaN(value)) { - return "Minimum is 1"; + return t("form.field.minValue", [1]); } if (formValues.firstDay > formValues.lastDay) { return false; diff --git a/webapp/src/components/App/Singlestudy/explore/Configuration/General/index.tsx b/webapp/src/components/App/Singlestudy/explore/Configuration/General/index.tsx index b3c7e3303c..e75d791b29 100644 --- a/webapp/src/components/App/Singlestudy/explore/Configuration/General/index.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Configuration/General/index.tsx @@ -9,6 +9,8 @@ import ScenarioPlaylistDialog from "./dialogs/ScenarioPlaylistDialog"; import { GeneralFormFields, getGeneralFormFields, + hasDayField, + pickDayFields, SetDialogStateType, setGeneralFormFields, } from "./utils"; @@ -27,14 +29,21 @@ function GeneralParameters() { // Event Handlers //////////////////////////////////////////////////////////////// - const handleSubmit = async (data: SubmitHandlerPlus) => { - return setGeneralFormFields(study.id, data.dirtyValues); - }; + const handleSubmit = (data: SubmitHandlerPlus) => { + const { values, dirtyValues } = data; + const newValues = hasDayField(dirtyValues) + ? { + ...dirtyValues, + // Required by server to validate values + ...pickDayFields(values), + } + : dirtyValues; - const handleCloseDialog = () => { - setDialog(""); + return setGeneralFormFields(study.id, newValues); }; + const handleCloseDialog = () => setDialog(""); + //////////////////////////////////////////////////////////////// // JSX //////////////////////////////////////////////////////////////// diff --git a/webapp/src/components/App/Singlestudy/explore/Configuration/General/utils.ts b/webapp/src/components/App/Singlestudy/explore/Configuration/General/utils.ts index 0949a2bb3f..9cee4db19c 100644 --- a/webapp/src/components/App/Singlestudy/explore/Configuration/General/utils.ts +++ b/webapp/src/components/App/Singlestudy/explore/Configuration/General/utils.ts @@ -1,3 +1,4 @@ +import * as R from "ramda"; import { StudyMetadata } from "../../../../../../common/types"; import client from "../../../../../../services/api/client"; @@ -131,3 +132,15 @@ export function setGeneralFormFields( ): Promise { return client.put(makeRequestURL(studyId), values); } + +export const hasDayField = R.anyPass([ + R.has("firstDay"), + R.has("lastDay"), + R.has("leapYear"), +]); + +export const pickDayFields = ( + values: GeneralFormFields +): Pick => { + return R.pickAll(["firstDay", "lastDay", "leapYear"], values); +}; From 08d3148622bfdbf52fc9ab1eeeadef0246106859 Mon Sep 17 00:00:00 2001 From: Samir Kamal <1954121+skamril@users.noreply.github.com> Date: Wed, 19 Apr 2023 19:20:38 +0200 Subject: [PATCH 37/70] fix(common): issue in Form component when an error is throw in submit handler --- webapp/src/components/common/Form/index.tsx | 22 ++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/webapp/src/components/common/Form/index.tsx b/webapp/src/components/common/Form/index.tsx index 17ab16decb..7bfafc5eb9 100644 --- a/webapp/src/components/common/Form/index.tsx +++ b/webapp/src/components/common/Form/index.tsx @@ -60,7 +60,7 @@ export interface FormProps< onSubmit?: ( data: SubmitHandlerPlus, event?: React.BaseSyntheticEvent - ) => any | Promise; + ) => void | Promise; onSubmitError?: SubmitErrorHandler; children: | ((formApi: UseFormReturnPlus) => React.ReactNode) @@ -198,7 +198,7 @@ function Form( //////////////////////////////////////////////////////////////// const submit = () => { - const callback = handleSubmit(function onValid(data, e) { + const callback = handleSubmit(function onValid(data, event) { lastSubmittedData.current = data; const dirtyValues = getDirtyValues(dirtyFields, data) as DeepPartial< @@ -220,19 +220,19 @@ function Form( } if (onSubmit) { - res.push(onSubmit({ values: data, dirtyValues }, e)); + res.push(onSubmit({ values: data, dirtyValues }, event)); } - return Promise.all(res); + return Promise.all(res) + .catch((error) => { + enqueueErrorSnackbar(t("form.submit.error"), error); + }) + .finally(() => { + preventClose.current = false; + }); }, onSubmitError); - return callback() - .catch((error) => { - enqueueErrorSnackbar(t("form.submit.error"), error); - }) - .finally(() => { - preventClose.current = false; - }); + return callback(); }; const submitDebounced = useDebounce(submit, autoSubmitConfig.wait); From c39865dd609d0c821805d063eaa4b386e78d11fd Mon Sep 17 00:00:00 2001 From: Samir Kamal <1954121+skamril@users.noreply.github.com> Date: Thu, 27 Apr 2023 12:58:03 +0200 Subject: [PATCH 38/70] feat(eslint): add rule --- webapp/.eslintrc.json | 1 + 1 file changed, 1 insertion(+) diff --git a/webapp/.eslintrc.json b/webapp/.eslintrc.json index 542b985ee9..f55addeab3 100644 --- a/webapp/.eslintrc.json +++ b/webapp/.eslintrc.json @@ -59,6 +59,7 @@ } ], "no-shadow": "off", + "no-throw-literal": "error", "no-underscore-dangle": "off", "no-unused-vars": "off", "no-use-before-define": ["error", { "functions": false }], From 35bb1a88b9fd8071d3e987ee2767bbe4029a2bc0 Mon Sep 17 00:00:00 2001 From: Samir Kamal <1954121+skamril@users.noreply.github.com> Date: Thu, 27 Apr 2023 12:56:11 +0200 Subject: [PATCH 39/70] fix(ui): add return on some form submit handlers --- .../ScenarioBuilderDialog/Rulesets.tsx | 66 ++++++++++--------- .../ScenarioBuilderDialog/tabs/Table.tsx | 9 ++- .../dialogs/ScenarioPlaylistDialog/index.tsx | 2 +- .../dialogs/ThematicTrimmingDialog/index.tsx | 2 +- .../Areas/Hydro/ManagementOptions/index.tsx | 2 +- 5 files changed, 41 insertions(+), 40 deletions(-) diff --git a/webapp/src/components/App/Singlestudy/explore/Configuration/General/dialogs/ScenarioBuilderDialog/Rulesets.tsx b/webapp/src/components/App/Singlestudy/explore/Configuration/General/dialogs/ScenarioBuilderDialog/Rulesets.tsx index 1b82dbc0d4..2e305c5e80 100644 --- a/webapp/src/components/App/Singlestudy/explore/Configuration/General/dialogs/ScenarioBuilderDialog/Rulesets.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Configuration/General/dialogs/ScenarioBuilderDialog/Rulesets.tsx @@ -7,7 +7,6 @@ import * as RA from "ramda-adjunct"; import { LoadingButton } from "@mui/lab"; import FileCopyIcon from "@mui/icons-material/FileCopy"; import AddIcon from "@mui/icons-material/Add"; -import { AxiosError } from "axios"; import SelectFE from "../../../../../../../common/fieldEditors/SelectFE"; import ConfigContext from "./ConfigContext"; import StringFE from "../../../../../../../common/fieldEditors/StringFE"; @@ -34,15 +33,6 @@ function Rulesets() { const enqueueErrorSnackbar = useEnqueueErrorSnackbar(); const allowDelete = activeRuleset && Object.keys(config).length > 1; - //////////////////////////////////////////////////////////////// - // Utils - //////////////////////////////////////////////////////////////// - - const catchError = (message: string) => (err: AxiosError) => { - reloadConfig(); - enqueueErrorSnackbar(message, err); - }; - //////////////////////////////////////////////////////////////// // Event Handlers //////////////////////////////////////////////////////////////// @@ -54,17 +44,20 @@ function Rulesets() { ); setActiveRuleset(name); - updateScenarioBuilderConfig(studyId, { + return updateScenarioBuilderConfig(studyId, { [activeRuleset]: "", [name]: activeRuleset, - }).catch( - catchError( + }).catch((err) => { + reloadConfig(); + + throw new Error( t( "study.configuration.general.mcScenarioBuilder.error.ruleset.rename", [activeRuleset] - ) - ) - ); + ), + { cause: err } + ); + }); }; const handleAdd = ({ values: { name } }: SubmitHandlerType) => { @@ -72,15 +65,18 @@ function Rulesets() { setConfig((prev) => ({ [name]: {}, ...prev })); setActiveRuleset(name); - updateScenarioBuilderConfig(studyId, { + return updateScenarioBuilderConfig(studyId, { [name]: {}, - }).catch( - catchError( + }).catch((err) => { + reloadConfig(); + + throw new Error( t("study.configuration.general.mcScenarioBuilder.error.ruleset.add", [ name, - ]) - ) - ); + ]), + { cause: err } + ); + }); }; const handleDelete = () => { @@ -91,14 +87,17 @@ function Rulesets() { updateScenarioBuilderConfig(studyId, { [activeRuleset]: "", - }).catch( - catchError( + }).catch((err) => { + reloadConfig(); + + enqueueErrorSnackbar( t( "study.configuration.general.mcScenarioBuilder.error.ruleset.delete", [activeRuleset] - ) - ) - ); + ), + err + ); + }); }; const handleDuplicate = () => { @@ -108,14 +107,17 @@ function Rulesets() { updateScenarioBuilderConfig(studyId, { [newRulesetName]: activeRuleset, - }).catch( - catchError( + }).catch((err) => { + reloadConfig(); + + enqueueErrorSnackbar( t( "study.configuration.general.mcScenarioBuilder.error.ruleset.duplicate", [activeRuleset] - ) - ) - ); + ), + err + ); + }); }; //////////////////////////////////////////////////////////////// diff --git a/webapp/src/components/App/Singlestudy/explore/Configuration/General/dialogs/ScenarioBuilderDialog/tabs/Table.tsx b/webapp/src/components/App/Singlestudy/explore/Configuration/General/dialogs/ScenarioBuilderDialog/tabs/Table.tsx index 2e5bdaeeac..6dcf9f6c1b 100644 --- a/webapp/src/components/App/Singlestudy/explore/Configuration/General/dialogs/ScenarioBuilderDialog/tabs/Table.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Configuration/General/dialogs/ScenarioBuilderDialog/tabs/Table.tsx @@ -15,7 +15,6 @@ import FormTable from "../../../../../../../../common/FormTable"; import ConfigContext from "../ConfigContext"; import { updateScenarioBuilderConfig } from "../utils"; import { SubmitHandlerPlus } from "../../../../../../../../common/Form/types"; -import useEnqueueErrorSnackbar from "../../../../../../../../../hooks/useEnqueueErrorSnackbar"; type ElementList = Array<{ id: string; @@ -41,7 +40,6 @@ function Table(props: Props) { const { nbYears, symbol, rowType, areaId } = props; const { config, setConfig, reloadConfig, activeRuleset, studyId } = useContext(ConfigContext); - const enqueueErrorSnackbar = useEnqueueErrorSnackbar(); const { t } = useTranslation(); const valuesFromConfig = R.path( @@ -101,13 +99,14 @@ function Table(props: Props) { setConfig(R.mergeDeepLeft(newData)); - updateScenarioBuilderConfig(studyId, newData).catch((err) => { + return updateScenarioBuilderConfig(studyId, newData).catch((err) => { reloadConfig(); - enqueueErrorSnackbar( + + throw new Error( t("study.configuration.general.mcScenarioBuilder.error.table", [ `${activeRuleset}.${symbol}`, ]), - err + { cause: err } ); }); }; diff --git a/webapp/src/components/App/Singlestudy/explore/Configuration/General/dialogs/ScenarioPlaylistDialog/index.tsx b/webapp/src/components/App/Singlestudy/explore/Configuration/General/dialogs/ScenarioPlaylistDialog/index.tsx index 7ca0d31935..d4c58e10c5 100644 --- a/webapp/src/components/App/Singlestudy/explore/Configuration/General/dialogs/ScenarioPlaylistDialog/index.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Configuration/General/dialogs/ScenarioPlaylistDialog/index.tsx @@ -56,7 +56,7 @@ function ScenarioPlaylistDialog(props: Props) { }; const handleSubmit = (data: SubmitHandlerPlus) => { - setPlaylist(study.id, data.values); + return setPlaylist(study.id, data.values); }; const handleCellsRender: HandsontableProps["cells"] = function cells( diff --git a/webapp/src/components/App/Singlestudy/explore/Configuration/General/dialogs/ThematicTrimmingDialog/index.tsx b/webapp/src/components/App/Singlestudy/explore/Configuration/General/dialogs/ThematicTrimmingDialog/index.tsx index 845e8a19c3..01d5c106c9 100644 --- a/webapp/src/components/App/Singlestudy/explore/Configuration/General/dialogs/ThematicTrimmingDialog/index.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Configuration/General/dialogs/ThematicTrimmingDialog/index.tsx @@ -44,7 +44,7 @@ function ThematicTrimmingDialog(props: Props) { const handleSubmit = ( data: SubmitHandlerPlus ) => { - setThematicTrimmingConfig(study.id, data.values); + return setThematicTrimmingConfig(study.id, data.values); }; //////////////////////////////////////////////////////////////// diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/ManagementOptions/index.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/ManagementOptions/index.tsx index cfc05ae208..61a58c0881 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/ManagementOptions/index.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/ManagementOptions/index.tsx @@ -22,7 +22,7 @@ function ManagementOptions() { //////////////////////////////////////////////////////////////// const handleSubmit = (data: SubmitHandlerPlus) => { - setManagementOptionsFormFields(studyId, areaId, data.dirtyValues); + return setManagementOptionsFormFields(studyId, areaId, data.dirtyValues); }; //////////////////////////////////////////////////////////////// From db4a3a90df476420a8e752d5c645a56fa9225c0f Mon Sep 17 00:00:00 2001 From: Samir Kamal <1954121+skamril@users.noreply.github.com> Date: Wed, 19 Apr 2023 19:21:43 +0200 Subject: [PATCH 40/70] fix(ui): missing key attribute in MenuWrapper component --- webapp/src/components/wrappers/MenuWrapper/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webapp/src/components/wrappers/MenuWrapper/index.tsx b/webapp/src/components/wrappers/MenuWrapper/index.tsx index 04b5451f24..774e3826b9 100644 --- a/webapp/src/components/wrappers/MenuWrapper/index.tsx +++ b/webapp/src/components/wrappers/MenuWrapper/index.tsx @@ -153,8 +153,8 @@ function MenuWrapper(props: Props) { if (elm.id === "tasks.title") { return ( - - + + Date: Wed, 19 Apr 2023 19:19:09 +0200 Subject: [PATCH 41/70] style(hoc): add comments --- webapp/src/hoc/reactHookFormSupport.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/webapp/src/hoc/reactHookFormSupport.tsx b/webapp/src/hoc/reactHookFormSupport.tsx index 511efbe1d4..affd7a81a6 100644 --- a/webapp/src/hoc/reactHookFormSupport.tsx +++ b/webapp/src/hoc/reactHookFormSupport.tsx @@ -71,9 +71,15 @@ function reactHookFormSupport( ) { const { preValidate, setValueAs = R.identity } = options; + /** + * Wrap in a higher component the specified field editor component + */ function wrapWithReactHookFormSupport< TProps extends FieldEditorProps >(FieldEditor: React.ComponentType) { + /** + * The wrapper component + */ function ReactHookFormSupport< TFieldValues extends FieldValues = FieldValues, TFieldName extends FieldPath = FieldPath, From f4b76975ef6be2e3ee1718776dd416d6b0013d58 Mon Sep 17 00:00:00 2001 From: Samir Kamal <1954121+skamril@users.noreply.github.com> Date: Thu, 27 Apr 2023 12:57:23 +0200 Subject: [PATCH 42/70] style(ui): fix typo --- webapp/src/components/common/fieldEditors/NumberFE.tsx | 4 ++-- webapp/src/hoc/reactHookFormSupport.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/webapp/src/components/common/fieldEditors/NumberFE.tsx b/webapp/src/components/common/fieldEditors/NumberFE.tsx index 86026d5fb6..eb1de8ea53 100644 --- a/webapp/src/components/common/fieldEditors/NumberFE.tsx +++ b/webapp/src/components/common/fieldEditors/NumberFE.tsx @@ -1,6 +1,6 @@ import { TextField, TextFieldProps } from "@mui/material"; import * as RA from "ramda-adjunct"; -import withReactHookFormSupport from "../../../hoc/reactHookFormSupport"; +import reactHookFormSupport from "../../../hoc/reactHookFormSupport"; export type NumberFEProps = { value?: number; @@ -11,7 +11,7 @@ function NumberFE(props: NumberFEProps) { return ; } -export default withReactHookFormSupport({ +export default reactHookFormSupport({ defaultValue: "" as unknown as number, // Returning empty string allow to type negative number setValueAs: (v) => (v === "" ? "" : Number(v)), diff --git a/webapp/src/hoc/reactHookFormSupport.tsx b/webapp/src/hoc/reactHookFormSupport.tsx index affd7a81a6..687ad77628 100644 --- a/webapp/src/hoc/reactHookFormSupport.tsx +++ b/webapp/src/hoc/reactHookFormSupport.tsx @@ -170,7 +170,7 @@ function reactHookFormSupport( return validate; }, [validate]); - const getDefaultValuesFromOptions = () => { + const getDefaultValueFromOptions = () => { const { defaultValue } = options; return RA.isFunction(defaultValue) ? defaultValue(feProps) @@ -188,7 +188,7 @@ function reactHookFormSupport( name={feProps.name as TFieldName} // useForm's defaultValues take precedence defaultValue={ - (feProps.defaultValue ?? getDefaultValuesFromOptions()) as any + (feProps.defaultValue ?? getDefaultValueFromOptions()) as any } rules={{ ...restRules, validate: validateWrapper }} shouldUnregister={shouldUnregister} From c40f763f6578c7000ab0573e280de8e3d0f5a98c Mon Sep 17 00:00:00 2001 From: MartinBelthle <102529366+MartinBelthle@users.noreply.github.com> Date: Tue, 2 May 2023 17:50:37 +0200 Subject: [PATCH 43/70] feat(core): enhance `/version` API endpoint to show dependency versions (#1475) Co-authored-by: Laurent LAPORTE --- antarest/core/core_blueprint.py | 10 ++-- antarest/core/version_info.py | 64 +++++++++++++++++++++--- tests/core/test_version_info.py | 35 +++++++++++++ tests/integration/test_core_blueprint.py | 27 ++++++++-- 4 files changed, 120 insertions(+), 16 deletions(-) create mode 100644 tests/core/test_version_info.py diff --git a/antarest/core/core_blueprint.py b/antarest/core/core_blueprint.py index 40a2e4dba4..73fd56fce9 100644 --- a/antarest/core/core_blueprint.py +++ b/antarest/core/core_blueprint.py @@ -5,7 +5,11 @@ from antarest.core.jwt import JWTUser from antarest.core.requests import UserHasNotPermissionError from antarest.core.utils.web import APITag -from antarest.core.version_info import VersionInfoDTO, get_commit_id +from antarest.core.version_info import ( + VersionInfoDTO, + get_commit_id, + get_dependencies, +) from antarest.login.auth import Auth from fastapi import APIRouter, Depends from pydantic import BaseModel @@ -39,18 +43,18 @@ def version_info() -> Any: """ Returns the current version of the application, along with relevant dependency information. + - `name`: The name of the application. - `version`: The current version of the application. - `gitcommit`: The commit ID of the current version's Git repository. - `dependencies`: A dictionary of dependencies, where the key is the dependency name and the value is its version number. """ - from antareslauncher import __version__ as antares_launcher_version from antarest import __version__ as antarest_version return VersionInfoDTO( version=antarest_version, gitcommit=get_commit_id(config.resources_path), - dependencies={"Antares_Launcher": antares_launcher_version}, + dependencies=get_dependencies(), ) @bp.get("/kill", include_in_schema=False) diff --git a/antarest/core/version_info.py b/antarest/core/version_info.py index 88572704a3..f6532b9cdb 100644 --- a/antarest/core/version_info.py +++ b/antarest/core/version_info.py @@ -3,7 +3,7 @@ """ import subprocess from pathlib import Path -from typing import Dict, Optional +from typing import Dict from pydantic import BaseModel @@ -14,6 +14,22 @@ class VersionInfoDTO(BaseModel): gitcommit: str dependencies: Dict[str, str] + class Config: + schema_extra = { + "example": { + "name": "AntaREST", + "version": "2.13.2", + "gitcommit": "879d9d641fc2e7e30e626084b431ce014de63532", + "dependencies": { + "click": "8.0.4", + "Deprecated": "1.2.13", + "fastapi": "0.73.0", + "Flask": "2.1.3", + "gunicorn": "20.1.0", + }, + } + } + def get_commit_id(resources_dir: Path) -> str: """ @@ -36,10 +52,42 @@ def get_commit_id(resources_dir: Path) -> str: try: return path_commit_id.read_text(encoding="utf-8").strip() except FileNotFoundError: - command = "git log -1 HEAD --format=%H" - try: - return subprocess.check_output( - command, encoding="utf-8", shell=True - ).strip() - except (subprocess.CalledProcessError, FileNotFoundError): - return "" + return get_last_commit_from_git() + + +def get_last_commit_from_git() -> str: + """Returns the commit ID of the current Git HEAD, or "".""" + command = "git log -1 HEAD --format=%H" + try: + return subprocess.check_output( + command, encoding="utf-8", shell=True + ).strip() + except (subprocess.CalledProcessError, FileNotFoundError): + return "" + + +def get_dependencies() -> Dict[str, str]: + """ + Retrieve the list of installed dependencies and their versions. + + Returns: + A dictionary containing the package names and their corresponding versions installed in the + current Python environment. The dictionary keys are the package names (as strings), and the + values are the corresponding version numbers (also as strings). + + Raises: + subprocess.CalledProcessError: + If the `pip freeze` command fails for some reason. + """ + # fmt: off + output = subprocess.check_output("pip freeze", encoding="utf-8", shell=True) + lines = ( + line + for line in output.splitlines(keepends=False) + if "==" in line + ) + # noinspection PyTypeChecker + packages = dict(line.split("==", 1) for line in lines) + # AntaREST is not a dependency of AntaREST + return {k: v for k, v in packages.items() if k.lower() != "antarest"} + # fmt: on diff --git a/tests/core/test_version_info.py b/tests/core/test_version_info.py new file mode 100644 index 0000000000..195280631d --- /dev/null +++ b/tests/core/test_version_info.py @@ -0,0 +1,35 @@ +import re +from unittest.mock import patch + +import pytest +from antarest.core.version_info import get_commit_id, get_dependencies + + +class TestVersionInfo: + @pytest.mark.unit_test + def test_get_dependencies(self) -> None: + dependencies = get_dependencies() + assert isinstance(dependencies, dict) + # AntaREST is not a dependency of AntaREST + assert "AntaREST" not in dependencies + # lazy checking: we only check that FastAPI exist ;-) + assert "fastapi" in dependencies + assert all( + # match at least one number. eg: "pywin32 == 306" + re.fullmatch(r"\d+(?:\.\d+)*", ver) + for ver in dependencies.values() + ) + + @pytest.mark.unit_test + def test_get_commit_id__commit_id__exist(self, tmp_path) -> None: + path_commit_id = tmp_path.joinpath("commit_id") + path_commit_id.write_text("fake_commit") + assert get_commit_id(tmp_path) == "fake_commit" + + @pytest.mark.unit_test + def test_get_commit_id__commit_id__missing(self, tmp_path) -> None: + with patch( + "antarest.core.version_info.get_last_commit_from_git", + return_value="mock commit", + ): + assert get_commit_id(tmp_path) == "mock commit" diff --git a/tests/integration/test_core_blueprint.py b/tests/integration/test_core_blueprint.py index aaa6cb83e9..60949ea8c9 100644 --- a/tests/integration/test_core_blueprint.py +++ b/tests/integration/test_core_blueprint.py @@ -1,20 +1,37 @@ +import re from unittest import mock from fastapi import FastAPI -from http import HTTPStatus from starlette.testclient import TestClient +class RegEx: + """A helper object that compares equal to a regex.""" + + def __init__(self, regex): + self.regex = regex + self.match = re.compile(self.regex).fullmatch + + def __eq__(self, other): + return isinstance(other, str) and self.match(other) + + def __ne__(self, other): + return not isinstance(other, str) or not self.match(other) + + def __repr__(self): + return f"" + + class TestVersionInfo: def test_version_info(self, app: FastAPI): client = TestClient(app, raise_server_exceptions=False) res = client.get("/version") - assert res.status_code == HTTPStatus.OK + res.raise_for_status() actual = res.json() expected = { "name": "AntaREST", - "version": mock.ANY, - "gitcommit": mock.ANY, - "dependencies": {"Antares_Launcher": mock.ANY}, + "version": RegEx(r"\d+(?:\.\d+)+"), + "gitcommit": RegEx(r"^[0-9a-fA-F]{40}$"), + "dependencies": mock.ANY, } assert actual == expected From 0840ce59f9e6dc518c90c4886f6c912a66906cd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Omn=C3=A8s?= Date: Wed, 3 May 2023 10:43:21 +0200 Subject: [PATCH 44/70] fix(package): do not create symlink for `AntaresWebServer` during package creation (#1401) --- scripts/package_antares_web.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/scripts/package_antares_web.sh b/scripts/package_antares_web.sh index 54f6d14c9a..ee8865f5f7 100755 --- a/scripts/package_antares_web.sh +++ b/scripts/package_antares_web.sh @@ -46,8 +46,6 @@ fi echo "Creating shortcuts" if [[ "$OSTYPE" == "msys"* ]]; then cp ../resources/AntaresWebServerShortcut.lnk ../dist/ -else - ln -s ../dist/AntaresWeb/AntaresWebServer ../dist/AntaresWebServer fi echo "Unzipping example study" From 904b3bc0957836c7c21cd72e3c41a2f1ba6767eb Mon Sep 17 00:00:00 2001 From: Laurent LAPORTE <43534797+laurent-laporte-pro@users.noreply.github.com> Date: Wed, 3 May 2023 15:35:42 +0200 Subject: [PATCH 45/70] build(git): remove `antares-launcher` submodule (#1495) Signed-off-by: Sylvain Leclerc Co-authored-by: Sylvain Leclerc --- .github/workflows/compatibility.yml | 4 +--- .github/workflows/deploy.yml | 1 - .github/workflows/main.yml | 4 ---- .gitmodules | 3 --- Dockerfile | 10 ---------- README.md | 2 -- antares-launcher | 1 - docs/install/0-INSTALL.md | 2 -- docs/install/2-DEPLOY.md | 4 ++-- 9 files changed, 3 insertions(+), 28 deletions(-) delete mode 100644 .gitmodules delete mode 160000 antares-launcher diff --git a/.github/workflows/compatibility.yml b/.github/workflows/compatibility.yml index 58c44c5cc4..9131425f79 100644 --- a/.github/workflows/compatibility.yml +++ b/.github/workflows/compatibility.yml @@ -14,10 +14,8 @@ jobs: python-version: [ 3.8 ] steps: - - name: Checkout github repo (+ download lfs dependencies) + - name: Checkout github repo uses: actions/checkout@v2 - with: - submodules: recursive - name: Set up Python uses: actions/setup-python@v1 with: diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 4f920c083e..0d0687ea8a 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -17,7 +17,6 @@ jobs: - name: Checkout github repo (+ download lfs dependencies) uses: actions/checkout@v3 with: - submodules: recursive fetch-depth: 0 - name: Install wget for windows if: matrix.os == 'windows-latest' diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e5061506b1..d0f8c4c7ac 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -10,8 +10,6 @@ jobs: steps: - name: Checkout github repo (+ download lfs dependencies) uses: actions/checkout@v2 - with: - submodules: recursive - name: Set up Python uses: actions/setup-python@v1 with: @@ -41,8 +39,6 @@ jobs: steps: - name: Checkout github repo (+ download lfs dependencies) uses: actions/checkout@v2 - with: - submodules: recursive - name: Set up Python uses: actions/setup-python@v1 with: diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 50629e9bb7..0000000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "antares-launcher"] - path = antares-launcher - url = https://github.com/AntaresSimulatorTeam/antares-launcher.git diff --git a/Dockerfile b/Dockerfile index f99b8d7559..631ca6c336 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,16 +13,6 @@ COPY ./scripts /scripts COPY ./alembic /alembic COPY ./alembic.ini /alembic.ini -# > IMPORTANT: The `antares-launcher` project (source files) is no longer needed, -# > because the `Antares-Launcher` Python library is now declared as a dependency -# > in the `requirements.txt` file. -# > In other words, we can dispense with the creation of the symbolic link. - -# COPY ./antares-launcher /antares-launcher -# RUN ln -s /antares-launcher/antareslauncher /antareslauncher -# RUN mkdir /conf/antares-launcher -# RUN cp /antares-launcher/requirements.txt /conf/antares-launcher/requirements.txt - RUN ./scripts/install-debug.sh RUN pip3 install --upgrade pip \ diff --git a/README.md b/README.md index a9a3db1298..98748de7a6 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,6 @@ First clone the projet: ```shell script git clone https://github.com/AntaresSimulatorTeam/AntaREST.git cd AntaREST -git submodule init -git submodule update ``` Install back dependencies diff --git a/antares-launcher b/antares-launcher deleted file mode 160000 index ba92020341..0000000000 --- a/antares-launcher +++ /dev/null @@ -1 +0,0 @@ -Subproject commit ba92020341f85c526f067aa0b6658b1ec4cef893 diff --git a/docs/install/0-INSTALL.md b/docs/install/0-INSTALL.md index 6703b7e3d8..a51c624450 100644 --- a/docs/install/0-INSTALL.md +++ b/docs/install/0-INSTALL.md @@ -14,8 +14,6 @@ Requirements : ``` git clone https://github.com/AntaresSimulatorTeam/AntaREST.git cd AntaREST -git submodule init -git submodule update ``` 2. Install back dependencies diff --git a/docs/install/2-DEPLOY.md b/docs/install/2-DEPLOY.md index 701cd77464..8db71603c7 100644 --- a/docs/install/2-DEPLOY.md +++ b/docs/install/2-DEPLOY.md @@ -28,8 +28,8 @@ Requirements: These steps should work on any linux system with docker and docker-compose installed. -1. First, the steps 1 and 3 of the [quick start build](0-INSTALL.md#quick-start) must have been done. So this guide will assume that you have previously cloned the [code repository](https://github.com/AntaresSimulatorTeam/AntaREST) - (don't forget the git submodule), the frontend built and that your working directory is at the root of the project. +1. First, the steps 1 and 3 of the [quick start build](0-INSTALL.md#quick-start) must have been done. So this guide will assume that you have previously cloned the [code repository](https://github.com/AntaresSimulatorTeam/AntaREST), + the frontend built and that your working directory is at the root of the project. 2. Then download and unzip AntaresSimulator binaries: From 255de01d2df7834bc9aa67016d48dc380a2d0bdf Mon Sep 17 00:00:00 2001 From: Samir Kamal <1954121+skamril@users.noreply.github.com> Date: Wed, 3 May 2023 18:30:19 +0200 Subject: [PATCH 46/70] fix(api): issue in day fields validation for general form --- antarest/study/business/general_management.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/antarest/study/business/general_management.py b/antarest/study/business/general_management.py index 46f0dfbb64..724b3d7f98 100644 --- a/antarest/study/business/general_management.py +++ b/antarest/study/business/general_management.py @@ -87,8 +87,12 @@ def day_fields_validation(cls, values: Dict[str, Any]) -> Dict[str, Any]: first_day = values.get("first_day") last_day = values.get("last_day") leap_year = values.get("leap_year") + day_fields = [first_day, last_day, leap_year] - if any(v is None for v in [first_day, last_day, leap_year]): + if all(v is None for v in day_fields): + return values + + if any(v is None for v in day_fields): raise ValueError( "First day, last day and leap year fields must be defined together" ) From 345540b50cc24507f182d5f09bd161693082fa3f Mon Sep 17 00:00:00 2001 From: Samir Kamal <1954121+skamril@users.noreply.github.com> Date: Wed, 3 May 2023 18:32:37 +0200 Subject: [PATCH 47/70] feat(config): add translations --- webapp/public/locales/en/main.json | 7 ++++++- webapp/public/locales/fr/main.json | 5 +++++ .../explore/Configuration/General/Fields.tsx | 16 ++++++++++------ 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/webapp/public/locales/en/main.json b/webapp/public/locales/en/main.json index b1b98f126d..8cd363d9c6 100644 --- a/webapp/public/locales/en/main.json +++ b/webapp/public/locales/en/main.json @@ -109,6 +109,7 @@ "form.field.required": "Field required", "form.field.minLength": "{{0}} character(s) minimum", "form.field.minValue": "The minimum value is {{0}}", + "form.field.maxValue": "The maximum value is {{0}}", "form.field.notAllowedValue": "Not allowed value", "matrix.graphSelector": "Columns", "matrix.message.importHint": "Click or drag and drop a matrix here", @@ -273,10 +274,14 @@ "study.configuration.general.mode": "Mode", "study.configuration.general.firstDay": "First day", "study.configuration.general.lastDay": "Last day", + "study.configuration.general.day.error.leapYearMax": "Maximum is 366 for a leap year", + "study.configuration.general.day.error.nonLeapYearMax": "Maximum is 365 for a non-leap year", "study.configuration.general.horizon": "Horizon", "study.configuration.general.year": "Year", "study.configuration.general.week": "Week", "study.configuration.general.firstDayOfYear": "1st January", + "study.configuration.general.nbYears": "Number", + "study.configuration.general.nbYears.error.derated": "Value must be 1 when building mode is derated", "study.configuration.general.leapYear": "Leap year", "study.configuration.general.buildingMode": "Building mode", "study.configuration.general.selectionMode": "Selection mode", @@ -394,7 +399,7 @@ "study.modelization.load": "Load", "study.modelization.thermal": "Thermal Clus.", "study.modelization.hydro": "Hydro", - "study.modelization.hydro.allocation.error.field.delete": "Error when deleting the allocation", + "study.modelization.hydro.allocation.error.field.delete": "Error when deleting the allocation", "study.modelization.wind": "Wind", "study.modelization.solar": "Solar", "study.modelization.renewables": "Renewables Clus.", diff --git a/webapp/public/locales/fr/main.json b/webapp/public/locales/fr/main.json index 8a38cd2ba0..fa95b10725 100644 --- a/webapp/public/locales/fr/main.json +++ b/webapp/public/locales/fr/main.json @@ -109,6 +109,7 @@ "form.field.required": "Champ requis", "form.field.minLength": "{{0}} caractère(s) minimum", "form.field.minValue": "La valeur minimum est {{0}}", + "form.field.maxValue": "La valeur maximum est {{0}}", "form.field.notAllowedValue": "Valeur non autorisée", "matrix.graphSelector": "Colonnes", "matrix.message.importHint": "Cliquer ou glisser une matrice ici", @@ -273,10 +274,14 @@ "study.configuration.general.mode": "Mode", "study.configuration.general.firstDay": "Premier jour", "study.configuration.general.lastDay": "Dernier jour", + "study.configuration.general.day.error.leapYearMax": "Le maximum est 366 pour une année bissextile", + "study.configuration.general.day.error.nonLeapYearMax": "Le maximum est 365 pour une année non bissextile", "study.configuration.general.horizon": "Horizon", "study.configuration.general.year": "Année", "study.configuration.general.week": "Semaine", "study.configuration.general.firstDayOfYear": "1er Janvier", + "study.configuration.general.nbYears": "Nombre", + "study.configuration.general.nbYears.error.derated": "La valeur doit être 1 lorsque building mode est 'derated'", "study.configuration.general.leapYear": "Année bissextile", "study.configuration.general.buildingMode": "Building mode", "study.configuration.general.selectionMode": "Selection mode", diff --git a/webapp/src/components/App/Singlestudy/explore/Configuration/General/Fields.tsx b/webapp/src/components/App/Singlestudy/explore/Configuration/General/Fields.tsx index 9c3b822014..0354877df0 100644 --- a/webapp/src/components/App/Singlestudy/explore/Configuration/General/Fields.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Configuration/General/Fields.tsx @@ -91,9 +91,13 @@ function Fields(props: Props) { return false; } if (formValues.leapYear) { - return value <= 366 ? true : "Maximum is 366 for a leap year"; + return value <= 366 + ? true + : t("study.configuration.general.day.error.leapYearMax"); } - return value <= 365 ? true : "Maximum is 365 for a non-leap year"; + return value <= 365 + ? true + : t("study.configuration.general.day.error.nonLeapYearMax"); }; const handleNbYearsValidation: Validate = ( @@ -103,12 +107,12 @@ function Fields(props: Props) { if (formValues.buildingMode === BuildingMode.Derated) { return value === 1 ? true - : "Value must be 1 when building mode is derated"; + : t("study.configuration.general.nbYears.error.derated"); } if (value < 1) { - return "Minimum is 1"; + return t("form.field.minValue", [1]); } - return value <= 50000 ? true : "Maximum is 50000"; + return value <= 50000 ? true : t("form.field.maxValue", [50000]); }; //////////////////////////////////////////////////////////////// @@ -202,7 +206,7 @@ function Fields(props: Props) { > Date: Wed, 3 May 2023 19:43:41 +0200 Subject: [PATCH 48/70] test(api): add unit tests for `GeneralFormFields` validation (#1500) --- antarest/study/business/general_management.py | 4 +- .../test_config_general.py | 63 +++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 tests/integration/study_data_blueprint/test_config_general.py diff --git a/antarest/study/business/general_management.py b/antarest/study/business/general_management.py index 724b3d7f98..25780f2fb7 100644 --- a/antarest/study/business/general_management.py +++ b/antarest/study/business/general_management.py @@ -90,6 +90,8 @@ def day_fields_validation(cls, values: Dict[str, Any]) -> Dict[str, Any]: day_fields = [first_day, last_day, leap_year] if all(v is None for v in day_fields): + # The user wishes to update another field than these three. + # no need to validate anything: return values if any(v is None for v in day_fields): @@ -264,7 +266,7 @@ def set_field_values( ) ) - if len(commands) > 0: + if commands: execute_or_add_commands( study, file_study, commands, self.storage_service ) diff --git a/tests/integration/study_data_blueprint/test_config_general.py b/tests/integration/study_data_blueprint/test_config_general.py new file mode 100644 index 0000000000..e64c50aa83 --- /dev/null +++ b/tests/integration/study_data_blueprint/test_config_general.py @@ -0,0 +1,63 @@ +from http import HTTPStatus + +import pytest +from starlette.testclient import TestClient + + +@pytest.mark.unit_test +class TestConfigGeneralForm: + """ + Test the end points related to hydraulic correlation. + + Those tests use the "examples/studies/STA-mini.zip" Study, + which contains the following areas: ["de", "es", "fr", "it"]. + """ + + def test_get_general_form_values( + self, + client: TestClient, + user_access_token: str, + study_id: str, + ): + """Check `set_general_form_values` end point""" + res = client.get( + f"/v1/studies/{study_id}/config/general/form", + headers={"Authorization": f"Bearer {user_access_token}"}, + ) + assert res.status_code == HTTPStatus.OK, res.json() + actual = res.json() + expected = { + "buildingMode": "Custom", + "filtering": True, + "firstDay": 1, + "firstJanuary": "Monday", + "firstMonth": "january", + "firstWeekDay": "Monday", + "horizon": "2030", + "lastDay": 7, + "leapYear": False, + "mcScenario": True, + "mode": "Adequacy", + "nbYears": 1, + "selectionMode": True, + "simulationSynthesis": True, + "yearByYear": False, + } + assert actual == expected + + def test_set_general_form_values( + self, + client: TestClient, + user_access_token: str, + study_id: str, + ): + """Check `set_general_form_values` end point""" + obj = {"horizon": 2020} + res = client.put( + f"/v1/studies/{study_id}/config/general/form", + headers={"Authorization": f"Bearer {user_access_token}"}, + json=obj, + ) + assert res.status_code == HTTPStatus.OK, res.json() + actual = res.json() + assert actual is None From c4deac594763ca76d264d1da17230cb80a61dbbc Mon Sep 17 00:00:00 2001 From: hatim dinia Date: Wed, 3 May 2023 19:34:44 +0200 Subject: [PATCH 49/70] feat(ui-hydro): add hydro matrix dialog --- webapp/public/locales/en/main.json | 4 +- webapp/public/locales/fr/main.json | 2 + .../Areas/Hydro/Allocation/index.tsx | 60 ++++++++++------- .../Areas/Hydro/Allocation/utils.ts | 15 ++++- .../Hydro/Correlation/CorrelationField.tsx | 7 +- .../Areas/Hydro/Correlation/Fields.tsx | 9 ++- .../Areas/Hydro/Correlation/index.tsx | 65 +++++++++++-------- .../Areas/Hydro/Correlation/utils.ts | 15 ++++- .../Modelization/Areas/Hydro/HydroMatrix.tsx | 6 +- .../Areas/Hydro/HydroMatrixDialog.tsx | 43 ++++++++++++ .../Areas/Hydro/InflowStructure.tsx | 6 +- .../Areas/Hydro/ViewMatrixButton.tsx | 24 +++++++ .../explore/Modelization/Areas/Hydro/style.ts | 14 +++- .../explore/Modelization/Areas/Hydro/utils.ts | 60 +++++++++++------ webapp/src/components/common/DynamicList.tsx | 10 ++- .../common/EditableMatrix/index.tsx | 13 ++-- .../components/common/MatrixInput/index.tsx | 58 +++++++++++++---- 17 files changed, 309 insertions(+), 102 deletions(-) create mode 100644 webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/HydroMatrixDialog.tsx create mode 100644 webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/ViewMatrixButton.tsx diff --git a/webapp/public/locales/en/main.json b/webapp/public/locales/en/main.json index 8cd363d9c6..a2dee7c3db 100644 --- a/webapp/public/locales/en/main.json +++ b/webapp/public/locales/en/main.json @@ -399,7 +399,9 @@ "study.modelization.load": "Load", "study.modelization.thermal": "Thermal Clus.", "study.modelization.hydro": "Hydro", - "study.modelization.hydro.allocation.error.field.delete": "Error when deleting the allocation", + "study.modelization.hydro.correlation.view": "View all correlations", + "study.modelization.hydro.allocation.view": "View all allocations", + "study.modelization.hydro.allocation.error.field.delete": "Error when deleting the allocation", "study.modelization.wind": "Wind", "study.modelization.solar": "Solar", "study.modelization.renewables": "Renewables Clus.", diff --git a/webapp/public/locales/fr/main.json b/webapp/public/locales/fr/main.json index fa95b10725..fe59d6bf6e 100644 --- a/webapp/public/locales/fr/main.json +++ b/webapp/public/locales/fr/main.json @@ -399,6 +399,8 @@ "study.modelization.load": "Conso", "study.modelization.thermal": "Clus. Thermiques", "study.modelization.hydro": "Hydro", + "study.modelization.hydro.correlation.view": "Voir les correlations", + "study.modelization.hydro.allocation.view": "Voir les allocations", "study.modelization.hydro.allocation.error.field.delete": "Erreur lors de la suppression de l'allocation", "study.modelization.wind": "Éolien", "study.modelization.solar": "Solaire", diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/index.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/index.tsx index 48820c1529..845325c053 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/index.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/index.tsx @@ -1,5 +1,6 @@ -import { Box, Paper } from "@mui/material"; +import { Grid } from "@mui/material"; import { useOutletContext } from "react-router"; +import { useState } from "react"; import Form from "../../../../../../../common/Form"; import Fields from "./Fields"; import { StudyMetadata } from "../../../../../../../../common/types"; @@ -11,11 +12,16 @@ import { setAllocationFormFields, } from "./utils"; import { SubmitHandlerPlus } from "../../../../../../../common/Form/types"; +import HydroMatrixDialog from "../HydroMatrixDialog"; +import { HydroMatrixType } from "../utils"; +import { FormBox, FormPaper } from "../style"; +import ViewMatrixButton from "../ViewMatrixButton"; function Allocation() { const { study: { id: studyId }, } = useOutletContext<{ study: StudyMetadata }>(); + const [matrixDialogOpen, setMatrixDialogOpen] = useState(false); const areaId = useAppSelector(getCurrentAreaId); //////////////////////////////////////////////////////////////// @@ -33,32 +39,42 @@ function Allocation() { //////////////////////////////////////////////////////////////// return ( - - + -
getAllocationFormFields(studyId, areaId), - }} - onSubmit={handleSubmit} - sx={{ p: 3 }} - > - - -
-
+ + +
getAllocationFormFields(studyId, areaId), + }} + onSubmit={handleSubmit} + sx={{ p: 3 }} + > + + +
+ + setMatrixDialogOpen(true)} + /> + +
+ + {matrixDialogOpen && ( + setMatrixDialogOpen(false)} + /> + )} + ); } diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/utils.ts b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/utils.ts index 60a8ffcebb..4534fea5e1 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/utils.ts +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Allocation/utils.ts @@ -1,4 +1,8 @@ -import { StudyMetadata, Area } from "../../../../../../../../common/types"; +import { + StudyMetadata, + Area, + MatrixType, +} from "../../../../../../../../common/types"; import client from "../../../../../../../../services/api/client"; import { AreaCoefficientItem } from "../utils"; @@ -37,3 +41,12 @@ export async function setAllocationFormFields( const res = await client.put(makeRequestURL(studyId, areaId), values); return res.data; } + +export const getAllocationMatrix = async ( + studyId: StudyMetadata["id"] +): Promise => { + const res = await client.get( + `v1/studies/${studyId}/areas/hydro/allocation/matrix` + ); + return res.data; +}; diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Correlation/CorrelationField.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Correlation/CorrelationField.tsx index 5c6b88258c..f248f9ac79 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Correlation/CorrelationField.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Correlation/CorrelationField.tsx @@ -4,6 +4,8 @@ import { useTranslation } from "react-i18next"; import NumberFE from "../../../../../../../common/fieldEditors/NumberFE"; import { CorrelationFormFields } from "./utils"; import { useFormContextPlus } from "../../../../../../../common/Form"; +import useAppSelector from "../../../../../../../../redux/hooks/useAppSelector"; +import { getCurrentArea } from "../../../../../../../../redux/selectors"; interface Props { field: FieldArrayWithId; @@ -14,6 +16,7 @@ interface Props { // TODO merge with AllocationField function CorrelationField({ field, index, label }: Props) { const { control } = useFormContextPlus(); + const currentArea = useAppSelector(getCurrentArea); const { t } = useTranslation(); //////////////////////////////////////////////////////////////// @@ -36,6 +39,7 @@ function CorrelationField({ field, index, label }: Props) { diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Correlation/Fields.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Correlation/Fields.tsx index 1c252a8e70..6a45074fd7 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Correlation/Fields.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Correlation/Fields.tsx @@ -2,7 +2,10 @@ import { useFieldArray } from "react-hook-form"; import { useOutletContext } from "react-router"; import { StudyMetadata } from "../../../../../../../../common/types"; import useAppSelector from "../../../../../../../../redux/hooks/useAppSelector"; -import { getAreasById } from "../../../../../../../../redux/selectors"; +import { + getAreasById, + getCurrentArea, +} from "../../../../../../../../redux/selectors"; import DynamicList from "../../../../../../../common/DynamicList"; import { useFormContextPlus } from "../../../../../../../common/Form"; import { useAreasOptions } from "../hooks/useAreasOptions"; @@ -14,6 +17,7 @@ function Fields() { study: { id: studyId }, } = useOutletContext<{ study: StudyMetadata }>(); const areasById = useAppSelector((state) => getAreasById(state, studyId)); + const currentArea = useAppSelector(getCurrentArea); const { control } = useFormContextPlus(); const { fields, append, remove } = useFieldArray({ control, @@ -38,7 +42,7 @@ function Fields() { /> )} options={options} - onAdd={(value: string) => + onAdd={(value) => append({ areaId: value, coefficient: 0, @@ -46,6 +50,7 @@ function Fields() { } onDelete={remove} allowEmpty={false} + disableDelete={(item) => item.areaId === currentArea?.id} /> ); } diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Correlation/index.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Correlation/index.tsx index 3edbfdad6d..405de20645 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Correlation/index.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Correlation/index.tsx @@ -1,5 +1,6 @@ -import { Box, Paper } from "@mui/material"; +import { Grid } from "@mui/material"; import { useOutletContext } from "react-router"; +import { useState } from "react"; import Form from "../../../../../../../common/Form"; import { StudyMetadata } from "../../../../../../../../common/types"; import useAppSelector from "../../../../../../../../redux/hooks/useAppSelector"; @@ -11,11 +12,16 @@ import { setCorrelationFormFields, } from "./utils"; import Fields from "./Fields"; +import HydroMatrixDialog from "../HydroMatrixDialog"; +import { HydroMatrixType } from "../utils"; +import { FormBox, FormPaper } from "../style"; +import ViewMatrixButton from "../ViewMatrixButton"; function Correlation() { const { study: { id: studyId }, } = useOutletContext<{ study: StudyMetadata }>(); + const [matrixDialogOpen, setMatrixDialogOpen] = useState(false); const areaId = useAppSelector(getCurrentAreaId); //////////////////////////////////////////////////////////////// @@ -33,32 +39,37 @@ function Correlation() { //////////////////////////////////////////////////////////////// return ( - - -
getCorrelationFormFields(studyId, areaId), - }} - onSubmit={handleSubmit} - sx={{ p: 3 }} - > - - -
-
+ + + + +
getCorrelationFormFields(studyId, areaId), + }} + onSubmit={handleSubmit} + sx={{ p: 3 }} + > + + +
+ + setMatrixDialogOpen(true)} + /> + +
+
+ {matrixDialogOpen && ( + setMatrixDialogOpen(false)} + /> + )} +
); } diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Correlation/utils.ts b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Correlation/utils.ts index 6c9a945961..706b439dd4 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Correlation/utils.ts +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/Correlation/utils.ts @@ -1,4 +1,8 @@ -import { StudyMetadata, Area } from "../../../../../../../../common/types"; +import { + StudyMetadata, + Area, + MatrixType, +} from "../../../../../../../../common/types"; import client from "../../../../../../../../services/api/client"; import { AreaCoefficientItem } from "../utils"; @@ -37,3 +41,12 @@ export async function setCorrelationFormFields( const res = await client.put(makeRequestURL(studyId, areaId), values); return res.data; } + +export async function getCorrelationMatrix( + studyId: StudyMetadata["id"] +): Promise { + const res = await client.get( + `v1/studies/${studyId}/areas/hydro/correlation/matrix` + ); + return res.data; +} diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/HydroMatrix.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/HydroMatrix.tsx index 8498bfa384..ed7f8a60b9 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/HydroMatrix.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/HydroMatrix.tsx @@ -4,10 +4,10 @@ import useAppSelector from "../../../../../../../redux/hooks/useAppSelector"; import { getCurrentAreaId } from "../../../../../../../redux/selectors"; import MatrixInput from "../../../../../../common/MatrixInput"; import { Root } from "./style"; -import { MATRICES, MatrixType } from "./utils"; +import { MATRICES, HydroMatrixType } from "./utils"; interface Props { - type: MatrixType; + type: HydroMatrixType; } function HydroMatrix({ type }: Props) { @@ -29,6 +29,8 @@ function HydroMatrix({ type }: Props) { study={study} url={hydroMatrix.url.replace("{areaId}", areaId)} computStats={hydroMatrix.stats} + fetchFn={hydroMatrix.fetchFn} + disableEdit={hydroMatrix.disableEdit} /> ); diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/HydroMatrixDialog.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/HydroMatrixDialog.tsx new file mode 100644 index 0000000000..d91c2a9f18 --- /dev/null +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/HydroMatrixDialog.tsx @@ -0,0 +1,43 @@ +import { Button, Box } from "@mui/material"; +import { useTranslation } from "react-i18next"; +import BasicDialog, { + BasicDialogProps, +} from "../../../../../../common/dialogs/BasicDialog"; +import HydroMatrix from "./HydroMatrix"; +import { HydroMatrixType } from "./utils"; + +interface HydroMatrixDialogProps { + open: boolean; + onClose: () => void; + type: HydroMatrixType; +} + +function HydroMatrixDialog({ open, onClose, type }: HydroMatrixDialogProps) { + const { t } = useTranslation(); + const dialogProps: BasicDialogProps = { + open, + onClose, + actions: ( + + ), + }; + + return ( + + + + + + ); +} + +export default HydroMatrixDialog; diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/InflowStructure.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/InflowStructure.tsx index ab5a150f58..e389b2406f 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/InflowStructure.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/InflowStructure.tsx @@ -1,12 +1,12 @@ import SplitLayoutView from "../../../../../../common/SplitLayoutView"; import HydroMatrix from "./HydroMatrix"; -import { MatrixType } from "./utils"; +import { HydroMatrixType } from "./utils"; function InflowStructure() { return ( } - right={} + left={} + right={} sx={{ ".SplitLayoutView__Left": { width: "50%", diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/ViewMatrixButton.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/ViewMatrixButton.tsx new file mode 100644 index 0000000000..4b82730e90 --- /dev/null +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/ViewMatrixButton.tsx @@ -0,0 +1,24 @@ +import { Button } from "@mui/material"; +import { useTranslation } from "react-i18next"; + +interface ViewMatrixButtonProps { + label: string; + onClick: () => void; +} + +function ViewMatrixButton({ label, onClick }: ViewMatrixButtonProps) { + const { t } = useTranslation(); + + return ( + + ); +} + +export default ViewMatrixButton; diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/style.ts b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/style.ts index 40ee796db3..3103e60f79 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/style.ts +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/style.ts @@ -1,4 +1,4 @@ -import { styled, Box } from "@mui/material"; +import { styled, Box, Paper } from "@mui/material"; export const Root = styled(Box)(({ theme }) => ({ width: "100%", @@ -7,3 +7,15 @@ export const Root = styled(Box)(({ theme }) => ({ display: "flex", overflow: "auto", })); + +export const FormBox = styled(Box)(({ theme }) => ({ + width: "100%", + height: "100%", + padding: theme.spacing(2), + overflow: "auto", +})); + +export const FormPaper = styled(Paper)(() => ({ + backgroundImage: + "linear-gradient(rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0.05))", +})); diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/utils.ts b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/utils.ts index 2cc92c520e..77a9fc4047 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/utils.ts +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/utils.ts @@ -1,10 +1,12 @@ -import { MatrixStats } from "../../../../../../../common/types"; +import { MatrixStats, MatrixType } from "../../../../../../../common/types"; +import { getAllocationMatrix } from "./Allocation/utils"; +import { getCorrelationMatrix } from "./Correlation/utils"; //////////////////////////////////////////////////////////////// // Enums //////////////////////////////////////////////////////////////// -export enum MatrixType { +export enum HydroMatrixType { Dailypower, EnergyCredits, ReservoirLevels, @@ -13,21 +15,27 @@ export enum MatrixType { RunOfRiver, InflowPattern, OverallMonthlyHydro, + Allocation, + Correlation, } //////////////////////////////////////////////////////////////// // Types //////////////////////////////////////////////////////////////// -interface HydroMatrixProps { +export type fetchMatrixFn = (studyId: string) => Promise; + +export interface HydroMatrixProps { title: string; url: string; cols?: string[]; rows?: string[]; stats: MatrixStats; + fetchFn?: fetchMatrixFn; + disableEdit?: boolean; } -type Matrices = Record; +type Matrices = Record; export interface HydroRoute { path: string; @@ -46,39 +54,39 @@ export interface AreaCoefficientItem { export const HYDRO_ROUTES: HydroRoute[] = [ { path: "dailypower", - type: MatrixType.Dailypower, + type: HydroMatrixType.Dailypower, }, { path: "energycredits", - type: MatrixType.EnergyCredits, + type: HydroMatrixType.EnergyCredits, }, { path: "reservoirlevels", - type: MatrixType.ReservoirLevels, + type: HydroMatrixType.ReservoirLevels, }, { path: "watervalues", - type: MatrixType.WaterValues, + type: HydroMatrixType.WaterValues, }, { path: "hydrostorage", - type: MatrixType.HydroStorage, + type: HydroMatrixType.HydroStorage, }, { path: "ror", - type: MatrixType.RunOfRiver, + type: HydroMatrixType.RunOfRiver, }, ]; export const MATRICES: Matrices = { - [MatrixType.Dailypower]: { + [HydroMatrixType.Dailypower]: { title: "Daily power", url: "input/hydro/common/capacity/creditmodulations_{areaId}", cols: generateColumns(), rows: ["Generating Power", "Pumping Power"], stats: MatrixStats.NOCOL, }, - [MatrixType.EnergyCredits]: { + [HydroMatrixType.EnergyCredits]: { title: "Standard credit", url: "input/hydro/common/capacity/maxpower_{areaId}", cols: [ @@ -89,35 +97,35 @@ export const MATRICES: Matrices = { ], stats: MatrixStats.NOCOL, }, - [MatrixType.ReservoirLevels]: { + [HydroMatrixType.ReservoirLevels]: { title: "Reservoir levels", url: "input/hydro/common/capacity/reservoir_{areaId}", cols: ["Lev Low (p.u)", "Lev Avg (p.u)", "Lev High (p.u)"], stats: MatrixStats.NOCOL, }, - [MatrixType.WaterValues]: { + [HydroMatrixType.WaterValues]: { title: "Water values", url: "input/hydro/common/capacity/waterValues_{areaId}", cols: generateColumns("%"), stats: MatrixStats.NOCOL, }, - [MatrixType.HydroStorage]: { + [HydroMatrixType.HydroStorage]: { title: "Hydro storage", url: "input/hydro/series/{areaId}/mod", stats: MatrixStats.STATS, }, - [MatrixType.RunOfRiver]: { + [HydroMatrixType.RunOfRiver]: { title: "Run of river", url: "input/hydro/series/{areaId}/ror", stats: MatrixStats.STATS, }, - [MatrixType.InflowPattern]: { + [HydroMatrixType.InflowPattern]: { title: "Inflow pattern", url: "input/hydro/common/capacity/inflowPattern_{areaId}", cols: ["Inflow Pattern (X)"], stats: MatrixStats.NOCOL, }, - [MatrixType.OverallMonthlyHydro]: { + [HydroMatrixType.OverallMonthlyHydro]: { title: "Overall monthly hydro", url: "input/hydro/prepro/{areaId}/energy", cols: [ @@ -129,7 +137,7 @@ export const MATRICES: Matrices = { ], rows: [ "January", - "Febuary", + "February", "March", "April", "May", @@ -143,6 +151,20 @@ export const MATRICES: Matrices = { ], stats: MatrixStats.NOCOL, }, + [HydroMatrixType.Allocation]: { + title: "Allocation", + url: "", + stats: MatrixStats.NOCOL, + fetchFn: getAllocationMatrix, + disableEdit: true, + }, + [HydroMatrixType.Correlation]: { + title: "Correlation", + url: "", + stats: MatrixStats.NOCOL, + fetchFn: getCorrelationMatrix, + disableEdit: true, + }, }; //////////////////////////////////////////////////////////////// diff --git a/webapp/src/components/common/DynamicList.tsx b/webapp/src/components/common/DynamicList.tsx index 979aac457a..43e4fbed5c 100644 --- a/webapp/src/components/common/DynamicList.tsx +++ b/webapp/src/components/common/DynamicList.tsx @@ -16,6 +16,7 @@ export interface DynamicListProps { onAdd: (value: string) => void; onDelete: (index: number) => void; allowEmpty?: boolean; + disableDelete?: (item: T) => boolean; } function DynamicList({ @@ -25,9 +26,8 @@ function DynamicList({ onAdd, onDelete, allowEmpty = true, + disableDelete, }: DynamicListProps) { - const disableDelete = items.length === 1 && !allowEmpty; - return ( @@ -38,7 +38,11 @@ function DynamicList({ onDelete(index)} - disabled={disableDelete} + disabled={ + // Disable the delete based on the provided disableDelete function or if there's only one item and allowEmpty is false + (disableDelete && disableDelete(item)) || + (items.length === 1 && !allowEmpty) + } > diff --git a/webapp/src/components/common/EditableMatrix/index.tsx b/webapp/src/components/common/EditableMatrix/index.tsx index 0c9249f417..ca3ac5608d 100644 --- a/webapp/src/components/common/EditableMatrix/index.tsx +++ b/webapp/src/components/common/EditableMatrix/index.tsx @@ -119,10 +119,8 @@ function EditableMatrix(props: PropTypes) { const tmpData = data.map((row, i) => { let tmpRow = row as (string | number)[]; - if (prependIndex) { - if (matrixIndex) { - tmpRow = [createDateFromIndex(i, matrixIndex)].concat(row); - } + if (prependIndex && matrixIndex) { + tmpRow = [createDateFromIndex(i, matrixIndex)].concat(row); } if (computStats) { tmpRow = tmpRow.concat( @@ -143,6 +141,9 @@ function EditableMatrix(props: PropTypes) { computStats, ]); + const matrixRowNames = + rowNames || (matrixIndex && index.map((i) => String(i))); + //////////////////////////////////////////////////////////////// // JSX //////////////////////////////////////////////////////////////// @@ -158,13 +159,13 @@ function EditableMatrix(props: PropTypes) { stretchH="all" className="editableMatrix" colHeaders - rowHeaderWidth={rowNames ? 150 : undefined} + rowHeaderWidth={matrixRowNames ? 150 : undefined} afterChange={(change, source) => onUpdate && handleSlice(change || [], source) } beforeKeyDown={(e) => handleKeyDown(e)} columns={formatedColumns} - rowHeaders={rowNames || true} + rowHeaders={matrixRowNames || true} manualColumnResize /> ) : ( diff --git a/webapp/src/components/common/MatrixInput/index.tsx b/webapp/src/components/common/MatrixInput/index.tsx index cd573b1fd8..191e792502 100644 --- a/webapp/src/components/common/MatrixInput/index.tsx +++ b/webapp/src/components/common/MatrixInput/index.tsx @@ -33,20 +33,31 @@ import EditableMatrix from "../EditableMatrix"; import ImportDialog from "../dialogs/ImportDialog"; import MatrixAssignDialog from "./MatrixAssignDialog"; import { downloadMatrix } from "../../../utils/matrixUtils"; +import { fetchMatrixFn } from "../../App/Singlestudy/explore/Modelization/Areas/Hydro/utils"; const logErr = debug("antares:createimportform:error"); -interface PropsType { +interface MatrixInputProps { study: StudyMetadata; url: string; columnsNames?: string[]; rowNames?: string[]; title?: string; computStats: MatrixStats; + fetchFn?: fetchMatrixFn; + disableEdit?: boolean; } -function MatrixInput(props: PropsType) { - const { study, url, columnsNames, rowNames, title, computStats } = props; +function MatrixInput({ + study, + url, + columnsNames, + rowNames: initialRowNames, + title, + computStats, + fetchFn, + disableEdit, +}: MatrixInputProps) { const { enqueueSnackbar } = useSnackbar(); const enqueueErrorSnackbar = useEnqueueErrorSnackbar(); const [t] = useTranslation(); @@ -55,12 +66,14 @@ function MatrixInput(props: PropsType) { const [openMatrixAsignDialog, setOpenMatrixAsignDialog] = useState(false); const { - data, + data: matrixData, isLoading, reload: reloadMatrix, } = usePromiseWithSnackbarError( async () => { - const res = await getStudyData(study.id, url); + const res = fetchFn + ? await fetchFn(study.id) + : await getStudyData(study.id, url); if (typeof res === "string") { const fixed = res .replace(/NaN/g, '"NaN"') @@ -75,14 +88,33 @@ function MatrixInput(props: PropsType) { } ); + /** + * This hook fetches the matrix index either from the matrixData or from the API. + * If the fetchFn is provided, it means that we have custom row names (area names), + * instead of the default row numbers and timestamps. + * In this case, we get the row names from the matrixData's index property. + */ const { data: matrixIndex } = usePromiseWithSnackbarError( - () => getStudyMatrixIndex(study.id, url), + async () => { + if (fetchFn) { + return matrixData?.index; + } + return getStudyMatrixIndex(study.id, url); + }, { errorMessage: t("matrix.error.failedToretrieveIndex"), - deps: [study, url], + deps: [study, url, fetchFn, matrixData], } ); + /** + * The rowNames variable determines which row names to display in the EditableMatrix component. + * If the fetchFn is provided, it means we have custom row names (area names), so we use + * the matrixIndex fetched from the matrixData to display area names. + * Otherwise, we use the initialRowNames to display the default row numbers and timestamps. + */ + const rowNames = fetchFn ? matrixIndex : initialRowNames; + //////////////////////////////////////////////////////////////// // Event Handlers //////////////////////////////////////////////////////////////// @@ -139,7 +171,7 @@ function MatrixInput(props: PropsType) { {title || t("xpansion.timeSeries")} - {!isLoading && data?.columns?.length >= 1 && ( + {!isLoading && matrixData?.columns?.length >= 1 && ( setToggleView((prev) => !prev)}> {toggleView ? ( @@ -170,7 +202,7 @@ function MatrixInput(props: PropsType) { > {t("global.import")} - {data?.columns?.length >= 1 && ( + {matrixData?.columns?.length >= 1 && ( + + {matrixData?.columns?.length >= 1 && (

@@ -100,7 +100,8 @@ function Fields() { onChange={(_, checked) => { setValue( `${type}.stochasticTsStatus`, - !checked as never + !checked as never, + { shouldDirty: true } ); }} /> diff --git a/webapp/src/components/App/Singlestudy/explore/Configuration/TimeSeriesManagement/index.tsx b/webapp/src/components/App/Singlestudy/explore/Configuration/TimeSeriesManagement/index.tsx index f08f29d972..8d95b55ab0 100644 --- a/webapp/src/components/App/Singlestudy/explore/Configuration/TimeSeriesManagement/index.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Configuration/TimeSeriesManagement/index.tsx @@ -29,7 +29,6 @@ function TimeSeriesManagement() { key={study.id} config={{ defaultValues: () => getTimeSeriesFormFields(study.id) }} onSubmit={handleSubmit} - autoSubmit > diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/ManagementOptions/index.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/ManagementOptions/index.tsx index 61a58c0881..b10c822574 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/ManagementOptions/index.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/ManagementOptions/index.tsx @@ -36,7 +36,7 @@ function ManagementOptions() { defaultValues: () => getManagementOptionsFormFields(studyId, areaId), }} onSubmit={handleSubmit} - autoSubmit + sx={{ pb: 2 }} > diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Properties/index.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Properties/index.tsx index 970e67c9e3..3fad1f31e5 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Properties/index.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Properties/index.tsx @@ -20,7 +20,6 @@ function Properties() { config={{ defaultValues: () => getDefaultValues(study.id, currentArea, t), }} - autoSubmit > {(formApi) => ( Date: Thu, 11 May 2023 17:17:08 +0200 Subject: [PATCH 59/70] fix(api): wrong types in adequacy patch manager --- .../business/adequacy_patch_management.py | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/antarest/study/business/adequacy_patch_management.py b/antarest/study/business/adequacy_patch_management.py index 953197d5fc..836d6804fe 100644 --- a/antarest/study/business/adequacy_patch_management.py +++ b/antarest/study/business/adequacy_patch_management.py @@ -1,7 +1,7 @@ from enum import Enum -from typing import Optional, List, Any, Dict, Union +from typing import Optional, List, Any, Dict -from pydantic.types import StrictBool, StrictFloat, StrictInt +from pydantic.types import StrictBool, confloat from antarest.study.business.utils import ( FormFieldsBaseModel, @@ -21,6 +21,9 @@ class PriceTakingOrder(str, Enum): LOAD = "Load" +ThresholdType = confloat(ge=0) + + class AdequacyPatchFormFields(FormFieldsBaseModel): # version 830 enable_adequacy_patch: Optional[StrictBool] @@ -32,15 +35,9 @@ class AdequacyPatchFormFields(FormFieldsBaseModel): price_taking_order: Optional[PriceTakingOrder] include_hurdle_cost_csr: Optional[StrictBool] check_csr_cost_function: Optional[StrictBool] - threshold_initiate_curtailment_sharing_rule: Optional[ - Union[StrictFloat, StrictInt] - ] - threshold_display_local_matching_rule_violations: Optional[ - Union[StrictFloat, StrictInt] - ] - threshold_csr_variable_bounds_relaxation: Optional[ - Union[StrictFloat, StrictInt] - ] + threshold_initiate_curtailment_sharing_rule: Optional[ThresholdType] # type: ignore + threshold_display_local_matching_rule_violations: Optional[ThresholdType] # type: ignore + threshold_csr_variable_bounds_relaxation: Optional[ThresholdType] # type: ignore ADEQUACY_PATCH_PATH = f"{GENERAL_DATA_PATH}/adequacy patch" From 38e7f1bd7e5205599f2d756051cd941ff3e21562 Mon Sep 17 00:00:00 2001 From: Samir Kamal <1954121+skamril@users.noreply.github.com> Date: Thu, 11 May 2023 18:00:43 +0200 Subject: [PATCH 60/70] fix(api): wrong end version for filtering field in general manager --- antarest/study/business/general_management.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/antarest/study/business/general_management.py b/antarest/study/business/general_management.py index 25780f2fb7..e0e1544cf3 100644 --- a/antarest/study/business/general_management.py +++ b/antarest/study/business/general_management.py @@ -175,7 +175,7 @@ def day_fields_validation(cls, values: Dict[str, Any]) -> Dict[str, Any]: "filtering": { "path": f"{GENERAL_PATH}/filtering", "default_value": False, - "end_version": 700, + "end_version": 710, }, "geographic_trimming": { "path": f"{GENERAL_PATH}/geographic-trimming", @@ -217,10 +217,10 @@ def get_value(field_name: str, field_info: FieldInfo) -> Any: path = field_info["path"] study_ver = file_study.config.version - start_ver = field_info.get("start_version", -1) - end_ver = field_info.get("end_version", study_ver) + start_ver = cast(int, field_info.get("start_version", -1)) + end_ver = cast(int, field_info.get("end_version", study_ver + 1)) target_name = path.split("/")[-1] - is_in_version = start_ver <= study_ver <= end_ver # type: ignore + is_in_version = start_ver <= study_ver < end_ver parent = general if GENERAL_PATH in path else output return ( From 9bbb26465e3b4165ce082840b3fda20e0b851786 Mon Sep 17 00:00:00 2001 From: Samir Kamal <1954121+skamril@users.noreply.github.com> Date: Thu, 11 May 2023 17:19:30 +0200 Subject: [PATCH 61/70] style(api): clean code --- antarest/study/business/optimization_management.py | 7 ++++--- antarest/study/business/thematic_trimming_management.py | 7 +++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/antarest/study/business/optimization_management.py b/antarest/study/business/optimization_management.py index 401aed6497..3339347081 100644 --- a/antarest/study/business/optimization_management.py +++ b/antarest/study/business/optimization_management.py @@ -1,5 +1,5 @@ from enum import Enum -from typing import Optional, Union, List, Any, Dict +from typing import Optional, Union, List, Any, Dict, cast from pydantic.types import StrictBool @@ -129,9 +129,10 @@ def get_field_values(self, study: Study) -> OptimizationFormFields: def get_value(field_info: FieldInfo) -> Any: path = field_info["path"] - start_version = field_info.get("start_version", -1) + study_ver = file_study.config.version + start_ver = cast(int, field_info.get("start_version", -1)) target_name = path.split("/")[-1] - is_in_version = file_study.config.version >= start_version # type: ignore + is_in_version = start_ver <= study_ver return ( parent.get(target_name, field_info["default_value"]) diff --git a/antarest/study/business/thematic_trimming_management.py b/antarest/study/business/thematic_trimming_management.py index 691ef35f01..5a455dbc11 100644 --- a/antarest/study/business/thematic_trimming_management.py +++ b/antarest/study/business/thematic_trimming_management.py @@ -1,4 +1,4 @@ -from typing import Optional, Dict, Any, List +from typing import Optional, Dict, Any, List, cast from pydantic.types import StrictBool @@ -235,10 +235,9 @@ def set_field_values( keys_by_bool: Dict[bool, List[Any]] = {True: [], False: []} for name, info in FIELDS_INFO.items(): - start_ver = info.get("start_version", 0) - end_ver = info.get("end_version", study_ver) + start_ver = cast(int, info.get("start_version", 0)) - if start_ver <= study_ver <= end_ver: # type: ignore + if start_ver <= study_ver: keys_by_bool[field_values_dict[name]].append(info["path"]) config_data: Dict[str, Any] From a3b0c1451ee8b09a056349b49825048e760aaa35 Mon Sep 17 00:00:00 2001 From: Samir Kamal <1954121+skamril@users.noreply.github.com> Date: Thu, 11 May 2023 17:21:50 +0200 Subject: [PATCH 62/70] fix(config): add study version check for fields in adequacy patch form --- .../Configuration/AdequacyPatch/Fields.tsx | 127 +++++++++++------- 1 file changed, 79 insertions(+), 48 deletions(-) diff --git a/webapp/src/components/App/Singlestudy/explore/Configuration/AdequacyPatch/Fields.tsx b/webapp/src/components/App/Singlestudy/explore/Configuration/AdequacyPatch/Fields.tsx index 9faf800cf2..91bd9bd2d7 100644 --- a/webapp/src/components/App/Singlestudy/explore/Configuration/AdequacyPatch/Fields.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Configuration/AdequacyPatch/Fields.tsx @@ -1,15 +1,19 @@ import { Box } from "@mui/material"; import { useTranslation } from "react-i18next"; +import { useOutletContext } from "react-router"; import NumberFE from "../../../../../common/fieldEditors/NumberFE"; import SelectFE from "../../../../../common/fieldEditors/SelectFE"; import SwitchFE from "../../../../../common/fieldEditors/SwitchFE"; import Fieldset from "../../../../../common/Fieldset"; import { useFormContextPlus } from "../../../../../common/Form"; import { AdequacyPatchFormFields, PRICE_TAKING_ORDER_OPTIONS } from "./utils"; +import { StudyMetadata } from "../../../../../../common/types"; function Fields() { const { t } = useTranslation(); const { control } = useFormContextPlus(); + const { study } = useOutletContext<{ study: StudyMetadata }>(); + const studyVersion = Number(study.version); return ( @@ -42,55 +46,82 @@ function Fields() { control={control} /> -
- - -
-
= 850 && ( + <> +
+ + +
+ +
- - - - - -
+ > + + + + + +
+ + )}
); } From d26a8161b95c2b1eff4e17bc09cf87a8a7265241 Mon Sep 17 00:00:00 2001 From: Samir Kamal <1954121+skamril@users.noreply.github.com> Date: Thu, 11 May 2023 17:22:14 +0200 Subject: [PATCH 63/70] fix(common): add fieldWidth prop to Fieldset --- webapp/src/components/common/Fieldset.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/webapp/src/components/common/Fieldset.tsx b/webapp/src/components/common/Fieldset.tsx index 7c38752cc4..a0ea3c7444 100644 --- a/webapp/src/components/common/Fieldset.tsx +++ b/webapp/src/components/common/Fieldset.tsx @@ -7,6 +7,7 @@ interface FieldsetProps extends Omit { children: React.ReactNode; contentProps?: BoxProps; fullFieldWidth?: boolean; + fieldWidth?: number; } function Fieldset(props: FieldsetProps) { @@ -16,6 +17,7 @@ function Fieldset(props: FieldsetProps) { sx, contentProps, fullFieldWidth = false, + fieldWidth = 220, ...rest } = props; @@ -34,7 +36,7 @@ function Fieldset(props: FieldsetProps) { flexWrap: "wrap", gap: 2, ".MuiFormControl-root": { - width: fullFieldWidth ? 1 : 220, + width: fullFieldWidth ? 1 : fieldWidth, }, }, }, From 79cbb9db1c035bc0b75461a5036af218e85ebb18 Mon Sep 17 00:00:00 2001 From: Samir Kamal <1954121+skamril@users.noreply.github.com> Date: Thu, 11 May 2023 17:23:10 +0200 Subject: [PATCH 64/70] feat(config): change some field sizes in adequacy patch form --- .../Singlestudy/explore/Configuration/AdequacyPatch/Fields.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/src/components/App/Singlestudy/explore/Configuration/AdequacyPatch/Fields.tsx b/webapp/src/components/App/Singlestudy/explore/Configuration/AdequacyPatch/Fields.tsx index 91bd9bd2d7..5c8ddec03c 100644 --- a/webapp/src/components/App/Singlestudy/explore/Configuration/AdequacyPatch/Fields.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Configuration/AdequacyPatch/Fields.tsx @@ -70,7 +70,7 @@ function Fields() {
Date: Fri, 12 May 2023 09:39:34 +0200 Subject: [PATCH 65/70] feat(ui-study): prevent upgrade for latest study version (#1509) --- .../components/App/Singlestudy/NavHeader.tsx | 48 +++++++++++-------- .../src/components/App/Singlestudy/index.tsx | 6 ++- webapp/src/redux/selectors.ts | 7 +++ 3 files changed, 39 insertions(+), 22 deletions(-) diff --git a/webapp/src/components/App/Singlestudy/NavHeader.tsx b/webapp/src/components/App/Singlestudy/NavHeader.tsx index e48aa5188c..eba5b34b97 100644 --- a/webapp/src/components/App/Singlestudy/NavHeader.tsx +++ b/webapp/src/components/App/Singlestudy/NavHeader.tsx @@ -52,7 +52,10 @@ import { displayVersionName, } from "../../../services/utils"; import useEnqueueErrorSnackbar from "../../../hooks/useEnqueueErrorSnackbar"; -import { isCurrentStudyFavorite } from "../../../redux/selectors"; +import { + getLatestStudyVersion, + isCurrentStudyFavorite, +} from "../../../redux/selectors"; import ExportDialog from "../Studies/ExportModal"; import StarToggle from "../../common/StarToggle"; import ConfirmationDialog from "../../common/dialogs/ConfirmationDialog"; @@ -102,6 +105,9 @@ function NavHeader(props: Props) { } = props; const [t, i18n] = useTranslation(); const navigate = useNavigate(); + const dispatch = useAppDispatch(); + const isStudyFavorite = useAppSelector(isCurrentStudyFavorite); + const latestVersion = useAppSelector(getLatestStudyVersion); const [anchorEl, setAnchorEl] = useState(null); const [openMenu, setOpenMenu] = useState(""); const [openLauncherDialog, setOpenLauncherDialog] = useState(false); @@ -112,9 +118,7 @@ function NavHeader(props: Props) { const [openExportDialog, setOpenExportDialog] = useState(false); const { enqueueSnackbar } = useSnackbar(); const enqueueErrorSnackbar = useEnqueueErrorSnackbar(); - const isStudyFavorite = useAppSelector(isCurrentStudyFavorite); - const dispatch = useAppDispatch(); - + const isLatestVersion = study?.version === latestVersion; const publicModeLabel = PUBLIC_MODE_LIST.find((mode) => mode.id === study?.publicMode)?.name || ""; @@ -410,23 +414,25 @@ function NavHeader(props: Props) { {t("study.properties")} - { - setOpenUpgradeDialog(true); - handleClose(); - }} - > - - - - {t("study.upgrade")} - + {!isLatestVersion && ( + { + setOpenUpgradeDialog(true); + handleClose(); + }} + > + + + + {t("study.upgrade")} + + )} { setOpenExportDialog(true); diff --git a/webapp/src/components/App/Singlestudy/index.tsx b/webapp/src/components/App/Singlestudy/index.tsx index 63485d5fa9..06a59b4bd7 100644 --- a/webapp/src/components/App/Singlestudy/index.tsx +++ b/webapp/src/components/App/Singlestudy/index.tsx @@ -20,7 +20,10 @@ import { } from "../../../services/api/variant"; import TabWrapper from "./explore/TabWrapper"; import HomeView from "./HomeView"; -import { setCurrentStudy } from "../../../redux/ducks/studies"; +import { + fetchStudyVersions, + setCurrentStudy, +} from "../../../redux/ducks/studies"; import { findNodeInTree } from "../../../services/utils"; import CommandDrawer from "./Commands"; import { addWsMessageListener } from "../../../services/webSockets"; @@ -106,6 +109,7 @@ function SingleStudy(props: Props) { const init = async () => { if (studyId) { dispatch(setCurrentStudy(studyId)); + dispatch(fetchStudyVersions()); updateStudyData(); } }; diff --git a/webapp/src/redux/selectors.ts b/webapp/src/redux/selectors.ts index d7fa749a0a..46a02cf7f3 100644 --- a/webapp/src/redux/selectors.ts +++ b/webapp/src/redux/selectors.ts @@ -1,4 +1,5 @@ import { createEntityAdapter, createSelector } from "@reduxjs/toolkit"; +import { last } from "ramda"; import { AllClustersAndLinks, Area, @@ -124,6 +125,12 @@ export const getStudyVersions = ( return getStudiesState(state).versionList; }; +export const getLatestStudyVersion = ( + state: AppState +): StudyMetadata["version"] | null => { + return last(getStudyVersions(state)) ?? null; +}; + export const getStudyVersionsFormatted = createSelector( getStudyVersions, convertVersions From 535ef883eeb2745173dddaecda62fad486cb0dae Mon Sep 17 00:00:00 2001 From: Laurent LAPORTE Date: Tue, 9 May 2023 09:37:43 +0200 Subject: [PATCH 66/70] build: prepare next minor release v2.14 --- antarest/__init__.py | 4 ++-- setup.py | 2 +- sonar-project.properties | 2 +- webapp/package-lock.json | 2 +- webapp/package.json | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/antarest/__init__.py b/antarest/__init__.py index 4533cd61d1..be3c0ef92d 100644 --- a/antarest/__init__.py +++ b/antarest/__init__.py @@ -7,9 +7,9 @@ # Standard project metadata -__version__ = "2.13.2" +__version__ = "2.14.0" __author__ = "RTE, Antares Web Team" -__date__ = "2023-04-25" +__date__ = "(unreleased))" # noinspection SpellCheckingInspection __credits__ = "(c) Réseau de Transport de l’Électricité (RTE)" diff --git a/setup.py b/setup.py index 22f85d9bbf..4db7d9b83b 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setuptools.setup( name="AntaREST", - version="2.13.2", + version="2.14.0", description="Antares Server", long_description=long_description, long_description_content_type="text/markdown", diff --git a/sonar-project.properties b/sonar-project.properties index 48f76a506c..efb731da29 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -6,5 +6,5 @@ sonar.exclusions=antarest/gui.py,antarest/main.py sonar.python.coverage.reportPaths=coverage.xml sonar.python.version=3.8 sonar.javascript.lcov.reportPaths=webapp/coverage/lcov.info -sonar.projectVersion=2.13.2 +sonar.projectVersion=2.14.0 sonar.coverage.exclusions=antarest/gui.py,antarest/main.py,antarest/singleton_services.py,antarest/worker/archive_worker_service.py,webapp/**/* \ No newline at end of file diff --git a/webapp/package-lock.json b/webapp/package-lock.json index 73553538f1..d011f3aed9 100644 --- a/webapp/package-lock.json +++ b/webapp/package-lock.json @@ -1,6 +1,6 @@ { "name": "antares-web", - "version": "2.13.0", + "version": "2.14.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/webapp/package.json b/webapp/package.json index 0893bb9ae1..27c305dfcc 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -1,6 +1,6 @@ { "name": "antares-web", - "version": "2.13.2", + "version": "2.14.0", "private": true, "dependencies": { "@emotion/react": "11.10.6", From fd52d93de53139ab060c9614e65d44286dc1d4df Mon Sep 17 00:00:00 2001 From: Laurent LAPORTE Date: Wed, 10 May 2023 09:36:36 +0200 Subject: [PATCH 67/70] docs: update change log to prepare the release v2.14.0 --- docs/CHANGELOG.md | 51 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index b7e038c0f2..f8752c2dac 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,6 +1,57 @@ Antares Web Changelog ===================== +v2.14.0 (unreleased) +-------------------- + +### Features + +* **api-hydro:** add allocation form endpoints ([b2bee0e](https://github.com/AntaresSimulatorTeam/AntaREST/commit/b2bee0ed8e9817da2ed642474504fb25a95a8360)) +* **api:** update optimization form endpoint and add adequacy patch form endpoint ([dfa1b27](https://github.com/AntaresSimulatorTeam/AntaREST/commit/dfa1b2729ddb3e46f3b7f65a4a0079211da2c69c)) +* **ui-config:** update optimization form and add adequacy patch form ([f68c54b](https://github.com/AntaresSimulatorTeam/AntaREST/commit/f68c54b9b846d32e65d32c14c8931c625a6bd498)) +* **ui-hydro:** add allocation form ([5dbb85f](https://github.com/AntaresSimulatorTeam/AntaREST/commit/5dbb85fdc733731c5fc16a258666869486b5cddf)) +* **ui-hydro:** add inflow structure tab ([a466e34](https://github.com/AntaresSimulatorTeam/AntaREST/commit/a466e3459e25ece8f2d80c8eb501ba05c717d5fa)) +* **ui-hydro:** add row names ([94dc38c](https://github.com/AntaresSimulatorTeam/AntaREST/commit/94dc38c1fe2f5163f6b44dc31cc3639e63cd2131)) +* **ui-hydro:** display area name instead of ID ([0df0b21](https://github.com/AntaresSimulatorTeam/AntaREST/commit/0df0b2121e761a91946452874d70bc80dbe07647)) +* **ui-hydro:** update allocation form styles ([ac470c1](https://github.com/AntaresSimulatorTeam/AntaREST/commit/ac470c19410bf2d13b57ecc0bab650b24b77c495)) +* **ui-matrix:** update "Time" column and add index row headers ([3d50bf9](https://github.com/AntaresSimulatorTeam/AntaREST/commit/3d50bf9617367fe8d1fcd21e6a9835834456a10f)) +* **ui:** add @total-typescript/ts-reset lib and tsUtils (#1408) ([aa5e3e8](https://github.com/AntaresSimulatorTeam/AntaREST/commit/aa5e3e87d95b8b5061030025e89443e1fc71823d)) +* **ui:** update react-hook-form lib and use the new API (#1444) ([1d129d9](https://github.com/AntaresSimulatorTeam/AntaREST/commit/1d129d9d6bac97deee9ebc98d3334117fe837444)) + + +### Bug Fixes + +* **common:** field array change doesn't trigger on auto submit (#1439) ([910db64](https://github.com/AntaresSimulatorTeam/AntaREST/commit/910db64ca872468a1f01ced99083962022daa05c)) +* **matrix:** correct the frequency of some matrices (#1384) ([2644416](https://github.com/AntaresSimulatorTeam/AntaREST/commit/26444169b9ab60f54e8ee7a2d16fb10dbc4d537e)) +* **ui-common:** add matrices float handling ([99ba81f](https://github.com/AntaresSimulatorTeam/AntaREST/commit/99ba81fce26bbd99340990d0207761463558d4a7)) +* **ui-hydro:** correct column names ([e529a79](https://github.com/AntaresSimulatorTeam/AntaREST/commit/e529a799071e9c5485e2cba35eb5a7c2c18c25e7)) +* **ui-hydro:** update hydro matrices columns ([56641d7](https://github.com/AntaresSimulatorTeam/AntaREST/commit/56641d7ad995d8b7dd6755b13f1689b32b6296d8)) +* **ui:** fix typo on error page (#1390) ([da00131](https://github.com/AntaresSimulatorTeam/AntaREST/commit/da0013190d7e31e1afe9d8f5c3b03c378ca41507)) +* **ui:** size issue with HandsonTable ([f63edda](https://github.com/AntaresSimulatorTeam/AntaREST/commit/f63edda65345bf9848fb44a8a067a885ca5fbd83)) + + +### Styles + +* **api-tablemode:** fix typo ([5e5e4e7](https://github.com/AntaresSimulatorTeam/AntaREST/commit/5e5e4e7efcfc93e4682825a9c514417679fba89b)) +* **ui:** fix filename ([ad9f9c0](https://github.com/AntaresSimulatorTeam/AntaREST/commit/ad9f9c055713ef81a94b8c7bb01caae783ab8de9)) + + +### Documentation + +* **api:** add API documentation for the hydraulic allocation (and fix minor awkwardness) ([08680af](https://github.com/AntaresSimulatorTeam/AntaREST/commit/08680af4344b7dd9aa365267a0deb8d9094f0294)) +* **study-upgrade:** add the "How to upgrade a study?" topic in the documentation (#1400) ([2d03bef](https://github.com/AntaresSimulatorTeam/AntaREST/commit/2d03befe999e558c989e1cce1f51186beff5502b)) + +> IMPORTANT: The `antares-launcher` Git submodule is dropped. + + +### Contributors + +hdinia, +skamril, +flomnes, +laurent-laporte-pro + + v2.13.2 (2023-04-25) -------------------- From c0033fccc9e523cc6893f556a484425d34a9771b Mon Sep 17 00:00:00 2001 From: Laurent LAPORTE Date: Wed, 10 May 2023 09:46:46 +0200 Subject: [PATCH 68/70] test: skip tests which run randomly on Windows --- tests/integration/test_studies_upgrade.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/integration/test_studies_upgrade.py b/tests/integration/test_studies_upgrade.py index 644efbc6e2..1cac0c1542 100644 --- a/tests/integration/test_studies_upgrade.py +++ b/tests/integration/test_studies_upgrade.py @@ -1,3 +1,4 @@ +import os import time import pytest @@ -5,6 +6,8 @@ from fastapi import FastAPI from starlette.testclient import TestClient +RUN_ON_WINDOWS = os.name == "nt" + def wait_task_completion( client: TestClient, @@ -81,6 +84,7 @@ def fixture_study_id( study_ids = res.json() return next(iter(study_ids)) + @pytest.mark.skipif(RUN_ON_WINDOWS, reason="This test runs randomly on Windows") def test_upgrade_study__next_version( self, client: TestClient, user_access_token: str, study_id: str ): @@ -97,6 +101,7 @@ def test_upgrade_study__next_version( "710" in task.result.message ), f"Version not in {task.result.message=}" + @pytest.mark.skipif(RUN_ON_WINDOWS, reason="This test runs randomly on Windows") def test_upgrade_study__target_version( self, client: TestClient, user_access_token: str, study_id: str ): @@ -115,6 +120,7 @@ def test_upgrade_study__target_version( target_version in task.result.message ), f"Version not in {task.result.message=}" + @pytest.mark.skipif(RUN_ON_WINDOWS, reason="This test runs randomly on Windows") def test_upgrade_study__bad_target_version( self, client: TestClient, user_access_token: str, study_id: str ): From 48e0f40236621994875f4315e033939b9d9a2ce5 Mon Sep 17 00:00:00 2001 From: Laurent LAPORTE Date: Wed, 10 May 2023 10:06:05 +0200 Subject: [PATCH 69/70] style: correct indentation in test_studies_upgrade.py --- tests/integration/test_studies_upgrade.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tests/integration/test_studies_upgrade.py b/tests/integration/test_studies_upgrade.py index 1cac0c1542..1d57dda4ca 100644 --- a/tests/integration/test_studies_upgrade.py +++ b/tests/integration/test_studies_upgrade.py @@ -84,7 +84,9 @@ def fixture_study_id( study_ids = res.json() return next(iter(study_ids)) - @pytest.mark.skipif(RUN_ON_WINDOWS, reason="This test runs randomly on Windows") + @pytest.mark.skipif( + RUN_ON_WINDOWS, reason="This test runs randomly on Windows" + ) def test_upgrade_study__next_version( self, client: TestClient, user_access_token: str, study_id: str ): @@ -101,7 +103,9 @@ def test_upgrade_study__next_version( "710" in task.result.message ), f"Version not in {task.result.message=}" - @pytest.mark.skipif(RUN_ON_WINDOWS, reason="This test runs randomly on Windows") + @pytest.mark.skipif( + RUN_ON_WINDOWS, reason="This test runs randomly on Windows" + ) def test_upgrade_study__target_version( self, client: TestClient, user_access_token: str, study_id: str ): @@ -120,7 +124,9 @@ def test_upgrade_study__target_version( target_version in task.result.message ), f"Version not in {task.result.message=}" - @pytest.mark.skipif(RUN_ON_WINDOWS, reason="This test runs randomly on Windows") + @pytest.mark.skipif( + RUN_ON_WINDOWS, reason="This test runs randomly on Windows" + ) def test_upgrade_study__bad_target_version( self, client: TestClient, user_access_token: str, study_id: str ): From 74768e06a94ba422d2bef08851e53628e23caa2b Mon Sep 17 00:00:00 2001 From: Laurent LAPORTE Date: Fri, 12 May 2023 17:21:53 +0200 Subject: [PATCH 70/70] build: next minor release v2.14 (2023-05-12) --- antarest/__init__.py | 2 +- docs/CHANGELOG.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/antarest/__init__.py b/antarest/__init__.py index be3c0ef92d..3731beda17 100644 --- a/antarest/__init__.py +++ b/antarest/__init__.py @@ -9,7 +9,7 @@ __version__ = "2.14.0" __author__ = "RTE, Antares Web Team" -__date__ = "(unreleased))" +__date__ = "2023-05-12" # noinspection SpellCheckingInspection __credits__ = "(c) Réseau de Transport de l’Électricité (RTE)" diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index f8752c2dac..79c79672a8 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,7 +1,7 @@ Antares Web Changelog ===================== -v2.14.0 (unreleased) +v2.14.0 (2023-05-12) -------------------- ### Features