From ef29761e0a2743456df411e95c5560522febcb0a Mon Sep 17 00:00:00 2001 From: AleksOBM Date: Sun, 9 Nov 2025 21:36:24 +0300 Subject: [PATCH 1/3] =?UTF-8?q?=D0=9F=D0=BE=D0=B4=D0=B3=D0=BE=D1=82=D0=BE?= =?UTF-8?q?=D0=B2=D0=B8=D1=82=D1=8C=20=D0=BF=D1=80=D0=BE=D0=B5=D0=BA=D1=82?= =?UTF-8?q?=20=D0=BA=20=D0=BF=D0=B5=D1=80=D0=B2=D0=BE=D0=BC=D1=83=20=D1=80?= =?UTF-8?q?=D0=B5=D0=B2=D1=8C=D1=8E=20=D0=B2=D0=BE=D1=81=D1=8C=D0=BC=D0=BE?= =?UTF-8?q?=D0=B3=D0=BE=20=D1=81=D0=BF=D1=80=D0=B8=D0=BD=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Main.java | 372 +++++++++++++------- src/data/Epic.java | 27 +- src/data/Subtask.java | 30 +- src/data/Task.java | 70 +++- src/manager/FileBackedTaskManager.java | 165 +++++---- src/manager/InMemoryTaskManager.java | 268 ++++++++++---- src/manager/TaskManager.java | 11 +- test/data/EpicTest.java | 8 +- test/data/SubtaskTest.java | 7 +- test/data/TaskTest.java | 29 +- test/manager/FileBackedTaskManagerTest.java | 332 ++--------------- test/manager/HistoryManagerTest.java | 14 +- test/manager/TaskManagerTest.java | 97 ++++- 13 files changed, 830 insertions(+), 600 deletions(-) diff --git a/src/Main.java b/src/Main.java index 9203d7d..a2d09e0 100644 --- a/src/Main.java +++ b/src/Main.java @@ -6,6 +6,8 @@ import manager.TaskManagerType; import java.io.File; +import java.time.Duration; +import java.time.LocalDateTime; import java.util.List; import java.util.Scanner; @@ -13,141 +15,134 @@ public class Main { static TaskManager manager = Managers.getTaskManager(TaskManagerType.FILE_BACKED); static Scanner scanner = new Scanner(System.in); - static final int ms = 1500; + static final int MS = 1500; public static void main(String[] args) { -// insertStartData(); -// -// waitSecond(ms); -// -// printListAll(); -// -// testSubtasks(); -// -// waitSecond(ms); -// -// printListAll(); -// -// printHistory(); -// -// testEpics(); -// -// waitSecond(ms); -// -// printHistory(); -// -// testTasks(); -// -// printHistory(); - start(); } /// Ввести исходные данные static void insertStartData() { - Task task1 = manager.setTask(new Task( + Task task1 = manager.addTask(new Task( null, "Познакомиться с девушкой", - "Она должна понравиться моей маме" + "Она должна понравиться моей маме", + null, + LocalDateTime.of(2025, 11, 07, 12, 0), + Duration.ofMinutes(135) )); - waitSecond(ms); + waitSecond(MS); - Task task2 = manager.setTask(new Task( + Task task2 = manager.addTask(new Task( null, "Сводить девушку на свидание", "По правилам этикета нужно заплатить за нее в ресторане" )); - waitSecond(ms); + waitSecond(MS); - Task task3 = manager.setTask(new Task( + Task task3 = manager.addTask(new Task( null, "Подарить девушке цветы", "Не дарить хризантемы" )); - waitSecond(ms); + waitSecond(MS); - Epic epic1 = manager.setEpic(new Epic( + Epic epic1 = manager.addEpic(new Epic( null, "Найти девушку для знакомства", "Использовать различные способы поиска, для ускорения процесса" )); - waitSecond(ms); + waitSecond(MS); - Subtask subtask1 = manager.setSubtask(new Subtask( + Subtask subtask1 = manager.addSubtask(new Subtask( null, "Поиск на сайтах знакомств", null, null, - epic1.getId() + epic1.getId(), + null, + null )); - waitSecond(ms); + waitSecond(MS); - Subtask subtask2 = manager.setSubtask(new Subtask( + Subtask subtask2 = manager.addSubtask(new Subtask( null, "Поиск через знакомых", null, null, - epic1.getId() + epic1.getId(), + null, + null )); - waitSecond(ms); + waitSecond(MS); - Subtask subtask3 = manager.setSubtask(new Subtask( + Subtask subtask3 = manager.addSubtask(new Subtask( null, "Поиск в литературном клубе", null, null, - epic1.getId() + epic1.getId(), + null, + null )); - waitSecond(ms); + waitSecond(MS); - Epic epic2 = manager.setEpic(new Epic( + Epic epic2 = manager.addEpic(new Epic( null, "Оптимизировать поиск девушки", "Получить результат быстрее и с меньшими затратами" )); - waitSecond(ms); + waitSecond(MS); - Subtask subtask4 = manager.setSubtask(new Subtask( + Subtask subtask4 = manager.addSubtask(new Subtask( null, "Выбрать наиболее оптимальный способ поиска", null, null, - epic2.getId() + epic2.getId(), + null, + null )); - waitSecond(ms); + waitSecond(MS); - Epic epic3 = manager.setEpic(new Epic( + Epic epic3 = manager.addEpic(new Epic( null, "Создать систему оценки способов поиска", "Без статистики не обойтись" )); - waitSecond(ms); + waitSecond(MS); - Subtask subtask5 = manager.setSubtask(new Subtask( + Subtask subtask5 = manager.addSubtask(new Subtask( null, "Задать параметры системы оценки", "Возможно бальная система", null, - epic3.getId() + epic3.getId(), + null, + null )); - waitSecond(ms); + waitSecond(MS); - Subtask subtask6 = manager.setSubtask(new Subtask( + Subtask subtask6 = manager.addSubtask(new Subtask( null, "Собрать статистику способов", null, null, - epic3.getId() + epic3.getId(), + null, + null )); - waitSecond(ms); + waitSecond(MS); - Subtask subtask7 = manager.setSubtask(new Subtask( + Subtask subtask7 = manager.addSubtask(new Subtask( null, "Провести статистическое исследование", null, null, - epic3.getId() + epic3.getId(), + null, + null )); } @@ -184,12 +179,12 @@ static void testTasks() { Status.IN_PROGRESS ))); System.out.println("\n" + "-".repeat(20) + "\n"); - waitSecond(ms); + waitSecond(MS); System.out.println("Удаление задачи по идентификатору:" + "\n"); System.out.println(removeTask(2)); System.out.println("\n" + "-".repeat(20) + "\n"); - waitSecond(ms); + waitSecond(MS); System.out.println("Получение списка всех задач:" + "\n"); System.out.println(getAllTasksList()); @@ -212,10 +207,12 @@ static void testSubtasks() { null, null, Status.DONE, + null, + null, null ))); System.out.println("\n" + "-".repeat(20) + "\n"); - waitSecond(ms); + waitSecond(MS); System.out.println("Изменение статуса подзадачи:" + "\n"); System.out.println(manager.updateSubtask(new Subtask( @@ -223,15 +220,17 @@ static void testSubtasks() { null, null, Status.IN_PROGRESS, + null, + null, null ))); System.out.println("\n" + "-".repeat(20) + "\n"); - waitSecond(ms); + waitSecond(MS); System.out.println("Удаление подзадачи по идентификатору:" + "\n"); System.out.println(removeSubtask(6)); System.out.println("\n" + "-".repeat(20) + "\n"); - waitSecond(ms); + waitSecond(MS); System.out.println("Получение списка всех подзадач:" + "\n"); System.out.println(getAllSubtasksList()); @@ -255,12 +254,12 @@ static void testEpics() { "" ))); System.out.println("\n" + "-".repeat(20) + "\n"); - waitSecond(ms); + waitSecond(MS); System.out.println("Удаление эпика по идентификатору:" + "\n"); System.out.println(removeEpic(8)); System.out.println("\n" + "-".repeat(20) + "\n"); - waitSecond(ms); + waitSecond(MS); System.out.println("Получение эпика со списком подзадач:" + "\n"); System.out.println(getEpicWithSubtasksList(10)); @@ -398,7 +397,6 @@ static StringBuilder removeEpicWithSubtasks(int epicId) { /// Удаление всех подзадач эпика static StringBuilder removeAllEpicSubtasks(int epicId) { StringBuilder list = new StringBuilder(); - Epic epic = manager.getEpic(epicId); List removedSudtasks = manager.getAllSubTasksByEpic(epicId); manager.removeAllSubtasksByEpic(epicId); @@ -451,12 +449,14 @@ static void printHelp() { System.out.println("\n" + "-".repeat(20) + "\n"); System.out.println("Обновить данные --> update"); System.out.println("Поиск по id --> find"); - System.out.println("Вывести весь список --> print-all"); + System.out.println("Вывести весь список --> print"); System.out.println("Добавить задачу --> add-task"); System.out.println("Добавить эпик --> add-epic"); System.out.println("Добавить подзадачу --> add-sub"); System.out.println("Удалить --> remove"); - System.out.println("Изменить статус (задача или подзадача) --> set-st"); + System.out.println("Изменить статус (задача или подзадача) --> set-status"); + System.out.println("Изменить дату старта --> set-time"); + System.out.println("Изменить срок (в минутах) --> set-dur"); System.out.println("Показать историю --> history"); System.out.println("Вывести инструкцию --> help"); System.out.println("Выход --> exit"); @@ -487,7 +487,7 @@ static void start() { } System.out.println("Данные загружены."); break; - case "print-all": + case "print": printListAll(); break; case "history": @@ -504,65 +504,27 @@ static void start() { System.out.println("\n" + "-".repeat(20) + "\n"); break; case "add-task": - System.out.println("\n" + "-".repeat(20) + "\n"); - System.out.print("Заголовок (без пробелов) --> "); - String taskTitle = scanner.next(); - System.out.print("Описание (без пробелов) --> "); - String taskDescription = scanner.next(); - Task newTsk = new Task(null, taskTitle, taskDescription, null); - manager.setTask(newTsk); - System.out.println("Задача добавлена"); - System.out.println(newTsk); - System.out.println("\n" + "-".repeat(20) + "\n"); + addTask(); break; case "add-epic": - System.out.println("\n" + "-".repeat(20) + "\n"); - System.out.print("Заголовок (без пробелов) --> "); - String epicTitle = scanner.next(); - System.out.print("Описание (без пробелов) --> "); - String epicDescription = scanner.next(); - Epic newEp = new Epic(null, epicTitle, epicDescription); - manager.setEpic(newEp); - System.out.println("Эпик добавлен"); - System.out.println(newEp); - System.out.println("\n" + "-".repeat(20) + "\n"); + addEpic(); break; case "add-sub": addSub(); break; - case "set-st": - setST(); + case "set-status": + setSt(); + break; + case "set-time": + setTime(); + break; + case "set-dur": + setDur(); break; case "remove": - Type rmType = null; - while (true) { - System.out.println("\n" + "-".repeat(20) + "\n"); - System.out.println("Назад --> 0"); - System.out.print("id --> "); - int rmId = Integer.parseInt(scanner.next()); - if (rmId == 0) { - System.out.println("\n" + "-".repeat(20) + "\n"); - break; - } - if (manager.getWithoutHistory(rmId) == null) { - System.out.println("Такого id не существует"); - continue; - } - try { - rmType = manager.getType(rmId); - } catch (NullPointerException e) { - System.out.println("Такого id не существует"); - continue; - } - switch (rmType) { - case TASK -> System.out.println(removeTask(rmId)); - case EPIC -> System.out.println(removeEpic(rmId)); - case SUBTASK -> System.out.println(removeSubtask(rmId)); - } - System.out.println("\n" + "-".repeat(20) + "\n"); - break; - } + remove(); break; + default: System.out.println("Такой команды нет"); System.out.println("\n" + "-".repeat(20) + "\n"); @@ -589,7 +551,171 @@ static void setStatus(Task taskObject, Status status) { } } - static void setST() { + static void setDur() { + Type type = null; + while (true) { + System.out.println("\n" + "-".repeat(20) + "\n"); + System.out.println("Назад --> 0"); + System.out.print("id --> "); + int id = 0; + try { + id = Integer.parseInt(scanner.next()); + scanner.nextLine(); + } catch (NumberFormatException e) { + System.out.println("Такого id не существует"); + continue; + } + if (id == 0) { + System.out.println("\n" + "-".repeat(20) + "\n"); + break; + } + if (manager.getWithoutHistory(id) == null) { + System.out.println("Такого id не существует"); + continue; + } + try { + type = manager.getType(id); + } catch (NullPointerException e) { + System.out.println("Такого id не существует"); + continue; + } + if (manager.getType(id) == Type.EPIC) { + System.out.println("Это id эпика - срок автоматический"); + continue; + } + + System.out.print("Минуты --> "); + int dur = Integer.parseInt(scanner.next()); + Duration duration = Duration.ofMinutes(dur); + System.out.println("\n" + "-".repeat(20) + "\n"); + + switch (type) { + case TASK -> System.out.println(manager.updateTask(new Task(id, null, null, null, null, duration))); + case SUBTASK -> + System.out.println(manager.updateSubtask((new Subtask(id, null, null, null, null, null, duration)))); + } + System.out.println("\n" + "-".repeat(20) + "\n"); + break; + } + } + + static void setTime() { + Type type = null; + while (true) { + System.out.println("\n" + "-".repeat(20) + "\n"); + System.out.println("Назад --> 0"); + System.out.print("id --> "); + int id; + try { + id = Integer.parseInt(scanner.next()); + scanner.nextLine(); + } catch (NumberFormatException e) { + System.out.println("Такого id не существует"); + continue; + } + if (id == 0) { + System.out.println("\n" + "-".repeat(20) + "\n"); + break; + } + if (manager.getWithoutHistory(id) == null) { + System.out.println("Такого id не существует"); + continue; + } + try { + type = manager.getType(id); + } catch (NullPointerException e) { + System.out.println("Такого id не существует"); + continue; + } + if (manager.getType(id) == Type.EPIC) { + System.out.println("Это id эпика - время автоматическиое"); + System.out.println("\n" + "-".repeat(20) + "\n"); + return; + } + + System.out.print("Год --> "); + int year = Integer.parseInt(scanner.next()); + System.out.print("Месяц --> "); + int month = Integer.parseInt(scanner.next()); + System.out.print("День --> "); + int day = Integer.parseInt(scanner.next()); + System.out.print("Часы --> "); + int hours = Integer.parseInt(scanner.next()); + System.out.print("Минуты --> "); + int minutes = Integer.parseInt(scanner.next()); + LocalDateTime dateTime = LocalDateTime.of(year, month, day, hours, minutes); + System.out.println("\n" + "-".repeat(20) + "\n"); + + switch (type) { + case TASK -> System.out.println(manager.updateTask(new Task(id, null, null, null, dateTime, null))); + case SUBTASK -> + System.out.println(manager.updateSubtask((new Subtask(id, null, null, null, null, dateTime, null)))); + } + System.out.println("\n" + "-".repeat(20) + "\n"); + break; + } + + } + + static void remove() { + Type rmType = null; + while (true) { + System.out.println("\n" + "-".repeat(20) + "\n"); + System.out.println("Назад --> 0"); + System.out.print("id --> "); + int rmId = Integer.parseInt(scanner.next()); + if (rmId == 0) { + System.out.println("\n" + "-".repeat(20) + "\n"); + break; + } + if (manager.getWithoutHistory(rmId) == null) { + System.out.println("Такого id не существует"); + continue; + } + try { + rmType = manager.getType(rmId); + } catch (NullPointerException e) { + System.out.println("Такого id не существует"); + continue; + } + System.out.println("\n" + "-".repeat(20) + "\n"); + switch (rmType) { + case TASK -> System.out.println(removeTask(rmId)); + case EPIC -> System.out.println(removeEpic(rmId)); + case SUBTASK -> System.out.println(removeSubtask(rmId)); + } + System.out.println("\n" + "-".repeat(20) + "\n"); + break; + } + } + + static void addTask() { + System.out.println("\n" + "-".repeat(20) + "\n"); + System.out.print("Заголовок (без пробелов) --> "); + String taskTitle = scanner.next(); + System.out.print("Описание (без пробелов) --> "); + String taskDescription = scanner.next(); + Task newTsk = new Task(null, taskTitle, taskDescription, null); + manager.addTask(newTsk); + System.out.println("Задача добавлена"); + System.out.println(newTsk); + System.out.println("\n" + "-".repeat(20) + "\n"); + } + + static void addEpic() { + System.out.println("\n" + "-".repeat(20) + "\n"); + System.out.print("Заголовок (без пробелов) --> "); + String epicTitle = scanner.next(); + System.out.print("Описание (без пробелов) --> "); + String epicDescription = scanner.next(); + Epic newEp = new Epic(null, epicTitle, epicDescription); + manager.addEpic(newEp); + System.out.println("Эпик добавлен"); + System.out.println(newEp); + System.out.println("\n" + "-".repeat(20) + "\n"); + } + + static void setSt() { System.out.println("\n" + "-".repeat(20) + "\n"); System.out.print("id --> "); int statusesId = Integer.parseInt(scanner.next()); @@ -650,7 +776,7 @@ static void addSub() { return; } - if (manager.getEpic(epiccId) == null) { + if (manager.getWithoutHistory(epiccId) == null) { System.out.println("Такого id эпика нет"); continue; } @@ -660,8 +786,8 @@ static void addSub() { String subtaskTitle = scanner.next(); System.out.print("Описание (без пробелов) --> "); String subtaskDescription = scanner.next(); - Subtask newSub = new Subtask(null, subtaskTitle, subtaskDescription, null, epiccId); - manager.setSubtask(newSub); + Subtask newSub = new Subtask(null, subtaskTitle, subtaskDescription, null, epiccId, null, null); + manager.addSubtask(newSub); System.out.println("Подзадача добавлена"); System.out.println(newSub); System.out.println("\n" + "-".repeat(20) + "\n"); diff --git a/src/data/Epic.java b/src/data/Epic.java index daee0f0..2c4a804 100644 --- a/src/data/Epic.java +++ b/src/data/Epic.java @@ -1,11 +1,14 @@ package data; +import java.time.Duration; +import java.time.LocalDateTime; import java.util.ArrayList; +import java.util.List; public class Epic extends Task { /// Хранилище ID подзадач - private final ArrayList subtaskIds = new ArrayList<>(); + private ArrayList subtaskIds = new ArrayList<>(); /// Конструктор public Epic(Integer id, String title, String description) { @@ -13,7 +16,7 @@ public Epic(Integer id, String title, String description) { } /// Получение всех ID подзадач - public ArrayList getSubtaskIds() { + public List getSubtaskIds() { return new ArrayList<>(subtaskIds); } @@ -33,16 +36,29 @@ public void removeSubtaskId(Integer subtaskId) { subtaskIds.remove(subtaskId); } + @Override + public void setDuration(Duration duration) { + this.duration = duration; + } + + public void setEndTime(LocalDateTime endTime) { + this.endTime = endTime; + } + @Override public Type getType() { return Type.EPIC; } - @Override /// Получение копии объекта + @Override public Epic getCopy() { Epic newEpic = new Epic(this.id, this.title, this.description); - newEpic.setStatus(this.status); + newEpic.status = this.status; + newEpic.startTime = this.startTime; + newEpic.duration = this.duration; + newEpic.endTime = this.endTime; + newEpic.subtaskIds = this.subtaskIds; return newEpic; } @@ -53,6 +69,9 @@ public String toString() { ", title='" + super.getTitle() + '\'' + ", description='" + super.getDescription() + '\'' + ", status='" + super.getStatus() + '\'' + + ", startTime='" + (startTime == null ? "null" : startTime.format(DATE_TIME_FORMATTER)) + '\'' + + ", endTime='" + (endTime == null ? "null" : endTime.format(DATE_TIME_FORMATTER)) + '\'' + + ", duration='" + (duration == null ? "null" : duration.toMinutes()) + '\'' + '}'; } } diff --git a/src/data/Subtask.java b/src/data/Subtask.java index 1de266e..e8963a5 100644 --- a/src/data/Subtask.java +++ b/src/data/Subtask.java @@ -1,14 +1,27 @@ package data; +import java.time.Duration; +import java.time.LocalDateTime; + public class Subtask extends Task { /// ID целевого эпика private final Integer epicId; /// Конструктор - public Subtask(Integer id, String title, String description, Status status, Integer epicId) { + public Subtask( + Integer id, + String title, + String description, + Status status, + Integer epicId, + LocalDateTime startTime, + Duration duration + ) { super(id, title, description, status); this.epicId = epicId; + this.startTime = startTime; + setDuration(duration); } /// Получение ID целевого эпика @@ -21,10 +34,18 @@ public Type getType() { return Type.SUBTASK; } - @Override /// Получение копии объекта + @Override public Subtask getCopy() { - return new Subtask(this.id, this.title, this.description, this.status, this.epicId); + return new Subtask( + this.id, + this.title, + this.description, + this.status, + this.epicId, + this.startTime, + this.duration + ); } @Override @@ -35,6 +56,9 @@ public String toString() { ", title='" + super.getTitle() + '\'' + ", description='" + super.getDescription() + '\'' + ", status='" + super.getStatus() + '\'' + + ", startTime='" + (startTime == null ? "null" : startTime.format(DATE_TIME_FORMATTER)) + '\'' + + ", endTime='" + (endTime == null ? "null" : endTime.format(DATE_TIME_FORMATTER)) + '\'' + + ", duration='" + (duration == null ? "null" : duration.toMinutes()) + '\'' + '}'; } } diff --git a/src/data/Task.java b/src/data/Task.java index e960db1..7437e26 100644 --- a/src/data/Task.java +++ b/src/data/Task.java @@ -1,5 +1,8 @@ package data; +import java.time.Duration; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.Objects; public class Task implements Comparable { @@ -16,6 +19,17 @@ public class Task implements Comparable { /// Статус задачи protected Status status; + /// Дата и время, когда предполагается приступить к выполнению задачи + protected LocalDateTime startTime; + + /// Продолжительность задачи в минутах + protected Duration duration = Duration.ZERO; + + /// Дата и время завершения задачи + protected LocalDateTime endTime; + + protected static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"); + /// Конструктор 1 public Task(Integer id, String title, String description) { this.id = id; @@ -31,6 +45,23 @@ public Task(Integer id, String title, String description, Status status) { this.status = status; } + /// Конструктор 3 + public Task( + Integer id, + String title, + String description, + Status status, + LocalDateTime startTime, + Duration duration + ) { + this.id = id; + this.title = title; + this.description = description; + this.status = status; + this.startTime = startTime; + setDuration(duration); + } + /// Получение ID задачи public Integer getId() { return id; @@ -71,6 +102,38 @@ public void setStatus(Status status) { this.status = status; } + /// Дата и время начала выполнения задачи + public LocalDateTime getStartTime() { + return startTime; + } + + /// Внесение времени начала выполнения + public void setStartTime(LocalDateTime startTime) { + this.startTime = startTime; + if (startTime != null) { + endTime = this.startTime.plus(this.duration); + } + } + + /// Время выполнения задачи + public Duration getDuration() { + return duration; + } + + /// Внесение продолжительности задачи + public void setDuration(Duration duration) { + if (duration != null) { + this.duration = duration; + } + if (startTime != null) { + endTime = startTime.plus(this.duration); + } + } + + public LocalDateTime getEndTime() { + return endTime; + } + /// Получение типа объекта public Type getType() { return Type.TASK; @@ -78,7 +141,7 @@ public Type getType() { /// Получение копии объекта public Task getCopy() { - return new Task(this.id, this.title, this.description, this.status); + return new Task(this.id, this.title, this.description, this.status, this.startTime, this.duration); } @Override @@ -104,7 +167,10 @@ public String toString() { "id='" + id + '\'' + ", title='" + title + '\'' + ", description='" + description + '\'' + - ", status=" + status + + ", status='" + status + '\'' + + ", startTime='" + (startTime == null ? "null" : startTime.format(DATE_TIME_FORMATTER)) + '\'' + + ", endTime='" + (endTime == null ? "null" : endTime.format(DATE_TIME_FORMATTER)) + '\'' + + ", duration='" + (duration == null ? "null" : duration.toMinutes()) + '\'' + '}'; } } \ No newline at end of file diff --git a/src/manager/FileBackedTaskManager.java b/src/manager/FileBackedTaskManager.java index e6e8ae3..ed3e6ce 100644 --- a/src/manager/FileBackedTaskManager.java +++ b/src/manager/FileBackedTaskManager.java @@ -8,6 +8,9 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; +import java.time.Duration; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.*; public class FileBackedTaskManager extends InMemoryTaskManager { @@ -16,17 +19,21 @@ public class FileBackedTaskManager extends InMemoryTaskManager { private static FileBackedTaskManager instance; /// Параметры CSV файла - private static final String TABLE_HEADER = "id,type,name,status,description,epic"; + private static final String TABLE_HEADER = "start, end, duration, id,type,name,status,description,epic"; private static final Charset CHARSET = StandardCharsets.UTF_8; + private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"); private static final String DELIMITER = ","; private static final String QUOTE = "\""; /// Максимальный id private int maxId = 0; - /// Буфер чтения + /// Буфер чтения строк private List fileToList; + /// Буфер чтения объектов + private final TreeSet objectToList = new TreeSet<>(Task::compareTo); + /// Конструктор FileBackedTaskManager(File newFile) { try { @@ -70,50 +77,69 @@ private void loadFile(File newFile) throws ManagerSaveException { } } - /// Перезагрузить данные из файла + /// Загрузить объекты из буфера чтения в буфер объектов + private void loadObjectBuffer() throws ManagerSaveException { + int rowNumber = 1; + while (rowNumber < fileToList.size()) { + Task taskObject = getDataObjectFromString(fileToList.get(rowNumber)); + + if (taskObject == null) { + throw new ManagerSaveException("Ошибка преобразования при считывании из файла"); + } + + int taskId = taskObject.getId(); + if (maxId < taskId) { + maxId = taskId; + } + + objectToList.add(taskObject); + + rowNumber++; + } + } + + /// Перезагрузить данные из файла2 public void updateDataFromFile() throws ManagerSaveException { + loadObjectBuffer(); + super.removeAllTasks(); super.removeAllEpics(); - if (fileToList.size() > 1) { - setMaxId(); + if (objectToList.isEmpty()) { + return; } - int rowNumber = 1; - while (rowNumber < fileToList.size()) { - Task taskObject = getDataObjectFromString(fileToList.get(rowNumber)); + for (Task taskObject : objectToList) { + switch (taskObject.getType()) { + case TASK -> idToTask.put(taskObject.getId(), taskObject); + case EPIC -> idToEpic.put(taskObject.getId(), (Epic) taskObject); + case SUBTASK -> { + Subtask subtask = (Subtask) taskObject; + int subtaskId = subtask.getId(); + int epicId = subtask.getEpicId(); + + if (epicId == 0) { + System.out.println("Подзадача c id = " + subtaskId + " не привязана к эпику. Epic id = 0."); + continue; + } - if (taskObject == null) { - return; - } + Epic epic = idToEpic.get(epicId); - maxId = taskObject.getId(); + if (epic == null) { + System.out.println("У подзадачи c id = " + subtask.getId() + + " отсутствует эпик c id = " + epicId + "."); + continue; + } - if (taskObject instanceof Epic epic) { - idToEpic.put(maxId, epic); - } else if (taskObject instanceof Subtask subtask) { - int epicId = subtask.getEpicId(); - if (epicId == 0) { - System.out.println("Подзадача c id = " + subtask.getId() + " не привязана к эпику. Epic id = 0."); - rowNumber++; - continue; - } - Epic thisEpic = idToEpic.get(epicId); - if (thisEpic == null) { - System.out.println("У подзадачи c id = " + subtask.getId() - + " отсутствует эпик c id = " + epicId + "."); - rowNumber++; - continue; - } - thisEpic.addSubtaskId(maxId); + epic.addSubtaskId(subtaskId); - idToSubtask.put(maxId, subtask); - updateEpicsStatus(thisEpic.getId()); - } else { - idToTask.put(maxId, taskObject); + idToSubtask.put(subtaskId, subtask); + updateEpicsStatus(epicId); + updateEpicTime(epic); + } + default -> throw new ManagerSaveException("Не верный тип задачи в таблице"); } - rowNumber++; } if (maxId != 0) { @@ -123,7 +149,6 @@ public void updateDataFromFile() throws ManagerSaveException { counter = maxId + 1; } } - } /// Очистить содержимое файла, добавить шапку таблицы @@ -140,7 +165,11 @@ private void resetFile() throws ManagerSaveException { /// Выгрузить все данные в файл private void save() throws ManagerSaveException { - List all = getAll(); + List all = getAll().stream() + .sorted(Comparator.comparing(Task::getStartTime, + Comparator.nullsLast(Comparator.naturalOrder()))) + .toList(); + if (all.isEmpty()) { resetFile(); readFileToList(); @@ -161,13 +190,6 @@ private void save() throws ManagerSaveException { } } - private void setMaxId() { - Task lsatTaskObject = getDataObjectFromString(fileToList.getLast()); - if (lsatTaskObject != null) { - maxId = lsatTaskObject.getId(); - } - } - /// Прочитать содержимое файла в буфер private void readFileToList() throws ManagerSaveException { try { @@ -180,6 +202,14 @@ private void readFileToList() throws ManagerSaveException { /// Получить строку для записи в файл из объекта private String getStringToFileSave(Task taskObject) { + String startTime = taskObject.getStartTime() != null ? + taskObject.getStartTime().format(DATE_TIME_FORMATTER) : "null"; + + String endTime = taskObject.getEndTime() != null ? + taskObject.getEndTime().format(DATE_TIME_FORMATTER) : "null"; + + long duration = taskObject.getDuration().toMinutes(); + Integer id = taskObject.getId(); Type type = getType(id); @@ -193,14 +223,17 @@ private String getStringToFileSave(Task taskObject) { String status = taskObject.getStatus() != null ? taskObject.getStatus().toString() : "null"; - String epic = type.equals(Type.SUBTASK) ? String.valueOf(((Subtask) taskObject).getEpicId()) : ""; + String epicId = type.equals(Type.SUBTASK) ? String.valueOf(((Subtask) taskObject).getEpicId()) : ""; - return id + DELIMITER + + return startTime + DELIMITER + + endTime + DELIMITER + + duration + DELIMITER + + id + DELIMITER + type + DELIMITER + title + DELIMITER + status + DELIMITER + description + DELIMITER + - epic; + epicId; } /// Получить объект данных из строки @@ -211,31 +244,36 @@ private Task getDataObjectFromString(String stringFromFile) { } String[] splitStr = stringFromFile.split(DELIMITER + "(?!\\s)"); - if (splitStr.length < 5) { + if (splitStr.length < 8 || splitStr.length > 9) { throw new ManagerSaveException("Не поддерживаемый формат строки"); } - Integer id = Integer.parseInt(splitStr[0]); - Type type = Type.valueOf(splitStr[1]); - String title = splitStr[2].replace(QUOTE, ""); - String description = splitStr[4].replace(QUOTE, ""); - Status status = !splitStr[3].equals("null") ? Status.valueOf(splitStr[3]) : null; + LocalDateTime startTime = splitStr[0].equals("null") ? null : + LocalDateTime.parse(splitStr[0], DATE_TIME_FORMATTER); + Duration duration = Duration.ofMinutes(Integer.parseInt(splitStr[2])); + Integer id = Integer.parseInt(splitStr[3]); + Type type = Type.valueOf(splitStr[4]); + String title = splitStr[5].replace(QUOTE, ""); + String description = splitStr[7].replace(QUOTE, ""); + Status status = !splitStr[6].equals("null") ? Status.valueOf(splitStr[6]) : null; switch (type) { case TASK: - return new Task(id, title, description, status); + return new Task(id, title, description, status, startTime, duration); case EPIC: Epic newEpic = new Epic(id, title, description); + newEpic.setStartTime(startTime); newEpic.setStatus(status); + newEpic.setDuration(duration); return newEpic; case SUBTASK: int epicId = 0; try { - epicId = Integer.parseInt(splitStr[5]); + epicId = Integer.parseInt(splitStr[8]); } catch (IndexOutOfBoundsException e) { - // epicId = 0 + // epicId == 0 } - return new Subtask(id, title, description, status, epicId); + return new Subtask(id, title, description, status, epicId, startTime, duration); default: return null; } @@ -246,26 +284,23 @@ private Task getDataObjectFromString(String stringFromFile) { } @Override - public Task setTask(Task newTask) { - Task result = super.setTask(newTask); + public Task addTask(Task newTask) { + Task result = super.addTask(newTask); save(); - setMaxId(); return result; } @Override - public Epic setEpic(Epic newEpic) { - Epic result = super.setEpic(newEpic); + public Epic addEpic(Epic newEpic) { + Epic result = super.addEpic(newEpic); save(); - setMaxId(); return result; } @Override - public Subtask setSubtask(Subtask newSubtask) { - Subtask result = super.setSubtask(newSubtask); + public Subtask addSubtask(Subtask newSubtask) { + Subtask result = super.addSubtask(newSubtask); save(); - setMaxId(); return result; } diff --git a/src/manager/InMemoryTaskManager.java b/src/manager/InMemoryTaskManager.java index 49be0a3..2fa4094 100644 --- a/src/manager/InMemoryTaskManager.java +++ b/src/manager/InMemoryTaskManager.java @@ -2,7 +2,9 @@ import data.*; +import java.time.Duration; import java.util.*; +import java.util.stream.Stream; class InMemoryTaskManager implements TaskManager { @@ -18,6 +20,9 @@ class InMemoryTaskManager implements TaskManager { /// Менеджер истории просмотров protected final HistoryManager historyManager = Managers.getHistoryManager(); + /// Хранилище всех тасок в порядке приоритета + protected Set prioritizedTasks = new TreeSet<>(Comparator.comparing(Task::getStartTime)); + /// Счетчик ID protected int counter = 1; @@ -26,6 +31,7 @@ private int generateNewId() { return counter++; } + /// Получить объект по id с сохранением в историю @Override public Task get(int id) { if (idToTask.containsKey(id)) { @@ -45,6 +51,7 @@ public Task get(int id) { return null; } + /// Получить объект по id без сохранения в историю @Override public Task getWithoutHistory(int id) { if (idToTask.containsKey(id)) { @@ -58,6 +65,7 @@ public Task getWithoutHistory(int id) { return null; } + /// Получить тип объекта по id @Override public Type getType(int id) { Task taskObject = getWithoutHistory(id); @@ -67,78 +75,90 @@ public Type getType(int id) { return taskObject.getType(); } - public List getAll() { + /// Получить все объекты из всех хранилищ + public TreeSet getAll() { if (counter == 1) { - return new ArrayList<>(); - } - TreeSet allTasks = new TreeSet<>(); - if (!idToTask.isEmpty()) { - allTasks.addAll(getAllTasks()); - } - if (!idToEpic.isEmpty()) { - allTasks.addAll(getAllEpics()); - } - if (!idToSubtask.isEmpty()) { - allTasks.addAll(getAllSubtasks()); + return new TreeSet<>(); } - return new ArrayList<>(allTasks); + List all = Stream.of(getAllTasks(), getAllEpics(), getAllSubtasks()) + .flatMap(Collection::stream) + .toList(); + + return new TreeSet<>(all); } /// Внесение задачи в хранилище по объекту @Override - public Task setTask(Task newTask) { + public Task addTask(Task newTask) { if (newTask.getType() != Type.TASK) { System.out.println("Ошибка добавления новой задачи - неверные входные данные."); return null; } + + if (!findIntersections(newTask).isEmpty()) { + System.out.println("Ошибка добавления новой задачи - задача пересекается по срокам с уже существующими."); + return null; + } newTask.setId(generateNewId()); Task savedTask = newTask.getCopy(); idToTask.put(savedTask.getId(), savedTask); + if (savedTask.getStartTime() != null) { + prioritizedTasks.add(savedTask); + } return newTask; } /// Внесение эпика в хранилище по объекту @Override - public Epic setEpic(Epic newEpic) { + public Epic addEpic(Epic newEpic) { if (newEpic.getType() != Type.EPIC) { System.out.println("Ошибка добавления нового эпика - неверные входные данные."); return null; } + + updateEpicTime(newEpic); newEpic.setId(generateNewId()); newEpic.setStatus(Status.NEW); - Epic savedEpic = new Epic(newEpic.getId(), newEpic.getTitle(), newEpic.getDescription()); + Epic savedEpic = newEpic.getCopy(); idToEpic.put(savedEpic.getId(), savedEpic); - savedEpic.setStatus(Status.NEW); return newEpic; } /// Внесение подзадачи в хранилище по объекту @Override - public Subtask setSubtask(Subtask newSubtask) { + public Subtask addSubtask(Subtask newSubtask) { Integer epicId = newSubtask.getEpicId(); if (epicId == null || idToEpic.get(epicId) == null || newSubtask.getType() != Type.SUBTASK) { System.out.println("Ошибка добавления новой подзадачи - неверные входные данные."); return null; } + if (!findIntersections(newSubtask).isEmpty()) { + System.out.println("Ошибка добавления новой подзадачи - подзадача пересекается по срокам с уже существующими."); + return null; + } + Integer subtaskId = generateNewId(); newSubtask.setId(subtaskId); - Subtask savedTask = newSubtask.getCopy(); + Subtask savedSubtask = newSubtask.getCopy(); Epic epic = idToEpic.get(epicId); epic.addSubtaskId(subtaskId); - - idToSubtask.put(subtaskId, savedTask); + idToSubtask.put(subtaskId, savedSubtask); + updateEpicTime(epic); updateEpicsStatus(epic.getId()); + if (savedSubtask.getStartTime() != null) { + prioritizedTasks.add(savedSubtask); + } return newSubtask; } - /// Получение задачи по ID + /// Получение задачи по ID с сохранением в историю @Override public Task getTask(int taskID) { Task newTask = copyOfTask(taskID); @@ -149,7 +169,7 @@ public Task getTask(int taskID) { return newTask; } - /// Получение эпика по ID + /// Получение эпика по ID с сохранением в историю @Override public Epic getEpic(int epicID) { Epic newEpic = copyOfEpic(epicID); @@ -160,7 +180,7 @@ public Epic getEpic(int epicID) { return newEpic; } - /// Получение подзадачи по ID + /// Получение подзадачи по ID с сохранением в историю @Override public Subtask getSubtask(int subTaskID) { Subtask newSubtask = copyOfSubtask(subTaskID); @@ -176,8 +196,7 @@ private Task copyOfTask(int taskId) { if (!idToTask.containsKey(taskId)) { return null; } - Task task = idToTask.get(taskId); - return task.getCopy(); + return idToTask.get(taskId).getCopy(); } /// Получение копии эпика по ID @@ -185,12 +204,7 @@ private Epic copyOfEpic(int epicId) { if (!idToEpic.containsKey(epicId)) { return null; } - Epic epic = idToEpic.get(epicId); - Epic newEpic = epic.getCopy(); - for (int subtaskId : epic.getSubtaskIds()) { - newEpic.addSubtaskId(subtaskId); - } - return newEpic; + return idToEpic.get(epicId).getCopy(); } /// Получение копии подзадачи по ID @@ -198,52 +212,37 @@ private Subtask copyOfSubtask(int subtaskId) { if (!idToSubtask.containsKey(subtaskId)) { return null; } - Subtask subtask = idToSubtask.get(subtaskId); - return subtask.getCopy(); + return idToSubtask.get(subtaskId).getCopy(); } /// Получение списка всех задач в виде объекта @Override - public ArrayList getAllTasks() { - ArrayList allTasks = new ArrayList<>(idToTask.values()); - allTasks.sort(Task::compareTo); - return allTasks; + public List getAllTasks() { + return idToTask.values().stream().sorted(Task::compareTo).toList(); } /// Получение списка всех эпиков в виде объекта @Override - public ArrayList getAllEpics() { - ArrayList allEpics = new ArrayList<>(idToEpic.values()); - allEpics.sort(Task::compareTo); - return allEpics; + public List getAllEpics() { + return idToEpic.values().stream().sorted(Task::compareTo).toList(); } /// Получение списка всех подзадач в виде объекта @Override - public ArrayList getAllSubtasks() { - ArrayList allSubtasks = new ArrayList<>(idToSubtask.values()); - allSubtasks.sort(Task::compareTo); - return allSubtasks; + public List getAllSubtasks() { + return idToSubtask.values().stream().sorted(Task::compareTo).toList(); } /// Получение списка всех подзадач эпика в виде объекта @Override - public ArrayList getAllSubTasksByEpic(int epicId) { + public List getAllSubTasksByEpic(int epicId) { if (!idToEpic.containsKey(epicId)) { return null; } - Epic epic = idToEpic.get(epicId); - ArrayList resultSubtasks = new ArrayList<>(); - - for (int subtaskId : epic.getSubtaskIds()) { - if (idToSubtask.containsKey(subtaskId)) { - Subtask subtask = idToSubtask.get(subtaskId); - resultSubtasks.add(subtask); - } - } - - return resultSubtasks; + return idToSubtask.values().stream() + .filter(subtask -> idToEpic.get(epicId).getSubtaskIds().contains(subtask.getId())) + .toList(); } /// Обновление задачи по объекту @@ -251,6 +250,12 @@ public ArrayList getAllSubTasksByEpic(int epicId) { public Task updateTask(Task newTask) { Integer id = newTask.getId(); if (id == null || !(idToTask.containsKey(id))) { + System.out.println("Ошибка обновления задачи - неверные входные данные."); + return null; + } + + if (isFoundIntersections(newTask)) { + System.out.println("Ошибка обновления задачи - это время уже занято."); return null; } @@ -258,22 +263,27 @@ public Task updateTask(Task newTask) { if (newTask.getTitle() != null) { oldTask.setTitle(newTask.getTitle()); - } else { - newTask.setTitle(oldTask.getTitle()); } if (newTask.getDescription() != null) { oldTask.setDescription(newTask.getDescription()); - } else { - newTask.setDescription(oldTask.getDescription()); } if (newTask.getStatus() != null) { oldTask.setStatus(newTask.getStatus()); - } else { - newTask.setStatus(oldTask.getStatus()); } + if (newTask.getDuration() != Duration.ZERO) { + oldTask.setDuration(newTask.getDuration()); + } + + if (newTask.getStartTime() != null) { + oldTask.setStartTime(newTask.getStartTime()); + prioritizedTasks.add(oldTask); + } + + newTask = oldTask.getCopy(); + return newTask; } @@ -282,23 +292,29 @@ public Task updateTask(Task newTask) { public Epic updateEpic(Epic newEpic) { Integer id = newEpic.getId(); if (id == null || !(idToEpic.containsKey(id))) { + System.out.println("Ошибка обновления эпика - неверные входные данные."); return null; } + Epic oldEpic = idToEpic.get(id); if (newEpic.getTitle() != null) { oldEpic.setTitle(newEpic.getTitle()); - } else { - newEpic.setTitle(oldEpic.getTitle()); } if (newEpic.getDescription() != null) { oldEpic.setDescription(newEpic.getDescription()); - } else { - newEpic.setDescription(oldEpic.getDescription()); } - return oldEpic; + if (newEpic.getStartTime() != null) { + oldEpic.setStartTime(newEpic.getStartTime()); + } + + newEpic = oldEpic.getCopy(); + + updateEpicTime(oldEpic); + + return newEpic; } /// Обновление подзадачи по объекту @@ -306,30 +322,61 @@ public Epic updateEpic(Epic newEpic) { public Subtask updateSubtask(Subtask newSubtask) { Integer id = newSubtask.getId(); if (id == null || !(idToSubtask.containsKey(id))) { + System.out.println("Ошибка обновления подзадачи - неверные входные данные."); + return null; + } + + if (isFoundIntersections(newSubtask)) { + System.out.println("Ошибка обновления подзадачи - это время уже занято."); return null; } + Subtask oldSubtask = idToSubtask.get(id); if (newSubtask.getTitle() != null) { oldSubtask.setTitle(newSubtask.getTitle()); - } else { - newSubtask.setTitle(oldSubtask.getTitle()); } if (newSubtask.getDescription() != null) { oldSubtask.setDescription(newSubtask.getDescription()); - } else { - newSubtask.setDescription(oldSubtask.getDescription()); } if (newSubtask.getStatus() != null) { oldSubtask.setStatus(newSubtask.getStatus()); - } else { - newSubtask.setStatus(oldSubtask.getStatus()); } - updateEpicsStatus(oldSubtask.getEpicId()); - return oldSubtask; + if (newSubtask.getDuration() != Duration.ZERO) { + oldSubtask.setDuration(newSubtask.getDuration()); + } + + if (newSubtask.getStartTime() != null) { + oldSubtask.setStartTime(newSubtask.getStartTime()); + prioritizedTasks.add(oldSubtask); + } + + newSubtask = oldSubtask.getCopy(); + + int epicId = oldSubtask.getEpicId(); + updateEpicsStatus(epicId); + updateEpicTime(idToEpic.get(epicId)); + + return newSubtask; + } + + /// Проверка пересечений времени + private boolean isFoundIntersections(Task taskObject) { + Task savedTask = getWithoutHistory(taskObject.getId()); + Task testTask = taskObject.getCopy(); + + if (testTask.getStartTime() == null) { + testTask.setStartTime(savedTask.getStartTime()); + } + + if (testTask.getDuration() == null) { + testTask.setDuration(savedTask.getDuration()); + } + + return !findIntersections(testTask).isEmpty(); } /// Удаление задачи по ID @@ -338,6 +385,7 @@ public boolean removeTask(int taskId) { if (!idToTask.containsKey(taskId)) { return false; } + prioritizedTasks.removeIf(s -> s.getId() == taskId); idToTask.remove(taskId); historyManager.remove(taskId); return true; @@ -364,9 +412,11 @@ public boolean removeSubtask(int subtaskId) { Subtask subtask = idToSubtask.get(subtaskId); int epicId = subtask.getEpicId(); Epic epic = idToEpic.get(epicId); + prioritizedTasks.removeIf(s -> s.getId() == subtaskId); idToSubtask.remove(subtaskId); epic.removeSubtaskId(subtaskId); updateEpicsStatus(epicId); + updateEpicTime(epic); historyManager.remove(subtaskId); return true; } @@ -381,9 +431,11 @@ public void removeAllSubtasksByEpic(int epicId) { for (int subtaskId : epic.getSubtaskIds()) { idToSubtask.remove(subtaskId); historyManager.remove(subtaskId); + prioritizedTasks.removeIf(s -> s.getId() == subtaskId); } epic.removeAllSubtaskIds(); updateEpicsStatus(epicId); + clearEpicTime(epic); } /// Удаление всех задач @@ -391,6 +443,7 @@ public void removeAllSubtasksByEpic(int epicId) { public void removeAllTasks() { for (int taskId : idToTask.keySet()) { historyManager.remove(taskId); + prioritizedTasks.remove(idToTask.get(taskId)); } idToTask.clear(); } @@ -407,6 +460,7 @@ public void removeAllEpics() { historyManager.remove(subtaskId); } idToSubtask.clear(); + prioritizedTasks.removeIf(task -> task.getType() == Type.EPIC); } /// Удаление всех подзадач @@ -420,7 +474,9 @@ public void removeAllSubTasks() { for (Epic epic : idToEpic.values()) { epic.removeAllSubtaskIds(); updateEpicsStatus(epic.getId()); + clearEpicTime(epic); } + prioritizedTasks.removeIf(task -> task.getType() == Type.SUBTASK); } /// Получить список с историей @@ -470,4 +526,62 @@ protected void updateEpicsStatus(int epicId) { epic.setStatus(Status.IN_PROGRESS); } } + + /// Поиск пересекающихся тасок + public List findIntersections(Task taskObject) { + if (prioritizedTasks.isEmpty() || taskObject.getStartTime() == null) { + return new ArrayList<>(); + } + + return prioritizedTasks.stream() + .filter(task -> !Objects.equals(task.getId(), taskObject.getId())) + .filter(task -> + (taskObject.getStartTime().isBefore(task.getEndTime()) && + task.getStartTime().isBefore(taskObject.getEndTime())) + ) + .toList(); + } + + /// Обнуление полей времени эпика + protected void clearEpicTime(Epic epic) { + epic.setStartTime(null); + epic.setEndTime(null); + epic.setDuration(Duration.ZERO); + } + + /// Обновление продолжительности эпика + protected void updateEpicTime(Epic epic) { + if (epic.getId() == null || epic.getStartTime() == null) { + return; + } + List prioritizedSubtasks = getAllSubTasksByEpic(epic.getId()).stream() + .filter(subtask -> subtask.getStartTime() != null) + .toList(); + + if (prioritizedSubtasks.isEmpty()) { + clearEpicTime(epic); + return; + } + + Subtask firstSubtask = prioritizedSubtasks.stream() + .min(Comparator.comparing(Subtask::getStartTime)) + .get(); + + Subtask lastSubtask = prioritizedSubtasks.stream() + .max(Comparator.comparing(Subtask::getStartTime)) + .get(); + + if (firstSubtask.equals(lastSubtask)) { + clearEpicTime(epic); + epic.setStartTime(firstSubtask.getStartTime()); + } + + Optional sumDuration = Optional.ofNullable(prioritizedSubtasks.stream() + .map(Subtask::getDuration) + .reduce(Duration.ZERO, Duration::plus)); + + epic.setStartTime(firstSubtask.getStartTime()); + epic.setEndTime(lastSubtask.getEndTime()); + epic.setDuration(sumDuration.orElse(Duration.ZERO)); + } } diff --git a/src/manager/TaskManager.java b/src/manager/TaskManager.java index 0824a25..7b4998d 100644 --- a/src/manager/TaskManager.java +++ b/src/manager/TaskManager.java @@ -6,14 +6,15 @@ import data.Type; import java.util.List; +import java.util.TreeSet; public interface TaskManager { - Task setTask(Task newTask); + Task addTask(Task newTask); - Epic setEpic(Epic newEpic); + Epic addEpic(Epic newEpic); - Subtask setSubtask(Subtask newSubtask); + Subtask addSubtask(Subtask newSubtask); Task getTask(int taskID); @@ -49,9 +50,9 @@ public interface TaskManager { void removeAllSubTasks(); - List getHistory(); + List getHistory(); - List getAll(); + TreeSet getAll(); Task get(int id); diff --git a/test/data/EpicTest.java b/test/data/EpicTest.java index 475b368..8983e60 100644 --- a/test/data/EpicTest.java +++ b/test/data/EpicTest.java @@ -56,7 +56,7 @@ void shouldBeEmptyAfterRemoveAllSubtasks() { epic.removeAllSubtaskIds(); assertEquals("[]", epic.getSubtaskIds().toString(), "Эпик не удаляет все подзадачи одной командой." - ); + ); } @Test @@ -64,7 +64,11 @@ void shouldReturnCorrectStringWhenGetToString() { assertEquals("Epic{id='1', " + "title='This is epic', " + "description='This is description of epic', " + - "status='null'}", + "status='null'" + + ", startTime='null'" + + ", endTime='null'" + + ", duration='0'" + + '}', epic.toString(), "Эпик возвращает некорректную строку при вызове toString."); } diff --git a/test/data/SubtaskTest.java b/test/data/SubtaskTest.java index 8c1ffdf..9e074a0 100644 --- a/test/data/SubtaskTest.java +++ b/test/data/SubtaskTest.java @@ -5,7 +5,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; -public class SubtaskTest { +class SubtaskTest { static Subtask subtask; @@ -16,6 +16,8 @@ void addSubtask() { "this is Subtask", "This is Subtask description", null, + null, + null, null ); } @@ -28,6 +30,9 @@ void shouldReturnCorrectStringWhenGetToString() { "title='this is Subtask', " + "description='This is Subtask description', " + "status='null'" + + ", startTime='null'" + + ", endTime='null'" + + ", duration='0'" + "}", subtask.toString(), "Подзадача возвращает некорректную строку при вызове toString."); diff --git a/test/data/TaskTest.java b/test/data/TaskTest.java index 7b8442e..45828b7 100644 --- a/test/data/TaskTest.java +++ b/test/data/TaskTest.java @@ -2,11 +2,21 @@ import org.junit.jupiter.api.Test; +import java.time.Duration; +import java.time.LocalDateTime; + import static org.junit.jupiter.api.Assertions.*; -public class TaskTest { +class TaskTest { + + static Task task1 = new Task( + 1, + "This is task1", + "This is description of task1", + null, + LocalDateTime.of(2025, 11, 11, 11, 11), + Duration.ofMinutes(30)); - static Task task1 = new Task(1, "This is task1", "This is description of task1"); static Task task2 = new Task(2, "This is task1", "This is description of task1"); static Task task3 = new Task(1, "This is task2", "This is description of task2", Status.DONE); @@ -22,7 +32,7 @@ void shouldHashcodeBeEqualsWhenIdIsSame() { @Test void shouldBeNotEqualsWhenIdIsDifferent() { - assertNotEquals(task1, task2,"Задачи с разным ID равны по equals"); + assertNotEquals(task1, task2, "Задачи с разным ID равны по equals"); } @Test @@ -35,9 +45,20 @@ void shouldReturnCorrectStringWhenGetToString() { assertEquals("Task{id='1', " + "title='This is task2', " + "description='This is description of task2', " + - "status=DONE}", + "status='DONE'" + + ", startTime='null'" + + ", endTime='null'" + + ", duration='0'" + + '}', task3.toString(), "Задача возвращает некорректную строку при вызове toString."); } + @Test + void shouldReturnDateTimeAfterGet() { + assertEquals("2025-11-11T11:41", task1.getEndTime().toString(), + "Не верно вычисляется дата завершения задачи" + ); + } + } diff --git a/test/manager/FileBackedTaskManagerTest.java b/test/manager/FileBackedTaskManagerTest.java index 6f446bc..63858c2 100644 --- a/test/manager/FileBackedTaskManagerTest.java +++ b/test/manager/FileBackedTaskManagerTest.java @@ -10,45 +10,32 @@ import java.io.File; import java.io.IOException; -import java.util.ArrayList; -import java.util.List; +import java.time.Duration; +import java.time.LocalDateTime; -import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; -public class FileBackedTaskManagerTest { - static TaskManager manager; - static TaskManager manager2; - - static Task task; - static int taskId; - static Task savedTask; - static List tasks; +class FileBackedTaskManagerTest extends TaskManagerTest { - static Epic epic; - static int epicId; - static Epic savedEpic; - static List epics; + static TaskManager manager2; - static List subtasks; static File file; - void updateTaskHistory(List history) { - manager.getTask(taskId); - history.clear(); - history.addAll(manager.getHistory()); - } - @AfterEach void afterEach() { file.deleteOnExit(); } + @Override @BeforeEach - void beforeEach() throws IOException { + void beforeEach() { // Создать новый темп-файл - file = File.createTempFile(".test", ".csv", null); + try { + file = File.createTempFile(".test", ".csv", null); + } catch (IOException e) { + System.out.println("Ошибка создания файла"); + } // Создать новый менеджер задач manager = new FileBackedTaskManager(file); @@ -57,11 +44,14 @@ void beforeEach() throws IOException { task = new Task( 777, "Test addNewTask title", - "Test addNewTask description" + "Test addNewTask description", + null, + LocalDateTime.of(2025, 11, 11, 11, 11), + Duration.ofMinutes(30) ); // Добавить задачу в базу и получить идентификатор добавленной задачи - taskId = manager.setTask(task).getId(); + taskId = manager.addTask(task).getId(); // Получить задачу из базы по идентификатору savedTask = manager.getTask(taskId); @@ -85,24 +75,28 @@ void beforeEach() throws IOException { ); // Добавить эпик в базу и получить идентификатор добавленного эпика - epicId = manager.setEpic(epic).getId(); + epicId = manager.addEpic(epic).getId(); // Добавить первую подзадачу в созданный эпик - manager.setSubtask(new Subtask( + manager.addSubtask(new Subtask( 777, "This is first Subtask", "This is description of first Subtask", Status.IN_PROGRESS, - epicId + epicId, + null, + null )); // Добавить вторую подзадачу в созданный эпик - manager.setSubtask(new Subtask( + manager.addSubtask(new Subtask( null, "This is third Subtask", "This is description of third Subtask", Status.DONE, - epicId + epicId, + null, + null )); // Получить все подзадачи @@ -116,268 +110,6 @@ void beforeEach() throws IOException { // Получить подзадачу по ID manager.getSubtask(4); - - } - - - @Test - void shouldBeNotNullAfterGet() { - assertNotNull(savedTask, "Задача не найдена."); - } - - @Test - void shouldBeEqualsCreatedAndSavedTasks() { - assertEquals(task, savedTask, "Задачи не совпадают."); - } - - @Test - void shouldBeNotNullListAllTasks() { - assertNotNull(tasks, "Задачи не возвращаются."); - } - - @Test - void shouldBeOneElementOfListAllTasks() { - assertEquals(1, tasks.size(), "Неверное количество задач."); - } - - @Test - void shouldBeEqualsCreatedTaskAndTaskOfListAllTasks() { - assertEquals(task, tasks.getFirst(), "Созданная задача не соответствует задаче из списка."); - } - - @Test - void shouldBeOneElementOfHistoryList() { - List history = new ArrayList<>(manager.getHistory()); - assertEquals(3, history.size(), "Неверное количество задач в истории."); - } - - @Test - void shouldBeEqualsCreatedTaskAndTaskOfHistoryList() { - ArrayList history = new ArrayList<>(manager.getHistory()); - assertEquals(task, history.getFirst(), "Созданная задача не соответствует задаче из истории"); - } - - @Test - void shouldBeTwoElementOfHistoryListAfterUpdateAndGet() { - List history = new ArrayList<>(manager.getHistory()); - updateTaskHistory(history); - assertEquals(3, history.size(), "Неверное количество задач в истории."); - } - - @Test - void shouldBeStatusEqualsOfCreatedTaskAndFirstTaskOfHistoryListAfterUpdateAndGet() { - manager.updateTask(new Task(1, null, null, Status.NEW)); - List history = new ArrayList<>(manager.getHistory()); - updateTaskHistory(history); - Task testedTask = manager.getTask(1); - assertEquals(testedTask.getStatus(), history.getLast().getStatus(), - "Статус созданной задачи не соответствует статусу последней задачи из истории." - ); - } - - @Test - void shouldBeStatusEqualsOfSavedTaskAndLastTaskOfHistoryListAfterUpdateAndGet() { - List history = new ArrayList<>(manager.getHistory()); - updateTaskHistory(history); - assertEquals(manager.getTask(1).getStatus(), history.getLast().getStatus(), - "Статус сохраненной задачи не соответствует статусу последней задачи из истории." - ); - } - - @Test - void shouldBeNotNullAfterGetEpic() { - assertNotNull(savedEpic, "Эпик не найден."); - } - - @Test - void shouldBeEqualsCreatedAndReceivedEpics() { - assertEquals(epic, savedEpic, "Эпики не совпадают."); - } - - @Test - void shouldBeNotNullListAllEpics() { - assertNotNull(epics, "Эпики не возвращаются."); - } - - @Test - void shouldBeOneElementOfListAllEpics() { - assertEquals(1, epics.size(), "Получено неверное количество эпиков."); - } - - @Test - void shouldBeEqualsCreatedEpicAndTaskOfListAllEpics() { - assertEquals(epic, epics.getFirst(), "Созданный эпик не соответствует эпику из списка."); - } - - @Test - void shouldUpdateEpicTitleWhenUpdateEpic() { - manager.updateEpic(new Epic(2, "HelloWorld!", null)); - assertEquals("HelloWorld!", manager.getEpic(2).getTitle(), "Менеджер неправильно обновляет эпик."); - } - - @Test - void shouldReturnTwoAfterSetNewIdOfCashedEpic() { - manager.getEpic(2).setId(3); - int testEpicId = manager.getAllEpics().getLast().getId(); - assertEquals(2, testEpicId, - "Изменение эпика через сеттер влияет на эпик хранящийся в менеджере"); - } - - @Test - void shouldBeNotNullListSubtasksOfSavedEpic() { - assertNotNull(savedEpic.getSubtaskIds(), "Сохраненный эпик не возвращает идентификаторы подзадач."); - } - - @Test - void shouldBeTwoSubtaskIdsContainsOfSavedEpicAfterRemoveSubtask() { - manager.removeSubtask(3); - Epic testedEpic = manager.getEpic(2); - assertEquals(1, testedEpic.getSubtaskIds().size(), - "Сохраненный эпик возвращает неверное количество идентификаторов подзадач " + - "после удаления подзадачи."); - } - - @Test - void shouldBeIsEmptySubtaskListAfterRemoveAllSubtasks() { - manager.removeAllSubtasksByEpic(epicId); - Epic testedEpic = manager.getEpic(epicId); - assertEquals("[]", testedEpic.getSubtaskIds().toString(), - "Список идентификаторов подзадач эпика должен быть пуст после удаления всех подзадач эпика."); - } - - @Test - void shouldContainsTwoElementsInSubtasksList() { - assertEquals(2, subtasks.size(), "Список всех подзадач возвращает не верное количество."); - } - - @Test - void shouldReturnStatusNewWhenAllSubtasksInStatusesNull() { - manager.setSubtask(new Subtask(null, null, null, null, 2)); - manager.setSubtask(new Subtask(null, null, null, null, 2)); - manager.removeSubtask(3); - manager.removeSubtask(4); - assertEquals(Status.NEW, manager.getEpic(2).getStatus(), - "Эпик не выставляет статус NEW когда все подзадачи в статусе null"); - } - - @Test - void shouldReturnNullWhenAddingSubtaskWithMissingId() { - try { - manager.setSubtask(new Subtask(null, "Hello", "World", null, 777)); - } catch (NullPointerException e) { - System.out.print(""); - } - assertEquals(2, subtasks.size(), "Подзадача добавляется в несуществующий эпик."); - } - - @Test - void shouldEqualsTwoWhenRemoveAllSubtaskWithMissingEpicId() { - manager.removeAllSubtasksByEpic(777); - assertEquals(2, subtasks.size(), - "Менеджер удаляет подзадачи несуществующего эпика" - ); - } - - @Test - void shouldTwoWhenGetAllSubtasksByEpic() { - assertEquals(2, manager.getAllSubTasksByEpic(2).size(), - "Менеджер возвращает не верное количество подзадач при вызове всех подзадач эпика"); - } - - @Test - void shouldReturnThreeAndFourWhenGetAllSubtasksIdsByEpic() { - Epic testEpic = manager.getEpic(2); - assertEquals(3, testEpic.getSubtaskIds().getFirst()); - assertEquals(4, testEpic.getSubtaskIds().getLast(), - "Внутри эпиков остаются неактуальные id подзадач."); - } - - @Test - void shouldReturnNullWhenTryingToGetAllSubtasks() { - assertNull(manager.getAllSubTasksByEpic(100), - "Менеджер не возвращает null при попытке получения всех подзадач несуществующего эпика"); - } - - @Test - void shouldUpdateEpicStatusWhenAllHimSubtasksIsDone() { - manager.updateSubtask(new Subtask(3, null, null, Status.DONE, null)); - assertEquals(Status.DONE, manager.getAllEpics().getLast().getStatus(), - "Статус эпика не обновляется когда все его подзадачи завершены"); - } - - @Test - void shouldReturnNullWhenGettingRemovedTask() { - manager.removeTask(1); - assertNull(manager.getTask(1), "Менеджер не удаляет задачу."); - } - - @Test - void shouldReturnNullWhenGettingRemovedSubtask() { - manager.removeSubtask(4); - assertNull(manager.getSubtask(4), "Менеджер не удаляет подзадачу."); - } - - @Test - void shouldReturnNullWhenGettingRemovedEpic() { - manager.removeEpic(2); - assertNull(manager.getEpic(2), "Менеджер не удаляет эпик."); - } - - @Test - void shouldReturnEmptyAfterRemoveAllSubtasks() { - manager.removeAllSubTasks(); - assertEquals("[]", manager.getAllSubtasks().toString(), - "Менеджер не очищает все подзадачи."); - } - - @Test - void shouldReturnEpicStatusNewAfterRemoveAllSubtasks() { - manager.removeAllSubTasks(); - Epic testedEpic = manager.getEpic(2); - assertEquals(Status.NEW, testedEpic.getStatus(), - "Менеджер не присваивает эпикам статус NEW после очистки главного хранилища подзадач."); - } - - @Test - void shouldReturnNullAfterRemoveAllEpics() { - manager.removeAllEpics(); - assertEquals("[]", manager.getAllSubtasks().toString(), - "Менеджер не очищает хранилище подзадач после очистки хранилища эпиков."); - } - - @Test - void shouldReturnTwoWhenAllTasksIsDeleted() { - manager.removeAllTasks(); - assertEquals(2, manager.getHistory().getFirst().getId(), - "При удалении всех задач они не удаляются из истории" - ); - } - - @Test - void shouldReturnListWithOneElementWhenAllEpicsIsDeleted() { - manager.removeAllEpics(); - assertEquals(1, manager.getHistory().size(), - "При удалении всех эпиков эпики не удаляются из истории с их подзадачами" - ); - } - - @Test - void shouldReturnTwoWhenAllSubtasksIsDeleted() { - manager.removeAllSubTasks(); - assertEquals(2, manager.getHistory().size(), - "При удалении всех подзадач они не удаляются из истории" - ); - } - - @Test - void shouldReturnTwoWhenAllSubtasksByEpicIsDeleted() { - manager.setEpic(new Epic(null, null, null)); - manager.setSubtask(new Subtask(null, null, null, null, 5)); - manager.getSubtask(6); - manager.removeAllSubtasksByEpic(2); - assertEquals(3, manager.getHistory().size(), - "При удалении всех подзадач эпика они не удаляются из истории" - ); } @Test @@ -390,7 +122,7 @@ void shouldReturnFourWhenGetAllTaskCountFromFile() { @Test void shouldReturnActualStatusesAfterUpdateDataFromFile() { manager.removeTask(1); - manager.setTask(new Task(null, "LastTask", "eee", Status.NEW)); + manager.addTask(new Task(null, "LastTask", "eee", Status.NEW)); manager2 = new FileBackedTaskManager(file); StringBuilder statuses = new StringBuilder(); for (Task taskObject : manager2.getAll()) { @@ -406,8 +138,16 @@ void shouldReturnActualStatusesAfterUpdateDataFromFile() { @Test void shouldReturnNullWhenGetFakeTaskAfterThisAdding() { // Фэйковая задача - Task subtask = new Subtask(null, "last subtask", null, null, 2); - manager.setTask(subtask); + Task subtask = new Subtask( + null, + "last subtask", + null, + null, + 2, + null, + null + ); + manager.addTask(subtask); assertNull(manager.get(5), "Менеджер сохранил фэйковую задачу."); } diff --git a/test/manager/HistoryManagerTest.java b/test/manager/HistoryManagerTest.java index e38dab4..f4b8344 100644 --- a/test/manager/HistoryManagerTest.java +++ b/test/manager/HistoryManagerTest.java @@ -12,7 +12,7 @@ import static org.junit.jupiter.api.Assertions.*; -public class HistoryManagerTest { +class HistoryManagerTest { static HistoryManager historyManager; static List history; @@ -23,7 +23,15 @@ void beforeEach() { Task task = new Task(1, "Hello", "World", Status.DONE); historyManager.add(task); historyManager.add(new Epic(2, "Hello", "World")); - historyManager.add(new Subtask(3, "Hello", "World", Status.DONE, 2)); + historyManager.add(new Subtask( + 3, + "Hello", + "World", + Status.DONE, + 2, + null, + null + )); historyManager.add(new Task(1, "Goodbye", "World", Status.IN_PROGRESS)); history = historyManager.getHistory(); @@ -33,7 +41,7 @@ void beforeEach() { @Test void shouldReturnEmptyListWhenHistoryIsEmpty() { HistoryManager hm = new InMemoryHistoryManager(); - hm.add(new Subtask(1, null, null, null, 2)); + hm.add(new Subtask(1, null, null, null, 2, null, null)); hm.remove(1); assertEquals("[]", hm.getHistory().toString(), "Пустая история не выдает пустой лист"); diff --git a/test/manager/TaskManagerTest.java b/test/manager/TaskManagerTest.java index 5d1ee2e..4b911aa 100644 --- a/test/manager/TaskManagerTest.java +++ b/test/manager/TaskManagerTest.java @@ -8,12 +8,15 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import java.time.Duration; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.List; import static org.junit.jupiter.api.Assertions.*; -public class TaskManagerTest { +class TaskManagerTest { static TaskManager manager; @@ -44,12 +47,15 @@ void beforeEach() { task = new Task( 777, "Test addNewTask title", - "Test addNewTask description" + "Test addNewTask description", + null, + LocalDateTime.of(2025, 11, 11, 11, 11), + Duration.ofMinutes(30) ); // Добавить задачу в базу и получить идентификатор добавленной задачи assertNotNull(manager); - taskId = manager.setTask(task).getId(); + taskId = manager.addTask(task).getId(); // Получить задачу из базы по идентификатору savedTask = manager.getTask(taskId); @@ -73,24 +79,28 @@ void beforeEach() { ); // Добавить эпик в базу и получить идентификатор добавленного эпика - epicId = manager.setEpic(epic).getId(); + epicId = manager.addEpic(epic).getId(); // Добавить первую подзадачу в созданный эпик - manager.setSubtask(new Subtask( + manager.addSubtask(new Subtask( 777, "This is first Subtask", "This is description of first Subtask", Status.IN_PROGRESS, - epicId + epicId, + null, + null )); // Добавить вторую подзадачу в созданный эпик - manager.setSubtask(new Subtask( + manager.addSubtask(new Subtask( null, "This is third Subtask", "This is description of third Subtask", Status.DONE, - epicId + epicId, + null, + null )); // Получить все подзадачи @@ -199,7 +209,8 @@ void shouldBeEqualsCreatedEpicAndTaskOfListAllEpics() { @Test void shouldUpdateEpicTitleWhenUpdateEpic() { manager.updateEpic(new Epic(2, "HelloWorld!", null)); - assertEquals("HelloWorld!", manager.getEpic(2).getTitle(), "Менеджер неправильно обновляет эпик."); + assertEquals("HelloWorld!", manager.getEpic(2).getTitle(), + "Менеджер неправильно обновляет эпик."); } @Test @@ -239,8 +250,24 @@ void shouldContainsTwoElementsInSubtasksList() { @Test void shouldReturnStatusNewWhenAllSubtasksInStatusesNull() { - manager.setSubtask(new Subtask(null, null, null, null, 2)); - manager.setSubtask(new Subtask(null, null, null, null, 2)); + manager.addSubtask(new Subtask( + null, + null, + null, + null, + 2, + null, + null + )); + manager.addSubtask(new Subtask( + null, + null, + null, + null, + 2, + null, + null + )); manager.removeSubtask(3); manager.removeSubtask(4); assertEquals(Status.NEW, manager.getEpic(2).getStatus(), @@ -250,7 +277,15 @@ void shouldReturnStatusNewWhenAllSubtasksInStatusesNull() { @Test void shouldReturnNullWhenAddingSubtaskWithMissingId() { try { - manager.setSubtask(new Subtask(null, "Hello", "World", null, 777)); + manager.addSubtask(new Subtask( + null, + "Hello", + "World", + null, + 777, + null, + null + )); } catch (NullPointerException e) { System.out.print(""); } @@ -287,7 +322,15 @@ void shouldReturnNullWhenTryingToGetAllSubtasks() { @Test void shouldUpdateEpicStatusWhenAllHimSubtasksIsDone() { - manager.updateSubtask(new Subtask(3, null, null, Status.DONE, null)); + manager.updateSubtask(new Subtask( + 3, + null, + null, + Status.DONE, + null, + null, + null + )); assertEquals(Status.DONE, manager.getAllEpics().getLast().getStatus(), "Статус эпика не обновляется когда все его подзадачи завершены"); } @@ -358,12 +401,36 @@ void shouldReturnTwoWhenAllSubtasksIsDeleted() { @Test void shouldReturnTwoWhenAllSubtasksByEpicIsDeleted() { - manager.setEpic(new Epic(null, null, null)); - manager.setSubtask(new Subtask(null, null, null, null, 5)); + manager.addEpic(new Epic(null, null, null)); + manager.addSubtask(new Subtask( + null, + null, + null, + null, + 5, + null, + null + )); manager.getSubtask(6); manager.removeAllSubtasksByEpic(2); assertEquals(3, manager.getHistory().size(), "При удалении всех подзадач эпика они не удаляются из истории" ); } + + @Test + void shouldReturnNullWhenAddIntersectionTasks() { + assertNull(manager.addTask(new Task(null, null, null, null, + LocalDateTime.parse("2025-11-11T10:30"), Duration.ofMinutes(50))), + "Менеджер добавляет пересекающиеся задачи по верхней границе."); + + assertNull(manager.addTask(new Task(null, null, null, null, + LocalDateTime.parse("2025-11-11T11:15"), Duration.ofMinutes(10))), + "Менеджер добавляет пересекающиеся задачи внутрь временного контура."); + + assertNull(manager.addTask(new Task(null, null, null, null, + LocalDateTime.parse("2025-11-11T11:30"), Duration.ofMinutes(50))), + "Менеджер добавляет пересекающиеся задачи по нижней границе."); + } + } From 136b4eaa991cc20fa4e112d3df25bd94c7aa564b Mon Sep 17 00:00:00 2001 From: AleksOBM Date: Sun, 9 Nov 2025 21:37:48 +0300 Subject: [PATCH 2/3] =?UTF-8?q?=D0=9F=D0=BE=D0=B4=D0=B3=D0=BE=D1=82=D0=BE?= =?UTF-8?q?=D0=B2=D0=B8=D1=82=D1=8C=20=D0=BF=D1=80=D0=BE=D0=B5=D0=BA=D1=82?= =?UTF-8?q?=20=D0=BA=20=D0=BF=D0=B5=D1=80=D0=B2=D0=BE=D0=BC=D1=83=20=D1=80?= =?UTF-8?q?=D0=B5=D0=B2=D1=8C=D1=8E=20=D0=B2=D0=BE=D1=81=D1=8C=D0=BC=D0=BE?= =?UTF-8?q?=D0=B3=D0=BE=20=D1=81=D0=BF=D1=80=D0=B8=D0=BD=D1=82=D0=B0=20(?= =?UTF-8?q?=D1=83=D0=B1=D1=80=D0=B0=D1=82=D1=8C=20unusedImport)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/manager/TaskManagerTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/test/manager/TaskManagerTest.java b/test/manager/TaskManagerTest.java index 4b911aa..ac48ba4 100644 --- a/test/manager/TaskManagerTest.java +++ b/test/manager/TaskManagerTest.java @@ -10,7 +10,6 @@ import java.time.Duration; import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.List; From 4641b5167dd533772c7749cfcfa26b5f1b89fb7d Mon Sep 17 00:00:00 2001 From: AleksOBM Date: Mon, 10 Nov 2025 21:05:19 +0300 Subject: [PATCH 3/3] =?UTF-8?q?=D0=9F=D0=BE=D0=B4=D0=B3=D0=BE=D1=82=D0=BE?= =?UTF-8?q?=D0=B2=D0=B8=D1=82=D1=8C=20=D0=BF=D1=80=D0=BE=D0=B5=D0=BA=D1=82?= =?UTF-8?q?=20=D0=BA=D0=BE=20=D0=B2=D1=82=D0=BE=D1=80=D0=BE=D0=BC=D1=83=20?= =?UTF-8?q?=D1=80=D0=B5=D0=B2=D1=8C=D1=8E=20=D0=B2=D0=BE=D1=81=D1=8C=D0=BC?= =?UTF-8?q?=D0=BE=D0=B3=D0=BE=20=D1=81=D0=BF=D1=80=D0=B8=D0=BD=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Main.java | 16 +- src/data/Epic.java | 43 +-- src/data/Subtask.java | 4 +- src/data/Task.java | 24 +- src/manager/FileBackedTaskManager.java | 34 ++- src/manager/InMemoryTaskManager.java | 291 +++++++++++--------- test/data/EpicTest.java | 2 +- test/data/SubtaskTest.java | 2 +- test/data/TaskTest.java | 2 +- test/manager/FileBackedTaskManagerTest.java | 14 +- test/manager/TaskManagerTest.java | 87 +++++- 11 files changed, 310 insertions(+), 209 deletions(-) diff --git a/src/Main.java b/src/Main.java index a2d09e0..a83228e 100644 --- a/src/Main.java +++ b/src/Main.java @@ -632,18 +632,18 @@ static void setTime() { System.out.println("\n" + "-".repeat(20) + "\n"); return; } - - System.out.print("Год --> "); - int year = Integer.parseInt(scanner.next()); - System.out.print("Месяц --> "); - int month = Integer.parseInt(scanner.next()); - System.out.print("День --> "); - int day = Integer.parseInt(scanner.next()); +// System.out.print("Год --> "); +// int year = Integer.parseInt(scanner.next()); +// System.out.print("Месяц --> "); +// int month = Integer.parseInt(scanner.next()); +// System.out.print("День --> "); +// int day = Integer.parseInt(scanner.next()); System.out.print("Часы --> "); int hours = Integer.parseInt(scanner.next()); System.out.print("Минуты --> "); int minutes = Integer.parseInt(scanner.next()); - LocalDateTime dateTime = LocalDateTime.of(year, month, day, hours, minutes); + LocalDateTime now = LocalDateTime.now(); + LocalDateTime dateTime = LocalDateTime.of(now.getYear(), now.getMonth(), now.getDayOfMonth(), hours, minutes); System.out.println("\n" + "-".repeat(20) + "\n"); switch (type) { diff --git a/src/data/Epic.java b/src/data/Epic.java index 2c4a804..729e9d7 100644 --- a/src/data/Epic.java +++ b/src/data/Epic.java @@ -1,6 +1,5 @@ package data; -import java.time.Duration; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; @@ -10,6 +9,9 @@ public class Epic extends Task { /// Хранилище ID подзадач private ArrayList subtaskIds = new ArrayList<>(); + /// Дата и время завершения эпика + private LocalDateTime endTime; + /// Конструктор public Epic(Integer id, String title, String description) { super(id, title, description); @@ -20,31 +22,17 @@ public List getSubtaskIds() { return new ArrayList<>(subtaskIds); } + @Override + public LocalDateTime getEndTime() { + return endTime; + } + /// Добавление новой ID подзадачи public void addSubtaskId(Integer subtaskId) { subtaskIds.remove(subtaskId); subtaskIds.add(subtaskId); } - /// Удаление всех ID подзадач - public void removeAllSubtaskIds() { - subtaskIds.clear(); - } - - /// Удаление ID подзадачи - public void removeSubtaskId(Integer subtaskId) { - subtaskIds.remove(subtaskId); - } - - @Override - public void setDuration(Duration duration) { - this.duration = duration; - } - - public void setEndTime(LocalDateTime endTime) { - this.endTime = endTime; - } - @Override public Type getType() { return Type.EPIC; @@ -62,6 +50,21 @@ public Epic getCopy() { return newEpic; } + /// Внесение даты и времени завершения + public void setEndTime(LocalDateTime endTime) { + this.endTime = endTime; + } + + /// Удаление всех ID подзадач + public void removeAllSubtaskIds() { + subtaskIds.clear(); + } + + /// Удаление ID подзадачи + public void removeSubtaskId(Integer subtaskId) { + subtaskIds.remove(subtaskId); + } + @Override public String toString() { return "Epic{" + diff --git a/src/data/Subtask.java b/src/data/Subtask.java index e8963a5..cd186e0 100644 --- a/src/data/Subtask.java +++ b/src/data/Subtask.java @@ -9,7 +9,7 @@ public class Subtask extends Task { private final Integer epicId; /// Конструктор - public Subtask( + public Subtask( Integer id, String title, String description, @@ -57,7 +57,7 @@ public String toString() { ", description='" + super.getDescription() + '\'' + ", status='" + super.getStatus() + '\'' + ", startTime='" + (startTime == null ? "null" : startTime.format(DATE_TIME_FORMATTER)) + '\'' + - ", endTime='" + (endTime == null ? "null" : endTime.format(DATE_TIME_FORMATTER)) + '\'' + + ", endTime='" + (getEndTime() == null ? "null" : getEndTime().format(DATE_TIME_FORMATTER)) + '\'' + ", duration='" + (duration == null ? "null" : duration.toMinutes()) + '\'' + '}'; } diff --git a/src/data/Task.java b/src/data/Task.java index 7437e26..ba57ee2 100644 --- a/src/data/Task.java +++ b/src/data/Task.java @@ -23,10 +23,7 @@ public class Task implements Comparable { protected LocalDateTime startTime; /// Продолжительность задачи в минутах - protected Duration duration = Duration.ZERO; - - /// Дата и время завершения задачи - protected LocalDateTime endTime; + protected Duration duration; protected static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"); @@ -59,7 +56,7 @@ public Task( this.description = description; this.status = status; this.startTime = startTime; - setDuration(duration); + this.duration = duration; } /// Получение ID задачи @@ -110,9 +107,6 @@ public LocalDateTime getStartTime() { /// Внесение времени начала выполнения public void setStartTime(LocalDateTime startTime) { this.startTime = startTime; - if (startTime != null) { - endTime = this.startTime.plus(this.duration); - } } /// Время выполнения задачи @@ -122,16 +116,14 @@ public Duration getDuration() { /// Внесение продолжительности задачи public void setDuration(Duration duration) { - if (duration != null) { - this.duration = duration; - } - if (startTime != null) { - endTime = startTime.plus(this.duration); - } + this.duration = duration; } public LocalDateTime getEndTime() { - return endTime; + if (startTime != null && duration != null) { + return startTime.plus(duration); + } + return null; } /// Получение типа объекта @@ -169,7 +161,7 @@ public String toString() { ", description='" + description + '\'' + ", status='" + status + '\'' + ", startTime='" + (startTime == null ? "null" : startTime.format(DATE_TIME_FORMATTER)) + '\'' + - ", endTime='" + (endTime == null ? "null" : endTime.format(DATE_TIME_FORMATTER)) + '\'' + + ", endTime='" + (getEndTime() == null ? "null" : getEndTime().format(DATE_TIME_FORMATTER)) + '\'' + ", duration='" + (duration == null ? "null" : duration.toMinutes()) + '\'' + '}'; } diff --git a/src/manager/FileBackedTaskManager.java b/src/manager/FileBackedTaskManager.java index ed3e6ce..3b5265f 100644 --- a/src/manager/FileBackedTaskManager.java +++ b/src/manager/FileBackedTaskManager.java @@ -19,7 +19,8 @@ public class FileBackedTaskManager extends InMemoryTaskManager { private static FileBackedTaskManager instance; /// Параметры CSV файла - private static final String TABLE_HEADER = "start, end, duration, id,type,name,status,description,epic"; + // 0 1 2 3 4 5 6 7 8 + private static final String TABLE_HEADER = "id,type,name,status,description,start,end,duration,epic"; private static final Charset CHARSET = StandardCharsets.UTF_8; private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"); private static final String DELIMITER = ","; @@ -109,7 +110,13 @@ public void updateDataFromFile() throws ManagerSaveException { return; } + prioritizedTasks.addAll(objectToList.stream() + .filter(task -> task.getType() != Type.EPIC) + .filter(task -> task.getStartTime() != null).toList() + ); + for (Task taskObject : objectToList) { + switch (taskObject.getType()) { case TASK -> idToTask.put(taskObject.getId(), taskObject); case EPIC -> idToEpic.put(taskObject.getId(), (Epic) taskObject); @@ -225,14 +232,14 @@ private String getStringToFileSave(Task taskObject) { String epicId = type.equals(Type.SUBTASK) ? String.valueOf(((Subtask) taskObject).getEpicId()) : ""; - return startTime + DELIMITER + - endTime + DELIMITER + - duration + DELIMITER + - id + DELIMITER + + return id + DELIMITER + type + DELIMITER + title + DELIMITER + status + DELIMITER + description + DELIMITER + + startTime + DELIMITER + + endTime + DELIMITER + + duration + DELIMITER + epicId; } @@ -248,14 +255,15 @@ private Task getDataObjectFromString(String stringFromFile) { throw new ManagerSaveException("Не поддерживаемый формат строки"); } - LocalDateTime startTime = splitStr[0].equals("null") ? null : - LocalDateTime.parse(splitStr[0], DATE_TIME_FORMATTER); - Duration duration = Duration.ofMinutes(Integer.parseInt(splitStr[2])); - Integer id = Integer.parseInt(splitStr[3]); - Type type = Type.valueOf(splitStr[4]); - String title = splitStr[5].replace(QUOTE, ""); - String description = splitStr[7].replace(QUOTE, ""); - Status status = !splitStr[6].equals("null") ? Status.valueOf(splitStr[6]) : null; + Integer id = Integer.parseInt(splitStr[0]); + Type type = Type.valueOf(splitStr[1]); + String title = splitStr[2].replace(QUOTE, ""); + Status status = !splitStr[3].equals("null") ? Status.valueOf(splitStr[3]) : null; + String description = splitStr[4].replace(QUOTE, ""); + LocalDateTime startTime = splitStr[5].equals("null") ? null : + LocalDateTime.parse(splitStr[5], DATE_TIME_FORMATTER); + // endTime здесь не нужен - пропускаем. + Duration duration = Duration.ofMinutes(Integer.parseInt(splitStr[7])); switch (type) { case TASK: diff --git a/src/manager/InMemoryTaskManager.java b/src/manager/InMemoryTaskManager.java index 2fa4094..63b35fe 100644 --- a/src/manager/InMemoryTaskManager.java +++ b/src/manager/InMemoryTaskManager.java @@ -3,6 +3,7 @@ import data.*; import java.time.Duration; +import java.time.LocalDateTime; import java.util.*; import java.util.stream.Stream; @@ -101,6 +102,11 @@ public Task addTask(Task newTask) { return null; } newTask.setId(generateNewId()); + + if (newTask.getDuration() == null) { + newTask.setDuration(Duration.ZERO); + } + Task savedTask = newTask.getCopy(); idToTask.put(savedTask.getId(), savedTask); if (savedTask.getStartTime() != null) { @@ -118,9 +124,13 @@ public Epic addEpic(Epic newEpic) { return null; } - updateEpicTime(newEpic); newEpic.setId(generateNewId()); newEpic.setStatus(Status.NEW); + + if (newEpic.getDuration() == null) { + newEpic.setDuration(Duration.ZERO); + } + Epic savedEpic = newEpic.getCopy(); idToEpic.put(savedEpic.getId(), savedEpic); @@ -144,6 +154,10 @@ public Subtask addSubtask(Subtask newSubtask) { Integer subtaskId = generateNewId(); newSubtask.setId(subtaskId); + if (newSubtask.getDuration() == null) { + newSubtask.setDuration(Duration.ZERO); + } + Subtask savedSubtask = newSubtask.getCopy(); Epic epic = idToEpic.get(epicId); @@ -254,12 +268,23 @@ public Task updateTask(Task newTask) { return null; } - if (isFoundIntersections(newTask)) { + Task oldTask = idToTask.get(id); + + Task testTask = newTask.getCopy(); + + if (testTask.getDuration() == null) { + testTask.setDuration(oldTask.getDuration()); + } + + if (testTask.getStartTime() == null) { + testTask.setStartTime(oldTask.getStartTime()); + } + + if (testTask.getEndTime() != null && !findIntersections(testTask).isEmpty()) { System.out.println("Ошибка обновления задачи - это время уже занято."); return null; } - Task oldTask = idToTask.get(id); if (newTask.getTitle() != null) { oldTask.setTitle(newTask.getTitle()); @@ -273,7 +298,7 @@ public Task updateTask(Task newTask) { oldTask.setStatus(newTask.getStatus()); } - if (newTask.getDuration() != Duration.ZERO) { + if (newTask.getDuration() != null) { oldTask.setDuration(newTask.getDuration()); } @@ -306,33 +331,37 @@ public Epic updateEpic(Epic newEpic) { oldEpic.setDescription(newEpic.getDescription()); } - if (newEpic.getStartTime() != null) { - oldEpic.setStartTime(newEpic.getStartTime()); - } - newEpic = oldEpic.getCopy(); - updateEpicTime(oldEpic); - return newEpic; } /// Обновление подзадачи по объекту @Override public Subtask updateSubtask(Subtask newSubtask) { - Integer id = newSubtask.getId(); - if (id == null || !(idToSubtask.containsKey(id))) { + Integer subtaskId = newSubtask.getId(); + if (subtaskId == null || !(idToSubtask.containsKey(subtaskId))) { System.out.println("Ошибка обновления подзадачи - неверные входные данные."); return null; } - if (isFoundIntersections(newSubtask)) { + Subtask oldSubtask = idToSubtask.get(subtaskId); + + Subtask testSubtask = newSubtask.getCopy(); + + if (testSubtask.getDuration() == null) { + testSubtask.setDuration(oldSubtask.getDuration()); + } + + if (testSubtask.getStartTime() == null) { + testSubtask.setStartTime(oldSubtask.getStartTime()); + } + + if (testSubtask.getStartTime() != null && !findIntersections(testSubtask).isEmpty()) { System.out.println("Ошибка обновления подзадачи - это время уже занято."); return null; } - Subtask oldSubtask = idToSubtask.get(id); - if (newSubtask.getTitle() != null) { oldSubtask.setTitle(newSubtask.getTitle()); } @@ -345,7 +374,7 @@ public Subtask updateSubtask(Subtask newSubtask) { oldSubtask.setStatus(newSubtask.getStatus()); } - if (newSubtask.getDuration() != Duration.ZERO) { + if (newSubtask.getDuration() != null) { oldSubtask.setDuration(newSubtask.getDuration()); } @@ -357,26 +386,132 @@ public Subtask updateSubtask(Subtask newSubtask) { newSubtask = oldSubtask.getCopy(); int epicId = oldSubtask.getEpicId(); + Epic epic = idToEpic.get(epicId); + epic.addSubtaskId(subtaskId); updateEpicsStatus(epicId); - updateEpicTime(idToEpic.get(epicId)); + updateEpicTime(epic); return newSubtask; } - /// Проверка пересечений времени - private boolean isFoundIntersections(Task taskObject) { - Task savedTask = getWithoutHistory(taskObject.getId()); - Task testTask = taskObject.getCopy(); + /// Обновление продолжительности эпика + protected void updateEpicTime(Epic epic) { + if (epic.getId() == null) { + return; + } + + clearEpicTime(epic); + + List subtasks = getAllSubTasksByEpic(epic.getId()); - if (testTask.getStartTime() == null) { - testTask.setStartTime(savedTask.getStartTime()); + if (subtasks == null) { + return; } - if (testTask.getDuration() == null) { - testTask.setDuration(savedTask.getDuration()); + List subtasksWithDateTimeUnits = subtasks.stream() + .filter(subtask -> subtask.getStartTime() != null || subtask.getDuration() != null) + .toList(); + + if (subtasksWithDateTimeUnits.isEmpty()) { + return; + } + + Optional sumDuration = Optional.ofNullable(subtasksWithDateTimeUnits.stream() + .map(Subtask::getDuration) + .reduce(Duration.ZERO, Duration::plus)); + + epic.setDuration(sumDuration.orElse(Duration.ZERO)); + + List prioritizedSubtasks = subtasksWithDateTimeUnits.stream() + .filter(subtask -> subtask.getStartTime() != null) + .toList(); + + if (prioritizedSubtasks.isEmpty()) { + return; + } + + if (prioritizedSubtasks.size() == 1) { + LocalDateTime time = subtasksWithDateTimeUnits.getFirst().getStartTime(); + epic.setStartTime(time); + epic.setEndTime(time); + epic.setDuration(sumDuration.orElse(Duration.ZERO)); + return; + } + + Subtask firstSubtask = prioritizedSubtasks.stream() + .min(Comparator.comparing(Subtask::getStartTime)) + .get(); + + Subtask lastSubtask = prioritizedSubtasks.stream() + .max(Comparator.comparing(Subtask::getStartTime)) + .get(); + + epic.setStartTime(firstSubtask.getStartTime()); + epic.setEndTime(lastSubtask.getEndTime()); + } + + /// Обновление статуса эпика + protected void updateEpicsStatus(int epicId) { + Epic epic = idToEpic.get(epicId); + if (epic.getStatus() == null) { + return; + } + + int statusNullCount = 0; + ArrayList epicStatuses = new ArrayList<>(); + for (int subtaskId : epic.getSubtaskIds()) { + Subtask subtask = idToSubtask.get(subtaskId); + if (subtask.getEpicId() == epicId && subtask.getStatus() != null) { + epicStatuses.add(subtask.getStatus()); + } else if (subtask.getEpicId() == epicId && subtask.getStatus() == null) { + statusNullCount++; + epicStatuses.add(Status.NEW); + } + } + + if (epicStatuses.isEmpty() || statusNullCount == epicStatuses.size()) { + epic.setStatus(Status.NEW); + return; + } + + int statusNewCount = 0; + int statusDoneCount = 0; + for (Status status : epicStatuses) { + switch (status) { + case Status.NEW -> statusNewCount++; + case Status.DONE -> statusDoneCount++; + } + } + + if (statusNewCount == epicStatuses.size()) { + epic.setStatus(Status.NEW); + } else if (statusDoneCount == epicStatuses.size()) { + epic.setStatus(Status.DONE); + } else { + epic.setStatus(Status.IN_PROGRESS); + } + } + + /// Обнуление полей времени эпика + protected void clearEpicTime(Epic epic) { + epic.setStartTime(null); + epic.setEndTime(null); + epic.setDuration(Duration.ZERO); + } + + /// Поиск пересекающихся тасок + public List findIntersections(Task taskObject) { + if (prioritizedTasks.isEmpty() || taskObject.getStartTime() == null) { + return new ArrayList<>(); } - return !findIntersections(testTask).isEmpty(); + return prioritizedTasks.stream() + .filter(task -> !Objects.equals(task.getId(), taskObject.getId())) + .filter(task -> + (taskObject.getStartTime().isBefore(task.getEndTime()) && + task.getStartTime().isBefore(taskObject.getEndTime())) + ) + .toList(); } /// Удаление задачи по ID @@ -458,9 +593,9 @@ public void removeAllEpics() { for (int subtaskId : idToSubtask.keySet()) { historyManager.remove(subtaskId); + prioritizedTasks.removeIf(task -> task.getId() == subtaskId); } idToSubtask.clear(); - prioritizedTasks.removeIf(task -> task.getType() == Type.EPIC); } /// Удаление всех подзадач @@ -468,6 +603,7 @@ public void removeAllEpics() { public void removeAllSubTasks() { for (int subtaskId : idToSubtask.keySet()) { historyManager.remove(subtaskId); + prioritizedTasks.removeIf(task -> task.getId() == subtaskId); } idToSubtask.clear(); @@ -476,7 +612,6 @@ public void removeAllSubTasks() { updateEpicsStatus(epic.getId()); clearEpicTime(epic); } - prioritizedTasks.removeIf(task -> task.getType() == Type.SUBTASK); } /// Получить список с историей @@ -484,104 +619,4 @@ public void removeAllSubTasks() { public List getHistory() { return historyManager.getHistory(); } - - /// Обновление статуса эпика - protected void updateEpicsStatus(int epicId) { - Epic epic = idToEpic.get(epicId); - if (epic.getStatus() == null) { - return; - } - - int statusNullCount = 0; - ArrayList epicStatuses = new ArrayList<>(); - for (int subtaskId : epic.getSubtaskIds()) { - Subtask subtask = idToSubtask.get(subtaskId); - if (subtask.getEpicId() == epicId && subtask.getStatus() != null) { - epicStatuses.add(subtask.getStatus()); - } else if (subtask.getEpicId() == epicId && subtask.getStatus() == null) { - statusNullCount++; - epicStatuses.add(Status.NEW); - } - } - - if (epicStatuses.isEmpty() || statusNullCount == epicStatuses.size()) { - epic.setStatus(Status.NEW); - return; - } - - int statusNewCount = 0; - int statusDoneCount = 0; - for (Status status : epicStatuses) { - switch (status) { - case Status.NEW -> statusNewCount++; - case Status.DONE -> statusDoneCount++; - } - } - - if (statusNewCount == epicStatuses.size()) { - epic.setStatus(Status.NEW); - } else if (statusDoneCount == epicStatuses.size()) { - epic.setStatus(Status.DONE); - } else { - epic.setStatus(Status.IN_PROGRESS); - } - } - - /// Поиск пересекающихся тасок - public List findIntersections(Task taskObject) { - if (prioritizedTasks.isEmpty() || taskObject.getStartTime() == null) { - return new ArrayList<>(); - } - - return prioritizedTasks.stream() - .filter(task -> !Objects.equals(task.getId(), taskObject.getId())) - .filter(task -> - (taskObject.getStartTime().isBefore(task.getEndTime()) && - task.getStartTime().isBefore(taskObject.getEndTime())) - ) - .toList(); - } - - /// Обнуление полей времени эпика - protected void clearEpicTime(Epic epic) { - epic.setStartTime(null); - epic.setEndTime(null); - epic.setDuration(Duration.ZERO); - } - - /// Обновление продолжительности эпика - protected void updateEpicTime(Epic epic) { - if (epic.getId() == null || epic.getStartTime() == null) { - return; - } - List prioritizedSubtasks = getAllSubTasksByEpic(epic.getId()).stream() - .filter(subtask -> subtask.getStartTime() != null) - .toList(); - - if (prioritizedSubtasks.isEmpty()) { - clearEpicTime(epic); - return; - } - - Subtask firstSubtask = prioritizedSubtasks.stream() - .min(Comparator.comparing(Subtask::getStartTime)) - .get(); - - Subtask lastSubtask = prioritizedSubtasks.stream() - .max(Comparator.comparing(Subtask::getStartTime)) - .get(); - - if (firstSubtask.equals(lastSubtask)) { - clearEpicTime(epic); - epic.setStartTime(firstSubtask.getStartTime()); - } - - Optional sumDuration = Optional.ofNullable(prioritizedSubtasks.stream() - .map(Subtask::getDuration) - .reduce(Duration.ZERO, Duration::plus)); - - epic.setStartTime(firstSubtask.getStartTime()); - epic.setEndTime(lastSubtask.getEndTime()); - epic.setDuration(sumDuration.orElse(Duration.ZERO)); - } } diff --git a/test/data/EpicTest.java b/test/data/EpicTest.java index 8983e60..b4ec4b3 100644 --- a/test/data/EpicTest.java +++ b/test/data/EpicTest.java @@ -67,7 +67,7 @@ void shouldReturnCorrectStringWhenGetToString() { "status='null'" + ", startTime='null'" + ", endTime='null'" + - ", duration='0'" + + ", duration='null'" + '}', epic.toString(), "Эпик возвращает некорректную строку при вызове toString."); diff --git a/test/data/SubtaskTest.java b/test/data/SubtaskTest.java index 9e074a0..1d9db6f 100644 --- a/test/data/SubtaskTest.java +++ b/test/data/SubtaskTest.java @@ -32,7 +32,7 @@ void shouldReturnCorrectStringWhenGetToString() { "status='null'" + ", startTime='null'" + ", endTime='null'" + - ", duration='0'" + + ", duration='null'" + "}", subtask.toString(), "Подзадача возвращает некорректную строку при вызове toString."); diff --git a/test/data/TaskTest.java b/test/data/TaskTest.java index 45828b7..13c5f5d 100644 --- a/test/data/TaskTest.java +++ b/test/data/TaskTest.java @@ -48,7 +48,7 @@ void shouldReturnCorrectStringWhenGetToString() { "status='DONE'" + ", startTime='null'" + ", endTime='null'" + - ", duration='0'" + + ", duration='null'" + '}', task3.toString(), "Задача возвращает некорректную строку при вызове toString."); diff --git a/test/manager/FileBackedTaskManagerTest.java b/test/manager/FileBackedTaskManagerTest.java index 63858c2..fb6e8ca 100644 --- a/test/manager/FileBackedTaskManagerTest.java +++ b/test/manager/FileBackedTaskManagerTest.java @@ -67,7 +67,7 @@ void beforeEach() { Status.IN_PROGRESS )); - // Создать новый эпик + // Создать новый эпик id будет 2 epic = new Epic( 777, "Test addNewEpic title", @@ -77,26 +77,26 @@ void beforeEach() { // Добавить эпик в базу и получить идентификатор добавленного эпика epicId = manager.addEpic(epic).getId(); - // Добавить первую подзадачу в созданный эпик + // Добавить первую подзадачу (id будет 3) в созданный эпик manager.addSubtask(new Subtask( 777, "This is first Subtask", "This is description of first Subtask", Status.IN_PROGRESS, epicId, - null, - null + LocalDateTime.parse("2025-11-10T08:30"), + Duration.ofMinutes(70) )); - // Добавить вторую подзадачу в созданный эпик + // Добавить вторую подзадачу (id будет 4) в созданный эпик manager.addSubtask(new Subtask( null, "This is third Subtask", "This is description of third Subtask", Status.DONE, epicId, - null, - null + LocalDateTime.parse("2025-11-10T08:00"), + Duration.ofMinutes(30) )); // Получить все подзадачи diff --git a/test/manager/TaskManagerTest.java b/test/manager/TaskManagerTest.java index ac48ba4..fa2186e 100644 --- a/test/manager/TaskManagerTest.java +++ b/test/manager/TaskManagerTest.java @@ -70,7 +70,7 @@ void beforeEach() { Status.IN_PROGRESS )); - // Создать новый эпик + // Создать новый эпик id будет 2 epic = new Epic( 777, "Test addNewEpic title", @@ -80,26 +80,26 @@ void beforeEach() { // Добавить эпик в базу и получить идентификатор добавленного эпика epicId = manager.addEpic(epic).getId(); - // Добавить первую подзадачу в созданный эпик + // Добавить первую подзадачу (id будет 3) в созданный эпик manager.addSubtask(new Subtask( 777, "This is first Subtask", "This is description of first Subtask", Status.IN_PROGRESS, epicId, - null, - null + LocalDateTime.parse("2025-11-10T08:30"), + Duration.ofMinutes(70) )); - // Добавить вторую подзадачу в созданный эпик + // Добавить вторую подзадачу (id будет 4) в созданный эпик manager.addSubtask(new Subtask( null, "This is third Subtask", "This is description of third Subtask", Status.DONE, epicId, - null, - null + LocalDateTime.parse("2025-11-10T08:00"), + Duration.ofMinutes(30) )); // Получить все подзадачи @@ -315,7 +315,7 @@ void shouldReturnThreeAndFourWhenGetAllSubtasksIdsByEpic() { @Test void shouldReturnNullWhenTryingToGetAllSubtasks() { - assertNull( manager.getAllSubTasksByEpic(100), + assertNull(manager.getAllSubTasksByEpic(100), "Менеджер не возвращает null при попытке получения всех подзадач несуществующего эпика"); } @@ -379,7 +379,7 @@ void shouldReturnTwoWhenAllTasksIsDeleted() { manager.removeAllTasks(); assertEquals(2, manager.getHistory().getFirst().getId(), "При удалении всех задач они не удаляются из истории" - ); + ); } @Test @@ -420,16 +420,79 @@ void shouldReturnTwoWhenAllSubtasksByEpicIsDeleted() { @Test void shouldReturnNullWhenAddIntersectionTasks() { assertNull(manager.addTask(new Task(null, null, null, null, - LocalDateTime.parse("2025-11-11T10:30"), Duration.ofMinutes(50))), + LocalDateTime.parse("2025-11-11T10:30"), Duration.ofMinutes(50))), "Менеджер добавляет пересекающиеся задачи по верхней границе."); assertNull(manager.addTask(new Task(null, null, null, null, - LocalDateTime.parse("2025-11-11T11:15"), Duration.ofMinutes(10))), + LocalDateTime.parse("2025-11-11T11:15"), Duration.ofMinutes(10))), "Менеджер добавляет пересекающиеся задачи внутрь временного контура."); assertNull(manager.addTask(new Task(null, null, null, null, - LocalDateTime.parse("2025-11-11T11:30"), Duration.ofMinutes(50))), + LocalDateTime.parse("2025-11-11T11:30"), Duration.ofMinutes(50))), "Менеджер добавляет пересекающиеся задачи по нижней границе."); } + @Test + void shouldReturnExpectedDateTimeOfEpicAfterAddPrioritizedSubtasks() { + assertEquals("2025-11-10T08:00", manager.getEpic(2).getStartTime().toString(), + "Менеджер не правильно считает время старта эпика."); + + assertEquals("2025-11-10T09:40", manager.getEpic(2).getEndTime().toString(), + "Менеджер не правильно считает завершения старта эпика."); + + assertEquals(100, manager.getEpic(2).getDuration().toMinutes(), + "Менеджер не правильно считает общее время эпика."); + } + + @Test + void shouldReturnExpectedDateTimeAfterUpdateSubtask() { + assertEquals("2025-11-10T08:00", manager.updateSubtask(new Subtask( + 3, null, null, null, null, + LocalDateTime.parse("2025-11-10T06:50"), null)).getEndTime().toString(), + "Менеджер возвращает подзадачу с некорректной датой завершения после обновления даты старта"); + + assertEquals("2025-11-10T06:50", manager.updateSubtask(new Subtask( + 3, null, null, null, null, + null, Duration.ZERO)).getEndTime().toString(), + "Менеджер возвращает подзадачу с некорректной датой завершения после обновления срока"); + } + + @Test + void shouldReturnExpectedDateTimeOfEpicAfterUpdateSubtasksDate() { + manager.updateSubtask(new Subtask( + 3, null, null, null, null, + LocalDateTime.parse("2025-11-10T18:00"), null)); + + assertEquals("2025-11-10T08:00", manager.getEpic(2).getStartTime().toString(), + "Менеджер возвращает не верную дату начала эпика после обновления его подзадачи."); + assertEquals("2025-11-10T19:10", manager.getEpic(2).getEndTime().toString(), + "Менеджер возвращает не верную дату завершения эпика после обновления его подзадачи."); + assertEquals(100, manager.getEpic(2).getDuration().toMinutes(), + "Менеджер возвращает не верный срок эпика после обновления его подзадачи."); + + manager.addSubtask(new Subtask( + null, + null, + null, + null, + 2, + null, + null + )); + + manager.updateSubtask(new Subtask(5, null, null, null, null, + LocalDateTime.parse("2025-11-10T19:10"), + null + )); + + manager.updateSubtask(new Subtask(5, null, null, null, null, + null, + Duration.ofMinutes(140) + )); + + assertEquals(240, manager.getEpic(2).getDuration().toMinutes(), + "Менеджер возвращает не верный срок эпика после добавления в него подзадачи."); + + } + }