Compare commits

..

No commits in common. "master" and "issue-113" have entirely different histories.

28 changed files with 357 additions and 642 deletions

View File

@ -5,7 +5,10 @@ import core.api.models.timemanager.history.TaskgroupActivityInfo;
import core.api.models.timemanager.history.WorkingStatus; import core.api.models.timemanager.history.WorkingStatus;
import core.entities.timemanager.AbstractSchedule; import core.entities.timemanager.AbstractSchedule;
import core.entities.timemanager.Taskgroup; import core.entities.timemanager.Taskgroup;
import core.services.*; import core.services.PermissionResult;
import core.services.ServiceExitCode;
import core.services.TaskScheduleService;
import core.services.TaskgroupService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
@ -21,7 +24,6 @@ import java.util.*;
public class StatisticController { public class StatisticController {
@Autowired private TaskScheduleService taskScheduleService; @Autowired private TaskScheduleService taskScheduleService;
@Autowired private StatisticService statisticService;
@Autowired private TaskgroupService taskgroupService; @Autowired private TaskgroupService taskgroupService;
@GetMapping("/history/workingStatus") @GetMapping("/history/workingStatus")
@ -40,29 +42,24 @@ public class StatisticController {
return ResponseEntity.ok(new WorkingStatus(missedSchedules, activeTime)); return ResponseEntity.ok(new WorkingStatus(missedSchedules, activeTime));
} }
@GetMapping("/statistics/{startingDate}/{endingDate}") @GetMapping("/statistics/taskgroup-activity/{taskgroupID}/{startingDate}/{endingDate}/{includeSubTaskgroups}")
public ResponseEntity<?> getTaskgroupActivity(@PathVariable String startingDate, @PathVariable String endingDate){ public ResponseEntity<?> getTaskgroupActivity(@PathVariable long taskgroupID, @PathVariable String startingDate, @PathVariable String endingDate, @PathVariable boolean includeSubTaskgroups){
LocalDate starting = LocalDate.parse(startingDate, DateTimeFormatter.ofPattern("yyyy-MM-dd")); PermissionResult<Taskgroup> taskgroupPermissionResult = taskgroupService.getTaskgroupByIDAndUsername(taskgroupID, SecurityContextHolder.getContext().getAuthentication().getName());
LocalDate ending = LocalDate.parse(endingDate, DateTimeFormatter.ofPattern("yyyy-MM-dd")); if(taskgroupPermissionResult.isNoPermissions()) {
return ResponseEntity.status(403).body(new SimpleStatusResponse("failed"));
var taskgroupActivityInfos = statisticService.calcActivityByUser(null, SecurityContextHolder.getContext().getAuthentication().getName(), starting, ending); } else if(taskgroupPermissionResult.getExitCode() == ServiceExitCode.MISSING_ENTITY) {
List<TaskgroupActivityInfo> outgoingResult = StatisticService.convertInternActivityInfo(taskgroupActivityInfos); return ResponseEntity.status(404).body(new SimpleStatusResponse("failed"));
return ResponseEntity.ok(outgoingResult);
} }
@GetMapping("/statistics/{taskgroupID}/{startingDate}/{endingDate}") List<TaskgroupActivityInfo> activityInfos = taskgroupPermissionResult.getResult().calcActivityInfo(includeSubTaskgroups,
public ResponseEntity<?> getTaskgroupActivity(@PathVariable long taskgroupID, @PathVariable String startingDate, @PathVariable String endingDate){ LocalDate.parse(startingDate, DateTimeFormatter.ofPattern("yyyy-MM-dd")), LocalDate.parse(endingDate, DateTimeFormatter.ofPattern("yyyy-MM-dd")));
LocalDate starting = LocalDate.parse(startingDate, DateTimeFormatter.ofPattern("yyyy-MM-dd")); activityInfos.sort(new Comparator<TaskgroupActivityInfo>() {
LocalDate ending = LocalDate.parse(endingDate, DateTimeFormatter.ofPattern("yyyy-MM-dd")); @Override
String username = SecurityContextHolder.getContext().getAuthentication().getName(); public int compare(TaskgroupActivityInfo o1, TaskgroupActivityInfo o2) {
var permissionResult = taskgroupService.getTaskgroupByIDAndUsername(taskgroupID, username); return o1.getDate().compareTo(o2.getDate());
if(permissionResult.hasIssue()) {
return permissionResult.mapToResponseEntity();
} }
});
var taskgroupActivityInfos = statisticService.calcActivityByUser(permissionResult.getResult(), username, starting, ending); return ResponseEntity.ok(activityInfos);
List<TaskgroupActivityInfo> outgoingResult = StatisticService.convertInternActivityInfo(taskgroupActivityInfos);
return ResponseEntity.ok(outgoingResult);
} }
@GetMapping("/history/schedules/{date}") @GetMapping("/history/schedules/{date}")

View File

@ -1,30 +0,0 @@
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;
}
}

