Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
06767c9
feat: add duration and start time to Task
ksupyl Apr 14, 2026
7853dc7
feat: add duration and start time to Subtask
ksupyl Apr 14, 2026
d1eda1c
feat: add calculated time fields to Epic
ksupyl Apr 14, 2026
a4aaf79
fix: use defensive copy in Epic setSubtaskIds
ksupyl Apr 14, 2026
6a40426
merge: bring file manager fixes into time-and-duration branch
ksupyl Apr 15, 2026
c7c5c46
fix: return full copies of tasks with time fields
ksupyl Apr 16, 2026
7e81a47
feat: add prioritized tasks storage with TreeSet
ksupyl Apr 16, 2026
3b4b597
feat: add task time overlap validation
ksupyl Apr 16, 2026
07ab5ea
feat: calculate epic duration and time based on subtasks
ksupyl Apr 16, 2026
7ae11d2
feat: save and load task time fields in csv
ksupyl Apr 16, 2026
5374a8e
refactor: replace loops with stream api in task processing
ksupyl Apr 16, 2026
9b730bd
fix: save time fields in history snapshots
ksupyl Apr 16, 2026
033fdef
test: add base task manager test class
ksupyl Apr 16, 2026
d41c8c4
test: simplify in-memory task manager test setup
ksupyl Apr 16, 2026
3c9ecb5
test: add file-backed task manager persistence tests
ksupyl Apr 16, 2026
e430a7a
test: add history manager edge case tests
ksupyl Apr 16, 2026
7cc4bc5
fix: preserve calculated fields in Epic during update
ksupyl Apr 16, 2026
727eee0
fix: prevent creating subtask without existing epic
ksupyl Apr 16, 2026
9f37d13
fix: validate epic existence in updateSubtask()
ksupyl Apr 16, 2026
d03d017
refactor: preserve task and subtask status on creation
ksupyl Apr 16, 2026
3c5bf44
refactor: improve safety in getEpicSubtasks()
ksupyl Apr 16, 2026
f0c8de0
test: add tests for clear operations in TaskManager
ksupyl Apr 16, 2026
ea4d861
test: add tests for update operations in TaskManager
ksupyl Apr 16, 2026
1f552a0
test: add tests for delete operations
ksupyl Apr 16, 2026
7b13134
test: add edge case tests for entity lookup in TaskManager
ksupyl Apr 16, 2026
74db527
test: add history and subtask validation tests in TaskManager
ksupyl Apr 16, 2026
fac189f
test: add overlap validation tests for tasks and subtasks
ksupyl Apr 16, 2026
c059c85
fix: preserve epic connection when updating subtask
ksupyl Apr 16, 2026
1e5ee6f
test: add tests for epic subtasks and prioritized tasks
ksupyl Apr 16, 2026
1eb78bb
refactor: move null time validation to hasTimeOverlap
ksupyl Apr 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 32 additions & 9 deletions src/model/Epic.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package model;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;

public class Epic extends Task {
private ArrayList<Integer> subtaskIds = new ArrayList<>();
private LocalDateTime endTime;

public Epic(String name, String description) {
super(name, description, Status.NEW);
Expand Down Expand Up @@ -34,19 +37,39 @@ public void removeSubtaskId(Integer subtaskId) {
subtaskIds.remove(subtaskId);
}

public void setEpicDuration(Duration duration) {
setDuration(duration);
}

public void setEpicStartTime(LocalDateTime startTime) {
setStartTime(startTime);
}

public void setEpicEndTime(LocalDateTime endTime) {
this.endTime = endTime;
}

@Override
public LocalDateTime getEndTime() {
return endTime;
}

@Override
public TaskType getType() {
return TaskType.EPIC;
}

@Override
public String toString() {
return "Epic{" +
"id=" + getId() +
", name='" + getName() + '\'' +
", description='" + getDescription() + '\'' +
", status=" + getStatus() +
", subtaskIds=" + subtaskIds +
'}';
}
}
return "Epic{"
+ "id=" + getId()
+ ", name='" + getName() + '\''
+ ", description='" + getDescription() + '\''
+ ", status=" + getStatus()
+ ", duration=" + getDuration()
+ ", startTime=" + getStartTime()
+ ", endTime=" + getEndTime()
+ ", subtaskIds=" + subtaskIds
+ '}';
}
}
26 changes: 18 additions & 8 deletions src/model/Subtask.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
package model;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Objects;

