Commit 6b0862e3 authored by calocedre TAC's avatar calocedre TAC
Browse files

new scoring selection implementation

add a test showing a pb in scoring selection
make test selection a unit tests
some renaming
parent d8cf5811
package fr.gouv.clea.clea.scoring.configuration;
import lombok.extern.slf4j.Slf4j;
import java.util.List;
import java.util.stream.Collectors;
import java.util.Optional;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public abstract class ScoringConfiguration {
......@@ -12,77 +12,16 @@ public abstract class ScoringConfiguration {
public ScoringRule getConfigurationFor(int venueType, int venueCategory1, int venueCategory2) {
log.debug("Fetching rules candidates for venueType : {}, venueCategory1 : {}, venuCategory2: {}", venueType, venueCategory1, venueCategory2);
List<ScoringRule> compatibleRules = this.getScorings().stream()
Optional<? extends ScoringRule> matchingRule = this.getScorings().stream()
.filter(scoring -> scoring.isCompatibleWith(venueType, venueCategory1, venueCategory2))
.collect(Collectors.toList());
log.debug("Found {} compatibles rules", compatibleRules.size());
//Only one match found
if (compatibleRules.size() == 1) {
log.debug("Found suitable rule {}", compatibleRules.get(0));
return compatibleRules.get(0);
}
//All are wildcarded, then rule to apply is the default one
if (allAreWilcarded(venueType, venueCategory1, venueCategory2)) {
getConfigurationFor(
ScoringRule.wildcardValue,
ScoringRule.wildcardValue,
ScoringRule.wildcardValue);
}
ScoringRule bestRuleCandidate = compatibleRules.get(0);
for (ScoringRule rule : compatibleRules) {
if (rule.isFullMatch()) {
log.debug("Found full matching rule {}", rule);
return rule;
} else if (bothCategoryAreWildcard(bestRuleCandidate) && firstCategoryIsNotWildcarded(rule)) {
bestRuleCandidate = rule;
} else if (eitherOneCategoryIsWildcard(bestRuleCandidate) && firstCategoryIsNotWildcarded(rule)) {
bestRuleCandidate = rule;
} else if (firstCategoryIsNotWildcarded(bestRuleCandidate) && secondCategoryIsNotWildcarded(bestRuleCandidate)) {
bestRuleCandidate = rule;
}
}
log.info("Retrieving best rule {} for venueType : {}, venueCategory1 : {}, venuCategory2: {}", bestRuleCandidate, venueType, venueCategory1, venueCategory2);
return bestRuleCandidate;
}
private boolean allAreWilcarded(int venueType, int venueCategory1, int venueCategory2) {
int count = 0;
if (venueType == ScoringRule.wildcardValue) {
count++;
.max(new ScoringRuleComparator());
if (matchingRule.isPresent()) {
log.debug("Found matching rulefor venueType : {}, venueCategory1 : {}, venuCategory2: {}", matchingRule.get(), venueType, venueCategory1, venueCategory2);
return matchingRule.get();
} else {
log.error("No scoring matching found for venueType : {}, venueCategory1 : {}, venuCategory2: {}", venueType, venueCategory1, venueCategory2);
return null;
}
if (venueCategory1 == ScoringRule.wildcardValue) {
count++;
}
if (venueCategory2 == ScoringRule.wildcardValue) {
count++;
}
return count == 3;
}
private boolean eitherOneCategoryIsWildcard(ScoringRule rule) {
return isWildcarded(rule.getVenueCategory1()) || isWildcarded(rule.getVenueCategory2());
}
private boolean bothCategoryAreWildcard(ScoringRule rule) {
return isWildcarded(rule.getVenueCategory1()) && isWildcarded(rule.getVenueCategory2());
}
private boolean isWildcarded(int venueItem) {
return venueItem == ScoringRule.wildcardValue;
}
private boolean firstCategoryIsNotWildcarded(ScoringRule rule) {
return isWildcarded(rule.getVenueCategory1());
}
private boolean secondCategoryIsNotWildcarded(ScoringRule rule) {
return isWildcarded(rule.getVenueCategory2());
}
}
package fr.gouv.clea.clea.scoring.configuration;
import javax.validation.Valid;
import javax.validation.constraints.Min;
import fr.gouv.clea.clea.scoring.configuration.validators.NoDuplicates;
import fr.gouv.clea.clea.scoring.configuration.validators.ValidateWildcards;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
import lombok.experimental.SuperBuilder;
import javax.validation.Valid;
import javax.validation.constraints.Min;
import java.util.Objects;
// @Valid
@SuperBuilder
@AllArgsConstructor
@EqualsAndHashCode
@ToString
@Getter
@Valid
......@@ -21,7 +21,7 @@ import java.util.Objects;
@NoDuplicates
public class ScoringRule {
public static int wildcardValue = -1;
public final static int WILDCARD_VALUE = -1;
@Min(value = -1)
private int venueType;
......@@ -39,26 +39,26 @@ public class ScoringRule {
}
public boolean hasVenueTypeCompatibleWith(int venueType) {
return this.getVenueType() == venueType || this.getVenueType() == ScoringRule.wildcardValue;
return this.getVenueType() == venueType || this.getVenueType() == ScoringRule.WILDCARD_VALUE;
}
public boolean hasVenueCategory1CompatibleWith(int venueCategory1) {
return this.getVenueCategory1() == venueCategory1 || this.getVenueCategory1() == ScoringRule.wildcardValue;
return this.getVenueCategory1() == venueCategory1 || this.getVenueCategory1() == ScoringRule.WILDCARD_VALUE;
}
public boolean hasVenueCategory2CompatibleWith(int venueCategory2) {
return this.getVenueCategory2() == venueCategory2 || this.getVenueCategory2() == ScoringRule.wildcardValue;
return this.getVenueCategory2() == venueCategory2 || this.getVenueCategory2() == ScoringRule.WILDCARD_VALUE;
}
public int getWildCardCount() {
int count = 0;
if (this.venueType == ScoringRule.wildcardValue) {
if (this.venueType == ScoringRule.WILDCARD_VALUE) {
count++;
}
if (this.venueCategory1 == ScoringRule.wildcardValue) {
if (this.venueCategory1 == ScoringRule.WILDCARD_VALUE) {
count++;
}
if (this.venueCategory2 == ScoringRule.wildcardValue) {
if (this.venueCategory2 == ScoringRule.WILDCARD_VALUE) {
count++;
}
return count;
......@@ -67,17 +67,17 @@ public class ScoringRule {
public boolean isFullMatch() {
return this.getWildCardCount() == 0;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ScoringRule that = (ScoringRule) o;
return getVenueType() == that.getVenueType() && getVenueCategory1() == that.getVenueCategory1() && getVenueCategory2() == that.getVenueCategory2();
public boolean isDefaultRule() {
return this.getWildCardCount() == 3;
}
public boolean isCategory1Wildcarded() {
return this.getVenueCategory1() == ScoringRule.WILDCARD_VALUE;
}
@Override
public int hashCode() {
return Objects.hash(getVenueType(), getVenueCategory1(), getVenueCategory2());
public boolean isCategory2Wildcarded() {
return this.getVenueCategory2() == ScoringRule.WILDCARD_VALUE;
}
}
package fr.gouv.clea.clea.scoring.configuration;
import java.util.Comparator;
/**
* I compare rules MATCHING a given tuple (venue type, venue categrory1, venue category 2).
* I cannot be used to compare rules not mathcing the same tuple.
*/
public class ScoringRuleComparator implements Comparator<ScoringRule> {
@Override
public int compare(ScoringRule rule1, ScoringRule rule2) {
if (rule1.isDefaultRule()) {
return -1;
}
if (rule1.isFullMatch()) {
return 1;
}
// No check on venue type : Wildcard on venue type is not allowed except for the default rule
if (rule1.isCategory1Wildcarded() && !rule2.isCategory1Wildcarded()) {
return -1;
}
if (!rule1.isCategory1Wildcarded() && rule2.isCategory1Wildcarded()) {
return 1;
}
if (rule1.isCategory2Wildcarded() && !rule2.isCategory2Wildcarded()) {
return -1;
}
if (!rule1.isCategory2Wildcarded() && rule2.isCategory2Wildcarded()) {
return 1;
}
return 0;
}
}
......@@ -25,7 +25,7 @@ public class ExposureTimeConfigurationConverter implements Converter<String, Exp
}
public int stringToInt(String s) {
return s.equals(WILDCARD) ? ExposureTimeRule.wildcardValue : Integer.parseInt(s);
return s.equals(WILDCARD) ? ExposureTimeRule.WILDCARD_VALUE : Integer.parseInt(s);
}
}
......@@ -25,7 +25,7 @@ public class RiskConfigurationConverter implements Converter<String, RiskRule>{
}
public int stringToInt(String s) {
return s.equals(WILDCARD) ? RiskRule.wildcardValue : Integer.parseInt(s);
return s.equals(WILDCARD) ? RiskRule.WILDCARD_VALUE : Integer.parseInt(s);
}
}
......@@ -19,15 +19,15 @@ public class VenueTypeValidator implements ConstraintValidator<ValidateWildcards
}
private boolean venueTypeIsNotWildcarded(ScoringRule scoringRule) {
return scoringRule.getVenueType() != ScoringRule.wildcardValue ||
(scoringRule.getVenueCategory1() != ScoringRule.wildcardValue &&
scoringRule.getVenueCategory2() != ScoringRule.wildcardValue);
return scoringRule.getVenueType() != ScoringRule.WILDCARD_VALUE ||
(scoringRule.getVenueCategory1() != ScoringRule.WILDCARD_VALUE &&
scoringRule.getVenueCategory2() != ScoringRule.WILDCARD_VALUE);
}
private boolean allFieldsAreWildcards(ScoringRule scoringRule) {
return scoringRule.getVenueType() == ScoringRule.wildcardValue &&
scoringRule.getVenueCategory1() == ScoringRule.wildcardValue &&
scoringRule.getVenueCategory2() == ScoringRule.wildcardValue;
return scoringRule.getVenueType() == ScoringRule.WILDCARD_VALUE &&
scoringRule.getVenueCategory1() == ScoringRule.WILDCARD_VALUE &&
scoringRule.getVenueCategory2() == ScoringRule.WILDCARD_VALUE;
}
......
......@@ -34,8 +34,8 @@ class ExposureTimeConfigurationTest {
ExposureTimeRule scoring = (ExposureTimeRule) configuration.getScorings().get(2);
assertThat(scoring.getVenueType()).isEqualTo(3);
assertThat(scoring.getVenueCategory1()).isEqualTo(ScoringRule.wildcardValue);
assertThat(scoring.getVenueCategory2()).isEqualTo(ScoringRule.wildcardValue);
assertThat(scoring.getVenueCategory1()).isEqualTo(ScoringRule.WILDCARD_VALUE);
assertThat(scoring.getVenueCategory2()).isEqualTo(ScoringRule.WILDCARD_VALUE);
assertThat(scoring.getExposureTimeBackward()).isEqualTo(1);
assertThat(scoring.getExposureTimeForward()).isEqualTo(11);
assertThat(scoring.getExposureTimeStaffBackward()).isEqualTo(21);
......
......@@ -34,8 +34,8 @@ public class RiskConfigurationTest {
RiskRule scoring = (RiskRule) configuration.getScorings().get(3);
assertThat(scoring.getVenueType()).isEqualTo(3);
assertThat(scoring.getVenueCategory1()).isEqualTo(ScoringRule.wildcardValue);
assertThat(scoring.getVenueCategory2()).isEqualTo(ScoringRule.wildcardValue);
assertThat(scoring.getVenueCategory1()).isEqualTo(ScoringRule.WILDCARD_VALUE);
assertThat(scoring.getVenueCategory2()).isEqualTo(ScoringRule.WILDCARD_VALUE);
assertThat(scoring.getClusterThresholdBackward()).isEqualTo(3);
assertThat(scoring.getClusterThresholdForward()).isEqualTo(1);
assertThat(scoring.getRiskLevelBackward()).isEqualTo(3.0f);
......
package fr.gouv.clea.clea.scoring.configuration;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.jupiter.api.DisplayNameGeneration;
import org.junit.jupiter.api.DisplayNameGenerator;
import org.junit.jupiter.api.Test;
import fr.gouv.clea.clea.scoring.configuration.risk.RiskConfigurationConverter;
import fr.gouv.clea.clea.scoring.configuration.risk.RiskRule;
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
public class ScoringRuleComparatorTest {
final static RiskConfigurationConverter converter = new RiskConfigurationConverter();
@Test
void should_return_lower_priority_rules_first() {
final List<String> rawRules = List.of(
"3,*,*,3,1,3.0,2.0",
"3,1,*,3,1,3.0,2.0",
"*,*,*,3,1,3.0,2.0",
"3,*,2,3,1,3.0,2.0",
"3,1,2,3,1,3.0,2.0"
);
final List<RiskRule> rules = rawRules.stream().map(converter::convert).collect(Collectors.toList());
Collections.sort(rules, new ScoringRuleComparator());
List<String> expectedSortedRules = List.of(
"*,*,*,3,1,3.0,2.0",
"3,*,*,3,1,3.0,2.0",
"3,*,2,3,1,3.0,2.0",
"3,1,*,3,1,3.0,2.0",
"3,1,2,3,1,3.0,2.0"
);
assertThat(rules).containsExactlyElementsOf(expectedSortedRules.stream().map(converter::convert).collect(Collectors.toList()));
}
}
......@@ -2,27 +2,41 @@ package fr.gouv.clea.clea.scoring.configuration;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayNameGeneration;
import org.junit.jupiter.api.DisplayNameGenerator;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import fr.gouv.clea.clea.scoring.configuration.exposure.ExposureTimeConfiguration;
import fr.gouv.clea.clea.scoring.configuration.exposure.ExposureTimeConfigurationConverter;
import fr.gouv.clea.clea.scoring.configuration.risk.RiskConfiguration;
import fr.gouv.clea.clea.scoring.configuration.risk.RiskConfigurationConverter;
import fr.gouv.clea.clea.scoring.configuration.risk.RiskRule;
@SpringBootTest()
@ExtendWith(SpringExtension.class)
@EnableConfigurationProperties(value = {RiskConfiguration.class, ExposureTimeConfiguration.class})
@ContextConfiguration(classes = {RiskConfigurationConverter.class, ExposureTimeConfigurationConverter.class})
class ScoringRuleTest {
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
class ScoringRuleSelectionTest {
@Autowired
private RiskConfiguration riskConfiguration;
final static List<String> rawRules = List.of(
"*,*,*,3,1,3.0,2.0", // 0
"1,1,1,3,1,3.0,2.0", // 1
"1,2,3,3,1,3.0,2.0", // 2
"3,*,*,3,1,3.0,2.0", // 3
"3,1,*,3,1,3.0,2.0", // 4
"3,*,2,3,1,3.0,2.0", // 5
"3,*,3,3,1,3.0,2.0", // 6
"3,1,2,3,1,3.0,2.0" // 7
);
final static RiskConfigurationConverter converter = new RiskConfigurationConverter();
final static List<RiskRule> rules = rawRules.stream().map(converter::convert).collect(Collectors.toList());
@BeforeEach
void setUp() {
riskConfiguration = new RiskConfiguration();
riskConfiguration.setRules(rules);
}
@Test
void should_return_the_full_wildcard_rule() {
......@@ -50,7 +64,7 @@ class ScoringRuleTest {
@Test
void should_return_the_rule_three_one_wildcard() {
assertThat(riskConfiguration.getConfigurationFor(3, 1, 3))
assertThat(riskConfiguration.getConfigurationFor(3, 1, 5))
.isEqualTo(riskConfiguration.getScorings().get(4));
}
......@@ -63,7 +77,13 @@ class ScoringRuleTest {
@Test
void should_return_the_rule_three_one_two() {
assertThat(riskConfiguration.getConfigurationFor(3, 1, 2))
.isEqualTo(riskConfiguration.getScorings().get(6));
.isEqualTo(riskConfiguration.getScorings().get(7));
}
@Test
void should_return_the_rule_with_specific_cat1_when_rule_with_cat1_wildcard_and_rule_with_cat2_wildcard_matching() {
assertThat(riskConfiguration.getConfigurationFor(3, 1, 3))
.isEqualTo(riskConfiguration.getScorings().get(4));
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment