Skip to content

Commit 657adbe

Browse files
authored
Add pagination and filtering for one bus analysis (#41)
Add an endpoint to get paginated FeederResults for one bus analysis. Add the possibility to filter the data. Add a new mode BASIC to get the faults included in the ShortCircuitAnalysisResult DTO but without the limit violations or feeders. Drop the database because of schema changes. Add some comments. Signed-off-by: Florent MILLOT <[email protected]>
1 parent 83e70fe commit 657adbe

14 files changed

+862
-50
lines changed

src/main/java/org/gridsuite/shortcircuit/server/ShortCircuitController.java

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*/
77
package org.gridsuite.shortcircuit.server;
88

9+
import com.fasterxml.jackson.core.JsonProcessingException;
910
import com.powsybl.shortcircuit.ShortCircuitParameters;
1011
import io.swagger.v3.oas.annotations.Operation;
1112
import io.swagger.v3.oas.annotations.Parameter;
@@ -14,11 +15,7 @@
1415
import io.swagger.v3.oas.annotations.responses.ApiResponse;
1516
import io.swagger.v3.oas.annotations.responses.ApiResponses;
1617
import io.swagger.v3.oas.annotations.tags.Tag;
17-
18-
import org.gridsuite.shortcircuit.server.dto.FaultResult;
19-
import org.gridsuite.shortcircuit.server.dto.FaultResultsMode;
20-
import org.gridsuite.shortcircuit.server.dto.ShortCircuitAnalysisResult;
21-
import org.gridsuite.shortcircuit.server.dto.ShortCircuitAnalysisStatus;
18+
import org.gridsuite.shortcircuit.server.dto.*;
2219
import org.gridsuite.shortcircuit.server.service.ShortCircuitRunContext;
2320
import org.gridsuite.shortcircuit.server.service.ShortCircuitService;
2421
import org.springframework.data.domain.Page;
@@ -74,7 +71,10 @@ public ResponseEntity<UUID> runAndSave(@Parameter(description = "Network UUID")
7471
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The short circuit analysis result"),
7572
@ApiResponse(responseCode = "404", description = "Short circuit analysis result has not been found")})
7673
public ResponseEntity<ShortCircuitAnalysisResult> getResult(@Parameter(description = "Result UUID") @PathVariable("resultUuid") UUID resultUuid,
77-
@Parameter(description = "Full or only those with limit violations or none fault results") @RequestParam(name = "mode", required = false, defaultValue = "WITH_LIMIT_VIOLATIONS") FaultResultsMode mode) {
74+
@Parameter(description = "BASIC (faults without limits and feeders), " +
75+
"FULL (faults with both), " +
76+
"WITH_LIMIT_VIOLATIONS (like FULL but only those with limit violations) or " +
77+
"NONE (no fault)") @RequestParam(name = "mode", required = false, defaultValue = "WITH_LIMIT_VIOLATIONS") FaultResultsMode mode) {
7878
ShortCircuitAnalysisResult result = shortCircuitService.getResult(resultUuid, mode);
7979
return result != null ? ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(result)
8080
: ResponseEntity.notFound().build();
@@ -85,11 +85,27 @@ public ResponseEntity<ShortCircuitAnalysisResult> getResult(@Parameter(descripti
8585
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The page of fault results"),
8686
@ApiResponse(responseCode = "404", description = "Short circuit analysis result has not been found")})
8787
public ResponseEntity<Page<FaultResult>> getPagedFaultResults(@Parameter(description = "Result UUID") @PathVariable("resultUuid") UUID resultUuid,
88-
@Parameter(description = "Full or only those with limit violations or none fault results") @RequestParam(name = "mode", required = false, defaultValue = "WITH_LIMIT_VIOLATIONS") FaultResultsMode mode,
89-
Pageable pageable) {
88+
@Parameter(description = "BASIC (faults without limits and feeders), " +
89+
"FULL (faults with both), " +
90+
"WITH_LIMIT_VIOLATIONS (like FULL but only those with limit violations) or " +
91+
"NONE (no fault)") @RequestParam(name = "mode", required = false, defaultValue = "WITH_LIMIT_VIOLATIONS") FaultResultsMode mode,
92+
Pageable pageable) {
9093
Page<FaultResult> faultResultsPage = shortCircuitService.getFaultResultsPage(resultUuid, mode, pageable);
9194
return faultResultsPage != null ? ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(faultResultsPage)
92-
: ResponseEntity.notFound().build();
95+
: ResponseEntity.notFound().build();
96+
}
97+
98+
@GetMapping(value = "/results/{resultUuid}/feeder_results/paged", produces = APPLICATION_JSON_VALUE)
99+
@Operation(summary = "Get a feeder results page for a given short circuit analysis result")
100+
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The page of feeder results"),
101+
@ApiResponse(responseCode = "404", description = "Short circuit analysis result has not been found")})
102+
public ResponseEntity<Page<FeederResult>> getPagedFeederResults(@Parameter(description = "Result UUID") @PathVariable("resultUuid") UUID resultUuid,
103+
@Parameter(description = "Filters") @RequestParam(name = "filters", required = false) String stringFilters,
104+
Pageable pageable) throws JsonProcessingException {
105+
List<ResourceFilter> resourceFilters = ResourceFilter.fromStringToList(stringFilters);
106+
Page<FeederResult> feederResultsPage = shortCircuitService.getFeederResultsPage(resultUuid, resourceFilters, pageable);
107+
return feederResultsPage != null ? ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(feederResultsPage)
108+
: ResponseEntity.notFound().build();
93109
}
94110

