diff --git a/src/main/java/org/gridsuite/filter/server/ExpertFilterRepositoryProxy.java b/src/main/java/org/gridsuite/filter/server/ExpertFilterRepositoryProxy.java new file mode 100644 index 00000000..bfafa799 --- /dev/null +++ b/src/main/java/org/gridsuite/filter/server/ExpertFilterRepositoryProxy.java @@ -0,0 +1,168 @@ +/** + * 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.filter.server; + +import com.powsybl.commons.PowsyblException; +import org.gridsuite.filter.server.dto.*; +import org.gridsuite.filter.server.dto.expertrule.*; +import org.gridsuite.filter.server.entities.*; +import org.gridsuite.filter.server.repositories.ExpertFilterRepository; +import org.gridsuite.filter.server.utils.EquipmentType; +import org.gridsuite.filter.server.utils.FilterType; + +import java.util.Collections; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * @author Antoine Bouhours + */ +public class ExpertFilterRepositoryProxy extends AbstractFilterRepositoryProxy { + private final ExpertFilterRepository expertFilterRepository; + + public ExpertFilterRepositoryProxy(ExpertFilterRepository expertFilterRepository) { + this.expertFilterRepository = expertFilterRepository; + } + + @Override + ExpertFilterRepository getRepository() { + return expertFilterRepository; + } + + @Override + AbstractFilter toDto(ExpertFilterEntity filterEntity) { + return ExpertFilter.builder() + .id(filterEntity.getId()) + .modificationDate(filterEntity.getModificationDate()) + .equipmentType(filterEntity.getEquipmentType()) + .rules(entityToDto(filterEntity.getRules())) + .build(); + } + + public static AbstractExpertRule entityToDto(ExpertRuleEntity filterEntity) { + switch (filterEntity.getDataType()) { + case COMBINATOR -> { + return CombinatorExpertRule.builder() + .combinator(filterEntity.getCombinator()) + .rules(entitiesToDto(filterEntity.getRules())) + .build(); + } + case BOOLEAN -> { + return BooleanExpertRule.builder() + .field(filterEntity.getField()) + .operator(filterEntity.getOperator()) + .value(Boolean.parseBoolean(filterEntity.getValue())) + .build(); + } + case NUMBER -> { + return NumberExpertRule.builder() + .field(filterEntity.getField()) + .operator(filterEntity.getOperator()) + .value(Double.valueOf(filterEntity.getValue())) + .build(); + } + case STRING -> { + return StringExpertRule.builder() + .field(filterEntity.getField()) + .operator(filterEntity.getOperator()) + .value(filterEntity.getValue()) + .build(); + } + case ENUM -> { + return EnumExpertRule.builder() + .field(filterEntity.getField()) + .operator(filterEntity.getOperator()) + .value(filterEntity.getValue()) + .build(); + } + default -> throw new PowsyblException("Unknown rule data type: " + filterEntity.getDataType() + ", supported data types are: COMBINATOR, BOOLEAN, NUMBER, STRING, ENUM"); + } + } + + private static List entitiesToDto(List entities) { + if (entities == null) { + return Collections.emptyList(); + } + + return entities.stream() + .map(ExpertFilterRepositoryProxy::entityToDto) + .collect(Collectors.toList()); + } + + @Override + ExpertFilterEntity fromDto(AbstractFilter dto) { + if (dto instanceof ExpertFilter filter) { + var expertFilterEntityBuilder = ExpertFilterEntity.builder() + .modificationDate(filter.getModificationDate()) + .equipmentType(filter.getEquipmentType()) + .rules(dtoToEntity(filter.getRules())); + buildAbstractFilter(expertFilterEntityBuilder, filter); + return expertFilterEntityBuilder.build(); + } + throw new PowsyblException(WRONG_FILTER_TYPE); + } + + public static ExpertRuleEntity dtoToEntity(AbstractExpertRule filter) { + var expertFilterEntityBuilder = ExpertRuleEntity.builder() + .id(UUID.randomUUID()) + .combinator(filter.getCombinator()) + .operator(filter.getOperator()) + .dataType(filter.getDataType()) + .field(filter.getField()) + .value(filter.getStringValue()); + expertFilterEntityBuilder.rules(dtoToEntities(filter.getRules(), expertFilterEntityBuilder.build())); + return expertFilterEntityBuilder.build(); + } + + private static List dtoToEntities(List ruleFromDto, ExpertRuleEntity parentRuleEntity) { + if (ruleFromDto == null) { + return Collections.emptyList(); + } + + return ruleFromDto.stream() + .map(rule -> { + var expertRuleEntityBuilder = ExpertRuleEntity.builder() + .id(UUID.randomUUID()) + .combinator(rule.getCombinator()) + .operator(rule.getOperator()) + .dataType(rule.getDataType()) + .field(rule.getField()) + .value(rule.getStringValue()) + .parentRule(parentRuleEntity); + + List rules = dtoToEntities(rule.getRules(), expertRuleEntityBuilder.build()); + expertRuleEntityBuilder.rules(rules); + + return expertRuleEntityBuilder.build(); + }) + .collect(Collectors.toList()); + } + + @Override + FilterType getFilterType() { + return FilterType.EXPERT; + } + + @Override + public EquipmentType getEquipmentType() { + throw new UnsupportedOperationException("A filter id must be provided to get equipment type !!"); + } + + @Override + public EquipmentType getEquipmentType(UUID id) { + return expertFilterRepository.findById(id) + .map(ExpertFilterEntity::getEquipmentType) + .orElseThrow(() -> new PowsyblException("Identifier list filter " + id + " not found")); + } + + @Override + public AbstractEquipmentFilterForm buildEquipmentFormFilter(AbstractFilterEntity entity) { + return null; + } +} diff --git a/src/main/java/org/gridsuite/filter/server/dto/ExpertFilter.java b/src/main/java/org/gridsuite/filter/server/dto/ExpertFilter.java new file mode 100644 index 00000000..1867e4d2 --- /dev/null +++ b/src/main/java/org/gridsuite/filter/server/dto/ExpertFilter.java @@ -0,0 +1,43 @@ +/** + * 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.filter.server.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; +import org.gridsuite.filter.server.dto.expertrule.AbstractExpertRule; +import org.gridsuite.filter.server.utils.EquipmentType; +import org.gridsuite.filter.server.utils.FilterType; + +import java.util.Date; +import java.util.UUID; + +/** + * @author Antoine Bouhours + */ +@Getter +@Schema(description = "Expert Filters", allOf = AbstractFilter.class) +@SuperBuilder +@NoArgsConstructor +public class ExpertFilter extends AbstractFilter { + + @Schema(description = "Rules") + private AbstractExpertRule rules; + + public ExpertFilter(UUID id, Date modificationDate, EquipmentType equipmentType, AbstractExpertRule rules) { + super(id, modificationDate, equipmentType); + this.rules = rules; + } + + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + @Override + public FilterType getType() { + return FilterType.EXPERT; + } +} diff --git a/src/main/java/org/gridsuite/filter/server/dto/expertrule/AbstractExpertRule.java b/src/main/java/org/gridsuite/filter/server/dto/expertrule/AbstractExpertRule.java new file mode 100644 index 00000000..09b17987 --- /dev/null +++ b/src/main/java/org/gridsuite/filter/server/dto/expertrule/AbstractExpertRule.java @@ -0,0 +1,65 @@ +/** + * 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.filter.server.dto.expertrule; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +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.CombinatorType; +import org.gridsuite.filter.server.utils.DataType; +import org.gridsuite.filter.server.utils.FieldType; +import org.gridsuite.filter.server.utils.OperatorType; + +import java.util.List; + +/** + * @author Antoine Bouhours + */ +@JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + property = "dataType", + include = JsonTypeInfo.As.EXISTING_PROPERTY) +@JsonSubTypes({ + @JsonSubTypes.Type(value = StringExpertRule.class, name = "STRING"), + @JsonSubTypes.Type(value = BooleanExpertRule.class, name = "BOOLEAN"), + @JsonSubTypes.Type(value = EnumExpertRule.class, name = "ENUM"), + @JsonSubTypes.Type(value = NumberExpertRule.class, name = "NUMBER"), + @JsonSubTypes.Type(value = CombinatorExpertRule.class, name = "COMBINATOR") +}) +@JsonInclude(JsonInclude.Include.NON_NULL) +@NoArgsConstructor +@AllArgsConstructor +@Getter +@SuperBuilder +public abstract class AbstractExpertRule { + + @Schema(description = "Combinator") + private CombinatorType combinator; + + @Schema(description = "Field") + private FieldType field; + + @Schema(description = "Operator") + private OperatorType operator; + + @Schema(description = "Rules") + private List rules; + + public abstract boolean evaluateRule(Identifiable identifiable); + + public abstract DataType getDataType(); + + @JsonIgnore + public abstract String getStringValue(); +} diff --git a/src/main/java/org/gridsuite/filter/server/dto/expertrule/BooleanExpertRule.java b/src/main/java/org/gridsuite/filter/server/dto/expertrule/BooleanExpertRule.java new file mode 100644 index 00000000..4f33e4ef --- /dev/null +++ b/src/main/java/org/gridsuite/filter/server/dto/expertrule/BooleanExpertRule.java @@ -0,0 +1,58 @@ +/** + * 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.filter.server.dto.expertrule; + +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.DataType; + +import static org.gridsuite.filter.server.utils.ExpertFilterUtils.getFieldValue; + +/** + * @author Antoine Bouhours + */ +@AllArgsConstructor +@NoArgsConstructor +@Getter +@SuperBuilder +public class BooleanExpertRule extends AbstractExpertRule { + + @Schema(description = "Value") + private boolean value; + + private static boolean getBooleanValue(String value) { + return Boolean.parseBoolean(value); + } + + @Override + public String getStringValue() { + return String.valueOf(isValue()); + } + + @Override + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public DataType getDataType() { + return DataType.BOOLEAN; + } + + @Override + public boolean evaluateRule(Identifiable identifiable) { + boolean identifiableValue = getBooleanValue(getFieldValue(this.getField(), identifiable)); + boolean filterValue = this.isValue(); + return switch (this.getOperator()) { + case EQUALS -> identifiableValue == filterValue; + case NOT_EQUALS -> identifiableValue != filterValue; + default -> throw new PowsyblException(this.getOperator() + " operator not supported with " + this.getDataType() + " rule data type"); + }; + } +} diff --git a/src/main/java/org/gridsuite/filter/server/dto/expertrule/CombinatorExpertRule.java b/src/main/java/org/gridsuite/filter/server/dto/expertrule/CombinatorExpertRule.java new file mode 100644 index 00000000..28455b61 --- /dev/null +++ b/src/main/java/org/gridsuite/filter/server/dto/expertrule/CombinatorExpertRule.java @@ -0,0 +1,62 @@ +/** + * 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.filter.server.dto.expertrule; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.powsybl.commons.PowsyblException; +import com.powsybl.iidm.network.Identifiable; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.experimental.SuperBuilder; +import org.gridsuite.filter.server.utils.CombinatorType; +import org.gridsuite.filter.server.utils.DataType; + +/** + * @author Antoine Bouhours + */ +@AllArgsConstructor +@Getter +@SuperBuilder +public class CombinatorExpertRule extends AbstractExpertRule { + + @Override + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public DataType getDataType() { + return DataType.COMBINATOR; + } + + @Override + public boolean evaluateRule(Identifiable identifiable) { + // As long as there are rules, we go down the tree + if (CombinatorType.AND == this.getCombinator()) { + for (AbstractExpertRule rule : this.getRules()) { + // Recursively evaluate the rule + if (!rule.evaluateRule(identifiable)) { + // If any rule is false, the whole combination is false + return false; + } + } + return true; + } else if (CombinatorType.OR == this.getCombinator()) { + for (AbstractExpertRule rule : this.getRules()) { + // Recursively evaluate the rule + if (rule.evaluateRule(identifiable)) { + // If any rule is true, the whole combination is true + return true; + } + } + return false; + } else { + throw new PowsyblException(this.getCombinator() + " combinator type is not implemented with expert filter"); + } + } + + @Override + public String getStringValue() { + return null; + } +} diff --git a/src/main/java/org/gridsuite/filter/server/dto/expertrule/EnumExpertRule.java b/src/main/java/org/gridsuite/filter/server/dto/expertrule/EnumExpertRule.java new file mode 100644 index 00000000..932933f8 --- /dev/null +++ b/src/main/java/org/gridsuite/filter/server/dto/expertrule/EnumExpertRule.java @@ -0,0 +1,53 @@ +/** + * 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.filter.server.dto.expertrule; + +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.DataType; + +import static org.gridsuite.filter.server.utils.ExpertFilterUtils.getFieldValue; + +/** + * @author Antoine Bouhours + */ +@AllArgsConstructor +@NoArgsConstructor +@Getter +@SuperBuilder +public class EnumExpertRule extends AbstractExpertRule { + + @Schema(description = "Value") + private String value; + + @Override + public String getStringValue() { + return getValue(); + } + + @Override + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public DataType getDataType() { + return DataType.ENUM; + } + + @Override + public boolean evaluateRule(Identifiable identifiable) { + String identifiableValue = getFieldValue(this.getField(), identifiable); + return switch (this.getOperator()) { + case EQUALS -> identifiableValue.equals(this.getValue()); + case NOT_EQUALS -> !identifiableValue.equals(this.getValue()); + default -> throw new PowsyblException(this.getOperator() + " operator not supported with " + this.getDataType() + " rule data type"); + }; + } +} diff --git a/src/main/java/org/gridsuite/filter/server/dto/expertrule/NumberExpertRule.java b/src/main/java/org/gridsuite/filter/server/dto/expertrule/NumberExpertRule.java new file mode 100644 index 00000000..aeef1a84 --- /dev/null +++ b/src/main/java/org/gridsuite/filter/server/dto/expertrule/NumberExpertRule.java @@ -0,0 +1,61 @@ +/** + * 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.filter.server.dto.expertrule; + +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.DataType; + +import static org.gridsuite.filter.server.utils.ExpertFilterUtils.getFieldValue; + +/** + * @author Antoine Bouhours + */ +@AllArgsConstructor +@NoArgsConstructor +@Getter +@SuperBuilder +public class NumberExpertRule extends AbstractExpertRule { + + @Schema(description = "Value") + private Double value; + + public static Double getNumberValue(String value) { + return Double.parseDouble(value); + } + + @Override + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public DataType getDataType() { + return DataType.NUMBER; + } + + @Override + public boolean evaluateRule(Identifiable identifiable) { + Double identifiableValue = getNumberValue(getFieldValue(this.getField(), identifiable)); + Double filterValue = this.getValue(); + return switch (this.getOperator()) { + case EQUALS -> identifiableValue.equals(filterValue); + case GREATER_OR_EQUALS -> identifiableValue.compareTo(filterValue) >= 0; + case GREATER -> identifiableValue.compareTo(filterValue) > 0; + case LOWER_OR_EQUALS -> identifiableValue.compareTo(filterValue) <= 0; + case LOWER -> identifiableValue.compareTo(filterValue) < 0; + default -> throw new PowsyblException(this.getOperator() + " operator not supported with " + this.getDataType() + " rule data type"); + }; + } + + @Override + public String getStringValue() { + return String.valueOf(this.getValue()); + } +} diff --git a/src/main/java/org/gridsuite/filter/server/dto/expertrule/StringExpertRule.java b/src/main/java/org/gridsuite/filter/server/dto/expertrule/StringExpertRule.java new file mode 100644 index 00000000..03330166 --- /dev/null +++ b/src/main/java/org/gridsuite/filter/server/dto/expertrule/StringExpertRule.java @@ -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.filter.server.dto.expertrule; + +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.apache.commons.lang3.StringUtils; +import org.gridsuite.filter.server.utils.DataType; + +import static org.gridsuite.filter.server.utils.ExpertFilterUtils.getFieldValue; + +/** + * @author Antoine Bouhours + */ +@AllArgsConstructor +@NoArgsConstructor +@Getter +@SuperBuilder +public class StringExpertRule extends AbstractExpertRule { + + @Schema(description = "Value") + private String value; + + @Override + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public DataType getDataType() { + return DataType.STRING; + } + + @Override + public boolean evaluateRule(Identifiable identifiable) { + String identifiableValue = getFieldValue(this.getField(), identifiable); + return switch (this.getOperator()) { + case IS -> identifiableValue.equalsIgnoreCase(this.getValue()); + case CONTAINS -> StringUtils.containsIgnoreCase(identifiableValue, this.getValue()); + case BEGINS_WITH -> StringUtils.startsWithIgnoreCase(identifiableValue, this.getValue()); + case ENDS_WITH -> StringUtils.endsWithIgnoreCase(identifiableValue, this.getValue()); + default -> throw new PowsyblException(this.getOperator() + " operator not supported with " + this.getDataType() + " rule data type"); + }; + } + + @Override + public String getStringValue() { + return getValue(); + } +} diff --git a/src/main/java/org/gridsuite/filter/server/entities/ExpertFilterEntity.java b/src/main/java/org/gridsuite/filter/server/entities/ExpertFilterEntity.java new file mode 100644 index 00000000..d595e08e --- /dev/null +++ b/src/main/java/org/gridsuite/filter/server/entities/ExpertFilterEntity.java @@ -0,0 +1,39 @@ +/* + * 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.filter.server.entities; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; +import org.gridsuite.filter.server.utils.EquipmentType; + +/** + * @author Antoine Bouhours + */ +@Getter +@NoArgsConstructor +@AllArgsConstructor +@SuperBuilder +@Entity +@Table(name = "expert_filter") +public class ExpertFilterEntity extends AbstractFilterEntity { + + @Enumerated(EnumType.STRING) + @Column(name = "equipmentType") + private EquipmentType equipmentType; + + @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true) + @JoinColumn(name = "rules_id", + referencedColumnName = "id", + foreignKey = @ForeignKey( + name = "expertRule_rules_fk" + )) + private ExpertRuleEntity rules; +} diff --git a/src/main/java/org/gridsuite/filter/server/entities/ExpertRuleEntity.java b/src/main/java/org/gridsuite/filter/server/entities/ExpertRuleEntity.java new file mode 100644 index 00000000..8d270ad3 --- /dev/null +++ b/src/main/java/org/gridsuite/filter/server/entities/ExpertRuleEntity.java @@ -0,0 +1,65 @@ +/* + * 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.filter.server.entities; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; +import org.gridsuite.filter.server.utils.CombinatorType; +import org.gridsuite.filter.server.utils.DataType; +import org.gridsuite.filter.server.utils.FieldType; +import org.gridsuite.filter.server.utils.OperatorType; + +import java.util.List; +import java.util.UUID; + +/** + * @author Antoine Bouhours + */ +@Getter +@NoArgsConstructor +@SuperBuilder +@AllArgsConstructor +@Entity +@Table(name = "expert_rule") +public class ExpertRuleEntity { + @Id + @Column(name = "id") + private UUID id; + + @Enumerated(EnumType.STRING) + @Column(name = "combinator") + private CombinatorType combinator; + + @Enumerated(EnumType.STRING) + @Column(name = "field") + private FieldType field; + + @Column(name = "value_") // "value" is not supported in UT with H2 + private String value; + + @Enumerated(EnumType.STRING) + @Column(name = "operator") + private OperatorType operator; + + @Enumerated(EnumType.STRING) + @Column(name = "dataType") + private DataType dataType; + + @OneToMany(cascade = CascadeType.ALL, mappedBy = "parentRule") + private List rules; + + @ManyToOne + @JoinColumn(name = "parentRule_id", referencedColumnName = "id", + foreignKey = @ForeignKey( + name = "expertRule_parentRule_fk" + ), nullable = true) + private ExpertRuleEntity parentRule; +} diff --git a/src/main/java/org/gridsuite/filter/server/repositories/ExpertFilterRepository.java b/src/main/java/org/gridsuite/filter/server/repositories/ExpertFilterRepository.java new file mode 100644 index 00000000..1cb0eae9 --- /dev/null +++ b/src/main/java/org/gridsuite/filter/server/repositories/ExpertFilterRepository.java @@ -0,0 +1,18 @@ +/** + * 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.filter.server.repositories; + +import org.gridsuite.filter.server.entities.ExpertFilterEntity; +import org.springframework.stereotype.Repository; + + +/** + * @author Antoine Bouhours + */ +@Repository +public interface ExpertFilterRepository extends FilterRepository { +} diff --git a/src/main/java/org/gridsuite/filter/server/utils/CombinatorType.java b/src/main/java/org/gridsuite/filter/server/utils/CombinatorType.java new file mode 100644 index 00000000..21f4ef31 --- /dev/null +++ b/src/main/java/org/gridsuite/filter/server/utils/CombinatorType.java @@ -0,0 +1,15 @@ +/** + * 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.filter.server.utils; + +/** + * @author Antoine Bouhours + */ +public enum CombinatorType { + AND, + OR, +} diff --git a/src/main/java/org/gridsuite/filter/server/utils/DataType.java b/src/main/java/org/gridsuite/filter/server/utils/DataType.java new file mode 100644 index 00000000..ca0e7e81 --- /dev/null +++ b/src/main/java/org/gridsuite/filter/server/utils/DataType.java @@ -0,0 +1,18 @@ +/** + * 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.filter.server.utils; + +/** + * @author Antoine Bouhours + */ +public enum DataType { + STRING, + NUMBER, + ENUM, + BOOLEAN, + COMBINATOR, +} diff --git a/src/main/java/org/gridsuite/filter/server/utils/ExpertFilterUtils.java b/src/main/java/org/gridsuite/filter/server/utils/ExpertFilterUtils.java new file mode 100644 index 00000000..bbd5b5d6 --- /dev/null +++ b/src/main/java/org/gridsuite/filter/server/utils/ExpertFilterUtils.java @@ -0,0 +1,53 @@ +/** + * 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.filter.server.utils; + +import com.powsybl.commons.PowsyblException; +import com.powsybl.iidm.network.*; + +import java.util.Optional; + +/** + * @author Antoine Bouhours + */ +public final class ExpertFilterUtils { + private ExpertFilterUtils() { } + + public static > String getFieldValue(FieldType field, Identifiable identifiable) { + return switch (identifiable.getType()) { + case GENERATOR -> getGeneratorFieldValue(field, (Generator) identifiable); + case LOAD -> getLoadFieldValue(field, (Load) identifiable); + default -> throw new PowsyblException(identifiable.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"); + }; + } + + 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 = 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()); + case TARGET_V -> String.valueOf(generator.getTargetV()); + case TARGET_P -> String.valueOf(generator.getTargetP()); + case TARGET_Q -> String.valueOf(generator.getTargetQ()); + case VOLTAGE_REGULATOR_ON -> String.valueOf(generator.isVoltageRegulatorOn()); + }; + } +} diff --git a/src/main/java/org/gridsuite/filter/server/utils/FieldType.java b/src/main/java/org/gridsuite/filter/server/utils/FieldType.java new file mode 100644 index 00000000..ce44d1fc --- /dev/null +++ b/src/main/java/org/gridsuite/filter/server/utils/FieldType.java @@ -0,0 +1,24 @@ +/** + * 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.filter.server.utils; + +/** + * @author Antoine Bouhours + */ +public enum FieldType { + ID, + NAME, + NOMINAL_VOLTAGE, + MIN_P, + MAX_P, + TARGET_P, + TARGET_V, + TARGET_Q, + ENERGY_SOURCE, + COUNTRY, + VOLTAGE_REGULATOR_ON +} diff --git a/src/main/java/org/gridsuite/filter/server/utils/OperatorType.java b/src/main/java/org/gridsuite/filter/server/utils/OperatorType.java new file mode 100644 index 00000000..2eeabde7 --- /dev/null +++ b/src/main/java/org/gridsuite/filter/server/utils/OperatorType.java @@ -0,0 +1,26 @@ +/** + * 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.filter.server.utils; + +/** + * @author Antoine Bouhours + */ +public enum OperatorType { + // Common + EQUALS, + NOT_EQUALS, + // Number + LOWER, + LOWER_OR_EQUALS, + GREATER, + GREATER_OR_EQUALS, + // String + IS, + CONTAINS, + BEGINS_WITH, + ENDS_WITH, +} diff --git a/src/test/java/org/gridsuite/filter/server/FilterEntityControllerTest.java b/src/test/java/org/gridsuite/filter/server/FilterEntityControllerTest.java index a45a79af..4a7031ad 100644 --- a/src/test/java/org/gridsuite/filter/server/FilterEntityControllerTest.java +++ b/src/test/java/org/gridsuite/filter/server/FilterEntityControllerTest.java @@ -38,7 +38,6 @@ import org.gridsuite.filter.server.utils.*; import org.gridsuite.filter.server.utils.expertfilter.CombinatorType; import org.gridsuite.filter.server.utils.expertfilter.FieldType; -import org.gridsuite.filter.server.utils.expertfilter.OperatorType; import org.junit.After; import org.junit.Before; import org.junit.Test;