From 31aa6a2348a0854de0a41e0cc4cc61cc0ca17077 Mon Sep 17 00:00:00 2001 From: Trung Mai Date: Fri, 2 Feb 2024 14:48:54 +0700 Subject: [PATCH 001/143] TE-538: init workflow estimator --- workflow-estimator-demo/.classpath | 28 +++++++++++ workflow-estimator-demo/.gitignore | 19 +++++++ workflow-estimator-demo/.project | 49 +++++++++++++++++++ workflow-estimator-demo/.settings/.jsdtscope | 12 +++++ .../.settings/ch.ivyteam.ivy.designer.prefs | 5 ++ .../.settings/org.eclipse.jdt.core.prefs | 10 ++++ .../org.eclipse.wst.common.component | 10 ++++ ...se.wst.common.project.facet.core.prefs.xml | 7 +++ ....eclipse.wst.common.project.facet.core.xml | 8 +++ .../.settings/org.eclipse.wst.css.core.prefs | 2 + ...rg.eclipse.wst.jsdt.ui.superType.container | 1 + .../org.eclipse.wst.jsdt.ui.superType.name | 1 + .../config/custom-fields.yaml | 22 +++++++++ workflow-estimator-demo/config/databases.yaml | 2 + workflow-estimator-demo/config/overrides.any | 1 + .../config/persistence.xml | 2 + .../config/rest-clients.yaml | 2 + workflow-estimator-demo/config/roles.xml | 4 ++ workflow-estimator-demo/config/users.xml | 2 + workflow-estimator-demo/config/variables.yaml | 6 +++ .../config/webservice-clients.yaml | 2 + .../utils/estimator/demo/Data.ivyClass | 2 + workflow-estimator-demo/pom.xml | 19 +++++++ workflow-estimator-product/.project | 17 +++++++ .../.settings/org.eclipse.m2e.core.prefs | 4 ++ .../README.md | 0 .../pom.xml | 4 +- .../product.json | 0 .../zip.xml | 0 workflow-estimator/.classpath | 28 +++++++++++ workflow-estimator/.gitignore | 19 +++++++ workflow-estimator/.project | 49 +++++++++++++++++++ workflow-estimator/.settings/.jsdtscope | 12 +++++ .../.settings/ch.ivyteam.ivy.designer.prefs | 5 ++ .../.settings/org.eclipse.jdt.core.prefs | 10 ++++ .../org.eclipse.wst.common.component | 10 ++++ ...se.wst.common.project.facet.core.prefs.xml | 7 +++ ....eclipse.wst.common.project.facet.core.xml | 8 +++ .../.settings/org.eclipse.wst.css.core.prefs | 2 + ...rg.eclipse.wst.jsdt.ui.superType.container | 1 + .../org.eclipse.wst.jsdt.ui.superType.name | 1 + workflow-estimator/config/custom-fields.yaml | 22 +++++++++ workflow-estimator/config/databases.yaml | 2 + workflow-estimator/config/overrides.any | 1 + workflow-estimator/config/persistence.xml | 2 + workflow-estimator/config/rest-clients.yaml | 2 + workflow-estimator/config/roles.xml | 4 ++ workflow-estimator/config/users.xml | 2 + workflow-estimator/config/variables.yaml | 6 +++ .../config/webservice-clients.yaml | 2 + .../com/axonivy/utils/estimator/Data.ivyClass | 2 + workflow-estimator/pom.xml | 19 +++++++ 52 files changed, 455 insertions(+), 2 deletions(-) create mode 100644 workflow-estimator-demo/.classpath create mode 100644 workflow-estimator-demo/.gitignore create mode 100644 workflow-estimator-demo/.project create mode 100644 workflow-estimator-demo/.settings/.jsdtscope create mode 100644 workflow-estimator-demo/.settings/ch.ivyteam.ivy.designer.prefs create mode 100644 workflow-estimator-demo/.settings/org.eclipse.jdt.core.prefs create mode 100644 workflow-estimator-demo/.settings/org.eclipse.wst.common.component create mode 100644 workflow-estimator-demo/.settings/org.eclipse.wst.common.project.facet.core.prefs.xml create mode 100644 workflow-estimator-demo/.settings/org.eclipse.wst.common.project.facet.core.xml create mode 100644 workflow-estimator-demo/.settings/org.eclipse.wst.css.core.prefs create mode 100644 workflow-estimator-demo/.settings/org.eclipse.wst.jsdt.ui.superType.container create mode 100644 workflow-estimator-demo/.settings/org.eclipse.wst.jsdt.ui.superType.name create mode 100644 workflow-estimator-demo/config/custom-fields.yaml create mode 100644 workflow-estimator-demo/config/databases.yaml create mode 100644 workflow-estimator-demo/config/overrides.any create mode 100644 workflow-estimator-demo/config/persistence.xml create mode 100644 workflow-estimator-demo/config/rest-clients.yaml create mode 100644 workflow-estimator-demo/config/roles.xml create mode 100644 workflow-estimator-demo/config/users.xml create mode 100644 workflow-estimator-demo/config/variables.yaml create mode 100644 workflow-estimator-demo/config/webservice-clients.yaml create mode 100644 workflow-estimator-demo/dataclasses/com/axonivy/utils/estimator/demo/Data.ivyClass create mode 100644 workflow-estimator-demo/pom.xml create mode 100644 workflow-estimator-product/.project create mode 100644 workflow-estimator-product/.settings/org.eclipse.m2e.core.prefs rename {MY-PRODUCT-NAME-product => workflow-estimator-product}/README.md (100%) rename {MY-PRODUCT-NAME-product => workflow-estimator-product}/pom.xml (93%) rename {MY-PRODUCT-NAME-product => workflow-estimator-product}/product.json (100%) rename {MY-PRODUCT-NAME-product => workflow-estimator-product}/zip.xml (100%) create mode 100644 workflow-estimator/.classpath create mode 100644 workflow-estimator/.gitignore create mode 100644 workflow-estimator/.project create mode 100644 workflow-estimator/.settings/.jsdtscope create mode 100644 workflow-estimator/.settings/ch.ivyteam.ivy.designer.prefs create mode 100644 workflow-estimator/.settings/org.eclipse.jdt.core.prefs create mode 100644 workflow-estimator/.settings/org.eclipse.wst.common.component create mode 100644 workflow-estimator/.settings/org.eclipse.wst.common.project.facet.core.prefs.xml create mode 100644 workflow-estimator/.settings/org.eclipse.wst.common.project.facet.core.xml create mode 100644 workflow-estimator/.settings/org.eclipse.wst.css.core.prefs create mode 100644 workflow-estimator/.settings/org.eclipse.wst.jsdt.ui.superType.container create mode 100644 workflow-estimator/.settings/org.eclipse.wst.jsdt.ui.superType.name create mode 100644 workflow-estimator/config/custom-fields.yaml create mode 100644 workflow-estimator/config/databases.yaml create mode 100644 workflow-estimator/config/overrides.any create mode 100644 workflow-estimator/config/persistence.xml create mode 100644 workflow-estimator/config/rest-clients.yaml create mode 100644 workflow-estimator/config/roles.xml create mode 100644 workflow-estimator/config/users.xml create mode 100644 workflow-estimator/config/variables.yaml create mode 100644 workflow-estimator/config/webservice-clients.yaml create mode 100644 workflow-estimator/dataclasses/com/axonivy/utils/estimator/Data.ivyClass create mode 100644 workflow-estimator/pom.xml diff --git a/workflow-estimator-demo/.classpath b/workflow-estimator-demo/.classpath new file mode 100644 index 00000000..a24d7ccd --- /dev/null +++ b/workflow-estimator-demo/.classpath @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/workflow-estimator-demo/.gitignore b/workflow-estimator-demo/.gitignore new file mode 100644 index 00000000..1b2547b2 --- /dev/null +++ b/workflow-estimator-demo/.gitignore @@ -0,0 +1,19 @@ +# general +Thumbs.db +.DS_Store +*~ +*.log + +# java +*.class +hs_err_pid* + +# maven +target/ +lib/mvn-deps/ + +# ivy +classes/ +src_dataClasses/ +src_wsproc/ +logs/ diff --git a/workflow-estimator-demo/.project b/workflow-estimator-demo/.project new file mode 100644 index 00000000..da4b423e --- /dev/null +++ b/workflow-estimator-demo/.project @@ -0,0 +1,49 @@ + + + workflow-estimator-demo + + + + + + ch.ivyteam.ivy.designer.dataClasses.ui.ivyDataClassBuilder + + + + + ch.ivyteam.ivy.designer.process.ui.ivyWebServiceProcessClassBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.wst.common.project.facet.core.builder + + + + + ch.ivyteam.ivy.designer.ide.ivyModelValidationBuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + ch.ivyteam.ivy.project.IvyProjectNature + org.eclipse.wst.common.modulecore.ModuleCoreNature + org.eclipse.jem.workbench.JavaEMFNature + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + org.eclipse.jem.beaninfo.BeanInfoNature + org.eclipse.wst.common.project.facet.core.nature + org.eclipse.wst.jsdt.core.jsNature + + diff --git a/workflow-estimator-demo/.settings/.jsdtscope b/workflow-estimator-demo/.settings/.jsdtscope new file mode 100644 index 00000000..cf5ec79e --- /dev/null +++ b/workflow-estimator-demo/.settings/.jsdtscope @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/workflow-estimator-demo/.settings/ch.ivyteam.ivy.designer.prefs b/workflow-estimator-demo/.settings/ch.ivyteam.ivy.designer.prefs new file mode 100644 index 00000000..04c8e75a --- /dev/null +++ b/workflow-estimator-demo/.settings/ch.ivyteam.ivy.designer.prefs @@ -0,0 +1,5 @@ +ch.ivyteam.ivy.designer.preferences.DataClassPreferencePage\:DEFAULT_DATA_CLASS=com.axonivy.utils.estimator.demo.Data +ch.ivyteam.ivy.designer.preferences.DataClassPreferencePage\:DEFAULT_NAMESPACE=com.axonivy.utils.estimator.demo +ch.ivyteam.ivy.project.preferences\:PRIMEFACES_VERSION=11 +ch.ivyteam.ivy.project.preferences\:PROJECT_VERSION=112000 +eclipse.preferences.version=1 diff --git a/workflow-estimator-demo/.settings/org.eclipse.jdt.core.prefs b/workflow-estimator-demo/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000..f78f7f77 --- /dev/null +++ b/workflow-estimator-demo/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,10 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=17 +org.eclipse.jdt.core.compiler.compliance=17 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning +org.eclipse.jdt.core.compiler.release=enabled +org.eclipse.jdt.core.compiler.source=17 diff --git a/workflow-estimator-demo/.settings/org.eclipse.wst.common.component b/workflow-estimator-demo/.settings/org.eclipse.wst.common.component new file mode 100644 index 00000000..beb78e2c --- /dev/null +++ b/workflow-estimator-demo/.settings/org.eclipse.wst.common.component @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/workflow-estimator-demo/.settings/org.eclipse.wst.common.project.facet.core.prefs.xml b/workflow-estimator-demo/.settings/org.eclipse.wst.common.project.facet.core.prefs.xml new file mode 100644 index 00000000..0d46547f --- /dev/null +++ b/workflow-estimator-demo/.settings/org.eclipse.wst.common.project.facet.core.prefs.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/workflow-estimator-demo/.settings/org.eclipse.wst.common.project.facet.core.xml b/workflow-estimator-demo/.settings/org.eclipse.wst.common.project.facet.core.xml new file mode 100644 index 00000000..c2098f9c --- /dev/null +++ b/workflow-estimator-demo/.settings/org.eclipse.wst.common.project.facet.core.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/workflow-estimator-demo/.settings/org.eclipse.wst.css.core.prefs b/workflow-estimator-demo/.settings/org.eclipse.wst.css.core.prefs new file mode 100644 index 00000000..96b96cde --- /dev/null +++ b/workflow-estimator-demo/.settings/org.eclipse.wst.css.core.prefs @@ -0,0 +1,2 @@ +css-profile/=org.eclipse.wst.css.core.cssprofile.css3 +eclipse.preferences.version=1 diff --git a/workflow-estimator-demo/.settings/org.eclipse.wst.jsdt.ui.superType.container b/workflow-estimator-demo/.settings/org.eclipse.wst.jsdt.ui.superType.container new file mode 100644 index 00000000..3bd5d0a4 --- /dev/null +++ b/workflow-estimator-demo/.settings/org.eclipse.wst.jsdt.ui.superType.container @@ -0,0 +1 @@ +org.eclipse.wst.jsdt.launching.baseBrowserLibrary \ No newline at end of file diff --git a/workflow-estimator-demo/.settings/org.eclipse.wst.jsdt.ui.superType.name b/workflow-estimator-demo/.settings/org.eclipse.wst.jsdt.ui.superType.name new file mode 100644 index 00000000..05bd71b6 --- /dev/null +++ b/workflow-estimator-demo/.settings/org.eclipse.wst.jsdt.ui.superType.name @@ -0,0 +1 @@ +Window \ No newline at end of file diff --git a/workflow-estimator-demo/config/custom-fields.yaml b/workflow-estimator-demo/config/custom-fields.yaml new file mode 100644 index 00000000..bb20b708 --- /dev/null +++ b/workflow-estimator-demo/config/custom-fields.yaml @@ -0,0 +1,22 @@ +# yaml-language-server: $schema=https://json-schema.axonivy.com/app/0.0.1/custom-fields.json +# +# == Custom Fields Information == +# +# You can define here your project custom fields. +# Have a look at our documentation for more information. +# +CustomFields: +# Tasks: +# MyTaskCustomField: +# Label: My task custom field +# Description: This new task custom field can be used to ... +# Type: STRING +# Cases: +# MyCaseCustomField: +# Label: My case custom field +# Description: This new case custom field can be used to ... +# Type: STRING +# Starts: +# MyStartCustomField: +# Label: My start custom field +# Description: This new start custom field can be used to ... diff --git a/workflow-estimator-demo/config/databases.yaml b/workflow-estimator-demo/config/databases.yaml new file mode 100644 index 00000000..10319e2e --- /dev/null +++ b/workflow-estimator-demo/config/databases.yaml @@ -0,0 +1,2 @@ +# yaml-language-server: $schema=https://json-schema.axonivy.com/app/0.0.1/databases.json +Databases: diff --git a/workflow-estimator-demo/config/overrides.any b/workflow-estimator-demo/config/overrides.any new file mode 100644 index 00000000..f59ec20a --- /dev/null +++ b/workflow-estimator-demo/config/overrides.any @@ -0,0 +1 @@ +* \ No newline at end of file diff --git a/workflow-estimator-demo/config/persistence.xml b/workflow-estimator-demo/config/persistence.xml new file mode 100644 index 00000000..d6b96d79 --- /dev/null +++ b/workflow-estimator-demo/config/persistence.xml @@ -0,0 +1,2 @@ + + diff --git a/workflow-estimator-demo/config/rest-clients.yaml b/workflow-estimator-demo/config/rest-clients.yaml new file mode 100644 index 00000000..4bffaca0 --- /dev/null +++ b/workflow-estimator-demo/config/rest-clients.yaml @@ -0,0 +1,2 @@ +# yaml-language-server: $schema=https://json-schema.axonivy.com/app/0.0.1/rest-clients.json +RestClients: diff --git a/workflow-estimator-demo/config/roles.xml b/workflow-estimator-demo/config/roles.xml new file mode 100644 index 00000000..59892fed --- /dev/null +++ b/workflow-estimator-demo/config/roles.xml @@ -0,0 +1,4 @@ + + + Everybody + diff --git a/workflow-estimator-demo/config/users.xml b/workflow-estimator-demo/config/users.xml new file mode 100644 index 00000000..51a69066 --- /dev/null +++ b/workflow-estimator-demo/config/users.xml @@ -0,0 +1,2 @@ + + diff --git a/workflow-estimator-demo/config/variables.yaml b/workflow-estimator-demo/config/variables.yaml new file mode 100644 index 00000000..fd144582 --- /dev/null +++ b/workflow-estimator-demo/config/variables.yaml @@ -0,0 +1,6 @@ +# == Variables == +# +# You can define here your project Variables. +# +Variables: +# myVariable: value diff --git a/workflow-estimator-demo/config/webservice-clients.yaml b/workflow-estimator-demo/config/webservice-clients.yaml new file mode 100644 index 00000000..688047a7 --- /dev/null +++ b/workflow-estimator-demo/config/webservice-clients.yaml @@ -0,0 +1,2 @@ +# yaml-language-server: $schema=https://json-schema.axonivy.com/app/0.0.1/webservice-clients.json +WebServiceClients: diff --git a/workflow-estimator-demo/dataclasses/com/axonivy/utils/estimator/demo/Data.ivyClass b/workflow-estimator-demo/dataclasses/com/axonivy/utils/estimator/demo/Data.ivyClass new file mode 100644 index 00000000..bd6127d3 --- /dev/null +++ b/workflow-estimator-demo/dataclasses/com/axonivy/utils/estimator/demo/Data.ivyClass @@ -0,0 +1,2 @@ +Data #class +com.axonivy.utils.estimator.demo #namespace diff --git a/workflow-estimator-demo/pom.xml b/workflow-estimator-demo/pom.xml new file mode 100644 index 00000000..6752c25c --- /dev/null +++ b/workflow-estimator-demo/pom.xml @@ -0,0 +1,19 @@ + + + 4.0.0 + com.axonivy.utils.estimator + workflow-estimator-demo + 1.0.0-SNAPSHOT + iar + + + + com.axonivy.ivy.ci + project-build-plugin + 11.2.0 + true + + + + diff --git a/workflow-estimator-product/.project b/workflow-estimator-product/.project new file mode 100644 index 00000000..11705a8e --- /dev/null +++ b/workflow-estimator-product/.project @@ -0,0 +1,17 @@ + + + workflow-estimator-product + + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.m2e.core.maven2Nature + + diff --git a/workflow-estimator-product/.settings/org.eclipse.m2e.core.prefs b/workflow-estimator-product/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 00000000..14b697b7 --- /dev/null +++ b/workflow-estimator-product/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/MY-PRODUCT-NAME-product/README.md b/workflow-estimator-product/README.md similarity index 100% rename from MY-PRODUCT-NAME-product/README.md rename to workflow-estimator-product/README.md diff --git a/MY-PRODUCT-NAME-product/pom.xml b/workflow-estimator-product/pom.xml similarity index 93% rename from MY-PRODUCT-NAME-product/pom.xml rename to workflow-estimator-product/pom.xml index dd590118..beaf9abd 100644 --- a/MY-PRODUCT-NAME-product/pom.xml +++ b/workflow-estimator-product/pom.xml @@ -1,12 +1,12 @@ 4.0.0 com.axonivy.market - MY-PRODUCT-NAME-product + workflow-estimator-product 10.0.0-SNAPSHOT pom - ../MY-PRODUCT-NAME/config/variables.yaml + ../workflow-estimator/config/variables.yaml diff --git a/MY-PRODUCT-NAME-product/product.json b/workflow-estimator-product/product.json similarity index 100% rename from MY-PRODUCT-NAME-product/product.json rename to workflow-estimator-product/product.json diff --git a/MY-PRODUCT-NAME-product/zip.xml b/workflow-estimator-product/zip.xml similarity index 100% rename from MY-PRODUCT-NAME-product/zip.xml rename to workflow-estimator-product/zip.xml diff --git a/workflow-estimator/.classpath b/workflow-estimator/.classpath new file mode 100644 index 00000000..a24d7ccd --- /dev/null +++ b/workflow-estimator/.classpath @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/workflow-estimator/.gitignore b/workflow-estimator/.gitignore new file mode 100644 index 00000000..1b2547b2 --- /dev/null +++ b/workflow-estimator/.gitignore @@ -0,0 +1,19 @@ +# general +Thumbs.db +.DS_Store +*~ +*.log + +# java +*.class +hs_err_pid* + +# maven +target/ +lib/mvn-deps/ + +# ivy +classes/ +src_dataClasses/ +src_wsproc/ +logs/ diff --git a/workflow-estimator/.project b/workflow-estimator/.project new file mode 100644 index 00000000..c3e62b34 --- /dev/null +++ b/workflow-estimator/.project @@ -0,0 +1,49 @@ + + + workflow-estimator + + + + + + ch.ivyteam.ivy.designer.dataClasses.ui.ivyDataClassBuilder + + + + + ch.ivyteam.ivy.designer.process.ui.ivyWebServiceProcessClassBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.wst.common.project.facet.core.builder + + + + + ch.ivyteam.ivy.designer.ide.ivyModelValidationBuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + ch.ivyteam.ivy.project.IvyProjectNature + org.eclipse.wst.common.modulecore.ModuleCoreNature + org.eclipse.jem.workbench.JavaEMFNature + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + org.eclipse.jem.beaninfo.BeanInfoNature + org.eclipse.wst.common.project.facet.core.nature + org.eclipse.wst.jsdt.core.jsNature + + diff --git a/workflow-estimator/.settings/.jsdtscope b/workflow-estimator/.settings/.jsdtscope new file mode 100644 index 00000000..cf5ec79e --- /dev/null +++ b/workflow-estimator/.settings/.jsdtscope @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/workflow-estimator/.settings/ch.ivyteam.ivy.designer.prefs b/workflow-estimator/.settings/ch.ivyteam.ivy.designer.prefs new file mode 100644 index 00000000..1dafe4f6 --- /dev/null +++ b/workflow-estimator/.settings/ch.ivyteam.ivy.designer.prefs @@ -0,0 +1,5 @@ +ch.ivyteam.ivy.designer.preferences.DataClassPreferencePage\:DEFAULT_DATA_CLASS=com.axonivy.utils.estimator.Data +ch.ivyteam.ivy.designer.preferences.DataClassPreferencePage\:DEFAULT_NAMESPACE=com.axonivy.utils.estimator +ch.ivyteam.ivy.project.preferences\:PRIMEFACES_VERSION=11 +ch.ivyteam.ivy.project.preferences\:PROJECT_VERSION=112000 +eclipse.preferences.version=1 diff --git a/workflow-estimator/.settings/org.eclipse.jdt.core.prefs b/workflow-estimator/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000..f78f7f77 --- /dev/null +++ b/workflow-estimator/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,10 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=17 +org.eclipse.jdt.core.compiler.compliance=17 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning +org.eclipse.jdt.core.compiler.release=enabled +org.eclipse.jdt.core.compiler.source=17 diff --git a/workflow-estimator/.settings/org.eclipse.wst.common.component b/workflow-estimator/.settings/org.eclipse.wst.common.component new file mode 100644 index 00000000..1d61737c --- /dev/null +++ b/workflow-estimator/.settings/org.eclipse.wst.common.component @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/workflow-estimator/.settings/org.eclipse.wst.common.project.facet.core.prefs.xml b/workflow-estimator/.settings/org.eclipse.wst.common.project.facet.core.prefs.xml new file mode 100644 index 00000000..0d46547f --- /dev/null +++ b/workflow-estimator/.settings/org.eclipse.wst.common.project.facet.core.prefs.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/workflow-estimator/.settings/org.eclipse.wst.common.project.facet.core.xml b/workflow-estimator/.settings/org.eclipse.wst.common.project.facet.core.xml new file mode 100644 index 00000000..c2098f9c --- /dev/null +++ b/workflow-estimator/.settings/org.eclipse.wst.common.project.facet.core.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/workflow-estimator/.settings/org.eclipse.wst.css.core.prefs b/workflow-estimator/.settings/org.eclipse.wst.css.core.prefs new file mode 100644 index 00000000..96b96cde --- /dev/null +++ b/workflow-estimator/.settings/org.eclipse.wst.css.core.prefs @@ -0,0 +1,2 @@ +css-profile/=org.eclipse.wst.css.core.cssprofile.css3 +eclipse.preferences.version=1 diff --git a/workflow-estimator/.settings/org.eclipse.wst.jsdt.ui.superType.container b/workflow-estimator/.settings/org.eclipse.wst.jsdt.ui.superType.container new file mode 100644 index 00000000..3bd5d0a4 --- /dev/null +++ b/workflow-estimator/.settings/org.eclipse.wst.jsdt.ui.superType.container @@ -0,0 +1 @@ +org.eclipse.wst.jsdt.launching.baseBrowserLibrary \ No newline at end of file diff --git a/workflow-estimator/.settings/org.eclipse.wst.jsdt.ui.superType.name b/workflow-estimator/.settings/org.eclipse.wst.jsdt.ui.superType.name new file mode 100644 index 00000000..05bd71b6 --- /dev/null +++ b/workflow-estimator/.settings/org.eclipse.wst.jsdt.ui.superType.name @@ -0,0 +1 @@ +Window \ No newline at end of file diff --git a/workflow-estimator/config/custom-fields.yaml b/workflow-estimator/config/custom-fields.yaml new file mode 100644 index 00000000..bb20b708 --- /dev/null +++ b/workflow-estimator/config/custom-fields.yaml @@ -0,0 +1,22 @@ +# yaml-language-server: $schema=https://json-schema.axonivy.com/app/0.0.1/custom-fields.json +# +# == Custom Fields Information == +# +# You can define here your project custom fields. +# Have a look at our documentation for more information. +# +CustomFields: +# Tasks: +# MyTaskCustomField: +# Label: My task custom field +# Description: This new task custom field can be used to ... +# Type: STRING +# Cases: +# MyCaseCustomField: +# Label: My case custom field +# Description: This new case custom field can be used to ... +# Type: STRING +# Starts: +# MyStartCustomField: +# Label: My start custom field +# Description: This new start custom field can be used to ... diff --git a/workflow-estimator/config/databases.yaml b/workflow-estimator/config/databases.yaml new file mode 100644 index 00000000..10319e2e --- /dev/null +++ b/workflow-estimator/config/databases.yaml @@ -0,0 +1,2 @@ +# yaml-language-server: $schema=https://json-schema.axonivy.com/app/0.0.1/databases.json +Databases: diff --git a/workflow-estimator/config/overrides.any b/workflow-estimator/config/overrides.any new file mode 100644 index 00000000..f59ec20a --- /dev/null +++ b/workflow-estimator/config/overrides.any @@ -0,0 +1 @@ +* \ No newline at end of file diff --git a/workflow-estimator/config/persistence.xml b/workflow-estimator/config/persistence.xml new file mode 100644 index 00000000..d6b96d79 --- /dev/null +++ b/workflow-estimator/config/persistence.xml @@ -0,0 +1,2 @@ + + diff --git a/workflow-estimator/config/rest-clients.yaml b/workflow-estimator/config/rest-clients.yaml new file mode 100644 index 00000000..4bffaca0 --- /dev/null +++ b/workflow-estimator/config/rest-clients.yaml @@ -0,0 +1,2 @@ +# yaml-language-server: $schema=https://json-schema.axonivy.com/app/0.0.1/rest-clients.json +RestClients: diff --git a/workflow-estimator/config/roles.xml b/workflow-estimator/config/roles.xml new file mode 100644 index 00000000..59892fed --- /dev/null +++ b/workflow-estimator/config/roles.xml @@ -0,0 +1,4 @@ + + + Everybody + diff --git a/workflow-estimator/config/users.xml b/workflow-estimator/config/users.xml new file mode 100644 index 00000000..51a69066 --- /dev/null +++ b/workflow-estimator/config/users.xml @@ -0,0 +1,2 @@ + + diff --git a/workflow-estimator/config/variables.yaml b/workflow-estimator/config/variables.yaml new file mode 100644 index 00000000..fd144582 --- /dev/null +++ b/workflow-estimator/config/variables.yaml @@ -0,0 +1,6 @@ +# == Variables == +# +# You can define here your project Variables. +# +Variables: +# myVariable: value diff --git a/workflow-estimator/config/webservice-clients.yaml b/workflow-estimator/config/webservice-clients.yaml new file mode 100644 index 00000000..688047a7 --- /dev/null +++ b/workflow-estimator/config/webservice-clients.yaml @@ -0,0 +1,2 @@ +# yaml-language-server: $schema=https://json-schema.axonivy.com/app/0.0.1/webservice-clients.json +WebServiceClients: diff --git a/workflow-estimator/dataclasses/com/axonivy/utils/estimator/Data.ivyClass b/workflow-estimator/dataclasses/com/axonivy/utils/estimator/Data.ivyClass new file mode 100644 index 00000000..ec802996 --- /dev/null +++ b/workflow-estimator/dataclasses/com/axonivy/utils/estimator/Data.ivyClass @@ -0,0 +1,2 @@ +Data #class +com.axonivy.utils.estimator #namespace diff --git a/workflow-estimator/pom.xml b/workflow-estimator/pom.xml new file mode 100644 index 00000000..9aa07a6b --- /dev/null +++ b/workflow-estimator/pom.xml @@ -0,0 +1,19 @@ + + + 4.0.0 + com.axonivy.utils.estimator + workflow-estimator + 1.0.0-SNAPSHOT + iar + + + + com.axonivy.ivy.ci + project-build-plugin + 11.2.0 + true + + + + From edcf92dc22dd778bf130482000afded001ac7abf Mon Sep 17 00:00:00 2001 From: Trung Mai Date: Fri, 2 Feb 2024 15:00:23 +0700 Subject: [PATCH 002/143] TE-538: Create WorkflowEstimator class --- .../utils/estimator/WorkflowEstimator.java | 26 +++++++++++++++ .../utils/estimator/model/EstimatedTask.java | 33 +++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java create mode 100644 workflow-estimator/src/com/axonivy/utils/estimator/model/EstimatedTask.java diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java new file mode 100644 index 00000000..df7fff1f --- /dev/null +++ b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java @@ -0,0 +1,26 @@ +package com.axonivy.utils.estimator; + +import static java.util.Collections.emptyList; + +import java.util.List; + +import com.axonivy.utils.estimator.model.EstimatedTask; + +import ch.ivyteam.ivy.bpm.engine.restricted.model.IProcess; +import ch.ivyteam.ivy.process.model.BaseElement; + +public class WorkflowEstimator { + public WorkflowEstimator(IProcess process, Enum useCase, String flowName) { + + } + + public List findAllTasks(BaseElement startAtElement) { + // TODO: Implement here + return emptyList(); + } + + public List findTasksOnPath(BaseElement startAtElement) { + // TODO: Implement here + return emptyList(); + } +} diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/model/EstimatedTask.java b/workflow-estimator/src/com/axonivy/utils/estimator/model/EstimatedTask.java new file mode 100644 index 00000000..72d52097 --- /dev/null +++ b/workflow-estimator/src/com/axonivy/utils/estimator/model/EstimatedTask.java @@ -0,0 +1,33 @@ +package com.axonivy.utils.estimator.model; + +import java.util.Date; + +public class EstimatedTask { + private String pid; + private String taskName; + private Date estimatedStartTimestamp; + + public String getPid() { + return pid; + } + + public void setPid(String pid) { + this.pid = pid; + } + + public String getTaskName() { + return taskName; + } + + public void setTaskName(String taskName) { + this.taskName = taskName; + } + + public Date getEstimatedStartTimestamp() { + return estimatedStartTimestamp; + } + + public void setEstimatedStartTimestamp(Date estimatedStartTimestamp) { + this.estimatedStartTimestamp = estimatedStartTimestamp; + } +} From 4fbf441debc4559117d21582e3e29db1711a3ffe Mon Sep 17 00:00:00 2001 From: Trung Mai Date: Fri, 2 Feb 2024 15:08:37 +0700 Subject: [PATCH 003/143] TE-538: Create test module --- pom.xml | 8 +-- workflow-estimator-test/.classpath | 35 +++++++++++++ workflow-estimator-test/.gitignore | 19 +++++++ workflow-estimator-test/.project | 49 +++++++++++++++++++ workflow-estimator-test/.settings/.jsdtscope | 12 +++++ .../.settings/ch.ivyteam.ivy.designer.prefs | 5 ++ .../.settings/org.eclipse.jdt.core.prefs | 10 ++++ .../org.eclipse.wst.common.component | 10 ++++ ...se.wst.common.project.facet.core.prefs.xml | 7 +++ ....eclipse.wst.common.project.facet.core.xml | 8 +++ .../.settings/org.eclipse.wst.css.core.prefs | 2 + ...rg.eclipse.wst.jsdt.ui.superType.container | 1 + .../org.eclipse.wst.jsdt.ui.superType.name | 1 + .../config/custom-fields.yaml | 22 +++++++++ workflow-estimator-test/config/databases.yaml | 2 + workflow-estimator-test/config/overrides.any | 1 + .../config/persistence.xml | 2 + .../config/rest-clients.yaml | 2 + workflow-estimator-test/config/roles.xml | 4 ++ workflow-estimator-test/config/users.xml | 2 + workflow-estimator-test/config/variables.yaml | 6 +++ .../config/webservice-clients.yaml | 2 + .../utils/estimator/test/Data.ivyClass | 2 + workflow-estimator-test/pom.xml | 28 +++++++++++ 24 files changed, 236 insertions(+), 4 deletions(-) create mode 100644 workflow-estimator-test/.classpath create mode 100644 workflow-estimator-test/.gitignore create mode 100644 workflow-estimator-test/.project create mode 100644 workflow-estimator-test/.settings/.jsdtscope create mode 100644 workflow-estimator-test/.settings/ch.ivyteam.ivy.designer.prefs create mode 100644 workflow-estimator-test/.settings/org.eclipse.jdt.core.prefs create mode 100644 workflow-estimator-test/.settings/org.eclipse.wst.common.component create mode 100644 workflow-estimator-test/.settings/org.eclipse.wst.common.project.facet.core.prefs.xml create mode 100644 workflow-estimator-test/.settings/org.eclipse.wst.common.project.facet.core.xml create mode 100644 workflow-estimator-test/.settings/org.eclipse.wst.css.core.prefs create mode 100644 workflow-estimator-test/.settings/org.eclipse.wst.jsdt.ui.superType.container create mode 100644 workflow-estimator-test/.settings/org.eclipse.wst.jsdt.ui.superType.name create mode 100644 workflow-estimator-test/config/custom-fields.yaml create mode 100644 workflow-estimator-test/config/databases.yaml create mode 100644 workflow-estimator-test/config/overrides.any create mode 100644 workflow-estimator-test/config/persistence.xml create mode 100644 workflow-estimator-test/config/rest-clients.yaml create mode 100644 workflow-estimator-test/config/roles.xml create mode 100644 workflow-estimator-test/config/users.xml create mode 100644 workflow-estimator-test/config/variables.yaml create mode 100644 workflow-estimator-test/config/webservice-clients.yaml create mode 100644 workflow-estimator-test/dataclasses/com/axonivy/utils/estimator/test/Data.ivyClass create mode 100644 workflow-estimator-test/pom.xml diff --git a/pom.xml b/pom.xml index 517336be..43d3635c 100644 --- a/pom.xml +++ b/pom.xml @@ -1,9 +1,9 @@ 4.0.0 - com.axonivy.market - my-product - my-product-modules - 10.0.0-SNAPSHOT + com.axonivy.utils.estimator + workflow-estimator + workflow-estimator-modules + 11.0.0-SNAPSHOT pom diff --git a/workflow-estimator-test/.classpath b/workflow-estimator-test/.classpath new file mode 100644 index 00000000..4ce5bedb --- /dev/null +++ b/workflow-estimator-test/.classpath @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/workflow-estimator-test/.gitignore b/workflow-estimator-test/.gitignore new file mode 100644 index 00000000..1b2547b2 --- /dev/null +++ b/workflow-estimator-test/.gitignore @@ -0,0 +1,19 @@ +# general +Thumbs.db +.DS_Store +*~ +*.log + +# java +*.class +hs_err_pid* + +# maven +target/ +lib/mvn-deps/ + +# ivy +classes/ +src_dataClasses/ +src_wsproc/ +logs/ diff --git a/workflow-estimator-test/.project b/workflow-estimator-test/.project new file mode 100644 index 00000000..1390f12a --- /dev/null +++ b/workflow-estimator-test/.project @@ -0,0 +1,49 @@ + + + workflow-estimator-test + + + + + + ch.ivyteam.ivy.designer.dataClasses.ui.ivyDataClassBuilder + + + + + ch.ivyteam.ivy.designer.process.ui.ivyWebServiceProcessClassBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.wst.common.project.facet.core.builder + + + + + ch.ivyteam.ivy.designer.ide.ivyModelValidationBuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + ch.ivyteam.ivy.project.IvyProjectNature + org.eclipse.wst.common.modulecore.ModuleCoreNature + org.eclipse.jem.workbench.JavaEMFNature + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + org.eclipse.jem.beaninfo.BeanInfoNature + org.eclipse.wst.common.project.facet.core.nature + org.eclipse.wst.jsdt.core.jsNature + + diff --git a/workflow-estimator-test/.settings/.jsdtscope b/workflow-estimator-test/.settings/.jsdtscope new file mode 100644 index 00000000..cf5ec79e --- /dev/null +++ b/workflow-estimator-test/.settings/.jsdtscope @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/workflow-estimator-test/.settings/ch.ivyteam.ivy.designer.prefs b/workflow-estimator-test/.settings/ch.ivyteam.ivy.designer.prefs new file mode 100644 index 00000000..11908273 --- /dev/null +++ b/workflow-estimator-test/.settings/ch.ivyteam.ivy.designer.prefs @@ -0,0 +1,5 @@ +ch.ivyteam.ivy.designer.preferences.DataClassPreferencePage\:DEFAULT_DATA_CLASS=com.axonivy.utils.estimator.test.Data +ch.ivyteam.ivy.designer.preferences.DataClassPreferencePage\:DEFAULT_NAMESPACE=com.axonivy.utils.estimator.test +ch.ivyteam.ivy.project.preferences\:PRIMEFACES_VERSION=11 +ch.ivyteam.ivy.project.preferences\:PROJECT_VERSION=112000 +eclipse.preferences.version=1 diff --git a/workflow-estimator-test/.settings/org.eclipse.jdt.core.prefs b/workflow-estimator-test/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000..f78f7f77 --- /dev/null +++ b/workflow-estimator-test/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,10 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=17 +org.eclipse.jdt.core.compiler.compliance=17 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning +org.eclipse.jdt.core.compiler.release=enabled +org.eclipse.jdt.core.compiler.source=17 diff --git a/workflow-estimator-test/.settings/org.eclipse.wst.common.component b/workflow-estimator-test/.settings/org.eclipse.wst.common.component new file mode 100644 index 00000000..cc1460a2 --- /dev/null +++ b/workflow-estimator-test/.settings/org.eclipse.wst.common.component @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/workflow-estimator-test/.settings/org.eclipse.wst.common.project.facet.core.prefs.xml b/workflow-estimator-test/.settings/org.eclipse.wst.common.project.facet.core.prefs.xml new file mode 100644 index 00000000..0d46547f --- /dev/null +++ b/workflow-estimator-test/.settings/org.eclipse.wst.common.project.facet.core.prefs.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/workflow-estimator-test/.settings/org.eclipse.wst.common.project.facet.core.xml b/workflow-estimator-test/.settings/org.eclipse.wst.common.project.facet.core.xml new file mode 100644 index 00000000..c2098f9c --- /dev/null +++ b/workflow-estimator-test/.settings/org.eclipse.wst.common.project.facet.core.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/workflow-estimator-test/.settings/org.eclipse.wst.css.core.prefs b/workflow-estimator-test/.settings/org.eclipse.wst.css.core.prefs new file mode 100644 index 00000000..96b96cde --- /dev/null +++ b/workflow-estimator-test/.settings/org.eclipse.wst.css.core.prefs @@ -0,0 +1,2 @@ +css-profile/=org.eclipse.wst.css.core.cssprofile.css3 +eclipse.preferences.version=1 diff --git a/workflow-estimator-test/.settings/org.eclipse.wst.jsdt.ui.superType.container b/workflow-estimator-test/.settings/org.eclipse.wst.jsdt.ui.superType.container new file mode 100644 index 00000000..3bd5d0a4 --- /dev/null +++ b/workflow-estimator-test/.settings/org.eclipse.wst.jsdt.ui.superType.container @@ -0,0 +1 @@ +org.eclipse.wst.jsdt.launching.baseBrowserLibrary \ No newline at end of file diff --git a/workflow-estimator-test/.settings/org.eclipse.wst.jsdt.ui.superType.name b/workflow-estimator-test/.settings/org.eclipse.wst.jsdt.ui.superType.name new file mode 100644 index 00000000..05bd71b6 --- /dev/null +++ b/workflow-estimator-test/.settings/org.eclipse.wst.jsdt.ui.superType.name @@ -0,0 +1 @@ +Window \ No newline at end of file diff --git a/workflow-estimator-test/config/custom-fields.yaml b/workflow-estimator-test/config/custom-fields.yaml new file mode 100644 index 00000000..bb20b708 --- /dev/null +++ b/workflow-estimator-test/config/custom-fields.yaml @@ -0,0 +1,22 @@ +# yaml-language-server: $schema=https://json-schema.axonivy.com/app/0.0.1/custom-fields.json +# +# == Custom Fields Information == +# +# You can define here your project custom fields. +# Have a look at our documentation for more information. +# +CustomFields: +# Tasks: +# MyTaskCustomField: +# Label: My task custom field +# Description: This new task custom field can be used to ... +# Type: STRING +# Cases: +# MyCaseCustomField: +# Label: My case custom field +# Description: This new case custom field can be used to ... +# Type: STRING +# Starts: +# MyStartCustomField: +# Label: My start custom field +# Description: This new start custom field can be used to ... diff --git a/workflow-estimator-test/config/databases.yaml b/workflow-estimator-test/config/databases.yaml new file mode 100644 index 00000000..10319e2e --- /dev/null +++ b/workflow-estimator-test/config/databases.yaml @@ -0,0 +1,2 @@ +# yaml-language-server: $schema=https://json-schema.axonivy.com/app/0.0.1/databases.json +Databases: diff --git a/workflow-estimator-test/config/overrides.any b/workflow-estimator-test/config/overrides.any new file mode 100644 index 00000000..f59ec20a --- /dev/null +++ b/workflow-estimator-test/config/overrides.any @@ -0,0 +1 @@ +* \ No newline at end of file diff --git a/workflow-estimator-test/config/persistence.xml b/workflow-estimator-test/config/persistence.xml new file mode 100644 index 00000000..d6b96d79 --- /dev/null +++ b/workflow-estimator-test/config/persistence.xml @@ -0,0 +1,2 @@ + + diff --git a/workflow-estimator-test/config/rest-clients.yaml b/workflow-estimator-test/config/rest-clients.yaml new file mode 100644 index 00000000..4bffaca0 --- /dev/null +++ b/workflow-estimator-test/config/rest-clients.yaml @@ -0,0 +1,2 @@ +# yaml-language-server: $schema=https://json-schema.axonivy.com/app/0.0.1/rest-clients.json +RestClients: diff --git a/workflow-estimator-test/config/roles.xml b/workflow-estimator-test/config/roles.xml new file mode 100644 index 00000000..59892fed --- /dev/null +++ b/workflow-estimator-test/config/roles.xml @@ -0,0 +1,4 @@ + + + Everybody + diff --git a/workflow-estimator-test/config/users.xml b/workflow-estimator-test/config/users.xml new file mode 100644 index 00000000..51a69066 --- /dev/null +++ b/workflow-estimator-test/config/users.xml @@ -0,0 +1,2 @@ + + diff --git a/workflow-estimator-test/config/variables.yaml b/workflow-estimator-test/config/variables.yaml new file mode 100644 index 00000000..fd144582 --- /dev/null +++ b/workflow-estimator-test/config/variables.yaml @@ -0,0 +1,6 @@ +# == Variables == +# +# You can define here your project Variables. +# +Variables: +# myVariable: value diff --git a/workflow-estimator-test/config/webservice-clients.yaml b/workflow-estimator-test/config/webservice-clients.yaml new file mode 100644 index 00000000..688047a7 --- /dev/null +++ b/workflow-estimator-test/config/webservice-clients.yaml @@ -0,0 +1,2 @@ +# yaml-language-server: $schema=https://json-schema.axonivy.com/app/0.0.1/webservice-clients.json +WebServiceClients: diff --git a/workflow-estimator-test/dataclasses/com/axonivy/utils/estimator/test/Data.ivyClass b/workflow-estimator-test/dataclasses/com/axonivy/utils/estimator/test/Data.ivyClass new file mode 100644 index 00000000..b64bfef4 --- /dev/null +++ b/workflow-estimator-test/dataclasses/com/axonivy/utils/estimator/test/Data.ivyClass @@ -0,0 +1,2 @@ +Data #class +com.axonivy.utils.estimator.test #namespace diff --git a/workflow-estimator-test/pom.xml b/workflow-estimator-test/pom.xml new file mode 100644 index 00000000..660a8d37 --- /dev/null +++ b/workflow-estimator-test/pom.xml @@ -0,0 +1,28 @@ + + + 4.0.0 + com.axonivy.utils.estimator + workflow-estimator-test + 1.0.0-SNAPSHOT + iar + + + com.axonivy.utils.estimator + workflow-estimator + ${project.version} + iar + + + + src_test + + + com.axonivy.ivy.ci + project-build-plugin + 11.2.0 + true + + + + From d8ff911a8bbd7c738fde16b2f71524a05fa61bf6 Mon Sep 17 00:00:00 2001 From: Trung Mai Date: Fri, 16 Feb 2024 09:23:21 +0700 Subject: [PATCH 004/143] TE-538: Update project name and unit test dependency --- workflow-estimator-product/README.md | 2 +- workflow-estimator-product/product.json | 12 ++++++------ workflow-estimator-test/pom.xml | 11 ++++++++++- .../utils/estimator/test/WorkflowEstimatorTest.java | 8 ++++++++ 4 files changed, 25 insertions(+), 8 deletions(-) create mode 100644 workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/WorkflowEstimatorTest.java diff --git a/workflow-estimator-product/README.md b/workflow-estimator-product/README.md index ee9062a5..144fe864 100644 --- a/workflow-estimator-product/README.md +++ b/workflow-estimator-product/README.md @@ -1,4 +1,4 @@ -# MY-PRODUCT-NAME +# workflow-estimator YOUR DESCRIPTION GOES HERE diff --git a/workflow-estimator-product/product.json b/workflow-estimator-product/product.json index a5a4b330..e728d3f0 100644 --- a/workflow-estimator-product/product.json +++ b/workflow-estimator-product/product.json @@ -6,8 +6,8 @@ "data": { "projects": [ { - "groupId": "MY-GROUP-ID", - "artifactId": "MY-PRODUCT-NAME-demo", + "groupId": "com.axonivy.utils.estimator", + "artifactId": "workflow-estimator-demo", "version": "${version}", "type": "iar" } @@ -28,8 +28,8 @@ "data": { "dependencies": [ { - "groupId": "MY-GROUP-ID", - "artifactId": "MY-PRODUCT-NAME", + "groupId": "com.axonivy.utils.estimator", + "artifactId": "workflow-estimator", "version": "${version}", "type": "iar" } @@ -50,8 +50,8 @@ "data": { "dependencies": [ { - "groupId": "MY-GROUP-ID", - "artifactId": "MY-PRODUCT-NAME", + "groupId": "com.axonivy.utils.estimator", + "artifactId": "workflow-estimator", "version": "${version}" } ], diff --git a/workflow-estimator-test/pom.xml b/workflow-estimator-test/pom.xml index 660a8d37..b1a8be6f 100644 --- a/workflow-estimator-test/pom.xml +++ b/workflow-estimator-test/pom.xml @@ -6,6 +6,9 @@ workflow-estimator-test 1.0.0-SNAPSHOT iar + + 11.2.0 + com.axonivy.utils.estimator @@ -13,6 +16,12 @@ ${project.version} iar + + com.axonivy.ivy.test + unit-tester + 11.2.0 + test + src_test @@ -20,7 +29,7 @@ com.axonivy.ivy.ci project-build-plugin - 11.2.0 + ${build.plugin.version} true diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/WorkflowEstimatorTest.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/WorkflowEstimatorTest.java new file mode 100644 index 00000000..9e147ab1 --- /dev/null +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/WorkflowEstimatorTest.java @@ -0,0 +1,8 @@ +package com.axonivy.utils.estimator.test; + +import ch.ivyteam.ivy.environment.IvyTest; + +@IvyTest +public class WorkflowEstimatorTest { + +} From 07675548449b832ee85b8e3206b8f2d5bfae4180 Mon Sep 17 00:00:00 2001 From: Trung Mai Date: Mon, 19 Feb 2024 09:33:00 +0700 Subject: [PATCH 005/143] TE-538: Unit test findAllTasks function --- .../processes/MainTest.p.json | 92 +++++++++++++++++++ .../estimator/test/WorkflowEstimatorTest.java | 27 ++++++ .../utils/estimator/WorkflowEstimator.java | 39 +++++++- 3 files changed, 153 insertions(+), 5 deletions(-) create mode 100644 workflow-estimator-test/processes/MainTest.p.json diff --git a/workflow-estimator-test/processes/MainTest.p.json b/workflow-estimator-test/processes/MainTest.p.json new file mode 100644 index 00000000..bc683254 --- /dev/null +++ b/workflow-estimator-test/processes/MainTest.p.json @@ -0,0 +1,92 @@ +{ + "$schema" : "https://json-schema.axonivy.com/process/11.2.2/process.json", + "id" : "18DB09D33872E009", + "config" : { + "data" : "com.axonivy.utils.estimator.test.Data" + }, + "elements" : [ { + "id" : "f0", + "type" : "RequestStart", + "name" : "start", + "config" : { + "signature" : "start" + }, + "visual" : { + "at" : { "x" : 96, "y" : 64 } + }, + "connect" : [ + { "id" : "f2", "to" : "f1" } + ] + }, { + "id" : "f1", + "type" : "UserTask", + "name" : "Task A", + "visual" : { + "at" : { "x" : 288, "y" : 64 } + }, + "connect" : [ + { "id" : "f4", "to" : "f3" } + ] + }, { + "id" : "f3", + "type" : "Alternative", + "visual" : { + "at" : { "x" : 480, "y" : 64 } + }, + "connect" : [ + { "id" : "f6", "to" : "f5" }, + { "id" : "f16", "to" : "f9" } + ] + }, { + "id" : "f5", + "type" : "Alternative", + "visual" : { + "at" : { "x" : 880, "y" : 64 } + }, + "connect" : [ + { "id" : "f8", "to" : "f7" } + ] + }, { + "id" : "f7", + "type" : "TaskEnd", + "visual" : { + "at" : { "x" : 1048, "y" : 64 } + } + }, { + "id" : "f9", + "type" : "Alternative", + "visual" : { + "at" : { "x" : 480, "y" : 208 } + }, + "connect" : [ + { "id" : "f11", "to" : "f10", "label" : { + "name" : "{internal}" + } }, + { "id" : "f14", "to" : "f13", "via" : [ { "x" : 480, "y" : 328 } ], "label" : { + "name" : "{external}", + "segment" : 1.21, + "offset" : { "x" : 63, "y" : 3 } + } } + ] + }, { + "id" : "f10", + "type" : "UserTask", + "name" : "Task B", + "visual" : { + "at" : { "x" : 736, "y" : 208 } + }, + "connect" : [ + { "id" : "f12", "to" : "f5", "via" : [ { "x" : 880, "y" : 208 } ] } + ] + }, { + "id" : "f13", + "type" : "UserTask", + "name" : "Task C", + "visual" : { + "at" : { "x" : 736, "y" : 328 } + }, + "connect" : [ + { "id" : "f15", "to" : "f10" } + ] + } ] +} \ No newline at end of file diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/WorkflowEstimatorTest.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/WorkflowEstimatorTest.java index 9e147ab1..2de03891 100644 --- a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/WorkflowEstimatorTest.java +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/WorkflowEstimatorTest.java @@ -1,8 +1,35 @@ package com.axonivy.utils.estimator.test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.axonivy.utils.estimator.WorkflowEstimator; + +import ch.ivyteam.ivy.application.IProcessModelVersion; +import ch.ivyteam.ivy.environment.Ivy; import ch.ivyteam.ivy.environment.IvyTest; +import ch.ivyteam.ivy.workflow.IProcessStart; +import ch.ivyteam.ivy.workflow.IWorkflowProcessModelVersion; @IvyTest public class WorkflowEstimatorTest { + private IProcessStart processStart; + private WorkflowEstimator workflowEstimator; + + private + + @BeforeEach + void setup() { + IProcessModelVersion pmv = Ivy.request().getProcessModelVersion(); + processStart = IWorkflowProcessModelVersion.of(pmv).findProcessStartByUserFriendlyRequestPath("MainTest/start.ivp"); + //workflowEstimator = new WorkflowEstimator(process, null, ""); + + } + + @Test + void shouldfindAllTasks() { + System.out.println("--------------------"); + //workflowEstimator.findAllTasks(process.); + } } diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java index df7fff1f..e1555e5a 100644 --- a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java +++ b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java @@ -2,25 +2,54 @@ import static java.util.Collections.emptyList; +import java.util.Date; import java.util.List; import com.axonivy.utils.estimator.model.EstimatedTask; -import ch.ivyteam.ivy.bpm.engine.restricted.model.IProcess; +import ch.ivyteam.ivy.process.model.Process; import ch.ivyteam.ivy.process.model.BaseElement; +import ch.ivyteam.ivy.process.model.element.SingleTaskCreator; public class WorkflowEstimator { - public WorkflowEstimator(IProcess process, Enum useCase, String flowName) { - + + private Process process; + private Enum useCase; + private String flowName; + + public WorkflowEstimator(Process process, Enum useCase, String flowName) { + this.process = process; + this.useCase = useCase; + this.flowName = flowName; } public List findAllTasks(BaseElement startAtElement) { - // TODO: Implement here - return emptyList(); + + List elements = startAtElement.getRootProcess().getElements(); + List estimatedTasks = elements.stream() + //filter to get task only + .filter(el -> { + return el instanceof SingleTaskCreator; + }) + //filter the tash which have estimated if needed + .map(task -> createEstimatedTask((SingleTaskCreator) task)) + .toList(); + + return estimatedTasks; } public List findTasksOnPath(BaseElement startAtElement) { // TODO: Implement here return emptyList(); } + + private EstimatedTask createEstimatedTask(SingleTaskCreator task) { + EstimatedTask estimatedTask = new EstimatedTask(); + estimatedTask.setPid(task.getPid().getRawPid()); + estimatedTask.setTaskName(task.getName()); + estimatedTask.setEstimatedStartTimestamp(new Date()); + + return estimatedTask; + } + } From 66ba4357c27bf05340f6479ab00b7a8424c0d4bb Mon Sep 17 00:00:00 2001 From: Trung Mai Date: Wed, 21 Feb 2024 09:00:15 +0700 Subject: [PATCH 006/143] TE-538: convert project into a plugin and add ch.ivyteam.ivy.process.rdm --- workflow-estimator-test/.classpath | 1 + workflow-estimator-test/.project | 11 +++++++++++ workflow-estimator-test/META-INF/MANIFEST.MF | 9 +++++++++ workflow-estimator-test/build.properties | 6 ++++++ .../estimator/test/WorkflowEstimatorTest.java | 14 ++++++++------ 5 files changed, 35 insertions(+), 6 deletions(-) create mode 100644 workflow-estimator-test/META-INF/MANIFEST.MF create mode 100644 workflow-estimator-test/build.properties diff --git a/workflow-estimator-test/.classpath b/workflow-estimator-test/.classpath index 4ce5bedb..424728eb 100644 --- a/workflow-estimator-test/.classpath +++ b/workflow-estimator-test/.classpath @@ -31,5 +31,6 @@ + diff --git a/workflow-estimator-test/.project b/workflow-estimator-test/.project index 1390f12a..30664fe8 100644 --- a/workflow-estimator-test/.project +++ b/workflow-estimator-test/.project @@ -35,6 +35,16 @@ + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + ch.ivyteam.ivy.project.IvyProjectNature @@ -45,5 +55,6 @@ org.eclipse.jem.beaninfo.BeanInfoNature org.eclipse.wst.common.project.facet.core.nature org.eclipse.wst.jsdt.core.jsNature + org.eclipse.pde.PluginNature diff --git a/workflow-estimator-test/META-INF/MANIFEST.MF b/workflow-estimator-test/META-INF/MANIFEST.MF new file mode 100644 index 00000000..9e231cbb --- /dev/null +++ b/workflow-estimator-test/META-INF/MANIFEST.MF @@ -0,0 +1,9 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: workflow-estimator-test +Bundle-SymbolicName: workflow-estimator-test +Bundle-Version: 1.0.0.qualifier +Bundle-RequiredExecutionEnvironment: JavaSE-17 +Automatic-Module-Name: workflow.estimator.test +Export-Package: com.axonivy.utils.estimator.test +Require-Bundle: ch.ivyteam.ivy.process.rdm;bundle-version="11.2.1" diff --git a/workflow-estimator-test/build.properties b/workflow-estimator-test/build.properties new file mode 100644 index 00000000..8825ff58 --- /dev/null +++ b/workflow-estimator-test/build.properties @@ -0,0 +1,6 @@ +source.. = src/,\ + src_wsproc/,\ + src_dataClasses/,\ + src_test/ +bin.includes = META-INF/,\ + . diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/WorkflowEstimatorTest.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/WorkflowEstimatorTest.java index 2de03891..f2edd116 100644 --- a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/WorkflowEstimatorTest.java +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/WorkflowEstimatorTest.java @@ -5,24 +5,26 @@ import com.axonivy.utils.estimator.WorkflowEstimator; -import ch.ivyteam.ivy.application.IProcessModelVersion; import ch.ivyteam.ivy.environment.Ivy; import ch.ivyteam.ivy.environment.IvyTest; -import ch.ivyteam.ivy.workflow.IProcessStart; -import ch.ivyteam.ivy.workflow.IWorkflowProcessModelVersion; +import ch.ivyteam.ivy.process.model.Process; +import ch.ivyteam.ivy.process.rdm.IProcessManager; @IvyTest +@SuppressWarnings("restriction") public class WorkflowEstimatorTest { - private IProcessStart processStart; + private Process process; private WorkflowEstimator workflowEstimator; private @BeforeEach void setup() { - IProcessModelVersion pmv = Ivy.request().getProcessModelVersion(); - processStart = IWorkflowProcessModelVersion.of(pmv).findProcessStartByUserFriendlyRequestPath("MainTest/start.ivp"); + var pmv = Ivy.request().getProcessModelVersion(); + var manager = IProcessManager.instance().getProjectDataModelFor(pmv); + this.process = manager.findProcessByPath("MainTest").getModel(); + //workflowEstimator = new WorkflowEstimator(process, null, ""); } From 8fb6e45293fdae3b490f695756f836f800a95951 Mon Sep 17 00:00:00 2001 From: Trung Mai Date: Tue, 20 Feb 2024 09:27:19 +0700 Subject: [PATCH 007/143] TE-538: Using Ivy10 --- workflow-estimator-demo/pom.xml | 5 +- workflow-estimator-test/pom.xml | 8 +- .../processes/MainTest.p.json | 82 +++++++++---------- .../estimator/test/WorkflowEstimatorTest.java | 21 ++--- workflow-estimator/pom.xml | 5 +- .../utils/estimator/WorkflowEstimator.java | 1 + 6 files changed, 64 insertions(+), 58 deletions(-) diff --git a/workflow-estimator-demo/pom.xml b/workflow-estimator-demo/pom.xml index 6752c25c..769cf2b2 100644 --- a/workflow-estimator-demo/pom.xml +++ b/workflow-estimator-demo/pom.xml @@ -6,12 +6,15 @@ workflow-estimator-demo 1.0.0-SNAPSHOT iar + + 10.0.14 + com.axonivy.ivy.ci project-build-plugin - 11.2.0 + ${build.plugin.version} true diff --git a/workflow-estimator-test/pom.xml b/workflow-estimator-test/pom.xml index b1a8be6f..65857c83 100644 --- a/workflow-estimator-test/pom.xml +++ b/workflow-estimator-test/pom.xml @@ -6,9 +6,9 @@ workflow-estimator-test 1.0.0-SNAPSHOT iar - - 11.2.0 - + + 10.0.14 + com.axonivy.utils.estimator @@ -19,7 +19,7 @@ com.axonivy.ivy.test unit-tester - 11.2.0 + 10.0.14 test diff --git a/workflow-estimator-test/processes/MainTest.p.json b/workflow-estimator-test/processes/MainTest.p.json index bc683254..cb89c1ba 100644 --- a/workflow-estimator-test/processes/MainTest.p.json +++ b/workflow-estimator-test/processes/MainTest.p.json @@ -1,92 +1,90 @@ { - "$schema" : "https://json-schema.axonivy.com/process/11.2.2/process.json", - "id" : "18DB09D33872E009", + "format" : "10.0.0", + "id" : "18DC44E096FDFF75", "config" : { "data" : "com.axonivy.utils.estimator.test.Data" }, "elements" : [ { "id" : "f0", "type" : "RequestStart", - "name" : "start", + "name" : "start.ivp", "config" : { - "signature" : "start" + "callSignature" : "start", + "outLink" : "start.ivp" }, "visual" : { "at" : { "x" : 96, "y" : 64 } }, - "connect" : [ - { "id" : "f2", "to" : "f1" } - ] + "connect" : { "id" : "f3", "to" : "f2" } }, { "id" : "f1", + "type" : "TaskEnd", + "visual" : { + "at" : { "x" : 944, "y" : 64 } + } + }, { + "id" : "f2", "type" : "UserTask", "name" : "Task A", "visual" : { "at" : { "x" : 288, "y" : 64 } }, - "connect" : [ - { "id" : "f4", "to" : "f3" } - ] + "connect" : { "id" : "f5", "to" : "f4" } }, { - "id" : "f3", + "id" : "f4", "type" : "Alternative", "visual" : { "at" : { "x" : 480, "y" : 64 } }, "connect" : [ - { "id" : "f6", "to" : "f5" }, - { "id" : "f16", "to" : "f9" } - ] - }, { - "id" : "f5", - "type" : "Alternative", - "visual" : { - "at" : { "x" : 880, "y" : 64 } - }, - "connect" : [ - { "id" : "f8", "to" : "f7" } + { "id" : "f9", "to" : "f8", "label" : { + "name" : [ + "{internal}", + "{external}" + ] + } }, + { "id" : "f14", "to" : "f6" } ] }, { "id" : "f7", - "type" : "TaskEnd", + "type" : "UserTask", + "name" : "Task B", "visual" : { - "at" : { "x" : 1048, "y" : 64 } - } + "at" : { "x" : 696, "y" : 224 } + }, + "connect" : { "id" : "f15", "to" : "f6", "via" : [ { "x" : 808, "y" : 224 } ] } }, { - "id" : "f9", + "id" : "f8", "type" : "Alternative", "visual" : { - "at" : { "x" : 480, "y" : 208 } + "at" : { "x" : 480, "y" : 224 } }, "connect" : [ - { "id" : "f11", "to" : "f10", "label" : { + { "id" : "f10", "to" : "f7", "label" : { "name" : "{internal}" } }, - { "id" : "f14", "to" : "f13", "via" : [ { "x" : 480, "y" : 328 } ], "label" : { + { "id" : "f12", "to" : "f11", "via" : [ { "x" : 480, "y" : 344 } ], "label" : { "name" : "{external}", - "segment" : 1.21, - "offset" : { "x" : 63, "y" : 3 } + "segment" : 1.1, + "offset" : { "x" : 73, "y" : -2 } } } ] }, { - "id" : "f10", + "id" : "f11", "type" : "UserTask", - "name" : "Task B", + "name" : "Task C", "visual" : { - "at" : { "x" : 736, "y" : 208 } + "at" : { "x" : 696, "y" : 344 } }, - "connect" : [ - { "id" : "f12", "to" : "f5", "via" : [ { "x" : 880, "y" : 208 } ] } - ] + "connect" : { "id" : "f13", "to" : "f7" } }, { - "id" : "f13", - "type" : "UserTask", - "name" : "Task C", + "id" : "f6", + "type" : "Alternative", "visual" : { - "at" : { "x" : 736, "y" : 328 } + "at" : { "x" : 808, "y" : 64 } }, "connect" : [ - { "id" : "f15", "to" : "f10" } + { "id" : "f16", "to" : "f1" } ] } ] } \ No newline at end of file diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/WorkflowEstimatorTest.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/WorkflowEstimatorTest.java index f2edd116..41771dcf 100644 --- a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/WorkflowEstimatorTest.java +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/WorkflowEstimatorTest.java @@ -9,6 +9,9 @@ import ch.ivyteam.ivy.environment.IvyTest; import ch.ivyteam.ivy.process.model.Process; import ch.ivyteam.ivy.process.rdm.IProcessManager; +import ch.ivyteam.ivy.process.IProcessManager; +import ch.ivyteam.ivy.workflow.IProcessStart; +import ch.ivyteam.ivy.workflow.IWorkflowProcessModelVersion; @IvyTest @SuppressWarnings("restriction") @@ -16,22 +19,20 @@ public class WorkflowEstimatorTest { private Process process; private WorkflowEstimator workflowEstimator; - - private - + @BeforeEach void setup() { - var pmv = Ivy.request().getProcessModelVersion(); - var manager = IProcessManager.instance().getProjectDataModelFor(pmv); + var pmv = Ivy.request().getProcessModelVersion(); + var manager = IProcessManager.instance().getProjectDataModelFor(pmv); this.process = manager.findProcessByPath("MainTest").getModel(); - - //workflowEstimator = new WorkflowEstimator(process, null, ""); - + + // workflowEstimator = new WorkflowEstimator(process, null, ""); + } @Test void shouldfindAllTasks() { - System.out.println("--------------------"); - //workflowEstimator.findAllTasks(process.); + System.out.println("--------------------"); + // workflowEstimator.findAllTasks(process.); } } diff --git a/workflow-estimator/pom.xml b/workflow-estimator/pom.xml index 9aa07a6b..3ac3a850 100644 --- a/workflow-estimator/pom.xml +++ b/workflow-estimator/pom.xml @@ -6,12 +6,15 @@ workflow-estimator 1.0.0-SNAPSHOT iar + + 10.0.14 + com.axonivy.ivy.ci project-build-plugin - 11.2.0 + ${build.plugin.version} true diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java index e1555e5a..b09f5917 100644 --- a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java +++ b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java @@ -9,6 +9,7 @@ import ch.ivyteam.ivy.process.model.Process; import ch.ivyteam.ivy.process.model.BaseElement; +import ch.ivyteam.ivy.process.model.NodeElement; import ch.ivyteam.ivy.process.model.element.SingleTaskCreator; public class WorkflowEstimator { From d45938e4a2e981918c774dee2dd59873ab0041e4 Mon Sep 17 00:00:00 2001 From: Trung Mai Date: Tue, 20 Feb 2024 10:38:17 +0700 Subject: [PATCH 008/143] TE-538: Unit test get all task from RequestStart without follow name --- pom.xml | 2 +- .../utils/estimator/test/ProcessGraph.java | 17 ++++++++++ .../estimator/test/WorkflowEstimatorTest.java | 29 +++++++++++----- .../utils/estimator/WorkflowEstimator.java | 33 ++++++++++--------- 4 files changed, 55 insertions(+), 26 deletions(-) create mode 100644 workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/ProcessGraph.java diff --git a/pom.xml b/pom.xml index 43d3635c..1f0c6016 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ com.axonivy.utils.estimator workflow-estimator workflow-estimator-modules - 11.0.0-SNAPSHOT + 1.0.0-SNAPSHOT pom diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/ProcessGraph.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/ProcessGraph.java new file mode 100644 index 00000000..d5e3658d --- /dev/null +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/ProcessGraph.java @@ -0,0 +1,17 @@ +package com.axonivy.utils.estimator.test; + +import ch.ivyteam.ivy.process.model.Process; +import ch.ivyteam.ivy.process.model.element.event.start.RequestStart; + +@SuppressWarnings("restriction") +public class ProcessGraph { + public final Process process; + + public ProcessGraph(Process process) { + this.process = process; + } + + public RequestStart findStart() { + return process.search().type(RequestStart.class).findOne(); + } +} diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/WorkflowEstimatorTest.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/WorkflowEstimatorTest.java index 41771dcf..b148580d 100644 --- a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/WorkflowEstimatorTest.java +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/WorkflowEstimatorTest.java @@ -1,17 +1,20 @@ package com.axonivy.utils.estimator.test; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Comparator; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import com.axonivy.utils.estimator.WorkflowEstimator; +import com.axonivy.utils.estimator.model.EstimatedTask; import ch.ivyteam.ivy.environment.Ivy; import ch.ivyteam.ivy.environment.IvyTest; +import ch.ivyteam.ivy.process.IProcessManager; import ch.ivyteam.ivy.process.model.Process; import ch.ivyteam.ivy.process.rdm.IProcessManager; -import ch.ivyteam.ivy.process.IProcessManager; -import ch.ivyteam.ivy.workflow.IProcessStart; -import ch.ivyteam.ivy.workflow.IWorkflowProcessModelVersion; @IvyTest @SuppressWarnings("restriction") @@ -19,20 +22,28 @@ public class WorkflowEstimatorTest { private Process process; private WorkflowEstimator workflowEstimator; + private ProcessGraph graph; @BeforeEach void setup() { var pmv = Ivy.request().getProcessModelVersion(); var manager = IProcessManager.instance().getProjectDataModelFor(pmv); this.process = manager.findProcessByPath("MainTest").getModel(); - - // workflowEstimator = new WorkflowEstimator(process, null, ""); - + this.graph = new ProcessGraph(process); + + // workflowEstimator = new WorkflowEstimator(process, null, "");a } @Test - void shouldfindAllTasks() { - System.out.println("--------------------"); - // workflowEstimator.findAllTasks(process.); + void shouldfindAllTasksAtStartRequestWithoutFlowName() { + var workflowEstimator = new WorkflowEstimator(process, null, null); + + var estimatedTasks = workflowEstimator.findAllTasks(graph.findStart()).stream() + .sorted(Comparator.comparing(EstimatedTask::getTaskName)).toList(); + + assertEquals(3, estimatedTasks.size()); + assertEquals("Task A", estimatedTasks.get(0).getTaskName()); + assertEquals("Task B", estimatedTasks.get(1).getTaskName()); + assertEquals("Task C", estimatedTasks.get(2).getTaskName()); } } diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java index b09f5917..052738cb 100644 --- a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java +++ b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java @@ -11,13 +11,15 @@ import ch.ivyteam.ivy.process.model.BaseElement; import ch.ivyteam.ivy.process.model.NodeElement; import ch.ivyteam.ivy.process.model.element.SingleTaskCreator; +import ch.ivyteam.ivy.process.model.element.activity.UserTask; +@SuppressWarnings("restriction") public class WorkflowEstimator { - - private Process process; + + private Process process; private Enum useCase; private String flowName; - + public WorkflowEstimator(Process process, Enum useCase, String flowName) { this.process = process; this.useCase = useCase; @@ -25,17 +27,16 @@ public WorkflowEstimator(Process process, Enum useCase, String flowName) { } public List findAllTasks(BaseElement startAtElement) { - + List elements = startAtElement.getRootProcess().getElements(); List estimatedTasks = elements.stream() - //filter to get task only - .filter(el -> { - return el instanceof SingleTaskCreator; - }) - //filter the tash which have estimated if needed - .map(task -> createEstimatedTask((SingleTaskCreator) task)) - .toList(); - + // filter to get task only + .filter(el -> { + return el instanceof UserTask; + }) + // filter the tash which have estimated if needed + .map(task -> createEstimatedTask((UserTask) task)).toList(); + return estimatedTasks; } @@ -43,14 +44,14 @@ public List findTasksOnPath(BaseElement startAtElement) { // TODO: Implement here return emptyList(); } - - private EstimatedTask createEstimatedTask(SingleTaskCreator task) { + + private EstimatedTask createEstimatedTask(UserTask task) { EstimatedTask estimatedTask = new EstimatedTask(); estimatedTask.setPid(task.getPid().getRawPid()); estimatedTask.setTaskName(task.getName()); estimatedTask.setEstimatedStartTimestamp(new Date()); - + return estimatedTask; } - + } From 9fe2e998d5e8ea0117718ea862cd7ccff9f0d0e8 Mon Sep 17 00:00:00 2001 From: Trung Mai Date: Tue, 20 Feb 2024 17:41:06 +0700 Subject: [PATCH 009/143] TE-538: Unit test with start element from task B base on TDD --- .../utils/estimator/test/ProcessGraph.java | 22 +++++++++++++------ .../estimator/test/WorkflowEstimatorTest.java | 12 +++++++++- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/ProcessGraph.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/ProcessGraph.java index d5e3658d..666f0c66 100644 --- a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/ProcessGraph.java +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/ProcessGraph.java @@ -1,17 +1,25 @@ package com.axonivy.utils.estimator.test; +import org.apache.commons.lang3.StringUtils; + import ch.ivyteam.ivy.process.model.Process; +import ch.ivyteam.ivy.process.model.element.SingleTaskCreator; import ch.ivyteam.ivy.process.model.element.event.start.RequestStart; @SuppressWarnings("restriction") public class ProcessGraph { - public final Process process; + public final Process process; - public ProcessGraph(Process process) { - this.process = process; - } + public ProcessGraph(Process process) { + this.process = process; + } - public RequestStart findStart() { - return process.search().type(RequestStart.class).findOne(); - } + public RequestStart findStart() { + return process.search().type(RequestStart.class).findOne(); + } + + public SingleTaskCreator findByTaskName(String taskName) { + + return null; + } } diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/WorkflowEstimatorTest.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/WorkflowEstimatorTest.java index b148580d..40872806 100644 --- a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/WorkflowEstimatorTest.java +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/WorkflowEstimatorTest.java @@ -12,7 +12,6 @@ import ch.ivyteam.ivy.environment.Ivy; import ch.ivyteam.ivy.environment.IvyTest; -import ch.ivyteam.ivy.process.IProcessManager; import ch.ivyteam.ivy.process.model.Process; import ch.ivyteam.ivy.process.rdm.IProcessManager; @@ -46,4 +45,15 @@ void shouldfindAllTasksAtStartRequestWithoutFlowName() { assertEquals("Task B", estimatedTasks.get(1).getTaskName()); assertEquals("Task C", estimatedTasks.get(2).getTaskName()); } + + @Test + void shouldfindAllTasksAtTaskB() { + var workflowEstimator = new WorkflowEstimator(process, null, null); + var taskB = this.graph.findByTaskName("Task B"); + var estimatedTasks = workflowEstimator.findAllTasks(taskB).stream() + .sorted(Comparator.comparing(EstimatedTask::getTaskName)).toList(); + + assertEquals(1, estimatedTasks.size()); + assertEquals("Task B", estimatedTasks.get(1).getTaskName()); + } } From 76fcd76b576bc5ad55d58f6ddb629f9191182ca9 Mon Sep 17 00:00:00 2001 From: Trung Mai Date: Wed, 21 Feb 2024 09:38:21 +0700 Subject: [PATCH 010/143] TE-538: Update build plugin --- workflow-estimator-demo/pom.xml | 39 +++++---- workflow-estimator-product/pom.xml | 126 ++++++++++++++--------------- workflow-estimator-test/pom.xml | 69 ++++++++-------- workflow-estimator/pom.xml | 39 +++++---- 4 files changed, 135 insertions(+), 138 deletions(-) diff --git a/workflow-estimator-demo/pom.xml b/workflow-estimator-demo/pom.xml index 769cf2b2..bc7072ef 100644 --- a/workflow-estimator-demo/pom.xml +++ b/workflow-estimator-demo/pom.xml @@ -1,22 +1,21 @@ - - 4.0.0 - com.axonivy.utils.estimator - workflow-estimator-demo - 1.0.0-SNAPSHOT - iar - - 10.0.14 - - - - - com.axonivy.ivy.ci - project-build-plugin - ${build.plugin.version} - true - - - + + 4.0.0 + com.axonivy.utils.estimator + workflow-estimator-demo + 1.0.0-SNAPSHOT + iar + + 11.2.0 + + + + + com.axonivy.ivy.ci + project-build-plugin + ${build.plugin.version} + true + + + diff --git a/workflow-estimator-product/pom.xml b/workflow-estimator-product/pom.xml index beaf9abd..523b204d 100644 --- a/workflow-estimator-product/pom.xml +++ b/workflow-estimator-product/pom.xml @@ -1,67 +1,67 @@ - 4.0.0 - com.axonivy.market - workflow-estimator-product - 10.0.0-SNAPSHOT - pom + 4.0.0 + com.axonivy.market + workflow-estimator-product + 1.0.0-SNAPSHOT + pom - - ../workflow-estimator/config/variables.yaml - + + ../workflow-estimator/config/variables.yaml + - - - - org.apache.maven.plugins - maven-assembly-plugin - 3.3.0 - - - package - - single - - - false - - zip.xml - - - - - - - maven-antrun-plugin - 1.7 - - - generate-sources - - ${skip-readme} - - - - - - - - - - - run - - - - - - - - - org.apache.maven.plugins - maven-deploy-plugin - 3.0.0-M1 - - - - + + + + org.apache.maven.plugins + maven-assembly-plugin + 3.3.0 + + + package + + single + + + false + + zip.xml + + + + + + + maven-antrun-plugin + 1.7 + + + generate-sources + + ${skip-readme} + + + + + + + + + + + run + + + + + + + + + org.apache.maven.plugins + maven-deploy-plugin + 3.0.0-M1 + + + + diff --git a/workflow-estimator-test/pom.xml b/workflow-estimator-test/pom.xml index 65857c83..81af7dbe 100644 --- a/workflow-estimator-test/pom.xml +++ b/workflow-estimator-test/pom.xml @@ -1,37 +1,36 @@ - - 4.0.0 - com.axonivy.utils.estimator - workflow-estimator-test - 1.0.0-SNAPSHOT - iar - - 10.0.14 - - - - com.axonivy.utils.estimator - workflow-estimator - ${project.version} - iar - - - com.axonivy.ivy.test - unit-tester - 10.0.14 - test - - - - src_test - - - com.axonivy.ivy.ci - project-build-plugin - ${build.plugin.version} - true - - - + + 4.0.0 + com.axonivy.utils.estimator + workflow-estimator-test + 1.0.0-SNAPSHOT + iar + + 11.2.0 + + + + com.axonivy.utils.estimator + workflow-estimator + ${project.version} + iar + + + com.axonivy.ivy.test + unit-tester + 11.2.0 + test + + + + src_test + + + com.axonivy.ivy.ci + project-build-plugin + ${build.plugin.version} + true + + + diff --git a/workflow-estimator/pom.xml b/workflow-estimator/pom.xml index 3ac3a850..41543a82 100644 --- a/workflow-estimator/pom.xml +++ b/workflow-estimator/pom.xml @@ -1,22 +1,21 @@ - - 4.0.0 - com.axonivy.utils.estimator - workflow-estimator - 1.0.0-SNAPSHOT - iar - - 10.0.14 - - - - - com.axonivy.ivy.ci - project-build-plugin - ${build.plugin.version} - true - - - + + 4.0.0 + com.axonivy.utils.estimator + workflow-estimator + 1.0.0-SNAPSHOT + iar + + 11.2.0 + + + + + com.axonivy.ivy.ci + project-build-plugin + ${build.plugin.version} + true + + + From 7b155f00795837318b0295e825c7bf06f01bec60 Mon Sep 17 00:00:00 2001 From: Trung Mai Date: Wed, 21 Feb 2024 11:50:17 +0700 Subject: [PATCH 011/143] TE-538: Unit test findAllTasks --- .../utils/estimator/test/ProcessGraph.java | 14 +++---- .../estimator/test/WorkflowEstimatorTest.java | 30 +++++++++---- .../utils/estimator/WorkflowEstimator.java | 42 ++++++++++++++----- 3 files changed, 60 insertions(+), 26 deletions(-) diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/ProcessGraph.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/ProcessGraph.java index 666f0c66..5489d13d 100644 --- a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/ProcessGraph.java +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/ProcessGraph.java @@ -1,9 +1,7 @@ package com.axonivy.utils.estimator.test; -import org.apache.commons.lang3.StringUtils; - +import ch.ivyteam.ivy.process.model.BaseElement; import ch.ivyteam.ivy.process.model.Process; -import ch.ivyteam.ivy.process.model.element.SingleTaskCreator; import ch.ivyteam.ivy.process.model.element.event.start.RequestStart; @SuppressWarnings("restriction") @@ -17,9 +15,11 @@ public ProcessGraph(Process process) { public RequestStart findStart() { return process.search().type(RequestStart.class).findOne(); } - - public SingleTaskCreator findByTaskName(String taskName) { - - return null; + + public BaseElement findByElementName(String name) { + return process.getElements().stream() + .filter(el -> el.getName().equals(name)) + .findFirst() + .orElse(null); } } diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/WorkflowEstimatorTest.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/WorkflowEstimatorTest.java index 40872806..017e39ae 100644 --- a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/WorkflowEstimatorTest.java +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/WorkflowEstimatorTest.java @@ -19,8 +19,7 @@ @SuppressWarnings("restriction") public class WorkflowEstimatorTest { - private Process process; - private WorkflowEstimator workflowEstimator; + private Process process; private ProcessGraph graph; @BeforeEach @@ -28,9 +27,7 @@ void setup() { var pmv = Ivy.request().getProcessModelVersion(); var manager = IProcessManager.instance().getProjectDataModelFor(pmv); this.process = manager.findProcessByPath("MainTest").getModel(); - this.graph = new ProcessGraph(process); - - // workflowEstimator = new WorkflowEstimator(process, null, "");a + this.graph = new ProcessGraph(process); } @Test @@ -38,7 +35,8 @@ void shouldfindAllTasksAtStartRequestWithoutFlowName() { var workflowEstimator = new WorkflowEstimator(process, null, null); var estimatedTasks = workflowEstimator.findAllTasks(graph.findStart()).stream() - .sorted(Comparator.comparing(EstimatedTask::getTaskName)).toList(); + .sorted(Comparator.comparing(EstimatedTask::getTaskName)) + .toList(); assertEquals(3, estimatedTasks.size()); assertEquals("Task A", estimatedTasks.get(0).getTaskName()); @@ -49,11 +47,25 @@ void shouldfindAllTasksAtStartRequestWithoutFlowName() { @Test void shouldfindAllTasksAtTaskB() { var workflowEstimator = new WorkflowEstimator(process, null, null); - var taskB = this.graph.findByTaskName("Task B"); + var taskB = this.graph.findByElementName("Task B"); var estimatedTasks = workflowEstimator.findAllTasks(taskB).stream() - .sorted(Comparator.comparing(EstimatedTask::getTaskName)).toList(); + .sorted(Comparator.comparing(EstimatedTask::getTaskName)) + .toList(); assertEquals(1, estimatedTasks.size()); - assertEquals("Task B", estimatedTasks.get(1).getTaskName()); + assertEquals("Task B", estimatedTasks.get(0).getTaskName()); + } + + @Test + void shouldfindAllTasksAtTaskC() { + var workflowEstimator = new WorkflowEstimator(process, null, null); + var taskB = this.graph.findByElementName("Task C"); + var estimatedTasks = workflowEstimator.findAllTasks(taskB).stream() + .sorted(Comparator.comparing(EstimatedTask::getTaskName)) + .toList(); + + assertEquals(2, estimatedTasks.size()); + assertEquals("Task B", estimatedTasks.get(0).getTaskName()); + assertEquals("Task C", estimatedTasks.get(1).getTaskName()); } } diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java index 052738cb..f22b25b9 100644 --- a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java +++ b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java @@ -2,15 +2,17 @@ import static java.util.Collections.emptyList; +import java.util.ArrayList; import java.util.Date; import java.util.List; +import java.util.stream.Stream; import com.axonivy.utils.estimator.model.EstimatedTask; -import ch.ivyteam.ivy.process.model.Process; import ch.ivyteam.ivy.process.model.BaseElement; import ch.ivyteam.ivy.process.model.NodeElement; -import ch.ivyteam.ivy.process.model.element.SingleTaskCreator; +import ch.ivyteam.ivy.process.model.Process; +import ch.ivyteam.ivy.process.model.connector.SequenceFlow; import ch.ivyteam.ivy.process.model.element.activity.UserTask; @SuppressWarnings("restriction") @@ -27,15 +29,20 @@ public WorkflowEstimator(Process process, Enum useCase, String flowName) { } public List findAllTasks(BaseElement startAtElement) { + List estimatedTasks = emptyList(); + + if (startAtElement instanceof NodeElement) { + List nextNodes = nextNodeElements((NodeElement) startAtElement); - List elements = startAtElement.getRootProcess().getElements(); - List estimatedTasks = elements.stream() - // filter to get task only - .filter(el -> { - return el instanceof UserTask; - }) - // filter the tash which have estimated if needed - .map(task -> createEstimatedTask((UserTask) task)).toList(); + estimatedTasks = Stream.concat(Stream.of(startAtElement), nextNodes.stream()) + // filter to get task only + .filter(node -> { + return node instanceof UserTask; + }) + // filter the task which have estimated if needed + .map(task -> createEstimatedTask((UserTask) task)) + .toList(); + } return estimatedTasks; } @@ -54,4 +61,19 @@ private EstimatedTask createEstimatedTask(UserTask task) { return estimatedTask; } + public List nextNodeElements(NodeElement from) { + var nexts = new ArrayList(); + + if (from != null) { + List outs = from.getOutgoing(); + for (SequenceFlow out : outs) { + NodeElement target = out.getTarget(); + List nextElements = nextNodeElements(target); + nexts.add(target); + nexts.addAll(nextElements); + } + } + + return nexts.stream().distinct().toList(); + } } From 7e5caf96c69370e94d5e0c207d5264acf002ba4c Mon Sep 17 00:00:00 2001 From: Trung Mai Date: Thu, 22 Feb 2024 09:48:29 +0700 Subject: [PATCH 012/143] TE-538: Fix to get the task order --- .../estimator/test/WorkflowEstimatorTest.java | 23 ++--- .../utils/estimator/WorkflowEstimator.java | 94 ++++++++++++++++--- 2 files changed, 89 insertions(+), 28 deletions(-) diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/WorkflowEstimatorTest.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/WorkflowEstimatorTest.java index 017e39ae..da4b9f59 100644 --- a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/WorkflowEstimatorTest.java +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/WorkflowEstimatorTest.java @@ -1,6 +1,7 @@ package com.axonivy.utils.estimator.test; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import java.util.Comparator; @@ -27,21 +28,17 @@ void setup() { var pmv = Ivy.request().getProcessModelVersion(); var manager = IProcessManager.instance().getProjectDataModelFor(pmv); this.process = manager.findProcessByPath("MainTest").getModel(); - this.graph = new ProcessGraph(process); + this.graph = new ProcessGraph(process); } @Test void shouldfindAllTasksAtStartRequestWithoutFlowName() { var workflowEstimator = new WorkflowEstimator(process, null, null); - var estimatedTasks = workflowEstimator.findAllTasks(graph.findStart()).stream() - .sorted(Comparator.comparing(EstimatedTask::getTaskName)) - .toList(); + var estimatedTasks = workflowEstimator.findAllTasks(graph.findStart()); - assertEquals(3, estimatedTasks.size()); + assertEquals(1, estimatedTasks.size()); assertEquals("Task A", estimatedTasks.get(0).getTaskName()); - assertEquals("Task B", estimatedTasks.get(1).getTaskName()); - assertEquals("Task C", estimatedTasks.get(2).getTaskName()); } @Test @@ -59,13 +56,11 @@ void shouldfindAllTasksAtTaskB() { @Test void shouldfindAllTasksAtTaskC() { var workflowEstimator = new WorkflowEstimator(process, null, null); - var taskB = this.graph.findByElementName("Task C"); - var estimatedTasks = workflowEstimator.findAllTasks(taskB).stream() - .sorted(Comparator.comparing(EstimatedTask::getTaskName)) - .toList(); + var taskC = this.graph.findByElementName("Task C"); + var estimatedTasks = workflowEstimator.findAllTasks(taskC); - assertEquals(2, estimatedTasks.size()); - assertEquals("Task B", estimatedTasks.get(0).getTaskName()); - assertEquals("Task C", estimatedTasks.get(1).getTaskName()); + assertEquals(2, estimatedTasks.size()); + assertEquals("Task C", estimatedTasks.get(0).getTaskName()); + assertEquals("Task B", estimatedTasks.get(1).getTaskName()); } } diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java index f22b25b9..44147c9c 100644 --- a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java +++ b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java @@ -3,9 +3,12 @@ import static java.util.Collections.emptyList; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; import java.util.Date; import java.util.List; -import java.util.stream.Stream; + +import org.apache.commons.lang3.StringUtils; import com.axonivy.utils.estimator.model.EstimatedTask; @@ -32,9 +35,13 @@ public List findAllTasks(BaseElement startAtElement) { List estimatedTasks = emptyList(); if (startAtElement instanceof NodeElement) { - List nextNodes = nextNodeElements((NodeElement) startAtElement); - - estimatedTasks = Stream.concat(Stream.of(startAtElement), nextNodes.stream()) + List> paths = findPaths(startAtElement); + + estimatedTasks = paths.stream() + //If have more than on path. + .min(Comparator.comparingInt(List::size)) + .orElse(emptyList()) + .stream() // filter to get task only .filter(node -> { return node instanceof UserTask; @@ -52,15 +59,6 @@ public List findTasksOnPath(BaseElement startAtElement) { return emptyList(); } - private EstimatedTask createEstimatedTask(UserTask task) { - EstimatedTask estimatedTask = new EstimatedTask(); - estimatedTask.setPid(task.getPid().getRawPid()); - estimatedTask.setTaskName(task.getName()); - estimatedTask.setEstimatedStartTimestamp(new Date()); - - return estimatedTask; - } - public List nextNodeElements(NodeElement from) { var nexts = new ArrayList(); @@ -75,5 +73,73 @@ public List nextNodeElements(NodeElement from) { } return nexts.stream().distinct().toList(); - } + } + + private List> findPaths(BaseElement from) { + return findPaths(from, null); + } + + private List> findPaths(BaseElement from, String flowName) { + List> paths = findNextNodeElementPath(from); + paths.forEach(path -> { + path.add(0, from); + }); + + List> pathByFlowName = paths.stream() + .filter(path -> hasFlowName(path, flowName)) + .toList(); + + return pathByFlowName; + } + + private List> findNextNodeElementPath(BaseElement from) { + var nexts = new ArrayList>(); + + if (from != null && from instanceof NodeElement) { + List outs = ((NodeElement) from).getOutgoing(); + for (SequenceFlow out : outs) { + + NodeElement target = out.getTarget(); + List> nextElements = findNextNodeElementPath(target); + + if (nextElements.isEmpty()) { + nextElements.add(new ArrayList(Arrays.asList(target, out))); + } else { + nextElements.forEach(nodes -> { + nodes.add(0, target); + nodes.add(1, out); + }); + } + + nexts.addAll(nextElements); + } + } + return nexts; + } + + private boolean hasFlowName(List elements, String flowName) { + if (StringUtils.isBlank(flowName)) { + return true; + } + + for (BaseElement el : elements) { + if (el instanceof SequenceFlow) { + String label = ((SequenceFlow) el).getEdge().getLabel().getText(); + if (label.contains(flowName)) { + return true; + } + } + } + + return false; + } + + private EstimatedTask createEstimatedTask(UserTask task) { + EstimatedTask estimatedTask = new EstimatedTask(); + estimatedTask.setPid(task.getPid().getRawPid()); + estimatedTask.setTaskName(task.getName()); + estimatedTask.setEstimatedStartTimestamp(new Date()); + + return estimatedTask; + } } From 9a92d72899c8f9356b31f109314385069511169e Mon Sep 17 00:00:00 2001 From: Trung Mai Date: Thu, 22 Feb 2024 10:23:19 +0700 Subject: [PATCH 013/143] TE-538: Get task on path --- .../estimator/test/WorkflowEstimatorTest.java | 28 +++++++++-- .../utils/estimator/WorkflowEstimator.java | 50 ++++++++++++------- 2 files changed, 55 insertions(+), 23 deletions(-) diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/WorkflowEstimatorTest.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/WorkflowEstimatorTest.java index da4b9f59..202f8c3a 100644 --- a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/WorkflowEstimatorTest.java +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/WorkflowEstimatorTest.java @@ -1,7 +1,6 @@ package com.axonivy.utils.estimator.test; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; import java.util.Comparator; @@ -32,7 +31,7 @@ void setup() { } @Test - void shouldfindAllTasksAtStartRequestWithoutFlowName() { + void shouldFindAllTasksAtStartRequestWithoutFlowName() { var workflowEstimator = new WorkflowEstimator(process, null, null); var estimatedTasks = workflowEstimator.findAllTasks(graph.findStart()); @@ -42,7 +41,7 @@ void shouldfindAllTasksAtStartRequestWithoutFlowName() { } @Test - void shouldfindAllTasksAtTaskB() { + void shouldFfindAllTasksAtTaskB() { var workflowEstimator = new WorkflowEstimator(process, null, null); var taskB = this.graph.findByElementName("Task B"); var estimatedTasks = workflowEstimator.findAllTasks(taskB).stream() @@ -54,7 +53,7 @@ void shouldfindAllTasksAtTaskB() { } @Test - void shouldfindAllTasksAtTaskC() { + void shouldFindAllTasksAtTaskC() { var workflowEstimator = new WorkflowEstimator(process, null, null); var taskC = this.graph.findByElementName("Task C"); var estimatedTasks = workflowEstimator.findAllTasks(taskC); @@ -63,4 +62,25 @@ void shouldfindAllTasksAtTaskC() { assertEquals("Task C", estimatedTasks.get(0).getTaskName()); assertEquals("Task B", estimatedTasks.get(1).getTaskName()); } + + @Test + void shouldfindAllTasksOfInternalFlow() { + var workflowEstimator = new WorkflowEstimator(process, null, "internal"); + var estimatedTasks = workflowEstimator.findTasksOnPath(graph.findStart()); + + assertEquals(2, estimatedTasks.size()); + assertEquals("Task A", estimatedTasks.get(0).getTaskName()); + assertEquals("Task B", estimatedTasks.get(1).getTaskName()); + } + + @Test + void shouldfindAllTasksOfExternalFlow() { + var workflowEstimator = new WorkflowEstimator(process, null, "external"); + var estimatedTasks = workflowEstimator.findTasksOnPath(graph.findStart()); + + assertEquals(3, estimatedTasks.size()); + assertEquals("Task A", estimatedTasks.get(0).getTaskName()); + assertEquals("Task C", estimatedTasks.get(1).getTaskName()); + assertEquals("Task B", estimatedTasks.get(2).getTaskName()); + } } diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java index 44147c9c..500c2230 100644 --- a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java +++ b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java @@ -36,27 +36,21 @@ public List findAllTasks(BaseElement startAtElement) { if (startAtElement instanceof NodeElement) { List> paths = findPaths(startAtElement); - - estimatedTasks = paths.stream() - //If have more than on path. - .min(Comparator.comparingInt(List::size)) - .orElse(emptyList()) - .stream() - // filter to get task only - .filter(node -> { - return node instanceof UserTask; - }) - // filter the task which have estimated if needed - .map(task -> createEstimatedTask((UserTask) task)) - .toList(); + estimatedTasks = convertToEstimatedTasks(paths); } - + return estimatedTasks; } public List findTasksOnPath(BaseElement startAtElement) { - // TODO: Implement here - return emptyList(); + List estimatedTasks = emptyList(); + + if (startAtElement instanceof NodeElement) { + List> paths = findPaths(startAtElement, flowName); + estimatedTasks = convertToEstimatedTasks(paths); + } + + return estimatedTasks; } public List nextNodeElements(NodeElement from) { @@ -79,6 +73,18 @@ private List> findPaths(BaseElement from) { return findPaths(from, null); } + private List convertToEstimatedTasks(List> paths) { + return paths.stream() + // If have more than on path. + .min(Comparator.comparingInt(List::size)).orElse(emptyList()).stream() + // filter to get task only + .filter(node -> { + return node instanceof UserTask; + }) + // filter the task which have estimated if needed + .map(task -> createEstimatedTask((UserTask) task)).toList(); + } + private List> findPaths(BaseElement from, String flowName) { List> paths = findNextNodeElementPath(from); paths.forEach(path -> { @@ -122,16 +128,22 @@ private boolean hasFlowName(List elements, String flowName) { return true; } + boolean existsFlowName = false; for (BaseElement el : elements) { if (el instanceof SequenceFlow) { String label = ((SequenceFlow) el).getEdge().getLabel().getText(); - if (label.contains(flowName)) { - return true; + if(StringUtils.isNotBlank(label)) { + //Have label but not contain flowName -> Exit check + if(!label.contains(flowName)) { + return false; + } else { + existsFlowName = true; + } } } } - return false; + return existsFlowName; } private EstimatedTask createEstimatedTask(UserTask task) { From 443d531210706d711af1319a37a2a5cd753ab4f3 Mon Sep 17 00:00:00 2001 From: Trung Mai Date: Thu, 22 Feb 2024 11:53:02 +0700 Subject: [PATCH 014/143] TE-538: Update comment --- .../src/com/axonivy/utils/estimator/WorkflowEstimator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java index 500c2230..962f1733 100644 --- a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java +++ b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java @@ -75,7 +75,7 @@ private List> findPaths(BaseElement from) { private List convertToEstimatedTasks(List> paths) { return paths.stream() - // If have more than on path. + // If have more than one path, we get the shortest .min(Comparator.comparingInt(List::size)).orElse(emptyList()).stream() // filter to get task only .filter(node -> { From 954f29a3dcf8ce4f76e9682b349b0f6c26bb1b25 Mon Sep 17 00:00:00 2001 From: Trung Mai Date: Thu, 22 Feb 2024 15:06:19 +0700 Subject: [PATCH 015/143] TE-538: Update unit test with get longest path --- .../estimator/test/WorkflowEstimatorTest.java | 41 +++++++++---------- .../utils/estimator/WorkflowEstimator.java | 4 +- 2 files changed, 21 insertions(+), 24 deletions(-) diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/WorkflowEstimatorTest.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/WorkflowEstimatorTest.java index 202f8c3a..6e09e7dd 100644 --- a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/WorkflowEstimatorTest.java +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/WorkflowEstimatorTest.java @@ -2,13 +2,10 @@ import static org.junit.jupiter.api.Assertions.assertEquals; -import java.util.Comparator; - import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import com.axonivy.utils.estimator.WorkflowEstimator; -import com.axonivy.utils.estimator.model.EstimatedTask; import ch.ivyteam.ivy.environment.Ivy; import ch.ivyteam.ivy.environment.IvyTest; @@ -19,7 +16,7 @@ @SuppressWarnings("restriction") public class WorkflowEstimatorTest { - private Process process; + private Process process; private ProcessGraph graph; @BeforeEach @@ -27,7 +24,7 @@ void setup() { var pmv = Ivy.request().getProcessModelVersion(); var manager = IProcessManager.instance().getProjectDataModelFor(pmv); this.process = manager.findProcessByPath("MainTest").getModel(); - this.graph = new ProcessGraph(process); + this.graph = new ProcessGraph(process); } @Test @@ -36,22 +33,22 @@ void shouldFindAllTasksAtStartRequestWithoutFlowName() { var estimatedTasks = workflowEstimator.findAllTasks(graph.findStart()); - assertEquals(1, estimatedTasks.size()); + assertEquals(3, estimatedTasks.size()); assertEquals("Task A", estimatedTasks.get(0).getTaskName()); + assertEquals("Task C", estimatedTasks.get(1).getTaskName()); + assertEquals("Task B", estimatedTasks.get(2).getTaskName()); } - + @Test void shouldFfindAllTasksAtTaskB() { var workflowEstimator = new WorkflowEstimator(process, null, null); var taskB = this.graph.findByElementName("Task B"); - var estimatedTasks = workflowEstimator.findAllTasks(taskB).stream() - .sorted(Comparator.comparing(EstimatedTask::getTaskName)) - .toList(); + var estimatedTasks = workflowEstimator.findAllTasks(taskB); - assertEquals(1, estimatedTasks.size()); - assertEquals("Task B", estimatedTasks.get(0).getTaskName()); + assertEquals(1, estimatedTasks.size()); + assertEquals("Task B", estimatedTasks.get(0).getTaskName()); } - + @Test void shouldFindAllTasksAtTaskC() { var workflowEstimator = new WorkflowEstimator(process, null, null); @@ -60,27 +57,27 @@ void shouldFindAllTasksAtTaskC() { assertEquals(2, estimatedTasks.size()); assertEquals("Task C", estimatedTasks.get(0).getTaskName()); - assertEquals("Task B", estimatedTasks.get(1).getTaskName()); + assertEquals("Task B", estimatedTasks.get(1).getTaskName()); } - + @Test - void shouldfindAllTasksOfInternalFlow() { - var workflowEstimator = new WorkflowEstimator(process, null, "internal"); + void shouldFindAllTasksOfInternalFlow() { + var workflowEstimator = new WorkflowEstimator(process, null, "internal"); var estimatedTasks = workflowEstimator.findTasksOnPath(graph.findStart()); assertEquals(2, estimatedTasks.size()); assertEquals("Task A", estimatedTasks.get(0).getTaskName()); - assertEquals("Task B", estimatedTasks.get(1).getTaskName()); + assertEquals("Task B", estimatedTasks.get(1).getTaskName()); } - + @Test - void shouldfindAllTasksOfExternalFlow() { - var workflowEstimator = new WorkflowEstimator(process, null, "external"); + void shouldFindAllTasksOfExternalFlow() { + var workflowEstimator = new WorkflowEstimator(process, null, "external"); var estimatedTasks = workflowEstimator.findTasksOnPath(graph.findStart()); assertEquals(3, estimatedTasks.size()); assertEquals("Task A", estimatedTasks.get(0).getTaskName()); assertEquals("Task C", estimatedTasks.get(1).getTaskName()); - assertEquals("Task B", estimatedTasks.get(2).getTaskName()); + assertEquals("Task B", estimatedTasks.get(2).getTaskName()); } } diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java index 962f1733..20d32c59 100644 --- a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java +++ b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java @@ -75,8 +75,8 @@ private List> findPaths(BaseElement from) { private List convertToEstimatedTasks(List> paths) { return paths.stream() - // If have more than one path, we get the shortest - .min(Comparator.comparingInt(List::size)).orElse(emptyList()).stream() + // If have more than one path, we get the longest + .max(Comparator.comparingInt(List::size)).orElse(emptyList()).stream() // filter to get task only .filter(node -> { return node instanceof UserTask; From 5dbaf208d7e33ff96704da78d31254fbce9f7d6a Mon Sep 17 00:00:00 2001 From: Timo Rupp Date: Thu, 22 Feb 2024 17:21:36 +0100 Subject: [PATCH 016/143] Added more example processes. --- .../processes/FlowExampleBasic.p.json | 216 ++++++++++++++++++ .../processes/FlowExampleError.p.json | 110 +++++++++ .../processes/FlowExampleLoop.p.json | 83 +++++++ .../processes/MainTest.p.json | 90 -------- .../processes/ParallelTasksExample.p.json | 176 ++++++++++++++ .../processes/TaskTypesExample.p.json | 108 +++++++++ ...torTest.java => FlowExampleBasicTest.java} | 4 +- 7 files changed, 695 insertions(+), 92 deletions(-) create mode 100644 workflow-estimator-test/processes/FlowExampleBasic.p.json create mode 100644 workflow-estimator-test/processes/FlowExampleError.p.json create mode 100644 workflow-estimator-test/processes/FlowExampleLoop.p.json delete mode 100644 workflow-estimator-test/processes/MainTest.p.json create mode 100644 workflow-estimator-test/processes/ParallelTasksExample.p.json create mode 100644 workflow-estimator-test/processes/TaskTypesExample.p.json rename workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/{WorkflowEstimatorTest.java => FlowExampleBasicTest.java} (93%) diff --git a/workflow-estimator-test/processes/FlowExampleBasic.p.json b/workflow-estimator-test/processes/FlowExampleBasic.p.json new file mode 100644 index 00000000..9aad0235 --- /dev/null +++ b/workflow-estimator-test/processes/FlowExampleBasic.p.json @@ -0,0 +1,216 @@ +{ + "$schema" : "https://json-schema.axonivy.com/process/11.2.2/process.json", + "id" : "18DC44E096FDFF75", + "config" : { + "data" : "com.axonivy.utils.estimator.test.Data" + }, + "elements" : [ { + "id" : "f0", + "type" : "RequestStart", + "name" : "start.ivp", + "config" : { + "signature" : "start" + }, + "visual" : { + "at" : { "x" : 96, "y" : 64 } + }, + "connect" : [ + { "id" : "f3", "to" : "f2" } + ] + }, { + "id" : "f1", + "type" : "TaskEnd", + "visual" : { + "at" : { "x" : 944, "y" : 64 } + } + }, { + "id" : "f2", + "type" : "UserTask", + "name" : [ + "Task A", + "(Element Label)" + ], + "config" : { + "task" : { + "name" : "Task A" + } + }, + "visual" : { + "at" : { "x" : 288, "y" : 64 } + }, + "connect" : [ + { "id" : "f5", "to" : "f4" } + ] + }, { + "id" : "f4", + "type" : "Alternative", + "config" : { + "conditions" : { + "f9" : "true" + } + }, + "visual" : { + "at" : { "x" : 480, "y" : 64 } + }, + "connect" : [ + { "id" : "f9", "to" : "f8", "label" : { + "name" : [ + "{internal}", + "{external}", + "{mixed}" + ] + } }, + { "id" : "f14", "to" : "f6", "color" : "default path" } + ] + }, { + "id" : "f7", + "type" : "UserTask", + "name" : "Task B", + "config" : { + "task" : { + "name" : "Task B" + } + }, + "visual" : { + "at" : { "x" : 696, "y" : 224 } + }, + "connect" : [ + { "id" : "f15", "to" : "f6", "via" : [ { "x" : 808, "y" : 224 } ] } + ] + }, { + "id" : "f8", + "type" : "Alternative", + "name" : "int/ext?", + "config" : { + "conditions" : { + "f12" : "true", + "f10" : "" + } + }, + "visual" : { + "at" : { "x" : 480, "y" : 224 }, + "labelOffset" : { "x" : -24, "y" : 0 } + }, + "connect" : [ + { "id" : "f10", "to" : "f7", "color" : "default path", "label" : { + "name" : "{internal}" + } }, + { "id" : "f12", "to" : "f11", "via" : [ { "x" : 480, "y" : 344 } ], "label" : { + "name" : "{external}", + "segment" : 1.1, + "offset" : { "x" : 73, "y" : 1 } + } } + ] + }, { + "id" : "f11", + "type" : "UserTask", + "name" : "Task C", + "config" : { + "task" : { + "name" : "Task C" + } + }, + "visual" : { + "at" : { "x" : 696, "y" : 344 } + }, + "connect" : [ + { "id" : "f13", "to" : "f7" } + ] + }, { + "id" : "f6", + "type" : "Alternative", + "visual" : { + "at" : { "x" : 808, "y" : 64 } + }, + "connect" : [ + { "id" : "f16", "to" : "f1" } + ] + }, { + "id" : "f17", + "type" : "ProcessAnnotation", + "name" : "green path = default path with empty condition", + "visual" : { + "at" : { "x" : 224, "y" : 135 }, + "size" : { "width" : 294, "height" : 19 }, + "color" : "default path" + } + }, { + "id" : "f18", + "type" : "ProcessAnnotation", + "name" : [ + "useCase=null / flowName = null", + "", + "findAllTasks( start.ivp ) => Task A, Task C, Task B", + "findAllTasks( Task B ) => Task B", + "findAllTasks( Task C ) => Task C, Task B", + "findAllTasks( NewStart ) => Task B", + "", + "findTasksOnPath( start.ivp ) => Task A", + "findTasksOnPath( Task B ) => Task B", + "findTasksOnPath( Task C ) => Task C, Task B", + "findTasksOnPath( NewStart ) => Task B" + ], + "visual" : { + "at" : { "x" : 235, "y" : 543 }, + "size" : { "width" : 333, "height" : 239 } + } + }, { + "id" : "f19", + "type" : "ProcessAnnotation", + "name" : [ + "useCase=null / flowName = internal", + "", + "findAllTasks( start.ivp ) => Task A, Task C, Task B", + "findTasksOnPath( start.ivp ) => Task A, Task B", + "findTasksOnPath( NewStart ) => Task B", + "", + "----", + "", + "useCase=null / flowName = external", + "", + "findTasksOnPath( start.ivp ) => Task A, Task C, Task B", + "findTasksOnPath( NewStart ) => Task C, Task B", + "", + "----", + "", + "useCase=null / flowName = mixed", + "", + "findTasksOnPath( start.ivp ) => Task A, Task B", + "findTasksOnPath( NewStart ) => Task B" + ], + "visual" : { + "at" : { "x" : 595, "y" : 580 }, + "size" : { "width" : 334, "height" : 312 } + } + }, { + "id" : "f20", + "type" : "RequestStart", + "name" : "NewStart", + "config" : { + "signature" : "NewStart" + }, + "visual" : { + "at" : { "x" : 96, "y" : 224 } + }, + "connect" : [ + { "id" : "f21", "to" : "f8" } + ] + }, { + "id" : "f22", + "type" : "ProcessAnnotation", + "name" : [ + "Note about difference of element name (shown in the BPMN diagram) and the task name!", + "", + "For Task A, I set both fields to different values. In the result data, we want to see the task name, not process element name." + ], + "visual" : { + "at" : { "x" : 279, "y" : 327 }, + "size" : { "width" : 251, "height" : 127 } + } + } ], + "layout" : { + "colors" : { + "default path" : "#06f416" + } + } +} \ No newline at end of file diff --git a/workflow-estimator-test/processes/FlowExampleError.p.json b/workflow-estimator-test/processes/FlowExampleError.p.json new file mode 100644 index 00000000..c49aac63 --- /dev/null +++ b/workflow-estimator-test/processes/FlowExampleError.p.json @@ -0,0 +1,110 @@ +{ + "$schema" : "https://json-schema.axonivy.com/process/11.2.2/process.json", + "id" : "18DD17872B3D53E2", + "config" : { + "data" : "com.axonivy.utils.estimator.test.Data" + }, + "elements" : [ { + "id" : "f0", + "type" : "RequestStart", + "name" : "start", + "config" : { + "signature" : "start" + }, + "visual" : { + "at" : { "x" : 96, "y" : 64 } + }, + "connect" : [ + { "id" : "f2", "to" : "f5" } + ] + }, { + "id" : "f1", + "type" : "TaskEnd", + "visual" : { + "at" : { "x" : 936, "y" : 64 } + } + }, { + "id" : "f3", + "type" : "UserTask", + "name" : "Task A", + "visual" : { + "at" : { "x" : 472, "y" : 64 } + }, + "connect" : [ + { "id" : "f4", "to" : "f7" } + ] + }, { + "id" : "f5", + "type" : "Alternative", + "visual" : { + "at" : { "x" : 264, "y" : 64 } + }, + "connect" : [ + { "id" : "f6", "to" : "f3" } + ] + }, { + "id" : "f7", + "type" : "Alternative", + "config" : { + "conditions" : { + "f8" : "true" + } + }, + "visual" : { + "at" : { "x" : 712, "y" : 64 } + }, + "connect" : [ + { "id" : "f8", "to" : "f1", "label" : { + "name" : "{success}" + } }, + { "id" : "f10", "to" : "f9", "via" : [ { "x" : 712, "y" : 184 } ], "color" : "default path", "label" : { + "name" : "cancel path", + "segment" : 1.19, + "offset" : { "x" : 27, "y" : -37 } + } } + ] + }, { + "id" : "f9", + "type" : "UserTask", + "name" : "Task B", + "visual" : { + "at" : { "x" : 472, "y" : 184 } + }, + "connect" : [ + { "id" : "f11", "to" : "f5", "via" : [ { "x" : 264, "y" : 184 } ] } + ] + }, { + "id" : "f17", + "type" : "ProcessAnnotation", + "name" : "green path = default path with empty condition", + "visual" : { + "at" : { "x" : 920, "y" : 119 }, + "size" : { "width" : 294, "height" : 19 }, + "color" : "default path" + } + }, { + "id" : "f19", + "type" : "ProcessAnnotation", + "name" : [ + "useCase=null / flowName = null", + "", + "findAllTasks( start.ivp ) => Task A, Task B", + "findTasksOnPath( start.ivp ) => Task A, Task B", + "", + "----", + "", + "useCase=null / flowName = success", + "", + "findTasksOnPath( start.ivp ) => Task A" + ], + "visual" : { + "at" : { "x" : 231, "y" : 304 }, + "size" : { "width" : 299, "height" : 166 } + } + } ], + "layout" : { + "colors" : { + "default path" : "#12f202" + } + } +} \ No newline at end of file diff --git a/workflow-estimator-test/processes/FlowExampleLoop.p.json b/workflow-estimator-test/processes/FlowExampleLoop.p.json new file mode 100644 index 00000000..61c71423 --- /dev/null +++ b/workflow-estimator-test/processes/FlowExampleLoop.p.json @@ -0,0 +1,83 @@ +{ + "$schema" : "https://json-schema.axonivy.com/process/11.2.2/process.json", + "id" : "18DD16F8AA39F5DE", + "config" : { + "data" : "com.axonivy.utils.estimator.test.Data" + }, + "elements" : [ { + "id" : "f0", + "type" : "RequestStart", + "name" : "start", + "config" : { + "signature" : "start" + }, + "visual" : { + "at" : { "x" : 96, "y" : 64 } + }, + "connect" : [ + { "id" : "f2", "to" : "f3" } + ] + }, { + "id" : "f1", + "type" : "TaskEnd", + "visual" : { + "at" : { "x" : 672, "y" : 64 } + } + }, { + "id" : "f3", + "type" : "UserTask", + "name" : "Task A", + "visual" : { + "at" : { "x" : 256, "y" : 64 } + }, + "connect" : [ + { "id" : "f4", "to" : "f7" } + ] + }, { + "id" : "f7", + "type" : "Alternative", + "config" : { + "conditions" : { + "f8" : "true", + "f10" : "true" + } + }, + "visual" : { + "at" : { "x" : 448, "y" : 64 } + }, + "connect" : [ + { "id" : "f8", "to" : "f1", "label" : { + "name" : "{success}" + } }, + { "id" : "f10", "to" : "f9", "via" : [ { "x" : 448, "y" : 184 } ] } + ] + }, { + "id" : "f9", + "type" : "UserTask", + "name" : "Task B", + "visual" : { + "at" : { "x" : 552, "y" : 184 } + }, + "connect" : [ + { "id" : "f11", "to" : "f1", "via" : [ { "x" : 672, "y" : 184 } ] } + ] + }, { + "id" : "f19", + "type" : "ProcessAnnotation", + "name" : [ + "useCase=null / flowName = null", + "", + "findTasksOnPath( start.ivp ) => Exception", + "", + "----", + "", + "useCase=null / flowName = success", + "", + "findTasksOnPath( start.ivp ) => Task A" + ], + "visual" : { + "at" : { "x" : 231, "y" : 256 }, + "size" : { "width" : 299, "height" : 166 } + } + } ] +} \ No newline at end of file diff --git a/workflow-estimator-test/processes/MainTest.p.json b/workflow-estimator-test/processes/MainTest.p.json deleted file mode 100644 index cb89c1ba..00000000 --- a/workflow-estimator-test/processes/MainTest.p.json +++ /dev/null @@ -1,90 +0,0 @@ -{ - "format" : "10.0.0", - "id" : "18DC44E096FDFF75", - "config" : { - "data" : "com.axonivy.utils.estimator.test.Data" - }, - "elements" : [ { - "id" : "f0", - "type" : "RequestStart", - "name" : "start.ivp", - "config" : { - "callSignature" : "start", - "outLink" : "start.ivp" - }, - "visual" : { - "at" : { "x" : 96, "y" : 64 } - }, - "connect" : { "id" : "f3", "to" : "f2" } - }, { - "id" : "f1", - "type" : "TaskEnd", - "visual" : { - "at" : { "x" : 944, "y" : 64 } - } - }, { - "id" : "f2", - "type" : "UserTask", - "name" : "Task A", - "visual" : { - "at" : { "x" : 288, "y" : 64 } - }, - "connect" : { "id" : "f5", "to" : "f4" } - }, { - "id" : "f4", - "type" : "Alternative", - "visual" : { - "at" : { "x" : 480, "y" : 64 } - }, - "connect" : [ - { "id" : "f9", "to" : "f8", "label" : { - "name" : [ - "{internal}", - "{external}" - ] - } }, - { "id" : "f14", "to" : "f6" } - ] - }, { - "id" : "f7", - "type" : "UserTask", - "name" : "Task B", - "visual" : { - "at" : { "x" : 696, "y" : 224 } - }, - "connect" : { "id" : "f15", "to" : "f6", "via" : [ { "x" : 808, "y" : 224 } ] } - }, { - "id" : "f8", - "type" : "Alternative", - "visual" : { - "at" : { "x" : 480, "y" : 224 } - }, - "connect" : [ - { "id" : "f10", "to" : "f7", "label" : { - "name" : "{internal}" - } }, - { "id" : "f12", "to" : "f11", "via" : [ { "x" : 480, "y" : 344 } ], "label" : { - "name" : "{external}", - "segment" : 1.1, - "offset" : { "x" : 73, "y" : -2 } - } } - ] - }, { - "id" : "f11", - "type" : "UserTask", - "name" : "Task C", - "visual" : { - "at" : { "x" : 696, "y" : 344 } - }, - "connect" : { "id" : "f13", "to" : "f7" } - }, { - "id" : "f6", - "type" : "Alternative", - "visual" : { - "at" : { "x" : 808, "y" : 64 } - }, - "connect" : [ - { "id" : "f16", "to" : "f1" } - ] - } ] -} \ No newline at end of file diff --git a/workflow-estimator-test/processes/ParallelTasksExample.p.json b/workflow-estimator-test/processes/ParallelTasksExample.p.json new file mode 100644 index 00000000..69b92fc8 --- /dev/null +++ b/workflow-estimator-test/processes/ParallelTasksExample.p.json @@ -0,0 +1,176 @@ +{ + "$schema" : "https://json-schema.axonivy.com/process/11.2.2/process.json", + "id" : "18DD185B60B6E769", + "config" : { + "data" : "com.axonivy.utils.estimator.test.Data" + }, + "elements" : [ { + "id" : "f0", + "type" : "RequestStart", + "name" : "start", + "config" : { + "signature" : "start" + }, + "visual" : { + "at" : { "x" : 96, "y" : 96 } + }, + "connect" : [ + { "id" : "f3", "to" : "f2", "var" : "in1" } + ] + }, { + "id" : "f1", + "type" : "TaskEnd", + "visual" : { + "at" : { "x" : 1264, "y" : 96 } + } + }, { + "id" : "f2", + "type" : "TaskSwitchGateway", + "name" : "Task1", + "config" : { + "tasks" : [ { + "id" : "TaskA" + }, { + "id" : "TaskB", + "name" : "Task1B" + } ] + }, + "visual" : { + "at" : { "x" : 240, "y" : 96 }, + "labelOffset" : { "x" : 14, "y" : 34 } + }, + "connect" : [ + { "id" : "f5", "to" : "f4", "condition" : "ivp==\"TaskA.ivp\"", "var" : "in1" }, + { "id" : "f8", "to" : "f7", "condition" : "ivp==\"TaskB.ivp\"" } + ] + }, { + "id" : "f4", + "type" : "TaskSwitchGateway", + "name" : "Join", + "config" : { + "tasks" : [ { + "id" : "TaskA", + "name" : "JoiningTask", + "responsible" : { + "activator" : "SYSTEM" + } + } ] + }, + "visual" : { + "at" : { "x" : 1056, "y" : 96 }, + "labelOffset" : { "x" : 14, "y" : 34 } + }, + "connect" : [ + { "id" : "f6", "to" : "f1", "condition" : "ivp==\"TaskA.ivp\"" } + ] + }, { + "id" : "f7", + "type" : "TaskSwitchEvent", + "name" : "Task2", + "visual" : { + "at" : { "x" : 312, "y" : 168 }, + "labelOffset" : { "x" : 14, "y" : 34 } + }, + "connect" : [ + { "id" : "f9", "to" : "f10" } + ] + }, { + "id" : "f10", + "type" : "Alternative", + "config" : { + "conditions" : { + "f14" : "true" + } + }, + "visual" : { + "at" : { "x" : 480, "y" : 168 } + }, + "connect" : [ + { "id" : "f14", "to" : "f4", "label" : { + "name" : "{shortcut}" + }, "var" : "in3" }, + { "id" : "f11", "to" : "f15", "color" : "def", "var" : "in2" } + ] + }, { + "id" : "f15", + "type" : "TaskSwitchGateway", + "name" : "Task3", + "config" : { + "tasks" : [ { + "id" : "TaskA", + "name" : "Task3A" + }, { + "id" : "TaskB", + "name" : "Task3B" + } ], + "output" : { + "map" : { + "out" : "in2" + } + } + }, + "visual" : { + "at" : { "x" : 480, "y" : 296 }, + "labelOffset" : { "x" : 14, "y" : 34 } + }, + "connect" : [ + { "id" : "f12", "to" : "f4", "condition" : "ivp==\"TaskA.ivp\"", "var" : "in2" }, + { "id" : "f16", "to" : "f13", "condition" : "ivp==\"TaskB.ivp\"" } + ] + }, { + "id" : "f13", + "type" : "Script", + "visual" : { + "at" : { "x" : 840, "y" : 296 } + }, + "connect" : [ + { "id" : "f17", "to" : "f4", "via" : [ { "x" : 1056, "y" : 296 } ], "var" : "in4" } + ] + }, { + "id" : "f18", + "type" : "ProcessAnnotation", + "name" : "green path = default path with empty condition", + "visual" : { + "at" : { "x" : 288, "y" : 231 }, + "size" : { "width" : 294, "height" : 19 }, + "color" : "def" + } + }, { + "id" : "f19", + "type" : "ProcessAnnotation", + "name" : [ + "useCase=null / flowName = null", + "", + "findAllTasks( start.ivp ) => Task1A, Task1B, Task2, Task3A, Task3B", + "findTasksOnPath( start.ivp ) => Task1A, Task1B, Task2, Task3A, Task3B", + "", + "----", + "", + "useCase=null / flowName = shortcut", + "", + "findTasksOnPath( start.ivp ) => Task1A, Task1B, Task2" + ], + "visual" : { + "at" : { "x" : 376, "y" : 463 }, + "size" : { "width" : 504, "height" : 163 } + } + }, { + "id" : "f20", + "type" : "ProcessAnnotation", + "name" : [ + "Notes:", + "Order of parallel task is not important. We don't know wheter Task1A or Task1B is followed first. But we need to calculate both when it comes to duration estimations.", + "", + "Tasks assigned to SYSTEM must be ignored. We don't handle them! For example the SYSTEM task of the join element will not show up in any result." + ], + "visual" : { + "at" : { "x" : 853, "y" : 461 }, + "size" : { "width" : 407, "height" : 158 } + } + } ], + "layout" : { + "colors" : { + "def" : "#2ff906" + } + } +} \ No newline at end of file diff --git a/workflow-estimator-test/processes/TaskTypesExample.p.json b/workflow-estimator-test/processes/TaskTypesExample.p.json new file mode 100644 index 00000000..3db0b9d9 --- /dev/null +++ b/workflow-estimator-test/processes/TaskTypesExample.p.json @@ -0,0 +1,108 @@ +{ + "$schema" : "https://json-schema.axonivy.com/process/11.2.2/process.json", + "id" : "18DD180E9D5D26F4", + "config" : { + "data" : "com.axonivy.utils.estimator.test.Data" + }, + "elements" : [ { + "id" : "f0", + "type" : "RequestStart", + "name" : "start", + "config" : { + "signature" : "start" + }, + "visual" : { + "at" : { "x" : 96, "y" : 64 } + }, + "connect" : [ + { "id" : "f2", "to" : "f3" } + ] + }, { + "id" : "f1", + "type" : "TaskEnd", + "visual" : { + "at" : { "x" : 1056, "y" : 64 } + } + }, { + "id" : "f3", + "type" : "DialogCall", + "name" : "UserDialog", + "visual" : { + "at" : { "x" : 224, "y" : 64 } + }, + "connect" : [ + { "id" : "f4", "to" : "f5" } + ] + }, { + "id" : "f5", + "type" : "UserTask", + "name" : "UserTask", + "visual" : { + "at" : { "x" : 384, "y" : 64 } + }, + "connect" : [ + { "id" : "f6", "to" : "f7" } + ] + }, { + "id" : "f7", + "type" : "TaskSwitchEvent", + "name" : "Task", + "visual" : { + "at" : { "x" : 512, "y" : 64 }, + "labelOffset" : { "x" : 14, "y" : 34 } + }, + "connect" : [ + { "id" : "f8", "to" : "f13" } + ] + }, { + "id" : "f11", + "type" : "TaskSwitchGateway", + "name" : "Tasks", + "config" : { + "tasks" : [ { + "id" : "TaskA", + "name" : "Tasks-Task1" + }, { + "id" : "TaskB", + "name" : "Tasks-TaskB" + } ] + }, + "visual" : { + "at" : { "x" : 864, "y" : 64 }, + "labelOffset" : { "x" : 14, "y" : 34 } + }, + "connect" : [ + { "id" : "f12", "to" : "f1", "condition" : "ivp==\"TaskA.ivp\"" }, + { "id" : "f16", "to" : "f15", "condition" : "ivp==\"TaskB.ivp\"" } + ] + }, { + "id" : "f13", + "type" : "Script", + "name" : "Script", + "visual" : { + "at" : { "x" : 640, "y" : 64 } + }, + "connect" : [ + { "id" : "f9", "to" : "f11", "var" : "in1" } + ] + }, { + "id" : "f18", + "type" : "ProcessAnnotation", + "name" : [ + "useCase=null / flowName = null", + "", + "findAllTasks( start.ivp ) => UserTask, Task, Tasks-TaskA, Tasks-TaskB", + "findTasksOnPath( start.ivp ) => UserTask, Task, Tasks-TaskA, Tasks-TaskB" + ], + "visual" : { + "at" : { "x" : 337, "y" : 172 }, + "size" : { "width" : 512, "height" : 88 } + } + }, { + "id" : "f15", + "type" : "TaskEnd", + "visual" : { + "at" : { "x" : 1056, "y" : 168 } + } + } ] +} \ No newline at end of file diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/WorkflowEstimatorTest.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleBasicTest.java similarity index 93% rename from workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/WorkflowEstimatorTest.java rename to workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleBasicTest.java index 6e09e7dd..2fc8af18 100644 --- a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/WorkflowEstimatorTest.java +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleBasicTest.java @@ -14,7 +14,7 @@ @IvyTest @SuppressWarnings("restriction") -public class WorkflowEstimatorTest { +public class FlowExampleBasicTest { private Process process; private ProcessGraph graph; @@ -23,7 +23,7 @@ public class WorkflowEstimatorTest { void setup() { var pmv = Ivy.request().getProcessModelVersion(); var manager = IProcessManager.instance().getProjectDataModelFor(pmv); - this.process = manager.findProcessByPath("MainTest").getModel(); + this.process = manager.findProcessByPath("FlowExampleBasic").getModel(); this.graph = new ProcessGraph(process); } From 694001439306660b45446dfeb179096be48bbf4c Mon Sep 17 00:00:00 2001 From: "AAVN\\ntnchuong" Date: Fri, 23 Feb 2024 17:38:52 +0700 Subject: [PATCH 017/143] TE-538: calculate estimated time --- workflow-estimator-test/.project | 6 +- .../processes/FlowExampleBasic.p.json | 21 ++++- workflow-estimator-test/src/UseCase.java | 4 + workflow-estimator-test/src/WfEstimate.java | 7 ++ .../estimator/test/FlowExampleBasicTest.java | 11 +++ .../utils/estimator/test/ProcessGraph.java | 9 +++ .../utils/estimator/WorkflowEstimator.java | 76 +++++++++++++++++-- .../utils/estimator/model/EstimatedTask.java | 33 ++++++++ 8 files changed, 156 insertions(+), 11 deletions(-) create mode 100644 workflow-estimator-test/src/UseCase.java create mode 100644 workflow-estimator-test/src/WfEstimate.java diff --git a/workflow-estimator-test/.project b/workflow-estimator-test/.project index 30664fe8..7f7cb48d 100644 --- a/workflow-estimator-test/.project +++ b/workflow-estimator-test/.project @@ -31,17 +31,17 @@ - org.eclipse.m2e.core.maven2Builder + org.eclipse.pde.ManifestBuilder - org.eclipse.pde.ManifestBuilder + org.eclipse.pde.SchemaBuilder - org.eclipse.pde.SchemaBuilder + org.eclipse.m2e.core.maven2Builder diff --git a/workflow-estimator-test/processes/FlowExampleBasic.p.json b/workflow-estimator-test/processes/FlowExampleBasic.p.json index 9aad0235..42d623d5 100644 --- a/workflow-estimator-test/processes/FlowExampleBasic.p.json +++ b/workflow-estimator-test/processes/FlowExampleBasic.p.json @@ -32,7 +32,12 @@ ], "config" : { "task" : { - "name" : "Task A" + "name" : "Task A", + "code" : [ + "import java.util.concurrent.TimeUnit;", + "", + "WfEstimate.setEstimate(5,TimeUnit.HOURS,UseCase.BIGPROJECT);" + ] } }, "visual" : { @@ -68,7 +73,12 @@ "name" : "Task B", "config" : { "task" : { - "name" : "Task B" + "name" : "Task B", + "code" : [ + "import java.util.concurrent.TimeUnit;", + "", + "WfEstimate.setEstimate(6,TimeUnit.HOURS,UseCase.BIGPROJECT);" + ] } }, "visual" : { @@ -107,7 +117,12 @@ "name" : "Task C", "config" : { "task" : { - "name" : "Task C" + "name" : "Task C", + "code" : [ + "import java.util.concurrent.TimeUnit;", + "", + "WfEstimate.setEstimate(4,TimeUnit.HOURS,UseCase.BIGPROJECT);" + ] } }, "visual" : { diff --git a/workflow-estimator-test/src/UseCase.java b/workflow-estimator-test/src/UseCase.java new file mode 100644 index 00000000..c7dbf7d4 --- /dev/null +++ b/workflow-estimator-test/src/UseCase.java @@ -0,0 +1,4 @@ + +public enum UseCase { + BIGPROJECT, SMALLPROJECT; +} diff --git a/workflow-estimator-test/src/WfEstimate.java b/workflow-estimator-test/src/WfEstimate.java new file mode 100644 index 00000000..ff1b99de --- /dev/null +++ b/workflow-estimator-test/src/WfEstimate.java @@ -0,0 +1,7 @@ +import java.util.concurrent.TimeUnit; + +public class WfEstimate { + + public static void setEstimate(int amound, TimeUnit unit, UseCase useCase) {}; + +} diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleBasicTest.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleBasicTest.java index 2fc8af18..f7e94739 100644 --- a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleBasicTest.java +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleBasicTest.java @@ -2,6 +2,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; +import java.time.Duration; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -10,6 +12,8 @@ import ch.ivyteam.ivy.environment.Ivy; import ch.ivyteam.ivy.environment.IvyTest; import ch.ivyteam.ivy.process.model.Process; +import ch.ivyteam.ivy.process.model.element.SingleTaskCreator; +import ch.ivyteam.ivy.process.model.element.value.task.TaskConfig; import ch.ivyteam.ivy.process.rdm.IProcessManager; @IvyTest @@ -80,4 +84,11 @@ void shouldFindAllTasksOfExternalFlow() { assertEquals("Task C", estimatedTasks.get(1).getTaskName()); assertEquals("Task B", estimatedTasks.get(2).getTaskName()); } + + @Test + void shouldCalculateTotalDuration() { + var workflowEstimator = new WorkflowEstimator(process, null, null); + Duration duration = workflowEstimator.calculateEstimatedDuration(graph.findStart()); + assertEquals(15, duration.toHours()); + } } diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/ProcessGraph.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/ProcessGraph.java index 5489d13d..074c95a6 100644 --- a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/ProcessGraph.java +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/ProcessGraph.java @@ -2,6 +2,7 @@ import ch.ivyteam.ivy.process.model.BaseElement; import ch.ivyteam.ivy.process.model.Process; +import ch.ivyteam.ivy.process.model.element.SingleTaskCreator; import ch.ivyteam.ivy.process.model.element.event.start.RequestStart; @SuppressWarnings("restriction") @@ -22,4 +23,12 @@ public BaseElement findByElementName(String name) { .findFirst() .orElse(null); } + + public BaseElement findByTaskName(String name) { + return process.getElements().stream() + .filter(el -> {return el instanceof SingleTaskCreator;}) + .filter(el ->((SingleTaskCreator) el).getTaskConfig().getName().getRawMacro().equals(name)) + .findFirst() + .orElse(null); + } } diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java index 20d32c59..2eb71af2 100644 --- a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java +++ b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java @@ -1,12 +1,16 @@ package com.axonivy.utils.estimator; import static java.util.Collections.emptyList; +import static org.apache.commons.lang3.StringUtils.EMPTY; +import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.Date; import java.util.List; +import java.util.Optional; +import java.util.concurrent.TimeUnit; import org.apache.commons.lang3.StringUtils; @@ -17,6 +21,7 @@ import ch.ivyteam.ivy.process.model.Process; import ch.ivyteam.ivy.process.model.connector.SequenceFlow; import ch.ivyteam.ivy.process.model.element.activity.UserTask; +import ch.ivyteam.ivy.process.model.element.value.task.TaskConfig; @SuppressWarnings("restriction") public class WorkflowEstimator { @@ -69,20 +74,48 @@ public List nextNodeElements(NodeElement from) { return nexts.stream().distinct().toList(); } + public Duration calculateEstimatedDuration(BaseElement startElement) { + List> paths = emptyList(); + if (startElement instanceof NodeElement && flowName != null) { + paths = findPaths(startElement, flowName); + } else { + paths = findPaths(startElement); + } + + List estimatedTasks = convertToEstimatedTasks(paths); + Duration total = Duration.ofHours(0); + for(EstimatedTask item : estimatedTasks) { + total = total.plus(item.getEstimatedDuration()); + } + return total; + } + private List> findPaths(BaseElement from) { return findPaths(from, null); } private List convertToEstimatedTasks(List> paths) { - return paths.stream() + List tasks = paths.stream() // If have more than one path, we get the longest .max(Comparator.comparingInt(List::size)).orElse(emptyList()).stream() // filter to get task only .filter(node -> { return node instanceof UserTask; }) + .map(UserTask.class::cast) // filter the task which have estimated if needed - .map(task -> createEstimatedTask((UserTask) task)).toList(); + .toList(); + //Convert to EstimatedTask + List estimatedTasks = new ArrayList<>(); + for(int i = 0; i < tasks.size(); i ++) { + EstimatedTask estimatedTask = new EstimatedTask(); + Date startTimestamp = i == 0 ? new Date() : estimatedTasks.get(i - 1).calculateEstimatedEndTimestamp(); + estimatedTask = createEstimatedTask(tasks.get(i), startTimestamp); + + estimatedTasks.add(estimatedTask); + } + + return estimatedTasks; } private List> findPaths(BaseElement from, String flowName) { @@ -146,12 +179,45 @@ private boolean hasFlowName(List elements, String flowName) { return existsFlowName; } - private EstimatedTask createEstimatedTask(UserTask task) { + private EstimatedTask createEstimatedTask(UserTask task, Date startTimestamp) { EstimatedTask estimatedTask = new EstimatedTask(); estimatedTask.setPid(task.getPid().getRawPid()); - estimatedTask.setTaskName(task.getName()); - estimatedTask.setEstimatedStartTimestamp(new Date()); + estimatedTask.setTaskName(task.getTaskConfig().getName().getRawMacro()); + estimatedTask.setParentElementNames(emptyList()); + + Duration estimatedDuration = getDurationByCode(task.getTaskConfig()); + estimatedTask.setEstimatedDuration(estimatedDuration); + estimatedTask.setEstimatedStartTimestamp(startTimestamp); return estimatedTask; } + + private Duration getDurationByCode(TaskConfig task) { + // strongly typed! + String script = Optional.of(task.getScript()).orElse(EMPTY); + String[] codeLines = script.split("\\n"); + String wfEstimateCode = Arrays.stream(codeLines) + .filter(line -> line.contains("WfEstimate.")) + .findFirst() + .orElse(EMPTY); + + if (StringUtils.isNotEmpty(wfEstimateCode)) { + String result = StringUtils.substringBetween(script, "(", "Use"); + int amount = Integer.parseInt(result.substring(0, result.indexOf(","))); + String unit = result.substring(result.indexOf(".") + 1, result.lastIndexOf(",")); + + if(TimeUnit.DAYS.toString().equals(unit)) { + return Duration.ofDays(amount); + } else if (TimeUnit.HOURS.toString().equals(unit)) { + return Duration.ofHours(amount); + } else if(TimeUnit.MINUTES.toString().equals(unit)) { + return Duration.ofMinutes(amount); + } else if (TimeUnit.SECONDS.toString().equals(unit)) { + return Duration.ofSeconds(amount); + } + } + + return Duration.ofHours(0); + } + } diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/model/EstimatedTask.java b/workflow-estimator/src/com/axonivy/utils/estimator/model/EstimatedTask.java index 72d52097..ddf1cf0d 100644 --- a/workflow-estimator/src/com/axonivy/utils/estimator/model/EstimatedTask.java +++ b/workflow-estimator/src/com/axonivy/utils/estimator/model/EstimatedTask.java @@ -1,10 +1,18 @@ package com.axonivy.utils.estimator.model; +import java.time.Duration; import java.util.Date; +import java.util.List; +import java.util.Optional; + +import org.apache.commons.lang3.time.DateUtils; public class EstimatedTask { + private String pid; private String taskName; + private Duration estimatedDuration; + private List parentElementNames; private Date estimatedStartTimestamp; public String getPid() { @@ -30,4 +38,29 @@ public Date getEstimatedStartTimestamp() { public void setEstimatedStartTimestamp(Date estimatedStartTimestamp) { this.estimatedStartTimestamp = estimatedStartTimestamp; } + + public Duration getEstimatedDuration() { + return estimatedDuration; + } + + public void setEstimatedDuration(Duration estimatedDuration) { + this.estimatedDuration = estimatedDuration; + } + + public List getParentElementNames() { + return parentElementNames; + } + + public void setParentElementNames(List parentElementNames) { + this.parentElementNames = parentElementNames; + } + + public Date calculateEstimatedEndTimestamp() { + int durationInSecond = Optional.ofNullable(this.estimatedDuration) + .map(Duration::getSeconds) + .map(se -> Long.valueOf(se).intValue()) + .orElse(0); + + return DateUtils.addSeconds(this.estimatedStartTimestamp, durationInSecond); + } } From 6414ba1632b91b5787106fe394206653956a43e9 Mon Sep 17 00:00:00 2001 From: Trung Mai Date: Fri, 23 Feb 2024 10:58:17 +0700 Subject: [PATCH 018/143] TE-538: Update unit test for flow example basic --- ...imatorTest-classpath-arg-1708398410117.txt | 1 + ...imatorTest-classpath-arg-1708399434099.txt | 1 + .../processes/FlowExampleBasic.p.json | 14 +-- .../estimator/test/FlowExampleBasicTest.java | 119 +++++++++++++----- .../axonivy/utils/estimator/ProcessGraph.java | 56 +++++++++ .../utils/estimator/WorkflowEstimator.java | 47 +++++-- 6 files changed, 189 insertions(+), 49 deletions(-) create mode 100644 workflow-estimator-test/.temp-WorkflowEstimatorTest-classpath-arg-1708398410117.txt create mode 100644 workflow-estimator-test/.temp-WorkflowEstimatorTest-classpath-arg-1708399434099.txt create mode 100644 workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java diff --git a/workflow-estimator-test/.temp-WorkflowEstimatorTest-classpath-arg-1708398410117.txt b/workflow-estimator-test/.temp-WorkflowEstimatorTest-classpath-arg-1708398410117.txt new file mode 100644 index 00000000..510323be --- /dev/null +++ b/workflow-estimator-test/.temp-WorkflowEstimatorTest-classpath-arg-1708398410117.txt @@ -0,0 +1 @@ +-classpath D:\AxonIvy\IVYMARKET\market-workflow-estimator\workflow-estimator-test\target\test-classes;D:\AxonIvy\IVYMARKET\market-workflow-estimator\workflow-estimator-test\target\classes;C:\Users\mhvtrung\AppData\Local\Temp\ivyRtProps10849105986333281056;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jersey_2.31.0.202208100658\lib\jersey-media-multipart.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jersey_2.31.0.202208100658\lib\jersey-server.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.equinox.common_3.16.0.v20220211-2322.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.core.resources_3.16.100.v20220214-1012.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.core.jobs_3.12.100.v20220120-1329.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.logging_2.19.0.202311141127\lib\lib-logging-config.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.logging_2.19.0.202311141127\lib\log4j-1.2-api-2.19.0.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.logging_2.19.0.202311141127\lib\log4j-api-2.19.0.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.logging_2.19.0.202311141127\lib\log4j-core-2.19.0.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.logging_2.19.0.202311141127\lib\log4j-slf4j-impl-2.19.0.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.logging_2.19.0.202311141127\lib\commons-logging-1.2.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.logging_2.19.0.202311141127\lib\slf4j-api-1.7.36.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.logging_2.19.0.202311141127\lib\jul-to-slf4j-1.7.36.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.logging_2.19.0.202311141127\lib\jcl-over-slf4j-1.7.36.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.logging_2.19.0.202311141127\lib\jboss-logging-3.5.0.Final.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.poi_4.1.2.202104060804\lib\poi-4.1.2.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.poi_4.1.2.202104060804\lib\poi-ooxml-4.1.2.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.poi_4.1.2.202104060804\lib\poi-ooxml-schemas-4.1.2.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.poi_4.1.2.202104060804\lib\xmlbeans-3.1.0.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.db_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.application_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.util_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.util.api_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.util.di_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.util.log_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jaxb_2.3.5.202312071357\lib\jakarta.xml.bind-api.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jaxws_2.3.1.202112291433\lib\jaxws-api-2.3.1.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jaxws_2.3.1.202112291433\lib\javax.xml.soap-api-1.4.0.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jaxws_2.3.1.202112291433\lib\javax.jws-api-1.1.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.scripting_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.scripting.objects_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.process.event_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.service_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.request_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.security_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.security.identity.jndi_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.bpm.exec_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.workflow_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.persistence_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.htmldialog_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.dialog.exec_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.server_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.cm_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.cm.exec_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.base_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.engine.migration_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.deployment_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.hibernate.entitymanager_5.4.33.202208031448\lib\hibernate-commons-annotations-5.1.2.Final.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.hibernate.entitymanager_5.4.33.202208031448\lib\hibernate-core-5.4.33.Final.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.hibernate.entitymanager_5.4.33.202208031448\lib\hibernate-entitymanager-5.4.33.Final.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.hibernate.entitymanager_5.4.33.202208031448\lib\jboss-transaction-api_1.2_spec-1.1.1.Final.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.hibernate.validator_6.2.5.202210070940\lib\hibernate-validator.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.database.exec_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.mail_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.config_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.configuration_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.cluster_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.webservice.configuration_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.webservice.execution_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.webservice.process_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jackson_2.13.5.202312280855\lib\jackson-annotations-2.13.5.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jackson_2.13.5.202312280855\lib\jackson-module-jaxb-annotations-2.13.5.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jackson_2.13.5.202312280855\lib\jackson-core-2.13.5.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jackson_2.13.5.202312280855\lib\jackson-databind-2.13.5.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jackson_2.13.5.202312280855\lib\jackson-datatype-jsr310-2.13.5.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jackson_2.13.5.202312280855\lib\jackson-jaxrs-json-provider-2.13.5.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jackson_2.13.5.202312280855\lib\jackson-jaxrs-base-2.13.5.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.data.cache_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.process.data.persistence_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.process.data.persistence.config_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.webserver_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\configuration\org.eclipse.osgi\100\0\.cp\lib\cos.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.project_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.resource.datamodel_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.runtime.env_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.components_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.pagedesigner.extensions_10.0.0.202401072231.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.bpm.engine_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.casemap.exec_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.trace_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.apache.commons.beanutils_1.9.4.202103091318\lib\commons-beanutils-1.9.4.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.apache.commons.codec_1.15.0.202103091318\lib\commons-codec-1.15.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.apache.commons.collections4_4.4.0.202103091318\lib\commons-collections4-4.4.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.apache.commons.compress_1.21.0.202110280623\lib\commons-compress-1.21.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.apache.commons.digester_2.1.0.202103091318\lib\commons-digester-2.1.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.apache.commons.fileupload_1.5.0.202312280855\lib\commons-fileupload-1.5.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.apache.commons.io_2.11.0.202110280624\lib\commons-io-2.11.0.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.apache.commons.lang3_3.12.0.202110280631\lib\commons-lang3-3.12.0.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.apache.commons.legacy.collections_3.2.2.202105101833\lib\commons-collections-3.2.2.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.apache.commons.legacy.lang_2.6.0.202103091318\lib\commons-lang-2.6.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.apache.httpclient_4.5.15.202112301615\lib\httpclient-4.5.13.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.apache.httpclient_4.5.15.202112301615\lib\httpcore-4.4.15.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.apache.myfaces_2.2.15.202206210954\lib\myfaces-api-2.2.15.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.ivymx_1.2.3.202204141308\lib\ivymx-1.2.3.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.javassist_3.29.2.202210071228\lib\javassist.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.swagger.annotations_2.2.9.202305101145\lib\swagger-annotations.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\com.google.inject_5.1.0.202208030917\lib\aopalliance-1.0.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\com.google.inject_5.1.0.202208030917\lib\guice-5.1.0.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\com.google.inject_5.1.0.202208030917\lib\guice-servlet-5.1.0.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\com.google.gson_2.8.9.202201180655\lib\gson.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\com.google.guava_30.1.1.202110280801\lib\guava-31.0.1-jre.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\com.google.guava_30.1.1.202110280801\lib\failureaccess-1.0.1.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\javax.activation_1.2.2.202112291433\lib\jakarta.activation.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\javax.annotation_1.3.5.v20200909-1856.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\javax.el_3.0.0.202112291433\lib\tomcat-el-api.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\javax.inject_1.0.0.v20091030.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\javax.mail_1.6.7.202112301623\lib\jakarta.mail-1.6.7.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\javax.persistence_2.2.3.202201241317\lib\jakarta.persistence-api.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\javax.servlet_4.0.0.202112291433\lib\tomcat-servlet-api.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\javax.servlet.jsp_2.3.0.202112291433\lib\tomcat-jsp-api.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\javax.validation_2.0.2.202112291433\lib\jakarta.validation-api.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\javax.websocket_1.1.2.202112291433\lib\jakarta.websocket-api.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\javax.ws.rs_2.1.6.202112291433\lib\jakarta.ws.rs-api.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\cxf-core.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\cxf-rt-bindings-soap.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\cxf-rt-features-logging.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\cxf-rt-frontend-simple.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\cxf-rt-management.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\cxf-rt-transports-http-hc.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\cxf-rt-transports-http.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\cxf-rt-ws-policy.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\cxf-rt-ws-rm.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.webservice.exec.cxf_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.thirdparty.license_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.rule.engine_10.0.0.202401182231.jar;D:\AxonIvy\IVYMARKET\market-workflow-estimator\workflow-estimator\target\classes;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.osgi_3.17.200.v20220215-2237.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.osgi.classpatcher_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.osgi.compatibility.state_1.2.600.v20220207-1403.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.awt2swt_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.awtExt_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.eclipse.util_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.icons_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.boot.osgi_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.bpmn2_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.casemap_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.cm.search_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.database.adapter_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.database.configuration_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.datawrapper_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.dialog.config_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.dialog.jsf_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.enginemanagerbase_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.extension_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.java_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.jersey.client_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.job_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.library_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.macro_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.page.engine_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.process.config_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.process.search_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.project.conversion_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.reference.base_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.renderer_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.scripting.dataclass_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.scripting.dataclass.search_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.search_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.security.identity.azure_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.server.configuration_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.server.control.center_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.server.exec_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.user.role_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.webservice.client.openapi.codegen_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.apache.commons.math3_3.6.1.202103091318\lib\commons-math3-3.6.1.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.apache.commons.pool2_2.11.1.202110280625\lib\commons-pool2-2.11.1.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.apache.commons.text_1.10.0.202210101240\lib\commons-text-1.10.0.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.apache.httpclient_4.5.15.202112301615\lib\httpcore-nio-4.4.15.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.apache.httpclient_4.5.15.202112301615\lib\httpasyncclient-4.1.5.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.apache.myfaces_2.2.15.202206210954\lib\myfaces-impl-2.2.15.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.classgraph_4.8.149.202208091047\lib\classgraph.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cup_11.0.0.202103091318\lib\java-cup-runtime-11.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\asm.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\cxf-rt-bindings-xml.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\cxf-rt-frontend-jaxws.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\cxf-rt-ws-addr.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\cxf-rt-wsdl.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\neethi.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\saaj-impl.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\stax-ex.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\stax2-api.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\woodstox-core.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\wsdl4j.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\xml-resolver.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\xmlschema-core.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\cxf-rt-databinding-jaxb.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\cxf-rt-ws-security.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\cxf-rt-security.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\wss4j-policy.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\wss4j-ws-security-common.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\wss4j-ws-security-dom.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\xmlsec.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\cxf-rt-security-saml.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.db.hsqldb_2.7.0.202208071122\lib\hsqldb.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.db.mariadb_2.7.6.202208061602\lib\mariadb-java-client.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.db.mssql_11.2.1.202210271255\lib\mssql-jdbc-11.2.1.jre17.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.db.mysql_8.0.30.202208061554\lib\mysql-connector-java.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.db.oracle_21.7.0.202210180645\lib\ojdbc11-21.7.0.0.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.db.oracle_21.7.0.202210180645\lib\orai18n-21.7.0.0.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.db.postgresql_42.5.0.202209121151\lib\postgresql.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.diff_4.12.0.202208091029\lib\java-diff-utils.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.ecs_1.4.2.202103091318\lib\ecs-1.4.2.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.fasterxml.classmate_1.5.1.202201241317\lib\classmate.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.freemarker_2.3.31.202110271200\lib\freemarker-2.3.31.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.hibernate.entitymanager_5.4.33.202208031448\lib\antlr-2.7.7.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.hibernate.entitymanager_5.4.33.202208031448\lib\jandex-2.2.3.Final.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.hibernate.entitymanager_5.4.33.202208031448\lib\dom4j-2.1.3.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.hibernate.entitymanager_5.4.33.202208031448\lib\byte-buddy-1.11.12.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.icons_10.0.0.202210110849\lib\icon.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.icons_10.0.0.202210110849\lib\core-icons.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jackson_2.13.5.202312280855\lib\jackson-dataformat-yaml-2.13.5.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jackson_2.13.5.202312280855\lib\jackson-datatype-jdk8-2.13.5.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jaxb_2.3.5.202312071357\lib\jaxb-runtime.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jaxb_2.3.5.202312071357\lib\istack-commons-runtime.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jaxb_2.3.5.202312071357\lib\txw2.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jersey_2.31.0.202208100658\lib\hk2-api.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jersey_2.31.0.202208100658\lib\hk2-locator.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jersey_2.31.0.202208100658\lib\hk2-utils.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jersey_2.31.0.202208100658\lib\osgi-resource-locator.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jersey_2.31.0.202208100658\lib\jersey-common.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jersey_2.31.0.202208100658\lib\jersey-container-servlet-core.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jersey_2.31.0.202208100658\lib\jersey-container-servlet.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jersey_2.31.0.202208100658\lib\jersey-entity-filtering.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jersey_2.31.0.202208100658\lib\jersey-media-jaxb.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jersey_2.31.0.202208100658\lib\jersey-media-json-jackson.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jersey_2.31.0.202208100658\lib\jersey-client.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jersey_2.31.0.202208100658\lib\guice-bridge.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jersey_2.31.0.202208100658\lib\mimepull.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jersey_2.31.0.202208100658\lib\jersey-apache-connector.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jersey_2.31.0.202208100658\lib\jersey-hk2.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jest_6.3.1.202201180655\lib\jest-6.3.1.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jest_6.3.1.202201180655\lib\jest-common-6.3.1.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jgoodies_2.7.0.202103091318\lib\jgoodies-common-1.8.1.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jgoodies_2.7.0.202103091318\lib\jgoodies-looks-2.7.0.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jgroups_5.2.7.202210101241\lib\jgroups.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jsoup_1.15.3.202209121151\lib\jsoup.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.maven_3.8.6.202208091029\lib\maven-artifact-3.8.6.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.maven_3.8.6.202208091029\lib\maven-model-3.8.6.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.maven_3.8.6.202208091029\lib\plexus-utils-3.3.1.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.mbassador_1.3.2.202103091318\lib\mbassador-1.3.2.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.opentelemetry_1.19.0.202210110958\lib\opentelemetry-api.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.opentelemetry_1.19.0.202210110958\lib\opentelemetry-api-logs.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.opentelemetry_1.19.0.202210110958\lib\opentelemetry-context.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.opentelemetry_1.19.0.202210110958\lib\opentelemetry-sdk-common.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.opentelemetry_1.19.0.202210110958\lib\opentelemetry-sdk-trace.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.opentelemetry_1.19.0.202210110958\lib\opentelemetry-sdk-extension-autoconfigure-spi.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.opentelemetry_1.19.0.202210110958\lib\opentelemetry-semconv.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.picocli_4.6.3.202208090951\lib\picocli.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.snakeyaml_1.33.0.202305101212\lib\snakeyaml.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.spring.crypto_5.7.3.202209121151\lib\spring-security-crypto.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.swagger_2.2.9.202305101145\lib\swagger-core.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.swagger_2.2.9.202305101145\lib\swagger-models.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.swagger.codegen_3.0.42.202305101145\lib\swagger-codegen.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.swagger.codegen_3.0.42.202305101145\lib\swagger-codegen-generators.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.swagger.codegen_3.0.42.202305101145\lib\handlebars.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.swagger.codegen_3.0.42.202305101145\lib\jmustache.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.swagger.jaxrs2_2.2.9.202305101145\lib\swagger-integration.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.swagger.jaxrs2_2.2.9.202305101145\lib\swagger-jaxrs2.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.swagger.parser_3.0.42.202305101145\lib\swagger-core-1.6.10.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.swagger.parser_3.0.42.202305101145\lib\swagger-models-1.6.10.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.swagger.parser_3.0.42.202305101145\lib\swagger-parser-1.0.65.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.swagger.parser_3.0.42.202305101145\lib\swagger-parser-2.1.13.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.swagger.parser_3.0.42.202305101145\lib\swagger-parser-v3-2.1.13.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.swagger.parser_3.0.42.202305101145\lib\swagger-parser-v2-converter-2.1.13.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.swagger.parser_3.0.42.202305101145\lib\swagger-parser-core-2.1.13.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.tamaya_0.3.0.202112291433\lib\tamaya-api-0.3-incubating.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.tamaya_0.3.0.202112291433\lib\tamaya-core-0.3-incubating.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.tamaya_0.3.0.202112291433\lib\tamaya-yaml-0.3-incubating.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.tamaya_0.3.0.202112291433\lib\tamaya-formats-0.3-incubating.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.tamaya_0.3.0.202112291433\lib\tamaya-functions-0.3-incubating.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.tamaya_0.3.0.202112291433\lib\tamaya-resources-0.3-incubating.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.tamaya_0.3.0.202112291433\lib\tamaya-spisupport-0.3-incubating.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.tamaya_0.3.0.202112291433\lib\tamaya-events-0.3-incubating.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.tamaya_0.3.0.202112291433\lib\tamaya-resolver-0.3-incubating.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.swt_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.tomcat_9.0.73.202303161538\lib\tomcat-catalina-ha.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.tomcat_9.0.73.202303161538\lib\tomcat-tribes.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.tomcat_9.0.73.202303161538\lib\tomcat-catalina.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.tomcat_9.0.73.202303161538\lib\tomcat-jasper-el.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.tomcat_9.0.73.202303161538\lib\tomcat-jasper.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.tomcat_9.0.73.202303161538\lib\tomcat-jaspic-api.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.tomcat_9.0.73.202303161538\lib\tomcat-coyote.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.tomcat_9.0.73.202303161538\lib\tomcat-dbcp.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.tomcat_9.0.73.202303161538\lib\tomcat-juli.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.tomcat_9.0.73.202303161538\lib\tomcat-api.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.tomcat_9.0.73.202303161538\lib\tomcat-ssi.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.tomcat_9.0.73.202303161538\lib\tomcat-jdbc.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.tomcat_9.0.73.202303161538\lib\tomcat-jni.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.tomcat_9.0.73.202303161538\lib\tomcat-util.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.tomcat_9.0.73.202303161538\lib\tomcat-util-scan.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.tomcat_9.0.73.202303161538\lib\tomcat-websocket.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.util.bus_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.util.io_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.util.yaml_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.core.commands_3.10.100.v20210722-1426.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.core.contenttype_3.8.100.v20210910-0640.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.core.databinding_1.11.0.v20220118-1028.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.core.databinding.beans_1.8.0.v20210619-1111.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.core.databinding.observable_1.12.0.v20211231-1006.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.core.databinding.property_1.9.0.v20210619-1129.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.core.expressions_3.8.100.v20210910-0640.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.core.externaltools_1.2.200.v20220125-2302.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.core.filebuffers_3.7.200.v20220202-1008.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.core.filesystem_1.9.300.v20220121-1426.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.core.filesystem.win32.x86_64_1.4.200.v20190812-0909.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.core.net_1.3.1100.v20210424-0724.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.core.net.win32.x86_64_1.1.500.v20190925-1337.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.core.resources.win32.x86_64_3.5.400.v20190812-0909.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.core.runtime_3.24.100.v20220211-2001.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.core.variables_3.5.100.v20210721-1355.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.equinox.preferences_3.9.100.v20211021-1418.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.jdt.core_3.29.0.v20220214-1307.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.jdt.core.formatterapp_1.1.0.v20210618-1653.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.jdt.core.manipulation_1.16.0.v20220214-0655.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.osgi.services_3.10.200.v20210723-0643.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.osgi.util_3.6.100.v20210723-1119.jar;D:\tools\AxonIvyDesigner10.0.16\webapps\ivy\WEB-INF\lib\barcode4j-light-2.1.jar;D:\tools\AxonIvyDesigner10.0.16\webapps\ivy\WEB-INF\lib\case-map-ui-10.0.0-SNAPSHOT.jar;D:\tools\AxonIvyDesigner10.0.16\webapps\ivy\WEB-INF\lib\cglib-2.2.2.jar;D:\tools\AxonIvyDesigner10.0.16\webapps\ivy\WEB-INF\lib\editor-client-standalone-10.0.1-SNAPSHOT.jar;D:\tools\AxonIvyDesigner10.0.16\webapps\ivy\WEB-INF\lib\font-awesome-10.0.12-SNAPSHOT.jar;D:\tools\AxonIvyDesigner10.0.16\webapps\ivy\WEB-INF\lib\freya-ivy-theme-10.0.12-SNAPSHOT.jar;D:\tools\AxonIvyDesigner10.0.16\webapps\ivy\WEB-INF\lib\ivy-jsf-beans.jar;D:\tools\AxonIvyDesigner10.0.16\webapps\ivy\WEB-INF\lib\ivy-jsf-webapp-config.jar;D:\tools\AxonIvyDesigner10.0.16\webapps\ivy\WEB-INF\lib\ivy-primefaces.jar;D:\tools\AxonIvyDesigner10.0.16\webapps\ivy\WEB-INF\lib\jstl-1.2.jar;D:\tools\AxonIvyDesigner10.0.16\webapps\ivy\WEB-INF\lib\owasp-java-html-sanitizer-20211018.2.jar;D:\tools\AxonIvyDesigner10.0.16\webapps\ivy\WEB-INF\lib\prettytime-5.0.2.Final.jar;D:\tools\AxonIvyDesigner10.0.16\webapps\ivy\WEB-INF\lib\prettytime-integration-jsf-5.0.2.Final.jar;D:\tools\AxonIvyDesigner10.0.16\webapps\ivy\WEB-INF\lib\primefaces-11.0.13.jar;D:\tools\AxonIvyDesigner10.0.16\webapps\ivy\WEB-INF\lib\primefaces-extensions-11.0.4.jar;D:\tools\AxonIvyDesigner10.0.16\webapps\ivy\WEB-INF\lib\primeflex-10.0.12-SNAPSHOT.jar;D:\tools\AxonIvyDesigner10.0.16\webapps\ivy\WEB-INF\lib\qrcodegen-1.6.0.jar;D:\tools\AxonIvyDesigner10.0.16\webapps\ivy\WEB-INF\lib\resources-ckeditor-11.0.4.jar;D:\tools\AxonIvyDesigner10.0.16\webapps\ivy\WEB-INF\lib\serenity-ivy-theme-10.0.12-SNAPSHOT.jar;D:\tools\AxonIvyDesigner10.0.16\webapps\ivy\WEB-INF\lib\serenity-layout-2.1.1.jar;D:\tools\AxonIvyDesigner10.0.16\webapps\ivy\WEB-INF\lib\serenity-theme-2.1.1.jar;D:\tools\AxonIvyDesigner10.0.16\webapps\ivy\WEB-INF\lib\streamline-icons-10.0.12-SNAPSHOT.jar;C:\Users\mhvtrung\.m2\repository\com\axonivy\ivy\test\unit-tester\10.0.14\unit-tester-10.0.14.jar;C:\Users\mhvtrung\.m2\repository\org\junit\jupiter\junit-jupiter-engine\5.8.1\junit-jupiter-engine-5.8.1.jar;C:\Users\mhvtrung\.m2\repository\org\junit\platform\junit-platform-engine\1.8.1\junit-platform-engine-1.8.1.jar;C:\Users\mhvtrung\.m2\repository\org\opentest4j\opentest4j\1.2.0\opentest4j-1.2.0.jar;C:\Users\mhvtrung\.m2\repository\org\junit\platform\junit-platform-commons\1.8.1\junit-platform-commons-1.8.1.jar;C:\Users\mhvtrung\.m2\repository\org\junit\jupiter\junit-jupiter-api\5.8.1\junit-jupiter-api-5.8.1.jar;C:\Users\mhvtrung\.m2\repository\org\apiguardian\apiguardian-api\1.1.2\apiguardian-api-1.1.2.jar;C:\Users\mhvtrung\.m2\repository\org\assertj\assertj-core\3.23.1\assertj-core-3.23.1.jar;C:\Users\mhvtrung\.m2\repository\net\bytebuddy\byte-buddy\1.12.10\byte-buddy-1.12.10.jar;C:\Users\mhvtrung\.m2\repository\org\junit\platform\junit-platform-launcher\1.8.1\junit-platform-launcher-1.8.1.jar;D:\tools\AxonIvyDesigner10.0.16\configuration\org.eclipse.osgi\409\0\.cp;D:\tools\AxonIvyDesigner10.0.16\configuration\org.eclipse.osgi\407\0\.cp \ No newline at end of file diff --git a/workflow-estimator-test/.temp-WorkflowEstimatorTest-classpath-arg-1708399434099.txt b/workflow-estimator-test/.temp-WorkflowEstimatorTest-classpath-arg-1708399434099.txt new file mode 100644 index 00000000..3aafbaef --- /dev/null +++ b/workflow-estimator-test/.temp-WorkflowEstimatorTest-classpath-arg-1708399434099.txt @@ -0,0 +1 @@ +-classpath D:\AxonIvy\IVYMARKET\market-workflow-estimator\workflow-estimator-test\target\test-classes;D:\AxonIvy\IVYMARKET\market-workflow-estimator\workflow-estimator-test\target\classes;C:\Users\mhvtrung\AppData\Local\Temp\ivyRtProps4403698638069681538;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jersey_2.31.0.202208100658\lib\jersey-media-multipart.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jersey_2.31.0.202208100658\lib\jersey-server.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.equinox.common_3.16.0.v20220211-2322.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.core.resources_3.16.100.v20220214-1012.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.core.jobs_3.12.100.v20220120-1329.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.logging_2.19.0.202311141127\lib\lib-logging-config.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.logging_2.19.0.202311141127\lib\log4j-1.2-api-2.19.0.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.logging_2.19.0.202311141127\lib\log4j-api-2.19.0.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.logging_2.19.0.202311141127\lib\log4j-core-2.19.0.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.logging_2.19.0.202311141127\lib\log4j-slf4j-impl-2.19.0.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.logging_2.19.0.202311141127\lib\commons-logging-1.2.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.logging_2.19.0.202311141127\lib\slf4j-api-1.7.36.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.logging_2.19.0.202311141127\lib\jul-to-slf4j-1.7.36.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.logging_2.19.0.202311141127\lib\jcl-over-slf4j-1.7.36.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.logging_2.19.0.202311141127\lib\jboss-logging-3.5.0.Final.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.poi_4.1.2.202104060804\lib\poi-4.1.2.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.poi_4.1.2.202104060804\lib\poi-ooxml-4.1.2.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.poi_4.1.2.202104060804\lib\poi-ooxml-schemas-4.1.2.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.poi_4.1.2.202104060804\lib\xmlbeans-3.1.0.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.db_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.application_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.util_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.util.api_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.util.di_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.util.log_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jaxb_2.3.5.202312071357\lib\jakarta.xml.bind-api.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jaxws_2.3.1.202112291433\lib\jaxws-api-2.3.1.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jaxws_2.3.1.202112291433\lib\javax.xml.soap-api-1.4.0.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jaxws_2.3.1.202112291433\lib\javax.jws-api-1.1.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.scripting_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.scripting.objects_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.process.event_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.service_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.request_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.security_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.security.identity.jndi_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.bpm.exec_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.workflow_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.persistence_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.htmldialog_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.dialog.exec_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.server_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.cm_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.cm.exec_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.base_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.engine.migration_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.deployment_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.hibernate.entitymanager_5.4.33.202208031448\lib\hibernate-commons-annotations-5.1.2.Final.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.hibernate.entitymanager_5.4.33.202208031448\lib\hibernate-core-5.4.33.Final.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.hibernate.entitymanager_5.4.33.202208031448\lib\hibernate-entitymanager-5.4.33.Final.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.hibernate.entitymanager_5.4.33.202208031448\lib\jboss-transaction-api_1.2_spec-1.1.1.Final.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.hibernate.validator_6.2.5.202210070940\lib\hibernate-validator.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.database.exec_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.mail_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.config_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.configuration_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.cluster_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.webservice.configuration_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.webservice.execution_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.webservice.process_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jackson_2.13.5.202312280855\lib\jackson-annotations-2.13.5.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jackson_2.13.5.202312280855\lib\jackson-module-jaxb-annotations-2.13.5.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jackson_2.13.5.202312280855\lib\jackson-core-2.13.5.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jackson_2.13.5.202312280855\lib\jackson-databind-2.13.5.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jackson_2.13.5.202312280855\lib\jackson-datatype-jsr310-2.13.5.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jackson_2.13.5.202312280855\lib\jackson-jaxrs-json-provider-2.13.5.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jackson_2.13.5.202312280855\lib\jackson-jaxrs-base-2.13.5.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.data.cache_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.process.data.persistence_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.process.data.persistence.config_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.webserver_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\configuration\org.eclipse.osgi\100\0\.cp\lib\cos.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.project_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.resource.datamodel_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.runtime.env_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.components_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.pagedesigner.extensions_10.0.0.202401072231.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.bpm.engine_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.casemap.exec_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.trace_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.apache.commons.beanutils_1.9.4.202103091318\lib\commons-beanutils-1.9.4.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.apache.commons.codec_1.15.0.202103091318\lib\commons-codec-1.15.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.apache.commons.collections4_4.4.0.202103091318\lib\commons-collections4-4.4.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.apache.commons.compress_1.21.0.202110280623\lib\commons-compress-1.21.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.apache.commons.digester_2.1.0.202103091318\lib\commons-digester-2.1.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.apache.commons.fileupload_1.5.0.202312280855\lib\commons-fileupload-1.5.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.apache.commons.io_2.11.0.202110280624\lib\commons-io-2.11.0.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.apache.commons.lang3_3.12.0.202110280631\lib\commons-lang3-3.12.0.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.apache.commons.legacy.collections_3.2.2.202105101833\lib\commons-collections-3.2.2.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.apache.commons.legacy.lang_2.6.0.202103091318\lib\commons-lang-2.6.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.apache.httpclient_4.5.15.202112301615\lib\httpclient-4.5.13.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.apache.httpclient_4.5.15.202112301615\lib\httpcore-4.4.15.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.apache.myfaces_2.2.15.202206210954\lib\myfaces-api-2.2.15.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.ivymx_1.2.3.202204141308\lib\ivymx-1.2.3.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.javassist_3.29.2.202210071228\lib\javassist.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.swagger.annotations_2.2.9.202305101145\lib\swagger-annotations.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\com.google.inject_5.1.0.202208030917\lib\aopalliance-1.0.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\com.google.inject_5.1.0.202208030917\lib\guice-5.1.0.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\com.google.inject_5.1.0.202208030917\lib\guice-servlet-5.1.0.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\com.google.gson_2.8.9.202201180655\lib\gson.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\com.google.guava_30.1.1.202110280801\lib\guava-31.0.1-jre.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\com.google.guava_30.1.1.202110280801\lib\failureaccess-1.0.1.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\javax.activation_1.2.2.202112291433\lib\jakarta.activation.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\javax.annotation_1.3.5.v20200909-1856.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\javax.el_3.0.0.202112291433\lib\tomcat-el-api.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\javax.inject_1.0.0.v20091030.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\javax.mail_1.6.7.202112301623\lib\jakarta.mail-1.6.7.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\javax.persistence_2.2.3.202201241317\lib\jakarta.persistence-api.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\javax.servlet_4.0.0.202112291433\lib\tomcat-servlet-api.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\javax.servlet.jsp_2.3.0.202112291433\lib\tomcat-jsp-api.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\javax.validation_2.0.2.202112291433\lib\jakarta.validation-api.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\javax.websocket_1.1.2.202112291433\lib\jakarta.websocket-api.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\javax.ws.rs_2.1.6.202112291433\lib\jakarta.ws.rs-api.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\cxf-core.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\cxf-rt-bindings-soap.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\cxf-rt-features-logging.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\cxf-rt-frontend-simple.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\cxf-rt-management.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\cxf-rt-transports-http-hc.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\cxf-rt-transports-http.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\cxf-rt-ws-policy.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\cxf-rt-ws-rm.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.webservice.exec.cxf_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.thirdparty.license_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.rule.engine_10.0.0.202401182231.jar;D:\AxonIvy\IVYMARKET\market-workflow-estimator\workflow-estimator\target\classes;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.osgi_3.17.200.v20220215-2237.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.osgi.classpatcher_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.osgi.compatibility.state_1.2.600.v20220207-1403.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.awt2swt_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.awtExt_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.eclipse.util_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.icons_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.boot.osgi_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.bpmn2_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.casemap_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.cm.search_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.database.adapter_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.database.configuration_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.datawrapper_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.dialog.config_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.dialog.jsf_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.enginemanagerbase_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.extension_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.java_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.jersey.client_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.job_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.library_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.macro_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.page.engine_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.process.config_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.process.search_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.project.conversion_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.reference.base_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.renderer_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.scripting.dataclass_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.scripting.dataclass.search_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.search_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.security.identity.azure_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.server.configuration_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.server.control.center_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.server.exec_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.user.role_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.ivy.webservice.client.openapi.codegen_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.apache.commons.math3_3.6.1.202103091318\lib\commons-math3-3.6.1.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.apache.commons.pool2_2.11.1.202110280625\lib\commons-pool2-2.11.1.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.apache.commons.text_1.10.0.202210101240\lib\commons-text-1.10.0.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.apache.httpclient_4.5.15.202112301615\lib\httpcore-nio-4.4.15.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.apache.httpclient_4.5.15.202112301615\lib\httpasyncclient-4.1.5.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.apache.myfaces_2.2.15.202206210954\lib\myfaces-impl-2.2.15.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.classgraph_4.8.149.202208091047\lib\classgraph.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cup_11.0.0.202103091318\lib\java-cup-runtime-11.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\asm.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\cxf-rt-bindings-xml.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\cxf-rt-frontend-jaxws.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\cxf-rt-ws-addr.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\cxf-rt-wsdl.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\neethi.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\saaj-impl.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\stax-ex.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\stax2-api.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\woodstox-core.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\wsdl4j.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\xml-resolver.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\xmlschema-core.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\cxf-rt-databinding-jaxb.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\cxf-rt-ws-security.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\cxf-rt-security.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\wss4j-policy.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\wss4j-ws-security-common.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\wss4j-ws-security-dom.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\xmlsec.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.cxf.webservice_3.5.7.202312071357\lib\cxf-rt-security-saml.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.db.hsqldb_2.7.0.202208071122\lib\hsqldb.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.db.mariadb_2.7.6.202208061602\lib\mariadb-java-client.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.db.mssql_11.2.1.202210271255\lib\mssql-jdbc-11.2.1.jre17.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.db.mysql_8.0.30.202208061554\lib\mysql-connector-java.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.db.oracle_21.7.0.202210180645\lib\ojdbc11-21.7.0.0.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.db.oracle_21.7.0.202210180645\lib\orai18n-21.7.0.0.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.db.postgresql_42.5.0.202209121151\lib\postgresql.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.diff_4.12.0.202208091029\lib\java-diff-utils.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.ecs_1.4.2.202103091318\lib\ecs-1.4.2.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.fasterxml.classmate_1.5.1.202201241317\lib\classmate.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.freemarker_2.3.31.202110271200\lib\freemarker-2.3.31.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.hibernate.entitymanager_5.4.33.202208031448\lib\antlr-2.7.7.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.hibernate.entitymanager_5.4.33.202208031448\lib\jandex-2.2.3.Final.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.hibernate.entitymanager_5.4.33.202208031448\lib\dom4j-2.1.3.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.hibernate.entitymanager_5.4.33.202208031448\lib\byte-buddy-1.11.12.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.icons_10.0.0.202210110849\lib\icon.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.icons_10.0.0.202210110849\lib\core-icons.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jackson_2.13.5.202312280855\lib\jackson-dataformat-yaml-2.13.5.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jackson_2.13.5.202312280855\lib\jackson-datatype-jdk8-2.13.5.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jaxb_2.3.5.202312071357\lib\jaxb-runtime.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jaxb_2.3.5.202312071357\lib\istack-commons-runtime.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jaxb_2.3.5.202312071357\lib\txw2.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jersey_2.31.0.202208100658\lib\hk2-api.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jersey_2.31.0.202208100658\lib\hk2-locator.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jersey_2.31.0.202208100658\lib\hk2-utils.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jersey_2.31.0.202208100658\lib\osgi-resource-locator.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jersey_2.31.0.202208100658\lib\jersey-common.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jersey_2.31.0.202208100658\lib\jersey-container-servlet-core.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jersey_2.31.0.202208100658\lib\jersey-container-servlet.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jersey_2.31.0.202208100658\lib\jersey-entity-filtering.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jersey_2.31.0.202208100658\lib\jersey-media-jaxb.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jersey_2.31.0.202208100658\lib\jersey-media-json-jackson.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jersey_2.31.0.202208100658\lib\jersey-client.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jersey_2.31.0.202208100658\lib\guice-bridge.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jersey_2.31.0.202208100658\lib\mimepull.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jersey_2.31.0.202208100658\lib\jersey-apache-connector.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jersey_2.31.0.202208100658\lib\jersey-hk2.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jest_6.3.1.202201180655\lib\jest-6.3.1.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jest_6.3.1.202201180655\lib\jest-common-6.3.1.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jgoodies_2.7.0.202103091318\lib\jgoodies-common-1.8.1.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jgoodies_2.7.0.202103091318\lib\jgoodies-looks-2.7.0.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jgroups_5.2.7.202210101241\lib\jgroups.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.jsoup_1.15.3.202209121151\lib\jsoup.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.maven_3.8.6.202208091029\lib\maven-artifact-3.8.6.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.maven_3.8.6.202208091029\lib\maven-model-3.8.6.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.maven_3.8.6.202208091029\lib\plexus-utils-3.3.1.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.mbassador_1.3.2.202103091318\lib\mbassador-1.3.2.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.opentelemetry_1.19.0.202210110958\lib\opentelemetry-api.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.opentelemetry_1.19.0.202210110958\lib\opentelemetry-api-logs.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.opentelemetry_1.19.0.202210110958\lib\opentelemetry-context.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.opentelemetry_1.19.0.202210110958\lib\opentelemetry-sdk-common.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.opentelemetry_1.19.0.202210110958\lib\opentelemetry-sdk-trace.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.opentelemetry_1.19.0.202210110958\lib\opentelemetry-sdk-extension-autoconfigure-spi.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.opentelemetry_1.19.0.202210110958\lib\opentelemetry-semconv.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.picocli_4.6.3.202208090951\lib\picocli.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.snakeyaml_1.33.0.202305101212\lib\snakeyaml.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.spring.crypto_5.7.3.202209121151\lib\spring-security-crypto.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.swagger_2.2.9.202305101145\lib\swagger-core.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.swagger_2.2.9.202305101145\lib\swagger-models.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.swagger.codegen_3.0.42.202305101145\lib\swagger-codegen.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.swagger.codegen_3.0.42.202305101145\lib\swagger-codegen-generators.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.swagger.codegen_3.0.42.202305101145\lib\handlebars.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.swagger.codegen_3.0.42.202305101145\lib\jmustache.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.swagger.jaxrs2_2.2.9.202305101145\lib\swagger-integration.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.swagger.jaxrs2_2.2.9.202305101145\lib\swagger-jaxrs2.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.swagger.parser_3.0.42.202305101145\lib\swagger-core-1.6.10.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.swagger.parser_3.0.42.202305101145\lib\swagger-models-1.6.10.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.swagger.parser_3.0.42.202305101145\lib\swagger-parser-1.0.65.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.swagger.parser_3.0.42.202305101145\lib\swagger-parser-2.1.13.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.swagger.parser_3.0.42.202305101145\lib\swagger-parser-v3-2.1.13.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.swagger.parser_3.0.42.202305101145\lib\swagger-parser-v2-converter-2.1.13.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.swagger.parser_3.0.42.202305101145\lib\swagger-parser-core-2.1.13.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.tamaya_0.3.0.202112291433\lib\tamaya-api-0.3-incubating.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.tamaya_0.3.0.202112291433\lib\tamaya-core-0.3-incubating.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.tamaya_0.3.0.202112291433\lib\tamaya-yaml-0.3-incubating.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.tamaya_0.3.0.202112291433\lib\tamaya-formats-0.3-incubating.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.tamaya_0.3.0.202112291433\lib\tamaya-functions-0.3-incubating.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.tamaya_0.3.0.202112291433\lib\tamaya-resources-0.3-incubating.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.tamaya_0.3.0.202112291433\lib\tamaya-spisupport-0.3-incubating.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.tamaya_0.3.0.202112291433\lib\tamaya-events-0.3-incubating.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.lib.tamaya_0.3.0.202112291433\lib\tamaya-resolver-0.3-incubating.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.swt_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.tomcat_9.0.73.202303161538\lib\tomcat-catalina-ha.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.tomcat_9.0.73.202303161538\lib\tomcat-tribes.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.tomcat_9.0.73.202303161538\lib\tomcat-catalina.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.tomcat_9.0.73.202303161538\lib\tomcat-jasper-el.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.tomcat_9.0.73.202303161538\lib\tomcat-jasper.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.tomcat_9.0.73.202303161538\lib\tomcat-jaspic-api.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.tomcat_9.0.73.202303161538\lib\tomcat-coyote.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.tomcat_9.0.73.202303161538\lib\tomcat-dbcp.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.tomcat_9.0.73.202303161538\lib\tomcat-juli.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.tomcat_9.0.73.202303161538\lib\tomcat-api.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.tomcat_9.0.73.202303161538\lib\tomcat-ssi.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.tomcat_9.0.73.202303161538\lib\tomcat-jdbc.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.tomcat_9.0.73.202303161538\lib\tomcat-jni.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.tomcat_9.0.73.202303161538\lib\tomcat-util.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.tomcat_9.0.73.202303161538\lib\tomcat-util-scan.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.tomcat_9.0.73.202303161538\lib\tomcat-websocket.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.util.bus_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.util.io_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\ch.ivyteam.util.yaml_10.0.16.202401191200.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.core.commands_3.10.100.v20210722-1426.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.core.contenttype_3.8.100.v20210910-0640.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.core.databinding_1.11.0.v20220118-1028.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.core.databinding.beans_1.8.0.v20210619-1111.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.core.databinding.observable_1.12.0.v20211231-1006.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.core.databinding.property_1.9.0.v20210619-1129.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.core.expressions_3.8.100.v20210910-0640.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.core.externaltools_1.2.200.v20220125-2302.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.core.filebuffers_3.7.200.v20220202-1008.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.core.filesystem_1.9.300.v20220121-1426.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.core.filesystem.win32.x86_64_1.4.200.v20190812-0909.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.core.net_1.3.1100.v20210424-0724.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.core.net.win32.x86_64_1.1.500.v20190925-1337.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.core.resources.win32.x86_64_3.5.400.v20190812-0909.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.core.runtime_3.24.100.v20220211-2001.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.core.variables_3.5.100.v20210721-1355.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.equinox.preferences_3.9.100.v20211021-1418.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.jdt.core_3.29.0.v20220214-1307.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.jdt.core.formatterapp_1.1.0.v20210618-1653.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.jdt.core.manipulation_1.16.0.v20220214-0655.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.osgi.services_3.10.200.v20210723-0643.jar;D:\tools\AxonIvyDesigner10.0.16\plugins\org.eclipse.osgi.util_3.6.100.v20210723-1119.jar;D:\tools\AxonIvyDesigner10.0.16\webapps\ivy\WEB-INF\lib\barcode4j-light-2.1.jar;D:\tools\AxonIvyDesigner10.0.16\webapps\ivy\WEB-INF\lib\case-map-ui-10.0.0-SNAPSHOT.jar;D:\tools\AxonIvyDesigner10.0.16\webapps\ivy\WEB-INF\lib\cglib-2.2.2.jar;D:\tools\AxonIvyDesigner10.0.16\webapps\ivy\WEB-INF\lib\editor-client-standalone-10.0.1-SNAPSHOT.jar;D:\tools\AxonIvyDesigner10.0.16\webapps\ivy\WEB-INF\lib\font-awesome-10.0.12-SNAPSHOT.jar;D:\tools\AxonIvyDesigner10.0.16\webapps\ivy\WEB-INF\lib\freya-ivy-theme-10.0.12-SNAPSHOT.jar;D:\tools\AxonIvyDesigner10.0.16\webapps\ivy\WEB-INF\lib\ivy-jsf-beans.jar;D:\tools\AxonIvyDesigner10.0.16\webapps\ivy\WEB-INF\lib\ivy-jsf-webapp-config.jar;D:\tools\AxonIvyDesigner10.0.16\webapps\ivy\WEB-INF\lib\ivy-primefaces.jar;D:\tools\AxonIvyDesigner10.0.16\webapps\ivy\WEB-INF\lib\jstl-1.2.jar;D:\tools\AxonIvyDesigner10.0.16\webapps\ivy\WEB-INF\lib\owasp-java-html-sanitizer-20211018.2.jar;D:\tools\AxonIvyDesigner10.0.16\webapps\ivy\WEB-INF\lib\prettytime-5.0.2.Final.jar;D:\tools\AxonIvyDesigner10.0.16\webapps\ivy\WEB-INF\lib\prettytime-integration-jsf-5.0.2.Final.jar;D:\tools\AxonIvyDesigner10.0.16\webapps\ivy\WEB-INF\lib\primefaces-11.0.13.jar;D:\tools\AxonIvyDesigner10.0.16\webapps\ivy\WEB-INF\lib\primefaces-extensions-11.0.4.jar;D:\tools\AxonIvyDesigner10.0.16\webapps\ivy\WEB-INF\lib\primeflex-10.0.12-SNAPSHOT.jar;D:\tools\AxonIvyDesigner10.0.16\webapps\ivy\WEB-INF\lib\qrcodegen-1.6.0.jar;D:\tools\AxonIvyDesigner10.0.16\webapps\ivy\WEB-INF\lib\resources-ckeditor-11.0.4.jar;D:\tools\AxonIvyDesigner10.0.16\webapps\ivy\WEB-INF\lib\serenity-ivy-theme-10.0.12-SNAPSHOT.jar;D:\tools\AxonIvyDesigner10.0.16\webapps\ivy\WEB-INF\lib\serenity-layout-2.1.1.jar;D:\tools\AxonIvyDesigner10.0.16\webapps\ivy\WEB-INF\lib\serenity-theme-2.1.1.jar;D:\tools\AxonIvyDesigner10.0.16\webapps\ivy\WEB-INF\lib\streamline-icons-10.0.12-SNAPSHOT.jar;C:\Users\mhvtrung\.m2\repository\com\axonivy\ivy\test\unit-tester\10.0.14\unit-tester-10.0.14.jar;C:\Users\mhvtrung\.m2\repository\org\junit\jupiter\junit-jupiter-engine\5.8.1\junit-jupiter-engine-5.8.1.jar;C:\Users\mhvtrung\.m2\repository\org\junit\platform\junit-platform-engine\1.8.1\junit-platform-engine-1.8.1.jar;C:\Users\mhvtrung\.m2\repository\org\opentest4j\opentest4j\1.2.0\opentest4j-1.2.0.jar;C:\Users\mhvtrung\.m2\repository\org\junit\platform\junit-platform-commons\1.8.1\junit-platform-commons-1.8.1.jar;C:\Users\mhvtrung\.m2\repository\org\junit\jupiter\junit-jupiter-api\5.8.1\junit-jupiter-api-5.8.1.jar;C:\Users\mhvtrung\.m2\repository\org\apiguardian\apiguardian-api\1.1.2\apiguardian-api-1.1.2.jar;C:\Users\mhvtrung\.m2\repository\org\assertj\assertj-core\3.23.1\assertj-core-3.23.1.jar;C:\Users\mhvtrung\.m2\repository\net\bytebuddy\byte-buddy\1.12.10\byte-buddy-1.12.10.jar;C:\Users\mhvtrung\.m2\repository\org\junit\platform\junit-platform-launcher\1.8.1\junit-platform-launcher-1.8.1.jar;D:\tools\AxonIvyDesigner10.0.16\configuration\org.eclipse.osgi\409\0\.cp;D:\tools\AxonIvyDesigner10.0.16\configuration\org.eclipse.osgi\407\0\.cp \ No newline at end of file diff --git a/workflow-estimator-test/processes/FlowExampleBasic.p.json b/workflow-estimator-test/processes/FlowExampleBasic.p.json index 42d623d5..055028a1 100644 --- a/workflow-estimator-test/processes/FlowExampleBasic.p.json +++ b/workflow-estimator-test/processes/FlowExampleBasic.p.json @@ -7,7 +7,7 @@ "elements" : [ { "id" : "f0", "type" : "RequestStart", - "name" : "start.ivp", + "name" : "start", "config" : { "signature" : "start" }, @@ -155,12 +155,12 @@ "name" : [ "useCase=null / flowName = null", "", - "findAllTasks( start.ivp ) => Task A, Task C, Task B", + "findAllTasks( start ) => Task A, Task C, Task B", "findAllTasks( Task B ) => Task B", "findAllTasks( Task C ) => Task C, Task B", "findAllTasks( NewStart ) => Task B", "", - "findTasksOnPath( start.ivp ) => Task A", + "findTasksOnPath( start ) => Task A", "findTasksOnPath( Task B ) => Task B", "findTasksOnPath( Task C ) => Task C, Task B", "findTasksOnPath( NewStart ) => Task B" @@ -175,22 +175,22 @@ "name" : [ "useCase=null / flowName = internal", "", - "findAllTasks( start.ivp ) => Task A, Task C, Task B", - "findTasksOnPath( start.ivp ) => Task A, Task B", + "findAllTasks( start ) => Task A, Task C, Task B", + "findTasksOnPath( start ) => Task A, Task B", "findTasksOnPath( NewStart ) => Task B", "", "----", "", "useCase=null / flowName = external", "", - "findTasksOnPath( start.ivp ) => Task A, Task C, Task B", + "findTasksOnPath( start ) => Task A, Task C, Task B", "findTasksOnPath( NewStart ) => Task C, Task B", "", "----", "", "useCase=null / flowName = mixed", "", - "findTasksOnPath( start.ivp ) => Task A, Task B", + "findTasksOnPath( start ) => Task A, Task B", "findTasksOnPath( NewStart ) => Task B" ], "visual" : { diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleBasicTest.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleBasicTest.java index f7e94739..f2b59e5f 100644 --- a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleBasicTest.java +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleBasicTest.java @@ -1,16 +1,26 @@ package com.axonivy.utils.estimator.test; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; +<<<<<<< HEAD import java.time.Duration; +======= +import java.util.List; + +import org.assertj.core.util.Arrays; +import org.junit.jupiter.api.BeforeAll; +>>>>>>> 0330178 (TE-538: Update unit test for flow example basic) import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import com.axonivy.utils.estimator.WorkflowEstimator; +import com.axonivy.utils.estimator.model.EstimatedTask; import ch.ivyteam.ivy.environment.Ivy; import ch.ivyteam.ivy.environment.IvyTest; +import ch.ivyteam.ivy.process.model.BaseElement; import ch.ivyteam.ivy.process.model.Process; import ch.ivyteam.ivy.process.model.element.SingleTaskCreator; import ch.ivyteam.ivy.process.model.element.value.task.TaskConfig; @@ -20,69 +30,116 @@ @SuppressWarnings("restriction") public class FlowExampleBasicTest { - private Process process; - private ProcessGraph graph; - - @BeforeEach - void setup() { + private static Process process; + private static ProcessGraph graph; + private static BaseElement start; + private static BaseElement newStart; + private static BaseElement taskB; + private static BaseElement taskC; + + @BeforeAll + public static void setup() { var pmv = Ivy.request().getProcessModelVersion(); var manager = IProcessManager.instance().getProjectDataModelFor(pmv); - this.process = manager.findProcessByPath("FlowExampleBasic").getModel(); - this.graph = new ProcessGraph(process); + process = manager.findProcessByPath("FlowExampleBasic").getModel(); + graph = new ProcessGraph(process); + + start = graph.findByElementName("start"); + newStart = graph.findByElementName("NewStart"); + taskB = graph.findByElementName("Task B"); + taskC = graph.findByElementName("Task C"); } @Test - void shouldFindAllTasksAtStartRequestWithoutFlowName() { + void shouldFindAllTasksAtStart() { var workflowEstimator = new WorkflowEstimator(process, null, null); + List estimatedTasks = workflowEstimator.findAllTasks(start); - var estimatedTasks = workflowEstimator.findAllTasks(graph.findStart()); - - assertEquals(3, estimatedTasks.size()); - assertEquals("Task A", estimatedTasks.get(0).getTaskName()); - assertEquals("Task C", estimatedTasks.get(1).getTaskName()); - assertEquals("Task B", estimatedTasks.get(2).getTaskName()); + assertArrayEquals(Arrays.array("Task A", "Task C", "Task B"), getTaskNames(estimatedTasks)); } @Test void shouldFfindAllTasksAtTaskB() { var workflowEstimator = new WorkflowEstimator(process, null, null); - var taskB = this.graph.findByElementName("Task B"); var estimatedTasks = workflowEstimator.findAllTasks(taskB); - assertEquals(1, estimatedTasks.size()); - assertEquals("Task B", estimatedTasks.get(0).getTaskName()); + assertArrayEquals(Arrays.array("Task B"), getTaskNames(estimatedTasks)); } @Test void shouldFindAllTasksAtTaskC() { var workflowEstimator = new WorkflowEstimator(process, null, null); - var taskC = this.graph.findByElementName("Task C"); var estimatedTasks = workflowEstimator.findAllTasks(taskC); - assertEquals(2, estimatedTasks.size()); - assertEquals("Task C", estimatedTasks.get(0).getTaskName()); - assertEquals("Task B", estimatedTasks.get(1).getTaskName()); + assertArrayEquals(Arrays.array("Task C", "Task B"), getTaskNames(estimatedTasks)); } @Test - void shouldFindAllTasksOfInternalFlow() { + void shouldFindAllTasksAtNewStart() { + var workflowEstimator = new WorkflowEstimator(process, null, null); + var estimatedTasks = workflowEstimator.findAllTasks(newStart); + + assertArrayEquals(Arrays.array("Task C", "Task B"), getTaskNames(estimatedTasks)); + } + + @Test + void shouldFindTasksOnPathWithoutFlowNameAtStart() { + var workflowEstimator = new WorkflowEstimator(process, null, null); + var estimatedTasks = workflowEstimator.findTasksOnPath(start); + + assertArrayEquals(Arrays.array("Task A"), getTaskNames(estimatedTasks)); + } + + @Test + void shouldFindTasksOnPathWithoutFlowNameAtTaskB() { + var workflowEstimator = new WorkflowEstimator(process, null, null); + var estimatedTasks = workflowEstimator.findTasksOnPath(taskB); + + assertArrayEquals(Arrays.array("Task B"), getTaskNames(estimatedTasks)); + } + + @Test + void shouldFindTasksOnPathWithoutFlowNameAtTaskC() { + var workflowEstimator = new WorkflowEstimator(process, null, null); + var estimatedTasks = workflowEstimator.findTasksOnPath(taskC); + + assertArrayEquals(Arrays.array("Task C", "Task B"), getTaskNames(estimatedTasks)); + } + + @Test + void shouldFindTasksOnPathWithoutFlowNameAtNewStart() { + var workflowEstimator = new WorkflowEstimator(process, null, null); + var estimatedTasks = workflowEstimator.findTasksOnPath(taskC); + + assertArrayEquals(Arrays.array("Task B"), getTaskNames(estimatedTasks)); + } + + @Test + void shouldFindTasksOnPathOfInternalFlowAtStart() { var workflowEstimator = new WorkflowEstimator(process, null, "internal"); - var estimatedTasks = workflowEstimator.findTasksOnPath(graph.findStart()); + var estimatedTasks = workflowEstimator.findTasksOnPath(start); - assertEquals(2, estimatedTasks.size()); - assertEquals("Task A", estimatedTasks.get(0).getTaskName()); - assertEquals("Task B", estimatedTasks.get(1).getTaskName()); + assertArrayEquals(Arrays.array("Task A", "Task B"), getTaskNames(estimatedTasks)); } + @Test + void shouldFindTasksOnPathOfInternalFlowAtNewStart() { + var workflowEstimator = new WorkflowEstimator(process, null, "internal"); + var estimatedTasks = workflowEstimator.findTasksOnPath(newStart); + + assertArrayEquals(Arrays.array("Task B"), getTaskNames(estimatedTasks)); + } + @Test void shouldFindAllTasksOfExternalFlow() { var workflowEstimator = new WorkflowEstimator(process, null, "external"); - var estimatedTasks = workflowEstimator.findTasksOnPath(graph.findStart()); + var estimatedTasks = workflowEstimator.findTasksOnPath(start); - assertEquals(3, estimatedTasks.size()); - assertEquals("Task A", estimatedTasks.get(0).getTaskName()); - assertEquals("Task C", estimatedTasks.get(1).getTaskName()); - assertEquals("Task B", estimatedTasks.get(2).getTaskName()); + assertArrayEquals(Arrays.array("Task A", "Task C", "Task B"), getTaskNames(estimatedTasks)); + } + + private String[] getTaskNames(List tasks ) { + return tasks.stream().map(EstimatedTask::getTaskName).toArray(String[]::new); } @Test diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java b/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java new file mode 100644 index 00000000..631dce77 --- /dev/null +++ b/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java @@ -0,0 +1,56 @@ +package com.axonivy.utils.estimator; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import ch.ivyteam.ivy.process.model.BaseElement; +import ch.ivyteam.ivy.process.model.NodeElement; +import ch.ivyteam.ivy.process.model.Process; +import ch.ivyteam.ivy.process.model.connector.SequenceFlow; +import ch.ivyteam.ivy.process.model.element.event.start.RequestStart; + +@SuppressWarnings("restriction") +public class ProcessGraph { + public final Process process; + + public ProcessGraph(Process process) { + this.process = process; + } + + public RequestStart findStart() { + return process.search().type(RequestStart.class).findOne(); + } + + public BaseElement findByElementName(String name) { + return process.getElements().stream() + .filter(el -> el.getName().equals(name)) + .findFirst() + .orElse(null); + } + + private List> findNextNodeElementPath(BaseElement from) { + var nexts = new ArrayList>(); + + if (from != null && from instanceof NodeElement) { + List outs = ((NodeElement) from).getOutgoing(); + for (SequenceFlow out : outs) { + + NodeElement target = out.getTarget(); + List> nextElements = findNextNodeElementPath(target); + + if (nextElements.isEmpty()) { + nextElements.add(new ArrayList(Arrays.asList(target, out))); + } else { + nextElements.forEach(nodes -> { + nodes.add(0, target); + nodes.add(1, out); + }); + } + + nexts.addAll(nextElements); + } + } + return nexts; + } +} diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java index 2eb71af2..5b771753 100644 --- a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java +++ b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java @@ -91,7 +91,7 @@ public Duration calculateEstimatedDuration(BaseElement startElement) { } private List> findPaths(BaseElement from) { - return findPaths(from, null); + return findAllPaths(from); } private List convertToEstimatedTasks(List> paths) { @@ -119,16 +119,27 @@ private List convertToEstimatedTasks(List> path } private List> findPaths(BaseElement from, String flowName) { + List> paths = findAllPaths(from); + + List> pathByFlowName = emptyList(); + if (StringUtils.isNotBlank(flowName)) { + pathByFlowName = paths.stream().filter(path -> hasFlowName(path, flowName)).toList(); + } else { + pathByFlowName = paths.stream().filter(path -> hasDefaultPath(path)).toList(); + } + + return pathByFlowName; + } + + private List> findAllPaths(BaseElement from) { List> paths = findNextNodeElementPath(from); + + //Insert from element at first position paths.forEach(path -> { path.add(0, from); }); - - List> pathByFlowName = paths.stream() - .filter(path -> hasFlowName(path, flowName)) - .toList(); - - return pathByFlowName; + + return paths; } private List> findNextNodeElementPath(BaseElement from) { @@ -179,19 +190,34 @@ private boolean hasFlowName(List elements, String flowName) { return existsFlowName; } + private EstimatedTask createEstimatedTask(UserTask task, Date startTimestamp) { EstimatedTask estimatedTask = new EstimatedTask(); estimatedTask.setPid(task.getPid().getRawPid()); estimatedTask.setTaskName(task.getTaskConfig().getName().getRawMacro()); estimatedTask.setParentElementNames(emptyList()); - + Duration estimatedDuration = getDurationByCode(task.getTaskConfig()); estimatedTask.setEstimatedDuration(estimatedDuration); estimatedTask.setEstimatedStartTimestamp(startTimestamp); return estimatedTask; } - + + private boolean hasDefaultPath(List elements) { + String defaultPathColor = "default path"; + for (BaseElement el : elements) { + if (el instanceof SequenceFlow) { + String colorReference = ((SequenceFlow) el).getEdge().color().getReference(); + if (defaultPathColor.equals(colorReference)) { + return true; + } + } + } + + return false; + } + private Duration getDurationByCode(TaskConfig task) { // strongly typed! String script = Optional.of(task.getScript()).orElse(EMPTY); @@ -218,6 +244,5 @@ private Duration getDurationByCode(TaskConfig task) { } return Duration.ofHours(0); - } - + } } From dbe94afe868405217435bfaacac2cf5c53d5f6a4 Mon Sep 17 00:00:00 2001 From: Trung Mai Date: Mon, 26 Feb 2024 10:33:14 +0700 Subject: [PATCH 019/143] TE-538: Unit test findTasksOnPath --- .../processes/FlowExampleBasic.p.json | 4 +- .../processes/FlowExampleError.p.json | 53 ++------ .../processes/FlowExampleLoop.p.json | 53 ++++++-- .../estimator/test/FlowExampleBasicTest.java | 43 ++++-- .../axonivy/utils/estimator/ProcessGraph.java | 109 +++++++++++++-- .../utils/estimator/WorkflowEstimator.java | 128 ++---------------- 6 files changed, 197 insertions(+), 193 deletions(-) diff --git a/workflow-estimator-test/processes/FlowExampleBasic.p.json b/workflow-estimator-test/processes/FlowExampleBasic.p.json index 055028a1..7982bc35 100644 --- a/workflow-estimator-test/processes/FlowExampleBasic.p.json +++ b/workflow-estimator-test/processes/FlowExampleBasic.p.json @@ -158,7 +158,7 @@ "findAllTasks( start ) => Task A, Task C, Task B", "findAllTasks( Task B ) => Task B", "findAllTasks( Task C ) => Task C, Task B", - "findAllTasks( NewStart ) => Task B", + "findAllTasks( NewStart ) => Task C, Task B", "", "findTasksOnPath( start ) => Task A", "findTasksOnPath( Task B ) => Task B", @@ -166,7 +166,7 @@ "findTasksOnPath( NewStart ) => Task B" ], "visual" : { - "at" : { "x" : 235, "y" : 543 }, + "at" : { "x" : 231, "y" : 543 }, "size" : { "width" : 333, "height" : 239 } } }, { diff --git a/workflow-estimator-test/processes/FlowExampleError.p.json b/workflow-estimator-test/processes/FlowExampleError.p.json index c49aac63..61c71423 100644 --- a/workflow-estimator-test/processes/FlowExampleError.p.json +++ b/workflow-estimator-test/processes/FlowExampleError.p.json @@ -1,6 +1,6 @@ { "$schema" : "https://json-schema.axonivy.com/process/11.2.2/process.json", - "id" : "18DD17872B3D53E2", + "id" : "18DD16F8AA39F5DE", "config" : { "data" : "com.axonivy.utils.estimator.test.Data" }, @@ -15,81 +15,59 @@ "at" : { "x" : 96, "y" : 64 } }, "connect" : [ - { "id" : "f2", "to" : "f5" } + { "id" : "f2", "to" : "f3" } ] }, { "id" : "f1", "type" : "TaskEnd", "visual" : { - "at" : { "x" : 936, "y" : 64 } + "at" : { "x" : 672, "y" : 64 } } }, { "id" : "f3", "type" : "UserTask", "name" : "Task A", "visual" : { - "at" : { "x" : 472, "y" : 64 } + "at" : { "x" : 256, "y" : 64 } }, "connect" : [ { "id" : "f4", "to" : "f7" } ] - }, { - "id" : "f5", - "type" : "Alternative", - "visual" : { - "at" : { "x" : 264, "y" : 64 } - }, - "connect" : [ - { "id" : "f6", "to" : "f3" } - ] }, { "id" : "f7", "type" : "Alternative", "config" : { "conditions" : { - "f8" : "true" + "f8" : "true", + "f10" : "true" } }, "visual" : { - "at" : { "x" : 712, "y" : 64 } + "at" : { "x" : 448, "y" : 64 } }, "connect" : [ { "id" : "f8", "to" : "f1", "label" : { "name" : "{success}" } }, - { "id" : "f10", "to" : "f9", "via" : [ { "x" : 712, "y" : 184 } ], "color" : "default path", "label" : { - "name" : "cancel path", - "segment" : 1.19, - "offset" : { "x" : 27, "y" : -37 } - } } + { "id" : "f10", "to" : "f9", "via" : [ { "x" : 448, "y" : 184 } ] } ] }, { "id" : "f9", "type" : "UserTask", "name" : "Task B", "visual" : { - "at" : { "x" : 472, "y" : 184 } + "at" : { "x" : 552, "y" : 184 } }, "connect" : [ - { "id" : "f11", "to" : "f5", "via" : [ { "x" : 264, "y" : 184 } ] } + { "id" : "f11", "to" : "f1", "via" : [ { "x" : 672, "y" : 184 } ] } ] - }, { - "id" : "f17", - "type" : "ProcessAnnotation", - "name" : "green path = default path with empty condition", - "visual" : { - "at" : { "x" : 920, "y" : 119 }, - "size" : { "width" : 294, "height" : 19 }, - "color" : "default path" - } }, { "id" : "f19", "type" : "ProcessAnnotation", "name" : [ "useCase=null / flowName = null", "", - "findAllTasks( start.ivp ) => Task A, Task B", - "findTasksOnPath( start.ivp ) => Task A, Task B", + "findTasksOnPath( start.ivp ) => Exception", "", "----", "", @@ -98,13 +76,8 @@ "findTasksOnPath( start.ivp ) => Task A" ], "visual" : { - "at" : { "x" : 231, "y" : 304 }, + "at" : { "x" : 231, "y" : 256 }, "size" : { "width" : 299, "height" : 166 } } - } ], - "layout" : { - "colors" : { - "default path" : "#12f202" - } - } + } ] } \ No newline at end of file diff --git a/workflow-estimator-test/processes/FlowExampleLoop.p.json b/workflow-estimator-test/processes/FlowExampleLoop.p.json index 61c71423..c49aac63 100644 --- a/workflow-estimator-test/processes/FlowExampleLoop.p.json +++ b/workflow-estimator-test/processes/FlowExampleLoop.p.json @@ -1,6 +1,6 @@ { "$schema" : "https://json-schema.axonivy.com/process/11.2.2/process.json", - "id" : "18DD16F8AA39F5DE", + "id" : "18DD17872B3D53E2", "config" : { "data" : "com.axonivy.utils.estimator.test.Data" }, @@ -15,59 +15,81 @@ "at" : { "x" : 96, "y" : 64 } }, "connect" : [ - { "id" : "f2", "to" : "f3" } + { "id" : "f2", "to" : "f5" } ] }, { "id" : "f1", "type" : "TaskEnd", "visual" : { - "at" : { "x" : 672, "y" : 64 } + "at" : { "x" : 936, "y" : 64 } } }, { "id" : "f3", "type" : "UserTask", "name" : "Task A", "visual" : { - "at" : { "x" : 256, "y" : 64 } + "at" : { "x" : 472, "y" : 64 } }, "connect" : [ { "id" : "f4", "to" : "f7" } ] + }, { + "id" : "f5", + "type" : "Alternative", + "visual" : { + "at" : { "x" : 264, "y" : 64 } + }, + "connect" : [ + { "id" : "f6", "to" : "f3" } + ] }, { "id" : "f7", "type" : "Alternative", "config" : { "conditions" : { - "f8" : "true", - "f10" : "true" + "f8" : "true" } }, "visual" : { - "at" : { "x" : 448, "y" : 64 } + "at" : { "x" : 712, "y" : 64 } }, "connect" : [ { "id" : "f8", "to" : "f1", "label" : { "name" : "{success}" } }, - { "id" : "f10", "to" : "f9", "via" : [ { "x" : 448, "y" : 184 } ] } + { "id" : "f10", "to" : "f9", "via" : [ { "x" : 712, "y" : 184 } ], "color" : "default path", "label" : { + "name" : "cancel path", + "segment" : 1.19, + "offset" : { "x" : 27, "y" : -37 } + } } ] }, { "id" : "f9", "type" : "UserTask", "name" : "Task B", "visual" : { - "at" : { "x" : 552, "y" : 184 } + "at" : { "x" : 472, "y" : 184 } }, "connect" : [ - { "id" : "f11", "to" : "f1", "via" : [ { "x" : 672, "y" : 184 } ] } + { "id" : "f11", "to" : "f5", "via" : [ { "x" : 264, "y" : 184 } ] } ] + }, { + "id" : "f17", + "type" : "ProcessAnnotation", + "name" : "green path = default path with empty condition", + "visual" : { + "at" : { "x" : 920, "y" : 119 }, + "size" : { "width" : 294, "height" : 19 }, + "color" : "default path" + } }, { "id" : "f19", "type" : "ProcessAnnotation", "name" : [ "useCase=null / flowName = null", "", - "findTasksOnPath( start.ivp ) => Exception", + "findAllTasks( start.ivp ) => Task A, Task B", + "findTasksOnPath( start.ivp ) => Task A, Task B", "", "----", "", @@ -76,8 +98,13 @@ "findTasksOnPath( start.ivp ) => Task A" ], "visual" : { - "at" : { "x" : 231, "y" : 256 }, + "at" : { "x" : 231, "y" : 304 }, "size" : { "width" : 299, "height" : 166 } } - } ] + } ], + "layout" : { + "colors" : { + "default path" : "#12f202" + } + } } \ No newline at end of file diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleBasicTest.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleBasicTest.java index f2b59e5f..89ac2bdf 100644 --- a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleBasicTest.java +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleBasicTest.java @@ -3,16 +3,11 @@ import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; -<<<<<<< HEAD import java.time.Duration; - -======= import java.util.List; import org.assertj.core.util.Arrays; import org.junit.jupiter.api.BeforeAll; ->>>>>>> 0330178 (TE-538: Update unit test for flow example basic) -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import com.axonivy.utils.estimator.WorkflowEstimator; @@ -22,8 +17,6 @@ import ch.ivyteam.ivy.environment.IvyTest; import ch.ivyteam.ivy.process.model.BaseElement; import ch.ivyteam.ivy.process.model.Process; -import ch.ivyteam.ivy.process.model.element.SingleTaskCreator; -import ch.ivyteam.ivy.process.model.element.value.task.TaskConfig; import ch.ivyteam.ivy.process.rdm.IProcessManager; @IvyTest @@ -109,11 +102,19 @@ void shouldFindTasksOnPathWithoutFlowNameAtTaskC() { @Test void shouldFindTasksOnPathWithoutFlowNameAtNewStart() { var workflowEstimator = new WorkflowEstimator(process, null, null); - var estimatedTasks = workflowEstimator.findTasksOnPath(taskC); + var estimatedTasks = workflowEstimator.findTasksOnPath(newStart); assertArrayEquals(Arrays.array("Task B"), getTaskNames(estimatedTasks)); } + @Test + void shouldFindAllTasksOfInternalFlowAtStart() { + var workflowEstimator = new WorkflowEstimator(process, null, "internal"); + var estimatedTasks = workflowEstimator.findAllTasks(start); + + assertArrayEquals(Arrays.array("Task A", "Task B", "Task C"), getTaskNames(estimatedTasks)); + } + @Test void shouldFindTasksOnPathOfInternalFlowAtStart() { var workflowEstimator = new WorkflowEstimator(process, null, "internal"); @@ -131,13 +132,37 @@ void shouldFindTasksOnPathOfInternalFlowAtNewStart() { } @Test - void shouldFindAllTasksOfExternalFlow() { + void shouldFindAllTasksOfExternalFlowAtStart() { var workflowEstimator = new WorkflowEstimator(process, null, "external"); var estimatedTasks = workflowEstimator.findTasksOnPath(start); assertArrayEquals(Arrays.array("Task A", "Task C", "Task B"), getTaskNames(estimatedTasks)); } + @Test + void shouldFindTasksOnPathOfExternalFlowAtNewStart() { + var workflowEstimator = new WorkflowEstimator(process, null, "external"); + var estimatedTasks = workflowEstimator.findTasksOnPath(newStart); + + assertArrayEquals(Arrays.array("Task C", "Task B"), getTaskNames(estimatedTasks)); + } + +// @Test +// void shouldFindAllTasksOfMixedFlowAtStart() { +// var workflowEstimator = new WorkflowEstimator(process, null, "mixed"); +// var estimatedTasks = workflowEstimator.findTasksOnPath(start); +// +// assertArrayEquals(Arrays.array("Task A", "Task B"), getTaskNames(estimatedTasks)); +// } +// +// @Test +// void shouldFindAllTasksOfMixedFlowAtNewStart() { +// var workflowEstimator = new WorkflowEstimator(process, null, "mixed"); +// var estimatedTasks = workflowEstimator.findTasksOnPath(newStart); +// +// assertArrayEquals(Arrays.array("Task B"), getTaskNames(estimatedTasks)); +// } + private String[] getTaskNames(List tasks ) { return tasks.stream().map(EstimatedTask::getTaskName).toArray(String[]::new); } diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java b/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java index 631dce77..ba43779e 100644 --- a/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java +++ b/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java @@ -1,14 +1,24 @@ package com.axonivy.utils.estimator; +import static java.util.Collections.emptyList; +import static org.apache.commons.lang3.StringUtils.defaultString; +import static org.apache.commons.lang3.StringUtils.isNotBlank; + import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map.Entry; + +import org.apache.commons.lang3.StringUtils; + +import com.google.common.base.Objects; import ch.ivyteam.ivy.process.model.BaseElement; import ch.ivyteam.ivy.process.model.NodeElement; import ch.ivyteam.ivy.process.model.Process; import ch.ivyteam.ivy.process.model.connector.SequenceFlow; -import ch.ivyteam.ivy.process.model.element.event.start.RequestStart; +import ch.ivyteam.ivy.process.model.element.gateway.Alternative; +import ch.ivyteam.ivy.process.model.element.value.IvyScriptExpression; @SuppressWarnings("restriction") public class ProcessGraph { @@ -17,17 +27,39 @@ public class ProcessGraph { public ProcessGraph(Process process) { this.process = process; } - - public RequestStart findStart() { - return process.search().type(RequestStart.class).findOne(); + + /** + * Using Recursion Algorithm To Find All The Path On Graph. + * Return all paths + * Exception: Throw stack overflow error if there are loop node + */ + public List> findPaths(BaseElement from) { + return findAllPaths(from); } + + public List> findPaths(BaseElement from, String flowName) { + List> paths = findAllPaths(from); - public BaseElement findByElementName(String name) { - return process.getElements().stream() - .filter(el -> el.getName().equals(name)) - .findFirst() - .orElse(null); - } + List> pathByFlowName = emptyList(); + if (isNotBlank(flowName)) { + pathByFlowName = paths.stream().filter(path -> hasFlowName(path, flowName)).toList(); + } else { + pathByFlowName = paths.stream().filter(path -> isAllEmptyCondition(path)).toList(); + } + //Maybe throw an exception if there are not path + return pathByFlowName; + } + + private List> findAllPaths(BaseElement from) { + List> paths = findNextNodeElementPath(from); + + //Insert from element at first position + paths.forEach(path -> { + path.add(0, from); + }); + + return paths; + } private List> findNextNodeElementPath(BaseElement from) { var nexts = new ArrayList>(); @@ -40,11 +72,11 @@ private List> findNextNodeElementPath(BaseElement from) { List> nextElements = findNextNodeElementPath(target); if (nextElements.isEmpty()) { - nextElements.add(new ArrayList(Arrays.asList(target, out))); + nextElements.add(new ArrayList(Arrays.asList(out, target))); } else { nextElements.forEach(nodes -> { - nodes.add(0, target); - nodes.add(1, out); + nodes.add(0, out); + nodes.add(1, target); }); } @@ -53,4 +85,55 @@ private List> findNextNodeElementPath(BaseElement from) { } return nexts; } + + private boolean hasFlowName(List elements, String flowName) { + if (StringUtils.isBlank(flowName)) { + return true; + } + + boolean existsFlowName = false; + for (BaseElement el : elements) { + if (el instanceof SequenceFlow) { + String label = ((SequenceFlow) el).getEdge().getLabel().getText(); + if(StringUtils.isNotBlank(label)) { + //Have label but not contain flowName -> Exit check + if(!label.contains(flowName)) { + return false; + } else { + existsFlowName = true; + } + } + } + } + + return existsFlowName; + } + + private boolean isAllEmptyCondition(List elements) { + + for (int i = 1; i < elements.size(); i++) { + var currentElement = elements.get(i); + var previousElement = elements.get(i - 1); + if (previousElement instanceof Alternative) { + String currentElementId = currentElement.getPid().getFieldId(); + String nextTargetId = getNextTargetIdByCondition((Alternative) previousElement, StringUtils.EMPTY); + if (!Objects.equal(currentElementId, nextTargetId)) { + return false; + } + } + } + + return true; + } + + private String getNextTargetIdByCondition(Alternative alternative, String condition) { + IvyScriptExpression script = IvyScriptExpression.script(defaultString(condition)); + String nextTargetId = alternative.getConditions().conditions().entrySet().stream() + .filter(entry -> script.equals(entry.getValue())) + .findFirst() + .map(Entry::getKey) + .orElse(null); + + return nextTargetId; + } } diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java index 5b771753..2ae00889 100644 --- a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java +++ b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java @@ -19,28 +19,31 @@ import ch.ivyteam.ivy.process.model.BaseElement; import ch.ivyteam.ivy.process.model.NodeElement; import ch.ivyteam.ivy.process.model.Process; -import ch.ivyteam.ivy.process.model.connector.SequenceFlow; import ch.ivyteam.ivy.process.model.element.activity.UserTask; import ch.ivyteam.ivy.process.model.element.value.task.TaskConfig; + @SuppressWarnings("restriction") public class WorkflowEstimator { private Process process; private Enum useCase; private String flowName; - + + public final ProcessGraph graph; + public WorkflowEstimator(Process process, Enum useCase, String flowName) { this.process = process; this.useCase = useCase; this.flowName = flowName; + this.graph = new ProcessGraph(process); } public List findAllTasks(BaseElement startAtElement) { List estimatedTasks = emptyList(); if (startAtElement instanceof NodeElement) { - List> paths = findPaths(startAtElement); + List> paths = graph.findPaths(startAtElement); estimatedTasks = convertToEstimatedTasks(paths); } @@ -51,35 +54,19 @@ public List findTasksOnPath(BaseElement startAtElement) { List estimatedTasks = emptyList(); if (startAtElement instanceof NodeElement) { - List> paths = findPaths(startAtElement, flowName); + List> paths = graph.findPaths(startAtElement, flowName); estimatedTasks = convertToEstimatedTasks(paths); } return estimatedTasks; } - - public List nextNodeElements(NodeElement from) { - var nexts = new ArrayList(); - - if (from != null) { - List outs = from.getOutgoing(); - for (SequenceFlow out : outs) { - NodeElement target = out.getTarget(); - List nextElements = nextNodeElements(target); - nexts.add(target); - nexts.addAll(nextElements); - } - } - - return nexts.stream().distinct().toList(); - } public Duration calculateEstimatedDuration(BaseElement startElement) { List> paths = emptyList(); - if (startElement instanceof NodeElement && flowName != null) { - paths = findPaths(startElement, flowName); + if (startElement instanceof NodeElement && StringUtils.isNotEmpty(flowName)) { + paths = graph.findPaths(startElement, flowName); } else { - paths = findPaths(startElement); + paths = graph.findPaths(startElement); } List estimatedTasks = convertToEstimatedTasks(paths); @@ -89,11 +76,7 @@ public Duration calculateEstimatedDuration(BaseElement startElement) { } return total; } - - private List> findPaths(BaseElement from) { - return findAllPaths(from); - } - + private List convertToEstimatedTasks(List> paths) { List tasks = paths.stream() // If have more than one path, we get the longest @@ -118,79 +101,6 @@ private List convertToEstimatedTasks(List> path return estimatedTasks; } - private List> findPaths(BaseElement from, String flowName) { - List> paths = findAllPaths(from); - - List> pathByFlowName = emptyList(); - if (StringUtils.isNotBlank(flowName)) { - pathByFlowName = paths.stream().filter(path -> hasFlowName(path, flowName)).toList(); - } else { - pathByFlowName = paths.stream().filter(path -> hasDefaultPath(path)).toList(); - } - - return pathByFlowName; - } - - private List> findAllPaths(BaseElement from) { - List> paths = findNextNodeElementPath(from); - - //Insert from element at first position - paths.forEach(path -> { - path.add(0, from); - }); - - return paths; - } - - private List> findNextNodeElementPath(BaseElement from) { - var nexts = new ArrayList>(); - - if (from != null && from instanceof NodeElement) { - List outs = ((NodeElement) from).getOutgoing(); - for (SequenceFlow out : outs) { - - NodeElement target = out.getTarget(); - List> nextElements = findNextNodeElementPath(target); - - if (nextElements.isEmpty()) { - nextElements.add(new ArrayList(Arrays.asList(target, out))); - } else { - nextElements.forEach(nodes -> { - nodes.add(0, target); - nodes.add(1, out); - }); - } - - nexts.addAll(nextElements); - } - } - return nexts; - } - - private boolean hasFlowName(List elements, String flowName) { - if (StringUtils.isBlank(flowName)) { - return true; - } - - boolean existsFlowName = false; - for (BaseElement el : elements) { - if (el instanceof SequenceFlow) { - String label = ((SequenceFlow) el).getEdge().getLabel().getText(); - if(StringUtils.isNotBlank(label)) { - //Have label but not contain flowName -> Exit check - if(!label.contains(flowName)) { - return false; - } else { - existsFlowName = true; - } - } - } - } - - return existsFlowName; - } - - private EstimatedTask createEstimatedTask(UserTask task, Date startTimestamp) { EstimatedTask estimatedTask = new EstimatedTask(); estimatedTask.setPid(task.getPid().getRawPid()); @@ -203,21 +113,7 @@ private EstimatedTask createEstimatedTask(UserTask task, Date startTimestamp) { return estimatedTask; } - - private boolean hasDefaultPath(List elements) { - String defaultPathColor = "default path"; - for (BaseElement el : elements) { - if (el instanceof SequenceFlow) { - String colorReference = ((SequenceFlow) el).getEdge().color().getReference(); - if (defaultPathColor.equals(colorReference)) { - return true; - } - } - } - - return false; - } - + private Duration getDurationByCode(TaskConfig task) { // strongly typed! String script = Optional.of(task.getScript()).orElse(EMPTY); From a447c847dd9394d81847af72c5e29aa25b904dab Mon Sep 17 00:00:00 2001 From: Trung Mai Date: Mon, 26 Feb 2024 16:01:12 +0700 Subject: [PATCH 020/143] TE-538: Unit test findTasksOnPath with mixed --- .../estimator/test/FlowExampleBasicTest.java | 32 +++---- .../axonivy/utils/estimator/ProcessGraph.java | 88 +++++++++++++++---- 2 files changed, 86 insertions(+), 34 deletions(-) diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleBasicTest.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleBasicTest.java index 89ac2bdf..dac82804 100644 --- a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleBasicTest.java +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleBasicTest.java @@ -112,7 +112,7 @@ void shouldFindAllTasksOfInternalFlowAtStart() { var workflowEstimator = new WorkflowEstimator(process, null, "internal"); var estimatedTasks = workflowEstimator.findAllTasks(start); - assertArrayEquals(Arrays.array("Task A", "Task B", "Task C"), getTaskNames(estimatedTasks)); + assertArrayEquals(Arrays.array("Task A", "Task C", "Task B"), getTaskNames(estimatedTasks)); } @Test @@ -147,21 +147,21 @@ void shouldFindTasksOnPathOfExternalFlowAtNewStart() { assertArrayEquals(Arrays.array("Task C", "Task B"), getTaskNames(estimatedTasks)); } -// @Test -// void shouldFindAllTasksOfMixedFlowAtStart() { -// var workflowEstimator = new WorkflowEstimator(process, null, "mixed"); -// var estimatedTasks = workflowEstimator.findTasksOnPath(start); -// -// assertArrayEquals(Arrays.array("Task A", "Task B"), getTaskNames(estimatedTasks)); -// } -// -// @Test -// void shouldFindAllTasksOfMixedFlowAtNewStart() { -// var workflowEstimator = new WorkflowEstimator(process, null, "mixed"); -// var estimatedTasks = workflowEstimator.findTasksOnPath(newStart); -// -// assertArrayEquals(Arrays.array("Task B"), getTaskNames(estimatedTasks)); -// } + @Test + void shouldFindAllTasksOfMixedFlowAtStart() { + var workflowEstimator = new WorkflowEstimator(process, null, "mixed"); + var estimatedTasks = workflowEstimator.findTasksOnPath(start); + + assertArrayEquals(Arrays.array("Task A", "Task B"), getTaskNames(estimatedTasks)); + } + + @Test + void shouldFindAllTasksOfMixedFlowAtNewStart() { + var workflowEstimator = new WorkflowEstimator(process, null, "mixed"); + var estimatedTasks = workflowEstimator.findTasksOnPath(newStart); + + assertArrayEquals(Arrays.array("Task B"), getTaskNames(estimatedTasks)); + } private String[] getTaskNames(List tasks ) { return tasks.stream().map(EstimatedTask::getTaskName).toArray(String[]::new); diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java b/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java index ba43779e..01a77ee9 100644 --- a/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java +++ b/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java @@ -1,6 +1,7 @@ package com.axonivy.utils.estimator; import static java.util.Collections.emptyList; +import static org.apache.commons.lang3.StringUtils.EMPTY; import static org.apache.commons.lang3.StringUtils.defaultString; import static org.apache.commons.lang3.StringUtils.isNotBlank; @@ -42,7 +43,7 @@ public List> findPaths(BaseElement from, String flowName) { List> pathByFlowName = emptyList(); if (isNotBlank(flowName)) { - pathByFlowName = paths.stream().filter(path -> hasFlowName(path, flowName)).toList(); + pathByFlowName = paths.stream().filter(path -> hasFlowNameOrDefaultPath(path, flowName)).toList(); } else { pathByFlowName = paths.stream().filter(path -> isAllEmptyCondition(path)).toList(); } @@ -86,38 +87,50 @@ private List> findNextNodeElementPath(BaseElement from) { return nexts; } - private boolean hasFlowName(List elements, String flowName) { + private boolean hasFlowNameOrDefaultPath(List elements, String flowName) { if (StringUtils.isBlank(flowName)) { return true; } - boolean existsFlowName = false; - for (BaseElement el : elements) { - if (el instanceof SequenceFlow) { - String label = ((SequenceFlow) el).getEdge().getLabel().getText(); - if(StringUtils.isNotBlank(label)) { - //Have label but not contain flowName -> Exit check - if(!label.contains(flowName)) { - return false; - } else { - existsFlowName = true; - } + for (int i = 0; i < elements.size(); i++) { + var currentElement = elements.get(i); + var previousElement = i > 0 ? elements.get(i - 1) : null; + if (currentElement instanceof SequenceFlow) { + var passedFlowNameCheck = hasFlowNameOrEmpty((SequenceFlow) currentElement, flowName); + + var passedDefaultPathCheck = false; + if (previousElement instanceof Alternative) { + passedDefaultPathCheck = isDefaultPath(currentElement, (Alternative) previousElement); + } + + if (!passedFlowNameCheck && !passedDefaultPathCheck) { + return false; } } } - return existsFlowName; + return true; } + private boolean hasFlowNameOrEmpty(SequenceFlow sequenceFlow, String flowName) { + + String label = sequenceFlow.getEdge().getLabel().getText(); + if (isNotBlank(label)) { + if (!label.contains(flowName)) { + return false; + } + } + + return true; + } + private boolean isAllEmptyCondition(List elements) { for (int i = 1; i < elements.size(); i++) { var currentElement = elements.get(i); var previousElement = elements.get(i - 1); - if (previousElement instanceof Alternative) { - String currentElementId = currentElement.getPid().getFieldId(); - String nextTargetId = getNextTargetIdByCondition((Alternative) previousElement, StringUtils.EMPTY); - if (!Objects.equal(currentElementId, nextTargetId)) { + if (previousElement instanceof Alternative) { + if (!isDefaultPath(currentElement, (Alternative) previousElement)) { return false; } } @@ -126,6 +139,12 @@ private boolean isAllEmptyCondition(List elements) { return true; } + private boolean isDefaultPath(BaseElement currentElement, Alternative previousElement) { + String currentElementId = currentElement.getPid().getFieldId(); + String nextTargetId = getNextTargetIdByCondition((Alternative) previousElement, EMPTY); + return Objects.equal(currentElementId, nextTargetId); + } + private String getNextTargetIdByCondition(Alternative alternative, String condition) { IvyScriptExpression script = IvyScriptExpression.script(defaultString(condition)); String nextTargetId = alternative.getConditions().conditions().entrySet().stream() @@ -136,4 +155,37 @@ private String getNextTargetIdByCondition(Alternative alternative, String condit return nextTargetId; } + + + /** + * Find base on start node and check condition at each + * @param from + * @param flowName + * @return + */ +// public List findPathByFlowName(BaseElement from, String flowName) { +// List path = new ArrayList<>(); +// path.add(from); +// +// +// if (from instanceof NodeElement) { +// NodeElement currentNode = (NodeElement) from; +// List outs = currentNode.getOutgoing(); +// while (outs.size() > 0) { +// SequenceFlow flow = findCorrectSequenceFlow(outs, flowName); +// +// if(flow == null) { +// break; +// } +// +// path.add(from); +// +// } +// } +// return emptyList(); +// } +// +// private SequenceFlow findCorrectSequenceFlow(List outs, String flowName) { +// return outs.stream().filter(out -> hasFlowNameOrEmpty(out, flowName)).findFirst().orElse(null); +// } } From 2b681438d6adb4f031b0d8803ed5deeaff03fb89 Mon Sep 17 00:00:00 2001 From: Timo Rupp Date: Mon, 26 Feb 2024 14:19:30 +0100 Subject: [PATCH 021/143] Add example for embedded sub-processes. --- .../processes/FlowSubprocess.p.json | 155 ++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 workflow-estimator-test/processes/FlowSubprocess.p.json diff --git a/workflow-estimator-test/processes/FlowSubprocess.p.json b/workflow-estimator-test/processes/FlowSubprocess.p.json new file mode 100644 index 00000000..0107bc61 --- /dev/null +++ b/workflow-estimator-test/processes/FlowSubprocess.p.json @@ -0,0 +1,155 @@ +{ + "$schema" : "https://json-schema.axonivy.com/process/11.2.2/process.json", + "id" : "18DE58E0441486DF", + "config" : { + "data" : "com.axonivy.utils.estimator.test.Data" + }, + "elements" : [ { + "id" : "f0", + "type" : "RequestStart", + "name" : "start", + "config" : { + "signature" : "start" + }, + "visual" : { + "at" : { "x" : 96, "y" : 64 } + }, + "connect" : [ + { "id" : "f2", "to" : "S10" } + ] + }, { + "id" : "f1", + "type" : "TaskEnd", + "visual" : { + "at" : { "x" : 688, "y" : 64 } + } + }, { + "id" : "S10", + "type" : "GenericBpmnElement", + "name" : "sub with two levels", + "elements" : [ { + "id" : "S10-g0", + "type" : "EmbeddedStart", + "visual" : { + "at" : { "x" : 64, "y" : 256 } + }, + "parentConnector" : "f2", + "connect" : [ + { "id" : "S10-f0", "to" : "S10-S10" } + ] + }, { + "id" : "S10-g1", + "type" : "EmbeddedEnd", + "visual" : { + "at" : { "x" : 576, "y" : 256 } + }, + "parentConnector" : "f3" + }, { + "id" : "S10-S10", + "type" : "UserBpmnElement", + "name" : "2nd level sub", + "elements" : [ { + "id" : "S10-S10-g0", + "type" : "EmbeddedStart", + "visual" : { + "at" : { "x" : 64, "y" : 272 } + }, + "parentConnector" : "S10-f0", + "connect" : [ + { "id" : "S10-S10-f1", "to" : "S10-S10-f0" } + ] + }, { + "id" : "S10-S10-g1", + "type" : "EmbeddedEnd", + "visual" : { + "at" : { "x" : 576, "y" : 272 } + }, + "parentConnector" : "S10-f1" + }, { + "id" : "S10-S10-f0", + "type" : "UserTask", + "name" : "Task A", + "config" : { + "task" : { + "name" : "Task A" + } + }, + "visual" : { + "at" : { "x" : 320, "y" : 272 } + }, + "connect" : [ + { "id" : "S10-S10-f2", "to" : "S10-S10-g1" } + ] + } ], + "visual" : { + "at" : { "x" : 312, "y" : 256 } + }, + "connect" : [ + { "id" : "S10-f1", "to" : "S10-g1" } + ] + } ], + "visual" : { + "at" : { "x" : 272, "y" : 64 } + }, + "connect" : [ + { "id" : "f3", "to" : "S20" } + ] + }, { + "id" : "S20", + "type" : "ManualBpmnElement", + "name" : "sub with one level", + "elements" : [ { + "id" : "S20-g0", + "type" : "EmbeddedStart", + "visual" : { + "at" : { "x" : 64, "y" : 256 } + }, + "parentConnector" : "f3", + "connect" : [ + { "id" : "S20-f0", "to" : "S20-f1" } + ] + }, { + "id" : "S20-g1", + "type" : "EmbeddedEnd", + "visual" : { + "at" : { "x" : 576, "y" : 256 } + }, + "parentConnector" : "f4" + }, { + "id" : "S20-f1", + "type" : "TaskSwitchEvent", + "name" : "Task B", + "config" : { + "task" : { + "name" : "Task B" + } + }, + "visual" : { + "at" : { "x" : 336, "y" : 256 }, + "labelOffset" : { "x" : 14, "y" : 34 } + }, + "connect" : [ + { "id" : "S20-f2", "to" : "S20-g1" } + ] + } ], + "visual" : { + "at" : { "x" : 520, "y" : 64 } + }, + "connect" : [ + { "id" : "f4", "to" : "f1" } + ] + }, { + "id" : "f19", + "type" : "ProcessAnnotation", + "name" : [ + "useCase=null / flowName = null", + "", + "findAllTasks( start.ivp ) => TaskA, TaskB", + "findTasksOnPath( start.ivp ) => TaskA, TaskB" + ], + "visual" : { + "at" : { "x" : 322, "y" : 206 }, + "size" : { "width" : 476, "height" : 97 } + } + } ] +} \ No newline at end of file From 1dc09539223d58861a1b962d52bf4730b259fb21 Mon Sep 17 00:00:00 2001 From: "AAVN\\ntnchuong" Date: Tue, 27 Feb 2024 09:25:48 +0700 Subject: [PATCH 022/143] TE-538: modify logic and add unit test --- workflow-estimator-test/.gitignore | 3 + .../processes/FlowExampleError.p.json | 2 +- .../processes/ParallelTasksExample.p.json | 53 +++++++--------- .../processes/TaskTypesExample.p.json | 2 +- .../estimator/test/FlowExampleBasicTest.java | 35 ++++------- .../estimator/test/FlowExampleErrorTest.java | 55 +++++++++++++++++ .../estimator/test/FlowExampleLoopTest.java | 57 ++++++++++++++++++ .../utils/estimator/test/FlowExampleTest.java | 26 ++++++++ .../test/ParallelTasksExampleTest.java | 60 +++++++++++++++++++ .../estimator/test/TaskTypesExampleTest.java | 48 +++++++++++++++ .../utils/estimator/WorkflowEstimator.java | 59 +++++++++++------- 11 files changed, 326 insertions(+), 74 deletions(-) create mode 100644 workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleErrorTest.java create mode 100644 workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleLoopTest.java create mode 100644 workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleTest.java create mode 100644 workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/ParallelTasksExampleTest.java create mode 100644 workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/TaskTypesExampleTest.java diff --git a/workflow-estimator-test/.gitignore b/workflow-estimator-test/.gitignore index 1b2547b2..c75b6862 100644 --- a/workflow-estimator-test/.gitignore +++ b/workflow-estimator-test/.gitignore @@ -17,3 +17,6 @@ classes/ src_dataClasses/ src_wsproc/ logs/ + +# test +.temp-Rerun* \ No newline at end of file diff --git a/workflow-estimator-test/processes/FlowExampleError.p.json b/workflow-estimator-test/processes/FlowExampleError.p.json index 61c71423..cb1ea66e 100644 --- a/workflow-estimator-test/processes/FlowExampleError.p.json +++ b/workflow-estimator-test/processes/FlowExampleError.p.json @@ -67,7 +67,7 @@ "name" : [ "useCase=null / flowName = null", "", - "findTasksOnPath( start.ivp ) => Exception", + "findTasksOnPath( start ) => Exception", "", "----", "", diff --git a/workflow-estimator-test/processes/ParallelTasksExample.p.json b/workflow-estimator-test/processes/ParallelTasksExample.p.json index 69b92fc8..fdb5f716 100644 --- a/workflow-estimator-test/processes/ParallelTasksExample.p.json +++ b/workflow-estimator-test/processes/ParallelTasksExample.p.json @@ -29,7 +29,8 @@ "name" : "Task1", "config" : { "tasks" : [ { - "id" : "TaskA" + "id" : "TaskA", + "name" : "Task1A" }, { "id" : "TaskB", "name" : "Task1B" @@ -40,28 +41,8 @@ "labelOffset" : { "x" : 14, "y" : 34 } }, "connect" : [ - { "id" : "f5", "to" : "f4", "condition" : "ivp==\"TaskA.ivp\"", "var" : "in1" }, - { "id" : "f8", "to" : "f7", "condition" : "ivp==\"TaskB.ivp\"" } - ] - }, { - "id" : "f4", - "type" : "TaskSwitchGateway", - "name" : "Join", - "config" : { - "tasks" : [ { - "id" : "TaskA", - "name" : "JoiningTask", - "responsible" : { - "activator" : "SYSTEM" - } - } ] - }, - "visual" : { - "at" : { "x" : 1056, "y" : 96 }, - "labelOffset" : { "x" : 14, "y" : 34 } - }, - "connect" : [ - { "id" : "f6", "to" : "f1", "condition" : "ivp==\"TaskA.ivp\"" } + { "id" : "f8", "to" : "f7", "condition" : "ivp==\"TaskB.ivp\"" }, + { "id" : "f5", "to" : "f21", "condition" : "ivp==\"TaskA.ivp\"", "var" : "in1" } ] }, { "id" : "f7", @@ -79,17 +60,18 @@ "type" : "Alternative", "config" : { "conditions" : { - "f14" : "true" + "f14" : "true", + "f11" : "" } }, "visual" : { "at" : { "x" : 480, "y" : 168 } }, "connect" : [ - { "id" : "f14", "to" : "f4", "label" : { + { "id" : "f11", "to" : "f15", "color" : "def", "var" : "in2" }, + { "id" : "f14", "to" : "f21", "label" : { "name" : "{shortcut}" - }, "var" : "in3" }, - { "id" : "f11", "to" : "f15", "color" : "def", "var" : "in2" } + }, "var" : "in3" } ] }, { "id" : "f15", @@ -114,8 +96,8 @@ "labelOffset" : { "x" : 14, "y" : 34 } }, "connect" : [ - { "id" : "f12", "to" : "f4", "condition" : "ivp==\"TaskA.ivp\"", "var" : "in2" }, - { "id" : "f16", "to" : "f13", "condition" : "ivp==\"TaskB.ivp\"" } + { "id" : "f16", "to" : "f13", "condition" : "ivp==\"TaskB.ivp\"" }, + { "id" : "f12", "to" : "f21", "condition" : "ivp==\"TaskA.ivp\"", "var" : "in2" } ] }, { "id" : "f13", @@ -124,7 +106,7 @@ "at" : { "x" : 840, "y" : 296 } }, "connect" : [ - { "id" : "f17", "to" : "f4", "via" : [ { "x" : 1056, "y" : 296 } ], "var" : "in4" } + { "id" : "f17", "to" : "f21", "via" : [ { "x" : 1056, "y" : 296 } ], "var" : "in4" } ] }, { "id" : "f18", @@ -167,6 +149,17 @@ "at" : { "x" : 853, "y" : 461 }, "size" : { "width" : 407, "height" : 158 } } + }, { + "id" : "f21", + "type" : "Join", + "name" : "Join", + "visual" : { + "at" : { "x" : 1056, "y" : 96 }, + "labelOffset" : { "x" : 16, "y" : -16 } + }, + "connect" : [ + { "id" : "f4", "to" : "f1" } + ] } ], "layout" : { "colors" : { diff --git a/workflow-estimator-test/processes/TaskTypesExample.p.json b/workflow-estimator-test/processes/TaskTypesExample.p.json index 3db0b9d9..3e3e4cc9 100644 --- a/workflow-estimator-test/processes/TaskTypesExample.p.json +++ b/workflow-estimator-test/processes/TaskTypesExample.p.json @@ -61,7 +61,7 @@ "config" : { "tasks" : [ { "id" : "TaskA", - "name" : "Tasks-Task1" + "name" : "Tasks-TaskA" }, { "id" : "TaskB", "name" : "Tasks-TaskB" diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleBasicTest.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleBasicTest.java index dac82804..97100153 100644 --- a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleBasicTest.java +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleBasicTest.java @@ -21,22 +21,17 @@ @IvyTest @SuppressWarnings("restriction") -public class FlowExampleBasicTest { +public class FlowExampleBasicTest extends FlowExampleTest { - private static Process process; - private static ProcessGraph graph; private static BaseElement start; private static BaseElement newStart; private static BaseElement taskB; private static BaseElement taskC; + private static final String PROCESS_NAME = "FlowExampleBasic"; @BeforeAll public static void setup() { - var pmv = Ivy.request().getProcessModelVersion(); - var manager = IProcessManager.instance().getProjectDataModelFor(pmv); - process = manager.findProcessByPath("FlowExampleBasic").getModel(); - graph = new ProcessGraph(process); - + setup(PROCESS_NAME); start = graph.findByElementName("start"); newStart = graph.findByElementName("NewStart"); taskB = graph.findByElementName("Task B"); @@ -76,7 +71,7 @@ void shouldFindAllTasksAtNewStart() { } @Test - void shouldFindTasksOnPathWithoutFlowNameAtStart() { + void shouldFindTasksOnPathWithoutFlowNameAtStart() throws Exception { var workflowEstimator = new WorkflowEstimator(process, null, null); var estimatedTasks = workflowEstimator.findTasksOnPath(start); @@ -84,7 +79,7 @@ void shouldFindTasksOnPathWithoutFlowNameAtStart() { } @Test - void shouldFindTasksOnPathWithoutFlowNameAtTaskB() { + void shouldFindTasksOnPathWithoutFlowNameAtTaskB() throws Exception { var workflowEstimator = new WorkflowEstimator(process, null, null); var estimatedTasks = workflowEstimator.findTasksOnPath(taskB); @@ -92,7 +87,7 @@ void shouldFindTasksOnPathWithoutFlowNameAtTaskB() { } @Test - void shouldFindTasksOnPathWithoutFlowNameAtTaskC() { + void shouldFindTasksOnPathWithoutFlowNameAtTaskC() throws Exception { var workflowEstimator = new WorkflowEstimator(process, null, null); var estimatedTasks = workflowEstimator.findTasksOnPath(taskC); @@ -100,7 +95,7 @@ void shouldFindTasksOnPathWithoutFlowNameAtTaskC() { } @Test - void shouldFindTasksOnPathWithoutFlowNameAtNewStart() { + void shouldFindTasksOnPathWithoutFlowNameAtNewStart() throws Exception { var workflowEstimator = new WorkflowEstimator(process, null, null); var estimatedTasks = workflowEstimator.findTasksOnPath(newStart); @@ -116,7 +111,7 @@ void shouldFindAllTasksOfInternalFlowAtStart() { } @Test - void shouldFindTasksOnPathOfInternalFlowAtStart() { + void shouldFindTasksOnPathOfInternalFlowAtStart() throws Exception { var workflowEstimator = new WorkflowEstimator(process, null, "internal"); var estimatedTasks = workflowEstimator.findTasksOnPath(start); @@ -124,7 +119,7 @@ void shouldFindTasksOnPathOfInternalFlowAtStart() { } @Test - void shouldFindTasksOnPathOfInternalFlowAtNewStart() { + void shouldFindTasksOnPathOfInternalFlowAtNewStart() throws Exception { var workflowEstimator = new WorkflowEstimator(process, null, "internal"); var estimatedTasks = workflowEstimator.findTasksOnPath(newStart); @@ -132,7 +127,7 @@ void shouldFindTasksOnPathOfInternalFlowAtNewStart() { } @Test - void shouldFindAllTasksOfExternalFlowAtStart() { + void shouldFindAllTasksOfExternalFlowAtStart() throws Exception { var workflowEstimator = new WorkflowEstimator(process, null, "external"); var estimatedTasks = workflowEstimator.findTasksOnPath(start); @@ -140,7 +135,7 @@ void shouldFindAllTasksOfExternalFlowAtStart() { } @Test - void shouldFindTasksOnPathOfExternalFlowAtNewStart() { + void shouldFindTasksOnPathOfExternalFlowAtNewStart() throws Exception { var workflowEstimator = new WorkflowEstimator(process, null, "external"); var estimatedTasks = workflowEstimator.findTasksOnPath(newStart); @@ -148,7 +143,7 @@ void shouldFindTasksOnPathOfExternalFlowAtNewStart() { } @Test - void shouldFindAllTasksOfMixedFlowAtStart() { + void shouldFindAllTasksOfMixedFlowAtStart() throws Exception { var workflowEstimator = new WorkflowEstimator(process, null, "mixed"); var estimatedTasks = workflowEstimator.findTasksOnPath(start); @@ -156,17 +151,13 @@ void shouldFindAllTasksOfMixedFlowAtStart() { } @Test - void shouldFindAllTasksOfMixedFlowAtNewStart() { + void shouldFindAllTasksOfMixedFlowAtNewStart() throws Exception { var workflowEstimator = new WorkflowEstimator(process, null, "mixed"); var estimatedTasks = workflowEstimator.findTasksOnPath(newStart); assertArrayEquals(Arrays.array("Task B"), getTaskNames(estimatedTasks)); } - private String[] getTaskNames(List tasks ) { - return tasks.stream().map(EstimatedTask::getTaskName).toArray(String[]::new); - } - @Test void shouldCalculateTotalDuration() { var workflowEstimator = new WorkflowEstimator(process, null, null); diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleErrorTest.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleErrorTest.java new file mode 100644 index 00000000..9d959d6e --- /dev/null +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleErrorTest.java @@ -0,0 +1,55 @@ +package com.axonivy.utils.estimator.test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.List; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import com.axonivy.utils.estimator.WorkflowEstimator; +import com.axonivy.utils.estimator.model.EstimatedTask; + +import ch.ivyteam.ivy.environment.Ivy; +import ch.ivyteam.ivy.environment.IvyTest; +import ch.ivyteam.ivy.process.model.BaseElement; +import ch.ivyteam.ivy.process.rdm.IProcessManager; + +@IvyTest +@SuppressWarnings("restriction") +public class FlowExampleErrorTest extends FlowExampleTest { + + private static BaseElement start; + private static final String PROCESS_NAME = "FlowExampleError"; + + @BeforeAll + public static void setup() { + setup(PROCESS_NAME); + start = graph.findByElementName("start"); + } + + @Test + void shouldFindAllTasksOnPathAtStartWithFlowNameSuccess() throws Exception { + var workflowEstimator = new WorkflowEstimator(process, null, "success"); + List estimatedTasks = workflowEstimator.findTasksOnPath(start); + + assertEquals(1, estimatedTasks.size()); + assertEquals("Task A", getTaskNames(estimatedTasks)[0]); + } + + @Test + void shouldFindAllTasksOnPathAtStartWithFlowNameNull() { + var workflowEstimator = new WorkflowEstimator(process, null, null); + + Exception exception = assertThrows(Exception.class, () -> { + workflowEstimator.findTasksOnPath(start); + }); + + String expectedMessage = "Not found"; + String actualMessage = exception.getMessage(); + + assertEquals(expectedMessage, actualMessage); + } + +} diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleLoopTest.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleLoopTest.java new file mode 100644 index 00000000..ae2bade7 --- /dev/null +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleLoopTest.java @@ -0,0 +1,57 @@ +package com.axonivy.utils.estimator.test; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; + +import org.assertj.core.util.Arrays; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import com.axonivy.utils.estimator.WorkflowEstimator; +import com.axonivy.utils.estimator.model.EstimatedTask; + +import ch.ivyteam.ivy.environment.Ivy; +import ch.ivyteam.ivy.environment.IvyTest; +import ch.ivyteam.ivy.process.model.BaseElement; +import ch.ivyteam.ivy.process.rdm.IProcessManager; + +@IvyTest +@SuppressWarnings("restriction") +public class FlowExampleLoopTest extends FlowExampleTest { + + private static BaseElement start; + private static final String PROCESS_NAME = "FlowExampleLoop"; + + @BeforeAll + public static void setup() { + setup(PROCESS_NAME); + start = graph.findByElementName("start"); + } + + @Test + void shouldFindAllTasksOnPathAtStartWithFlowNameNull() throws Exception { + var workflowEstimator = new WorkflowEstimator(process, null, null); + List estimatedTasks = workflowEstimator.findTasksOnPath(start); + + assertArrayEquals(Arrays.array("Task A", "Task B"), getTaskNames(estimatedTasks)); + } + + @Test + void shouldFindAllTasksStartWithFlowNameNull() throws Exception { + var workflowEstimator = new WorkflowEstimator(process, null, null); + List estimatedTasks = workflowEstimator.findAllTasks(start); + + assertArrayEquals(Arrays.array("Task A", "Task B"), getTaskNames(estimatedTasks)); + } + + @Test + void shouldFindAllTasksOnPathAtStartWithFlowNameSuccess() throws Exception { + var workflowEstimator = new WorkflowEstimator(process, null, "success"); + List estimatedTasks = workflowEstimator.findTasksOnPath(start); + + assertArrayEquals(Arrays.array("UserTask", "Task", "Tasks-TaskA", "Tasks-TaskB"), getTaskNames(estimatedTasks)); + } + +} diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleTest.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleTest.java new file mode 100644 index 00000000..bd153561 --- /dev/null +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleTest.java @@ -0,0 +1,26 @@ +package com.axonivy.utils.estimator.test; + +import java.util.List; + +import com.axonivy.utils.estimator.model.EstimatedTask; + +import ch.ivyteam.ivy.environment.Ivy; +import ch.ivyteam.ivy.process.model.Process; +import ch.ivyteam.ivy.process.rdm.IProcessManager; + +public abstract class FlowExampleTest { + + protected static Process process; + protected static ProcessGraph graph; + + protected static void setup(String processName) { + var pmv = Ivy.request().getProcessModelVersion(); + var manager = IProcessManager.instance().getProjectDataModelFor(pmv); + process = manager.findProcessByPath(processName).getModel(); + graph = new ProcessGraph(process); + } + + protected String[] getTaskNames(List tasks ) { + return tasks.stream().map(EstimatedTask::getTaskName).toArray(String[]::new); + } +} diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/ParallelTasksExampleTest.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/ParallelTasksExampleTest.java new file mode 100644 index 00000000..f88604f2 --- /dev/null +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/ParallelTasksExampleTest.java @@ -0,0 +1,60 @@ +package com.axonivy.utils.estimator.test; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; + +import org.assertj.core.util.Arrays; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import com.axonivy.utils.estimator.WorkflowEstimator; +import com.axonivy.utils.estimator.model.EstimatedTask; + +import ch.ivyteam.ivy.environment.Ivy; +import ch.ivyteam.ivy.environment.IvyTest; +import ch.ivyteam.ivy.process.model.BaseElement; +import ch.ivyteam.ivy.process.rdm.IProcessManager; + +@IvyTest +@SuppressWarnings("restriction") +public class ParallelTasksExampleTest extends FlowExampleTest { + + private static BaseElement start; + private static final String PROCESS_NAME = "ParallelTasksExample"; + + @BeforeAll + public static void setup() { + setup(PROCESS_NAME); + start = graph.findByElementName("start"); + } + + @Test + void shouldFindAllTasksAtStartWithFlowNameNull() throws Exception { + var workflowEstimator = new WorkflowEstimator(process, null, null); + List estimatedTasks = workflowEstimator.findAllTasks(start); + + var names = getTaskNames(estimatedTasks); + assertArrayEquals(Arrays.array("Task1A", "Task1B", "Task2", "Task3A", "Task3B"), names); + } + + @Test + void shouldFindAllTasksOnPathAtStartWithFlowNameNull() throws Exception { + var workflowEstimator = new WorkflowEstimator(process, null, null); + List estimatedTasks = workflowEstimator.findTasksOnPath(start); + + var names = getTaskNames(estimatedTasks); + assertArrayEquals(Arrays.array("Task1A", "Task1B", "Task2", "Task3A", "Task3B"), names); + } + + @Test + void shouldFindAllTasksAtStartWithFlowNameShortcut() throws Exception { + var workflowEstimator = new WorkflowEstimator(process, null, "shortcut"); + List estimatedTasks = workflowEstimator.findTasksOnPath(start); + + var names = getTaskNames(estimatedTasks); + assertArrayEquals(Arrays.array("Task1A", "Task1B", "Task2"), names); + } + +} diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/TaskTypesExampleTest.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/TaskTypesExampleTest.java new file mode 100644 index 00000000..18dfda69 --- /dev/null +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/TaskTypesExampleTest.java @@ -0,0 +1,48 @@ +package com.axonivy.utils.estimator.test; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import java.util.List; + +import org.assertj.core.util.Arrays; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import com.axonivy.utils.estimator.WorkflowEstimator; +import com.axonivy.utils.estimator.model.EstimatedTask; + +import ch.ivyteam.ivy.environment.IvyTest; +import ch.ivyteam.ivy.process.model.BaseElement; + +@IvyTest +@SuppressWarnings("restriction") +public class TaskTypesExampleTest extends FlowExampleTest { + + private static BaseElement start; + private static final String PROCESS_NAME = "TaskTypesExample"; + + @BeforeAll + public static void setup() { + setup(PROCESS_NAME); + start = graph.findByElementName("start"); + } + + @Test + void shouldFindAllTasksOnPathAtStartWithFlowNameNull() throws Exception { + var workflowEstimator = new WorkflowEstimator(process, null, null); + List estimatedTasks = workflowEstimator.findTasksOnPath(start); + + var names = getTaskNames(estimatedTasks); + assertArrayEquals(Arrays.array("UserTask", "Task", "Tasks-TaskA", "Tasks-TaskB"), names); + } + + @Test + void shouldFindAllTasksAtStartWithFlowNameNull() throws Exception { + var workflowEstimator = new WorkflowEstimator(process, null, null); + List estimatedTasks = workflowEstimator.findAllTasks(start); + + var names = getTaskNames(estimatedTasks); + assertArrayEquals(Arrays.array("UserTask", "Task", "Tasks-TaskA", "Tasks-TaskB"), names); + } + +} diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java index 2ae00889..fd0e030e 100644 --- a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java +++ b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java @@ -2,6 +2,7 @@ import static java.util.Collections.emptyList; import static org.apache.commons.lang3.StringUtils.EMPTY; +import static org.apache.commons.lang3.StringUtils.defaultIfEmpty; import java.time.Duration; import java.util.ArrayList; @@ -19,7 +20,8 @@ import ch.ivyteam.ivy.process.model.BaseElement; import ch.ivyteam.ivy.process.model.NodeElement; import ch.ivyteam.ivy.process.model.Process; -import ch.ivyteam.ivy.process.model.element.activity.UserTask; +import ch.ivyteam.ivy.process.model.element.TaskAndCaseModifier; +import ch.ivyteam.ivy.process.model.element.event.start.RequestStart; import ch.ivyteam.ivy.process.model.element.value.task.TaskConfig; @@ -50,11 +52,16 @@ public List findAllTasks(BaseElement startAtElement) { return estimatedTasks; } - public List findTasksOnPath(BaseElement startAtElement) { + public List findTasksOnPath(BaseElement startAtElement) throws Exception { List estimatedTasks = emptyList(); if (startAtElement instanceof NodeElement) { List> paths = graph.findPaths(startAtElement, flowName); + //Maybe throw an exception if there are not path + if(paths.isEmpty()) { + throw new Exception("Not found"); + } + estimatedTasks = convertToEstimatedTasks(paths); } @@ -78,40 +85,52 @@ public Duration calculateEstimatedDuration(BaseElement startElement) { } private List convertToEstimatedTasks(List> paths) { - List tasks = paths.stream() + + List taskCaseModifier = paths.stream() // If have more than one path, we get the longest .max(Comparator.comparingInt(List::size)).orElse(emptyList()).stream() // filter to get task only .filter(node -> { - return node instanceof UserTask; + return node instanceof TaskAndCaseModifier; + }) + .filter(node -> { + return node instanceof RequestStart == false; }) - .map(UserTask.class::cast) + .map(TaskAndCaseModifier.class::cast) // filter the task which have estimated if needed .toList(); //Convert to EstimatedTask List estimatedTasks = new ArrayList<>(); - for(int i = 0; i < tasks.size(); i ++) { - EstimatedTask estimatedTask = new EstimatedTask(); + for(int i = 0; i < taskCaseModifier.size(); i ++) { + List estimatedTaskResults = new ArrayList<>(); Date startTimestamp = i == 0 ? new Date() : estimatedTasks.get(i - 1).calculateEstimatedEndTimestamp(); - estimatedTask = createEstimatedTask(tasks.get(i), startTimestamp); + estimatedTaskResults = createEstimatedTask(taskCaseModifier.get(i), startTimestamp); - estimatedTasks.add(estimatedTask); + estimatedTasks.addAll(estimatedTaskResults); } return estimatedTasks; } - private EstimatedTask createEstimatedTask(UserTask task, Date startTimestamp) { - EstimatedTask estimatedTask = new EstimatedTask(); - estimatedTask.setPid(task.getPid().getRawPid()); - estimatedTask.setTaskName(task.getTaskConfig().getName().getRawMacro()); - estimatedTask.setParentElementNames(emptyList()); - - Duration estimatedDuration = getDurationByCode(task.getTaskConfig()); - estimatedTask.setEstimatedDuration(estimatedDuration); - estimatedTask.setEstimatedStartTimestamp(startTimestamp); - - return estimatedTask; + private List createEstimatedTask(TaskAndCaseModifier task, Date startTimestamp) { + List taskConfigs = task.getAllTaskConfigs(); + + List estimatedTasks = new ArrayList<>(); + + taskConfigs.forEach(item -> { + EstimatedTask estimatedTask = new EstimatedTask(); + + estimatedTask.setPid(task.getPid().getRawPid()); + estimatedTask.setParentElementNames(emptyList()); + estimatedTask.setTaskName(defaultIfEmpty(item.getName().getRawMacro(), task.getName())); + Duration estimatedDuration = getDurationByCode(item); + estimatedTask.setEstimatedDuration(estimatedDuration); + estimatedTask.setEstimatedStartTimestamp(startTimestamp); + + estimatedTasks.add(estimatedTask); + }); + + return estimatedTasks; } private Duration getDurationByCode(TaskConfig task) { From 84a113bf42fab1d919030104dd7287169b2e2c39 Mon Sep 17 00:00:00 2001 From: Trung Mai Date: Tue, 27 Feb 2024 15:46:40 +0700 Subject: [PATCH 023/143] TE-538: Fix to prevent loop --- .../estimator/test/FlowExampleBasicTest.java | 7 +- .../estimator/test/FlowExampleErrorTest.java | 6 +- .../estimator/test/FlowExampleLoopTest.java | 9 +- .../test/ParallelTasksExampleTest.java | 3 - .../axonivy/utils/estimator/ProcessGraph.java | 315 +++++++++++------- .../utils/estimator/WorkflowEstimator.java | 2 +- .../utils/estimator/model/EstimatedTask.java | 7 + 7 files changed, 214 insertions(+), 135 deletions(-) diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleBasicTest.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleBasicTest.java index 97100153..e2913c41 100644 --- a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleBasicTest.java +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleBasicTest.java @@ -13,11 +13,8 @@ import com.axonivy.utils.estimator.WorkflowEstimator; import com.axonivy.utils.estimator.model.EstimatedTask; -import ch.ivyteam.ivy.environment.Ivy; import ch.ivyteam.ivy.environment.IvyTest; import ch.ivyteam.ivy.process.model.BaseElement; -import ch.ivyteam.ivy.process.model.Process; -import ch.ivyteam.ivy.process.rdm.IProcessManager; @IvyTest @SuppressWarnings("restriction") @@ -143,7 +140,7 @@ void shouldFindTasksOnPathOfExternalFlowAtNewStart() throws Exception { } @Test - void shouldFindAllTasksOfMixedFlowAtStart() throws Exception { + void shouldFindTasksOnPathOfMixedFlowAtStart() throws Exception { var workflowEstimator = new WorkflowEstimator(process, null, "mixed"); var estimatedTasks = workflowEstimator.findTasksOnPath(start); @@ -151,7 +148,7 @@ void shouldFindAllTasksOfMixedFlowAtStart() throws Exception { } @Test - void shouldFindAllTasksOfMixedFlowAtNewStart() throws Exception { + void shouldFindTasksOnPathOfMixedFlowAtNewStart() throws Exception { var workflowEstimator = new WorkflowEstimator(process, null, "mixed"); var estimatedTasks = workflowEstimator.findTasksOnPath(newStart); diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleErrorTest.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleErrorTest.java index 9d959d6e..ddfb6a63 100644 --- a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleErrorTest.java +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleErrorTest.java @@ -11,10 +11,8 @@ import com.axonivy.utils.estimator.WorkflowEstimator; import com.axonivy.utils.estimator.model.EstimatedTask; -import ch.ivyteam.ivy.environment.Ivy; import ch.ivyteam.ivy.environment.IvyTest; import ch.ivyteam.ivy.process.model.BaseElement; -import ch.ivyteam.ivy.process.rdm.IProcessManager; @IvyTest @SuppressWarnings("restriction") @@ -30,7 +28,7 @@ public static void setup() { } @Test - void shouldFindAllTasksOnPathAtStartWithFlowNameSuccess() throws Exception { + void shouldFindTasksOnPathAtStartWithFlowNameSuccess() throws Exception { var workflowEstimator = new WorkflowEstimator(process, null, "success"); List estimatedTasks = workflowEstimator.findTasksOnPath(start); @@ -39,7 +37,7 @@ void shouldFindAllTasksOnPathAtStartWithFlowNameSuccess() throws Exception { } @Test - void shouldFindAllTasksOnPathAtStartWithFlowNameNull() { + void shouldFindTasksOnPathAtStartWithFlowNameNull() { var workflowEstimator = new WorkflowEstimator(process, null, null); Exception exception = assertThrows(Exception.class, () -> { diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleLoopTest.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleLoopTest.java index ae2bade7..4b55b296 100644 --- a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleLoopTest.java +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleLoopTest.java @@ -1,7 +1,6 @@ package com.axonivy.utils.estimator.test; import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.List; @@ -12,10 +11,8 @@ import com.axonivy.utils.estimator.WorkflowEstimator; import com.axonivy.utils.estimator.model.EstimatedTask; -import ch.ivyteam.ivy.environment.Ivy; import ch.ivyteam.ivy.environment.IvyTest; import ch.ivyteam.ivy.process.model.BaseElement; -import ch.ivyteam.ivy.process.rdm.IProcessManager; @IvyTest @SuppressWarnings("restriction") @@ -31,7 +28,7 @@ public static void setup() { } @Test - void shouldFindAllTasksOnPathAtStartWithFlowNameNull() throws Exception { + void shouldFindTasksOnPathAtStartWithFlowNameNull() throws Exception { var workflowEstimator = new WorkflowEstimator(process, null, null); List estimatedTasks = workflowEstimator.findTasksOnPath(start); @@ -47,11 +44,11 @@ void shouldFindAllTasksStartWithFlowNameNull() throws Exception { } @Test - void shouldFindAllTasksOnPathAtStartWithFlowNameSuccess() throws Exception { + void shouldFindTasksOnPathAtStartWithFlowNameSuccess() throws Exception { var workflowEstimator = new WorkflowEstimator(process, null, "success"); List estimatedTasks = workflowEstimator.findTasksOnPath(start); - assertArrayEquals(Arrays.array("UserTask", "Task", "Tasks-TaskA", "Tasks-TaskB"), getTaskNames(estimatedTasks)); + assertArrayEquals(Arrays.array("Task A"), getTaskNames(estimatedTasks)); } } diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/ParallelTasksExampleTest.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/ParallelTasksExampleTest.java index f88604f2..ab94e2c5 100644 --- a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/ParallelTasksExampleTest.java +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/ParallelTasksExampleTest.java @@ -1,7 +1,6 @@ package com.axonivy.utils.estimator.test; import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.List; @@ -12,10 +11,8 @@ import com.axonivy.utils.estimator.WorkflowEstimator; import com.axonivy.utils.estimator.model.EstimatedTask; -import ch.ivyteam.ivy.environment.Ivy; import ch.ivyteam.ivy.environment.IvyTest; import ch.ivyteam.ivy.process.model.BaseElement; -import ch.ivyteam.ivy.process.rdm.IProcessManager; @IvyTest @SuppressWarnings("restriction") diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java b/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java index 01a77ee9..58f6b069 100644 --- a/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java +++ b/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java @@ -7,10 +7,13 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.Comparator; import java.util.List; +import java.util.Map; import java.util.Map.Entry; -import org.apache.commons.lang3.StringUtils; +import org.apache.commons.collections4.ListUtils; +import org.apache.commons.collections4.map.HashedMap; import com.google.common.base.Objects; @@ -32,91 +35,113 @@ public ProcessGraph(Process process) { /** * Using Recursion Algorithm To Find All The Path On Graph. * Return all paths - * Exception: Throw stack overflow error if there are loop node */ public List> findPaths(BaseElement from) { - return findAllPaths(from); + List path = findPath(from, emptyList()); + if (path.isEmpty()) { + return emptyList(); + } + return Arrays.asList(path); } public List> findPaths(BaseElement from, String flowName) { - List> paths = findAllPaths(from); - - List> pathByFlowName = emptyList(); - if (isNotBlank(flowName)) { - pathByFlowName = paths.stream().filter(path -> hasFlowNameOrDefaultPath(path, flowName)).toList(); - } else { - pathByFlowName = paths.stream().filter(path -> isAllEmptyCondition(path)).toList(); + List path = findPathByFlowName(from, flowName); + if (path.isEmpty()) { + return emptyList(); } - //Maybe throw an exception if there are not path - return pathByFlowName; - } - - private List> findAllPaths(BaseElement from) { - List> paths = findNextNodeElementPath(from); - - //Insert from element at first position - paths.forEach(path -> { - path.add(0, from); - }); - - return paths; + return Arrays.asList(path); } - - private List> findNextNodeElementPath(BaseElement from) { - var nexts = new ArrayList>(); - if (from != null && from instanceof NodeElement) { - List outs = ((NodeElement) from).getOutgoing(); - for (SequenceFlow out : outs) { - - NodeElement target = out.getTarget(); - List> nextElements = findNextNodeElementPath(target); - - if (nextElements.isEmpty()) { - nextElements.add(new ArrayList(Arrays.asList(out, target))); - } else { - nextElements.forEach(nodes -> { - nodes.add(0, out); - nodes.add(1, target); - }); - } - - nexts.addAll(nextElements); - } - } - return nexts; - } +// public List> findPaths(BaseElement from, String flowName) { +// List> paths = findAllPaths(from); +// +// List> pathByFlowName = emptyList(); +// if (isNotBlank(flowName)) { +// pathByFlowName = paths.stream().filter(path -> hasFlowNameOrDefaultPath(path, flowName)).toList(); +// } else { +// pathByFlowName = paths.stream().filter(path -> isAllEmptyCondition(path)).toList(); +// } +// //Maybe throw an exception if there are not path +// return pathByFlowName; +// } +// +// private List> findAllPaths(BaseElement from) { +// List> paths = findNextNodeElementPath(from, emptyList()); +// +// //Insert from element at first position +// paths.forEach(path -> { +// path.add(0, from); +// }); +// +// return paths; +// } - private boolean hasFlowNameOrDefaultPath(List elements, String flowName) { - if (StringUtils.isBlank(flowName)) { - return true; - } - - for (int i = 0; i < elements.size(); i++) { - var currentElement = elements.get(i); - var previousElement = i > 0 ? elements.get(i - 1) : null; - if (currentElement instanceof SequenceFlow) { - var passedFlowNameCheck = hasFlowNameOrEmpty((SequenceFlow) currentElement, flowName); - - var passedDefaultPathCheck = false; - if (previousElement instanceof Alternative) { - passedDefaultPathCheck = isDefaultPath(currentElement, (Alternative) previousElement); - } - - if (!passedFlowNameCheck && !passedDefaultPathCheck) { - return false; - } - } - } - - return true; - } +// private List> findNextNodeElementPath(BaseElement from, List> parentPaths) { +// var paths = new ArrayList>(); +// +// if (from != null && from instanceof NodeElement) { +// List outs = ((NodeElement) from).getOutgoing(); +// for (SequenceFlow out : outs) { +// +// NodeElement target = out.getTarget(); +// List parentElements = Arrays.asList(out, target); +// +// var x = addToParentPaths(parentPaths, parentElements); +// +// List> nextElements = findNextNodeElementPath(target, x); +// +// if (nextElements.isEmpty()) { +// nextElements.add(new ArrayList(parentElements)); +// } else { +// nextElements.forEach(nodes -> { +// nodes.addAll(0, parentElements); +// }); +// } +// +// paths.addAll(nextElements); +// } +// } +// return paths; +// } + +// private List> addToParentPaths(List> parentPaths, List elements) { +// if (parentPaths.isEmpty()) { +// return new ArrayList>(Arrays.asList(elements)); +// } else { +// return parentPaths.stream().map(path -> Stream.concat(path.stream(), elements.stream()).toList()).toList(); +// } +// } +// +// private boolean hasFlowNameOrDefaultPath(List elements, String flowName) { +// if (StringUtils.isBlank(flowName)) { +// return true; +// } +// +// for (int i = 0; i < elements.size(); i++) { +// var currentElement = elements.get(i); +// var previousElement = i > 0 ? elements.get(i - 1) : null; +// if (currentElement instanceof SequenceFlow) { +// var passedFlowNameCheck = hasFlowNameOrEmpty((SequenceFlow) currentElement, flowName); +// +// var passedDefaultPathCheck = false; +// if (previousElement instanceof Alternative) { +// passedDefaultPathCheck = isDefaultPath(currentElement, (Alternative) previousElement); +// } +// +// if (!passedFlowNameCheck && !passedDefaultPathCheck) { +// return false; +// } +// } +// } +// +// return true; +// } private boolean hasFlowNameOrEmpty(SequenceFlow sequenceFlow, String flowName) { - + String label = sequenceFlow.getEdge().getLabel().getText(); if (isNotBlank(label)) { - if (!label.contains(flowName)) { + if (flowName == null || !label.contains(flowName)) { return false; } } @@ -124,20 +149,20 @@ private boolean hasFlowNameOrEmpty(SequenceFlow sequenceFlow, String flowName) { return true; } - private boolean isAllEmptyCondition(List elements) { - - for (int i = 1; i < elements.size(); i++) { - var currentElement = elements.get(i); - var previousElement = elements.get(i - 1); - if (previousElement instanceof Alternative) { - if (!isDefaultPath(currentElement, (Alternative) previousElement)) { - return false; - } - } - } - - return true; - } +// private boolean isAllEmptyCondition(List elements) { +// +// for (int i = 1; i < elements.size(); i++) { +// var currentElement = elements.get(i); +// var previousElement = elements.get(i - 1); +// if (previousElement instanceof Alternative) { +// if (!isDefaultPath(currentElement, (Alternative) previousElement)) { +// return false; +// } +// } +// } +// +// return true; +// } private boolean isDefaultPath(BaseElement currentElement, Alternative previousElement) { String currentElementId = currentElement.getPid().getFieldId(); @@ -145,6 +170,15 @@ private boolean isDefaultPath(BaseElement currentElement, Alternative previousEl return Objects.equal(currentElementId, nextTargetId); } + private boolean isDefaultPath(SequenceFlow flow) { + NodeElement sourceElement = flow.getSource(); + if(sourceElement instanceof Alternative) { + return isDefaultPath(flow, (Alternative) sourceElement); + } + + return false; + } + private String getNextTargetIdByCondition(Alternative alternative, String condition) { IvyScriptExpression script = IvyScriptExpression.script(defaultString(condition)); String nextTargetId = alternative.getConditions().conditions().entrySet().stream() @@ -158,34 +192,83 @@ private String getNextTargetIdByCondition(Alternative alternative, String condit /** - * Find base on start node and check condition at each - * @param from - * @param flowName - * @return + * Find base on start node and check condition at each */ -// public List findPathByFlowName(BaseElement from, String flowName) { -// List path = new ArrayList<>(); -// path.add(from); -// -// -// if (from instanceof NodeElement) { -// NodeElement currentNode = (NodeElement) from; -// List outs = currentNode.getOutgoing(); -// while (outs.size() > 0) { -// SequenceFlow flow = findCorrectSequenceFlow(outs, flowName); -// -// if(flow == null) { -// break; -// } -// -// path.add(from); -// -// } -// } -// return emptyList(); -// } -// -// private SequenceFlow findCorrectSequenceFlow(List outs, String flowName) { -// return outs.stream().filter(out -> hasFlowNameOrEmpty(out, flowName)).findFirst().orElse(null); -// } + public List findPath(BaseElement from, List previousElements) { + + //Prevent loop + if (previousElements.indexOf(from) >= 0) { + return emptyList(); + } + + List path = new ArrayList<>(); + path.add(from); + + if (from instanceof NodeElement) { + List outs = ((NodeElement) from).getOutgoing(); + + Map> paths = new HashedMap<>(); + for (SequenceFlow out : outs) { + paths.put(out, findPath(out.getTarget(), ListUtils.union(previousElements, Arrays.asList(from)))); + } + + var longestPath = paths.entrySet().stream() + .max(Comparator.comparingInt(entry -> entry.getValue().size())) + .orElse(null); + + if(longestPath != null) { + path.add(longestPath.getKey()); + path.addAll(longestPath.getValue()); + } + } + + return path; + } + + public List findPathByFlowName(BaseElement from, String flowName) { + List path = new ArrayList<>(); + path.add(from); + + if (from instanceof NodeElement) { + NodeElement target = (NodeElement) from; + while (target.getOutgoing().size() > 0) { + SequenceFlow flow = findCorrectSequenceFlow(target, flowName); + + if(flow == null) { + //Can not find any way + path.clear(); + break; + } + + path.add(flow); + + target = flow.getTarget(); + //Prevent loop + if (path.indexOf(target) >= 0) { + break; + } + path.add(target); + } + } + + return path; + } + + private SequenceFlow findCorrectSequenceFlow(NodeElement nodeElement, String flowName) { + List outs = nodeElement.getOutgoing(); + if(flowName == null && nodeElement instanceof Alternative) { + return outs.stream().filter(out -> isDefaultPath(out)).findFirst().orElse(null); + } + + SequenceFlow flow = outs.stream() + .filter(out -> hasFlowNameOrEmpty(out, flowName)) + .findFirst() + .orElse(null); + + if(flow == null) { + flow = outs.stream().filter(out -> isDefaultPath(out)).findFirst().orElse(null); + } + + return flow; + } } diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java index fd0e030e..88b6d559 100644 --- a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java +++ b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java @@ -62,7 +62,7 @@ public List findTasksOnPath(BaseElement startAtElement) throws Ex throw new Exception("Not found"); } - estimatedTasks = convertToEstimatedTasks(paths); + estimatedTasks = convertToEstimatedTasks(paths); } return estimatedTasks; diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/model/EstimatedTask.java b/workflow-estimator/src/com/axonivy/utils/estimator/model/EstimatedTask.java index ddf1cf0d..223404f2 100644 --- a/workflow-estimator/src/com/axonivy/utils/estimator/model/EstimatedTask.java +++ b/workflow-estimator/src/com/axonivy/utils/estimator/model/EstimatedTask.java @@ -4,6 +4,8 @@ import java.util.Date; import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.apache.commons.lang3.time.DateUtils; @@ -63,4 +65,9 @@ public Date calculateEstimatedEndTimestamp() { return DateUtils.addSeconds(this.estimatedStartTimestamp, durationInSecond); } + + @Override + public String toString() { + return Stream.of(pid, taskName).collect(Collectors.joining("-")); + } } From 9189200f93f2d1fff11410000f77d5608e5f8078 Mon Sep 17 00:00:00 2001 From: Trung Mai Date: Wed, 28 Feb 2024 14:29:36 +0700 Subject: [PATCH 024/143] TE-538: Adapt Task type --- .../processes/ParallelTasksExample.p.json | 50 ++++--- .../test/ParallelTasksExampleTest.java | 4 +- .../axonivy/utils/estimator/ProcessGraph.java | 122 +++++++++++++++--- .../utils/estimator/WorkflowEstimator.java | 12 +- 4 files changed, 145 insertions(+), 43 deletions(-) diff --git a/workflow-estimator-test/processes/ParallelTasksExample.p.json b/workflow-estimator-test/processes/ParallelTasksExample.p.json index fdb5f716..b3dce6eb 100644 --- a/workflow-estimator-test/processes/ParallelTasksExample.p.json +++ b/workflow-estimator-test/processes/ParallelTasksExample.p.json @@ -41,8 +41,28 @@ "labelOffset" : { "x" : 14, "y" : 34 } }, "connect" : [ - { "id" : "f8", "to" : "f7", "condition" : "ivp==\"TaskB.ivp\"" }, - { "id" : "f5", "to" : "f21", "condition" : "ivp==\"TaskA.ivp\"", "var" : "in1" } + { "id" : "f5", "to" : "f4", "condition" : "ivp==\"TaskA.ivp\"", "var" : "in1" }, + { "id" : "f8", "to" : "f7", "condition" : "ivp==\"TaskB.ivp\"" } + ] + }, { + "id" : "f4", + "type" : "TaskSwitchGateway", + "name" : "Join", + "config" : { + "tasks" : [ { + "id" : "TaskA", + "name" : "JoiningTask", + "responsible" : { + "activator" : "SYSTEM" + } + } ] + }, + "visual" : { + "at" : { "x" : 1056, "y" : 96 }, + "labelOffset" : { "x" : 14, "y" : 34 } + }, + "connect" : [ + { "id" : "f6", "to" : "f1", "condition" : "ivp==\"TaskA.ivp\"" } ] }, { "id" : "f7", @@ -60,18 +80,17 @@ "type" : "Alternative", "config" : { "conditions" : { - "f14" : "true", - "f11" : "" + "f14" : "true" } }, "visual" : { "at" : { "x" : 480, "y" : 168 } }, "connect" : [ - { "id" : "f11", "to" : "f15", "color" : "def", "var" : "in2" }, - { "id" : "f14", "to" : "f21", "label" : { + { "id" : "f14", "to" : "f4", "label" : { "name" : "{shortcut}" - }, "var" : "in3" } + }, "var" : "in3" }, + { "id" : "f11", "to" : "f15", "color" : "def", "var" : "in2" } ] }, { "id" : "f15", @@ -96,8 +115,8 @@ "labelOffset" : { "x" : 14, "y" : 34 } }, "connect" : [ - { "id" : "f16", "to" : "f13", "condition" : "ivp==\"TaskB.ivp\"" }, - { "id" : "f12", "to" : "f21", "condition" : "ivp==\"TaskA.ivp\"", "var" : "in2" } + { "id" : "f12", "to" : "f4", "condition" : "ivp==\"TaskA.ivp\"", "var" : "in2" }, + { "id" : "f16", "to" : "f13", "condition" : "ivp==\"TaskB.ivp\"" } ] }, { "id" : "f13", @@ -106,7 +125,7 @@ "at" : { "x" : 840, "y" : 296 } }, "connect" : [ - { "id" : "f17", "to" : "f21", "via" : [ { "x" : 1056, "y" : 296 } ], "var" : "in4" } + { "id" : "f17", "to" : "f4", "via" : [ { "x" : 1056, "y" : 296 } ], "var" : "in4" } ] }, { "id" : "f18", @@ -149,17 +168,6 @@ "at" : { "x" : 853, "y" : 461 }, "size" : { "width" : 407, "height" : 158 } } - }, { - "id" : "f21", - "type" : "Join", - "name" : "Join", - "visual" : { - "at" : { "x" : 1056, "y" : 96 }, - "labelOffset" : { "x" : 16, "y" : -16 } - }, - "connect" : [ - { "id" : "f4", "to" : "f1" } - ] } ], "layout" : { "colors" : { diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/ParallelTasksExampleTest.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/ParallelTasksExampleTest.java index ab94e2c5..1bfd94f5 100644 --- a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/ParallelTasksExampleTest.java +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/ParallelTasksExampleTest.java @@ -37,7 +37,7 @@ void shouldFindAllTasksAtStartWithFlowNameNull() throws Exception { } @Test - void shouldFindAllTasksOnPathAtStartWithFlowNameNull() throws Exception { + void shouldFindTasksOnPathAtStartWithFlowNameNull() throws Exception { var workflowEstimator = new WorkflowEstimator(process, null, null); List estimatedTasks = workflowEstimator.findTasksOnPath(start); @@ -46,7 +46,7 @@ void shouldFindAllTasksOnPathAtStartWithFlowNameNull() throws Exception { } @Test - void shouldFindAllTasksAtStartWithFlowNameShortcut() throws Exception { + void shouldFindTasksOnPathAtStartWithFlowNameShortcut() throws Exception { var workflowEstimator = new WorkflowEstimator(process, null, "shortcut"); List estimatedTasks = workflowEstimator.findTasksOnPath(start); diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java b/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java index 58f6b069..9d45dc98 100644 --- a/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java +++ b/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java @@ -1,8 +1,10 @@ package com.axonivy.utils.estimator; import static java.util.Collections.emptyList; +import static java.util.stream.Collectors.toList; import static org.apache.commons.lang3.StringUtils.EMPTY; import static org.apache.commons.lang3.StringUtils.defaultString; +import static org.apache.commons.lang3.StringUtils.isEmpty; import static org.apache.commons.lang3.StringUtils.isNotBlank; import java.util.ArrayList; @@ -11,6 +13,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.stream.Collectors; import org.apache.commons.collections4.ListUtils; import org.apache.commons.collections4.map.HashedMap; @@ -21,7 +24,10 @@ import ch.ivyteam.ivy.process.model.NodeElement; import ch.ivyteam.ivy.process.model.Process; import ch.ivyteam.ivy.process.model.connector.SequenceFlow; +import ch.ivyteam.ivy.process.model.element.event.end.TaskEnd; import ch.ivyteam.ivy.process.model.element.gateway.Alternative; +import ch.ivyteam.ivy.process.model.element.gateway.Join; +import ch.ivyteam.ivy.process.model.element.gateway.TaskSwitchGateway; import ch.ivyteam.ivy.process.model.element.value.IvyScriptExpression; @SuppressWarnings("restriction") @@ -137,18 +143,31 @@ public List> findPaths(BaseElement from, String flowName) { // return true; // } - private boolean hasFlowNameOrEmpty(SequenceFlow sequenceFlow, String flowName) { + private boolean hasFlowNameOrEmpty(SequenceFlow sequenceFlow, String flowName) { + if(isEmpty(flowName)) { + return true; + } - String label = sequenceFlow.getEdge().getLabel().getText(); - if (isNotBlank(label)) { - if (flowName == null || !label.contains(flowName)) { - return false; - } + if(hasFlowName(sequenceFlow, flowName)) { + return true; } + + if(isEmpty(sequenceFlow.getEdge().getLabel().getText())) { + return true; + }; - return true; + return false; } + private boolean hasFlowName(SequenceFlow sequenceFlow, String flowName) { + String label = sequenceFlow.getEdge().getLabel().getText(); + if (isNotBlank(label) && label.contains(flowName)) { + return true; + } + + return false; + } + // private boolean isAllEmptyCondition(List elements) { // // for (int i = 1; i < elements.size(); i++) { @@ -232,11 +251,21 @@ public List findPathByFlowName(BaseElement from, String flowName) { if (from instanceof NodeElement) { NodeElement target = (NodeElement) from; while (target.getOutgoing().size() > 0) { - SequenceFlow flow = findCorrectSequenceFlow(target, flowName); + SequenceFlow flow = null; + if(target instanceof TaskSwitchGateway) { + List parallelTasks = findPathOfTaskSwitchGatewayByFlowName((TaskSwitchGateway) target, flowName); + path.addAll(parallelTasks); + flow = ((NodeElement) parallelTasks.get(parallelTasks.size() - 1)).getOutgoing().stream().findFirst().orElse(null); + } else { + flow = findCorrectSequenceFlow(target, flowName); + } + if(flow == null) { - //Can not find any way - path.clear(); + if(!(path.get(path.size() - 1) instanceof TaskEnd)) { + //Can not find any way + path.clear(); + } break; } @@ -248,27 +277,82 @@ public List findPathByFlowName(BaseElement from, String flowName) { break; } path.add(target); + } + } + + return path.stream().distinct().toList(); + } + + private List findPathOfTaskSwitchGatewayByFlowName(TaskSwitchGateway from, String flowName) { + List> paths = new ArrayList>(); + + List outs = from.getOutgoing(); + for (SequenceFlow out : outs) { + + List path = new ArrayList<>(); + path.add(out); + + NodeElement target = out.getTarget(); + while (target.getOutgoing().size() > 0) { + path.add(target); + + SequenceFlow flow = null; + if(target instanceof TaskSwitchGateway) { + List subTasks = findPathOfTaskSwitchGatewayByFlowName((TaskSwitchGateway) target, flowName); + path.addAll(subTasks); + } else { + flow = findCorrectSequenceFlow(target, flowName); + } + + if(flow == null) { + target = null; + break; + } + + path.add(flow); + + target = flow.getTarget(); + // Prevent loop + if (path.indexOf(target) >= 0) { + break; + } + } + + if(target != null) { + path.add(target); } + + paths.add(path); } + + List elements = paths.stream() + .sorted(Comparator.comparing(List::size, Comparator.reverseOrder())) + .flatMap(List::stream) + .collect(Collectors.toList()); - return path; + return elements; } private SequenceFlow findCorrectSequenceFlow(NodeElement nodeElement, String flowName) { List outs = nodeElement.getOutgoing(); - if(flowName == null && nodeElement instanceof Alternative) { - return outs.stream().filter(out -> isDefaultPath(out)).findFirst().orElse(null); + //High priority for checking default path if flowName is null + if(nodeElement instanceof Alternative) { + if(isEmpty(flowName)) { + return outs.stream().filter(out -> isDefaultPath(out)).findFirst().orElse(null); + } else { + SequenceFlow flow = outs.stream().filter(out -> hasFlowName(out, flowName)).findFirst().orElse(null); + if(flow == null) { + flow = outs.stream().filter(out -> isDefaultPath(out)).findFirst().orElse(null); + } + return flow; + } } SequenceFlow flow = outs.stream() .filter(out -> hasFlowNameOrEmpty(out, flowName)) .findFirst() - .orElse(null); - - if(flow == null) { - flow = outs.stream().filter(out -> isDefaultPath(out)).findFirst().orElse(null); - } - + .orElse(null); + return flow; } } diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java index 88b6d559..24e6405f 100644 --- a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java +++ b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java @@ -20,6 +20,7 @@ import ch.ivyteam.ivy.process.model.BaseElement; import ch.ivyteam.ivy.process.model.NodeElement; import ch.ivyteam.ivy.process.model.Process; +import ch.ivyteam.ivy.process.model.element.SingleTaskCreator; import ch.ivyteam.ivy.process.model.element.TaskAndCaseModifier; import ch.ivyteam.ivy.process.model.element.event.start.RequestStart; import ch.ivyteam.ivy.process.model.element.value.task.TaskConfig; @@ -93,9 +94,14 @@ private List convertToEstimatedTasks(List> path .filter(node -> { return node instanceof TaskAndCaseModifier; }) + .map(TaskAndCaseModifier.class::cast) .filter(node -> { return node instanceof RequestStart == false; }) + //Remove SYSTEM task + .filter(node -> { + return isSystemTask(node) == false; + }) .map(TaskAndCaseModifier.class::cast) // filter the task which have estimated if needed .toList(); @@ -159,5 +165,9 @@ private Duration getDurationByCode(TaskConfig task) { } return Duration.ofHours(0); - } + } + + private boolean isSystemTask(TaskAndCaseModifier task) { + return task.getAllTaskConfigs().stream().anyMatch(it -> "SYSTEM".equals(it.getActivator().getName())); + } } From 16007431298965ca2acab21bf77cca691f223c74 Mon Sep 17 00:00:00 2001 From: "AAVN\\ntnchuong" Date: Wed, 28 Feb 2024 15:51:49 +0700 Subject: [PATCH 025/143] TE-538: add customer info --- .../processes/FlowExampleBasic.p.json | 3 ++- workflow-estimator-test/src/WfEstimate.java | 2 ++ .../estimator/test/FlowExampleBasicTest.java | 7 ++++++ .../utils/estimator/WorkflowEstimator.java | 25 +++++++++++++++---- .../utils/estimator/model/EstimatedTask.java | 9 +++++++ 5 files changed, 40 insertions(+), 6 deletions(-) diff --git a/workflow-estimator-test/processes/FlowExampleBasic.p.json b/workflow-estimator-test/processes/FlowExampleBasic.p.json index 7982bc35..49a4779e 100644 --- a/workflow-estimator-test/processes/FlowExampleBasic.p.json +++ b/workflow-estimator-test/processes/FlowExampleBasic.p.json @@ -77,7 +77,8 @@ "code" : [ "import java.util.concurrent.TimeUnit;", "", - "WfEstimate.setEstimate(6,TimeUnit.HOURS,UseCase.BIGPROJECT);" + "WfEstimate.setEstimate(6,TimeUnit.HOURS,UseCase.BIGPROJECT);", + "WfEstimate.setCustomInfo(\"abc\");" ] } }, diff --git a/workflow-estimator-test/src/WfEstimate.java b/workflow-estimator-test/src/WfEstimate.java index ff1b99de..96646465 100644 --- a/workflow-estimator-test/src/WfEstimate.java +++ b/workflow-estimator-test/src/WfEstimate.java @@ -3,5 +3,7 @@ public class WfEstimate { public static void setEstimate(int amound, TimeUnit unit, UseCase useCase) {}; + + public static void setCustomInfo(String info) {}; } diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleBasicTest.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleBasicTest.java index e2913c41..2d778c8d 100644 --- a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleBasicTest.java +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleBasicTest.java @@ -161,4 +161,11 @@ void shouldCalculateTotalDuration() { Duration duration = workflowEstimator.calculateEstimatedDuration(graph.findStart()); assertEquals(15, duration.toHours()); } + + @Test + void shouldCheckCustomInfo() throws Exception { + var workflowEstimator = new WorkflowEstimator(process, null, "internal"); + var estimatedTasks = workflowEstimator.findTasksOnPath(newStart); + assertEquals("abc", estimatedTasks.get(0).getCustomInfo()); + } } diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java index 88b6d559..3b8596d4 100644 --- a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java +++ b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java @@ -125,25 +125,31 @@ private List createEstimatedTask(TaskAndCaseModifier task, Date s estimatedTask.setTaskName(defaultIfEmpty(item.getName().getRawMacro(), task.getName())); Duration estimatedDuration = getDurationByCode(item); estimatedTask.setEstimatedDuration(estimatedDuration); - estimatedTask.setEstimatedStartTimestamp(startTimestamp); - + estimatedTask.setEstimatedStartTimestamp(startTimestamp); + String customerInfo = getCustomInfoByCode(item); + estimatedTask.setCustomInfo(customerInfo); estimatedTasks.add(estimatedTask); }); return estimatedTasks; } - private Duration getDurationByCode(TaskConfig task) { + private String getWfEstimateLineFromCode(TaskConfig task, String prefix) { // strongly typed! String script = Optional.of(task.getScript()).orElse(EMPTY); String[] codeLines = script.split("\\n"); String wfEstimateCode = Arrays.stream(codeLines) - .filter(line -> line.contains("WfEstimate.")) + .filter(line -> line.contains(prefix)) .findFirst() .orElse(EMPTY); + return wfEstimateCode; + } + + private Duration getDurationByCode(TaskConfig task) { + String wfEstimateCode = getWfEstimateLineFromCode(task, "WfEstimate.setEstimate"); if (StringUtils.isNotEmpty(wfEstimateCode)) { - String result = StringUtils.substringBetween(script, "(", "Use"); + String result = StringUtils.substringBetween(wfEstimateCode, "(", "UseCase"); int amount = Integer.parseInt(result.substring(0, result.indexOf(","))); String unit = result.substring(result.indexOf(".") + 1, result.lastIndexOf(",")); @@ -160,4 +166,13 @@ private Duration getDurationByCode(TaskConfig task) { return Duration.ofHours(0); } + + private String getCustomInfoByCode(TaskConfig task) { + String wfEstimateCode = getWfEstimateLineFromCode(task, "WfEstimate.setCustomInfo"); + if (StringUtils.isNotEmpty(wfEstimateCode)) { + String result = StringUtils.substringBetween(wfEstimateCode, "(\"", "\")"); + return result; + } + return null; + } } diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/model/EstimatedTask.java b/workflow-estimator/src/com/axonivy/utils/estimator/model/EstimatedTask.java index 223404f2..e42e1a8d 100644 --- a/workflow-estimator/src/com/axonivy/utils/estimator/model/EstimatedTask.java +++ b/workflow-estimator/src/com/axonivy/utils/estimator/model/EstimatedTask.java @@ -16,6 +16,7 @@ public class EstimatedTask { private Duration estimatedDuration; private List parentElementNames; private Date estimatedStartTimestamp; + private String customInfo; public String getPid() { return pid; @@ -57,6 +58,14 @@ public void setParentElementNames(List parentElementNames) { this.parentElementNames = parentElementNames; } + public String getCustomInfo() { + return customInfo; + } + + public void setCustomInfo(String customInfo) { + this.customInfo = customInfo; + } + public Date calculateEstimatedEndTimestamp() { int durationInSecond = Optional.ofNullable(this.estimatedDuration) .map(Duration::getSeconds) From 86ab4ce8cb3c0385eba18aacdd31d9904e5d6863 Mon Sep 17 00:00:00 2001 From: Trung Mai Date: Thu, 29 Feb 2024 09:00:41 +0700 Subject: [PATCH 026/143] TE-538: Remove unuse code --- .../axonivy/utils/estimator/ProcessGraph.java | 105 +----------------- .../utils/estimator/WorkflowEstimator.java | 52 +++++---- 2 files changed, 30 insertions(+), 127 deletions(-) diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java b/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java index 9d45dc98..8d3d85d5 100644 --- a/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java +++ b/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java @@ -1,7 +1,6 @@ package com.axonivy.utils.estimator; import static java.util.Collections.emptyList; -import static java.util.stream.Collectors.toList; import static org.apache.commons.lang3.StringUtils.EMPTY; import static org.apache.commons.lang3.StringUtils.defaultString; import static org.apache.commons.lang3.StringUtils.isEmpty; @@ -26,7 +25,6 @@ import ch.ivyteam.ivy.process.model.connector.SequenceFlow; import ch.ivyteam.ivy.process.model.element.event.end.TaskEnd; import ch.ivyteam.ivy.process.model.element.gateway.Alternative; -import ch.ivyteam.ivy.process.model.element.gateway.Join; import ch.ivyteam.ivy.process.model.element.gateway.TaskSwitchGateway; import ch.ivyteam.ivy.process.model.element.value.IvyScriptExpression; @@ -57,91 +55,6 @@ public List> findPaths(BaseElement from, String flowName) { } return Arrays.asList(path); } - -// public List> findPaths(BaseElement from, String flowName) { -// List> paths = findAllPaths(from); -// -// List> pathByFlowName = emptyList(); -// if (isNotBlank(flowName)) { -// pathByFlowName = paths.stream().filter(path -> hasFlowNameOrDefaultPath(path, flowName)).toList(); -// } else { -// pathByFlowName = paths.stream().filter(path -> isAllEmptyCondition(path)).toList(); -// } -// //Maybe throw an exception if there are not path -// return pathByFlowName; -// } -// -// private List> findAllPaths(BaseElement from) { -// List> paths = findNextNodeElementPath(from, emptyList()); -// -// //Insert from element at first position -// paths.forEach(path -> { -// path.add(0, from); -// }); -// -// return paths; -// } - -// private List> findNextNodeElementPath(BaseElement from, List> parentPaths) { -// var paths = new ArrayList>(); -// -// if (from != null && from instanceof NodeElement) { -// List outs = ((NodeElement) from).getOutgoing(); -// for (SequenceFlow out : outs) { -// -// NodeElement target = out.getTarget(); -// List parentElements = Arrays.asList(out, target); -// -// var x = addToParentPaths(parentPaths, parentElements); -// -// List> nextElements = findNextNodeElementPath(target, x); -// -// if (nextElements.isEmpty()) { -// nextElements.add(new ArrayList(parentElements)); -// } else { -// nextElements.forEach(nodes -> { -// nodes.addAll(0, parentElements); -// }); -// } -// -// paths.addAll(nextElements); -// } -// } -// return paths; -// } - -// private List> addToParentPaths(List> parentPaths, List elements) { -// if (parentPaths.isEmpty()) { -// return new ArrayList>(Arrays.asList(elements)); -// } else { -// return parentPaths.stream().map(path -> Stream.concat(path.stream(), elements.stream()).toList()).toList(); -// } -// } -// -// private boolean hasFlowNameOrDefaultPath(List elements, String flowName) { -// if (StringUtils.isBlank(flowName)) { -// return true; -// } -// -// for (int i = 0; i < elements.size(); i++) { -// var currentElement = elements.get(i); -// var previousElement = i > 0 ? elements.get(i - 1) : null; -// if (currentElement instanceof SequenceFlow) { -// var passedFlowNameCheck = hasFlowNameOrEmpty((SequenceFlow) currentElement, flowName); -// -// var passedDefaultPathCheck = false; -// if (previousElement instanceof Alternative) { -// passedDefaultPathCheck = isDefaultPath(currentElement, (Alternative) previousElement); -// } -// -// if (!passedFlowNameCheck && !passedDefaultPathCheck) { -// return false; -// } -// } -// } -// -// return true; -// } private boolean hasFlowNameOrEmpty(SequenceFlow sequenceFlow, String flowName) { if(isEmpty(flowName)) { @@ -168,21 +81,6 @@ private boolean hasFlowName(SequenceFlow sequenceFlow, String flowName) { return false; } -// private boolean isAllEmptyCondition(List elements) { -// -// for (int i = 1; i < elements.size(); i++) { -// var currentElement = elements.get(i); -// var previousElement = elements.get(i - 1); -// if (previousElement instanceof Alternative) { -// if (!isDefaultPath(currentElement, (Alternative) previousElement)) { -// return false; -// } -// } -// } -// -// return true; -// } - private boolean isDefaultPath(BaseElement currentElement, Alternative previousElement) { String currentElementId = currentElement.getPid().getFieldId(); String nextTargetId = getNextTargetIdByCondition((Alternative) previousElement, EMPTY); @@ -228,7 +126,8 @@ public List findPath(BaseElement from, List previousEl Map> paths = new HashedMap<>(); for (SequenceFlow out : outs) { - paths.put(out, findPath(out.getTarget(), ListUtils.union(previousElements, Arrays.asList(from)))); + List currentPath = ListUtils.union(previousElements, Arrays.asList(from)); + paths.put(out, findPath(out.getTarget(), currentPath)); } var longestPath = paths.entrySet().stream() diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java index d286b1a9..ce686abe 100644 --- a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java +++ b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java @@ -46,7 +46,7 @@ public List findAllTasks(BaseElement startAtElement) { if (startAtElement instanceof NodeElement) { List> paths = graph.findPaths(startAtElement); - estimatedTasks = convertToEstimatedTasks(paths); + estimatedTasks = convertToEstimatedTasks(paths).get(0); } return estimatedTasks; @@ -62,7 +62,7 @@ public List findTasksOnPath(BaseElement startAtElement) throws Ex throw new Exception("Not found"); } - estimatedTasks = convertToEstimatedTasks(paths); + estimatedTasks = convertToEstimatedTasks(paths).get(0); } return estimatedTasks; @@ -76,45 +76,49 @@ public Duration calculateEstimatedDuration(BaseElement startElement) { paths = graph.findPaths(startElement); } - List estimatedTasks = convertToEstimatedTasks(paths); + List estimatedTasks = convertToEstimatedTasks(paths).get(0); Duration total = Duration.ofHours(0); for(EstimatedTask item : estimatedTasks) { total = total.plus(item.getEstimatedDuration()); } return total; } + + private List> convertToEstimatedTasks(List> paths) { - private List convertToEstimatedTasks(List> paths) { + List> taskPaths = paths.stream().map(path -> filterAcceptedTask(path)).toList(); - List taskCaseModifier = paths.stream() - // If have more than one path, we get the longest - .max(Comparator.comparingInt(List::size)).orElse(emptyList()).stream() + List> estimatedTaskPaths = taskPaths.stream().map(taskPath -> { + //Convert to EstimatedTask + List estimatedTasks = new ArrayList<>(); + for(int i = 0; i < taskPath.size(); i ++) { + List estimatedTaskResults = new ArrayList<>(); + Date startTimestamp = i == 0 ? new Date() : estimatedTasks.get(i - 1).calculateEstimatedEndTimestamp(); + estimatedTaskResults = createEstimatedTask(taskPath.get(i), startTimestamp); + + estimatedTasks.addAll(estimatedTaskResults); + } + return estimatedTasks; + }).toList(); + + return estimatedTaskPaths.stream().sorted(Comparator.comparing(List::size, Comparator.reverseOrder())).toList(); + } + + private List filterAcceptedTask(List path) { + return path.stream() // filter to get task only .filter(node -> { return node instanceof TaskAndCaseModifier; - }) - .map(TaskAndCaseModifier.class::cast) + }).map(TaskAndCaseModifier.class::cast) .filter(node -> { return node instanceof RequestStart == false; }) - //Remove SYSTEM task + // Remove SYSTEM task .filter(node -> { - return isSystemTask(node) == false; - }) - .map(TaskAndCaseModifier.class::cast) + return isSystemTask(node) == false; + }).map(TaskAndCaseModifier.class::cast) // filter the task which have estimated if needed .toList(); - //Convert to EstimatedTask - List estimatedTasks = new ArrayList<>(); - for(int i = 0; i < taskCaseModifier.size(); i ++) { - List estimatedTaskResults = new ArrayList<>(); - Date startTimestamp = i == 0 ? new Date() : estimatedTasks.get(i - 1).calculateEstimatedEndTimestamp(); - estimatedTaskResults = createEstimatedTask(taskCaseModifier.get(i), startTimestamp); - - estimatedTasks.addAll(estimatedTaskResults); - } - - return estimatedTasks; } private List createEstimatedTask(TaskAndCaseModifier task, Date startTimestamp) { From 95a37255b6a2975ca8fba1c0ec87f1df1e2fe8ee Mon Sep 17 00:00:00 2001 From: "AAVN\\ntnchuong" Date: Thu, 29 Feb 2024 13:44:47 +0700 Subject: [PATCH 027/143] TE-538: add find task sub process --- .../estimator/test/FlowSubProcessTest.java | 46 +++++++++++++++++++ .../axonivy/utils/estimator/ProcessGraph.java | 30 ++++++++++-- 2 files changed, 72 insertions(+), 4 deletions(-) create mode 100644 workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowSubProcessTest.java diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowSubProcessTest.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowSubProcessTest.java new file mode 100644 index 00000000..a8d52d3b --- /dev/null +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowSubProcessTest.java @@ -0,0 +1,46 @@ +package com.axonivy.utils.estimator.test; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import java.util.List; + +import org.assertj.core.util.Arrays; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import com.axonivy.utils.estimator.WorkflowEstimator; +import com.axonivy.utils.estimator.model.EstimatedTask; + +import ch.ivyteam.ivy.environment.IvyTest; +import ch.ivyteam.ivy.process.model.BaseElement; + +@IvyTest +@SuppressWarnings("restriction") +public class FlowSubProcessTest extends FlowExampleTest { + + private static BaseElement start; + private static final String PROCESS_NAME = "FlowSubprocess"; + + @BeforeAll + public static void setup() { + setup(PROCESS_NAME); + start = graph.findByElementName("start"); + } + + @Test + void shouldFindTasksOnPathAtStartWithFlowNameNull() throws Exception { + var workflowEstimator = new WorkflowEstimator(process, null, null); + List estimatedTasks = workflowEstimator.findTasksOnPath(start); + + assertArrayEquals(Arrays.array("Task A", "Task B"), getTaskNames(estimatedTasks)); + } + + @Test + void shouldFindAllTasksAtStartWithFlowNameNull() throws Exception { + var workflowEstimator = new WorkflowEstimator(process, null, null); + List estimatedTasks = workflowEstimator.findAllTasks(start); + + assertArrayEquals(Arrays.array("Task A", "Task B"), getTaskNames(estimatedTasks)); + } + +} diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java b/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java index 8d3d85d5..24ad99e2 100644 --- a/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java +++ b/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java @@ -23,7 +23,9 @@ import ch.ivyteam.ivy.process.model.NodeElement; import ch.ivyteam.ivy.process.model.Process; import ch.ivyteam.ivy.process.model.connector.SequenceFlow; +import ch.ivyteam.ivy.process.model.element.EmbeddedProcessElement; import ch.ivyteam.ivy.process.model.element.event.end.TaskEnd; +import ch.ivyteam.ivy.process.model.element.event.start.StartEvent; import ch.ivyteam.ivy.process.model.element.gateway.Alternative; import ch.ivyteam.ivy.process.model.element.gateway.TaskSwitchGateway; import ch.ivyteam.ivy.process.model.element.value.IvyScriptExpression; @@ -148,15 +150,19 @@ public List findPathByFlowName(BaseElement from, String flowName) { path.add(from); if (from instanceof NodeElement) { - NodeElement target = (NodeElement) from; + NodeElement target = (NodeElement) from; while (target.getOutgoing().size() > 0) { - SequenceFlow flow = null; - if(target instanceof TaskSwitchGateway) { + SequenceFlow flow = null; + if(target instanceof EmbeddedProcessElement) { + List subPaths = findPathOfSubProcessByFlowName((EmbeddedProcessElement)target, flowName); + path.addAll(subPaths); + flow = findCorrectSequenceFlow(target, flowName); + } else if(target instanceof TaskSwitchGateway) { List parallelTasks = findPathOfTaskSwitchGatewayByFlowName((TaskSwitchGateway) target, flowName); path.addAll(parallelTasks); flow = ((NodeElement) parallelTasks.get(parallelTasks.size() - 1)).getOutgoing().stream().findFirst().orElse(null); - } else { + } else { flow = findCorrectSequenceFlow(target, flowName); } @@ -182,6 +188,22 @@ public List findPathByFlowName(BaseElement from, String flowName) { return path.stream().distinct().toList(); } + /** + * Find path on sub process + */ + private List findPathOfSubProcessByFlowName(EmbeddedProcessElement subProcessElement, String flowName) { + // find start element + BaseElement start = findStartElementOfProcess(subProcessElement.getEmbeddedProcess()); + var listElement = findPathByFlowName(start, flowName); + + return listElement; + } + + private BaseElement findStartElementOfProcess(Process subProcess) { + BaseElement start = subProcess.getElements().stream().filter(item -> item instanceof StartEvent).findFirst().orElse(null); + return start; + } + private List findPathOfTaskSwitchGatewayByFlowName(TaskSwitchGateway from, String flowName) { List> paths = new ArrayList>(); From 5b3fbf9cb36de97d347367e411e191827d7d6b99 Mon Sep 17 00:00:00 2001 From: Trung Mai Date: Thu, 29 Feb 2024 14:14:11 +0700 Subject: [PATCH 028/143] TE-538: Get one path only --- .../axonivy/utils/estimator/ProcessGraph.java | 18 +++---- .../utils/estimator/WorkflowEstimator.java | 50 +++++++++---------- 2 files changed, 29 insertions(+), 39 deletions(-) diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java b/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java index 24ad99e2..fe028531 100644 --- a/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java +++ b/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java @@ -42,20 +42,14 @@ public ProcessGraph(Process process) { * Using Recursion Algorithm To Find All The Path On Graph. * Return all paths */ - public List> findPaths(BaseElement from) { - List path = findPath(from, emptyList()); - if (path.isEmpty()) { - return emptyList(); - } - return Arrays.asList(path); + public List findPath(BaseElement from) { + List path = findPath(from, emptyList()); + return path; } - public List> findPaths(BaseElement from, String flowName) { + public List findPath(BaseElement from, String flowName) { List path = findPathByFlowName(from, flowName); - if (path.isEmpty()) { - return emptyList(); - } - return Arrays.asList(path); + return path; } private boolean hasFlowNameOrEmpty(SequenceFlow sequenceFlow, String flowName) { @@ -113,7 +107,7 @@ private String getNextTargetIdByCondition(Alternative alternative, String condit /** * Find base on start node and check condition at each */ - public List findPath(BaseElement from, List previousElements) { + private List findPath(BaseElement from, List previousElements) { //Prevent loop if (previousElements.indexOf(from) >= 0) { diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java index ce686abe..bf3c1f89 100644 --- a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java +++ b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java @@ -45,8 +45,8 @@ public List findAllTasks(BaseElement startAtElement) { List estimatedTasks = emptyList(); if (startAtElement instanceof NodeElement) { - List> paths = graph.findPaths(startAtElement); - estimatedTasks = convertToEstimatedTasks(paths).get(0); + List path = graph.findPath(startAtElement); + estimatedTasks = convertToEstimatedTasks(path); } return estimatedTasks; @@ -56,27 +56,27 @@ public List findTasksOnPath(BaseElement startAtElement) throws Ex List estimatedTasks = emptyList(); if (startAtElement instanceof NodeElement) { - List> paths = graph.findPaths(startAtElement, flowName); + List path = graph.findPath(startAtElement, flowName); //Maybe throw an exception if there are not path - if(paths.isEmpty()) { + if(path.isEmpty()) { throw new Exception("Not found"); } - estimatedTasks = convertToEstimatedTasks(paths).get(0); + estimatedTasks = convertToEstimatedTasks(path); } return estimatedTasks; } public Duration calculateEstimatedDuration(BaseElement startElement) { - List> paths = emptyList(); + List path = emptyList(); if (startElement instanceof NodeElement && StringUtils.isNotEmpty(flowName)) { - paths = graph.findPaths(startElement, flowName); + path = graph.findPath(startElement, flowName); } else { - paths = graph.findPaths(startElement); + path = graph.findPath(startElement); } - List estimatedTasks = convertToEstimatedTasks(paths).get(0); + List estimatedTasks = convertToEstimatedTasks(path); Duration total = Duration.ofHours(0); for(EstimatedTask item : estimatedTasks) { total = total.plus(item.getEstimatedDuration()); @@ -84,24 +84,20 @@ public Duration calculateEstimatedDuration(BaseElement startElement) { return total; } - private List> convertToEstimatedTasks(List> paths) { - - List> taskPaths = paths.stream().map(path -> filterAcceptedTask(path)).toList(); - - List> estimatedTaskPaths = taskPaths.stream().map(taskPath -> { - //Convert to EstimatedTask - List estimatedTasks = new ArrayList<>(); - for(int i = 0; i < taskPath.size(); i ++) { - List estimatedTaskResults = new ArrayList<>(); - Date startTimestamp = i == 0 ? new Date() : estimatedTasks.get(i - 1).calculateEstimatedEndTimestamp(); - estimatedTaskResults = createEstimatedTask(taskPath.get(i), startTimestamp); - - estimatedTasks.addAll(estimatedTaskResults); - } - return estimatedTasks; - }).toList(); - - return estimatedTaskPaths.stream().sorted(Comparator.comparing(List::size, Comparator.reverseOrder())).toList(); + private List convertToEstimatedTasks(List path) { + + List taskPath = filterAcceptedTask(path); + + // Convert to EstimatedTask + List estimatedTasks = new ArrayList<>(); + for (int i = 0; i < taskPath.size(); i++) { + List estimatedTaskResults = new ArrayList<>(); + Date startTimestamp = i == 0 ? new Date() : estimatedTasks.get(i - 1).calculateEstimatedEndTimestamp(); + estimatedTaskResults = createEstimatedTask(taskPath.get(i), startTimestamp); + + estimatedTasks.addAll(estimatedTaskResults); + } + return estimatedTasks; } private List filterAcceptedTask(List path) { From de59bfd6ba421cb21312ef3ac3d9c7f9a303d617 Mon Sep 17 00:00:00 2001 From: Trung Mai Date: Thu, 29 Feb 2024 16:53:31 +0700 Subject: [PATCH 029/143] TE-538: Update findAllTasks with new login --- .../axonivy/utils/estimator/ProcessGraph.java | 109 ++++++++++++++---- .../utils/estimator/WorkflowEstimator.java | 2 +- 2 files changed, 89 insertions(+), 22 deletions(-) diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java b/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java index fe028531..ea7ecd5f 100644 --- a/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java +++ b/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java @@ -12,19 +12,22 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Objects; +import java.util.Optional; import java.util.stream.Collectors; +import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.ListUtils; import org.apache.commons.collections4.map.HashedMap; -import com.google.common.base.Objects; - import ch.ivyteam.ivy.process.model.BaseElement; import ch.ivyteam.ivy.process.model.NodeElement; import ch.ivyteam.ivy.process.model.Process; import ch.ivyteam.ivy.process.model.connector.SequenceFlow; import ch.ivyteam.ivy.process.model.element.EmbeddedProcessElement; +import ch.ivyteam.ivy.process.model.element.TaskAndCaseModifier; import ch.ivyteam.ivy.process.model.element.event.end.TaskEnd; +import ch.ivyteam.ivy.process.model.element.event.start.RequestStart; import ch.ivyteam.ivy.process.model.element.event.start.StartEvent; import ch.ivyteam.ivy.process.model.element.gateway.Alternative; import ch.ivyteam.ivy.process.model.element.gateway.TaskSwitchGateway; @@ -80,7 +83,7 @@ private boolean hasFlowName(SequenceFlow sequenceFlow, String flowName) { private boolean isDefaultPath(BaseElement currentElement, Alternative previousElement) { String currentElementId = currentElement.getPid().getFieldId(); String nextTargetId = getNextTargetIdByCondition((Alternative) previousElement, EMPTY); - return Objects.equal(currentElementId, nextTargetId); + return Objects.equals(currentElementId, nextTargetId); } private boolean isDefaultPath(SequenceFlow flow) { @@ -103,40 +106,38 @@ private String getNextTargetIdByCondition(Alternative alternative, String condit return nextTargetId; } - /** * Find base on start node and check condition at each */ private List findPath(BaseElement from, List previousElements) { - - //Prevent loop + + // Prevent loop if (previousElements.indexOf(from) >= 0) { return emptyList(); } List path = new ArrayList<>(); path.add(from); - + if (from instanceof NodeElement) { List outs = ((NodeElement) from).getOutgoing(); - - Map> paths = new HashedMap<>(); + + Map> paths = new HashedMap<>(); for (SequenceFlow out : outs) { List currentPath = ListUtils.union(previousElements, Arrays.asList(from)); - paths.put(out, findPath(out.getTarget(), currentPath)); - } - - var longestPath = paths.entrySet().stream() - .max(Comparator.comparingInt(entry -> entry.getValue().size())) - .orElse(null); - - if(longestPath != null) { - path.add(longestPath.getKey()); - path.addAll(longestPath.getValue()); + List nextOfPath = findPath(out.getTarget(), currentPath); + paths.put(out, nextOfPath); } + + paths.entrySet().stream() + .sorted(Map.Entry.comparingByValue(Comparator.comparing(List::size, Comparator.reverseOrder()))) + .forEach(entry -> { + path.add(entry.getKey()); + path.addAll(entry.getValue()); + }); } - - return path; + + return path.stream().distinct().toList(); } public List findPathByFlowName(BaseElement from, String flowName) { @@ -182,6 +183,49 @@ public List findPathByFlowName(BaseElement from, String flowName) { return path.stream().distinct().toList(); } + + public List findPath(BaseElement from, String flowName, boolean isFindAllTask) { + List path = new ArrayList<>(); + path.add(from); + + if (from instanceof NodeElement) { + NodeElement target = (NodeElement) from; + while (target.getOutgoing().size() > 0) { + + SequenceFlow flow = null; + if(target instanceof EmbeddedProcessElement) { + List subPaths = findPathOfSubProcessByFlowName((EmbeddedProcessElement)target, flowName); + path.addAll(subPaths); + flow = findCorrectSequenceFlow(target, flowName); + } else if(target instanceof TaskSwitchGateway) { + List parallelTasks = findPathOfTaskSwitchGatewayByFlowName((TaskSwitchGateway) target, flowName); + path.addAll(parallelTasks); + flow = ((NodeElement) parallelTasks.get(parallelTasks.size() - 1)).getOutgoing().stream().findFirst().orElse(null); + } else { + flow = findCorrectSequenceFlow(target, flowName); + } + + if(flow == null) { + if(!(path.get(path.size() - 1) instanceof TaskEnd)) { + //Can not find any way + path.clear(); + } + break; + } + + path.add(flow); + + target = flow.getTarget(); + //Prevent loop + if (path.indexOf(target) >= 0) { + break; + } + path.add(target); + } + } + + return path.stream().distinct().toList(); + } /** * Find path on sub process */ @@ -250,6 +294,10 @@ private List findPathOfTaskSwitchGatewayByFlowName(TaskSwitchGatewa private SequenceFlow findCorrectSequenceFlow(NodeElement nodeElement, String flowName) { List outs = nodeElement.getOutgoing(); + if(CollectionUtils.isEmpty(outs)) { + return null; + } + //High priority for checking default path if flowName is null if(nodeElement instanceof Alternative) { if(isEmpty(flowName)) { @@ -270,4 +318,23 @@ private SequenceFlow findCorrectSequenceFlow(NodeElement nodeElement, String flo return flow; } + + private boolean isAcceptedTask(BaseElement element) { + return Optional.ofNullable(element) + // filter to get task only + .filter(node -> { + return node instanceof TaskAndCaseModifier; + }).map(TaskAndCaseModifier.class::cast) + .filter(node -> { + return node instanceof RequestStart == false; + }) + // Remove SYSTEM task + .filter(node -> { + return isSystemTask(node) == false; + }).isPresent(); + } + + private boolean isSystemTask(TaskAndCaseModifier task) { + return task.getAllTaskConfigs().stream().anyMatch(it -> "SYSTEM".equals(it.getActivator().getName())); + } } diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java index bf3c1f89..277567f6 100644 --- a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java +++ b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java @@ -92,7 +92,7 @@ private List convertToEstimatedTasks(List path) { List estimatedTasks = new ArrayList<>(); for (int i = 0; i < taskPath.size(); i++) { List estimatedTaskResults = new ArrayList<>(); - Date startTimestamp = i == 0 ? new Date() : estimatedTasks.get(i - 1).calculateEstimatedEndTimestamp(); + Date startTimestamp = i == 0 ? new Date() : estimatedTasks.get(estimatedTasks.size() - 1).calculateEstimatedEndTimestamp(); estimatedTaskResults = createEstimatedTask(taskPath.get(i), startTimestamp); estimatedTasks.addAll(estimatedTaskResults); From db37c6a08b5dd185787cf048d1eb3f87868fc5dc Mon Sep 17 00:00:00 2001 From: Trung Mai Date: Thu, 29 Feb 2024 21:01:16 +0700 Subject: [PATCH 030/143] TE-538: Using recursion for both case --- .../estimator/test/FlowExampleBasicTest.java | 12 +- .../estimator/test/FlowExampleErrorTest.java | 2 +- .../axonivy/utils/estimator/ProcessGraph.java | 307 ++++++------------ .../utils/estimator/WorkflowEstimator.java | 11 +- 4 files changed, 103 insertions(+), 229 deletions(-) diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleBasicTest.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleBasicTest.java index 2d778c8d..d42fee7a 100644 --- a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleBasicTest.java +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleBasicTest.java @@ -36,7 +36,7 @@ public static void setup() { } @Test - void shouldFindAllTasksAtStart() { + void shouldFindAllTasksAtStart() throws Exception { var workflowEstimator = new WorkflowEstimator(process, null, null); List estimatedTasks = workflowEstimator.findAllTasks(start); @@ -44,7 +44,7 @@ void shouldFindAllTasksAtStart() { } @Test - void shouldFfindAllTasksAtTaskB() { + void shouldFfindAllTasksAtTaskB() throws Exception { var workflowEstimator = new WorkflowEstimator(process, null, null); var estimatedTasks = workflowEstimator.findAllTasks(taskB); @@ -52,7 +52,7 @@ void shouldFfindAllTasksAtTaskB() { } @Test - void shouldFindAllTasksAtTaskC() { + void shouldFindAllTasksAtTaskC() throws Exception { var workflowEstimator = new WorkflowEstimator(process, null, null); var estimatedTasks = workflowEstimator.findAllTasks(taskC); @@ -60,7 +60,7 @@ void shouldFindAllTasksAtTaskC() { } @Test - void shouldFindAllTasksAtNewStart() { + void shouldFindAllTasksAtNewStart() throws Exception { var workflowEstimator = new WorkflowEstimator(process, null, null); var estimatedTasks = workflowEstimator.findAllTasks(newStart); @@ -100,7 +100,7 @@ void shouldFindTasksOnPathWithoutFlowNameAtNewStart() throws Exception { } @Test - void shouldFindAllTasksOfInternalFlowAtStart() { + void shouldFindAllTasksOfInternalFlowAtStart() throws Exception { var workflowEstimator = new WorkflowEstimator(process, null, "internal"); var estimatedTasks = workflowEstimator.findAllTasks(start); @@ -156,7 +156,7 @@ void shouldFindTasksOnPathOfMixedFlowAtNewStart() throws Exception { } @Test - void shouldCalculateTotalDuration() { + void shouldCalculateTotalDuration() throws Exception { var workflowEstimator = new WorkflowEstimator(process, null, null); Duration duration = workflowEstimator.calculateEstimatedDuration(graph.findStart()); assertEquals(15, duration.toHours()); diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleErrorTest.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleErrorTest.java index ddfb6a63..217596d8 100644 --- a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleErrorTest.java +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleErrorTest.java @@ -44,7 +44,7 @@ void shouldFindTasksOnPathAtStartWithFlowNameNull() { workflowEstimator.findTasksOnPath(start); }); - String expectedMessage = "Not found"; + String expectedMessage = "Not found path"; String actualMessage = exception.getMessage(); assertEquals(expectedMessage, actualMessage); diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java b/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java index ea7ecd5f..c31e31ca 100644 --- a/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java +++ b/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java @@ -14,19 +14,18 @@ import java.util.Map.Entry; import java.util.Objects; import java.util.Optional; -import java.util.stream.Collectors; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.ListUtils; import org.apache.commons.collections4.map.HashedMap; +import ch.ivyteam.ivy.environment.Ivy; import ch.ivyteam.ivy.process.model.BaseElement; import ch.ivyteam.ivy.process.model.NodeElement; import ch.ivyteam.ivy.process.model.Process; import ch.ivyteam.ivy.process.model.connector.SequenceFlow; import ch.ivyteam.ivy.process.model.element.EmbeddedProcessElement; import ch.ivyteam.ivy.process.model.element.TaskAndCaseModifier; -import ch.ivyteam.ivy.process.model.element.event.end.TaskEnd; import ch.ivyteam.ivy.process.model.element.event.start.RequestStart; import ch.ivyteam.ivy.process.model.element.event.start.StartEvent; import ch.ivyteam.ivy.process.model.element.gateway.Alternative; @@ -42,75 +41,20 @@ public ProcessGraph(Process process) { } /** - * Using Recursion Algorithm To Find All The Path On Graph. - * Return all paths + * Using Recursion Algorithm To Find Tasks On Graph. + * @throws Exception */ - public List findPath(BaseElement from) { - List path = findPath(from, emptyList()); + public List findPath(BaseElement from) throws Exception { + List path = findPath(from, EMPTY, true, emptyList()); return path; } - public List findPath(BaseElement from, String flowName) { - List path = findPathByFlowName(from, flowName); + public List findPath(BaseElement from, String flowName) throws Exception { + List path = findPath(from, flowName, false, emptyList()); return path; } - - private boolean hasFlowNameOrEmpty(SequenceFlow sequenceFlow, String flowName) { - if(isEmpty(flowName)) { - return true; - } - - if(hasFlowName(sequenceFlow, flowName)) { - return true; - } - - if(isEmpty(sequenceFlow.getEdge().getLabel().getText())) { - return true; - }; - - return false; - } - - private boolean hasFlowName(SequenceFlow sequenceFlow, String flowName) { - String label = sequenceFlow.getEdge().getLabel().getText(); - if (isNotBlank(label) && label.contains(flowName)) { - return true; - } - - return false; - } - - private boolean isDefaultPath(BaseElement currentElement, Alternative previousElement) { - String currentElementId = currentElement.getPid().getFieldId(); - String nextTargetId = getNextTargetIdByCondition((Alternative) previousElement, EMPTY); - return Objects.equals(currentElementId, nextTargetId); - } - - private boolean isDefaultPath(SequenceFlow flow) { - NodeElement sourceElement = flow.getSource(); - if(sourceElement instanceof Alternative) { - return isDefaultPath(flow, (Alternative) sourceElement); - } - - return false; - } - - private String getNextTargetIdByCondition(Alternative alternative, String condition) { - IvyScriptExpression script = IvyScriptExpression.script(defaultString(condition)); - String nextTargetId = alternative.getConditions().conditions().entrySet().stream() - .filter(entry -> script.equals(entry.getValue())) - .findFirst() - .map(Entry::getKey) - .orElse(null); - - return nextTargetId; - } - - /** - * Find base on start node and check condition at each - */ - private List findPath(BaseElement from, List previousElements) { + public List findPath(BaseElement from, String flowName, boolean isFindAllTasks, List previousElements) throws Exception { // Prevent loop if (previousElements.indexOf(from) >= 0) { return emptyList(); @@ -120,12 +64,22 @@ private List findPath(BaseElement from, List previousE path.add(from); if (from instanceof NodeElement) { - List outs = ((NodeElement) from).getOutgoing(); - + if(from instanceof EmbeddedProcessElement) { + List pathFromSubProcess = findPathOfSubProcess((EmbeddedProcessElement) from, flowName, isFindAllTasks); + path.addAll(pathFromSubProcess); + } + + List outs = getSequenceFlows((NodeElement) from, flowName, isFindAllTasks); + + if (from instanceof Alternative && outs.isEmpty()) { + Ivy.log().error("Can not found the out going from a alternative {0}", from.getPid().getRawPid()); + throw new Exception("Not found path"); + } + Map> paths = new HashedMap<>(); for (SequenceFlow out : outs) { List currentPath = ListUtils.union(previousElements, Arrays.asList(from)); - List nextOfPath = findPath(out.getTarget(), currentPath); + List nextOfPath = findPath(out.getTarget(), flowName, isFindAllTasks, currentPath); paths.put(out, nextOfPath); } @@ -136,103 +90,17 @@ private List findPath(BaseElement from, List previousE path.addAll(entry.getValue()); }); } - - return path.stream().distinct().toList(); - } - - public List findPathByFlowName(BaseElement from, String flowName) { - List path = new ArrayList<>(); - path.add(from); - - if (from instanceof NodeElement) { - NodeElement target = (NodeElement) from; - while (target.getOutgoing().size() > 0) { - - SequenceFlow flow = null; - if(target instanceof EmbeddedProcessElement) { - List subPaths = findPathOfSubProcessByFlowName((EmbeddedProcessElement)target, flowName); - path.addAll(subPaths); - flow = findCorrectSequenceFlow(target, flowName); - } else if(target instanceof TaskSwitchGateway) { - List parallelTasks = findPathOfTaskSwitchGatewayByFlowName((TaskSwitchGateway) target, flowName); - path.addAll(parallelTasks); - flow = ((NodeElement) parallelTasks.get(parallelTasks.size() - 1)).getOutgoing().stream().findFirst().orElse(null); - } else { - flow = findCorrectSequenceFlow(target, flowName); - } - - if(flow == null) { - if(!(path.get(path.size() - 1) instanceof TaskEnd)) { - //Can not find any way - path.clear(); - } - break; - } - - path.add(flow); - - target = flow.getTarget(); - //Prevent loop - if (path.indexOf(target) >= 0) { - break; - } - path.add(target); - } - } return path.stream().distinct().toList(); } - - public List findPath(BaseElement from, String flowName, boolean isFindAllTask) { - List path = new ArrayList<>(); - path.add(from); - - if (from instanceof NodeElement) { - NodeElement target = (NodeElement) from; - while (target.getOutgoing().size() > 0) { - - SequenceFlow flow = null; - if(target instanceof EmbeddedProcessElement) { - List subPaths = findPathOfSubProcessByFlowName((EmbeddedProcessElement)target, flowName); - path.addAll(subPaths); - flow = findCorrectSequenceFlow(target, flowName); - } else if(target instanceof TaskSwitchGateway) { - List parallelTasks = findPathOfTaskSwitchGatewayByFlowName((TaskSwitchGateway) target, flowName); - path.addAll(parallelTasks); - flow = ((NodeElement) parallelTasks.get(parallelTasks.size() - 1)).getOutgoing().stream().findFirst().orElse(null); - } else { - flow = findCorrectSequenceFlow(target, flowName); - } - - if(flow == null) { - if(!(path.get(path.size() - 1) instanceof TaskEnd)) { - //Can not find any way - path.clear(); - } - break; - } - - path.add(flow); - - target = flow.getTarget(); - //Prevent loop - if (path.indexOf(target) >= 0) { - break; - } - path.add(target); - } - } - - return path.stream().distinct().toList(); - } /** * Find path on sub process - */ - private List findPathOfSubProcessByFlowName(EmbeddedProcessElement subProcessElement, String flowName) { + */ + private List findPathOfSubProcess(EmbeddedProcessElement subProcessElement, String flowName, boolean isFindAllTasks) throws Exception { // find start element BaseElement start = findStartElementOfProcess(subProcessElement.getEmbeddedProcess()); - var listElement = findPathByFlowName(start, flowName); + var listElement = findPath(start, flowName, isFindAllTasks, emptyList()); return listElement; } @@ -242,83 +110,94 @@ private BaseElement findStartElementOfProcess(Process subProcess) { return start; } - private List findPathOfTaskSwitchGatewayByFlowName(TaskSwitchGateway from, String flowName) { - List> paths = new ArrayList>(); - - List outs = from.getOutgoing(); - for (SequenceFlow out : outs) { - - List path = new ArrayList<>(); - path.add(out); - - NodeElement target = out.getTarget(); - while (target.getOutgoing().size() > 0) { - path.add(target); - - SequenceFlow flow = null; - if(target instanceof TaskSwitchGateway) { - List subTasks = findPathOfTaskSwitchGatewayByFlowName((TaskSwitchGateway) target, flowName); - path.addAll(subTasks); - } else { - flow = findCorrectSequenceFlow(target, flowName); - } - - if(flow == null) { - target = null; - break; - } - - path.add(flow); - - target = flow.getTarget(); - // Prevent loop - if (path.indexOf(target) >= 0) { - break; - } - } - - if(target != null) { - path.add(target); - } - - paths.add(path); + private List getSequenceFlows(NodeElement from, String flowName, boolean isFindAllTasks) { + if (isFindAllTasks || from instanceof TaskSwitchGateway) { + return from.getOutgoing(); + } else { + return getSequenceFlow(from, flowName).map(Arrays::asList).orElse(emptyList()); } - - List elements = paths.stream() - .sorted(Comparator.comparing(List::size, Comparator.reverseOrder())) - .flatMap(List::stream) - .collect(Collectors.toList()); - - return elements; } - private SequenceFlow findCorrectSequenceFlow(NodeElement nodeElement, String flowName) { + private Optional getSequenceFlow(NodeElement nodeElement, String flowName) { List outs = nodeElement.getOutgoing(); if(CollectionUtils.isEmpty(outs)) { - return null; + return Optional.empty(); } - - //High priority for checking default path if flowName is null + if(nodeElement instanceof Alternative) { + // High priority for checking default path if flowName is null if(isEmpty(flowName)) { - return outs.stream().filter(out -> isDefaultPath(out)).findFirst().orElse(null); + return outs.stream().filter(out -> isDefaultPath(out)).findFirst(); } else { - SequenceFlow flow = outs.stream().filter(out -> hasFlowName(out, flowName)).findFirst().orElse(null); - if(flow == null) { - flow = outs.stream().filter(out -> isDefaultPath(out)).findFirst().orElse(null); + //If flowName is not null, check flowName first + Optional flow = outs.stream().filter(out -> hasFlowName(out, flowName)).findFirst(); + // Then check default path + if(!flow.isPresent()) { + flow = outs.stream().filter(out -> isDefaultPath(out)).findFirst(); } return flow; } } - SequenceFlow flow = outs.stream() + Optional flow = outs.stream() .filter(out -> hasFlowNameOrEmpty(out, flowName)) - .findFirst() - .orElse(null); + .findFirst(); return flow; } + + private boolean hasFlowNameOrEmpty(SequenceFlow sequenceFlow, String flowName) { + if(isEmpty(flowName)) { + return true; + } + + if(hasFlowName(sequenceFlow, flowName)) { + return true; + } + + if(isEmpty(sequenceFlow.getEdge().getLabel().getText())) { + return true; + }; + + return false; + } + + private boolean hasFlowName(SequenceFlow sequenceFlow, String flowName) { + String label = sequenceFlow.getEdge().getLabel().getText(); + if (isNotBlank(label) && label.contains(flowName)) { + return true; + } + + return false; + } + + private boolean isDefaultPath(BaseElement currentElement, Alternative previousElement) { + String currentElementId = currentElement.getPid().getFieldId(); + String nextTargetId = getNextTargetIdByCondition((Alternative) previousElement, EMPTY); + return Objects.equals(currentElementId, nextTargetId); + } + + private boolean isDefaultPath(SequenceFlow flow) { + NodeElement sourceElement = flow.getSource(); + if(sourceElement instanceof Alternative) { + return isDefaultPath(flow, (Alternative) sourceElement); + } + + return false; + } + + private String getNextTargetIdByCondition(Alternative alternative, String condition) { + IvyScriptExpression script = IvyScriptExpression.script(defaultString(condition)); + String nextTargetId = alternative.getConditions().conditions().entrySet().stream() + .filter(entry -> script.equals(entry.getValue())) + .findFirst() + .map(Entry::getKey) + .orElse(null); + + return nextTargetId; + } + private boolean isAcceptedTask(BaseElement element) { return Optional.ofNullable(element) // filter to get task only diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java index 277567f6..306bd1d2 100644 --- a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java +++ b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java @@ -41,7 +41,7 @@ public WorkflowEstimator(Process process, Enum useCase, String flowName) { this.graph = new ProcessGraph(process); } - public List findAllTasks(BaseElement startAtElement) { + public List findAllTasks(BaseElement startAtElement) throws Exception { List estimatedTasks = emptyList(); if (startAtElement instanceof NodeElement) { @@ -56,19 +56,14 @@ public List findTasksOnPath(BaseElement startAtElement) throws Ex List estimatedTasks = emptyList(); if (startAtElement instanceof NodeElement) { - List path = graph.findPath(startAtElement, flowName); - //Maybe throw an exception if there are not path - if(path.isEmpty()) { - throw new Exception("Not found"); - } - + List path = graph.findPath(startAtElement, flowName); estimatedTasks = convertToEstimatedTasks(path); } return estimatedTasks; } - public Duration calculateEstimatedDuration(BaseElement startElement) { + public Duration calculateEstimatedDuration(BaseElement startElement) throws Exception { List path = emptyList(); if (startElement instanceof NodeElement && StringUtils.isNotEmpty(flowName)) { path = graph.findPath(startElement, flowName); From 1671a9a7b539296629f741d52fb33da23ddaceef Mon Sep 17 00:00:00 2001 From: Trung Mai Date: Fri, 1 Mar 2024 08:59:03 +0700 Subject: [PATCH 031/143] TE-538: Unit test with complex process --- .../processes/FlowExampleComplex.p.json | 348 ++++++++++++++++++ .../test/FlowExampleComplexTest.java | 63 ++++ 2 files changed, 411 insertions(+) create mode 100644 workflow-estimator-test/processes/FlowExampleComplex.p.json create mode 100644 workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleComplexTest.java diff --git a/workflow-estimator-test/processes/FlowExampleComplex.p.json b/workflow-estimator-test/processes/FlowExampleComplex.p.json new file mode 100644 index 00000000..e4914161 --- /dev/null +++ b/workflow-estimator-test/processes/FlowExampleComplex.p.json @@ -0,0 +1,348 @@ +{ + "$schema" : "https://json-schema.axonivy.com/process/11.2.2/process.json", + "id" : "18DF31B990019995", + "config" : { + "data" : "com.axonivy.utils.estimator.test.Data" + }, + "elements" : [ { + "id" : "f0", + "type" : "RequestStart", + "name" : "start", + "config" : { + "signature" : "start" + }, + "visual" : { + "at" : { "x" : 96, "y" : 64 } + }, + "connect" : [ + { "id" : "f3", "to" : "f2" } + ] + }, { + "id" : "f1", + "type" : "TaskEnd", + "visual" : { + "at" : { "x" : 1736, "y" : 64 } + } + }, { + "id" : "f2", + "type" : "UserTask", + "name" : [ + "Task A", + "(Element Label)" + ], + "config" : { + "task" : { + "name" : "Task A", + "code" : [ + "import java.util.concurrent.TimeUnit;", + "", + "WfEstimate.setEstimate(5,TimeUnit.HOURS,UseCase.BIGPROJECT);" + ] + } + }, + "visual" : { + "at" : { "x" : 288, "y" : 64 } + }, + "connect" : [ + { "id" : "f5", "to" : "f4" } + ] + }, { + "id" : "f4", + "type" : "Alternative", + "config" : { + "conditions" : { + "f9" : "true" + } + }, + "visual" : { + "at" : { "x" : 480, "y" : 64 } + }, + "connect" : [ + { "id" : "f9", "to" : "f8", "label" : { + "name" : [ + "{internal}", + "{external}", + "{mixed}" + ] + } }, + { "id" : "f14", "to" : "f6", "color" : "default path" } + ] + }, { + "id" : "f7", + "type" : "UserTask", + "name" : "Task B", + "config" : { + "task" : { + "name" : "Task B", + "code" : [ + "import java.util.concurrent.TimeUnit;", + "", + "WfEstimate.setEstimate(6,TimeUnit.HOURS,UseCase.BIGPROJECT);", + "WfEstimate.setCustomInfo(\"abc\");" + ] + } + }, + "visual" : { + "at" : { "x" : 696, "y" : 224 } + }, + "connect" : [ + { "id" : "f15", "to" : "f6", "via" : [ { "x" : 808, "y" : 224 } ] } + ] + }, { + "id" : "f8", + "type" : "Alternative", + "name" : "int/ext?", + "config" : { + "conditions" : { + "f12" : "true", + "f10" : "" + } + }, + "visual" : { + "at" : { "x" : 480, "y" : 224 }, + "labelOffset" : { "x" : -24, "y" : 0 } + }, + "connect" : [ + { "id" : "f10", "to" : "f7", "color" : "default path", "label" : { + "name" : "{internal}" + } }, + { "id" : "f12", "to" : "f11", "via" : [ { "x" : 480, "y" : 344 } ], "label" : { + "name" : "{external}", + "segment" : 1.1, + "offset" : { "x" : 73, "y" : 1 } + } } + ] + }, { + "id" : "f11", + "type" : "UserTask", + "name" : "Task C", + "config" : { + "task" : { + "name" : "Task C", + "code" : [ + "import java.util.concurrent.TimeUnit;", + "", + "WfEstimate.setEstimate(4,TimeUnit.HOURS,UseCase.BIGPROJECT);" + ] + } + }, + "visual" : { + "at" : { "x" : 696, "y" : 344 } + }, + "connect" : [ + { "id" : "f22", "to" : "f13", "var" : "in1" } + ] + }, { + "id" : "f6", + "type" : "Alternative", + "visual" : { + "at" : { "x" : 808, "y" : 64 } + }, + "connect" : [ + { "id" : "f29", "to" : "f16", "color" : "default path" } + ] + }, { + "id" : "f17", + "type" : "ProcessAnnotation", + "name" : "green path = default path with empty condition", + "visual" : { + "at" : { "x" : 224, "y" : 135 }, + "size" : { "width" : 294, "height" : 19 }, + "color" : "default path" + } + }, { + "id" : "f18", + "type" : "ProcessAnnotation", + "name" : [ + "useCase=null / flowName = null", + "", + "findAllTasks( start ) => Task A, Task C, Task1A, Task1B, Task D, Task E, Task2A, Task2B, Task G, Task H, Task F, Task K, Task B", + "", + "useCase=null / flowName = internal", + "findTasksOnPath( start ) => Task A, Task B, Task E, Task2A, Task2B, Task G, Task H" + ], + "visual" : { + "at" : { "x" : 507, "y" : 547 }, + "size" : { "width" : 773, "height" : 247 } + } + }, { + "id" : "f13", + "type" : "TaskSwitchGateway", + "name" : "Task1", + "config" : { + "tasks" : [ { + "id" : "TaskA", + "name" : "Task1A" + }, { + "id" : "TaskB", + "name" : "Task1B" + } ] + }, + "visual" : { + "at" : { "x" : 912, "y" : 344 }, + "labelOffset" : { "x" : 56, "y" : -8 } + }, + "connect" : [ + { "id" : "f24", "to" : "f25", "via" : [ { "x" : 912, "y" : 456 } ], "condition" : "ivp==\"TaskA.ivp\"" }, + { "id" : "f26", "to" : "f23", "condition" : "ivp==\"TaskB.ivp\"" } + ] + }, { + "id" : "f23", + "type" : "UserTask", + "name" : "Task D", + "visual" : { + "at" : { "x" : 912, "y" : 168 } + }, + "connect" : [ + { "id" : "f31", "to" : "f16" } + ] + }, { + "id" : "f25", + "type" : "UserTask", + "name" : "Task E", + "visual" : { + "at" : { "x" : 1096, "y" : 456 } + }, + "connect" : [ + { "id" : "f27", "to" : "f47" } + ] + }, { + "id" : "f16", + "type" : "Alternative", + "config" : { + "conditions" : { + "f21" : "true", + "f40" : "" + } + }, + "visual" : { + "at" : { "x" : 1096, "y" : 64 } + }, + "connect" : [ + { "id" : "f40", "to" : "f30", "color" : "default path" }, + { "id" : "f21", "to" : "f25", "label" : { + "name" : [ + "{internal}", + "{external}", + "{mixed}" + ] + } } + ] + }, { + "id" : "f32", + "type" : "TaskSwitchGateway", + "name" : "Task2", + "config" : { + "tasks" : [ { + "id" : "TaskA", + "name" : "Task2A" + }, { + "id" : "TaskB", + "name" : "Task2B" + } ] + }, + "visual" : { + "at" : { "x" : 1432, "y" : 456 }, + "labelOffset" : { "x" : 14, "y" : 34 } + }, + "connect" : [ + { "id" : "f36", "to" : "f35", "condition" : "ivp==\"TaskA.ivp\"" }, + { "id" : "f43", "to" : "f42", "condition" : "ivp==\"TaskB.ivp\"" } + ] + }, { + "id" : "f34", + "type" : "UserTask", + "name" : "Task F", + "visual" : { + "at" : { "x" : 1248, "y" : 256 } + }, + "connect" : [ + { "id" : "f41", "to" : "f39", "var" : "in1" } + ] + }, { + "id" : "f35", + "type" : "UserTask", + "name" : "Task H", + "visual" : { + "at" : { "x" : 1608, "y" : 456 } + }, + "connect" : [ + { "id" : "f38", "to" : "f37" } + ] + }, { + "id" : "f37", + "type" : "TaskEnd", + "visual" : { + "at" : { "x" : 1816, "y" : 456 } + } + }, { + "id" : "f39", + "type" : "TaskSwitchGateway", + "name" : "Join", + "config" : { + "tasks" : [ { + "id" : "TaskA", + "name" : "JoiningTask", + "responsible" : { + "activator" : "SYSTEM" + } + }, { + "id" : "TaskB" + } ] + }, + "visual" : { + "at" : { "x" : 1432, "y" : 256 } + }, + "connect" : [ + { "id" : "f46", "to" : "f45", "condition" : "ivp==\"TaskA.ivp\"" }, + { "id" : "f33", "to" : "f16", "condition" : "ivp==\"TaskB.ivp\"" } + ] + }, { + "id" : "f42", + "type" : "UserTask", + "name" : "Task G", + "visual" : { + "at" : { "x" : 1432, "y" : 344 } + }, + "connect" : [ + { "id" : "f44", "to" : "f39", "var" : "in2" } + ] + }, { + "id" : "f45", + "type" : "TaskEnd", + "visual" : { + "at" : { "x" : 1736, "y" : 256 } + } + }, { + "id" : "f47", + "type" : "Alternative", + "config" : { + "conditions" : { + "f28" : "true", + "f48" : "" + } + }, + "visual" : { + "at" : { "x" : 1248, "y" : 456 } + }, + "connect" : [ + { "id" : "f48", "to" : "f32", "color" : "default path", "var" : "in1" }, + { "id" : "f28", "to" : "f34" } + ] + }, { + "id" : "f30", + "type" : "UserTask", + "name" : "Task K", + "visual" : { + "at" : { "x" : 1480, "y" : 64 } + }, + "connect" : [ + { "id" : "f49", "to" : "f1" } + ] + } ], + "layout" : { + "colors" : { + "default path" : "#06f416" + } + } +} \ No newline at end of file diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleComplexTest.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleComplexTest.java new file mode 100644 index 00000000..e3a775eb --- /dev/null +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleComplexTest.java @@ -0,0 +1,63 @@ +package com.axonivy.utils.estimator.test; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import java.util.List; + +import org.assertj.core.util.Arrays; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import com.axonivy.utils.estimator.WorkflowEstimator; +import com.axonivy.utils.estimator.model.EstimatedTask; + +import ch.ivyteam.ivy.environment.IvyTest; +import ch.ivyteam.ivy.process.model.BaseElement; + +@IvyTest +@SuppressWarnings("restriction") +public class FlowExampleComplexTest extends FlowExampleTest { + + private static BaseElement start; + private static BaseElement taskD; + private static final String PROCESS_NAME = "FlowExampleComplex"; + + @BeforeAll + public static void setup() { + setup(PROCESS_NAME); + start = graph.findByElementName("start"); + taskD = graph.findByElementName("Task D"); + } + + @Test + void shouldFindAllTasksAtStart() throws Exception { + var workflowEstimator = new WorkflowEstimator(process, null, null); + List estimatedTasks = workflowEstimator.findAllTasks(start); + + var expected = Arrays.array("Task A", "Task C", "Task1A", "Task1B", "Task D", "Task E", "Task2A", "Task2B", + "Task G", "Task H", "Task F", "Task K", "Task B"); + var taskNames = getTaskNames(estimatedTasks); + assertArrayEquals(expected, taskNames); + } + + @Test + void shouldFindAllTasksAtTaskD() throws Exception { + var workflowEstimator = new WorkflowEstimator(process, null, null); + List estimatedTasks = workflowEstimator.findAllTasks(taskD); + + var expected = Arrays.array("Task D", "Task E", "Task2A", "Task2B", "Task G", "Task H", "Task F", "Task K"); + var taskNames = getTaskNames(estimatedTasks); + assertArrayEquals(expected, taskNames); + } + + @Test + void shouldFindTasksOnPathAtStart() throws Exception { + var workflowEstimator = new WorkflowEstimator(process, null, "internal"); + List estimatedTasks = workflowEstimator.findTasksOnPath(start); + + var expected = Arrays.array("Task A", "Task B", "Task E", "Task2A", "Task2B", "Task G", "Task H"); + var taskNames = getTaskNames(estimatedTasks); + assertArrayEquals(expected, taskNames); + } + +} From 00c99109fb43ec2790d4f0a1f8e16a9b237e5c78 Mon Sep 17 00:00:00 2001 From: Trung Mai Date: Fri, 1 Mar 2024 16:08:17 +0700 Subject: [PATCH 032/143] TE-538: Implement find all task with list start element --- .../test/FlowExampleComplexTest.java | 24 ++++++++++ .../axonivy/utils/estimator/ProcessGraph.java | 21 +++++--- .../utils/estimator/WorkflowEstimator.java | 48 +++++++++---------- 3 files changed, 62 insertions(+), 31 deletions(-) diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleComplexTest.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleComplexTest.java index e3a775eb..6c6c9ec4 100644 --- a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleComplexTest.java +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleComplexTest.java @@ -20,6 +20,7 @@ public class FlowExampleComplexTest extends FlowExampleTest { private static BaseElement start; private static BaseElement taskD; + private static BaseElement taskE; private static final String PROCESS_NAME = "FlowExampleComplex"; @BeforeAll @@ -27,6 +28,7 @@ public static void setup() { setup(PROCESS_NAME); start = graph.findByElementName("start"); taskD = graph.findByElementName("Task D"); + taskE = graph.findByElementName("Task E"); } @Test @@ -40,6 +42,19 @@ void shouldFindAllTasksAtStart() throws Exception { assertArrayEquals(expected, taskNames); } + @Test + void shouldFindAllTasksAtTaskKAndTaskF() throws Exception { + var workflowEstimator = new WorkflowEstimator(process, null, null); + var taskK = graph.findByElementName("Task K"); + var taskF = graph.findByElementName("Task F"); + + List estimatedTasks = workflowEstimator.findAllTasks(List.of(taskK, taskF)); + + var expected = Arrays.array("Task K", "Task F", "Task E", "Task2A", "Task2B", "Task H", "Task G"); + var taskNames = getTaskNames(estimatedTasks); + assertArrayEquals(expected, taskNames); + } + @Test void shouldFindAllTasksAtTaskD() throws Exception { var workflowEstimator = new WorkflowEstimator(process, null, null); @@ -60,4 +75,13 @@ void shouldFindTasksOnPathAtStart() throws Exception { assertArrayEquals(expected, taskNames); } + @Test + void shouldFindTasksOnPathWithoutFlowNameAtTaskDAndTaskE() throws Exception { + var workflowEstimator = new WorkflowEstimator(process, null, null); + List estimatedTasks = workflowEstimator.findTasksOnPath(List.of(taskD, taskE)); + + var expected = Arrays.array("Task D", "Task K", "Task E", "Task2A", "Task2B", "Task G", "Task H"); + var taskNames = getTaskNames(estimatedTasks); + assertArrayEquals(expected, taskNames); + } } diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java b/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java index c31e31ca..f56874bd 100644 --- a/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java +++ b/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java @@ -42,19 +42,28 @@ public ProcessGraph(Process process) { /** * Using Recursion Algorithm To Find Tasks On Graph. - * @throws Exception */ - public List findPath(BaseElement from) throws Exception { - List path = findPath(from, EMPTY, true, emptyList()); + public List findPath(BaseElement... from) throws Exception { + List path = findPath(Arrays.asList(from), null, true, emptyList()); return path; } - public List findPath(BaseElement from, String flowName) throws Exception { - List path = findPath(from, flowName, false, emptyList()); + public List findPath(String flowName, BaseElement... from) throws Exception { + List path = findPath(Arrays.asList(from), flowName, false, emptyList()); return path; } - public List findPath(BaseElement from, String flowName, boolean isFindAllTasks, List previousElements) throws Exception { + private List findPath(List froms, String flowName, boolean isFindAllTasks, List previousElements) throws Exception { + List result = new ArrayList<>(); + for(BaseElement from : froms) { + var path = findPath(from, flowName, isFindAllTasks, emptyList()); + result.addAll(path); + } + + return result.stream().distinct().toList(); + } + + private List findPath(BaseElement from, String flowName, boolean isFindAllTasks, List previousElements) throws Exception { // Prevent loop if (previousElements.indexOf(from) >= 0) { return emptyList(); diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java index 306bd1d2..7621daab 100644 --- a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java +++ b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java @@ -3,6 +3,7 @@ import static java.util.Collections.emptyList; import static org.apache.commons.lang3.StringUtils.EMPTY; import static org.apache.commons.lang3.StringUtils.defaultIfEmpty; +import static org.apache.commons.lang3.StringUtils.isNotEmpty; import java.time.Duration; import java.util.ArrayList; @@ -18,7 +19,6 @@ import com.axonivy.utils.estimator.model.EstimatedTask; import ch.ivyteam.ivy.process.model.BaseElement; -import ch.ivyteam.ivy.process.model.NodeElement; import ch.ivyteam.ivy.process.model.Process; import ch.ivyteam.ivy.process.model.element.TaskAndCaseModifier; import ch.ivyteam.ivy.process.model.element.event.start.RequestStart; @@ -42,40 +42,38 @@ public WorkflowEstimator(Process process, Enum useCase, String flowName) { } public List findAllTasks(BaseElement startAtElement) throws Exception { - List estimatedTasks = emptyList(); - - if (startAtElement instanceof NodeElement) { - List path = graph.findPath(startAtElement); - estimatedTasks = convertToEstimatedTasks(path); - } - + List path = graph.findPath(startAtElement); + List estimatedTasks = convertToEstimatedTasks(path); + return estimatedTasks; + } + + public List findAllTasks(List startAtElements) throws Exception { + List path = graph.findPath(startAtElements.toArray(new BaseElement[0])); + List estimatedTasks = convertToEstimatedTasks(path); return estimatedTasks; } public List findTasksOnPath(BaseElement startAtElement) throws Exception { - List estimatedTasks = emptyList(); - - if (startAtElement instanceof NodeElement) { - List path = graph.findPath(startAtElement, flowName); - estimatedTasks = convertToEstimatedTasks(path); - } - + List path = graph.findPath(flowName, startAtElement); + List estimatedTasks = convertToEstimatedTasks(path); + return estimatedTasks; + } + + public List findTasksOnPath(List startAtElements) throws Exception { + List path = graph.findPath(flowName, startAtElements.toArray(new BaseElement[0])); + List estimatedTasks = convertToEstimatedTasks(path); return estimatedTasks; } public Duration calculateEstimatedDuration(BaseElement startElement) throws Exception { - List path = emptyList(); - if (startElement instanceof NodeElement && StringUtils.isNotEmpty(flowName)) { - path = graph.findPath(startElement, flowName); - } else { - path = graph.findPath(startElement); - } + List path = isNotEmpty(flowName) + ? graph.findPath(flowName, startElement) + : graph.findPath(startElement); List estimatedTasks = convertToEstimatedTasks(path); - Duration total = Duration.ofHours(0); - for(EstimatedTask item : estimatedTasks) { - total = total.plus(item.getEstimatedDuration()); - } + + Duration total = estimatedTasks.stream().map(EstimatedTask::getEstimatedDuration).reduce((a,b) -> a.plus(b)).orElse(Duration.ZERO); + return total; } From 1b6c26888192ee6ba2ee4164295b1b153b6486fd Mon Sep 17 00:00:00 2001 From: "AAVN\\ntnchuong" Date: Mon, 4 Mar 2024 09:11:13 +0700 Subject: [PATCH 033/143] TE-538: add check task and sorting --- .../utils/estimator/test/FlowExampleComplexTest.java | 2 +- .../com/axonivy/utils/estimator/ProcessGraph.java | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleComplexTest.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleComplexTest.java index 6c6c9ec4..7a4dcd46 100644 --- a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleComplexTest.java +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleComplexTest.java @@ -50,7 +50,7 @@ void shouldFindAllTasksAtTaskKAndTaskF() throws Exception { List estimatedTasks = workflowEstimator.findAllTasks(List.of(taskK, taskF)); - var expected = Arrays.array("Task K", "Task F", "Task E", "Task2A", "Task2B", "Task H", "Task G"); + var expected = Arrays.array("Task K", "Task F", "Task E", "Task2A", "Task2B", "Task G", "Task H"); var taskNames = getTaskNames(estimatedTasks); assertArrayEquals(expected, taskNames); } diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java b/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java index f56874bd..50a1a73f 100644 --- a/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java +++ b/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java @@ -93,10 +93,10 @@ private List findPath(BaseElement from, String flowName, boolean is } paths.entrySet().stream() - .sorted(Map.Entry.comparingByValue(Comparator.comparing(List::size, Comparator.reverseOrder()))) + .sorted(Map.Entry.comparingByValue(Comparator.comparing(ProcessGraph::countNumberAcceptedTasks, Comparator.reverseOrder()))) .forEach(entry -> { path.add(entry.getKey()); - path.addAll(entry.getValue()); + path.addAll(entry.getValue()); }); } @@ -207,7 +207,11 @@ private String getNextTargetIdByCondition(Alternative alternative, String condit return nextTargetId; } - private boolean isAcceptedTask(BaseElement element) { + private static long countNumberAcceptedTasks(List listElements) { + return listElements.stream().filter(item -> isAcceptedTask(item)).count(); + } + + private static boolean isAcceptedTask(BaseElement element) { return Optional.ofNullable(element) // filter to get task only .filter(node -> { @@ -222,7 +226,7 @@ private boolean isAcceptedTask(BaseElement element) { }).isPresent(); } - private boolean isSystemTask(TaskAndCaseModifier task) { + private static boolean isSystemTask(TaskAndCaseModifier task) { return task.getAllTaskConfigs().stream().anyMatch(it -> "SYSTEM".equals(it.getActivator().getName())); } } From 6845a17c451e34838ee9808f7ae7a8da71ba562e Mon Sep 17 00:00:00 2001 From: Trung Mai Date: Mon, 4 Mar 2024 11:08:50 +0700 Subject: [PATCH 034/143] TE-538: Implement WorkflowEstimatorHelper.getBaseElementOf --- workflow-estimator-test/.gitignore | 44 ++++++++--------- .../processes/FlowExampleCommon.p.json | 37 ++++++++++++++ .../helper/WorkflowEstimatorHelperTest.java | 48 +++++++++++++++++++ workflow-estimator/.classpath | 1 + workflow-estimator/.project | 11 +++++ workflow-estimator/META-INF/MANIFEST.MF | 11 +++++ workflow-estimator/build.properties | 5 ++ .../helper/WorkflowEstimatorHelper.java | 24 ++++++++++ 8 files changed, 159 insertions(+), 22 deletions(-) create mode 100644 workflow-estimator-test/processes/FlowExampleCommon.p.json create mode 100644 workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/helper/WorkflowEstimatorHelperTest.java create mode 100644 workflow-estimator/META-INF/MANIFEST.MF create mode 100644 workflow-estimator/build.properties create mode 100644 workflow-estimator/src/com/axonivy/utils/estimator/helper/WorkflowEstimatorHelper.java diff --git a/workflow-estimator-test/.gitignore b/workflow-estimator-test/.gitignore index c75b6862..fba859a4 100644 --- a/workflow-estimator-test/.gitignore +++ b/workflow-estimator-test/.gitignore @@ -1,22 +1,22 @@ -# general -Thumbs.db -.DS_Store -*~ -*.log - -# java -*.class -hs_err_pid* - -# maven -target/ -lib/mvn-deps/ - -# ivy -classes/ -src_dataClasses/ -src_wsproc/ -logs/ - -# test -.temp-Rerun* \ No newline at end of file +# general +Thumbs.db +.DS_Store +*~ +*.log + +# java +*.class +hs_err_pid* + +# maven +target/ +lib/mvn-deps/ + +# ivy +classes/ +src_dataClasses/ +src_wsproc/ +logs/ + +# test +.temp-* diff --git a/workflow-estimator-test/processes/FlowExampleCommon.p.json b/workflow-estimator-test/processes/FlowExampleCommon.p.json new file mode 100644 index 00000000..13a6de99 --- /dev/null +++ b/workflow-estimator-test/processes/FlowExampleCommon.p.json @@ -0,0 +1,37 @@ +{ + "$schema" : "https://json-schema.axonivy.com/process/11.2.2/process.json", + "id" : "18E07978E73830FA", + "config" : { + "data" : "com.axonivy.utils.estimator.test.Data" + }, + "elements" : [ { + "id" : "f0", + "type" : "RequestStart", + "name" : "start", + "config" : { + "signature" : "start" + }, + "visual" : { + "at" : { "x" : 96, "y" : 64 } + }, + "connect" : [ + { "id" : "f3", "to" : "f2" } + ] + }, { + "id" : "f1", + "type" : "TaskEnd", + "visual" : { + "at" : { "x" : 472, "y" : 64 } + } + }, { + "id" : "f2", + "type" : "UserTask", + "name" : "TaskA", + "visual" : { + "at" : { "x" : 288, "y" : 64 } + }, + "connect" : [ + { "id" : "f4", "to" : "f1" } + ] + } ] +} \ No newline at end of file diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/helper/WorkflowEstimatorHelperTest.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/helper/WorkflowEstimatorHelperTest.java new file mode 100644 index 00000000..8fed26f3 --- /dev/null +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/helper/WorkflowEstimatorHelperTest.java @@ -0,0 +1,48 @@ +package com.axonivy.utils.estimator.test.helper; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +import org.junit.jupiter.api.Test; + +import com.axonivy.utils.estimator.helper.WorkflowEstimatorHelper; + +import ch.ivyteam.ivy.bpm.engine.client.BpmClient; +import ch.ivyteam.ivy.bpm.engine.client.ExecutionResult; +import ch.ivyteam.ivy.bpm.engine.client.element.BpmElement; +import ch.ivyteam.ivy.bpm.engine.client.element.BpmProcess; +import ch.ivyteam.ivy.bpm.exec.client.IvyProcessTest; +import ch.ivyteam.ivy.process.model.BaseElement; +import ch.ivyteam.ivy.workflow.ITask; + +@IvyProcessTest +@SuppressWarnings("restriction") +public class WorkflowEstimatorHelperTest { + private static final BpmProcess FLOW_EXAMPLE_COMMON = BpmProcess.name("FlowExampleCommon"); + private static final BpmElement FLOW_EXAMPLE_COMMON_START = FLOW_EXAMPLE_COMMON.elementName("start"); + + @Test + void shouldGetBaseElementOfTaskB(BpmClient bpmClient) { + ExecutionResult result = bpmClient.start().process(FLOW_EXAMPLE_COMMON_START).execute(); + ITask startTask = result.workflow().executedTask(); + + BaseElement startElement = WorkflowEstimatorHelper.getBaseElementOf(startTask); + assertNotNull(startElement); + assertEquals("start", startElement.getName()); + + result = bpmClient.start().anyActiveTask(result).as().everybody().execute(); + ITask taskA = result.workflow().executedTask(); + + BaseElement taskAElement = WorkflowEstimatorHelper.getBaseElementOf(taskA); + assertNotNull(taskA); + assertEquals("TaskA", taskAElement.getName()); + } + + @Test + void shouldGetBaseElementOfIsNull(BpmClient bpmClient) { + + BaseElement startElement = WorkflowEstimatorHelper.getBaseElementOf(null); + assertNull(startElement); + } +} diff --git a/workflow-estimator/.classpath b/workflow-estimator/.classpath index a24d7ccd..122ece25 100644 --- a/workflow-estimator/.classpath +++ b/workflow-estimator/.classpath @@ -24,5 +24,6 @@ + diff --git a/workflow-estimator/.project b/workflow-estimator/.project index c3e62b34..f68fddae 100644 --- a/workflow-estimator/.project +++ b/workflow-estimator/.project @@ -35,6 +35,16 @@ + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + ch.ivyteam.ivy.project.IvyProjectNature @@ -45,5 +55,6 @@ org.eclipse.jem.beaninfo.BeanInfoNature org.eclipse.wst.common.project.facet.core.nature org.eclipse.wst.jsdt.core.jsNature + org.eclipse.pde.PluginNature diff --git a/workflow-estimator/META-INF/MANIFEST.MF b/workflow-estimator/META-INF/MANIFEST.MF new file mode 100644 index 00000000..4702def0 --- /dev/null +++ b/workflow-estimator/META-INF/MANIFEST.MF @@ -0,0 +1,11 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: workflow-estimator +Bundle-SymbolicName: workflow-estimator +Bundle-Version: 1.0.0.qualifier +Bundle-RequiredExecutionEnvironment: JavaSE-17 +Automatic-Module-Name: workflow.estimator +Export-Package: com.axonivy.utils.estimator, + com.axonivy.utils.estimator.helper, + com.axonivy.utils.estimator.model +Require-Bundle: ch.ivyteam.ivy.process.rdm;bundle-version="11.2.1" diff --git a/workflow-estimator/build.properties b/workflow-estimator/build.properties new file mode 100644 index 00000000..54c38485 --- /dev/null +++ b/workflow-estimator/build.properties @@ -0,0 +1,5 @@ +source.. = src/,\ + src_wsproc/,\ + src_dataClasses/ +bin.includes = META-INF/,\ + . diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/helper/WorkflowEstimatorHelper.java b/workflow-estimator/src/com/axonivy/utils/estimator/helper/WorkflowEstimatorHelper.java new file mode 100644 index 00000000..5c1b3074 --- /dev/null +++ b/workflow-estimator/src/com/axonivy/utils/estimator/helper/WorkflowEstimatorHelper.java @@ -0,0 +1,24 @@ +package com.axonivy.utils.estimator.helper; + +import ch.ivyteam.ivy.process.model.BaseElement; +import ch.ivyteam.ivy.process.model.Process; +import ch.ivyteam.ivy.process.rdm.IProcessManager; +import ch.ivyteam.ivy.workflow.ITask; +import ch.ivyteam.ivy.workflow.IWorkflowProcessModelVersion; + +@SuppressWarnings("restriction") +public class WorkflowEstimatorHelper { + + public static BaseElement getBaseElementOf(ITask task) { + if(task == null) { + return null; + } + + var pid = task.getStart().getProcessElementId(); + IWorkflowProcessModelVersion pmv = task.getProcessModelVersion(); + var manager = IProcessManager.instance().getProjectDataModelFor(pmv); + Process processRdm = (Process) manager.findProcess(pid.getProcessGuid(), true).getModel(); + BaseElement taskElement = processRdm.search().pid(pid).findOneDeep(); + return taskElement; + } +} From e69d62c618bc0badccb1d317309b3db2655afb86 Mon Sep 17 00:00:00 2001 From: Trung Mai Date: Mon, 4 Mar 2024 16:01:07 +0700 Subject: [PATCH 035/143] TE-538: Set process flow override --- .../axonivy/utils/estimator/ProcessGraph.java | 31 +++++++++++++++++-- .../utils/estimator/WorkflowEstimator.java | 10 ++++-- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java b/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java index 50a1a73f..fcb2645b 100644 --- a/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java +++ b/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java @@ -1,14 +1,17 @@ package com.axonivy.utils.estimator; import static java.util.Collections.emptyList; +import static java.util.Collections.emptyMap; import static org.apache.commons.lang3.StringUtils.EMPTY; import static org.apache.commons.lang3.StringUtils.defaultString; import static org.apache.commons.lang3.StringUtils.isEmpty; import static org.apache.commons.lang3.StringUtils.isNotBlank; +import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -35,11 +38,22 @@ @SuppressWarnings("restriction") public class ProcessGraph { public final Process process; - + + private Map durationOverrides = emptyMap(); + private Map processFlowOverrides = emptyMap() ; + public ProcessGraph(Process process) { this.process = process; } + public void setProcessFlowOverrides(HashMap processFlowOverrides) { + this.processFlowOverrides = processFlowOverrides; + } + + public void setDurationOverrides(HashMap durationOverrides) { + this.durationOverrides = durationOverrides; + } + /** * Using Recursion Algorithm To Find Tasks On Graph. */ @@ -123,7 +137,20 @@ private List getSequenceFlows(NodeElement from, String flowName, b if (isFindAllTasks || from instanceof TaskSwitchGateway) { return from.getOutgoing(); } else { - return getSequenceFlow(from, flowName).map(Arrays::asList).orElse(emptyList()); + Optional flow = Optional.empty(); + + //Always is priority check flow from flowOverrides first. + if(from instanceof Alternative) { + String flowIdFromOrverride = processFlowOverrides.get(from.getPid().getRawPid()); + flow = from.getOutgoing().stream().filter(out -> out.getPid().getRawPid().equals(flowIdFromOrverride)).findFirst(); + } + + //If it don't find out the flow from flowOverrides, it is base on the default flow in process + if(flow.isEmpty()) { + flow = getSequenceFlow(from, flowName); + } + + return flow.map(Arrays::asList).orElse(emptyList()); } } diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java index 7621daab..828bd399 100644 --- a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java +++ b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java @@ -10,6 +10,7 @@ import java.util.Arrays; import java.util.Comparator; import java.util.Date; +import java.util.HashMap; import java.util.List; import java.util.Optional; import java.util.concurrent.TimeUnit; @@ -28,14 +29,12 @@ @SuppressWarnings("restriction") public class WorkflowEstimator { - private Process process; private Enum useCase; private String flowName; public final ProcessGraph graph; - public WorkflowEstimator(Process process, Enum useCase, String flowName) { - this.process = process; + public WorkflowEstimator(Process process, Enum useCase, String flowName) { this.useCase = useCase; this.flowName = flowName; this.graph = new ProcessGraph(process); @@ -77,6 +76,11 @@ public Duration calculateEstimatedDuration(BaseElement startElement) throws Exce return total; } + public WorkflowEstimator setProcessFlowOverrides(HashMap processFlowOverrides) { + graph.setProcessFlowOverrides(processFlowOverrides); + return this; + } + private List convertToEstimatedTasks(List path) { List taskPath = filterAcceptedTask(path); From 31dac15a7c04dffe861d317ebbed0d88efd0d4a7 Mon Sep 17 00:00:00 2001 From: "AAVN\\ntnchuong" Date: Tue, 5 Mar 2024 11:07:14 +0700 Subject: [PATCH 036/143] TE-538: add set override duration --- .../processes/ParallelTasksExample.p.json | 14 +++- .../test/ParallelTasksExampleTest.java | 23 ++++++ .../axonivy/utils/estimator/ProcessGraph.java | 69 +++++++++++++++++- .../utils/estimator/WorkflowEstimator.java | 70 +++---------------- 4 files changed, 112 insertions(+), 64 deletions(-) diff --git a/workflow-estimator-test/processes/ParallelTasksExample.p.json b/workflow-estimator-test/processes/ParallelTasksExample.p.json index b3dce6eb..71be3f03 100644 --- a/workflow-estimator-test/processes/ParallelTasksExample.p.json +++ b/workflow-estimator-test/processes/ParallelTasksExample.p.json @@ -30,7 +30,12 @@ "config" : { "tasks" : [ { "id" : "TaskA", - "name" : "Task1A" + "name" : "Task1A", + "code" : [ + "import java.util.concurrent.TimeUnit;", + "", + "WfEstimate.setEstimate(5,TimeUnit.HOURS,UseCase.BIGPROJECT);" + ] }, { "id" : "TaskB", "name" : "Task1B" @@ -99,7 +104,12 @@ "config" : { "tasks" : [ { "id" : "TaskA", - "name" : "Task3A" + "name" : "Task3A", + "code" : [ + "import java.util.concurrent.TimeUnit;", + "", + "WfEstimate.setEstimate(5,TimeUnit.HOURS,UseCase.BIGPROJECT);" + ] }, { "id" : "TaskB", "name" : "Task3B" diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/ParallelTasksExampleTest.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/ParallelTasksExampleTest.java index 1bfd94f5..8fc37c11 100644 --- a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/ParallelTasksExampleTest.java +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/ParallelTasksExampleTest.java @@ -1,7 +1,10 @@ package com.axonivy.utils.estimator.test; import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import java.time.Duration; +import java.util.HashMap; import java.util.List; import org.assertj.core.util.Arrays; @@ -53,5 +56,25 @@ void shouldFindTasksOnPathAtStartWithFlowNameShortcut() throws Exception { var names = getTaskNames(estimatedTasks); assertArrayEquals(Arrays.array("Task1A", "Task1B", "Task2"), names); } + + @Test + void shouldFindOverrideDuration() throws Exception { + var workflowEstimator = new WorkflowEstimator(process, null, null); + + HashMap hashMap = new HashMap<>(); + hashMap.put("18DD185B60B6E769-f15-TaskA", Duration.ofHours(10)); + workflowEstimator.setDurationOverrides(hashMap); + + List estimatedTasks = workflowEstimator.findTasksOnPath(start); + assertEquals(estimatedTasks.get(3).getEstimatedDuration().toHours(), 10); + } + + @Test + void shouldFindDefaultDuration() throws Exception { + var workflowEstimator = new WorkflowEstimator(process, null, null); + List estimatedTasks = workflowEstimator.findTasksOnPath(start); + + assertEquals(estimatedTasks.get(3).getEstimatedDuration().toHours(), 5); + } } diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java b/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java index fcb2645b..e7b221fc 100644 --- a/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java +++ b/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java @@ -6,6 +6,7 @@ import static org.apache.commons.lang3.StringUtils.defaultString; import static org.apache.commons.lang3.StringUtils.isEmpty; import static org.apache.commons.lang3.StringUtils.isNotBlank; +import static org.apache.commons.lang3.StringUtils.isNotEmpty; import java.time.Duration; import java.util.ArrayList; @@ -15,12 +16,14 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.concurrent.TimeUnit; import java.util.Objects; import java.util.Optional; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.ListUtils; import org.apache.commons.collections4.map.HashedMap; +import org.apache.commons.lang3.StringUtils; import ch.ivyteam.ivy.environment.Ivy; import ch.ivyteam.ivy.process.model.BaseElement; @@ -34,6 +37,7 @@ import ch.ivyteam.ivy.process.model.element.gateway.Alternative; import ch.ivyteam.ivy.process.model.element.gateway.TaskSwitchGateway; import ch.ivyteam.ivy.process.model.element.value.IvyScriptExpression; +import ch.ivyteam.ivy.process.model.element.value.task.TaskConfig; @SuppressWarnings("restriction") public class ProcessGraph { @@ -66,6 +70,37 @@ public List findPath(String flowName, BaseElement... from) throws E List path = findPath(Arrays.asList(from), flowName, false, emptyList()); return path; } + + public String getCustomInfoByCode(TaskConfig task) { + String wfEstimateCode = getCodeLineByPrefix(task, "WfEstimate.setCustomInfo"); + String result = null; + if (isNotEmpty(wfEstimateCode)) { + result = StringUtils.substringBetween(wfEstimateCode, "(\"", "\")"); + } + return result; + } + + public String getTaskId(TaskAndCaseModifier task, TaskConfig taskConfig) { + String id = task.getPid().getRawPid(); + if (task instanceof TaskSwitchGateway) { + return id + "-" + taskConfig.getTaskIdentifier().getRawIdentifier(); + } else { + return id; + } + } + + public Duration getDuration(TaskAndCaseModifier task, TaskConfig taskConfig) { + String key = getTaskId(task, taskConfig); + if(durationOverrides.get(key) != null) { + return durationOverrides.get(key); + } else { + return getDurationByTaskScript(taskConfig); + } + } + + public static boolean isSystemTask(TaskAndCaseModifier task) { + return task.getAllTaskConfigs().stream().anyMatch(it -> "SYSTEM".equals(it.getActivator().getName())); + } private List findPath(List froms, String flowName, boolean isFindAllTasks, List previousElements) throws Exception { List result = new ArrayList<>(); @@ -182,7 +217,6 @@ private Optional getSequenceFlow(NodeElement nodeElement, String f return flow; } - private boolean hasFlowNameOrEmpty(SequenceFlow sequenceFlow, String flowName) { if(isEmpty(flowName)) { return true; @@ -253,7 +287,36 @@ private static boolean isAcceptedTask(BaseElement element) { }).isPresent(); } - private static boolean isSystemTask(TaskAndCaseModifier task) { - return task.getAllTaskConfigs().stream().anyMatch(it -> "SYSTEM".equals(it.getActivator().getName())); + private String getCodeLineByPrefix(TaskConfig task, String prefix) { + // strongly typed! + String script = Optional.of(task.getScript()).orElse(EMPTY); + String[] codeLines = script.split("\\n"); + String wfEstimateCode = Arrays.stream(codeLines) + .filter(line -> line.contains(prefix)) + .findFirst() + .orElse(EMPTY); + return wfEstimateCode; } + + private Duration getDurationByTaskScript(TaskConfig task) { + + String wfEstimateCode = getCodeLineByPrefix(task, "WfEstimate.setEstimate"); + if (StringUtils.isNotEmpty(wfEstimateCode)) { + String result = StringUtils.substringBetween(wfEstimateCode, "(", "UseCase"); + int amount = Integer.parseInt(result.substring(0, result.indexOf(","))); + String unit = result.substring(result.indexOf(".") + 1, result.lastIndexOf(",")); + + if(TimeUnit.DAYS.toString().equals(unit)) { + return Duration.ofDays(amount); + } else if (TimeUnit.HOURS.toString().equals(unit)) { + return Duration.ofHours(amount); + } else if(TimeUnit.MINUTES.toString().equals(unit)) { + return Duration.ofMinutes(amount); + } else if (TimeUnit.SECONDS.toString().equals(unit)) { + return Duration.ofSeconds(amount); + } + } + + return Duration.ofHours(0); + } } diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java index 828bd399..0d75036a 100644 --- a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java +++ b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java @@ -1,22 +1,15 @@ package com.axonivy.utils.estimator; import static java.util.Collections.emptyList; -import static org.apache.commons.lang3.StringUtils.EMPTY; import static org.apache.commons.lang3.StringUtils.defaultIfEmpty; import static org.apache.commons.lang3.StringUtils.isNotEmpty; import java.time.Duration; import java.util.ArrayList; -import java.util.Arrays; import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.List; -import java.util.Optional; -import java.util.concurrent.TimeUnit; - -import org.apache.commons.lang3.StringUtils; - import com.axonivy.utils.estimator.model.EstimatedTask; import ch.ivyteam.ivy.process.model.BaseElement; @@ -81,6 +74,11 @@ public WorkflowEstimator setProcessFlowOverrides(HashMap process return this; } + public WorkflowEstimator setDurationOverrides(HashMap durationOverrides) { + graph.setDurationOverrides(durationOverrides); + return this; + } + private List convertToEstimatedTasks(List path) { List taskPath = filterAcceptedTask(path); @@ -108,7 +106,7 @@ private List filterAcceptedTask(List path) { }) // Remove SYSTEM task .filter(node -> { - return isSystemTask(node) == false; + return ProcessGraph.isSystemTask(node) == false; }).map(TaskAndCaseModifier.class::cast) // filter the task which have estimated if needed .toList(); @@ -119,16 +117,16 @@ private List createEstimatedTask(TaskAndCaseModifier task, Date s List estimatedTasks = new ArrayList<>(); - taskConfigs.forEach(item -> { + taskConfigs.forEach(taskConfig -> { EstimatedTask estimatedTask = new EstimatedTask(); - estimatedTask.setPid(task.getPid().getRawPid()); + estimatedTask.setPid(graph.getTaskId(task, taskConfig)); estimatedTask.setParentElementNames(emptyList()); - estimatedTask.setTaskName(defaultIfEmpty(item.getName().getRawMacro(), task.getName())); - Duration estimatedDuration = getDurationByCode(item); + estimatedTask.setTaskName(defaultIfEmpty(taskConfig.getName().getRawMacro(), task.getName())); + Duration estimatedDuration = graph.getDuration(task, taskConfig); estimatedTask.setEstimatedDuration(estimatedDuration); estimatedTask.setEstimatedStartTimestamp(startTimestamp); - String customerInfo = getCustomInfoByCode(item); + String customerInfo = graph.getCustomInfoByCode(taskConfig); estimatedTask.setCustomInfo(customerInfo); estimatedTasks.add(estimatedTask); }); @@ -137,50 +135,4 @@ private List createEstimatedTask(TaskAndCaseModifier task, Date s .sorted(Comparator.comparing(EstimatedTask::getTaskName)) .toList(); } - - private String getWfEstimateLineFromCode(TaskConfig task, String prefix) { - // strongly typed! - String script = Optional.of(task.getScript()).orElse(EMPTY); - String[] codeLines = script.split("\\n"); - String wfEstimateCode = Arrays.stream(codeLines) - .filter(line -> line.contains(prefix)) - .findFirst() - .orElse(EMPTY); - return wfEstimateCode; - } - - private Duration getDurationByCode(TaskConfig task) { - - String wfEstimateCode = getWfEstimateLineFromCode(task, "WfEstimate.setEstimate"); - if (StringUtils.isNotEmpty(wfEstimateCode)) { - String result = StringUtils.substringBetween(wfEstimateCode, "(", "UseCase"); - int amount = Integer.parseInt(result.substring(0, result.indexOf(","))); - String unit = result.substring(result.indexOf(".") + 1, result.lastIndexOf(",")); - - if(TimeUnit.DAYS.toString().equals(unit)) { - return Duration.ofDays(amount); - } else if (TimeUnit.HOURS.toString().equals(unit)) { - return Duration.ofHours(amount); - } else if(TimeUnit.MINUTES.toString().equals(unit)) { - return Duration.ofMinutes(amount); - } else if (TimeUnit.SECONDS.toString().equals(unit)) { - return Duration.ofSeconds(amount); - } - } - - return Duration.ofHours(0); - } - - private String getCustomInfoByCode(TaskConfig task) { - String wfEstimateCode = getWfEstimateLineFromCode(task, "WfEstimate.setCustomInfo"); - if (StringUtils.isNotEmpty(wfEstimateCode)) { - String result = StringUtils.substringBetween(wfEstimateCode, "(\"", "\")"); - return result; - } - return null; - } - - private boolean isSystemTask(TaskAndCaseModifier task) { - return task.getAllTaskConfigs().stream().anyMatch(it -> "SYSTEM".equals(it.getActivator().getName())); - } } \ No newline at end of file From 44f8d93f48edf8565bf6c37f3e9b876e2546ff4a Mon Sep 17 00:00:00 2001 From: "AAVN\\ntnchuong" Date: Tue, 5 Mar 2024 14:33:35 +0700 Subject: [PATCH 037/143] TE-538: add calculate duration based on many starts --- .../processes/FlowExampleComplex.p.json | 45 +++++++++++++++++++ .../test/FlowExampleComplexTest.java | 10 +++++ .../utils/estimator/WorkflowEstimator.java | 12 +++++ 3 files changed, 67 insertions(+) diff --git a/workflow-estimator-test/processes/FlowExampleComplex.p.json b/workflow-estimator-test/processes/FlowExampleComplex.p.json index e4914161..e93b12c9 100644 --- a/workflow-estimator-test/processes/FlowExampleComplex.p.json +++ b/workflow-estimator-test/processes/FlowExampleComplex.p.json @@ -190,6 +190,15 @@ "id" : "f23", "type" : "UserTask", "name" : "Task D", + "config" : { + "task" : { + "code" : [ + "import java.util.concurrent.TimeUnit;", + "", + "WfEstimate.setEstimate(5,TimeUnit.HOURS,UseCase.BIGPROJECT);" + ] + } + }, "visual" : { "at" : { "x" : 912, "y" : 168 } }, @@ -200,6 +209,15 @@ "id" : "f25", "type" : "UserTask", "name" : "Task E", + "config" : { + "task" : { + "code" : [ + "import java.util.concurrent.TimeUnit;", + "", + "WfEstimate.setEstimate(6,TimeUnit.HOURS,UseCase.BIGPROJECT);" + ] + } + }, "visual" : { "at" : { "x" : 1096, "y" : 456 } }, @@ -253,6 +271,15 @@ "id" : "f34", "type" : "UserTask", "name" : "Task F", + "config" : { + "task" : { + "code" : [ + "import java.util.concurrent.TimeUnit;", + "", + "WfEstimate.setEstimate(2,TimeUnit.HOURS,UseCase.BIGPROJECT);" + ] + } + }, "visual" : { "at" : { "x" : 1248, "y" : 256 } }, @@ -263,6 +290,15 @@ "id" : "f35", "type" : "UserTask", "name" : "Task H", + "config" : { + "task" : { + "code" : [ + "import java.util.concurrent.TimeUnit;", + "", + "WfEstimate.setEstimate(5,TimeUnit.HOURS,UseCase.BIGPROJECT);" + ] + } + }, "visual" : { "at" : { "x" : 1608, "y" : 456 } }, @@ -301,6 +337,15 @@ "id" : "f42", "type" : "UserTask", "name" : "Task G", + "config" : { + "task" : { + "code" : [ + "import java.util.concurrent.TimeUnit;", + "", + "WfEstimate.setEstimate(1,TimeUnit.HOURS,UseCase.BIGPROJECT);" + ] + } + }, "visual" : { "at" : { "x" : 1432, "y" : 344 } }, diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleComplexTest.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleComplexTest.java index 7a4dcd46..a0d6b2ce 100644 --- a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleComplexTest.java +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleComplexTest.java @@ -1,7 +1,9 @@ package com.axonivy.utils.estimator.test; import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import java.time.Duration; import java.util.List; import org.assertj.core.util.Arrays; @@ -84,4 +86,12 @@ void shouldFindTasksOnPathWithoutFlowNameAtTaskDAndTaskE() throws Exception { var taskNames = getTaskNames(estimatedTasks); assertArrayEquals(expected, taskNames); } + + @Test + void shouldCalculateEstimateDurationBasedOnManyStartElements() throws Exception { + var workflowEstimator = new WorkflowEstimator(process, null, null); + Duration duration = workflowEstimator.calculateEstimatedDuration(List.of(taskD, taskE)); + + assertEquals(Duration.ofHours(19), duration); + } } diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java index 0d75036a..507b5063 100644 --- a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java +++ b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java @@ -68,6 +68,18 @@ public Duration calculateEstimatedDuration(BaseElement startElement) throws Exce return total; } + + public Duration calculateEstimatedDuration(List startElements) throws Exception { + List path = isNotEmpty(flowName) + ? graph.findPath(flowName, startElements.toArray(new BaseElement[0])) + : graph.findPath(startElements.toArray(new BaseElement[0])); + + List estimatedTasks = convertToEstimatedTasks(path); + + Duration total = estimatedTasks.stream().map(EstimatedTask::getEstimatedDuration).reduce((a,b) -> a.plus(b)).orElse(Duration.ZERO); + + return total; + } public WorkflowEstimator setProcessFlowOverrides(HashMap processFlowOverrides) { graph.setProcessFlowOverrides(processFlowOverrides); From 12b2f0d2e5efe9f65d8cfa3969e70dc8559d022c Mon Sep 17 00:00:00 2001 From: "AAVN\\ntnchuong" Date: Tue, 5 Mar 2024 14:33:35 +0700 Subject: [PATCH 038/143] TE-538: add calculate duration based on many starts --- .../processes/FlowExampleComplex.p.json | 45 +++++++++++++++++++ .../test/FlowExampleComplexTest.java | 10 +++++ .../test/ParallelTasksExampleTest.java | 6 +-- .../axonivy/utils/estimator/ProcessGraph.java | 28 ++++++------ .../utils/estimator/WorkflowEstimator.java | 12 +++++ 5 files changed, 84 insertions(+), 17 deletions(-) diff --git a/workflow-estimator-test/processes/FlowExampleComplex.p.json b/workflow-estimator-test/processes/FlowExampleComplex.p.json index e4914161..e93b12c9 100644 --- a/workflow-estimator-test/processes/FlowExampleComplex.p.json +++ b/workflow-estimator-test/processes/FlowExampleComplex.p.json @@ -190,6 +190,15 @@ "id" : "f23", "type" : "UserTask", "name" : "Task D", + "config" : { + "task" : { + "code" : [ + "import java.util.concurrent.TimeUnit;", + "", + "WfEstimate.setEstimate(5,TimeUnit.HOURS,UseCase.BIGPROJECT);" + ] + } + }, "visual" : { "at" : { "x" : 912, "y" : 168 } }, @@ -200,6 +209,15 @@ "id" : "f25", "type" : "UserTask", "name" : "Task E", + "config" : { + "task" : { + "code" : [ + "import java.util.concurrent.TimeUnit;", + "", + "WfEstimate.setEstimate(6,TimeUnit.HOURS,UseCase.BIGPROJECT);" + ] + } + }, "visual" : { "at" : { "x" : 1096, "y" : 456 } }, @@ -253,6 +271,15 @@ "id" : "f34", "type" : "UserTask", "name" : "Task F", + "config" : { + "task" : { + "code" : [ + "import java.util.concurrent.TimeUnit;", + "", + "WfEstimate.setEstimate(2,TimeUnit.HOURS,UseCase.BIGPROJECT);" + ] + } + }, "visual" : { "at" : { "x" : 1248, "y" : 256 } }, @@ -263,6 +290,15 @@ "id" : "f35", "type" : "UserTask", "name" : "Task H", + "config" : { + "task" : { + "code" : [ + "import java.util.concurrent.TimeUnit;", + "", + "WfEstimate.setEstimate(5,TimeUnit.HOURS,UseCase.BIGPROJECT);" + ] + } + }, "visual" : { "at" : { "x" : 1608, "y" : 456 } }, @@ -301,6 +337,15 @@ "id" : "f42", "type" : "UserTask", "name" : "Task G", + "config" : { + "task" : { + "code" : [ + "import java.util.concurrent.TimeUnit;", + "", + "WfEstimate.setEstimate(1,TimeUnit.HOURS,UseCase.BIGPROJECT);" + ] + } + }, "visual" : { "at" : { "x" : 1432, "y" : 344 } }, diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleComplexTest.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleComplexTest.java index 7a4dcd46..a0d6b2ce 100644 --- a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleComplexTest.java +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleComplexTest.java @@ -1,7 +1,9 @@ package com.axonivy.utils.estimator.test; import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import java.time.Duration; import java.util.List; import org.assertj.core.util.Arrays; @@ -84,4 +86,12 @@ void shouldFindTasksOnPathWithoutFlowNameAtTaskDAndTaskE() throws Exception { var taskNames = getTaskNames(estimatedTasks); assertArrayEquals(expected, taskNames); } + + @Test + void shouldCalculateEstimateDurationBasedOnManyStartElements() throws Exception { + var workflowEstimator = new WorkflowEstimator(process, null, null); + Duration duration = workflowEstimator.calculateEstimatedDuration(List.of(taskD, taskE)); + + assertEquals(Duration.ofHours(19), duration); + } } diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/ParallelTasksExampleTest.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/ParallelTasksExampleTest.java index 8fc37c11..4caa36df 100644 --- a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/ParallelTasksExampleTest.java +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/ParallelTasksExampleTest.java @@ -61,9 +61,9 @@ void shouldFindTasksOnPathAtStartWithFlowNameShortcut() throws Exception { void shouldFindOverrideDuration() throws Exception { var workflowEstimator = new WorkflowEstimator(process, null, null); - HashMap hashMap = new HashMap<>(); - hashMap.put("18DD185B60B6E769-f15-TaskA", Duration.ofHours(10)); - workflowEstimator.setDurationOverrides(hashMap); + HashMap durationOverride = new HashMap<>(); + durationOverride.put("18DD185B60B6E769-f15-TaskA", Duration.ofHours(10)); + workflowEstimator.setDurationOverrides(durationOverride); List estimatedTasks = workflowEstimator.findTasksOnPath(start); assertEquals(estimatedTasks.get(3).getEstimatedDuration().toHours(), 10); diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java b/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java index e7b221fc..da5b67e0 100644 --- a/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java +++ b/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java @@ -91,11 +91,7 @@ public String getTaskId(TaskAndCaseModifier task, TaskConfig taskConfig) { public Duration getDuration(TaskAndCaseModifier task, TaskConfig taskConfig) { String key = getTaskId(task, taskConfig); - if(durationOverrides.get(key) != null) { - return durationOverrides.get(key); - } else { - return getDurationByTaskScript(taskConfig); - } + return durationOverrides.getOrDefault(key, getDurationByTaskScript(taskConfig)); } public static boolean isSystemTask(TaskAndCaseModifier task) { @@ -306,15 +302,19 @@ private Duration getDurationByTaskScript(TaskConfig task) { int amount = Integer.parseInt(result.substring(0, result.indexOf(","))); String unit = result.substring(result.indexOf(".") + 1, result.lastIndexOf(",")); - if(TimeUnit.DAYS.toString().equals(unit)) { - return Duration.ofDays(amount); - } else if (TimeUnit.HOURS.toString().equals(unit)) { - return Duration.ofHours(amount); - } else if(TimeUnit.MINUTES.toString().equals(unit)) { - return Duration.ofMinutes(amount); - } else if (TimeUnit.SECONDS.toString().equals(unit)) { - return Duration.ofSeconds(amount); - } + switch (TimeUnit.valueOf(unit.toUpperCase())) { + case DAYS: + return Duration.ofDays(amount); + case HOURS: + return Duration.ofHours(amount); + case MINUTES: + return Duration.ofMinutes(amount); + case SECONDS: + return Duration.ofSeconds(amount); + default: + // Handle any unexpected TimeUnit + break; + } } return Duration.ofHours(0); diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java index 0d75036a..507b5063 100644 --- a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java +++ b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java @@ -68,6 +68,18 @@ public Duration calculateEstimatedDuration(BaseElement startElement) throws Exce return total; } + + public Duration calculateEstimatedDuration(List startElements) throws Exception { + List path = isNotEmpty(flowName) + ? graph.findPath(flowName, startElements.toArray(new BaseElement[0])) + : graph.findPath(startElements.toArray(new BaseElement[0])); + + List estimatedTasks = convertToEstimatedTasks(path); + + Duration total = estimatedTasks.stream().map(EstimatedTask::getEstimatedDuration).reduce((a,b) -> a.plus(b)).orElse(Duration.ZERO); + + return total; + } public WorkflowEstimator setProcessFlowOverrides(HashMap processFlowOverrides) { graph.setProcessFlowOverrides(processFlowOverrides); From 6294123e57389b0aef244a8fa84885aae13cdbba Mon Sep 17 00:00:00 2001 From: Trung Mai Date: Tue, 5 Mar 2024 15:33:28 +0700 Subject: [PATCH 039/143] TE-538: Add java doc --- .../axonivy/utils/estimator/ProcessGraph.java | 6 +-- .../utils/estimator/WorkflowEstimator.java | 54 +++++++++++++++++++ .../utils/estimator/model/EstimatedTask.java | 7 +++ 3 files changed, 64 insertions(+), 3 deletions(-) diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java b/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java index da5b67e0..b7db7fc2 100644 --- a/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java +++ b/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java @@ -58,9 +58,6 @@ public void setDurationOverrides(HashMap durationOverrides) { this.durationOverrides = durationOverrides; } - /** - * Using Recursion Algorithm To Find Tasks On Graph. - */ public List findPath(BaseElement... from) throws Exception { List path = findPath(Arrays.asList(from), null, true, emptyList()); return path; @@ -108,6 +105,9 @@ private List findPath(List froms, String flowName, boo return result.stream().distinct().toList(); } + /** + * Using Recursion Algorithm To Find Tasks On Graph. + */ private List findPath(BaseElement from, String flowName, boolean isFindAllTasks, List previousElements) throws Exception { // Prevent loop if (previousElements.indexOf(from) >= 0) { diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java index 507b5063..d7818ebc 100644 --- a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java +++ b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java @@ -27,36 +27,70 @@ public class WorkflowEstimator { public final ProcessGraph graph; + /** + * @param process - The process that should be analyzed. + * @param useCase - Use case that should be used to read duration values. + * @param flowName - Tag name we want to follow at alternative gateways. + */ public WorkflowEstimator(Process process, Enum useCase, String flowName) { this.useCase = useCase; this.flowName = flowName; this.graph = new ProcessGraph(process); } + /** + * Return a list of all tasks in the process which can be reached from the starting element. + * @param startAtElement – Element where we start traversing the process + * @return + * @throws Exception + */ public List findAllTasks(BaseElement startAtElement) throws Exception { List path = graph.findPath(startAtElement); List estimatedTasks = convertToEstimatedTasks(path); return estimatedTasks; } + /** + * @param startAtElements - Elements where we start traversing the process. In case of parallel tasks, the list will contain multiple objects. + * @return + * @throws Exception + */ public List findAllTasks(List startAtElements) throws Exception { List path = graph.findPath(startAtElements.toArray(new BaseElement[0])); List estimatedTasks = convertToEstimatedTasks(path); return estimatedTasks; } + /** + * Return a list of all tasks which are created when process follows the tagged flow. Uses the flow name set in the constructor. + * @param startAtElement – Element where we start traversing the process + * @return + * @throws Exception + */ public List findTasksOnPath(BaseElement startAtElement) throws Exception { List path = graph.findPath(flowName, startAtElement); List estimatedTasks = convertToEstimatedTasks(path); return estimatedTasks; } + /** + * @param startAtElements - Elements where we start traversing the process. In case of parallel tasks, the list will contain multiple objects. + * @return + * @throws Exception + */ public List findTasksOnPath(List startAtElements) throws Exception { List path = graph.findPath(flowName, startAtElements.toArray(new BaseElement[0])); List estimatedTasks = convertToEstimatedTasks(path); return estimatedTasks; } + /** + * This method can be used to calculate expected duration from a starting point in a process until all task are done and end of process is reached. + * It will summarize duration of all tasks on the path. In case of parallel process flows, it will always use the “critical path” (which means path with longer duration). + * @param startElement – Element where we start traversing the process + * @return + * @throws Exception + */ public Duration calculateEstimatedDuration(BaseElement startElement) throws Exception { List path = isNotEmpty(flowName) ? graph.findPath(flowName, startElement) @@ -69,6 +103,12 @@ public Duration calculateEstimatedDuration(BaseElement startElement) throws Exce return total; } + /** + * @param startElement – Elements where we start traversing the process. In case of parallel tasks, the list will contain multiple objects. + * @param startElements + * @return + * @throws Exception + */ public Duration calculateEstimatedDuration(List startElements) throws Exception { List path = isNotEmpty(flowName) ? graph.findPath(flowName, startElements.toArray(new BaseElement[0])) @@ -81,11 +121,25 @@ public Duration calculateEstimatedDuration(List startElements) thro return total; } + /** + * This method can be used to override configured path taken after an alternative gateway. + * @param processFlowOverrides + * key: element ID + task identifier (for support of callable sub-processes, we also need to add the path of parent elements. However, not needed in first versions.) + * value: chosen output PID + * @return + */ public WorkflowEstimator setProcessFlowOverrides(HashMap processFlowOverrides) { graph.setProcessFlowOverrides(processFlowOverrides); return this; } + /** + * This method can be used to override configured task duration of the model by own values. + * @param durationOverrides + * key: element ID + task identifier (for support of callable sub-processes, we also need to add the path of parent elements. However, not needed in first versions.) + * value: new duration + * @return + */ public WorkflowEstimator setDurationOverrides(HashMap durationOverrides) { graph.setDurationOverrides(durationOverrides); return this; diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/model/EstimatedTask.java b/workflow-estimator/src/com/axonivy/utils/estimator/model/EstimatedTask.java index e42e1a8d..0b008dfa 100644 --- a/workflow-estimator/src/com/axonivy/utils/estimator/model/EstimatedTask.java +++ b/workflow-estimator/src/com/axonivy/utils/estimator/model/EstimatedTask.java @@ -14,8 +14,15 @@ public class EstimatedTask { private String pid; private String taskName; private Duration estimatedDuration; + /** + * Names of parent process elements in the order they appeared. + * In case the task is not inside of a sub-process element, list will be empty. + */ private List parentElementNames; private Date estimatedStartTimestamp; + /** + * Custom string which can be set on the task element + */ private String customInfo; public String getPid() { From 0407c9e28d75c6b7c2abc7b9676b409626f5863d Mon Sep 17 00:00:00 2001 From: Trung Mai Date: Tue, 5 Mar 2024 16:53:15 +0700 Subject: [PATCH 040/143] TE-538: Update unit test --- .../test/FlowExampleComplexTest.java | 27 ++++++++++--------- .../axonivy/utils/estimator/ProcessGraph.java | 1 + 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleComplexTest.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleComplexTest.java index a0d6b2ce..8cf44fb3 100644 --- a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleComplexTest.java +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleComplexTest.java @@ -2,11 +2,13 @@ import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.time.Duration; import java.util.List; import org.assertj.core.util.Arrays; +import org.assertj.core.util.Lists; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -38,10 +40,10 @@ void shouldFindAllTasksAtStart() throws Exception { var workflowEstimator = new WorkflowEstimator(process, null, null); List estimatedTasks = workflowEstimator.findAllTasks(start); - var expected = Arrays.array("Task A", "Task C", "Task1A", "Task1B", "Task D", "Task E", "Task2A", "Task2B", + var expected = Lists.list("Task A", "Task C", "Task1A", "Task1B", "Task D", "Task E", "Task2A", "Task2B", "Task G", "Task H", "Task F", "Task K", "Task B"); - var taskNames = getTaskNames(estimatedTasks); - assertArrayEquals(expected, taskNames); + var taskNames = Lists.list(getTaskNames(estimatedTasks)); + assertTrue(expected.containsAll(taskNames)); } @Test @@ -52,9 +54,9 @@ void shouldFindAllTasksAtTaskKAndTaskF() throws Exception { List estimatedTasks = workflowEstimator.findAllTasks(List.of(taskK, taskF)); - var expected = Arrays.array("Task K", "Task F", "Task E", "Task2A", "Task2B", "Task G", "Task H"); - var taskNames = getTaskNames(estimatedTasks); - assertArrayEquals(expected, taskNames); + var expected = Lists.list("Task K", "Task F", "Task E", "Task2A", "Task2B", "Task G", "Task H"); + var taskNames = Lists.list(getTaskNames(estimatedTasks)); + assertTrue(expected.containsAll(taskNames)); } @Test @@ -62,9 +64,10 @@ void shouldFindAllTasksAtTaskD() throws Exception { var workflowEstimator = new WorkflowEstimator(process, null, null); List estimatedTasks = workflowEstimator.findAllTasks(taskD); - var expected = Arrays.array("Task D", "Task E", "Task2A", "Task2B", "Task G", "Task H", "Task F", "Task K"); - var taskNames = getTaskNames(estimatedTasks); - assertArrayEquals(expected, taskNames); + var expected = Lists.list("Task D", "Task E", "Task2A", "Task2B", "Task G", "Task H", "Task F", "Task K"); + var taskNames = Lists.list(getTaskNames(estimatedTasks)); + + assertTrue(expected.containsAll(taskNames)); } @Test @@ -72,9 +75,9 @@ void shouldFindTasksOnPathAtStart() throws Exception { var workflowEstimator = new WorkflowEstimator(process, null, "internal"); List estimatedTasks = workflowEstimator.findTasksOnPath(start); - var expected = Arrays.array("Task A", "Task B", "Task E", "Task2A", "Task2B", "Task G", "Task H"); - var taskNames = getTaskNames(estimatedTasks); - assertArrayEquals(expected, taskNames); + var expected = Lists.list("Task A", "Task B", "Task E", "Task2A", "Task2B", "Task G", "Task H"); + var taskNames = Lists.list(getTaskNames(estimatedTasks)); + assertTrue(expected.containsAll(taskNames)); } @Test diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java b/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java index b7db7fc2..416f16aa 100644 --- a/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java +++ b/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java @@ -138,6 +138,7 @@ private List findPath(BaseElement from, String flowName, boolean is } paths.entrySet().stream() + //Consider to count all element of remove this sort (what flow is drawn first it will go first .sorted(Map.Entry.comparingByValue(Comparator.comparing(ProcessGraph::countNumberAcceptedTasks, Comparator.reverseOrder()))) .forEach(entry -> { path.add(entry.getKey()); From c69b4556160232da7708a8d609d58aab0adb129a Mon Sep 17 00:00:00 2001 From: Trung Mai Date: Wed, 6 Mar 2024 09:12:23 +0700 Subject: [PATCH 041/143] TE-538: Update unit test for flow overrides --- .../test/FlowExampleComplexTest.java | 28 +++++++++++++++++++ .../axonivy/utils/estimator/ProcessGraph.java | 1 + 2 files changed, 29 insertions(+) diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleComplexTest.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleComplexTest.java index 8cf44fb3..7136f3f0 100644 --- a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleComplexTest.java +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleComplexTest.java @@ -5,6 +5,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.time.Duration; +import java.util.HashMap; import java.util.List; import org.assertj.core.util.Arrays; @@ -59,6 +60,18 @@ void shouldFindAllTasksAtTaskKAndTaskF() throws Exception { assertTrue(expected.containsAll(taskNames)); } + @Test + void shouldFindAllTasksAtTaskFAndTaskE() throws Exception { + var workflowEstimator = new WorkflowEstimator(process, null, null); + var taskF = graph.findByElementName("Task F"); + + List estimatedTasks = workflowEstimator.findAllTasks(List.of(taskF, taskE)); + + var expected = Lists.list("Task F", "Task E", "Task2A", "Task2B", "Task G", "Task H", "Task K"); + var taskNames = Lists.list(getTaskNames(estimatedTasks)); + assertTrue(expected.containsAll(taskNames)); + } + @Test void shouldFindAllTasksAtTaskD() throws Exception { var workflowEstimator = new WorkflowEstimator(process, null, null); @@ -97,4 +110,19 @@ void shouldCalculateEstimateDurationBasedOnManyStartElements() throws Exception assertEquals(Duration.ofHours(19), duration); } + + @Test + void shouldFindAllTasksWitProcessFlowOverridesAtTaskE() throws Exception { + var workflowEstimator = new WorkflowEstimator(process, null, null); + var flowOverrides = new HashMap(); + flowOverrides.put("18DF31B990019995-f47", "18DF31B990019995-f28"); + flowOverrides.put("18DF31B990019995-f16", "18DF31B990019995-f21"); + workflowEstimator.setProcessFlowOverrides(flowOverrides); + + List estimatedTasks = workflowEstimator.findTasksOnPath(taskE); + + var expected = Arrays.array("Task E", "Task F"); + var taskNames = getTaskNames(estimatedTasks); + assertArrayEquals(expected, taskNames); + } } diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java b/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java index 416f16aa..eb0d497e 100644 --- a/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java +++ b/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java @@ -44,6 +44,7 @@ public class ProcessGraph { public final Process process; private Map durationOverrides = emptyMap(); + // It only impart to find task base in flowName private Map processFlowOverrides = emptyMap() ; public ProcessGraph(Process process) { From 6bc05895439a89c1c16ae69332f8bd777a937521 Mon Sep 17 00:00:00 2001 From: "AAVN\\ntnchuong" Date: Thu, 7 Mar 2024 08:32:58 +0700 Subject: [PATCH 042/143] TE-538: add parent element name --- .../axonivy/utils/estimator/ProcessGraph.java | 24 ++++++++++++++++++- .../utils/estimator/WorkflowEstimator.java | 8 ++++--- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java b/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java index da5b67e0..9d4a531b 100644 --- a/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java +++ b/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java @@ -11,6 +11,7 @@ import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; @@ -97,6 +98,27 @@ public Duration getDuration(TaskAndCaseModifier task, TaskConfig taskConfig) { public static boolean isSystemTask(TaskAndCaseModifier task) { return task.getAllTaskConfigs().stream().anyMatch(it -> "SYSTEM".equals(it.getActivator().getName())); } + + public List getParentElementNames(TaskAndCaseModifier task){ + List parentElementNames = emptyList(); + if(task.getParent() instanceof EmbeddedProcessElement) { + parentElementNames = getParentElementNamesEmbeddedProcessElement(task.getParent()); + } + + Collections.reverse(parentElementNames); + return parentElementNames ; + } + + public List getParentElementNamesEmbeddedProcessElement(BaseElement parentElement){ + List result = new ArrayList<>(); + if(parentElement instanceof EmbeddedProcessElement) { + result.add(parentElement.getName()); + + List parentElementNames = getParentElementNamesEmbeddedProcessElement(((EmbeddedProcessElement)parentElement).getParent()); + result.addAll(parentElementNames); + } + return result; + } private List findPath(List froms, String flowName, boolean isFindAllTasks, List previousElements) throws Exception { List result = new ArrayList<>(); @@ -318,5 +340,5 @@ private Duration getDurationByTaskScript(TaskConfig task) { } return Duration.ofHours(0); - } + } } diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java index 507b5063..b51eb8a1 100644 --- a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java +++ b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java @@ -6,19 +6,21 @@ import java.time.Duration; import java.util.ArrayList; +import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.List; + import com.axonivy.utils.estimator.model.EstimatedTask; import ch.ivyteam.ivy.process.model.BaseElement; import ch.ivyteam.ivy.process.model.Process; +import ch.ivyteam.ivy.process.model.element.EmbeddedProcessElement; import ch.ivyteam.ivy.process.model.element.TaskAndCaseModifier; import ch.ivyteam.ivy.process.model.element.event.start.RequestStart; import ch.ivyteam.ivy.process.model.element.value.task.TaskConfig; - @SuppressWarnings("restriction") public class WorkflowEstimator { @@ -128,12 +130,12 @@ private List createEstimatedTask(TaskAndCaseModifier task, Date s List taskConfigs = task.getAllTaskConfigs(); List estimatedTasks = new ArrayList<>(); - + taskConfigs.forEach(taskConfig -> { EstimatedTask estimatedTask = new EstimatedTask(); estimatedTask.setPid(graph.getTaskId(task, taskConfig)); - estimatedTask.setParentElementNames(emptyList()); + estimatedTask.setParentElementNames(graph.getParentElementNames(task)); estimatedTask.setTaskName(defaultIfEmpty(taskConfig.getName().getRawMacro(), task.getName())); Duration estimatedDuration = graph.getDuration(task, taskConfig); estimatedTask.setEstimatedDuration(estimatedDuration); From ff9dc20f5ece54bcca65a472621e1e452acbf012 Mon Sep 17 00:00:00 2001 From: "AAVN\\ntnchuong" Date: Thu, 7 Mar 2024 10:37:39 +0700 Subject: [PATCH 043/143] TE-538: add element name field and unit test --- .../utils/estimator/test/FlowSubProcessTest.java | 11 +++++++++++ .../axonivy/utils/estimator/model/EstimatedTask.java | 9 +++++++++ 2 files changed, 20 insertions(+) diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowSubProcessTest.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowSubProcessTest.java index a8d52d3b..1f5b62ef 100644 --- a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowSubProcessTest.java +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowSubProcessTest.java @@ -1,8 +1,10 @@ package com.axonivy.utils.estimator.test; import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.List; +import java.util.Optional; import org.assertj.core.util.Arrays; import org.junit.jupiter.api.BeforeAll; @@ -42,5 +44,14 @@ void shouldFindAllTasksAtStartWithFlowNameNull() throws Exception { assertArrayEquals(Arrays.array("Task A", "Task B"), getTaskNames(estimatedTasks)); } + + @Test + void shouldFindTaskParentNames() throws Exception { + var workflowEstimator = new WorkflowEstimator(process, null, null); + List estimatedTasks = workflowEstimator.findAllTasks(start); + Optional taskA = estimatedTasks.stream().filter(item -> item.getTaskName().equals("Task A")).findFirst(); + + assertEquals(java.util.Arrays.asList("sub with two levels", "2nd level sub") , taskA.get().getParentElementNames()); + } } diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/model/EstimatedTask.java b/workflow-estimator/src/com/axonivy/utils/estimator/model/EstimatedTask.java index e42e1a8d..67d5eb4a 100644 --- a/workflow-estimator/src/com/axonivy/utils/estimator/model/EstimatedTask.java +++ b/workflow-estimator/src/com/axonivy/utils/estimator/model/EstimatedTask.java @@ -13,6 +13,7 @@ public class EstimatedTask { private String pid; private String taskName; + private String elementName; private Duration estimatedDuration; private List parentElementNames; private Date estimatedStartTimestamp; @@ -66,6 +67,14 @@ public void setCustomInfo(String customInfo) { this.customInfo = customInfo; } + public String getElementName() { + return elementName; + } + + public void setElementName(String elementName) { + this.elementName = elementName; + } + public Date calculateEstimatedEndTimestamp() { int durationInSecond = Optional.ofNullable(this.estimatedDuration) .map(Duration::getSeconds) From 085e582e976435bb6d7a22fade8e23c415c1c153 Mon Sep 17 00:00:00 2001 From: Trung Mai Date: Thu, 7 Mar 2024 12:17:27 +0700 Subject: [PATCH 044/143] TE-538: Add process graph unit test --- .../estimator/test/ProcessGraphHelper.java} | 15 ++-- .../estimator/test/FlowExampleBasicTest.java | 10 +-- .../test/FlowExampleComplexTest.java | 12 +-- .../estimator/test/FlowExampleErrorTest.java | 2 +- .../estimator/test/FlowExampleLoopTest.java | 2 +- .../utils/estimator/test/FlowExampleTest.java | 4 +- .../estimator/test/FlowSubProcessTest.java | 2 +- .../test/ParallelTasksExampleTest.java | 2 +- .../estimator/test/ProcessGraphTest.java | 87 +++++++++++++++++++ .../estimator/test/TaskTypesExampleTest.java | 2 +- 10 files changed, 109 insertions(+), 29 deletions(-) rename workflow-estimator-test/{src_test/com/axonivy/utils/estimator/test/ProcessGraph.java => src/com/axonivy/utils/estimator/test/ProcessGraphHelper.java} (70%) create mode 100644 workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/ProcessGraphTest.java diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/ProcessGraph.java b/workflow-estimator-test/src/com/axonivy/utils/estimator/test/ProcessGraphHelper.java similarity index 70% rename from workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/ProcessGraph.java rename to workflow-estimator-test/src/com/axonivy/utils/estimator/test/ProcessGraphHelper.java index 074c95a6..34410b58 100644 --- a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/ProcessGraph.java +++ b/workflow-estimator-test/src/com/axonivy/utils/estimator/test/ProcessGraphHelper.java @@ -6,25 +6,20 @@ import ch.ivyteam.ivy.process.model.element.event.start.RequestStart; @SuppressWarnings("restriction") -public class ProcessGraph { - public final Process process; - - public ProcessGraph(Process process) { - this.process = process; - } - - public RequestStart findStart() { +public class ProcessGraphHelper { + + public static RequestStart findStart(Process process) { return process.search().type(RequestStart.class).findOne(); } - public BaseElement findByElementName(String name) { + public static BaseElement findByElementName(Process process, String name) { return process.getElements().stream() .filter(el -> el.getName().equals(name)) .findFirst() .orElse(null); } - public BaseElement findByTaskName(String name) { + public static BaseElement findByTaskName(Process process, String name) { return process.getElements().stream() .filter(el -> {return el instanceof SingleTaskCreator;}) .filter(el ->((SingleTaskCreator) el).getTaskConfig().getName().getRawMacro().equals(name)) diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleBasicTest.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleBasicTest.java index d42fee7a..ad112ed2 100644 --- a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleBasicTest.java +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleBasicTest.java @@ -29,10 +29,10 @@ public class FlowExampleBasicTest extends FlowExampleTest { @BeforeAll public static void setup() { setup(PROCESS_NAME); - start = graph.findByElementName("start"); - newStart = graph.findByElementName("NewStart"); - taskB = graph.findByElementName("Task B"); - taskC = graph.findByElementName("Task C"); + start = ProcessGraphHelper.findByElementName(process, "start"); + newStart = ProcessGraphHelper.findByElementName(process, "NewStart"); + taskB = ProcessGraphHelper.findByElementName(process, "Task B"); + taskC = ProcessGraphHelper.findByElementName(process, "Task C"); } @Test @@ -158,7 +158,7 @@ void shouldFindTasksOnPathOfMixedFlowAtNewStart() throws Exception { @Test void shouldCalculateTotalDuration() throws Exception { var workflowEstimator = new WorkflowEstimator(process, null, null); - Duration duration = workflowEstimator.calculateEstimatedDuration(graph.findStart()); + Duration duration = workflowEstimator.calculateEstimatedDuration(start); assertEquals(15, duration.toHours()); } diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleComplexTest.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleComplexTest.java index 7136f3f0..5225a659 100644 --- a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleComplexTest.java +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleComplexTest.java @@ -31,9 +31,9 @@ public class FlowExampleComplexTest extends FlowExampleTest { @BeforeAll public static void setup() { setup(PROCESS_NAME); - start = graph.findByElementName("start"); - taskD = graph.findByElementName("Task D"); - taskE = graph.findByElementName("Task E"); + start = ProcessGraphHelper.findByElementName(process, "start"); + taskD = ProcessGraphHelper.findByElementName(process, "Task D"); + taskE = ProcessGraphHelper.findByElementName(process, "Task E"); } @Test @@ -50,8 +50,8 @@ void shouldFindAllTasksAtStart() throws Exception { @Test void shouldFindAllTasksAtTaskKAndTaskF() throws Exception { var workflowEstimator = new WorkflowEstimator(process, null, null); - var taskK = graph.findByElementName("Task K"); - var taskF = graph.findByElementName("Task F"); + var taskK = ProcessGraphHelper.findByElementName(process, "Task K"); + var taskF = ProcessGraphHelper.findByElementName(process, "Task F"); List estimatedTasks = workflowEstimator.findAllTasks(List.of(taskK, taskF)); @@ -63,7 +63,7 @@ void shouldFindAllTasksAtTaskKAndTaskF() throws Exception { @Test void shouldFindAllTasksAtTaskFAndTaskE() throws Exception { var workflowEstimator = new WorkflowEstimator(process, null, null); - var taskF = graph.findByElementName("Task F"); + var taskF = ProcessGraphHelper.findByElementName(process, "Task F"); List estimatedTasks = workflowEstimator.findAllTasks(List.of(taskF, taskE)); diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleErrorTest.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleErrorTest.java index 217596d8..b59d5550 100644 --- a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleErrorTest.java +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleErrorTest.java @@ -24,7 +24,7 @@ public class FlowExampleErrorTest extends FlowExampleTest { @BeforeAll public static void setup() { setup(PROCESS_NAME); - start = graph.findByElementName("start"); + start = ProcessGraphHelper.findByElementName(process, "start"); } @Test diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleLoopTest.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleLoopTest.java index 4b55b296..cff1a165 100644 --- a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleLoopTest.java +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleLoopTest.java @@ -24,7 +24,7 @@ public class FlowExampleLoopTest extends FlowExampleTest { @BeforeAll public static void setup() { setup(PROCESS_NAME); - start = graph.findByElementName("start"); + start = ProcessGraphHelper.findByElementName(process, "start"); } @Test diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleTest.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleTest.java index bd153561..6f73dda5 100644 --- a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleTest.java +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleTest.java @@ -11,13 +11,11 @@ public abstract class FlowExampleTest { protected static Process process; - protected static ProcessGraph graph; protected static void setup(String processName) { var pmv = Ivy.request().getProcessModelVersion(); var manager = IProcessManager.instance().getProjectDataModelFor(pmv); - process = manager.findProcessByPath(processName).getModel(); - graph = new ProcessGraph(process); + process = manager.findProcessByPath(processName).getModel(); } protected String[] getTaskNames(List tasks ) { diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowSubProcessTest.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowSubProcessTest.java index a8d52d3b..c6d5def7 100644 --- a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowSubProcessTest.java +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowSubProcessTest.java @@ -24,7 +24,7 @@ public class FlowSubProcessTest extends FlowExampleTest { @BeforeAll public static void setup() { setup(PROCESS_NAME); - start = graph.findByElementName("start"); + start = ProcessGraphHelper.findByElementName(process, "start"); } @Test diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/ParallelTasksExampleTest.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/ParallelTasksExampleTest.java index 4caa36df..2cbe51ca 100644 --- a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/ParallelTasksExampleTest.java +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/ParallelTasksExampleTest.java @@ -27,7 +27,7 @@ public class ParallelTasksExampleTest extends FlowExampleTest { @BeforeAll public static void setup() { setup(PROCESS_NAME); - start = graph.findByElementName("start"); + start = ProcessGraphHelper.findByElementName(process, "start"); } @Test diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/ProcessGraphTest.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/ProcessGraphTest.java new file mode 100644 index 00000000..248c2fbc --- /dev/null +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/ProcessGraphTest.java @@ -0,0 +1,87 @@ +package com.axonivy.utils.estimator.test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Arrays; + +import org.junit.jupiter.api.Test; + +import com.axonivy.utils.estimator.ProcessGraph; + +import ch.ivyteam.ivy.environment.Ivy; +import ch.ivyteam.ivy.environment.IvyTest; +import ch.ivyteam.ivy.process.model.Process; +import ch.ivyteam.ivy.process.model.element.TaskAndCaseModifier; +import ch.ivyteam.ivy.process.rdm.IProcessManager; + +@IvyTest +@SuppressWarnings("restriction") +public class ProcessGraphTest { + private static final String FLOW_EXAMPLE_BASIC = "FlowExampleBasic"; + private static final String PARALLEL_TASKS_EXAMPLE = "ParallelTasksExample"; + private static final String FLOW_SUB_PROCESS = "FlowSubprocess"; + + private Process getProcessByName(String processName) { + var pmv = Ivy.request().getProcessModelVersion(); + var manager = IProcessManager.instance().getProjectDataModelFor(pmv); + return manager.findProcessByPath(processName).getModel(); + } + + @Test + void shouldFindPathAtStart() throws Exception { + Process process = getProcessByName(FLOW_EXAMPLE_BASIC); + var start = ProcessGraphHelper.findByElementName(process, "start"); + var processGraph = new ProcessGraph(process); + var result = processGraph.findPath("internal", start); + + var expected = Arrays.asList( + "RequestStartZ:start (18DC44E096FDFF75-f0)", + "SequenceFlowZ:RequestStartZ->UserTaskZ", + "UserTaskZ:Task A\n(Element Label) (18DC44E096FDFF75-f2)", + "SequenceFlowZ:UserTaskZ->AlternativeZ", + "AlternativeZ: (18DC44E096FDFF75-f4)", + "SequenceFlowZ:AlternativeZ->AlternativeZ", + "AlternativeZ:int/ext? (18DC44E096FDFF75-f8)", + "SequenceFlowZ:AlternativeZ->UserTaskZ", + "UserTaskZ:Task B (18DC44E096FDFF75-f7)", + "SequenceFlowZ:UserTaskZ->AlternativeZ", + "AlternativeZ: (18DC44E096FDFF75-f6)", + "SequenceFlowZ:AlternativeZ->TaskEndZ", + "TaskEndZ: (18DC44E096FDFF75-f1)"); + for(int i = 0; i Date: Thu, 7 Mar 2024 07:56:32 +0100 Subject: [PATCH 045/143] Fixed process model (because it was not compatible with execution before) --- .../processes/FlowExampleComplex.p.json | 108 +++++++++--------- .../processes/ParallelTasksExample.p.json | 82 +++++++++---- 2 files changed, 111 insertions(+), 79 deletions(-) diff --git a/workflow-estimator-test/processes/FlowExampleComplex.p.json b/workflow-estimator-test/processes/FlowExampleComplex.p.json index e93b12c9..ed8cb5fa 100644 --- a/workflow-estimator-test/processes/FlowExampleComplex.p.json +++ b/workflow-estimator-test/processes/FlowExampleComplex.p.json @@ -106,10 +106,10 @@ { "id" : "f10", "to" : "f7", "color" : "default path", "label" : { "name" : "{internal}" } }, - { "id" : "f12", "to" : "f11", "via" : [ { "x" : 480, "y" : 344 } ], "label" : { + { "id" : "f12", "to" : "f11", "via" : [ { "x" : 480, "y" : 312 } ], "label" : { "name" : "{external}", - "segment" : 1.1, - "offset" : { "x" : 73, "y" : 1 } + "segment" : 1.14, + "offset" : { "x" : 67, "y" : -2 } } } ] }, { @@ -127,7 +127,7 @@ } }, "visual" : { - "at" : { "x" : 696, "y" : 344 } + "at" : { "x" : 696, "y" : 312 } }, "connect" : [ { "id" : "f22", "to" : "f13", "var" : "in1" } @@ -154,16 +154,14 @@ "id" : "f18", "type" : "ProcessAnnotation", "name" : [ - "useCase=null / flowName = null", - "", "findAllTasks( start ) => Task A, Task C, Task1A, Task1B, Task D, Task E, Task2A, Task2B, Task G, Task H, Task F, Task K, Task B", "", "useCase=null / flowName = internal", - "findTasksOnPath( start ) => Task A, Task B, Task E, Task2A, Task2B, Task G, Task H" + "findTasksOnPath( start ) => Task A, Task B, Task2A, Task2B, Task G, Task H" ], "visual" : { - "at" : { "x" : 507, "y" : 547 }, - "size" : { "width" : 773, "height" : 247 } + "at" : { "x" : 509, "y" : 464 }, + "size" : { "width" : 776, "height" : 80 } } }, { "id" : "f13", @@ -179,12 +177,12 @@ } ] }, "visual" : { - "at" : { "x" : 912, "y" : 344 }, - "labelOffset" : { "x" : 56, "y" : -8 } + "at" : { "x" : 848, "y" : 312 }, + "labelOffset" : { "x" : -16, "y" : -8 } }, "connect" : [ - { "id" : "f24", "to" : "f25", "via" : [ { "x" : 912, "y" : 456 } ], "condition" : "ivp==\"TaskA.ivp\"" }, - { "id" : "f26", "to" : "f23", "condition" : "ivp==\"TaskB.ivp\"" } + { "id" : "f24", "to" : "f25", "via" : [ { "x" : 848, "y" : 360 } ], "condition" : "ivp==\"TaskA.ivp\"" }, + { "id" : "f26", "to" : "f23", "via" : [ { "x" : 848, "y" : 256 } ], "condition" : "ivp==\"TaskB.ivp\"" } ] }, { "id" : "f23", @@ -200,10 +198,10 @@ } }, "visual" : { - "at" : { "x" : 912, "y" : 168 } + "at" : { "x" : 952, "y" : 256 } }, "connect" : [ - { "id" : "f31", "to" : "f16" } + { "id" : "f31", "to" : "f33", "via" : [ { "x" : 1048, "y" : 256 } ], "var" : "in1" } ] }, { "id" : "f25", @@ -219,10 +217,10 @@ } }, "visual" : { - "at" : { "x" : 1096, "y" : 456 } + "at" : { "x" : 952, "y" : 360 } }, "connect" : [ - { "id" : "f27", "to" : "f47" } + { "id" : "f27", "to" : "f33", "via" : [ { "x" : 1048, "y" : 360 } ], "var" : "in2" } ] }, { "id" : "f16", @@ -234,11 +232,11 @@ } }, "visual" : { - "at" : { "x" : 1096, "y" : 64 } + "at" : { "x" : 1128, "y" : 64 } }, "connect" : [ { "id" : "f40", "to" : "f30", "color" : "default path" }, - { "id" : "f21", "to" : "f25", "label" : { + { "id" : "f21", "to" : "f47", "label" : { "name" : [ "{internal}", "{external}", @@ -281,10 +279,10 @@ } }, "visual" : { - "at" : { "x" : 1248, "y" : 256 } + "at" : { "x" : 1248, "y" : 312 } }, "connect" : [ - { "id" : "f41", "to" : "f39", "var" : "in1" } + { "id" : "f41", "to" : "f19", "via" : [ { "x" : 1248, "y" : 224 } ] } ] }, { "id" : "f35", @@ -309,30 +307,8 @@ "id" : "f37", "type" : "TaskEnd", "visual" : { - "at" : { "x" : 1816, "y" : 456 } + "at" : { "x" : 1736, "y" : 456 } } - }, { - "id" : "f39", - "type" : "TaskSwitchGateway", - "name" : "Join", - "config" : { - "tasks" : [ { - "id" : "TaskA", - "name" : "JoiningTask", - "responsible" : { - "activator" : "SYSTEM" - } - }, { - "id" : "TaskB" - } ] - }, - "visual" : { - "at" : { "x" : 1432, "y" : 256 } - }, - "connect" : [ - { "id" : "f46", "to" : "f45", "condition" : "ivp==\"TaskA.ivp\"" }, - { "id" : "f33", "to" : "f16", "condition" : "ivp==\"TaskB.ivp\"" } - ] }, { "id" : "f42", "type" : "UserTask", @@ -347,17 +323,11 @@ } }, "visual" : { - "at" : { "x" : 1432, "y" : 344 } + "at" : { "x" : 1432, "y" : 312 } }, "connect" : [ - { "id" : "f44", "to" : "f39", "var" : "in2" } + { "id" : "f44", "to" : "f19", "via" : [ { "x" : 1552, "y" : 312 } ] } ] - }, { - "id" : "f45", - "type" : "TaskEnd", - "visual" : { - "at" : { "x" : 1736, "y" : 256 } - } }, { "id" : "f47", "type" : "Alternative", @@ -368,10 +338,10 @@ } }, "visual" : { - "at" : { "x" : 1248, "y" : 456 } + "at" : { "x" : 1128, "y" : 312 } }, "connect" : [ - { "id" : "f48", "to" : "f32", "color" : "default path", "var" : "in1" }, + { "id" : "f48", "to" : "f32", "via" : [ { "x" : 1128, "y" : 456 } ], "color" : "default path", "var" : "in1" }, { "id" : "f28", "to" : "f34" } ] }, { @@ -379,11 +349,39 @@ "type" : "UserTask", "name" : "Task K", "visual" : { - "at" : { "x" : 1480, "y" : 64 } + "at" : { "x" : 1608, "y" : 64 } }, "connect" : [ { "id" : "f49", "to" : "f1" } ] + }, { + "id" : "f19", + "type" : "Alternative", + "visual" : { + "at" : { "x" : 1552, "y" : 224 } + }, + "connect" : [ + { "id" : "f20", "to" : "f16" } + ] + }, { + "id" : "f33", + "type" : "TaskSwitchGateway", + "name" : "join", + "config" : { + "tasks" : [ { + "id" : "TaskA", + "responsible" : { + "activator" : "SYSTEM" + } + } ] + }, + "visual" : { + "at" : { "x" : 1048, "y" : 312 }, + "labelOffset" : { "x" : -8, "y" : -8 } + }, + "connect" : [ + { "id" : "f39", "to" : "f47", "condition" : "ivp==\"TaskA.ivp\"" } + ] } ], "layout" : { "colors" : { diff --git a/workflow-estimator-test/processes/ParallelTasksExample.p.json b/workflow-estimator-test/processes/ParallelTasksExample.p.json index 71be3f03..d46f5418 100644 --- a/workflow-estimator-test/processes/ParallelTasksExample.p.json +++ b/workflow-estimator-test/processes/ParallelTasksExample.p.json @@ -12,7 +12,7 @@ "signature" : "start" }, "visual" : { - "at" : { "x" : 96, "y" : 96 } + "at" : { "x" : 40, "y" : 48 } }, "connect" : [ { "id" : "f3", "to" : "f2", "var" : "in1" } @@ -21,7 +21,7 @@ "id" : "f1", "type" : "TaskEnd", "visual" : { - "at" : { "x" : 1264, "y" : 96 } + "at" : { "x" : 1208, "y" : 48 } } }, { "id" : "f2", @@ -42,8 +42,8 @@ } ] }, "visual" : { - "at" : { "x" : 240, "y" : 96 }, - "labelOffset" : { "x" : 14, "y" : 34 } + "at" : { "x" : 184, "y" : 48 }, + "labelOffset" : { "x" : -8, "y" : -8 } }, "connect" : [ { "id" : "f5", "to" : "f4", "condition" : "ivp==\"TaskA.ivp\"", "var" : "in1" }, @@ -63,8 +63,8 @@ } ] }, "visual" : { - "at" : { "x" : 1056, "y" : 96 }, - "labelOffset" : { "x" : 14, "y" : 34 } + "at" : { "x" : 1000, "y" : 48 }, + "labelOffset" : { "x" : -8, "y" : -8 } }, "connect" : [ { "id" : "f6", "to" : "f1", "condition" : "ivp==\"TaskA.ivp\"" } @@ -74,7 +74,7 @@ "type" : "TaskSwitchEvent", "name" : "Task2", "visual" : { - "at" : { "x" : 312, "y" : 168 }, + "at" : { "x" : 184, "y" : 136 }, "labelOffset" : { "x" : 14, "y" : 34 } }, "connect" : [ @@ -85,17 +85,18 @@ "type" : "Alternative", "config" : { "conditions" : { - "f14" : "true" + "f14" : "true", + "f11" : "" } }, "visual" : { - "at" : { "x" : 480, "y" : 168 } + "at" : { "x" : 424, "y" : 136 } }, "connect" : [ - { "id" : "f14", "to" : "f4", "label" : { + { "id" : "f11", "to" : "f15", "color" : "def", "var" : "in2" }, + { "id" : "f14", "to" : "f23", "label" : { "name" : "{shortcut}" - }, "var" : "in3" }, - { "id" : "f11", "to" : "f15", "color" : "def", "var" : "in2" } + } } ] }, { "id" : "f15", @@ -103,6 +104,9 @@ "name" : "Task3", "config" : { "tasks" : [ { + "id" : "TaskB", + "name" : "Task3B" + }, { "id" : "TaskA", "name" : "Task3A", "code" : [ @@ -110,9 +114,6 @@ "", "WfEstimate.setEstimate(5,TimeUnit.HOURS,UseCase.BIGPROJECT);" ] - }, { - "id" : "TaskB", - "name" : "Task3B" } ], "output" : { "map" : { @@ -121,28 +122,28 @@ } }, "visual" : { - "at" : { "x" : 480, "y" : 296 }, - "labelOffset" : { "x" : 14, "y" : 34 } + "at" : { "x" : 424, "y" : 208 }, + "labelOffset" : { "x" : -8, "y" : -8 } }, "connect" : [ - { "id" : "f12", "to" : "f4", "condition" : "ivp==\"TaskA.ivp\"", "var" : "in2" }, - { "id" : "f16", "to" : "f13", "condition" : "ivp==\"TaskB.ivp\"" } + { "id" : "f16", "to" : "f13", "via" : [ { "x" : 424, "y" : 288 } ], "condition" : "ivp==\"TaskB.ivp\"" }, + { "id" : "f12", "to" : "f21", "condition" : "ivp==\"TaskA.ivp\"", "var" : "in2" } ] }, { "id" : "f13", "type" : "Script", "visual" : { - "at" : { "x" : 840, "y" : 296 } + "at" : { "x" : 744, "y" : 288 } }, "connect" : [ - { "id" : "f17", "to" : "f4", "via" : [ { "x" : 1056, "y" : 296 } ], "var" : "in4" } + { "id" : "f17", "to" : "f21", "via" : [ { "x" : 1000, "y" : 288 } ], "var" : "in4" } ] }, { "id" : "f18", "type" : "ProcessAnnotation", "name" : "green path = default path with empty condition", "visual" : { - "at" : { "x" : 288, "y" : 231 }, + "at" : { "x" : 200, "y" : 239 }, "size" : { "width" : 294, "height" : 19 }, "color" : "def" } @@ -162,7 +163,7 @@ "findTasksOnPath( start.ivp ) => Task1A, Task1B, Task2" ], "visual" : { - "at" : { "x" : 376, "y" : 463 }, + "at" : { "x" : 288, "y" : 431 }, "size" : { "width" : 504, "height" : 163 } } }, { @@ -175,9 +176,42 @@ "Tasks assigned to SYSTEM must be ignored. We don't handle them! For example the SYSTEM task of the join element will not show up in any result." ], "visual" : { - "at" : { "x" : 853, "y" : 461 }, + "at" : { "x" : 783, "y" : 432 }, "size" : { "width" : 407, "height" : 158 } } + }, { + "id" : "f21", + "type" : "TaskSwitchGateway", + "name" : "Join", + "config" : { + "tasks" : [ { + "id" : "TaskA", + "responsible" : { + "activator" : "SYSTEM" + } + } ], + "output" : { + "map" : { + "out" : "in2" + } + } + }, + "visual" : { + "at" : { "x" : 1000, "y" : 208 }, + "labelOffset" : { "x" : -8, "y" : -8 } + }, + "connect" : [ + { "id" : "f22", "to" : "f23", "condition" : "ivp==\"TaskA.ivp\"" } + ] + }, { + "id" : "f23", + "type" : "Alternative", + "visual" : { + "at" : { "x" : 1000, "y" : 136 } + }, + "connect" : [ + { "id" : "f24", "to" : "f4", "var" : "in2" } + ] } ], "layout" : { "colors" : { From 3553609c424619f703fce9cb33ef1913c72e7f5f Mon Sep 17 00:00:00 2001 From: Trung Mai Date: Thu, 7 Mar 2024 14:21:25 +0700 Subject: [PATCH 046/143] TE-538: Update unit test for ProcessGraph --- .../estimator/test/ProcessGraphHelper.java | 52 +++++++++++++++++-- .../estimator/test/ProcessGraphTest.java | 10 ++++ .../axonivy/utils/estimator/ProcessGraph.java | 2 +- 3 files changed, 60 insertions(+), 4 deletions(-) diff --git a/workflow-estimator-test/src/com/axonivy/utils/estimator/test/ProcessGraphHelper.java b/workflow-estimator-test/src/com/axonivy/utils/estimator/test/ProcessGraphHelper.java index 34410b58..941f9558 100644 --- a/workflow-estimator-test/src/com/axonivy/utils/estimator/test/ProcessGraphHelper.java +++ b/workflow-estimator-test/src/com/axonivy/utils/estimator/test/ProcessGraphHelper.java @@ -1,7 +1,17 @@ package com.axonivy.utils.estimator.test; +import static java.util.Collections.emptyList; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Collector; +import java.util.stream.Collectors; + +import ch.ivyteam.ivy.process.model.element.EmbeddedProcessElement; import ch.ivyteam.ivy.process.model.BaseElement; +import ch.ivyteam.ivy.process.model.EmbeddedProcess; import ch.ivyteam.ivy.process.model.Process; +import ch.ivyteam.ivy.process.model.element.ProcessElement; import ch.ivyteam.ivy.process.model.element.SingleTaskCreator; import ch.ivyteam.ivy.process.model.element.event.start.RequestStart; @@ -12,18 +22,54 @@ public static RequestStart findStart(Process process) { return process.search().type(RequestStart.class).findOne(); } - public static BaseElement findByElementName(Process process, String name) { - return process.getElements().stream() + public static BaseElement findByElementName(Process process, String name) { + return getElementOfProcess(process).stream() .filter(el -> el.getName().equals(name)) .findFirst() .orElse(null); } public static BaseElement findByTaskName(Process process, String name) { - return process.getElements().stream() + return getElementOfProcess(process).stream() .filter(el -> {return el instanceof SingleTaskCreator;}) .filter(el ->((SingleTaskCreator) el).getTaskConfig().getName().getRawMacro().equals(name)) .findFirst() .orElse(null); } + + private static List getElementOfProcess (Process process) { + var processElements = process.getProcessElements(); + var childElments = getElementOfProcesses(processElements); + var elements = process.getElements(); + elements.addAll(childElments); + + return elements; + } + + private static List getElementOfProcesses (List processElements) { + if(processElements.isEmpty()) { + return emptyList(); + } + var embeddedProcess = processElements.stream() + .filter(it -> it instanceof EmbeddedProcessElement == true) + .map(EmbeddedProcessElement.class::cast) + .map(it -> it.getEmbeddedProcess()) + .collect(Collectors.toList()); + + var elememets = embeddedProcess.stream() + .map(EmbeddedProcess::getElements) + .flatMap(List::stream) + .collect(Collectors.toList()); + + var childProcessElements = embeddedProcess.stream() + .map(EmbeddedProcess::getProcessElements) + .flatMap(List::stream) + .collect(Collectors.toList()); + + var childElememts = getElementOfProcesses(childProcessElements); + + elememets.addAll(childElememts); + + return elememets; + } } diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/ProcessGraphTest.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/ProcessGraphTest.java index 248c2fbc..b37b0402 100644 --- a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/ProcessGraphTest.java +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/ProcessGraphTest.java @@ -83,5 +83,15 @@ void shouldIsSystemTask() throws Exception { assertTrue(result); } + + @Test + void shouldGetParentElementNames() throws Exception { + Process process = getProcessByName(FLOW_SUB_PROCESS); + TaskAndCaseModifier joinTask = (TaskAndCaseModifier) ProcessGraphHelper.findByElementName(process, "Task A"); + var processGraph = new ProcessGraph(process); + var result = processGraph.getParentElementNames(joinTask); + + assertEquals("[sub with two levels, 2nd level sub]", result.toString()); + } } \ No newline at end of file diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java b/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java index 3974164f..fd8e0e19 100644 --- a/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java +++ b/workflow-estimator/src/com/axonivy/utils/estimator/ProcessGraph.java @@ -107,7 +107,7 @@ public List getParentElementNames(TaskAndCaseModifier task){ return parentElementNames ; } - public List getParentElementNamesEmbeddedProcessElement(BaseElement parentElement){ + private List getParentElementNamesEmbeddedProcessElement(BaseElement parentElement){ List result = new ArrayList<>(); if(parentElement instanceof EmbeddedProcessElement) { result.add(parentElement.getName()); From c7f2c67035d75462dc3891bc23fde842fa03d2a4 Mon Sep 17 00:00:00 2001 From: Trung Mai Date: Thu, 7 Mar 2024 14:26:29 +0700 Subject: [PATCH 047/143] TE-538: Update unit test for complex process --- .../axonivy/utils/estimator/test/FlowExampleComplexTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleComplexTest.java b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleComplexTest.java index 5225a659..9be29415 100644 --- a/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleComplexTest.java +++ b/workflow-estimator-test/src_test/com/axonivy/utils/estimator/test/FlowExampleComplexTest.java @@ -98,7 +98,7 @@ void shouldFindTasksOnPathWithoutFlowNameAtTaskDAndTaskE() throws Exception { var workflowEstimator = new WorkflowEstimator(process, null, null); List estimatedTasks = workflowEstimator.findTasksOnPath(List.of(taskD, taskE)); - var expected = Arrays.array("Task D", "Task K", "Task E", "Task2A", "Task2B", "Task G", "Task H"); + var expected = Arrays.array("Task D", "Task2A", "Task2B", "Task G", "Task K", "Task H", "Task E"); var taskNames = getTaskNames(estimatedTasks); assertArrayEquals(expected, taskNames); } From eab1175da25656365544f1d1040e699b80860112 Mon Sep 17 00:00:00 2001 From: "AAVN\\ntnchuong" Date: Thu, 7 Mar 2024 15:58:00 +0700 Subject: [PATCH 048/143] TE-538: fix unit test --- .../processes/FlowExampleComplex.p.json | 10 ++++++++++ .../processes/FlowExampleError.p.json | 5 +++++ .../processes/FlowExampleLoop.p.json | 10 ++++++++++ .../processes/ParallelTasksExample.p.json | 5 +++++ .../processes/TaskTypesExample.p.json | 10 ++++++++++ .../com/axonivy/utils/estimator/WorkflowEstimator.java | 3 ++- 6 files changed, 42 insertions(+), 1 deletion(-) diff --git a/workflow-estimator-test/processes/FlowExampleComplex.p.json b/workflow-estimator-test/processes/FlowExampleComplex.p.json index ed8cb5fa..769122a5 100644 --- a/workflow-estimator-test/processes/FlowExampleComplex.p.json +++ b/workflow-estimator-test/processes/FlowExampleComplex.p.json @@ -190,6 +190,7 @@ "name" : "Task D", "config" : { "task" : { + "name" : "Task D", "code" : [ "import java.util.concurrent.TimeUnit;", "", @@ -209,6 +210,7 @@ "name" : "Task E", "config" : { "task" : { + "name" : "Task E", "code" : [ "import java.util.concurrent.TimeUnit;", "", @@ -271,6 +273,7 @@ "name" : "Task F", "config" : { "task" : { + "name" : "Task F", "code" : [ "import java.util.concurrent.TimeUnit;", "", @@ -290,6 +293,7 @@ "name" : "Task H", "config" : { "task" : { + "name" : "Task H", "code" : [ "import java.util.concurrent.TimeUnit;", "", @@ -315,6 +319,7 @@ "name" : "Task G", "config" : { "task" : { + "name" : "Task G", "code" : [ "import java.util.concurrent.TimeUnit;", "", @@ -348,6 +353,11 @@ "id" : "f30", "type" : "UserTask", "name" : "Task K", + "config" : { + "task" : { + "name" : "Task K" + } + }, "visual" : { "at" : { "x" : 1608, "y" : 64 } }, diff --git a/workflow-estimator-test/processes/FlowExampleError.p.json b/workflow-estimator-test/processes/FlowExampleError.p.json index cb1ea66e..11fc42ea 100644 --- a/workflow-estimator-test/processes/FlowExampleError.p.json +++ b/workflow-estimator-test/processes/FlowExampleError.p.json @@ -27,6 +27,11 @@ "id" : "f3", "type" : "UserTask", "name" : "Task A", + "config" : { + "task" : { + "name" : "Task A" + } + }, "visual" : { "at" : { "x" : 256, "y" : 64 } }, diff --git a/workflow-estimator-test/processes/FlowExampleLoop.p.json b/workflow-estimator-test/processes/FlowExampleLoop.p.json index c49aac63..aef61eff 100644 --- a/workflow-estimator-test/processes/FlowExampleLoop.p.json +++ b/workflow-estimator-test/processes/FlowExampleLoop.p.json @@ -27,6 +27,11 @@ "id" : "f3", "type" : "UserTask", "name" : "Task A", + "config" : { + "task" : { + "name" : "Task A" + } + }, "visual" : { "at" : { "x" : 472, "y" : 64 } }, @@ -67,6 +72,11 @@ "id" : "f9", "type" : "UserTask", "name" : "Task B", + "config" : { + "task" : { + "name" : "Task B" + } + }, "visual" : { "at" : { "x" : 472, "y" : 184 } }, diff --git a/workflow-estimator-test/processes/ParallelTasksExample.p.json b/workflow-estimator-test/processes/ParallelTasksExample.p.json index d46f5418..a3e26beb 100644 --- a/workflow-estimator-test/processes/ParallelTasksExample.p.json +++ b/workflow-estimator-test/processes/ParallelTasksExample.p.json @@ -73,6 +73,11 @@ "id" : "f7", "type" : "TaskSwitchEvent", "name" : "Task2", + "config" : { + "task" : { + "name" : "Task2" + } + }, "visual" : { "at" : { "x" : 184, "y" : 136 }, "labelOffset" : { "x" : 14, "y" : 34 } diff --git a/workflow-estimator-test/processes/TaskTypesExample.p.json b/workflow-estimator-test/processes/TaskTypesExample.p.json index 3e3e4cc9..2dc6f312 100644 --- a/workflow-estimator-test/processes/TaskTypesExample.p.json +++ b/workflow-estimator-test/processes/TaskTypesExample.p.json @@ -37,6 +37,11 @@ "id" : "f5", "type" : "UserTask", "name" : "UserTask", + "config" : { + "task" : { + "name" : "UserTask" + } + }, "visual" : { "at" : { "x" : 384, "y" : 64 } }, @@ -47,6 +52,11 @@ "id" : "f7", "type" : "TaskSwitchEvent", "name" : "Task", + "config" : { + "task" : { + "name" : "Task" + } + }, "visual" : { "at" : { "x" : 512, "y" : 64 }, "labelOffset" : { "x" : 14, "y" : 34 } diff --git a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java index 163cf7bf..53cf9ec8 100644 --- a/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java +++ b/workflow-estimator/src/com/axonivy/utils/estimator/WorkflowEstimator.java @@ -190,7 +190,8 @@ private List createEstimatedTask(TaskAndCaseModifier task, Date s estimatedTask.setPid(graph.getTaskId(task, taskConfig)); estimatedTask.setParentElementNames(graph.getParentElementNames(task)); - estimatedTask.setTaskName(defaultIfEmpty(taskConfig.getName().getRawMacro(), task.getName())); + estimatedTask.setTaskName(taskConfig.getName().getRawMacro()); + estimatedTask.setElementName(task.getName()); Duration estimatedDuration = graph.getDuration(task, taskConfig); estimatedTask.setEstimatedDuration(estimatedDuration); estimatedTask.setEstimatedStartTimestamp(startTimestamp); From 7b4da515c71b85f698954a5b4db7afa12162349a Mon Sep 17 00:00:00 2001 From: Trung Mai Date: Thu, 7 Mar 2024 16:14:46 +0700 Subject: [PATCH 049/143] TE-538: Create demo project --- workflow-estimator-demo/.classpath | 1 + workflow-estimator-demo/.project | 11 ++ workflow-estimator-demo/META-INF/MANIFEST.MF | 12 ++ workflow-estimator-demo/build.properties | 5 + .../utils/estimator/demo/DemoData.ivyClass | 4 + workflow-estimator-demo/pom.xml | 14 ++ .../Bussiness Processes/FlowDemoBasic.p.json | 134 ++++++++++++++++++ .../processes/Start Processes/Demo.p.json | 55 +++++++ .../demo/WorkflowEstimatorDemoBean.java | 44 ++++++ .../estimator/demo/constant/FindType.java | 14 ++ .../utils/estimator/demo/model/Estimator.java | 48 +++++++ .../WorkflowEstimatorTable.rddescriptor | 7 + .../WorkflowEstimatorTable.xhtml | 35 +++++ .../WorkflowEstimatorTableData.ivyClass | 2 + .../WorkflowEstimatorTableProcess.p.json | 48 +++++++ .../webContent/layouts/basic-10.xhtml | 67 +++++++++ .../layouts/includes/exception-details.xhtml | 109 ++++++++++++++ .../layouts/includes/exception.xhtml | 47 ++++++ .../webContent/layouts/includes/footer.xhtml | 18 +++ .../layouts/includes/progress-loader.xhtml | 15 ++ 20 files changed, 690 insertions(+) create mode 100644 workflow-estimator-demo/META-INF/MANIFEST.MF create mode 100644 workflow-estimator-demo/build.properties create mode 100644 workflow-estimator-demo/dataclasses/com/axonivy/utils/estimator/demo/DemoData.ivyClass create mode 100644 workflow-estimator-demo/processes/Bussiness Processes/FlowDemoBasic.p.json create mode 100644 workflow-estimator-demo/processes/Start Processes/Demo.p.json create mode 100644 workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/WorkflowEstimatorDemoBean.java create mode 100644 workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/constant/FindType.java create mode 100644 workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/model/Estimator.java create mode 100644 workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTable.rddescriptor create mode 100644 workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTable.xhtml create mode 100644 workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTableData.ivyClass create mode 100644 workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTableProcess.p.json create mode 100644 workflow-estimator/webContent/layouts/basic-10.xhtml create mode 100644 workflow-estimator/webContent/layouts/includes/exception-details.xhtml create mode 100644 workflow-estimator/webContent/layouts/includes/exception.xhtml create mode 100644 workflow-estimator/webContent/layouts/includes/footer.xhtml create mode 100644 workflow-estimator/webContent/layouts/includes/progress-loader.xhtml diff --git a/workflow-estimator-demo/.classpath b/workflow-estimator-demo/.classpath index a24d7ccd..122ece25 100644 --- a/workflow-estimator-demo/.classpath +++ b/workflow-estimator-demo/.classpath @@ -24,5 +24,6 @@ + diff --git a/workflow-estimator-demo/.project b/workflow-estimator-demo/.project index da4b423e..d4c52a67 100644 --- a/workflow-estimator-demo/.project +++ b/workflow-estimator-demo/.project @@ -35,6 +35,16 @@ + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + ch.ivyteam.ivy.project.IvyProjectNature @@ -45,5 +55,6 @@ org.eclipse.jem.beaninfo.BeanInfoNature org.eclipse.wst.common.project.facet.core.nature org.eclipse.wst.jsdt.core.jsNature + org.eclipse.pde.PluginNature diff --git a/workflow-estimator-demo/META-INF/MANIFEST.MF b/workflow-estimator-demo/META-INF/MANIFEST.MF new file mode 100644 index 00000000..0157d005 --- /dev/null +++ b/workflow-estimator-demo/META-INF/MANIFEST.MF @@ -0,0 +1,12 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: workflow-estimator-demo +Bundle-SymbolicName: workflow-estimator-demo +Bundle-Version: 1.0.0.qualifier +Bundle-RequiredExecutionEnvironment: JavaSE-17 +Automatic-Module-Name: workflow.estimator.demo +Export-Package: com.axonivy.utils.estimator.WorkflowEstimatorTable, + com.axonivy.utils.estimator.demo, + com.axonivy.utils.estimator.demo.constant, + com.axonivy.utils.estimator.demo.model +Require-Bundle: ch.ivyteam.ivy.process.rdm;bundle-version="11.2.1" diff --git a/workflow-estimator-demo/build.properties b/workflow-estimator-demo/build.properties new file mode 100644 index 00000000..54c38485 --- /dev/null +++ b/workflow-estimator-demo/build.properties @@ -0,0 +1,5 @@ +source.. = src/,\ + src_wsproc/,\ + src_dataClasses/ +bin.includes = META-INF/,\ + . diff --git a/workflow-estimator-demo/dataclasses/com/axonivy/utils/estimator/demo/DemoData.ivyClass b/workflow-estimator-demo/dataclasses/com/axonivy/utils/estimator/demo/DemoData.ivyClass new file mode 100644 index 00000000..b968c90c --- /dev/null +++ b/workflow-estimator-demo/dataclasses/com/axonivy/utils/estimator/demo/DemoData.ivyClass @@ -0,0 +1,4 @@ +DemoData #class +com.axonivy.utils.estimator.demo #namespace +bean com.axonivy.utils.estimator.demo.WorkflowEstimatorDemoBean #field +bean PERSISTENT #fieldModifier diff --git a/workflow-estimator-demo/pom.xml b/workflow-estimator-demo/pom.xml index bc7072ef..e7d23fd2 100644 --- a/workflow-estimator-demo/pom.xml +++ b/workflow-estimator-demo/pom.xml @@ -8,6 +8,20 @@ 11.2.0 + + + com.axonivy.utils.estimator + workflow-estimator + ${project.version} + iar + + + com.axonivy.portal + portal-components + 11.2.1 + iar + + diff --git a/workflow-estimator-demo/processes/Bussiness Processes/FlowDemoBasic.p.json b/workflow-estimator-demo/processes/Bussiness Processes/FlowDemoBasic.p.json new file mode 100644 index 00000000..e263b1cb --- /dev/null +++ b/workflow-estimator-demo/processes/Bussiness Processes/FlowDemoBasic.p.json @@ -0,0 +1,134 @@ +{ + "$schema" : "https://json-schema.axonivy.com/process/11.2.2/process.json", + "id" : "18E180A64355D4D9", + "config" : { + "data" : "com.axonivy.utils.estimator.demo.Data", + "permissions" : { + "view" : { + "allowed" : false + } + } + }, + "elements" : [ { + "id" : "f0", + "type" : "RequestStart", + "name" : "Start", + "config" : { + "signature" : "start", + "request" : { + "isVisibleOnStartList" : false + } + }, + "visual" : { + "at" : { "x" : 96, "y" : 64 } + }, + "connect" : [ + { "id" : "f3", "to" : "f2" } + ] + }, { + "id" : "f1", + "type" : "TaskEnd", + "visual" : { + "at" : { "x" : 1152, "y" : 64 } + } + }, { + "id" : "f2", + "type" : "UserTask", + "name" : "Task A", + "visual" : { + "at" : { "x" : 320, "y" : 64 } + }, + "connect" : [ + { "id" : "f5", "to" : "f4" } + ] + }, { + "id" : "f4", + "type" : "Alternative", + "visual" : { + "at" : { "x" : 464, "y" : 64 } + }, + "connect" : [ + { "id" : "f13", "to" : "f12" }, + { "id" : "f15", "to" : "f7" } + ] + }, { + "id" : "f6", + "type" : "Alternative", + "visual" : { + "at" : { "x" : 768, "y" : 64 } + }, + "connect" : [ + { "id" : "f9", "to" : "f8" } + ] + }, { + "id" : "f8", + "type" : "Alternative", + "visual" : { + "at" : { "x" : 960, "y" : 64 } + }, + "connect" : [ + { "id" : "f10", "to" : "f1" } + ] + }, { + "id" : "f11", + "type" : "UserTask", + "name" : "Task C", + "visual" : { + "at" : { "x" : 616, "y" : 192 } + }, + "connect" : [ + { "id" : "f19", "to" : "f6", "via" : [ { "x" : 768, "y" : 192 } ] } + ] + }, { + "id" : "f12", + "type" : "Alternative", + "visual" : { + "at" : { "x" : 464, "y" : 192 } + }, + "connect" : [ + { "id" : "f14", "to" : "f11" }, + { "id" : "f21", "to" : "f20", "via" : [ { "x" : 464, "y" : 328 } ] } + ] + }, { + "id" : "f7", + "type" : "UserTask", + "name" : "Task B", + "config" : { + "task" : { + "name" : "Task B" + } + }, + "visual" : { + "at" : { "x" : 616, "y" : 64 } + }, + "connect" : [ + { "id" : "f16", "to" : "f6" } + ] + }, { + "id" : "f17", + "type" : "RequestStart", + "name" : "NewStart", + "config" : { + "signature" : "start2", + "request" : { + "isVisibleOnStartList" : false + } + }, + "visual" : { + "at" : { "x" : 104, "y" : 192 } + }, + "connect" : [ + { "id" : "f18", "to" : "f12" } + ] + }, { + "id" : "f20", + "type" : "UserTask", + "name" : "Task D", + "visual" : { + "at" : { "x" : 616, "y" : 328 } + }, + "connect" : [ + { "id" : "f22", "to" : "f8", "via" : [ { "x" : 960, "y" : 328 } ] } + ] + } ] +} \ No newline at end of file diff --git a/workflow-estimator-demo/processes/Start Processes/Demo.p.json b/workflow-estimator-demo/processes/Start Processes/Demo.p.json new file mode 100644 index 00000000..d2a91be2 --- /dev/null +++ b/workflow-estimator-demo/processes/Start Processes/Demo.p.json @@ -0,0 +1,55 @@ +{ + "$schema" : "https://json-schema.axonivy.com/process/11.2.2/process.json", + "id" : "18E180F943F963D5", + "config" : { + "data" : "com.axonivy.utils.estimator.demo.DemoData" + }, + "elements" : [ { + "id" : "f0", + "type" : "RequestStart", + "name" : "start", + "config" : { + "signature" : "start" + }, + "visual" : { + "at" : { "x" : 96, "y" : 64 } + }, + "connect" : [ + { "id" : "f2", "to" : "f5" } + ] + }, { + "id" : "f1", + "type" : "TaskEnd", + "visual" : { + "at" : { "x" : 552, "y" : 64 } + } + }, { + "id" : "f3", + "type" : "DialogCall", + "name" : "Table Show", + "config" : { + "dialog" : "com.axonivy.utils.estimator.WorkflowEstimatorTable:start()" + }, + "visual" : { + "at" : { "x" : 408, "y" : 64 } + }, + "connect" : [ + { "id" : "f4", "to" : "f1" } + ] + }, { + "id" : "f5", + "type" : "Script", + "name" : "init data", + "config" : { + "output" : { + "code" : "in.bean.getAllProcesses();" + } + }, + "visual" : { + "at" : { "x" : 224, "y" : 64 } + }, + "connect" : [ + { "id" : "f6", "to" : "f3" } + ] + } ] +} \ No newline at end of file diff --git a/workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/WorkflowEstimatorDemoBean.java b/workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/WorkflowEstimatorDemoBean.java new file mode 100644 index 00000000..de13e118 --- /dev/null +++ b/workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/WorkflowEstimatorDemoBean.java @@ -0,0 +1,44 @@ +package com.axonivy.utils.estimator.demo; + +import static java.util.Collections.emptyList; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import com.axonivy.utils.estimator.demo.model.Estimator; + +import ch.ivyteam.ivy.application.IProcessModelVersion; +import ch.ivyteam.ivy.process.model.Process; +import ch.ivyteam.ivy.process.model.element.TaskModifier; +import ch.ivyteam.ivy.process.rdm.IProcessManager; + +@SuppressWarnings("restriction") +public class WorkflowEstimatorDemoBean { + + private List estimators = new ArrayList<>(); + + public WorkflowEstimatorDemoBean() { + + } + + public void onAddEstimator() { + estimators.add(new Estimator()); + } + + public List getAllTaskModifier(Process process) { + + return emptyList(); + } + + public List getAllProcesses() { + var manager = IProcessManager.instance().getProjectDataModelFor(IProcessModelVersion.current()); + List templates = manager.search().find().stream() + .map(start -> start.getRootProcess()) + // .map(process -> + // process.search().type(EmbeddedProcessElement.class).findOne().getEmbeddedProcess()) + .collect(Collectors.toList()); + + return templates; + } +} diff --git a/workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/constant/FindType.java b/workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/constant/FindType.java new file mode 100644 index 00000000..6a0ac58b --- /dev/null +++ b/workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/constant/FindType.java @@ -0,0 +1,14 @@ +package com.axonivy.utils.estimator.demo.constant; + +public enum FindType { + ALL_TASK("Find All Task"), TASK_ON_PATH("Find Task On Path"); + + private final String findType; + + private FindType(String findType) { + this.findType = findType; + } + public String getFindType() { + return this.findType; + } +} diff --git a/workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/model/Estimator.java b/workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/model/Estimator.java new file mode 100644 index 00000000..9ba118da --- /dev/null +++ b/workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/model/Estimator.java @@ -0,0 +1,48 @@ +package com.axonivy.utils.estimator.demo.model; + +import java.util.List; + +import com.axonivy.utils.estimator.demo.constant.FindType; +import com.axonivy.utils.estimator.model.EstimatedTask; + +import ch.ivyteam.ivy.process.model.Process; +import ch.ivyteam.ivy.process.model.element.TaskModifier; + +public class Estimator { + private Process process; + private TaskModifier from; + private FindType findType; + private List tasks; + + public Process getProcess() { + return process; + } + + public void setProcess(Process process) { + this.process = process; + } + + public TaskModifier getFrom() { + return from; + } + + public void setFrom(TaskModifier from) { + this.from = from; + } + + public FindType getFindType() { + return findType; + } + + public void setFindType(FindType findType) { + this.findType = findType; + } + + public List getTasks() { + return tasks; + } + + public void setTasks(List tasks) { + this.tasks = tasks; + } +} diff --git a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTable.rddescriptor b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTable.rddescriptor new file mode 100644 index 00000000..ae605f0d --- /dev/null +++ b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTable.rddescriptor @@ -0,0 +1,7 @@ + + + + viewTechnology + JSF + + diff --git a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTable.xhtml b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTable.xhtml new file mode 100644 index 00000000..21f9e559 --- /dev/null +++ b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTable.xhtml @@ -0,0 +1,35 @@ + + + + WorkflowEstimatorTable + + +

+ This is an + Html Dialog + implemented with JSF and Primefaces as widget library +

+ + + + + + + + +
+
+ + +
+
+ +
+
+
+ + diff --git a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTableData.ivyClass b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTableData.ivyClass new file mode 100644 index 00000000..2ca02535 --- /dev/null +++ b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTableData.ivyClass @@ -0,0 +1,2 @@ +WorkflowEstimatorTableData #class +com.axonivy.utils.estimator.WorkflowEstimatorTable #namespace diff --git a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTableProcess.p.json b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTableProcess.p.json new file mode 100644 index 00000000..8900b903 --- /dev/null +++ b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTableProcess.p.json @@ -0,0 +1,48 @@ +{ + "$schema" : "https://json-schema.axonivy.com/process/11.2.2/process.json", + "id" : "18E18129A25A9EEC", + "kind" : "HTML_DIALOG", + "config" : { + "data" : "com.axonivy.utils.estimator.WorkflowEstimatorTable.WorkflowEstimatorTableData" + }, + "elements" : [ { + "id" : "f0", + "type" : "HtmlDialogStart", + "name" : "start()", + "config" : { + "signature" : "start", + "guid" : "18E18129A262E35E" + }, + "visual" : { + "at" : { "x" : 96, "y" : 64 } + }, + "connect" : [ + { "id" : "f2", "to" : "f1" } + ] + }, { + "id" : "f1", + "type" : "HtmlDialogEnd", + "visual" : { + "at" : { "x" : 224, "y" : 64 } + } + }, { + "id" : "f3", + "type" : "HtmlDialogEventStart", + "name" : "close", + "config" : { + "guid" : "18E18129A2768836" + }, + "visual" : { + "at" : { "x" : 96, "y" : 160 } + }, + "connect" : [ + { "id" : "f5", "to" : "f4" } + ] + }, { + "id" : "f4", + "type" : "HtmlDialogExit", + "visual" : { + "at" : { "x" : 224, "y" : 160 } + } + } ] +} \ No newline at end of file diff --git a/workflow-estimator/webContent/layouts/basic-10.xhtml b/workflow-estimator/webContent/layouts/basic-10.xhtml new file mode 100644 index 00000000..71a0c491 --- /dev/null +++ b/workflow-estimator/webContent/layouts/basic-10.xhtml @@ -0,0 +1,67 @@ + + + + + + + + + + <ui:insert name="title">Ivy Html Dialog</ui:insert> + + + + + + + + + + + + + +
+ + default content + + + +
+
+ +
+
+
+ + + + +
+ \ No newline at end of file diff --git a/workflow-estimator/webContent/layouts/includes/exception-details.xhtml b/workflow-estimator/webContent/layouts/includes/exception-details.xhtml new file mode 100644 index 00000000..bbc3cce7 --- /dev/null +++ b/workflow-estimator/webContent/layouts/includes/exception-details.xhtml @@ -0,0 +1,109 @@ + + + + + + +

+ +

+ + +

Error id

+

#{errorPage.exceptionId}

+

Error Timestamp

+

#{errorPage.createdAt}

+
+ + + + +

Attributes

+
+ + + + + + + + + + + + + + + +
NameValue
+
+
+

Thrown by

+

Process: + +
Element: + +

+
+ + +

Process call stack

+ +
#{caller.callerElement}
+
+
+ +

Technical cause

+
#{causedBy.class.simpleName}: #{causedBy.message.trim()}
+
+
+ +

Request Uri

+

#{errorPage.getRequestUri()}

+
+

Servlet

+

#{errorPage.getServletName()}

+
+ +

Application

+

#{errorPage.applicationName}

+
+ + +

Thread local values

+
+ + + + + + + + + + + + + + + +
KeyValue
+
+
+
+ +

Stack-Trace

+
#{errorPage.getStackTrace()}
+
+ diff --git a/workflow-estimator/webContent/layouts/includes/exception.xhtml b/workflow-estimator/webContent/layouts/includes/exception.xhtml new file mode 100644 index 00000000..1b255a27 --- /dev/null +++ b/workflow-estimator/webContent/layouts/includes/exception.xhtml @@ -0,0 +1,47 @@ + + + + + + + + + +
+
+ + +
+ + + + + + + + + +
+ + \ No newline at end of file diff --git a/workflow-estimator/webContent/layouts/includes/footer.xhtml b/workflow-estimator/webContent/layouts/includes/footer.xhtml new file mode 100644 index 00000000..f21699e8 --- /dev/null +++ b/workflow-estimator/webContent/layouts/includes/footer.xhtml @@ -0,0 +1,18 @@ + + + +
+ + #{ivyAdvisor.applicationName} + + +
+
+ + \ No newline at end of file diff --git a/workflow-estimator/webContent/layouts/includes/progress-loader.xhtml b/workflow-estimator/webContent/layouts/includes/progress-loader.xhtml new file mode 100644 index 00000000..0d68a75d --- /dev/null +++ b/workflow-estimator/webContent/layouts/includes/progress-loader.xhtml @@ -0,0 +1,15 @@ + + + + +
+
+
Loading...
+
+
+ + + +
+
\ No newline at end of file From c646c60816b57e55a39b5ae1b6903d2a7031692a Mon Sep 17 00:00:00 2001 From: Trung Mai Date: Fri, 8 Mar 2024 09:38:03 +0700 Subject: [PATCH 050/143] TE-538: find all process --- workflow-estimator-demo/.project | 6 +-- workflow-estimator-demo/pom.xml | 6 --- .../processes/Start Processes/Demo.p.json | 9 ++-- .../demo/WorkflowEstimatorDemoBean.java | 53 ++++++++++++++++--- 4 files changed, 54 insertions(+), 20 deletions(-) diff --git a/workflow-estimator-demo/.project b/workflow-estimator-demo/.project index d4c52a67..e0613b50 100644 --- a/workflow-estimator-demo/.project +++ b/workflow-estimator-demo/.project @@ -31,17 +31,17 @@ - org.eclipse.m2e.core.maven2Builder + org.eclipse.pde.ManifestBuilder - org.eclipse.pde.ManifestBuilder + org.eclipse.pde.SchemaBuilder - org.eclipse.pde.SchemaBuilder + org.eclipse.m2e.core.maven2Builder diff --git a/workflow-estimator-demo/pom.xml b/workflow-estimator-demo/pom.xml index e7d23fd2..34b2e0a7 100644 --- a/workflow-estimator-demo/pom.xml +++ b/workflow-estimator-demo/pom.xml @@ -15,12 +15,6 @@ ${project.version} iar - - com.axonivy.portal - portal-components - 11.2.1 - iar - diff --git a/workflow-estimator-demo/processes/Start Processes/Demo.p.json b/workflow-estimator-demo/processes/Start Processes/Demo.p.json index d2a91be2..73ba70bb 100644 --- a/workflow-estimator-demo/processes/Start Processes/Demo.p.json +++ b/workflow-estimator-demo/processes/Start Processes/Demo.p.json @@ -9,10 +9,13 @@ "type" : "RequestStart", "name" : "start", "config" : { - "signature" : "start" + "signature" : "start", + "request" : { + "name" : "WorkflowEstimatorDemo" + } }, "visual" : { - "at" : { "x" : 96, "y" : 64 } + "at" : { "x" : 80, "y" : 64 } }, "connect" : [ { "id" : "f2", "to" : "f5" } @@ -42,7 +45,7 @@ "name" : "init data", "config" : { "output" : { - "code" : "in.bean.getAllProcesses();" + "code" : "in.bean.initData();" } }, "visual" : { diff --git a/workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/WorkflowEstimatorDemoBean.java b/workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/WorkflowEstimatorDemoBean.java index de13e118..5697fab4 100644 --- a/workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/WorkflowEstimatorDemoBean.java +++ b/workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/WorkflowEstimatorDemoBean.java @@ -3,42 +3,79 @@ import static java.util.Collections.emptyList; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; import com.axonivy.utils.estimator.demo.model.Estimator; import ch.ivyteam.ivy.application.IProcessModelVersion; +import ch.ivyteam.ivy.environment.Ivy; +import ch.ivyteam.ivy.process.model.BaseElement; import ch.ivyteam.ivy.process.model.Process; import ch.ivyteam.ivy.process.model.element.TaskModifier; import ch.ivyteam.ivy.process.rdm.IProcessManager; @SuppressWarnings("restriction") public class WorkflowEstimatorDemoBean { + private static final List PROCESS_FOLDERS = Arrays.asList("Bussiness Processes"); private List estimators = new ArrayList<>(); + private List processes = emptyList(); public WorkflowEstimatorDemoBean() { + + } + + public List getEstimators() { + return estimators; + } + + + public void setEstimators(List estimators) { + this.estimators = estimators; + } + + + public List getProcesses() { + return processes; + } + + public void setProcesses(List processes) { + this.processes = processes; + } + + + public void initData() { + processes = getAllProcesses(); } public void onAddEstimator() { estimators.add(new Estimator()); } + + public void onSelectedProcess() { + + } public List getAllTaskModifier(Process process) { - + return emptyList(); } - public List getAllProcesses() { + private List getAllProcesses() { var manager = IProcessManager.instance().getProjectDataModelFor(IProcessModelVersion.current()); - List templates = manager.search().find().stream() + List processes = manager.search().find().stream() .map(start -> start.getRootProcess()) - // .map(process -> - // process.search().type(EmbeddedProcessElement.class).findOne().getEmbeddedProcess()) - .collect(Collectors.toList()); - - return templates; + .filter(process -> isAcceptedProcess(PROCESS_FOLDERS, process.getFullQualifiedName().getName())) + .distinct() + .collect(Collectors.toList()); + + return processes; + } + + private boolean isAcceptedProcess(Listfolders, String fullQualifiedName) { + return folders.stream().anyMatch(folder -> fullQualifiedName.contains(folder)); } } From f7763b26ad4cc3da94e48f4d3983a38e56f3d532 Mon Sep 17 00:00:00 2001 From: Trung Mai Date: Fri, 8 Mar 2024 10:04:12 +0700 Subject: [PATCH 051/143] TE-538: Add estimator component --- .../WorkflowEstimatorTable.xhtml | 18 ++- .../WorkflowEstimatorTableData.ivyClass | 2 + .../WorkflowEstimatorTableProcess.p.json | 34 ++++++ .../EstimatorDetail.rddescriptor | 7 ++ .../EstimatorDetail/EstimatorDetail.xhtml | 18 +++ .../EstimatorDetailData.ivyClass | 2 + .../EstimatorDetailProcess.p.json | 48 ++++++++ .../layouts/frame-10-full-width.xhtml | 60 ++++++++++ .../layouts/includes/exception-details.xhtml | 109 ++++++++++++++++++ .../layouts/includes/exception.xhtml | 47 ++++++++ .../webContent/layouts/includes/footer.xhtml | 18 +++ .../layouts/includes/progress-loader.xhtml | 15 +++ 12 files changed, 368 insertions(+), 10 deletions(-) create mode 100644 workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetail.rddescriptor create mode 100644 workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetail.xhtml create mode 100644 workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetailData.ivyClass create mode 100644 workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetailProcess.p.json create mode 100644 workflow-estimator-demo/webContent/layouts/frame-10-full-width.xhtml create mode 100644 workflow-estimator-demo/webContent/layouts/includes/exception-details.xhtml create mode 100644 workflow-estimator-demo/webContent/layouts/includes/exception.xhtml create mode 100644 workflow-estimator-demo/webContent/layouts/includes/footer.xhtml create mode 100644 workflow-estimator-demo/webContent/layouts/includes/progress-loader.xhtml diff --git a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTable.xhtml b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTable.xhtml index 21f9e559..aa98741c 100644 --- a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTable.xhtml +++ b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTable.xhtml @@ -3,18 +3,19 @@ xmlns:ic="http://ivyteam.ch/jsf/component" xmlns:p="http://primefaces.org/ui" xmlns:pe="http://primefaces.org/ui/extensions"> - + WorkflowEstimatorTable -

- This is an - Html Dialog - implemented with JSF and Primefaces as widget library -

+

Workflow Estimator Demo

+
+ +
+ + @@ -22,10 +23,7 @@
-
- - -
+
diff --git a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTableData.ivyClass b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTableData.ivyClass index 2ca02535..b97850c0 100644 --- a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTableData.ivyClass +++ b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTableData.ivyClass @@ -1,2 +1,4 @@ WorkflowEstimatorTableData #class com.axonivy.utils.estimator.WorkflowEstimatorTable #namespace +workflowEstimatorBean com.axonivy.utils.estimator.demo.WorkflowEstimatorDemoBean #field +workflowEstimatorBean PERSISTENT #fieldModifier diff --git a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTableProcess.p.json b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTableProcess.p.json index 8900b903..3b0acff1 100644 --- a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTableProcess.p.json +++ b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTableProcess.p.json @@ -44,5 +44,39 @@ "visual" : { "at" : { "x" : 224, "y" : 160 } } + }, { + "id" : "f6", + "type" : "HtmlDialogEventStart", + "name" : "addEstimator", + "config" : { + "guid" : "18E1C023A2A35CBB" + }, + "visual" : { + "at" : { "x" : 104, "y" : 264 } + }, + "connect" : [ + { "id" : "f8", "to" : "f7" } + ] + }, { + "id" : "f7", + "type" : "Script", + "name" : "add estimator", + "config" : { + "output" : { + "code" : "in.workflowEstimatorBean.onAddEstimator();" + } + }, + "visual" : { + "at" : { "x" : 296, "y" : 264 } + }, + "connect" : [ + { "id" : "f10", "to" : "f9" } + ] + }, { + "id" : "f9", + "type" : "HtmlDialogEnd", + "visual" : { + "at" : { "x" : 488, "y" : 264 } + } } ] } \ No newline at end of file diff --git a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetail.rddescriptor b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetail.rddescriptor new file mode 100644 index 00000000..ae605f0d --- /dev/null +++ b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetail.rddescriptor @@ -0,0 +1,7 @@ + + + + viewTechnology + JSF + + diff --git a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetail.xhtml b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetail.xhtml new file mode 100644 index 00000000..6d6c5aba --- /dev/null +++ b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetail.xhtml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + diff --git a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetailData.ivyClass b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetailData.ivyClass new file mode 100644 index 00000000..669a3e9a --- /dev/null +++ b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetailData.ivyClass @@ -0,0 +1,2 @@ +EstimatorDetailData #class +com.axonivy.utils.estimator.component.EstimatorDetail #namespace diff --git a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetailProcess.p.json b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetailProcess.p.json new file mode 100644 index 00000000..e88f60cb --- /dev/null +++ b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetailProcess.p.json @@ -0,0 +1,48 @@ +{ + "$schema" : "https://json-schema.axonivy.com/process/11.2.2/process.json", + "id" : "18E1C085F4D4C0F7", + "kind" : "HTML_DIALOG", + "config" : { + "data" : "com.axonivy.utils.estimator.component.EstimatorDetail.EstimatorDetailData" + }, + "elements" : [ { + "id" : "f0", + "type" : "HtmlDialogStart", + "name" : "start()", + "config" : { + "signature" : "start", + "guid" : "18E1C085F4DDCE95" + }, + "visual" : { + "at" : { "x" : 96, "y" : 64 } + }, + "connect" : [ + { "id" : "f2", "to" : "f1" } + ] + }, { + "id" : "f1", + "type" : "HtmlDialogEnd", + "visual" : { + "at" : { "x" : 224, "y" : 64 } + } + }, { + "id" : "f3", + "type" : "HtmlDialogEventStart", + "name" : "close", + "config" : { + "guid" : "18E1C085F4D6BE3E" + }, + "visual" : { + "at" : { "x" : 96, "y" : 160 } + }, + "connect" : [ + { "id" : "f5", "to" : "f4" } + ] + }, { + "id" : "f4", + "type" : "HtmlDialogExit", + "visual" : { + "at" : { "x" : 224, "y" : 160 } + } + } ] +} \ No newline at end of file diff --git a/workflow-estimator-demo/webContent/layouts/frame-10-full-width.xhtml b/workflow-estimator-demo/webContent/layouts/frame-10-full-width.xhtml new file mode 100644 index 00000000..d1997ea6 --- /dev/null +++ b/workflow-estimator-demo/webContent/layouts/frame-10-full-width.xhtml @@ -0,0 +1,60 @@ + + + + + + + + + + <ui:insert name="title">Ivy Html Dialog</ui:insert> + + + + + + + + + +
+ + default content + +
+ + + + + + + +
+ \ No newline at end of file diff --git a/workflow-estimator-demo/webContent/layouts/includes/exception-details.xhtml b/workflow-estimator-demo/webContent/layouts/includes/exception-details.xhtml new file mode 100644 index 00000000..bbc3cce7 --- /dev/null +++ b/workflow-estimator-demo/webContent/layouts/includes/exception-details.xhtml @@ -0,0 +1,109 @@ + + + + + + +

+ +

+ + +

Error id

+

#{errorPage.exceptionId}

+

Error Timestamp

+

#{errorPage.createdAt}

+
+ + + + +

Attributes

+
+ + + + + + + + + + + + + + + +
NameValue
+
+
+

Thrown by

+

Process: + +
Element: + +

+
+ + +

Process call stack

+ +
#{caller.callerElement}
+
+
+ +

Technical cause

+
#{causedBy.class.simpleName}: #{causedBy.message.trim()}
+
+
+ +

Request Uri

+

#{errorPage.getRequestUri()}

+
+

Servlet

+

#{errorPage.getServletName()}

+
+ +

Application

+

#{errorPage.applicationName}

+
+ + +

Thread local values

+
+ + + + + + + + + + + + + + + +
KeyValue
+
+
+
+ +

Stack-Trace

+
#{errorPage.getStackTrace()}
+
+ diff --git a/workflow-estimator-demo/webContent/layouts/includes/exception.xhtml b/workflow-estimator-demo/webContent/layouts/includes/exception.xhtml new file mode 100644 index 00000000..1b255a27 --- /dev/null +++ b/workflow-estimator-demo/webContent/layouts/includes/exception.xhtml @@ -0,0 +1,47 @@ + + + + + + + + + +
+
+ + +
+ + + + + + + + + +
+ + \ No newline at end of file diff --git a/workflow-estimator-demo/webContent/layouts/includes/footer.xhtml b/workflow-estimator-demo/webContent/layouts/includes/footer.xhtml new file mode 100644 index 00000000..f21699e8 --- /dev/null +++ b/workflow-estimator-demo/webContent/layouts/includes/footer.xhtml @@ -0,0 +1,18 @@ + + + +
+ + #{ivyAdvisor.applicationName} + + +
+
+ + \ No newline at end of file diff --git a/workflow-estimator-demo/webContent/layouts/includes/progress-loader.xhtml b/workflow-estimator-demo/webContent/layouts/includes/progress-loader.xhtml new file mode 100644 index 00000000..0d68a75d --- /dev/null +++ b/workflow-estimator-demo/webContent/layouts/includes/progress-loader.xhtml @@ -0,0 +1,15 @@ + + + + +
+
+
Loading...
+
+
+ + + +
+
\ No newline at end of file From 83992111d2f1a18713b47f3f55bcdf6a4005cc9c Mon Sep 17 00:00:00 2001 From: Trung Mai Date: Fri, 8 Mar 2024 11:44:57 +0700 Subject: [PATCH 052/143] TE-538: Delete an estimator --- .../processes/Start Processes/Demo.p.json | 13 ++++- .../demo/WorkflowEstimatorDemoBean.java | 4 ++ .../WorkflowEstimatorTable.xhtml | 53 ++++++++++--------- .../WorkflowEstimatorTableData.ivyClass | 4 +- .../WorkflowEstimatorTableProcess.p.json | 14 +++-- .../EstimatorDetail/EstimatorDetail.xhtml | 23 +++++--- .../EstimatorDetailData.ivyClass | 4 ++ .../EstimatorDetailProcess.p.json | 53 ++++++++++++++++++- 8 files changed, 127 insertions(+), 41 deletions(-) diff --git a/workflow-estimator-demo/processes/Start Processes/Demo.p.json b/workflow-estimator-demo/processes/Start Processes/Demo.p.json index 73ba70bb..9b1beeae 100644 --- a/workflow-estimator-demo/processes/Start Processes/Demo.p.json +++ b/workflow-estimator-demo/processes/Start Processes/Demo.p.json @@ -31,7 +31,12 @@ "type" : "DialogCall", "name" : "Table Show", "config" : { - "dialog" : "com.axonivy.utils.estimator.WorkflowEstimatorTable:start()" + "dialog" : "com.axonivy.utils.estimator.WorkflowEstimatorTable:start(com.axonivy.utils.estimator.demo.WorkflowEstimatorDemoBean)", + "call" : { + "map" : { + "param.workflowEstimatorDemoBean" : "in.bean" + } + } }, "visual" : { "at" : { "x" : 408, "y" : 64 } @@ -45,7 +50,11 @@ "name" : "init data", "config" : { "output" : { - "code" : "in.bean.initData();" + "code" : [ + "import com.axonivy.utils.estimator.demo.WorkflowEstimatorDemoBean;", + "import com.axonivy.utils.estimator.demo.WorkflowEstimatorDemoBean;", + "in.bean = new WorkflowEstimatorDemoBean();" + ] } }, "visual" : { diff --git a/workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/WorkflowEstimatorDemoBean.java b/workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/WorkflowEstimatorDemoBean.java index 5697fab4..7c41933a 100644 --- a/workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/WorkflowEstimatorDemoBean.java +++ b/workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/WorkflowEstimatorDemoBean.java @@ -55,6 +55,10 @@ public void onAddEstimator() { estimators.add(new Estimator()); } + public void onDeleteEstimator(Estimator estimator) { + estimators.remove(estimator); + } + public void onSelectedProcess() { } diff --git a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTable.xhtml b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTable.xhtml index aa98741c..487cc584 100644 --- a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTable.xhtml +++ b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTable.xhtml @@ -1,33 +1,34 @@ - + - - WorkflowEstimatorTable - + + WorkflowEstimatorTable + -

Workflow Estimator Demo

+

Workflow Estimator Demo

- - -
- -
- - - + + +
+ +
- - -
-
- -
- -
-
+ + + + +
+
diff --git a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTableData.ivyClass b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTableData.ivyClass index b97850c0..889a3c7a 100644 --- a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTableData.ivyClass +++ b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTableData.ivyClass @@ -1,4 +1,4 @@ WorkflowEstimatorTableData #class com.axonivy.utils.estimator.WorkflowEstimatorTable #namespace -workflowEstimatorBean com.axonivy.utils.estimator.demo.WorkflowEstimatorDemoBean #field -workflowEstimatorBean PERSISTENT #fieldModifier +workflowEstimatorDemoBean com.axonivy.utils.estimator.demo.WorkflowEstimatorDemoBean #field +workflowEstimatorDemoBean PERSISTENT #fieldModifier diff --git a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTableProcess.p.json b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTableProcess.p.json index 3b0acff1..77424699 100644 --- a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTableProcess.p.json +++ b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTableProcess.p.json @@ -8,13 +8,21 @@ "elements" : [ { "id" : "f0", "type" : "HtmlDialogStart", - "name" : "start()", + "name" : "start(WorkflowEstimatorDemoBean)", "config" : { "signature" : "start", + "input" : { + "params" : [ + { "name" : "workflowEstimatorDemoBean", "type" : "com.axonivy.utils.estimator.demo.WorkflowEstimatorDemoBean", "desc" : "" } + ], + "map" : { + "out.workflowEstimatorDemoBean" : "param. workflowEstimatorDemoBean" + } + }, "guid" : "18E18129A262E35E" }, "visual" : { - "at" : { "x" : 96, "y" : 64 } + "at" : { "x" : 112, "y" : 64 } }, "connect" : [ { "id" : "f2", "to" : "f1" } @@ -63,7 +71,7 @@ "name" : "add estimator", "config" : { "output" : { - "code" : "in.workflowEstimatorBean.onAddEstimator();" + "code" : "in.workflowEstimatorDemoBean.onAddEstimator();" } }, "visual" : { diff --git a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetail.xhtml b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetail.xhtml index 6d6c5aba..39cd32af 100644 --- a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetail.xhtml +++ b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetail.xhtml @@ -1,17 +1,26 @@ - + - + +

Estimator Demo

- - + +
+ +
diff --git a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetailData.ivyClass b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetailData.ivyClass index 669a3e9a..f103197b 100644 --- a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetailData.ivyClass +++ b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetailData.ivyClass @@ -1,2 +1,6 @@ EstimatorDetailData #class com.axonivy.utils.estimator.component.EstimatorDetail #namespace +workflowEstimatorDemoBean com.axonivy.utils.estimator.demo.WorkflowEstimatorDemoBean #field +workflowEstimatorDemoBean PERSISTENT #fieldModifier +deletedEstimator com.axonivy.utils.estimator.demo.model.Estimator #field +deletedEstimator PERSISTENT #fieldModifier diff --git a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetailProcess.p.json b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetailProcess.p.json index e88f60cb..5c0ffa0e 100644 --- a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetailProcess.p.json +++ b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetailProcess.p.json @@ -8,9 +8,17 @@ "elements" : [ { "id" : "f0", "type" : "HtmlDialogStart", - "name" : "start()", + "name" : "start(WorkflowEstimatorDemoBean)", "config" : { "signature" : "start", + "input" : { + "params" : [ + { "name" : "workflowEstimatorDemoBean", "type" : "com.axonivy.utils.estimator.demo.WorkflowEstimatorDemoBean", "desc" : "" } + ], + "map" : { + "out.workflowEstimatorDemoBean" : "param.workflowEstimatorDemoBean" + } + }, "guid" : "18E1C085F4DDCE95" }, "visual" : { @@ -44,5 +52,48 @@ "visual" : { "at" : { "x" : 224, "y" : 160 } } + }, { + "id" : "f7", + "type" : "Script", + "name" : "delete estimator", + "config" : { + "output" : { + "code" : "in.workflowEstimatorDemoBean.onDeleteEstimator(in.deletedEstimator);" + } + }, + "visual" : { + "at" : { "x" : 288, "y" : 280 } + }, + "connect" : [ + { "id" : "f10", "to" : "f9" } + ] + }, { + "id" : "f9", + "type" : "HtmlDialogEnd", + "visual" : { + "at" : { "x" : 480, "y" : 280 } + } + }, { + "id" : "f11", + "type" : "HtmlDialogMethodStart", + "name" : "deleteEstimator(Estimator)", + "config" : { + "signature" : "deleteEstimator", + "input" : { + "params" : [ + { "name" : "estimator", "type" : "com.axonivy.utils.estimator.demo.model.Estimator", "desc" : "" } + ], + "map" : { + "out.deletedEstimator" : "param.estimator" + } + }, + "guid" : "18E1C27977F782B7" + }, + "visual" : { + "at" : { "x" : 96, "y" : 280 } + }, + "connect" : [ + { "id" : "f12", "to" : "f7" } + ] } ] } \ No newline at end of file From d594d311dbd47661d291c3dea27d947d0f3f1231 Mon Sep 17 00:00:00 2001 From: "AAVN\\ntnchuong" Date: Fri, 8 Mar 2024 17:30:35 +0700 Subject: [PATCH 053/143] TE-538: add demo project WIP --- .../processes/Start Processes/Demo.p.json | 1 - .../demo/WorkflowEstimatorDemoBean.java | 41 ++++---- .../demo/converter/PojoConverter.java | 73 +++++++++++++++ .../utils/estimator/demo/model/Estimator.java | 30 ++++-- .../EstimatorDetail/EstimatorDetail.xhtml | 93 ++++++++++++++++--- .../EstimatorDetailData.ivyClass | 4 +- .../EstimatorDetailProcess.p.json | 90 +++++++++++++++++- workflow-estimator/.project | 6 +- .../src/UseCase.java | 0 .../src/WfEstimate.java | 0 10 files changed, 293 insertions(+), 45 deletions(-) create mode 100644 workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/converter/PojoConverter.java rename {workflow-estimator-test => workflow-estimator}/src/UseCase.java (100%) rename {workflow-estimator-test => workflow-estimator}/src/WfEstimate.java (100%) diff --git a/workflow-estimator-demo/processes/Start Processes/Demo.p.json b/workflow-estimator-demo/processes/Start Processes/Demo.p.json index 9b1beeae..301ca54a 100644 --- a/workflow-estimator-demo/processes/Start Processes/Demo.p.json +++ b/workflow-estimator-demo/processes/Start Processes/Demo.p.json @@ -51,7 +51,6 @@ "config" : { "output" : { "code" : [ - "import com.axonivy.utils.estimator.demo.WorkflowEstimatorDemoBean;", "import com.axonivy.utils.estimator.demo.WorkflowEstimatorDemoBean;", "in.bean = new WorkflowEstimatorDemoBean();" ] diff --git a/workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/WorkflowEstimatorDemoBean.java b/workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/WorkflowEstimatorDemoBean.java index 7c41933a..d3dbfed4 100644 --- a/workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/WorkflowEstimatorDemoBean.java +++ b/workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/WorkflowEstimatorDemoBean.java @@ -7,13 +7,14 @@ import java.util.List; import java.util.stream.Collectors; +import com.axonivy.utils.estimator.WorkflowEstimator; +import com.axonivy.utils.estimator.demo.constant.FindType; import com.axonivy.utils.estimator.demo.model.Estimator; +import com.axonivy.utils.estimator.model.EstimatedTask; import ch.ivyteam.ivy.application.IProcessModelVersion; -import ch.ivyteam.ivy.environment.Ivy; -import ch.ivyteam.ivy.process.model.BaseElement; import ch.ivyteam.ivy.process.model.Process; -import ch.ivyteam.ivy.process.model.element.TaskModifier; +import ch.ivyteam.ivy.process.model.element.SingleTaskCreator; import ch.ivyteam.ivy.process.rdm.IProcessManager; @SuppressWarnings("restriction") @@ -24,7 +25,7 @@ public class WorkflowEstimatorDemoBean { private List processes = emptyList(); public WorkflowEstimatorDemoBean() { - + processes = getAllProcesses(); } public List getEstimators() { @@ -41,16 +42,6 @@ public List getProcesses() { return processes; } - - public void setProcesses(List processes) { - this.processes = processes; - } - - - public void initData() { - processes = getAllProcesses(); - } - public void onAddEstimator() { estimators.add(new Estimator()); } @@ -63,9 +54,27 @@ public void onSelectedProcess() { } - public List getAllTaskModifier(Process process) { + public List getAllTaskModifier(Process process) { + return process.getElements().stream() + .filter(item -> item instanceof SingleTaskCreator) + .map(SingleTaskCreator.class::cast) + .toList(); + } + + public List getAllFindType(){ + return Arrays.stream(FindType.values()).toList(); + } + + public List getEstimatedtask(Estimator estimator) throws Exception{ + var workflowEstimator = new WorkflowEstimator(estimator.getProcess(), null, estimator.getFlowName()); + List estimatedTasks = null; + if(FindType.ALL_TASK.equals(estimator.getFindType())) { + estimatedTasks = workflowEstimator.findAllTasks(estimator.getStartElement()); + } else { + estimatedTasks = workflowEstimator.findTasksOnPath(estimator.getStartElement()); + } - return emptyList(); + return estimatedTasks; } private List getAllProcesses() { diff --git a/workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/converter/PojoConverter.java b/workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/converter/PojoConverter.java new file mode 100644 index 00000000..63b283c1 --- /dev/null +++ b/workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/converter/PojoConverter.java @@ -0,0 +1,73 @@ +package com.axonivy.utils.estimator.demo.converter; + +import java.util.Map; + +import javax.faces.component.UIComponent; +import javax.faces.component.UIViewRoot; +import javax.faces.context.FacesContext; +import javax.faces.convert.Converter; +import javax.faces.convert.ConverterException; +import javax.faces.convert.FacesConverter; + +/** + * Converter that can handle POJOs. Can handle each type of object. + * + * Attention: If a no selection item is provided its item value must be ''. An empty string ('') is + * not allowed as choosable item value (it will always be converted to {@code null}). + * + * Attention: If the POJOs must match each other (e.g. in a dropdown) over multiple views it is + * required that the POJOs implement the hasCode method properly. + * + * Attention: This converter could fail in rare cases because it is based on the identityHashcode which is not unique for an object. + */ +@FacesConverter("pojoConverter") +public class PojoConverter implements Converter { + private static final String UNIQUE_CONVERTER_IDENTIFIER = PojoConverter.class.getName(); + private static final String KEY_DELIMITER = ":::"; + private static final String MAP_KEY_TEMPLATE = UNIQUE_CONVERTER_IDENTIFIER + KEY_DELIMITER + "%s" + KEY_DELIMITER + + "%s"; + + /** + * {@inheritDoc}
+ * This implementation:
+ * Returns the item's hash code or the default no selection value. + */ + @Override + public String getAsString(FacesContext context, UIComponent component, Object item) throws ConverterException { + if (item != null && !isEmptyString(item)) { + Map viewMap = getViewMap(context); + String identityHashCode = String.valueOf(System.identityHashCode(item)); + String mapKey = String.format(MAP_KEY_TEMPLATE, component.getId(), identityHashCode); + viewMap.put(mapKey, item); + + return identityHashCode; + } + return ""; + } + + /** + * {@inheritDoc}
+ * This implementation:
+ * Returns the item that corresponds the the given selected value. + */ + @Override + public Object getAsObject(FacesContext context, UIComponent component, String selectedvalue) { + if (selectedvalue != null && selectedvalue.length() > 0) { + + String mapKey = String.format(MAP_KEY_TEMPLATE, component.getId(), selectedvalue); + Map viewMap = getViewMap(context); + + return viewMap.get(mapKey); + } + return null; + } + + private boolean isEmptyString(Object item) { + return String.class.isAssignableFrom(item.getClass()) && "".equals(item); + } + + private Map getViewMap(FacesContext context) { + UIViewRoot viewRoot = context.getViewRoot(); + return viewRoot.getViewMap(); + } +} diff --git a/workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/model/Estimator.java b/workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/model/Estimator.java index 9ba118da..620e9372 100644 --- a/workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/model/Estimator.java +++ b/workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/model/Estimator.java @@ -6,12 +6,14 @@ import com.axonivy.utils.estimator.model.EstimatedTask; import ch.ivyteam.ivy.process.model.Process; -import ch.ivyteam.ivy.process.model.element.TaskModifier; +import ch.ivyteam.ivy.process.model.element.SingleTaskCreator; public class Estimator { + private String flowName; private Process process; - private TaskModifier from; + private List elements; private FindType findType; + private SingleTaskCreator startElement; private List tasks; public Process getProcess() { @@ -22,12 +24,20 @@ public void setProcess(Process process) { this.process = process; } - public TaskModifier getFrom() { - return from; + public List getElements() { + return elements; } - public void setFrom(TaskModifier from) { - this.from = from; + public void setElements(List elements) { + this.elements = elements; + } + + public SingleTaskCreator getStartElement() { + return startElement; + } + + public void setStartElement(SingleTaskCreator startElement) { + this.startElement = startElement; } public FindType getFindType() { @@ -45,4 +55,12 @@ public List getTasks() { public void setTasks(List tasks) { this.tasks = tasks; } + + public String getFlowName() { + return flowName; + } + + public void setFlowName(String flowName) { + this.flowName = flowName; + } } diff --git a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetail.xhtml b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetail.xhtml index 39cd32af..a9a16606 100644 --- a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetail.xhtml +++ b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetail.xhtml @@ -1,27 +1,90 @@ + xmlns:f="http://xmlns.jcp.org/jsf/core" + xmlns:h="http://xmlns.jcp.org/jsf/html" + xmlns:ui="http://xmlns.jcp.org/jsf/facelets" + xmlns:cc="http://xmlns.jcp.org/jsf/composite" + xmlns:ic="http://ivyteam.ch/jsf/component" + xmlns:p="http://primefaces.org/ui" + xmlns:pe="http://primefaces.org/ui/extensions" + xmlns:c="http://java.sun.com/jsp/jstl/core"> - + -

Estimator Demo

+ +

Estimator Demo

- -
- -
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ +
diff --git a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetailData.ivyClass b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetailData.ivyClass index f103197b..16dbc249 100644 --- a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetailData.ivyClass +++ b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetailData.ivyClass @@ -2,5 +2,5 @@ EstimatorDetailData #class com.axonivy.utils.estimator.component.EstimatorDetail #namespace workflowEstimatorDemoBean com.axonivy.utils.estimator.demo.WorkflowEstimatorDemoBean #field workflowEstimatorDemoBean PERSISTENT #fieldModifier -deletedEstimator com.axonivy.utils.estimator.demo.model.Estimator #field -deletedEstimator PERSISTENT #fieldModifier +selectedEstimator com.axonivy.utils.estimator.demo.model.Estimator #field +selectedEstimator PERSISTENT #fieldModifier diff --git a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetailProcess.p.json b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetailProcess.p.json index 5c0ffa0e..405abbed 100644 --- a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetailProcess.p.json +++ b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetailProcess.p.json @@ -58,7 +58,7 @@ "name" : "delete estimator", "config" : { "output" : { - "code" : "in.workflowEstimatorDemoBean.onDeleteEstimator(in.deletedEstimator);" + "code" : "in.workflowEstimatorDemoBean.onDeleteEstimator(in.selectedEstimator);" } }, "visual" : { @@ -84,7 +84,7 @@ { "name" : "estimator", "type" : "com.axonivy.utils.estimator.demo.model.Estimator", "desc" : "" } ], "map" : { - "out.deletedEstimator" : "param.estimator" + "out.selectedEstimator" : "param.estimator" } }, "guid" : "18E1C27977F782B7" @@ -95,5 +95,91 @@ "connect" : [ { "id" : "f12", "to" : "f7" } ] + }, { + "id" : "f6", + "type" : "HtmlDialogMethodStart", + "name" : "onChooseProcess(Estimator)", + "config" : { + "signature" : "onChooseProcess", + "input" : { + "params" : [ + { "name" : "estimator", "type" : "com.axonivy.utils.estimator.demo.model.Estimator", "desc" : "" } + ], + "map" : { + "out.selectedEstimator" : "param.estimator" + } + }, + "guid" : "18E1D2C4C70FD6CC" + }, + "visual" : { + "at" : { "x" : 96, "y" : 384 } + }, + "connect" : [ + { "id" : "f13", "to" : "f8" } + ] + }, { + "id" : "f8", + "type" : "Script", + "name" : "init list start element", + "config" : { + "output" : { + "code" : "in.selectedEstimator.elements = in.workflowEstimatorDemoBean.getAllTaskModifier(in.selectedEstimator.process);" + } + }, + "visual" : { + "at" : { "x" : 288, "y" : 384 } + }, + "connect" : [ + { "id" : "f15", "to" : "f14" } + ] + }, { + "id" : "f14", + "type" : "HtmlDialogEnd", + "visual" : { + "at" : { "x" : 496, "y" : 384 } + } + }, { + "id" : "f16", + "type" : "HtmlDialogMethodStart", + "name" : "onGenerate(Estimator)", + "config" : { + "signature" : "onGenerate", + "input" : { + "params" : [ + { "name" : "estimator", "type" : "com.axonivy.utils.estimator.demo.model.Estimator", "desc" : "" } + ], + "map" : { + "out.selectedEstimator" : "param.estimator" + } + }, + "guid" : "18E1D7E219D8AD8C" + }, + "visual" : { + "at" : { "x" : 96, "y" : 536 } + }, + "connect" : [ + { "id" : "f18", "to" : "f17" } + ] + }, { + "id" : "f17", + "type" : "Script", + "name" : "generate estimated task", + "config" : { + "output" : { + "code" : "in.selectedEstimator.tasks = in.workflowEstimatorDemoBean.getEstimatedtask(in.selectedEstimator);" + } + }, + "visual" : { + "at" : { "x" : 288, "y" : 536 } + }, + "connect" : [ + { "id" : "f20", "to" : "f19" } + ] + }, { + "id" : "f19", + "type" : "HtmlDialogEnd", + "visual" : { + "at" : { "x" : 480, "y" : 536 } + } } ] } \ No newline at end of file diff --git a/workflow-estimator/.project b/workflow-estimator/.project index f68fddae..98c55e91 100644 --- a/workflow-estimator/.project +++ b/workflow-estimator/.project @@ -31,17 +31,17 @@ - org.eclipse.m2e.core.maven2Builder + org.eclipse.pde.ManifestBuilder - org.eclipse.pde.ManifestBuilder + org.eclipse.pde.SchemaBuilder - org.eclipse.pde.SchemaBuilder + org.eclipse.m2e.core.maven2Builder diff --git a/workflow-estimator-test/src/UseCase.java b/workflow-estimator/src/UseCase.java similarity index 100% rename from workflow-estimator-test/src/UseCase.java rename to workflow-estimator/src/UseCase.java diff --git a/workflow-estimator-test/src/WfEstimate.java b/workflow-estimator/src/WfEstimate.java similarity index 100% rename from workflow-estimator-test/src/WfEstimate.java rename to workflow-estimator/src/WfEstimate.java From 291e41a29b55e46aa0c3c5493be3cd910546ea72 Mon Sep 17 00:00:00 2001 From: Trung Mai Date: Mon, 11 Mar 2024 15:03:56 +0700 Subject: [PATCH 054/143] TE-538: Generate estimated task --- .../processes/Start Processes/Demo.p.json | 5 +- .../demo/WorkflowEstimatorDemoBean.java | 27 ++++- .../utils/estimator/demo/model/Estimator.java | 29 +++++ .../WorkflowEstimatorTable.xhtml | 17 ++- .../EstimatorDetail/EstimatorDetail.xhtml | 90 -------------- .../EstimatorDetailData.ivyClass | 6 - .../EstimatorDialog.rddescriptor} | 0 .../EstimatorDialog/EstimatorDialog.xhtml | 83 +++++++++++++ .../EstimatorDialogData.ivyClass | 4 + .../EstimatorDialogProcess.p.json} | 113 ++++++++---------- 10 files changed, 202 insertions(+), 172 deletions(-) delete mode 100644 workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetail.xhtml delete mode 100644 workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetailData.ivyClass rename workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/{EstimatorDetail/EstimatorDetail.rddescriptor => EstimatorDialog/EstimatorDialog.rddescriptor} (100%) create mode 100644 workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDialog/EstimatorDialog.xhtml create mode 100644 workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDialog/EstimatorDialogData.ivyClass rename workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/{EstimatorDetail/EstimatorDetailProcess.p.json => EstimatorDialog/EstimatorDialogProcess.p.json} (50%) diff --git a/workflow-estimator-demo/processes/Start Processes/Demo.p.json b/workflow-estimator-demo/processes/Start Processes/Demo.p.json index 301ca54a..3483fdab 100644 --- a/workflow-estimator-demo/processes/Start Processes/Demo.p.json +++ b/workflow-estimator-demo/processes/Start Processes/Demo.p.json @@ -51,8 +51,11 @@ "config" : { "output" : { "code" : [ + "import com.axonivy.utils.estimator.demo.model.Estimator;", "import com.axonivy.utils.estimator.demo.WorkflowEstimatorDemoBean;", - "in.bean = new WorkflowEstimatorDemoBean();" + "", + "in.bean = new WorkflowEstimatorDemoBean();", + "in.bean.seletedEstimator = new Estimator();" ] } }, diff --git a/workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/WorkflowEstimatorDemoBean.java b/workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/WorkflowEstimatorDemoBean.java index d3dbfed4..92499532 100644 --- a/workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/WorkflowEstimatorDemoBean.java +++ b/workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/WorkflowEstimatorDemoBean.java @@ -2,6 +2,7 @@ import static java.util.Collections.emptyList; +import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -23,6 +24,8 @@ public class WorkflowEstimatorDemoBean { private List estimators = new ArrayList<>(); private List processes = emptyList(); + + private Estimator seletedEstimator = null; public WorkflowEstimatorDemoBean() { processes = getAllProcesses(); @@ -48,8 +51,16 @@ public void onAddEstimator() { public void onDeleteEstimator(Estimator estimator) { estimators.remove(estimator); - } + } + public Estimator getSeletedEstimator() { + return seletedEstimator; + } + + public void setSeletedEstimator(Estimator seletedEstimator) { + this.seletedEstimator = seletedEstimator; + } + public void onSelectedProcess() { } @@ -65,7 +76,7 @@ public List getAllFindType(){ return Arrays.stream(FindType.values()).toList(); } - public List getEstimatedtask(Estimator estimator) throws Exception{ + public List getEstimatedTask(Estimator estimator) throws Exception{ var workflowEstimator = new WorkflowEstimator(estimator.getProcess(), null, estimator.getFlowName()); List estimatedTasks = null; if(FindType.ALL_TASK.equals(estimator.getFindType())) { @@ -77,6 +88,18 @@ public List getEstimatedtask(Estimator estimator) throws Exceptio return estimatedTasks; } + public Duration getEstimatedTaskCalculate(Estimator estimator) throws Exception{ + var workflowEstimator = new WorkflowEstimator(estimator.getProcess(), null, estimator.getFlowName()); + Duration total = Duration.ZERO; + if(FindType.ALL_TASK.equals(estimator.getFindType())) { + total = workflowEstimator.calculateEstimatedDuration(estimator.getStartElement()); + } else { + total = workflowEstimator.calculateEstimatedDuration(estimator.getStartElement()); + } + + return total; + } + private List getAllProcesses() { var manager = IProcessManager.instance().getProjectDataModelFor(IProcessModelVersion.current()); List processes = manager.search().find().stream() diff --git a/workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/model/Estimator.java b/workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/model/Estimator.java index 620e9372..5d002b42 100644 --- a/workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/model/Estimator.java +++ b/workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/model/Estimator.java @@ -1,6 +1,9 @@ package com.axonivy.utils.estimator.demo.model; +import java.time.Duration; import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; import com.axonivy.utils.estimator.demo.constant.FindType; import com.axonivy.utils.estimator.model.EstimatedTask; @@ -9,12 +12,22 @@ import ch.ivyteam.ivy.process.model.element.SingleTaskCreator; public class Estimator { + private String id; private String flowName; private Process process; private List elements; private FindType findType; private SingleTaskCreator startElement; private List tasks; + private Duration totalDuration; + + public Estimator() { + this.id = UUID.randomUUID().toString(); + } + + public String getId() { + return id; + } public Process getProcess() { return process; @@ -63,4 +76,20 @@ public String getFlowName() { public void setFlowName(String flowName) { this.flowName = flowName; } + + public Duration getTotalDuration() { + return totalDuration; + } + + public void setTotalDuration(Duration toDuration) { + this.totalDuration = toDuration; + } + + public long getTotalDurationInHours() { + return totalDuration.toHours(); + } + + public String getElementNames() { + return this.tasks.stream().map(EstimatedTask::getElementName).collect(Collectors.joining(" -> ")); + } } diff --git a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTable.xhtml b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTable.xhtml index 487cc584..910d167e 100644 --- a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTable.xhtml +++ b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTable.xhtml @@ -13,20 +13,19 @@

Workflow Estimator Demo

- +
- +
- - - + +
+ + +
diff --git a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetail.xhtml b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetail.xhtml deleted file mode 100644 index a9a16606..00000000 --- a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetail.xhtml +++ /dev/null @@ -1,90 +0,0 @@ - - - - - - - - - -

Estimator Demo

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
- - diff --git a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetailData.ivyClass b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetailData.ivyClass deleted file mode 100644 index 16dbc249..00000000 --- a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetailData.ivyClass +++ /dev/null @@ -1,6 +0,0 @@ -EstimatorDetailData #class -com.axonivy.utils.estimator.component.EstimatorDetail #namespace -workflowEstimatorDemoBean com.axonivy.utils.estimator.demo.WorkflowEstimatorDemoBean #field -workflowEstimatorDemoBean PERSISTENT #fieldModifier -selectedEstimator com.axonivy.utils.estimator.demo.model.Estimator #field -selectedEstimator PERSISTENT #fieldModifier diff --git a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetail.rddescriptor b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDialog/EstimatorDialog.rddescriptor similarity index 100% rename from workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetail.rddescriptor rename to workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDialog/EstimatorDialog.rddescriptor diff --git a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDialog/EstimatorDialog.xhtml b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDialog/EstimatorDialog.xhtml new file mode 100644 index 00000000..75709322 --- /dev/null +++ b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDialog/EstimatorDialog.xhtml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + +
+ + + +
+ + +
+
+
+ + diff --git a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDialog/EstimatorDialogData.ivyClass b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDialog/EstimatorDialogData.ivyClass new file mode 100644 index 00000000..e0fc98c1 --- /dev/null +++ b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDialog/EstimatorDialogData.ivyClass @@ -0,0 +1,4 @@ +EstimatorDialogData #class +com.axonivy.utils.estimator.component.EstimatorDialog #namespace +workflowEstimatorDemoBean com.axonivy.utils.estimator.demo.WorkflowEstimatorDemoBean #field +workflowEstimatorDemoBean PERSISTENT #fieldModifier diff --git a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetailProcess.p.json b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDialog/EstimatorDialogProcess.p.json similarity index 50% rename from workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetailProcess.p.json rename to workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDialog/EstimatorDialogProcess.p.json index 405abbed..f976f198 100644 --- a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDetail/EstimatorDetailProcess.p.json +++ b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDialog/EstimatorDialogProcess.p.json @@ -3,7 +3,7 @@ "id" : "18E1C085F4D4C0F7", "kind" : "HTML_DIALOG", "config" : { - "data" : "com.axonivy.utils.estimator.component.EstimatorDetail.EstimatorDetailData" + "data" : "com.axonivy.utils.estimator.component.EstimatorDialog.EstimatorDialogData" }, "elements" : [ { "id" : "f0", @@ -22,7 +22,8 @@ "guid" : "18E1C085F4DDCE95" }, "visual" : { - "at" : { "x" : 96, "y" : 64 } + "at" : { "x" : 96, "y" : 64 }, + "labelOffset" : { "x" : 75, "y" : 35 } }, "connect" : [ { "id" : "f2", "to" : "f1" } @@ -52,17 +53,34 @@ "visual" : { "at" : { "x" : 224, "y" : 160 } } + }, { + "id" : "f6", + "type" : "HtmlDialogEventStart", + "name" : "onChooseProcess", + "config" : { + "guid" : "18E2BA62EFBD615A" + }, + "visual" : { + "at" : { "x" : 96, "y" : 248 } + }, + "connect" : [ + { "id" : "f8", "to" : "f7" } + ] }, { "id" : "f7", "type" : "Script", - "name" : "delete estimator", + "name" : "init start elements", "config" : { "output" : { - "code" : "in.workflowEstimatorDemoBean.onDeleteEstimator(in.selectedEstimator);" + "code" : [ + "if(in.workflowEstimatorDemoBean.seletedEstimator is initialized) {", + " in.workflowEstimatorDemoBean.seletedEstimator.elements = in.workflowEstimatorDemoBean.getAllTaskModifier(in.workflowEstimatorDemoBean.seletedEstimator.process); ", + "}" + ] } }, "visual" : { - "at" : { "x" : 288, "y" : 280 } + "at" : { "x" : 288, "y" : 248 } }, "connect" : [ { "id" : "f10", "to" : "f9" } @@ -71,63 +89,35 @@ "id" : "f9", "type" : "HtmlDialogEnd", "visual" : { - "at" : { "x" : 480, "y" : 280 } + "at" : { "x" : 480, "y" : 248 } } }, { "id" : "f11", - "type" : "HtmlDialogMethodStart", - "name" : "deleteEstimator(Estimator)", - "config" : { - "signature" : "deleteEstimator", - "input" : { - "params" : [ - { "name" : "estimator", "type" : "com.axonivy.utils.estimator.demo.model.Estimator", "desc" : "" } - ], - "map" : { - "out.selectedEstimator" : "param.estimator" - } - }, - "guid" : "18E1C27977F782B7" - }, - "visual" : { - "at" : { "x" : 96, "y" : 280 } - }, - "connect" : [ - { "id" : "f12", "to" : "f7" } - ] - }, { - "id" : "f6", - "type" : "HtmlDialogMethodStart", - "name" : "onChooseProcess(Estimator)", + "type" : "HtmlDialogEventStart", + "name" : "onGenerate", "config" : { - "signature" : "onChooseProcess", - "input" : { - "params" : [ - { "name" : "estimator", "type" : "com.axonivy.utils.estimator.demo.model.Estimator", "desc" : "" } - ], - "map" : { - "out.selectedEstimator" : "param.estimator" - } - }, - "guid" : "18E1D2C4C70FD6CC" + "guid" : "18E2C4112161A72E" }, "visual" : { - "at" : { "x" : 96, "y" : 384 } + "at" : { "x" : 88, "y" : 336 } }, "connect" : [ - { "id" : "f13", "to" : "f8" } + { "id" : "f13", "to" : "f12" } ] }, { - "id" : "f8", + "id" : "f12", "type" : "Script", - "name" : "init list start element", + "name" : "generate list estimated task", "config" : { "output" : { - "code" : "in.selectedEstimator.elements = in.workflowEstimatorDemoBean.getAllTaskModifier(in.selectedEstimator.process);" + "code" : [ + "in.workflowEstimatorDemoBean.seletedEstimator.tasks = in.workflowEstimatorDemoBean.getEstimatedTask(in.workflowEstimatorDemoBean.seletedEstimator);", + "in.workflowEstimatorDemoBean.seletedEstimator.totalDuration = in.workflowEstimatorDemoBean.getEstimatedTaskCalculate(in.workflowEstimatorDemoBean.seletedEstimator);" + ] } }, "visual" : { - "at" : { "x" : 288, "y" : 384 } + "at" : { "x" : 280, "y" : 336 } }, "connect" : [ { "id" : "f15", "to" : "f14" } @@ -136,26 +126,17 @@ "id" : "f14", "type" : "HtmlDialogEnd", "visual" : { - "at" : { "x" : 496, "y" : 384 } + "at" : { "x" : 472, "y" : 336 } } }, { "id" : "f16", - "type" : "HtmlDialogMethodStart", - "name" : "onGenerate(Estimator)", + "type" : "HtmlDialogEventStart", + "name" : "onSaveEstimator", "config" : { - "signature" : "onGenerate", - "input" : { - "params" : [ - { "name" : "estimator", "type" : "com.axonivy.utils.estimator.demo.model.Estimator", "desc" : "" } - ], - "map" : { - "out.selectedEstimator" : "param.estimator" - } - }, - "guid" : "18E1D7E219D8AD8C" + "guid" : "18E2C8636ACDB041" }, "visual" : { - "at" : { "x" : 96, "y" : 536 } + "at" : { "x" : 72, "y" : 432 } }, "connect" : [ { "id" : "f18", "to" : "f17" } @@ -163,14 +144,18 @@ }, { "id" : "f17", "type" : "Script", - "name" : "generate estimated task", + "name" : "add estimator to list", "config" : { "output" : { - "code" : "in.selectedEstimator.tasks = in.workflowEstimatorDemoBean.getEstimatedtask(in.selectedEstimator);" + "code" : [ + "import com.axonivy.utils.estimator.demo.model.Estimator;", + "in.workflowEstimatorDemoBean.estimators.add(in.workflowEstimatorDemoBean.seletedEstimator);", + "in.workflowEstimatorDemoBean.seletedEstimator = new Estimator();" + ] } }, "visual" : { - "at" : { "x" : 288, "y" : 536 } + "at" : { "x" : 264, "y" : 432 } }, "connect" : [ { "id" : "f20", "to" : "f19" } @@ -179,7 +164,7 @@ "id" : "f19", "type" : "HtmlDialogEnd", "visual" : { - "at" : { "x" : 480, "y" : 536 } + "at" : { "x" : 456, "y" : 432 } } } ] } \ No newline at end of file From 056571124f8c4d7232916c7d2fa545020369e8af Mon Sep 17 00:00:00 2001 From: "AAVN\\ntnchuong" Date: Tue, 12 Mar 2024 09:15:00 +0700 Subject: [PATCH 055/143] TE-538: add and delete estimator demo --- .../demo/WorkflowEstimatorDemoBean.java | 3 +- .../WorkflowEstimatorTable.xhtml | 34 ++++++++++++-- .../WorkflowEstimatorTableProcess.p.json | 47 ++++++++++++++++++- .../EstimatorDialog/EstimatorDialog.xhtml | 3 +- .../EstimatorDialogProcess.p.json | 12 ++--- 5 files changed, 85 insertions(+), 14 deletions(-) diff --git a/workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/WorkflowEstimatorDemoBean.java b/workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/WorkflowEstimatorDemoBean.java index 92499532..27fc60d6 100644 --- a/workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/WorkflowEstimatorDemoBean.java +++ b/workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/WorkflowEstimatorDemoBean.java @@ -23,6 +23,7 @@ public class WorkflowEstimatorDemoBean { private static final List PROCESS_FOLDERS = Arrays.asList("Bussiness Processes"); private List estimators = new ArrayList<>(); + private List processes = emptyList(); private Estimator seletedEstimator = null; @@ -35,12 +36,10 @@ public List getEstimators() { return estimators; } - public void setEstimators(List estimators) { this.estimators = estimators; } - public List getProcesses() { return processes; } diff --git a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTable.xhtml b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTable.xhtml index 910d167e..a29d520d 100644 --- a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTable.xhtml +++ b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTable.xhtml @@ -19,11 +19,39 @@ - - + + + +
+ + +
+ + +
+ + +
+ + + +
+ +
+ +
+ + + + + +
+


+
- diff --git a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTableProcess.p.json b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTableProcess.p.json index 77424699..f6e355bf 100644 --- a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTableProcess.p.json +++ b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTableProcess.p.json @@ -22,7 +22,7 @@ "guid" : "18E18129A262E35E" }, "visual" : { - "at" : { "x" : 112, "y" : 64 } + "at" : { "x" : 104, "y" : 64 } }, "connect" : [ { "id" : "f2", "to" : "f1" } @@ -41,7 +41,7 @@ "guid" : "18E18129A2768836" }, "visual" : { - "at" : { "x" : 96, "y" : 160 } + "at" : { "x" : 104, "y" : 160 } }, "connect" : [ { "id" : "f5", "to" : "f4" } @@ -86,5 +86,48 @@ "visual" : { "at" : { "x" : 488, "y" : 264 } } + }, { + "id" : "f12", + "type" : "Script", + "name" : "delete estimator", + "config" : { + "output" : { + "code" : "in.workflowEstimatorDemoBean.estimators.remove(in.workflowEstimatorDemoBean.seletedEstimator);" + } + }, + "visual" : { + "at" : { "x" : 296, "y" : 400 } + }, + "connect" : [ + { "id" : "f15", "to" : "f14" } + ] + }, { + "id" : "f14", + "type" : "HtmlDialogEnd", + "visual" : { + "at" : { "x" : 488, "y" : 400 } + } + }, { + "id" : "f11", + "type" : "HtmlDialogMethodStart", + "name" : "deleteEstimator(Estimator)", + "config" : { + "signature" : "deleteEstimator", + "input" : { + "params" : [ + { "name" : "estimator", "type" : "com.axonivy.utils.estimator.demo.model.Estimator", "desc" : "" } + ], + "map" : { + "out.workflowEstimatorDemoBean.seletedEstimator" : "param.estimator" + } + }, + "guid" : "18E303AA97F682BA" + }, + "visual" : { + "at" : { "x" : 104, "y" : 400 } + }, + "connect" : [ + { "id" : "f13", "to" : "f12" } + ] } ] } \ No newline at end of file diff --git a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDialog/EstimatorDialog.xhtml b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDialog/EstimatorDialog.xhtml index 75709322..b29eb935 100644 --- a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDialog/EstimatorDialog.xhtml +++ b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDialog/EstimatorDialog.xhtml @@ -10,6 +10,7 @@ + @@ -71,7 +72,7 @@ value="Generate" process="@form" update="@form" icon="fa-solid fa-check" /> - diff --git a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDialog/EstimatorDialogProcess.p.json b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDialog/EstimatorDialogProcess.p.json index f976f198..910f9370 100644 --- a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDialog/EstimatorDialogProcess.p.json +++ b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/component/EstimatorDialog/EstimatorDialogProcess.p.json @@ -99,7 +99,7 @@ "guid" : "18E2C4112161A72E" }, "visual" : { - "at" : { "x" : 88, "y" : 336 } + "at" : { "x" : 96, "y" : 336 } }, "connect" : [ { "id" : "f13", "to" : "f12" } @@ -117,7 +117,7 @@ } }, "visual" : { - "at" : { "x" : 280, "y" : 336 } + "at" : { "x" : 288, "y" : 336 } }, "connect" : [ { "id" : "f15", "to" : "f14" } @@ -126,7 +126,7 @@ "id" : "f14", "type" : "HtmlDialogEnd", "visual" : { - "at" : { "x" : 472, "y" : 336 } + "at" : { "x" : 480, "y" : 336 } } }, { "id" : "f16", @@ -136,7 +136,7 @@ "guid" : "18E2C8636ACDB041" }, "visual" : { - "at" : { "x" : 72, "y" : 432 } + "at" : { "x" : 96, "y" : 432 } }, "connect" : [ { "id" : "f18", "to" : "f17" } @@ -155,7 +155,7 @@ } }, "visual" : { - "at" : { "x" : 264, "y" : 432 } + "at" : { "x" : 288, "y" : 432 } }, "connect" : [ { "id" : "f20", "to" : "f19" } @@ -164,7 +164,7 @@ "id" : "f19", "type" : "HtmlDialogEnd", "visual" : { - "at" : { "x" : 456, "y" : 432 } + "at" : { "x" : 480, "y" : 432 } } } ] } \ No newline at end of file From c63f49a0efb1a470c881197411ca23bb4a5881b3 Mon Sep 17 00:00:00 2001 From: Trung Mai Date: Tue, 12 Mar 2024 11:20:10 +0700 Subject: [PATCH 056/143] TE-538: Add process diagram --- .../Bussiness Processes/FlowDemoBasic.p.json | 31 ++++--- .../processes/Start Processes/Demo.p.json | 2 +- .../demo/WorkflowEstimatorDemoBean.java | 45 ++++++--- .../utils/estimator/demo/model/Estimator.java | 19 ++++ .../WorkflowEstimatorTable.xhtml | 93 ++++++++++++------- .../WorkflowEstimatorTableProcess.p.json | 8 +- .../EstimatorDialog/EstimatorDialog.xhtml | 59 +++++++----- .../EstimatorDialogProcess.p.json | 13 +-- 8 files changed, 174 insertions(+), 96 deletions(-) diff --git a/workflow-estimator-demo/processes/Bussiness Processes/FlowDemoBasic.p.json b/workflow-estimator-demo/processes/Bussiness Processes/FlowDemoBasic.p.json index e263b1cb..467d6be1 100644 --- a/workflow-estimator-demo/processes/Bussiness Processes/FlowDemoBasic.p.json +++ b/workflow-estimator-demo/processes/Bussiness Processes/FlowDemoBasic.p.json @@ -14,10 +14,7 @@ "type" : "RequestStart", "name" : "Start", "config" : { - "signature" : "start", - "request" : { - "isVisibleOnStartList" : false - } + "signature" : "start" }, "visual" : { "at" : { "x" : 96, "y" : 64 } @@ -44,12 +41,17 @@ }, { "id" : "f4", "type" : "Alternative", + "config" : { + "conditions" : { + "f13" : "true" + } + }, "visual" : { "at" : { "x" : 464, "y" : 64 } }, "connect" : [ { "id" : "f13", "to" : "f12" }, - { "id" : "f15", "to" : "f7" } + { "id" : "f15", "to" : "f7", "color" : "default path" } ] }, { "id" : "f6", @@ -82,12 +84,17 @@ }, { "id" : "f12", "type" : "Alternative", + "config" : { + "conditions" : { + "f14" : "true" + } + }, "visual" : { "at" : { "x" : 464, "y" : 192 } }, "connect" : [ { "id" : "f14", "to" : "f11" }, - { "id" : "f21", "to" : "f20", "via" : [ { "x" : 464, "y" : 328 } ] } + { "id" : "f21", "to" : "f20", "via" : [ { "x" : 464, "y" : 328 } ], "color" : "default path" } ] }, { "id" : "f7", @@ -109,10 +116,7 @@ "type" : "RequestStart", "name" : "NewStart", "config" : { - "signature" : "start2", - "request" : { - "isVisibleOnStartList" : false - } + "signature" : "start2" }, "visual" : { "at" : { "x" : 104, "y" : 192 } @@ -130,5 +134,10 @@ "connect" : [ { "id" : "f22", "to" : "f8", "via" : [ { "x" : 960, "y" : 328 } ] } ] - } ] + } ], + "layout" : { + "colors" : { + "default path" : "#1af929" + } + } } \ No newline at end of file diff --git a/workflow-estimator-demo/processes/Start Processes/Demo.p.json b/workflow-estimator-demo/processes/Start Processes/Demo.p.json index 3483fdab..b15ab2dd 100644 --- a/workflow-estimator-demo/processes/Start Processes/Demo.p.json +++ b/workflow-estimator-demo/processes/Start Processes/Demo.p.json @@ -55,7 +55,7 @@ "import com.axonivy.utils.estimator.demo.WorkflowEstimatorDemoBean;", "", "in.bean = new WorkflowEstimatorDemoBean();", - "in.bean.seletedEstimator = new Estimator();" + "in.bean.selectedEstimator = new Estimator();" ] } }, diff --git a/workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/WorkflowEstimatorDemoBean.java b/workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/WorkflowEstimatorDemoBean.java index 27fc60d6..664fc891 100644 --- a/workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/WorkflowEstimatorDemoBean.java +++ b/workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/WorkflowEstimatorDemoBean.java @@ -14,9 +14,13 @@ import com.axonivy.utils.estimator.model.EstimatedTask; import ch.ivyteam.ivy.application.IProcessModelVersion; +import ch.ivyteam.ivy.environment.Ivy; import ch.ivyteam.ivy.process.model.Process; import ch.ivyteam.ivy.process.model.element.SingleTaskCreator; import ch.ivyteam.ivy.process.rdm.IProcessManager; +import ch.ivyteam.ivy.process.viewer.api.ProcessViewer; +import ch.ivyteam.ivy.workflow.start.IProcessWebStartable; +import ch.ivyteam.ivy.workflow.start.IWebStartable; @SuppressWarnings("restriction") public class WorkflowEstimatorDemoBean { @@ -26,7 +30,7 @@ public class WorkflowEstimatorDemoBean { private List processes = emptyList(); - private Estimator seletedEstimator = null; + private Estimator selectedEstimator = null; public WorkflowEstimatorDemoBean() { processes = getAllProcesses(); @@ -44,24 +48,26 @@ public List getProcesses() { return processes; } - public void onAddEstimator() { - estimators.add(new Estimator()); + public void onAddEstimator(Estimator estimator) { + int index = estimators.indexOf(estimator); + if(index > -1) { + estimators.remove(index); + estimators.add(index, estimator); + } else { + estimators.add(estimator); + } } public void onDeleteEstimator(Estimator estimator) { estimators.remove(estimator); } - public Estimator getSeletedEstimator() { - return seletedEstimator; - } - - public void setSeletedEstimator(Estimator seletedEstimator) { - this.seletedEstimator = seletedEstimator; + public Estimator getSelectedEstimator() { + return selectedEstimator; } - public void onSelectedProcess() { - + public void setSelectedEstimator(Estimator selectedEstimator) { + this.selectedEstimator = selectedEstimator; } public List getAllTaskModifier(Process process) { @@ -98,18 +104,29 @@ public Duration getEstimatedTaskCalculate(Estimator estimator) throws Exception{ return total; } - + private List getAllProcesses() { var manager = IProcessManager.instance().getProjectDataModelFor(IProcessModelVersion.current()); List processes = manager.search().find().stream() .map(start -> start.getRootProcess()) .filter(process -> isAcceptedProcess(PROCESS_FOLDERS, process.getFullQualifiedName().getName())) .distinct() - .collect(Collectors.toList()); - + .collect(Collectors.toList()); + return processes; } + public String getProcessWebLink(Process process) { + String guid = processes.get(0).getPid().getProcessGuid(); + IWebStartable webStartable = Ivy.session().getStartables().stream().filter(it -> it.getLink().toRelativeUri().getPath().contains(guid)).findFirst().orElse(null); + + if(webStartable != null) { + return ProcessViewer.of((IProcessWebStartable) webStartable).url().toWebLink().getRelative(); + } + + return null; + } + private boolean isAcceptedProcess(Listfolders, String fullQualifiedName) { return folders.stream().anyMatch(folder -> fullQualifiedName.contains(folder)); } diff --git a/workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/model/Estimator.java b/workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/model/Estimator.java index 5d002b42..18dbd3fe 100644 --- a/workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/model/Estimator.java +++ b/workflow-estimator-demo/src/com/axonivy/utils/estimator/demo/model/Estimator.java @@ -1,5 +1,7 @@ package com.axonivy.utils.estimator.demo.model; +import static java.util.Collections.emptyList; + import java.time.Duration; import java.util.List; import java.util.UUID; @@ -15,6 +17,7 @@ public class Estimator { private String id; private String flowName; private Process process; + private String processPath; private List elements; private FindType findType; private SingleTaskCreator startElement; @@ -23,6 +26,14 @@ public class Estimator { public Estimator() { this.id = UUID.randomUUID().toString(); + this.flowName = null; + this.process = null; + this.processPath = null; + this.elements = emptyList(); + this.findType = null; + this.startElement = null; + this.tasks = emptyList(); + this.totalDuration = Duration.ZERO; } public String getId() { @@ -37,6 +48,14 @@ public void setProcess(Process process) { this.process = process; } + public String getProcessPath() { + return processPath; + } + + public void setProcessPath(String processPath) { + this.processPath = processPath; + } + public List getElements() { return elements; } diff --git a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTable.xhtml b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTable.xhtml index a29d520d..f998adc9 100644 --- a/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTable.xhtml +++ b/workflow-estimator-demo/src_hd/com/axonivy/utils/estimator/WorkflowEstimatorTable/WorkflowEstimatorTable.xhtml @@ -16,44 +16,67 @@
- +
- - - -
- - -
- - -
- - -
- - - -
- -
- -
- - - - - -
-


-
+ + + + +
+ + + +
+ + + +
+ + + +
+ + + +
+ +
+ + +