View File

@ -4,23 +4,25 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import core.api.models.timemanager.taskgroup.TaskgroupEntityInfo; import core.api.models.timemanager.taskgroup.TaskgroupEntityInfo;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.List;
public class TaskgroupActivityInfo { public class TaskgroupActivityInfo {
private TaskgroupEntityInfo taskgroup; @JsonProperty
private List<ActivityInfo> activityInfos; private LocalDate date;
public TaskgroupActivityInfo(TaskgroupEntityInfo taskgroup, List<ActivityInfo> activityInfos) { @JsonProperty
this.taskgroup = taskgroup; private int activeMinutes;
this.activityInfos = activityInfos;
public TaskgroupActivityInfo(int activeMinutes, LocalDate localDate) {
this.date = localDate;
this.activeMinutes = activeMinutes;
} }
public TaskgroupEntityInfo getTaskgroup() { public LocalDate getDate() {
return taskgroup; return date;
} }
public List<ActivityInfo> getActivityInfos() { public int getActiveMinutes() {
return activityInfos; return activeMinutes;
} }
} }

View File

@ -75,8 +75,6 @@ public class Task {
clonedSchedules.add(clonedSchedule); clonedSchedules.add(clonedSchedule);
clonedTask.getBasicTaskSchedules().clear(); clonedTask.getBasicTaskSchedules().clear();
clonedTask.getBasicTaskSchedules().add(clonedSchedule); clonedTask.getBasicTaskSchedules().add(clonedSchedule);
clonedSchedule.setTask(clonedTask);
} }
Set<Task> clonedSubtasks = new HashSet<>(); Set<Task> clonedSubtasks = new HashSet<>();

View File

@ -136,5 +136,54 @@ public class Taskgroup {
return Objects.hash(taskgroupID); 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;
}
} }

View File

@ -27,7 +27,4 @@ public interface ScheduleRepository extends CrudRepository<AbstractSchedule, Lon
@Transactional @Transactional
@Query(value = "DELETE FROM AbstractSchedule a WHERE a.task IN (SELECT t FROM Task t WHERE t.taskgroup = ?1)") @Query(value = "DELETE FROM AbstractSchedule a WHERE a.task IN (SELECT t FROM Task t WHERE t.taskgroup = ?1)")
void deleteByTaskgroup(Taskgroup taskgroup); 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);
} }

View File

@ -1,113 +0,0 @@
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;
}
}

View File

