Merge branch 'feature/parallel_multiinstance_tasks' into dev

This commit is contained in:
Aaron Louie 2020-06-28 16:37:58 -04:00
commit 8f0115ac48
14 changed files with 392 additions and 123 deletions

41
Pipfile
View File

@ -9,40 +9,41 @@ pbr = "*"
coverage = "*"
[packages]
alembic = "*"
connexion = {extras = ["swagger-ui"],version = "*"}
swagger-ui-bundle = "*"
coverage = "*"
docxtpl = "*"
flask = "*"
flask-admin = "*"
flask-bcrypt = "*"
flask-cors = "*"
flask-mail = "*"
flask-marshmallow = "*"
flask-migrate = "*"
flask-restful = "*"
gunicorn = "*"
httpretty = "*"
ldap3 = "*"
lxml = "*"
markdown = "*"
marshmallow = "*"
marshmallow-enum = "*"
marshmallow-sqlalchemy = "*"
openpyxl = "*"
pyjwt = "*"
requests = "*"
xlsxwriter = "*"
webtest = "*"
spiffworkflow = {editable = true,git = "https://github.com/sartography/SpiffWorkflow.git",ref = "deploy"}
alembic = "*"
coverage = "*"
sphinx = "*"
recommonmark = "*"
psycopg2-binary = "*"
docxtpl = "*"
python-dateutil = "*"
pandas = "*"
xlrd = "*"
ldap3 = "*"
gunicorn = "*"
werkzeug = "*"
psycopg2-binary = "*"
pyjwt = "*"
python-dateutil = "*"
recommonmark = "*"
requests = "*"
sentry-sdk = {extras = ["flask"],version = "==0.14.4"}
flask-mail = "*"
flask-admin = "*"
markdown = "*"
sphinx = "*"
spiffworkflow = {editable = true,git = "https://github.com/sartography/SpiffWorkflow.git",ref = "STG-26"}
swagger-ui-bundle = "*"
webtest = "*"
werkzeug = "*"
xlrd = "*"
xlsxwriter = "*"
[requires]
python_version = "3.7"

190
Pipfile.lock generated
View File

