Skip to content

Commit

Permalink
Add pagination and filtering for one bus analysis (#41)
Browse files Browse the repository at this point in the history
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 <millotflo@gmail.com>
  • Loading branch information
flomillot authored Nov 2, 2023
1 parent 83e70fe commit 657adbe
Show file tree
Hide file tree
Showing 14 changed files with 862 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/
package org.gridsuite.shortcircuit.server;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.powsybl.shortcircuit.ShortCircuitParameters;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
Expand All @@ -14,11 +15,7 @@
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;

import org.gridsuite.shortcircuit.server.dto.FaultResult;
import org.gridsuite.shortcircuit.server.dto.FaultResultsMode;
import org.gridsuite.shortcircuit.server.dto.ShortCircuitAnalysisResult;
import org.gridsuite.shortcircuit.server.dto.ShortCircuitAnalysisStatus;
import org.gridsuite.shortcircuit.server.dto.*;
import org.gridsuite.shortcircuit.server.service.ShortCircuitRunContext;
import org.gridsuite.shortcircuit.server.service.ShortCircuitService;
import org.springframework.data.domain.Page;
Expand Down Expand Up @@ -74,7 +71,10 @@ public ResponseEntity<UUID> runAndSave(@Parameter(description = "Network UUID")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The short circuit analysis result"),
@ApiResponse(responseCode = "404", description = "Short circuit analysis result has not been found")})
public ResponseEntity<ShortCircuitAnalysisResult> getResult(@Parameter(description = "Result UUID") @PathVariable("resultUuid") UUID resultUuid,
@Parameter(description = "Full or only those with limit violations or none fault results") @RequestParam(name = "mode", required = false, defaultValue = "WITH_LIMIT_VIOLATIONS") FaultResultsMode mode) {
@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) {
ShortCircuitAnalysisResult result = shortCircuitService.getResult(resultUuid, mode);
return result != null ? ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(result)
: ResponseEntity.notFound().build();
Expand All @@ -85,11 +85,27 @@ public ResponseEntity<ShortCircuitAnalysisResult> getResult(@Parameter(descripti
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The page of fault results"),
@ApiResponse(responseCode = "404", description = "Short circuit analysis result has not been found")})
public ResponseEntity<Page<FaultResult>> getPagedFaultResults(@Parameter(description = "Result UUID") @PathVariable("resultUuid") UUID resultUuid,
@Parameter(description = "Full or only those with limit violations or none fault results") @RequestParam(name = "mode", required = false, defaultValue = "WITH_LIMIT_VIOLATIONS") FaultResultsMode mode,
Pageable pageable) {
@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);
return faultResultsPage != null ? ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(faultResultsPage)
: ResponseEntity.notFound().build();
: ResponseEntity.notFound().build();
}

@GetMapping(value = "/results/{resultUuid}/feeder_results/paged", produces = APPLICATION_JSON_VALUE)
@Operation(summary = "Get a feeder results page for a given short circuit analysis result")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The page of feeder results"),
@ApiResponse(responseCode = "404", description = "Short circuit analysis result has not been found")})
public ResponseEntity<Page<FeederResult>> getPagedFeederResults(@Parameter(description = "Result UUID") @PathVariable("resultUuid") UUID resultUuid,
@Parameter(description = "Filters") @RequestParam(name = "filters", required = false) String stringFilters,
Pageable pageable) throws JsonProcessingException {
List<ResourceFilter> resourceFilters = ResourceFilter.fromStringToList(stringFilters);
Page<FeederResult> feederResultsPage = shortCircuitService.getFeederResultsPage(resultUuid, resourceFilters, pageable);
return feederResultsPage != null ? ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(feederResultsPage)
: ResponseEntity.notFound().build();
}

@DeleteMapping(value = "/results/{resultUuid}", produces = APPLICATION_JSON_VALUE)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,24 @@
package org.gridsuite.shortcircuit.server.dto;