public class Subtask extends Task {
private int epicId;

public Subtask(String name, String description, Status status, int epicId) {
super(name, description, status);
this(name, description, status, null, null, epicId);
}

public Subtask(String name, String description, Status status,
Duration duration, LocalDateTime startTime, int epicId) {
super(name, description, status, duration, startTime);
this.epicId = epicId;
}

Expand Down Expand Up @@ -49,12 +56,15 @@ public int hashCode() {

@Override
public String toString() {
return "Subtask{" +
"id=" + getId() +
", name='" + getName() + '\'' +
", description='" + getDescription() + '\'' +
", status=" + getStatus() +
", epicId=" + epicId +
'}';
return "Subtask{"
+ "id=" + getId()
+ ", name='" + getName() + '\''
+ ", description='" + getDescription() + '\''
+ ", status=" + getStatus()
+ ", duration=" + getDuration()
+ ", startTime=" + getStartTime()
+ ", endTime=" + getEndTime()
+ ", epicId=" + epicId
+ '}';
}
}
36 changes: 36 additions & 0 deletions src/model/Task.java
Original file line number Diff line number Diff line change
@@ -1,17 +1,27 @@
package model;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Objects;

public class Task {
private int id;
private String name;
private String description;
private Status status;
private Duration duration;
private LocalDateTime startTime;

public Task(String name, String description, Status status) {
this(name, description, status, null, null);
}

public Task(String name, String description, Status status, Duration duration, LocalDateTime startTime) {
this.name = name;
this.description = description;
this.status = status;
this.duration = duration;
this.startTime = startTime;
}

public TaskType getType() {
Expand Down Expand Up @@ -50,6 +60,29 @@ public void setStatus(Status status) {
this.status = status;
}

public Duration getDuration() {
return duration;
}

public void setDuration(Duration duration) {
this.duration = duration;
}

public LocalDateTime getStartTime() {
return startTime;
}

public void setStartTime(LocalDateTime startTime) {
this.startTime = startTime;
}

public LocalDateTime getEndTime() {
if (startTime == null || duration == null) {
return null;
}
return startTime.plus(duration);
}

// Переопределение методов equals и hashCode для ID
@Override
public boolean equals(Object obj) {
Expand All @@ -71,6 +104,9 @@ public String toString() {
+ ", name='" + name + '\''
+ ", description='" + description + '\''
+ ", status=" + status
+ ", duration=" + duration
+ ", startTime=" + startTime
+ ", endTime=" + getEndTime()
+ '}';
}
}
37 changes: 29 additions & 8 deletions src/service/FileBackedTaskManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.TreeMap;

public class FileBackedTaskManager extends InMemoryTaskManager {
Expand All @@ -24,7 +26,7 @@ public FileBackedTaskManager(File file) {
// Сохраняет текущее состояние менеджера в CSV-файл
private void save() {
try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) {
writer.write("id,type,name,status,description,epic");
writer.write("id,type,name,status,description,duration,startTime,epic");
writer.newLine();

TreeMap<Integer, Task> allTasks = new TreeMap<>();
Expand All @@ -50,26 +52,35 @@ private void save() {
}
}

// Преобразует задачу в строку формата CSV
// Преобразует задачу в строку формата CSV с учетом времени и продолжительности
private String toString(Task task) {
StringBuilder builder = new StringBuilder();

builder.append(task.getId()).append(",");
builder.append(task.getType()).append(",");

builder.append(task.getName()).append(",");
builder.append(task.getStatus()).append(",");
builder.append(task.getDescription()).append(",");

if (task instanceof Subtask) {
if (task.getDuration() != null) {
builder.append(task.getDuration().toMinutes());
}
builder.append(",");

if (task.getStartTime() != null) {
builder.append(task.getStartTime());
}
builder.append(",");

if (task.getType() == TaskType.SUBTASK) {
Subtask subtask = (Subtask) task;
builder.append(subtask.getEpicId());
}

return builder.toString();
}

// Преобразует строку CSV в объект задачи
// Преобразует строку CSV в объект задачи с учетом времени и продолжительности
private static Task fromString(String value) {
String[] fields = value.split(",", -1);

Expand All @@ -79,16 +90,26 @@ private static Task fromString(String value) {
Status status = Status.valueOf(fields[3]);
String description = fields[4];

Duration duration = null;
if (!fields[5].isBlank()) {
duration = Duration.ofMinutes(Long.parseLong(fields[5]));
}

LocalDateTime startTime = null;
if (!fields[6].isBlank()) {
startTime = LocalDateTime.parse(fields[6]);
}

Task task;

if (taskType == TaskType.TASK) {
task = new Task(name, description, status);
task = new Task(name, description, status, duration, startTime);
} else if (taskType == TaskType.EPIC) {
task = new Epic(name, description);
task.setStatus(status);
} else {
int epicId = Integer.parseInt(fields[5]);
task = new Subtask(name, description, status, epicId);
int epicId = Integer.parseInt(fields[7]);
task = new Subtask(name, description, status, duration, startTime, epicId);
}

task.setId(id);
Expand Down
13 changes: 12 additions & 1 deletion src/service/InMemoryHistoryManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ private Task makeSnapshot(Task task) {
original.getName(),
original.getDescription(),
original.getStatus(),
original.getDuration(),
original.getStartTime(),
original.getEpicId()
);
copy.setId(original.getId());
Expand All @@ -35,9 +37,18 @@ private Task makeSnapshot(Task task) {
copy.setId(original.getId());
copy.setStatus(original.getStatus());
copy.setSubtaskIds(original.getSubtaskIds());
copy.setEpicDuration(original.getDuration());
copy.setEpicStartTime(original.getStartTime());
copy.setEpicEndTime(original.getEndTime());
return copy;
} else {
Task copy = new Task(task.getName(), task.getDescription(), task.getStatus());
Task copy = new Task(
task.getName(),
task.getDescription(),
task.getStatus(),
task.getDuration(),
task.getStartTime()
);
copy.setId(task.getId());
return copy;
}
Expand Down
Loading
Loading