diff --git a/antarest/__init__.py b/antarest/__init__.py index bd53e7a23c..4023e2ca7b 100644 --- a/antarest/__init__.py +++ b/antarest/__init__.py @@ -7,9 +7,9 @@ # Standard project metadata -__version__ = "2.15.0" +__version__ = "2.15.1" __author__ = "RTE, Antares Web Team" -__date__ = "2023-09-30" +__date__ = "2023-10-05" # noinspection SpellCheckingInspection __credits__ = "(c) Réseau de Transport de l’Électricité (RTE)" diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index cd2ae9ff87..e6d96180aa 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,6 +1,29 @@ Antares Web Changelog ===================== +v2.15.1 (2023-10-05) +-------------------- + +### Features + +* **ui-results:** move filters on top of matrices [`#1751`](https://github.com/AntaresSimulatorTeam/AntaREST/pull/1751) +* **ui-outputs:** add outputs synthesis view [`#1737`](https://github.com/AntaresSimulatorTeam/AntaREST/pull/1737) + + +### Bug Fixes + +* **api:** allow `NaN`, `+Infinity`, and `-Infinity` values in JSON response [`7394248`](https://github.com/AntaresSimulatorTeam/AntaREST/commit/7394248821ad5e2e8e5b51d389896c745740225d) +* **ui-xpansion:** display issue in form [`#1754`](https://github.com/AntaresSimulatorTeam/AntaREST/pull/1754) +* **raw:** impossible to see matrix containing NaN values [`#1714`](https://github.com/AntaresSimulatorTeam/AntaREST/pull/1714) +* **raw:** allow NaN in matrices [`0cad1a9`](https://github.com/AntaresSimulatorTeam/AntaREST/commit/0cad1a969fd14e81cf502aecb821df4b2d7abcb6) + + +### Tests + +* **tests:** add a test to ensure GET `/raw` endpoint reads NaN values [`29b1f71`](https://github.com/AntaresSimulatorTeam/AntaREST/commit/29b1f71856463542dcc0170fe97bc6832ec4a72a) + + + v2.15.0 (2023-09-30) -------------------- diff --git a/setup.py b/setup.py index 323d8deb7b..92e4e34f98 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setup( name="AntaREST", - version="2.15.0", + version="2.15.1", description="Antares Server", long_description=Path("README.md").read_text(encoding="utf-8"), long_description_content_type="text/markdown", diff --git a/sonar-project.properties b/sonar-project.properties index 3fb85c7559..9bf66c1c14 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.15.0 +sonar.projectVersion=2.15.1 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 8a8edb5eb3..d6b5e9be0a 100644 --- a/webapp/package-lock.json +++ b/webapp/package-lock.json @@ -1,12 +1,12 @@ { "name": "antares-web", - "version": "2.15.0", + "version": "2.15.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "antares-web", - "version": "2.15.0", + "version": "2.15.1", "dependencies": { "@emotion/react": "11.11.1", "@emotion/styled": "11.11.0", diff --git a/webapp/package.json b/webapp/package.json index a4ab0b1bd6..4b108a3e0f 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -1,6 +1,6 @@ { "name": "antares-web", - "version": "2.15.0", + "version": "2.15.1", "private": true, "engines": { "node": "18.16.1" diff --git a/webapp/public/locales/en/main.json b/webapp/public/locales/en/main.json index f7651f99b1..e966640063 100644 --- a/webapp/public/locales/en/main.json +++ b/webapp/public/locales/en/main.json @@ -479,7 +479,6 @@ "study.modelization.bindingConst.question.deleteConstraintTerm": "Are you sure you want to delete this constraint term?", "study.modelization.bindingConst.question.deleteBindingConstraint": "Are you sure you want to delete this binding constraint?", "study.results.mc": "Monte-Carlo", - "study.results.mc.year": "Year", "study.results.display": "Display", "study.results.temporality": "Temporality", "study.results.noData": "No data available", diff --git a/webapp/public/locales/fr/main.json b/webapp/public/locales/fr/main.json index d60556d964..90a338597b 100644 --- a/webapp/public/locales/fr/main.json +++ b/webapp/public/locales/fr/main.json @@ -110,7 +110,7 @@ "form.submit.inProgress": "Le formulaire est en cours de soumission. Etes-vous sûr de vouloir quitter la page ?", "form.asyncDefaultValues.error": "Impossible d'obtenir les valeurs", "form.field.required": "Champ requis", - "form.field.duplicate": "Cette valeur existe déjà: {{0}}", + "form.field.duplicate": "Cette valeur existe déjà: {{0}}", "form.field.minLength": "{{0}} caractère(s) minimum", "form.field.minValue": "La valeur minimum est {{0}}", "form.field.maxValue": "La valeur maximum est {{0}}", @@ -198,7 +198,7 @@ "study.timeLimitHelper": "Limite de temps en heures (max: {{max}}h)", "study.nbCpu": "Nombre de coeurs", "study.clusterLoad": "Charge du cluster", - "study.synthesis": "Synthesis", + "study.synthesis": "Synthèse", "study.level": "Niveau", "study.years": "Années", "study.type": "Type", @@ -479,7 +479,6 @@ "study.modelization.bindingConst.question.deleteConstraintTerm": "Êtes-vous sûr de vouloir supprimer ce terme ?", "study.modelization.bindingConst.question.deleteBindingConstraint": "Êtes-vous sûr de vouloir supprimer cette contrainte couplante ?", "study.results.mc": "Monte-Carlo", - "study.results.mc.year": "année", "study.results.display": "Affichage", "study.results.temporality": "Temporalité", "study.results.noData": "Aucune donnée disponible", diff --git a/webapp/src/components/App/Singlestudy/explore/Results/ResultDetails/SelectionDrawer.tsx b/webapp/src/components/App/Singlestudy/explore/Results/ResultDetails/SelectionDrawer.tsx deleted file mode 100644 index da3aef3229..0000000000 --- a/webapp/src/components/App/Singlestudy/explore/Results/ResultDetails/SelectionDrawer.tsx +++ /dev/null @@ -1,124 +0,0 @@ -import { Box, Button, Drawer, RadioGroup } from "@mui/material"; -import { useEffect, useState } from "react"; -import { useTranslation } from "react-i18next"; -import BooleanFE from "../../../../../common/fieldEditors/BooleanFE"; -import NumberFE from "../../../../../common/fieldEditors/NumberFE"; -import RadioFE from "../../../../../common/fieldEditors/RadioFE"; -import Fieldset from "../../../../../common/Fieldset"; -import { DataType, MAX_YEAR, Timestep } from "./utils"; - -export interface SelectionDrawerProps { - open: boolean; - onClose: () => void; - values: { - dataType: DataType; - timestep: Timestep; - year: number; - }; - maxYear?: number; - onSelection: (values: SelectionDrawerProps["values"]) => void; -} - -function SelectionDrawer(props: SelectionDrawerProps) { - const { open, onClose, values, maxYear = MAX_YEAR, onSelection } = props; - const [dataTypeTmp, setDataTypeTmp] = useState(values.dataType); - const [timestepTmp, setTimestepTemp] = useState(values.timestep); - const [yearTmp, setYearTmp] = useState(values.year); - const { t } = useTranslation(); - - useEffect(() => { - setDataTypeTmp(values.dataType); - setTimestepTemp(values.timestep); - setYearTmp(values.year); - }, [values.dataType, values.timestep, values.year]); - - //////////////////////////////////////////////////////////////// - // Event Handlers - //////////////////////////////////////////////////////////////// - - const handleSelection = () => { - onSelection({ - dataType: dataTypeTmp, - timestep: timestepTmp, - year: yearTmp, - }); - onClose(); - }; - - const handleClose = () => { - setDataTypeTmp(values.dataType); - setTimestepTemp(values.timestep); - setYearTmp(values.year); - onClose(); - }; - - //////////////////////////////////////////////////////////////// - // JSX - //////////////////////////////////////////////////////////////// - - return ( - - -
- { - setYearTmp(event?.target.value ? -1 : 1); - }} - /> - {yearTmp > 0 && ( - { - const value = Number(event.target.value); - setYearTmp(value > maxYear ? maxYear : value); - }} - /> - )} -
-
- setDataTypeTmp(value as DataType)} - > - - - - - -
-
- setTimestepTemp(value as Timestep)} - > - - - - - - -
-
- - - -
- ); -} - -export default SelectionDrawer; diff --git a/webapp/src/components/App/Singlestudy/explore/Results/ResultDetails/index.tsx b/webapp/src/components/App/Singlestudy/explore/Results/ResultDetails/index.tsx index 36a81069c5..be181af183 100644 --- a/webapp/src/components/App/Singlestudy/explore/Results/ResultDetails/index.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Results/ResultDetails/index.tsx @@ -1,6 +1,7 @@ import { Box, Button, + Paper, Skeleton, ToggleButton, ToggleButtonGroup, @@ -31,14 +32,23 @@ import EditableMatrix from "../../../../../common/EditableMatrix"; import PropertiesView from "../../../../../common/PropertiesView"; import SplitLayoutView from "../../../../../common/SplitLayoutView"; import ListElement from "../../common/ListElement"; -import SelectionDrawer, { SelectionDrawerProps } from "./SelectionDrawer"; -import { createPath, DataType, OutputItemType, Timestep } from "./utils"; +import { + createPath, + DataType, + MAX_YEAR, + OutputItemType, + SYNTHESIS_ITEMS, + Timestep, +} from "./utils"; import UsePromiseCond, { mergeResponses, } from "../../../../../common/utils/UsePromiseCond"; import useStudySynthesis from "../../../../../../redux/hooks/useStudySynthesis"; import { downloadMatrix } from "../../../../../../utils/matrixUtils"; import ButtonBack from "../../../../../common/ButtonBack"; +import BooleanFE from "../../../../../common/fieldEditors/BooleanFE"; +import SelectFE from "../../../../../common/fieldEditors/SelectFE"; +import NumberFE from "../../../../../common/fieldEditors/NumberFE"; function ResultDetails() { const { study } = useOutletContext<{ study: StudyMetadata }>(); @@ -53,10 +63,10 @@ function ResultDetails() { const [dataType, setDataType] = useState(DataType.General); const [timestep, setTimeStep] = useState(Timestep.Hourly); const [year, setYear] = useState(-1); - const [showFilter, setShowFilter] = useState(false); const [itemType, setItemType] = useState(OutputItemType.Areas); const [selectedItemId, setSelectedItemId] = useState(""); const [searchValue, setSearchValue] = useState(""); + const isSynthesis = itemType === OutputItemType.Synthesis; const { t } = useTranslation(); const navigate = useNavigate(); @@ -67,15 +77,19 @@ function ResultDetails() { ) as Array<{ id: string; name: string; label?: string }>; const filteredItems = useMemo(() => { - return items.filter((item) => - isSearchMatching(searchValue, item.label || item.name), - ); - }, [items, searchValue]); + return isSynthesis + ? SYNTHESIS_ITEMS + : items.filter((item) => + isSearchMatching(searchValue, item.label || item.name) + ); + }, [isSynthesis, items, searchValue]); const selectedItem = filteredItems.find( (item) => item.id === selectedItemId, ) as (Area & { id: string }) | LinkElement | undefined; + const maxYear = output?.nbyears ?? MAX_YEAR; + useEffect( () => { const isValidSelectedItem = @@ -92,9 +106,9 @@ function ResultDetails() { const matrixRes = usePromise( async () => { - if (output && selectedItem) { + if (output && selectedItem && !isSynthesis) { const path = createPath({ - output: { ...output, id: outputId as string }, + output, item: selectedItem, dataType, timestep, @@ -116,8 +130,22 @@ function ResultDetails() { { resetDataOnReload: true, resetErrorOnReload: true, - deps: [study.id, output, selectedItem], + deps: [study.id, output, selectedItem, dataType, timestep, year], + } + ); + + const { data: synthesis } = usePromise( + async () => { + if (outputId && selectedItem && isSynthesis) { + const path = `output/${outputId}/economy/mc-all/grid/${selectedItem.id}`; + const res = await getStudyData(study.id, path); + return res; + } + return null; }, + { + deps: [study.id, outputId, selectedItem], + } ); //////////////////////////////////////////////////////////////// @@ -131,16 +159,6 @@ function ResultDetails() { setItemType(value); }; - const handleSelection: SelectionDrawerProps["onSelection"] = ({ - dataType, - timestep, - year, - }) => { - setDataType(dataType); - setTimeStep(timestep); - setYear(year); - }; - const handleDownload = (matrixData: MatrixType, fileName: string): void => { downloadMatrix(matrixData, fileName); }; @@ -150,49 +168,72 @@ function ResultDetails() { //////////////////////////////////////////////////////////////// return ( - <> - + navigate("..")} /> + + } + mainContent={ + <> + - navigate("..")} /> - - } - mainContent={ - <> - - - {t("study.areas")} - - - {t("study.links")} - - - setSelectedItemId(item.id)} - /> - - } - onSearchFilterChange={setSearchValue} - /> - } - right={ + + {t("study.areas")} + + + {t("study.links")} + + + {t("study.synthesis")} + + + setSelectedItemId(item.id)} + /> + + } + onSearchFilterChange={setSearchValue} + /> + } + right={ + isSynthesis ? ( + + + {synthesis} + + + ) : ( - {[ + {( [ - `${t("study.results.mc")}:`, - year > 0 ? `${t("study.results.mc.year")} ${year}` : "all", - ], - [`${t("study.results.display")}:`, dataType], - [`${t("study.results.temporality")}:`, timestep], - ].map(([label, value]) => ( - + [ + `${t("study.results.mc")}:`, + () => ( + <> + { + setYear(event?.target.value ? -1 : 1); + }} + /> + {year > 0 && ( + { + setYear(Number(event.target.value)); + }} + /> + )} + + ), + ], + [ + `${t("study.results.display")}:`, + () => ( + { + setDataType(event?.target.value as DataType); + }} + /> + ), + ], + [ + `${t("study.results.temporality")}:`, + () => ( + { + setTimeStep(event?.target.value as Timestep); + }} + /> + ), + ], + ] as const + ).map(([label, Field]) => ( + {label} - {value} + ))} - - @@ -286,16 +394,9 @@ function ResultDetails() { /> - } - /> - setShowFilter(false)} - values={{ dataType, timestep, year }} - maxYear={output?.nbyears} - onSelection={handleSelection} - /> - + ) + } + /> ); } diff --git a/webapp/src/components/App/Singlestudy/explore/Results/ResultDetails/utils.ts b/webapp/src/components/App/Singlestudy/explore/Results/ResultDetails/utils.ts index 6b2c53913b..a2bcdd4af9 100644 --- a/webapp/src/components/App/Singlestudy/explore/Results/ResultDetails/utils.ts +++ b/webapp/src/components/App/Singlestudy/explore/Results/ResultDetails/utils.ts @@ -3,6 +3,7 @@ import { Area, LinkElement, Simulation } from "../../../../../../common/types"; export enum OutputItemType { Areas = "areas", Links = "links", + Synthesis = "synthesis", } export enum DataType { @@ -43,3 +44,26 @@ export function createPath(params: Params): string { return `output/${id}/${mode}/${periodFolder}/${itemType}/${itemFolder}/${dataType}-${timestep}`; } + +export const SYNTHESIS_ITEMS = [ + { + id: "areas", + name: "Areas", + label: "Areas synthesis", + }, + { + id: "links", + name: "Links", + label: "Links synthesis", + }, + { + id: "digest", + name: "Digest", + label: "Digest", + }, + { + id: "thermal", + name: "Thermal", + label: "Thermal synthesis", + }, +]; diff --git a/webapp/src/components/App/Singlestudy/explore/Xpansion/Settings/SettingsForm.tsx b/webapp/src/components/App/Singlestudy/explore/Xpansion/Settings/SettingsForm.tsx index b3594de8f6..69091210f3 100644 --- a/webapp/src/components/App/Singlestudy/explore/Xpansion/Settings/SettingsForm.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Xpansion/Settings/SettingsForm.tsx @@ -180,22 +180,22 @@ function SettingsForm(props: PropType) { label={t("xpansion.solver")} data={currentSettings.solver || ""} handleChange={handleChange} + optional sx={{ minWidth: "100%", }} - optional - /> - - handleChange("batch_size", parseInt(e.target.value, 10)) - } - sx={{ mb: 1 }} /> + + handleChange("batch_size", parseInt(e.target.value, 10)) + } + sx={{ mb: 1 }} + />