From c6a40bb2a39c2efb912ef51127756fdb86afa728 Mon Sep 17 00:00:00 2001 From: Martin Rojas Miguel Angel Date: Thu, 6 Sep 2018 19:19:54 +0200 Subject: [PATCH] TSK-662 Add task classification report to monitor. --- .../impl/ClassificationReportBuilderImpl.java | 4 +- .../impl/WorkbasketReportBuilderImpl.java | 2 +- .../src/main/asciidoc/rest-api.adoc | 24 +- .../main/resources/sql/sample-data/task.sql | 12 +- .../MonitorControllerRestDocumentation.java | 37 +-- ...lassificationTimeIntervalColumnHeader.java | 33 +++ .../WorkbasketTimeIntervalColumnHeader.java | 26 ++ .../pro/taskana/rest/MonitorController.java | 99 ++++--- .../resource/assembler/ReportAssembler.java | 27 +- web/angular.json | 4 +- web/package-lock.json | 241 +++--------------- web/package.json | 2 +- .../details/workbasket-details.component.scss | 32 --- web/src/app/app-routing.module.ts | 6 +- web/src/app/guards/user-guard.ts | 6 - .../classification-tasks.component.html | 16 ++ .../classification-tasks.component.scss | 0 .../classification-tasks.component.spec.ts | 25 ++ .../classification-tasks.component.ts | 37 +++ web/src/app/monitor/models/chart-colors.ts | 71 ++++++ web/src/app/monitor/models/chart-data.ts | 4 + web/src/app/monitor/models/meta-info-data.ts | 8 + web/src/app/monitor/models/report-data.ts | 8 + .../app/monitor/models/report-info-data.ts | 3 + web/src/app/monitor/monitor.component.html | 33 ++- web/src/app/monitor/monitor.module.ts | 10 +- .../app/monitor/report/report.component.html | 42 +-- .../app/monitor/report/report.component.scss | 3 + .../app/monitor/report/report.component.ts | 45 +--- web/src/app/monitor/report/reportType.ts | 4 +- .../restConnector/rest-connector.service.ts | 50 +++- .../app/monitor/tasks/tasks.component.html | 13 +- web/src/app/monitor/tasks/tasks.component.ts | 33 +-- .../workbasket/workbasket.component.html | 34 +-- .../workbasket/workbasket.component.ts | 55 ++-- web/src/app/shared/util/taskana.date.ts | 7 + web/src/assets/_main.scss | 9 + web/src/assets/_site.scss | 15 +- web/src/assets/_tabs.scss | 32 +++ web/src/main.ts | 4 +- 40 files changed, 631 insertions(+), 485 deletions(-) create mode 100644 rest/taskana-rest-spring/src/main/java/pro/taskana/monitor/ClassificationTimeIntervalColumnHeader.java create mode 100644 rest/taskana-rest-spring/src/main/java/pro/taskana/monitor/WorkbasketTimeIntervalColumnHeader.java create mode 100644 web/src/app/monitor/classification-tasks/classification-tasks.component.html create mode 100644 web/src/app/monitor/classification-tasks/classification-tasks.component.scss create mode 100644 web/src/app/monitor/classification-tasks/classification-tasks.component.spec.ts create mode 100644 web/src/app/monitor/classification-tasks/classification-tasks.component.ts create mode 100644 web/src/app/monitor/models/chart-colors.ts create mode 100644 web/src/app/monitor/models/chart-data.ts create mode 100644 web/src/app/monitor/models/meta-info-data.ts create mode 100644 web/src/app/monitor/models/report-data.ts create mode 100644 web/src/app/monitor/models/report-info-data.ts create mode 100644 web/src/app/monitor/report/report.component.scss create mode 100644 web/src/assets/_main.scss create mode 100644 web/src/assets/_tabs.scss diff --git a/lib/taskana-core/src/main/java/pro/taskana/impl/ClassificationReportBuilderImpl.java b/lib/taskana-core/src/main/java/pro/taskana/impl/ClassificationReportBuilderImpl.java index da3832b56..ce558ade5 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/impl/ClassificationReportBuilderImpl.java +++ b/lib/taskana-core/src/main/java/pro/taskana/impl/ClassificationReportBuilderImpl.java @@ -43,7 +43,7 @@ public class ClassificationReportBuilderImpl @Override public ClassificationReport buildReport() throws InvalidArgumentException, NotAuthorizedException { LOGGER.debug("entry to buildReport(), this = {}", this); - this.taskanaEngine.checkRoleMembership(TaskanaRole.MONITOR); + this.taskanaEngine.checkRoleMembership(TaskanaRole.MONITOR, TaskanaRole.ADMIN); try { this.taskanaEngine.openConnection(); ClassificationReport report = new ClassificationReport(this.columnHeaders); @@ -62,7 +62,7 @@ public class ClassificationReportBuilderImpl @Override public DetailedClassificationReport buildDetailedReport() throws InvalidArgumentException, NotAuthorizedException { LOGGER.debug("entry to buildDetailedReport(), this = {}", this); - this.taskanaEngine.checkRoleMembership(TaskanaRole.MONITOR); + this.taskanaEngine.checkRoleMembership(TaskanaRole.MONITOR, TaskanaRole.ADMIN); try { this.taskanaEngine.openConnection(); DetailedClassificationReport report = new DetailedClassificationReport(this.columnHeaders); diff --git a/lib/taskana-core/src/main/java/pro/taskana/impl/WorkbasketReportBuilderImpl.java b/lib/taskana-core/src/main/java/pro/taskana/impl/WorkbasketReportBuilderImpl.java index 3c392e304..45a8708c5 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/impl/WorkbasketReportBuilderImpl.java +++ b/lib/taskana-core/src/main/java/pro/taskana/impl/WorkbasketReportBuilderImpl.java @@ -43,7 +43,7 @@ public class WorkbasketReportBuilderImpl @Override public WorkbasketReport buildReport() throws InvalidArgumentException, NotAuthorizedException { LOGGER.debug("entry to buildReport(), this = {}", this); - this.taskanaEngine.checkRoleMembership(TaskanaRole.MONITOR); + this.taskanaEngine.checkRoleMembership(TaskanaRole.MONITOR, TaskanaRole.ADMIN); try { this.taskanaEngine.openConnection(); WorkbasketReport report = new WorkbasketReport(this.columnHeaders); diff --git a/rest/taskana-rest-spring-example/src/main/asciidoc/rest-api.adoc b/rest/taskana-rest-spring-example/src/main/asciidoc/rest-api.adoc index d01196c2f..381fcfb25 100644 --- a/rest/taskana-rest-spring-example/src/main/asciidoc/rest-api.adoc +++ b/rest/taskana-rest-spring-example/src/main/asciidoc/rest-api.adoc @@ -603,21 +603,37 @@ include::../../../{snippets}/GetTaskStatusReportDocTest/http-response.adoc[] include::../../../{snippets}/GetTaskStatusReportDocTest/response-fields.adoc[] -=== Get a count of tasks sorted by state +=== Get a tasks workbasket report A `GET` request is used to get the number of tasks sorted by state. ==== Example Request -include::../../../{snippets}/GetCountByStateDocTest/http-request.adoc[] +include::../../../{snippets}/GetTaskWorkbasketReportDocTest/http-request.adoc[] ==== Example Response -include::../../../{snippets}/GetCountByStateDocTest/http-response.adoc[] +include::../../../{snippets}/GetTaskWorkbasketReportDocTest/http-response.adoc[] ==== Response Structure -include::../../../{snippets}/GetCountByStateDocTest/response-fields.adoc[] +Same as task status report + +=== Get a tasks classification report + +A `GET` request is used to get the number of tasks sorted by state. + +==== Example Request + +include::../../../{snippets}/GetTaskClassificationReportDocTest/http-request.adoc[] + +==== Example Response + +include::../../../{snippets}/GetTaskClassificationReportDocTest/http-response.adoc[] + +==== Response Structure + +Same as task status report == Other Resources (using the TaskanaEngineController) diff --git a/rest/taskana-rest-spring-example/src/main/resources/sql/sample-data/task.sql b/rest/taskana-rest-spring-example/src/main/resources/sql/sample-data/task.sql index 06c782212..863b2d7ba 100644 --- a/rest/taskana-rest-spring-example/src/main/resources/sql/sample-data/task.sql +++ b/rest/taskana-rest-spring-example/src/main/resources/sql/sample-data/task.sql @@ -6,8 +6,8 @@ INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000003', '2018-01-29 INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000004', '2018-01-29 15:55:04', null , null , '2018-01-29 15:55:04', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , 'READY' , 'EXTERN' , 'L1050' , 'CLI:100000000000000000000000000000000003', 'WBI:100000000000000000000000000000000001' , 'GPK_KSC' , 'DOMAIN_A', 'PI_0000000000004' , 'DOC_0000000000000000004' , null , '00' , 'PASystem' , '00' , 'VNR' , '11223344' , false , false , null , null , null , 'ade' , null , null , null , null , null , null , null , null , null , null , null , 'abc' , null , null ); INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000005', '2018-01-29 15:55:05', null , null , '2018-01-29 15:55:05', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , 'READY' , 'EXTERN' , 'L1050' , 'CLI:100000000000000000000000000000000003', 'WBI:100000000000000000000000000000000001' , 'GPK_KSC' , 'DOMAIN_A', 'PI_0000000000005' , 'DOC_0000000000000000005' , null , '00' , 'PASystem' , '00' , 'VNR' , '11223344' , false , false , null , null , null , null , null , null , null , null , null , null , null , null , null , null , null , 'abc' , null , null ); INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000006', '2018-01-29 15:55:06', null , null , '2018-01-29 15:55:06', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , 'READY' , 'EXTERN' , 'L1050' , 'CLI:100000000000000000000000000000000003', 'WBI:100000000000000000000000000000000001' , 'GPK_KSC' , 'DOMAIN_A', 'PI_0000000000006' , 'DOC_0000000000000000006' , null , '00' , 'PASystem' , '00' , 'VNR' , '11223344' , false , false , null , null , null , null , null , null , null , null , null , null , null , null , null , null , null , 'abc' , null , null ); -INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000007', '2018-01-29 15:55:07', null , null , '2018-01-29 15:55:07', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , 'READY' , 'EXTERN' , 'L1050' , 'CLI:100000000000000000000000000000000003', 'WBI:100000000000000000000000000000000001' , 'GPK_KSC' , 'DOMAIN_A', 'PI_0000000000007' , 'DOC_0000000000000000007' , null , '00' , 'PASystem' , '00' , 'VNR' , '11223344' , false , false , null , null , null , null , 'ffg' , null , null , null , null , null , null , null , null , null , null , 'abc' , null , null ); -INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000008', '2018-01-29 15:55:08', null , null , '2018-01-29 15:55:08', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , 'READY' , 'EXTERN' , 'L1050' , 'CLI:100000000000000000000000000000000003', 'WBI:100000000000000000000000000000000001' , 'GPK_KSC' , 'DOMAIN_A', 'PI_0000000000008' , 'DOC_0000000000000000008' , null , '00' , 'PASystem' , '00' , 'VNR' , '22334455' , false , false , null , null , null , null , null , null , null , null , null , null , null , null , null , null , null , 'abc' , null , null ); +INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000007', '2018-01-29 15:55:07', null , null , '2018-01-29 15:55:07', '2018-01-29 15:55:00', '2018-08-11 15:55:00', 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , 'READY' , 'EXTERN' , 'L1050' , 'CLI:100000000000000000000000000000000003', 'WBI:100000000000000000000000000000000001' , 'GPK_KSC' , 'DOMAIN_A', 'PI_0000000000007' , 'DOC_0000000000000000007' , null , '00' , 'PASystem' , '00' , 'VNR' , '11223344' , false , false , null , null , null , null , 'ffg' , null , null , null , null , null , null , null , null , null , null , 'abc' , null , null ); +INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000008', '2018-01-29 15:55:08', null , null , '2018-01-29 15:55:08', '2018-01-29 15:55:00', '2018-08-11 15:55:00', 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , 'READY' , 'EXTERN' , 'L1050' , 'CLI:100000000000000000000000000000000003', 'WBI:100000000000000000000000000000000001' , 'GPK_KSC' , 'DOMAIN_A', 'PI_0000000000008' , 'DOC_0000000000000000008' , null , '00' , 'PASystem' , '00' , 'VNR' , '22334455' , false , false , null , null , null , null , null , null , null , null , null , null , null , null , null , null , null , 'abc' , null , null ); INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000009', '2018-01-29 15:55:09', null , null , '2018-01-29 15:55:09', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , 'READY' , 'EXTERN' , 'L1050' , 'CLI:100000000000000000000000000000000003', 'WBI:100000000000000000000000000000000001' , 'GPK_KSC' , 'DOMAIN_A', 'PI_0000000000009' , 'DOC_0000000000000000009' , null , '00' , 'PASystem' , '00' , 'VNR' , '22334455' , false , false , null , null , null , null , null , null , null , null , null , null , null , null , null , null , null , 'abc' , null , null ); INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000010', '2018-01-29 15:55:10', null , null , '2018-01-29 15:55:10', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , 'READY' , 'EXTERN' , 'L1050' , 'CLI:100000000000000000000000000000000003', 'WBI:100000000000000000000000000000000001' , 'GPK_KSC' , 'DOMAIN_A', 'PI_0000000000010' , 'DOC_0000000000000000010' , null , '00' , 'PASystem' , '00' , 'VNR' , '22334455' , false , false , null , null , null , null , null , 'rty' , null , null , null , null , null , null , null , null , null , 'abc' , null , null ); INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000011', '2018-01-29 15:55:11', null , null , '2018-01-29 15:55:11', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , 'READY' , 'EXTERN' , 'L1050' , 'CLI:100000000000000000000000000000000003', 'WBI:100000000000000000000000000000000001' , 'GPK_KSC' , 'DOMAIN_A', 'PI_0000000000011' , 'DOC_0000000000000000011' , null , '00' , 'PASystem' , '00' , 'VNR' , '22334455' , false , false , null , null , null , null , null , null , null , null , null , null , null , null , null , null , null , 'abc' , null , null ); @@ -58,10 +58,10 @@ INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000048', '2018-01-29 INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000049', '2018-01-29 15:55:08', null , null , '2018-01-29 15:55:08', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Widerruf' , 'creator_user_id2' , 'Widerruf' , null , 2 , 'READY' , 'EXTERN' , 'L1060' , 'CLI:200000000000000000000000000000000017', 'WBI:100000000000000000000000000000000015' , 'USER_3_2' , 'DOMAIN_B', 'PI_0000000000008' , 'DOC_0000000000000000003' , null , '00' , 'PASyste1' , '00' , 'VNR' , '22334455' , false , false , null , null , null , null , null , null , null , null , null , null , null , null , null , null , null , 'abc' , null , null ); INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000050', '2018-01-29 15:55:09', null , null , '2018-01-29 15:55:09', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Widerruf' , 'creator_user_id2' , 'Widerruf' , null , 2 , 'READY' , 'EXTERN' , 'L1060' , 'CLI:200000000000000000000000000000000017', 'WBI:100000000000000000000000000000000015' , 'USER_3_2' , 'DOMAIN_B', 'PI_0000000000009' , 'DOC_0000000000000000009' , null , '00' , 'PASyste1' , '05' , 'VNR' , '22334455' , false , false , null , null , null , null , null , null , null , null , null , null , null , null , null , null , null , 'abc' , null , null ); INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000051', '2018-01-29 15:55:10', null , null , '2018-01-29 15:55:10', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Widerruf' , 'creator_user_id2' , 'Widerruf' , null , 2 , 'READY' , 'EXTERN' , 'L1060' , 'CLI:200000000000000000000000000000000017', 'WBI:100000000000000000000000000000000015' , 'USER_3_2' , 'DOMAIN_B', 'PI_0000000000010' , 'DOC_0000000000000000010' , null , '00' , 'PASyste1' , '00' , 'VNR' , '22334455' , false , false , null , null , null , null , null , null , null , null , null , null , null , null , null , null , null , 'abc' , null , null ); -INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000052', '2018-01-29 15:55:11', null , null , '2018-01-29 15:55:11', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , 'READY' , 'EXTERN' , 'L1060' , 'CLI:200000000000000000000000000000000017', 'WBI:100000000000000000000000000000000015' , 'USER_3_2' , 'DOMAIN_B', 'PI_0000000000011' , 'DOC_0000000000000000011' , null , '00' , 'PASystem' , '04' , 'VNR' , '22334455' , false , false , null , null , null , null , null , null , null , null , null , null , null , null , null , null , null , 'abc' , null , null ); -INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000053', '2018-01-29 15:55:12', null , null , '2018-01-29 15:55:12', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Widerruf' , 'creator_user_id3' , 'Widerruf' , null , 2 , 'READY' , 'EXTERN' , 'L1060' , 'CLI:200000000000000000000000000000000017', 'WBI:100000000000000000000000000000000015' , 'USER_3_2' , 'DOMAIN_B', 'PI_0000000000012' , 'DOC_0000000000000000012' , null , '00' , 'PASystem' , '00' , 'VNR' , '22334455' , false , false , null , null , null , null , null , null , null , null , null , null , null , null , null , null , null , 'abc' , null , null ); -INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000054', '2018-01-29 15:55:13', null , null , '2018-01-29 15:55:13', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Widerruf' , 'erstellerSpezial' , 'Widerruf' , null , 2 , 'READY' , 'EXTERN' , 'L1060' , 'CLI:200000000000000000000000000000000017', 'WBI:100000000000000000000000000000000015' , 'USER_3_2' , 'DOMAIN_B', 'PI_0000000000010' , 'DOC_0000000000000000011' , null , '00' , 'PASystem' , '00' , 'VNR' , '22334455' , false , false , null , null , null , null , null , null , null , null , null , null , null , null , null , null , null , 'abc' , null , null ); -INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000055', '2018-01-29 15:55:14', null , null , '2018-01-29 15:55:14', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Widerruf' , 'Ersteller1' , 'Widerruf' , null , 2 , 'READY' , 'EXTERN' , 'L1060' , 'CLI:200000000000000000000000000000000017', 'WBI:100000000000000000000000000000000015' , 'USER_3_2' , 'DOMAIN_B', 'PI_0000000000014' , 'DOC_0000000000000000014' , null , '00' , 'PASyste1' , '04' , 'VNR' , '12345678' , false , false , null , null , null , null , null , null , null , null , null , null , null , null , null , null , null , 'abc' , null , null ); +INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000052', '2018-01-29 15:55:11', null , null , '2018-01-29 15:55:11', '2018-01-29 15:55:00', '2018-08-15 15:55:00', 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , 'READY' , 'EXTERN' , 'L1060' , 'CLI:200000000000000000000000000000000017', 'WBI:100000000000000000000000000000000015' , 'USER_3_2' , 'DOMAIN_B', 'PI_0000000000011' , 'DOC_0000000000000000011' , null , '00' , 'PASystem' , '04' , 'VNR' , '22334455' , false , false , null , null , null , null , null , null , null , null , null , null , null , null , null , null , null , 'abc' , null , null ); +INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000053', '2018-01-29 15:55:12', null , null , '2018-01-29 15:55:12', '2018-01-29 15:55:00', '2018-08-14 15:55:00', 'Widerruf' , 'creator_user_id3' , 'Widerruf' , null , 2 , 'READY' , 'EXTERN' , 'L1060' , 'CLI:200000000000000000000000000000000017', 'WBI:100000000000000000000000000000000015' , 'USER_3_2' , 'DOMAIN_B', 'PI_0000000000012' , 'DOC_0000000000000000012' , null , '00' , 'PASystem' , '00' , 'VNR' , '22334455' , false , false , null , null , null , null , null , null , null , null , null , null , null , null , null , null , null , 'abc' , null , null ); +INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000054', '2018-01-29 15:55:13', null , null , '2018-01-29 15:55:13', '2018-01-29 15:55:00', '2018-08-13 15:55:00', 'Widerruf' , 'erstellerSpezial' , 'Widerruf' , null , 2 , 'READY' , 'EXTERN' , 'L1060' , 'CLI:200000000000000000000000000000000017', 'WBI:100000000000000000000000000000000015' , 'USER_3_2' , 'DOMAIN_B', 'PI_0000000000010' , 'DOC_0000000000000000011' , null , '00' , 'PASystem' , '00' , 'VNR' , '22334455' , false , false , null , null , null , null , null , null , null , null , null , null , null , null , null , null , null , 'abc' , null , null ); +INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000055', '2018-01-29 15:55:14', null , null , '2018-01-29 15:55:14', '2018-01-29 15:55:00', '2018-08-13 15:55:00', 'Widerruf' , 'Ersteller1' , 'Widerruf' , null , 2 , 'READY' , 'EXTERN' , 'L1060' , 'CLI:200000000000000000000000000000000017', 'WBI:100000000000000000000000000000000015' , 'USER_3_2' , 'DOMAIN_B', 'PI_0000000000014' , 'DOC_0000000000000000014' , null , '00' , 'PASyste1' , '04' , 'VNR' , '12345678' , false , false , null , null , null , null , null , null , null , null , null , null , null , null , null , null , null , 'abc' , null , null ); INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000056', '2018-01-29 15:55:15', null , null , '2018-01-29 15:55:15', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Widerruf' , 'Ersteller1' , 'Widerruf' , null , 2 , 'READY' , 'EXTERN' , 'L1060' , 'CLI:200000000000000000000000000000000017', 'WBI:100000000000000000000000000000000015' , 'USER_3_2' , 'DOMAIN_B', 'PI_0000000000015' , 'DOC_0000000000000000015' , null , '00' , 'PASyste1' , '00' , 'VNR' , '23456789' , false , true , null , null , null , null , null , null , null , null , null , null , null , null , null , null , null , 'abc' , null , null ); INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000057', '2018-01-29 15:55:16', null , null , '2018-01-29 15:55:16', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , 'READY' , 'EXTERN' , 'L1060' , 'CLI:200000000000000000000000000000000017', 'WBI:100000000000000000000000000000000015' , 'USER_3_2' , 'DOMAIN_B', 'PI_0000000000010' , 'DOC_0000000000000000011' , null , '00' , 'PASyste2' , '00' , 'VNR' , '34567890' , false , true , null , null , null , null , null , null , null , null , null , null , null , null , null , null , null , 'abc' , null , null ); INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000058', '2018-01-29 15:55:17', null , null , '2018-01-29 15:55:17', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , 'READY' , 'EXTERN' , 'L1060' , 'CLI:200000000000000000000000000000000017', 'WBI:100000000000000000000000000000000015' , 'USER_3_2' , 'DOMAIN_B', 'PI_0000000000017' , 'DOC_0000000000000000017' , null , '00' , 'PASystem' , '03' , 'VNR' , '45678901' , false , false , null , null , null , null , null , null , null , null , null , null , null , null , null , null , null , 'abc' , null , null ); diff --git a/rest/taskana-rest-spring-example/src/test/java/pro/taskana/doc/api/MonitorControllerRestDocumentation.java b/rest/taskana-rest-spring-example/src/test/java/pro/taskana/doc/api/MonitorControllerRestDocumentation.java index 557d20b56..f82d98dbd 100644 --- a/rest/taskana-rest-spring-example/src/test/java/pro/taskana/doc/api/MonitorControllerRestDocumentation.java +++ b/rest/taskana-rest-spring-example/src/test/java/pro/taskana/doc/api/MonitorControllerRestDocumentation.java @@ -45,9 +45,8 @@ public class MonitorControllerRestDocumentation { private MockMvc mockMvc; - private FieldDescriptor[] taskStatusReportFieldDescriptors; - private FieldDescriptor[] countByStateFieldDescriptors; - + private FieldDescriptor[] taskReportFieldDescriptors; + @Before public void setUp() { document("{methodName}", @@ -62,7 +61,7 @@ public class MonitorControllerRestDocumentation { .withRequestDefaults(prettyPrint())) .build(); - taskStatusReportFieldDescriptors = new FieldDescriptor[] { + taskReportFieldDescriptors = new FieldDescriptor[] { fieldWithPath("meta").description("Object holding metainfo on the report"), fieldWithPath("meta.name").description("Name of the report"), fieldWithPath("meta.date").description("Date of the report creation"), @@ -77,31 +76,37 @@ public class MonitorControllerRestDocumentation { fieldWithPath("sumRow.total").description("Total number of tasks"), fieldWithPath("_links.self.href").ignored() }; - - countByStateFieldDescriptors = new FieldDescriptor[] { - fieldWithPath("[]..state").description("The state the tasks are in"), - fieldWithPath("[]..counter").description("Number of tasks in the corresponding state") - }; } @Test public void getTaskStatusReport() throws Exception { this.mockMvc.perform(RestDocumentationRequestBuilders - .get("http://127.0.0.1:" + port + "/v1/monitor/taskStatusReport") + .get("http://127.0.0.1:" + port + "/v1/monitor/tasks-status-report") .header("Authorization", "Basic YWRtaW46YWRtaW4=")) .andExpect(MockMvcResultMatchers.status().isOk()) .andDo(MockMvcRestDocumentation.document("GetTaskStatusReportDocTest", - responseFields(taskStatusReportFieldDescriptors))); + responseFields(taskReportFieldDescriptors))); } @Test - public void getCountByState() throws Exception { + public void tasksWorkbasketReport() throws Exception { this.mockMvc.perform(RestDocumentationRequestBuilders - .get("http://127.0.0.1:" + port + "/v1/monitor/countByState?states=READY,CLAIMED,COMPLETED") + .get("http://127.0.0.1:" + port + "/v1/monitor/tasks-workbasket-report?daysInPast=4&states=READY,CLAIMED,COMPLETED") .accept("application/hal+json") - .header("Authorization", "Basic dGVhbWxlYWRfMTp0ZWFtbGVhZF8x")) + .header("Authorization", "Basic YWRtaW46YWRtaW4=")) .andExpect(MockMvcResultMatchers.status().isOk()) - .andDo(MockMvcRestDocumentation.document("GetCountByStateDocTest", - responseFields(countByStateFieldDescriptors))); + .andDo(MockMvcRestDocumentation.document("GetTaskWorkbasketReportDocTest", + responseFields(taskReportFieldDescriptors))); + } + + @Test + public void tasksClassificationReport() throws Exception { + this.mockMvc.perform(RestDocumentationRequestBuilders + .get("http://127.0.0.1:" + port + "/v1/monitor/tasks-classification-report") + .accept("application/hal+json") + .header("Authorization", "Basic YWRtaW46YWRtaW4=")) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andDo(MockMvcRestDocumentation.document("GetTaskClassificationReportDocTest", + responseFields(taskReportFieldDescriptors))); } } diff --git a/rest/taskana-rest-spring/src/main/java/pro/taskana/monitor/ClassificationTimeIntervalColumnHeader.java b/rest/taskana-rest-spring/src/main/java/pro/taskana/monitor/ClassificationTimeIntervalColumnHeader.java new file mode 100644 index 000000000..d62fe1f73 --- /dev/null +++ b/rest/taskana-rest-spring/src/main/java/pro/taskana/monitor/ClassificationTimeIntervalColumnHeader.java @@ -0,0 +1,33 @@ +package pro.taskana.monitor; + +import pro.taskana.impl.report.TimeIntervalColumnHeader; + +/** + * Class for Classification time Interval Column Header, overrides displayName. + * + * @author mmr + */ +public class ClassificationTimeIntervalColumnHeader extends TimeIntervalColumnHeader { + + public ClassificationTimeIntervalColumnHeader(int ageInDays) { + super(ageInDays); + } + + public ClassificationTimeIntervalColumnHeader(int lowerAgeLimit, int upperAgeLimit) { + super(lowerAgeLimit, upperAgeLimit); + } + + @Override + public String getDisplayName() { + if (this.getLowerAgeLimit() == Integer.MIN_VALUE) { + return "<" + this.getUpperAgeLimit(); + } else if (this.getUpperAgeLimit() == Integer.MAX_VALUE) { + return ">" + this.getLowerAgeLimit(); + } else if (this.getLowerAgeLimit() == this.getUpperAgeLimit()) { + return this.getUpperAgeLimit() + ""; + } else if (this.getLowerAgeLimit() != this.getUpperAgeLimit()) { + return "[" + this.getLowerAgeLimit() + " ... " + this.getUpperAgeLimit() + "]"; + } + return super.getDisplayName(); + } +} diff --git a/rest/taskana-rest-spring/src/main/java/pro/taskana/monitor/WorkbasketTimeIntervalColumnHeader.java b/rest/taskana-rest-spring/src/main/java/pro/taskana/monitor/WorkbasketTimeIntervalColumnHeader.java new file mode 100644 index 000000000..8d2bfed5a --- /dev/null +++ b/rest/taskana-rest-spring/src/main/java/pro/taskana/monitor/WorkbasketTimeIntervalColumnHeader.java @@ -0,0 +1,26 @@ +package pro.taskana.monitor; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Locale; + +import pro.taskana.impl.report.TimeIntervalColumnHeader; + +/** + * Class for Workbasket Time Interval Column Header, overrides displayName. + * + * @author mmr + */ +public class WorkbasketTimeIntervalColumnHeader extends TimeIntervalColumnHeader { + + public WorkbasketTimeIntervalColumnHeader(int ageInDays) { + super(ageInDays); + } + + @Override + public String getDisplayName() { + LocalDateTime ldt = LocalDateTime.now().plusDays(getLowerAgeLimit()); + DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd", Locale.ENGLISH); + return dateFormat.format(ldt); + } +} diff --git a/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/MonitorController.java b/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/MonitorController.java index 8e65f9c93..5d06ba8b0 100644 --- a/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/MonitorController.java +++ b/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/MonitorController.java @@ -1,6 +1,9 @@ package pro.taskana.rest; +import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; @@ -15,6 +18,9 @@ import pro.taskana.TaskMonitorService; import pro.taskana.TaskState; import pro.taskana.exceptions.InvalidArgumentException; import pro.taskana.exceptions.NotAuthorizedException; +import pro.taskana.impl.report.TimeIntervalColumnHeader; +import pro.taskana.monitor.ClassificationTimeIntervalColumnHeader; +import pro.taskana.monitor.WorkbasketTimeIntervalColumnHeader; import pro.taskana.rest.resource.ReportResource; import pro.taskana.rest.resource.assembler.ReportAssembler; @@ -31,54 +37,63 @@ public class MonitorController { @Autowired private ReportAssembler reportAssembler; - @GetMapping(path = "/countByState") + @GetMapping(path = "/tasks-status-report") @Transactional(readOnly = true, rollbackFor = Exception.class) - public ResponseEntity getTaskcountForState( - @RequestParam(value = "states") List taskStates) { - String taskCount = "[{\"state\": \"READY\", \"counter\": 7},{\"state\": \"CLAIMED\",\"counter\": 4},{\"state\": \"COMPLETED\",\"counter\": 4 }]"; - return ResponseEntity.status(HttpStatus.OK).body(taskCount); - } - - @GetMapping(path = "/taskcountByWorkbasketDaysAndState") - @Transactional(readOnly = true, rollbackFor = Exception.class) - public ResponseEntity getTaskCountByWorkbasketAndDaysInPastAndState( - @RequestParam(value = "daysInPast") Long daysInPast, - @RequestParam(value = "states") List states) { - - StringBuilder builder = new StringBuilder(); - builder.append( - "{ \"dates\": [\"02.02.2018\",\"03.02.2018\",\"04.02.2018\", \"05.02.2018\", \"06.02.2018\", \"07.02.2018\",\"08.02.2018\",\"09.02.2018\",\"10.02.2018\",\"11.02.2018\", \"12.02.2018\"],"); - builder.append("\"data\": ["); - builder.append("{\"data\": [0,0,0,0,0,0,0,0,0,0,0],\"label\": \"Basket1\"},"); - builder.append("{\"data\": [0,0,0,0,0,0,0,0,0,0,0],\"label\": \"Basket2\"},"); - builder.append("{\"data\": [0,0,0,0,0,0,0,0,0,0,0],\"label\": \"Basket3\"},"); - builder.append("{\"data\": [0,0,0,0,0,0,0,0,0,0,0],\"label\": \"Basket4\"},"); - builder.append("{\"data\": [0,0,0,0,0,0,0,0,0,0,0],\"label\": \"Gruppenpostkorb KSC\"},"); - builder.append("{\"data\": [0,0,0,0,0,0,0,0,0,0,0],\"label\": \"Gruppenpostkorb KSC 1\"},"); - builder.append("{\"data\": [0,0,0,0,0,0,0,0,0,0,0],\"label\": \"Gruppenpostkorb KSC 2\"},"); - builder.append("{\"data\": [0,0,0,0,0,0,0,0,0,0,0],\"label\": \"PPK Teamlead KSC 1\"},"); - builder.append("{\"data\": [0,0,0,0,0,0,0,0,0,0,0],\"label\": \"PPK Teamlead KSC 2\"},"); - builder.append("{\"data\": [0,0,0,0,0,0,0,0,0,0,0],\"label\": \"PPK User 1 KSC 1\"},"); - builder.append("{\"data\": [0,0,0,0,0,0,0,0,0,0,0],\"label\": \"PPK User 2 KSC 1\"},"); - builder.append("{\"data\": [0,0,0,0,0,0,0,0,0,0,0],\"label\": \"PPK User 1 KSC 2\"},"); - builder.append("{\"data\": [0,0,0,0,0,0,0,0,0,0,0],\"label\": \"PPK User 2 KSC 2\"},"); - builder.append("{\"data\": [0,0,0,0,0,0,0,0,0,0,0],\"label\": \"Gruppenpostkorb KSC B\"},"); - builder.append("{\"data\": [0,0,0,0,0,0,0,0,0,0,0],\"label\": \"Gruppenpostkorb KSC B1\"},"); - builder.append("{\"data\": [0,0,0,0,0,0,0,0,0,0,0],\"label\": \"Gruppenpostkorb KSC B2\"}"); - builder.append("]}"); - return ResponseEntity.status(HttpStatus.OK).body(builder.toString()); - } - - @GetMapping(path = "/taskStatusReport") - @Transactional(readOnly = true, rollbackFor = Exception.class) - public ResponseEntity getTaskStatusReport(@RequestParam(required = false) List domains, + public ResponseEntity getTasksStatusReport(@RequestParam(required = false) List domains, @RequestParam(required = false) List states) throws NotAuthorizedException, InvalidArgumentException { - // return ResponseEntity.status(HttpStatus.OK) - // .body(reportAssembler.toResource(taskMonitorService.getTaskStatusReport(domains, states), domains, states)); return ResponseEntity.status(HttpStatus.OK) .body(reportAssembler.toResource( taskMonitorService.createTaskStatusReportBuilder().stateIn(states).domainIn(domains).buildReport(), domains, states)); } + + @GetMapping(path = "/tasks-workbasket-report") + @Transactional(readOnly = true, rollbackFor = Exception.class) + public ResponseEntity getTasksWorkbasketReport( + @RequestParam(value = "daysInPast") int daysInPast, + @RequestParam(value = "states") List states) + throws NotAuthorizedException, InvalidArgumentException { + return ResponseEntity.status(HttpStatus.OK) + .body(reportAssembler.toResource( + taskMonitorService.createWorkbasketReportBuilder() + .stateIn(states) + .withColumnHeaders(getTasksWorkbasketsTimeInterval(daysInPast)).buildReport(), daysInPast, states)); + + } + + @GetMapping(path = "/tasks-classification-report") + @Transactional(readOnly = true, rollbackFor = Exception.class) + public ResponseEntity getTasksClassificationReport() + throws NotAuthorizedException, InvalidArgumentException { + + return ResponseEntity.status(HttpStatus.OK) + .body(reportAssembler.toResource( + taskMonitorService.createClassificationReportBuilder() + .withColumnHeaders(getTaskClassificationTimeInterval()) + .buildReport())); + } + + private List getTaskClassificationTimeInterval() { + return Stream.concat(Stream.concat( + Stream.of(new ClassificationTimeIntervalColumnHeader(Integer.MIN_VALUE, -10), + new ClassificationTimeIntervalColumnHeader(-10, -5) + ), + Stream.of(-4, -3, -2, -1, 0, 1, 2, 3, 4) + .map(ClassificationTimeIntervalColumnHeader::new) + ), + Stream.of(new ClassificationTimeIntervalColumnHeader(5, 10), + new ClassificationTimeIntervalColumnHeader(10, Integer.MAX_VALUE) + )) + .collect(Collectors.toList()); + } + + private List getTasksWorkbasketsTimeInterval(int daysInPast) { + + List columnHeaders = new ArrayList<>(); + for (int i = 0; i <= daysInPast; i++) { + columnHeaders.add(new WorkbasketTimeIntervalColumnHeader(i - daysInPast)); + } + return columnHeaders; + } } diff --git a/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/resource/assembler/ReportAssembler.java b/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/resource/assembler/ReportAssembler.java index c5c905257..57e6a98ef 100644 --- a/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/resource/assembler/ReportAssembler.java +++ b/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/resource/assembler/ReportAssembler.java @@ -19,6 +19,9 @@ import pro.taskana.report.Report; import pro.taskana.report.ReportColumnHeader; import pro.taskana.report.ReportRow; import pro.taskana.report.TaskStatusReport; +import pro.taskana.report.ClassificationReport; +import pro.taskana.report.WorkbasketReport; + import pro.taskana.rest.MonitorController; import pro.taskana.rest.resource.ReportResource; @@ -30,14 +33,32 @@ public class ReportAssembler { public ReportResource toResource(TaskStatusReport report, List domains, List states) throws NotAuthorizedException, InvalidArgumentException { - ReportResource resource = toResource(report); + ReportResource resource = toReportResource(report); resource.add( - linkTo(methodOn(MonitorController.class).getTaskStatusReport(domains, states)) + linkTo(methodOn(MonitorController.class).getTasksStatusReport(domains, states)) .withSelfRel().expand()); return resource; } - private > ReportResource toResource( + public ReportResource toResource(ClassificationReport report) + throws NotAuthorizedException, InvalidArgumentException { + ReportResource resource = toReportResource(report); + resource.add( + linkTo(methodOn(MonitorController.class).getTasksClassificationReport()) + .withSelfRel().expand()); + return resource; + } + + public ReportResource toResource(WorkbasketReport report, int daysInPast, List states) + throws NotAuthorizedException, InvalidArgumentException { + ReportResource resource = toReportResource(report); + resource.add( + linkTo(methodOn(MonitorController.class).getTasksWorkbasketReport(daysInPast, states)) + .withSelfRel().expand()); + return resource; + } + + private > ReportResource toReportResource( Report report) { String[] header = report.getColumnHeaders() .stream() diff --git a/web/angular.json b/web/angular.json index 3ad5fe604..d63cc964f 100644 --- a/web/angular.json +++ b/web/angular.json @@ -21,7 +21,7 @@ "src/environments/data-sources" ], "styles": [ - "src/assets/_styles.scss" + "src/assets/_main.scss" ], "scripts": [ "node_modules/jquery/dist/jquery.min.js", @@ -78,7 +78,7 @@ "node_modules/bootstrap/dist/js/bootstrap.min.js" ], "styles": [ - "src/assets/_styles.scss" + "src/assets/_main.scss" ], "assets": [ "src/assets", diff --git a/web/package-lock.json b/web/package-lock.json index ba3c76be5..104f21a4e 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1669,9 +1669,9 @@ "dev": true }, "@types/selenium-webdriver": { - "version": "2.53.43", - "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-2.53.43.tgz", - "integrity": "sha512-UBYHWph6P3tutkbXpW6XYg9ZPbTKjw/YC2hGG1/GEvWwTbvezBUv3h+mmUFw79T3RFPnmedpiXdOBbXX+4l0jg==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-3.0.10.tgz", + "integrity": "sha512-ikB0JHv6vCR1KYUQAzTO4gi/lXLElT4Tx+6De2pc/OZwizE9LRNiTa+U8TBFKBD/nntPnr/MPSHSnOTybjhqNA==", "dev": true }, "@types/strip-bom": { @@ -2059,9 +2059,9 @@ "optional": true }, "adm-zip": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.4.tgz", - "integrity": "sha1-ph7VrmkFw66lizplfSUDMJEFJzY=", + "version": "0.4.11", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.11.tgz", + "integrity": "sha512-L8vcjDTCOIJk7wFvmlEUN7AsSb8T+2JrdP7KINBjzr24TJ5Mwj590sLu3BC7zNZowvJWa/JtPmD8eJCzdtDWjA==", "dev": true }, "after": { @@ -2964,6 +2964,15 @@ "electron-to-chromium": "^1.3.47" } }, + "browserstack": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/browserstack/-/browserstack-1.5.1.tgz", + "integrity": "sha512-O8VMT64P9NOLhuIoD4YngyxBURefaSdR4QdhG8l6HZ9VxtU7jc3m6jLufFwKA5gaf7fetfB2TnRJnMxyob+heg==", + "dev": true, + "requires": { + "https-proxy-agent": "^2.2.1" + } + }, "buffer": { "version": "4.9.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", @@ -5145,7 +5154,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.1.1", @@ -5221,6 +5231,7 @@ "version": "2.10.1", "bundled": true, "dev": true, + "optional": true, "requires": { "hoek": "2.x.x" } @@ -5392,6 +5403,7 @@ "version": "1.0.11", "bundled": true, "dev": true, + "optional": true, "requires": { "graceful-fs": "^4.1.2", "inherits": "~2.0.0", @@ -5459,7 +5471,8 @@ "graceful-fs": { "version": "4.1.11", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "har-schema": { "version": "1.0.5", @@ -5653,6 +5666,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -5942,6 +5956,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -10720,12 +10735,6 @@ } } }, - "options": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/options/-/options-0.0.6.tgz", - "integrity": "sha1-7CLTEoBrtT5zF3Pnza788cZDEo8=", - "dev": true - }, "original": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/original/-/original-1.0.1.tgz", @@ -11285,15 +11294,16 @@ } }, "protractor": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/protractor/-/protractor-5.3.2.tgz", - "integrity": "sha512-pw4uwwiy5lHZjIguxNpkEwJJa7hVz+bJsvaTI+IbXlfn2qXwzbF8eghW/RmrZwE2sGx82I8etb8lVjQ+JrjejA==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/protractor/-/protractor-5.4.0.tgz", + "integrity": "sha512-6TSYqMhUUzxr4/wN0ttSISqPMKvcVRXF4k8jOEpGWD8OioLak4KLgfzHK9FJ49IrjzRrZ+Mx1q2Op8Rk0zEcnQ==", "dev": true, "requires": { "@types/node": "^6.0.46", "@types/q": "^0.0.32", - "@types/selenium-webdriver": "~2.53.39", + "@types/selenium-webdriver": "^3.0.0", "blocking-proxy": "^1.0.0", + "browserstack": "^1.5.1", "chalk": "^1.1.3", "glob": "^7.0.3", "jasmine": "2.8.0", @@ -11303,50 +11313,14 @@ "saucelabs": "^1.5.0", "selenium-webdriver": "3.6.0", "source-map-support": "~0.4.0", - "webdriver-js-extender": "^1.0.0", + "webdriver-js-extender": "2.0.0", "webdriver-manager": "^12.0.6" }, "dependencies": { "@types/node": { - "version": "6.0.114", - "resolved": "https://registry.npmjs.org/@types/node/-/node-6.0.114.tgz", - "integrity": "sha512-5ViC9dwf1VIAtrOFTvOuN04lJgw28eKjuy0Vg2Bd/fSlxKP2feCSkIw04ZgOENL2ywdWrtbkthp1XVLEjJmouw==", - "dev": true - }, - "adm-zip": { - "version": "0.4.11", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.11.tgz", - "integrity": "sha512-L8vcjDTCOIJk7wFvmlEUN7AsSb8T+2JrdP7KINBjzr24TJ5Mwj590sLu3BC7zNZowvJWa/JtPmD8eJCzdtDWjA==", - "dev": true - }, - "ajv": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", - "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", - "dev": true, - "requires": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "version": "6.0.116", + "resolved": "https://registry.npmjs.org/@types/node/-/node-6.0.116.tgz", + "integrity": "sha512-vToa8YEeulfyYg1gSOeHjvvIRqrokng62VMSj2hoZrwZNcYrp2h3AWo6KeBVuymIklQUaY5zgVJvVsC4KiiLkQ==", "dev": true }, "del": { @@ -11364,28 +11338,6 @@ "rimraf": "^2.2.8" } }, - "form-data": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", - "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "1.0.6", - "mime-types": "^2.1.12" - }, - "dependencies": { - "combined-stream": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", - "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", - "dev": true, - "requires": { - "delayed-stream": "~1.0.0" - } - } - } - }, "globby": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", @@ -11400,70 +11352,6 @@ "pinkie-promise": "^2.0.0" } }, - "har-validator": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", - "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", - "dev": true, - "requires": { - "ajv": "^5.1.0", - "har-schema": "^2.0.0" - } - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "dev": true - }, - "request": { - "version": "2.87.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.87.0.tgz", - "integrity": "sha512-fcogkm7Az5bsS6Sl0sibkbhcKsnyon/jV1kF3ajGmF0c8HrttdKTPRT9hieOaQHA5HEq6r8OyWOo/o781C1tNw==", - "dev": true, - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.6.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.5", - "extend": "~3.0.1", - "forever-agent": "~0.6.1", - "form-data": "~2.3.1", - "har-validator": "~5.0.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.17", - "oauth-sign": "~0.8.2", - "performance-now": "^2.1.0", - "qs": "~6.5.1", - "safe-buffer": "^5.1.1", - "tough-cookie": "~2.3.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.1.0" - } - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, - "requires": { - "safe-buffer": "^5.0.1" - } - }, "webdriver-manager": { "version": "12.1.0", "resolved": "https://registry.npmjs.org/webdriver-manager/-/webdriver-manager-12.1.0.tgz", @@ -14974,66 +14862,13 @@ } }, "webdriver-js-extender": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/webdriver-js-extender/-/webdriver-js-extender-1.0.0.tgz", - "integrity": "sha1-gcUzqeM9W/tZe05j4s2yW1R3dRU=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/webdriver-js-extender/-/webdriver-js-extender-2.0.0.tgz", + "integrity": "sha512-fbyKiVu3azzIc5d4+26YfuPQcFTlgFQV5yQ/0OQj4Ybkl4g1YQuIPskf5v5wqwRJhHJnPHthB6tqCjWHOKLWag==", "dev": true, "requires": { - "@types/selenium-webdriver": "^2.53.35", - "selenium-webdriver": "^2.53.2" - }, - "dependencies": { - "sax": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-0.6.1.tgz", - "integrity": "sha1-VjsZx8HeiS4Jv8Ty/DDjwn8JUrk=", - "dev": true - }, - "selenium-webdriver": { - "version": "2.53.3", - "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-2.53.3.tgz", - "integrity": "sha1-0p/1qVff8aG0ncRXdW5OS/vc4IU=", - "dev": true, - "requires": { - "adm-zip": "0.4.4", - "rimraf": "^2.2.8", - "tmp": "0.0.24", - "ws": "^1.0.1", - "xml2js": "0.4.4" - } - }, - "tmp": { - "version": "0.0.24", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.24.tgz", - "integrity": "sha1-1qXhmNFKmDXMby18PZ4wJCjIzxI=", - "dev": true - }, - "ultron": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz", - "integrity": "sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po=", - "dev": true - }, - "ws": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/ws/-/ws-1.1.5.tgz", - "integrity": "sha512-o3KqipXNUdS7wpQzBHSe180lBGO60SoK0yVo3CYJgb2MkobuWuBX6dhkYP5ORCLd55y+SaflMOV5fqAB53ux4w==", - "dev": true, - "requires": { - "options": ">=0.0.5", - "ultron": "1.0.x" - } - }, - "xml2js": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.4.tgz", - "integrity": "sha1-MREBAAMAiuGSQOuhdJe1fHKcVV0=", - "dev": true, - "requires": { - "sax": "0.6.x", - "xmlbuilder": ">=1.0.0" - } - } + "@types/selenium-webdriver": "^3.0.0", + "selenium-webdriver": "^3.0.1" } }, "webpack": { @@ -15790,6 +15625,7 @@ "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "1.0.0", "concat-map": "0.0.1" @@ -15958,6 +15794,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } diff --git a/web/package.json b/web/package.json index c1b03e693..10f03b7fe 100644 --- a/web/package.json +++ b/web/package.json @@ -57,7 +57,7 @@ "karma-jasmine-html-reporter": "0.2.2", "moment": "2.22.1", "ng2-mock-component": "0.1.1", - "protractor": "5.3.2", + "protractor": "^5.4.0", "ts-mockito": "2.3.0", "ts-node": "4.1.0", "tslint": "5.9.1", diff --git a/web/src/app/administration/workbasket/details/workbasket-details.component.scss b/web/src/app/administration/workbasket/details/workbasket-details.component.scss index 132c55fc4..e69de29bb 100644 --- a/web/src/app/administration/workbasket/details/workbasket-details.component.scss +++ b/web/src/app/administration/workbasket/details/workbasket-details.component.scss @@ -1,32 +0,0 @@ -.nav.nav-tabs { - & > li { - & > a { - min-height: 52px; - padding-top: 15px; - border-radius: 0px; - &.has-changes{ - border-bottom: solid #f0ad4e; - } - cursor: pointer; - } - - &:first-child > a{ - border-left: none; - } - } - & > li.active > a { - border-top: 3px solid #479ea9; - padding-top: 13px; - background-color: #f5f5f5; - } - & > p{ - margin: 0px; - } - & > li.disabled { - cursor: not-allowed; - } - - & > li.disabled > a { - pointer-events: none; - } -} diff --git a/web/src/app/app-routing.module.ts b/web/src/app/app-routing.module.ts index 8dc4bcbcb..583bf03d6 100644 --- a/web/src/app/app-routing.module.ts +++ b/web/src/app/app-routing.module.ts @@ -3,9 +3,9 @@ import { RouterModule, Routes } from '@angular/router'; import { AppComponent } from './app.component'; -import { BusinessAdminGuard } from 'app/guards/business-admin-guard'; -import { MonitorGuard } from 'app/guards/monitor-guard'; -import { UserGuard } from 'app/guards/user-guard'; +import { BusinessAdminGuard } from './guards/business-admin-guard'; +import { MonitorGuard } from './guards/monitor-guard'; +import { UserGuard } from './guards/user-guard'; import { NoAccessComponent } from './components/no-access/no-access.component'; const appRoutes: Routes = [ diff --git a/web/src/app/guards/user-guard.ts b/web/src/app/guards/user-guard.ts index a27ff082c..2092e1a26 100644 --- a/web/src/app/guards/user-guard.ts +++ b/web/src/app/guards/user-guard.ts @@ -1,12 +1,6 @@ -import { Observable } from 'rxjs'; -import { HttpClient } from '@angular/common/http'; import { CanActivate, Router } from '@angular/router'; import { Injectable } from '@angular/core'; -import { DomainService } from 'app/services/domain/domain.service'; -import { ErrorModalService } from 'app/services/errorModal/error-modal.service'; -import { ErrorModel } from 'app/models/modal-error'; import { TaskanaEngineService } from 'app/services/taskana-engine/taskana-engine.service'; -import { WindowRefService } from 'app/services/window/window.service'; @Injectable() export class UserGuard implements CanActivate { diff --git a/web/src/app/monitor/classification-tasks/classification-tasks.component.html b/web/src/app/monitor/classification-tasks/classification-tasks.component.html new file mode 100644 index 000000000..2d2266719 --- /dev/null +++ b/web/src/app/monitor/classification-tasks/classification-tasks.component.html @@ -0,0 +1,16 @@ +
+
+

