Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dynamic Simulation - Curve Configuration - Add some expert filter's criteria for Load and Generator and new operators IN, NOT_IN #78

Merged
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
13 changes: 13 additions & 0 deletions src/main/java/org/gridsuite/filter/server/FilterController.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import org.springframework.web.bind.annotation.*;

import jakarta.persistence.EntityNotFoundException;

import java.util.List;
import java.util.Optional;
import java.util.UUID;
Expand Down Expand Up @@ -158,4 +159,16 @@ public ResponseEntity<List<FilterEquipments>> exportFilters(@RequestParam("ids")
.contentType(MediaType.APPLICATION_JSON)
.body(ret);
}

@PostMapping(value = "/filters/evaluate", produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Export matched elements to JSON format")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "The list of matched elements")
})
public ResponseEntity<List<IdentifiableAttributes>> evaluateFilter(@RequestParam(value = "networkUuid") UUID networkUuid,
@RequestParam(value = "variantId", required = false) String variantId,
@RequestBody AbstractFilter filter) {
List<IdentifiableAttributes> identifiableAttributes = service.evaluateFilter(filter, networkUuid, variantId);
return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(identifiableAttributes);
}
}
5 changes: 5 additions & 0 deletions src/main/java/org/gridsuite/filter/server/FilterService.java
Original file line number Diff line number Diff line change
Expand Up @@ -685,6 +685,11 @@ private List<IdentifiableAttributes> getIdentifiableAttributes(AbstractFilter fi
}
}

public List<IdentifiableAttributes> evaluateFilter(AbstractFilter filter, UUID networkUuid, String variantId) {
Objects.requireNonNull(filter);
return getIdentifiableAttributes(filter, networkUuid, variantId);
}