@ -62,7 +62,6 @@ public class TaskSeriesService {
List<AbstractSchedule> clonedSchedules = new ArrayList<>(); List<AbstractSchedule> clonedSchedules = new ArrayList<>();
LocalDate currentDate = rootTask.getStartDate().plusDays(offsetMap.get(rootTask)); LocalDate currentDate = rootTask.getStartDate().plusDays(offsetMap.get(rootTask));
long index = 1;
while(currentDate.isBefore(endingDate)) { while(currentDate.isBefore(endingDate)) {
var cloneResult = rootTask.cloneTask(); var cloneResult = rootTask.cloneTask();
Task clonedRootTask = cloneResult.getValue00(); Task clonedRootTask = cloneResult.getValue00();
@ -74,11 +73,10 @@ public class TaskSeriesService {
taskSerie.addItem(item); taskSerie.addItem(item);
} }
clonedRootTask.shiftTask(index * offsetMap.get(rootTask)); clonedRootTask.shiftTask(offsetMap.get(rootTask));
currentDate = currentDate.plusDays(offsetMap.get(rootTask)); currentDate = currentDate.plusDays(offsetMap.get(rootTask));
itemIndex += offsetMap.size(); itemIndex += offsetMap.size();
index++;
} }
return new Tupel<>(clonedTasks, clonedSchedules); return new Tupel<>(clonedTasks, clonedSchedules);
@ -107,7 +105,7 @@ public class TaskSeriesService {
HashMap<Task, Integer> offsetMap = new HashMap<>(); HashMap<Task, Integer> offsetMap = new HashMap<>();
offsetMap.put(rootTask, taskRepeatInfo.getOffset()); offsetMap.put(rootTask, taskRepeatInfo.getOffset());
var repeatingResult = repeatTask(rootTask, taskRepeatInfo.getEndingDate(), offsetMap, taskSerie, 1); var repeatingResult = repeatTask(rootTask, taskRepeatInfo.getEndingDate(), offsetMap, taskSerie, 0);
List<Task> clonedTasks = new ArrayList<>(repeatingResult.getValue00()); List<Task> clonedTasks = new ArrayList<>(repeatingResult.getValue00());
List<AbstractSchedule> clonedSchedules = new ArrayList<>(repeatingResult.getValue01()); List<AbstractSchedule> clonedSchedules = new ArrayList<>(repeatingResult.getValue01());

View File

@ -17,7 +17,6 @@ encoder.ts
git_push.sh git_push.sh
index.ts index.ts
model/accountDeleteRequest.ts model/accountDeleteRequest.ts
model/activityInfo.ts
model/advancedScheduleFieldInfo.ts model/advancedScheduleFieldInfo.ts
model/advancedScheduleInfo.ts model/advancedScheduleInfo.ts
model/advancedScheduleInfoAllOf.ts model/advancedScheduleInfoAllOf.ts

View File

@ -202,86 +202,29 @@ export class HistoryService {
); );
} }
/**
* @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 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 statisticsStartingDateEndingDateGet.');
}
if (endingDate === null || endingDate === undefined) {
throw new Error('Required parameter endingDate was null or undefined when calling statisticsStartingDateEndingDateGet.');
}
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(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 taskgroupID internal id of taskgroup
* @param startingDate starting date * @param startingDate starting date
* @param endingDate 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 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. * @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 statisticsTaskgroupActivityTaskgroupIDStartingDateEndingDateIncludeSubTaskgroupsGet(taskgroupID: number, startingDate: string, endingDate: string, includeSubTaskgroups: boolean, 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 statisticsTaskgroupActivityTaskgroupIDStartingDateEndingDateIncludeSubTaskgroupsGet(taskgroupID: number, startingDate: string, endingDate: string, includeSubTaskgroups: boolean, 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 statisticsTaskgroupActivityTaskgroupIDStartingDateEndingDateIncludeSubTaskgroupsGet(taskgroupID: number, startingDate: string, endingDate: string, includeSubTaskgroups: boolean, 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> { 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) { if (taskgroupID === null || taskgroupID === undefined) {
throw new Error('Required parameter taskgroupID was null or undefined when calling statisticsTaskgroupIDStartingDateEndingDateGet.'); throw new Error('Required parameter taskgroupID was null or undefined when calling statisticsTaskgroupActivityTaskgroupIDStartingDateEndingDateIncludeSubTaskgroupsGet.');
} }
if (startingDate === null || startingDate === undefined) { if (startingDate === null || startingDate === undefined) {
throw new Error('Required parameter startingDate was null or undefined when calling statisticsTaskgroupIDStartingDateEndingDateGet.'); throw new Error('Required parameter startingDate was null or undefined when calling statisticsTaskgroupActivityTaskgroupIDStartingDateEndingDateIncludeSubTaskgroupsGet.');
} }
if (endingDate === null || endingDate === undefined) { if (endingDate === null || endingDate === undefined) {
throw new Error('Required parameter endingDate was null or undefined when calling statisticsTaskgroupIDStartingDateEndingDateGet.'); 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.');
} }
let localVarHeaders = this.defaultHeaders; let localVarHeaders = this.defaultHeaders;
@ -316,7 +259,7 @@ export class HistoryService {
responseType_ = 'text'; responseType_ = 'text';
} }
return this.httpClient.get<Array<TaskgroupActivityInfo>>(`${this.configuration.basePath}/statistics/${encodeURIComponent(String(taskgroupID))}/${encodeURIComponent(String(startingDate))}/${encodeURIComponent(String(endingDate))}`, return this.httpClient.get<Array<TaskgroupActivityInfo>>(`${this.configuration.basePath}/statistics/taskgroup-activity/${encodeURIComponent(String(taskgroupID))}/${encodeURIComponent(String(startingDate))}/${encodeURIComponent(String(endingDate))}/${encodeURIComponent(String(includeSubTaskgroups))}`,
{ {
context: localVarHttpContext, context: localVarHttpContext,
responseType: <any>responseType_, responseType: <any>responseType_,

View File

@ -1,21 +0,0 @@
/**
* 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;
}

View File

@ -1,5 +1,4 @@
export * from './accountDeleteRequest'; export * from './accountDeleteRequest';
export * from './activityInfo';
export * from './advancedScheduleFieldInfo'; export * from './advancedScheduleFieldInfo';
export * from './advancedScheduleInfo'; export * from './advancedScheduleInfo';
export * from './advancedScheduleInfoAllOf'; export * from './advancedScheduleInfoAllOf';

View File

@ -9,12 +9,13 @@
* https://openapi-generator.tech * https://openapi-generator.tech
* Do not edit the class manually. * Do not edit the class manually.
*/ */
import { ActivityInfo } from './activityInfo';
import { TaskgroupEntityInfo } from './taskgroupEntityInfo';
export interface TaskgroupActivityInfo { export interface TaskgroupActivityInfo {
taskgroup: TaskgroupEntityInfo; date: string;
activityInfos: Array<ActivityInfo>; /**
* Number of minutes the task was active
*/
activeMinutes: number;
} }