{{reportData.meta.name}} ({{reportData.meta.date | date : 'dd.MM.yyyy HH:mm:ss'}})

+
+
+
+
+
+ +
+
+
+ +
+
\ No newline at end of file diff --git a/web/src/app/monitor/classification-tasks/classification-tasks.component.scss b/web/src/app/monitor/classification-tasks/classification-tasks.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/web/src/app/monitor/classification-tasks/classification-tasks.component.spec.ts b/web/src/app/monitor/classification-tasks/classification-tasks.component.spec.ts new file mode 100644 index 000000000..331d06007 --- /dev/null +++ b/web/src/app/monitor/classification-tasks/classification-tasks.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ClassificationTasksComponent } from './classification-tasks.component'; + +describe('ClassificationTasksComponent', () => { + let component: ClassificationTasksComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ClassificationTasksComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ClassificationTasksComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/web/src/app/monitor/classification-tasks/classification-tasks.component.ts b/web/src/app/monitor/classification-tasks/classification-tasks.component.ts new file mode 100644 index 000000000..887bde09e --- /dev/null +++ b/web/src/app/monitor/classification-tasks/classification-tasks.component.ts @@ -0,0 +1,37 @@ +import { Component, OnInit } from '@angular/core'; +import { ReportType } from '../report/reportType'; +import { RestConnectorService } from 'app/monitor/services/restConnector/rest-connector.service'; +import { ReportData } from '../models/report-data'; +import { ChartData } from 'app/monitor/models/chart-data'; +import { ChartColorsDefinition } from '../models/chart-colors'; + +@Component({ + selector: 'taskana-monitor-classification-tasks', + templateUrl: './classification-tasks.component.html', + styleUrls: ['./classification-tasks.component.scss'] +}) +export class ClassificationTasksComponent implements OnInit { + reportType = ReportType.ClassificationStatus; + reportData: ReportData; + + + lineChartLabels: Array; + lineChartLegend = true; + lineChartType = 'line'; + lineChartData: Array; + lineChartOptions: any = { + responsive: true + }; + lineChartColors = ChartColorsDefinition.getColors(); + + constructor(private restConnectorService: RestConnectorService) { + } + + ngOnInit() { + this.restConnectorService.getClassificationTasksReport().subscribe((data: ReportData) => { + this.reportData = data; + this.lineChartData = this.restConnectorService.getChartData(data); + this.lineChartLabels = this.restConnectorService.getChartHeaders(data); + }) + } +} diff --git a/web/src/app/monitor/models/chart-colors.ts b/web/src/app/monitor/models/chart-colors.ts new file mode 100644 index 000000000..33e8e1777 --- /dev/null +++ b/web/src/app/monitor/models/chart-colors.ts @@ -0,0 +1,71 @@ +export class ChartColorsDefinition { + public static getColors(): Array { + return [ + { // red + backgroundColor: 'rgba(215, 40, 40, 0.2)', + borderColor: 'rgba(215, 40, 40, 1)', + pointBackgroundColor: 'rgba(215, 40, 40, 1)', + pointBorderColor: '#fff', + pointHoverBackgroundColor: '#fff', + pointHoverBorderColor: 'rgba(215, 40, 40, 0.8)' + }, + { // yellow + backgroundColor: 'rgba(208, 205, 44, 0.2)', + borderColor: 'rgba(208, 205, 44, 1)', + pointBackgroundColor: 'rgba(208, 205, 44, 1)', + pointBorderColor: '#fff', + pointHoverBackgroundColor: '#fff', + pointHoverBorderColor: 'rgba(208, 205, 44, 0.8)' + }, + { // green + backgroundColor: 'rgba(45, 205, 44, 0.2)', + borderColor: 'rgba(45, 205, 44, 1)', + pointBackgroundColor: 'rgba(45, 205, 44, 1)', + pointBorderColor: '#fff', + pointHoverBackgroundColor: '#fff', + pointHoverBorderColor: 'rgba(45, 205, 44, 0.8)' + }, + { // blue + backgroundColor: 'rgba(45, 45, 201, 0.2)', + borderColor: 'rgba(45, 45, 201, 1)', + pointBackgroundColor: 'rgba(45, 45, 201, 1)', + pointBorderColor: '#fff', + pointHoverBackgroundColor: '#fff', + pointHoverBorderColor: 'rgba(45, 45, 201, 0.8)' + }, + { // cyan + backgroundColor: 'rgba(45, 190, 201, 0.2)', + borderColor: 'rgba(45, 190, 201, 1)', + pointBackgroundColor: 'rgba(45, 190, 201, 1)', + pointBorderColor: '#fff', + pointHoverBackgroundColor: '#fff', + pointHoverBorderColor: 'rgba(45, 190, 201, 0.8)' + }, + { // black-grey + backgroundColor: 'rgba(37, 35, 38, 0.2)', + borderColor: 'rgba(37, 35, 38, 1)', + pointBackgroundColor: 'rgba(37, 35, 38, 1)', + pointBorderColor: '#fff', + pointHoverBackgroundColor: '#fff', + pointHoverBorderColor: 'rgba(37, 35, 38, 0.8)' + }, + { // orange + backgroundColor: 'rgba(221, 125, 0, 0.2)', + borderColor: 'rgba(221, 125, 0, 1)', + pointBackgroundColor: 'rgba(221, 125, 0, 1)', + pointBorderColor: '#fff', + pointHoverBackgroundColor: '#fff', + pointHoverBorderColor: 'rgba(221, 125, 0, 0.8)' + }, + { // lile + backgroundColor: 'rgba(180, 137, 255, 0.2)', + borderColor: 'rgba(180, 137, 255, 1)', + pointBackgroundColor: 'rgba(180, 137, 255, 1)', + pointBorderColor: '#fff', + pointHoverBackgroundColor: '#fff', + pointHoverBorderColor: 'rgba(180, 137, 255, 0.8)' + } + ] + } + +} diff --git a/web/src/app/monitor/models/chart-data.ts b/web/src/app/monitor/models/chart-data.ts new file mode 100644 index 000000000..697771d03 --- /dev/null +++ b/web/src/app/monitor/models/chart-data.ts @@ -0,0 +1,4 @@ +export class ChartData { + data: Array; + label: string; +} diff --git a/web/src/app/monitor/models/meta-info-data.ts b/web/src/app/monitor/models/meta-info-data.ts new file mode 100644 index 000000000..168c679e7 --- /dev/null +++ b/web/src/app/monitor/models/meta-info-data.ts @@ -0,0 +1,8 @@ + +export class MetaInfoData { + date: string; + header: Array; + name: string; + rowDesc: string; + TotalDesc: string; +} diff --git a/web/src/app/monitor/models/report-data.ts b/web/src/app/monitor/models/report-data.ts new file mode 100644 index 000000000..f8789405e --- /dev/null +++ b/web/src/app/monitor/models/report-data.ts @@ -0,0 +1,8 @@ +import { ReportInfoData } from './report-info-data'; +import { MetaInfoData } from './meta-info-data'; + +export class ReportData { + meta: MetaInfoData; + rows: Map; + sumRow: ReportInfoData; +} diff --git a/web/src/app/monitor/models/report-info-data.ts b/web/src/app/monitor/models/report-info-data.ts new file mode 100644 index 000000000..9b07e1344 --- /dev/null +++ b/web/src/app/monitor/models/report-info-data.ts @@ -0,0 +1,3 @@ +export class ReportInfoData { + cells: Map; +} diff --git a/web/src/app/monitor/monitor.component.html b/web/src/app/monitor/monitor.component.html index e354a85eb..8a01e092c 100644 --- a/web/src/app/monitor/monitor.component.html +++ b/web/src/app/monitor/monitor.component.html @@ -1,10 +1,27 @@
- - - - - - - - + +
+
+ +
+
+ +
+
+ +
+
diff --git a/web/src/app/monitor/monitor.module.ts b/web/src/app/monitor/monitor.module.ts index 4a627f268..a428c1709 100644 --- a/web/src/app/monitor/monitor.module.ts +++ b/web/src/app/monitor/monitor.module.ts @@ -7,12 +7,13 @@ import { TabsModule } from 'ngx-bootstrap/tabs'; import { HttpClientModule } from '@angular/common/http'; import { AngularSvgIconModule } from 'angular-svg-icon'; import { MonitorRoutingModule } from './monitor-routing.module'; -import { SharedModule } from 'app/shared/shared.module'; +import { SharedModule } from '../shared/shared.module'; -import { TasksComponent } from './tasks/tasks.component'; -import { WorkbasketComponent } from './workbasket/workbasket.component'; import { ReportComponent } from './report/report.component'; import { MonitorComponent } from './monitor.component'; +import { TasksComponent } from './tasks/tasks.component'; +import { WorkbasketComponent } from './workbasket/workbasket.component'; +import { ClassificationTasksComponent } from './classification-tasks/classification-tasks.component'; import { RestConnectorService } from './services/restConnector/rest-connector.service'; @@ -31,7 +32,8 @@ const DECLARATIONS = [ TasksComponent, WorkbasketComponent, ReportComponent, - MonitorComponent + MonitorComponent, + ClassificationTasksComponent ]; @NgModule({ diff --git a/web/src/app/monitor/report/report.component.html b/web/src/app/monitor/report/report.component.html index 0840ff582..7abe7c9b2 100644 --- a/web/src/app/monitor/report/report.component.html +++ b/web/src/app/monitor/report/report.component.html @@ -1,22 +1,28 @@ -
+
-

{{meta.name}} ({{meta.date | date : 'dd.MM.yyyy HH:mm:ss'}})

- - - - - - - - - - - - - - - - +
{{meta.rowDesc}}{{header}}{{meta.totalDesc}}
{{row.key}}{{row.val.cells[header]}}{{row.val.total}}
{{meta.totalDesc}}{{sumRow.cells[header]}}{{sumRow.total}}
+ + + + + + + + + + + + + + + + + + + + +
{{reportData.meta.rowDesc}} + {{header}}{{reportData.meta.totalDesc}}
{{row.key}}{{row.val.cells[header]}}{{row.val.total}}
{{reportData.meta.totalDesc}}{{reportData.sumRow.cells[header]}}{{reportData.sumRow.total}}
diff --git a/web/src/app/monitor/report/report.component.scss b/web/src/app/monitor/report/report.component.scss new file mode 100644 index 000000000..ebd37f945 --- /dev/null +++ b/web/src/app/monitor/report/report.component.scss @@ -0,0 +1,3 @@ +.report{ + padding-top: 20px; +} \ No newline at end of file diff --git a/web/src/app/monitor/report/report.component.ts b/web/src/app/monitor/report/report.component.ts index 575fba0c2..1df76b094 100644 --- a/web/src/app/monitor/report/report.component.ts +++ b/web/src/app/monitor/report/report.component.ts @@ -1,55 +1,26 @@ import { Component, Input, OnInit } from '@angular/core'; -import { RestConnectorService } from 'app/monitor/services/restConnector/rest-connector.service'; -import { error } from 'util'; import { ReportType } from './reportType'; +import { ReportData } from 'app/monitor/models/report-data'; +import { RestConnectorService } from 'app/monitor/services/restConnector/rest-connector.service'; @Component({ selector: 'taskana-report', - templateUrl: './report.component.html' + templateUrl: './report.component.html', + styleUrls: ['./report.component.scss'] }) export class ReportComponent implements OnInit { @Input() type: ReportType; + @Input() + reportData: ReportData - meta: ReportMeta; - /* - * The keys of the rows object are unknown. They represent the name of that specific row. - * Each row (value of rows Object) has two keys: 'cells:Object' and 'total:number'. - * The keys of 'cells' are the same as 'meta.header:number'. - * This also applies to sumRow. - */ - rows: Object; - sumRow: Object; - - - isDataAvailable = false; + reportType = ReportType; constructor(private restConnector: RestConnectorService) { } ngOnInit(): void { - switch (this.type) { - case ReportType.WorkbasketStatus: - this.restConnector.getTaskStatusReport().subscribe(res => { - this.meta = res['meta']; - this.rows = res['rows']; - this.sumRow = res['sumRow']; - this.isDataAvailable = true; - }); - break; - default: - error(`unknown ReportType ${this.type}`); - } + } - -} - -class ReportMeta { - name: string; - date: string; - header: Array; - rowDesc: string; - totalDesc: string; - } diff --git a/web/src/app/monitor/report/reportType.ts b/web/src/app/monitor/report/reportType.ts index 5a29c07fe..b9a838b64 100644 --- a/web/src/app/monitor/report/reportType.ts +++ b/web/src/app/monitor/report/reportType.ts @@ -1,3 +1,5 @@ export enum ReportType { - WorkbasketStatus + TasksStatus, + WorkbasketStatus, + ClassificationStatus } diff --git a/web/src/app/monitor/services/restConnector/rest-connector.service.ts b/web/src/app/monitor/services/restConnector/rest-connector.service.ts index eadf12919..1f70502ed 100644 --- a/web/src/app/monitor/services/restConnector/rest-connector.service.ts +++ b/web/src/app/monitor/services/restConnector/rest-connector.service.ts @@ -1,9 +1,9 @@ import { Injectable } from '@angular/core'; -import { HttpClient, HttpHeaders } from '@angular/common/http'; -import { environment } from '../../../../environments/environment'; +import { HttpClient } from '@angular/common/http'; +import { environment } from 'environments/environment'; import { Observable } from 'rxjs'; -import { State } from 'app/models/state'; -import { WorkbasketCounter } from 'app/monitor/models/workbasket-counter'; +import { ReportData } from '../../models/report-data'; +import { ChartData } from 'app/monitor/models/chart-data'; @Injectable() export class RestConnectorService { @@ -11,18 +11,44 @@ export class RestConnectorService { constructor(private httpClient: HttpClient) { } - getTaskStatistics(): Observable { - return this.httpClient.get>(environment.taskanaRestUrl + '/v1/monitor/countByState?states=READY,CLAIMED,COMPLETED') + getTaskStatusReport(): Observable { + return this.httpClient.get(environment.taskanaRestUrl + '/v1/monitor/tasks-status-report?states=READY,CLAIMED,COMPLETED') } - - getWorkbasketStatistics(): Observable { - return this.httpClient.get(environment.taskanaRestUrl - + '/v1/monitor/taskcountByWorkbasketDaysAndState?daysInPast=5&states=READY,CLAIMED,COMPLETED') + getWorkbasketStatistics(): Observable { + return this.httpClient.get(environment.taskanaRestUrl + + '/v1/monitor/tasks-workbasket-report?daysInPast=5&states=READY,CLAIMED,COMPLETED'); } - getTaskStatusReport(): Observable { - return this.httpClient.get(environment.taskanaRestUrl + '/v1/monitor/taskStatusReport') + getClassificationTasksReport(): Observable { + return this.httpClient.get(environment.taskanaRestUrl + + '/v1/monitor/tasks-classification-report'); } + getChartData(source: ReportData): Array { + const result = new Array(); + + Object.keys(source.rows).forEach(key => { + const rowData = new ChartData(); + + rowData.label = key; + rowData.data = new Array(); + + source.meta.header.forEach((headerValue: string) => { + rowData.data.push(source.rows[key].cells[headerValue]); + }) + + result.push(rowData) + }) + + return result; + } + + getChartHeaders(source: ReportData): Array { + const result = new Array(); + source.meta.header.forEach((header: string) => { + result.push(header); + }) + return result; + } } diff --git a/web/src/app/monitor/tasks/tasks.component.html b/web/src/app/monitor/tasks/tasks.component.html index 5fd9cc235..2bee785eb 100644 --- a/web/src/app/monitor/tasks/tasks.component.html +++ b/web/src/app/monitor/tasks/tasks.component.html @@ -1,4 +1,11 @@ -
- +
+
+

{{reportData.meta.name}} ({{reportData.meta.date | date : 'dd.MM.yyyy HH:mm:ss'}})

+
+
+
+ +
+ +
- diff --git a/web/src/app/monitor/tasks/tasks.component.ts b/web/src/app/monitor/tasks/tasks.component.ts index dbd63c172..478b28289 100644 --- a/web/src/app/monitor/tasks/tasks.component.ts +++ b/web/src/app/monitor/tasks/tasks.component.ts @@ -1,9 +1,10 @@ import { Component, OnInit } from '@angular/core'; import { ReportType } from '../report/reportType'; import { RestConnectorService } from '../services/restConnector/rest-connector.service'; +import { ReportData } from 'app/monitor/models/report-data'; @Component({ - selector: 'taskana-tasks', + selector: 'taskana-monitor-tasks', templateUrl: './tasks.component.html', styleUrls: ['./tasks.component.scss'], }) @@ -13,30 +14,20 @@ export class TasksComponent implements OnInit { pieChartLabels: string[] = ['Ready', 'Claimed', 'Completed']; pieChartData: number[] = []; pieChartType = 'pie'; - isDataAvailable = false; - reportType = ReportType.WorkbasketStatus; + reportData: ReportData + reportType = ReportType.TasksStatus; constructor(private restConnectorService: RestConnectorService) { } ngOnInit() { - this.restConnectorService.getTaskStatistics().subscribe(data => { - if (data.find(x => x.state === 'READY') !== null) { - this.pieChartData.push(data.find(x => x.state === 'READY').counter); - } else { - this.pieChartData.push(0); - } - if (data.find(x => x.state === 'CLAIMED') !== null) { - this.pieChartData.push(data.find(x => x.state === 'CLAIMED').counter); - } else { - this.pieChartData.push(0); - } - if (data.find(x => x.state === 'COMPLETED') !== null) { - this.pieChartData.push(data.find(x => x.state === 'COMPLETED').counter); - } else { - this.pieChartData.push(0); - } - this.isDataAvailable = true; - }); + this.restConnectorService.getTaskStatusReport().subscribe((data: ReportData) => { + this.reportData = data; + Object.keys(data.sumRow.cells).forEach(key => { + this.pieChartData.push(data.sumRow.cells[key]); + }) + + }) + } } diff --git a/web/src/app/monitor/workbasket/workbasket.component.html b/web/src/app/monitor/workbasket/workbasket.component.html index 80e085e52..8fe14fc0f 100644 --- a/web/src/app/monitor/workbasket/workbasket.component.html +++ b/web/src/app/monitor/workbasket/workbasket.component.html @@ -1,22 +1,16 @@ -
-
- - - - - - - - - -
Workbaskets{{label}}
{{d.label}}{{d && d.data[j]}}
+
+
+

{{reportData.meta.name}} ({{reportData.meta.date | date : 'dd.MM.yyyy HH:mm:ss'}})

+
+
+
+
+
+ +
+
+
+
-
-
-
- -
-
-
\ No newline at end of file diff --git a/web/src/app/monitor/workbasket/workbasket.component.ts b/web/src/app/monitor/workbasket/workbasket.component.ts index 33bcb3c9f..98373dbe7 100644 --- a/web/src/app/monitor/workbasket/workbasket.component.ts +++ b/web/src/app/monitor/workbasket/workbasket.component.ts @@ -1,60 +1,39 @@ import { Component, OnInit } from '@angular/core'; import { RestConnectorService } from 'app/monitor/services/restConnector/rest-connector.service'; -import { WorkbasketCounter } from 'app/monitor/models/workbasket-counter'; -import { WorkbasketCounterData } from 'app/monitor/models/workbasket-counter-data'; +import { ChartColorsDefinition } from '../models/chart-colors'; +import { ReportData } from 'app/monitor/models/report-data'; +import { ReportType } from 'app/monitor/report/reportType'; +import { ChartData } from 'app/monitor/models/chart-data'; @Component({ - selector: 'taskana-workbasket', + selector: 'taskana-monitor-workbaskets', templateUrl: './workbasket.component.html', styleUrls: ['./workbasket.component.scss'], providers: [RestConnectorService] }) export class WorkbasketComponent implements OnInit { - isDataAvailable = false; + reportType = ReportType.WorkbasketStatus; + reportData: ReportData; + + lineChartLabels: Array; lineChartLegend = true; lineChartType = 'line'; - lineChartData: Array; + lineChartData: Array; lineChartOptions: any = { responsive: true }; - lineChartColors: Array = [ - { // grey - backgroundColor: 'rgba(148,159,177,0.2)', - borderColor: 'rgba(148,159,177,1)', - pointBackgroundColor: 'rgba(148,159,177,1)', - pointBorderColor: '#fff', - pointHoverBackgroundColor: '#fff', - pointHoverBorderColor: 'rgba(148,159,177,0.8)' - }, - { // dark grey - backgroundColor: 'rgba(77,83,96,0.2)', - borderColor: 'rgba(77,83,96,1)', - pointBackgroundColor: 'rgba(77,83,96,1)', - pointBorderColor: '#fff', - pointHoverBackgroundColor: '#fff', - pointHoverBorderColor: 'rgba(77,83,96,1)' - }, - { // grey - backgroundColor: 'rgba(148,159,177,0.2)', - borderColor: 'rgba(148,159,177,1)', - pointBackgroundColor: 'rgba(148,159,177,1)', - pointBorderColor: '#fff', - pointHoverBackgroundColor: '#fff', - pointHoverBorderColor: 'rgba(148,159,177,0.8)' - } - ]; - - private counter: WorkbasketCounter; + lineChartColors = ChartColorsDefinition.getColors(); constructor(private restConnectorService: RestConnectorService) { } + ngOnInit() { - this.restConnectorService.getWorkbasketStatistics().subscribe(data => { - this.lineChartLabels = data.dates; - this.lineChartData = data.data; - this.isDataAvailable = true; - }); + this.restConnectorService.getWorkbasketStatistics().subscribe((data: ReportData) => { + this.reportData = data; + this.lineChartLabels = this.restConnectorService.getChartHeaders(data); + this.lineChartData = this.restConnectorService.getChartData(data); + }) } } diff --git a/web/src/app/shared/util/taskana.date.ts b/web/src/app/shared/util/taskana.date.ts index 1c7783fe9..c2bf76fca 100644 --- a/web/src/app/shared/util/taskana.date.ts +++ b/web/src/app/shared/util/taskana.date.ts @@ -7,4 +7,11 @@ export class TaskanaDate { const datePipe = new DatePipe(dateLocale); return datePipe.transform(Date.now(), dateFormat) + 'Z'; } + + public static convertSimpleDate(date: Date): string { + const dateFormat = 'yyyy-MM-dd'; + const dateLocale = 'en-US'; + const datePipe = new DatePipe(dateLocale); + return datePipe.transform(date, dateFormat); + } } diff --git a/web/src/assets/_main.scss b/web/src/assets/_main.scss new file mode 100644 index 000000000..8dedc8e2f --- /dev/null +++ b/web/src/assets/_main.scss @@ -0,0 +1,9 @@ +@import 'variables'; +@import '../../node_modules/bootstrap-sass/assets/stylesheets/_bootstrap'; +@import '../../node_modules/angular-tree-component/dist/angular-tree-component.css'; +@import 'site'; +@import 'forms'; +@import 'tree'; +@import 'type-ahead'; +@import 'checkboxes'; +@import 'tabs'; diff --git a/web/src/assets/_site.scss b/web/src/assets/_site.scss index baa03d916..94830f24d 100644 --- a/web/src/assets/_site.scss +++ b/web/src/assets/_site.scss @@ -272,7 +272,7 @@ svg-icon.fa-fw > svg { } -taskana-workbasket-information, taskana-workbasket-access-items, taskana-workbaskets-distribution-targets { +taskana-workbasket-information, taskana-workbasket-access-items, taskana-workbaskets-distribution-targets, taskana-monitor-tasks, taskana-monitor-workbaskets, taskana-monitor-classification-tasks { &> .panel{ border: none; box-shadow: none; @@ -285,6 +285,15 @@ taskana-workbasket-information, taskana-workbasket-access-items, taskana-workbas } } +taskana-monitor-tasks, taskana-monitor-workbaskets, taskana-monitor-classification-tasks { + &> .panel { + &> .panel-heading { + border-left: 1px solid #ddd; + border-right: 1px solid #ddd; + } + } +} + li.list-group-item.active:hover, { color: #fff; background-color: $green; @@ -323,3 +332,7 @@ li.list-group-item:hover, { .center-block { text-align: center; } + +.align-center { + text-align: center; +} diff --git a/web/src/assets/_tabs.scss b/web/src/assets/_tabs.scss new file mode 100644 index 000000000..132c55fc4 --- /dev/null +++ b/web/src/assets/_tabs.scss @@ -0,0 +1,32 @@ +.nav.nav-tabs { + & > li { + & > a { + min-height: 52px; + padding-top: 15px; + border-radius: 0px; + &.has-changes{ + border-bottom: solid #f0ad4e; + } + cursor: pointer; + } + + &:first-child > a{ + border-left: none; + } + } + & > li.active > a { + border-top: 3px solid #479ea9; + padding-top: 13px; + background-color: #f5f5f5; + } + & > p{ + margin: 0px; + } + & > li.disabled { + cursor: not-allowed; + } + + & > li.disabled > a { + pointer-events: none; + } +} diff --git a/web/src/main.ts b/web/src/main.ts index a9ca1caf8..4b391703c 100644 --- a/web/src/main.ts +++ b/web/src/main.ts @@ -1,8 +1,8 @@ import { enableProdMode } from '@angular/core'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; -import { AppModule } from './app/app.module'; -import { environment } from './environments/environment'; +import { AppModule } from 'app/app.module'; +import { environment } from 'environments/environment'; if (environment.production) { enableProdMode();