@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
"sha256": "deb9e257fe8240d12bf82940ff22f5ddb338b305491f33655a82adda9438990f"
"sha256": "45ac71a0a66c2f55518be6fbc93a1b76e6a53ad3c7a557c3cb371d07781698b6"
},
"pipfile-spec": 6,
"requires": {
@ -35,6 +35,7 @@
"sha256:24dbaff8ce4f30566bb88976b398e8c4e77637171af3af6f1b9650f48890e60b",
"sha256:bb68f8d2bced8f93ccfd07d96c689b716b3227720add971be980accfc2952139"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==2.6.0"
},
"aniso8601": {
@ -49,6 +50,7 @@
"sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c",
"sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==19.3.0"
},
"babel": {
@ -56,6 +58,7 @@
"sha256:1aac2ae2d0d8ea368fa90906567f5c08463d98ade155c0c4bfedd6a0f7160e38",
"sha256:d670ea0b10f8b723672d3a6abeb87b565b244da220d76b4dba1b66269ec152d4"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.8.0"
},
"bcrypt": {
@ -79,6 +82,7 @@
"sha256:d7bdc26475679dd073ba0ed2766445bb5b20ca4793ca0db32b399dccc6bc84b7",
"sha256:ff032765bb8716d9387fd5376d987a937254b0619eff0972779515b5c98820bc"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==3.1.7"
},
"beautifulsoup4": {
@ -107,6 +111,7 @@
"sha256:ef17d7dffde7fc73ecab3a3b6389d93d3213bac53fa7f28e68e33647ad50b916",
"sha256:fd77e4248bb1b7af5f7922dd8e81156f540306e3a5c4b1c24167c1f5f06025da"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==4.4.6"
},
"certifi": {
@ -161,6 +166,7 @@
"sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a",
"sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==7.1.2"
},
"clickclick": {
@ -182,6 +188,7 @@
"sha256:2ca44140ee259b5e3d8aaf47c79c36a7ab0d5e94d70bd4105c03ede7a20ea5a1",
"sha256:cffc044844040c7ce04e9acd1838b5f2e5fa3170182f6fda4d2ea8b0099dbadd"
],
"markers": "python_version >= '3.6'",
"version": "==5.0.0"
},
"connexion": {
@ -237,6 +244,7 @@
"sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af",
"sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==0.16"
},
"docxtpl": {
@ -319,12 +327,14 @@
"sha256:0b656fbf87c5f24109d859bafa791d29751fabbda2302b606881ae5485b557a5",
"sha256:fcfe6df52cd2ed8a63008ca36b86a51fa7a4b70cef1c39e5625f722fca32308e"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.4.3"
},
"future": {
"hashes": [
"sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"
],
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==0.18.2"
},
"gunicorn": {
@ -347,6 +357,7 @@
"sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb",
"sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.9"
},
"imagesize": {
@ -354,6 +365,7 @@
"sha256:6965f19a6a2039c7d48bca7dba2473069ff854c36ae6f19d2cde309d998228a1",
"sha256:b1f6b5a4eab1f73479a50fb79fcf729514a900c341d8503d62a62dbc4127a2b1"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.2.0"
},
"importlib-metadata": {
@ -369,6 +381,7 @@
"sha256:88b101b2668a1d81d6d72d4c2018e53bc6c7fc544c987849da1c7f77545c3bc9",
"sha256:f576e85132d34f5bf7df5183c2c6f94cfb32e528f53065345cf71329ba0b8924"
],
"markers": "python_version >= '3.5'",
"version": "==0.5.0"
},
"itsdangerous": {
@ -376,6 +389,7 @@
"sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19",
"sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.1.0"
},
"jdcal": {
@ -390,6 +404,7 @@
"sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0",
"sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==2.11.2"
},
"jsonschema": {
@ -404,11 +419,16 @@
"sha256:be48cdffb54a2194d93ad6533d73f69408486483d189fe9f5990ee24255b0e0a",
"sha256:ca1b45faac8c0b18493d02a8571792f3c40291cf2bcf1f55afed3d8f3aa7ba74"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==4.6.11"
},
"ldap3": {
"hashes": [
"sha256:17f04298b70bf7ecaa5db8a7d8622b5a962ef7fc2b245b2eea705ac1c24338c0",
"sha256:298769ab0232b3a3efa1e84881096c24526fe37911c83a11285f222fe4975efd",
"sha256:4fd2db72d0412cc16ee86be01332095e86e361329c3579b314231eb2e56c7871",
"sha256:52ab557b3c4908db4a90bea16731aa714b1b54e039b54fd4c4b83994c6c48c0c",
"sha256:53aaae5bf14f3827c69600ddf4d61b88f49c055bb93060e9702c5bafd206c744",
"sha256:81df4ac8b6df10fb1f05b17c18d0cb8c4c344d5a03083c382824960ed959cf5b"
],
"index": "pypi",
@ -444,6 +464,7 @@
"sha256:f95d28193c3863132b1f55c1056036bf580b5a488d908f7d22a04ace8935a3a9",
"sha256:fadd2a63a2bfd7fb604508e553d1cf68eca250b2fbdbd81213b5f6f2fbf23529"
],
"index": "pypi",
"version": "==4.5.1"
},
"mako": {
@ -451,6 +472,7 @@
"sha256:8195c8c1400ceb53496064314c6736719c6f25e7479cd24c77be3d9361cddc27",
"sha256:93729a258e4ff0747c876bd9e20df1b9758028946e976324ccd2d68245c7b6a9"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.1.3"
},
"markdown": {
@ -497,6 +519,7 @@
"sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7",
"sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.1.1"
},
"marshmallow": {
@ -552,6 +575,7 @@
"sha256:df1889701e2dfd8ba4dc9b1a010f0a60950077fb5242bb92c8b5c7f1a6f2668a",
"sha256:fa1fe75b4a9e18b66ae7f0b122543c42debcf800aaafa0212aaff3ad273c2596"
],
"markers": "python_version >= '3.6'",
"version": "==1.19.0"
},
"openapi-spec-validator": {
@ -574,6 +598,7 @@
"sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8",
"sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==20.4"
},
"pandas": {
@ -636,8 +661,19 @@
},
"pyasn1": {
"hashes": [
"sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359",
"sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576",
"sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf",
"sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7",
"sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d",
"sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba"
"sha256:5c9414dcfede6e441f7e8f81b43b34e834731003427e5b09e4e00e3172a10f00",
"sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8",
"sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86",
"sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12",
"sha256:99fcc3c8d804d1bc6d9a099921e39d827026409a58f2a720dcdb89374ea0c776",
"sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba",
"sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2",
"sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3"
],
"version": "==0.4.8"
},
@ -646,6 +682,7 @@
"sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0",
"sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.20"
},
"pygments": {
@ -653,6 +690,7 @@
"sha256:647344a061c249a3b74e230c739f434d7ea4d8b1d5f3721bc0f3558049b38f44",
"sha256:ff7a40b4860b727ab48fad6360eb351cc1b33cbf9b15a0f689ca5353e9463324"
],
"markers": "python_version >= '3.5'",
"version": "==2.6.1"
},
"pyjwt": {
@ -668,6 +706,7 @@
"sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1",
"sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"
],
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.4.7"
},
"pyrsistent": {
@ -694,10 +733,67 @@
"hashes": [
"sha256:1bf6e860a8ad52a14c3ee1252d5dc25b2030618ed80c022598f00176adc8367d",
"sha256:51fda6bcc5ddbbb7063b2af7509e43bd84bfc32a4ff71349ec7847713882327b",
"sha256:5f98b069316ea1c2ed3f67e7f5df6c0d8f10b689964a4a811ff64f0106819ec8"
"sha256:5f98b069316ea1c2ed3f67e7f5df6c0d8f10b689964a4a811ff64f0106819ec8",
"sha256:c3da2053dbab6b29c94e43c486ff67206eafbe7eb52dbec7390b5e2fb05aac77",
"sha256:ea87e17f6ec459e780e4221f295411462e0d0810858e055fc514684350a2f522"
],
"version": "==1.0.4"
},
"python-levenshtein-wheels": {
"hashes": [
"sha256:0065529c8aec4c044468286177761857d36981ba6f7fdb62d7d5f7ffd143de5d",
"sha256:016924a59d689f9f47d5f7b26b70f31e309255e8dd72602c91e93ceb752b9f92",
"sha256:089d046ea7727e583233c71fef1046663ed67b96967063ae8ddc9f551e86a4fc",
"sha256:09f9faaaa8f65726f91b44c11d3d622fee0f1780cfbe2bf3f410dd0e7345adcb",
"sha256:0aea217eab612acd45dcc3424a2e8dbd977cc309f80359d0c01971f1e65b9a9b",
"sha256:0beb91ad80b1573829066e5af36b80190c367be6e0a65292f073353b0388c7fc",
"sha256:0ec1bc73f5ed3a1a06e02d13bb3cd22a0b32ebf65a9667bbccba106bfa0546f1",
"sha256:0fa2ca69ef803bc6037a8c919e2e8a17b55e94c9c9ffcb4c21befbb15a1d0f40",
"sha256:11c77d0d74ab7f46f89a58ae9c2d67349ebc1ae3e18636627f9939d810167c31",
"sha256:19a68716a322486ddffc8bf7e5cf44a82f7700b05a10658e6e7fc5c7ae92b13d",
"sha256:19a95a01d28d63b042438ba860c4ace90362906a038fa77962ba33325d377d10",
"sha256:1a61f3a51e00a3608659bbaabb3f27af37c9dbe84d843369061a3e45cf0d5103",
"sha256:1c50aebebab403fb2dd415d70355446ac364dece502b0e2737a1a085bb9a4aa4",
"sha256:1d2390d04f9b673391e5ce1a0b054d0565f2e00ea5d1187a044221dc5c02c3e6",
"sha256:1e51cdc123625a28709662d24ea0cb4cf6f991845e6054d9f803c78da1d6b08f",
"sha256:1eca6dc97dfcf588f53281fe48a6d5c423d4e14bdab658a1aa6efd447acc64e0",
"sha256:1f0056d3216b0fe38f25c6f8ebc84bd9f6d34c55a7a9414341b674fb98961399",
"sha256:228b59460e9a786e498bdfc8011838b89c6054650b115c86c9c819a055a793b0",
"sha256:23020f9ff2cb3457a926dcc470b84f9bd5b7646bd8b8e06b915bdbbc905cb23f",
"sha256:2b7b7cf0f43b677f818aa9a610464abf06106c19a51b9ac35bd051a439f337a5",
"sha256:3b591c9a7e91480f0d7bf2041d325f578b9b9c2f2d593304377cb28862e7f9a2",
"sha256:3ca9c70411ab587d071c1d8fc8b69d0558be8e4aa920f2595e2cb5eb229ccc4c",
"sha256:3e6bcca97a7ff4e720352b57ddc26380c0583dcdd4b791acef7b574ad58468a7",
"sha256:3ed88f9e638da57647149115c34e0e120cae6f3d35eee7d77e22cc9c1d8eced3",
"sha256:445bf7941cb1fa05d6c2a4a502ad4868a5cacd92e8eb77b2bd008cdda9d37c55",
"sha256:4ba5e147d76d7ee884fd6eae461438b080bcc9f2c6eb9b576811e1bcfe8f808e",
"sha256:4bb128b719c30f3b9feacfe71a338ae07d39dbffc077139416f3535c89f12362",
"sha256:4e951907b9b5d40c9f1b611c8bdfe46ff8cf8371877cebbd589bf5840feab662",
"sha256:53c0c9964390368fd64460b690f168221c669766b193b7e80ae3950c2b9551f8",
"sha256:57c4edef81611098d37176278f2b6a3712bf864eed313496d7d80504805896d1",
"sha256:5b36e406937c6463d1c1ef3dd82d3f771d9d845f21351e8a026fe4dd398ea8d0",
"sha256:7d0821dab24b430dfdc2cba70a06e6d7a45cb839d0dd0e6db97bb99e23c3d884",
"sha256:7f7283dfe50eac8a8cd9b777de9eb50b1edf7dbb46fc7cc9d9b0050d0c135021",
"sha256:7f9759095b3fc825464a72b1cae95125e610eba3c70f91557754c32a0bf32ea2",
"sha256:8005a4df455569c0d490ddfd9e5a163f21293477fd0ed4ea9effdd723ddd8eaa",
"sha256:86e865f29ad3dc3bb4733e5247220173d90f05ac8d2ad18e9689a220f90de55f",
"sha256:98727050ba70eb8d318ec8a8203531c20119347fc8f281102b097326812742ab",
"sha256:ac9cdf044dcb9481c7da782db01b50c1f0e7cdd78c8507b963b6d072829c0263",
"sha256:acfad8ffed96891fe7c583d92717cd8ec0c03b59a954c389fd4e26a5cdeac610",
"sha256:ad15f25abff8220e556d64e2a27c646241b08f00faf1bc02313655696cd3edfa",
"sha256:b679f951f842c38665aa54bea4d7403099131f71fac6d8584f893a731fe1266d",
"sha256:b8c183dc4aa4e95dc5c373eedc3d205c176805835611fcfec5d9050736c695c4",
"sha256:c097a6829967c76526a037ed34500a028f78f0d765c8e3dbd1a7717afd09fb92",
"sha256:c2c76f483d05eddec60a5cd89e92385adef565a4f243b1d9a6abe2f6bd2a7c0a",
"sha256:c388baa3c04272a7c585d3da24030c142353eb26eb531dd2681502e6be7d7a26",
"sha256:cb0f2a711db665b5bf8697b5af3b9884bb1139385c5c12c2e472e4bbee62da99",
"sha256:cbac984d7b36e75b440d1c8ff9d3425d778364a0cbc23f8943383d4decd35d5e",
"sha256:f55adf069be2d655f8d668594fe1be1b84d9dc8106d380a9ada06f34941c33c8",
"sha256:f9084ed3b8997ad4353d124b903f2860a9695b9e080663276d9e58c32e293244",
"sha256:fb7df3504222fcb1fa593f76623abbb54d6019eec15aac5d05cd07ad90ac016c"
],
"version": "==0.13.1"
},
"pytz": {
"hashes": [
"sha256:a494d53b6d39c3c6e44c3bec237336e14305e4f29bbf800b599253057fbb79ed",
@ -753,6 +849,7 @@
"sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259",
"sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.15.0"
},
"snowballstemmer": {
@ -767,6 +864,7 @@
"sha256:1634eea42ab371d3d346309b93df7870a88610f0725d47528be902a0d95ecc55",
"sha256:a59dc181727e95d25f781f0eb4fd1825ff45590ec8ff49eadfd7f1a537cc0232"
],
"markers": "python_version >= '3.5'",
"version": "==2.0.1"
},
"sphinx": {
@ -782,6 +880,7 @@
"sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a",
"sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"
],
"markers": "python_version >= '3.5'",
"version": "==1.0.2"
},
"sphinxcontrib-devhelp": {
@ -789,6 +888,7 @@
"sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e",
"sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"
],
"markers": "python_version >= '3.5'",
"version": "==1.0.2"
},
"sphinxcontrib-htmlhelp": {
@ -796,6 +896,7 @@
"sha256:3c0bc24a2c41e340ac37c85ced6dafc879ab485c095b1d65d2461ac2f7cca86f",
"sha256:e8f5bb7e31b2dbb25b9cc435c8ab7a79787ebf7f906155729338f3156d93659b"
],
"markers": "python_version >= '3.5'",
"version": "==1.0.3"
},
"sphinxcontrib-jsmath": {
@ -803,6 +904,7 @@
"sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178",
"sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"
],
"markers": "python_version >= '3.5'",
"version": "==1.0.1"
},
"sphinxcontrib-qthelp": {
@ -810,6 +912,7 @@
"sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72",
"sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"
],
"markers": "python_version >= '3.5'",
"version": "==1.0.3"
},
"sphinxcontrib-serializinghtml": {
@ -817,45 +920,47 @@
"sha256:eaa0eccc86e982a9b939b2b82d12cc5d013385ba5eadcc7e4fed23f4405f77bc",
"sha256:f242a81d423f59617a8e5cf16f5d4d74e28ee9a66f9e5b637a18082991db5a9a"
],
"markers": "python_version >= '3.5'",
"version": "==1.1.4"
},
"spiffworkflow": {
"editable": true,
"git": "https://github.com/sartography/SpiffWorkflow.git",
"ref": "5450dc0463a95811d386b7de063d950bf6179d2b"
"ref": "599f41fcf9257196710806e16bef023c836735f4"
},
"sqlalchemy": {
"hashes": [
"sha256:128bc917ed20d78143a45024455ff0aed7d3b96772eba13d5dbaf9cc57e5c41b",
"sha256:156a27548ba4e1fed944ff9fcdc150633e61d350d673ae7baaf6c25c04ac1f71",
"sha256:27e2efc8f77661c9af2681755974205e7462f1ae126f498f4fe12a8b24761d15",
"sha256:2a12f8be25b9ea3d1d5b165202181f2b7da4b3395289000284e5bb86154ce87c",
"sha256:31c043d5211aa0e0773821fcc318eb5cbe2ec916dfbc4c6eea0c5188971988eb",
"sha256:65eb3b03229f684af0cf0ad3bcc771970c1260a82a791a8d07bffb63d8c95bcc",
"sha256:6cd157ce74a911325e164441ff2d9b4e244659a25b3146310518d83202f15f7a",
"sha256:703c002277f0fbc3c04d0ae4989a174753a7554b2963c584ce2ec0cddcf2bc53",
"sha256:869bbb637de58ab0a912b7f20e9192132f9fbc47fc6b5111cd1e0f6cdf5cf9b0",
"sha256:8a0e0cd21da047ea10267c37caf12add400a92f0620c8bc09e4a6531a765d6d7",
"sha256:8d01e949a5d22e5c4800d59b50617c56125fc187fbeb8fa423e99858546de616",
"sha256:925b4fe5e7c03ed76912b75a9a41dfd682d59c0be43bce88d3b27f7f5ba028fb",
"sha256:9cb1819008f0225a7c066cac8bb0cf90847b2c4a6eb9ebb7431dbd00c56c06c5",
"sha256:a87d496884f40c94c85a647c385f4fd5887941d2609f71043e2b73f2436d9c65",
"sha256:a9030cd30caf848a13a192c5e45367e3c6f363726569a56e75dc1151ee26d859",
"sha256:a9e75e49a0f1583eee0ce93270232b8e7bb4b1edc89cc70b07600d525aef4f43",
"sha256:b50f45d0e82b4562f59f0e0ca511f65e412f2a97d790eea5f60e34e5f1aabc9a",
"sha256:b7878e59ec31f12d54b3797689402ee3b5cfcb5598f2ebf26491732758751908",
"sha256:ce1ddaadee913543ff0154021d31b134551f63428065168e756d90bdc4c686f5",
"sha256:ce2646e4c0807f3461be0653502bb48c6e91a5171d6e450367082c79e12868bf",
"sha256:ce6c3d18b2a8ce364013d47b9cad71db815df31d55918403f8db7d890c9d07ae",
"sha256:e4e2664232005bd306f878b0f167a31f944a07c4de0152c444f8c61bbe3cfb38",
"sha256:e8aa395482728de8bdcca9cc0faf3765ab483e81e01923aaa736b42f0294f570",
"sha256:eb4fcf7105bf071c71068c6eee47499ab8d4b8f5a11fc35147c934f0faa60f23",
"sha256:ed375a79f06cad285166e5be74745df1ed6845c5624aafadec4b7a29c25866ef",
"sha256:f35248f7e0d63b234a109dd72fbfb4b5cb6cb6840b221d0df0ecbf54ab087654",
"sha256:f502ef245c492b391e0e23e94cba030ab91722dcc56963c85bfd7f3441ea2bbe",
"sha256:fe01bac7226499aedf472c62fa3b85b2c619365f3f14dd222ffe4f3aa91e5f98"
"sha256:0942a3a0df3f6131580eddd26d99071b48cfe5aaf3eab2783076fbc5a1c1882e",
"sha256:0ec575db1b54909750332c2e335c2bb11257883914a03bc5a3306a4488ecc772",
"sha256:109581ccc8915001e8037b73c29590e78ce74be49ca0a3630a23831f9e3ed6c7",
"sha256:16593fd748944726540cd20f7e83afec816c2ac96b082e26ae226e8f7e9688cf",
"sha256:427273b08efc16a85aa2b39892817e78e3ed074fcb89b2a51c4979bae7e7ba98",
"sha256:50c4ee32f0e1581828843267d8de35c3298e86ceecd5e9017dc45788be70a864",
"sha256:512a85c3c8c3995cc91af3e90f38f460da5d3cade8dc3a229c8e0879037547c9",
"sha256:57aa843b783179ab72e863512e14bdcba186641daf69e4e3a5761d705dcc35b1",
"sha256:621f58cd921cd71ba6215c42954ffaa8a918eecd8c535d97befa1a8acad986dd",
"sha256:6ac2558631a81b85e7fb7a44e5035347938b0a73f5fdc27a8566777d0792a6a4",
"sha256:716754d0b5490bdcf68e1e4925edc02ac07209883314ad01a137642ddb2056f1",
"sha256:736d41cfebedecc6f159fc4ac0769dc89528a989471dc1d378ba07d29a60ba1c",
"sha256:8619b86cb68b185a778635be5b3e6018623c0761dde4df2f112896424aa27bd8",
"sha256:87fad64529cde4f1914a5b9c383628e1a8f9e3930304c09cf22c2ae118a1280e",
"sha256:89494df7f93b1836cae210c42864b292f9b31eeabca4810193761990dc689cce",
"sha256:8cac7bb373a5f1423e28de3fd5fc8063b9c8ffe8957dc1b1a59cb90453db6da1",
"sha256:8fd452dc3d49b3cc54483e033de6c006c304432e6f84b74d7b2c68afa2569ae5",
"sha256:adad60eea2c4c2a1875eb6305a0b6e61a83163f8e233586a4d6a55221ef984fe",
"sha256:c26f95e7609b821b5f08a72dab929baa0d685406b953efd7c89423a511d5c413",
"sha256:cbe1324ef52ff26ccde2cb84b8593c8bf930069dfc06c1e616f1bfd4e47f48a3",
"sha256:d05c4adae06bd0c7f696ae3ec8d993ed8ffcc4e11a76b1b35a5af8a099bd2284",
"sha256:d98bc827a1293ae767c8f2f18be3bb5151fd37ddcd7da2a5f9581baeeb7a3fa1",
"sha256:da2fb75f64792c1fc64c82313a00c728a7c301efe6a60b7a9fe35b16b4368ce7",
"sha256:e4624d7edb2576cd72bb83636cd71c8ce544d8e272f308bd80885056972ca299",
"sha256:e89e0d9e106f8a9180a4ca92a6adde60c58b1b0299e1b43bd5e0312f535fbf33",
"sha256:f11c2437fb5f812d020932119ba02d9e2bc29a6eca01a055233a8b449e3e1e7d",
"sha256:f57be5673e12763dd400fea568608700a63ce1c6bd5bdbc3cc3a2c5fdb045274",
"sha256:fc728ece3d5c772c196fd338a99798e7efac7a04f9cb6416299a3638ee9a94cd"
],
"version": "==1.3.17"
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.3.18"
},
"swagger-ui-bundle": {
"hashes": [
@ -871,6 +976,7 @@
"sha256:3018294ebefce6572a474f0604c2021e33b3fd8006ecd11d62107a5d2a963527",
"sha256:88206b0eb87e6d677d424843ac5209e3fb9d0190d0ee169599165ec25e9d9115"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
"version": "==1.25.9"
},
"vine": {
@ -878,6 +984,7 @@
"sha256:133ee6d7a9016f177ddeaf191c1f58421a1dcc6ee9a42c58b34bed40e1d2cd87",
"sha256:ea4947cc56d1fd6f2095c8d543ee25dad966f78692528e68b4fada11ba3f98af"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.3.0"
},
"waitress": {
@ -885,6 +992,7 @@
"sha256:1bb436508a7487ac6cb097ae7a7fe5413aefca610550baf58f0940e51ecfb261",
"sha256:3d633e78149eb83b60a07dfabb35579c29aac2d24bb803c18b26fb2ab1a584db"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==1.4.4"
},
"webob": {
@ -892,6 +1000,7 @@
"sha256:a3c89a8e9ba0aeb17382836cdb73c516d0ecf6630ec40ec28288f3ed459ce87b",
"sha256:aa3a917ed752ba3e0b242234b2a373f9c4e2a75d35291dcbe977649bd21fd108"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.8.6"
},
"webtest": {
@ -938,6 +1047,7 @@
"sha256:aa36550ff0c0b7ef7fa639055d797116ee891440eac1a56f378e2d3179e0320b",
"sha256:c599e4d75c98f6798c509911d08a22e6c021d074469042177c8c86fb92eefd96"
],
"markers": "python_version >= '3.6'",
"version": "==3.1.0"
}
},
@ -947,6 +1057,7 @@
"sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c",
"sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==19.3.0"
},
"coverage": {
@ -999,6 +1110,7 @@
"sha256:68c70cc7167bdf5c7c9d8f6954a7837089c6a36bf565383919bb595efb8a17e5",
"sha256:b78134b2063dd214000685165d81c154522c3ee0a1c0d4d113c80361c234c5a2"
],
"markers": "python_version >= '3.5'",
"version": "==8.4.0"
},
"packaging": {
@ -1006,6 +1118,7 @@
"sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8",
"sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==20.4"
},
"pbr": {
@ -1021,20 +1134,23 @@
"sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0",
"sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==0.13.1"
},
"py": {
"hashes": [
"sha256:a673fa23d7000440cc885c17dbd34fafcb7d7a6e230b29f6766400de36a33c44",
"sha256:f3b3a4c36512a4c4f024041ab51866f11761cc169670204b235f6b20523d4e6b"
"sha256:366389d1db726cd2fcfc79732e75410e5fe4d31db13692115529d34069a043c2",
"sha256:9ca6883ce56b4e8da7e79ac18787889fa5206c79dcc67fb065376cd2fe03f342"
],
"version": "==1.8.2"
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.9.0"
},
"pyparsing": {
"hashes": [
"sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1",
"sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"
],
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.4.7"
},
"pytest": {
@ -1050,6 +1166,7 @@
"sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259",
"sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.15.0"
},
"wcwidth": {
@ -1064,6 +1181,7 @@
"sha256:aa36550ff0c0b7ef7fa639055d797116ee891440eac1a56f378e2d3179e0320b",
"sha256:c599e4d75c98f6798c509911d08a22e6c021d074469042177c8c86fb92eefd96"
],
"markers": "python_version >= '3.6'",
"version": "==3.1.0"
}
}

