fix-statistics #89
@ -23,6 +23,7 @@ import java.time.LocalDateTime;
|
|||||||
import java.time.LocalTime;
|
import java.time.LocalTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@ -59,7 +60,14 @@ public class StatisticController {
|
|||||||
return ResponseEntity.status(404).body(new SimpleStatusResponse("failed"));
|
return ResponseEntity.status(404).body(new SimpleStatusResponse("failed"));
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResponseEntity.ok(taskgroupPermissionResult.getResult().calcActivityInfo(includeSubTaskgroups,
|
List<TaskgroupActivityInfo> activityInfos = taskgroupPermissionResult.getResult().calcActivityInfo(includeSubTaskgroups,
|
||||||
LocalDate.parse(startingDate, DateTimeFormatter.ofPattern("yyyy-MM-dd")), LocalDate.parse(endingDate, DateTimeFormatter.ofPattern("yyyy-MM-dd"))));
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,4 +17,12 @@ public class TaskgroupActivityInfo {
|
|||||||
this.date = localDate;
|
this.date = localDate;
|
||||||
this.activeMinutes = activeMinutes;
|
this.activeMinutes = activeMinutes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LocalDate getDate() {
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getActiveMinutes() {
|
||||||
|
return activeMinutes;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,9 @@ public class TaskgroupPathInfo {
|
|||||||
@JsonProperty
|
@JsonProperty
|
||||||
private String taskgroupPath;
|
private String taskgroupPath;
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
private TaskgroupEntityInfo rootTasktroup;
|
||||||
|
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private List<TaskgroupEntityInfo> directChildren;
|
private List<TaskgroupEntityInfo> directChildren;
|
||||||
public TaskgroupPathInfo(Taskgroup taskgroup) {
|
public TaskgroupPathInfo(Taskgroup taskgroup) {
|
||||||
@ -21,6 +24,7 @@ public class TaskgroupPathInfo {
|
|||||||
stringBuilder.append("/");
|
stringBuilder.append("/");
|
||||||
}
|
}
|
||||||
this.taskgroupPath = stringBuilder.substring(0, stringBuilder.length()-1);
|
this.taskgroupPath = stringBuilder.substring(0, stringBuilder.length()-1);
|
||||||
|
this.rootTasktroup = new TaskgroupEntityInfo(taskgroup);
|
||||||
directChildren = taskgroup.getChildren().stream().map(TaskgroupEntityInfo::new).toList();
|
directChildren = taskgroup.getChildren().stream().map(TaskgroupEntityInfo::new).toList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,5 +18,6 @@ export interface TaskgroupPathInfo {
|
|||||||
*/
|
*/
|
||||||
taskgroupPath: string;
|
taskgroupPath: string;
|
||||||
directChildren: Array<TaskgroupEntityInfo>;
|
directChildren: Array<TaskgroupEntityInfo>;
|
||||||
|
rootTasktroup: TaskgroupEntityInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,6 +83,8 @@ import { DraggableSchedulerComponent } from './schedules/draggable-scheduler/dra
|
|||||||
import { TaskgroupActivityComponent } from './statistics/taskgroup-activity/taskgroup-activity.component';
|
import { TaskgroupActivityComponent } from './statistics/taskgroup-activity/taskgroup-activity.component';
|
||||||
import {NgxSliderModule} from "ngx-slider-v2";
|
import {NgxSliderModule} from "ngx-slider-v2";
|
||||||
import {NgApexchartsModule} from "ng-apexcharts";
|
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({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
AppComponent,
|
AppComponent,
|
||||||
@ -124,6 +126,8 @@ import {NgApexchartsModule} from "ng-apexcharts";
|
|||||||
DateTimePickerComponent,
|
DateTimePickerComponent,
|
||||||
DraggableSchedulerComponent,
|
DraggableSchedulerComponent,
|
||||||
TaskgroupActivityComponent,
|
TaskgroupActivityComponent,
|
||||||
|
SimpleActivityDiagramComponent,
|
||||||
|
HeatmapActivityComponent,
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
<div style="text-align:center">
|
||||||
|
<apx-chart
|
||||||
|
[series]="chartOptions.series!"
|
||||||
|
[chart]="chartOptions.chart!"
|
||||||
|
[xaxis]="chartOptions.xaxis!"
|
||||||
|
[title]="chartOptions.title!"
|
||||||
|
></apx-chart>
|
||||||
|
</div>
|
@ -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<HeatmapActivityComponent>;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [HeatmapActivityComponent]
|
||||||
|
});
|
||||||
|
fixture = TestBed.createComponent(HeatmapActivityComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -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<ChartOptions> = this.generateChartOptions()
|
||||||
|
|
||||||
|
@Input() selectedTaskgroupPath?: TaskgroupPathInfo
|
||||||
|
|
||||||
|
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 = []
|
||||||
|
|
||||||
|
if(this.selectedTaskgroupPath != undefined) {
|
||||||
|
const startingDate = new Date(2023, 4, 9);
|
||||||
|
const endDate = new Date(2023, 11, 20);
|
||||||
|
let activityInfos: TaskgroupActivityInfo[];
|
||||||
|
this.historyService.statisticsTaskgroupActivityTaskgroupIDStartingDateEndingDateIncludeSubTaskgroupsGet(
|
||||||
|
this.selectedTaskgroupPath.rootTasktroup.taskgroupID,
|
||||||
|
moment().subtract(6, "months").format("YYYY-MM-DD"),
|
||||||
|
moment().add(2, 'days').format("YYYY-MM-DD"),
|
||||||
|
true
|
||||||
|
).subscribe({
|
||||||
|
next: resp => {
|
||||||
|
activityInfos = resp;
|
||||||
|
const data: XYData[][] = [];
|
||||||
|
let currentDate: moment.Moment = moment(activityInfos[0].date).subtract(moment(activityInfos[0].date).day(), 'days');
|
||||||
|
|
||||||
|
//Offset until data starts
|
||||||
|
let index = currentDate.day();
|
||||||
|
while(currentDate < moment(activityInfos[0].date)) {
|
||||||
|
if(data[currentDate.day()] == undefined) {
|
||||||
|
data[currentDate.day()] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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))
|
||||||
|
})
|
||||||
|
currentDate = currentDate.add(1, 'days');
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
setSelectedTaskgroupPath(taskgroupPath: TaskgroupPathInfo) {
|
||||||
|
this.selectedTaskgroupPath = taskgroupPath;
|
||||||
|
this.chartOptions = this.generateChartOptions();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
<div style="text-align:center">
|
||||||
|
<apx-chart
|
||||||
|
[series]="chartOptions.series!"
|
||||||
|
[chart]="chartOptions.chart!"
|
||||||
|
[xaxis]="chartOptions.xaxis!"
|
||||||
|
[title]="chartOptions.title!"
|
||||||
|
></apx-chart>
|
||||||
|
</div>
|
||||||
|
<div class="custom-slider">
|
||||||
|
<ngx-slider class="ngx-slider" [options]="options" [formControl]="sliderControl"
|
||||||
|
(userChange)="onUserChange($event)"></ngx-slider>
|
||||||
|
</div>
|
@ -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<SimpleActivityDiagramComponent>;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [SimpleActivityDiagramComponent]
|
||||||
|
});
|
||||||
|
fixture = TestBed.createComponent(SimpleActivityDiagramComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -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<ChartOptions> = 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<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();
|
||||||
|
}
|
||||||
|
|
||||||
|
setSelectedTaskgroupPath(selectedTaskgroupPath: TaskgroupPathInfo) {
|
||||||
|
this.selectedTaskgroupPath = selectedTaskgroupPath;
|
||||||
|
this.updateSerieSelection();
|
||||||
|
}
|
||||||
|
}
|
@ -1,28 +1,18 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<app-navigation-link-list #navLinkList [navigationLinks]="defaultNavigationLinkPath"></app-navigation-link-list>
|
<app-navigation-link-list #navLinkList [navigationLinks]="defaultNavigationLinkPath"></app-navigation-link-list>
|
||||||
<mat-form-field style="width: 90%">
|
<mat-form-field style="width: 90%">
|
||||||
<mat-label>Toppings</mat-label>
|
<mat-label>Taskgroup</mat-label>
|
||||||
<mat-select [(ngModel)]="selectedTaskgroupPath" (ngModelChange)="updateSerieSelection()">
|
<mat-select [(ngModel)]="selectedTaskgroupPath" (ngModelChange)="onSelectTaskgroupPath()">
|
||||||
<mat-option *ngFor="let topping of taskgroupPaths" [value]="topping">{{topping.taskgroupPath}}</mat-option>
|
<mat-option *ngFor="let topping of taskgroupPaths" [value]="topping">{{topping.taskgroupPath}}</mat-option>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<mat-form-field style="width: 10%;">
|
<mat-form-field style="width: 10%;">
|
||||||
<mat-label>Toppings</mat-label>
|
<mat-label>ChartType</mat-label>
|
||||||
<mat-select [(ngModel)]="selectedChartype" (ngModelChange)="updateSerieSelection()">
|
<mat-select [(ngModel)]="selectedChartype" (ngModelChange)="onSelectChartType()">
|
||||||
<mat-option *ngFor="let topping of availableChartTypes" [value]="topping">{{topping}}</mat-option>
|
<mat-option *ngFor="let topping of availableChartTypes" [value]="topping">{{topping}}</mat-option>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
<div style="text-align:center">
|
<app-simple-activity-diagram *ngIf="selectedChartype !== 'heatmap'" #simpleActivityDiagram [selectedChartype]="selectedChartype" [selectedTaskgroupPath]="selectedTaskgroupPath"></app-simple-activity-diagram>
|
||||||
<apx-chart
|
<app-heatmap-activity *ngIf="selectedChartype === 'heatmap'" #heatMap></app-heatmap-activity>
|
||||||
[series]="chartOptions.series!"
|
|
||||||
[chart]="chartOptions.chart!"
|
|
||||||
[xaxis]="chartOptions.xaxis!"
|
|
||||||
[title]="chartOptions.title!"
|
|
||||||
></apx-chart>
|
|
||||||
</div>
|
|
||||||
<div class="custom-slider">
|
|
||||||
<ngx-slider class="ngx-slider" [options]="options" [formControl]="sliderControl" (userChangeStart)="onUserChangeStart($event)"
|
|
||||||
(userChangeEnd)="onUserChangeStop($event)" (userChange)="onUserChange($event)"></ngx-slider>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -7,17 +7,22 @@ import {
|
|||||||
ApexAxisChartSeries,
|
ApexAxisChartSeries,
|
||||||
ApexChart,
|
ApexChart,
|
||||||
ApexXAxis,
|
ApexXAxis,
|
||||||
ApexTitleSubtitle, ChartType
|
ApexTitleSubtitle, ChartType, ApexPlotOptions, ApexDataLabels
|
||||||
} from "ng-apexcharts";
|
} from "ng-apexcharts";
|
||||||
import {timeInterval} from "rxjs";
|
import {timeInterval} from "rxjs";
|
||||||
import {FormControl} from "@angular/forms";
|
import {FormControl} from "@angular/forms";
|
||||||
import {HistoryService, TaskgroupPathInfo, TaskgroupService} from "../../../api";
|
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 = {
|
export type ChartOptions = {
|
||||||
series: ApexAxisChartSeries;
|
series: ApexAxisChartSeries;
|
||||||
chart: ApexChart;
|
chart: ApexChart;
|
||||||
xaxis: ApexXAxis;
|
xaxis: ApexXAxis;
|
||||||
title: ApexTitleSubtitle;
|
title: ApexTitleSubtitle;
|
||||||
|
plotOptions: ApexPlotOptions,
|
||||||
|
colors: any,
|
||||||
|
dataLabels: ApexDataLabels;
|
||||||
};
|
};
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-taskgroup-activity',
|
selector: 'app-taskgroup-activity',
|
||||||
@ -40,32 +45,13 @@ export class TaskgroupActivityComponent implements OnInit{
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ViewChild('simpleActivityDiagram') simpleActivityDiagram ?: SimpleActivityDiagramComponent
|
||||||
|
@ViewChild('heatMap') heatMap ?: HeatmapActivityComponent
|
||||||
|
|
||||||
selectedChartype: string = "bar";
|
selectedChartype: string = "bar";
|
||||||
availableChartTypes: string[] = ["bar", "line", "area"]
|
availableChartTypes: string[] = ["bar", "line", "area", "heatmap"]
|
||||||
selectedTaskgroupPath: TaskgroupPathInfo | undefined
|
selectedTaskgroupPath: TaskgroupPathInfo | undefined
|
||||||
taskgroupPaths: TaskgroupPathInfo[] = []
|
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<ChartOptions> = this.generateChartOptions()
|
|
||||||
|
|
||||||
constructor(private taskgroupService: TaskgroupService,
|
|
||||||
private historyService: HistoryService) {
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.taskgroupService.taskgroupsPathGet().subscribe({
|
this.taskgroupService.taskgroupsPathGet().subscribe({
|
||||||
@ -75,82 +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[] = [];
|
|
||||||
const numberDays = maxValue.diff(minValue, 'days');
|
|
||||||
for(let i=0; i<=numberDays; i++) {
|
|
||||||
dates.push(minValue.add(i, 'd').toDate());
|
|
||||||
}
|
|
||||||
return dates;
|
|
||||||
}
|
|
||||||
|
|
||||||
onUserChangeStart(changeContext: ChangeContext) {
|
|
||||||
|
|
||||||
//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!))
|
|
||||||
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 = []
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
})
|
|
||||||
return series;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateSerieSelection() {
|
onSelectTaskgroupPath() {
|
||||||
this.chartOptions = this.generateChartOptions()
|
if(this.simpleActivityDiagram != undefined) {
|
||||||
|
this.simpleActivityDiagram.setSelectedTaskgroupPath(this.selectedTaskgroupPath!);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.heatMap?.setSelectedTaskgroupPath(this.selectedTaskgroupPath!);
|
||||||
|
}
|
||||||
|
|
||||||
|
onSelectChartType() {
|
||||||
|
if(this.simpleActivityDiagram != undefined) {
|
||||||
|
this.simpleActivityDiagram.setSelectedChartType(this.selectedChartype);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2612,6 +2612,7 @@ components:
|
|||||||
required:
|
required:
|
||||||
- taskgroupPath
|
- taskgroupPath
|
||||||
- directChildren
|
- directChildren
|
||||||
|
- rootTasktroup
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
properties:
|
properties:
|
||||||
taskgroupPath:
|
taskgroupPath:
|
||||||
@ -2621,6 +2622,9 @@ components:
|
|||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: '#/components/schemas/TaskgroupEntityInfo'
|
$ref: '#/components/schemas/TaskgroupEntityInfo'
|
||||||
|
rootTasktroup:
|
||||||
|
type: object
|
||||||
|
$ref: '#/components/schemas/TaskgroupEntityInfo'
|
||||||
TaskgroupActivityInfo:
|
TaskgroupActivityInfo:
|
||||||
required:
|
required:
|
||||||
- date
|
- date
|
||||||
|
Loading…
Reference in New Issue
Block a user