Compare commits
22 Commits
Author | SHA1 | Date | |
---|---|---|---|
7dce3a434f | |||
|
952dddff2e | ||
|
dd58ec6520 | ||
|
57be3393c9 | ||
bc239f9b86 | |||
|
a8fb54f337 | ||
2bc3b3ab63 | |||
|
d537fd5d57 | ||
|
f0a596ff0c | ||
|
ba5faeed6a | ||
|
cd98423f6b | ||
|
8dffb6f9ad | ||
|
7cd1d68739 | ||
|
6417b7afb9 | ||
0d3ba5d7d7 | |||
d1527e6d9c | |||
|
28f390f88a | ||
|
bbdeaabd1e | ||
|
183030611b | ||
|
e1f69b340d | ||
|
985aa5f83b | ||
|
e53a1797fe |
@ -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);
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
|
||||
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(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;
|
||||
}
|
||||
}
|
||||
|
@ -75,6 +75,8 @@ public class Task {
|
||||
clonedSchedules.add(clonedSchedule);
|
||||
clonedTask.getBasicTaskSchedules().clear();
|
||||
clonedTask.getBasicTaskSchedules().add(clonedSchedule);
|
||||
|
||||
clonedSchedule.setTask(clonedTask);
|
||||
}
|
||||
|
||||
Set<Task> clonedSubtasks = new HashSet<>();
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -62,6 +62,7 @@ public class TaskSeriesService {
|
||||
List<AbstractSchedule> clonedSchedules = new ArrayList<>();
|
||||
|
||||
LocalDate currentDate = rootTask.getStartDate().plusDays(offsetMap.get(rootTask));
|
||||
long index = 1;
|
||||
while(currentDate.isBefore(endingDate)) {
|
||||
var cloneResult = rootTask.cloneTask();
|
||||
Task clonedRootTask = cloneResult.getValue00();
|
||||
@ -73,10 +74,11 @@ public class TaskSeriesService {
|
||||
taskSerie.addItem(item);
|
||||
}
|
||||
|
||||
clonedRootTask.shiftTask(offsetMap.get(rootTask));
|
||||
clonedRootTask.shiftTask(index * offsetMap.get(rootTask));
|
||||
|
||||
currentDate = currentDate.plusDays(offsetMap.get(rootTask));
|
||||
itemIndex += offsetMap.size();
|
||||
index++;
|
||||
}
|
||||
|
||||
return new Tupel<>(clonedTasks, clonedSchedules);
|
||||
@ -105,7 +107,7 @@ public class TaskSeriesService {
|
||||
HashMap<Task, Integer> offsetMap = new HashMap<>();
|
||||
offsetMap.put(rootTask, taskRepeatInfo.getOffset());
|
||||
|
||||
var repeatingResult = repeatTask(rootTask, taskRepeatInfo.getEndingDate(), offsetMap, taskSerie, 0);
|
||||
var repeatingResult = repeatTask(rootTask, taskRepeatInfo.getEndingDate(), offsetMap, taskSerie, 1);
|
||||
List<Task> clonedTasks = new ArrayList<>(repeatingResult.getValue00());
|
||||
List<AbstractSchedule> clonedSchedules = new ArrayList<>(repeatingResult.getValue01());
|
||||
|
||||
|
@ -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>;
|
||||
}
|
||||
|
||||
|
@ -6,16 +6,18 @@
|
||||
</mat-card>
|
||||
<mat-card *ngIf="activeSchedule != undefined">
|
||||
<mat-card-header>
|
||||
<mat-card-title><a routerLink="/" class="link-no-deco">{{activeSchedule!.task.taskName}}</a></mat-card-title>
|
||||
<mat-card-title><a [routerLink]="['/taskgroups', taskgroupID, 'tasks', activeSchedule!.task.taskID]" class="link-no-deco">{{activeSchedule!.task.taskName}}</a></mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<span *ngFor="let taskgroupPath of activeSchedule!.taskgroupPath">{{taskgroupPath.taskgroupName}} /</span>
|
||||
<span *ngFor="let taskgroupPath of activeSchedule!.taskgroupPath">
|
||||
<a class="link-no-deco" [routerLink]="['/taskgroups', taskgroupPath.taskgroupID]"> {{taskgroupPath.taskgroupName}} </a>/
|
||||
</span>
|
||||
<p class="gray-text">Running for {{displayTime}}</p>
|
||||
</mat-card-content>
|
||||
<mat-card-actions>
|
||||
<button class="btn btn-secondary" (click)="stopTask(false)">Stop</button>
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-success">Finish</button>
|
||||
<button type="button" class="btn btn-success" (click)="finishSchedule()">Finish</button>
|
||||
<button type="button" class="btn btn-success dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<span class="visually-hidden">Toggle Dropdown</span>
|
||||
</button>
|
||||
|
@ -1,5 +1,11 @@
|
||||
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
|
||||
import {ScheduleInfo, ScheduleService, TaskOverviewInfo, TaskScheduleStopResponse} from "../../../api";
|
||||
import {
|
||||
ScheduleInfo,
|
||||
ScheduleService,
|
||||
TaskgroupEntityInfo,
|
||||
TaskOverviewInfo,
|
||||
TaskScheduleStopResponse
|
||||
} from "../../../api";
|
||||
import {StopActiveScheduleInfo} from "./StopActiveScheduleInfo";
|
||||
import {TaskOverviewComponent} from "../task-overview/task-overview.component";
|
||||
import {MatDialog} from "@angular/material/dialog";
|
||||
@ -19,6 +25,7 @@ export interface StopActiveScheduleEmitterInfo {
|
||||
})
|
||||
export class ActiveScheduleComponent implements OnInit{
|
||||
activeSchedule: ScheduleInfo | undefined
|
||||
taskgroupID: number | undefined
|
||||
|
||||
startTime: number = 0;
|
||||
currentTime: number = 0;
|
||||
@ -56,11 +63,15 @@ export class ActiveScheduleComponent implements OnInit{
|
||||
next: resp => {
|
||||
if(resp.scheduleID >= 0) {
|
||||
this.activateSchedule(resp);
|
||||
this.taskgroupID = resp.taskgroupPath[resp.taskgroupPath.length-1].taskgroupID
|
||||
console.log(this.taskgroupID)
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
})
|
||||
|
||||
|
||||
}
|
||||
|
||||
stopTask(finish: boolean) {
|
||||
@ -99,6 +110,14 @@ export class ActiveScheduleComponent implements OnInit{
|
||||
|
||||
}
|
||||
|
||||
finishSchedule() {
|
||||
this.scheduleService.schedulesScheduleIDStopFinishPost(this.activeSchedule!.scheduleID, true).subscribe({
|
||||
next: resp => {
|
||||
this.activeSchedule = undefined;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
finishManual() {
|
||||
const dialogRef = this.dialog.open(StopScheduleManuallyComponent, {
|
||||
data: this.activeSchedule,
|
||||
|
@ -23,16 +23,18 @@
|
||||
<mat-card *ngFor="let schedule of schedules">
|
||||
<mat-card-header>
|
||||
<mat-card-title>
|
||||
<a routerLink="/" class="link-no-deco">{{schedule.task.taskName}}</a>
|
||||
<a [routerLink]="['/taskgroups', schedule.taskgroupPath[schedule.taskgroupPath.length-1].taskgroupID, 'tasks', schedule.task.taskID]" class="link-no-deco">{{schedule.task.taskName}}</a>
|
||||
</mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<span *ngFor="let taskgroupPath of schedule.taskgroupPath">{{taskgroupPath.taskgroupName}} /</span>
|
||||
<span *ngFor="let taskgroupPath of schedule.taskgroupPath">
|
||||
<a class="link-no-deco" [routerLink]="['/taskgroups', taskgroupPath.taskgroupID]"> {{taskgroupPath.taskgroupName}} /</a>
|
||||
</span>
|
||||
<p class="gray-text" *ngIf="schedule.scheduleType==='BASIC'">To be done sometime today</p>
|
||||
</mat-card-content>
|
||||
<mat-card-actions>
|
||||
<button mat-raised-button color="primary" class="primaryBtn" (click)="startSchedule(schedule)" [disabled]="activeScheduleComponent!.activeSchedule != undefined">Start now</button>
|
||||
<button mat-raised-button class="yellowBtn">Reschedule</button>
|
||||
<button mat-raised-button class="yellowBtn" [routerLink]="['/taskgroups', schedule.taskgroupPath[schedule.taskgroupPath.length-1].taskgroupID, 'tasks', schedule.task.taskID, 'schedule', schedule.scheduleID]">Reschedule</button>
|
||||
</mat-card-actions>
|
||||
</mat-card>
|
||||
</div>
|
||||
|
@ -5,6 +5,11 @@
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.navi-link-container {
|
||||
margin: 20px auto;
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
.spacer {
|
||||
margin-bottom: 2.5%;
|
||||
}
|
||||
|
@ -1,4 +1,8 @@
|
||||
<div class="navi-link-container">
|
||||
<app-navigation-link-list [navigationLinks]="defaultNavigationLinks"></app-navigation-link-list>
|
||||
</div>
|
||||
<div class="container">
|
||||
|
||||
<div class="calendar-container">
|
||||
<h1 mat-dialog-title>Register forgotten activity</h1>
|
||||
<mwl-calendar-day-view
|
||||
|
@ -17,6 +17,7 @@ import * as moment from "moment/moment";
|
||||
import {EventColor} from "calendar-utils";
|
||||
import {TaskOverviewData} from "../taskgroup-overview/taskgroup-overview.component";
|
||||
import {Router} from "@angular/router";
|
||||
import {NavigationLink} from "../../navigation-link-list/navigation-link-list.component";
|
||||
|
||||
|
||||
const colors: Record<string, EventColor> = {
|
||||
@ -65,6 +66,16 @@ export class ForgottenTaskStartDialogComponent implements OnInit{
|
||||
},
|
||||
},
|
||||
];
|
||||
defaultNavigationLinks: NavigationLink[] = [
|
||||
{
|
||||
linkText: "Dashboard",
|
||||
routerLink: ['/']
|
||||
},
|
||||
{
|
||||
linkText: "Register Forgotten Activity",
|
||||
routerLink: ["/forgotten"]
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
constructor(private router: Router,
|
||||
@ -124,6 +135,8 @@ export class ForgottenTaskStartDialogComponent implements OnInit{
|
||||
event.start = newStart;
|
||||
if (newEnd) {
|
||||
event.end = newEnd;
|
||||
} else {
|
||||
event.end = moment(event.start).add( 30, "minutes").toDate()
|
||||
}
|
||||
this.events = [...this.events];
|
||||
}
|
||||
@ -169,6 +182,8 @@ export class ForgottenTaskStartDialogComponent implements OnInit{
|
||||
}
|
||||
|
||||
register() {
|
||||
console.log(this.events[this.events.length-1].start)
|
||||
console.log(this.events[this.events.length-1].end)
|
||||
this.scheduleService.schedulesTaskIDForgottenPost(this.events[this.events.length-1].meta.taskID, {
|
||||
startTime: moment(this.events[this.events.length-1].start).format('YYYY-MM-DDTHH:mm:ss.SSSZ'),
|
||||
endTime: moment(this.events[this.events.length-1].end).format('YYYY-MM-DDTHH:mm:ss.SSSZ'),
|
||||
|
@ -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()] = [];
|
||||
const taskgroupActivityInfo = resp;
|
||||
console.log("Chat Options")
|
||||
this.chartOptions = {
|
||||
series: [
|
||||
{
|
||||
name: "Monday",
|
||||
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: 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 => ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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({
|
||||
name: "Monday",
|
||||
data: data[1]
|
||||
});
|
||||
series.push({
|
||||
name: "Sunday",
|
||||
data: data[0]
|
||||
});
|
||||
};
|
||||
}
|
||||
})
|
||||
return series;
|
||||
} else {
|
||||
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))
|
||||
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
|
||||
})
|
||||
currentDate = currentDate.add(1, 'days');
|
||||
currentDate = currentDate.add(7, "days");
|
||||
}
|
||||
return data;
|
||||
return series;
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -34,7 +34,7 @@
|
||||
</div>
|
||||
|
||||
<div style="float:right;">
|
||||
<button mat-raised-button color="warn">Delete</button>
|
||||
<button mat-raised-button color="warn" (click)="deleteTask()">Delete</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
@ -187,4 +187,12 @@ export class TaskDetailOverviewComponent implements OnInit {
|
||||
minWidth: "400px"
|
||||
})
|
||||
}
|
||||
|
||||
deleteTask() {
|
||||
this.taskService.tasksTaskIDDelete(this.task!.taskID).subscribe({
|
||||
next: resp => {
|
||||
this.router.navigateByUrl("/")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
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