View File

@ -626,6 +626,12 @@ paths:
schema:
type: string
format: uuid
- name: terminate_loop
in: query
required: false
description: Terminate the loop on a looping task
schema:
type: boolean
put:
operationId: crc.api.workflow.update_task
summary: Exclusively for User Tasks, submits form data as a flat set of key/values.

View File

@ -129,7 +129,7 @@ def set_current_task(workflow_id, task_id):
return WorkflowApiSchema().dump(workflow_api_model)
def update_task(workflow_id, task_id, body):
def update_task(workflow_id, task_id, body, terminate_loop=None):
workflow_model = session.query(WorkflowModel).filter_by(id=workflow_id).first()
if workflow_model is None:
@ -145,8 +145,10 @@ def update_task(workflow_id, task_id, body):
if spiff_task.state != spiff_task.READY:
raise ApiError("invalid_state", "You may not update a task unless it is in the READY state. "
"Consider calling a token reset to make this task Ready.")
if body: # IF and only if we get the body back, update the task data with the content.
spiff_task.data = body # Accept the data from the front end as complete. Do not merge it in, as then it is impossible to remove items.
if terminate_loop:
spiff_task.terminate_loop()
spiff_task.update_data(body)
processor.complete_task(spiff_task)
processor.do_engine_steps()
processor.save()

