Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.gridsuite.study.server.dto.networkexport.ExportNetworkStatus;
import org.gridsuite.study.server.dto.networkexport.NodeExportInfos;
import org.gridsuite.study.server.dto.sequence.NodeSequenceType;
import org.gridsuite.study.server.dto.studyexport.NetworkModificationExportInfos;
import org.gridsuite.study.server.dto.studyexport.TreeExportInfos;
import org.gridsuite.study.server.elasticsearch.EquipmentInfosService;
import org.gridsuite.study.server.error.StudyException;
Expand All @@ -39,6 +40,7 @@
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.beans.PropertyEditorSupport;
import java.util.*;
Expand Down Expand Up @@ -954,8 +956,8 @@ public ResponseEntity<String> getNetworkModifications(@Parameter(description = "
@Operation(summary = "Get network modifications to export for a given node")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The network modifications to export was returned"), @ApiResponse(responseCode = "404",
description = "The study/node is not found")})
public ResponseEntity<String> getExportedNetworkModifications(@Parameter(description = "Study UUID") @PathVariable("studyUuid") UUID studyUuid,
@Parameter(description = "Node UUID") @PathVariable("nodeUuid") UUID nodeUuid) {
public ResponseEntity<NetworkModificationExportInfos> getExportedNetworkModifications(@Parameter(description = "Study UUID") @PathVariable("studyUuid") UUID studyUuid,
@Parameter(description = "Node UUID") @PathVariable("nodeUuid") UUID nodeUuid) {
studyService.assertIsStudyAndNodeExist(studyUuid, nodeUuid);
return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(studyService.getExportedNetworkModifications(studyUuid, nodeUuid));
}
Expand Down Expand Up @@ -1622,12 +1624,13 @@ public ResponseEntity<Resource> exportStudy(@PathVariable("studyUuid") UUID stud
return ResponseEntity.ok().headers(headers).body(studyExportService.exportStudy(studyUuid, userId));
}

@PostMapping(value = "/studies/import")
@PostMapping(value = "/studies/import", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@Operation(summary = "Create a study and its root networks from a previously exported study archive")
@ApiResponse(responseCode = "200", description = "Study import initiated successfully")
public ResponseEntity<Void> importStudy(@RequestBody TreeExportInfos treeExportInfos,
public ResponseEntity<Void> importStudy(@RequestPart("treeExportInfos") TreeExportInfos treeExportInfos,
@RequestPart("modificationsArchive") MultipartFile modificationsArchive,
@RequestHeader(HEADER_USER_ID) String userId) {
studyImportService.importStudy(treeExportInfos, userId);
studyImportService.importStudy(treeExportInfos, modificationsArchive, userId);
return ResponseEntity.ok().build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* Copyright (c) 2026, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.gridsuite.study.server.dto.studyexport;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonNode;
import org.gridsuite.filter.AbstractFilter;

import java.util.List;
import java.util.Map;
import java.util.UUID;

/**
* @author Ghazwa Rehili <ghazwa.rehili at rte-france.com>
*/
public record NetworkModificationExportInfos(
@JsonProperty("modifications")
List<JsonNode> exportedModifications,
@JsonProperty("filters")
Map<UUID, List<AbstractFilter>> exportedFilters,
@JsonProperty("loadFlowParameters")
Map<UUID, UUID> exportedLoadFlowParameters
) { }
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* Copyright (c) 2026, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.gridsuite.study.server.dto.studyexport;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonNode;
import org.gridsuite.filter.AbstractFilter;

import java.util.List;
import java.util.Map;
import java.util.UUID;

/**
* @author Ghazwa Rehili <ghazwa.rehili at rte-france.com>
*/
public record NetworkModificationImportInfos(
@JsonProperty("modifications")
List<JsonNode> modifications,
@JsonProperty("filtersByOldId")
Map<UUID, AbstractFilter> filtersByOldId,
@JsonProperty("loadFlowParametersIdMapping")
Map<UUID, UUID> loadFlowParametersIdMapping
) { }
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
import org.gridsuite.study.server.dto.BuildInfos;
import org.gridsuite.study.server.dto.NodeReceiver;
import org.gridsuite.study.server.dto.modification.*;
import org.gridsuite.study.server.dto.studyexport.NetworkModificationExportInfos;
import org.gridsuite.study.server.dto.studyexport.NetworkModificationImportInfos;
import org.gridsuite.study.server.dto.workflow.AbstractWorkflowInfos;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ParameterizedTypeReference;
Expand Down Expand Up @@ -148,14 +150,15 @@ public String getModifications(UUID groupUUid, boolean stashedModifications, boo
return restTemplate.exchange(getNetworkModificationServerURI(false) + path, HttpMethod.GET, null, String.class).getBody();
}

public String getModificationsToExport(UUID groupUUid) {
public NetworkModificationExportInfos getModificationsToExport(UUID groupUUid) {
Objects.requireNonNull(groupUUid);
var path = UriComponentsBuilder.fromPath(GROUP_PATH + DELIMITER + NETWORK_MODIFICATIONS_PATH + DELIMITER + "export")
.queryParam(QUERY_PARAM_ERROR_ON_GROUP_NOT_FOUND, false)
.buildAndExpand(groupUUid)
.toUriString();

return restTemplate.exchange(getNetworkModificationServerURI(false) + path, HttpMethod.GET, null, String.class).getBody();
return restTemplate.exchange(getNetworkModificationServerURI(false) + path,
HttpMethod.GET, null, NetworkModificationExportInfos.class).getBody();
}

public Integer getModificationsCount(UUID groupUUid, boolean stashedModifications) {
Expand Down Expand Up @@ -468,6 +471,24 @@ public Map<UUID, UUID> duplicateModificationsGroup(UUID sourceGroupUuid, UUID gr
).getBody();
}

public Map<UUID, UUID> importNetworkModifications(UUID groupUuid, NetworkModificationImportInfos networkModificationImportInfos) {
Objects.requireNonNull(groupUuid);
var path = UriComponentsBuilder.fromPath("groups/{groupUuid}/network-modifications/import")
.buildAndExpand(groupUuid)
.toUriString();

HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<NetworkModificationImportInfos> httpEntity = new HttpEntity<>(networkModificationImportInfos, headers);

return restTemplate.exchange(
getNetworkModificationServerURI(false) + path,
HttpMethod.POST,
httpEntity,
new ParameterizedTypeReference<Map<UUID, UUID>>() { }
).getBody();
}

public NetworkModificationsResult duplicateModificationsFromGroup(UUID groupUuid, UUID originGroupUuid, Pair<List<UUID>, List<ModificationApplicationContext>> modificationContextInfos) {
return handleModifications(groupUuid, originGroupUuid, StudyConstants.ModificationsActionType.COPY, modificationContextInfos);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,18 @@
*/
package org.gridsuite.study.server.service;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.apache.commons.collections4.CollectionUtils;
import org.gridsuite.filter.AbstractFilter;
import org.gridsuite.study.server.dto.LoadFlowParametersInfos;
import org.gridsuite.study.server.dto.networkexport.PermissionType;
import org.gridsuite.study.server.dto.studyexport.NetworkModificationExportInfos;
import org.gridsuite.study.server.dto.studyexport.RootNetworkExportInfos;
import org.gridsuite.study.server.dto.studyexport.TreeExportInfos;
import org.gridsuite.study.server.error.StudyException;
import org.gridsuite.study.server.service.loadflow.LoadFlowService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.InputStreamResource;
Expand All @@ -23,10 +30,7 @@
import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.*;
import java.util.stream.Stream;
import java.util.zip.GZIPInputStream;
import java.util.zip.ZipEntry;
Expand All @@ -41,17 +45,23 @@
public class StudyExportService {
private static final Logger LOGGER = LoggerFactory.getLogger(StudyExportService.class);
public static final String TREE_JSON_FILE_NAME = "tree.json";
public static final String NETWORK_MODIFICATIONS_JSON = "network-modification.json";
public static final String NETWORK_MODIFICATION_FILTERS_JSON = "network-modification-filters.json";
public static final String NETWORK_MODIFICATION_LOAD_FLOW_PARAMETERS_JSON = "network-modification-load-flow-parameters.json";
public static final String CASES_FOLDER = "cases";

private final StudyService studyService;
private final CaseService caseService;
private final DirectoryService directoryService;
private final LoadFlowService loadFlowService;
private final ObjectMapper objectMapper;

public StudyExportService(StudyService studyService, CaseService caseService, DirectoryService directoryService, ObjectMapper objectMapper) {
public StudyExportService(StudyService studyService, CaseService caseService, DirectoryService directoryService,
LoadFlowService loadFlowService, ObjectMapper objectMapper) {
this.studyService = studyService;
this.caseService = caseService;
this.directoryService = directoryService;
this.loadFlowService = loadFlowService;
this.objectMapper = objectMapper;
}

Expand Down Expand Up @@ -95,6 +105,10 @@
TreeExportInfos treeExportInfos = studyService.buildTreeExport(studyUuid);
Path studyJsonPath = tempDir.resolve(TREE_JSON_FILE_NAME);
objectMapper.writerWithDefaultPrettyPrinter().writeValue(studyJsonPath.toFile(), treeExportInfos);
Map<UUID, NetworkModificationExportInfos> modificationsByGroup = studyService.buildNetworkModificationsExport(treeExportInfos.nodeTree());
writeNetworkModificationsExport(modificationsByGroup, tempDir);
writeNetworkModificationFiltersExport(modificationsByGroup, tempDir);
writeLoadFlowParametersExport(modificationsByGroup, tempDir);
Path casesDir = Files.createDirectories(tempDir.resolve(CASES_FOLDER));
for (RootNetworkExportInfos rootNetworkInfos : treeExportInfos.rootNetworks()) {
UUID caseUuid = rootNetworkInfos.caseInfos().getCaseUuid();
Expand All @@ -109,6 +123,48 @@
return zipFile;
}

private void writeNetworkModificationsExport(Map<UUID, NetworkModificationExportInfos> modificationsByGroup, Path tempDir) throws IOException {
Map<String, Map<String, Collection<JsonNode>>> root = new LinkedHashMap<>();
for (Map.Entry<UUID, NetworkModificationExportInfos> entry : modificationsByGroup.entrySet()) {
root.put(entry.getKey().toString(), Map.of("modifications", CollectionUtils.emptyIfNull(entry.getValue().exportedModifications())));
}
Path networkModificationsJsonPath = tempDir.resolve(NETWORK_MODIFICATIONS_JSON);
objectMapper.writerWithDefaultPrettyPrinter().writeValue(networkModificationsJsonPath.toFile(), root);
}

private void writeNetworkModificationFiltersExport(Map<UUID, NetworkModificationExportInfos> modificationsByGroup, Path tempDir) throws IOException {
Map<UUID, AbstractFilter> filtersById = new LinkedHashMap<>();
for (NetworkModificationExportInfos groupExport : modificationsByGroup.values()) {
Map<UUID, List<AbstractFilter>> filtersByModification = groupExport.exportedFilters();
if (filtersByModification == null) {
continue;
}
filtersByModification.values().forEach(filtersForModification ->
CollectionUtils.emptyIfNull(filtersForModification).forEach(filter -> filtersById.put(filter.getId(), filter)));
}
Path filtersJsonPath = tempDir.resolve(NETWORK_MODIFICATION_FILTERS_JSON);
objectMapper.writerWithDefaultPrettyPrinter().writeValue(filtersJsonPath.toFile(), filtersById);
}

private void writeLoadFlowParametersExport(Map<UUID, NetworkModificationExportInfos> modificationsByGroup, Path tempDir) throws IOException {
Set<UUID> loadFlowParametersIds = new LinkedHashSet<>();
for (NetworkModificationExportInfos groupExport : modificationsByGroup.values()) {
Map<UUID, UUID> loadFlowParametersByModification = groupExport.exportedLoadFlowParameters();
if (loadFlowParametersByModification != null) {
loadFlowParametersIds.addAll(loadFlowParametersByModification.values());
}
}
ObjectNode loadFlowParametersRoot = objectMapper.createObjectNode();
for (UUID loadFlowParametersId : loadFlowParametersIds) {
LoadFlowParametersInfos loadFlowParametersInfos = loadFlowService.getLoadFlowParameters(loadFlowParametersId);
if (loadFlowParametersInfos != null) {
loadFlowParametersRoot.set(loadFlowParametersId.toString(), objectMapper.valueToTree(loadFlowParametersInfos));
}
}
Path loadFlowParametersJsonPath = tempDir.resolve(NETWORK_MODIFICATION_LOAD_FLOW_PARAMETERS_JSON);
objectMapper.writerWithDefaultPrettyPrinter().writeValue(loadFlowParametersJsonPath.toFile(), loadFlowParametersRoot);
}

private Path createTempWorkDir(UUID studyUuid) {
return createTempPath(studyUuid, "temp directory", "rwx------",
attr -> Files.createTempDirectory("study-export-" + studyUuid, attr));
Expand All @@ -119,7 +175,7 @@
attr -> Files.createTempFile("study-export-" + studyUuid, ".zip", attr));
}

private Path createTempPath(UUID studyUuid, String errorContext, String permissions,

Check warning on line 178 in src/main/java/org/gridsuite/study/server/service/StudyExportService.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove these unused method parameters "studyUuid", "errorContext".

See more on https://sonarcloud.io/project/issues?id=org.gridsuite%3Astudy-server&issues=AaAUNA6WeSDkOxo1r36y&open=AaAUNA6WeSDkOxo1r36y&pullRequest=1064
IOFunction<FileAttribute<Set<PosixFilePermission>>, Path> creator) {
FileAttribute<Set<PosixFilePermission>> attr =
PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString(permissions));
Expand Down
Loading
Loading