Skip to content

Commit

Permalink
Add filtering to fault results (#48)
Browse files Browse the repository at this point in the history
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 <millotflo@gmail.com>
  • Loading branch information
flomillot authored Nov 2, 2023
1 parent 657adbe commit a25c0f1
Show file tree
Hide file tree
Showing 12 changed files with 641 additions and 246 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
package org.gridsuite.shortcircuit.server;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.powsybl.security.LimitViolationType;
import com.powsybl.shortcircuit.ShortCircuitParameters;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
Expand All @@ -24,9 +25,9 @@
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.UUID;
import java.util.*;

import static com.powsybl.shortcircuit.Fault.FaultType;
import static org.gridsuite.shortcircuit.server.service.NotificationService.HEADER_USER_ID;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;

Expand Down Expand Up @@ -88,9 +89,11 @@ public ResponseEntity<Page<FaultResult>> getPagedFaultResults(@Parameter(descrip
@Parameter(description = "BASIC (faults without limits and feeders), " +
"FULL (faults with both), " +
"WITH_LIMIT_VIOLATIONS (like FULL but only those with limit violations) or " +
"NONE (no fault)") @RequestParam(name = "mode", required = false, defaultValue = "WITH_LIMIT_VIOLATIONS") FaultResultsMode mode,
Pageable pageable) {
Page<FaultResult> faultResultsPage = shortCircuitService.getFaultResultsPage(resultUuid, mode, pageable);
"NONE (no fault)") @RequestParam(name = "mode", required = false, defaultValue = "FULL") FaultResultsMode mode,
@Parameter(description = "Filters") @RequestParam(name = "filters", required = false) String stringFilters,
Pageable pageable) throws JsonProcessingException {
List<ResourceFilter> resourceFilters = ResourceFilter.fromStringToList(stringFilters);
Page<FaultResult> faultResultsPage = shortCircuitService.getFaultResultsPage(resultUuid, mode, resourceFilters, pageable);
return faultResultsPage != null ? ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(faultResultsPage)
: ResponseEntity.notFound().build();
}
Expand Down Expand Up @@ -149,4 +152,18 @@ public ResponseEntity<Void> stop(@Parameter(description = "Result UUID") @PathVa
return ResponseEntity.ok().build();
}

@GetMapping(value = "/fault-types", produces = APPLICATION_JSON_VALUE)
@Operation(summary = "Get list of fault types")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The list of fault types")})
public ResponseEntity<FaultType[]> getFaultTypes() {
return ResponseEntity.ok().body(FaultType.values());
}

@GetMapping(value = "/limit-violation-types", produces = APPLICATION_JSON_VALUE)
@Operation(summary = "Get list of limit violation types")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The list of limit violation types")})
public ResponseEntity<LimitViolationType[]> getLimitTypes() {
return ResponseEntity.ok().body(LimitViolationType.values());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
* @param field the field on which the filter will be applied
* @author Florent MILLOT <florent.millot@rte-france.com>
*/
public record ResourceFilter(DataType dataType, Type type, String value, String field) {
public record ResourceFilter(DataType dataType, Type type, Object value, String field) {

private static ObjectMapper objectMapper = new ObjectMapper();

Expand All @@ -34,6 +34,8 @@ public enum DataType {
}

public enum Type {
@JsonProperty("equals")
EQUALS,
@JsonProperty("contains")
CONTAINS,
@JsonProperty("startsWith")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,22 @@
package org.gridsuite.shortcircuit.server.repositories;

import org.gridsuite.shortcircuit.server.entities.FaultResultEntity;
import org.gridsuite.shortcircuit.server.entities.ShortCircuitAnalysisResultEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.EntityGraph.EntityGraphType;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Repository;

import java.util.UUID;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;


/**
* @author Sylvain Bouzols <sylvain.bouzols at rte-france.com
*/
@Repository
public interface FaultResultRepository extends JpaRepository<FaultResultEntity, UUID> {
Optional<Page<FaultResultEntity>> findPagedByResult(ShortCircuitAnalysisResultEntity result, Pageable pageable);

Optional<Page<FaultResultEntity>> findPagedByResultAndNbLimitViolationsGreaterThan(ShortCircuitAnalysisResultEntity result, int nbLimitViolations, Pageable pageable);

public interface FaultResultRepository extends JpaRepository<FaultResultEntity, UUID>, JpaSpecificationExecutor<FaultResultEntity> {
@EntityGraph(attributePaths = {"limitViolations"}, type = EntityGraphType.LOAD)
Set<FaultResultEntity> findAllWithLimitViolationsByFaultResultUuidIn(List<UUID> faultResultsUUID);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@
import com.powsybl.shortcircuit.*;
import lombok.extern.slf4j.Slf4j;
import org.gridsuite.shortcircuit.server.dto.FaultResultsMode;
import org.gridsuite.shortcircuit.server.dto.ResourceFilter;
import org.gridsuite.shortcircuit.server.dto.ShortCircuitLimits;
import org.gridsuite.shortcircuit.server.entities.*;
import org.gridsuite.shortcircuit.server.utils.FaultResultSpecificationBuilder;
import org.gridsuite.shortcircuit.server.utils.FeederResultSpecificationBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
Expand Down Expand Up @@ -221,35 +224,39 @@ public Optional<ShortCircuitAnalysisResultEntity> findResultsWithLimitViolations
}

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

@Transactional(readOnly = true)
public Page<FeederResultEntity> findFeederResultsPage(Specification<FeederResultEntity> specification, Pageable pageable) {
Objects.requireNonNull(specification);
public Page<FeederResultEntity> findFeederResultsPage(ShortCircuitAnalysisResultEntity result, List<ResourceFilter> resourceFilters, Pageable pageable) {
Objects.requireNonNull(result);
Specification<FeederResultEntity> specification = FeederResultSpecificationBuilder.buildSpecification(result.getResultUuid(), resourceFilters);
return feederResultRepository.findAll(specification, pageable);
}

@Transactional(readOnly = true)
public Optional<Page<FaultResultEntity>> findFaultResultsWithLimitViolationsPage(ShortCircuitAnalysisResultEntity result, Pageable pageable) {
public Page<FaultResultEntity> findFaultResultsWithLimitViolationsPage(ShortCircuitAnalysisResultEntity result, List<ResourceFilter> resourceFilters, Pageable pageable) {
Objects.requireNonNull(result);
Specification<FaultResultEntity> specification = FaultResultSpecificationBuilder.buildSpecification(result.getResultUuid(), resourceFilters);
specification = FaultResultSpecificationBuilder.appendWithLimitViolationsToSpecification(specification);
// WARN org.hibernate.hql.internal.ast.QueryTranslatorImpl -
// HHH000104: firstResult/maxResults specified with collection fetch; applying in memory!
// cf. https://vladmihalcea.com/fix-hibernate-hhh000104-entity-fetch-pagination-warning-message/
// We must separate in two requests, one with pagination the other one with Join Fetch
Optional<Page<FaultResultEntity>> faultResultsPage = faultResultRepository.findPagedByResultAndNbLimitViolationsGreaterThan(result, 0, pageable);
if (faultResultsPage.isPresent()) {
appendLimitViolationsAndFeederResults(faultResultsPage.get());
Page<FaultResultEntity> faultResultsPage = faultResultRepository.findAll(specification, pageable);
if (faultResultsPage.hasContent()) {
appendLimitViolationsAndFeederResults(faultResultsPage);
}
return faultResultsPage;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,17 @@
*/
package org.gridsuite.shortcircuit.server.service;

import com.powsybl.ws.commons.LogUtils;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.powsybl.ws.commons.LogUtils;
import org.gridsuite.shortcircuit.server.dto.*;
import org.gridsuite.shortcircuit.server.entities.*;
import org.gridsuite.shortcircuit.server.repositories.ShortCircuitAnalysisResultRepository;
import org.gridsuite.shortcircuit.server.utils.FeederResultSpecifications;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
Expand Down Expand Up @@ -65,10 +62,10 @@ private static ShortCircuitAnalysisResult fromEntity(ShortCircuitAnalysisResultE
List<FaultResult> faultResults = new ArrayList<>();
switch (mode) {
case BASIC, FULL:
faultResults = resultEntity.getFaultResults().stream().map(fr -> fromEntity(fr, mode)).collect(Collectors.toList());
faultResults = resultEntity.getFaultResults().stream().map(fr -> fromEntity(fr, mode)).toList();
break;
case WITH_LIMIT_VIOLATIONS:
faultResults = resultEntity.getFaultResults().stream().filter(fr -> !fr.getLimitViolations().isEmpty()).map(fr -> fromEntity(fr, mode)).collect(Collectors.toList());
faultResults = resultEntity.getFaultResults().stream().filter(fr -> !fr.getLimitViolations().isEmpty()).map(fr -> fromEntity(fr, mode)).toList();
break;
case NONE:
default:
Expand All @@ -87,8 +84,8 @@ private static FaultResult fromEntity(FaultResultEntity faultResultEntity, Fault
List<FeederResult> feederResults = new ArrayList<>();
if (mode != FaultResultsMode.BASIC) {
// 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
limitViolations = faultResultEntity.getLimitViolations().stream().map(lv -> fromEntity(lv)).collect(Collectors.toList());
feederResults = faultResultEntity.getFeederResults().stream().map(fr -> fromEntity(fr)).collect(Collectors.toList());
limitViolations = faultResultEntity.getLimitViolations().stream().map(ShortCircuitService::fromEntity).toList();
feederResults = faultResultEntity.getFeederResults().stream().map(ShortCircuitService::fromEntity).toList();
}
return new FaultResult(fault, current, positiveMagnitude, shortCircuitPower, limitViolations, feederResults, shortCircuitLimits);
}
Expand Down Expand Up @@ -136,39 +133,40 @@ public ShortCircuitAnalysisResult getResult(UUID resultUuid, FaultResultsMode mo
ShortCircuitAnalysisResultEntity sortedResult = sortByElementId(result.get());

ShortCircuitAnalysisResult res = fromEntity(sortedResult, mode);
LOGGER.info("Get ShortCircuit Results {} in {}ms", resultUuid, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime.get()));
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Get ShortCircuit Results {} in {}ms", resultUuid, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime.get()));
}
return res;
}
return null;
}

@Transactional(readOnly = true)
public Page<FaultResult> getFaultResultsPage(UUID resultUuid, FaultResultsMode mode, Pageable pageable) {
public Page<FaultResult> getFaultResultsPage(UUID resultUuid, FaultResultsMode mode, List<ResourceFilter> resourceFilters, Pageable pageable) {
AtomicReference<Long> startTime = new AtomicReference<>();
startTime.set(System.nanoTime());
Optional<ShortCircuitAnalysisResultEntity> result;
// get without faultResults : FaultResultsM.NONE
result = resultRepository.find(resultUuid);
if (result.isPresent()) {
Optional<Page<FaultResultEntity>> faultResultEntitiesPage = Optional.empty();
Page<FaultResultEntity> faultResultEntitiesPage = Page.empty();
switch (mode) {
case BASIC, FULL:
faultResultEntitiesPage = resultRepository.findFaultResultsPage(result.get(), pageable, mode);
faultResultEntitiesPage = resultRepository.findFaultResultsPage(result.get(), resourceFilters, pageable, mode);
break;
case WITH_LIMIT_VIOLATIONS:
faultResultEntitiesPage = resultRepository.findFaultResultsWithLimitViolationsPage(result.get(), pageable);
faultResultEntitiesPage = resultRepository.findFaultResultsWithLimitViolationsPage(result.get(), resourceFilters, pageable);
break;
case NONE:
default:
break;
}
if (faultResultEntitiesPage.isPresent()) {
Page<FaultResult> faultResultsPage = faultResultEntitiesPage.get().map(fr -> fromEntity(fr, mode));
Page<FaultResult> faultResultsPage = faultResultEntitiesPage.map(fr -> fromEntity(fr, mode));
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Get ShortCircuit Results {} in {}ms", resultUuid, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime.get()));
String pageableStr = LogUtils.sanitizeParam(pageable.toString());
LOGGER.info("pageable = {}", pageableStr);
return faultResultsPage;
LOGGER.info("pageable = {}", LogUtils.sanitizeParam(pageable.toString()));
}
return faultResultsPage;
}
return null;
}
Expand All @@ -179,12 +177,12 @@ public Page<FeederResult> getFeederResultsPage(UUID resultUuid, List<ResourceFil
startTime.set(System.nanoTime());
Optional<ShortCircuitAnalysisResultEntity> result = resultRepository.find(resultUuid);
if (result.isPresent()) {
Specification<FeederResultEntity> specification = FeederResultSpecifications.buildSpecification(result.get().getResultUuid(), resourceFilters);
Page<FeederResultEntity> feederResultEntitiesPage = resultRepository.findFeederResultsPage(specification, pageable);
Page<FeederResult> feederResultsPage = feederResultEntitiesPage.map(fr -> fromEntity(fr));
LOGGER.info("Get ShortCircuit Results {} in {}ms", resultUuid, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime.get()));
String pageableStr = LogUtils.sanitizeParam(pageable.toString());
LOGGER.info("pageable = {}", pageableStr);
Page<FeederResultEntity> feederResultEntitiesPage = resultRepository.findFeederResultsPage(result.get(), resourceFilters, pageable);
Page<FeederResult> feederResultsPage = feederResultEntitiesPage.map(ShortCircuitService::fromEntity);
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Get ShortCircuit Results {} in {}ms", resultUuid, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime.get()));
LOGGER.info("pageable = {}", LogUtils.sanitizeParam(pageable.toString()));
}
return feederResultsPage;
}
return null;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* Copyright (c) 2023, 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.shortcircuit.server.utils;

import org.gridsuite.shortcircuit.server.dto.ResourceFilter;
import org.gridsuite.shortcircuit.server.entities.FaultResultEntity;
import org.springframework.data.jpa.domain.Specification;

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

/**
* @author Florent MILLOT <florent.millot@rte-france.com>
*/
public final class FaultResultSpecificationBuilder {

// Utility class, so no constructor
private FaultResultSpecificationBuilder() {
}

public static Specification<FaultResultEntity> resultUuidEquals(UUID value) {
return (faultResult, cq, cb) -> cb.equal(faultResult.get("result").get("resultUuid"), value);
}

public static Specification<FaultResultEntity> buildSpecification(UUID resultUuid, List<ResourceFilter> resourceFilters) {
Specification<FaultResultEntity> specification = Specification.where(resultUuidEquals(resultUuid));
return SpecificationUtils.appendFiltersToSpecification(specification, resourceFilters);
}

public static Specification<FaultResultEntity> appendWithLimitViolationsToSpecification(Specification<FaultResultEntity> specification) {
return specification.and(SpecificationUtils.isNotEmpty("limitViolations"));
}
}
Loading

0 comments on commit a25c0f1

Please sign in to comment.