public Optional<List<IdentifiableAttributes>> exportFilter(UUID id, UUID networkUuid, String variantId) {
Objects.requireNonNull(id);
return getFilter(id).map(filter -> getIdentifiableAttributes(filter, networkUuid, variantId));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,7 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import com.powsybl.commons.PowsyblException;
import com.powsybl.iidm.network.Identifiable;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import org.gridsuite.filter.server.utils.expertfilter.DataType;

Expand All @@ -22,18 +19,8 @@
* @author Antoine Bouhours <antoine.bouhours at rte-france.com>
*/
@AllArgsConstructor
@NoArgsConstructor
@Getter
@SuperBuilder
public class EnumExpertRule extends AbstractExpertRule {

@Schema(description = "Value")
private String value;

@Override
public String getStringValue() {
return getValue();
}
public class EnumExpertRule extends StringExpertRule {

@Override
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
Expand All @@ -47,6 +34,8 @@ public boolean evaluateRule(Identifiable<?> identifiable) {
return switch (this.getOperator()) {
case EQUALS -> identifiableValue.equals(this.getValue());
case NOT_EQUALS -> !identifiableValue.equals(this.getValue());
case IN -> this.getValues().contains(identifiableValue);
case NOT_IN -> !this.getValues().contains(identifiableValue);
default -> throw new PowsyblException(this.getOperator() + " operator not supported with " + this.getDataType() + " rule data type");
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
package org.gridsuite.filter.server.dto.expertfilter.expertrule;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.powsybl.commons.PowsyblException;
import com.powsybl.iidm.network.Identifiable;
import io.swagger.v3.oas.annotations.media.Schema;
Expand All @@ -16,7 +17,12 @@
import lombok.experimental.SuperBuilder;
import org.gridsuite.filter.server.utils.expertfilter.DataType;

import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;

import static org.gridsuite.filter.server.utils.expertfilter.ExpertFilterUtils.getFieldValue;
import static org.gridsuite.filter.server.utils.expertfilter.OperatorType.isMultipleCriteriaOperator;

/**
* @author Antoine Bouhours <antoine.bouhours at rte-france.com>
Expand All @@ -30,6 +36,10 @@ public class NumberExpertRule extends AbstractExpertRule {
@Schema(description = "Value")
private Double value;

@Schema(description = "Values")
@JsonDeserialize(as = HashSet.class)
private Set<Double> values;

public static Double getNumberValue(String value) {
return Double.parseDouble(value);
}
Expand All @@ -54,12 +64,18 @@ public boolean evaluateRule(Identifiable<?> identifiable) {
case LOWER_OR_EQUALS -> identifiableValue.compareTo(filterValue) <= 0;
case LOWER -> identifiableValue.compareTo(filterValue) < 0;
case EXISTS -> true; // We return true here because we already test above if identifiableValue is NaN.
case IN -> this.getValues().contains(identifiableValue);
case NOT_IN -> !this.getValues().contains(identifiableValue);
Comment on lines +67 to +68
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential NPE

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Idem! Should manage at DTO level by a validator to do a cross validation between two fields. This will be corrected in another PR

default -> throw new PowsyblException(this.getOperator() + " operator not supported with " + this.getDataType() + " rule data type");
};
}

@Override
public String getStringValue() {
return String.valueOf(this.getValue());
if (isMultipleCriteriaOperator(this.getOperator())) { // multiple values
return this.getValues().stream().map(String::valueOf).collect(Collectors.joining(","));
} else { // single value or absence
Comment on lines +75 to +77
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe return an empty string, null or String.valueOf(Collections.<String>emptySet()) if this.values is null ?
This is to prevent a NPE.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should manage at DTO level by a validator to do a cross validation between two fields. This will be corrected in another PR

return this.getValue() != null ? String.valueOf(this.getValue()) : null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
package org.gridsuite.filter.server.dto.expertfilter.expertrule;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.powsybl.commons.PowsyblException;
import com.powsybl.iidm.network.Identifiable;
import io.swagger.v3.oas.annotations.media.Schema;
Expand All @@ -17,7 +18,11 @@
import org.apache.commons.lang3.StringUtils;
import org.gridsuite.filter.server.utils.expertfilter.DataType;

import java.util.HashSet;
import java.util.Set;

import static org.gridsuite.filter.server.utils.expertfilter.ExpertFilterUtils.getFieldValue;
import static org.gridsuite.filter.server.utils.expertfilter.OperatorType.isMultipleCriteriaOperator;

/**
* @author Antoine Bouhours <antoine.bouhours at rte-france.com>
Expand All @@ -31,6 +36,19 @@ public class StringExpertRule extends AbstractExpertRule {
@Schema(description = "Value")
private String value;

@Schema(description = "Values")
@JsonDeserialize(as = HashSet.class)
private Set<String> values;

@Override
public String getStringValue() {
if (isMultipleCriteriaOperator(this.getOperator())) { // multiple values
return String.join(",", this.getValues());
} else { // single value or absence
Comment on lines +45 to +47
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, throws a NPE if this.getValues() is null.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Idem! Should manage at DTO level by a validator to do a cross validation between two fields. This will be corrected in another PR

return this.getValue();
}
}

@Override
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
public DataType getDataType() {
Expand All @@ -46,12 +64,9 @@ public boolean evaluateRule(Identifiable<?> identifiable) {
case BEGINS_WITH -> StringUtils.startsWithIgnoreCase(identifiableValue, this.getValue());
case ENDS_WITH -> StringUtils.endsWithIgnoreCase(identifiableValue, this.getValue());
case EXISTS -> !StringUtils.isEmpty(identifiableValue);
case IN -> this.getValues().contains(identifiableValue);
case NOT_IN -> !this.getValues().contains(identifiableValue);
Comment on lines +67 to +68
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential NPE

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Idem! Should manage at DTO level by a validator to do a cross validation between two fields. This will be corrected in another PR

default -> throw new PowsyblException(this.getOperator() + " operator not supported with " + this.getDataType() + " rule data type");
};
}

@Override
public String getStringValue() {
return getValue();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
package org.gridsuite.filter.server.repositories.proxies.expertfiler;

import com.powsybl.commons.PowsyblException;
import org.apache.commons.lang3.math.NumberUtils;
import org.gridsuite.filter.server.dto.criteriafilter.AbstractEquipmentFilterForm;
import org.gridsuite.filter.server.dto.AbstractFilter;
import org.gridsuite.filter.server.dto.expertfilter.ExpertFilter;
Expand All @@ -25,6 +24,9 @@
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static org.gridsuite.filter.server.utils.expertfilter.OperatorType.isMultipleCriteriaOperator;

/**
* @author Antoine Bouhours <antoine.bouhours at rte-france.com>
Expand Down Expand Up @@ -67,31 +69,43 @@ public static AbstractExpertRule entityToDto(ExpertRuleEntity filterEntity) {
.build();
}
case NUMBER -> {
Double newValue;
if (NumberUtils.isCreatable(filterEntity.getValue())) {
newValue = NumberUtils.createDouble(filterEntity.getValue());
} else {
newValue = Double.NaN;
}
return NumberExpertRule.builder()
NumberExpertRule.NumberExpertRuleBuilder<?, ?> ruleBuilder = NumberExpertRule.builder()
.field(filterEntity.getField())
.operator(filterEntity.getOperator())
.value(newValue)
.build();
.operator(filterEntity.getOperator());
if (filterEntity.getValue() != null) {
if (isMultipleCriteriaOperator(filterEntity.getOperator())) { // for multiple values
ruleBuilder.values(Stream.of(filterEntity.getValue().split(",")).map(Double::valueOf).collect(Collectors.toSet()));
} else { // for single value
ruleBuilder.value(Double.valueOf(filterEntity.getValue()));
}
}
return ruleBuilder.build();
}
case STRING -> {
return StringExpertRule.builder()
StringExpertRule.StringExpertRuleBuilder<?, ?> ruleBuilder = StringExpertRule.builder()
.field(filterEntity.getField())
.operator(filterEntity.getOperator())
.value(filterEntity.getValue())
.build();
.operator(filterEntity.getOperator());
if (filterEntity.getValue() != null) {
if (isMultipleCriteriaOperator(filterEntity.getOperator())) { // for multiple values
ruleBuilder.values(Stream.of(filterEntity.getValue().split(",")).collect(Collectors.toSet()));
} else { // for single value
ruleBuilder.value(filterEntity.getValue());
}
}
return ruleBuilder.build();
}
case ENUM -> {
return EnumExpertRule.builder()
EnumExpertRule.EnumExpertRuleBuilder<?, ?> ruleBuilder = EnumExpertRule.builder()
.field(filterEntity.getField())
.operator(filterEntity.getOperator())
.value(filterEntity.getValue())
.build();
.operator(filterEntity.getOperator());
if (filterEntity.getValue() != null) {
if (isMultipleCriteriaOperator(filterEntity.getOperator())) { // for multiple values
ruleBuilder.values(Stream.of(filterEntity.getValue().split(",")).collect(Collectors.toSet()));
} else { // for single value
ruleBuilder.value(filterEntity.getValue());
}
}
return ruleBuilder.build();
}
default -> throw new PowsyblException("Unknown rule data type: " + filterEntity.getDataType() + ", supported data types are: COMBINATOR, BOOLEAN, NUMBER, STRING, ENUM");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,22 +26,28 @@ public static <I extends Identifiable<I>> String getFieldValue(FieldType field,
};
}

private static String getInjectionFieldValue(FieldType field, Injection<?> injection) {
return switch (field) {
case ID -> injection.getId();
case NAME -> injection.getNameOrId();
case COUNTRY -> {
Optional<Country> country = injection.getTerminal().getVoltageLevel().getSubstation().flatMap(Substation::getCountry);
yield country.isPresent() ? String.valueOf(country.get()) : "";
}
case NOMINAL_VOLTAGE -> String.valueOf(injection.getTerminal().getVoltageLevel().getNominalV());
case VOLTAGE_LEVEL_ID -> injection.getTerminal().getVoltageLevel().getId();
default -> throw new PowsyblException("Field " + field + " with " + injection.getType() + " injection type is not implemented with expert filter");
};
}

private static String getLoadFieldValue(FieldType field, Load load) {
return switch (field) {
case ID -> load.getId();
default -> throw new PowsyblException("Field " + field + " with " + load.getType() + " injection type is not implemented with expert filter");
default -> getInjectionFieldValue(field, load);
};
}

private static String getGeneratorFieldValue(FieldType field, Generator generator) {
return switch (field) {
case ID -> generator.getId();
case NAME -> generator.getNameOrId();
case NOMINAL_VOLTAGE -> String.valueOf(generator.getTerminal().getVoltageLevel().getNominalV());
case COUNTRY -> {
Optional<Country> country = generator.getTerminal().getVoltageLevel().getSubstation().flatMap(Substation::getCountry);
yield country.isPresent() ? String.valueOf(country.get()) : "";
}
case ENERGY_SOURCE -> String.valueOf(generator.getEnergySource());
case MIN_P -> String.valueOf(generator.getMinP());
case MAX_P -> String.valueOf(generator.getMaxP());
Expand All @@ -56,6 +62,7 @@ private static String getGeneratorFieldValue(FieldType field, Generator generato
}
yield String.valueOf(Double.NaN);
}
default -> getInjectionFieldValue(field, generator);
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,6 @@ public enum FieldType {
ENERGY_SOURCE,
COUNTRY,
VOLTAGE_REGULATOR_ON,
PLANNED_ACTIVE_POWER_SET_POINT
PLANNED_ACTIVE_POWER_SET_POINT,
VOLTAGE_LEVEL_ID,
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ public enum OperatorType {
// Common
EQUALS,
NOT_EQUALS,
IN,
NOT_IN,
// Number and String
EXISTS,
// Number
Expand All @@ -24,5 +26,9 @@ public enum OperatorType {
IS,
CONTAINS,
BEGINS_WITH,
ENDS_WITH,
ENDS_WITH;

public static boolean isMultipleCriteriaOperator(OperatorType operator) {
return operator == IN || operator == NOT_IN;
}
}
Loading
Loading