diff --git a/backend/src/main/java/core/api/controller/StatisticController.java b/backend/src/main/java/core/api/controller/StatisticController.java index 36e2e0e..ee66710 100644 --- a/backend/src/main/java/core/api/controller/StatisticController.java +++ b/backend/src/main/java/core/api/controller/StatisticController.java @@ -5,10 +5,7 @@ import core.api.models.timemanager.history.TaskgroupActivityInfo; import core.api.models.timemanager.history.WorkingStatus; import core.entities.timemanager.AbstractSchedule; import core.entities.timemanager.Taskgroup; -import core.services.PermissionResult; -import core.services.ServiceExitCode; -import core.services.TaskScheduleService; -import core.services.TaskgroupService; +import core.services.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.security.core.context.SecurityContextHolder; @@ -24,7 +21,7 @@ import java.util.*; public class StatisticController { @Autowired private TaskScheduleService taskScheduleService; - @Autowired private TaskgroupService taskgroupService; + @Autowired private StatisticService statisticService; @GetMapping("/history/workingStatus") public ResponseEntity getWorkingStatus() { @@ -42,24 +39,14 @@ public class StatisticController { return ResponseEntity.ok(new WorkingStatus(missedSchedules, activeTime)); } - @GetMapping("/statistics/taskgroup-activity/{taskgroupID}/{startingDate}/{endingDate}/{includeSubTaskgroups}") - public ResponseEntity getTaskgroupActivity(@PathVariable long taskgroupID, @PathVariable String startingDate, @PathVariable String endingDate, @PathVariable boolean includeSubTaskgroups){ - PermissionResult taskgroupPermissionResult = taskgroupService.getTaskgroupByIDAndUsername(taskgroupID, SecurityContextHolder.getContext().getAuthentication().getName()); - if(taskgroupPermissionResult.isNoPermissions()) { - return ResponseEntity.status(403).body(new SimpleStatusResponse("failed")); - } else if(taskgroupPermissionResult.getExitCode() == ServiceExitCode.MISSING_ENTITY) { - return ResponseEntity.status(404).body(new SimpleStatusResponse("failed")); - } + @GetMapping("/statistics/{startingDate}/{endingDate}/{includeSubTaskgroups}") + public ResponseEntity getTaskgroupActivity(@PathVariable String startingDate, @PathVariable String endingDate){ + LocalDate starting = LocalDate.parse(startingDate, DateTimeFormatter.ofPattern("yyyy-MM-dd")); + LocalDate ending = LocalDate.parse(endingDate, DateTimeFormatter.ofPattern("yyyy-MM-dd")); - List activityInfos = taskgroupPermissionResult.getResult().calcActivityInfo(includeSubTaskgroups, - LocalDate.parse(startingDate, DateTimeFormatter.ofPattern("yyyy-MM-dd")), LocalDate.parse(endingDate, DateTimeFormatter.ofPattern("yyyy-MM-dd"))); - activityInfos.sort(new Comparator() { - @Override - public int compare(TaskgroupActivityInfo o1, TaskgroupActivityInfo o2) { - return o1.getDate().compareTo(o2.getDate()); - } - }); - return ResponseEntity.ok(activityInfos); + var taskgroupActivityInfos = statisticService.calcActivityByUser(SecurityContextHolder.getContext().getAuthentication().getName(), starting, ending); + List outgoingResult = StatisticService.convertInternActivityInfo(taskgroupActivityInfos); + return ResponseEntity.ok(outgoingResult); } @GetMapping("/history/schedules/{date}") diff --git a/backend/src/main/java/core/api/models/timemanager/history/ActivityInfo.java b/backend/src/main/java/core/api/models/timemanager/history/ActivityInfo.java new file mode 100644 index 0000000..44d0b5a --- /dev/null +++ b/backend/src/main/java/core/api/models/timemanager/history/ActivityInfo.java @@ -0,0 +1,30 @@ +package core.api.models.timemanager.history; + +import java.time.LocalDate; + +public class ActivityInfo { + + private LocalDate scheduleDate; + private int workedMinutes; + + public ActivityInfo(LocalDate scheduleDate, int workedMinutes) { + this.scheduleDate = scheduleDate; + this.workedMinutes = workedMinutes; + } + + public LocalDate getScheduleDate() { + return scheduleDate; + } + + public void setScheduleDate(LocalDate scheduleDate) { + this.scheduleDate = scheduleDate; + } + + public int getWorkedMinutes() { + return workedMinutes; + } + + public void setWorkedMinutes(int workedMinutes) { + this.workedMinutes = workedMinutes; + } +} diff --git a/backend/src/main/java/core/api/models/timemanager/history/TaskgroupActivityInfo.java b/backend/src/main/java/core/api/models/timemanager/history/TaskgroupActivityInfo.java index 51a6d73..fd164df 100644 --- a/backend/src/main/java/core/api/models/timemanager/history/TaskgroupActivityInfo.java +++ b/backend/src/main/java/core/api/models/timemanager/history/TaskgroupActivityInfo.java @@ -4,25 +4,23 @@ import com.fasterxml.jackson.annotation.JsonProperty; import core.api.models.timemanager.taskgroup.TaskgroupEntityInfo; import java.time.LocalDate; +import java.util.List; public class TaskgroupActivityInfo { - @JsonProperty - private LocalDate date; + private TaskgroupEntityInfo taskgroup; + private List activityInfos; - @JsonProperty - private int activeMinutes; - - public TaskgroupActivityInfo(int activeMinutes, LocalDate localDate) { - this.date = localDate; - this.activeMinutes = activeMinutes; + public TaskgroupActivityInfo(TaskgroupEntityInfo taskgroup, List activityInfos) { + this.taskgroup = taskgroup; + this.activityInfos = activityInfos; } - public LocalDate getDate() { - return date; + public TaskgroupEntityInfo getTaskgroup() { + return taskgroup; } - public int getActiveMinutes() { - return activeMinutes; + public List getActivityInfos() { + return activityInfos; } } diff --git a/backend/src/main/java/core/entities/timemanager/Taskgroup.java b/backend/src/main/java/core/entities/timemanager/Taskgroup.java index ffea384..26a6861 100644 --- a/backend/src/main/java/core/entities/timemanager/Taskgroup.java +++ b/backend/src/main/java/core/entities/timemanager/Taskgroup.java @@ -136,54 +136,5 @@ public class Taskgroup { return Objects.hash(taskgroupID); } - public List calcActivityInfo(boolean includeChildTasks, LocalDate start, LocalDate end) { - HashMap activityInfos = new HashMap<>(); - if(includeChildTasks) { - Queue queue = new LinkedList<>(children); - while(!queue.isEmpty()) { - Taskgroup childTraskgroup = queue.poll(); - LocalDate currentDate = start; - while(!currentDate.isAfter(end)) { - int activeMinutes = 0; - for(Task task : childTraskgroup.getTasks()) { - activeMinutes += task.calcOverallActivityInfo(currentDate); - } - - if(activityInfos.containsKey(currentDate)) { - activityInfos.put(currentDate, activityInfos.get(currentDate) + activeMinutes); - } else { - activityInfos.put(currentDate, activeMinutes); - } - - currentDate = currentDate.plusDays(1); - } - - - queue.addAll(childTraskgroup.getChildren()); - } - } - - LocalDate currentDate = start; - while(!currentDate.isAfter(end)) { - int activeMinutes = 0; - for(Task task : tasks) { - activeMinutes += task.calcOverallActivityInfo(currentDate); - } - - if(activityInfos.containsKey(currentDate)) { - activityInfos.put(currentDate, activityInfos.get(currentDate) + activeMinutes); - } else { - activityInfos.put(currentDate, activeMinutes); - } - - currentDate = currentDate.plusDays(1); - } - - List taskgroupActivityInfos = new ArrayList<>(); - for(Map.Entry entry : activityInfos.entrySet()) { - taskgroupActivityInfos.add(new TaskgroupActivityInfo(entry.getValue(), entry.getKey())); - } - return taskgroupActivityInfos; - } } diff --git a/backend/src/main/java/core/repositories/timemanager/ScheduleRepository.java b/backend/src/main/java/core/repositories/timemanager/ScheduleRepository.java index da5e43a..fe3ada9 100644 --- a/backend/src/main/java/core/repositories/timemanager/ScheduleRepository.java +++ b/backend/src/main/java/core/repositories/timemanager/ScheduleRepository.java @@ -27,4 +27,7 @@ public interface ScheduleRepository extends CrudRepository getAllFinishedSchedulesByUser(String username); } diff --git a/backend/src/main/java/core/services/StatisticService.java b/backend/src/main/java/core/services/StatisticService.java new file mode 100644 index 0000000..36f29f0 --- /dev/null +++ b/backend/src/main/java/core/services/StatisticService.java @@ -0,0 +1,84 @@ +package core.services; + +import core.api.models.timemanager.history.ActivityInfo; +import core.api.models.timemanager.history.TaskgroupActivityInfo; +import core.api.models.timemanager.taskgroup.TaskgroupEntityInfo; +import core.entities.timemanager.AbstractSchedule; +import core.entities.timemanager.Taskgroup; +import core.repositories.timemanager.ScheduleRepository; +import core.repositories.timemanager.TaskgroupRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.time.Duration; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.*; + +@Service +public class StatisticService { + + @Autowired private ScheduleRepository scheduleRepository; + + public HashMap> calcActivityByUser(String username, LocalDate startinDate, LocalDate endingDate) { + HashMap> taskgroupActivityInfos = new HashMap<>(); + List startedSchedules = scheduleRepository.getAllFinishedSchedulesByUser(username); + for(AbstractSchedule schedule : startedSchedules) { + Taskgroup topTaskgroup = findTopTaskgroup(schedule.getTask().getTaskgroup()); + HashMap taskgroupActivity; + if(taskgroupActivityInfos.containsKey(topTaskgroup)) { + taskgroupActivity = taskgroupActivityInfos.get(topTaskgroup); + } else { + taskgroupActivity = new HashMap<>(); + taskgroupActivityInfos.put(topTaskgroup, taskgroupActivity); + } + + if(schedule.getStartTime().toLocalDate().isEqual(schedule.getStopTime().toLocalDate())) { + insertActivity(taskgroupActivity, schedule.getStartTime().toLocalDate(), schedule.getActiveTime()); + } else { + //Starting Date + LocalDateTime startingDayEnd = schedule.getStartTime().toLocalDate().atTime(LocalTime.MAX); + Duration startingActivity = Duration.between(schedule.getStartTime(), startingDayEnd); + insertActivity(taskgroupActivity, schedule.getStartTime().toLocalDate(), (int) startingActivity.toMinutes()); + + //Ending Date + LocalDateTime endingDayStart = schedule.getStopTime().toLocalDate().atStartOfDay(); + Duration endingActivity = Duration.between(endingDayStart, schedule.getStopTime()); + insertActivity(taskgroupActivity, schedule.getStopTime().toLocalDate(), (int) endingActivity.toMinutes()); + } + } + + return taskgroupActivityInfos; + } + + public static List convertInternActivityInfo(HashMap> taskgroupActivity) { + List taskgroupActivityInfos = new ArrayList<>(); + for(Map.Entry> entry: taskgroupActivity.entrySet()) { + List activityInfos = new ArrayList<>(); + for(Map.Entry dateActivity : entry.getValue().entrySet()) { + activityInfos.add(new ActivityInfo(dateActivity.getKey(), dateActivity.getValue())); + } + taskgroupActivityInfos.add(new TaskgroupActivityInfo(new TaskgroupEntityInfo(entry.getKey()), activityInfos)); + } + return taskgroupActivityInfos; + } + + private static void insertActivity(HashMap activityInfo, LocalDate date, int deltaActivity) { + if(activityInfo.containsKey(date)) { + int activity = activityInfo.get(date); + activity += deltaActivity; + activityInfo.put(date, activity); + } else { + activityInfo.put(date, deltaActivity); + } + } + + private Taskgroup findTopTaskgroup(Taskgroup taskgroup) { + Taskgroup currentTaskgroup = taskgroup; + while(currentTaskgroup.getParent() != null) { + currentTaskgroup = currentTaskgroup.getParent(); + } + return currentTaskgroup; + } +} diff --git a/openapi.yaml b/openapi.yaml index 4df888a..afe6415 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -2060,20 +2060,13 @@ paths: schema: type: object $ref: "#/components/schemas/SimpleStatusResponse" - /statistics/taskgroup-activity/{taskgroupID}/{startingDate}/{endingDate}/{includeSubTaskgroups}: + /statistics/{startingDate}/{endingDate}: get: security: - API_TOKEN: [] tags: - history parameters: - - name: taskgroupID - in: path - description: internal id of taskgroup - required: true - schema: - type: number - example: 1 - name: startingDate in: path description: starting date @@ -2088,13 +2081,6 @@ paths: schema: type: string format: date - - name: includeSubTaskgroups - in: path - description: determines whether to include subtaskgroups or not - required: true - schema: - type: boolean - example: false responses: 200: description: Operation successfull @@ -2948,7 +2934,7 @@ components: rootTasktroup: type: object $ref: '#/components/schemas/TaskgroupEntityInfo' - TaskgroupActivityInfo: + ActivityInfo: required: - date - activeMinutes @@ -2961,6 +2947,19 @@ components: type: number description: Number of minutes the task was active example: 122 + TaskgroupActivityInfo: + required: + - taskgroup + - activityInfos + additionalProperties: false + properties: + taskgroup: + type: object + $ref: '#/components/schemas/TaskgroupEntityInfo' + activityInfos: + type: array + items: + $ref: '#/components/schemas/ActivityInfo' ManualScheduleStopInfo: required: - duration