Ломбок, Новая логика приоритетов, Исправление багов

This commit is contained in:
Mark Struchkov 2019-07-12 17:12:49 +03:00
parent c7eda5c61d
commit f2ad7bee0f
7 changed files with 107 additions and 129 deletions

View File

@ -15,8 +15,6 @@ import org.sadtech.autoresponder.util.Parser;
import java.util.HashSet; import java.util.HashSet;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** /**
* Реализуют основную логику автоответчика. * Реализуют основную логику автоответчика.
@ -24,7 +22,7 @@ import java.util.regex.Pattern;
* @author upagge [07/07/2019] * @author upagge [07/07/2019]
*/ */
@Slf4j @Slf4j
public class Autoresponder { public class AutoResponder {
@Description("Компоратор для сортировки Unit-ов") @Description("Компоратор для сортировки Unit-ов")
private static final UnitPriorityComparator UNIT_PRIORITY_COMPARATOR = new UnitPriorityComparator(); private static final UnitPriorityComparator UNIT_PRIORITY_COMPARATOR = new UnitPriorityComparator();
@ -40,7 +38,7 @@ public class Autoresponder {
@Getter @Getter
private final UnitPointerService unitPointerService; private final UnitPointerService unitPointerService;
public Autoresponder(UnitPointerService unitPointerService, Set<Unit> startUnits) { public AutoResponder(UnitPointerService unitPointerService, Set<Unit> startUnits) {
this.startUnits = startUnits; this.startUnits = startUnits;
this.unitPointerService = unitPointerService; this.unitPointerService = unitPointerService;
} }
@ -56,7 +54,7 @@ public class Autoresponder {
UnitPointer unitPointer = checkAndAddPerson(personId); UnitPointer unitPointer = checkAndAddPerson(personId);
Unit unit; Unit unit;
try { try {
if (unitPointer.getUnit() == null || unitPointer.getUnit().getNextUnits() == null) { if (unitPointer.getUnit() == null || unitPointer.getUnit().getNextUnits() == null || unitPointer.getUnit().getNextUnits().isEmpty()) {
unit = nextUnit(startUnits, message); unit = nextUnit(startUnits, message);
} else { } else {
unit = nextUnit(unitPointer.getUnit().getNextUnits(), message); unit = nextUnit(unitPointer.getUnit().getNextUnits(), message);
@ -75,23 +73,22 @@ public class Autoresponder {
* @param message Запрос пользователя - текстовое сообщение * @param message Запрос пользователя - текстовое сообщение
* @return Юнит, который нуждается в обработке в соответствии с запросом пользователя * @return Юнит, который нуждается в обработке в соответствии с запросом пользователя
*/ */
private Unit nextUnit(Set<Unit> nextUnits, String message) { private Unit nextUnit(@NonNull Set<Unit> nextUnits, String message) {
Optional<Unit> unit = nextUnits.stream() Set<Unit> searchUnit = new HashSet<>();
nextUnits.stream()
.filter(nextUnit -> nextUnit.getPattern() != null && patternReg(nextUnit, message)) .filter(nextUnit -> nextUnit.getPattern() != null && patternReg(nextUnit, message))
.max(UNIT_PRIORITY_COMPARATOR); .forEach(searchUnit::add);
if (!unit.isPresent()) { nextUnits.stream()
unit = nextUnits.stream() .filter(nextUnit -> percentageMatch(nextUnit, Parser.parse(message)) >= nextUnit.getMatchThreshold())
.filter(nextUnit -> percentageMatch(nextUnit, Parser.parse(message)) >= nextUnit.getMatchThreshold()) .forEach(searchUnit::add);
.max(UNIT_PRIORITY_COMPARATOR);
}
if (!unit.isPresent()) { nextUnits.stream()
unit = nextUnits.stream() .filter(nextUnit -> (nextUnit.getPattern() == null && (nextUnit.getKeyWords() == null || nextUnit.getKeyWords().isEmpty())))
.filter(nextUnit -> (nextUnit.getPattern() == null && nextUnit.getKeyWords() == null)) .forEach(searchUnit::add);
.max(UNIT_PRIORITY_COMPARATOR);
} return searchUnit.stream().max(UNIT_PRIORITY_COMPARATOR).orElseThrow(NotFoundUnitException::new);
return unit.orElseThrow(NotFoundUnitException::new);
} }
/** /**
@ -100,7 +97,7 @@ public class Autoresponder {
* @param personId Идентификатор пользователя * @param personId Идентификатор пользователя
* @return {@link UnitPointer}, который сохраняет {@link Unit}, который был обработан последним * @return {@link UnitPointer}, который сохраняет {@link Unit}, который был обработан последним
*/ */
private UnitPointer checkAndAddPerson(Integer personId) { private UnitPointer checkAndAddPerson(@NonNull Integer personId) {
UnitPointer unitPointer; UnitPointer unitPointer;
if (unitPointerService.check(personId)) { if (unitPointerService.check(personId)) {
unitPointer = unitPointerService.getByEntityId(personId); unitPointer = unitPointerService.getByEntityId(personId);
@ -112,14 +109,12 @@ public class Autoresponder {
} }
private boolean patternReg(Unit unit, String message) { private boolean patternReg(@NonNull Unit unit, String message) {
Pattern pattern = unit.getPattern(); return message.matches(unit.getPattern().pattern());
Matcher m = pattern.matcher(message);
return m.find();
} }
private Double percentageMatch(Unit unit, Set<String> words) { private Double percentageMatch(Unit unit, Set<String> words) {
if (unit.getKeyWords() != null) { if (unit.getKeyWords() != null && !unit.getKeyWords().isEmpty()) {
Set<String> temp = new HashSet<>(unit.getKeyWords()); Set<String> temp = new HashSet<>(unit.getKeyWords());
temp.retainAll(words); temp.retainAll(words);
log.info("Ключевые слова юнита: {} ({})", unit.getKeyWords(), unit.getKeyWords().size()); log.info("Ключевые слова юнита: {} ({})", unit.getKeyWords(), unit.getKeyWords().size());

View File

@ -3,10 +3,10 @@ package org.sadtech.autoresponder.entity;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import lombok.ToString;
import org.sadtech.autoresponder.util.Description; import org.sadtech.autoresponder.util.Description;
import java.util.Arrays; import java.util.Optional;
import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -17,52 +17,30 @@ import java.util.regex.Pattern;
*/ */
@Getter @Getter
@EqualsAndHashCode @EqualsAndHashCode
@ToString
@Setter
public abstract class Unit { public abstract class Unit {
@Description("Ключевые слова") @Description("Ключевые слова")
private Set<String> keyWords; protected Set<String> keyWords;
@Setter
@Description("Регулярное выражение") @Description("Регулярное выражение")
private Pattern pattern; protected Pattern pattern;
@Setter @Description("Значение минимального отношения количества найденых ключевых слов, к количеству ключевых слов Unit-а")
// todo [upagge] [07/07/2019]: Придумать нормальное описание protected Integer matchThreshold;
private Integer matchThreshold = 10;
@Setter
@Description("Значение приоритета") @Description("Значение приоритета")
private Integer priority = 10; protected Integer priority;
@Description("Множество следующих Unit в сценарии") @Description("Множество следующих Unit в сценарии")
private Set<Unit> nextUnits; protected Set<Unit> nextUnits;
public void setKeyWord(String... keyWord) { protected Unit(Set<String> keyWords, Pattern pattern, Integer matchThreshold, Integer priority, Set<Unit> nextUnits) {
if (this.keyWords == null) { this.keyWords = keyWords;
this.keyWords = new HashSet<>(); this.pattern = pattern;
} this.matchThreshold = Optional.ofNullable(matchThreshold).orElse(10);
this.keyWords.addAll(Arrays.asList(keyWord)); this.priority = Optional.ofNullable(priority).orElse(10);
this.nextUnits = nextUnits;
} }
public void setKeyWords(Set<String> keyWords) {
if (this.keyWords == null) {
this.keyWords = new HashSet<>();
}
this.keyWords.addAll(keyWords);
}
public void setNextUnit(Unit... unit) {
if (nextUnits == null) {
nextUnits = new HashSet<>();
}
nextUnits.addAll(Arrays.asList(unit));
}
public void setNextUnits(Set<Unit> nextUnits) {
if (nextUnits == null) {
nextUnits = new HashSet<>();
}
this.nextUnits.addAll(nextUnits);
}
} }

View File

@ -6,4 +6,5 @@ package org.sadtech.autoresponder.exception;
* @author upagge [07/07/2019] * @author upagge [07/07/2019]
*/ */
public class NotFoundUnitException extends RuntimeException { public class NotFoundUnitException extends RuntimeException {
} }

View File

@ -1,5 +1,6 @@
package org.sadtech.autoresponder.repository; package org.sadtech.autoresponder.repository;
import lombok.NonNull;
import org.sadtech.autoresponder.entity.UnitPointer; import org.sadtech.autoresponder.entity.UnitPointer;
import java.util.Collection; import java.util.Collection;
@ -16,27 +17,27 @@ public class UnitPointerRepositoryMap implements UnitPointerRepository {
private Map<Integer, UnitPointer> unitPointerMap = new HashMap<>(); private Map<Integer, UnitPointer> unitPointerMap = new HashMap<>();
@Override @Override
public void add(UnitPointer unitPointer) { public void add(@NonNull UnitPointer unitPointer) {
unitPointerMap.put(unitPointer.getEntityId(), unitPointer); unitPointerMap.put(unitPointer.getEntityId(), unitPointer);
} }
@Override @Override
public void edit(UnitPointer unitPointer) { public void edit(@NonNull UnitPointer unitPointer) {
unitPointerMap.get(unitPointer.getEntityId()).setUnit(unitPointer.getUnit()); unitPointerMap.get(unitPointer.getEntityId()).setUnit(unitPointer.getUnit());
} }
@Override @Override
public void remove(Integer entityId) { public void remove(@NonNull Integer entityId) {
unitPointerMap.remove(entityId); unitPointerMap.remove(entityId);
} }
@Override @Override
public void addAll(Collection<UnitPointer> unitPointers) { public void addAll(@NonNull Collection<UnitPointer> unitPointers) {
unitPointers.parallelStream().forEach(unitPointer -> unitPointerMap.put(unitPointer.getEntityId(), unitPointer)); unitPointers.parallelStream().forEach(unitPointer -> unitPointerMap.put(unitPointer.getEntityId(), unitPointer));
} }
@Override @Override
public UnitPointer findByEntityId(Integer entityId) { public UnitPointer findByEntityId(@NonNull Integer entityId) {
return unitPointerMap.get(entityId); return unitPointerMap.get(entityId);
} }
} }

View File

@ -1,44 +1,40 @@
package org.sadtech.autoresponder.service; package org.sadtech.autoresponder.service;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.sadtech.autoresponder.entity.Unit; import org.sadtech.autoresponder.entity.Unit;
import org.sadtech.autoresponder.entity.UnitPointer; import org.sadtech.autoresponder.entity.UnitPointer;
import org.sadtech.autoresponder.repository.UnitPointerRepository; import org.sadtech.autoresponder.repository.UnitPointerRepository;
import org.sadtech.autoresponder.repository.UnitPointerRepositoryMap;
@Slf4j @Slf4j
public class UnitPointerServiceImpl implements UnitPointerService { public class UnitPointerServiceImpl implements UnitPointerService {
private UnitPointerRepository unitPointerRepository; private final UnitPointerRepository unitPointerRepository;
public UnitPointerServiceImpl() {
this.unitPointerRepository = new UnitPointerRepositoryMap();
}
public UnitPointerServiceImpl(UnitPointerRepository unitPointerRepository) { public UnitPointerServiceImpl(UnitPointerRepository unitPointerRepository) {
this.unitPointerRepository = unitPointerRepository; this.unitPointerRepository = unitPointerRepository;
} }
@Override @Override
public UnitPointer getByEntityId(Integer entityId) { public UnitPointer getByEntityId(@NonNull Integer entityId) {
return unitPointerRepository.findByEntityId(entityId); return unitPointerRepository.findByEntityId(entityId);
} }
@Override @Override
public void edit(Integer personId, Unit unit) { public void edit(@NonNull Integer personId, Unit unit) {
if (check(personId)) { if (check(personId)) {
unitPointerRepository.edit(new UnitPointer(personId, unit)); unitPointerRepository.edit(new UnitPointer(personId, unit));
} }
} }
@Override @Override
public void add(UnitPointer unitPointer) { public void add(@NonNull UnitPointer unitPointer) {
unitPointerRepository.add(unitPointer); unitPointerRepository.add(unitPointer);
log.info("Пользователь отправлен в репозиторий"); log.info("Пользователь отправлен в репозиторий");
} }
@Override @Override
public boolean check(Integer entityId) { public boolean check(@NonNull Integer entityId) {
return unitPointerRepository.findByEntityId(entityId) != null; return unitPointerRepository.findByEntityId(entityId) != null;
} }
} }

View File

@ -12,42 +12,53 @@ import org.sadtech.autoresponder.test.TestUnit;
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;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class AutoresponderTest { public class AutoResponderTest {
private Autoresponder autoresponder; private AutoResponder autoresponder;
@Before @Before
public void setUp() { public void setUp() {
TestUnit dela = new TestUnit(); TestUnit dela = TestUnit.builder()
dela.setKeyWord("дела", "делишки"); .keyWord("дела")
dela.setMessage("хорошо"); .keyWord("делишки")
.message("хорошо")
.build();
TestUnit delaTop = new TestUnit(); TestUnit delaTop = TestUnit.builder()
delaTop.setPriority(100); .priority(100)
delaTop.setKeyWord("делишки"); .keyWord("делишки")
delaTop.setMessage("отлично"); .message("отлично")
.build();
TestUnit hello = new TestUnit(); TestUnit hello = TestUnit.builder()
hello.setMessage("тест"); .keyWord("привет")
hello.setKeyWord("привет"); .message("тест")
hello.setNextUnit(dela); .nextUnit(dela)
hello.setNextUnit(delaTop); .nextUnit(delaTop)
.build();
TestUnit number = new TestUnit();
number.setKeyWord("89101234567");
number.setMessage("ответ");
TestUnit regExp = new TestUnit(); TestUnit number = TestUnit.builder()
regExp.setPattern(Pattern.compile("^((8|\\+7)[\\- ]?)?(\\(?\\d{3}\\)?[\\- ]?)?[\\d\\- ]{7,10}$")); .keyWord("89101234567")
regExp.setMessage("регулярка"); .message("ответ")
dela.setNextUnit(regExp); .build();
dela.setNextUnit(number);
TestUnit unreal = new TestUnit(); TestUnit regExp = TestUnit.builder()
unreal.setKeyWord("витамин", "мультифрукт", "арбуз", "параметр"); .pattern(Pattern.compile("^((8|\\+7)[\\- ]?)?(\\(?\\d{3}\\)?[\\- ]?)?[\\d\\- ]{7,10}$"))
unreal.setMessage("победа"); .message("регулярка")
unreal.setMatchThreshold(100); .build();
dela.setNextUnits(Stream.of(regExp, number).collect(Collectors.toSet()));
Set<String> strings = Stream.of("витамин", "мультифрукт", "арбуз", "параметр").collect(Collectors.toSet());
TestUnit unreal = TestUnit.builder()
.keyWords(strings)
.message("победа")
.matchThreshold(100)
.build();
Set<Unit> testUnits = new HashSet<>(); Set<Unit> testUnits = new HashSet<>();
testUnits.add(hello); testUnits.add(hello);
@ -55,7 +66,7 @@ public class AutoresponderTest {
testUnits.add(unreal); testUnits.add(unreal);
UnitPointerServiceImpl unitPointerService = new UnitPointerServiceImpl(new UnitPointerRepositoryMap()); UnitPointerServiceImpl unitPointerService = new UnitPointerServiceImpl(new UnitPointerRepositoryMap());
autoresponder = new Autoresponder(unitPointerService, testUnits); autoresponder = new AutoResponder(unitPointerService, testUnits);
} }
@Test @Test
@ -68,8 +79,7 @@ public class AutoresponderTest {
@Test @Test
public void defaultAnswer() { public void defaultAnswer() {
TestUnit defaultUnit = new TestUnit(); TestUnit defaultUnit = TestUnit.builder().message("не знаю").build();
defaultUnit.setMessage("не знаю");
autoresponder.setDefaultUnit(defaultUnit); autoresponder.setDefaultUnit(defaultUnit);
Assert.assertEquals(((TestUnit) autoresponder.answer(2, "пока")).getMessage(), "не знаю"); Assert.assertEquals(((TestUnit) autoresponder.answer(2, "пока")).getMessage(), "не знаю");
@ -113,8 +123,7 @@ public class AutoresponderTest {
@Test @Test
public void generalAnswer() { public void generalAnswer() {
TestUnit defaultUnit = new TestUnit(); TestUnit defaultUnit = TestUnit.builder().message("не знаю").build();
defaultUnit.setMessage("не знаю");
autoresponder.setDefaultUnit(defaultUnit); autoresponder.setDefaultUnit(defaultUnit);
String answer = ((TestUnit) autoresponder.answer(1, "привет это тестирование функциональности")).getMessage(); String answer = ((TestUnit) autoresponder.answer(1, "привет это тестирование функциональности")).getMessage();
Assert.assertEquals("тест", answer); Assert.assertEquals("тест", answer);

View File

@ -1,32 +1,30 @@
package org.sadtech.autoresponder.test; package org.sadtech.autoresponder.test;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Singular;
import lombok.ToString;
import org.sadtech.autoresponder.entity.Unit; import org.sadtech.autoresponder.entity.Unit;
import java.util.Objects; import java.util.Set;
import java.util.regex.Pattern;
@Getter
@EqualsAndHashCode(callSuper = true)
@ToString
public class TestUnit extends Unit { public class TestUnit extends Unit {
private String message; private String message;
public String getMessage() { @Builder
return message; public TestUnit(@Singular(value = "keyWord") Set<String> keyWords,
} Pattern pattern,
Integer matchThreshold,
public void setMessage(String message) { Integer priority,
@Singular(value = "nextUnit") Set<Unit> nextUnits,
String message) {
super(keyWords, pattern, matchThreshold, priority, nextUnits);
this.message = message; this.message = message;
} }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof TestUnit)) return false;
if (!super.equals(o)) return false;
TestUnit testUnit = (TestUnit) o;
return Objects.equals(message, testUnit.message);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), message);
}
} }