View File

@ -6,18 +6,16 @@
</mat-card> </mat-card>
<mat-card *ngIf="activeSchedule != undefined"> <mat-card *ngIf="activeSchedule != undefined">
<mat-card-header> <mat-card-header>
<mat-card-title><a [routerLink]="['/taskgroups', taskgroupID, 'tasks', activeSchedule!.task.taskID]" class="link-no-deco">{{activeSchedule!.task.taskName}}</a></mat-card-title> <mat-card-title><a routerLink="/" class="link-no-deco">{{activeSchedule!.task.taskName}}</a></mat-card-title>
</mat-card-header> </mat-card-header>
<mat-card-content> <mat-card-content>
<span *ngFor="let taskgroupPath of activeSchedule!.taskgroupPath"> <span *ngFor="let taskgroupPath of activeSchedule!.taskgroupPath">{{taskgroupPath.taskgroupName}} /</span>
<a class="link-no-deco" [routerLink]="['/taskgroups', taskgroupPath.taskgroupID]"> {{taskgroupPath.taskgroupName}} </a>/
</span>
<p class="gray-text">Running for {{displayTime}}</p> <p class="gray-text">Running for {{displayTime}}</p>
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<button class="btn btn-secondary" (click)="stopTask(false)">Stop</button> <button class="btn btn-secondary" (click)="stopTask(false)">Stop</button>
<div class="btn-group"> <div class="btn-group">
<button type="button" class="btn btn-success" (click)="finishSchedule()">Finish</button> <button type="button" class="btn btn-success">Finish</button>
<button type="button" class="btn btn-success dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false"> <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> <span class="visually-hidden">Toggle Dropdown</span>
</button> </button>

View File