95111
@DeleteMapping(value = "/results/{resultUuid}", produces = APPLICATION_JSON_VALUE)

src/main/java/org/gridsuite/shortcircuit/server/dto/FaultResultsMode.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,24 @@
77
package org.gridsuite.shortcircuit.server.dto;
88

99
/**
10+
* Specify if the faults are present in the result DTO and if so what they contain
1011
* @author Sylvain Bouzols <sylvain.bouzols at rte-france.com
1112
*/
1213
public enum FaultResultsMode {
14+
/**
15+
* No fault present
16+
*/
1317
NONE,
18+
/**
19+
* Present but without the limit violations and feeders
20+
*/
21+
BASIC,
22+
/**
23+
* Present with all fields but filtered by limit violations presence
24+
*/
1425
WITH_LIMIT_VIOLATIONS,
26+
/**
27+
* Present with all fields
28+
*/
1529
FULL
1630
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/**
2+
* Copyright (c) 2023, RTE (http://www.rte-france.com)
3+
* This Source Code Form is subject to the terms of the Mozilla Public
4+
* License, v. 2.0. If a copy of the MPL was not distributed with this
5+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
6+
*/
7+
8+
package org.gridsuite.shortcircuit.server.dto;
9+
10+
import com.fasterxml.jackson.annotation.JsonProperty;
11+
import com.fasterxml.jackson.core.JsonProcessingException;
12+
import com.fasterxml.jackson.core.type.TypeReference;
13+
import com.fasterxml.jackson.databind.ObjectMapper;
14+
15+
import java.util.List;
16+
17+
/**
18+
* An object that can be used to filter data with the JPA Criteria API (via Spring Specification)
19+
* @param dataType the type of data we want to filter (text, number)
20+
* @param type the type of filter (contains, startsWith...)
21+
* @param value the value of the filter
22+
* @param field the field on which the filter will be applied
23+
* @author Florent MILLOT <[email protected]>
24+
*/
25+
public record ResourceFilter(DataType dataType, Type type, String value, String field) {
26+
27+
private static ObjectMapper objectMapper = new ObjectMapper();
28+
29+
public enum DataType {
30+
@JsonProperty("text")
31+
TEXT,
32+
@JsonProperty("number")
33+
NUMBER,
34+
}
35+
36+
public enum Type {
37+
@JsonProperty("contains")
38+
CONTAINS,
39+
@JsonProperty("startsWith")
40+
STARTS_WITH,
41+
@JsonProperty("notEqual")
42+
NOT_EQUAL,
43+
@JsonProperty("lessThanOrEqual")
44+
LESS_THAN_OR_EQUAL,
45+
@JsonProperty("greaterThanOrEqual")
46+
GREATER_THAN_OR_EQUAL
47+
}
48+
49+
public static List<ResourceFilter> fromStringToList(String filters) throws JsonProcessingException {
50+
if (filters == null || filters.isEmpty()) {
51+
return List.of();
52+
}
53+
return objectMapper.readValue(filters, new TypeReference<>() {
54+
});
55+
}
56+
}

src/main/java/org/gridsuite/shortcircuit/server/entities/FaultResultEntity.java

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,16 @@ public class FaultResultEntity {
5151
columnList = "fault_result_entity_fault_result_uuid")})
5252
private List<LimitViolationEmbeddable> limitViolations;
5353

