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,6 +21,7 @@ import java.util.*;
 | 
			
		||||
public class StatisticController {
 | 
			
		||||
 | 
			
		||||
    @Autowired private TaskScheduleService taskScheduleService;
 | 
			
		||||
    @Autowired private StatisticService statisticService;
 | 
			
		||||
    @Autowired private TaskgroupService taskgroupService;
 | 
			
		||||
 | 
			
		||||
    @GetMapping("/history/workingStatus")
 | 
			
		||||
@ -42,24 +40,29 @@ 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}")
 | 
			
		||||
    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"));
 | 
			
		||||
 | 
			
		||||
        var taskgroupActivityInfos = statisticService.calcActivityByUser(null, SecurityContextHolder.getContext().getAuthentication().getName(), starting, ending);
 | 
			
		||||
        List<TaskgroupActivityInfo> outgoingResult = StatisticService.convertInternActivityInfo(taskgroupActivityInfos);
 | 
			
		||||
        return ResponseEntity.ok(outgoingResult);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        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());
 | 
			
		||||
    @GetMapping("/statistics/{taskgroupID}/{startingDate}/{endingDate}")
 | 
			
		||||
    public ResponseEntity<?> getTaskgroupActivity(@PathVariable long taskgroupID, @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"));
 | 
			
		||||
        String username = SecurityContextHolder.getContext().getAuthentication().getName();
 | 
			
		||||
        var permissionResult = taskgroupService.getTaskgroupByIDAndUsername(taskgroupID, username);
 | 
			
		||||
        if(permissionResult.hasIssue()) {
 | 
			
		||||
            return permissionResult.mapToResponseEntity();
 | 
			
		||||
        }
 | 
			
		||||
        });
 | 
			
		||||
        return ResponseEntity.ok(activityInfos);
 | 
			
		||||
 | 
			
		||||
        var taskgroupActivityInfos = statisticService.calcActivityByUser(permissionResult.getResult(), username, 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 activeMinutes;
 | 
			
		||||
 | 
			
		||||
    public ActivityInfo(LocalDate scheduleDate, int workedMinutes) {
 | 
			
		||||
        this.scheduleDate = scheduleDate;
 | 
			
		||||
        this.activeMinutes = workedMinutes;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public LocalDate getScheduleDate() {
 | 
			
		||||
        return scheduleDate;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setScheduleDate(LocalDate scheduleDate) {
 | 
			
		||||
        this.scheduleDate = scheduleDate;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int getActiveMinutes() {
 | 
			
		||||
        return activeMinutes;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setActiveMinutes(int activeMinutes) {
 | 
			
		||||
        this.activeMinutes = activeMinutes;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										113
									
								
								backend/src/main/java/core/services/StatisticService.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								backend/src/main/java/core/services/StatisticService.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,113 @@
 | 
			
		||||
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(Taskgroup taskgroup, String username, LocalDate startinDate, LocalDate endingDate) {
 | 
			
		||||
        HashMap<Taskgroup, HashMap<LocalDate, Integer>> taskgroupActivityInfos = new HashMap<>();
 | 
			
		||||
        List<AbstractSchedule> startedSchedules = scheduleRepository.getAllFinishedSchedulesByUser(username);
 | 
			
		||||
        for(AbstractSchedule schedule : startedSchedules) {
 | 
			
		||||
            if(schedule.getStartTime().toLocalDate().isAfter(endingDate) || schedule.getStopTime().toLocalDate().isBefore(startinDate)) {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            Taskgroup topTaskgroup;
 | 
			
		||||
            if(taskgroup == null) {
 | 
			
		||||
                topTaskgroup = findTopTaskgroup(schedule.getTask().getTaskgroup());
 | 
			
		||||
            } else {
 | 
			
		||||
                topTaskgroup = findTaskgroupOfLayer(taskgroup, schedule.getTask().getTaskgroup());
 | 
			
		||||
                if(topTaskgroup == null) {
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Taskgroup findTaskgroupOfLayer(Taskgroup targetLayer, Taskgroup childTaskgroup) {
 | 
			
		||||
        if(targetLayer.getTaskgroupID() == childTaskgroup.getTaskgroupID()) {
 | 
			
		||||
            return childTaskgroup;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Taskgroup currentTaskgroup = childTaskgroup;
 | 
			
		||||
        while (currentTaskgroup.getParent() != null && currentTaskgroup.getParent().getTaskgroupID() != targetLayer.getTaskgroupID()) {
 | 
			
		||||
            currentTaskgroup = currentTaskgroup.getParent();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if(currentTaskgroup.getParent() == null) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        return currentTaskgroup;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -17,6 +17,7 @@ encoder.ts
 | 
			
		||||
git_push.sh
 | 
			
		||||
index.ts
 | 
			
		||||
model/accountDeleteRequest.ts
 | 
			
		||||
model/activityInfo.ts
 | 
			
		||||
model/advancedScheduleFieldInfo.ts
 | 
			
		||||
model/advancedScheduleInfo.ts
 | 
			
		||||
model/advancedScheduleInfoAllOf.ts
 | 
			
		||||
 | 
			
		||||
@ -203,28 +203,20 @@ export class HistoryService {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param taskgroupID internal id of taskgroup
 | 
			
		||||
     * @param startingDate starting date
 | 
			
		||||
     * @param endingDate starting date
 | 
			
		||||
     * @param includeSubTaskgroups determines whether to include subtaskgroups or not
 | 
			
		||||
     * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
 | 
			
		||||
     * @param reportProgress flag to report request and response progress.
 | 
			
		||||
     */
 | 
			
		||||
    public statisticsTaskgroupActivityTaskgroupIDStartingDateEndingDateIncludeSubTaskgroupsGet(taskgroupID: number, startingDate: string, endingDate: string, includeSubTaskgroups: boolean, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<Array<TaskgroupActivityInfo>>;
 | 
			
		||||
    public statisticsTaskgroupActivityTaskgroupIDStartingDateEndingDateIncludeSubTaskgroupsGet(taskgroupID: number, startingDate: string, endingDate: string, includeSubTaskgroups: boolean, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<HttpResponse<Array<TaskgroupActivityInfo>>>;
 | 
			
		||||
    public statisticsTaskgroupActivityTaskgroupIDStartingDateEndingDateIncludeSubTaskgroupsGet(taskgroupID: number, startingDate: string, endingDate: string, includeSubTaskgroups: boolean, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<HttpEvent<Array<TaskgroupActivityInfo>>>;
 | 
			
		||||
    public statisticsTaskgroupActivityTaskgroupIDStartingDateEndingDateIncludeSubTaskgroupsGet(taskgroupID: number, startingDate: string, endingDate: string, includeSubTaskgroups: boolean, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<any> {
 | 
			
		||||
        if (taskgroupID === null || taskgroupID === undefined) {
 | 
			
		||||
            throw new Error('Required parameter taskgroupID was null or undefined when calling statisticsTaskgroupActivityTaskgroupIDStartingDateEndingDateIncludeSubTaskgroupsGet.');
 | 
			
		||||
        }
 | 
			
		||||
    public statisticsStartingDateEndingDateGet(startingDate: string, endingDate: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<Array<TaskgroupActivityInfo>>;
 | 
			
		||||
    public statisticsStartingDateEndingDateGet(startingDate: string, endingDate: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<HttpResponse<Array<TaskgroupActivityInfo>>>;
 | 
			
		||||
    public statisticsStartingDateEndingDateGet(startingDate: string, endingDate: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<HttpEvent<Array<TaskgroupActivityInfo>>>;
 | 
			
		||||
    public statisticsStartingDateEndingDateGet(startingDate: string, endingDate: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<any> {
 | 
			
		||||
        if (startingDate === null || startingDate === undefined) {
 | 
			
		||||
            throw new Error('Required parameter startingDate was null or undefined when calling statisticsTaskgroupActivityTaskgroupIDStartingDateEndingDateIncludeSubTaskgroupsGet.');
 | 
			
		||||
            throw new Error('Required parameter startingDate was null or undefined when calling statisticsStartingDateEndingDateGet.');
 | 
			
		||||
        }
 | 
			
		||||
        if (endingDate === null || endingDate === undefined) {
 | 
			
		||||
            throw new Error('Required parameter endingDate was null or undefined when calling statisticsTaskgroupActivityTaskgroupIDStartingDateEndingDateIncludeSubTaskgroupsGet.');
 | 
			
		||||
        }
 | 
			
		||||
        if (includeSubTaskgroups === null || includeSubTaskgroups === undefined) {
 | 
			
		||||
            throw new Error('Required parameter includeSubTaskgroups was null or undefined when calling statisticsTaskgroupActivityTaskgroupIDStartingDateEndingDateIncludeSubTaskgroupsGet.');
 | 
			
		||||
            throw new Error('Required parameter endingDate was null or undefined when calling statisticsStartingDateEndingDateGet.');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let localVarHeaders = this.defaultHeaders;
 | 
			
		||||
@ -259,7 +251,72 @@ export class HistoryService {
 | 
			
		||||
            responseType_ = 'text';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return this.httpClient.get<Array<TaskgroupActivityInfo>>(`${this.configuration.basePath}/statistics/taskgroup-activity/${encodeURIComponent(String(taskgroupID))}/${encodeURIComponent(String(startingDate))}/${encodeURIComponent(String(endingDate))}/${encodeURIComponent(String(includeSubTaskgroups))}`,
 | 
			
		||||
        return this.httpClient.get<Array<TaskgroupActivityInfo>>(`${this.configuration.basePath}/statistics/${encodeURIComponent(String(startingDate))}/${encodeURIComponent(String(endingDate))}`,
 | 
			
		||||
            {
 | 
			
		||||
                context: localVarHttpContext,
 | 
			
		||||
                responseType: <any>responseType_,
 | 
			
		||||
                withCredentials: this.configuration.withCredentials,
 | 
			
		||||
                headers: localVarHeaders,
 | 
			
		||||
                observe: observe,
 | 
			
		||||
                reportProgress: reportProgress
 | 
			
		||||
            }
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param taskgroupID internal id of taskgroup
 | 
			
		||||
     * @param startingDate starting date
 | 
			
		||||
     * @param endingDate starting date
 | 
			
		||||
     * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
 | 
			
		||||
     * @param reportProgress flag to report request and response progress.
 | 
			
		||||
     */
 | 
			
		||||
    public statisticsTaskgroupIDStartingDateEndingDateGet(taskgroupID: number, startingDate: string, endingDate: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<Array<TaskgroupActivityInfo>>;
 | 
			
		||||
    public statisticsTaskgroupIDStartingDateEndingDateGet(taskgroupID: number, startingDate: string, endingDate: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<HttpResponse<Array<TaskgroupActivityInfo>>>;
 | 
			
		||||
    public statisticsTaskgroupIDStartingDateEndingDateGet(taskgroupID: number, startingDate: string, endingDate: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<HttpEvent<Array<TaskgroupActivityInfo>>>;
 | 
			
		||||
    public statisticsTaskgroupIDStartingDateEndingDateGet(taskgroupID: number, startingDate: string, endingDate: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<any> {
 | 
			
		||||
        if (taskgroupID === null || taskgroupID === undefined) {
 | 
			
		||||
            throw new Error('Required parameter taskgroupID was null or undefined when calling statisticsTaskgroupIDStartingDateEndingDateGet.');
 | 
			
		||||
        }
 | 
			
		||||
        if (startingDate === null || startingDate === undefined) {
 | 
			
		||||
            throw new Error('Required parameter startingDate was null or undefined when calling statisticsTaskgroupIDStartingDateEndingDateGet.');
 | 
			
		||||
        }
 | 
			
		||||
        if (endingDate === null || endingDate === undefined) {
 | 
			
		||||
            throw new Error('Required parameter endingDate was null or undefined when calling statisticsTaskgroupIDStartingDateEndingDateGet.');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let localVarHeaders = this.defaultHeaders;
 | 
			
		||||
 | 
			
		||||
        let localVarCredential: string | undefined;
 | 
			
		||||
        // authentication (API_TOKEN) required
 | 
			
		||||
        localVarCredential = this.configuration.lookupCredential('API_TOKEN');
 | 
			
		||||
        if (localVarCredential) {
 | 
			
		||||
            localVarHeaders = localVarHeaders.set('Authorization', 'Bearer ' + localVarCredential);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let localVarHttpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept;
 | 
			
		||||
        if (localVarHttpHeaderAcceptSelected === undefined) {
 | 
			
		||||
            // to determine the Accept header
 | 
			
		||||
            const httpHeaderAccepts: string[] = [
 | 
			
		||||
                'application/json'
 | 
			
		||||
            ];
 | 
			
		||||
            localVarHttpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts);
 | 
			
		||||
        }
 | 
			
		||||
        if (localVarHttpHeaderAcceptSelected !== undefined) {
 | 
			
		||||
            localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let localVarHttpContext: HttpContext | undefined = options && options.context;
 | 
			
		||||
        if (localVarHttpContext === undefined) {
 | 
			
		||||
            localVarHttpContext = new HttpContext();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        let responseType_: 'text' | 'json' = 'json';
 | 
			
		||||
        if(localVarHttpHeaderAcceptSelected && localVarHttpHeaderAcceptSelected.startsWith('text')) {
 | 
			
		||||
            responseType_ = 'text';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return this.httpClient.get<Array<TaskgroupActivityInfo>>(`${this.configuration.basePath}/statistics/${encodeURIComponent(String(taskgroupID))}/${encodeURIComponent(String(startingDate))}/${encodeURIComponent(String(endingDate))}`,
 | 
			
		||||
            {
 | 
			
		||||
                context: localVarHttpContext,
 | 
			
		||||
                responseType: <any>responseType_,
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										21
									
								
								frontend/src/api/model/activityInfo.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								frontend/src/api/model/activityInfo.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,21 @@
 | 
			
		||||
/**
 | 
			
		||||
 * API Title
 | 
			
		||||
 * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
 | 
			
		||||
 *
 | 
			
		||||
 * The version of the OpenAPI document: 1.0
 | 
			
		||||
 * 
 | 
			
		||||
 *
 | 
			
		||||
 * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
 | 
			
		||||
 * https://openapi-generator.tech
 | 
			
		||||
 * Do not edit the class manually.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
export interface ActivityInfo { 
 | 
			
		||||
    date: string;
 | 
			
		||||
    /**
 | 
			
		||||
     * Number of minutes the task was active
 | 
			
		||||
     */
 | 
			
		||||
    activeMinutes: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,5 @@
 | 
			
		||||
export * from './accountDeleteRequest';
 | 
			
		||||
export * from './activityInfo';
 | 
			
		||||
export * from './advancedScheduleFieldInfo';
 | 
			
		||||
export * from './advancedScheduleInfo';
 | 
			
		||||
export * from './advancedScheduleInfoAllOf';
 | 
			
		||||
 | 
			
		||||
@ -9,13 +9,12 @@
 | 
			
		||||
 * https://openapi-generator.tech
 | 
			
		||||
 * Do not edit the class manually.
 | 
			
		||||
 */
 | 
			
		||||
import { ActivityInfo } from './activityInfo';
 | 
			
		||||
import { TaskgroupEntityInfo } from './taskgroupEntityInfo';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
export interface TaskgroupActivityInfo { 
 | 
			
		||||
    date: string;
 | 
			
		||||
    /**
 | 
			
		||||
     * Number of minutes the task was active
 | 
			
		||||
     */
 | 
			
		||||
    activeMinutes: number;
 | 
			
		||||
    taskgroup: TaskgroupEntityInfo;
 | 
			
		||||
    activityInfos: Array<ActivityInfo>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,11 @@
 | 
			
		||||
<div style="text-align:center">
 | 
			
		||||
<div style="text-align:center" *ngIf="selectedTaskgroupPath != undefined">
 | 
			
		||||
  <apx-chart
 | 
			
		||||
    [series]="chartOptions.series!"
 | 
			
		||||
    [chart]="chartOptions.chart!"
 | 
			
		||||
    [xaxis]="chartOptions.xaxis!"
 | 
			
		||||
    [title]="chartOptions.title!"
 | 
			
		||||
    [series]="chartOptions!.series!"
 | 
			
		||||
    [chart]="chartOptions!.chart!"
 | 
			
		||||
    [title]="chartOptions!.title!"
 | 
			
		||||
    [plotOptions]="chartOptions!.plotOptions!"
 | 
			
		||||
    [xaxis]="chartOptions!.xAxis!"
 | 
			
		||||
    [yaxis]="chartOptions!.yAxis!"
 | 
			
		||||
    [tooltip]="chartOptions!.tooltip!"
 | 
			
		||||
  ></apx-chart>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
@ -1,168 +1,180 @@
 | 
			
		||||
import {Component, Input, OnInit, ViewChild} from '@angular/core';
 | 
			
		||||
import {ApexAxisChartSeries, ChartComponent, ChartType} from "ng-apexcharts";
 | 
			
		||||
import {ChartOptions} from "../taskgroup-activity.component";
 | 
			
		||||
import {Component, Input, OnChanges, ViewChild} from '@angular/core';
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
  ApexAxisChartSeries,
 | 
			
		||||
  ApexTitleSubtitle,
 | 
			
		||||
  ApexDataLabels,
 | 
			
		||||
  ApexChart,
 | 
			
		||||
  ApexPlotOptions, ChartComponent, ApexXAxis, ApexYAxis, ApexTooltip
 | 
			
		||||
} from "ng-apexcharts";
 | 
			
		||||
import {generate} from "rxjs";
 | 
			
		||||
import * as moment from "moment";
 | 
			
		||||
import {HistoryService, TaskgroupActivityInfo, TaskgroupPathInfo} from "../../../../api";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
interface XYData {
 | 
			
		||||
  x: any,
 | 
			
		||||
  y: any
 | 
			
		||||
}
 | 
			
		||||
export type ChartOptions = {
 | 
			
		||||
  series: ApexAxisChartSeries;
 | 
			
		||||
  chart: ApexChart;
 | 
			
		||||
  dataLabels: ApexDataLabels;
 | 
			
		||||
  title: ApexTitleSubtitle;
 | 
			
		||||
  plotOptions: ApexPlotOptions;
 | 
			
		||||
  xAxis: ApexXAxis,
 | 
			
		||||
  yAxis: ApexYAxis,
 | 
			
		||||
  tooltip: ApexTooltip
 | 
			
		||||
};
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'app-heatmap-activity',
 | 
			
		||||
  templateUrl: './heatmap-activity.component.html',
 | 
			
		||||
  styleUrls: ['./heatmap-activity.component.css']
 | 
			
		||||
})
 | 
			
		||||
export class HeatmapActivityComponent implements OnInit{
 | 
			
		||||
export class HeatmapActivityComponent implements OnChanges{
 | 
			
		||||
 | 
			
		||||
  @ViewChild("chart") chart?: ChartComponent;
 | 
			
		||||
  public chartOptions: Partial<ChartOptions> = this.generateChartOptions()
 | 
			
		||||
  @ViewChild("chart") chart: ChartComponent | undefined;
 | 
			
		||||
  @Input() selectedTaskgroupPath: TaskgroupPathInfo | undefined
 | 
			
		||||
  public chartOptions: Partial<ChartOptions> | undefined;
 | 
			
		||||
 | 
			
		||||
  @Input() selectedTaskgroupPath?: TaskgroupPathInfo
 | 
			
		||||
  maxValue = 120
 | 
			
		||||
 | 
			
		||||
  constructor(private historyService: HistoryService) {
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ngOnInit() {
 | 
			
		||||
    this.chartOptions = this.generateChartOptions();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  generateChartOptions(): Partial<ChartOptions> {
 | 
			
		||||
    return {
 | 
			
		||||
      series: this.generateSeries(),
 | 
			
		||||
      chart: {
 | 
			
		||||
        height: 350,
 | 
			
		||||
        type: 'heatmap',
 | 
			
		||||
      },
 | 
			
		||||
      title: {
 | 
			
		||||
        text: ""
 | 
			
		||||
      },
 | 
			
		||||
      dataLabels: {
 | 
			
		||||
        enabled: false
 | 
			
		||||
      },
 | 
			
		||||
      colors: ["#008FFB"],
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  generateSeries() : ApexAxisChartSeries {
 | 
			
		||||
    const series: ApexAxisChartSeries = []
 | 
			
		||||
 | 
			
		||||
  ngOnChanges() {
 | 
			
		||||
    if(this.selectedTaskgroupPath != undefined) {
 | 
			
		||||
      const startingDate = new Date(2023, 4, 9);
 | 
			
		||||
      const endDate = new Date(2023, 11, 20);
 | 
			
		||||
      let activityInfos: TaskgroupActivityInfo[];
 | 
			
		||||
      this.historyService.statisticsTaskgroupActivityTaskgroupIDStartingDateEndingDateIncludeSubTaskgroupsGet(
 | 
			
		||||
        this.selectedTaskgroupPath.rootTasktroup.taskgroupID,
 | 
			
		||||
        moment().subtract(6, "months").format("YYYY-MM-DD"),
 | 
			
		||||
        moment().add(2, 'days').format("YYYY-MM-DD"),
 | 
			
		||||
        true
 | 
			
		||||
      /*this.historyService.statisticsTaskgroupActivityTaskgroupIDStartingDateEndingDateIncludeSubTaskgroupsGet(
 | 
			
		||||
        this.selectedTaskgroupPath!.rootTasktroup.taskgroupID,
 | 
			
		||||
        moment().startOf('year').format("YYYY-MM-DD"),
 | 
			
		||||
        moment().endOf("year").format("YYYY-MM-DD"),
 | 
			
		||||
        false
 | 
			
		||||
      ).subscribe({
 | 
			
		||||
        next: resp => {
 | 
			
		||||
          activityInfos = resp;
 | 
			
		||||
          const data: XYData[][] = [];
 | 
			
		||||
          let currentDate: moment.Moment = moment(activityInfos[0].date).subtract(moment(activityInfos[0].date).day(), 'days');
 | 
			
		||||
 | 
			
		||||
          //Offset until data starts
 | 
			
		||||
          let index = currentDate.day();
 | 
			
		||||
          while(currentDate < moment(activityInfos[0].date)) {
 | 
			
		||||
            if(data[currentDate.day()] == undefined) {
 | 
			
		||||
              data[currentDate.day()] = [];
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            data[currentDate.day()][index] = {
 | 
			
		||||
              x: moment(currentDate).toDate(),
 | 
			
		||||
              y: 0
 | 
			
		||||
            }
 | 
			
		||||
            currentDate.add(1, 'days');
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          //inside data
 | 
			
		||||
          activityInfos.forEach(activityInfo => {
 | 
			
		||||
            const momentDate: moment.Moment = moment(activityInfo.date);
 | 
			
		||||
            const seriesIndex = momentDate.day();
 | 
			
		||||
 | 
			
		||||
            if(data[seriesIndex] == undefined) {
 | 
			
		||||
              data[seriesIndex] = [];
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            data[seriesIndex][index] = {
 | 
			
		||||
              x: activityInfo.date,
 | 
			
		||||
              y: activityInfo.activeMinutes
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            if(seriesIndex == 6) {
 | 
			
		||||
              index++;
 | 
			
		||||
            }
 | 
			
		||||
          })
 | 
			
		||||
 | 
			
		||||
          currentDate = moment(activityInfos[activityInfos.length-1].date);
 | 
			
		||||
          currentDate = currentDate.add(1, "days");
 | 
			
		||||
          //offset outside data
 | 
			
		||||
          for(let i=moment(activityInfos[activityInfos.length-1].date).day(); i<7; i++) {
 | 
			
		||||
            data[i][index] = {
 | 
			
		||||
              x: moment(currentDate).toDate(),
 | 
			
		||||
              y: 0
 | 
			
		||||
            }
 | 
			
		||||
            console.log(currentDate)
 | 
			
		||||
            currentDate = currentDate.add(1, "days");
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
          series.push({
 | 
			
		||||
            name: "Saturday",
 | 
			
		||||
            data: data[6]
 | 
			
		||||
          });
 | 
			
		||||
          series.push({
 | 
			
		||||
            name: "Friday",
 | 
			
		||||
            data: data[5]
 | 
			
		||||
          });
 | 
			
		||||
          series.push({
 | 
			
		||||
            name: "Thursday",
 | 
			
		||||
            data: data[4]
 | 
			
		||||
          });
 | 
			
		||||
          series.push({
 | 
			
		||||
            name: "Wednesday",
 | 
			
		||||
            data: data[3]
 | 
			
		||||
          });
 | 
			
		||||
          series.push({
 | 
			
		||||
            name: "Tuesday",
 | 
			
		||||
            data: data[2]
 | 
			
		||||
          });
 | 
			
		||||
          series.push({
 | 
			
		||||
          const taskgroupActivityInfo = resp;
 | 
			
		||||
          console.log("Chat Options")
 | 
			
		||||
          this.chartOptions = {
 | 
			
		||||
            series: [
 | 
			
		||||
              {
 | 
			
		||||
                name: "Monday",
 | 
			
		||||
            data: data[1]
 | 
			
		||||
          });
 | 
			
		||||
          series.push({
 | 
			
		||||
                data: this.generateData(moment().startOf('year'),taskgroupActivityInfo)
 | 
			
		||||
              },
 | 
			
		||||
              {
 | 
			
		||||
                name: "Tuesday",
 | 
			
		||||
                data: this.generateData(moment().startOf('year').add(1, "days"),taskgroupActivityInfo)
 | 
			
		||||
              },
 | 
			
		||||
              {
 | 
			
		||||
                name: "Wednesday",
 | 
			
		||||
                data: this.generateData(moment().startOf('year').add(2, "days"), taskgroupActivityInfo)
 | 
			
		||||
              },
 | 
			
		||||
              {
 | 
			
		||||
                name: "Thursday",
 | 
			
		||||
                data: this.generateData(moment().startOf('year').add(3, "days"), taskgroupActivityInfo)
 | 
			
		||||
              },
 | 
			
		||||
              {
 | 
			
		||||
                name: "Friday",
 | 
			
		||||
                data: this.generateData(moment().startOf('year').add(4, "days"), taskgroupActivityInfo)
 | 
			
		||||
              },
 | 
			
		||||
              {
 | 
			
		||||
                name: "Saturday",
 | 
			
		||||
                data: this.generateData(moment().startOf('year').add(5, "days"), taskgroupActivityInfo)
 | 
			
		||||
              },
 | 
			
		||||
              {
 | 
			
		||||
                name: "Sunday",
 | 
			
		||||
            data: data[0]
 | 
			
		||||
          });
 | 
			
		||||
                data: this.generateData(moment().startOf('year').add(6, "days"), taskgroupActivityInfo)
 | 
			
		||||
              }
 | 
			
		||||
            ],
 | 
			
		||||
            chart: {
 | 
			
		||||
              height: 350,
 | 
			
		||||
              type: "heatmap"
 | 
			
		||||
            },
 | 
			
		||||
            xAxis: {
 | 
			
		||||
              labels: {
 | 
			
		||||
                show: false
 | 
			
		||||
              }
 | 
			
		||||
            },
 | 
			
		||||
            yAxis: {
 | 
			
		||||
              labels: {
 | 
			
		||||
                show: true
 | 
			
		||||
              }
 | 
			
		||||
            },
 | 
			
		||||
            plotOptions: {
 | 
			
		||||
              heatmap: {
 | 
			
		||||
                shadeIntensity: 0.5,
 | 
			
		||||
                colorScale: {
 | 
			
		||||
                  ranges: [
 | 
			
		||||
                    {
 | 
			
		||||
                      from: 0,
 | 
			
		||||
                      to: 5,
 | 
			
		||||
                      name: "low",
 | 
			
		||||
                      color: "#00A100"
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                      from: 6,
 | 
			
		||||
                      to: 20,
 | 
			
		||||
                      name: "medium",
 | 
			
		||||
                      color: "#128FD9"
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                      from: 21,
 | 
			
		||||
                      to: 45,
 | 
			
		||||
                      name: "high",
 | 
			
		||||
                      color: "#FFB200"
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                      from: 46,
 | 
			
		||||
                      to: this.maxValue,
 | 
			
		||||
                      name: "extreme",
 | 
			
		||||
                      color: "#FF0000"
 | 
			
		||||
                    }
 | 
			
		||||
                  ]
 | 
			
		||||
                }
 | 
			
		||||
              }
 | 
			
		||||
            },
 | 
			
		||||
            dataLabels: {
 | 
			
		||||
              enabled: true,
 | 
			
		||||
              style: {
 | 
			
		||||
                fontSize: "12px"
 | 
			
		||||
              }
 | 
			
		||||
            },
 | 
			
		||||
            title: {
 | 
			
		||||
              text: "HeatMap Chart with Color Range"
 | 
			
		||||
            },
 | 
			
		||||
            tooltip: {
 | 
			
		||||
              enabled: true,
 | 
			
		||||
              y: {
 | 
			
		||||
                formatter: function (value, { seriesIndex, dataPointIndex, w }) {
 | 
			
		||||
                  return "<span style='font-weight: normal'>Spent </span>" +  value + " <span style='font-weight: normal'>Minutes on <br>" + w.config.series[seriesIndex].data[dataPointIndex].extraInfo.format("dddd, MMMM D, YYYY") + "</span>"
 | 
			
		||||
                },
 | 
			
		||||
                title: {
 | 
			
		||||
                  formatter: seriesName => ""
 | 
			
		||||
                }
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          };
 | 
			
		||||
        }
 | 
			
		||||
      })*/
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private generateData(startingDate: moment.Moment, data: TaskgroupActivityInfo[]) {
 | 
			
		||||
    let currentDate = startingDate;
 | 
			
		||||
 | 
			
		||||
    let series = []
 | 
			
		||||
    let endingDate = startingDate.clone().endOf('year');
 | 
			
		||||
 | 
			
		||||
    while(currentDate.isBefore(endingDate)) {
 | 
			
		||||
      let x = "w" + currentDate.isoWeek();
 | 
			
		||||
      let y = this.findTaskgroupActivityInfoByDate(currentDate, data)
 | 
			
		||||
      let extraInfo = currentDate.clone()
 | 
			
		||||
 | 
			
		||||
      series.push({
 | 
			
		||||
        x: x,
 | 
			
		||||
        y: y,
 | 
			
		||||
        extraInfo: extraInfo
 | 
			
		||||
      })
 | 
			
		||||
      return series;
 | 
			
		||||
    } else {
 | 
			
		||||
      currentDate = currentDate.add(7, "days");
 | 
			
		||||
    }
 | 
			
		||||
    return series;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  generateData(start: moment.Moment, end: moment.Moment) {
 | 
			
		||||
    const data: TaskgroupActivityInfo[] = [];
 | 
			
		||||
    let currentDate: moment.Moment = moment(start);
 | 
			
		||||
    while(currentDate <= end) {
 | 
			
		||||
      data.push({
 | 
			
		||||
        date: currentDate.format("YYYY-MM-DD"),
 | 
			
		||||
        activeMinutes: Math.floor(Math.random() * (100 + 1))
 | 
			
		||||
      })
 | 
			
		||||
      currentDate = currentDate.add(1, 'days');
 | 
			
		||||
    }
 | 
			
		||||
    return data;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setSelectedTaskgroupPath(taskgroupPath: TaskgroupPathInfo) {
 | 
			
		||||
    this.selectedTaskgroupPath = taskgroupPath;
 | 
			
		||||
    this.chartOptions = this.generateChartOptions();
 | 
			
		||||
  private findTaskgroupActivityInfoByDate(date: moment.Moment, data: TaskgroupActivityInfo[]) {
 | 
			
		||||
    //return data.find(taskActivity => moment(taskActivity.date).isSame(date))
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
<div style="text-align:center">
 | 
			
		||||
  <apx-chart
 | 
			
		||||
  <apx-chart #chart *ngIf="chartOptions != undefined"
 | 
			
		||||
    [series]="chartOptions.series!"
 | 
			
		||||
    [chart]="chartOptions.chart!"
 | 
			
		||||
    [xaxis]="chartOptions.xaxis!"
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,8 @@
 | 
			
		||||
import {Component, Input, ViewChild} from '@angular/core';
 | 
			
		||||
import {Component, Input, OnChanges, OnInit, SimpleChanges, ViewChild} from '@angular/core';
 | 
			
		||||
import {FormControl} from "@angular/forms";
 | 
			
		||||
import {ChangeContext, LabelType, Options} from "ngx-slider-v2";
 | 
			
		||||
import {ApexAxisChartSeries, ChartComponent, ChartType} from "ng-apexcharts";
 | 
			
		||||
import {HistoryService, TaskgroupPathInfo, TaskgroupService} from "../../../../api";
 | 
			
		||||
import {HistoryService, TaskgroupActivityInfo, TaskgroupPathInfo, TaskgroupService} from "../../../../api";
 | 
			
		||||
import * as moment from "moment/moment";
 | 
			
		||||
import {ChartOptions} from "../taskgroup-activity.component";
 | 
			
		||||
 | 
			
		||||
@ -11,8 +11,8 @@ import {ChartOptions} from "../taskgroup-activity.component";
 | 
			
		||||
  templateUrl: './simple-activity-diagram.component.html',
 | 
			
		||||
  styleUrls: ['./simple-activity-diagram.component.css']
 | 
			
		||||
})
 | 
			
		||||
export class SimpleActivityDiagramComponent {
 | 
			
		||||
  @Input('selectedChartype') selectedChartype: string = "bar";
 | 
			
		||||
export class SimpleActivityDiagramComponent implements OnChanges {
 | 
			
		||||
  @Input('selectedChartype') selectedChartype: string | undefined
 | 
			
		||||
  @Input() selectedTaskgroupPath: TaskgroupPathInfo | undefined;
 | 
			
		||||
 | 
			
		||||
  sliderControl: FormControl = new FormControl()
 | 
			
		||||
@ -31,15 +31,83 @@ export class SimpleActivityDiagramComponent {
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  @ViewChild("chart") chart?: ChartComponent;
 | 
			
		||||
  public chartOptions: Partial<ChartOptions> = this.generateChartOptions()
 | 
			
		||||
 | 
			
		||||
  chartOptions: Partial<ChartOptions> | undefined;
 | 
			
		||||
 | 
			
		||||
  constructor(private taskgroupService: TaskgroupService,
 | 
			
		||||
              private historyService: HistoryService) {
 | 
			
		||||
  }
 | 
			
		||||
  ngOnChanges() {
 | 
			
		||||
    this.fetchData()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  fetchData() {
 | 
			
		||||
    const startingDate = moment(this.dateRange[0]).format("yyyy-MM-DD");
 | 
			
		||||
    const endingDate = moment(this.dateRange[this.dateRange.length-1]).format("yyyy-MM-DD");
 | 
			
		||||
 | 
			
		||||
    if(this.selectedTaskgroupPath == undefined) {
 | 
			
		||||
      this.historyService.statisticsStartingDateEndingDateGet(startingDate, endingDate).subscribe({
 | 
			
		||||
        next: resp => {
 | 
			
		||||
          const series = this.createChartSeries(resp);
 | 
			
		||||
          this.chartOptions = this.createChartOptions(series);
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
    } else {
 | 
			
		||||
      this.historyService.statisticsTaskgroupIDStartingDateEndingDateGet(
 | 
			
		||||
        this.selectedTaskgroupPath.rootTasktroup.taskgroupID,
 | 
			
		||||
        startingDate,
 | 
			
		||||
        endingDate
 | 
			
		||||
      ).subscribe({
 | 
			
		||||
        next: resp => {
 | 
			
		||||
          const series = this.createChartSeries(resp);
 | 
			
		||||
          this.chartOptions = this.createChartOptions(series)
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  createChartSeries(data: TaskgroupActivityInfo[]) {
 | 
			
		||||
    const series: ApexAxisChartSeries = [];
 | 
			
		||||
    data.forEach(taskgroupActivityInfo => {
 | 
			
		||||
      const data: any[] = [];
 | 
			
		||||
      this.dateRange.map(date => {
 | 
			
		||||
        const selectedActivity = taskgroupActivityInfo.activityInfos.find(activity => moment(date).isSame(moment(activity.date), "day"))
 | 
			
		||||
        if(selectedActivity != undefined) {
 | 
			
		||||
          data.push(selectedActivity.activeMinutes)
 | 
			
		||||
        } else {
 | 
			
		||||
          data.push(0)
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      series.push({
 | 
			
		||||
        name: taskgroupActivityInfo.taskgroup.taskgroupName,
 | 
			
		||||
        data: data
 | 
			
		||||
      })
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    return series;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  createChartOptions(series: any[] = []) {
 | 
			
		||||
    return {
 | 
			
		||||
      series: series,
 | 
			
		||||
      chart: {
 | 
			
		||||
        height: 350,
 | 
			
		||||
        type: this.selectedChartype as ChartType,
 | 
			
		||||
        stacked: true
 | 
			
		||||
      },
 | 
			
		||||
      title: {
 | 
			
		||||
        text: ""
 | 
			
		||||
      },
 | 
			
		||||
      xaxis: {
 | 
			
		||||
        categories: this.selectedDateRange.map(date => date.toDateString())
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  createDateRange(): Date[] {
 | 
			
		||||
    const dates: Date[] = [];
 | 
			
		||||
    for (let i: number = 1; i <= 31; i++) {
 | 
			
		||||
    for (let i: number = 1; i <= 30; i++) {
 | 
			
		||||
 | 
			
		||||
      dates.push(moment().subtract(30-i, 'd').toDate());
 | 
			
		||||
    }
 | 
			
		||||
@ -63,84 +131,10 @@ export class SimpleActivityDiagramComponent {
 | 
			
		||||
    console.log("min " + moment(changeContext.value).format("YYYY-MM-DD"));
 | 
			
		||||
    console.log("max " + moment(changeContext.highValue!).format("YYYY-MM-DD"))
 | 
			
		||||
    this.selectedDateRange = this.createDateRangeBetween(moment(changeContext.value), moment(changeContext.highValue!))
 | 
			
		||||
    this.chartOptions = this.generateChartOptions()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  generateChartOptions(): Partial<ChartOptions> {
 | 
			
		||||
    return {
 | 
			
		||||
      series: this.generateSeries(),
 | 
			
		||||
      chart: {
 | 
			
		||||
        height: 350,
 | 
			
		||||
        type: this.selectedChartype as ChartType,
 | 
			
		||||
        stacked: true
 | 
			
		||||
      },
 | 
			
		||||
      title: {
 | 
			
		||||
        text: ""
 | 
			
		||||
      },
 | 
			
		||||
      xaxis: {
 | 
			
		||||
        categories: this.selectedDateRange.map(date => date.toDateString())
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  generateSeries() : ApexAxisChartSeries {
 | 
			
		||||
    const series: ApexAxisChartSeries = []
 | 
			
		||||
 | 
			
		||||
    if(this.selectedTaskgroupPath != undefined) {
 | 
			
		||||
      this.historyService.statisticsTaskgroupActivityTaskgroupIDStartingDateEndingDateIncludeSubTaskgroupsGet(
 | 
			
		||||
        this.selectedTaskgroupPath!.rootTasktroup.taskgroupID,
 | 
			
		||||
        moment(this.selectedDateRange[0]).format("YYYY-MM-DD"),
 | 
			
		||||
        moment(this.selectedDateRange[this.selectedDateRange.length-1]).format("YYYY-MM-DD"),
 | 
			
		||||
        false
 | 
			
		||||
      ).subscribe({
 | 
			
		||||
        next: resp => {
 | 
			
		||||
          series.push(
 | 
			
		||||
            {
 | 
			
		||||
              name: this.selectedTaskgroupPath!.rootTasktroup.taskgroupName,
 | 
			
		||||
              data: resp.map(dailyActivityInfo => dailyActivityInfo.activeMinutes)
 | 
			
		||||
            }
 | 
			
		||||
          );
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    this.selectedTaskgroupPath?.directChildren.forEach(taskgroup => {
 | 
			
		||||
 | 
			
		||||
      this.historyService.statisticsTaskgroupActivityTaskgroupIDStartingDateEndingDateIncludeSubTaskgroupsGet(
 | 
			
		||||
        taskgroup.taskgroupID,
 | 
			
		||||
        moment(this.selectedDateRange[0]).format("YYYY-MM-DD"),
 | 
			
		||||
        moment(this.selectedDateRange[this.selectedDateRange.length-1]).format("YYYY-MM-DD"), true
 | 
			
		||||
      ).subscribe({
 | 
			
		||||
        next: resp => {
 | 
			
		||||
          series.push(
 | 
			
		||||
            {
 | 
			
		||||
              name: taskgroup.taskgroupName,
 | 
			
		||||
              data: resp.map(dailyActivityInfo => dailyActivityInfo.activeMinutes)
 | 
			
		||||
            }
 | 
			
		||||
          )
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
    })
 | 
			
		||||
    console.log(series);
 | 
			
		||||
    return series;
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  updateSerieSelection() {
 | 
			
		||||
    this.chartOptions = this.generateChartOptions()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setSelectedChartType(selectedChartype: string) {
 | 
			
		||||
    this.selectedChartype = selectedChartype;
 | 
			
		||||
    this.updateSerieSelection();
 | 
			
		||||
  }
 | 
			
		||||
    //this.selectedChartype = selectedChartype;
 | 
			
		||||
 | 
			
		||||
  setSelectedTaskgroupPath(selectedTaskgroupPath: TaskgroupPathInfo) {
 | 
			
		||||
    this.selectedTaskgroupPath = selectedTaskgroupPath;
 | 
			
		||||
    this.updateSerieSelection();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,7 @@
 | 
			
		||||
  <app-navigation-link-list #navLinkList [navigationLinks]="defaultNavigationLinkPath"></app-navigation-link-list>
 | 
			
		||||
  <mat-form-field style="width: 90%">
 | 
			
		||||
    <mat-label>Taskgroup</mat-label>
 | 
			
		||||
    <mat-select [(ngModel)]="selectedTaskgroupPath" (ngModelChange)="onSelectTaskgroupPath()">
 | 
			
		||||
    <mat-select [(ngModel)]="selectedTaskgroupPath">
 | 
			
		||||
      <mat-option *ngFor="let topping of taskgroupPaths" [value]="topping">{{topping.taskgroupPath}}</mat-option>
 | 
			
		||||
    </mat-select>
 | 
			
		||||
  </mat-form-field>
 | 
			
		||||
@ -14,5 +14,5 @@
 | 
			
		||||
  </mat-form-field>
 | 
			
		||||
 | 
			
		||||
  <app-simple-activity-diagram *ngIf="selectedChartype !== 'heatmap'" #simpleActivityDiagram [selectedChartype]="selectedChartype" [selectedTaskgroupPath]="selectedTaskgroupPath"></app-simple-activity-diagram>
 | 
			
		||||
  <app-heatmap-activity *ngIf="selectedChartype === 'heatmap'" #heatMap></app-heatmap-activity>
 | 
			
		||||
  <app-heatmap-activity *ngIf="selectedChartype === 'heatmap'" #heatMap [selectedTaskgroupPath]="selectedTaskgroupPath!"></app-heatmap-activity>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
@ -65,14 +65,6 @@ export class TaskgroupActivityComponent implements OnInit{
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  onSelectTaskgroupPath() {
 | 
			
		||||
    if(this.simpleActivityDiagram != undefined) {
 | 
			
		||||
      this.simpleActivityDiagram.setSelectedTaskgroupPath(this.selectedTaskgroupPath!);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.heatMap?.setSelectedTaskgroupPath(this.selectedTaskgroupPath!);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  onSelectChartType() {
 | 
			
		||||
    if(this.simpleActivityDiagram != undefined) {
 | 
			
		||||
      this.simpleActivityDiagram.setSelectedChartType(this.selectedChartype);
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										66
									
								
								openapi.yaml
									
									
									
									
									
								
							
							
						
						
									
										66
									
								
								openapi.yaml
									
									
									
									
									
								
							@ -2060,7 +2060,49 @@ 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: startingDate
 | 
			
		||||
          in: path
 | 
			
		||||
          description: starting date
 | 
			
		||||
          required: true
 | 
			
		||||
          schema:
 | 
			
		||||
            type: string
 | 
			
		||||
            format: date
 | 
			
		||||
        - name: endingDate
 | 
			
		||||
          in: path
 | 
			
		||||
          description: starting date
 | 
			
		||||
          required: true
 | 
			
		||||
          schema:
 | 
			
		||||
            type: string
 | 
			
		||||
            format: date
 | 
			
		||||
      responses:
 | 
			
		||||
        200:
 | 
			
		||||
          description: Operation successfull
 | 
			
		||||
          content:
 | 
			
		||||
            application/json:
 | 
			
		||||
              schema:
 | 
			
		||||
                type: array
 | 
			
		||||
                items:
 | 
			
		||||
                  $ref: '#/components/schemas/TaskgroupActivityInfo'
 | 
			
		||||
        403:
 | 
			
		||||
          description: No permission
 | 
			
		||||
          content:
 | 
			
		||||
            application/json:
 | 
			
		||||
              schema:
 | 
			
		||||
                $ref: '#/components/schemas/SimpleStatusResponse'
 | 
			
		||||
        404:
 | 
			
		||||
          description: Schedule not found
 | 
			
		||||
          content:
 | 
			
		||||
            application/json:
 | 
			
		||||
              schema:
 | 
			
		||||
                $ref: '#/components/schemas/SimpleStatusResponse'
 | 
			
		||||
  /statistics/{taskgroupID}/{startingDate}/{endingDate}:
 | 
			
		||||
    get:
 | 
			
		||||
      security:
 | 
			
		||||
        - API_TOKEN: []
 | 
			
		||||
@ -2088,13 +2130,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 +2983,7 @@ components:
 | 
			
		||||
        rootTasktroup:
 | 
			
		||||
          type: object
 | 
			
		||||
          $ref: '#/components/schemas/TaskgroupEntityInfo'
 | 
			
		||||
    TaskgroupActivityInfo:
 | 
			
		||||
    ActivityInfo:
 | 
			
		||||
      required:
 | 
			
		||||
        - date
 | 
			
		||||
        - activeMinutes
 | 
			
		||||
@ -2961,6 +2996,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