Build data of series by data provided through historyService
This commit is contained in:
parent
cf0bbabb85
commit
882169840d
@ -1,21 +1,30 @@
|
|||||||
package core.api.controller;
|
package core.api.controller;
|
||||||
|
|
||||||
|
import core.api.models.auth.SimpleStatusResponse;
|
||||||
|
import core.api.models.timemanager.history.TaskgroupActivityInfo;
|
||||||
import core.api.models.timemanager.history.WorkingStatus;
|
import core.api.models.timemanager.history.WorkingStatus;
|
||||||
|
import core.api.models.timemanager.taskgroup.TaskgroupEntityInfo;
|
||||||
import core.entities.timemanager.AbstractSchedule;
|
import core.entities.timemanager.AbstractSchedule;
|
||||||
|
import core.entities.timemanager.Task;
|
||||||
|
import core.entities.timemanager.Taskgroup;
|
||||||
|
import core.services.PermissionResult;
|
||||||
|
import core.services.ServiceExitCode;
|
||||||
import core.services.TaskScheduleService;
|
import core.services.TaskScheduleService;
|
||||||
|
import core.services.TaskgroupService;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.web.bind.annotation.CrossOrigin;
|
import org.springframework.security.core.parameters.P;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.LocalTime;
|
import java.time.LocalTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
@CrossOrigin(origins = "*", maxAge = 3600)
|
@CrossOrigin(origins = "*", maxAge = 3600)
|
||||||
@RestController
|
@RestController
|
||||||
@ -23,6 +32,7 @@ import java.util.List;
|
|||||||
public class StatisticController {
|
public class StatisticController {
|
||||||
|
|
||||||
@Autowired private TaskScheduleService taskScheduleService;
|
@Autowired private TaskScheduleService taskScheduleService;
|
||||||
|
@Autowired private TaskgroupService taskgroupService;
|
||||||
|
|
||||||
@GetMapping("/history/workingStatus")
|
@GetMapping("/history/workingStatus")
|
||||||
public ResponseEntity<?> getWorkingStatus() {
|
public ResponseEntity<?> getWorkingStatus() {
|
||||||
@ -39,4 +49,17 @@ public class StatisticController {
|
|||||||
|
|
||||||
return ResponseEntity.ok(new WorkingStatus(missedSchedules, activeTime));
|
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.isHasPermissions()) {
|
||||||
|
return ResponseEntity.status(403).body(new SimpleStatusResponse("failed"));
|
||||||
|
} else if(taskgroupPermissionResult.getExitCode() == ServiceExitCode.MISSING_ENTITY) {
|
||||||
|
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"))));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
package core.api.models.timemanager.history;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import core.api.models.timemanager.taskgroup.TaskgroupEntityInfo;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
public class TaskgroupActivityInfo {
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
private LocalDate date;
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
private int activeMinutes;
|
||||||
|
|
||||||
|
public TaskgroupActivityInfo(int activeMinutes, LocalDate localDate) {
|
||||||
|
this.date = localDate;
|
||||||
|
this.activeMinutes = activeMinutes;
|
||||||
|
}
|
||||||
|
}
|
@ -9,13 +9,18 @@ import java.util.List;
|
|||||||
public class TaskgroupPathInfo {
|
public class TaskgroupPathInfo {
|
||||||
|
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private List<String> taskgroupPathNames;
|
private String taskgroupPath;
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
private List<TaskgroupEntityInfo> directChildren;
|
||||||
public TaskgroupPathInfo(Taskgroup taskgroup) {
|
public TaskgroupPathInfo(Taskgroup taskgroup) {
|
||||||
taskgroupPathNames = new ArrayList<>();
|
|
||||||
List<Taskgroup> taskgroupPath = Taskgroup.getAncestorList(taskgroup);
|
List<Taskgroup> taskgroupPath = Taskgroup.getAncestorList(taskgroup);
|
||||||
for(Taskgroup cT : taskgroupPath) {
|
StringBuilder stringBuilder = new StringBuilder();
|
||||||
taskgroupPathNames.add(cT.getTaskgroupName());
|
for(Taskgroup taskgroupPathEntity : taskgroupPath) {
|
||||||
|
stringBuilder.append(taskgroupPathEntity.getTaskgroupName());
|
||||||
|
stringBuilder.append("/");
|
||||||
}
|
}
|
||||||
|
this.taskgroupPath = stringBuilder.substring(0, stringBuilder.length()-1);
|
||||||
|
directChildren = taskgroup.getChildren().stream().map(TaskgroupEntityInfo::new).toList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -93,6 +93,10 @@ public abstract class AbstractSchedule {
|
|||||||
return startTime != null && stopTime == null;
|
return startTime != null && stopTime == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isCompleted() {
|
||||||
|
return startTime != null && stopTime != null;
|
||||||
|
}
|
||||||
|
|
||||||
public int getActiveTime() {
|
public int getActiveTime() {
|
||||||
if(startTime == null) {
|
if(startTime == null) {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -188,4 +188,14 @@ public class Task {
|
|||||||
public void setFinishable(boolean finishable) {
|
public void setFinishable(boolean finishable) {
|
||||||
this.finishable = finishable;
|
this.finishable = finishable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int calcOverallActivityInfo(LocalDate date) {
|
||||||
|
int activeMinutes = 0;
|
||||||
|
for(AbstractSchedule schedule : getBasicTaskSchedules()) {
|
||||||
|
if(schedule.isCompleted() && schedule.getStartTime().toLocalDate().isEqual(date)) {
|
||||||
|
activeMinutes += schedule.calcActiveMinutes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return activeMinutes;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
package core.entities.timemanager;
|
package core.entities.timemanager;
|
||||||
|
|
||||||
|
import core.api.models.timemanager.history.TaskgroupActivityInfo;
|
||||||
|
import core.api.models.timemanager.taskgroup.TaskgroupEntityInfo;
|
||||||
import core.entities.User;
|
import core.entities.User;
|
||||||
|
|
||||||
import javax.persistence.*;
|
import javax.persistence.*;
|
||||||
import javax.validation.constraints.NotBlank;
|
import javax.validation.constraints.NotBlank;
|
||||||
|
import java.time.LocalDate;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@ -132,4 +135,55 @@ public class Taskgroup {
|
|||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(taskgroupID);
|
return Objects.hash(taskgroupID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<TaskgroupActivityInfo> calcActivityInfo(boolean includeChildTasks, LocalDate start, LocalDate end) {
|
||||||
|
HashMap<LocalDate, Integer> activityInfos = new HashMap<>();
|
||||||
|
if(includeChildTasks) {
|
||||||
|
|
||||||
|
Queue<Taskgroup> queue = new LinkedList<>(children);
|
||||||
|
while(!queue.isEmpty()) {
|
||||||
|
Taskgroup childTraskgroup = queue.poll();
|
||||||
|
LocalDate currentDate = start;
|
||||||
|
while(!currentDate.isAfter(end)) {
|
||||||
|
int activeMinutes = 0;
|
||||||
|
for(Task task : childTraskgroup.getTasks()) {
|
||||||
|
activeMinutes += task.calcOverallActivityInfo(currentDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(activityInfos.containsKey(currentDate)) {
|
||||||
|
activityInfos.put(currentDate, activityInfos.get(currentDate) + activeMinutes);
|
||||||
|
} else {
|
||||||
|
activityInfos.put(currentDate, activeMinutes);
|
||||||
|
}
|
||||||
|
|
||||||
|
currentDate = currentDate.plusDays(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
queue.addAll(childTraskgroup.getChildren());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalDate currentDate = start;
|
||||||
|
while(!currentDate.isAfter(end)) {
|
||||||
|
int activeMinutes = 0;
|
||||||
|
for(Task task : tasks) {
|
||||||
|
activeMinutes += task.calcOverallActivityInfo(currentDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(activityInfos.containsKey(currentDate)) {
|
||||||
|
activityInfos.put(currentDate, activityInfos.get(currentDate) + activeMinutes);
|
||||||
|
} else {
|
||||||
|
activityInfos.put(currentDate, activeMinutes);
|
||||||
|
}
|
||||||
|
|
||||||
|
currentDate = currentDate.plusDays(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<TaskgroupActivityInfo> taskgroupActivityInfos = new ArrayList<>();
|
||||||
|
for(Map.Entry<LocalDate, Integer> entry : activityInfos.entrySet()) {
|
||||||
|
taskgroupActivityInfos.add(new TaskgroupActivityInfo(entry.getValue(), entry.getKey()));
|
||||||
|
}
|
||||||
|
return taskgroupActivityInfos;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
package core.repositories.timemanager;
|
package core.repositories.timemanager;
|
||||||
|
|
||||||
import core.entities.timemanager.AbstractSchedule;
|
import core.entities.timemanager.AbstractSchedule;
|
||||||
|
import core.entities.timemanager.Taskgroup;
|
||||||
import org.springframework.data.jpa.repository.Query;
|
import org.springframework.data.jpa.repository.Query;
|
||||||
import org.springframework.data.repository.CrudRepository;
|
import org.springframework.data.repository.CrudRepository;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@ -9,9 +9,14 @@
|
|||||||
* https://openapi-generator.tech
|
* https://openapi-generator.tech
|
||||||
* Do not edit the class manually.
|
* Do not edit the class manually.
|
||||||
*/
|
*/
|
||||||
|
import { TaskgroupEntityInfo } from './taskgroupEntityInfo';
|
||||||
|
|
||||||
|
|
||||||
export interface TaskgroupPathInfo {
|
export interface TaskgroupPathInfo {
|
||||||
taskgroupPathNames: Array<string>;
|
/**
|
||||||
|
* TaskgroupPath
|
||||||
|
*/
|
||||||
|
taskgroupPath: string;
|
||||||
|
directChildren: Array<TaskgroupEntityInfo>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
<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: 100%">
|
<mat-form-field style="width: 100%">
|
||||||
<mat-label>Toppings</mat-label>
|
<mat-label>Toppings</mat-label>
|
||||||
<mat-select [(ngModel)]="selectedSeries" multiple (ngModelChange)="updateSerieSelection()">
|
<mat-select [(ngModel)]="selectedTaskgroupPath" (ngModelChange)="updateSerieSelection()">
|
||||||
<mat-option *ngFor="let topping of toppingList" [value]="topping">{{topping}}</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>
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ import {
|
|||||||
} 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 {TaskgroupService} from "../../../api";
|
import {HistoryService, TaskgroupPathInfo, TaskgroupService} from "../../../api";
|
||||||
|
|
||||||
export type ChartOptions = {
|
export type ChartOptions = {
|
||||||
series: ApexAxisChartSeries;
|
series: ApexAxisChartSeries;
|
||||||
@ -41,9 +41,9 @@ export class TaskgroupActivityComponent implements OnInit{
|
|||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
toppings = new FormControl();
|
|
||||||
toppingList: string[] = [];
|
selectedTaskgroupPath: TaskgroupPathInfo | undefined
|
||||||
selectedSeries: string[] = []
|
taskgroupPaths: TaskgroupPathInfo[] = []
|
||||||
sliderControl: FormControl = new FormControl()
|
sliderControl: FormControl = new FormControl()
|
||||||
dateRange: Date[] = this.createDateRange();
|
dateRange: Date[] = this.createDateRange();
|
||||||
selectedDateRange: Date[] = this.dateRange;
|
selectedDateRange: Date[] = this.dateRange;
|
||||||
@ -62,17 +62,14 @@ export class TaskgroupActivityComponent implements OnInit{
|
|||||||
@ViewChild("chart") chart?: ChartComponent;
|
@ViewChild("chart") chart?: ChartComponent;
|
||||||
public chartOptions: Partial<ChartOptions> = this.generateChartOptions()
|
public chartOptions: Partial<ChartOptions> = this.generateChartOptions()
|
||||||
|
|
||||||
constructor(private taskgroupService: TaskgroupService) {
|
constructor(private taskgroupService: TaskgroupService,
|
||||||
|
private historyService: HistoryService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.taskgroupService.taskgroupsPathGet().subscribe({
|
this.taskgroupService.taskgroupsPathGet().subscribe({
|
||||||
next: resp => {
|
next: resp => {
|
||||||
resp.forEach(taskgroupPath => {
|
this.taskgroupPaths = resp;
|
||||||
let taskgroupPathName = "";
|
|
||||||
taskgroupPath.taskgroupPathNames.forEach(name => taskgroupPathName += (name + "/"))
|
|
||||||
this.toppingList.push(taskgroupPathName)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -113,7 +110,7 @@ export class TaskgroupActivityComponent implements OnInit{
|
|||||||
|
|
||||||
generateChartOptions(): Partial<ChartOptions> {
|
generateChartOptions(): Partial<ChartOptions> {
|
||||||
return {
|
return {
|
||||||
series: this.selectedSeries.map(serie => this.generateSeries(serie)!),
|
series: this.generateSeries(),
|
||||||
chart: {
|
chart: {
|
||||||
height: 350,
|
height: 350,
|
||||||
type: "bar",
|
type: "bar",
|
||||||
@ -128,28 +125,26 @@ export class TaskgroupActivityComponent implements OnInit{
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
generateSeries(serie: string) {
|
generateSeries() : ApexAxisChartSeries {
|
||||||
if(serie == "KIT/") {
|
const series: ApexAxisChartSeries = []
|
||||||
return {
|
this.selectedTaskgroupPath?.directChildren.forEach(taskgroup => {
|
||||||
name: "Marine Sprite",
|
this.historyService.statisticsTaskgroupActivityTaskgroupIDStartingDateEndingDateIncludeSubTaskgroupsGet(
|
||||||
data: [44, 55, 41, 37, 22, 43, 21]
|
taskgroup.taskgroupID,
|
||||||
}
|
moment(this.selectedDateRange[0]).format("YYYY-MM-DD"),
|
||||||
} else if(serie == "Striking Calf") {
|
moment(this.selectedDateRange[this.selectedDateRange.length-1]).format("YYYY-MM-DD"), true
|
||||||
return {
|
).subscribe({
|
||||||
name: "Striking Calf",
|
next: resp => {
|
||||||
data: [53, 32, 33, 52, 13, 43, 32]
|
series.push(
|
||||||
}
|
{
|
||||||
} else if(serie == "Tank Picture") {
|
name: taskgroup.taskgroupName,
|
||||||
return {
|
data: resp.map(dailyActivityInfo => dailyActivityInfo.activeMinutes)
|
||||||
name: "Tank Picture",
|
}
|
||||||
data: [12, 17, 11, 9, 15, 11, 20]
|
)
|
||||||
}
|
}
|
||||||
} else {
|
})
|
||||||
return {
|
|
||||||
name: "",
|
})
|
||||||
data: []
|
return series;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
80
openapi.yaml
80
openapi.yaml
@ -1905,6 +1905,63 @@ paths:
|
|||||||
schema:
|
schema:
|
||||||
type: object
|
type: object
|
||||||
$ref: "#/components/schemas/SimpleStatusResponse"
|
$ref: "#/components/schemas/SimpleStatusResponse"
|
||||||
|
/statistics/taskgroup-activity/{taskgroupID}/{startingDate}/{endingDate}/{includeSubTaskgroups}:
|
||||||
|
get:
|
||||||
|
security:
|
||||||
|
- API_TOKEN: []
|
||||||
|
tags:
|
||||||
|
- history
|
||||||
|
parameters:
|
||||||
|
- name: taskgroupID
|
||||||
|
in: path
|
||||||
|
description: internal id of taskgroup
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: number
|
||||||
|
example: 1
|
||||||
|
- 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
|
||||||
|
- name: includeSubTaskgroups
|
||||||
|
in: path
|
||||||
|
description: determines whether to include subtaskgroups or not
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: boolean
|
||||||
|
example: false
|
||||||
|
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'
|
||||||
|
|
||||||
|
|
||||||
components:
|
components:
|
||||||
securitySchemes:
|
securitySchemes:
|
||||||
@ -2549,10 +2606,27 @@ components:
|
|||||||
format: date
|
format: date
|
||||||
TaskgroupPathInfo:
|
TaskgroupPathInfo:
|
||||||
required:
|
required:
|
||||||
- taskgroupPathNames
|
- taskgroupPath
|
||||||
|
- directChildren
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
properties:
|
properties:
|
||||||
taskgroupPathNames:
|
taskgroupPath:
|
||||||
|
type: string
|
||||||
|
description: TaskgroupPath
|
||||||
|
directChildren:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
type: string
|
$ref: '#/components/schemas/TaskgroupEntityInfo'
|
||||||
|
TaskgroupActivityInfo:
|
||||||
|
required:
|
||||||
|
- date
|
||||||
|
- activeMinutes
|
||||||
|
additionalProperties: false
|
||||||
|
properties:
|
||||||
|
date:
|
||||||
|
type: string
|
||||||
|
format: date
|
||||||
|
activeMinutes:
|
||||||
|
type: number
|
||||||
|
description: Number of minutes the task was active
|
||||||
|
example: 122
|
Loading…
Reference in New Issue
Block a user