fix-statistics #110
@ -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<Taskgroup> 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<TaskgroupActivityInfo> 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<TaskgroupActivityInfo>() {
|
||||
@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<TaskgroupActivityInfo> outgoingResult = StatisticService.convertInternActivityInfo(taskgroupActivityInfos);
|
||||
return ResponseEntity.ok(outgoingResult);
|
||||
}
|
||||
|
||||
@GetMapping("/history/schedules/{date}")
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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<ActivityInfo> activityInfos;
|
||||
|
||||
@JsonProperty
|
||||
private int activeMinutes;
|
||||
|
||||
public TaskgroupActivityInfo(int activeMinutes, LocalDate localDate) {
|
||||
this.date = localDate;
|
||||
this.activeMinutes = activeMinutes;
|
||||
public TaskgroupActivityInfo(TaskgroupEntityInfo taskgroup, List<ActivityInfo> activityInfos) {
|
||||
this.taskgroup = taskgroup;
|
||||
this.activityInfos = activityInfos;
|
||||
}
|
||||
|
||||
public LocalDate getDate() {
|
||||
return date;
|
||||
public TaskgroupEntityInfo getTaskgroup() {
|
||||
return taskgroup;
|
||||
}
|
||||
|
||||
public int getActiveMinutes() {
|
||||
return activeMinutes;
|
||||
public List<ActivityInfo> getActivityInfos() {
|
||||
return activityInfos;
|
||||
}
|
||||
}
|
||||
|
@ -136,54 +136,5 @@ public class Taskgroup {
|
||||
return Objects.hash(taskgroupID);
|
||||
}
|
||||
|
||||
public List<TaskgroupActivityInfo> calcActivityInfo(boolean includeChildTasks, LocalDate start, LocalDate end) {
|
||||
HashMap<LocalDate, Integer> activityInfos = new HashMap<>();
|
||||
if(includeChildTasks) {
|
||||
|
||||
Queue<Taskgroup> 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<TaskgroupActivityInfo> taskgroupActivityInfos = new ArrayList<>();
|
||||
for(Map.Entry<LocalDate, Integer> entry : activityInfos.entrySet()) {
|
||||
taskgroupActivityInfos.add(new TaskgroupActivityInfo(entry.getValue(), entry.getKey()));
|
||||
}
|
||||
return taskgroupActivityInfos;
|
||||
}
|
||||
}
|
||||
|
@ -27,4 +27,7 @@ public interface ScheduleRepository extends CrudRepository<AbstractSchedule, Lon
|
||||
@Transactional
|
||||
@Query(value = "DELETE FROM AbstractSchedule a WHERE a.task IN (SELECT t FROM Task t WHERE t.taskgroup = ?1)")
|
||||
void deleteByTaskgroup(Taskgroup taskgroup);
|
||||
|
||||
@Query(value = "SELECT s FROM AbstractSchedule s WHERE s.task.taskgroup.user.username = ?1 AND s.startTime is NOT NULL AND s.stopTime IS NOT NULL")
|
||||
List<AbstractSchedule> getAllFinishedSchedulesByUser(String username);
|
||||
}
|
||||
|
84
backend/src/main/java/core/services/StatisticService.java
Normal file
84
backend/src/main/java/core/services/StatisticService.java
Normal file
@ -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<Taskgroup, HashMap<LocalDate, Integer>> calcActivityByUser(String username, LocalDate startinDate, LocalDate endingDate) {
|
||||
HashMap<Taskgroup, HashMap<LocalDate, Integer>> taskgroupActivityInfos = new HashMap<>();
|
||||
List<AbstractSchedule> startedSchedules = scheduleRepository.getAllFinishedSchedulesByUser(username);
|
||||
for(AbstractSchedule schedule : startedSchedules) {
|
||||
Taskgroup topTaskgroup = findTopTaskgroup(schedule.getTask().getTaskgroup());
|
||||
HashMap<LocalDate, Integer> 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<TaskgroupActivityInfo> convertInternActivityInfo(HashMap<Taskgroup, HashMap<LocalDate, Integer>> taskgroupActivity) {
|
||||
List<TaskgroupActivityInfo> taskgroupActivityInfos = new ArrayList<>();
|
||||
for(Map.Entry<Taskgroup, HashMap<LocalDate, Integer>> entry: taskgroupActivity.entrySet()) {
|
||||
List<ActivityInfo> activityInfos = new ArrayList<>();
|
||||
for(Map.Entry<LocalDate, Integer> 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<LocalDate, Integer> 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;
|
||||
}
|
||||
}
|
31
openapi.yaml
31
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
|
||||
|
Loading…
Reference in New Issue
Block a user