diff --git a/pom.xml b/pom.xml index 097eeb2..8762eb7 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ dev.struchkov autoresponder - 3.0.0 + 3.1.0 jar Abstract Autoresponder diff --git a/src/main/java/dev/struchkov/autoresponder/Responder.java b/src/main/java/dev/struchkov/autoresponder/Responder.java index 444c720..010ed29 100644 --- a/src/main/java/dev/struchkov/autoresponder/Responder.java +++ b/src/main/java/dev/struchkov/autoresponder/Responder.java @@ -1,6 +1,7 @@ package dev.struchkov.autoresponder; import dev.struchkov.autoresponder.compare.UnitPriorityComparator; +import dev.struchkov.autoresponder.entity.KeyWord; import dev.struchkov.autoresponder.entity.Unit; import dev.struchkov.autoresponder.util.Message; import org.slf4j.Logger; @@ -42,10 +43,10 @@ public final class Responder { * @return Юнит, который нуждается в обработке в соответствии с запросом пользователя */ public static > Optional nextUnit(String message, Collection nextUnits) { - isNotNull(nextUnits, message); + isNotNull(nextUnits); final Set searchUnit = new HashSet<>(); - if (nextUnits != null) { + if (message != null && nextUnits != null) { for (U unit : nextUnits) { final String unitPhrase = unit.getPhrase(); if ( @@ -60,16 +61,16 @@ public final class Responder { searchUnit.add(unit); } - if (percentageMatch(unit, splitWords(message)) >= unit.getMatchThreshold()) { + if (percentageMatch(unit, message) >= unit.getMatchThreshold()) { searchUnit.add(unit); } } + } - if (searchUnit.isEmpty()) { - for (U nextUnit : nextUnits) { - if ((nextUnit.getPattern() == null && (nextUnit.getKeyWords() == null || nextUnit.getKeyWords().isEmpty()))) { - searchUnit.add(nextUnit); - } + if (searchUnit.isEmpty()) { + for (U nextUnit : nextUnits) { + if (isNotPattern(nextUnit) && isNotKeyWords(nextUnit) && isNotPhrase(nextUnit)) { + searchUnit.add(nextUnit); } } } @@ -77,24 +78,51 @@ public final class Responder { return searchUnit.stream().max(UNIT_PRIORITY_COMPARATOR); } + private static > boolean isNotPhrase(U nextUnit) { + return nextUnit.getPhrase() == null; + } + + private static > boolean isNotPattern(U nextUnit) { + return nextUnit.getPattern() == null; + } + + private static > boolean isNotKeyWords(U nextUnit) { + return nextUnit.getKeyWords() == null || nextUnit.getKeyWords().isEmpty(); + } + private static boolean patternReg(Pattern pattern, String message) { isNotNull(pattern); return message.matches(pattern.pattern()); } - private static > Double percentageMatch(U unit, Set words) { - final Set keyWords = unit.getKeyWords(); - if (keyWords != null && !keyWords.isEmpty()) { - final Set temp = new HashSet<>(keyWords); - temp.retainAll(words); - log.trace(Message.UNIT_KEYWORDS, keyWords, keyWords.size()); - log.trace(Message.USER_MESSAGE_KEYWORDS, words); - log.trace(Message.INTERSECTION, temp, temp.size()); - log.trace(Message.CROSSING_PERCENTAGE, (double) temp.size() / (double) keyWords.size() * 100.0, unit.getMatchThreshold()); - return (double) temp.size() / (double) keyWords.size() * 100.0; + private static > double percentageMatch(U unit, String message) { + final Set unitKeyWords = unit.getKeyWords(); + if (unitKeyWords != null && !unitKeyWords.isEmpty()) { + final Set messageWords = splitWords(message); + final Set intersection = getIntersection(unitKeyWords, messageWords); + final double intersectionWeight = getIntersectionWeight(intersection); + log.debug(Message.UNIT_KEYWORDS, unitKeyWords, unitKeyWords.size()); + log.debug(Message.USER_MESSAGE_KEYWORDS, messageWords); + log.debug(Message.INTERSECTION, intersection, intersectionWeight); + log.debug(Message.CROSSING_PERCENTAGE, intersectionWeight / (double) unitKeyWords.size() * 100.0, unit.getMatchThreshold()); + return (double) intersection.size() / (double) unitKeyWords.size() * 100.0; } else { return 0.0; } } + private static double getIntersectionWeight(Set intersection) { + return intersection.stream().mapToInt(KeyWord::getImportant).sum(); + } + + private static Set getIntersection(Set unitKeyWords, Set messageWords) { + final Set intersection = new HashSet<>(); + for (KeyWord unitKeyWord : unitKeyWords) { + if (messageWords.contains(unitKeyWord.getWord())) { + intersection.add(unitKeyWord); + } + } + return intersection; + } + } diff --git a/src/main/java/dev/struchkov/autoresponder/entity/KeyWord.java b/src/main/java/dev/struchkov/autoresponder/entity/KeyWord.java new file mode 100644 index 0000000..458f1d4 --- /dev/null +++ b/src/main/java/dev/struchkov/autoresponder/entity/KeyWord.java @@ -0,0 +1,37 @@ +package dev.struchkov.autoresponder.entity; + +import dev.struchkov.autoresponder.exception.AutoresponderException; + +/** + * Ключевое слово для юнитов. + */ +public class KeyWord { + + private final Integer important; + private final String word; + + private KeyWord(Integer important, String word) { + if (important < 0 || important > 10) { + throw new AutoresponderException("Вес слова должен быть значением от 0 до 100"); + } + this.important = important; + this.word = word; + } + + public static KeyWord of(Integer weight, String word) { + return new KeyWord(weight, word); + } + + public static KeyWord of(String word) { + return new KeyWord(1, word); + } + + public Integer getImportant() { + return important; + } + + public String getWord() { + return word; + } + +} diff --git a/src/main/java/dev/struchkov/autoresponder/entity/Unit.java b/src/main/java/dev/struchkov/autoresponder/entity/Unit.java index 44f7dd5..9c7df84 100644 --- a/src/main/java/dev/struchkov/autoresponder/entity/Unit.java +++ b/src/main/java/dev/struchkov/autoresponder/entity/Unit.java @@ -15,7 +15,7 @@ public abstract class Unit> { /** * Ключевые слова. */ - protected Set keyWords = new HashSet<>(); + protected Set keyWords = new HashSet<>(); /** * Точная фраза. @@ -42,12 +42,14 @@ public abstract class Unit> { */ protected Set nextUnits = new HashSet<>(); - protected Unit(Set keyWords, - String phrase, - Pattern pattern, - Integer matchThreshold, - Integer priority, - Set nextUnits) { + protected Unit( + Set keyWords, + String phrase, + Pattern pattern, + Integer matchThreshold, + Integer priority, + Set nextUnits + ) { this.keyWords = keyWords; this.phrase = phrase; this.pattern = pattern; @@ -56,11 +58,11 @@ public abstract class Unit> { this.nextUnits = nextUnits; } - public Set getKeyWords() { + public Set getKeyWords() { return keyWords; } - public void setKeyWords(Set keyWords) { + public void setKeyWords(Set keyWords) { this.keyWords = keyWords; } diff --git a/src/main/java/dev/struchkov/autoresponder/exception/AutoresponderException.java b/src/main/java/dev/struchkov/autoresponder/exception/AutoresponderException.java new file mode 100644 index 0000000..7de3d97 --- /dev/null +++ b/src/main/java/dev/struchkov/autoresponder/exception/AutoresponderException.java @@ -0,0 +1,9 @@ +package dev.struchkov.autoresponder.exception; + +public class AutoresponderException extends RuntimeException { + + public AutoresponderException(String message) { + super(message); + } + +} diff --git a/src/main/java/dev/struchkov/autoresponder/util/Message.java b/src/main/java/dev/struchkov/autoresponder/util/Message.java index 35392a5..96a34e3 100644 --- a/src/main/java/dev/struchkov/autoresponder/util/Message.java +++ b/src/main/java/dev/struchkov/autoresponder/util/Message.java @@ -9,7 +9,7 @@ public final class Message { static final String UTILITY_CLASS = "Клас утилита"; public static final String UNIT_KEYWORDS = "Ключевые слова юнита: {} ({})"; - public static final String USER_MESSAGE_KEYWORDS = "Ключевые слова от пользователя: {}"; + public static final String USER_MESSAGE_KEYWORDS = "Слова, которые прислал пользователь: {}"; public static final String INTERSECTION = "Пересечение: {} ({})"; public static final String CROSSING_PERCENTAGE = "Процент: {} Необходимо: {}"; diff --git a/src/main/java/dev/struchkov/autoresponder/util/Parser.java b/src/main/java/dev/struchkov/autoresponder/util/Parser.java index 8209595..df966db 100644 --- a/src/main/java/dev/struchkov/autoresponder/util/Parser.java +++ b/src/main/java/dev/struchkov/autoresponder/util/Parser.java @@ -4,7 +4,7 @@ import java.util.Arrays; import java.util.Set; import java.util.stream.Collectors; -import static dev.struchkov.autoresponder.util.Message.UTILITY_CLASS; +import static dev.struchkov.haiti.utils.Exceptions.utilityClass; /** * Разбивает строку на множество слов, удаляя предлоги. @@ -18,10 +18,11 @@ public class Parser { */ private static final Set pretexts = Set.of( "в", "без", "до", "из", "к", "на", "по", "о", "от", "перед", "при", "с", "у", "за", "над", "об", - "под", "про", "для"); + "под", "про", "для" + ); private Parser() { - throw new IllegalStateException(UTILITY_CLASS); + utilityClass(); } /** diff --git a/src/test/java/dev/struchkov/autoresponder/test/TestUnit.java b/src/test/java/dev/struchkov/autoresponder/test/TestUnit.java index b190b59..9e78d6c 100644 --- a/src/test/java/dev/struchkov/autoresponder/test/TestUnit.java +++ b/src/test/java/dev/struchkov/autoresponder/test/TestUnit.java @@ -1,108 +1,108 @@ -package dev.struchkov.autoresponder.test; - -import dev.struchkov.autoresponder.entity.Unit; - -import java.util.HashSet; -import java.util.Set; -import java.util.regex.Pattern; - -public class TestUnit extends Unit { - - private String message; - - public TestUnit(Set keyWords, - String phrase, - Pattern pattern, - Integer matchThreshold, - Integer priority, - Set nextUnits, - String message) { - super(keyWords, phrase, pattern, matchThreshold, priority, nextUnits); - this.message = message; - } - - private TestUnit(Builder builder) { - super(builder.keyWords, builder.phrase, builder.pattern, builder.matchThreshold, builder.priority, builder.nextUnits); - message = builder.message; - } - - public static Builder builder() { - return new Builder(); - } - - public String getMessage() { - return message; - } - - @Override - public String toString() { - return "TestUnit{" + - "keyWords=" + keyWords + - ", phrase='" + phrase + '\'' + - ", pattern=" + pattern + - ", matchThreshold=" + matchThreshold + - ", priority=" + priority + - ", message='" + message + '\'' + - '}'; - } - - - public static final class Builder { - private Set keyWords = new HashSet<>(); - private String phrase; - private Pattern pattern; - private Integer matchThreshold; - private Integer priority; - private Set nextUnits = new HashSet<>(); - private String message; - - public Builder keyWords(Set val) { - keyWords = val; - return this; - } - - public Builder keyWord(String val) { - keyWords.add(val); - return this; - } - - public Builder phrase(String val) { - phrase = val; - return this; - } - - public Builder pattern(Pattern val) { - pattern = val; - return this; - } - - public Builder matchThreshold(Integer val) { - matchThreshold = val; - return this; - } - - public Builder priority(Integer val) { - priority = val; - return this; - } - - public Builder nextUnits(Set val) { - nextUnits = val; - return this; - } - - public Builder nextUnit(TestUnit val) { - nextUnits.add(val); - return this; - } - - public Builder message(String val) { - message = val; - return this; - } - - public TestUnit build() { - return new TestUnit(this); - } - } -} +//package dev.struchkov.autoresponder.test; +// +//import dev.struchkov.autoresponder.entity.Unit; +// +//import java.util.HashSet; +//import java.util.Set; +//import java.util.regex.Pattern; +// +//public class TestUnit extends Unit { +// +// private String message; +// +// public TestUnit(Set keyWords, +// String phrase, +// Pattern pattern, +// Integer matchThreshold, +// Integer priority, +// Set nextUnits, +// String message) { +// super(keyWords, phrase, pattern, matchThreshold, priority, nextUnits); +// this.message = message; +// } +// +// private TestUnit(Builder builder) { +// super(builder.keyWords, builder.phrase, builder.pattern, builder.matchThreshold, builder.priority, builder.nextUnits); +// message = builder.message; +// } +// +// public static Builder builder() { +// return new Builder(); +// } +// +// public String getMessage() { +// return message; +// } +// +// @Override +// public String toString() { +// return "TestUnit{" + +// "keyWords=" + keyWords + +// ", phrase='" + phrase + '\'' + +// ", pattern=" + pattern + +// ", matchThreshold=" + matchThreshold + +// ", priority=" + priority + +// ", message='" + message + '\'' + +// '}'; +// } +// +// +// public static final class Builder { +// private Set keyWords = new HashSet<>(); +// private String phrase; +// private Pattern pattern; +// private Integer matchThreshold; +// private Integer priority; +// private Set nextUnits = new HashSet<>(); +// private String message; +// +// public Builder keyWords(Set val) { +// keyWords = val; +// return this; +// } +// +// public Builder keyWord(String val) { +// keyWords.add(val); +// return this; +// } +// +// public Builder phrase(String val) { +// phrase = val; +// return this; +// } +// +// public Builder pattern(Pattern val) { +// pattern = val; +// return this; +// } +// +// public Builder matchThreshold(Integer val) { +// matchThreshold = val; +// return this; +// } +// +// public Builder priority(Integer val) { +// priority = val; +// return this; +// } +// +// public Builder nextUnits(Set val) { +// nextUnits = val; +// return this; +// } +// +// public Builder nextUnit(TestUnit val) { +// nextUnits.add(val); +// return this; +// } +// +// public Builder message(String val) { +// message = val; +// return this; +// } +// +// public TestUnit build() { +// return new TestUnit(this); +// } +// } +//}