Теперь для каждого ключевого слова можно указывать его важность.

This commit is contained in:
Struchkov Mark 2022-07-09 07:24:54 +03:00
parent deb53d600e
commit 252d987857
8 changed files with 217 additions and 140 deletions

View File

@ -6,7 +6,7 @@
<groupId>dev.struchkov</groupId> <groupId>dev.struchkov</groupId>
<artifactId>autoresponder</artifactId> <artifactId>autoresponder</artifactId>
<version>3.0.0</version> <version>3.1.0</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>Abstract Autoresponder</name> <name>Abstract Autoresponder</name>

View File

@ -1,6 +1,7 @@
package dev.struchkov.autoresponder; package dev.struchkov.autoresponder;
import dev.struchkov.autoresponder.compare.UnitPriorityComparator; import dev.struchkov.autoresponder.compare.UnitPriorityComparator;
import dev.struchkov.autoresponder.entity.KeyWord;
import dev.struchkov.autoresponder.entity.Unit; import dev.struchkov.autoresponder.entity.Unit;
import dev.struchkov.autoresponder.util.Message; import dev.struchkov.autoresponder.util.Message;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -42,10 +43,10 @@ public final class Responder {
* @return Юнит, который нуждается в обработке в соответствии с запросом пользователя * @return Юнит, который нуждается в обработке в соответствии с запросом пользователя
*/ */
public static <U extends Unit<U>> Optional<U> nextUnit(String message, Collection<U> nextUnits) { public static <U extends Unit<U>> Optional<U> nextUnit(String message, Collection<U> nextUnits) {
isNotNull(nextUnits, message); isNotNull(nextUnits);
final Set<U> searchUnit = new HashSet<>(); final Set<U> searchUnit = new HashSet<>();
if (nextUnits != null) { if (message != null && nextUnits != null) {
for (U unit : nextUnits) { for (U unit : nextUnits) {
final String unitPhrase = unit.getPhrase(); final String unitPhrase = unit.getPhrase();
if ( if (
@ -60,16 +61,16 @@ public final class Responder {
searchUnit.add(unit); searchUnit.add(unit);
} }
if (percentageMatch(unit, splitWords(message)) >= unit.getMatchThreshold()) { if (percentageMatch(unit, message) >= unit.getMatchThreshold()) {
searchUnit.add(unit); searchUnit.add(unit);
} }
} }
}
if (searchUnit.isEmpty()) { if (searchUnit.isEmpty()) {
for (U nextUnit : nextUnits) { for (U nextUnit : nextUnits) {
if ((nextUnit.getPattern() == null && (nextUnit.getKeyWords() == null || nextUnit.getKeyWords().isEmpty()))) { if (isNotPattern(nextUnit) && isNotKeyWords(nextUnit) && isNotPhrase(nextUnit)) {
searchUnit.add(nextUnit); searchUnit.add(nextUnit);
}
} }
} }
} }
@ -77,24 +78,51 @@ public final class Responder {
return searchUnit.stream().max(UNIT_PRIORITY_COMPARATOR); return searchUnit.stream().max(UNIT_PRIORITY_COMPARATOR);
} }
private static <U extends Unit<U>> boolean isNotPhrase(U nextUnit) {
return nextUnit.getPhrase() == null;
}
private static <U extends Unit<U>> boolean isNotPattern(U nextUnit) {
return nextUnit.getPattern() == null;
}
private static <U extends Unit<U>> boolean isNotKeyWords(U nextUnit) {
return nextUnit.getKeyWords() == null || nextUnit.getKeyWords().isEmpty();
}
private static boolean patternReg(Pattern pattern, String message) { private static boolean patternReg(Pattern pattern, String message) {
isNotNull(pattern); isNotNull(pattern);
return message.matches(pattern.pattern()); return message.matches(pattern.pattern());
} }
private static <U extends Unit<U>> Double percentageMatch(U unit, Set<String> words) { private static <U extends Unit<U>> double percentageMatch(U unit, String message) {
final Set<String> keyWords = unit.getKeyWords(); final Set<KeyWord> unitKeyWords = unit.getKeyWords();
if (keyWords != null && !keyWords.isEmpty()) { if (unitKeyWords != null && !unitKeyWords.isEmpty()) {
final Set<String> temp = new HashSet<>(keyWords); final Set<String> messageWords = splitWords(message);
temp.retainAll(words); final Set<KeyWord> intersection = getIntersection(unitKeyWords, messageWords);
log.trace(Message.UNIT_KEYWORDS, keyWords, keyWords.size()); final double intersectionWeight = getIntersectionWeight(intersection);
log.trace(Message.USER_MESSAGE_KEYWORDS, words); log.debug(Message.UNIT_KEYWORDS, unitKeyWords, unitKeyWords.size());
log.trace(Message.INTERSECTION, temp, temp.size()); log.debug(Message.USER_MESSAGE_KEYWORDS, messageWords);
log.trace(Message.CROSSING_PERCENTAGE, (double) temp.size() / (double) keyWords.size() * 100.0, unit.getMatchThreshold()); log.debug(Message.INTERSECTION, intersection, intersectionWeight);
return (double) temp.size() / (double) keyWords.size() * 100.0; log.debug(Message.CROSSING_PERCENTAGE, intersectionWeight / (double) unitKeyWords.size() * 100.0, unit.getMatchThreshold());
return (double) intersection.size() / (double) unitKeyWords.size() * 100.0;
} else { } else {
return 0.0; return 0.0;
} }
} }
private static double getIntersectionWeight(Set<KeyWord> intersection) {
return intersection.stream().mapToInt(KeyWord::getImportant).sum();
}
private static Set<KeyWord> getIntersection(Set<KeyWord> unitKeyWords, Set<String> messageWords) {
final Set<KeyWord> intersection = new HashSet<>();
for (KeyWord unitKeyWord : unitKeyWords) {
if (messageWords.contains(unitKeyWord.getWord())) {
intersection.add(unitKeyWord);
}
}
return intersection;
}
} }

View File

@ -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;
}
}

