feat(search): add occurrence counting menu and student settings
- add occurrence counting menu using StudentOccurrenceIndexFinder - add student settings window for configuring search target - display occurrence count and matching indices - update current collection menu to delegate occurrence counting
This commit is contained in:
@@ -4,6 +4,7 @@ import com.googlecode.lanterna.gui2.ActionListBox;
|
|||||||
import com.googlecode.lanterna.gui2.WindowBasedTextGUI;
|
import com.googlecode.lanterna.gui2.WindowBasedTextGUI;
|
||||||
import ru.project.model.AppState;
|
import ru.project.model.AppState;
|
||||||
import ru.project.ui.base.BaseModalWindow;
|
import ru.project.ui.base.BaseModalWindow;
|
||||||
|
import ru.project.ui.collection.counting.CountOccurrencesMenu;
|
||||||
import ru.project.ui.collection.search.SearchCollectionMenu;
|
import ru.project.ui.collection.search.SearchCollectionMenu;
|
||||||
import ru.project.ui.collection.sorting.SortCollectionMenu;
|
import ru.project.ui.collection.sorting.SortCollectionMenu;
|
||||||
import ru.project.ui.common.CollectionDisplayWindow;
|
import ru.project.ui.common.CollectionDisplayWindow;
|
||||||
@@ -15,8 +16,7 @@ public class CurrentCollectionMenu extends BaseModalWindow {
|
|||||||
|
|
||||||
private final SortCollectionMenu sortCollectionMenu;
|
private final SortCollectionMenu sortCollectionMenu;
|
||||||
private final SearchCollectionMenu searchCollectionMenu;
|
private final SearchCollectionMenu searchCollectionMenu;
|
||||||
|
private final CountOccurrencesMenu countOccurrencesMenu;
|
||||||
// private final CountOccurrencesMenu countOccurrencesMenu;
|
|
||||||
|
|
||||||
public CurrentCollectionMenu(WindowBasedTextGUI gui, AppState state) {
|
public CurrentCollectionMenu(WindowBasedTextGUI gui, AppState state) {
|
||||||
|
|
||||||
@@ -28,7 +28,7 @@ public class CurrentCollectionMenu extends BaseModalWindow {
|
|||||||
|
|
||||||
this.searchCollectionMenu = new SearchCollectionMenu(gui, state);
|
this.searchCollectionMenu = new SearchCollectionMenu(gui, state);
|
||||||
|
|
||||||
// this.countCollectionMenu = new CountOccurrencesMenu(gui, state);
|
this.countOccurrencesMenu = new CountOccurrencesMenu(gui, state);
|
||||||
|
|
||||||
ActionListBox menu = new ActionListBox();
|
ActionListBox menu = new ActionListBox();
|
||||||
menu.addItem("Показать коллекцию...", this::showCurrentCollection);
|
menu.addItem("Показать коллекцию...", this::showCurrentCollection);
|
||||||
@@ -39,7 +39,7 @@ public class CurrentCollectionMenu extends BaseModalWindow {
|
|||||||
|
|
||||||
menu.addItem("Меню поиска элемента в коллекции", searchCollectionMenu::showModal);
|
menu.addItem("Меню поиска элемента в коллекции", searchCollectionMenu::showModal);
|
||||||
|
|
||||||
menu.addItem("Меню подсчёта количества вхождений элемента", this::countOccurrences);
|
menu.addItem("Меню подсчёта количества вхождений элемента", countOccurrencesMenu::showModal);
|
||||||
|
|
||||||
menu.addItem("Выйти", this::close);
|
menu.addItem("Выйти", this::close);
|
||||||
|
|
||||||
@@ -57,10 +57,4 @@ public class CurrentCollectionMenu extends BaseModalWindow {
|
|||||||
|
|
||||||
window.showModal();
|
window.showModal();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void countOccurrences() {
|
|
||||||
// CountOccurrencesMenu countOccurrencesMenu = new CountOccurrencesMenu(gui, state);
|
|
||||||
//
|
|
||||||
// countOccurrencesMenu.showModal();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,109 @@
|
|||||||
|
package ru.project.ui.collection.counting;
|
||||||
|
|
||||||
|
import com.googlecode.lanterna.gui2.Button;
|
||||||
|
import com.googlecode.lanterna.gui2.Label;
|
||||||
|
import com.googlecode.lanterna.gui2.LinearLayout;
|
||||||
|
import com.googlecode.lanterna.gui2.Panel;
|
||||||
|
import com.googlecode.lanterna.gui2.WindowBasedTextGUI;
|
||||||
|
import ru.project.collection.MyList;
|
||||||
|
import ru.project.finder.StudentOccurrenceIndexFinder;
|
||||||
|
import ru.project.model.AppState;
|
||||||
|
import ru.project.student.Student;
|
||||||
|
import ru.project.ui.base.BaseModalWindow;
|
||||||
|
import ru.project.ui.common.MessageWindow;
|
||||||
|
|
||||||
|
public class CountOccurrencesMenu extends BaseModalWindow {
|
||||||
|
|
||||||
|
private final AppState state;
|
||||||
|
|
||||||
|
private final OccurrenceStudentSettingsWindow occurrenceStudentSettingsWindow;
|
||||||
|
|
||||||
|
private Student selectedStudent;
|
||||||
|
private int occurrences;
|
||||||
|
|
||||||
|
private final Label studentLabel;
|
||||||
|
private final Label occurrencesLabel;
|
||||||
|
|
||||||
|
public CountOccurrencesMenu(WindowBasedTextGUI gui, AppState state) {
|
||||||
|
|
||||||
|
super("Меню подсчёта количества вхождений элемента", gui);
|
||||||
|
|
||||||
|
this.state = state;
|
||||||
|
this.selectedStudent = null;
|
||||||
|
this.occurrences = 0;
|
||||||
|
|
||||||
|
this.studentLabel = new Label("Элемент для поиска: не задан");
|
||||||
|
|
||||||
|
this.occurrencesLabel = new Label("Количество вхождений: не подсчитано");
|
||||||
|
|
||||||
|
this.occurrenceStudentSettingsWindow =
|
||||||
|
new OccurrenceStudentSettingsWindow(
|
||||||
|
gui,
|
||||||
|
student -> {
|
||||||
|
selectedStudent = student;
|
||||||
|
|
||||||
|
studentLabel.setText("Элемент для поиска: задан");
|
||||||
|
});
|
||||||
|
|
||||||
|
Panel panel = new Panel();
|
||||||
|
panel.setLayoutManager(new LinearLayout());
|
||||||
|
|
||||||
|
panel.addComponent(studentLabel);
|
||||||
|
panel.addComponent(occurrencesLabel);
|
||||||
|
|
||||||
|
panel.addComponent(new Button("Настроить элемент для поиска...", this::setupStudent));
|
||||||
|
|
||||||
|
panel.addComponent(new Button("Выполнить подсчёт", this::countOccurrences));
|
||||||
|
|
||||||
|
panel.addComponent(new Button("Выйти", this::close));
|
||||||
|
|
||||||
|
setComponent(panel);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupStudent() {
|
||||||
|
occurrenceStudentSettingsWindow.showModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void countOccurrences() {
|
||||||
|
MyList<Student> collection = state.getMainCollection();
|
||||||
|
|
||||||
|
if (collection == null || collection.isEmpty()) {
|
||||||
|
MessageWindow.showModal(gui, "Текущая коллекция пуста.");
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedStudent == null) {
|
||||||
|
MessageWindow.showModal(gui, "Сначала настройте элемент для поиска.");
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
StudentOccurrenceIndexFinder finder = new StudentOccurrenceIndexFinder();
|
||||||
|
|
||||||
|
int[] occurrenceIndices = finder.findOccurrences(collection, selectedStudent);
|
||||||
|
|
||||||
|
occurrences = occurrenceIndices.length;
|
||||||
|
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
|
||||||
|
result.append("Количество вхождений: ").append(occurrences);
|
||||||
|
|
||||||
|
result.append("\nИндексы: ");
|
||||||
|
|
||||||
|
if (occurrenceIndices.length == 0) {
|
||||||
|
result.append("нет");
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < occurrenceIndices.length; i++) {
|
||||||
|
|
||||||
|
if (i > 0) {
|
||||||
|
result.append(", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
result.append(occurrenceIndices[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
occurrencesLabel.setText(result.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
+172
@@ -0,0 +1,172 @@
|
|||||||
|
package ru.project.ui.collection.counting;
|
||||||
|
|
||||||
|
import com.googlecode.lanterna.gui2.Button;
|
||||||
|
import com.googlecode.lanterna.gui2.Label;
|
||||||
|
import com.googlecode.lanterna.gui2.LinearLayout;
|
||||||
|
import com.googlecode.lanterna.gui2.Panel;
|
||||||
|
import com.googlecode.lanterna.gui2.WindowBasedTextGUI;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import ru.project.student.Student;
|
||||||
|
import ru.project.student.StudentBuilder;
|
||||||
|
import ru.project.ui.base.BaseModalWindow;
|
||||||
|
import ru.project.ui.common.InputValueWindow;
|
||||||
|
import ru.project.ui.common.MessageWindow;
|
||||||
|
|
||||||
|
public class OccurrenceStudentSettingsWindow extends BaseModalWindow {
|
||||||
|
|
||||||
|
private final StudentBuilder studentBuilder;
|
||||||
|
private final Consumer<Student> onApply;
|
||||||
|
|
||||||
|
private String groupNumber;
|
||||||
|
private Double averageGrade;
|
||||||
|
private Integer recordBookNumber;
|
||||||
|
|
||||||
|
private final Label groupNumberLabel;
|
||||||
|
private final Label averageGradeLabel;
|
||||||
|
private final Label recordBookNumberLabel;
|
||||||
|
|
||||||
|
public OccurrenceStudentSettingsWindow(WindowBasedTextGUI gui, Consumer<Student> onApply) {
|
||||||
|
|
||||||
|
super("Настройка элемента для поиска", gui);
|
||||||
|
|
||||||
|
this.studentBuilder = new StudentBuilder();
|
||||||
|
this.onApply = onApply;
|
||||||
|
|
||||||
|
Panel panel = new Panel();
|
||||||
|
panel.setLayoutManager(new LinearLayout());
|
||||||
|
|
||||||
|
groupNumberLabel = new Label("Номер группы: не задан");
|
||||||
|
|
||||||
|
averageGradeLabel = new Label("Средний балл: не задан");
|
||||||
|
|
||||||
|
recordBookNumberLabel = new Label("Номер зачётной книжки: не задан");
|
||||||
|
|
||||||
|
panel.addComponent(groupNumberLabel);
|
||||||
|
panel.addComponent(averageGradeLabel);
|
||||||
|
panel.addComponent(recordBookNumberLabel);
|
||||||
|
|
||||||
|
panel.addComponent(new Button("Задать номер группы...", this::setGroupNumber));
|
||||||
|
|
||||||
|
panel.addComponent(new Button("Задать средний балл...", this::setAverageGrade));
|
||||||
|
|
||||||
|
panel.addComponent(new Button("Задать номер зачётной книжки...", this::setRecordBookNumber));
|
||||||
|
|
||||||
|
panel.addComponent(new Button("Применить", this::apply));
|
||||||
|
|
||||||
|
panel.addComponent(new Button("Выйти", this::close));
|
||||||
|
|
||||||
|
setComponent(panel);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setGroupNumber() {
|
||||||
|
InputValueWindow window =
|
||||||
|
new InputValueWindow(
|
||||||
|
gui,
|
||||||
|
"Номер группы",
|
||||||
|
"Введите номер группы (например, A12):",
|
||||||
|
groupNumber,
|
||||||
|
value -> {
|
||||||
|
if (!value.matches(Student.getGroupNumberPattern())) {
|
||||||
|
|
||||||
|
MessageWindow.showModal(gui, "Номер группы должен соответствовать формату A12.");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
groupNumber = value;
|
||||||
|
studentBuilder.setGroupNumber(value);
|
||||||
|
|
||||||
|
groupNumberLabel.setText("Номер группы: " + groupNumber);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
window.showModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setAverageGrade() {
|
||||||
|
InputValueWindow window =
|
||||||
|
new InputValueWindow(
|
||||||
|
gui,
|
||||||
|
"Средний балл",
|
||||||
|
"Введите средний балл от 0.0 до 5.0:",
|
||||||
|
averageGrade == null ? null : String.valueOf(averageGrade),
|
||||||
|
value -> {
|
||||||
|
try {
|
||||||
|
double parsedValue = Double.parseDouble(value);
|
||||||
|
|
||||||
|
if (parsedValue < Student.getMinAverageGrade()
|
||||||
|
|| parsedValue > Student.getMaxAverageGrade()) {
|
||||||
|
|
||||||
|
MessageWindow.showModal(gui, "Средний балл должен быть от 0.0 до 5.0.");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
averageGrade = parsedValue;
|
||||||
|
studentBuilder.setAverageGrade(parsedValue);
|
||||||
|
|
||||||
|
averageGradeLabel.setText("Средний балл: " + averageGrade);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
MessageWindow.showModal(gui, "Введите корректное число.");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
window.showModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setRecordBookNumber() {
|
||||||
|
InputValueWindow window =
|
||||||
|
new InputValueWindow(
|
||||||
|
gui,
|
||||||
|
"Номер зачётной книжки",
|
||||||
|
"Введите номер зачётной книжки:",
|
||||||
|
recordBookNumber == null ? null : String.valueOf(recordBookNumber),
|
||||||
|
value -> {
|
||||||
|
try {
|
||||||
|
int parsedValue = Integer.parseInt(value);
|
||||||
|
|
||||||
|
if (parsedValue < Student.getMinRecordBookNumber()) {
|
||||||
|
|
||||||
|
MessageWindow.showModal(gui, "Номер зачётной книжки должен быть положительным.");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
recordBookNumber = parsedValue;
|
||||||
|
studentBuilder.setRecordBookNumber(parsedValue);
|
||||||
|
|
||||||
|
recordBookNumberLabel.setText("Номер зачётной книжки: " + recordBookNumber);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
MessageWindow.showModal(gui, "Введите целое число.");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
window.showModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void apply() {
|
||||||
|
if (groupNumber == null || averageGrade == null || recordBookNumber == null) {
|
||||||
|
|
||||||
|
MessageWindow.showModal(gui, "Необходимо задать все поля элемента.");
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Student student = studentBuilder.build();
|
||||||
|
|
||||||
|
onApply.accept(student);
|
||||||
|
|
||||||
|
MessageWindow.showModal(gui, "Элемент для поиска успешно настроен.");
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user