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