View File

@ -15,7 +15,7 @@ public abstract class Unit<U extends Unit<U>> {
/** /**
* Ключевые слова. * Ключевые слова.
*/ */
protected Set<String> keyWords = new HashSet<>(); protected Set<KeyWord> keyWords = new HashSet<>();
/** /**
* Точная фраза. * Точная фраза.
@ -42,12 +42,14 @@ public abstract class Unit<U extends Unit<U>> {
*/ */
protected Set<U> nextUnits = new HashSet<>(); protected Set<U> nextUnits = new HashSet<>();
protected Unit(Set<String> keyWords, protected Unit(
String phrase, Set<KeyWord> keyWords,
Pattern pattern, String phrase,
Integer matchThreshold, Pattern pattern,
Integer priority, Integer matchThreshold,
Set<U> nextUnits) { Integer priority,
Set<U> nextUnits
) {
this.keyWords = keyWords; this.keyWords = keyWords;
this.phrase = phrase; this.phrase = phrase;
this.pattern = pattern; this.pattern = pattern;
@ -56,11 +58,11 @@ public abstract class Unit<U extends Unit<U>> {
this.nextUnits = nextUnits; this.nextUnits = nextUnits;
} }
public Set<String> getKeyWords() { public Set<KeyWord> getKeyWords() {
return keyWords; return keyWords;
} }
public void setKeyWords(Set<String> keyWords) { public void setKeyWords(Set<KeyWord> keyWords) {
this.keyWords = keyWords; this.keyWords = keyWords;
} }

View File

@ -0,0 +1,9 @@
package dev.struchkov.autoresponder.exception;
public class AutoresponderException extends RuntimeException {
public AutoresponderException(String message) {
super(message);
}
}

View File

@ -9,7 +9,7 @@ public final class Message {
static final String UTILITY_CLASS = "Клас утилита"; static final String UTILITY_CLASS = "Клас утилита";
public static final String UNIT_KEYWORDS = "Ключевые слова юнита: {} ({})"; 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 INTERSECTION = "Пересечение: {} ({})";
public static final String CROSSING_PERCENTAGE = "Процент: {} Необходимо: {}"; public static final String CROSSING_PERCENTAGE = "Процент: {} Необходимо: {}";

View File

@ -4,7 +4,7 @@ import java.util.Arrays;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; 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<String> pretexts = Set.of( private static final Set<String> pretexts = Set.of(
"в", "без", "до", "из", "к", "на", "по", "о", "от", "перед", "при", "с", "у", "за", "над", "об", "в", "без", "до", "из", "к", "на", "по", "о", "от", "перед", "при", "с", "у", "за", "над", "об",
"под", "про", "для"); "под", "про", "для"
);
private Parser() { private Parser() {
throw new IllegalStateException(UTILITY_CLASS); utilityClass();
} }
/** /**

View File

@ -1,108 +1,108 @@
package dev.struchkov.autoresponder.test; //package dev.struchkov.autoresponder.test;
//
import dev.struchkov.autoresponder.entity.Unit; //import dev.struchkov.autoresponder.entity.Unit;
//
import java.util.HashSet; //import java.util.HashSet;
import java.util.Set; //import java.util.Set;
import java.util.regex.Pattern; //import java.util.regex.Pattern;
//
public class TestUnit extends Unit<TestUnit> { //public class TestUnit extends Unit<TestUnit> {
//
private String message; // private String message;
//
public TestUnit(Set<String> keyWords, // public TestUnit(Set<String> keyWords,
String phrase, // String phrase,
Pattern pattern, // Pattern pattern,
Integer matchThreshold, // Integer matchThreshold,
Integer priority, // Integer priority,
Set<TestUnit> nextUnits, // Set<TestUnit> nextUnits,
String message) { // String message) {
super(keyWords, phrase, pattern, matchThreshold, priority, nextUnits); // super(keyWords, phrase, pattern, matchThreshold, priority, nextUnits);
this.message = message; // this.message = message;
} // }
//
private TestUnit(Builder builder) { // private TestUnit(Builder builder) {
super(builder.keyWords, builder.phrase, builder.pattern, builder.matchThreshold, builder.priority, builder.nextUnits); // super(builder.keyWords, builder.phrase, builder.pattern, builder.matchThreshold, builder.priority, builder.nextUnits);
message = builder.message; // message = builder.message;
} // }
//
public static Builder builder() { // public static Builder builder() {
return new Builder(); // return new Builder();
} // }
//
public String getMessage() { // public String getMessage() {
return message; // return message;
} // }
//
@Override // @Override
public String toString() { // public String toString() {
return "TestUnit{" + // return "TestUnit{" +
"keyWords=" + keyWords + // "keyWords=" + keyWords +
", phrase='" + phrase + '\'' + // ", phrase='" + phrase + '\'' +
", pattern=" + pattern + // ", pattern=" + pattern +
", matchThreshold=" + matchThreshold + // ", matchThreshold=" + matchThreshold +
", priority=" + priority + // ", priority=" + priority +
", message='" + message + '\'' + // ", message='" + message + '\'' +
'}'; // '}';
} // }
//
//
public static final class Builder { // public static final class Builder {
private Set<String> keyWords = new HashSet<>(); // private Set<String> keyWords = new HashSet<>();
private String phrase; // private String phrase;
private Pattern pattern; // private Pattern pattern;
private Integer matchThreshold; // private Integer matchThreshold;
private Integer priority; // private Integer priority;
private Set<TestUnit> nextUnits = new HashSet<>(); // private Set<TestUnit> nextUnits = new HashSet<>();
private String message; // private String message;
//
public Builder keyWords(Set<String> val) { // public Builder keyWords(Set<String> val) {
keyWords = val; // keyWords = val;
return this; // return this;
} // }
//
public Builder keyWord(String val) { // public Builder keyWord(String val) {
keyWords.add(val); // keyWords.add(val);
return this; // return this;
} // }
//
public Builder phrase(String val) { // public Builder phrase(String val) {
phrase = val; // phrase = val;
return this; // return this;
} // }
//
public Builder pattern(Pattern val) { // public Builder pattern(Pattern val) {
pattern = val; // pattern = val;
return this; // return this;
} // }
//
public Builder matchThreshold(Integer val) { // public Builder matchThreshold(Integer val) {
matchThreshold = val; // matchThreshold = val;
return this; // return this;
} // }
//
public Builder priority(Integer val) { // public Builder priority(Integer val) {
priority = val; // priority = val;
return this; // return this;
} // }
//
public Builder nextUnits(Set<TestUnit> val) { // public Builder nextUnits(Set<TestUnit> val) {
nextUnits = val; // nextUnits = val;
return this; // return this;
} // }
//
public Builder nextUnit(TestUnit val) { // public Builder nextUnit(TestUnit val) {
nextUnits.add(val); // nextUnits.add(val);
return this; // return this;
} // }
//
public Builder message(String val) { // public Builder message(String val) {
message = val; // message = val;
return this; // return this;
} // }
//
public TestUnit build() { // public TestUnit build() {
return new TestUnit(this); // return new TestUnit(this);
} // }
} // }
} //}