/**
* Specify if the faults are present in the result DTO and if so what they contain
* @author Sylvain Bouzols <sylvain.bouzols at rte-france.com
*/
public enum FaultResultsMode {
/**
* No fault present
*/
NONE,
/**
* Present but without the limit violations and feeders
*/
BASIC,
/**
* Present with all fields but filtered by limit violations presence
*/
WITH_LIMIT_VIOLATIONS,
/**
* Present with all fields
*/
FULL
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/**
* 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.dto;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.util.List;

/**
* An object that can be used to filter data with the JPA Criteria API (via Spring Specification)
* @param dataType the type of data we want to filter (text, number)
* @param type the type of filter (contains, startsWith...)
* @param value the value of the filter
* @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) {

private static ObjectMapper objectMapper = new ObjectMapper();

public enum DataType {
@JsonProperty("text")
TEXT,
@JsonProperty("number")
NUMBER,
}

public enum Type {
@JsonProperty("contains")
CONTAINS,
@JsonProperty("startsWith")
STARTS_WITH,
@JsonProperty("notEqual")
NOT_EQUAL,
@JsonProperty("lessThanOrEqual")
LESS_THAN_OR_EQUAL,
@JsonProperty("greaterThanOrEqual")
GREATER_THAN_OR_EQUAL
}

public static List<ResourceFilter> fromStringToList(String filters) throws JsonProcessingException {
if (filters == null || filters.isEmpty()) {
return List.of();
}
return objectMapper.readValue(filters, new TypeReference<>() {
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,16 @@ public class FaultResultEntity {
columnList = "fault_result_entity_fault_result_uuid")})
private List<LimitViolationEmbeddable> limitViolations;

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

@Column
private double ipMax;
Expand Down Expand Up @@ -99,19 +104,28 @@ public class FaultResultEntity {
@AttributeOverride(name = "angleC", column = @Column(name = "fortescue_voltage_angle_c"))
private FortescueResultEmbeddable fortescueVoltage;

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) {
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) {
this.fault = fault;
this.current = current;
this.shortCircuitPower = shortCircuitPower;
this.limitViolations = limitViolations;
this.nbLimitViolations = limitViolations.size();
this.feederResults = feederResults;
if (limitViolations != null) {
this.limitViolations = limitViolations;
this.nbLimitViolations = limitViolations.size();
}
this.ipMin = ipMin;
this.ipMax = ipMax;
this.fortescueCurrent = fortescueCurrent;
this.fortescueVoltage = fortescueVoltage;
this.deltaCurrentIpMin = deltaCurrentIpMin;
this.deltaCurrentIpMax = deltaCurrentIpMax;
if (feederResults != null) {
setFeederResults(feederResults);
}
}

public void setFeederResults(List<FeederResultEntity> feederResults) {
this.feederResults = feederResults;
feederResults.stream().forEach(feederResultEntity -> feederResultEntity.setFaultResult(this));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,34 @@
*/
package org.gridsuite.shortcircuit.server.entities;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

import jakarta.persistence.*;

import java.util.UUID;

/**
* @author Nicolas Noir <nicolas.noir at rte-france.com>
*/
@Getter
@AllArgsConstructor
@NoArgsConstructor
@Embeddable
public class FeederResultEmbeddable {
@Entity
@Table(name = "feeder_results",
indexes = {@Index(name = "feeder_results_fault_result_idx",
columnList = "fault_result_entity_fault_result_uuid")})
public class FeederResultEntity {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private UUID feederResultUuid;

@ManyToOne(
cascade = CascadeType.ALL,
fetch = FetchType.LAZY
)
@JoinColumn(name = "fault_result_entity_fault_result_uuid")
private FaultResultEntity faultResult;

@Column
private String connectableId;
Expand All @@ -45,4 +59,14 @@ public class FeederResultEmbeddable {
public double getPositiveMagnitude() {
return this.getFortescueCurrent() != null ? this.getFortescueCurrent().getPositiveMagnitude() : Double.NaN;
}

public void setFaultResult(FaultResultEntity faultResult) {
this.faultResult = faultResult;
}

public FeederResultEntity(String connectableId, double current, FortescueResultEmbeddable fortescueCurrent) {
this.connectableId = connectableId;
this.current = current;
this.fortescueCurrent = fortescueCurrent;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* 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.repositories;

import org.gridsuite.shortcircuit.server.entities.FeederResultEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Repository;

import java.util.UUID;

/**
* @author Florent MILLOT <florent.millot@rte-france.com>
*/
@Repository
public interface FeederResultRepository extends JpaRepository<FeederResultEntity, UUID>, JpaSpecificationExecutor<FeederResultEntity> {
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,14 @@
public interface ResultRepository extends JpaRepository<ShortCircuitAnalysisResultEntity, UUID> {
Optional<ShortCircuitAnalysisResultEntity> findByResultUuid(UUID resultUuid);

@EntityGraph(attributePaths = {"faultResults"}, type = EntityGraphType.LOAD)
Optional<ShortCircuitAnalysisResultEntity> findWithFaultResultsByResultUuid(UUID resultUuid);

@EntityGraph(attributePaths = {"faultResults", "faultResults.limitViolations"}, type = EntityGraphType.LOAD)
Optional<ShortCircuitAnalysisResultEntity> findAllWithLimitViolationsByResultUuid(UUID resultUuid);
Optional<ShortCircuitAnalysisResultEntity> findWithFaultResultsAndLimitViolationsByResultUuid(UUID resultUuid);

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

void deleteByResultUuid(UUID resultUuid);
}
Loading

0 comments on commit 657adbe

Please sign in to comment.