mirror of
https://github.com/sartography/cr-connect-workflow.git
synced 2025-02-20 11:48:16 +00:00
Merge branch 'dev' into 152-workflow-spec-pull
This commit is contained in:
commit
5921ce9f10
398
Pipfile.lock
generated
398
Pipfile.lock
generated
@ -33,10 +33,10 @@
|
||||
},
|
||||
"aniso8601": {
|
||||
"hashes": [
|
||||
"sha256:529dcb1f5f26ee0df6c0a1ee84b7b27197c3c50fc3a6321d66c544689237d072",
|
||||
"sha256:c033f63d028b9a58e3ab0c2c7d0532ab4bfa7452bfc788fbfe3ddabd327b181a"
|
||||
"sha256:246bf8d3611527030889e6df970878969d3a2f760ba3eb694fa1fb10e6ce53f9",
|
||||
"sha256:51047d4fb51d7b8afd522b70f2d21a1b2487cbb7f7bd84ea852e9aa7808e7704"
|
||||
],
|
||||
"version": "==8.0.0"
|
||||
"version": "==8.1.0"
|
||||
},
|
||||
"attrs": {
|
||||
"hashes": [
|
||||
@ -80,10 +80,10 @@
|
||||
},
|
||||
"certifi": {
|
||||
"hashes": [
|
||||
"sha256:1f422849db327d534e3d0c5f02a263458c3955ec0aae4ff09b95f195c59f4edd",
|
||||
"sha256:f05def092c44fbf25834a51509ef6e631dc19765ab8a57b4e7ab85531f0a9cf4"
|
||||
"sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c",
|
||||
"sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"
|
||||
],
|
||||
"version": "==2020.11.8"
|
||||
"version": "==2020.12.5"
|
||||
},
|
||||
"cffi": {
|
||||
"hashes": [
|
||||
@ -108,12 +108,14 @@
|
||||
"sha256:9cc46bc107224ff5b6d04369e7c595acb700c3613ad7bcf2e2012f62ece80c35",
|
||||
"sha256:9f7a31251289b2ab6d4012f6e83e58bc3b96bd151f5b5262467f4bb6b34a7c26",
|
||||
"sha256:9ffb888f19d54a4d4dfd4b3f29bc2c16aa4972f1c2ab9c4ab09b8ab8685b9c2b",
|
||||
"sha256:a5ed8c05548b54b998b9498753fb9cadbfd92ee88e884641377d8a8b291bcc01",
|
||||
"sha256:a7711edca4dcef1a75257b50a2fbfe92a65187c47dab5a0f1b9b332c5919a3fb",
|
||||
"sha256:af5c59122a011049aad5dd87424b8e65a80e4a6477419c0c1015f73fb5ea0293",
|
||||
"sha256:b18e0a9ef57d2b41f5c68beefa32317d286c3d6ac0484efd10d6e07491bb95dd",
|
||||
"sha256:b4e248d1087abf9f4c10f3c398896c87ce82a9856494a7155823eb45a892395d",
|
||||
"sha256:ba4e9e0ae13fc41c6b23299545e5ef73055213e466bd107953e4a013a5ddd7e3",
|
||||
"sha256:c6332685306b6417a91b1ff9fae889b3ba65c2292d64bd9245c093b1b284809d",
|
||||
"sha256:d5ff0621c88ce83a28a10d2ce719b2ee85635e85c515f12bac99a95306da4b2e",
|
||||
"sha256:d9efd8b7a3ef378dd61a1e77367f1924375befc2eba06168b6ebfa903a5e59ca",
|
||||
"sha256:df5169c4396adc04f9b0a05f13c074df878b6052430e03f50e68adf3a57aa28d",
|
||||
"sha256:ebb253464a5d0482b191274f1c8bf00e33f7e0b9c66405fbffc61ed2c839c775",
|
||||
@ -126,10 +128,10 @@
|
||||
},
|
||||
"chardet": {
|
||||
"hashes": [
|
||||
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
|
||||
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
|
||||
"sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa",
|
||||
"sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"
|
||||
],
|
||||
"version": "==3.0.4"
|
||||
"version": "==4.0.0"
|
||||
},
|
||||
"click": {
|
||||
"hashes": [
|
||||
@ -165,43 +167,58 @@
|
||||
},
|
||||
"coverage": {
|
||||
"hashes": [
|
||||
"sha256:0203acd33d2298e19b57451ebb0bed0ab0c602e5cf5a818591b4918b1f97d516",
|
||||
"sha256:0f313707cdecd5cd3e217fc68c78a960b616604b559e9ea60cc16795c4304259",
|
||||
"sha256:1c6703094c81fa55b816f5ae542c6ffc625fec769f22b053adb42ad712d086c9",
|
||||
"sha256:1d44bb3a652fed01f1f2c10d5477956116e9b391320c94d36c6bf13b088a1097",
|
||||
"sha256:280baa8ec489c4f542f8940f9c4c2181f0306a8ee1a54eceba071a449fb870a0",
|
||||
"sha256:29a6272fec10623fcbe158fdf9abc7a5fa032048ac1d8631f14b50fbfc10d17f",
|
||||
"sha256:2b31f46bf7b31e6aa690d4c7a3d51bb262438c6dcb0d528adde446531d0d3bb7",
|
||||
"sha256:2d43af2be93ffbad25dd959899b5b809618a496926146ce98ee0b23683f8c51c",
|
||||
"sha256:381ead10b9b9af5f64646cd27107fb27b614ee7040bb1226f9c07ba96625cbb5",
|
||||
"sha256:47a11bdbd8ada9b7ee628596f9d97fbd3851bd9999d398e9436bd67376dbece7",
|
||||
"sha256:4d6a42744139a7fa5b46a264874a781e8694bb32f1d76d8137b68138686f1729",
|
||||
"sha256:50691e744714856f03a86df3e2bff847c2acede4c191f9a1da38f088df342978",
|
||||
"sha256:530cc8aaf11cc2ac7430f3614b04645662ef20c348dce4167c22d99bec3480e9",
|
||||
"sha256:582ddfbe712025448206a5bc45855d16c2e491c2dd102ee9a2841418ac1c629f",
|
||||
"sha256:63808c30b41f3bbf65e29f7280bf793c79f54fb807057de7e5238ffc7cc4d7b9",
|
||||
"sha256:71b69bd716698fa62cd97137d6f2fdf49f534decb23a2c6fc80813e8b7be6822",
|
||||
"sha256:7858847f2d84bf6e64c7f66498e851c54de8ea06a6f96a32a1d192d846734418",
|
||||
"sha256:78e93cc3571fd928a39c0b26767c986188a4118edc67bc0695bc7a284da22e82",
|
||||
"sha256:7f43286f13d91a34fadf61ae252a51a130223c52bfefb50310d5b2deb062cf0f",
|
||||
"sha256:86e9f8cd4b0cdd57b4ae71a9c186717daa4c5a99f3238a8723f416256e0b064d",
|
||||
"sha256:8f264ba2701b8c9f815b272ad568d555ef98dfe1576802ab3149c3629a9f2221",
|
||||
"sha256:9342dd70a1e151684727c9c91ea003b2fb33523bf19385d4554f7897ca0141d4",
|
||||
"sha256:9361de40701666b034c59ad9e317bae95c973b9ff92513dd0eced11c6adf2e21",
|
||||
"sha256:9669179786254a2e7e57f0ecf224e978471491d660aaca833f845b72a2df3709",
|
||||
"sha256:aac1ba0a253e17889550ddb1b60a2063f7474155465577caa2a3b131224cfd54",
|
||||
"sha256:aef72eae10b5e3116bac6957de1df4d75909fc76d1499a53fb6387434b6bcd8d",
|
||||
"sha256:bd3166bb3b111e76a4f8e2980fa1addf2920a4ca9b2b8ca36a3bc3dedc618270",
|
||||
"sha256:c1b78fb9700fc961f53386ad2fd86d87091e06ede5d118b8a50dea285a071c24",
|
||||
"sha256:c3888a051226e676e383de03bf49eb633cd39fc829516e5334e69b8d81aae751",
|
||||
"sha256:c5f17ad25d2c1286436761b462e22b5020d83316f8e8fcb5deb2b3151f8f1d3a",
|
||||
"sha256:c851b35fc078389bc16b915a0a7c1d5923e12e2c5aeec58c52f4aa8085ac8237",
|
||||
"sha256:cb7df71de0af56000115eafd000b867d1261f786b5eebd88a0ca6360cccfaca7",
|
||||
"sha256:cedb2f9e1f990918ea061f28a0f0077a07702e3819602d3507e2ff98c8d20636",
|
||||
"sha256:e8caf961e1b1a945db76f1b5fa9c91498d15f545ac0ababbe575cfab185d3bd8"
|
||||
"sha256:08b3ba72bd981531fd557f67beee376d6700fba183b167857038997ba30dd297",
|
||||
"sha256:2757fa64e11ec12220968f65d086b7a29b6583d16e9a544c889b22ba98555ef1",
|
||||
"sha256:3102bb2c206700a7d28181dbe04d66b30780cde1d1c02c5f3c165cf3d2489497",
|
||||
"sha256:3498b27d8236057def41de3585f317abae235dd3a11d33e01736ffedb2ef8606",
|
||||
"sha256:378ac77af41350a8c6b8801a66021b52da8a05fd77e578b7380e876c0ce4f528",
|
||||
"sha256:38f16b1317b8dd82df67ed5daa5f5e7c959e46579840d77a67a4ceb9cef0a50b",
|
||||
"sha256:3911c2ef96e5ddc748a3c8b4702c61986628bb719b8378bf1e4a6184bbd48fe4",
|
||||
"sha256:3a3c3f8863255f3c31db3889f8055989527173ef6192a283eb6f4db3c579d830",
|
||||
"sha256:3b14b1da110ea50c8bcbadc3b82c3933974dbeea1832e814aab93ca1163cd4c1",
|
||||
"sha256:535dc1e6e68fad5355f9984d5637c33badbdc987b0c0d303ee95a6c979c9516f",
|
||||
"sha256:6f61319e33222591f885c598e3e24f6a4be3533c1d70c19e0dc59e83a71ce27d",
|
||||
"sha256:723d22d324e7997a651478e9c5a3120a0ecbc9a7e94071f7e1954562a8806cf3",
|
||||
"sha256:76b2775dda7e78680d688daabcb485dc87cf5e3184a0b3e012e1d40e38527cc8",
|
||||
"sha256:782a5c7df9f91979a7a21792e09b34a658058896628217ae6362088b123c8500",
|
||||
"sha256:7e4d159021c2029b958b2363abec4a11db0ce8cd43abb0d9ce44284cb97217e7",
|
||||
"sha256:8dacc4073c359f40fcf73aede8428c35f84639baad7e1b46fce5ab7a8a7be4bb",
|
||||
"sha256:8f33d1156241c43755137288dea619105477961cfa7e47f48dbf96bc2c30720b",
|
||||
"sha256:8ffd4b204d7de77b5dd558cdff986a8274796a1e57813ed005b33fd97e29f059",
|
||||
"sha256:93a280c9eb736a0dcca19296f3c30c720cb41a71b1f9e617f341f0a8e791a69b",
|
||||
"sha256:9a4f66259bdd6964d8cf26142733c81fb562252db74ea367d9beb4f815478e72",
|
||||
"sha256:9a9d4ff06804920388aab69c5ea8a77525cf165356db70131616acd269e19b36",
|
||||
"sha256:a2070c5affdb3a5e751f24208c5c4f3d5f008fa04d28731416e023c93b275277",
|
||||
"sha256:a4857f7e2bc6921dbd487c5c88b84f5633de3e7d416c4dc0bb70256775551a6c",
|
||||
"sha256:a607ae05b6c96057ba86c811d9c43423f35e03874ffb03fbdcd45e0637e8b631",
|
||||
"sha256:a66ca3bdf21c653e47f726ca57f46ba7fc1f260ad99ba783acc3e58e3ebdb9ff",
|
||||
"sha256:ab110c48bc3d97b4d19af41865e14531f300b482da21783fdaacd159251890e8",
|
||||
"sha256:b239711e774c8eb910e9b1ac719f02f5ae4bf35fa0420f438cdc3a7e4e7dd6ec",
|
||||
"sha256:be0416074d7f253865bb67630cf7210cbc14eb05f4099cc0f82430135aaa7a3b",
|
||||
"sha256:c46643970dff9f5c976c6512fd35768c4a3819f01f61169d8cdac3f9290903b7",
|
||||
"sha256:c5ec71fd4a43b6d84ddb88c1df94572479d9a26ef3f150cef3dacefecf888105",
|
||||
"sha256:c6e5174f8ca585755988bc278c8bb5d02d9dc2e971591ef4a1baabdf2d99589b",
|
||||
"sha256:c89b558f8a9a5a6f2cfc923c304d49f0ce629c3bd85cb442ca258ec20366394c",
|
||||
"sha256:cc44e3545d908ecf3e5773266c487ad1877be718d9dc65fc7eb6e7d14960985b",
|
||||
"sha256:cc6f8246e74dd210d7e2b56c76ceaba1cc52b025cd75dbe96eb48791e0250e98",
|
||||
"sha256:cd556c79ad665faeae28020a0ab3bda6cd47d94bec48e36970719b0b86e4dcf4",
|
||||
"sha256:ce6f3a147b4b1a8b09aae48517ae91139b1b010c5f36423fa2b866a8b23df879",
|
||||
"sha256:ceb499d2b3d1d7b7ba23abe8bf26df5f06ba8c71127f188333dddcf356b4b63f",
|
||||
"sha256:cef06fb382557f66d81d804230c11ab292d94b840b3cb7bf4450778377b592f4",
|
||||
"sha256:e448f56cfeae7b1b3b5bcd99bb377cde7c4eb1970a525c770720a352bc4c8044",
|
||||
"sha256:e52d3d95df81c8f6b2a1685aabffadf2d2d9ad97203a40f8d61e51b70f191e4e",
|
||||
"sha256:ee2f1d1c223c3d2c24e3afbb2dd38be3f03b1a8d6a83ee3d9eb8c36a52bee899",
|
||||
"sha256:f2c6888eada180814b8583c3e793f3f343a692fc802546eed45f40a001b1169f",
|
||||
"sha256:f51dbba78d68a44e99d484ca8c8f604f17e957c1ca09c3ebc2c7e3bbd9ba0448",
|
||||
"sha256:f54de00baf200b4539a5a092a759f000b5f45fd226d6d25a76b0dff71177a714",
|
||||
"sha256:fa10fee7e32213f5c7b0d6428ea92e3a3fdd6d725590238a3f92c0de1c78b9d2",
|
||||
"sha256:fabeeb121735d47d8eab8671b6b031ce08514c86b7ad8f7d5490a7b6dcd6267d",
|
||||
"sha256:fac3c432851038b3e6afe086f777732bcf7f6ebbfd90951fa04ee53db6d0bcdd",
|
||||
"sha256:fda29412a66099af6d6de0baa6bd7c52674de177ec2ad2630ca264142d69c6c7",
|
||||
"sha256:ff1330e8bc996570221b450e2d539134baa9465f5cb98aff0e0f73f34172e0ae"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==5.3"
|
||||
"version": "==5.3.1"
|
||||
},
|
||||
"deprecated": {
|
||||
"hashes": [
|
||||
@ -469,11 +486,11 @@
|
||||
},
|
||||
"marshmallow": {
|
||||
"hashes": [
|
||||
"sha256:73facc37462dfc0b27f571bdaffbef7709e19f7a616beb3802ea425b07843f4e",
|
||||
"sha256:e26763201474b588d144dae9a32bdd945cd26a06c943bc746a6882e850475378"
|
||||
"sha256:4ab2fdb7f36eb61c3665da67a7ce281c8900db08d72ba6bf0e695828253581f7",
|
||||
"sha256:eca81d53aa4aafbc0e20566973d0d2e50ce8bf0ee15165bb799bec0df1e50177"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.9.1"
|
||||
"version": "==3.10.0"
|
||||
},
|
||||
"marshmallow-enum": {
|
||||
"hashes": [
|
||||
@ -548,40 +565,33 @@
|
||||
},
|
||||
"packaging": {
|
||||
"hashes": [
|
||||
"sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8",
|
||||
"sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181"
|
||||
"sha256:24e0da08660a87484d1602c30bb4902d74816b6985b93de36926f5bc95741858",
|
||||
"sha256:78598185a7008a470d64526a8059de9aaa449238f280fc9eb6b13ba6c4109093"
|
||||
],
|
||||
"version": "==20.4"
|
||||
"version": "==20.8"
|
||||
},
|
||||
"pandas": {
|
||||
"hashes": [
|
||||
"sha256:09e0503758ad61afe81c9069505f8cb8c1e36ea8cc1e6826a95823ef5b327daf",
|
||||
"sha256:0a11a6290ef3667575cbd4785a1b62d658c25a2fd70a5adedba32e156a8f1773",
|
||||
"sha256:0d9a38a59242a2f6298fff45d09768b78b6eb0c52af5919ea9e45965d7ba56d9",
|
||||
"sha256:112c5ba0f9ea0f60b2cc38c25f87ca1d5ca10f71efbee8e0f1bee9cf584ed5d5",
|
||||
"sha256:185cf8c8f38b169dbf7001e1a88c511f653fbb9dfa3e048f5e19c38049e991dc",
|
||||
"sha256:3aa8e10768c730cc1b610aca688f588831fa70b65a26cb549fbb9f35049a05e0",
|
||||
"sha256:41746d520f2b50409dffdba29a15c42caa7babae15616bcf80800d8cfcae3d3e",
|
||||
"sha256:43cea38cbcadb900829858884f49745eb1f42f92609d368cabcc674b03e90efc",
|
||||
"sha256:5378f58172bd63d8c16dd5d008d7dcdd55bf803fcdbe7da2dcb65dbbf322f05b",
|
||||
"sha256:54404abb1cd3f89d01f1fb5350607815326790efb4789be60508f458cdd5ccbf",
|
||||
"sha256:5dac3aeaac5feb1016e94bde851eb2012d1733a222b8afa788202b836c97dad5",
|
||||
"sha256:5fdb2a61e477ce58d3f1fdf2470ee142d9f0dde4969032edaf0b8f1a9dafeaa2",
|
||||
"sha256:6613c7815ee0b20222178ad32ec144061cb07e6a746970c9160af1ebe3ad43b4",
|
||||
"sha256:6d2b5b58e7df46b2c010ec78d7fb9ab20abf1d306d0614d3432e7478993fbdb0",
|
||||
"sha256:8a5d7e57b9df2c0a9a202840b2881bb1f7a648eba12dd2d919ac07a33a36a97f",
|
||||
"sha256:8b4c2055ebd6e497e5ecc06efa5b8aa76f59d15233356eb10dad22a03b757805",
|
||||
"sha256:a15653480e5b92ee376f8458197a58cca89a6e95d12cccb4c2d933df5cecc63f",
|
||||
"sha256:a7d2547b601ecc9a53fd41561de49a43d2231728ad65c7713d6b616cd02ddbed",
|
||||
"sha256:a979d0404b135c63954dea79e6246c45dd45371a88631cdbb4877d844e6de3b6",
|
||||
"sha256:b1f8111635700de7ac350b639e7e452b06fc541a328cf6193cf8fc638804bab8",
|
||||
"sha256:c5a3597880a7a29a31ebd39b73b2c824316ae63a05c3c8a5ce2aea3fc68afe35",
|
||||
"sha256:c681e8fcc47a767bf868341d8f0d76923733cbdcabd6ec3a3560695c69f14a1e",
|
||||
"sha256:cf135a08f306ebbcfea6da8bf775217613917be23e5074c69215b91e180caab4",
|
||||
"sha256:e2b8557fe6d0a18db4d61c028c6af61bfed44ef90e419ed6fadbdc079eba141e"
|
||||
"sha256:0be6102dd99910513e75ed6536284743ead810349c51bdeadd2a5b6649f30abb",
|
||||
"sha256:272675a98fa4954b9fc0933df775596fc942e50015d7e75d8f19548808a2bfdf",
|
||||
"sha256:2d8b4f532db37418121831a461fd107d826c240b098f52e7a1b4ab3d5aaa4fb2",
|
||||
"sha256:33318fa24b192b1a4684347ff76679a7267fd4e547da9f71556a5914f0dc10e7",
|
||||
"sha256:3bc6d2be03cb75981d8cbeda09503cd9d6d699fc0dc28a65e197165ad527b7b8",
|
||||
"sha256:43482789c55cbabeed9482263cfc98a11e8fcae900cb63ef038948acb4a72570",
|
||||
"sha256:616478c1bd8fe1e600f521ae2da434e021c11e7a4e5da3451d02906143d3629a",
|
||||
"sha256:6c1a57e4d0d6f9633a07817c44e6b36d81c265fe4c52d0c0505513a2d0f7953c",
|
||||
"sha256:7904ee438549b5223ce8dc008772458dd7c5cf0ccc64cf903e81202400702235",
|
||||
"sha256:7b54c14130a3448d81eed1348f52429c23e27188d9db6e6d4afeae792bc49c11",
|
||||
"sha256:8f92b07cdbfa3704d85b4264e52c216cafe6c0059b0d07cdad8cb29e0b90f2b8",
|
||||
"sha256:91fd0b94e7b98528177a05e6f65efea79d7ef9dec15ee48c7c69fc39fdd87235",
|
||||
"sha256:9c6692cea6d56da8650847172bdb148622f545e7782d17995822434c79d7a211",
|
||||
"sha256:9e18631d996fe131de6cb31a8bdae18965cc8f39eb23fdfbbf42808ecc63dabf",
|
||||
"sha256:cba93d4fd3b0a42858b2b599495aff793fb5d94587979f45a14177d1217ba446",
|
||||
"sha256:e03386615b970b8b41da6a68afe717626741bb2431cec993640685614c0680e4",
|
||||
"sha256:f8b87d2f541cd9bc4ecfe85a561abac85c33fe4de4ce70cca36b2768af2611f5"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.1.4"
|
||||
"version": "==1.2.0"
|
||||
},
|
||||
"psycopg2-binary": {
|
||||
"hashes": [
|
||||
@ -640,18 +650,18 @@
|
||||
},
|
||||
"pygithub": {
|
||||
"hashes": [
|
||||
"sha256:776befaddab9d8fddd525d52a6ca1ac228cf62b5b1e271836d766f4925e1452e",
|
||||
"sha256:8ad656bf79958e775ec59f7f5a3dbcbadac12147ae3dc42708b951064096af15"
|
||||
"sha256:300bc16e62886ca6537b0830e8f516ea4bc3ef12d308e0c5aff8bdbd099173d4",
|
||||
"sha256:87afd6a67ea582aa7533afdbf41635725f13d12581faed7e3e04b1579c0c0627"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.53"
|
||||
"version": "==1.54.1"
|
||||
},
|
||||
"pygments": {
|
||||
"hashes": [
|
||||
"sha256:381985fcc551eb9d37c52088a32914e00517e57f4a21609f48141ba08e193fa0",
|
||||
"sha256:88a0bbcd659fcb9573703957c6b9cff9fab7295e6e76db54c9d00ae42df32773"
|
||||
"sha256:ccf3acacf3782cbed4a989426012f1c535c9a90d3a7fc3f16d231b9372d2b716",
|
||||
"sha256:f275b6c0909e5dafd2d6269a656aa90fa58ebf4a74f8fcf9053195d226b24a08"
|
||||
],
|
||||
"version": "==2.7.2"
|
||||
"version": "==2.7.3"
|
||||
},
|
||||
"pyjwt": {
|
||||
"hashes": [
|
||||
@ -713,10 +723,10 @@
|
||||
},
|
||||
"pytz": {
|
||||
"hashes": [
|
||||
"sha256:3e6b7dd2d1e0a59084bcee14a17af60c5c562cdc16d828e8eba2e683d3a7e268",
|
||||
"sha256:5c55e189b682d420be27c6995ba6edce0c0a77dd67bfbe2ae6607134d5851ffd"
|
||||
"sha256:16962c5fb8db4a8f63a26646d8886e9d769b6c511543557bc84e9569fb9a9cb4",
|
||||
"sha256:180befebb1927b16f6b57101720075a984c019ac16b1b7575673bea42c6c3da5"
|
||||
],
|
||||
"version": "==2020.4"
|
||||
"version": "==2020.5"
|
||||
},
|
||||
"pyyaml": {
|
||||
"hashes": [
|
||||
@ -738,19 +748,19 @@
|
||||
},
|
||||
"recommonmark": {
|
||||
"hashes": [
|
||||
"sha256:29cd4faeb6c5268c633634f2d69aef9431e0f4d347f90659fd0aab20e541efeb",
|
||||
"sha256:2ec4207a574289355d5b6ae4ae4abb29043346ca12cdd5f07d374dc5987d2852"
|
||||
"sha256:1b1db69af0231efce3fa21b94ff627ea33dee7079a01dd0a7f8482c3da148b3f",
|
||||
"sha256:bdb4db649f2222dcd8d2d844f0006b958d627f732415d399791ee436a3686d67"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.6.0"
|
||||
"version": "==0.7.1"
|
||||
},
|
||||
"requests": {
|
||||
"hashes": [
|
||||
"sha256:7f1a0b932f4a60a1a65caa4263921bb7d9ee911957e0ae4a23a6dd08185ad5f8",
|
||||
"sha256:e786fa28d8c9154e6a4de5d46a1d921b8749f8b74e28bde23768e5e16eece998"
|
||||
"sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804",
|
||||
"sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.25.0"
|
||||
"version": "==2.25.1"
|
||||
},
|
||||
"sentry-sdk": {
|
||||
"extras": [
|
||||
@ -779,19 +789,19 @@
|
||||
},
|
||||
"soupsieve": {
|
||||
"hashes": [
|
||||
"sha256:1634eea42ab371d3d346309b93df7870a88610f0725d47528be902a0d95ecc55",
|
||||
"sha256:a59dc181727e95d25f781f0eb4fd1825ff45590ec8ff49eadfd7f1a537cc0232"
|
||||
"sha256:4bb21a6ee4707bf43b61230e80740e71bfe56e55d1f1f50924b087bb2975c851",
|
||||
"sha256:6dc52924dc0bc710a5d16794e6b3480b2c7c08b07729505feab2b2c16661ff6e"
|
||||
],
|
||||
"markers": "python_version >= '3.0'",
|
||||
"version": "==2.0.1"
|
||||
"version": "==2.1"
|
||||
},
|
||||
"sphinx": {
|
||||
"hashes": [
|
||||
"sha256:1e8d592225447104d1172be415bc2972bd1357e3e12fdc76edf2261105db4300",
|
||||
"sha256:d4e59ad4ea55efbb3c05cde3bfc83bfc14f0c95aa95c3d75346fcce186a47960"
|
||||
"sha256:aeef652b14629431c82d3fe994ce39ead65b3fe87cf41b9a3714168ff8b83376",
|
||||
"sha256:e450cb205ff8924611085183bf1353da26802ae73d9251a8fcdf220a8f8712ef"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.3.1"
|
||||
"version": "==3.4.1"
|
||||
},
|
||||
"sphinxcontrib-applehelp": {
|
||||
"hashes": [
|
||||
@ -837,50 +847,50 @@
|
||||
},
|
||||
"spiffworkflow": {
|
||||
"git": "https://github.com/sartography/SpiffWorkflow.git",
|
||||
"ref": "6b2ed24bb340ebd31049312bd321f66ebf7b6b26"
|
||||
"ref": "6632930a28a790f3e804a99c7589593cd732a690"
|
||||
},
|
||||
"sqlalchemy": {
|
||||
"hashes": [
|
||||
"sha256:009e8388d4d551a2107632921320886650b46332f61dc935e70c8bcf37d8e0d6",
|
||||
"sha256:0157c269701d88f5faf1fa0e4560e4d814f210c01a5b55df3cab95e9346a8bcc",
|
||||
"sha256:0a92745bb1ebbcb3985ed7bda379b94627f0edbc6c82e9e4bac4fb5647ae609a",
|
||||
"sha256:0cca1844ba870e81c03633a99aa3dc62256fb96323431a5dec7d4e503c26372d",
|
||||
"sha256:166917a729b9226decff29416f212c516227c2eb8a9c9f920d69ced24e30109f",
|
||||
"sha256:1f5f369202912be72fdf9a8f25067a5ece31a2b38507bb869306f173336348da",
|
||||
"sha256:2909dffe5c9a615b7e6c92d1ac2d31e3026dc436440a4f750f4749d114d88ceb",
|
||||
"sha256:2b5dafed97f778e9901b79cc01b88d39c605e0545b4541f2551a2fd785adc15b",
|
||||
"sha256:2e9bd5b23bba8ae8ce4219c9333974ff5e103c857d9ff0e4b73dc4cb244c7d86",
|
||||
"sha256:3aa6d45e149a16aa1f0c46816397e12313d5e37f22205c26e06975e150ffcf2a",
|
||||
"sha256:4bdbdb8ca577c6c366d15791747c1de6ab14529115a2eb52774240c412a7b403",
|
||||
"sha256:53fd857c6c8ffc0aa6a5a3a2619f6a74247e42ec9e46b836a8ffa4abe7aab327",
|
||||
"sha256:5cdfe54c1e37279dc70d92815464b77cd8ee30725adc9350f06074f91dbfeed2",
|
||||
"sha256:5d92c18458a4aa27497a986038d5d797b5279268a2de303cd00910658e8d149c",
|
||||
"sha256:632b32183c0cb0053194a4085c304bc2320e5299f77e3024556fa2aa395c2a8b",
|
||||
"sha256:7c735c7a6db8ee9554a3935e741cf288f7dcbe8706320251eb38c412e6a4281d",
|
||||
"sha256:7cd40cb4bc50d9e87b3540b23df6e6b24821ba7e1f305c1492b0806c33dbdbec",
|
||||
"sha256:84f0ac4a09971536b38cc5d515d6add7926a7e13baa25135a1dbb6afa351a376",
|
||||
"sha256:8dcbf377529a9af167cbfc5b8acec0fadd7c2357fc282a1494c222d3abfc9629",
|
||||
"sha256:950f0e17ffba7a7ceb0dd056567bc5ade22a11a75920b0e8298865dc28c0eff6",
|
||||
"sha256:9e379674728f43a0cd95c423ac0e95262500f9bfd81d33b999daa8ea1756d162",
|
||||
"sha256:b15002b9788ffe84e42baffc334739d3b68008a973d65fad0a410ca5d0531980",
|
||||
"sha256:b6f036ecc017ec2e2cc2a40615b41850dc7aaaea6a932628c0afc73ab98ba3fb",
|
||||
"sha256:bad73f9888d30f9e1d57ac8829f8a12091bdee4949b91db279569774a866a18e",
|
||||
"sha256:bbc58fca72ce45a64bb02b87f73df58e29848b693869e58bd890b2ddbb42d83b",
|
||||
"sha256:bca4d367a725694dae3dfdc86cf1d1622b9f414e70bd19651f5ac4fb3aa96d61",
|
||||
"sha256:be41d5de7a8e241864189b7530ca4aaf56a5204332caa70555c2d96379e18079",
|
||||
"sha256:bf53d8dddfc3e53a5bda65f7f4aa40fae306843641e3e8e701c18a5609471edf",
|
||||
"sha256:c092fe282de83d48e64d306b4bce03114859cdbfe19bf8a978a78a0d44ddadb1",
|
||||
"sha256:c3ab23ee9674336654bf9cac30eb75ac6acb9150dc4b1391bec533a7a4126471",
|
||||
"sha256:ce64a44c867d128ab8e675f587aae7f61bd2db836a3c4ba522d884cd7c298a77",
|
||||
"sha256:d05cef4a164b44ffda58200efcb22355350979e000828479971ebca49b82ddb1",
|
||||
"sha256:d2f25c7f410338d31666d7ddedfa67570900e248b940d186b48461bd4e5569a1",
|
||||
"sha256:d3b709d64b5cf064972b3763b47139e4a0dc4ae28a36437757f7663f67b99710",
|
||||
"sha256:e32e3455db14602b6117f0f422f46bc297a3853ae2c322ecd1e2c4c04daf6ed5",
|
||||
"sha256:ed53209b5f0f383acb49a927179fa51a6e2259878e164273ebc6815f3a752465",
|
||||
"sha256:f605f348f4e6a2ba00acb3399c71d213b92f27f2383fc4abebf7a37368c12142",
|
||||
"sha256:fcdb3755a7c355bc29df1b5e6fb8226d5c8b90551d202d69d0076a8a5649d68b"
|
||||
"sha256:04f995fcbf54e46cddeb4f75ce9dfc17075d6ae04ac23b2bacb44b3bc6f6bf11",
|
||||
"sha256:0c6406a78a714a540d980a680b86654feadb81c8d0eecb59f3d6c554a4c69f19",
|
||||
"sha256:0c72b90988be749e04eff0342dcc98c18a14461eb4b2ad59d611b57b31120f90",
|
||||
"sha256:108580808803c7732f34798eb4a329d45b04c562ed83ee90f09f6a184a42b766",
|
||||
"sha256:1418f5e71d6081aa1095a1d6b567a562d2761996710bdce9b6e6ba20a03d0864",
|
||||
"sha256:17610d573e698bf395afbbff946544fbce7c5f4ee77b5bcb1f821b36345fae7a",
|
||||
"sha256:216ba5b4299c95ed179b58f298bda885a476b16288ab7243e89f29f6aeced7e0",
|
||||
"sha256:2ff132a379838b1abf83c065be54cef32b47c987aedd06b82fc76476c85225eb",
|
||||
"sha256:314f5042c0b047438e19401d5f29757a511cfc2f0c40d28047ca0e4c95eabb5b",
|
||||
"sha256:318b5b727e00662e5fc4b4cd2bf58a5116d7c1b4dd56ffaa7d68f43458a8d1ed",
|
||||
"sha256:3ab5b44a07b8c562c6dcb7433c6a6c6e03266d19d64f87b3333eda34e3b9936b",
|
||||
"sha256:426ece890153ccc52cc5151a1a0ed540a5a7825414139bb4c95a868d8da54a52",
|
||||
"sha256:491fe48adc07d13e020a8b07ef82eefc227003a046809c121bea81d3dbf1832d",
|
||||
"sha256:4a84c7c7658dd22a33dab2e2aa2d17c18cb004a42388246f2e87cb4085ef2811",
|
||||
"sha256:54da615e5b92c339e339fe8536cce99fe823b6ed505d4ea344852aefa1c205fb",
|
||||
"sha256:5a7f224cdb7233182cec2a45d4c633951268d6a9bcedac37abbf79dd07012aea",
|
||||
"sha256:61628715931f4962e0cdb2a7c87ff39eea320d2aa96bd471a3c293d146f90394",
|
||||
"sha256:62285607a5264d1f91590abd874d6a498e229d5840669bd7d9f654cfaa599bd0",
|
||||
"sha256:62fb881ba51dbacba9af9b779211cf9acff3442d4f2993142015b22b3cd1f92a",
|
||||
"sha256:68428818cf80c60dc04aa0f38da20ad39b28aba4d4d199f949e7d6e04444ea86",
|
||||
"sha256:6aaa13ee40c4552d5f3a59f543f0db6e31712cc4009ec7385407be4627259d41",
|
||||
"sha256:70121f0ae48b25ef3e56e477b88cd0b0af0e1f3a53b5554071aa6a93ef378a03",
|
||||
"sha256:715b34578cc740b743361f7c3e5f584b04b0f1344f45afc4e87fbac4802eb0a0",
|
||||
"sha256:758fc8c4d6c0336e617f9f6919f9daea3ab6bb9b07005eda9a1a682e24a6cacc",
|
||||
"sha256:7d4b8de6bb0bc736161cb0bbd95366b11b3eb24dd6b814a143d8375e75af9990",
|
||||
"sha256:81d8d099a49f83111cce55ec03cc87eef45eec0d90f9842b4fc674f860b857b0",
|
||||
"sha256:888d5b4b5aeed0d3449de93ea80173653e939e916cc95fe8527079e50235c1d2",
|
||||
"sha256:95bde07d19c146d608bccb9b16e144ec8f139bcfe7fd72331858698a71c9b4f5",
|
||||
"sha256:9bf572e4f5aa23f88dd902f10bb103cb5979022a38eec684bfa6d61851173fec",
|
||||
"sha256:bab5a1e15b9466a25c96cda19139f3beb3e669794373b9ce28c4cf158c6e841d",
|
||||
"sha256:bd4b1af45fd322dcd1fb2a9195b4f93f570d1a5902a842e3e6051385fac88f9c",
|
||||
"sha256:bde677047305fe76c7ee3e4492b545e0018918e44141cc154fe39e124e433991",
|
||||
"sha256:c389d7cc2b821853fb018c85457da3e7941db64f4387720a329bc7ff06a27963",
|
||||
"sha256:d055ff750fcab69ca4e57b656d9c6ad33682e9b8d564f2fbe667ab95c63591b0",
|
||||
"sha256:d53f59744b01f1440a1b0973ed2c3a7de204135c593299ee997828aad5191693",
|
||||
"sha256:f115150cc4361dd46153302a640c7fa1804ac207f9cc356228248e351a8b4676",
|
||||
"sha256:f1e88b30da8163215eab643962ae9d9252e47b4ea53404f2c4f10f24e70ddc62",
|
||||
"sha256:f8191fef303025879e6c3548ecd8a95aafc0728c764ab72ec51a0bdf0c91a341"
|
||||
],
|
||||
"version": "==1.3.20"
|
||||
"version": "==1.3.22"
|
||||
},
|
||||
"swagger-ui-bundle": {
|
||||
"hashes": [
|
||||
@ -942,11 +952,11 @@
|
||||
},
|
||||
"xlrd": {
|
||||
"hashes": [
|
||||
"sha256:546eb36cee8db40c3eaa46c351e67ffee6eeb5fa2650b71bc4c758a29a1b29b2",
|
||||
"sha256:e551fb498759fa3a5384a94ccd4c3c02eb7c00ea424426e212ac0c57be9dfbde"
|
||||
"sha256:6a33ee89877bd9abc1158129f6e94be74e2679636b8a205b43b85206c3f0bbdd",
|
||||
"sha256:f72f148f54442c6b056bf931dbc34f986fd0c3b0b6b5a58d013c9aef274d0c88"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.2.0"
|
||||
"version": "==2.0.1"
|
||||
},
|
||||
"xlsxwriter": {
|
||||
"hashes": [
|
||||
@ -967,43 +977,58 @@
|
||||
},
|
||||
"coverage": {
|
||||
"hashes": [
|
||||
"sha256:0203acd33d2298e19b57451ebb0bed0ab0c602e5cf5a818591b4918b1f97d516",
|
||||
"sha256:0f313707cdecd5cd3e217fc68c78a960b616604b559e9ea60cc16795c4304259",
|
||||
"sha256:1c6703094c81fa55b816f5ae542c6ffc625fec769f22b053adb42ad712d086c9",
|
||||
"sha256:1d44bb3a652fed01f1f2c10d5477956116e9b391320c94d36c6bf13b088a1097",
|
||||
"sha256:280baa8ec489c4f542f8940f9c4c2181f0306a8ee1a54eceba071a449fb870a0",
|
||||
"sha256:29a6272fec10623fcbe158fdf9abc7a5fa032048ac1d8631f14b50fbfc10d17f",
|
||||
"sha256:2b31f46bf7b31e6aa690d4c7a3d51bb262438c6dcb0d528adde446531d0d3bb7",
|
||||
"sha256:2d43af2be93ffbad25dd959899b5b809618a496926146ce98ee0b23683f8c51c",
|
||||
"sha256:381ead10b9b9af5f64646cd27107fb27b614ee7040bb1226f9c07ba96625cbb5",
|
||||
"sha256:47a11bdbd8ada9b7ee628596f9d97fbd3851bd9999d398e9436bd67376dbece7",
|
||||
"sha256:4d6a42744139a7fa5b46a264874a781e8694bb32f1d76d8137b68138686f1729",
|
||||
"sha256:50691e744714856f03a86df3e2bff847c2acede4c191f9a1da38f088df342978",
|
||||
"sha256:530cc8aaf11cc2ac7430f3614b04645662ef20c348dce4167c22d99bec3480e9",
|
||||
"sha256:582ddfbe712025448206a5bc45855d16c2e491c2dd102ee9a2841418ac1c629f",
|
||||
"sha256:63808c30b41f3bbf65e29f7280bf793c79f54fb807057de7e5238ffc7cc4d7b9",
|
||||
"sha256:71b69bd716698fa62cd97137d6f2fdf49f534decb23a2c6fc80813e8b7be6822",
|
||||
"sha256:7858847f2d84bf6e64c7f66498e851c54de8ea06a6f96a32a1d192d846734418",
|
||||
"sha256:78e93cc3571fd928a39c0b26767c986188a4118edc67bc0695bc7a284da22e82",
|
||||
"sha256:7f43286f13d91a34fadf61ae252a51a130223c52bfefb50310d5b2deb062cf0f",
|
||||
"sha256:86e9f8cd4b0cdd57b4ae71a9c186717daa4c5a99f3238a8723f416256e0b064d",
|
||||
"sha256:8f264ba2701b8c9f815b272ad568d555ef98dfe1576802ab3149c3629a9f2221",
|
||||
"sha256:9342dd70a1e151684727c9c91ea003b2fb33523bf19385d4554f7897ca0141d4",
|
||||
"sha256:9361de40701666b034c59ad9e317bae95c973b9ff92513dd0eced11c6adf2e21",
|
||||
"sha256:9669179786254a2e7e57f0ecf224e978471491d660aaca833f845b72a2df3709",
|
||||
"sha256:aac1ba0a253e17889550ddb1b60a2063f7474155465577caa2a3b131224cfd54",
|
||||
"sha256:aef72eae10b5e3116bac6957de1df4d75909fc76d1499a53fb6387434b6bcd8d",
|
||||
"sha256:bd3166bb3b111e76a4f8e2980fa1addf2920a4ca9b2b8ca36a3bc3dedc618270",
|
||||
"sha256:c1b78fb9700fc961f53386ad2fd86d87091e06ede5d118b8a50dea285a071c24",
|
||||
"sha256:c3888a051226e676e383de03bf49eb633cd39fc829516e5334e69b8d81aae751",
|
||||
"sha256:c5f17ad25d2c1286436761b462e22b5020d83316f8e8fcb5deb2b3151f8f1d3a",
|
||||
"sha256:c851b35fc078389bc16b915a0a7c1d5923e12e2c5aeec58c52f4aa8085ac8237",
|
||||
"sha256:cb7df71de0af56000115eafd000b867d1261f786b5eebd88a0ca6360cccfaca7",
|
||||
"sha256:cedb2f9e1f990918ea061f28a0f0077a07702e3819602d3507e2ff98c8d20636",
|
||||
"sha256:e8caf961e1b1a945db76f1b5fa9c91498d15f545ac0ababbe575cfab185d3bd8"
|
||||
"sha256:08b3ba72bd981531fd557f67beee376d6700fba183b167857038997ba30dd297",
|
||||
"sha256:2757fa64e11ec12220968f65d086b7a29b6583d16e9a544c889b22ba98555ef1",
|
||||
"sha256:3102bb2c206700a7d28181dbe04d66b30780cde1d1c02c5f3c165cf3d2489497",
|
||||
"sha256:3498b27d8236057def41de3585f317abae235dd3a11d33e01736ffedb2ef8606",
|
||||
"sha256:378ac77af41350a8c6b8801a66021b52da8a05fd77e578b7380e876c0ce4f528",
|
||||
"sha256:38f16b1317b8dd82df67ed5daa5f5e7c959e46579840d77a67a4ceb9cef0a50b",
|
||||
"sha256:3911c2ef96e5ddc748a3c8b4702c61986628bb719b8378bf1e4a6184bbd48fe4",
|
||||
"sha256:3a3c3f8863255f3c31db3889f8055989527173ef6192a283eb6f4db3c579d830",
|
||||
"sha256:3b14b1da110ea50c8bcbadc3b82c3933974dbeea1832e814aab93ca1163cd4c1",
|
||||
"sha256:535dc1e6e68fad5355f9984d5637c33badbdc987b0c0d303ee95a6c979c9516f",
|
||||
"sha256:6f61319e33222591f885c598e3e24f6a4be3533c1d70c19e0dc59e83a71ce27d",
|
||||
"sha256:723d22d324e7997a651478e9c5a3120a0ecbc9a7e94071f7e1954562a8806cf3",
|
||||
"sha256:76b2775dda7e78680d688daabcb485dc87cf5e3184a0b3e012e1d40e38527cc8",
|
||||
"sha256:782a5c7df9f91979a7a21792e09b34a658058896628217ae6362088b123c8500",
|
||||
"sha256:7e4d159021c2029b958b2363abec4a11db0ce8cd43abb0d9ce44284cb97217e7",
|
||||
"sha256:8dacc4073c359f40fcf73aede8428c35f84639baad7e1b46fce5ab7a8a7be4bb",
|
||||
"sha256:8f33d1156241c43755137288dea619105477961cfa7e47f48dbf96bc2c30720b",
|
||||
"sha256:8ffd4b204d7de77b5dd558cdff986a8274796a1e57813ed005b33fd97e29f059",
|
||||
"sha256:93a280c9eb736a0dcca19296f3c30c720cb41a71b1f9e617f341f0a8e791a69b",
|
||||
"sha256:9a4f66259bdd6964d8cf26142733c81fb562252db74ea367d9beb4f815478e72",
|
||||
"sha256:9a9d4ff06804920388aab69c5ea8a77525cf165356db70131616acd269e19b36",
|
||||
"sha256:a2070c5affdb3a5e751f24208c5c4f3d5f008fa04d28731416e023c93b275277",
|
||||
"sha256:a4857f7e2bc6921dbd487c5c88b84f5633de3e7d416c4dc0bb70256775551a6c",
|
||||
"sha256:a607ae05b6c96057ba86c811d9c43423f35e03874ffb03fbdcd45e0637e8b631",
|
||||
"sha256:a66ca3bdf21c653e47f726ca57f46ba7fc1f260ad99ba783acc3e58e3ebdb9ff",
|
||||
"sha256:ab110c48bc3d97b4d19af41865e14531f300b482da21783fdaacd159251890e8",
|
||||
"sha256:b239711e774c8eb910e9b1ac719f02f5ae4bf35fa0420f438cdc3a7e4e7dd6ec",
|
||||
"sha256:be0416074d7f253865bb67630cf7210cbc14eb05f4099cc0f82430135aaa7a3b",
|
||||
"sha256:c46643970dff9f5c976c6512fd35768c4a3819f01f61169d8cdac3f9290903b7",
|
||||
"sha256:c5ec71fd4a43b6d84ddb88c1df94572479d9a26ef3f150cef3dacefecf888105",
|
||||
"sha256:c6e5174f8ca585755988bc278c8bb5d02d9dc2e971591ef4a1baabdf2d99589b",
|
||||
"sha256:c89b558f8a9a5a6f2cfc923c304d49f0ce629c3bd85cb442ca258ec20366394c",
|
||||
"sha256:cc44e3545d908ecf3e5773266c487ad1877be718d9dc65fc7eb6e7d14960985b",
|
||||
"sha256:cc6f8246e74dd210d7e2b56c76ceaba1cc52b025cd75dbe96eb48791e0250e98",
|
||||
"sha256:cd556c79ad665faeae28020a0ab3bda6cd47d94bec48e36970719b0b86e4dcf4",
|
||||
"sha256:ce6f3a147b4b1a8b09aae48517ae91139b1b010c5f36423fa2b866a8b23df879",
|
||||
"sha256:ceb499d2b3d1d7b7ba23abe8bf26df5f06ba8c71127f188333dddcf356b4b63f",
|
||||
"sha256:cef06fb382557f66d81d804230c11ab292d94b840b3cb7bf4450778377b592f4",
|
||||
"sha256:e448f56cfeae7b1b3b5bcd99bb377cde7c4eb1970a525c770720a352bc4c8044",
|
||||
"sha256:e52d3d95df81c8f6b2a1685aabffadf2d2d9ad97203a40f8d61e51b70f191e4e",
|
||||
"sha256:ee2f1d1c223c3d2c24e3afbb2dd38be3f03b1a8d6a83ee3d9eb8c36a52bee899",
|
||||
"sha256:f2c6888eada180814b8583c3e793f3f343a692fc802546eed45f40a001b1169f",
|
||||
"sha256:f51dbba78d68a44e99d484ca8c8f604f17e957c1ca09c3ebc2c7e3bbd9ba0448",
|
||||
"sha256:f54de00baf200b4539a5a092a759f000b5f45fd226d6d25a76b0dff71177a714",
|
||||
"sha256:fa10fee7e32213f5c7b0d6428ea92e3a3fdd6d725590238a3f92c0de1c78b9d2",
|
||||
"sha256:fabeeb121735d47d8eab8671b6b031ce08514c86b7ad8f7d5490a7b6dcd6267d",
|
||||
"sha256:fac3c432851038b3e6afe086f777732bcf7f6ebbfd90951fa04ee53db6d0bcdd",
|
||||
"sha256:fda29412a66099af6d6de0baa6bd7c52674de177ec2ad2630ca264142d69c6c7",
|
||||
"sha256:ff1330e8bc996570221b450e2d539134baa9465f5cb98aff0e0f73f34172e0ae"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==5.3"
|
||||
"version": "==5.3.1"
|
||||
},
|
||||
"iniconfig": {
|
||||
"hashes": [
|
||||
@ -1014,10 +1039,10 @@
|
||||
},
|
||||
"packaging": {
|
||||
"hashes": [
|
||||
"sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8",
|
||||
"sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181"
|
||||
"sha256:24e0da08660a87484d1602c30bb4902d74816b6985b93de36926f5bc95741858",
|
||||
"sha256:78598185a7008a470d64526a8059de9aaa449238f280fc9eb6b13ba6c4109093"
|
||||
],
|
||||
"version": "==20.4"
|
||||
"version": "==20.8"
|
||||
},
|
||||
"pbr": {
|
||||
"hashes": [
|
||||
@ -1036,10 +1061,10 @@
|
||||
},
|
||||
"py": {
|
||||
"hashes": [
|
||||
"sha256:366389d1db726cd2fcfc79732e75410e5fe4d31db13692115529d34069a043c2",
|
||||
"sha256:9ca6883ce56b4e8da7e79ac18787889fa5206c79dcc67fb065376cd2fe03f342"
|
||||
"sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3",
|
||||
"sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"
|
||||
],
|
||||
"version": "==1.9.0"
|
||||
"version": "==1.10.0"
|
||||
},
|
||||
"pyparsing": {
|
||||
"hashes": [
|
||||
@ -1050,18 +1075,11 @@
|
||||
},
|
||||
"pytest": {
|
||||
"hashes": [
|
||||
"sha256:4288fed0d9153d9646bfcdf0c0428197dba1ecb27a33bb6e031d002fa88653fe",
|
||||
"sha256:c0a7e94a8cdbc5422a51ccdad8e6f1024795939cc89159a0ae7f0b316ad3823e"
|
||||
"sha256:1969f797a1a0dbd8ccf0fecc80262312729afea9c17f1d70ebf85c5e76c6f7c8",
|
||||
"sha256:66e419b1899bc27346cb2c993e12c5e5e8daba9073c1fbce33b9807abc95c306"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==6.1.2"
|
||||
},
|
||||
"six": {
|
||||
"hashes": [
|
||||
"sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259",
|
||||
"sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"
|
||||
],
|
||||
"version": "==1.15.0"
|
||||
"version": "==6.2.1"
|
||||
},
|
||||
"toml": {
|
||||
"hashes": [
|
||||
|
38
README.md
38
README.md
@ -64,6 +64,44 @@ it should always be visible in the Docker Desktop Daskboard with a friendly litt
|
||||
stop button for your clicking pleasure.
|
||||
|
||||
|
||||
### Environment Setup Part #2
|
||||
If you want to run CR-Connect in development mode you must have the following two services running:
|
||||
1. `Postgres Docker`: There is a sub-directory called Postgres that contains a docker image that will set up an empty
|
||||
database for CR-Connect, and for Protocol Builder Mock, mentioned below. For must systems, you can 'cd' into this
|
||||
directory and just run start.sh to fire up the Postgres service, and stop.sh to shut it back down again.
|
||||
create .env file in /postgres with the following two lines in it:
|
||||
```
|
||||
DB_USER=crc_user
|
||||
DB_PASS=crc_pass
|
||||
```
|
||||
With this in place, from the command line:
|
||||
```bash
|
||||
cd postgres
|
||||
./start.sh
|
||||
```
|
||||
You can now build the database structure in the newly created database with the following lines
|
||||
```baseh
|
||||
cd .. (into base directory)
|
||||
flask db upgrade
|
||||
flask load-example-data (this creates some basic workflows for you to use)
|
||||
```
|
||||
|
||||
|
||||
|
||||
2. `Protocol Builder Mock`: We created a mock of the Protocol Builder, a critical service at UVA that is a deep
|
||||
dependency for CR-Connect. You can find the details here: [Protocol Builder Mock](https://github.com/sartography/protocol-builder-mock)
|
||||
Be sure this is up and running on Port 5002 or you will encounter errors when the system starts up.
|
||||
|
||||
With Protocol Builder Mock up and running, visit http://localhost:5001 and create a study. Set the user
|
||||
and primary investigator to dhf8r - which is a user in the mock ldap service, and this will later show up when you
|
||||
fire up the interface.
|
||||
|
||||
### Configuration
|
||||
1. `instance/config.py`: This will
|
||||
|
||||
|
||||
|
||||
|
||||
### Project Initialization
|
||||
1. Clone this repository.
|
||||
2. In PyCharm:
|
||||
|
@ -89,6 +89,7 @@ def load_example_data():
|
||||
from example_data import ExampleDataLoader
|
||||
ExampleDataLoader.clean_db()
|
||||
ExampleDataLoader().load_all()
|
||||
ExampleDataLoader().load_default_user()
|
||||
|
||||
|
||||
@app.cli.command()
|
||||
@ -98,6 +99,13 @@ def load_example_rrt_data():
|
||||
ExampleDataLoader.clean_db()
|
||||
ExampleDataLoader().load_rrt()
|
||||
|
||||
|
||||
@app.cli.command()
|
||||
def load_reference_files():
|
||||
"""Load example data into the database."""
|
||||
from example_data import ExampleDataLoader
|
||||
ExampleDataLoader().load_reference_documents()
|
||||
|
||||
@app.cli.command()
|
||||
def clear_db():
|
||||
"""Load example data into the database."""
|
||||
|
43
crc/api.yml
43
crc/api.yml
@ -850,20 +850,8 @@ paths:
|
||||
format: int32
|
||||
get:
|
||||
operationId: crc.api.workflow.get_workflow
|
||||
summary: Returns a workflow, can also be used to do a soft or hard reset on the workflow.
|
||||
summary: Returns a workflow.
|
||||
parameters:
|
||||
- name: soft_reset
|
||||
in: query
|
||||
required: false
|
||||
description: Set this to true to use the latest workflow specification to load minor modifications to the spec.
|
||||
schema:
|
||||
type: boolean
|
||||
- name: hard_reset
|
||||
in: query
|
||||
required: false
|
||||
description: Set this to true to reset the workflow
|
||||
schema:
|
||||
type: boolean
|
||||
- name: do_engine_steps
|
||||
in: query
|
||||
required: false
|
||||
@ -889,6 +877,35 @@ paths:
|
||||
responses:
|
||||
'204':
|
||||
description: The workflow was removed
|
||||
/workflow/{workflow_id}/restart:
|
||||
parameters:
|
||||
- name: workflow_id
|
||||
in: path
|
||||
required: true
|
||||
description: The id of the workflow
|
||||
schema:
|
||||
type: integer
|
||||
format: int32
|
||||
get:
|
||||
operationId: crc.api.workflow.restart_workflow
|
||||
summary: Restarts a workflow with the latest spec. Can also clear data.
|
||||
parameters:
|
||||
- name: clear_data
|
||||
in: query
|
||||
required: false
|
||||
description: Set this to true to clear data when starting workflow.
|
||||
schema:
|
||||
type: boolean
|
||||
tags:
|
||||
- Workflows and Tasks
|
||||
responses:
|
||||
'200':
|
||||
description: Returns updated workflow, possibly without data.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Workflow"
|
||||
|
||||
/workflow/{workflow_id}/task/{task_id}/data:
|
||||
parameters:
|
||||
- name: workflow_id
|
||||
|
@ -4,6 +4,8 @@ from flask import g
|
||||
|
||||
from crc import ma, app
|
||||
|
||||
import sentry_sdk
|
||||
|
||||
|
||||
class ApiError(Exception):
|
||||
def __init__(self, code, message, status_code=400,
|
||||
@ -16,6 +18,13 @@ class ApiError(Exception):
|
||||
self.file_name = file_name or "" # OPTIONAL: The file that caused the error.
|
||||
self.tag = tag or "" # OPTIONAL: The XML Tag that caused the issue.
|
||||
self.task_data = task_data or "" # OPTIONAL: A snapshot of data connected to the task when error ocurred.
|
||||
if hasattr(g,'user'):
|
||||
user = g.user.uid
|
||||
else:
|
||||
user = 'Unknown'
|
||||
self.task_user = user
|
||||
# This is for sentry logging into Slack
|
||||
sentry_sdk.set_context("User", {'user': user})
|
||||
Exception.__init__(self, self.message)
|
||||
|
||||
@classmethod
|
||||
@ -59,7 +68,7 @@ class ApiError(Exception):
|
||||
class ApiErrorSchema(ma.Schema):
|
||||
class Meta:
|
||||
fields = ("code", "message", "workflow_name", "file_name", "task_name", "task_id",
|
||||
"task_data")
|
||||
"task_data", "task_user")
|
||||
|
||||
|
||||
@app.errorhandler(ApiError)
|
||||
|
@ -1,11 +1,13 @@
|
||||
import uuid
|
||||
|
||||
from flask import g
|
||||
from crc import session, db, app
|
||||
|
||||
from crc import session
|
||||
from crc.api.common import ApiError, ApiErrorSchema
|
||||
from crc.models.api_models import WorkflowApi, WorkflowApiSchema, NavigationItem, NavigationItemSchema
|
||||
from crc.models.file import FileModel, LookupDataSchema, FileDataModel
|
||||
from crc.models.api_models import WorkflowApiSchema
|
||||
from crc.models.file import FileModel, LookupDataSchema
|
||||
from crc.models.study import StudyModel, WorkflowMetadata
|
||||
from crc.models.task_event import TaskEventModel, TaskEventModelSchema, TaskEvent, TaskEventSchema
|
||||
from crc.models.task_event import TaskEventModel, TaskEvent, TaskEventSchema
|
||||
from crc.models.workflow import WorkflowModel, WorkflowSpecModelSchema, WorkflowSpecModel, WorkflowSpecCategoryModel, \
|
||||
WorkflowSpecCategoryModelSchema
|
||||
from crc.services.file_service import FileService
|
||||
@ -15,6 +17,7 @@ from crc.services.user_service import UserService
|
||||
from crc.services.workflow_processor import WorkflowProcessor
|
||||
from crc.services.workflow_service import WorkflowService
|
||||
|
||||
|
||||
def all_specifications():
|
||||
schema = WorkflowSpecModelSchema(many=True)
|
||||
return schema.dump(session.query(WorkflowSpecModel).all())
|
||||
@ -93,21 +96,29 @@ def delete_workflow_specification(spec_id):
|
||||
session.commit()
|
||||
|
||||
|
||||
def get_workflow(workflow_id, soft_reset=False, hard_reset=False, do_engine_steps=True):
|
||||
"""Soft reset will attempt to update to the latest spec without starting over,
|
||||
Hard reset will update to the latest spec and start from the beginning.
|
||||
Read Only will return the workflow in a read only state, without running any
|
||||
engine tasks or logging any events. """
|
||||
def get_workflow(workflow_id, do_engine_steps=True):
|
||||
"""Retrieve workflow based on workflow_id, and return it in the last saved State.
|
||||
If do_engine_steps is False, return the workflow without running any engine tasks or logging any events. """
|
||||
workflow_model: WorkflowModel = session.query(WorkflowModel).filter_by(id=workflow_id).first()
|
||||
processor = WorkflowProcessor(workflow_model, soft_reset=soft_reset, hard_reset=hard_reset)
|
||||
processor = WorkflowProcessor(workflow_model)
|
||||
|
||||
if do_engine_steps:
|
||||
processor.do_engine_steps()
|
||||
processor.save()
|
||||
WorkflowService.update_task_assignments(processor)
|
||||
|
||||
workflow_api_model = WorkflowService.processor_to_workflow_api(processor)
|
||||
return WorkflowApiSchema().dump(workflow_api_model)
|
||||
|
||||
|
||||
def restart_workflow(workflow_id, clear_data=False):
|
||||
"""Restart a workflow with the latest spec.
|
||||
Clear data allows user to restart the workflow without previous data."""
|
||||
workflow_model: WorkflowModel = session.query(WorkflowModel).filter_by(id=workflow_id).first()
|
||||
WorkflowProcessor.reset(workflow_model, clear_data=clear_data)
|
||||
return get_workflow(workflow_model.id)
|
||||
|
||||
|
||||
def get_task_events(action = None, workflow = None, study = None):
|
||||
"""Provides a way to see a history of what has happened, or get a list of tasks that need your attention."""
|
||||
query = session.query(TaskEventModel).filter(TaskEventModel.user_uid == g.user.uid)
|
||||
@ -145,7 +156,7 @@ def set_current_task(workflow_id, task_id):
|
||||
"currently set to COMPLETE or READY.")
|
||||
|
||||
# If we have an interrupt task, run it.
|
||||
processor.bpmn_workflow.cancel_notify()
|
||||
processor.cancel_notify()
|
||||
|
||||
# Only reset the token if the task doesn't already have it.
|
||||
if spiff_task.state == spiff_task.COMPLETED:
|
||||
|
@ -1,7 +1,5 @@
|
||||
import hashlib
|
||||
import json
|
||||
import pandas as pd
|
||||
import requests
|
||||
from crc import session, app
|
||||
from crc.api.common import ApiError
|
||||
from crc.models.file import FileModel, FileDataModel
|
||||
@ -47,42 +45,55 @@ def get_changed_workflows(remote,as_df=False):
|
||||
# get the local thumbprints & make sure that 'workflow_spec_id' is a column, not an index
|
||||
local = get_all_spec_state_dataframe().reset_index()
|
||||
|
||||
if local.empty:
|
||||
# return the list as a dict, let swagger convert it to json
|
||||
remote_workflows['new'] = True
|
||||
if as_df:
|
||||
return remote_workflows
|
||||
else:
|
||||
return remote_workflows.reset_index().to_dict(orient='records')
|
||||
|
||||
# merge these on workflow spec id and hash - this will
|
||||
# make two different date columns date_x and date_y
|
||||
different = remote_workflows.merge(local,
|
||||
different = remote_workflows.merge(local,
|
||||
right_on=['workflow_spec_id','md5_hash'],
|
||||
left_on=['workflow_spec_id','md5_hash'],
|
||||
how = 'outer' ,
|
||||
indicator=True).loc[lambda x : x['_merge']!='both']
|
||||
if len(different)==0:
|
||||
return []
|
||||
# each line has a tag on it - if was in the left or the right,
|
||||
# label it so we know if that was on the remote or local machine
|
||||
different.loc[different['_merge']=='left_only','location'] = 'remote'
|
||||
different.loc[different['_merge']=='right_only','location'] = 'local'
|
||||
|
||||
# this takes the different date_created_x and date-created_y columns and
|
||||
# combines them back into one date_created column
|
||||
index = different['date_created_x'].isnull()
|
||||
different.loc[index,'date_created_x'] = different[index]['date_created_y']
|
||||
different = different[['workflow_spec_id','date_created_x','location']].copy()
|
||||
different.columns=['workflow_spec_id','date_created','location']
|
||||
# If there are no differences, then we can just return.
|
||||
if not different.empty:
|
||||
|
||||
# our different list will have multiple entries for a workflow if there is a version on either side
|
||||
# we want to grab the most recent one, so we sort and grab the most recent one for each workflow
|
||||
changedfiles = different.sort_values('date_created',ascending=False).groupby('workflow_spec_id').first()
|
||||
# each line has a tag on it - if was in the left or the right,
|
||||
# label it so we know if that was on the remote or local machine
|
||||
different.loc[different['_merge']=='left_only','location'] = 'remote'
|
||||
different.loc[different['_merge']=='right_only','location'] = 'local'
|
||||
|
||||
# get an exclusive or list of workflow ids - that is we want lists of files that are
|
||||
# on one machine or the other, but not both
|
||||
remote_spec_ids = remote_workflows[['workflow_spec_id']]
|
||||
local_spec_ids = local[['workflow_spec_id']]
|
||||
left = remote_spec_ids[~remote_spec_ids['workflow_spec_id'].isin(local_spec_ids['workflow_spec_id'])]
|
||||
right = local_spec_ids[~local_spec_ids['workflow_spec_id'].isin(remote_spec_ids['workflow_spec_id'])]
|
||||
# this takes the different date_created_x and date-created_y columns and
|
||||
# combines them back into one date_created column
|
||||
index = different['date_created_x'].isnull()
|
||||
different.loc[index,'date_created_x'] = different[index]['date_created_y']
|
||||
different = different[['workflow_spec_id','date_created_x','location']].copy()
|
||||
different.columns=['workflow_spec_id','date_created','location']
|
||||
|
||||
# flag files as new that are only on the remote box and remove the files that are only on the local box
|
||||
changedfiles['new'] = False
|
||||
changedfiles.loc[changedfiles.index.isin(left['workflow_spec_id']), 'new'] = True
|
||||
output = changedfiles[~changedfiles.index.isin(right['workflow_spec_id'])]
|
||||
# our different list will have multiple entries for a workflow if there is a version on either side
|
||||
# we want to grab the most recent one, so we sort and grab the most recent one for each workflow
|
||||
changedfiles = different.sort_values('date_created',ascending=False).groupby('workflow_spec_id').first()
|
||||
|
||||
# get an exclusive or list of workflow ids - that is we want lists of files that are
|
||||
# on one machine or the other, but not both
|
||||
remote_spec_ids = remote_workflows[['workflow_spec_id']]
|
||||
local_spec_ids = local[['workflow_spec_id']]
|
||||
left = remote_spec_ids[~remote_spec_ids['workflow_spec_id'].isin(local_spec_ids['workflow_spec_id'])]
|
||||
right = local_spec_ids[~local_spec_ids['workflow_spec_id'].isin(remote_spec_ids['workflow_spec_id'])]
|
||||
|
||||
# flag files as new that are only on the remote box and remove the files that are only on the local box
|
||||
changedfiles['new'] = False
|
||||
changedfiles.loc[changedfiles.index.isin(left['workflow_spec_id']), 'new'] = True
|
||||
output = changedfiles[~changedfiles.index.isin(right['workflow_spec_id'])]
|
||||
|
||||
else:
|
||||
output = different
|
||||
|
||||
# return the list as a dict, let swagger convert it to json
|
||||
if as_df:
|
||||
@ -366,6 +377,10 @@ def get_all_spec_state_dataframe():
|
||||
else:
|
||||
df = pd.DataFrame(filelist)
|
||||
|
||||
# If the file list is empty, return an empty data frame
|
||||
if df.empty:
|
||||
return df
|
||||
|
||||
# get a distinct list of file_model_id's with the most recent file_data retained
|
||||
df = df.sort_values('date_created').drop_duplicates(['file_model_id'],keep='last').copy()
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
import enum
|
||||
|
||||
import marshmallow
|
||||
from SpiffWorkflow.navigation import NavItem
|
||||
from marshmallow import INCLUDE
|
||||
from marshmallow_enum import EnumField
|
||||
|
||||
@ -15,22 +16,6 @@ class MultiInstanceType(enum.Enum):
|
||||
sequential = "sequential"
|
||||
|
||||
|
||||
class NavigationItem(object):
|
||||
def __init__(self, id, task_id, name, title, backtracks, level, indent, child_count, state, is_decision,
|
||||
task=None, lane=None):
|
||||
self.id = id
|
||||
self.task_id = task_id
|
||||
self.name = name,
|
||||
self.title = title
|
||||
self.backtracks = backtracks
|
||||
self.level = level
|
||||
self.indent = indent
|
||||
self.child_count = child_count
|
||||
self.state = state
|
||||
self.is_decision = is_decision
|
||||
self.task = task
|
||||
self.lane = lane
|
||||
|
||||
class Task(object):
|
||||
|
||||
##########################################################################
|
||||
@ -43,6 +28,7 @@ class Task(object):
|
||||
|
||||
# Autocomplete field
|
||||
FIELD_TYPE_AUTO_COMPLETE = "autocomplete"
|
||||
FIELD_TYPE_AUTO_COMPLETE_MAX = "autocomplete_num" # Not used directly, passed in from the front end.
|
||||
|
||||
# Required field
|
||||
FIELD_CONSTRAINT_REQUIRED = "required"
|
||||
@ -158,15 +144,28 @@ class TaskSchema(ma.Schema):
|
||||
|
||||
class NavigationItemSchema(ma.Schema):
|
||||
class Meta:
|
||||
fields = ["id", "task_id", "name", "title", "backtracks", "level", "indent", "child_count", "state",
|
||||
"is_decision", "task", "lane"]
|
||||
fields = ["spec_id", "name", "spec_type", "task_id", "description", "backtracks", "indent",
|
||||
"lane", "state", "children"]
|
||||
unknown = INCLUDE
|
||||
task = marshmallow.fields.Nested(TaskSchema, dump_only=True, required=False, allow_none=True)
|
||||
state = marshmallow.fields.String(required=False, allow_none=True)
|
||||
description = marshmallow.fields.String(required=False, allow_none=True)
|
||||
backtracks = marshmallow.fields.String(required=False, allow_none=True)
|
||||
lane = marshmallow.fields.String(required=False, allow_none=True)
|
||||
title = marshmallow.fields.String(required=False, allow_none=True)
|
||||
task_id = marshmallow.fields.String(required=False, allow_none=True)
|
||||
children = marshmallow.fields.List(marshmallow.fields.Nested(lambda: NavigationItemSchema()))
|
||||
|
||||
@marshmallow.post_load
|
||||
def make_nav(self, data, **kwargs):
|
||||
state = data.pop('state', None)
|
||||
task_id = data.pop('task_id', None)
|
||||
children = data.pop('children', [])
|
||||
spec_type = data.pop('spec_type', None)
|
||||
item = NavItem(**data)
|
||||
item.state = state
|
||||
item.task_id = task_id
|
||||
item.children = children
|
||||
item.spec_type = spec_type
|
||||
return item
|
||||
|
||||
class WorkflowApi(object):
|
||||
def __init__(self, id, status, next_task, navigation,
|
||||
|
@ -60,7 +60,6 @@ class StudyModel(db.Model):
|
||||
self.last_updated = pbs.DATE_MODIFIED
|
||||
|
||||
self.irb_status = IrbStatus.incomplete_in_protocol_builder
|
||||
self.status = StudyStatus.in_progress
|
||||
|
||||
|
||||
class StudyEvent(db.Model):
|
||||
|
@ -1,3 +1,5 @@
|
||||
import re
|
||||
|
||||
import markdown
|
||||
from jinja2 import Template
|
||||
|
||||
@ -14,23 +16,26 @@ class Email(Script):
|
||||
|
||||
def get_description(self):
|
||||
return """
|
||||
Creates an email, using the provided arguments (a list of UIDs)"
|
||||
Each argument will be used to look up personal information needed for
|
||||
the email creation.
|
||||
|
||||
Creates an email, using the provided arguments. The first argument is the subject of the email,
|
||||
all subsequent arguments should be email addresses in quotes, or variables containing an email address or a list
|
||||
of email addresses."
|
||||
The "documentation" should contain markdown that will become the body of the email message.
|
||||
Example:
|
||||
Email Subject ApprvlApprvr1 PIComputingID
|
||||
email ("My Subject", "dhf8r@virginia.edu", pi.email)
|
||||
"""
|
||||
|
||||
def do_task_validate_only(self, task, *args, **kwargs):
|
||||
self.get_subject(task, args)
|
||||
self.get_users_info(task, args)
|
||||
self.get_email_recipients(task, args)
|
||||
self.get_content(task)
|
||||
|
||||
def do_task(self, task, *args, **kwargs):
|
||||
args = [arg for arg in args if type(arg) == str]
|
||||
subject = self.get_subject(task, args)
|
||||
recipients = self.get_users_info(task, args)
|
||||
def do_task(self, task, study_id, workflow_id, *args, **kwargs):
|
||||
|
||||
if len(args) < 1:
|
||||
raise ApiError(code="missing_argument",
|
||||
message="Email script requires a subject and at least one email address as arguments")
|
||||
subject = args[0]
|
||||
recipients = self.get_email_recipients(task, args)
|
||||
content, content_html = self.get_content(task)
|
||||
if recipients:
|
||||
send_mail(
|
||||
@ -41,44 +46,53 @@ Email Subject ApprvlApprvr1 PIComputingID
|
||||
content_html=content_html
|
||||
)
|
||||
|
||||
def get_users_info(self, task, args):
|
||||
if len(args) < 1:
|
||||
raise ApiError(code="missing_argument",
|
||||
message="Email script requires at least one argument. The "
|
||||
"name of the variable in the task data that contains user"
|
||||
"id to process. Multiple arguments are accepted.")
|
||||
def check_valid_email(self, email):
|
||||
# regex from https://emailregex.com/
|
||||
regex = r"(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)"
|
||||
if (re.search(regex, email)):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def get_email_recipients(self, task, args):
|
||||
emails = []
|
||||
for arg in args:
|
||||
try:
|
||||
uid = task.workflow.script_engine.evaluate_expression(task, arg)
|
||||
except Exception as e:
|
||||
app.logger.error(f'Workflow engines could not parse {arg}', exc_info=True)
|
||||
continue
|
||||
user_info = LdapService.user_info(uid)
|
||||
email = user_info.email_address
|
||||
emails.append(user_info.email_address)
|
||||
if not isinstance(email, str):
|
||||
|
||||
if len(args) < 2:
|
||||
raise ApiError(code="missing_argument",
|
||||
message="Email script requires at least one email address as an argument. "
|
||||
"Multiple email addresses are accepted.")
|
||||
|
||||
# Every argument following the subject should be an email, or a list of emails.
|
||||
for arg in args[1:]:
|
||||
if isinstance(arg, str):
|
||||
emails_to_check = [arg]
|
||||
elif isinstance(arg, list):
|
||||
emails_to_check = arg
|
||||
else:
|
||||
raise ApiError(code="invalid_argument",
|
||||
message="The Email script requires at least 1 UID argument. The "
|
||||
"name of the variable in the task data that contains subject and"
|
||||
" user ids to process. This must point to an array or a string, but "
|
||||
"it currently points to a %s " % emails.__class__.__name__)
|
||||
message=f"Email script requires a valid email address, but received '{arg}'")
|
||||
|
||||
for e in emails_to_check:
|
||||
if self.check_valid_email(e):
|
||||
emails.append(e)
|
||||
else:
|
||||
raise ApiError(code="invalid_argument",
|
||||
message="The email address you provided could not be parsed. "
|
||||
"The value you provided is '%s" % e)
|
||||
|
||||
return emails
|
||||
|
||||
def get_subject(self, task, args):
|
||||
if len(args) < 1:
|
||||
# subject = ''
|
||||
if len(args[0]) < 1:
|
||||
raise ApiError(code="missing_argument",
|
||||
message="Email script requires at least one subject argument. The "
|
||||
"name of the variable in the task data that contains subject"
|
||||
" to process. Multiple arguments are accepted.")
|
||||
message="No subject was provided for the email message.")
|
||||
|
||||
subject = args[0]
|
||||
if not isinstance(subject, str):
|
||||
if not subject or not isinstance(subject, str):
|
||||
raise ApiError(code="invalid_argument",
|
||||
message="The Email script requires 1 argument. The "
|
||||
"the name of the variable in the task data that contains user"
|
||||
"ids to process. This must point to an array or a string, but "
|
||||
"it currently points to a %s " % subject.__class__.__name__)
|
||||
message="The subject you provided could not be parsed. "
|
||||
"The value is \"%s\" " % subject)
|
||||
|
||||
return subject
|
||||
|
||||
|
15
crc/scripts/failing_script.py
Normal file
15
crc/scripts/failing_script.py
Normal file
@ -0,0 +1,15 @@
|
||||
from crc.scripts.script import Script
|
||||
from crc.services.failing_service import FailingService
|
||||
|
||||
|
||||
class FailingScript(Script):
|
||||
|
||||
def get_description(self):
|
||||
return """It fails"""
|
||||
|
||||
def do_task_validate_only(self, task, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def do_task(self, task, *args, **kwargs):
|
||||
|
||||
FailingService.fail_as_service()
|
23
crc/scripts/is_file_uploaded.py
Normal file
23
crc/scripts/is_file_uploaded.py
Normal file
@ -0,0 +1,23 @@
|
||||
from crc.scripts.script import Script
|
||||
from crc.services.file_service import FileService
|
||||
|
||||
|
||||
class IsFileUploaded(Script):
|
||||
|
||||
def get_description(self):
|
||||
return """Test whether a file is uploaded for a study.
|
||||
Pass in the IRB Doc Code for the file."""
|
||||
|
||||
def do_task_validate_only(self, task, study_id, workflow_id, *args, **kwargs):
|
||||
doc_code = args[0]
|
||||
files = FileService.get_files_for_study(study_id)
|
||||
|
||||
def do_task(self, task, study_id, workflow_id, *args, **kwargs):
|
||||
|
||||
files = FileService.get_files_for_study(study_id)
|
||||
if len(files) > 0:
|
||||
doc_code = args[0]
|
||||
for file in files:
|
||||
if doc_code == file.irb_doc_code:
|
||||
return True
|
||||
return False
|
@ -326,7 +326,7 @@ Returns information specific to the protocol.
|
||||
"IS_HGT": None,
|
||||
"IS_IBC": None,
|
||||
"IS_IDE": None,
|
||||
"IS_IND": None,
|
||||
"IS_IND": 1,
|
||||
"IS_MENTAL_IMPAIRMENT_POP": None,
|
||||
"IS_MINOR": None,
|
||||
"IS_MINOR_PARTICIPANT": None,
|
||||
@ -407,9 +407,11 @@ Returns information specific to the protocol.
|
||||
retval = StudyService().get_documents_status(study_id)
|
||||
if cmd == 'protocol':
|
||||
retval = StudyService().get_protocol(study_id)
|
||||
if isinstance(retval, list):
|
||||
retval = [Box(item) for item in retval]
|
||||
if isinstance(retval,dict) and prefix is not None:
|
||||
return Box({x:retval[x] for x in retval.keys() if x[:len(prefix)] == prefix})
|
||||
elif isinstance(retval,dict):
|
||||
elif isinstance(retval,dict) :
|
||||
return Box(retval)
|
||||
else:
|
||||
return retval
|
||||
|
11
crc/services/failing_service.py
Normal file
11
crc/services/failing_service.py
Normal file
@ -0,0 +1,11 @@
|
||||
from crc.api.common import ApiError
|
||||
|
||||
|
||||
class FailingService(object):
|
||||
|
||||
@staticmethod
|
||||
def fail_as_service():
|
||||
"""It fails"""
|
||||
|
||||
raise ApiError(code='bad_service',
|
||||
message='This is my failing service')
|
@ -6,8 +6,8 @@ from github import Github, GithubObject, UnknownObjectException
|
||||
from uuid import UUID
|
||||
from lxml import etree
|
||||
|
||||
import flask
|
||||
from SpiffWorkflow.bpmn.parser.ValidationException import ValidationException
|
||||
from lxml.etree import XMLSyntaxError
|
||||
from pandas import ExcelFile
|
||||
from sqlalchemy import desc
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
@ -35,14 +35,22 @@ class FileService(object):
|
||||
def add_workflow_spec_file(workflow_spec: WorkflowSpecModel,
|
||||
name, content_type, binary_data, primary=False, is_status=False):
|
||||
"""Create a new file and associate it with a workflow spec."""
|
||||
file_model = FileModel(
|
||||
workflow_spec_id=workflow_spec.id,
|
||||
name=name,
|
||||
primary=primary,
|
||||
is_status=is_status,
|
||||
)
|
||||
# Raise ApiError if the file already exists
|
||||
if session.query(FileModel)\
|
||||
.filter(FileModel.workflow_spec_id == workflow_spec.id)\
|
||||
.filter(FileModel.name == name).first():
|
||||
|
||||
return FileService.update_file(file_model, binary_data, content_type)
|
||||
raise ApiError(code="Duplicate File",
|
||||
message='If you want to replace the file, use the update mechanism.')
|
||||
else:
|
||||
file_model = FileModel(
|
||||
workflow_spec_id=workflow_spec.id,
|
||||
name=name,
|
||||
primary=primary,
|
||||
is_status=is_status,
|
||||
)
|
||||
|
||||
return FileService.update_file(file_model, binary_data, content_type)
|
||||
|
||||
@staticmethod
|
||||
def is_allowed_document(code):
|
||||
@ -82,7 +90,7 @@ class FileService(object):
|
||||
you get '1.0' rather than '1'
|
||||
fixme: This is stupid stupid slow. Place it in the database and just check if it is up to date."""
|
||||
data_model = FileService.get_reference_file_data(reference_file_name)
|
||||
xls = ExcelFile(data_model.data)
|
||||
xls = ExcelFile(data_model.data, engine='openpyxl')
|
||||
df = xls.parse(xls.sheet_names[0])
|
||||
for c in int_columns:
|
||||
df[c] = df[c].fillna(0)
|
||||
@ -153,8 +161,11 @@ class FileService(object):
|
||||
|
||||
# If this is a BPMN, extract the process id.
|
||||
if file_model.type == FileType.bpmn:
|
||||
bpmn: etree.Element = etree.fromstring(binary_data)
|
||||
file_model.primary_process_id = FileService.get_process_id(bpmn)
|
||||
try:
|
||||
bpmn: etree.Element = etree.fromstring(binary_data)
|
||||
file_model.primary_process_id = FileService.get_process_id(bpmn)
|
||||
except XMLSyntaxError as xse:
|
||||
raise ApiError("invalid_xml", "Failed to parse xml: " + str(xse), file_name=file_model.name)
|
||||
|
||||
new_file_data_model = FileDataModel(
|
||||
data=binary_data, file_model_id=file_model.id, file_model=file_model,
|
||||
|
@ -21,6 +21,7 @@ import crc
|
||||
from crc import session, app
|
||||
from crc.api.common import ApiError
|
||||
from crc.models.file import FileDataModel, FileModel, FileType
|
||||
from crc.models.task_event import TaskEventModel
|
||||
from crc.models.workflow import WorkflowStatus, WorkflowModel, WorkflowSpecDependencyFile
|
||||
from crc.scripts.script import Script
|
||||
from crc.services.file_service import FileService
|
||||
@ -32,6 +33,14 @@ class CustomBpmnScriptEngine(BpmnScriptEngine):
|
||||
It will execute python code read in from the bpmn. It will also make any scripts in the
|
||||
scripts directory available for execution. """
|
||||
|
||||
def evaluate(self, task, expression):
|
||||
"""
|
||||
Evaluate the given expression, within the context of the given task and
|
||||
return the result.
|
||||
"""
|
||||
return self.evaluate_expression(task, expression)
|
||||
|
||||
|
||||
def execute(self, task: SpiffTask, script, data):
|
||||
|
||||
study_id = task.workflow.data[WorkflowProcessor.STUDY_ID_KEY]
|
||||
@ -100,8 +109,25 @@ class CustomBpmnScriptEngine(BpmnScriptEngine):
|
||||
Evaluate the given expression, within the context of the given task and
|
||||
return the result.
|
||||
"""
|
||||
exp, valid = self.validateExpression(expression)
|
||||
return self._eval(exp, **task.data)
|
||||
study_id = task.workflow.data[WorkflowProcessor.STUDY_ID_KEY]
|
||||
if WorkflowProcessor.WORKFLOW_ID_KEY in task.workflow.data:
|
||||
workflow_id = task.workflow.data[WorkflowProcessor.WORKFLOW_ID_KEY]
|
||||
else:
|
||||
workflow_id = None
|
||||
|
||||
try:
|
||||
if task.workflow.data[WorkflowProcessor.VALIDATION_PROCESS_KEY]:
|
||||
augmentMethods = Script.generate_augmented_validate_list(task, study_id, workflow_id)
|
||||
else:
|
||||
augmentMethods = Script.generate_augmented_list(task, study_id, workflow_id)
|
||||
exp, valid = self.validateExpression(expression)
|
||||
return self._eval(exp, externalMethods=augmentMethods, **task.data)
|
||||
|
||||
except Exception as e:
|
||||
raise WorkflowTaskExecException(task,
|
||||
"Error evaluating expression "
|
||||
"'%s', %s" % (expression, str(e)))
|
||||
|
||||
|
||||
@staticmethod
|
||||
def camel_to_snake(camel):
|
||||
@ -125,19 +151,12 @@ class WorkflowProcessor(object):
|
||||
STUDY_ID_KEY = "study_id"
|
||||
VALIDATION_PROCESS_KEY = "validate_only"
|
||||
|
||||
def __init__(self, workflow_model: WorkflowModel,
|
||||
soft_reset=False, hard_reset=False, validate_only=False):
|
||||
"""Create a Workflow Processor based on the serialized information available in the workflow model.
|
||||
If soft_reset is set to true, it will try to use the latest version of the workflow specification
|
||||
without resetting to the beginning of the workflow. This will work for some minor changes to the spec.
|
||||
If hard_reset is set to true, it will use the latest spec, and start the workflow over from the beginning.
|
||||
which should work in casees where a soft reset fails.
|
||||
If neither flag is set, it will use the same version of the specification that was used to originally
|
||||
create the workflow model. """
|
||||
def __init__(self, workflow_model: WorkflowModel, validate_only=False):
|
||||
"""Create a Workflow Processor based on the serialized information available in the workflow model."""
|
||||
|
||||
self.workflow_model = workflow_model
|
||||
|
||||
if soft_reset or len(workflow_model.dependencies) == 0: # Depenencies of 0 means the workflow was never started.
|
||||
if workflow_model.bpmn_workflow_json is None: # The workflow was never started.
|
||||
self.spec_data_files = FileService.get_spec_data_files(
|
||||
workflow_spec_id=workflow_model.workflow_spec_id)
|
||||
spec = self.get_spec(self.spec_data_files, workflow_model.workflow_spec_id)
|
||||
@ -162,22 +181,14 @@ class WorkflowProcessor(object):
|
||||
# can then load data as needed.
|
||||
self.bpmn_workflow.data[WorkflowProcessor.WORKFLOW_ID_KEY] = workflow_model.id
|
||||
workflow_model.bpmn_workflow_json = WorkflowProcessor._serializer.serialize_workflow(
|
||||
self.bpmn_workflow,include_spec=True)
|
||||
self.bpmn_workflow, include_spec=True)
|
||||
self.save()
|
||||
|
||||
except MissingSpecError as ke:
|
||||
raise ApiError(code="unexpected_workflow_structure",
|
||||
message="Failed to deserialize workflow"
|
||||
" '%s' version %s, due to a mis-placed or missing task '%s'" %
|
||||
(self.workflow_spec_id, self.get_version_string(), str(ke)) +
|
||||
" This is very likely due to a soft reset where there was a structural change.")
|
||||
if hard_reset:
|
||||
# Now that the spec is loaded, get the data and rebuild the bpmn with the new details
|
||||
self.hard_reset()
|
||||
workflow_model.bpmn_workflow_json = WorkflowProcessor._serializer.serialize_workflow(self.bpmn_workflow)
|
||||
self.save()
|
||||
if soft_reset:
|
||||
self.save()
|
||||
(self.workflow_spec_id, self.get_version_string(), str(ke)))
|
||||
|
||||
# set whether this is the latest spec file.
|
||||
if self.spec_data_files == FileService.get_spec_data_files(workflow_spec_id=workflow_model.workflow_spec_id):
|
||||
@ -185,6 +196,21 @@ class WorkflowProcessor(object):
|
||||
else:
|
||||
self.is_latest_spec = False
|
||||
|
||||
@classmethod
|
||||
def reset(cls, workflow_model, clear_data=False):
|
||||
print('WorkflowProcessor: reset: ')
|
||||
|
||||
workflow_model.bpmn_workflow_json = None
|
||||
if clear_data:
|
||||
# Clear form_data from task_events
|
||||
task_events = session.query(TaskEventModel). \
|
||||
filter(TaskEventModel.workflow_id == workflow_model.id).all()
|
||||
for task_event in task_events:
|
||||
task_event.form_data = {}
|
||||
session.add(task_event)
|
||||
session.commit()
|
||||
return cls(workflow_model)
|
||||
|
||||
def __get_bpmn_workflow(self, workflow_model: WorkflowModel, spec: WorkflowSpec, validate_only=False):
|
||||
if workflow_model.bpmn_workflow_json:
|
||||
bpmn_workflow = self._serializer.deserialize_workflow(workflow_model.bpmn_workflow_json,
|
||||
@ -330,18 +356,18 @@ class WorkflowProcessor(object):
|
||||
else:
|
||||
return WorkflowStatus.waiting
|
||||
|
||||
def hard_reset(self):
|
||||
"""Recreate this workflow. This will be useful when a workflow specification changes.
|
||||
"""
|
||||
self.spec_data_files = FileService.get_spec_data_files(workflow_spec_id=self.workflow_spec_id)
|
||||
new_spec = WorkflowProcessor.get_spec(self.spec_data_files, self.workflow_spec_id)
|
||||
new_bpmn_workflow = BpmnWorkflow(new_spec, script_engine=self._script_engine)
|
||||
new_bpmn_workflow.data = self.bpmn_workflow.data
|
||||
try:
|
||||
new_bpmn_workflow.do_engine_steps()
|
||||
except WorkflowException as we:
|
||||
raise ApiError.from_task_spec("hard_reset_engine_steps_error", str(we), we.sender)
|
||||
self.bpmn_workflow = new_bpmn_workflow
|
||||
# def hard_reset(self):
|
||||
# """Recreate this workflow. This will be useful when a workflow specification changes.
|
||||
# """
|
||||
# self.spec_data_files = FileService.get_spec_data_files(workflow_spec_id=self.workflow_spec_id)
|
||||
# new_spec = WorkflowProcessor.get_spec(self.spec_data_files, self.workflow_spec_id)
|
||||
# new_bpmn_workflow = BpmnWorkflow(new_spec, script_engine=self._script_engine)
|
||||
# new_bpmn_workflow.data = self.bpmn_workflow.data
|
||||
# try:
|
||||
# new_bpmn_workflow.do_engine_steps()
|
||||
# except WorkflowException as we:
|
||||
# raise ApiError.from_task_spec("hard_reset_engine_steps_error", str(we), we.sender)
|
||||
# self.bpmn_workflow = new_bpmn_workflow
|
||||
|
||||
def get_status(self):
|
||||
return self.status_of(self.bpmn_workflow)
|
||||
@ -352,6 +378,12 @@ class WorkflowProcessor(object):
|
||||
except WorkflowTaskExecException as we:
|
||||
raise ApiError.from_task("task_error", str(we), we.task)
|
||||
|
||||
def cancel_notify(self):
|
||||
try:
|
||||
self.bpmn_workflow.cancel_notify()
|
||||
except WorkflowTaskExecException as we:
|
||||
raise ApiError.from_task("task_error", str(we), we.task)
|
||||
|
||||
def serialize(self):
|
||||
return self._serializer.serialize_workflow(self.bpmn_workflow,include_spec=True)
|
||||
|
||||
|
@ -1,13 +1,12 @@
|
||||
import copy
|
||||
import json
|
||||
import string
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
import random
|
||||
import string
|
||||
from datetime import datetime
|
||||
from typing import List
|
||||
|
||||
import jinja2
|
||||
from SpiffWorkflow import Task as SpiffTask, WorkflowException
|
||||
from SpiffWorkflow import Task as SpiffTask, WorkflowException, NavItem
|
||||
from SpiffWorkflow.bpmn.specs.EndEvent import EndEvent
|
||||
from SpiffWorkflow.bpmn.specs.ManualTask import ManualTask
|
||||
from SpiffWorkflow.bpmn.specs.MultiInstanceTask import MultiInstanceTask
|
||||
@ -15,17 +14,16 @@ from SpiffWorkflow.bpmn.specs.ScriptTask import ScriptTask
|
||||
from SpiffWorkflow.bpmn.specs.StartEvent import StartEvent
|
||||
from SpiffWorkflow.bpmn.specs.UserTask import UserTask
|
||||
from SpiffWorkflow.dmn.specs.BusinessRuleTask import BusinessRuleTask
|
||||
from SpiffWorkflow.specs import CancelTask, StartTask
|
||||
from SpiffWorkflow.specs import CancelTask, StartTask, MultiChoice
|
||||
from SpiffWorkflow.util.deep_merge import DeepMerge
|
||||
from flask import g
|
||||
from jinja2 import Template
|
||||
|
||||
from crc import db, app
|
||||
from crc.api.common import ApiError
|
||||
from crc.models.api_models import Task, MultiInstanceType, NavigationItem, NavigationItemSchema, WorkflowApi
|
||||
from crc.models.api_models import Task, MultiInstanceType, WorkflowApi
|
||||
from crc.models.file import LookupDataModel
|
||||
from crc.models.task_event import TaskEventModel
|
||||
from crc.models.study import StudyModel
|
||||
from crc.models.task_event import TaskEventModel
|
||||
from crc.models.user import UserModel, UserModelSchema
|
||||
from crc.models.workflow import WorkflowModel, WorkflowStatus, WorkflowSpecModel
|
||||
from crc.services.file_service import FileService
|
||||
@ -98,6 +96,7 @@ class WorkflowService(object):
|
||||
|
||||
while not processor.bpmn_workflow.is_completed():
|
||||
try:
|
||||
processor.bpmn_workflow.get_deep_nav_list() # Assure no errors with navigation.
|
||||
processor.bpmn_workflow.do_engine_steps()
|
||||
tasks = processor.bpmn_workflow.get_tasks(SpiffTask.READY)
|
||||
for task in tasks:
|
||||
@ -116,6 +115,7 @@ class WorkflowService(object):
|
||||
raise ApiError.from_workflow_exception("workflow_validation_exception", str(we), we)
|
||||
|
||||
WorkflowService.delete_test_data()
|
||||
WorkflowService._process_documentation(processor.bpmn_workflow.last_task.parent.parent)
|
||||
return processor.bpmn_workflow.last_task.data
|
||||
|
||||
@staticmethod
|
||||
@ -127,6 +127,19 @@ class WorkflowService(object):
|
||||
form_data = task.data # Just like with the front end, we start with what was already there, and modify it.
|
||||
hide_groups = []
|
||||
for field in task_api.form.fields:
|
||||
# Assure we have a field type
|
||||
if field.type is None:
|
||||
raise ApiError(code='invalid_form_data',
|
||||
message = f'Type is missing for field "{field.id}". A field type must be provided.',
|
||||
task_id = task.id,
|
||||
task_name = task.get_name())
|
||||
# Assure we have valid ids
|
||||
if not WorkflowService.check_field_id(field.id):
|
||||
raise ApiError(code='invalid_form_id',
|
||||
message=f'Invalid Field name: "{field.id}". A field ID must begin with a letter, '
|
||||
f'and can only contain letters, numbers, and "_"',
|
||||
task_id = task.id,
|
||||
task_name = task.get_name())
|
||||
# Assure field has valid properties
|
||||
WorkflowService.check_field_properties(field, task)
|
||||
|
||||
@ -175,6 +188,18 @@ class WorkflowService(object):
|
||||
task.data = {}
|
||||
task.data.update(form_data)
|
||||
|
||||
@staticmethod
|
||||
def check_field_id(id):
|
||||
"""Assures that field names are valid Python and Javascript names."""
|
||||
if not id[0].isalpha():
|
||||
return False
|
||||
for char in id[1:len(id)]:
|
||||
if char.isalnum() or char == '_':
|
||||
pass
|
||||
else:
|
||||
return False
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def check_field_properties(field, task):
|
||||
"""Assures that all properties are valid on a given workflow."""
|
||||
@ -320,33 +345,9 @@ class WorkflowService(object):
|
||||
"""Returns an API model representing the state of the current workflow, if requested, and
|
||||
possible, next_task is set to the current_task."""
|
||||
|
||||
nav_dict = processor.bpmn_workflow.get_nav_list()
|
||||
navigation = processor.bpmn_workflow.get_deep_nav_list()
|
||||
WorkflowService.update_navigation(navigation, processor)
|
||||
|
||||
# Some basic cleanup of the title for the for the navigation.
|
||||
navigation = []
|
||||
for nav_item in nav_dict:
|
||||
spiff_task = processor.bpmn_workflow.get_task(nav_item['task_id'])
|
||||
if 'description' in nav_item:
|
||||
nav_item['title'] = nav_item.pop('description')
|
||||
# fixme: duplicate code from the workflow_service. Should only do this in one place.
|
||||
if nav_item['title'] is not None and ' ' in nav_item['title']:
|
||||
nav_item['title'] = nav_item['title'].partition(' ')[2]
|
||||
else:
|
||||
nav_item['title'] = ""
|
||||
if spiff_task:
|
||||
nav_item['task'] = WorkflowService.spiff_task_to_api_task(spiff_task, add_docs_and_forms=False)
|
||||
nav_item['title'] = nav_item['task'].title # Prefer the task title.
|
||||
|
||||
user_uids = WorkflowService.get_users_assigned_to_task(processor, spiff_task)
|
||||
if not UserService.in_list(user_uids, allow_admin_impersonate=True):
|
||||
nav_item['state'] = WorkflowService.TASK_STATE_LOCKED
|
||||
|
||||
else:
|
||||
nav_item['task'] = None
|
||||
|
||||
|
||||
navigation.append(NavigationItem(**nav_item))
|
||||
NavigationItemSchema().dump(nav_item)
|
||||
|
||||
spec = db.session.query(WorkflowSpecModel).filter_by(id=processor.workflow_spec_id).first()
|
||||
workflow_api = WorkflowApi(
|
||||
@ -375,6 +376,29 @@ class WorkflowService(object):
|
||||
workflow_api.next_task.state = WorkflowService.TASK_STATE_LOCKED
|
||||
return workflow_api
|
||||
|
||||
@staticmethod
|
||||
def update_navigation(navigation: List[NavItem], processor: WorkflowProcessor):
|
||||
# Recursive function to walk down through children, and clean up descriptions, and statuses
|
||||
for nav_item in navigation:
|
||||
spiff_task = processor.bpmn_workflow.get_task(nav_item.task_id)
|
||||
if spiff_task:
|
||||
# Use existing logic to set the description, and alter the state based on permissions.
|
||||
api_task = WorkflowService.spiff_task_to_api_task(spiff_task, add_docs_and_forms=False)
|
||||
nav_item.description = api_task.title
|
||||
user_uids = WorkflowService.get_users_assigned_to_task(processor, spiff_task)
|
||||
if (isinstance(spiff_task.task_spec, UserTask) or isinstance(spiff_task.task_spec, ManualTask)) \
|
||||
and not UserService.in_list(user_uids, allow_admin_impersonate=True):
|
||||
nav_item.state = WorkflowService.TASK_STATE_LOCKED
|
||||
else:
|
||||
# Strip off the first word in the description, to meet guidlines for BPMN.
|
||||
if nav_item.description:
|
||||
if nav_item.description is not None and ' ' in nav_item.description:
|
||||
nav_item.description = nav_item.description.partition(' ')[2]
|
||||
|
||||
# Recurse here
|
||||
WorkflowService.update_navigation(nav_item.children, processor)
|
||||
|
||||
|
||||
@staticmethod
|
||||
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."""
|
||||
@ -402,6 +426,7 @@ class WorkflowService(object):
|
||||
return {}
|
||||
|
||||
|
||||
|
||||
@staticmethod
|
||||
def spiff_task_to_api_task(spiff_task, add_docs_and_forms=False):
|
||||
task_type = spiff_task.task_spec.__class__.__name__
|
||||
@ -561,6 +586,13 @@ class WorkflowService(object):
|
||||
items = data_model.items() if isinstance(data_model, dict) else data_model
|
||||
options = []
|
||||
for item in items:
|
||||
if value_column not in item:
|
||||
raise ApiError.from_task("invalid_enum", f"The value column '{value_column}' does not exist for item {item}",
|
||||
task=spiff_task)
|
||||
if label_column not in item:
|
||||
raise ApiError.from_task("invalid_enum", f"The label column '{label_column}' does not exist for item {item}",
|
||||
task=spiff_task)
|
||||
|
||||
options.append({"id": item[value_column], "name": item[label_column], "data": item})
|
||||
return options
|
||||
|
||||
|
@ -1,11 +1,12 @@
|
||||
<?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" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="Definitions_06pyjz2" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="4.2.0">
|
||||
<bpmn:process id="Process_01143nb" name="PI's Pr" isExecutable="true">
|
||||
<bpmn:process id="UserTask_ShowInvalidUIDs" isExecutable="true">
|
||||
<bpmn:startEvent id="StartEvent_1">
|
||||
<bpmn:outgoing>Flow_0kcrx5l</bpmn:outgoing>
|
||||
</bpmn:startEvent>
|
||||
<bpmn:scriptTask id="ScriptTask_LoadPersonnel" name="Load IRB Personnel">
|
||||
<bpmn:incoming>Flow_0kcrx5l</bpmn:incoming>
|
||||
<bpmn:incoming>Flow_00zanzw</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_1dcsioh</bpmn:outgoing>
|
||||
<bpmn:script>current_user = ldap()
|
||||
investigators = study_info('investigators')
|
||||
@ -15,11 +16,11 @@ is_cu_pi = False
|
||||
if pi != None:
|
||||
hasPI = True
|
||||
if pi.get('uid', None) != None:
|
||||
pi_has_uid = True
|
||||
pi_invalid_uid = False
|
||||
if pi['uid'] == current_user['uid']:
|
||||
is_cu_pi = True
|
||||
else:
|
||||
pi_has_uid = False
|
||||
pi_invalid_uid = True
|
||||
else:
|
||||
hasPI = False
|
||||
|
||||
@ -27,9 +28,11 @@ else:
|
||||
dc = investigators.get('DEPT_CH', None)
|
||||
if dc != None:
|
||||
if dc.get('uid', None) != None:
|
||||
dc_has_uid = True
|
||||
dc_invalid_uid = False
|
||||
else:
|
||||
dc_has_uid = False
|
||||
dc_invalid_uid = True
|
||||
else:
|
||||
dc_invalid_uid = False
|
||||
|
||||
# Primary Coordinators
|
||||
pcs = {}
|
||||
@ -39,13 +42,19 @@ for k in investigators.keys():
|
||||
if k in ['SC_I','SC_II','IRBC']:
|
||||
investigator = investigators.get(k)
|
||||
if investigator.get('uid', None) != None:
|
||||
cnt_pcs_uid = cnt_pcs_uid + 1
|
||||
if investigator['uid'] != current_user['uid']:
|
||||
pcs[k] = investigator
|
||||
cnt_pcs_uid = cnt_pcs_uid + 1
|
||||
else:
|
||||
is_cu_pc = True
|
||||
is_cu_pc_role = investigator['label']
|
||||
else:
|
||||
pcs[k] = investigator
|
||||
cnt_pcs = len(pcs.keys())
|
||||
if cnt_pcs != cnt_pcs_uid:
|
||||
pcs_invalid_uid = True
|
||||
else:
|
||||
pcs_invalid_uid = False
|
||||
if cnt_pcs > 0:
|
||||
del(k)
|
||||
del(investigator)
|
||||
@ -58,13 +67,19 @@ for k in investigators.keys():
|
||||
if k == 'AS_C':
|
||||
investigator = investigators.get(k)
|
||||
if investigator.get('uid', None) != None:
|
||||
cnt_acs_uid = cnt_acs_uid + 1
|
||||
if investigator['uid'] != current_user['uid']:
|
||||
acs[k] = investigator
|
||||
cnt_acs_uid = cnt_acs_uid + 1
|
||||
else:
|
||||
is_cu_ac = True
|
||||
is_cu_ac_role = investigator['label']
|
||||
else:
|
||||
acs[k] = investigator
|
||||
cnt_acs = len(acs.keys())
|
||||
if cnt_pcs != cnt_pcs_uid:
|
||||
acs_invalid_uid = True
|
||||
else:
|
||||
acs_invalid_uid = False
|
||||
if cnt_acs > 0:
|
||||
del(k)
|
||||
del(investigator)
|
||||
@ -77,12 +92,18 @@ for k in investigators.keys():
|
||||
if k[:2] == 'SI':
|
||||
investigator = investigators.get(k)
|
||||
if investigator.get('uid', None) != None:
|
||||
cnt_subs_uid = cnt_subs_uid + 1
|
||||
if investigator['uid'] != current_user['uid']:
|
||||
subs[k] = investigator
|
||||
cnt_subs_uid = cnt_subs_uid + 1
|
||||
else:
|
||||
is_cu_subs = True
|
||||
else:
|
||||
subs[k] = investigator
|
||||
cnt_subs = len(subs.keys())
|
||||
if cnt_subs != cnt_subs_uid:
|
||||
subs_invalid_uid = True
|
||||
else:
|
||||
subs_invalid_uid = False
|
||||
if cnt_subs > 0:
|
||||
del(k)
|
||||
del(investigator)
|
||||
@ -95,13 +116,19 @@ for k in investigators.keys():
|
||||
if k in ['SCI','DC']:
|
||||
investigator = investigators.get(k)
|
||||
if investigator.get('uid', None) != None:
|
||||
cnt_aps_uid = cnt_aps_uid + 1
|
||||
if investigator['uid'] != current_user['uid']:
|
||||
aps[k] = investigator
|
||||
cnt_aps_uid = cnt_aps_uid + 1
|
||||
else:
|
||||
is_cu_ap = True
|
||||
is_cu_ap_role = investigator['label']
|
||||
else:
|
||||
aps[k] = investigator
|
||||
cnt_aps = len(aps.keys())
|
||||
if cnt_aps != cnt_aps_uid:
|
||||
aps_invalid_uid = True
|
||||
else:
|
||||
aps_invalid_uid = False
|
||||
if cnt_aps > 0:
|
||||
del(k)
|
||||
del(investigator)
|
||||
@ -132,12 +159,12 @@ Since you are the person entering this information, you already have access and
|
||||
<camunda:property id="rows" value="5" />
|
||||
</camunda:properties>
|
||||
</camunda:formField>
|
||||
<camunda:formField id="pi.access" label="Should the Principal Investigator have full editing access in the system?" type="boolean" defaultValue="true">
|
||||
<camunda:formField id="pi.access" label="Should the Principal Investigator have full editing access in the system?" type="boolean" defaultValue="True">
|
||||
<camunda:properties>
|
||||
<camunda:property id="hide_expression" value="is_cu_pi" />
|
||||
</camunda:properties>
|
||||
</camunda:formField>
|
||||
<camunda:formField id="pi.emails" label="Should the Principal Investigator receive automated email notifications?" type="boolean" defaultValue="true">
|
||||
<camunda:formField id="pi.emails" label="Should the Principal Investigator receive automated email notifications?" type="boolean" defaultValue="True">
|
||||
<camunda:properties>
|
||||
<camunda:property id="hide_expression" value="is_cu_pi" />
|
||||
</camunda:properties>
|
||||
@ -160,23 +187,23 @@ Since you are the person entering this information, you already have access and
|
||||
</bpmn:userTask>
|
||||
<bpmn:sequenceFlow id="Flow_0kcrx5l" sourceRef="StartEvent_1" targetRef="ScriptTask_LoadPersonnel" />
|
||||
<bpmn:sequenceFlow id="Flow_1dcsioh" sourceRef="ScriptTask_LoadPersonnel" targetRef="Gateway_CheckForPI" />
|
||||
<bpmn:exclusiveGateway id="Gateway_CheckForPI" name="PI Cnt" default="Flow_147b9li">
|
||||
<bpmn:exclusiveGateway id="Gateway_CheckForPI" name="PI With Valid UID?" default="Flow_147b9li">
|
||||
<bpmn:incoming>Flow_1dcsioh</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_147b9li</bpmn:outgoing>
|
||||
<bpmn:outgoing>Flow_00prawo</bpmn:outgoing>
|
||||
</bpmn:exclusiveGateway>
|
||||
<bpmn:sequenceFlow id="Flow_147b9li" name="1 PI from PB" sourceRef="Gateway_CheckForPI" targetRef="ScriptTask_DeterminePI_E0_Department" />
|
||||
<bpmn:sequenceFlow id="Flow_00prawo" name="No PI from PB" sourceRef="Gateway_CheckForPI" targetRef="Activity_1qwzwyi">
|
||||
<bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">not(hasPI) or (hasPI and not(pi_has_uid))</bpmn:conditionExpression>
|
||||
<bpmn:sequenceFlow id="Flow_147b9li" name="Yes" sourceRef="Gateway_CheckForPI" targetRef="Gateway_CheckUIDs" />
|
||||
<bpmn:sequenceFlow id="Flow_00prawo" name="No" sourceRef="Gateway_CheckForPI" targetRef="Activity_1qwzwyi">
|
||||
<bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">not(hasPI) or (hasPI and pi_invalid_uid)</bpmn:conditionExpression>
|
||||
</bpmn:sequenceFlow>
|
||||
<bpmn:manualTask id="Activity_1qwzwyi" name="Show No PI">
|
||||
<bpmn:manualTask id="Activity_1qwzwyi" name="Show No PI or Invalid UID">
|
||||
<bpmn:documentation>No PI entered in PB</bpmn:documentation>
|
||||
<bpmn:incoming>Flow_00prawo</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_16qr5jf</bpmn:outgoing>
|
||||
</bpmn:manualTask>
|
||||
<bpmn:exclusiveGateway id="Gateway_0jykh6r" name="How many Primary Coordinators?" default="Flow_0xifvai">
|
||||
<bpmn:incoming>Flow_0kpe12r</bpmn:incoming>
|
||||
<bpmn:incoming>SequenceFlow_0cdtt11</bpmn:incoming>
|
||||
<bpmn:incoming>Flow_1ayisx2</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_0xifvai</bpmn:outgoing>
|
||||
<bpmn:outgoing>Flow_1oqem42</bpmn:outgoing>
|
||||
</bpmn:exclusiveGateway>
|
||||
@ -210,7 +237,8 @@ Otherwise, edit each Coordinator as necessary and select the Save button for eac
|
||||
<bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">cnt_pcs == 0</bpmn:conditionExpression>
|
||||
</bpmn:sequenceFlow>
|
||||
<bpmn:scriptTask id="ScriptTask_DeterminePI_E0_Department" name="Determine PI E0 Department">
|
||||
<bpmn:incoming>Flow_147b9li</bpmn:incoming>
|
||||
<bpmn:incoming>Flow_0tfprc8</bpmn:incoming>
|
||||
<bpmn:incoming>Flow_0tsdclr</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_1grahhv</bpmn:outgoing>
|
||||
<bpmn:script>LDAP_dept = pi.department
|
||||
length_LDAP_dept = len(LDAP_dept)
|
||||
@ -274,36 +302,12 @@ else:
|
||||
<bpmn:incoming>Flow_0w4d2bz</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_1oo0ijr</bpmn:outgoing>
|
||||
</bpmn:businessRuleTask>
|
||||
<bpmn:userTask id="UserTask_109otvi" name="Update Chair Info" camunda:formKey="RO_Chair_Info">
|
||||
<bpmn:documentation>***Name & Degree:*** {{ RO_Chair_Name_Degree }}
|
||||
***School:*** {{ RO_School }}
|
||||
***Department:*** {{ RO_Department }}
|
||||
***Title:*** {{ RO_Chair_Title }}
|
||||
***Email:*** {{ RO_Chair_CID }}
|
||||
|
||||
|
||||
{% if RO_Chair_CID != dc.uid %}
|
||||
*Does not match the Department Chair specified in Protocol Builder, {{ dc.display_name }}*
|
||||
{% endif %}</bpmn:documentation>
|
||||
<bpmn:extensionElements>
|
||||
<camunda:formData>
|
||||
<camunda:formField id="RO_ChairAccess" label="Should the Department Chair have full editing access in the system?" type="boolean" defaultValue="false" />
|
||||
<camunda:formField id="RO_ChairEmails" label="Should the Department Chair receive automated email notifications?" type="boolean" defaultValue="false" />
|
||||
</camunda:formData>
|
||||
<camunda:properties>
|
||||
<camunda:property name="display_name" value=""Responsible Organization's Chair Info"" />
|
||||
</camunda:properties>
|
||||
</bpmn:extensionElements>
|
||||
<bpmn:incoming>Flow_0vi6thu</bpmn:incoming>
|
||||
<bpmn:outgoing>SequenceFlow_0cdtt11</bpmn:outgoing>
|
||||
</bpmn:userTask>
|
||||
<bpmn:sequenceFlow id="SequenceFlow_0cdtt11" sourceRef="UserTask_109otvi" targetRef="Gateway_0jykh6r" />
|
||||
<bpmn:exclusiveGateway id="Gateway_PI_is_DeptChair" name="PI is Dept Chair?" default="Flow_0vi6thu">
|
||||
<bpmn:incoming>Flow_070j5fg</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_0vi6thu</bpmn:outgoing>
|
||||
<bpmn:outgoing>Flow_00yhlrq</bpmn:outgoing>
|
||||
</bpmn:exclusiveGateway>
|
||||
<bpmn:sequenceFlow id="Flow_0vi6thu" name="No" sourceRef="Gateway_PI_is_DeptChair" targetRef="UserTask_109otvi" />
|
||||
<bpmn:sequenceFlow id="Flow_0vi6thu" name="No" sourceRef="Gateway_PI_is_DeptChair" targetRef="Activity_1sffono" />
|
||||
<bpmn:sequenceFlow id="Flow_00yhlrq" name="Yes" sourceRef="Gateway_PI_is_DeptChair" targetRef="Activity_ShowPI_is_DeptChair">
|
||||
<bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">RO_Chair_CID == pi.uid</bpmn:conditionExpression>
|
||||
</bpmn:sequenceFlow>
|
||||
@ -360,6 +364,7 @@ Otherwise, edit each Sub-Investigator as necessary and select the Save button fo
|
||||
<bpmn:outgoing>Flow_1kg5jot</bpmn:outgoing>
|
||||
<bpmn:script>pi.E0.schoolName = PI_E0_schoolName
|
||||
pi.E0.deptName = PI_E0_deptName
|
||||
pi.experience = user_data_get("pi_experience","")
|
||||
ro = {}
|
||||
ro['chair'] = {}</bpmn:script>
|
||||
</bpmn:scriptTask>
|
||||
@ -605,17 +610,17 @@ ro.schoolAbbrv = RO_StudySchool.value</bpmn:script>
|
||||
<bpmn:outgoing>Flow_0vff9k5</bpmn:outgoing>
|
||||
</bpmn:exclusiveGateway>
|
||||
<bpmn:sequenceFlow id="Flow_0vff9k5" sourceRef="Gateway_0zd7syo" targetRef="BusinessRuleTask_Determine_RO_Chair" />
|
||||
<bpmn:exclusiveGateway id="Gateway_13k761k" name="How many Additional Personnel? " default="Flow_0kp47dz">
|
||||
<bpmn:exclusiveGateway id="Gateway_13k761k" name="How many Additional Personnel? " default="Flow_0q56tn8">
|
||||
<bpmn:incoming>Flow_0ofpgml</bpmn:incoming>
|
||||
<bpmn:incoming>Flow_0jxzqw1</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_0q56tn8</bpmn:outgoing>
|
||||
<bpmn:outgoing>Flow_0kp47dz</bpmn:outgoing>
|
||||
</bpmn:exclusiveGateway>
|
||||
<bpmn:sequenceFlow id="Flow_0q56tn8" sourceRef="Gateway_13k761k" targetRef="Activity_1sra1vn">
|
||||
<bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">cnt_aps > 0</bpmn:conditionExpression>
|
||||
</bpmn:sequenceFlow>
|
||||
<bpmn:sequenceFlow id="Flow_0q56tn8" sourceRef="Gateway_13k761k" targetRef="Activity_1sra1vn" />
|
||||
<bpmn:sequenceFlow id="Flow_10zn0h1" sourceRef="Activity_1sra1vn" targetRef="EndEvent_1qor16n" />
|
||||
<bpmn:sequenceFlow id="Flow_0kp47dz" sourceRef="Gateway_13k761k" targetRef="EndEvent_1qor16n" />
|
||||
<bpmn:sequenceFlow id="Flow_0kp47dz" sourceRef="Gateway_13k761k" targetRef="EndEvent_1qor16n">
|
||||
<bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">cnt_aps == 0</bpmn:conditionExpression>
|
||||
</bpmn:sequenceFlow>
|
||||
<bpmn:userTask id="Activity_1sra1vn" name="Update Additional Personnel Info" camunda:formKey="AP_AccessEmails">
|
||||
<bpmn:documentation>The following Additional Personnel were entered in Protocol Builder:
|
||||
{%+ for key, value in aps.items() %}{{value.display_name}} ({{key}}){% if loop.index is lt cnt_aps %}, {% endif %}{% endfor %}
|
||||
@ -646,400 +651,545 @@ Otherwise, edit each Additional Personnel as necessary and select the Save butto
|
||||
<bpmn:outgoing>Flow_10zn0h1</bpmn:outgoing>
|
||||
<bpmn:multiInstanceLoopCharacteristics camunda:collection="aps" camunda:elementVariable="ap" />
|
||||
</bpmn:userTask>
|
||||
<bpmn:exclusiveGateway id="Gateway_CheckUIDs" name="Invalid UIDs?" default="Flow_0tfprc8">
|
||||
<bpmn:incoming>Flow_147b9li</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_0tfprc8</bpmn:outgoing>
|
||||
<bpmn:outgoing>Flow_0nz62mu</bpmn:outgoing>
|
||||
</bpmn:exclusiveGateway>
|
||||
<bpmn:sequenceFlow id="Flow_0tfprc8" name="No" sourceRef="Gateway_CheckUIDs" targetRef="ScriptTask_DeterminePI_E0_Department" />
|
||||
<bpmn:sequenceFlow id="Flow_0nz62mu" name="Yes" sourceRef="Gateway_CheckUIDs" targetRef="Activity_19z6vct">
|
||||
<bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">dc_invalid_uid or pcs_invalid_uid or acs_invalid_uid or subs_invalid_uid or aps_invalid_uid</bpmn:conditionExpression>
|
||||
</bpmn:sequenceFlow>
|
||||
<bpmn:userTask id="Activity_19z6vct" name="Show Invalid UIDs" camunda:formKey="ShowInvalidUIDs">
|
||||
<bpmn:documentation>Select No if all displayed invalid Computing IDs do not need system access and/or receive emails. If they do, correct in Protocol Builder first and then select Yes.
|
||||
|
||||
|
||||
{% if dc_invalid_uid %}
|
||||
Department Chair
|
||||
{{ dc.error }}
|
||||
{% endif %}
|
||||
{% if pcs_invalid_uid %}
|
||||
Primary Coordinators
|
||||
{% for k, pc in pcs.items() %}
|
||||
{% if pc.get('uid', None) == None: %}
|
||||
{{ pc.error }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if acs_invalid_uid %}
|
||||
Additional Coordinators
|
||||
{% for k, ac in acs.items() %}
|
||||
{% if ac.get('uid', None) == None: %}
|
||||
{{ ac.error }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if subs_invalid_uid %}
|
||||
Sub-Investigators
|
||||
{% for k, sub in subs.items() %}
|
||||
{% if sub.get('uid', None) == None: %}
|
||||
{{ sub.error }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if aps_invalid_uid %}
|
||||
Additional Personnnel
|
||||
{% for k, ap in aps.items() %}
|
||||
{% if ap.get('uid', None) == None: %}
|
||||
{{ ap.error }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}</bpmn:documentation>
|
||||
<bpmn:extensionElements>
|
||||
<camunda:formData>
|
||||
<camunda:formField id="FixInvalidUIDs" label="Do you want to fix?" type="boolean">
|
||||
<camunda:properties>
|
||||
<camunda:property id="description" value="Select Yes if you have corrected in Protocol Builder, No if you would like to proceed without correcting." />
|
||||
</camunda:properties>
|
||||
</camunda:formField>
|
||||
</camunda:formData>
|
||||
</bpmn:extensionElements>
|
||||
<bpmn:incoming>Flow_0nz62mu</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_16bkbuc</bpmn:outgoing>
|
||||
</bpmn:userTask>
|
||||
<bpmn:exclusiveGateway id="Gateway_FixInvalidUIDs" name="Fix Invalid UIDs?" default="Flow_00zanzw">
|
||||
<bpmn:incoming>Flow_16bkbuc</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_00zanzw</bpmn:outgoing>
|
||||
<bpmn:outgoing>Flow_0tsdclr</bpmn:outgoing>
|
||||
</bpmn:exclusiveGateway>
|
||||
<bpmn:sequenceFlow id="Flow_16bkbuc" sourceRef="Activity_19z6vct" targetRef="Gateway_FixInvalidUIDs" />
|
||||
<bpmn:sequenceFlow id="Flow_00zanzw" name="Yes" sourceRef="Gateway_FixInvalidUIDs" targetRef="ScriptTask_LoadPersonnel" />
|
||||
<bpmn:sequenceFlow id="Flow_0tsdclr" name="No" sourceRef="Gateway_FixInvalidUIDs" targetRef="ScriptTask_DeterminePI_E0_Department">
|
||||
<bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">not(FixInvalidUIDs)</bpmn:conditionExpression>
|
||||
</bpmn:sequenceFlow>
|
||||
<bpmn:userTask id="Activity_1sffono" name="Update Chair Info" camunda:formKey="RO_Chair_Info">
|
||||
<bpmn:documentation>***Name & Degree:*** {{ RO_Chair_Name_Degree }}
|
||||
***School:*** {{ RO_School }}
|
||||
***Department:*** {{ RO_Department }}
|
||||
***Title:*** {{ RO_Chair_Title }}
|
||||
***Email:*** {{ RO_Chair_CID }}
|
||||
|
||||
|
||||
{% if RO_Chair_CID != dc.uid %}
|
||||
*Does not match the Department Chair specified in Protocol Builder, {{ dc.display_name }}*
|
||||
{% endif %}</bpmn:documentation>
|
||||
<bpmn:extensionElements>
|
||||
<camunda:formData>
|
||||
<camunda:formField id="RO_ChairAccess" label="Should the Department Chair have full editing access in the system?" type="boolean" defaultValue="false" />
|
||||
<camunda:formField id="RO_ChairEmails" label="Should the Department Chair receive automated email notifications?" type="boolean" defaultValue="false" />
|
||||
</camunda:formData>
|
||||
<camunda:properties>
|
||||
<camunda:property name="display_name" value=""Responsible Organization's Chair Info"" />
|
||||
</camunda:properties>
|
||||
</bpmn:extensionElements>
|
||||
<bpmn:incoming>Flow_0vi6thu</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_1ayisx2</bpmn:outgoing>
|
||||
</bpmn:userTask>
|
||||
<bpmn:sequenceFlow id="Flow_1ayisx2" sourceRef="Activity_1sffono" targetRef="Gateway_0jykh6r" />
|
||||
</bpmn:process>
|
||||
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
|
||||
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_01143nb">
|
||||
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="UserTask_ShowInvalidUIDs">
|
||||
<bpmndi:BPMNEdge id="Flow_1ayisx2_di" bpmnElement="Flow_1ayisx2">
|
||||
<di:waypoint x="2810" y="290" />
|
||||
<di:waypoint x="2875" y="290" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_0tsdclr_di" bpmnElement="Flow_0tsdclr">
|
||||
<di:waypoint x="715" y="540" />
|
||||
<di:waypoint x="860" y="540" />
|
||||
<di:waypoint x="860" y="330" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="781" y="522" width="15" height="14" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_00zanzw_di" bpmnElement="Flow_00zanzw">
|
||||
<di:waypoint x="690" y="565" />
|
||||
<di:waypoint x="690" y="610" />
|
||||
<di:waypoint x="360" y="610" />
|
||||
<di:waypoint x="360" y="330" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="516" y="592" width="18" height="14" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_16bkbuc_di" bpmnElement="Flow_16bkbuc">
|
||||
<di:waypoint x="690" y="460" />
|
||||
<di:waypoint x="690" y="515" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_0nz62mu_di" bpmnElement="Flow_0nz62mu">
|
||||
<di:waypoint x="690" y="315" />
|
||||
<di:waypoint x="690" y="380" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="696" y="345" width="18" height="14" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_0tfprc8_di" bpmnElement="Flow_0tfprc8">
|
||||
<di:waypoint x="715" y="290" />
|
||||
<di:waypoint x="810" y="290" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="756" y="272" width="15" height="14" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_0kp47dz_di" bpmnElement="Flow_0kp47dz">
|
||||
<di:waypoint x="3800" y="265" />
|
||||
<di:waypoint x="3800" y="200" />
|
||||
<di:waypoint x="4150" y="200" />
|
||||
<di:waypoint x="4150" y="272" />
|
||||
<di:waypoint x="3960" y="265" />
|
||||
<di:waypoint x="3960" y="200" />
|
||||
<di:waypoint x="4310" y="200" />
|
||||
<di:waypoint x="4310" y="272" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_10zn0h1_di" bpmnElement="Flow_10zn0h1">
|
||||
<di:waypoint x="4030" y="290" />
|
||||
<di:waypoint x="4132" y="290" />
|
||||
<di:waypoint x="4190" y="290" />
|
||||
<di:waypoint x="4292" y="290" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_0q56tn8_di" bpmnElement="Flow_0q56tn8">
|
||||
<di:waypoint x="3825" y="290" />
|
||||
<di:waypoint x="3930" y="290" />
|
||||
<di:waypoint x="3985" y="290" />
|
||||
<di:waypoint x="4090" y="290" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_0vff9k5_di" bpmnElement="Flow_0vff9k5">
|
||||
<di:waypoint x="2040" y="375" />
|
||||
<di:waypoint x="2040" y="330" />
|
||||
<di:waypoint x="2200" y="375" />
|
||||
<di:waypoint x="2200" y="330" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_0iuzu7j_di" bpmnElement="Flow_0iuzu7j">
|
||||
<di:waypoint x="2015" y="830" />
|
||||
<di:waypoint x="1900" y="830" />
|
||||
<di:waypoint x="1900" y="760" />
|
||||
<di:waypoint x="2175" y="830" />
|
||||
<di:waypoint x="2060" y="830" />
|
||||
<di:waypoint x="2060" y="760" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="1905" y="783" width="49" height="14" />
|
||||
<dc:Bounds x="2065" y="783" width="49" height="14" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_0giqf35_di" bpmnElement="Flow_0giqf35">
|
||||
<di:waypoint x="2065" y="830" />
|
||||
<di:waypoint x="2180" y="830" />
|
||||
<di:waypoint x="2180" y="760" />
|
||||
<di:waypoint x="2225" y="830" />
|
||||
<di:waypoint x="2340" y="830" />
|
||||
<di:waypoint x="2340" y="760" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="2189" y="783" width="22" height="14" />
|
||||
<dc:Bounds x="2349" y="783" width="22" height="14" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_0mdjaid_di" bpmnElement="Flow_0mdjaid">
|
||||
<di:waypoint x="2040" y="900" />
|
||||
<di:waypoint x="2040" y="855" />
|
||||
<di:waypoint x="2200" y="900" />
|
||||
<di:waypoint x="2200" y="855" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_1vyg8ir_di" bpmnElement="Flow_1vyg8ir">
|
||||
<di:waypoint x="2180" y="680" />
|
||||
<di:waypoint x="2180" y="620" />
|
||||
<di:waypoint x="2065" y="620" />
|
||||
<di:waypoint x="2340" y="680" />
|
||||
<di:waypoint x="2340" y="620" />
|
||||
<di:waypoint x="2225" y="620" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_0zc01f9_di" bpmnElement="Flow_0zc01f9">
|
||||
<di:waypoint x="2040" y="680" />
|
||||
<di:waypoint x="2040" y="645" />
|
||||
<di:waypoint x="2200" y="680" />
|
||||
<di:waypoint x="2200" y="645" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_1vv63qa_di" bpmnElement="Flow_1vv63qa">
|
||||
<di:waypoint x="2040" y="460" />
|
||||
<di:waypoint x="2040" y="425" />
|
||||
<di:waypoint x="2200" y="460" />
|
||||
<di:waypoint x="2200" y="425" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_1azfvtx_di" bpmnElement="Flow_1azfvtx">
|
||||
<di:waypoint x="2040" y="805" />
|
||||
<di:waypoint x="2040" y="760" />
|
||||
<di:waypoint x="2200" y="805" />
|
||||
<di:waypoint x="2200" y="760" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="2047" y="783" width="45" height="14" />
|
||||
<dc:Bounds x="2207" y="783" width="45" height="14" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_0w4d2bz_di" bpmnElement="Flow_0w4d2bz">
|
||||
<di:waypoint x="1775" y="290" />
|
||||
<di:waypoint x="1990" y="290" />
|
||||
<di:waypoint x="1935" y="290" />
|
||||
<di:waypoint x="2150" y="290" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_1mplloa_di" bpmnElement="Flow_1mplloa">
|
||||
<di:waypoint x="1460" y="290" />
|
||||
<di:waypoint x="1540" y="290" />
|
||||
<di:waypoint x="1620" y="290" />
|
||||
<di:waypoint x="1700" y="290" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_1va8c15_di" bpmnElement="Flow_1va8c15">
|
||||
<di:waypoint x="1640" y="290" />
|
||||
<di:waypoint x="1725" y="290" />
|
||||
<di:waypoint x="1800" y="290" />
|
||||
<di:waypoint x="1885" y="290" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_0m9peiz_di" bpmnElement="Flow_0m9peiz">
|
||||
<di:waypoint x="2040" y="595" />
|
||||
<di:waypoint x="2040" y="540" />
|
||||
<di:waypoint x="2200" y="595" />
|
||||
<di:waypoint x="2200" y="540" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_0fw4rck_di" bpmnElement="Flow_0fw4rck">
|
||||
<di:waypoint x="2065" y="830" />
|
||||
<di:waypoint x="2270" y="830" />
|
||||
<di:waypoint x="2270" y="400" />
|
||||
<di:waypoint x="2065" y="400" />
|
||||
<di:waypoint x="2225" y="830" />
|
||||
<di:waypoint x="2430" y="830" />
|
||||
<di:waypoint x="2430" y="400" />
|
||||
<di:waypoint x="2225" y="400" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="2278" y="603" width="15" height="14" />
|
||||
<dc:Bounds x="2438" y="603" width="15" height="14" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_0whqr3p_di" bpmnElement="Flow_0whqr3p">
|
||||
<di:waypoint x="1900" y="680" />
|
||||
<di:waypoint x="1900" y="620" />
|
||||
<di:waypoint x="2015" y="620" />
|
||||
<di:waypoint x="2060" y="680" />
|
||||
<di:waypoint x="2060" y="620" />
|
||||
<di:waypoint x="2175" y="620" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_1yz8k2a_di" bpmnElement="Flow_1yz8k2a">
|
||||
<di:waypoint x="2040" y="1050" />
|
||||
<di:waypoint x="2040" y="980" />
|
||||
<di:waypoint x="2200" y="1050" />
|
||||
<di:waypoint x="2200" y="980" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_1fj9iz0_di" bpmnElement="Flow_1fj9iz0">
|
||||
<di:waypoint x="1800" y="1090" />
|
||||
<di:waypoint x="1990" y="1090" />
|
||||
<di:waypoint x="1960" y="1090" />
|
||||
<di:waypoint x="2150" y="1090" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_0ycdxbl_di" bpmnElement="Flow_0ycdxbl">
|
||||
<di:waypoint x="1750" y="855" />
|
||||
<di:waypoint x="1750" y="1050" />
|
||||
<di:waypoint x="1910" y="855" />
|
||||
<di:waypoint x="1910" y="1050" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="1722" y="933" width="15" height="14" />
|
||||
<dc:Bounds x="1882" y="933" width="15" height="14" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_13la8l3_di" bpmnElement="Flow_13la8l3">
|
||||
<di:waypoint x="1775" y="830" />
|
||||
<di:waypoint x="2015" y="830" />
|
||||
<di:waypoint x="1935" y="830" />
|
||||
<di:waypoint x="2175" y="830" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="1811" y="813" width="18" height="14" />
|
||||
<dc:Bounds x="1971" y="813" width="18" height="14" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_1yd7kbi_di" bpmnElement="Flow_1yd7kbi">
|
||||
<di:waypoint x="1750" y="315" />
|
||||
<di:waypoint x="1750" y="805" />
|
||||
<di:waypoint x="1910" y="315" />
|
||||
<di:waypoint x="1910" y="805" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="1722" y="691" width="15" height="14" />
|
||||
<dc:Bounds x="1882" y="691" width="15" height="14" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_0dt3pjw_di" bpmnElement="Flow_0dt3pjw">
|
||||
<di:waypoint x="3100" y="265" />
|
||||
<di:waypoint x="3100" y="180" />
|
||||
<di:waypoint x="3480" y="180" />
|
||||
<di:waypoint x="3480" y="265" />
|
||||
<di:waypoint x="3260" y="265" />
|
||||
<di:waypoint x="3260" y="180" />
|
||||
<di:waypoint x="3640" y="180" />
|
||||
<di:waypoint x="3640" y="265" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="3277" y="162" width="27" height="14" />
|
||||
<dc:Bounds x="3437" y="162" width="27" height="14" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_12ss6u8_di" bpmnElement="Flow_12ss6u8">
|
||||
<di:waypoint x="3340" y="290" />
|
||||
<di:waypoint x="3455" y="290" />
|
||||
<di:waypoint x="3500" y="290" />
|
||||
<di:waypoint x="3615" y="290" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_1gtl2o3_di" bpmnElement="Flow_1gtl2o3">
|
||||
<di:waypoint x="3125" y="290" />
|
||||
<di:waypoint x="3240" y="290" />
|
||||
<di:waypoint x="3285" y="290" />
|
||||
<di:waypoint x="3400" y="290" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="3159" y="272" width="48" height="14" />
|
||||
<dc:Bounds x="3319" y="272" width="48" height="14" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_070j5fg_di" bpmnElement="Flow_070j5fg">
|
||||
<di:waypoint x="2270" y="290" />
|
||||
<di:waypoint x="2365" y="290" />
|
||||
<di:waypoint x="2430" y="290" />
|
||||
<di:waypoint x="2525" y="290" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_1kg5jot_di" bpmnElement="Flow_1kg5jot">
|
||||
<di:waypoint x="1280" y="290" />
|
||||
<di:waypoint x="1360" y="290" />
|
||||
<di:waypoint x="1440" y="290" />
|
||||
<di:waypoint x="1520" y="290" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_16qr5jf_di" bpmnElement="Flow_16qr5jf">
|
||||
<di:waypoint x="580" y="410" />
|
||||
<di:waypoint x="642" y="410" />
|
||||
<di:waypoint x="740" y="150" />
|
||||
<di:waypoint x="822" y="150" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_0jxzqw1_di" bpmnElement="Flow_0jxzqw1">
|
||||
<di:waypoint x="3480" y="315" />
|
||||
<di:waypoint x="3480" y="390" />
|
||||
<di:waypoint x="3800" y="390" />
|
||||
<di:waypoint x="3800" y="315" />
|
||||
<di:waypoint x="3640" y="315" />
|
||||
<di:waypoint x="3640" y="390" />
|
||||
<di:waypoint x="3960" y="390" />
|
||||
<di:waypoint x="3960" y="315" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="3627" y="372" width="27" height="14" />
|
||||
<dc:Bounds x="3787" y="372" width="27" height="14" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_0ofpgml_di" bpmnElement="Flow_0ofpgml">
|
||||
<di:waypoint x="3710" y="290" />
|
||||
<di:waypoint x="3775" y="290" />
|
||||
<di:waypoint x="3870" y="290" />
|
||||
<di:waypoint x="3935" y="290" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_05rqrlf_di" bpmnElement="Flow_05rqrlf">
|
||||
<di:waypoint x="3505" y="290" />
|
||||
<di:waypoint x="3610" y="290" />
|
||||
<di:waypoint x="3665" y="290" />
|
||||
<di:waypoint x="3770" y="290" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="3534" y="272" width="48" height="14" />
|
||||
<dc:Bounds x="3694" y="272" width="48" height="14" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_0kpe12r_di" bpmnElement="Flow_0kpe12r">
|
||||
<di:waypoint x="2440" y="120" />
|
||||
<di:waypoint x="2740" y="120" />
|
||||
<di:waypoint x="2740" y="260" />
|
||||
<di:waypoint x="2600" y="120" />
|
||||
<di:waypoint x="2900" y="120" />
|
||||
<di:waypoint x="2900" y="260" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_00yhlrq_di" bpmnElement="Flow_00yhlrq">
|
||||
<di:waypoint x="2390" y="265" />
|
||||
<di:waypoint x="2390" y="160" />
|
||||
<di:waypoint x="2550" y="265" />
|
||||
<di:waypoint x="2550" y="160" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="2401" y="178" width="18" height="14" />
|
||||
<dc:Bounds x="2561" y="178" width="18" height="14" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_0vi6thu_di" bpmnElement="Flow_0vi6thu">
|
||||
<di:waypoint x="2415" y="290" />
|
||||
<di:waypoint x="2540" y="290" />
|
||||
<di:waypoint x="2575" y="290" />
|
||||
<di:waypoint x="2710" y="290" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="2471" y="272" width="15" height="14" />
|
||||
<dc:Bounds x="2620" y="272" width="15" height="14" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="SequenceFlow_0cdtt11_di" bpmnElement="SequenceFlow_0cdtt11">
|
||||
<di:waypoint x="2640" y="290" />
|
||||
<di:waypoint x="2715" y="290" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_1oo0ijr_di" bpmnElement="Flow_1oo0ijr">
|
||||
<di:waypoint x="2090" y="290" />
|
||||
<di:waypoint x="2170" y="290" />
|
||||
<di:waypoint x="2250" y="290" />
|
||||
<di:waypoint x="2330" y="290" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_1eaikyp_di" bpmnElement="Flow_1eaikyp">
|
||||
<di:waypoint x="920" y="290" />
|
||||
<di:waypoint x="1010" y="290" />
|
||||
<di:waypoint x="1070" y="290" />
|
||||
<di:waypoint x="1160" y="290" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_1wz38hl_di" bpmnElement="Flow_1wz38hl">
|
||||
<di:waypoint x="1110" y="290" />
|
||||
<di:waypoint x="1180" y="290" />
|
||||
<di:waypoint x="1260" y="290" />
|
||||
<di:waypoint x="1340" y="290" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_1grahhv_di" bpmnElement="Flow_1grahhv">
|
||||
<di:waypoint x="750" y="290" />
|
||||
<di:waypoint x="820" y="290" />
|
||||
<di:waypoint x="910" y="290" />
|
||||
<di:waypoint x="970" y="290" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_1oqem42_di" bpmnElement="Flow_1oqem42">
|
||||
<di:waypoint x="2740" y="315" />
|
||||
<di:waypoint x="2740" y="400" />
|
||||
<di:waypoint x="3100" y="400" />
|
||||
<di:waypoint x="3100" y="315" />
|
||||
<di:waypoint x="2900" y="315" />
|
||||
<di:waypoint x="2900" y="400" />
|
||||
<di:waypoint x="3260" y="400" />
|
||||
<di:waypoint x="3260" y="315" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="2916" y="383" width="27" height="14" />
|
||||
<dc:Bounds x="3076" y="383" width="27" height="14" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_1n0k4pd_di" bpmnElement="Flow_1n0k4pd">
|
||||
<di:waypoint x="2980" y="290" />
|
||||
<di:waypoint x="3075" y="290" />
|
||||
<di:waypoint x="3140" y="290" />
|
||||
<di:waypoint x="3235" y="290" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_0xifvai_di" bpmnElement="Flow_0xifvai">
|
||||
<di:waypoint x="2765" y="290" />
|
||||
<di:waypoint x="2880" y="290" />
|
||||
<di:waypoint x="2925" y="290" />
|
||||
<di:waypoint x="3040" y="290" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="2793" y="273" width="48" height="14" />
|
||||
<dc:Bounds x="2953" y="273" width="48" height="14" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_00prawo_di" bpmnElement="Flow_00prawo">
|
||||
<di:waypoint x="350" y="315" />
|
||||
<di:waypoint x="350" y="410" />
|
||||
<di:waypoint x="480" y="410" />
|
||||
<di:waypoint x="510" y="265" />
|
||||
<di:waypoint x="510" y="150" />
|
||||
<di:waypoint x="640" y="150" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="359" y="353" width="71" height="14" />
|
||||
<dc:Bounds x="483" y="204" width="15" height="14" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_147b9li_di" bpmnElement="Flow_147b9li">
|
||||
<di:waypoint x="375" y="290" />
|
||||
<di:waypoint x="650" y="290" />
|
||||
<di:waypoint x="535" y="290" />
|
||||
<di:waypoint x="665" y="290" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="402" y="273" width="63" height="14" />
|
||||
<dc:Bounds x="583" y="273" width="18" height="14" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_1dcsioh_di" bpmnElement="Flow_1dcsioh">
|
||||
<di:waypoint x="250" y="290" />
|
||||
<di:waypoint x="325" y="290" />
|
||||
<di:waypoint x="410" y="290" />
|
||||
<di:waypoint x="485" y="290" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_0kcrx5l_di" bpmnElement="Flow_0kcrx5l">
|
||||
<di:waypoint x="28" y="290" />
|
||||
<di:waypoint x="150" y="290" />
|
||||
<di:waypoint x="188" y="290" />
|
||||
<di:waypoint x="310" y="290" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
|
||||
<dc:Bounds x="-8" y="272" width="36" height="36" />
|
||||
<dc:Bounds x="152" y="272" width="36" height="36" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="ScriptTask_0h49cmf_di" bpmnElement="ScriptTask_LoadPersonnel">
|
||||
<dc:Bounds x="150" y="250" width="100" height="80" />
|
||||
<dc:Bounds x="310" y="250" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="EndEvent_1qor16n_di" bpmnElement="EndEvent_1qor16n">
|
||||
<dc:Bounds x="4132" y="272" width="36" height="36" />
|
||||
<dc:Bounds x="4292" y="272" width="36" height="36" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="4140" y="318" width="20" height="14" />
|
||||
<dc:Bounds x="4300" y="318" width="20" height="14" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_0d622qi_di" bpmnElement="Activity_EditPI">
|
||||
<dc:Bounds x="1360" y="250" width="100" height="80" />
|
||||
<dc:Bounds x="1520" y="250" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Gateway_0qzf1r3_di" bpmnElement="Gateway_CheckForPI" isMarkerVisible="true">
|
||||
<dc:Bounds x="325" y="265" width="50" height="50" />
|
||||
<dc:Bounds x="485" y="265" width="50" height="50" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="334" y="241" width="31" height="14" />
|
||||
<dc:Bounds x="478" y="325" width="63" height="27" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_0neg931_di" bpmnElement="Activity_1qwzwyi">
|
||||
<dc:Bounds x="480" y="370" width="100" height="80" />
|
||||
<dc:Bounds x="640" y="110" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Gateway_0jykh6r_di" bpmnElement="Gateway_0jykh6r" isMarkerVisible="true">
|
||||
<dc:Bounds x="2715" y="265" width="50" height="50" />
|
||||
<dc:Bounds x="2875" y="265" width="50" height="50" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="2745" y="309" width="70" height="40" />
|
||||
<dc:Bounds x="2905" y="309" width="70" height="40" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_1nz85vv_di" bpmnElement="TaskPMI_UpdateCoordinatorInfo">
|
||||
<dc:Bounds x="2880" y="250" width="100" height="80" />
|
||||
<dc:Bounds x="3040" y="250" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_1z05bvn_di" bpmnElement="ScriptTask_DeterminePI_E0_Department">
|
||||
<dc:Bounds x="650" y="250" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_0uz6yhu_di" bpmnElement="BusinessRule_PI_Dept">
|
||||
<dc:Bounds x="1010" y="250" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_1sn7wxh_di" bpmnElement="BusinessRule_PI_School">
|
||||
<dc:Bounds x="820" y="250" width="100" height="80" />
|
||||
<dc:Bounds x="810" y="250" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="UserTask_1a7hck9_di" bpmnElement="UserTask_SelectChair">
|
||||
<dc:Bounds x="1850" y="680" width="100" height="80" />
|
||||
<dc:Bounds x="2010" y="680" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_1sk9596_di" bpmnElement="BusinessRuleTask_Determine_RO_Chair">
|
||||
<dc:Bounds x="1990" y="250" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="UserTask_109otvi_di" bpmnElement="UserTask_109otvi">
|
||||
<dc:Bounds x="2540" y="250" width="100" height="80" />
|
||||
<dc:Bounds x="2150" y="250" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Gateway_1xio5hy_di" bpmnElement="Gateway_PI_is_DeptChair" isMarkerVisible="true">
|
||||
<dc:Bounds x="2365" y="265" width="50" height="50" />
|
||||
<dc:Bounds x="2525" y="265" width="50" height="50" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="2348" y="322" width="84" height="14" />
|
||||
<dc:Bounds x="2508" y="322" width="84" height="14" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_0i869dj_di" bpmnElement="Activity_ShowPI_is_DeptChair">
|
||||
<dc:Bounds x="2340" y="80" width="100" height="80" />
|
||||
<dc:Bounds x="2500" y="80" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Gateway_1oxt6h1_di" bpmnElement="Gateway_1oxt6h1" isMarkerVisible="true">
|
||||
<dc:Bounds x="3455" y="265" width="50" height="50" />
|
||||
<dc:Bounds x="3615" y="265" width="50" height="50" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="3500" y="315" width="79" height="27" />
|
||||
<dc:Bounds x="3660" y="315" width="79" height="27" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_0oyqfs3_di" bpmnElement="Activity_0yd4wuz">
|
||||
<dc:Bounds x="3610" y="250" width="100" height="80" />
|
||||
<dc:Bounds x="3770" y="250" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Event_0npjf2p_di" bpmnElement="Event_0npjf2p">
|
||||
<dc:Bounds x="642" y="392" width="36" height="36" />
|
||||
<dc:Bounds x="822" y="132" width="36" height="36" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_02led02_di" bpmnElement="ScriptTask_Update_PIData">
|
||||
<dc:Bounds x="1180" y="250" width="100" height="80" />
|
||||
<dc:Bounds x="1340" y="250" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_1mt9o4o_di" bpmnElement="ScriptTask_UpdateRO_Data">
|
||||
<dc:Bounds x="2170" y="250" width="100" height="80" />
|
||||
<dc:Bounds x="2330" y="250" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Gateway_0zrcknh_di" bpmnElement="Gateway_0zrcknh" isMarkerVisible="true">
|
||||
<dc:Bounds x="3075" y="265" width="50" height="50" />
|
||||
<dc:Bounds x="3235" y="265" width="50" height="50" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="3115" y="309" width="70" height="40" />
|
||||
<dc:Bounds x="3275" y="309" width="70" height="40" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_1gqvpu9_di" bpmnElement="Activity_1yjg742">
|
||||
<dc:Bounds x="3240" y="250" width="100" height="80" />
|
||||
<dc:Bounds x="3400" y="250" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Gateway_10ngpfu_di" bpmnElement="Gateway_10ngpfu" isMarkerVisible="true">
|
||||
<dc:Bounds x="1725" y="265" width="50" height="50" />
|
||||
<dc:Bounds x="1885" y="265" width="50" height="50" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="1706" y="170" width="88" height="80" />
|
||||
<dc:Bounds x="1866" y="170" width="88" height="80" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Gateway_141zszd_di" bpmnElement="Gateway_141zszd" isMarkerVisible="true">
|
||||
<dc:Bounds x="1725" y="805" width="50" height="50" />
|
||||
<dc:Bounds x="1885" y="805" width="50" height="50" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="1644" y="823" width="72" height="14" />
|
||||
<dc:Bounds x="1804" y="823" width="72" height="14" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_0vjz7wg_di" bpmnElement="Activity_1h5mjwh">
|
||||
<dc:Bounds x="1700" y="1050" width="100" height="80" />
|
||||
<dc:Bounds x="1860" y="1050" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_0evmxd3_di" bpmnElement="Activity_141w33n">
|
||||
<dc:Bounds x="1990" y="1050" width="100" height="80" />
|
||||
<dc:Bounds x="2150" y="1050" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Gateway_1dfciuq_di" bpmnElement="Gateway_1dfciuq" isMarkerVisible="true">
|
||||
<dc:Bounds x="2015" y="805" width="50" height="50" />
|
||||
<dc:Bounds x="2175" y="805" width="50" height="50" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="2055" y="846" width="70" height="27" />
|
||||
<dc:Bounds x="2215" y="846" width="70" height="27" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Gateway_1gzewp9_di" bpmnElement="Gateway_1gzewp9" isMarkerVisible="true">
|
||||
<dc:Bounds x="2015" y="595" width="50" height="50" />
|
||||
<dc:Bounds x="2175" y="595" width="50" height="50" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_0zjabzm_di" bpmnElement="BusinessRuleTask_Determine_RO">
|
||||
<dc:Bounds x="1990" y="460" width="100" height="80" />
|
||||
<dc:Bounds x="2150" y="460" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_11muu5l_di" bpmnElement="Activity_11muu5l">
|
||||
<dc:Bounds x="1990" y="680" width="100" height="80" />
|
||||
<dc:Bounds x="2150" y="680" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_18f2rpz_di" bpmnElement="ScriptTask_SetRO_SchoolAndDept">
|
||||
<dc:Bounds x="1700" y="250" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_1bvro77_di" bpmnElement="Activity_1bvro77">
|
||||
<dc:Bounds x="2130" y="680" width="100" height="80" />
|
||||
<dc:Bounds x="2290" y="680" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_1yen2d0_di" bpmnElement="Activity_1yen2d0">
|
||||
<dc:Bounds x="1990" y="900" width="100" height="80" />
|
||||
<dc:Bounds x="2150" y="900" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Gateway_0zd7syo_di" bpmnElement="Gateway_0zd7syo" isMarkerVisible="true">
|
||||
<dc:Bounds x="2015" y="375" width="50" height="50" />
|
||||
<dc:Bounds x="2175" y="375" width="50" height="50" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Gateway_13k761k_di" bpmnElement="Gateway_13k761k" isMarkerVisible="true">
|
||||
<dc:Bounds x="3775" y="265" width="50" height="50" />
|
||||
<dc:Bounds x="3935" y="265" width="50" height="50" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="3822" y="309" width="56" height="40" />
|
||||
<dc:Bounds x="3982" y="309" width="56" height="40" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_1m6r78y_di" bpmnElement="Activity_1sra1vn">
|
||||
<dc:Bounds x="3930" y="250" width="100" height="80" />
|
||||
<dc:Bounds x="4090" y="250" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_18f2rpz_di" bpmnElement="ScriptTask_SetRO_SchoolAndDept">
|
||||
<dc:Bounds x="1540" y="250" width="100" height="80" />
|
||||
<bpmndi:BPMNShape id="Gateway_1gq2m4q_di" bpmnElement="Gateway_CheckUIDs" isMarkerVisible="true">
|
||||
<dc:Bounds x="665" y="265" width="50" height="50" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="657" y="241" width="66" height="14" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_1l5u9mj_di" bpmnElement="Activity_19z6vct">
|
||||
<dc:Bounds x="640" y="380" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Gateway_01e55pl_di" bpmnElement="Gateway_FixInvalidUIDs" isMarkerVisible="true">
|
||||
<dc:Bounds x="665" y="515" width="50" height="50" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="570" y="530" width="84" height="14" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_1sffono_di" bpmnElement="Activity_1sffono">
|
||||
<dc:Bounds x="2710" y="250" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_1sn7wxh_di" bpmnElement="BusinessRule_PI_School">
|
||||
<dc:Bounds x="970" y="250" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_0uz6yhu_di" bpmnElement="BusinessRule_PI_Dept">
|
||||
<dc:Bounds x="1160" y="250" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
</bpmndi:BPMNPlane>
|
||||
</bpmndi:BPMNDiagram>
|
||||
|
@ -4,6 +4,7 @@ import os
|
||||
|
||||
from crc import app, db, session
|
||||
from crc.models.file import CONTENT_TYPES
|
||||
from crc.models.ldap import LdapModel
|
||||
from crc.models.user import UserModel
|
||||
from crc.models.workflow import WorkflowSpecModel, WorkflowSpecCategoryModel
|
||||
from crc.services.file_service import FileService
|
||||
@ -21,8 +22,6 @@ class ExampleDataLoader:
|
||||
def load_all(self):
|
||||
|
||||
self.load_reference_documents()
|
||||
self.load_default_user()
|
||||
|
||||
categories = [
|
||||
WorkflowSpecCategoryModel(
|
||||
id=0,
|
||||
@ -328,8 +327,10 @@ class ExampleDataLoader:
|
||||
file.close()
|
||||
|
||||
def load_default_user(self):
|
||||
user = UserModel(uid="xxx9x", email_address="xxx9x@virginia.edu", display_name="Development User")
|
||||
user = UserModel(uid="dhf8r", email_address="dhf8r@virginia.edu", display_name="Development User")
|
||||
ldap_info = LdapModel(uid="dhf8r", email_address="dhf8r@virginia.edu", display_name="Development User")
|
||||
db.session.add(user)
|
||||
db.session.add(ldap_info)
|
||||
db.session.commit()
|
||||
|
||||
def ldap(): return "x";
|
||||
|
@ -350,18 +350,30 @@ class BaseTest(unittest.TestCase):
|
||||
session.commit()
|
||||
return approval
|
||||
|
||||
def get_workflow_api(self, workflow, soft_reset=False, hard_reset=False, do_engine_steps=True, user_uid="dhf8r"):
|
||||
user = session.query(UserModel).filter_by(uid=user_uid).first()
|
||||
self.assertIsNotNone(user)
|
||||
rv = self.app.get(f'/v1.0/workflow/{workflow.id}'
|
||||
f'?soft_reset={str(soft_reset)}'
|
||||
f'&hard_reset={str(hard_reset)}'
|
||||
f'&do_engine_steps={str(do_engine_steps)}',
|
||||
def get_workflow_common(self, url, user):
|
||||
rv = self.app.get(url,
|
||||
headers=self.logged_in_headers(user),
|
||||
content_type="application/json")
|
||||
self.assert_success(rv)
|
||||
json_data = json.loads(rv.get_data(as_text=True))
|
||||
workflow_api = WorkflowApiSchema().load(json_data)
|
||||
return workflow_api
|
||||
|
||||
def get_workflow_api(self, workflow, do_engine_steps=True, user_uid="dhf8r"):
|
||||
user = session.query(UserModel).filter_by(uid=user_uid).first()
|
||||
self.assertIsNotNone(user)
|
||||
url = (f'/v1.0/workflow/{workflow.id}'
|
||||
f'?do_engine_steps={str(do_engine_steps)}')
|
||||
workflow_api = self.get_workflow_common(url, user)
|
||||
self.assertEqual(workflow.workflow_spec_id, workflow_api.workflow_spec_id)
|
||||
return workflow_api
|
||||
|
||||
def restart_workflow_api(self, workflow, clear_data=False, user_uid="dhf8r"):
|
||||
user = session.query(UserModel).filter_by(uid=user_uid).first()
|
||||
self.assertIsNotNone(user)
|
||||
url = (f'/v1.0/workflow/{workflow.id}/restart'
|
||||
f'?clear_data={str(clear_data)}')
|
||||
workflow_api = self.get_workflow_common(url, user)
|
||||
self.assertEqual(workflow.workflow_spec_id, workflow_api.workflow_spec_id)
|
||||
return workflow_api
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?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_0y2dq4f" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="3.7.0">
|
||||
<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_0y2dq4f" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="3.7.3">
|
||||
<bpmn:process id="Process_0tad5ma" name="Set Recipients" isExecutable="true">
|
||||
<bpmn:startEvent id="StartEvent_1">
|
||||
<bpmn:outgoing>Flow_1synsig</bpmn:outgoing>
|
||||
@ -20,7 +20,7 @@ Email content to be delivered to {{ ApprvlApprvr1 }}
|
||||
---</bpmn:documentation>
|
||||
<bpmn:incoming>Flow_08n2npe</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_1xlrgne</bpmn:outgoing>
|
||||
<bpmn:script>email("Camunda Email Subject",'ApprvlApprvr1','PIComputingID')</bpmn:script>
|
||||
<bpmn:script>email("Camunda Email Subject",ApprvlApprvr1,PIComputingID)</bpmn:script>
|
||||
</bpmn:scriptTask>
|
||||
<bpmn:sequenceFlow id="Flow_1synsig" sourceRef="StartEvent_1" targetRef="Activity_1l9vih3" />
|
||||
<bpmn:sequenceFlow id="Flow_1xlrgne" sourceRef="Activity_0s5v97n" targetRef="Event_0izrcj4" />
|
||||
|
72
tests/data/email_script/email_script.bpmn
Normal file
72
tests/data/email_script/email_script.bpmn
Normal file
@ -0,0 +1,72 @@
|
||||
<?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_bd39673" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="4.2.0">
|
||||
<bpmn:process id="Process_fe6205f" isExecutable="true">
|
||||
<bpmn:startEvent id="StartEvent_1">
|
||||
<bpmn:outgoing>Flow_0scd96e</bpmn:outgoing>
|
||||
</bpmn:startEvent>
|
||||
<bpmn:sequenceFlow id="Flow_0scd96e" sourceRef="StartEvent_1" targetRef="Activity_EmailForm" />
|
||||
<bpmn:userTask id="Activity_EmailForm" name="Email Form" camunda:formKey="email_form">
|
||||
<bpmn:extensionElements>
|
||||
<camunda:formData>
|
||||
<camunda:formField id="email_address" label="Enter Email" type="string" defaultValue="dan@sartography.com">
|
||||
<camunda:validation>
|
||||
<camunda:constraint name="required" config="true" />
|
||||
</camunda:validation>
|
||||
</camunda:formField>
|
||||
</camunda:formData>
|
||||
</bpmn:extensionElements>
|
||||
<bpmn:incoming>Flow_0scd96e</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_0c60gne</bpmn:outgoing>
|
||||
</bpmn:userTask>
|
||||
<bpmn:sequenceFlow id="Flow_0c60gne" sourceRef="Activity_EmailForm" targetRef="Activity_SendEmail" />
|
||||
<bpmn:endEvent id="Event_EndEvent">
|
||||
<bpmn:incoming>Flow_19fqvhc</bpmn:incoming>
|
||||
</bpmn:endEvent>
|
||||
<bpmn:sequenceFlow id="Flow_19fqvhc" sourceRef="Activity_SendEmail" targetRef="Event_EndEvent" />
|
||||
<bpmn:scriptTask id="Activity_SendEmail" name="Send Email">
|
||||
<bpmn:documentation>Dear Person,
|
||||
|
||||
|
||||
Thank you for using this email example.
|
||||
I hope this makes sense.
|
||||
|
||||
|
||||
Yours faithfully,
|
||||
|
||||
|
||||
Dan</bpmn:documentation>
|
||||
<bpmn:incoming>Flow_0c60gne</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_19fqvhc</bpmn:outgoing>
|
||||
<bpmn:script>subject = 'My Email Subject'
|
||||
email(subject, email_address)</bpmn:script>
|
||||
</bpmn:scriptTask>
|
||||
</bpmn:process>
|
||||
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
|
||||
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_fe6205f">
|
||||
<bpmndi:BPMNEdge id="Flow_19fqvhc_di" bpmnElement="Flow_19fqvhc">
|
||||
<di:waypoint x="530" y="117" />
|
||||
<di:waypoint x="592" y="117" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_0c60gne_di" bpmnElement="Flow_0c60gne">
|
||||
<di:waypoint x="370" y="117" />
|
||||
<di:waypoint x="430" y="117" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_0scd96e_di" bpmnElement="Flow_0scd96e">
|
||||
<di:waypoint x="215" y="117" />
|
||||
<di:waypoint x="270" y="117" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<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_0wqsfcj_di" bpmnElement="Activity_EmailForm">
|
||||
<dc:Bounds x="270" y="77" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Event_1wh1xsj_di" bpmnElement="Event_EndEvent">
|
||||
<dc:Bounds x="592" y="99" width="36" height="36" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_1ajacra_di" bpmnElement="Activity_SendEmail">
|
||||
<dc:Bounds x="430" y="77" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
</bpmndi:BPMNPlane>
|
||||
</bpmndi:BPMNDiagram>
|
||||
</bpmn:definitions>
|
52
tests/data/failing_workflow/failing_workflow.bpmn
Normal file
52
tests/data/failing_workflow/failing_workflow.bpmn
Normal file
@ -0,0 +1,52 @@
|
||||
<?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:di="http://www.omg.org/spec/DD/20100524/DI" id="Definitions_886a64d" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="4.2.0">
|
||||
<bpmn:process id="Process_FailingWorkflow" name="Failing Workflow" isExecutable="true">
|
||||
<bpmn:startEvent id="StartEvent_1">
|
||||
<bpmn:outgoing>Flow_0cszvz2</bpmn:outgoing>
|
||||
</bpmn:startEvent>
|
||||
<bpmn:sequenceFlow id="Flow_0cszvz2" sourceRef="StartEvent_1" targetRef="Activity_CallFailingScript" />
|
||||
<bpmn:scriptTask id="Activity_CallFailingScript" name="Call Failing Script">
|
||||
<bpmn:incoming>Flow_0cszvz2</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_1l02umo</bpmn:outgoing>
|
||||
<bpmn:script>failing_script()</bpmn:script>
|
||||
</bpmn:scriptTask>
|
||||
<bpmn:sequenceFlow id="Flow_1l02umo" sourceRef="Activity_CallFailingScript" targetRef="Activity_PlaceHolder" />
|
||||
<bpmn:scriptTask id="Activity_PlaceHolder" name="Place Holder">
|
||||
<bpmn:incoming>Flow_1l02umo</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_08zq7mf</bpmn:outgoing>
|
||||
<bpmn:script>print('I am a placeholder.')</bpmn:script>
|
||||
</bpmn:scriptTask>
|
||||
<bpmn:endEvent id="Event_0k0yvmv">
|
||||
<bpmn:incoming>Flow_08zq7mf</bpmn:incoming>
|
||||
</bpmn:endEvent>
|
||||
<bpmn:sequenceFlow id="Flow_08zq7mf" sourceRef="Activity_PlaceHolder" targetRef="Event_0k0yvmv" />
|
||||
</bpmn:process>
|
||||
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
|
||||
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_FailingWorkflow">
|
||||
<bpmndi:BPMNEdge id="Flow_0cszvz2_di" bpmnElement="Flow_0cszvz2">
|
||||
<di:waypoint x="215" y="117" />
|
||||
<di:waypoint x="270" y="117" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_1l02umo_di" bpmnElement="Flow_1l02umo">
|
||||
<di:waypoint x="370" y="117" />
|
||||
<di:waypoint x="430" y="117" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_08zq7mf_di" bpmnElement="Flow_08zq7mf">
|
||||
<di:waypoint x="530" y="117" />
|
||||
<di:waypoint x="592" y="117" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<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_045fev7_di" bpmnElement="Activity_CallFailingScript">
|
||||
<dc:Bounds x="270" y="77" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_0f3nusg_di" bpmnElement="Activity_PlaceHolder">
|
||||
<dc:Bounds x="430" y="77" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Event_0k0yvmv_di" bpmnElement="Event_0k0yvmv">
|
||||
<dc:Bounds x="592" y="99" width="36" height="36" />
|
||||
</bpmndi:BPMNShape>
|
||||
</bpmndi:BPMNPlane>
|
||||
</bpmndi:BPMNDiagram>
|
||||
</bpmn:definitions>
|
@ -25,7 +25,7 @@
|
||||
<camunda:formData>
|
||||
<camunda:formField id="favorite_color" label="What is your favorite color?" type="string" defaultValue="Yellow" />
|
||||
<camunda:formField id="quest" label="What is your quest?" type="string" defaultValue="To seek the holly Grail!" />
|
||||
<camunda:formField id="swallow_speed" label="What is the air speed velocity of an unladen swallow?" defaultValue="About 24 miles per hour" />
|
||||
<camunda:formField id="swallow_speed" label="What is the air speed velocity of an unladen swallow?" type="string" defaultValue="About 24 miles per hour" />
|
||||
</camunda:formData>
|
||||
</bpmn:extensionElements>
|
||||
<bpmn:incoming>Flow_0a7090c</bpmn:incoming>
|
||||
@ -48,13 +48,7 @@ Your supervisor provided the following feedback:
|
||||
{{feedback}}
|
||||
|
||||
|
||||
You are all done! WARNING: If you go back and reanswer the questions it will create a new approval request.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</bpmn:documentation>
|
||||
You are all done! WARNING: If you go back and reanswer the questions it will create a new approval request.</bpmn:documentation>
|
||||
<bpmn:incoming>Flow_1g38q6b</bpmn:incoming>
|
||||
</bpmn:endEvent>
|
||||
<bpmn:manualTask id="Activity_19ccxoj" name="Review Feedback">
|
||||
@ -68,13 +62,7 @@ Your Supervisor provided the following feedback:
|
||||
{{feedback}}
|
||||
|
||||
|
||||
Please press save to re-try the questions, and submit your responses again.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</bpmn:documentation>
|
||||
Please press save to re-try the questions, and submit your responses again.</bpmn:documentation>
|
||||
<bpmn:incoming>Flow_0vnghsi</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_070gq5r</bpmn:outgoing>
|
||||
</bpmn:manualTask>
|
||||
@ -108,29 +96,21 @@ Please press save to re-try the questions, and submit your responses again.
|
||||
<bpmndi:BPMNShape id="Participant_0ozb2sp_di" bpmnElement="Participant_0ozb2sp" isHorizontal="true">
|
||||
<dc:Bounds x="190" y="80" width="550" height="370" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Lane_1s1s7a1_di" bpmnElement="Lane_1s1s7a1" isHorizontal="true">
|
||||
<dc:Bounds x="220" y="80" width="520" height="245" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Lane_1m47545_di" bpmnElement="Lane_1m47545" isHorizontal="true">
|
||||
<dc:Bounds x="220" y="325" width="520" height="125" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="TextAnnotation_1ys83yq_di" bpmnElement="TextAnnotation_1ys83yq">
|
||||
<dc:Bounds x="250" y="100" width="130.6238034460753" height="68.28334396936822" />
|
||||
<bpmndi:BPMNShape id="Lane_1s1s7a1_di" bpmnElement="Lane_1s1s7a1" isHorizontal="true">
|
||||
<dc:Bounds x="220" y="80" width="520" height="245" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge id="Flow_0a7090c_di" bpmnElement="Flow_0a7090c">
|
||||
<di:waypoint x="276" y="260" />
|
||||
<di:waypoint x="330" y="260" />
|
||||
<bpmndi:BPMNEdge id="Flow_070gq5r_di" bpmnElement="Flow_070gq5r">
|
||||
<di:waypoint x="510" y="220" />
|
||||
<di:waypoint x="510" y="160" />
|
||||
<di:waypoint x="380" y="160" />
|
||||
<di:waypoint x="380" y="220" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_1gp4zfd_di" bpmnElement="Flow_1gp4zfd">
|
||||
<di:waypoint x="430" y="390" />
|
||||
<di:waypoint x="485" y="390" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_0vnghsi_di" bpmnElement="Flow_0vnghsi">
|
||||
<di:waypoint x="510" y="365" />
|
||||
<di:waypoint x="510" y="300" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="520" y="334" width="40" height="14" />
|
||||
</bpmndi:BPMNLabel>
|
||||
<bpmndi:BPMNEdge id="Flow_1hcpt7c_di" bpmnElement="Flow_1hcpt7c">
|
||||
<di:waypoint x="380" y="300" />
|
||||
<di:waypoint x="380" y="350" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_1g38q6b_di" bpmnElement="Flow_1g38q6b">
|
||||
<di:waypoint x="535" y="390" />
|
||||
@ -140,15 +120,20 @@ Please press save to re-try the questions, and submit your responses again.
|
||||
<dc:Bounds x="585" y="372" width="46" height="14" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_1hcpt7c_di" bpmnElement="Flow_1hcpt7c">
|
||||
<di:waypoint x="380" y="300" />
|
||||
<di:waypoint x="380" y="350" />
|
||||
<bpmndi:BPMNEdge id="Flow_0vnghsi_di" bpmnElement="Flow_0vnghsi">
|
||||
<di:waypoint x="510" y="365" />
|
||||
<di:waypoint x="510" y="300" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="520" y="334" width="40" height="14" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_070gq5r_di" bpmnElement="Flow_070gq5r">
|
||||
<di:waypoint x="510" y="220" />
|
||||
<di:waypoint x="510" y="160" />
|
||||
<di:waypoint x="380" y="160" />
|
||||
<di:waypoint x="380" y="220" />
|
||||
<bpmndi:BPMNEdge id="Flow_1gp4zfd_di" bpmnElement="Flow_1gp4zfd">
|
||||
<di:waypoint x="430" y="390" />
|
||||
<di:waypoint x="485" y="390" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_0a7090c_di" bpmnElement="Flow_0a7090c">
|
||||
<di:waypoint x="276" y="260" />
|
||||
<di:waypoint x="330" y="260" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
|
||||
<dc:Bounds x="240" y="242" width="36" height="36" />
|
||||
@ -168,6 +153,9 @@ Please press save to re-try the questions, and submit your responses again.
|
||||
<bpmndi:BPMNShape id="Activity_0zc7cgy_di" bpmnElement="Activity_14eor1x">
|
||||
<dc:Bounds x="330" y="350" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="TextAnnotation_1ys83yq_di" bpmnElement="TextAnnotation_1ys83yq">
|
||||
<dc:Bounds x="250" y="100" width="130.6238034460753" height="68.28334396936822" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge id="Association_1kcb9ou_di" bpmnElement="Association_1kcb9ou">
|
||||
<di:waypoint x="359" y="220" />
|
||||
<di:waypoint x="333" y="168" />
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?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:di="http://www.omg.org/spec/DD/20100524/DI" id="Definitions_0kmksnn" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="3.7.0">
|
||||
<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:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="Definitions_0kmksnn" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="3.7.0">
|
||||
<bpmn:process id="Process_0exnnpv" isExecutable="true">
|
||||
<bpmn:startEvent id="StartEvent_1">
|
||||
<bpmn:outgoing>SequenceFlow_1nfe5m9</bpmn:outgoing>
|
||||
@ -12,7 +12,7 @@
|
||||
</bpmn:scriptTask>
|
||||
<bpmn:sequenceFlow id="SequenceFlow_1bqiin0" sourceRef="Task_Script_Load_Study_Sponsors" targetRef="Activity_0cm6tn2" />
|
||||
<bpmn:endEvent id="EndEvent_171dj09">
|
||||
<bpmn:incoming>Flow_05136ua</bpmn:incoming>
|
||||
<bpmn:incoming>Flow_1efanns</bpmn:incoming>
|
||||
</bpmn:endEvent>
|
||||
<bpmn:sequenceFlow id="Flow_09cika8" sourceRef="Activity_0cm6tn2" targetRef="Activity_0d8iftx" />
|
||||
<bpmn:scriptTask id="Activity_0cm6tn2" name="setval">
|
||||
@ -37,7 +37,19 @@
|
||||
<bpmn:outgoing>Flow_05136ua</bpmn:outgoing>
|
||||
<bpmn:script>empty = user_data_get('testme','empty')</bpmn:script>
|
||||
</bpmn:scriptTask>
|
||||
<bpmn:sequenceFlow id="Flow_05136ua" sourceRef="Activity_0xw717o" targetRef="EndEvent_171dj09" />
|
||||
<bpmn:sequenceFlow id="Flow_05136ua" sourceRef="Activity_0xw717o" targetRef="Gateway_06osfqz" />
|
||||
<bpmn:exclusiveGateway id="Gateway_06osfqz" default="Flow_00s638e">
|
||||
<bpmn:incoming>Flow_05136ua</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_1efanns</bpmn:outgoing>
|
||||
<bpmn:outgoing>Flow_00s638e</bpmn:outgoing>
|
||||
</bpmn:exclusiveGateway>
|
||||
<bpmn:sequenceFlow id="Flow_1efanns" name="test flow expression" sourceRef="Gateway_06osfqz" targetRef="EndEvent_171dj09">
|
||||
<bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">user_data_get('nothing','default') == 'default'</bpmn:conditionExpression>
|
||||
</bpmn:sequenceFlow>
|
||||
<bpmn:endEvent id="Event_1yebjqg">
|
||||
<bpmn:incoming>Flow_00s638e</bpmn:incoming>
|
||||
</bpmn:endEvent>
|
||||
<bpmn:sequenceFlow id="Flow_00s638e" sourceRef="Gateway_06osfqz" targetRef="Event_1yebjqg" />
|
||||
</bpmn:process>
|
||||
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
|
||||
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_0exnnpv">
|
||||
@ -84,7 +96,24 @@
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge id="Flow_05136ua_di" bpmnElement="Flow_05136ua">
|
||||
<di:waypoint x="810" y="330" />
|
||||
<di:waypoint x="810" y="385" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNShape id="Gateway_06osfqz_di" bpmnElement="Gateway_06osfqz" isMarkerVisible="true">
|
||||
<dc:Bounds x="785" y="385" width="50" height="50" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge id="Flow_1efanns_di" bpmnElement="Flow_1efanns">
|
||||
<di:waypoint x="810" y="435" />
|
||||
<di:waypoint x="810" y="672" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="798" y="551" width="54" height="27" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNShape id="Event_1yebjqg_di" bpmnElement="Event_1yebjqg">
|
||||
<dc:Bounds x="1012" y="392" width="36" height="36" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge id="Flow_00s638e_di" bpmnElement="Flow_00s638e">
|
||||
<di:waypoint x="835" y="410" />
|
||||
<di:waypoint x="1012" y="410" />
|
||||
</bpmndi:BPMNEdge>
|
||||
</bpmndi:BPMNPlane>
|
||||
</bpmndi:BPMNDiagram>
|
||||
|
98
tests/data/verify_end_event/verify_end_event.bpmn
Normal file
98
tests/data/verify_end_event/verify_end_event.bpmn
Normal file
@ -0,0 +1,98 @@
|
||||
<?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:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="Definitions_00j2iu5" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="4.2.0">
|
||||
<bpmn:process id="Process_1gmf4la" isExecutable="true">
|
||||
<bpmn:documentation />
|
||||
<bpmn:startEvent id="StartEvent_1">
|
||||
<bpmn:outgoing>SequenceFlow_1fmyo77</bpmn:outgoing>
|
||||
</bpmn:startEvent>
|
||||
<bpmn:scriptTask id="ScriptTask_02924vs" name="Load IRB Details">
|
||||
<bpmn:incoming>SequenceFlow_1fmyo77</bpmn:incoming>
|
||||
<bpmn:outgoing>SequenceFlow_18nr0gf</bpmn:outgoing>
|
||||
<bpmn:script>details = study_info('details')</bpmn:script>
|
||||
</bpmn:scriptTask>
|
||||
<bpmn:sequenceFlow id="SequenceFlow_1fmyo77" sourceRef="StartEvent_1" targetRef="ScriptTask_02924vs" />
|
||||
<bpmn:sequenceFlow id="SequenceFlow_18nr0gf" sourceRef="ScriptTask_02924vs" targetRef="Activity_FromIRB-API" />
|
||||
<bpmn:endEvent id="EndEvent_1qvyxg7">
|
||||
<bpmn:documentation>| Data Point | Value | Help |
|
||||
|:-------------- |:-------- |:------ |
|
||||
{% for key, value in details.items() -%}
|
||||
| {{key}} | {%- if value == None -%}
|
||||
\-\-
|
||||
{%- else -%}
|
||||
{%- if value is number -%}
|
||||
{%- if value = 1 -%}
|
||||
True
|
||||
{%- elif value == 0 -%}
|
||||
False
|
||||
{%- else -%}
|
||||
{{value}}
|
||||
{%- endif -%}
|
||||
{%- elif value is string -%}
|
||||
{%- if value|length -%}
|
||||
{{value}}
|
||||
{%- else -%}
|
||||
Question not presented
|
||||
{%- endif -%}
|
||||
{%- endif -%}
|
||||
{%- endif -%} | [Context here](/help)
|
||||
{% endfor -%}</bpmn:documentation>
|
||||
<bpmn:incoming>Flow_0m7unlb</bpmn:incoming>
|
||||
</bpmn:endEvent>
|
||||
<bpmn:sequenceFlow id="Flow_0m7unlb" sourceRef="Activity_FromIRB-API" targetRef="EndEvent_1qvyxg7" />
|
||||
<bpmn:manualTask id="Activity_FromIRB-API" name="From IRB API">
|
||||
<bpmn:documentation>| Data Point | Value | Help |
|
||||
|:-------------- |:-------- |:------ |
|
||||
{% for key, value in details.items() -%}
|
||||
| {{key}} | {%- if value == None -%}
|
||||
\-\-
|
||||
{%- else -%}
|
||||
{%- if value is number -%}
|
||||
{%- if value == 1 -%}
|
||||
True
|
||||
{%- elif value == 0 -%}
|
||||
False
|
||||
{%- else -%}
|
||||
{{value}}
|
||||
{%- endif -%}
|
||||
{%- elif value is string -%}
|
||||
{%- if value|length -%}
|
||||
{{value}}
|
||||
{%- else -%}
|
||||
Question not presented
|
||||
{%- endif -%}
|
||||
{%- endif -%}
|
||||
{%- endif -%} | [Context here](/help)
|
||||
{% endfor -%}</bpmn:documentation>
|
||||
<bpmn:incoming>SequenceFlow_18nr0gf</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_0m7unlb</bpmn:outgoing>
|
||||
</bpmn:manualTask>
|
||||
</bpmn:process>
|
||||
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
|
||||
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1gmf4la">
|
||||
<bpmndi:BPMNEdge id="Flow_0m7unlb_di" bpmnElement="Flow_0m7unlb">
|
||||
<di:waypoint x="520" y="117" />
|
||||
<di:waypoint x="622" y="117" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="SequenceFlow_18nr0gf_di" bpmnElement="SequenceFlow_18nr0gf">
|
||||
<di:waypoint x="360" y="117" />
|
||||
<di:waypoint x="420" y="117" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="SequenceFlow_1fmyo77_di" bpmnElement="SequenceFlow_1fmyo77">
|
||||
<di:waypoint x="188" y="117" />
|
||||
<di:waypoint x="260" y="117" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
|
||||
<dc:Bounds x="152" y="99" width="36" height="36" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="ScriptTask_02924vs_di" bpmnElement="ScriptTask_02924vs">
|
||||
<dc:Bounds x="260" y="77" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="EndEvent_1qvyxg7_di" bpmnElement="EndEvent_1qvyxg7">
|
||||
<dc:Bounds x="622" y="99" width="36" height="36" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_19nawos_di" bpmnElement="Activity_FromIRB-API">
|
||||
<dc:Bounds x="420" y="77" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
</bpmndi:BPMNPlane>
|
||||
</bpmndi:BPMNDiagram>
|
||||
</bpmn:definitions>
|
@ -0,0 +1,86 @@
|
||||
<?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_06dpn07" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="4.2.0">
|
||||
<bpmn:process id="Process_1iqn8uk" isExecutable="true">
|
||||
<bpmn:startEvent id="StartEvent_1">
|
||||
<bpmn:outgoing>Flow_0dbfi6t</bpmn:outgoing>
|
||||
</bpmn:startEvent>
|
||||
<bpmn:sequenceFlow id="Flow_0dbfi6t" sourceRef="StartEvent_1" targetRef="Activity_09rr8u7" />
|
||||
<bpmn:manualTask id="Activity_09rr8u7" name="Hello">
|
||||
<bpmn:documentation><H1>Hello</H1></bpmn:documentation>
|
||||
<bpmn:incoming>Flow_0dbfi6t</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_02rje6r</bpmn:outgoing>
|
||||
</bpmn:manualTask>
|
||||
<bpmn:sequenceFlow id="Flow_02rje6r" sourceRef="Activity_09rr8u7" targetRef="Activity_GetName" />
|
||||
<bpmn:userTask id="Activity_GetName" name="Get Name" camunda:formKey="GetName">
|
||||
<bpmn:extensionElements>
|
||||
<camunda:formData>
|
||||
<camunda:formField id="name" label="Name" type="string" defaultValue="World" />
|
||||
</camunda:formData>
|
||||
</bpmn:extensionElements>
|
||||
<bpmn:incoming>Flow_02rje6r</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_1iphrck</bpmn:outgoing>
|
||||
</bpmn:userTask>
|
||||
<bpmn:sequenceFlow id="Flow_1iphrck" sourceRef="Activity_GetName" targetRef="Activity_GetTitle" />
|
||||
<bpmn:userTask id="Activity_GetTitle" name="Get Title" camunda:formKey="GetTitle">
|
||||
<bpmn:extensionElements>
|
||||
<camunda:formData>
|
||||
<camunda:formField id="user-title" label="Title" type="string" />
|
||||
</camunda:formData>
|
||||
</bpmn:extensionElements>
|
||||
<bpmn:incoming>Flow_1iphrck</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_0cxh51h</bpmn:outgoing>
|
||||
</bpmn:userTask>
|
||||
<bpmn:sequenceFlow id="Flow_0cxh51h" sourceRef="Activity_GetTitle" targetRef="Activity_SayHello" />
|
||||
<bpmn:scriptTask id="Activity_SayHello" name="Say Hello">
|
||||
<bpmn:incoming>Flow_0cxh51h</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_0hbiuz4</bpmn:outgoing>
|
||||
<bpmn:script>print('Hello', name)</bpmn:script>
|
||||
</bpmn:scriptTask>
|
||||
<bpmn:endEvent id="Event_13veu8t">
|
||||
<bpmn:incoming>Flow_0hbiuz4</bpmn:incoming>
|
||||
</bpmn:endEvent>
|
||||
<bpmn:sequenceFlow id="Flow_0hbiuz4" sourceRef="Activity_SayHello" targetRef="Event_13veu8t" />
|
||||
</bpmn:process>
|
||||
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
|
||||
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1iqn8uk">
|
||||
<bpmndi:BPMNEdge id="Flow_0dbfi6t_di" bpmnElement="Flow_0dbfi6t">
|
||||
<di:waypoint x="215" y="117" />
|
||||
<di:waypoint x="270" y="117" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_02rje6r_di" bpmnElement="Flow_02rje6r">
|
||||
<di:waypoint x="370" y="117" />
|
||||
<di:waypoint x="430" y="117" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_1iphrck_di" bpmnElement="Flow_1iphrck">
|
||||
<di:waypoint x="530" y="117" />
|
||||
<di:waypoint x="590" y="117" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_0cxh51h_di" bpmnElement="Flow_0cxh51h">
|
||||
<di:waypoint x="690" y="117" />
|
||||
<di:waypoint x="750" y="117" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_0hbiuz4_di" bpmnElement="Flow_0hbiuz4">
|
||||
<di:waypoint x="850" y="117" />
|
||||
<di:waypoint x="912" y="117" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<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_132twgr_di" bpmnElement="Activity_09rr8u7">
|
||||
<dc:Bounds x="270" y="77" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_0it9qzi_di" bpmnElement="Activity_GetName">
|
||||
<dc:Bounds x="430" y="77" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_19s9l3h_di" bpmnElement="Activity_GetTitle">
|
||||
<dc:Bounds x="590" y="77" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_05qpklh_di" bpmnElement="Activity_SayHello">
|
||||
<dc:Bounds x="750" y="77" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Event_13veu8t_di" bpmnElement="Event_13veu8t">
|
||||
<dc:Bounds x="912" y="99" width="36" height="36" />
|
||||
</bpmndi:BPMNShape>
|
||||
</bpmndi:BPMNPlane>
|
||||
</bpmndi:BPMNDiagram>
|
||||
</bpmn:definitions>
|
@ -0,0 +1,72 @@
|
||||
<?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_a699b4a" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="3.0.0-dev">
|
||||
<bpmn:process id="Process_PrintName" name="PrintName" isExecutable="true">
|
||||
<bpmn:startEvent id="StartEvent_1">
|
||||
<bpmn:outgoing>Flow_1wfzn0v</bpmn:outgoing>
|
||||
</bpmn:startEvent>
|
||||
<bpmn:sequenceFlow id="Flow_1wfzn0v" sourceRef="StartEvent_1" targetRef="Activity_GetName" />
|
||||
<bpmn:sequenceFlow id="Flow_0e9yohi" sourceRef="Activity_GetName" targetRef="Activity_PrintHello" />
|
||||
<bpmn:sequenceFlow id="Flow_1nt2lx5" sourceRef="Activity_PrintHello" targetRef="Activity_ThankYou" />
|
||||
<bpmn:endEvent id="Event_EndEvent" name="End Event">
|
||||
<bpmn:incoming>Flow_1yrn6kp</bpmn:incoming>
|
||||
</bpmn:endEvent>
|
||||
<bpmn:sequenceFlow id="Flow_1yrn6kp" sourceRef="Activity_ThankYou" targetRef="Event_EndEvent" />
|
||||
<bpmn:userTask id="Activity_GetName" name="Get Name" camunda:formKey="GetNameForm">
|
||||
<bpmn:extensionElements>
|
||||
<camunda:formData>
|
||||
<camunda:formField id="name" label="Name: " defaultValue="World" />
|
||||
</camunda:formData>
|
||||
</bpmn:extensionElements>
|
||||
<bpmn:incoming>Flow_1wfzn0v</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_0e9yohi</bpmn:outgoing>
|
||||
</bpmn:userTask>
|
||||
<bpmn:scriptTask id="Activity_PrintHello" name="Print Hello">
|
||||
<bpmn:incoming>Flow_0e9yohi</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_1nt2lx5</bpmn:outgoing>
|
||||
<bpmn:script>print('Hello', name)</bpmn:script>
|
||||
</bpmn:scriptTask>
|
||||
<bpmn:scriptTask id="Activity_ThankYou" name="Thank You">
|
||||
<bpmn:incoming>Flow_1nt2lx5</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_1yrn6kp</bpmn:outgoing>
|
||||
<bpmn:script>print('Thank You')</bpmn:script>
|
||||
</bpmn:scriptTask>
|
||||
</bpmn:process>
|
||||
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
|
||||
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_PrintName">
|
||||
<bpmndi:BPMNEdge id="Flow_1yrn6kp_di" bpmnElement="Flow_1yrn6kp">
|
||||
<di:waypoint x="690" y="177" />
|
||||
<di:waypoint x="752" y="177" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_1nt2lx5_di" bpmnElement="Flow_1nt2lx5">
|
||||
<di:waypoint x="530" y="177" />
|
||||
<di:waypoint x="590" y="177" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_0e9yohi_di" bpmnElement="Flow_0e9yohi">
|
||||
<di:waypoint x="370" y="177" />
|
||||
<di:waypoint x="430" y="177" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_1wfzn0v_di" bpmnElement="Flow_1wfzn0v">
|
||||
<di:waypoint x="215" y="177" />
|
||||
<di:waypoint x="270" y="177" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
|
||||
<dc:Bounds x="179" y="159" width="36" height="36" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Event_04bt6qi_di" bpmnElement="Event_EndEvent">
|
||||
<dc:Bounds x="752" y="159" width="36" height="36" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="745" y="202" width="51" height="14" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_170ilqg_di" bpmnElement="Activity_GetName">
|
||||
<dc:Bounds x="270" y="137" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_09hu4va_di" bpmnElement="Activity_PrintHello">
|
||||
<dc:Bounds x="430" y="137" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_0mtp7p7_di" bpmnElement="Activity_ThankYou">
|
||||
<dc:Bounds x="590" y="137" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
</bpmndi:BPMNPlane>
|
||||
</bpmndi:BPMNDiagram>
|
||||
</bpmn:definitions>
|
@ -1,6 +1,6 @@
|
||||
from tests.base_test import BaseTest
|
||||
from crc import mail
|
||||
from crc.models.email import EmailModel
|
||||
from tests.base_test import BaseTest
|
||||
|
||||
|
||||
class TestEmailScript(BaseTest):
|
||||
@ -9,8 +9,8 @@ class TestEmailScript(BaseTest):
|
||||
workflow = self.create_workflow('email')
|
||||
|
||||
task_data = {
|
||||
'PIComputingID': 'dhf8r',
|
||||
'ApprvlApprvr1': 'lb3dp'
|
||||
'PIComputingID': 'dhf8r@virginia.edu',
|
||||
'ApprvlApprvr1': 'lb3dp@virginia.edu'
|
||||
}
|
||||
task = self.get_workflow_api(workflow).next_task
|
||||
|
||||
|
@ -243,10 +243,12 @@ class TestFilesApi(BaseTest):
|
||||
self.load_example_data()
|
||||
spec = session.query(WorkflowSpecModel).first()
|
||||
file = session.query(FileModel).filter_by(workflow_spec_id=spec.id).first()
|
||||
file_id = file.id
|
||||
rv = self.app.get('/v1.0/file/%i' % file.id, headers=self.logged_in_headers())
|
||||
self.assert_success(rv)
|
||||
rv = self.app.delete('/v1.0/file/%i' % file.id, headers=self.logged_in_headers())
|
||||
rv = self.app.get('/v1.0/file/%i' % file.id, headers=self.logged_in_headers())
|
||||
db.session.flush()
|
||||
rv = self.app.get('/v1.0/file/%i' % file_id, headers=self.logged_in_headers())
|
||||
self.assertEqual(404, rv.status_code)
|
||||
|
||||
def test_delete_file_after_approval(self):
|
||||
|
62
tests/study/test_study_actions_status.py
Normal file
62
tests/study/test_study_actions_status.py
Normal file
@ -0,0 +1,62 @@
|
||||
from tests.base_test import BaseTest
|
||||
from crc import session
|
||||
from crc.models.study import StudyModel, StudyStatus, StudySchema
|
||||
import json
|
||||
|
||||
|
||||
class TestStudyActionsStatus(BaseTest):
|
||||
|
||||
def update_study_status(self, study, study_schema):
|
||||
self.app.put('/v1.0/study/%i' % study.id,
|
||||
content_type="application/json",
|
||||
headers=self.logged_in_headers(),
|
||||
data=json.dumps(study_schema))
|
||||
|
||||
# The error happened when the dashboard reloaded,
|
||||
# in particular, when we got the studies for the user
|
||||
api_response = self.app.get('/v1.0/study', headers=self.logged_in_headers(), content_type="application/json")
|
||||
self.assert_success(api_response)
|
||||
|
||||
study_result = session.query(StudyModel).filter(StudyModel.id == study.id).first()
|
||||
return study_result
|
||||
|
||||
def test_hold_study(self):
|
||||
self.load_example_data()
|
||||
|
||||
study = session.query(StudyModel).first()
|
||||
self.assertEqual(study.status, StudyStatus.in_progress)
|
||||
|
||||
study_schema = StudySchema().dump(study)
|
||||
study_schema['status'] = 'hold'
|
||||
study_schema['comment'] = 'This is my hold comment'
|
||||
|
||||
study_result = self.update_study_status(study, study_schema)
|
||||
self.assertEqual(StudyStatus.hold, study_result.status)
|
||||
|
||||
def test_abandon_study(self):
|
||||
self.load_example_data()
|
||||
|
||||
study = session.query(StudyModel).first()
|
||||
self.assertEqual(study.status, StudyStatus.in_progress)
|
||||
|
||||
study_schema = StudySchema().dump(study)
|
||||
study_schema['status'] = 'abandoned'
|
||||
study_schema['comment'] = 'This is my abandon comment'
|
||||
|
||||
study_result = self.update_study_status(study, study_schema)
|
||||
self.assertEqual(StudyStatus.abandoned, study_result.status)
|
||||
|
||||
def test_open_enrollment_study(self):
|
||||
self.load_example_data()
|
||||
|
||||
study = session.query(StudyModel).first()
|
||||
self.assertEqual(study.status, StudyStatus.in_progress)
|
||||
|
||||
study_schema = StudySchema().dump(study)
|
||||
study_schema['status'] = 'open_for_enrollment'
|
||||
study_schema['comment'] = 'This is my open enrollment comment'
|
||||
study_schema['enrollment_date'] = '2021-01-04T05:00:00.000Z'
|
||||
|
||||
study_result = self.update_study_status(study, study_schema)
|
||||
self.assertEqual(StudyStatus.open_for_enrollment, study_result.status)
|
||||
|
53
tests/test_email_script.py
Normal file
53
tests/test_email_script.py
Normal file
@ -0,0 +1,53 @@
|
||||
from tests.base_test import BaseTest
|
||||
from crc import mail
|
||||
|
||||
|
||||
class TestEmailScript(BaseTest):
|
||||
|
||||
def test_email_script(self):
|
||||
with mail.record_messages() as outbox:
|
||||
|
||||
workflow = self.create_workflow('email_script')
|
||||
|
||||
first_task = self.get_workflow_api(workflow).next_task
|
||||
|
||||
workflow = self.get_workflow_api(workflow)
|
||||
self.complete_form(workflow, first_task, {'email_address': 'test@example.com'})
|
||||
|
||||
self.assertEqual(1, len(outbox))
|
||||
self.assertEqual('My Email Subject', outbox[0].subject)
|
||||
self.assertEqual(['test@example.com'], outbox[0].recipients)
|
||||
|
||||
def test_email_script_multiple(self):
|
||||
with mail.record_messages() as outbox:
|
||||
|
||||
workflow = self.create_workflow('email_script')
|
||||
|
||||
first_task = self.get_workflow_api(workflow).next_task
|
||||
|
||||
workflow = self.get_workflow_api(workflow)
|
||||
self.complete_form(workflow, first_task, {'email_address': ['test@example.com', 'test2@example.com']})
|
||||
|
||||
self.assertEqual(1, len(outbox))
|
||||
self.assertEqual("My Email Subject", outbox[0].subject)
|
||||
self.assertEqual(2, len(outbox[0].recipients))
|
||||
self.assertEqual('test@example.com', outbox[0].recipients[0])
|
||||
self.assertEqual('test2@example.com', outbox[0].recipients[1])
|
||||
|
||||
def test_bad_email_address_1(self):
|
||||
workflow = self.create_workflow('email_script')
|
||||
|
||||
first_task = self.get_workflow_api(workflow).next_task
|
||||
|
||||
workflow = self.get_workflow_api(workflow)
|
||||
with self.assertRaises(AssertionError):
|
||||
self.complete_form(workflow, first_task, {'email_address': 'test@example'})
|
||||
|
||||
def test_bad_email_address_2(self):
|
||||
workflow = self.create_workflow('email_script')
|
||||
|
||||
first_task = self.get_workflow_api(workflow).next_task
|
||||
|
||||
workflow = self.get_workflow_api(workflow)
|
||||
with self.assertRaises(AssertionError):
|
||||
self.complete_form(workflow, first_task, {'email_address': 'test'})
|
41
tests/test_is_file_uploaded_script.py
Normal file
41
tests/test_is_file_uploaded_script.py
Normal file
@ -0,0 +1,41 @@
|
||||
from tests.base_test import BaseTest
|
||||
from crc.services.file_service import FileService
|
||||
from crc.scripts.is_file_uploaded import IsFileUploaded
|
||||
|
||||
|
||||
class TestIsFileUploaded(BaseTest):
|
||||
|
||||
def test_file_uploaded_pass(self):
|
||||
self.load_example_data()
|
||||
irb_code_1 = 'UVACompl_PRCAppr'
|
||||
irb_code_2 = 'Study_App_Doc'
|
||||
|
||||
workflow = self.create_workflow('empty_workflow')
|
||||
first_task = self.get_workflow_api(workflow).next_task
|
||||
study_id = workflow.study_id
|
||||
|
||||
# We shouldn't have any files yet.
|
||||
files = FileService.get_files_for_study(study_id)
|
||||
self.assertEqual(0, len(files))
|
||||
self.assertEqual(False, IsFileUploaded.do_task(IsFileUploaded, first_task, study_id, workflow.id, irb_code_1))
|
||||
|
||||
# Add a file
|
||||
FileService.add_workflow_file(workflow_id=workflow.id,
|
||||
name="something.png", content_type="text",
|
||||
binary_data=b'1234', irb_doc_code=irb_code_1)
|
||||
|
||||
# Make sure we find the file
|
||||
files = FileService.get_files_for_study(study_id)
|
||||
self.assertEqual(1, len(files))
|
||||
self.assertEqual(True, IsFileUploaded.do_task(IsFileUploaded, first_task, study_id, workflow.id, irb_code_1))
|
||||
|
||||
# Add second file
|
||||
FileService.add_workflow_file(workflow_id=workflow.id,
|
||||
name="anything.png", content_type="text",
|
||||
binary_data=b'5678', irb_doc_code=irb_code_2)
|
||||
|
||||
# Make sure we find both files.
|
||||
files = FileService.get_files_for_study(study_id)
|
||||
self.assertEqual(2, len(files))
|
||||
self.assertEqual(True, IsFileUploaded.do_task(IsFileUploaded, first_task, study_id, workflow.id, irb_code_1))
|
||||
self.assertEqual(True, IsFileUploaded.do_task(IsFileUploaded, first_task, study_id, workflow.id, irb_code_2))
|
@ -53,7 +53,9 @@ class TestLookupService(BaseTest):
|
||||
file.close()
|
||||
|
||||
# restart the workflow, so it can pick up the changes.
|
||||
WorkflowProcessor(workflow, soft_reset=True)
|
||||
|
||||
processor = WorkflowProcessor.reset(workflow)
|
||||
workflow = processor.workflow_model
|
||||
|
||||
LookupService.lookup(workflow, "sponsor", "sam", limit=10)
|
||||
lookup_records = session.query(LookupFileModel).all()
|
||||
|
@ -68,7 +68,7 @@ class TestTasksApi(BaseTest):
|
||||
# get the first form in the two form workflow.
|
||||
workflow_api = self.get_workflow_api(workflow)
|
||||
self.assertEqual('two_forms', workflow_api.workflow_spec_id)
|
||||
self.assertEqual(2, len(workflow_api.navigation))
|
||||
self.assertEqual(5, len(workflow_api.navigation))
|
||||
self.assertIsNotNone(workflow_api.next_task.form)
|
||||
self.assertEqual("UserTask", workflow_api.next_task.type)
|
||||
self.assertEqual("StepOne", workflow_api.next_task.name)
|
||||
@ -113,14 +113,20 @@ class TestTasksApi(BaseTest):
|
||||
|
||||
self.assertIsNotNone(workflow_api.navigation)
|
||||
nav = workflow_api.navigation
|
||||
self.assertEqual(5, len(nav))
|
||||
self.assertEqual("Do You Have Bananas", nav[0]['title'])
|
||||
self.assertEqual("Bananas?", nav[1]['title'])
|
||||
self.assertEqual("FUTURE", nav[1]['state'])
|
||||
self.assertEqual("yes", nav[2]['title'])
|
||||
self.assertEqual("NOOP", nav[2]['state'])
|
||||
self.assertEqual("no", nav[3]['title'])
|
||||
self.assertEqual("NOOP", nav[3]['state'])
|
||||
self.assertEqual(4, len(nav))
|
||||
self.assertEqual("Do You Have Bananas", nav[1].description)
|
||||
self.assertEqual("Bananas?", nav[2].description)
|
||||
self.assertEqual("LIKELY", nav[2].state)
|
||||
|
||||
self.assertEqual("yes", nav[2].children[0].description)
|
||||
self.assertEqual("LIKELY", nav[2].children[0].state)
|
||||
self.assertEqual("of Bananas", nav[2].children[0].children[0].description)
|
||||
self.assertEqual("EndEvent", nav[2].children[0].children[1].spec_type)
|
||||
|
||||
self.assertEqual("no", nav[2].children[1].description)
|
||||
self.assertEqual("MAYBE", nav[2].children[1].state)
|
||||
self.assertEqual("no bananas", nav[2].children[1].children[0].description)
|
||||
self.assertEqual("EndEvent", nav[2].children[1].children[1].spec_type)
|
||||
|
||||
def test_navigation_with_exclusive_gateway(self):
|
||||
workflow = self.create_workflow('exclusive_gateway_2')
|
||||
@ -130,13 +136,16 @@ class TestTasksApi(BaseTest):
|
||||
self.assertIsNotNone(workflow_api.navigation)
|
||||
nav = workflow_api.navigation
|
||||
self.assertEqual(7, len(nav))
|
||||
self.assertEqual("Task 1", nav[0]['title'])
|
||||
self.assertEqual("Which Branch?", nav[1]['title'])
|
||||
self.assertEqual("a", nav[2]['title'])
|
||||
self.assertEqual("Task 2a", nav[3]['title'])
|
||||
self.assertEqual("b", nav[4]['title'])
|
||||
self.assertEqual("Task 2b", nav[5]['title'])
|
||||
self.assertEqual("Task 3", nav[6]['title'])
|
||||
self.assertEqual("Task 1", nav[1].description)
|
||||
self.assertEqual("Which Branch?", nav[2].description)
|
||||
self.assertEqual("a", nav[2].children[0].description)
|
||||
self.assertEqual("Task 2a", nav[2].children[0].children[0].description)
|
||||
self.assertEqual("b", nav[2].children[1].description)
|
||||
self.assertEqual("Task 2b", nav[2].children[1].children[0].description)
|
||||
self.assertEqual(None, nav[3].description)
|
||||
self.assertEqual("Task 3", nav[4].description)
|
||||
self.assertEqual("EndEvent", nav[5].spec_type)
|
||||
|
||||
|
||||
def test_document_added_to_workflow_shows_up_in_file_list(self):
|
||||
self.create_reference_document()
|
||||
@ -195,7 +204,7 @@ class TestTasksApi(BaseTest):
|
||||
self.assertTrue(workflow_api.spec_version.startswith("v1 "))
|
||||
self.assertFalse(workflow_api.is_latest_spec)
|
||||
|
||||
workflow_api = self.get_workflow_api(workflow, hard_reset=True)
|
||||
workflow_api = self.restart_workflow_api(workflow_api, clear_data=True)
|
||||
self.assertTrue(workflow_api.spec_version.startswith("v2 "))
|
||||
self.assertTrue(workflow_api.is_latest_spec)
|
||||
|
||||
@ -204,30 +213,6 @@ class TestTasksApi(BaseTest):
|
||||
self.assertTrue(workflow_api.spec_version.startswith("v2 "))
|
||||
self.assertTrue(workflow_api.is_latest_spec)
|
||||
|
||||
def test_soft_reset_errors_out_and_next_result_is_on_original_version(self):
|
||||
# Start the basic two_forms workflow and complete a task.
|
||||
workflow = self.create_workflow('two_forms')
|
||||
workflow_api = self.get_workflow_api(workflow)
|
||||
self.complete_form(workflow, workflow_api.next_task, {"color": "blue"})
|
||||
self.assertTrue(workflow_api.is_latest_spec)
|
||||
|
||||
# Modify the specification, with a major change that alters the flow and can't be deserialized
|
||||
# effectively, if it uses the latest spec files.
|
||||
file_path = os.path.join(app.root_path, '..', 'tests', 'data', 'two_forms', 'modified', 'two_forms_struc_mod.bpmn')
|
||||
self.replace_file("two_forms.bpmn", file_path)
|
||||
|
||||
# perform a soft reset returns an error
|
||||
rv = self.app.get('/v1.0/workflow/%i?soft_reset=%s&hard_reset=%s' %
|
||||
(workflow.id, "true", "false"),
|
||||
content_type="application/json",
|
||||
headers=self.logged_in_headers())
|
||||
self.assert_failure(rv, error_code="unexpected_workflow_structure")
|
||||
|
||||
# Try again without a soft reset, and we are still ok, and on the original version.
|
||||
workflow_api = self.get_workflow_api(workflow)
|
||||
self.assertTrue(workflow_api.spec_version.startswith("v1 "))
|
||||
self.assertFalse(workflow_api.is_latest_spec)
|
||||
|
||||
|
||||
def test_manual_task_with_external_documentation(self):
|
||||
workflow = self.create_workflow('manual_task_with_external_documentation')
|
||||
@ -267,7 +252,7 @@ class TestTasksApi(BaseTest):
|
||||
# get the first form in the two form workflow.
|
||||
workflow = self.get_workflow_api(workflow)
|
||||
navigation = self.get_workflow_api(workflow).navigation
|
||||
self.assertEqual(4, len(navigation)) # Start task, form_task, multi_task, end task
|
||||
self.assertEqual(5, len(navigation)) # Start task, form_task, multi_task, end task
|
||||
self.assertEqual("UserTask", workflow.next_task.type)
|
||||
self.assertEqual(MultiInstanceType.sequential.value, workflow.next_task.multi_instance_type)
|
||||
self.assertEqual(5, workflow.next_task.multi_instance_count)
|
||||
@ -385,7 +370,7 @@ class TestTasksApi(BaseTest):
|
||||
navigation = workflow_api.navigation
|
||||
task = workflow_api.next_task
|
||||
|
||||
self.assertEqual(2, len(navigation))
|
||||
self.assertEqual(5, len(navigation))
|
||||
self.assertEqual("UserTask", task.type)
|
||||
self.assertEqual("Activity_A", task.name)
|
||||
self.assertEqual("My Sub Process", task.process_name)
|
||||
@ -452,8 +437,8 @@ class TestTasksApi(BaseTest):
|
||||
workflow = self.create_workflow('multi_instance_parallel')
|
||||
|
||||
workflow_api = self.get_workflow_api(workflow)
|
||||
self.assertEqual(8, len(workflow_api.navigation))
|
||||
ready_items = [nav for nav in workflow_api.navigation if nav['state'] == "READY"]
|
||||
self.assertEqual(9, len(workflow_api.navigation))
|
||||
ready_items = [nav for nav in workflow_api.navigation if nav.state == "READY"]
|
||||
self.assertEqual(5, len(ready_items))
|
||||
|
||||
self.assertEqual("UserTask", workflow_api.next_task.type)
|
||||
@ -461,8 +446,8 @@ class TestTasksApi(BaseTest):
|
||||
self.assertEqual("Primary Investigator", workflow_api.next_task.title)
|
||||
|
||||
for i in random.sample(range(5), 5):
|
||||
task = TaskSchema().load(ready_items[i]['task'])
|
||||
rv = self.app.put('/v1.0/workflow/%i/task/%s/set_token' % (workflow.id, task.id),
|
||||
task_id = ready_items[i].task_id
|
||||
rv = self.app.put('/v1.0/workflow/%i/task/%s/set_token' % (workflow.id, task_id),
|
||||
headers=self.logged_in_headers(),
|
||||
content_type="application/json")
|
||||
self.assert_success(rv)
|
||||
@ -470,7 +455,7 @@ class TestTasksApi(BaseTest):
|
||||
workflow = WorkflowApiSchema().load(json_data)
|
||||
data = workflow.next_task.data
|
||||
data['investigator']['email'] = "dhf8r@virginia.edu"
|
||||
self.complete_form(workflow, task, data)
|
||||
self.complete_form(workflow, workflow.next_task, data)
|
||||
#tasks = self.get_workflow_api(workflow).user_tasks
|
||||
|
||||
workflow = self.get_workflow_api(workflow)
|
||||
|
28
tests/test_user_in_logs.py
Normal file
28
tests/test_user_in_logs.py
Normal file
@ -0,0 +1,28 @@
|
||||
from tests.base_test import BaseTest
|
||||
from crc import db
|
||||
from crc.models.user import UserModel
|
||||
import json
|
||||
|
||||
|
||||
class TestUserID(BaseTest):
|
||||
|
||||
def test_user_id_in_request(self):
|
||||
"""This assures the uid is in response via ApiError"""
|
||||
|
||||
workflow = self.create_workflow('failing_workflow')
|
||||
user_uid = workflow.study.user_uid
|
||||
user = db.session.query(UserModel).filter_by(uid=user_uid).first()
|
||||
rv = self.app.get(f'/v1.0/workflow/{workflow.id}'
|
||||
f'?soft_reset={str(False)}'
|
||||
f'&hard_reset={str(False)}'
|
||||
f'&do_engine_steps={str(True)}',
|
||||
headers=self.logged_in_headers(user),
|
||||
content_type="application/json")
|
||||
data = json.loads(rv.data)
|
||||
self.assertEqual(data['task_user'], user_uid)
|
||||
|
||||
def test_user_id_in_sentry(self):
|
||||
"""This assures the uid is in Sentry.
|
||||
We use this to send errors to Slack."""
|
||||
# Currently have no clue how to do this :(
|
||||
pass
|
@ -1,6 +1,8 @@
|
||||
import json
|
||||
|
||||
from tests.base_test import BaseTest
|
||||
|
||||
from crc.models.api_models import NavigationItemSchema
|
||||
from crc.models.workflow import WorkflowStatus
|
||||
from crc import db
|
||||
from crc.api.common import ApiError
|
||||
@ -62,8 +64,8 @@ class TestTasksApi(BaseTest):
|
||||
workflow_api = self.get_workflow_api(workflow, user_uid=submitter.uid)
|
||||
|
||||
nav = workflow_api.navigation
|
||||
self.assertEqual(5, len(nav))
|
||||
self.assertEqual("supervisor", nav[1]['lane'])
|
||||
self.assertEqual(4, len(nav))
|
||||
self.assertEqual("supervisor", nav[2].lane)
|
||||
|
||||
def test_get_outstanding_tasks_awaiting_current_user(self):
|
||||
submitter = self.create_user(uid='lje5u')
|
||||
@ -121,12 +123,10 @@ class TestTasksApi(BaseTest):
|
||||
# Navigation as Submitter with ready task.
|
||||
workflow_api = self.get_workflow_api(workflow, user_uid=submitter.uid)
|
||||
nav = workflow_api.navigation
|
||||
self.assertEqual(5, len(nav))
|
||||
self.assertEqual('READY', nav[0]['state']) # First item is ready, no progress yet.
|
||||
self.assertEqual('LOCKED', nav[1]['state']) # Second item is locked, it is the review and doesn't belong to this user.
|
||||
self.assertEqual('LOCKED', nav[2]['state']) # third item is a gateway, and belongs to no one, and is locked.
|
||||
self.assertEqual('NOOP', nav[3]['state']) # Approved Path, has no operation
|
||||
self.assertEqual('NOOP', nav[4]['state']) # Rejected Path, has no operation.
|
||||
self.assertEqual(4, len(nav))
|
||||
self.assertEqual('READY', nav[1].state) # First item is ready, no progress yet.
|
||||
self.assertEqual('LOCKED', nav[2].state) # Second item is locked, it is the review and doesn't belong to this user.
|
||||
self.assertEqual('LIKELY', nav[3].state) # Third item is a gateway, which contains things that are also locked.
|
||||
self.assertEqual('READY', workflow_api.next_task.state)
|
||||
|
||||
# Navigation as Submitter after handoff to supervisor
|
||||
@ -134,10 +134,9 @@ class TestTasksApi(BaseTest):
|
||||
data['supervisor'] = supervisor.uid
|
||||
workflow_api = self.complete_form(workflow, workflow_api.next_task, data, user_uid=submitter.uid)
|
||||
nav = workflow_api.navigation
|
||||
self.assertEqual('COMPLETED', nav[0]['state']) # First item is ready, no progress yet.
|
||||
self.assertEqual('LOCKED', nav[1]['state']) # Second item is locked, it is the review and doesn't belong to this user.
|
||||
self.assertEqual('LOCKED', nav[2]['state']) # third item is a gateway, and belongs to no one, and is locked.
|
||||
self.assertEqual('LOCKED', workflow_api.next_task.state)
|
||||
self.assertEqual('COMPLETED', nav[1].state) # First item is ready, no progress yet.
|
||||
self.assertEqual('LOCKED', nav[2].state) # Second item is locked, it is the review and doesn't belong to this user.
|
||||
self.assertEqual('LIKELY', nav[3].state) # third item is a gateway, and belongs to no one
|
||||
# In the event the next task is locked, we should say something sensible here.
|
||||
# It is possible to look at the role of the task, and say The next task "TASK TITLE" will
|
||||
# be handled by 'dhf8r', who is full-filling the role of supervisor. the Task Data
|
||||
@ -149,10 +148,9 @@ class TestTasksApi(BaseTest):
|
||||
# Navigation as Supervisor
|
||||
workflow_api = self.get_workflow_api(workflow, user_uid=supervisor.uid)
|
||||
nav = workflow_api.navigation
|
||||
self.assertEqual(5, len(nav))
|
||||
self.assertEqual('LOCKED', nav[0]['state']) # First item belongs to the submitter, and is locked.
|
||||
self.assertEqual('READY', nav[1]['state']) # Second item is locked, it is the review and doesn't belong to this user.
|
||||
self.assertEqual('LOCKED', nav[2]['state']) # third item is a gateway, and belongs to no one, and is locked.
|
||||
self.assertEqual('LOCKED', nav[1].state) # First item belongs to the submitter, and is locked.
|
||||
self.assertEqual('READY', nav[2].state) # Second item is ready, as we are now the supervisor.
|
||||
self.assertEqual('LIKELY', nav[3].state) # Feedback is locked.
|
||||
self.assertEqual('READY', workflow_api.next_task.state)
|
||||
|
||||
data = workflow_api.next_task.data
|
||||
@ -161,28 +159,33 @@ class TestTasksApi(BaseTest):
|
||||
|
||||
# Navigation as Supervisor, after completing task.
|
||||
nav = workflow_api.navigation
|
||||
self.assertEqual(5, len(nav))
|
||||
self.assertEqual('LOCKED', nav[0]['state']) # First item belongs to the submitter, and is locked.
|
||||
self.assertEqual('COMPLETED', nav[1]['state']) # Second item is locked, it is the review and doesn't belong to this user.
|
||||
self.assertEqual('COMPLETED', nav[2]['state']) # third item is a gateway, and is now complete.
|
||||
self.assertEqual('LOCKED', nav[1].state) # First item belongs to the submitter, and is locked.
|
||||
self.assertEqual('COMPLETED', nav[2].state) # Second item is locked, it is the review and doesn't belong to this user.
|
||||
self.assertEqual('READY', nav[3].state) # Gateway is ready, and should be unfolded
|
||||
self.assertEqual(None, nav[3].children[0].state) # sequence flow for approved is none - we aren't going this way.
|
||||
self.assertEqual('READY', nav[3].children[1].state) # sequence flow for denied is ready
|
||||
self.assertEqual('LOCKED', nav[3].children[1].children[0].state) # Feedback is locked, it belongs to submitter
|
||||
self.assertEqual('LOCKED', nav[3].children[1].children[0].state) # Approval is locked, it belongs to the submitter
|
||||
self.assertEqual('LOCKED', workflow_api.next_task.state)
|
||||
|
||||
# Navigation as Submitter, coming back in to a rejected workflow to view the rejection message.
|
||||
workflow_api = self.get_workflow_api(workflow, user_uid=submitter.uid)
|
||||
nav = workflow_api.navigation
|
||||
self.assertEqual(5, len(nav))
|
||||
self.assertEqual('COMPLETED', nav[0]['state']) # First item belongs to the submitter, and is locked.
|
||||
self.assertEqual('LOCKED', nav[1]['state']) # Second item is locked, it is the review and doesn't belong to this user.
|
||||
self.assertEqual('LOCKED', nav[2]['state']) # third item is a gateway belonging to the supervisor, and is locked.
|
||||
self.assertEqual('READY', workflow_api.next_task.state)
|
||||
self.assertEqual(4, len(nav))
|
||||
self.assertEqual('COMPLETED', nav[1].state) # First item belongs to the submitter, and is locked.
|
||||
self.assertEqual('LOCKED', nav[2].state) # Second item is locked, it is the review and doesn't belong to this user.
|
||||
self.assertEqual('READY', nav[3].state)
|
||||
self.assertEqual(None, nav[3].children[0].state) # sequence flow for approved is none - we aren't going this way.
|
||||
self.assertEqual('READY', nav[3].children[1].state) # sequence flow for denied is ready
|
||||
self.assertEqual('READY', nav[3].children[1].children[0].state) # Feedback is locked, it belongs to submitter
|
||||
self.assertEqual('READY', nav[3].children[1].children[0].state) # Approval is locked, it belongs to the submitter
|
||||
|
||||
# Navigation as Submitter, re-completing the original request a second time, and sending it for review.
|
||||
workflow_api = self.complete_form(workflow, workflow_api.next_task, data, user_uid=submitter.uid)
|
||||
nav = workflow_api.navigation
|
||||
self.assertEqual(5, len(nav))
|
||||
self.assertEqual('READY', nav[0]['state']) # When you loop back the task is again in the ready state.
|
||||
self.assertEqual('LOCKED', nav[1]['state']) # Second item is locked, it is the review and doesn't belong to this user.
|
||||
self.assertEqual('LOCKED', nav[2]['state']) # third item is a gateway belonging to the supervisor, and is locked.
|
||||
self.assertEqual('READY', nav[1].state) # When you loop back the task is again in the ready state.
|
||||
self.assertEqual('LOCKED', nav[2].state) # Second item is locked, it is the review and doesn't belong to this user.
|
||||
self.assertEqual('COMPLETED', nav[3].state) # Feedback is completed
|
||||
self.assertEqual('READY', workflow_api.next_task.state)
|
||||
|
||||
data["favorite_color"] = "blue"
|
||||
@ -225,7 +228,8 @@ class TestTasksApi(BaseTest):
|
||||
self.assertEqual(1, len(self.get_assignment_task_events(supervisor.uid)))
|
||||
|
||||
# Resetting the workflow at this point should clear the event log.
|
||||
workflow_api = self.get_workflow_api(workflow, hard_reset=True, user_uid=submitter.uid)
|
||||
workflow_api = self.restart_workflow_api(workflow, user_uid=submitter.uid)
|
||||
workflow_api = self.get_workflow_api(workflow, user_uid=submitter.uid)
|
||||
self.assertEqual(0, len(self.get_assignment_task_events(supervisor.uid)))
|
||||
|
||||
# Re-complete first task, and awaiting tasks should shift to 0 for for submitter, and 1 for supervisor
|
||||
|
18
tests/test_verify_end_event.py
Normal file
18
tests/test_verify_end_event.py
Normal file
@ -0,0 +1,18 @@
|
||||
from tests.base_test import BaseTest
|
||||
from crc.services.workflow_service import WorkflowService
|
||||
from crc.api.common import ApiError
|
||||
from jinja2.exceptions import TemplateSyntaxError
|
||||
|
||||
|
||||
class TestValidateEndEvent(BaseTest):
|
||||
|
||||
def test_validate_end_event(self):
|
||||
|
||||
error_string = """Error processing template for task EndEvent_1qvyxg7: expected token 'end of statement block', got '='"""
|
||||
|
||||
self.load_example_data()
|
||||
spec_model = self.load_test_spec('verify_end_event')
|
||||
try:
|
||||
WorkflowService.test_spec(spec_model.id)
|
||||
except ApiError as e:
|
||||
self.assertEqual(str(e), error_string)
|
@ -1,7 +1,7 @@
|
||||
from unittest.mock import patch
|
||||
from tests.base_test import BaseTest
|
||||
|
||||
from crc import db
|
||||
from tests.base_test import BaseTest
|
||||
from crc.api.workflow_sync import get_all_spec_state, \
|
||||
get_changed_workflows, \
|
||||
get_workflow_spec_files, \
|
||||
|
32
tests/workflow/test_duplicate_workflow_spec_file.py
Normal file
32
tests/workflow/test_duplicate_workflow_spec_file.py
Normal file
@ -0,0 +1,32 @@
|
||||
from tests.base_test import BaseTest
|
||||
from crc import session
|
||||
from crc.api.common import ApiError
|
||||
from crc.models.workflow import WorkflowSpecModel
|
||||
from crc.services.file_service import FileService
|
||||
|
||||
|
||||
class TestDuplicateWorkflowSpecFile(BaseTest):
|
||||
|
||||
def test_duplicate_workflow_spec_file(self):
|
||||
# We want this to fail.
|
||||
# Users should not be able to upload a file that already exists.
|
||||
|
||||
self.load_example_data()
|
||||
spec = session.query(WorkflowSpecModel).first()
|
||||
|
||||
# Add a file
|
||||
file_model = FileService.add_workflow_spec_file(spec,
|
||||
name="something.png",
|
||||
content_type="text",
|
||||
binary_data=b'1234')
|
||||
self.assertEqual(file_model.name, 'something.png')
|
||||
self.assertEqual(file_model.content_type, 'text')
|
||||
|
||||
# Try to add it again
|
||||
try:
|
||||
FileService.add_workflow_spec_file(spec,
|
||||
name="something.png",
|
||||
content_type="text",
|
||||
binary_data=b'5678')
|
||||
except ApiError as ae:
|
||||
self.assertEqual(ae.message, 'If you want to replace the file, use the update mechanism.')
|
14
tests/workflow/test_workflow_form_field_name.py
Normal file
14
tests/workflow/test_workflow_form_field_name.py
Normal file
@ -0,0 +1,14 @@
|
||||
import json
|
||||
from tests.base_test import BaseTest
|
||||
|
||||
|
||||
class TestFormFieldName(BaseTest):
|
||||
|
||||
def test_form_field_name(self):
|
||||
spec_model = self.load_test_spec('workflow_form_field_name')
|
||||
rv = self.app.get('/v1.0/workflow-specification/%s/validate' % spec_model.id, headers=self.logged_in_headers())
|
||||
|
||||
json_data = json.loads(rv.get_data(as_text=True))
|
||||
self.assertEqual(json_data[0]['message'],
|
||||
'When populating all fields ... Invalid Field name: "user-title". A field ID must begin '
|
||||
'with a letter, and can only contain letters, numbers, and "_"')
|
14
tests/workflow/test_workflow_form_field_type.py
Normal file
14
tests/workflow/test_workflow_form_field_type.py
Normal file
@ -0,0 +1,14 @@
|
||||
import json
|
||||
from tests.base_test import BaseTest
|
||||
|
||||
|
||||
class TestFormFieldType(BaseTest):
|
||||
|
||||
def test_form_field_type(self):
|
||||
spec_model = self.load_test_spec('workflow_form_field_type')
|
||||
rv = self.app.get('/v1.0/workflow-specification/%s/validate' % spec_model.id, headers=self.logged_in_headers())
|
||||
|
||||
json_data = json.loads(rv.get_data(as_text=True))
|
||||
self.assertEqual(json_data[0]['message'],
|
||||
'When populating all fields ... Type is missing for field "name". A field type must be provided.')
|
||||
# print('TestFormFieldType: Good Form')
|
@ -173,28 +173,6 @@ class TestWorkflowProcessor(BaseTest):
|
||||
self.assertEqual("workflow_validation_error", context.exception.code)
|
||||
self.assertTrue("bpmn:startEvent" in context.exception.message)
|
||||
|
||||
def test_workflow_spec_key_error(self):
|
||||
"""Frequently seeing errors in the logs about a 'Key' error, where a workflow
|
||||
references something that doesn't exist in the midst of processing. Want to
|
||||
make sure we produce errors to the front end that allows us to debug this."""
|
||||
# Start the two_forms workflow, and enter some data in the first form.
|
||||
self.load_example_data()
|
||||
study = session.query(StudyModel).first()
|
||||
workflow_spec_model = self.load_test_spec("two_forms")
|
||||
processor = self.get_processor(study, workflow_spec_model)
|
||||
self.assertEqual(processor.workflow_model.workflow_spec_id, workflow_spec_model.id)
|
||||
task = processor.next_task()
|
||||
task.data = {"color": "blue"}
|
||||
processor.complete_task(task)
|
||||
|
||||
# Modify the specification, with a major change.
|
||||
file_path = os.path.join(app.root_path, '..', 'tests', 'data', 'two_forms', 'modified', 'two_forms_struc_mod.bpmn')
|
||||
self.replace_file("two_forms.bpmn", file_path)
|
||||
|
||||
# Attempting a soft update on a structural change should raise a sensible error.
|
||||
with self.assertRaises(ApiError) as context:
|
||||
processor3 = WorkflowProcessor(processor.workflow_model, soft_reset=True)
|
||||
self.assertEqual("unexpected_workflow_structure", context.exception.code)
|
||||
|
||||
def test_workflow_with_bad_expression_raises_sensible_error(self):
|
||||
self.load_example_data()
|
||||
@ -301,7 +279,9 @@ class TestWorkflowProcessor(BaseTest):
|
||||
self.assertFalse(processor2.is_latest_spec) # Still at version 1.
|
||||
|
||||
# Do a hard reset, which should bring us back to the beginning, but retain the data.
|
||||
processor3 = WorkflowProcessor(processor.workflow_model, hard_reset=True)
|
||||
WorkflowProcessor.reset(processor2.workflow_model)
|
||||
processor3 = WorkflowProcessor(processor.workflow_model)
|
||||
processor3.do_engine_steps()
|
||||
self.assertEqual("Step 1", processor3.next_task().task_spec.description)
|
||||
self.assertTrue(processor3.is_latest_spec) # Now at version 2.
|
||||
task = processor3.next_task()
|
||||
|
@ -52,11 +52,11 @@ class TestWorkflowProcessorMultiInstance(BaseTest):
|
||||
task_list = processor.get_ready_user_tasks()
|
||||
processor.complete_task(task_list[0])
|
||||
processor.do_engine_steps()
|
||||
nav_list = processor.bpmn_workflow.get_nav_list()
|
||||
nav_list = processor.bpmn_workflow.get_flat_nav_list()
|
||||
processor.save()
|
||||
# reload after save
|
||||
processor = WorkflowProcessor(workflow_spec_model)
|
||||
nav_list2 = processor.bpmn_workflow.get_nav_list()
|
||||
nav_list2 = processor.bpmn_workflow.get_flat_nav_list()
|
||||
self.assertEqual(nav_list,nav_list2)
|
||||
|
||||
@patch('crc.services.study_service.StudyService.get_investigators')
|
||||
@ -158,7 +158,7 @@ class TestWorkflowProcessorMultiInstance(BaseTest):
|
||||
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.assertEqual(6, len(processor.bpmn_workflow.get_nav_list()))
|
||||
self.assertEqual(7, len(processor.bpmn_workflow.get_flat_nav_list()))
|
||||
|
||||
# We can complete the tasks out of order.
|
||||
task = next_user_tasks[2]
|
||||
@ -171,12 +171,12 @@ class TestWorkflowProcessorMultiInstance(BaseTest):
|
||||
|
||||
# Assure navigation picks up the label of the current element variable.
|
||||
nav = WorkflowService.processor_to_workflow_api(processor, task).navigation
|
||||
self.assertEqual("Primary Investigator", nav[2].title)
|
||||
self.assertEqual("Primary Investigator", nav[2].description)
|
||||
|
||||
task.update_data({"investigator": {"email": "dhf8r@virginia.edu"}})
|
||||
processor.complete_task(task)
|
||||
processor.do_engine_steps()
|
||||
self.assertEqual(6, len(processor.bpmn_workflow.get_nav_list()))
|
||||
self.assertEqual(7, len(processor.bpmn_workflow.get_flat_nav_list()))
|
||||
|
||||
task = next_user_tasks[0]
|
||||
api_task = WorkflowService.spiff_task_to_api_task(task)
|
||||
@ -184,7 +184,7 @@ class TestWorkflowProcessorMultiInstance(BaseTest):
|
||||
task.update_data({"investigator":{"email":"asd3v@virginia.edu"}})
|
||||
processor.complete_task(task)
|
||||
processor.do_engine_steps()
|
||||
self.assertEqual(6, len(processor.bpmn_workflow.get_nav_list()))
|
||||
self.assertEqual(7, len(processor.bpmn_workflow.get_flat_nav_list()))
|
||||
|
||||
task = next_user_tasks[1]
|
||||
api_task = WorkflowService.spiff_task_to_api_task(task)
|
||||
@ -192,7 +192,7 @@ class TestWorkflowProcessorMultiInstance(BaseTest):
|
||||
task.update_data({"investigator":{"email":"asdf32@virginia.edu"}})
|
||||
processor.complete_task(task)
|
||||
processor.do_engine_steps()
|
||||
self.assertEqual(6, len(processor.bpmn_workflow.get_nav_list()))
|
||||
self.assertEqual(7, len(processor.bpmn_workflow.get_flat_nav_list()))
|
||||
|
||||
# Completing the tasks out of order, still provides the correct information.
|
||||
expected = self.mock_investigator_response
|
||||
@ -203,4 +203,4 @@ class TestWorkflowProcessorMultiInstance(BaseTest):
|
||||
task.data['StudyInfo']['investigators'])
|
||||
|
||||
self.assertEqual(WorkflowStatus.complete, processor.get_status())
|
||||
self.assertEqual(6, len(processor.bpmn_workflow.get_nav_list()))
|
||||
self.assertEqual(7, len(processor.bpmn_workflow.get_flat_nav_list()))
|
||||
|
34
tests/workflow/test_workflow_restart.py
Normal file
34
tests/workflow/test_workflow_restart.py
Normal file
@ -0,0 +1,34 @@
|
||||
from tests.base_test import BaseTest
|
||||
|
||||
|
||||
class TestMessageEvent(BaseTest):
|
||||
|
||||
def test_message_event(self):
|
||||
|
||||
workflow = self.create_workflow('message_event')
|
||||
|
||||
first_task = self.get_workflow_api(workflow).next_task
|
||||
self.assertEqual('Activity_GetData', first_task.name)
|
||||
workflow_api = self.get_workflow_api(workflow)
|
||||
|
||||
result = self.complete_form(workflow_api, first_task, {'formdata': 'asdf'})
|
||||
self.assertIn('formdata', result.next_task.data)
|
||||
self.assertEqual('asdf', result.next_task.data['formdata'])
|
||||
|
||||
workflow_api = self.get_workflow_api(workflow)
|
||||
self.assertEqual('Activity_HowMany', self.get_workflow_api(workflow_api).next_task.name)
|
||||
|
||||
# restart with data. should land at beginning with data
|
||||
workflow_api = self.restart_workflow_api(result)
|
||||
first_task = self.get_workflow_api(workflow_api).next_task
|
||||
self.assertEqual('Activity_GetData', first_task.name)
|
||||
self.assertIn('formdata', workflow_api.next_task.data)
|
||||
self.assertEqual('asdf', workflow_api.next_task.data['formdata'])
|
||||
|
||||
# restart without data.
|
||||
workflow_api = self.restart_workflow_api(workflow_api, clear_data=True)
|
||||
first_task = self.get_workflow_api(workflow).next_task
|
||||
self.assertEqual('Activity_GetData', first_task.name)
|
||||
self.assertNotIn('formdata', workflow_api.next_task.data)
|
||||
|
||||
print('Nice Test')
|
Loading…
x
Reference in New Issue
Block a user