@ -1,11 +1,5 @@
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core'; import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
import { import {ScheduleInfo, ScheduleService, TaskOverviewInfo, TaskScheduleStopResponse} from "../../../api";
ScheduleInfo,
ScheduleService,
TaskgroupEntityInfo,
TaskOverviewInfo,
TaskScheduleStopResponse
} from "../../../api";
import {StopActiveScheduleInfo} from "./StopActiveScheduleInfo"; import {StopActiveScheduleInfo} from "./StopActiveScheduleInfo";
import {TaskOverviewComponent} from "../task-overview/task-overview.component"; import {TaskOverviewComponent} from "../task-overview/task-overview.component";
import {MatDialog} from "@angular/material/dialog"; import {MatDialog} from "@angular/material/dialog";
@ -25,7 +19,6 @@ export interface StopActiveScheduleEmitterInfo {
}) })
export class ActiveScheduleComponent implements OnInit{ export class ActiveScheduleComponent implements OnInit{
activeSchedule: ScheduleInfo | undefined activeSchedule: ScheduleInfo | undefined
taskgroupID: number | undefined
startTime: number = 0; startTime: number = 0;
currentTime: number = 0; currentTime: number = 0;
@ -63,15 +56,11 @@ export class ActiveScheduleComponent implements OnInit{
next: resp => { next: resp => {
if(resp.scheduleID >= 0) { if(resp.scheduleID >= 0) {
this.activateSchedule(resp); this.activateSchedule(resp);
this.taskgroupID = resp.taskgroupPath[resp.taskgroupPath.length-1].taskgroupID
console.log(this.taskgroupID)
} }
}, },
}) })
} }
stopTask(finish: boolean) { stopTask(finish: boolean) {
@ -110,14 +99,6 @@ export class ActiveScheduleComponent implements OnInit{
} }
finishSchedule() {
this.scheduleService.schedulesScheduleIDStopFinishPost(this.activeSchedule!.scheduleID, true).subscribe({
next: resp => {
this.activeSchedule = undefined;
}
})
}
finishManual() { finishManual() {
const dialogRef = this.dialog.open(StopScheduleManuallyComponent, { const dialogRef = this.dialog.open(StopScheduleManuallyComponent, {
data: this.activeSchedule, data: this.activeSchedule,

View File

@ -23,18 +23,16 @@
<mat-card *ngFor="let schedule of schedules"> <mat-card *ngFor="let schedule of schedules">
<mat-card-header> <mat-card-header>
<mat-card-title> <mat-card-title>
<a [routerLink]="['/taskgroups', schedule.taskgroupPath[schedule.taskgroupPath.length-1].taskgroupID, 'tasks', schedule.task.taskID]" class="link-no-deco">{{schedule.task.taskName}}</a> <a routerLink="/" class="link-no-deco">{{schedule.task.taskName}}</a>
</mat-card-title> </mat-card-title>
</mat-card-header> </mat-card-header>
<mat-card-content> <mat-card-content>
<span *ngFor="let taskgroupPath of schedule.taskgroupPath"> <span *ngFor="let taskgroupPath of schedule.taskgroupPath">{{taskgroupPath.taskgroupName}} /</span>
<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> <p class="gray-text" *ngIf="schedule.scheduleType==='BASIC'">To be done sometime today</p>
</mat-card-content> </mat-card-content>
<mat-card-actions> <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 color="primary" class="primaryBtn" (click)="startSchedule(schedule)" [disabled]="activeScheduleComponent!.activeSchedule != undefined">Start now</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> <button mat-raised-button class="yellowBtn">Reschedule</button>
</mat-card-actions> </mat-card-actions>
</mat-card> </mat-card>
</div> </div>

View File

@ -5,11 +5,6 @@
justify-content: space-between; justify-content: space-between;
} }
.navi-link-container {
margin: 20px auto;
width: 80%;
}
.spacer { .spacer {
margin-bottom: 2.5%; margin-bottom: 2.5%;
} }

View File

@ -1,8 +1,4 @@
<div class="navi-link-container">
<app-navigation-link-list [navigationLinks]="defaultNavigationLinks"></app-navigation-link-list>
</div>
<div class="container"> <div class="container">
<div class="calendar-container"> <div class="calendar-container">
<h1 mat-dialog-title>Register forgotten activity</h1> <h1 mat-dialog-title>Register forgotten activity</h1>
<mwl-calendar-day-view <mwl-calendar-day-view

View File

@ -17,7 +17,6 @@ import * as moment from "moment/moment";
import {EventColor} from "calendar-utils"; import {EventColor} from "calendar-utils";
import {TaskOverviewData} from "../taskgroup-overview/taskgroup-overview.component"; import {TaskOverviewData} from "../taskgroup-overview/taskgroup-overview.component";
import {Router} from "@angular/router"; import {Router} from "@angular/router";
import {NavigationLink} from "../../navigation-link-list/navigation-link-list.component";
const colors: Record<string, EventColor> = { const colors: Record<string, EventColor> = {
@ -66,16 +65,6 @@ export class ForgottenTaskStartDialogComponent implements OnInit{
}, },
}, },
]; ];
defaultNavigationLinks: NavigationLink[] = [
{
linkText: "Dashboard",
routerLink: ['/']
},
{
linkText: "Register Forgotten Activity",
routerLink: ["/forgotten"]
}
];
constructor(private router: Router, constructor(private router: Router,
@ -135,8 +124,6 @@ export class ForgottenTaskStartDialogComponent implements OnInit{
event.start = newStart; event.start = newStart;
if (newEnd) { if (newEnd) {
event.end = newEnd; event.end = newEnd;
} else {
event.end = moment(event.start).add( 30, "minutes").toDate()
} }
this.events = [...this.events]; this.events = [...this.events];
} }
@ -182,8 +169,6 @@ export class ForgottenTaskStartDialogComponent implements OnInit{
} }
register() { 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, { 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'), 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'), endTime: moment(this.events[this.events.length-1].end).format('YYYY-MM-DDTHH:mm:ss.SSSZ'),

View File

@ -1,11 +1,8 @@
<div style="text-align:center" *ngIf="selectedTaskgroupPath != undefined"> <div style="text-align:center">
<apx-chart <apx-chart
[series]="chartOptions!.series!" [series]="chartOptions.series!"
[chart]="chartOptions!.chart!" [chart]="chartOptions.chart!"
[title]="chartOptions!.title!" [xaxis]="chartOptions.xaxis!"
[plotOptions]="chartOptions!.plotOptions!" [title]="chartOptions.title!"
[xaxis]="chartOptions!.xAxis!"
[yaxis]="chartOptions!.yAxis!"
[tooltip]="chartOptions!.tooltip!"
></apx-chart> ></apx-chart>
</div> </div>

View File

@ -1,180 +1,168 @@
import {Component, Input, OnChanges, ViewChild} from '@angular/core'; import {Component, Input, OnInit, ViewChild} from '@angular/core';
import {ApexAxisChartSeries, ChartComponent, ChartType} from "ng-apexcharts";
import { import {ChartOptions} from "../taskgroup-activity.component";
ApexAxisChartSeries,
ApexTitleSubtitle,
ApexDataLabels,
ApexChart,
ApexPlotOptions, ChartComponent, ApexXAxis, ApexYAxis, ApexTooltip
} from "ng-apexcharts";
import {generate} from "rxjs";
import * as moment from "moment"; import * as moment from "moment";
import {HistoryService, TaskgroupActivityInfo, TaskgroupPathInfo} from "../../../../api"; import {HistoryService, TaskgroupActivityInfo, TaskgroupPathInfo} from "../../../../api";
export type ChartOptions = {
series: ApexAxisChartSeries; interface XYData {
chart: ApexChart; x: any,
dataLabels: ApexDataLabels; y: any
title: ApexTitleSubtitle; }
plotOptions: ApexPlotOptions;
xAxis: ApexXAxis,
yAxis: ApexYAxis,
tooltip: ApexTooltip
};
@Component({ @Component({
selector: 'app-heatmap-activity', selector: 'app-heatmap-activity',
templateUrl: './heatmap-activity.component.html', templateUrl: './heatmap-activity.component.html',
styleUrls: ['./heatmap-activity.component.css'] styleUrls: ['./heatmap-activity.component.css']
}) })
export class HeatmapActivityComponent implements OnChanges{ export class HeatmapActivityComponent implements OnInit{
@ViewChild("chart") chart: ChartComponent | undefined; @ViewChild("chart") chart?: ChartComponent;
@Input() selectedTaskgroupPath: TaskgroupPathInfo | undefined public chartOptions: Partial<ChartOptions> = this.generateChartOptions()
public chartOptions: Partial<ChartOptions> | undefined;
maxValue = 120 @Input() selectedTaskgroupPath?: TaskgroupPathInfo
constructor(private historyService: HistoryService) { constructor(private historyService: HistoryService) {
} }
ngOnChanges() { ngOnInit() {
if(this.selectedTaskgroupPath != undefined) { this.chartOptions = this.generateChartOptions();
/*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 => {
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)
} }
],
generateChartOptions(): Partial<ChartOptions> {
return {
series: this.generateSeries(),
chart: { chart: {
height: 350, height: 350,
type: "heatmap" type: 'heatmap',
}, },
xAxis: { title: {
labels: { text: ""
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: { dataLabels: {
enabled: true, enabled: false
style: {
fontSize: "12px"
}
}, },
title: { colors: ["#008FFB"],
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 => ""
}
}
}
}; };
} }
})*/
} generateSeries() : ApexAxisChartSeries {
const series: ApexAxisChartSeries = []
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
).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()] = [];
} }
private generateData(startingDate: moment.Moment, data: TaskgroupActivityInfo[]) {
let currentDate = startingDate;
let series = [] data[currentDate.day()][index] = {
let endingDate = startingDate.clone().endOf('year'); 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");
}
while(currentDate.isBefore(endingDate)) {
let x = "w" + currentDate.isoWeek();
let y = this.findTaskgroupActivityInfoByDate(currentDate, data)
let extraInfo = currentDate.clone()
series.push({ series.push({
x: x, name: "Saturday",
y: y, data: data[6]
extraInfo: extraInfo });
}) series.push({
currentDate = currentDate.add(7, "days"); 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; return series;
} }
private findTaskgroupActivityInfoByDate(date: moment.Moment, data: TaskgroupActivityInfo[]) { }
//return data.find(taskActivity => moment(taskActivity.date).isSame(date))
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();
} }
} }

View File

@ -1,5 +1,5 @@
<div style="text-align:center"> <div style="text-align:center">
<apx-chart #chart *ngIf="chartOptions != undefined" <apx-chart
[series]="chartOptions.series!" [series]="chartOptions.series!"
[chart]="chartOptions.chart!" [chart]="chartOptions.chart!"
[xaxis]="chartOptions.xaxis!" [xaxis]="chartOptions.xaxis!"

View File

@ -1,8 +1,8 @@
import {Component, Input, OnChanges, OnInit, SimpleChanges, ViewChild} from '@angular/core'; import {Component, Input, ViewChild} from '@angular/core';
import {FormControl} from "@angular/forms"; import {FormControl} from "@angular/forms";
import {ChangeContext, LabelType, Options} from "ngx-slider-v2"; import {ChangeContext, LabelType, Options} from "ngx-slider-v2";
import {ApexAxisChartSeries, ChartComponent, ChartType} from "ng-apexcharts"; import {ApexAxisChartSeries, ChartComponent, ChartType} from "ng-apexcharts";
import {HistoryService, TaskgroupActivityInfo, TaskgroupPathInfo, TaskgroupService} from "../../../../api"; import {HistoryService, TaskgroupPathInfo, TaskgroupService} from "../../../../api";
import * as moment from "moment/moment"; import * as moment from "moment/moment";
import {ChartOptions} from "../taskgroup-activity.component"; import {ChartOptions} from "../taskgroup-activity.component";
@ -11,8 +11,8 @@ import {ChartOptions} from "../taskgroup-activity.component";
templateUrl: './simple-activity-diagram.component.html', templateUrl: './simple-activity-diagram.component.html',
styleUrls: ['./simple-activity-diagram.component.css'] styleUrls: ['./simple-activity-diagram.component.css']
}) })
export class SimpleActivityDiagramComponent implements OnChanges { export class SimpleActivityDiagramComponent {
@Input('selectedChartype') selectedChartype: string | undefined @Input('selectedChartype') selectedChartype: string = "bar";
@Input() selectedTaskgroupPath: TaskgroupPathInfo | undefined; @Input() selectedTaskgroupPath: TaskgroupPathInfo | undefined;
sliderControl: FormControl = new FormControl() sliderControl: FormControl = new FormControl()
@ -31,83 +31,15 @@ export class SimpleActivityDiagramComponent implements OnChanges {
}; };
@ViewChild("chart") chart?: ChartComponent; @ViewChild("chart") chart?: ChartComponent;
public chartOptions: Partial<ChartOptions> = this.generateChartOptions()
chartOptions: Partial<ChartOptions> | undefined;
constructor(private taskgroupService: TaskgroupService, constructor(private taskgroupService: TaskgroupService,
private historyService: HistoryService) { 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[] { createDateRange(): Date[] {
const dates: Date[] = []; const dates: Date[] = [];
for (let i: number = 1; i <= 30; i++) { for (let i: number = 1; i <= 31; i++) {
dates.push(moment().subtract(30-i, 'd').toDate()); dates.push(moment().subtract(30-i, 'd').toDate());
} }
@ -131,10 +63,84 @@ export class SimpleActivityDiagramComponent implements OnChanges {
console.log("min " + moment(changeContext.value).format("YYYY-MM-DD")); console.log("min " + moment(changeContext.value).format("YYYY-MM-DD"));
console.log("max " + moment(changeContext.highValue!).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.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) { setSelectedChartType(selectedChartype: string) {
//this.selectedChartype = selectedChartype; this.selectedChartype = selectedChartype;
this.updateSerieSelection();
}
setSelectedTaskgroupPath(selectedTaskgroupPath: TaskgroupPathInfo) {
this.selectedTaskgroupPath = selectedTaskgroupPath;
this.updateSerieSelection();
} }
} }

View File

@ -2,7 +2,7 @@
<app-navigation-link-list #navLinkList [navigationLinks]="defaultNavigationLinkPath"></app-navigation-link-list> <app-navigation-link-list #navLinkList [navigationLinks]="defaultNavigationLinkPath"></app-navigation-link-list>
<mat-form-field style="width: 90%"> <mat-form-field style="width: 90%">
<mat-label>Taskgroup</mat-label> <mat-label>Taskgroup</mat-label>
<mat-select [(ngModel)]="selectedTaskgroupPath"> <mat-select [(ngModel)]="selectedTaskgroupPath" (ngModelChange)="onSelectTaskgroupPath()">
<mat-option *ngFor="let topping of taskgroupPaths" [value]="topping">{{topping.taskgroupPath}}</mat-option> <mat-option *ngFor="let topping of taskgroupPaths" [value]="topping">{{topping.taskgroupPath}}</mat-option>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
@ -14,5 +14,5 @@
</mat-form-field> </mat-form-field>
<app-simple-activity-diagram *ngIf="selectedChartype !== 'heatmap'" #simpleActivityDiagram [selectedChartype]="selectedChartype" [selectedTaskgroupPath]="selectedTaskgroupPath"></app-simple-activity-diagram> <app-simple-activity-diagram *ngIf="selectedChartype !== 'heatmap'" #simpleActivityDiagram [selectedChartype]="selectedChartype" [selectedTaskgroupPath]="selectedTaskgroupPath"></app-simple-activity-diagram>
<app-heatmap-activity *ngIf="selectedChartype === 'heatmap'" #heatMap [selectedTaskgroupPath]="selectedTaskgroupPath!"></app-heatmap-activity> <app-heatmap-activity *ngIf="selectedChartype === 'heatmap'" #heatMap></app-heatmap-activity>
</div> </div>

View File

@ -65,6 +65,14 @@ export class TaskgroupActivityComponent implements OnInit{
} }
onSelectTaskgroupPath() {
if(this.simpleActivityDiagram != undefined) {
this.simpleActivityDiagram.setSelectedTaskgroupPath(this.selectedTaskgroupPath!);
}
this.heatMap?.setSelectedTaskgroupPath(this.selectedTaskgroupPath!);
}
onSelectChartType() { onSelectChartType() {
if(this.simpleActivityDiagram != undefined) { if(this.simpleActivityDiagram != undefined) {
this.simpleActivityDiagram.setSelectedChartType(this.selectedChartype); this.simpleActivityDiagram.setSelectedChartType(this.selectedChartype);

View File

@ -34,7 +34,7 @@
</div> </div>
<div style="float:right;"> <div style="float:right;">
<button mat-raised-button color="warn" (click)="deleteTask()">Delete</button> <button mat-raised-button color="warn">Delete</button>
</div> </div>
</div> </div>

View File

@ -187,12 +187,4 @@ export class TaskDetailOverviewComponent implements OnInit {
minWidth: "400px" minWidth: "400px"
}) })
} }
deleteTask() {
this.taskService.tasksTaskIDDelete(this.task!.taskID).subscribe({
next: resp => {
this.router.navigateByUrl("/")
}
})
}
} }