54-
@ElementCollection
55-
@CollectionTable(name = "feeder_results",
56-
indexes = {@Index(name = "feeder_results_fault_result_idx",
57-
columnList = "fault_result_entity_fault_result_uuid")})
58-
private List<FeederResultEmbeddable> feederResults;
54+
/*
55+
Bidirectional relation is not needed here and is done for performance
56+
https://vladmihalcea.com/the-best-way-to-map-a-onetomany-association-with-jpa-and-hibernate/
57+
*/
58+
@OneToMany(
59+
mappedBy = "faultResult",
60+
cascade = CascadeType.ALL,
61+
orphanRemoval = true
62+
)
63+
private List<FeederResultEntity> feederResults;
5964

6065
@Column
6166
private double ipMax;
@@ -99,19 +104,28 @@ public class FaultResultEntity {
99104
@AttributeOverride(name = "angleC", column = @Column(name = "fortescue_voltage_angle_c"))
100105
private FortescueResultEmbeddable fortescueVoltage;
101106

102-
public FaultResultEntity(FaultEmbeddable fault, double current, double shortCircuitPower, List<LimitViolationEmbeddable> limitViolations, List<FeederResultEmbeddable> feederResults, double ipMin, double ipMax, FortescueResultEmbeddable fortescueCurrent, FortescueResultEmbeddable fortescueVoltage, double deltaCurrentIpMin, double deltaCurrentIpMax) {
107+
public FaultResultEntity(FaultEmbeddable fault, double current, double shortCircuitPower, List<LimitViolationEmbeddable> limitViolations, List<FeederResultEntity> feederResults, double ipMin, double ipMax, FortescueResultEmbeddable fortescueCurrent, FortescueResultEmbeddable fortescueVoltage, double deltaCurrentIpMin, double deltaCurrentIpMax) {
103108
this.fault = fault;
104109
this.current = current;
105110
this.shortCircuitPower = shortCircuitPower;
106-
this.limitViolations = limitViolations;
107-
this.nbLimitViolations = limitViolations.size();
108-
this.feederResults = feederResults;
111+
if (limitViolations != null) {
112+
this.limitViolations = limitViolations;
113+
this.nbLimitViolations = limitViolations.size();
114+
}
109115
this.ipMin = ipMin;
110116
this.ipMax = ipMax;
111117
this.fortescueCurrent = fortescueCurrent;
112118
this.fortescueVoltage = fortescueVoltage;
113119
this.deltaCurrentIpMin = deltaCurrentIpMin;
114120
this.deltaCurrentIpMax = deltaCurrentIpMax;
121+
if (feederResults != null) {
122+
setFeederResults(feederResults);
123+
}
124+
}
125+
126+
public void setFeederResults(List<FeederResultEntity> feederResults) {
127+
this.feederResults = feederResults;
128+
feederResults.stream().forEach(feederResultEntity -> feederResultEntity.setFaultResult(this));
115129
}
116130

117131
@Override
Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,34 @@
66
*/
77
package org.gridsuite.shortcircuit.server.entities;
88

9-
import lombok.AllArgsConstructor;
109
import lombok.Getter;
1110
import lombok.NoArgsConstructor;
1211

1312
import jakarta.persistence.*;
1413

14+
import java.util.UUID;
15+
1516
/**
1617
* @author Nicolas Noir <nicolas.noir at rte-france.com>
1718
*/
1819
@Getter
19-
@AllArgsConstructor
2020
@NoArgsConstructor
21-
@Embeddable
22-
public class FeederResultEmbeddable {
21+
@Entity
22+
@Table(name = "feeder_results",
23+
indexes = {@Index(name = "feeder_results_fault_result_idx",
24+
columnList = "fault_result_entity_fault_result_uuid")})
25+
public class FeederResultEntity {
26+
27+
@Id
28+
@GeneratedValue(strategy = GenerationType.AUTO)
29+
private UUID feederResultUuid;
30+
31+
@ManyToOne(
32+
cascade = CascadeType.ALL,
33+
fetch = FetchType.LAZY
34+
)
35+
@JoinColumn(name = "fault_result_entity_fault_result_uuid")
36+
private FaultResultEntity faultResult;
2337

2438
@Column
2539
private String connectableId;
@@ -45,4 +59,14 @@ public class FeederResultEmbeddable {
4559
public double getPositiveMagnitude() {
4660
return this.getFortescueCurrent() != null ? this.getFortescueCurrent().getPositiveMagnitude() : Double.NaN;
4761
}
62+
63+
public void setFaultResult(FaultResultEntity faultResult) {
64+
this.faultResult = faultResult;
65+
}
66+
67+
public FeederResultEntity(String connectableId, double current, FortescueResultEmbeddable fortescueCurrent) {
68+
this.connectableId = connectableId;
69+
this.current = current;
70+
this.fortescueCurrent = fortescueCurrent;
71+
}
4872
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/**
2+
* Copyright (c) 2023, RTE (http://www.rte-france.com)
3+
* This Source Code Form is subject to the terms of the Mozilla Public
4+
* License, v. 2.0. If a copy of the MPL was not distributed with this
5+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
6+
*/
7+
8+
package org.gridsuite.shortcircuit.server.repositories;
9+
10+
import org.gridsuite.shortcircuit.server.entities.FeederResultEntity;
11+
import org.springframework.data.jpa.repository.JpaRepository;
12+
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
13+
import org.springframework.stereotype.Repository;
14+
15+
import java.util.UUID;
16+
17+
/**
18+
* @author Florent MILLOT <[email protected]>
19+
*/
20+
@Repository
21+
public interface FeederResultRepository extends JpaRepository<FeederResultEntity, UUID>, JpaSpecificationExecutor<FeederResultEntity> {
22+
}

src/main/java/org/gridsuite/shortcircuit/server/repositories/ResultRepository.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,14 @@
2222
public interface ResultRepository extends JpaRepository<ShortCircuitAnalysisResultEntity, UUID> {
2323
Optional<ShortCircuitAnalysisResultEntity> findByResultUuid(UUID resultUuid);
2424

25+
@EntityGraph(attributePaths = {"faultResults"}, type = EntityGraphType.LOAD)
26+
Optional<ShortCircuitAnalysisResultEntity> findWithFaultResultsByResultUuid(UUID resultUuid);
27+
2528
@EntityGraph(attributePaths = {"faultResults", "faultResults.limitViolations"}, type = EntityGraphType.LOAD)
26-
Optional<ShortCircuitAnalysisResultEntity> findAllWithLimitViolationsByResultUuid(UUID resultUuid);
29+
Optional<ShortCircuitAnalysisResultEntity> findWithFaultResultsAndLimitViolationsByResultUuid(UUID resultUuid);
2730

2831
@EntityGraph(attributePaths = {"faultResults", "faultResults.feederResults"}, type = EntityGraphType.LOAD)
29-
Optional<ShortCircuitAnalysisResultEntity> findAllWithFeederResultsByResultUuid(UUID resultUuid);
32+
Optional<ShortCircuitAnalysisResultEntity> findWithFaultResultsAndFeederResultsByResultUuid(UUID resultUuid);
3033

3134
void deleteByResultUuid(UUID resultUuid);
3235
}

0 commit comments

Comments
 (0)