feat: add periodStats to dashboard output
Adiciona breakdown diário/mensal ao endpoint /dashboard do usuário: - Novo record DashboardPeriodStats com year, month, day, finishedTasksCount e totalMinutesWorked - DashboardUseCase agrupa history items por LocalDate para minutos e finishedAt para contagem de tarefas - Lógica groupedBy: ≤62 dias → DAY, caso contrário → MONTH Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
15118177a2
commit
1b337e53c2
3 changed files with 57 additions and 13 deletions
|
|
@ -8,5 +8,7 @@ public record DashboardOutput(
|
||||||
List<DashboardProjectStats> projectStats,
|
List<DashboardProjectStats> projectStats,
|
||||||
List<DashboardCategoryStats> categoryStats,
|
List<DashboardCategoryStats> categoryStats,
|
||||||
List<CreateTaskOutput> yesterdayTasks,
|
List<CreateTaskOutput> yesterdayTasks,
|
||||||
List<CreateTaskOutput> finishedTasksInPeriod
|
List<CreateTaskOutput> finishedTasksInPeriod,
|
||||||
|
List<DashboardPeriodStats> periodStats,
|
||||||
|
String groupedBy
|
||||||
) {}
|
) {}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
package net.beetechgroup.beetask.usecase.dashboard;
|
||||||
|
|
||||||
|
public record DashboardPeriodStats(
|
||||||
|
int year,
|
||||||
|
int month,
|
||||||
|
int day,
|
||||||
|
int finishedTasksCount,
|
||||||
|
long totalMinutesWorked
|
||||||
|
) {}
|
||||||
|
|
@ -1,13 +1,17 @@
|
||||||
package net.beetechgroup.beetask.usecase.dashboard;
|
package net.beetechgroup.beetask.usecase.dashboard;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.LocalTime;
|
import java.time.LocalTime;
|
||||||
|
import java.time.temporal.ChronoUnit;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
import net.beetechgroup.beetask.entities.Category;
|
import net.beetechgroup.beetask.entities.Category;
|
||||||
import net.beetechgroup.beetask.entities.task.Task;
|
import net.beetechgroup.beetask.entities.task.Task;
|
||||||
|
|
@ -45,28 +49,36 @@ public class DashboardUseCase {
|
||||||
long totalMinutes = 0;
|
long totalMinutes = 0;
|
||||||
Map<Long, DashboardProjectStats> projectStatsMap = new HashMap<>();
|
Map<Long, DashboardProjectStats> projectStatsMap = new HashMap<>();
|
||||||
Map<Long, DashboardCategoryStats> categoryStatsMap = new HashMap<>();
|
Map<Long, DashboardCategoryStats> categoryStatsMap = new HashMap<>();
|
||||||
|
// [0] = totalMinutesWorked, [1] = finishedTasksCount grouped by day key
|
||||||
|
Map<LocalDate, long[]> periodMap = new TreeMap<>();
|
||||||
|
|
||||||
for (Task task : workedTasks) {
|
for (Task task : workedTasks) {
|
||||||
long taskMinutesInPeriod = task.getHistory().stream()
|
for (TaskHistoryItem h : task.getHistory()) {
|
||||||
.filter(h -> isWithinPeriod(h, input.startDate(), input.endDate()))
|
if (!isWithinPeriod(h, input.startDate(), input.endDate())) continue;
|
||||||
.mapToLong(h -> calculateMinutesInRange(h, input.startDate(), input.endDate()))
|
long minutes = calculateMinutesInRange(h, input.startDate(), input.endDate());
|
||||||
.sum();
|
totalMinutes += minutes;
|
||||||
|
LocalDate day = h.getStartAt().toLocalDate();
|
||||||
totalMinutes += taskMinutesInPeriod;
|
long[] bucket = periodMap.computeIfAbsent(day, k -> new long[]{0L, 0L});
|
||||||
|
bucket[0] += minutes;
|
||||||
|
}
|
||||||
|
|
||||||
if (Objects.nonNull(task.getProject())) {
|
if (Objects.nonNull(task.getProject())) {
|
||||||
|
long taskMinutesInPeriod = task.getHistory().stream()
|
||||||
|
.filter(h -> isWithinPeriod(h, input.startDate(), input.endDate()))
|
||||||
|
.mapToLong(h -> calculateMinutesInRange(h, input.startDate(), input.endDate()))
|
||||||
|
.sum();
|
||||||
Long projectId = task.getProject().getId();
|
Long projectId = task.getProject().getId();
|
||||||
DashboardProjectStats stats = projectStatsMap.getOrDefault(projectId,
|
DashboardProjectStats stats = projectStatsMap.getOrDefault(projectId,
|
||||||
new DashboardProjectStats(projectId, task.getProject().getName(), 0L));
|
new DashboardProjectStats(projectId, task.getProject().getName(), 0L));
|
||||||
|
|
||||||
projectStatsMap.put(projectId, new DashboardProjectStats(
|
projectStatsMap.put(projectId, new DashboardProjectStats(
|
||||||
projectId,
|
projectId, stats.projectName(), stats.totalMinutes() + taskMinutesInPeriod));
|
||||||
stats.projectName(),
|
|
||||||
stats.totalMinutes() + taskMinutesInPeriod
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Objects.nonNull(task.getCategory())) {
|
if (Objects.nonNull(task.getCategory())) {
|
||||||
|
long taskMinutesInPeriod = task.getHistory().stream()
|
||||||
|
.filter(h -> isWithinPeriod(h, input.startDate(), input.endDate()))
|
||||||
|
.mapToLong(h -> calculateMinutesInRange(h, input.startDate(), input.endDate()))
|
||||||
|
.sum();
|
||||||
Category cat = task.getCategory();
|
Category cat = task.getCategory();
|
||||||
DashboardCategoryStats existing = categoryStatsMap.getOrDefault(cat.getId(),
|
DashboardCategoryStats existing = categoryStatsMap.getOrDefault(cat.getId(),
|
||||||
new DashboardCategoryStats(cat.getId(), cat.getName(), cat.getColor(), cat.getIcon(), 0L));
|
new DashboardCategoryStats(cat.getId(), cat.getName(), cat.getColor(), cat.getIcon(), 0L));
|
||||||
|
|
@ -76,12 +88,33 @@ public class DashboardUseCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (Task task : finishedTasks) {
|
||||||
|
if (Objects.isNull(task.getFinishedAt())) continue;
|
||||||
|
LocalDate day = task.getFinishedAt().toLocalDate();
|
||||||
|
long[] bucket = periodMap.computeIfAbsent(day, k -> new long[]{0L, 0L});
|
||||||
|
bucket[1]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
long daySpan = ChronoUnit.DAYS.between(input.startDate().toLocalDate(), input.endDate().toLocalDate());
|
||||||
|
String groupedBy = daySpan <= 62 ? "DAY" : "MONTH";
|
||||||
|
|
||||||
|
List<DashboardPeriodStats> periodStats = periodMap.entrySet().stream()
|
||||||
|
.map(e -> new DashboardPeriodStats(
|
||||||
|
e.getKey().getYear(), e.getKey().getMonthValue(), e.getKey().getDayOfMonth(),
|
||||||
|
(int) e.getValue()[1], e.getValue()[0]))
|
||||||
|
.sorted(Comparator.comparingInt(DashboardPeriodStats::year)
|
||||||
|
.thenComparingInt(DashboardPeriodStats::month)
|
||||||
|
.thenComparingInt(DashboardPeriodStats::day))
|
||||||
|
.toList();
|
||||||
|
|
||||||
DashboardOutput output = new DashboardOutput(
|
DashboardOutput output = new DashboardOutput(
|
||||||
totalMinutes,
|
totalMinutes,
|
||||||
new ArrayList<>(projectStatsMap.values()),
|
new ArrayList<>(projectStatsMap.values()),
|
||||||
new ArrayList<>(categoryStatsMap.values()),
|
new ArrayList<>(categoryStatsMap.values()),
|
||||||
yesterdayTasks.stream().map(CreateTaskMapper::toCreateTaskOutput).toList(),
|
yesterdayTasks.stream().map(CreateTaskMapper::toCreateTaskOutput).toList(),
|
||||||
finishedTasks.stream().map(CreateTaskMapper::toCreateTaskOutput).toList()
|
finishedTasks.stream().map(CreateTaskMapper::toCreateTaskOutput).toList(),
|
||||||
|
periodStats,
|
||||||
|
groupedBy
|
||||||
);
|
);
|
||||||
LOGGER.infof("Dashboard calculated for user %s with %d worked tasks, %d finished tasks and %d total minutes",
|
LOGGER.infof("Dashboard calculated for user %s with %d worked tasks, %d finished tasks and %d total minutes",
|
||||||
input.userEmail(), workedTasks.size(), finishedTasks.size(), totalMinutes);
|
input.userEmail(), workedTasks.size(), finishedTasks.size(), totalMinutes);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue