From 303de733a1846377622f51922c6d13ba486a07f6 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Wed, 20 Dec 2023 11:39:05 +0100 Subject: [PATCH 1/4] Include root Taskgroup in statistics and sort activityData by dates --- .../core/api/controller/StatisticController.java | 12 ++++++++++-- .../timemanager/history/TaskgroupActivityInfo.java | 8 ++++++++ .../timemanager/taskgroup/TaskgroupPathInfo.java | 4 ++++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/backend/src/main/java/core/api/controller/StatisticController.java b/backend/src/main/java/core/api/controller/StatisticController.java index 9558db0..8867f1a 100644 --- a/backend/src/main/java/core/api/controller/StatisticController.java +++ b/backend/src/main/java/core/api/controller/StatisticController.java @@ -23,6 +23,7 @@ import java.time.LocalDateTime; import java.time.LocalTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; import java.util.Map; @@ -59,7 +60,14 @@ public class StatisticController { return ResponseEntity.status(404).body(new SimpleStatusResponse("failed")); } - return ResponseEntity.ok(taskgroupPermissionResult.getResult().calcActivityInfo(includeSubTaskgroups, - LocalDate.parse(startingDate, DateTimeFormatter.ofPattern("yyyy-MM-dd")), LocalDate.parse(endingDate, DateTimeFormatter.ofPattern("yyyy-MM-dd")))); + List 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() { + @Override + public int compare(TaskgroupActivityInfo o1, TaskgroupActivityInfo o2) { + return o1.getDate().compareTo(o2.getDate()); + } + }); + return ResponseEntity.ok(activityInfos); } } diff --git a/backend/src/main/java/core/api/models/timemanager/history/TaskgroupActivityInfo.java b/backend/src/main/java/core/api/models/timemanager/history/TaskgroupActivityInfo.java index 2441617..51a6d73 100644 --- a/backend/src/main/java/core/api/models/timemanager/history/TaskgroupActivityInfo.java +++ b/backend/src/main/java/core/api/models/timemanager/history/TaskgroupActivityInfo.java @@ -17,4 +17,12 @@ public class TaskgroupActivityInfo { this.date = localDate; this.activeMinutes = activeMinutes; } + + public LocalDate getDate() { + return date; + } + + public int getActiveMinutes() { + return activeMinutes; + } } diff --git a/backend/src/main/java/core/api/models/timemanager/taskgroup/TaskgroupPathInfo.java b/backend/src/main/java/core/api/models/timemanager/taskgroup/TaskgroupPathInfo.java index 2e5d9a4..0bc4089 100644 --- a/backend/src/main/java/core/api/models/timemanager/taskgroup/TaskgroupPathInfo.java +++ b/backend/src/main/java/core/api/models/timemanager/taskgroup/TaskgroupPathInfo.java @@ -11,6 +11,9 @@ public class TaskgroupPathInfo { @JsonProperty private String taskgroupPath; + @JsonProperty + private TaskgroupEntityInfo rootTasktroup; + @JsonProperty private List directChildren; public TaskgroupPathInfo(Taskgroup taskgroup) { @@ -21,6 +24,7 @@ public class TaskgroupPathInfo { stringBuilder.append("/"); } this.taskgroupPath = stringBuilder.substring(0, stringBuilder.length()-1); + this.rootTasktroup = new TaskgroupEntityInfo(taskgroup); directChildren = taskgroup.getChildren().stream().map(TaskgroupEntityInfo::new).toList(); } } From 10dfda903100f7b32ac6839a5ac263c7a4a93823 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Wed, 20 Dec 2023 12:06:36 +0100 Subject: [PATCH 2/4] Fix statistics --- frontend/src/api/model/taskgroupPathInfo.ts | 1 + .../taskgroup-activity.component.ts | 42 ++++++++++++++++--- openapi.yaml | 4 ++ 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/frontend/src/api/model/taskgroupPathInfo.ts b/frontend/src/api/model/taskgroupPathInfo.ts index b13e526..a7faa8e 100644 --- a/frontend/src/api/model/taskgroupPathInfo.ts +++ b/frontend/src/api/model/taskgroupPathInfo.ts @@ -18,5 +18,6 @@ export interface TaskgroupPathInfo { */ taskgroupPath: string; directChildren: Array; + rootTasktroup: TaskgroupEntityInfo; } diff --git a/frontend/src/app/statistics/taskgroup-activity/taskgroup-activity.component.ts b/frontend/src/app/statistics/taskgroup-activity/taskgroup-activity.component.ts index caf6965..0631364 100644 --- a/frontend/src/app/statistics/taskgroup-activity/taskgroup-activity.component.ts +++ b/frontend/src/app/statistics/taskgroup-activity/taskgroup-activity.component.ts @@ -88,10 +88,13 @@ export class TaskgroupActivityComponent implements OnInit{ createDateRangeBetween(minValue: moment.Moment, maxValue: moment.Moment) { const dates: Date[] = []; - const numberDays = maxValue.diff(minValue, 'days'); - for(let i=0; i<=numberDays; i++) { - dates.push(minValue.add(i, 'd').toDate()); + let currentDate = moment(minValue); + + while(currentDate <= maxValue) { + dates.push(currentDate.toDate()); + currentDate = currentDate.add(1, 'days'); } + return dates; } @@ -100,19 +103,24 @@ export class TaskgroupActivityComponent implements OnInit{ //console.log("onUserChangeStart" + new Date(changeContext.value)); } + onUserChangeStop(changeContext: ChangeContext) { //console.log("onUserChangeStop" + new Date(changeContext.highValue!)) } onUserChange(changeContext: ChangeContext) { - const dateRange = this.createDateRangeBetween(moment(changeContext.value), moment(changeContext.highValue!)) + 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 { + const series = this.generateSeries(); + return { - series: this.generateSeries(), + series: series, chart: { height: 350, type: this.selectedChartype as ChartType, @@ -129,7 +137,30 @@ export class TaskgroupActivityComponent implements OnInit{ 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"), @@ -146,6 +177,7 @@ export class TaskgroupActivityComponent implements OnInit{ }) }) + console.log(series); return series; } diff --git a/openapi.yaml b/openapi.yaml index 0e7f7e5..b77674a 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -2612,6 +2612,7 @@ components: required: - taskgroupPath - directChildren + - rootTasktroup additionalProperties: false properties: taskgroupPath: @@ -2621,6 +2622,9 @@ components: type: array items: $ref: '#/components/schemas/TaskgroupEntityInfo' + rootTasktroup: + type: object + $ref: '#/components/schemas/TaskgroupEntityInfo' TaskgroupActivityInfo: required: - date From 08352add9d72c079839c5bc995c146a60d495464 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Wed, 20 Dec 2023 18:25:34 +0100 Subject: [PATCH 3/4] Include headmap for statistics --- frontend/src/app/app.module.ts | 4 + .../heatmap-activity.component.css | 0 .../heatmap-activity.component.html | 8 + .../heatmap-activity.component.spec.ts | 21 +++ .../heatmap-activity.component.ts | 168 ++++++++++++++++++ .../simple-activity-diagram.component.css | 0 .../simple-activity-diagram.component.html | 12 ++ .../simple-activity-diagram.component.spec.ts | 21 +++ .../simple-activity-diagram.component.ts | 146 +++++++++++++++ .../taskgroup-activity.component.html | 22 +-- .../taskgroup-activity.component.ts | 140 ++------------- 11 files changed, 403 insertions(+), 139 deletions(-) create mode 100644 frontend/src/app/statistics/taskgroup-activity/heatmap-activity/heatmap-activity.component.css create mode 100644 frontend/src/app/statistics/taskgroup-activity/heatmap-activity/heatmap-activity.component.html create mode 100644 frontend/src/app/statistics/taskgroup-activity/heatmap-activity/heatmap-activity.component.spec.ts create mode 100644 frontend/src/app/statistics/taskgroup-activity/heatmap-activity/heatmap-activity.component.ts create mode 100644 frontend/src/app/statistics/taskgroup-activity/simple-activity-diagram/simple-activity-diagram.component.css create mode 100644 frontend/src/app/statistics/taskgroup-activity/simple-activity-diagram/simple-activity-diagram.component.html create mode 100644 frontend/src/app/statistics/taskgroup-activity/simple-activity-diagram/simple-activity-diagram.component.spec.ts create mode 100644 frontend/src/app/statistics/taskgroup-activity/simple-activity-diagram/simple-activity-diagram.component.ts diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index ed4e568..d04b089 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -83,6 +83,8 @@ import { DraggableSchedulerComponent } from './schedules/draggable-scheduler/dra import { TaskgroupActivityComponent } from './statistics/taskgroup-activity/taskgroup-activity.component'; import {NgxSliderModule} from "ngx-slider-v2"; import {NgApexchartsModule} from "ng-apexcharts"; +import { SimpleActivityDiagramComponent } from './statistics/taskgroup-activity/simple-activity-diagram/simple-activity-diagram.component'; +import { HeatmapActivityComponent } from './statistics/taskgroup-activity/heatmap-activity/heatmap-activity.component'; @NgModule({ declarations: [ AppComponent, @@ -124,6 +126,8 @@ import {NgApexchartsModule} from "ng-apexcharts"; DateTimePickerComponent, DraggableSchedulerComponent, TaskgroupActivityComponent, + SimpleActivityDiagramComponent, + HeatmapActivityComponent, ], imports: [ BrowserModule, diff --git a/frontend/src/app/statistics/taskgroup-activity/heatmap-activity/heatmap-activity.component.css b/frontend/src/app/statistics/taskgroup-activity/heatmap-activity/heatmap-activity.component.css new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/app/statistics/taskgroup-activity/heatmap-activity/heatmap-activity.component.html b/frontend/src/app/statistics/taskgroup-activity/heatmap-activity/heatmap-activity.component.html new file mode 100644 index 0000000..9c020bf --- /dev/null +++ b/frontend/src/app/statistics/taskgroup-activity/heatmap-activity/heatmap-activity.component.html @@ -0,0 +1,8 @@ +
+ +
diff --git a/frontend/src/app/statistics/taskgroup-activity/heatmap-activity/heatmap-activity.component.spec.ts b/frontend/src/app/statistics/taskgroup-activity/heatmap-activity/heatmap-activity.component.spec.ts new file mode 100644 index 0000000..2c3565f --- /dev/null +++ b/frontend/src/app/statistics/taskgroup-activity/heatmap-activity/heatmap-activity.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { HeatmapActivityComponent } from './heatmap-activity.component'; + +describe('HeatmapActivityComponent', () => { + let component: HeatmapActivityComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [HeatmapActivityComponent] + }); + fixture = TestBed.createComponent(HeatmapActivityComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/statistics/taskgroup-activity/heatmap-activity/heatmap-activity.component.ts b/frontend/src/app/statistics/taskgroup-activity/heatmap-activity/heatmap-activity.component.ts new file mode 100644 index 0000000..9933dff --- /dev/null +++ b/frontend/src/app/statistics/taskgroup-activity/heatmap-activity/heatmap-activity.component.ts @@ -0,0 +1,168 @@ +import {Component, Input, OnInit, ViewChild} from '@angular/core'; +import {ApexAxisChartSeries, ChartComponent, ChartType} from "ng-apexcharts"; +import {ChartOptions} from "../taskgroup-activity.component"; +import * as moment from "moment"; +import {HistoryService, TaskgroupActivityInfo, TaskgroupPathInfo} from "../../../../api"; + + +interface XYData { + x: any, + y: any +} +@Component({ + selector: 'app-heatmap-activity', + templateUrl: './heatmap-activity.component.html', + styleUrls: ['./heatmap-activity.component.css'] +}) +export class HeatmapActivityComponent implements OnInit{ + + @ViewChild("chart") chart?: ChartComponent; + public chartOptions: Partial = this.generateChartOptions() + + @Input() selectedTaskgroupPath?: TaskgroupPathInfo + + constructor(private historyService: HistoryService) { + } + + ngOnInit() { + this.chartOptions = this.generateChartOptions(); + } + + generateChartOptions(): Partial { + return { + series: this.generateSeries(), + chart: { + height: 350, + type: 'heatmap', + }, + title: { + text: "" + }, + dataLabels: { + enabled: false + }, + colors: ["#008FFB"], + }; + } + + 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().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()] = []; + } + + + 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 + } + + 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)) + }) + currentDate = currentDate.add(1, 'days'); + } + return data; + } + + setSelectedTaskgroupPath(taskgroupPath: TaskgroupPathInfo) { + this.selectedTaskgroupPath = taskgroupPath; + this.chartOptions = this.generateChartOptions(); + } +} diff --git a/frontend/src/app/statistics/taskgroup-activity/simple-activity-diagram/simple-activity-diagram.component.css b/frontend/src/app/statistics/taskgroup-activity/simple-activity-diagram/simple-activity-diagram.component.css new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/app/statistics/taskgroup-activity/simple-activity-diagram/simple-activity-diagram.component.html b/frontend/src/app/statistics/taskgroup-activity/simple-activity-diagram/simple-activity-diagram.component.html new file mode 100644 index 0000000..bda266d --- /dev/null +++ b/frontend/src/app/statistics/taskgroup-activity/simple-activity-diagram/simple-activity-diagram.component.html @@ -0,0 +1,12 @@ +
+ +
+
+ +
diff --git a/frontend/src/app/statistics/taskgroup-activity/simple-activity-diagram/simple-activity-diagram.component.spec.ts b/frontend/src/app/statistics/taskgroup-activity/simple-activity-diagram/simple-activity-diagram.component.spec.ts new file mode 100644 index 0000000..393e5d2 --- /dev/null +++ b/frontend/src/app/statistics/taskgroup-activity/simple-activity-diagram/simple-activity-diagram.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SimpleActivityDiagramComponent } from './simple-activity-diagram.component'; + +describe('SimpleActivityDiagramComponent', () => { + let component: SimpleActivityDiagramComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [SimpleActivityDiagramComponent] + }); + fixture = TestBed.createComponent(SimpleActivityDiagramComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/statistics/taskgroup-activity/simple-activity-diagram/simple-activity-diagram.component.ts b/frontend/src/app/statistics/taskgroup-activity/simple-activity-diagram/simple-activity-diagram.component.ts new file mode 100644 index 0000000..dc20fb4 --- /dev/null +++ b/frontend/src/app/statistics/taskgroup-activity/simple-activity-diagram/simple-activity-diagram.component.ts @@ -0,0 +1,146 @@ +import {Component, Input, 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 * as moment from "moment/moment"; +import {ChartOptions} from "../taskgroup-activity.component"; + +@Component({ + selector: 'app-simple-activity-diagram', + templateUrl: './simple-activity-diagram.component.html', + styleUrls: ['./simple-activity-diagram.component.css'] +}) +export class SimpleActivityDiagramComponent { + @Input('selectedChartype') selectedChartype: string = "bar"; + @Input() selectedTaskgroupPath: TaskgroupPathInfo | undefined; + + sliderControl: FormControl = new FormControl() + dateRange: Date[] = this.createDateRange(); + selectedDateRange: Date[] = this.dateRange; + value: number = this.dateRange[0].getTime(); + options: Options = { + stepsArray: this.dateRange.map((date: Date) => { + return { value: date.getTime() }; + }), + translate: (value: number, label: LabelType): string => { + return new Date(value).toDateString(); + }, + floor: 0, + ceil: this.dateRange.length, + }; + + @ViewChild("chart") chart?: ChartComponent; + public chartOptions: Partial = this.generateChartOptions() + + constructor(private taskgroupService: TaskgroupService, + private historyService: HistoryService) { + } + + createDateRange(): Date[] { + const dates: Date[] = []; + for (let i: number = 1; i <= 31; i++) { + + dates.push(moment().subtract(30-i, 'd').toDate()); + } + this.sliderControl.setValue([dates[0], dates[dates.length-1]]) + return dates; + } + + createDateRangeBetween(minValue: moment.Moment, maxValue: moment.Moment) { + const dates: Date[] = []; + let currentDate = moment(minValue); + + while(currentDate <= maxValue) { + dates.push(currentDate.toDate()); + currentDate = currentDate.add(1, 'days'); + } + + return dates; + } + + onUserChange(changeContext: ChangeContext) { + 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 { + 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(); + } + + setSelectedTaskgroupPath(selectedTaskgroupPath: TaskgroupPathInfo) { + this.selectedTaskgroupPath = selectedTaskgroupPath; + this.updateSerieSelection(); + } +} diff --git a/frontend/src/app/statistics/taskgroup-activity/taskgroup-activity.component.html b/frontend/src/app/statistics/taskgroup-activity/taskgroup-activity.component.html index 39ee84d..59d25d6 100644 --- a/frontend/src/app/statistics/taskgroup-activity/taskgroup-activity.component.html +++ b/frontend/src/app/statistics/taskgroup-activity/taskgroup-activity.component.html @@ -1,28 +1,18 @@
- Toppings - + Taskgroup + {{topping.taskgroupPath}} - Toppings - + ChartType + {{topping}} -
- -
-
- -
+ +
diff --git a/frontend/src/app/statistics/taskgroup-activity/taskgroup-activity.component.ts b/frontend/src/app/statistics/taskgroup-activity/taskgroup-activity.component.ts index 0631364..526fdf5 100644 --- a/frontend/src/app/statistics/taskgroup-activity/taskgroup-activity.component.ts +++ b/frontend/src/app/statistics/taskgroup-activity/taskgroup-activity.component.ts @@ -7,17 +7,22 @@ import { ApexAxisChartSeries, ApexChart, ApexXAxis, - ApexTitleSubtitle, ChartType + ApexTitleSubtitle, ChartType, ApexPlotOptions, ApexDataLabels } from "ng-apexcharts"; import {timeInterval} from "rxjs"; import {FormControl} from "@angular/forms"; import {HistoryService, TaskgroupPathInfo, TaskgroupService} from "../../../api"; +import {SimpleActivityDiagramComponent} from "./simple-activity-diagram/simple-activity-diagram.component"; +import {HeatmapActivityComponent} from "./heatmap-activity/heatmap-activity.component"; export type ChartOptions = { series: ApexAxisChartSeries; chart: ApexChart; xaxis: ApexXAxis; title: ApexTitleSubtitle; + plotOptions: ApexPlotOptions, + colors: any, + dataLabels: ApexDataLabels; }; @Component({ selector: 'app-taskgroup-activity', @@ -40,32 +45,13 @@ export class TaskgroupActivityComponent implements OnInit{ } ]; + @ViewChild('simpleActivityDiagram') simpleActivityDiagram ?: SimpleActivityDiagramComponent + @ViewChild('heatMap') heatMap ?: HeatmapActivityComponent selectedChartype: string = "bar"; - availableChartTypes: string[] = ["bar", "line", "area"] + availableChartTypes: string[] = ["bar", "line", "area", "heatmap"] selectedTaskgroupPath: TaskgroupPathInfo | undefined taskgroupPaths: TaskgroupPathInfo[] = [] - sliderControl: FormControl = new FormControl() - dateRange: Date[] = this.createDateRange(); - selectedDateRange: Date[] = this.dateRange; - value: number = this.dateRange[0].getTime(); - options: Options = { - stepsArray: this.dateRange.map((date: Date) => { - return { value: date.getTime() }; - }), - translate: (value: number, label: LabelType): string => { - return new Date(value).toDateString(); - }, - floor: 0, - ceil: this.dateRange.length, - }; - - @ViewChild("chart") chart?: ChartComponent; - public chartOptions: Partial = this.generateChartOptions() - - constructor(private taskgroupService: TaskgroupService, - private historyService: HistoryService) { - } ngOnInit() { this.taskgroupService.taskgroupsPathGet().subscribe({ @@ -75,114 +61,22 @@ export class TaskgroupActivityComponent implements OnInit{ }) } + constructor(private taskgroupService: TaskgroupService) { - createDateRange(): Date[] { - const dates: Date[] = []; - for (let i: number = 1; i <= 31; i++) { - - dates.push(moment().subtract(30-i, 'd').toDate()); - } - this.sliderControl.setValue([dates[0], dates[dates.length-1]]) - return dates; } - createDateRangeBetween(minValue: moment.Moment, maxValue: moment.Moment) { - const dates: Date[] = []; - let currentDate = moment(minValue); - - while(currentDate <= maxValue) { - dates.push(currentDate.toDate()); - currentDate = currentDate.add(1, 'days'); + onSelectTaskgroupPath() { + if(this.simpleActivityDiagram != undefined) { + this.simpleActivityDiagram.setSelectedTaskgroupPath(this.selectedTaskgroupPath!); } - return dates; + this.heatMap?.setSelectedTaskgroupPath(this.selectedTaskgroupPath!); } - onUserChangeStart(changeContext: ChangeContext) { - - //console.log("onUserChangeStart" + new Date(changeContext.value)); - } - - - onUserChangeStop(changeContext: ChangeContext) { - //console.log("onUserChangeStop" + new Date(changeContext.highValue!)) - } - - onUserChange(changeContext: ChangeContext) { - 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 { - const series = this.generateSeries(); - - - return { - series: series, - 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) - } - ); - } - }) + onSelectChartType() { + if(this.simpleActivityDiagram != undefined) { + this.simpleActivityDiagram.setSelectedChartType(this.selectedChartype); } - - - - - 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() - } } From 3925828d1e0d8b96e9cd7aad86247dbb0e1ff715 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Wed, 20 Dec 2023 18:29:03 +0100 Subject: [PATCH 4/4] Include current day in heatmap statistic --- .../heatmap-activity/heatmap-activity.component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/statistics/taskgroup-activity/heatmap-activity/heatmap-activity.component.ts b/frontend/src/app/statistics/taskgroup-activity/heatmap-activity/heatmap-activity.component.ts index 9933dff..768ebba 100644 --- a/frontend/src/app/statistics/taskgroup-activity/heatmap-activity/heatmap-activity.component.ts +++ b/frontend/src/app/statistics/taskgroup-activity/heatmap-activity/heatmap-activity.component.ts @@ -55,7 +55,7 @@ export class HeatmapActivityComponent implements OnInit{ this.historyService.statisticsTaskgroupActivityTaskgroupIDStartingDateEndingDateIncludeSubTaskgroupsGet( this.selectedTaskgroupPath.rootTasktroup.taskgroupID, moment().subtract(6, "months").format("YYYY-MM-DD"), - moment().format("YYYY-MM-DD"), + moment().add(2, 'days').format("YYYY-MM-DD"), true ).subscribe({ next: resp => { @@ -106,7 +106,7 @@ export class HeatmapActivityComponent implements OnInit{ x: moment(currentDate).toDate(), y: 0 } - + console.log(currentDate) currentDate = currentDate.add(1, "days"); }