View File

@ -2060,49 +2060,7 @@ paths:
schema: schema:
type: object type: object
$ref: "#/components/schemas/SimpleStatusResponse" $ref: "#/components/schemas/SimpleStatusResponse"
/statistics/{startingDate}/{endingDate}: /statistics/taskgroup-activity/{taskgroupID}/{startingDate}/{endingDate}/{includeSubTaskgroups}:
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: get:
security: security:
- API_TOKEN: [] - API_TOKEN: []
@ -2130,6 +2088,13 @@ paths:
schema: schema:
type: string type: string
format: date format: date
- name: includeSubTaskgroups
in: path
description: determines whether to include subtaskgroups or not
required: true
schema:
type: boolean
example: false
responses: responses:
200: 200:
description: Operation successfull description: Operation successfull
@ -2983,7 +2948,7 @@ components:
rootTasktroup: rootTasktroup:
type: object type: object
$ref: '#/components/schemas/TaskgroupEntityInfo' $ref: '#/components/schemas/TaskgroupEntityInfo'
ActivityInfo: TaskgroupActivityInfo:
required: required:
- date - date
- activeMinutes - activeMinutes
@ -2996,19 +2961,6 @@ components:
type: number type: number
description: Number of minutes the task was active description: Number of minutes the task was active
example: 122 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: ManualScheduleStopInfo:
required: required:
- duration - duration