Compare commits

...

22 Commits

Author SHA1 Message Date
7dce3a434f Merge pull request 'issue-117' (#119) from issue-117 into master
Some checks failed
Java CI with Maven / build-and-push-frontend (push) Failing after 20m37s
Java CI with Maven / build-and-push-backend (push) Failing after 40s
Reviewed-on: #119
2024-04-19 10:19:13 +02:00
Sebastian Böckelmann
952dddff2e Assign cloned schedule to cloned task and not to root task
All checks were successful
Java CI with Maven / build-and-push-frontend (push) Successful in 9s
Java CI with Maven / build-and-push-backend (push) Successful in 8s
2024-04-19 10:14:23 +02:00
Sebastian Böckelmann
dd58ec6520 Repare indecing (duplicate 00): Call RepeatTask with itemIndex 1, as the root task gets itemIndex 0
All checks were successful
Java CI with Maven / build-and-push-frontend (push) Successful in 9s
Java CI with Maven / build-and-push-backend (push) Successful in 9s
2024-04-19 09:59:43 +02:00
Sebastian Böckelmann
57be3393c9 Distribute cloned tasks on weeks
All checks were successful
Java CI with Maven / build-and-push-frontend (push) Successful in 8s
Java CI with Maven / build-and-push-backend (push) Successful in 7s
2024-04-19 09:51:14 +02:00
bc239f9b86 Merge pull request 'Fix way to long schedule after registering forgotten task' (#116) from issue-111 into master
All checks were successful
Java CI with Maven / build-and-push-frontend (push) Successful in 2m48s
Java CI with Maven / build-and-push-backend (push) Successful in 1m51s
Reviewed-on: #116
2024-04-18 20:34:36 +02:00
Sebastian Böckelmann
a8fb54f337 Fix way to long schedule after registering forgotten task
All checks were successful
Java CI with Maven / build-and-push-frontend (push) Successful in 6s
Java CI with Maven / build-and-push-backend (push) Successful in 7s
2024-04-18 20:30:36 +02:00
2bc3b3ab63 Merge pull request 'issue-112' (#115) from issue-112 into master
All checks were successful
Java CI with Maven / build-and-push-frontend (push) Successful in 2m56s
Java CI with Maven / build-and-push-backend (push) Successful in 1m39s
Reviewed-on: #115
2024-04-18 20:20:22 +02:00
Sebastian Böckelmann
d537fd5d57 Added Reschedule Redirection for Planned Schedules
All checks were successful
Java CI with Maven / build-and-push-frontend (push) Successful in 8s
Java CI with Maven / build-and-push-backend (push) Successful in 8s
2024-04-18 20:19:05 +02:00
Sebastian Böckelmann
f0a596ff0c Added Links in Schedules planned for today
All checks were successful
Java CI with Maven / build-and-push-frontend (push) Successful in 8s
Java CI with Maven / build-and-push-backend (push) Successful in 8s
2024-04-18 20:14:35 +02:00
Sebastian Böckelmann
ba5faeed6a Added Link from TaskgroupPathElement to Taskgroup-Overview in active-schedule
All checks were successful
Java CI with Maven / build-and-push-frontend (push) Successful in 8s
Java CI with Maven / build-and-push-backend (push) Successful in 8s
2024-04-18 20:11:04 +02:00
Sebastian Böckelmann
cd98423f6b Added Link from active schedule to task detail-overview
All checks were successful
Java CI with Maven / build-and-push-frontend (push) Successful in 9s
Java CI with Maven / build-and-push-backend (push) Successful in 8s
2024-04-18 20:09:06 +02:00
Sebastian Böckelmann
8dffb6f9ad Activate Finishing from active task schedule dashboard
All checks were successful
Java CI with Maven / build-and-push-frontend (push) Successful in 10s
Java CI with Maven / build-and-push-backend (push) Successful in 8s
2024-04-18 19:56:25 +02:00
Sebastian Böckelmann
7cd1d68739 Implement Delete Button in Task Detail Overview
All checks were successful
Java CI with Maven / build-and-push-frontend (push) Successful in 8s
Java CI with Maven / build-and-push-backend (push) Successful in 8s
2024-04-17 20:55:50 +02:00
Sebastian Böckelmann
6417b7afb9 Navigation Link Tree in Register Forgotten Task Schedule
All checks were successful
Java CI with Maven / build-and-push-frontend (push) Successful in 7s
Java CI with Maven / build-and-push-backend (push) Successful in 7s
2024-04-17 20:37:27 +02:00
0d3ba5d7d7 Merge pull request 'issue-113: Fix all Delete Operations' (#114) from issue-113 into master
All checks were successful
Java CI with Maven / build-and-push-frontend (push) Successful in 2m19s
Java CI with Maven / build-and-push-backend (push) Successful in 1m14s
Reviewed-on: #114
2024-04-17 20:25:39 +02:00
d1527e6d9c Merge pull request 'fix-statistics' (#110) from fix-statistics into master
Some checks failed
Java CI with Maven / build-and-push-frontend (push) Failing after 31s
Java CI with Maven / build-and-push-backend (push) Successful in 1m12s
Reviewed-on: #110
2024-03-18 15:25:19 +01:00
Sebastian Böckelmann
28f390f88a Document additional endpoint to get taskgroup specific activity info
All checks were successful
Java CI with Maven / build-and-push-frontend (push) Successful in 11s
Java CI with Maven / build-and-push-backend (push) Successful in 12s
2024-03-18 15:24:10 +01:00
Sebastian Böckelmann
bbdeaabd1e Activity of Taskgroups 2024-03-18 15:23:40 +01:00
Sebastian Böckelmann
183030611b Select top taskgroups by default in Activity Statistic
All checks were successful
Java CI with Maven / build-and-push-frontend (push) Successful in 13s
Java CI with Maven / build-and-push-backend (push) Successful in 13s
2024-03-18 14:45:25 +01:00
Sebastian Böckelmann
e1f69b340d Adapt Statistic Endpoint for Statistics independent of Taskgroup
All checks were successful
Java CI with Maven / build-and-push-frontend (push) Successful in 21s
Java CI with Maven / build-and-push-backend (push) Successful in 11s
2024-03-18 09:42:56 +01:00
Sebastian Böckelmann
985aa5f83b Partial Correct heatMap implementation
All checks were successful
Java CI with Maven / build-and-push-frontend (push) Successful in 14s
Java CI with Maven / build-and-push-backend (push) Successful in 10s
2024-03-17 16:01:48 +01:00
Sebastian Böckelmann
e53a1797fe Display only statistics until today
All checks were successful
Java CI with Maven / build-and-push-frontend (push) Successful in 7s
Java CI with Maven / build-and-push-backend (push) Successful in 11s
2024-03-17 13:28:49 +01:00
28 changed files with 642 additions and 357 deletions

View File

@ -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}")

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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<>();

View File

@ -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;
}
}

View File

@ -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);
}

View 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;
}
}

View File

@ -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());

View File

@ -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

View File

@ -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_,

View 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;
}

View File

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

View File

@ -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>;
}

View File

@ -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>

View File

@ -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,

View File

@ -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>

View File

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

View File

@ -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

View File

@ -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'),

View File

@ -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>

View File

@ -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))
}
}

View File

@ -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!"

View File

@ -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();
}
}

View File

@ -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>

View File

@ -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);

View File

@ -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>

View File

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

View File

@ -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