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 (
-
-
-
-
-
-
-
-
-
-
- );
-}
-
-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}
+
))}
-
- }
onClick={() =>
matrixRes.data &&
handleDownload(matrixRes.data, `matrix_${study.id}`)
}
disabled={matrixRes.isLoading}
>
- {t("global.download")}
+
@@ -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 }}
+ />