Skip to content

Commit a25c0f1

Browse files
authored
Add filtering to fault results (#48)
Add the possibility to filter the fault results. Change default fault mode to FULL for paged fault results. Add 2 endpoints to return fault types and limit violation types. Some codde cleaning and refactoring. Signed-off-by: Florent MILLOT <[email protected]>
1 parent 657adbe commit a25c0f1

12 files changed

+641
-246
lines changed

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

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
package org.gridsuite.shortcircuit.server;
88

99
import com.fasterxml.jackson.core.JsonProcessingException;
10+
import com.powsybl.security.LimitViolationType;
1011
import com.powsybl.shortcircuit.ShortCircuitParameters;
1112
import io.swagger.v3.oas.annotations.Operation;
1213
import io.swagger.v3.oas.annotations.Parameter;
@@ -24,9 +25,9 @@
2425
import org.springframework.http.ResponseEntity;
2526
import org.springframework.web.bind.annotation.*;
2627

27-
import java.util.List;
28-
import java.util.UUID;
28+
import java.util.*;
2929

30+
import static com.powsybl.shortcircuit.Fault.FaultType;
3031
import static org.gridsuite.shortcircuit.server.service.NotificationService.HEADER_USER_ID;
3132
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
3233

@@ -88,9 +89,11 @@ public ResponseEntity<Page<FaultResult>> getPagedFaultResults(@Parameter(descrip
8889
@Parameter(description = "BASIC (faults without limits and feeders), " +
8990
"FULL (faults with both), " +
9091
"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) {
93-
Page<FaultResult> faultResultsPage = shortCircuitService.getFaultResultsPage(resultUuid, mode, pageable);
92+
"NONE (no fault)") @RequestParam(name = "mode", required = false, defaultValue = "FULL") FaultResultsMode mode,
93+
@Parameter(description = "Filters") @RequestParam(name = "filters", required = false) String stringFilters,
94+
Pageable pageable) throws JsonProcessingException {
95+
List<ResourceFilter> resourceFilters = ResourceFilter.fromStringToList(stringFilters);
96+
Page<FaultResult> faultResultsPage = shortCircuitService.getFaultResultsPage(resultUuid, mode, resourceFilters, pageable);
9497
return faultResultsPage != null ? ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(faultResultsPage)
9598
: ResponseEntity.notFound().build();
9699
}
@@ -149,4 +152,18 @@ public ResponseEntity<Void> stop(@Parameter(description = "Result UUID") @PathVa
149152
return ResponseEntity.ok().build();
150153
}
151154

155+
@GetMapping(value = "/fault-types", produces = APPLICATION_JSON_VALUE)
156+
@Operation(summary = "Get list of fault types")
157+
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The list of fault types")})
158+
public ResponseEntity<FaultType[]> getFaultTypes() {
159+
return ResponseEntity.ok().body(FaultType.values());
160+
}
161+
162+
@GetMapping(value = "/limit-violation-types", produces = APPLICATION_JSON_VALUE)
163+
@Operation(summary = "Get list of limit violation types")
164+
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The list of limit violation types")})
165+
public ResponseEntity<LimitViolationType[]> getLimitTypes() {
166+
return ResponseEntity.ok().body(LimitViolationType.values());
167+
}
168+
152169
}

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
* @param field the field on which the filter will be applied
2323
* @author Florent MILLOT <[email protected]>
2424
*/
25-
public record ResourceFilter(DataType dataType, Type type, String value, String field) {
25+
public record ResourceFilter(DataType dataType, Type type, Object value, String field) {
2626

2727
private static ObjectMapper objectMapper = new ObjectMapper();
2828

@@ -34,6 +34,8 @@ public enum DataType {
3434
}
3535

3636
public enum Type {
37+
@JsonProperty("equals")
38+
EQUALS,
3739
@JsonProperty("contains")
3840
CONTAINS,
3941
@JsonProperty("startsWith")

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

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,29 +7,22 @@
77
package org.gridsuite.shortcircuit.server.repositories;
88

99
import org.gridsuite.shortcircuit.server.entities.FaultResultEntity;
10-
import org.gridsuite.shortcircuit.server.entities.ShortCircuitAnalysisResultEntity;
11-
import org.springframework.data.jpa.repository.JpaRepository;
12-
import org.springframework.stereotype.Repository;
13-
import org.springframework.data.domain.Page;
14-
import org.springframework.data.domain.Pageable;
1510
import org.springframework.data.jpa.repository.EntityGraph;
1611
import org.springframework.data.jpa.repository.EntityGraph.EntityGraphType;
12+
import org.springframework.data.jpa.repository.JpaRepository;
13+
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
14+
import org.springframework.stereotype.Repository;
1715

18-
import java.util.UUID;
1916
import java.util.List;
20-
import java.util.Optional;
2117
import java.util.Set;
18+
import java.util.UUID;
2219

2320

2421
/**
2522
* @author Sylvain Bouzols <sylvain.bouzols at rte-france.com
2623
*/
2724
@Repository
28-
public interface FaultResultRepository extends JpaRepository<FaultResultEntity, UUID> {
29-
Optional<Page<FaultResultEntity>> findPagedByResult(ShortCircuitAnalysisResultEntity result, Pageable pageable);
30-
31-
Optional<Page<FaultResultEntity>> findPagedByResultAndNbLimitViolationsGreaterThan(ShortCircuitAnalysisResultEntity result, int nbLimitViolations, Pageable pageable);
32-
25+
public interface FaultResultRepository extends JpaRepository<FaultResultEntity, UUID>, JpaSpecificationExecutor<FaultResultEntity> {
3326
@EntityGraph(attributePaths = {"limitViolations"}, type = EntityGraphType.LOAD)
3427
Set<FaultResultEntity> findAllWithLimitViolationsByFaultResultUuidIn(List<UUID> faultResultsUUID);
3528

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

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,11 @@
99
import com.powsybl.shortcircuit.*;
1010
import lombok.extern.slf4j.Slf4j;
1111
import org.gridsuite.shortcircuit.server.dto.FaultResultsMode;
12+
import org.gridsuite.shortcircuit.server.dto.ResourceFilter;
1213
import org.gridsuite.shortcircuit.server.dto.ShortCircuitLimits;
1314
import org.gridsuite.shortcircuit.server.entities.*;
15+
import org.gridsuite.shortcircuit.server.utils.FaultResultSpecificationBuilder;
16+
import org.gridsuite.shortcircuit.server.utils.FeederResultSpecificationBuilder;
1417
import org.springframework.beans.factory.annotation.Autowired;
1518
import org.springframework.data.domain.Page;
1619
import org.springframework.data.domain.Pageable;
@@ -221,35 +224,39 @@ public Optional<ShortCircuitAnalysisResultEntity> findResultsWithLimitViolations
221224
}
222225

223226
@Transactional(readOnly = true)
224-
public Optional<Page<FaultResultEntity>> findFaultResultsPage(ShortCircuitAnalysisResultEntity result, Pageable pageable, FaultResultsMode mode) {
227+
public Page<FaultResultEntity> findFaultResultsPage(ShortCircuitAnalysisResultEntity result, List<ResourceFilter> resourceFilters, Pageable pageable, FaultResultsMode mode) {
225228
Objects.requireNonNull(result);
229+
Specification<FaultResultEntity> specification = FaultResultSpecificationBuilder.buildSpecification(result.getResultUuid(), resourceFilters);
226230
// WARN org.hibernate.hql.internal.ast.QueryTranslatorImpl -
227231
// HHH000104: firstResult/maxResults specified with collection fetch; applying in memory!
228232
// cf. https://vladmihalcea.com/fix-hibernate-hhh000104-entity-fetch-pagination-warning-message/
229233
// We must separate in two requests, one with pagination the other one with Join Fetch
230-
Optional<Page<FaultResultEntity>> faultResultsPage = faultResultRepository.findPagedByResult(result, pageable);
231-
if (faultResultsPage.isPresent() && mode != FaultResultsMode.BASIC) {
232-
appendLimitViolationsAndFeederResults(faultResultsPage.get());
234+
Page<FaultResultEntity> faultResultsPage = faultResultRepository.findAll(specification, pageable);
235+
if (faultResultsPage.hasContent() && mode != FaultResultsMode.BASIC) {
236+
appendLimitViolationsAndFeederResults(faultResultsPage);
233237
}
234238
return faultResultsPage;
235239
}
236240

237241
@Transactional(readOnly = true)
238-
public Page<FeederResultEntity> findFeederResultsPage(Specification<FeederResultEntity> specification, Pageable pageable) {
239-
Objects.requireNonNull(specification);
242+
public Page<FeederResultEntity> findFeederResultsPage(ShortCircuitAnalysisResultEntity result, List<ResourceFilter> resourceFilters, Pageable pageable) {
243+
Objects.requireNonNull(result);
244+
Specification<FeederResultEntity> specification = FeederResultSpecificationBuilder.buildSpecification(result.getResultUuid(), resourceFilters);
240245
return feederResultRepository.findAll(specification, pageable);
241246
}
242247

243248
@Transactional(readOnly = true)
244-
public Optional<Page<FaultResultEntity>> findFaultResultsWithLimitViolationsPage(ShortCircuitAnalysisResultEntity result, Pageable pageable) {
249+
public Page<FaultResultEntity> findFaultResultsWithLimitViolationsPage(ShortCircuitAnalysisResultEntity result, List<ResourceFilter> resourceFilters, Pageable pageable) {
245250
Objects.requireNonNull(result);
251+
Specification<FaultResultEntity> specification = FaultResultSpecificationBuilder.buildSpecification(result.getResultUuid(), resourceFilters);
252+
specification = FaultResultSpecificationBuilder.appendWithLimitViolationsToSpecification(specification);
246253
// WARN org.hibernate.hql.internal.ast.QueryTranslatorImpl -
247254
// HHH000104: firstResult/maxResults specified with collection fetch; applying in memory!
248255
// cf. https://vladmihalcea.com/fix-hibernate-hhh000104-entity-fetch-pagination-warning-message/
249256
// We must separate in two requests, one with pagination the other one with Join Fetch
250-
Optional<Page<FaultResultEntity>> faultResultsPage = faultResultRepository.findPagedByResultAndNbLimitViolationsGreaterThan(result, 0, pageable);
251-
if (faultResultsPage.isPresent()) {
252-
appendLimitViolationsAndFeederResults(faultResultsPage.get());
257+
Page<FaultResultEntity> faultResultsPage = faultResultRepository.findAll(specification, pageable);
258+
if (faultResultsPage.hasContent()) {
259+
appendLimitViolationsAndFeederResults(faultResultsPage);
253260
}
254261
return faultResultsPage;
255262
}

src/main/java/org/gridsuite/shortcircuit/server/service/ShortCircuitService.java

Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,17 @@
66
*/
77
package org.gridsuite.shortcircuit.server.service;
88

9-
import com.powsybl.ws.commons.LogUtils;
10-
119
import com.fasterxml.jackson.databind.ObjectMapper;
10+
import com.powsybl.ws.commons.LogUtils;
1211
import org.gridsuite.shortcircuit.server.dto.*;
1312
import org.gridsuite.shortcircuit.server.entities.*;
1413
import org.gridsuite.shortcircuit.server.repositories.ShortCircuitAnalysisResultRepository;
15-
import org.gridsuite.shortcircuit.server.utils.FeederResultSpecifications;
14+
import org.slf4j.Logger;
15+
import org.slf4j.LoggerFactory;
1616
import org.springframework.beans.factory.annotation.Autowired;
1717
import org.springframework.data.domain.Page;
1818
import org.springframework.data.domain.Pageable;
19-
import org.springframework.data.jpa.domain.Specification;
2019
import org.springframework.stereotype.Service;
21-
import org.slf4j.Logger;
22-
import org.slf4j.LoggerFactory;
2320
import org.springframework.transaction.annotation.Transactional;
2421

2522
import java.util.*;
@@ -65,10 +62,10 @@ private static ShortCircuitAnalysisResult fromEntity(ShortCircuitAnalysisResultE
6562
List<FaultResult> faultResults = new ArrayList<>();
6663
switch (mode) {
6764
case BASIC, FULL:
68-
faultResults = resultEntity.getFaultResults().stream().map(fr -> fromEntity(fr, mode)).collect(Collectors.toList());
65+
faultResults = resultEntity.getFaultResults().stream().map(fr -> fromEntity(fr, mode)).toList();
6966
break;
7067
case WITH_LIMIT_VIOLATIONS:
71-
faultResults = resultEntity.getFaultResults().stream().filter(fr -> !fr.getLimitViolations().isEmpty()).map(fr -> fromEntity(fr, mode)).collect(Collectors.toList());
68+
faultResults = resultEntity.getFaultResults().stream().filter(fr -> !fr.getLimitViolations().isEmpty()).map(fr -> fromEntity(fr, mode)).toList();
7269
break;
7370
case NONE:
7471
default:
@@ -87,8 +84,8 @@ private static FaultResult fromEntity(FaultResultEntity faultResultEntity, Fault
8784
List<FeederResult> feederResults = new ArrayList<>();
8885
if (mode != FaultResultsMode.BASIC) {
8986
// if we enter here, by calling the getters, the limit violations and feeder results will be loaded even if we don't want to in some mode
90-
limitViolations = faultResultEntity.getLimitViolations().stream().map(lv -> fromEntity(lv)).collect(Collectors.toList());
91-
feederResults = faultResultEntity.getFeederResults().stream().map(fr -> fromEntity(fr)).collect(Collectors.toList());
87+
limitViolations = faultResultEntity.getLimitViolations().stream().map(ShortCircuitService::fromEntity).toList();
88+
feederResults = faultResultEntity.getFeederResults().stream().map(ShortCircuitService::fromEntity).toList();
9289
}
9390
return new FaultResult(fault, current, positiveMagnitude, shortCircuitPower, limitViolations, feederResults, shortCircuitLimits);
9491
}
@@ -136,39 +133,40 @@ public ShortCircuitAnalysisResult getResult(UUID resultUuid, FaultResultsMode mo
136133
ShortCircuitAnalysisResultEntity sortedResult = sortByElementId(result.get());
137134

138135
ShortCircuitAnalysisResult res = fromEntity(sortedResult, mode);
139-
LOGGER.info("Get ShortCircuit Results {} in {}ms", resultUuid, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime.get()));
136+
if (LOGGER.isInfoEnabled()) {
137+
LOGGER.info("Get ShortCircuit Results {} in {}ms", resultUuid, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime.get()));
138+
}
140139
return res;
141140
}
142141
return null;
143142
}
144143

145144
@Transactional(readOnly = true)
146-
public Page<FaultResult> getFaultResultsPage(UUID resultUuid, FaultResultsMode mode, Pageable pageable) {
145+
public Page<FaultResult> getFaultResultsPage(UUID resultUuid, FaultResultsMode mode, List<ResourceFilter> resourceFilters, Pageable pageable) {
147146
AtomicReference<Long> startTime = new AtomicReference<>();
148147
startTime.set(System.nanoTime());
149148
Optional<ShortCircuitAnalysisResultEntity> result;
150149
// get without faultResults : FaultResultsM.NONE
151150
result = resultRepository.find(resultUuid);
152151
if (result.isPresent()) {
153-
Optional<Page<FaultResultEntity>> faultResultEntitiesPage = Optional.empty();
152+
Page<FaultResultEntity> faultResultEntitiesPage = Page.empty();
154153
switch (mode) {
155154
case BASIC, FULL:
156-
faultResultEntitiesPage = resultRepository.findFaultResultsPage(result.get(), pageable, mode);
155+
faultResultEntitiesPage = resultRepository.findFaultResultsPage(result.get(), resourceFilters, pageable, mode);
157156
break;
158157
case WITH_LIMIT_VIOLATIONS:
159-
faultResultEntitiesPage = resultRepository.findFaultResultsWithLimitViolationsPage(result.get(), pageable);
158+
faultResultEntitiesPage = resultRepository.findFaultResultsWithLimitViolationsPage(result.get(), resourceFilters, pageable);
160159
break;
161160
case NONE:
162161
default:
163162
break;
164163
}
165-
if (faultResultEntitiesPage.isPresent()) {
166-
Page<FaultResult> faultResultsPage = faultResultEntitiesPage.get().map(fr -> fromEntity(fr, mode));
164+
Page<FaultResult> faultResultsPage = faultResultEntitiesPage.map(fr -> fromEntity(fr, mode));
165+
if (LOGGER.isInfoEnabled()) {
167166
LOGGER.info("Get ShortCircuit Results {} in {}ms", resultUuid, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime.get()));
168-
String pageableStr = LogUtils.sanitizeParam(pageable.toString());
169-
LOGGER.info("pageable = {}", pageableStr);
170-
return faultResultsPage;
167+
LOGGER.info("pageable = {}", LogUtils.sanitizeParam(pageable.toString()));
171168
}
169+
return faultResultsPage;
172170
}
173171
return null;
174172
}
@@ -179,12 +177,12 @@ public Page<FeederResult> getFeederResultsPage(UUID resultUuid, List<ResourceFil
179177
startTime.set(System.nanoTime());
180178
Optional<ShortCircuitAnalysisResultEntity> result = resultRepository.find(resultUuid);
181179
if (result.isPresent()) {
182-
Specification<FeederResultEntity> specification = FeederResultSpecifications.buildSpecification(result.get().getResultUuid(), resourceFilters);
183-
Page<FeederResultEntity> feederResultEntitiesPage = resultRepository.findFeederResultsPage(specification, pageable);
184-
Page<FeederResult> feederResultsPage = feederResultEntitiesPage.map(fr -> fromEntity(fr));
185-
LOGGER.info("Get ShortCircuit Results {} in {}ms", resultUuid, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime.get()));
186-
String pageableStr = LogUtils.sanitizeParam(pageable.toString());
187-
LOGGER.info("pageable = {}", pageableStr);
180+
Page<FeederResultEntity> feederResultEntitiesPage = resultRepository.findFeederResultsPage(result.get(), resourceFilters, pageable);
181+
Page<FeederResult> feederResultsPage = feederResultEntitiesPage.map(ShortCircuitService::fromEntity);
182+
if (LOGGER.isInfoEnabled()) {
183+
LOGGER.info("Get ShortCircuit Results {} in {}ms", resultUuid, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime.get()));
184+
LOGGER.info("pageable = {}", LogUtils.sanitizeParam(pageable.toString()));
185+
}
188186
return feederResultsPage;
189187
}
190188
return null;
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
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.utils;
9+
10+
import org.gridsuite.shortcircuit.server.dto.ResourceFilter;
11+
import org.gridsuite.shortcircuit.server.entities.FaultResultEntity;
12+
import org.springframework.data.jpa.domain.Specification;
13+
14+
import java.util.List;
15+
import java.util.UUID;
16+
17+
/**
18+
* @author Florent MILLOT <[email protected]>
19+
*/
20+
public final class FaultResultSpecificationBuilder {
21+
22+
// Utility class, so no constructor
23+
private FaultResultSpecificationBuilder() {
24+
}
25+
26+
public static Specification<FaultResultEntity> resultUuidEquals(UUID value) {
27+
return (faultResult, cq, cb) -> cb.equal(faultResult.get("result").get("resultUuid"), value);
28+
}
29+
30+
public static Specification<FaultResultEntity> buildSpecification(UUID resultUuid, List<ResourceFilter> resourceFilters) {
31+
Specification<FaultResultEntity> specification = Specification.where(resultUuidEquals(resultUuid));
32+
return SpecificationUtils.appendFiltersToSpecification(specification, resourceFilters);
33+
}
34+
35+
public static Specification<FaultResultEntity> appendWithLimitViolationsToSpecification(Specification<FaultResultEntity> specification) {
36+
return specification.and(SpecificationUtils.isNotEmpty("limitViolations"));
37+
}
38+
}

0 commit comments

Comments
 (0)