View File

@ -3,7 +3,7 @@ import json
import os
from datetime import datetime
from uuid import UUID
from xml.etree import ElementTree
from lxml import etree
import flask
from SpiffWorkflow.bpmn.parser.ValidationException import ValidationException
@ -151,7 +151,7 @@ class FileService(object):
# If this is a BPMN, extract the process id.
if file_model.type == FileType.bpmn:
bpmn: ElementTree.Element = ElementTree.fromstring(binary_data)
bpmn: etree.Element = etree.fromstring(binary_data)
file_model.primary_process_id = FileService.get_process_id(bpmn)
new_file_data_model = FileDataModel(
@ -165,7 +165,7 @@ class FileService(object):
return file_model
@staticmethod
def get_process_id(et_root: ElementTree.Element):
def get_process_id(et_root: etree.Element):
process_elements = []
for child in et_root:
if child.tag.endswith('process') and child.attrib.get('isExecutable', False):
@ -179,7 +179,7 @@ class FileService(object):
# Look for the element that has the startEvent in it
for e in process_elements:
this_element: ElementTree.Element = e
this_element: etree.Element = e
for child_element in list(this_element):
if child_element.tag.endswith('startEvent'):
return this_element.attrib['id']

View File

@ -1,6 +1,6 @@
import re
from lxml import etree
import shlex
import xml.etree.ElementTree as ElementTree
from datetime import datetime
from typing import List
@ -14,7 +14,6 @@ from SpiffWorkflow.camunda.parser.CamundaParser import CamundaParser
from SpiffWorkflow.dmn.parser.BpmnDmnParser import BpmnDmnParser
from SpiffWorkflow.exceptions import WorkflowTaskExecException
from SpiffWorkflow.specs import WorkflowSpec
from sqlalchemy import desc
from crc import session
from crc.api.common import ApiError
@ -269,12 +268,12 @@ class WorkflowProcessor(object):
for file_data in file_data_models:
if file_data.file_model.type == FileType.bpmn:
bpmn: ElementTree.Element = ElementTree.fromstring(file_data.data)
bpmn: etree.Element = etree.fromstring(file_data.data)
if file_data.file_model.primary:
process_id = FileService.get_process_id(bpmn)
parser.add_bpmn_xml(bpmn, filename=file_data.file_model.name)
elif file_data.file_model.type == FileType.dmn:
dmn: ElementTree.Element = ElementTree.fromstring(file_data.data)
dmn: etree.Element = etree.fromstring(file_data.data)
parser.add_dmn_xml(dmn, filename=file_data.file_model.name)
if process_id is None:
raise (ApiError(code="no_primary_bpmn_error",

View File

@ -240,20 +240,24 @@ class WorkflowService(object):
return workflow_api
@staticmethod
def get_previously_submitted_data(workflow_id, task):
def get_previously_submitted_data(workflow_id, spiff_task):
""" If the user has completed this task previously, find the form data for the last submission."""
latest_event = db.session.query(TaskEventModel) \
query = db.session.query(TaskEventModel) \
.filter_by(workflow_id=workflow_id) \
.filter_by(task_name=task.task_spec.name) \
.filter_by(action=WorkflowService.TASK_ACTION_COMPLETE) \
.order_by(TaskEventModel.date.desc()).first()
.filter_by(task_name=spiff_task.task_spec.name) \
.filter_by(action=WorkflowService.TASK_ACTION_COMPLETE)
if hasattr(spiff_task, 'internal_data') and 'runtimes' in spiff_task.internal_data:
query = query.filter_by(mi_index=spiff_task.internal_data['runtimes'])
latest_event = query.order_by(TaskEventModel.date.desc()).first()
if latest_event:
if latest_event.form_data is not None:
return latest_event.form_data
else:
app.logger.error("missing_form_dat", "We have lost data for workflow %i, task %s, it is not "
"in the task event model, "
"and it should be." % (workflow_id, task.task_spec.name))
app.logger.error("missing_form_data", "We have lost data for workflow %i, "
"task %s, it is not in the task event model, "
"and it should be." % (workflow_id, spiff_task.task_spec.name))
return {}
else:
return {}
@ -290,8 +294,8 @@ class WorkflowService(object):
props = {}
if hasattr(spiff_task.task_spec, 'extensions'):
for id, val in spiff_task.task_spec.extensions.items():
props[id] = val
for key, val in spiff_task.task_spec.extensions.items():
props[key] = val
task = Task(spiff_task.id,
spiff_task.task_spec.name,

View File

@ -290,7 +290,7 @@ class BaseTest(unittest.TestCase):
self.assertEqual(workflow.workflow_spec_id, workflow_api.workflow_spec_id)
return workflow_api
def complete_form(self, workflow_in, task_in, dict_data, error_code=None, user_uid="dhf8r"):
def complete_form(self, workflow_in, task_in, dict_data, error_code=None, terminate_loop=None, user_uid="dhf8r"):
prev_completed_task_count = workflow_in.completed_tasks
if isinstance(task_in, dict):
task_id = task_in["id"]
@ -299,7 +299,12 @@ class BaseTest(unittest.TestCase):
user = session.query(UserModel).filter_by(uid=user_uid).first()
self.assertIsNotNone(user)
if terminate_loop:
rv = self.app.put('/v1.0/workflow/%i/task/%s/data?terminate_loop=true' % (workflow_in.id, task_id),
headers=self.logged_in_headers(user=user),
content_type="application/json",
data=json.dumps(dict_data))
else:
rv = self.app.put('/v1.0/workflow/%i/task/%s/data' % (workflow_in.id, task_id),
headers=self.logged_in_headers(user=user),
content_type="application/json",
@ -316,6 +321,8 @@ class BaseTest(unittest.TestCase):
# The total number of tasks may change over time, as users move through gateways
# branches may be pruned. As we hit parallel Multi-Instance new tasks may be created...
self.assertIsNotNone(workflow.total_tasks)
# presumably, we also need to deal with sequential items here too . .
if not task_in.multi_instance_type == 'looping':
self.assertEqual(prev_completed_task_count + 1, workflow.completed_tasks)
# Assure a record exists in the Task Events
@ -335,6 +342,7 @@ class BaseTest(unittest.TestCase):
self.assertEqual(task_in.name, event.task_name)
self.assertEqual(task_in.title, event.task_title)
self.assertEqual(task_in.type, event.task_type)
if not task_in.multi_instance_type == 'looping':
self.assertEqual("COMPLETED", event.task_state)
# Not sure what voodoo is happening inside of marshmallow to get me in this state.
@ -344,6 +352,9 @@ class BaseTest(unittest.TestCase):
self.assertEqual(task_in.multi_instance_type, event.mi_type)
self.assertEqual(task_in.multi_instance_count, event.mi_count)
if task_in.multi_instance_type == 'looping' and not terminate_loop:
self.assertEqual(task_in.multi_instance_index+1, event.mi_index)
else:
self.assertEqual(task_in.multi_instance_index, event.mi_index)
self.assertEqual(task_in.process_name, event.process_name)
self.assertIsNotNone(event.date)

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="Definitions_1v9xfjq" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="3.7.0">
<bpmn:process id="looping_task" isExecutable="true">
<bpmn:startEvent id="StartEvent_1">
<bpmn:outgoing>Flow_0vlor2k</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:userTask id="GetNames" name="Get Names" camunda:formKey="GetNamesForm">
<bpmn:extensionElements>
<camunda:formData>
<camunda:formField id="GetNames_CurrentVar.Name" type="string" />
<camunda:formField id="GetNames_CurrentVar.Nickname" type="string" />
</camunda:formData>
</bpmn:extensionElements>
<bpmn:incoming>Flow_0vlor2k</bpmn:incoming>
<bpmn:outgoing>Flow_1tvod7v</bpmn:outgoing>
<bpmn:standardLoopCharacteristics />
</bpmn:userTask>
<bpmn:endEvent id="Event_End">
<bpmn:incoming>Flow_1tvod7v</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="Flow_1tvod7v" sourceRef="GetNames" targetRef="Event_End" />
<bpmn:sequenceFlow id="Flow_0vlor2k" sourceRef="StartEvent_1" targetRef="GetNames" />
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="looping_task">
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
<dc:Bounds x="179" y="99" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_1mpvzb7_di" bpmnElement="GetNames">
<dc:Bounds x="250" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_0dhdik5_di" bpmnElement="Event_End">
<dc:Bounds x="402" y="99" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Flow_1tvod7v_di" bpmnElement="Flow_1tvod7v">
<di:waypoint x="350" y="117" />
<di:waypoint x="402" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0vlor2k_di" bpmnElement="Flow_0vlor2k">
<di:waypoint x="215" y="117" />
<di:waypoint x="250" y="117" />
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>

View File

@ -8,8 +8,8 @@
<bpmn:endEvent id="Event_End" name="Event_End">
<bpmn:incoming>Flow_0ugjw69</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="Flow_0ugjw69" sourceRef="MutiInstanceTask" targetRef="Event_End" />
<bpmn:userTask id="MutiInstanceTask" name="Gather more information" camunda:formKey="GetEmail">
<bpmn:sequenceFlow id="Flow_0ugjw69" sourceRef="MultiInstanceTask" targetRef="Event_End" />
<bpmn:userTask id="MultiInstanceTask" name="Gather more information" camunda:formKey="GetEmail">
<bpmn:documentation># Please provide addtional information about:
## Investigator ID: {{investigator.NETBADGEID}}
## Role: {{investigator.INVESTIGATORTYPEFULL}}</bpmn:documentation>
@ -25,7 +25,7 @@
<bpmn:outgoing>Flow_0ugjw69</bpmn:outgoing>
<bpmn:multiInstanceLoopCharacteristics isSequential="true" camunda:collection="StudyInfo.investigators" camunda:elementVariable="investigator" />
</bpmn:userTask>
<bpmn:sequenceFlow id="SequenceFlow_1p568pp" sourceRef="Task_1v0e2zu" targetRef="MutiInstanceTask" />
<bpmn:sequenceFlow id="SequenceFlow_1p568pp" sourceRef="Task_1v0e2zu" targetRef="MultiInstanceTask" />
<bpmn:scriptTask id="Task_1v0e2zu" name="Load Personnel">
<bpmn:incoming>Flow_0t6p1sb</bpmn:incoming>
<bpmn:outgoing>SequenceFlow_1p568pp</bpmn:outgoing>
@ -58,7 +58,7 @@
<dc:Bounds x="585" y="142" width="54" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_1iyilui_di" bpmnElement="MutiInstanceTask">
<bpmndi:BPMNShape id="Activity_1iyilui_di" bpmnElement="MultiInstanceTask">
<dc:Bounds x="430" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="ScriptTask_0cbbirp_di" bpmnElement="Task_1v0e2zu">

View File

@ -8,8 +8,8 @@
<bpmn:endEvent id="Event_End" name="Event_End">
<bpmn:incoming>Flow_0ugjw69</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="Flow_0ugjw69" sourceRef="MutiInstanceTask" targetRef="Event_End" />
<bpmn:userTask id="MutiInstanceTask" name="Gather more information" camunda:formKey="GetEmail">
<bpmn:sequenceFlow id="Flow_0ugjw69" sourceRef="MultiInstanceTask" targetRef="Event_End" />
<bpmn:userTask id="MultiInstanceTask" name="Gather more information" camunda:formKey="GetEmail">
<bpmn:documentation># Please provide addtional information about:
## Investigator ID: {{investigator.user_id}}
## Role: {{investigator.type_full}}</bpmn:documentation>
@ -22,7 +22,7 @@
<bpmn:outgoing>Flow_0ugjw69</bpmn:outgoing>
<bpmn:multiInstanceLoopCharacteristics camunda:collection="StudyInfo.investigators" camunda:elementVariable="investigator" />
</bpmn:userTask>
<bpmn:sequenceFlow id="SequenceFlow_1p568pp" sourceRef="Task_1v0e2zu" targetRef="MutiInstanceTask" />
<bpmn:sequenceFlow id="SequenceFlow_1p568pp" sourceRef="Task_1v0e2zu" targetRef="MultiInstanceTask" />
<bpmn:scriptTask id="Task_1v0e2zu" name="Load Personnel">
<bpmn:incoming>Flow_0t6p1sb</bpmn:incoming>
<bpmn:outgoing>SequenceFlow_1p568pp</bpmn:outgoing>
@ -55,7 +55,7 @@
<dc:Bounds x="575" y="142" width="54" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_1iyilui_di" bpmnElement="MutiInstanceTask">
<bpmndi:BPMNShape id="Activity_1iyilui_di" bpmnElement="MultiInstanceTask">
<dc:Bounds x="410" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="ScriptTask_0cbbirp_di" bpmnElement="Task_1v0e2zu">

View File

@ -0,0 +1,54 @@
from unittest.mock import patch
from crc import session
from crc.models.api_models import MultiInstanceType
from crc.models.study import StudyModel
from crc.models.workflow import WorkflowStatus
from crc.services.study_service import StudyService
from crc.services.workflow_processor import WorkflowProcessor
from crc.services.workflow_service import WorkflowService
from tests.base_test import BaseTest
class TestWorkflowProcessorLoopingTask(BaseTest):
"""Tests the Workflow Processor as it deals with a Looping task"""
def _populate_form_with_random_data(self, task):
api_task = WorkflowService.spiff_task_to_api_task(task, add_docs_and_forms=True)
WorkflowService.populate_form_with_random_data(task, api_task, required_only=False)
def get_processor(self, study_model, spec_model):
workflow_model = StudyService._create_workflow_model(study_model, spec_model)
return WorkflowProcessor(workflow_model)
def test_create_and_complete_workflow(self):
# This depends on getting a list of investigators back from the protocol builder.
workflow = self.create_workflow('looping_task')
task = self.get_workflow_api(workflow).next_task
self.assertEqual("GetNames", task.name)
self.assertEqual(task.multi_instance_type, 'looping')
self.assertEqual(1, task.multi_instance_index)
self.complete_form(workflow,task,{'GetNames_CurrentVar':{'Name': 'Peter Norvig', 'Nickname': 'Pete'}})
task = self.get_workflow_api(workflow).next_task
self.assertEqual(task.multi_instance_type,'looping')
self.assertEqual(2, task.multi_instance_index)
self.complete_form(workflow,
task,
{'GetNames_CurrentVar':{'Name': 'Stuart Russell', 'Nickname': 'Stu'}},
terminate_loop=True)
task = self.get_workflow_api(workflow).next_task
self.assertEqual(task.name,'Event_End')
self.assertEqual(workflow.completed_tasks,workflow.total_tasks)
self.assertEqual(task.data, {'GetNames_CurrentVar': 2,
'GetNames': {'1': {'Name': 'Peter Norvig',
'Nickname': 'Pete'},
'2': {'Name': 'Stuart Russell',
'Nickname': 'Stu'}}})

View File

@ -440,7 +440,7 @@ class TestTasksApi(BaseTest):
self.assertEqual(9, len(ready_items))
self.assertEqual("UserTask", workflow_api.next_task.type)
self.assertEqual("MutiInstanceTask",workflow_api.next_task.name)
self.assertEqual("MultiInstanceTask",workflow_api.next_task.name)
self.assertEqual("more information", workflow_api.next_task.title)
for i in random.sample(range(9), 9):

View File

@ -1,13 +1,13 @@
from unittest.mock import patch
from tests.base_test import BaseTest
from crc import session
from crc import session, db
from crc.models.api_models import MultiInstanceType
from crc.models.study import StudyModel
from crc.models.workflow import WorkflowStatus
from crc.models.workflow import WorkflowStatus, WorkflowModel
from crc.services.study_service import StudyService
from crc.services.workflow_processor import WorkflowProcessor
from crc.services.workflow_service import WorkflowService
from tests.base_test import BaseTest
class TestWorkflowProcessorMultiInstance(BaseTest):
@ -32,7 +32,7 @@ class TestWorkflowProcessorMultiInstance(BaseTest):
'error': 'Unable to locate a user with id asd3v in LDAP'}}
def _populate_form_with_random_data(self, task):
WorkflowProcessor.populate_form_with_random_data(task)
WorkflowService.populate_form_with_random_data(task)
def get_processor(self, study_model, spec_model):
workflow_model = StudyService._create_workflow_model(study_model, spec_model)
@ -51,51 +51,72 @@ class TestWorkflowProcessorMultiInstance(BaseTest):
self.assertIsNotNone(processor)
self.assertEqual(WorkflowStatus.user_input_required, processor.get_status())
processor.bpmn_workflow.do_engine_steps()
next_user_tasks = processor.next_user_tasks()
self.assertEqual(1, len(next_user_tasks))
task = next_user_tasks[0]
workflow_api = WorkflowService.processor_to_workflow_api(processor)
self.assertIsNotNone(workflow_api)
self.assertIsNotNone(workflow_api.next_task)
# 1st investigator
api_task = workflow_api.next_task
self.assertEqual(WorkflowStatus.user_input_required, processor.get_status())
self.assertEqual("dhf8r", task.data["investigator"]["user_id"])
self.assertEqual("MutiInstanceTask", task.get_name())
api_task = WorkflowService.spiff_task_to_api_task(task)
self.assertEqual(MultiInstanceType.sequential, api_task.multi_instance_type)
self.assertEqual("dhf8r", api_task.data["investigator"]["user_id"])
self.assertEqual("MultiInstanceTask", api_task.name)
self.assertEqual(3, api_task.multi_instance_count)
self.assertEqual(1, api_task.multi_instance_index)
task = processor.get_current_user_tasks()[0]
self.assertEqual(task.id, api_task.id)
task.update_data({"investigator": {"email": "asd3v@virginia.edu"}})
processor.complete_task(task)
processor.do_engine_steps()
workflow_api = WorkflowService.processor_to_workflow_api(processor)
task = next_user_tasks[0]
api_task = WorkflowService.spiff_task_to_api_task(task)
self.assertEqual("MutiInstanceTask", api_task.name)
task.update_data({"investigator":{"email":"asdf32@virginia.edu"}})
# 2nd investigator
api_task = workflow_api.next_task
self.assertEqual(WorkflowStatus.user_input_required, processor.get_status())
self.assertEqual(None, api_task.data["investigator"]["user_id"])
self.assertEqual("MultiInstanceTask", api_task.name)
self.assertEqual(3, api_task.multi_instance_count)
self.assertEqual(2, api_task.multi_instance_index)
task = processor.get_current_user_tasks()[0]
self.assertEqual(task.id, api_task.id)
task.update_data({"investigator": {"email": "asdf32@virginia.edu"}})
processor.complete_task(task)
processor.do_engine_steps()
workflow_api = WorkflowService.processor_to_workflow_api(processor)
task = next_user_tasks[0]
api_task = WorkflowService.spiff_task_to_api_task(task)
self.assertEqual("MutiInstanceTask", task.get_name())
task.update_data({"investigator":{"email":"dhf8r@virginia.edu"}})
# 3rd investigator
api_task = workflow_api.next_task
self.assertEqual(WorkflowStatus.user_input_required, processor.get_status())
self.assertEqual("asd3v", api_task.data["investigator"]["user_id"])
self.assertEqual("MultiInstanceTask", api_task.name)
self.assertEqual(3, api_task.multi_instance_count)
self.assertEqual(3, api_task.multi_instance_index)
task = processor.get_current_user_tasks()[0]
self.assertEqual(task.id, api_task.id)
task.update_data({"investigator": {"email": "dhf8r@virginia.edu"}})
processor.complete_task(task)
processor.do_engine_steps()
task = processor.bpmn_workflow.last_task
workflow_api = WorkflowService.processor_to_workflow_api(processor)
# Last task
api_task = workflow_api.next_task
expected = self.mock_investigator_response
expected['PI']['email'] = "asd3v@virginia.edu"
expected['SC_I']['email'] = "asdf32@virginia.edu"
expected['DC']['email'] = "dhf8r@virginia.edu"
self.assertEqual(expected,
task.data['StudyInfo']['investigators'])
self.assertEqual(expected, api_task.data['StudyInfo']['investigators'])
self.assertEqual(WorkflowStatus.complete, processor.get_status())
def refresh_processor(self, processor):
"""Saves the processor, and returns a new one read in from the database"""
processor.save()
processor = WorkflowProcessor(processor.workflow_model)
return processor
@patch('crc.services.study_service.StudyService.get_investigators')
def test_create_and_complete_workflow_parallel(self, mock_study_service):
"""Unlike the test above, the parallel task allows us to complete the items in any order."""
@ -107,11 +128,15 @@ class TestWorkflowProcessorMultiInstance(BaseTest):
workflow_spec_model = self.load_test_spec("multi_instance_parallel")
study = session.query(StudyModel).first()
processor = self.get_processor(study, workflow_spec_model)
processor = self.refresh_processor(processor)
processor.bpmn_workflow.do_engine_steps()
# In the Parallel instance, there should be three tasks, all of them in the ready state.
next_user_tasks = processor.next_user_tasks()
self.assertEqual(3, len(next_user_tasks))
# There should be six tasks in the navigation: start event, the script task, end event, and three tasks
# for the three executions of hte multi-instance.
self.assertEquals(6, len(processor.bpmn_workflow.get_nav_list()))
# We can complete the tasks out of order.
task = next_user_tasks[2]
@ -124,20 +149,23 @@ class TestWorkflowProcessorMultiInstance(BaseTest):
task.update_data({"investigator": {"email": "dhf8r@virginia.edu"}})
processor.complete_task(task)
processor.do_engine_steps()
self.assertEquals(6, len(processor.bpmn_workflow.get_nav_list()))
task = next_user_tasks[0]
api_task = WorkflowService.spiff_task_to_api_task(task)
self.assertEqual("MutiInstanceTask", api_task.name)
self.assertEqual("MultiInstanceTask", api_task.name)
task.update_data({"investigator":{"email":"asd3v@virginia.edu"}})
processor.complete_task(task)
processor.do_engine_steps()
self.assertEquals(6, len(processor.bpmn_workflow.get_nav_list()))
task = next_user_tasks[1]
api_task = WorkflowService.spiff_task_to_api_task(task)
self.assertEqual("MutiInstanceTask", task.get_name())
self.assertEqual("MultiInstanceTask", task.get_name())
task.update_data({"investigator":{"email":"asdf32@virginia.edu"}})
processor.complete_task(task)
processor.do_engine_steps()
self.assertEquals(6, len(processor.bpmn_workflow.get_nav_list()))
# Completing the tasks out of order, still provides the correct information.
expected = self.mock_investigator_response
@ -148,3 +176,4 @@ class TestWorkflowProcessorMultiInstance(BaseTest):
task.data['StudyInfo']['investigators'])
self.assertEqual(WorkflowStatus.complete, processor.get_status())
self.assertEquals(6, len(processor.bpmn_workflow.get_nav_list()))