diff --git a/.gitignore b/.gitignore index 82f12698..96f9e3f3 100644 --- a/.gitignore +++ b/.gitignore @@ -154,3 +154,6 @@ staticfiles RELEASE .*venv + +# do not track generated docs +_build diff --git a/Pipfile b/Pipfile index 3e123349..7f3c096f 100644 --- a/Pipfile +++ b/Pipfile @@ -24,6 +24,7 @@ pytest-mock = "*" pytest-factoryboy = "*" sphinx = "*" sphinx-autobuild = "*" +sphinxcontrib-mermaid = "==0.7.1" furo = "*" myst-parser = "*" ansible-lint = "*" @@ -46,6 +47,7 @@ requests = ">=2" whitenoise = { extras = ["brotli"], version = "==4.1.4" } db-to-sqlite = "*" sqlite-utils = "*" +sqlalchemy = "~=1.4" sentry-sdk = "*" dramatiq = { extras = ["rabbitmq"], version = "*" } django-dramatiq = "==v0.10.0" diff --git a/Pipfile.lock b/Pipfile.lock index fdfcec08..6cf9a78b 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "0f9a156773b7bd60179a00fb02825233a17e1eb25ee8206593009ed77ab56eea" + "sha256": "f7e1721ca8d49c6cf4479a266e225e7ad9996c411e6a2a97de0c3406c19fc582" }, "pipfile-spec": 6, "requires": {}, @@ -141,36 +141,45 @@ }, "bcrypt": { "hashes": [ - "sha256:0b0f0c7141622a31e9734b7f649451147c04ebb5122327ac0bd23744df84be90", - "sha256:1c3334446fac200499e8bc04a530ce3cf0b3d7151e0e4ac5c0dddd3d95e97843", - "sha256:2d0dd19aad87e4ab882ef1d12df505f4c52b28b69666ce83c528f42c07379227", - "sha256:594780b364fb45f2634c46ec8d3e61c1c0f1811c4f2da60e8eb15594ecbf93ed", - "sha256:7c7dd6c1f05bf89e65261d97ac3a6520f34c2acb369afb57e3ea4449be6ff8fd", - "sha256:845b1daf4df2dd94d2fdbc9454953ca9dd0e12970a0bfc9f3dcc6faea3fa96e4", - "sha256:8780e69f9deec9d60f947b169507d2c9816e4f11548f1f7ebee2af38b9b22ae4", - "sha256:bf413f2a9b0a2950fc750998899013f2e718d20fa4a58b85ca50b6df5ed1bbf9", - "sha256:bfb67f6a6c72dfb0a02f3df51550aa1862708e55128b22543e2b42c74f3620d7", - "sha256:c59c170fc9225faad04dde1ba61d85b413946e8ce2e5f5f5ff30dfd67283f319", - "sha256:dc6ec3dc19b1c193b2f7cf279d3e32e7caf447532fbcb7af0906fe4398900c33", - "sha256:ede0f506554571c8eda80db22b83c139303ec6b595b8f60c4c8157bdd0bdee36" - ], - "version": "==4.0.0" + "sha256:089098effa1bc35dc055366740a067a2fc76987e8ec75349eb9484061c54f535", + "sha256:08d2947c490093a11416df18043c27abe3921558d2c03e2076ccb28a116cb6d0", + "sha256:0eaa47d4661c326bfc9d08d16debbc4edf78778e6aaba29c1bc7ce67214d4410", + "sha256:27d375903ac8261cfe4047f6709d16f7d18d39b1ec92aaf72af989552a650ebd", + "sha256:2b3ac11cf45161628f1f3733263e63194f22664bf4d0c0f3ab34099c02134665", + "sha256:2caffdae059e06ac23fce178d31b4a702f2a3264c20bfb5ff541b338194d8fab", + "sha256:3100851841186c25f127731b9fa11909ab7b1df6fc4b9f8353f4f1fd952fbf71", + "sha256:5ad4d32a28b80c5fa6671ccfb43676e8c1cc232887759d1cd7b6f56ea4355215", + "sha256:67a97e1c405b24f19d08890e7ae0c4f7ce1e56a712a016746c8b2d7732d65d4b", + "sha256:705b2cea8a9ed3d55b4491887ceadb0106acf7c6387699fca771af56b1cdeeda", + "sha256:8a68f4341daf7522fe8d73874de8906f3a339048ba406be6ddc1b3ccb16fc0d9", + "sha256:a522427293d77e1c29e303fc282e2d71864579527a04ddcfda6d4f8396c6c36a", + "sha256:ae88eca3024bb34bb3430f964beab71226e761f51b912de5133470b649d82344", + "sha256:b1023030aec778185a6c16cf70f359cbb6e0c289fd564a7cfa29e727a1c38f8f", + "sha256:b3b85202d95dd568efcb35b53936c5e3b3600c7cdcc6115ba461df3a8e89f38d", + "sha256:b57adba8a1444faf784394de3436233728a1ecaeb6e07e8c22c8848f179b893c", + "sha256:bf4fa8b2ca74381bb5442c089350f09a3f17797829d958fad058d6e44d9eb83c", + "sha256:ca3204d00d3cb2dfed07f2d74a25f12fc12f73e606fcaa6975d1f7ae69cacbb2", + "sha256:cbb03eec97496166b704ed663a53680ab57c5084b2fc98ef23291987b525cb7d", + "sha256:e9a51bbfe7e9802b5f3508687758b564069ba937748ad7b9e890086290d2f79e", + "sha256:fbdaec13c5105f0c4e5c52614d04f0bca5f5af007910daa8b6b12095edaa67b3" + ], + "version": "==4.0.1" }, "boto3": { "hashes": [ - "sha256:2c9004c1f0a47807c73247abe8cb2b8a7054c34b9cf6e90f448d51560de20ca1", - "sha256:714b06755bd76b2001f7099a8e406604c0a0e76a8ff0425480bf1555bcbecee1" + "sha256:7881fc380f2f489ae9b3a2f2448334aef9f7be58d85a7a0e8c10458247b09afa", + "sha256:db9f04eeb942e7998d1971c35e1658e2d9d6687166a4ebc036f484a71a79bbeb" ], "index": "pypi", - "version": "==1.24.85" + "version": "==1.24.93" }, "botocore": { "hashes": [ - "sha256:06b3ebacbe9a532074db4ad68040235e3c7bd52741e3ff6710cef8ab144ecdfe", - "sha256:2b4c86178b5a5a2baa0a065aaf9ef9d33a7ac67bf3f30e39005de151b0408cac" + "sha256:7648891692c3c69038eef902f8d64af59f91211f713fe9072fb83ecdd310dbbc", + "sha256:d5200f7b9150cdb91c9d3994980870d7bb4554e19c4d9d847f64626d8ceacf95" ], "markers": "python_version >= '3.7'", - "version": "==1.27.85" + "version": "==1.27.93" }, "brotli": { "hashes": [ @@ -335,11 +344,11 @@ "bcrypt" ], "hashes": [ - "sha256:115baf5049d5cf4163e43492cdc7139c306ed6d451e7d3571fe9612903903713", - "sha256:f71934b1a822f14a86c9ac9634053689279cd04ae69cb6ade4a59471b886582b" + "sha256:18ba8efa36b69cfcd4b670d0fa187c6fe7506596f0ababe580e16909bcdec121", + "sha256:3adc285124244724a394fa9b9839cc8cd116faf7d159554c43ecdaa8cdf0b94d" ], "index": "pypi", - "version": "==3.2.15" + "version": "==3.2.16" }, "django-anymail": { "extras": [ @@ -377,11 +386,11 @@ }, "django-countries": { "hashes": [ - "sha256:0df6d34193667c2343da8935cbfb8a2bd4fb0c97baf01ac10db4628ba1557a82", - "sha256:27fc8a0f66a87c9d839493f3926b4e0f4dd873ef66465aa8cd3e953f99758cc9" + "sha256:2eadbe91578fe7b5a0c7782c07487b2ceb4bde9bc5c76eb43458929969f25c62", + "sha256:9d553531de8eaf384fec7fdd5867f3cb9b922d384984c3e613b6b29031e5c3c2" ], "index": "pypi", - "version": "==7.3.2" + "version": "==7.4.2" }, "django-crispy-forms": { "hashes": [ @@ -957,37 +966,37 @@ }, "numpy": { "hashes": [ - "sha256:004f0efcb2fe1c0bd6ae1fcfc69cc8b6bf2407e0f18be308612007a0762b4089", - "sha256:09f6b7bdffe57fc61d869a22f506049825d707b288039d30f26a0d0d8ea05164", - "sha256:0ea3f98a0ffce3f8f57675eb9119f3f4edb81888b6874bc1953f91e0b1d4f440", - "sha256:17c0e467ade9bda685d5ac7f5fa729d8d3e76b23195471adae2d6a6941bd2c18", - "sha256:1f27b5322ac4067e67c8f9378b41c746d8feac8bdd0e0ffede5324667b8a075c", - "sha256:22d43376ee0acd547f3149b9ec12eec2f0ca4a6ab2f61753c5b29bb3e795ac4d", - "sha256:2ad3ec9a748a8943e6eb4358201f7e1c12ede35f510b1a2221b70af4bb64295c", - "sha256:301c00cf5e60e08e04d842fc47df641d4a181e651c7135c50dc2762ffe293dbd", - "sha256:39a664e3d26ea854211867d20ebcc8023257c1800ae89773cbba9f9e97bae036", - "sha256:51bf49c0cd1d52be0a240aa66f3458afc4b95d8993d2d04f0d91fa60c10af6cd", - "sha256:78a63d2df1d947bd9d1b11d35564c2f9e4b57898aae4626638056ec1a231c40c", - "sha256:7cd1328e5bdf0dee621912f5833648e2daca72e3839ec1d6695e91089625f0b4", - "sha256:8355fc10fd33a5a70981a5b8a0de51d10af3688d7a9e4a34fcc8fa0d7467bb7f", - "sha256:8c79d7cf86d049d0c5089231a5bcd31edb03555bd93d81a16870aa98c6cfb79d", - "sha256:91b8d6768a75247026e951dce3b2aac79dc7e78622fc148329135ba189813584", - "sha256:94c15ca4e52671a59219146ff584488907b1f9b3fc232622b47e2cf832e94fb8", - "sha256:98dcbc02e39b1658dc4b4508442a560fe3ca5ca0d989f0df062534e5ca3a5c1a", - "sha256:a64403f634e5ffdcd85e0b12c08f04b3080d3e840aef118721021f9b48fc1460", - "sha256:bc6e8da415f359b578b00bcfb1d08411c96e9a97f9e6c7adada554a0812a6cc6", - "sha256:bdc9febce3e68b697d931941b263c59e0c74e8f18861f4064c1f712562903411", - "sha256:c1ba66c48b19cc9c2975c0d354f24058888cdc674bebadceb3cdc9ec403fb5d1", - "sha256:c9f707b5bb73bf277d812ded9896f9512a43edff72712f31667d0a8c2f8e71ee", - "sha256:d5422d6a1ea9b15577a9432e26608c73a78faf0b9039437b075cf322c92e98e7", - "sha256:e5d5420053bbb3dd64c30e58f9363d7a9c27444c3648e61460c1237f9ec3fa14", - "sha256:e868b0389c5ccfc092031a861d4e158ea164d8b7fdbb10e3b5689b4fc6498df6", - "sha256:efd9d3abe5774404becdb0748178b48a218f1d8c44e0375475732211ea47c67e", - "sha256:f8c02ec3c4c4fcb718fdf89a6c6f709b14949408e8cf2a2be5bfa9c49548fd85", - "sha256:ffcf105ecdd9396e05a8e58e81faaaf34d3f9875f137c7372450baa5d77c9a54" + "sha256:0fe563fc8ed9dc4474cbf70742673fc4391d70f4363f917599a7fa99f042d5a8", + "sha256:12ac457b63ec8ded85d85c1e17d85efd3c2b0967ca39560b307a35a6703a4735", + "sha256:2341f4ab6dba0834b685cce16dad5f9b6606ea8a00e6da154f5dbded70fdc4dd", + "sha256:296d17aed51161dbad3c67ed6d164e51fcd18dbcd5dd4f9d0a9c6055dce30810", + "sha256:488a66cb667359534bc70028d653ba1cf307bae88eab5929cd707c761ff037db", + "sha256:4d52914c88b4930dafb6c48ba5115a96cbab40f45740239d9f4159c4ba779962", + "sha256:5e13030f8793e9ee42f9c7d5777465a560eb78fa7e11b1c053427f2ccab90c79", + "sha256:61be02e3bf810b60ab74e81d6d0d36246dbfb644a462458bb53b595791251911", + "sha256:7607b598217745cc40f751da38ffd03512d33ec06f3523fb0b5f82e09f6f676d", + "sha256:7a70a7d3ce4c0e9284e92285cba91a4a3f5214d87ee0e95928f3614a256a1488", + "sha256:7ab46e4e7ec63c8a5e6dbf5c1b9e1c92ba23a7ebecc86c336cb7bf3bd2fb10e5", + "sha256:8981d9b5619569899666170c7c9748920f4a5005bf79c72c07d08c8a035757b0", + "sha256:8c053d7557a8f022ec823196d242464b6955a7e7e5015b719e76003f63f82d0f", + "sha256:926db372bc4ac1edf81cfb6c59e2a881606b409ddc0d0920b988174b2e2a767f", + "sha256:95d79ada05005f6f4f337d3bb9de8a7774f259341c70bc88047a1f7b96a4bcb2", + "sha256:95de7dc7dc47a312f6feddd3da2500826defdccbc41608d0031276a24181a2c0", + "sha256:a0882323e0ca4245eb0a3d0a74f88ce581cc33aedcfa396e415e5bba7bf05f68", + "sha256:a8365b942f9c1a7d0f0dc974747d99dd0a0cdfc5949a33119caf05cb314682d3", + "sha256:a8aae2fb3180940011b4862b2dd3756616841c53db9734b27bb93813cd79fce6", + "sha256:c237129f0e732885c9a6076a537e974160482eab8f10db6292e92154d4c67d71", + "sha256:c67b833dbccefe97cdd3f52798d430b9d3430396af7cdb2a0c32954c3ef73894", + "sha256:ce03305dd694c4873b9429274fd41fc7eb4e0e4dea07e0af97a933b079a5814f", + "sha256:d331afac87c92373826af83d2b2b435f57b17a5c74e6268b79355b970626e329", + "sha256:dada341ebb79619fe00a291185bba370c9803b1e1d7051610e01ed809ef3a4ba", + "sha256:ed2cc92af0efad20198638c69bb0fc2870a58dabfba6eb722c933b48556c686c", + "sha256:f260da502d7441a45695199b4e7fd8ca87db659ba1c78f2bbf31f934fe76ae0e", + "sha256:f2f390aa4da44454db40a1f0201401f9036e8d578a25f01a6e237cea238337ef", + "sha256:f76025acc8e2114bb664294a07ede0727aa75d63a06d2fae96bf29a81747e4a7" ], "markers": "python_version >= '3.8'", - "version": "==1.23.3" + "version": "==1.23.4" }, "packaging": { "hashes": [ @@ -999,36 +1008,36 @@ }, "pandas": { "hashes": [ - "sha256:0d8d7433d19bfa33f11c92ad9997f15a902bda4f5ad3a4814a21d2e910894484", - "sha256:1642fc6138b4e45d57a12c1b464a01a6d868c0148996af23f72dde8d12486bbc", - "sha256:171cef540bfcec52257077816a4dbbac152acdb8236ba11d3196ae02bf0959d8", - "sha256:1b82ccc7b093e0a93f8dffd97a542646a3e026817140e2c01266aaef5fdde11b", - "sha256:1d34b1f43d9e3f4aea056ba251f6e9b143055ebe101ed04c847b41bb0bb4a989", - "sha256:207d63ac851e60ec57458814613ef4b3b6a5e9f0b33c57623ba2bf8126c311f8", - "sha256:2504c032f221ef9e4a289f5e46a42b76f5e087ecb67d62e342ccbba95a32a488", - "sha256:33a9d9e21ab2d91e2ab6e83598419ea6a664efd4c639606b299aae8097c1c94f", - "sha256:3ee61b881d2f64dd90c356eb4a4a4de75376586cd3c9341c6c0fcaae18d52977", - "sha256:41aec9f87455306496d4486df07c1b98c15569c714be2dd552a6124cd9fda88f", - "sha256:4e30a31039574d96f3d683df34ccb50bb435426ad65793e42a613786901f6761", - "sha256:5cc47f2ebaa20ef96ae72ee082f9e101b3dfbf74f0e62c7a12c0b075a683f03c", - "sha256:62e61003411382e20d7c2aec1ee8d7c86c8b9cf46290993dd8a0a3be44daeb38", - "sha256:73844e247a7b7dac2daa9df7339ecf1fcf1dfb8cbfd11e3ffe9819ae6c31c515", - "sha256:85a516a7f6723ca1528f03f7851fa8d0360d1d6121cf15128b290cf79b8a7f6a", - "sha256:86d87279ebc5bc20848b4ceb619073490037323f80f515e0ec891c80abad958a", - "sha256:8a4fc04838615bf0a8d3a03ed68197f358054f0df61f390bcc64fbe39e3d71ec", - "sha256:8e8e5edf97d8793f51d258c07c629bd49d271d536ce15d66ac00ceda5c150eb3", - "sha256:947ed9f896ee61adbe61829a7ae1ade493c5a28c66366ec1de85c0642009faac", - "sha256:a68a9b9754efff364b0c5ee5b0f18e15ca640c01afe605d12ba8b239ca304d6b", - "sha256:c76f1d104844c5360c21d2ef0e1a8b2ccf8b8ebb40788475e255b9462e32b2be", - "sha256:c7f38d91f21937fe2bec9449570d7bf36ad7136227ef43b321194ec249e2149d", - "sha256:de34636e2dc04e8ac2136a8d3c2051fd56ebe9fd6cd185581259330649e73ca9", - "sha256:e178ce2d7e3b934cf8d01dc2d48d04d67cb0abfaffdcc8aa6271fd5a436f39c8", - "sha256:e252a9e49b233ff96e2815c67c29702ac3a062098d80a170c506dff3470fd060", - "sha256:e9c5049333c5bebf993033f4bf807d163e30e8fada06e1da7fa9db86e2392009", - "sha256:fc987f7717e53d372f586323fff441263204128a1ead053c1b98d7288f836ac9" - ], - "index": "pypi", - "version": "==1.5.0" + "sha256:04e51b01d5192499390c0015630975f57836cc95c7411415b499b599b05c0c96", + "sha256:05c527c64ee02a47a24031c880ee0ded05af0623163494173204c5b72ddce658", + "sha256:0a78e05ec09731c5b3bd7a9805927ea631fe6f6cb06f0e7c63191a9a778d52b4", + "sha256:17da7035d9e6f9ea9cdc3a513161f8739b8f8489d31dc932bc5a29a27243f93d", + "sha256:249cec5f2a5b22096440bd85c33106b6102e0672204abd2d5c014106459804ee", + "sha256:2c25e5c16ee5c0feb6cf9d982b869eec94a22ddfda9aa2fbed00842cbb697624", + "sha256:32e3d9f65606b3f6e76555bfd1d0b68d94aff0929d82010b791b6254bf5a4b96", + "sha256:36aa1f8f680d7584e9b572c3203b20d22d697c31b71189322f16811d4ecfecd3", + "sha256:5b0c970e2215572197b42f1cff58a908d734503ea54b326412c70d4692256391", + "sha256:5cee0c74e93ed4f9d39007e439debcaadc519d7ea5c0afc3d590a3a7b2edf060", + "sha256:669c8605dba6c798c1863157aefde959c1796671ffb342b80fcb80a4c0bc4c26", + "sha256:66a1ad667b56e679e06ba73bb88c7309b3f48a4c279bd3afea29f65a766e9036", + "sha256:683779e5728ac9138406c59a11e09cd98c7d2c12f0a5fc2b9c5eecdbb4a00075", + "sha256:6bb391659a747cf4f181a227c3e64b6d197100d53da98dcd766cc158bdd9ec68", + "sha256:81f0674fa50b38b6793cd84fae5d67f58f74c2d974d2cb4e476d26eee33343d0", + "sha256:927e59c694e039c75d7023465d311277a1fc29ed7236b5746e9dddf180393113", + "sha256:932d2d7d3cab44cfa275601c982f30c2d874722ef6396bb539e41e4dc4618ed4", + "sha256:a52419d9ba5906db516109660b114faf791136c94c1a636ed6b29cbfff9187ee", + "sha256:b156a971bc451c68c9e1f97567c94fd44155f073e3bceb1b0d195fd98ed12048", + "sha256:bcf1a82b770b8f8c1e495b19a20d8296f875a796c4fe6e91da5ef107f18c5ecb", + "sha256:cb2a9cf1150302d69bb99861c5cddc9c25aceacb0a4ef5299785d0f5389a3209", + "sha256:d8c709f4700573deb2036d240d140934df7e852520f4a584b2a8d5443b71f54d", + "sha256:db45b94885000981522fb92349e6b76f5aee0924cc5315881239c7859883117d", + "sha256:ddf46b940ef815af4e542697eaf071f0531449407a7607dd731bf23d156e20a7", + "sha256:e675f8fe9aa6c418dc8d3aac0087b5294c1a4527f1eacf9fe5ea671685285454", + "sha256:eb7e8cf2cf11a2580088009b43de84cabbf6f5dae94ceb489f28dba01a17cb77", + "sha256:f340331a3f411910adfb4bbe46c2ed5872d9e473a783d7f14ecf49bc0869c594" + ], + "index": "pypi", + "version": "==1.5.1" }, "phpserialize": { "hashes": [ @@ -1039,10 +1048,10 @@ }, "pika": { "hashes": [ - "sha256:15357ddc47a5c28f0b07d80e93d504cbbf7a1ad5e1cd129ecd27afe76472c529", - "sha256:9195f37aed089862b205fd8f8ce1cc6ea0a7ee3cd80f58e6eea6cb9d8411a647" + "sha256:89f5e606646caebe3c00cbdbc4c2c609834adde45d7507311807b5775edac8e0", + "sha256:beb19ff6dd1547f99a29acc2c6987ebb2ba7c44bf44a3f8e305877c5ef7d2fdc" ], - "version": "==1.3.0" + "version": "==1.3.1" }, "pillow": { "hashes": [ @@ -1110,11 +1119,11 @@ }, "prometheus-client": { "hashes": [ - "sha256:522fded625282822a89e2773452f42df14b5a8e84a86433e3f8a189c1d54dc01", - "sha256:5459c427624961076277fdc6dc50540e2bacb98eebde99886e59ec55ed92093a" + "sha256:be26aa452490cfcf6da953f9436e95a9f2b4d578ca80094b4458930e5f584ab1", + "sha256:db7c05cbd13a0f79975592d112320f2605a325969b270a94b71dcabc47b931d2" ], "markers": "python_version >= '3.6'", - "version": "==0.14.1" + "version": "==0.15.0" }, "pygments": { "hashes": [ @@ -1142,10 +1151,10 @@ }, "pytz": { "hashes": [ - "sha256:2c0784747071402c6e99f0bafdb7da0fa22645f06554c7ae06bf6358897e9c91", - "sha256:48ce799d83b6f8aab2020e369b627446696619e79645419610b9facd909b3174" + "sha256:335ab46900b1465e714b4fda4963d87363264eb662aab5e65da039c25f1f5b22", + "sha256:c4d88f472f54d615e9cd582a5004d1e5f624854a6a27a6211591c251f22a6914" ], - "version": "==2022.4" + "version": "==2022.5" }, "pyyaml": { "hashes": [ @@ -1287,50 +1296,50 @@ }, "sqlalchemy": { "hashes": [ - "sha256:0002e829142b2af00b4eaa26c51728f3ea68235f232a2e72a9508a3116bd6ed0", - "sha256:0005bd73026cd239fc1e8ccdf54db58b6193be9a02b3f0c5983808f84862c767", - "sha256:0292f70d1797e3c54e862e6f30ae474014648bc9c723e14a2fda730adb0a9791", - "sha256:036d8472356e1d5f096c5e0e1a7e0f9182140ada3602f8fff6b7329e9e7cfbcd", - "sha256:05f0de3a1dc3810a776275763764bb0015a02ae0f698a794646ebc5fb06fad33", - "sha256:0990932f7cca97fece8017414f57fdd80db506a045869d7ddf2dda1d7cf69ecc", - "sha256:13e397a9371ecd25573a7b90bd037db604331cf403f5318038c46ee44908c44d", - "sha256:14576238a5f89bcf504c5f0a388d0ca78df61fb42cb2af0efe239dc965d4f5c9", - "sha256:199a73c31ac8ea59937cc0bf3dfc04392e81afe2ec8a74f26f489d268867846c", - "sha256:2082a2d2fca363a3ce21cfa3d068c5a1ce4bf720cf6497fb3a9fc643a8ee4ddd", - "sha256:22ff16cedab5b16a0db79f1bc99e46a6ddececb60c396562e50aab58ddb2871c", - "sha256:2307495d9e0ea00d0c726be97a5b96615035854972cc538f6e7eaed23a35886c", - "sha256:2ad2b727fc41c7f8757098903f85fafb4bf587ca6605f82d9bf5604bd9c7cded", - "sha256:2d6495f84c4fd11584f34e62f9feec81bf373787b3942270487074e35cbe5330", - "sha256:361f6b5e3f659e3c56ea3518cf85fbdae1b9e788ade0219a67eeaaea8a4e4d2a", - "sha256:3e2ef592ac3693c65210f8b53d0edcf9f4405925adcfc031ff495e8d18169682", - "sha256:4676d51c9f6f6226ae8f26dc83ec291c088fe7633269757d333978df78d931ab", - "sha256:4ba7e122510bbc07258dc42be6ed45997efdf38129bde3e3f12649be70683546", - "sha256:5102fb9ee2c258a2218281adcb3e1918b793c51d6c2b4666ce38c35101bb940e", - "sha256:5323252be2bd261e0aa3f33cb3a64c45d76829989fa3ce90652838397d84197d", - "sha256:58bb65b3274b0c8a02cea9f91d6f44d0da79abc993b33bdedbfec98c8440175a", - "sha256:59bdc291165b6119fc6cdbc287c36f7f2859e6051dd923bdf47b4c55fd2f8bd0", - "sha256:5facb7fd6fa8a7353bbe88b95695e555338fb038ad19ceb29c82d94f62775a05", - "sha256:639e1ae8d48b3c86ffe59c0daa9a02e2bfe17ca3d2b41611b30a0073937d4497", - "sha256:8eb8897367a21b578b26f5713833836f886817ee2ffba1177d446fa3f77e67c8", - "sha256:90484a2b00baedad361402c257895b13faa3f01780f18f4a104a2f5c413e4536", - "sha256:9c56e19780cd1344fcd362fd6265a15f48aa8d365996a37fab1495cae8fcd97d", - "sha256:b67fc780cfe2b306180e56daaa411dd3186bf979d50a6a7c2a5b5036575cbdbb", - "sha256:c0dcf127bb99458a9d211e6e1f0f3edb96c874dd12f2503d4d8e4f1fd103790b", - "sha256:c23d64a0b28fc78c96289ffbd0d9d1abd48d267269b27f2d34e430ea73ce4b26", - "sha256:ccfd238f766a5bb5ee5545a62dd03f316ac67966a6a658efb63eeff8158a4bbf", - "sha256:cd767cf5d7252b1c88fcfb58426a32d7bd14a7e4942497e15b68ff5d822b41ad", - "sha256:ce8feaa52c1640de9541eeaaa8b5fb632d9d66249c947bb0d89dd01f87c7c288", - "sha256:d2e054aed4645f9b755db85bc69fc4ed2c9020c19c8027976f66576b906a74f1", - "sha256:e16c2be5cb19e2c08da7bd3a87fed2a0d4e90065ee553a940c4fc1a0fb1ab72b", - "sha256:e4b12e3d88a8fffd0b4ca559f6d4957ed91bd4c0613a4e13846ab8729dc5c251", - "sha256:e570cfc40a29d6ad46c9aeaddbdcee687880940a3a327f2c668dd0e4ef0a441d", - "sha256:eb30cf008850c0a26b72bd1b9be6730830165ce049d239cfdccd906f2685f892", - "sha256:f37fa70d95658763254941ddd30ecb23fc4ec0c5a788a7c21034fc2305dab7cc", - "sha256:f5ebeeec5c14533221eb30bad716bc1fd32f509196318fb9caa7002c4a364e4c", - "sha256:f5fa526d027d804b1f85cdda1eb091f70bde6fb7d87892f6dd5a48925bc88898" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==1.4.41" + "sha256:04f2598c70ea4a29b12d429a80fad3a5202d56dce19dd4916cc46a965a5ca2e9", + "sha256:0501f74dd2745ec38f44c3a3900fb38b9db1ce21586b691482a19134062bf049", + "sha256:0ee377eb5c878f7cefd633ab23c09e99d97c449dd999df639600f49b74725b80", + "sha256:11b2ec26c5d2eefbc3e6dca4ec3d3d95028be62320b96d687b6e740424f83b7d", + "sha256:15d878929c30e41fb3d757a5853b680a561974a0168cd33a750be4ab93181628", + "sha256:177e41914c476ed1e1b77fd05966ea88c094053e17a85303c4ce007f88eff363", + "sha256:1811a0b19a08af7750c0b69e38dec3d46e47c4ec1d74b6184d69f12e1c99a5e0", + "sha256:1d0c23ecf7b3bc81e29459c34a3f4c68ca538de01254e24718a7926810dc39a6", + "sha256:22459fc1718785d8a86171bbe7f01b5c9d7297301ac150f508d06e62a2b4e8d2", + "sha256:28e881266a172a4d3c5929182fde6bb6fba22ac93f137d5380cc78a11a9dd124", + "sha256:2e56dfed0cc3e57b2f5c35719d64f4682ef26836b81067ee6cfad062290fd9e2", + "sha256:2fd49af453e590884d9cdad3586415922a8e9bb669d874ee1dc55d2bc425aacd", + "sha256:3ab7c158f98de6cb4f1faab2d12973b330c2878d0c6b689a8ca424c02d66e1b3", + "sha256:4948b6c5f4e56693bbeff52f574279e4ff972ea3353f45967a14c30fb7ae2beb", + "sha256:4e1c5f8182b4f89628d782a183d44db51b5af84abd6ce17ebb9804355c88a7b5", + "sha256:5ce6929417d5dce5ad1d3f147db81735a4a0573b8fb36e3f95500a06eaddd93e", + "sha256:5ede1495174e69e273fad68ad45b6d25c135c1ce67723e40f6cf536cb515e20b", + "sha256:5f966b64c852592469a7eb759615bbd351571340b8b344f1d3fa2478b5a4c934", + "sha256:6045b3089195bc008aee5c273ec3ba9a93f6a55bc1b288841bd4cfac729b6516", + "sha256:6c9d004eb78c71dd4d3ce625b80c96a827d2e67af9c0d32b1c1e75992a7916cc", + "sha256:6e39e97102f8e26c6c8550cb368c724028c575ec8bc71afbbf8faaffe2b2092a", + "sha256:723e3b9374c1ce1b53564c863d1a6b2f1dc4e97b1c178d9b643b191d8b1be738", + "sha256:876eb185911c8b95342b50a8c4435e1c625944b698a5b4a978ad2ffe74502908", + "sha256:9256563506e040daddccaa948d055e006e971771768df3bb01feeb4386c242b0", + "sha256:934472bb7d8666727746a75670a1f8d91a9cae8c464bba79da30a0f6faccd9e1", + "sha256:97ff50cd85bb907c2a14afb50157d0d5486a4b4639976b4a3346f34b6d1b5272", + "sha256:9b01d9cd2f9096f688c71a3d0f33f3cd0af8549014e66a7a7dee6fc214a7277d", + "sha256:9e3a65ce9ed250b2f096f7b559fe3ee92e6605fab3099b661f0397a9ac7c8d95", + "sha256:a7dd5b7b34a8ba8d181402d824b87c5cee8963cb2e23aa03dbfe8b1f1e417cde", + "sha256:a85723c00a636eed863adb11f1e8aaa36ad1c10089537823b4540948a8429798", + "sha256:b42c59ffd2d625b28cdb2ae4cde8488543d428cba17ff672a543062f7caee525", + "sha256:bd448b262544b47a2766c34c0364de830f7fb0772d9959c1c42ad61d91ab6565", + "sha256:ca9389a00f639383c93ed00333ed763812f80b5ae9e772ea32f627043f8c9c88", + "sha256:df76e9c60879fdc785a34a82bf1e8691716ffac32e7790d31a98d7dec6e81545", + "sha256:e12c6949bae10f1012ab5c0ea52ab8db99adcb8c7b717938252137cdf694c775", + "sha256:e4ef8cb3c5b326f839bfeb6af5f406ba02ad69a78c7aac0fbeeba994ad9bb48a", + "sha256:e7e740453f0149437c101ea4fdc7eea2689938c5760d7dcc436c863a12f1f565", + "sha256:effc89e606165ca55f04f3f24b86d3e1c605e534bf1a96e4e077ce1b027d0b71", + "sha256:f0f574465b78f29f533976c06b913e54ab4980b9931b69aa9d306afff13a9471", + "sha256:fa5b7eb2051e857bf83bade0641628efe5a88de189390725d3e6033a1fff4257", + "sha256:fdb94a3d1ba77ff2ef11912192c066f01e68416f554c194d769391638c8ad09a" + ], + "index": "pypi", + "version": "==1.4.42" }, "sqlite-fts4": { "hashes": [ @@ -1357,12 +1366,11 @@ }, "tabulate": { "hashes": [ - "sha256:0ba055423dbaa164b9e456abe7920c5e8ed33fcc16f6d1b2f2d152c8e1e8b4fc", - "sha256:436f1c768b424654fce8597290d2764def1eea6a77cfa5c33be00b1bc0f4f63d", - "sha256:6c57f3f3dd7ac2782770155f3adb2db0b1a269637e42f27599925e64b114f519" + "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c", + "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==0.8.10" + "markers": "python_version >= '3.7'", + "version": "==0.9.0" }, "tld": { "hashes": [ @@ -1387,11 +1395,11 @@ }, "typing-extensions": { "hashes": [ - "sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02", - "sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6" + "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa", + "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e" ], "markers": "python_version >= '3.7'", - "version": "==4.3.0" + "version": "==4.4.0" }, "unicodecsv": { "hashes": [ @@ -1563,11 +1571,11 @@ }, "zipp": { "hashes": [ - "sha256:05b45f1ee8f807d0cc928485ca40a07cb491cf092ff587c0df9cb1fd154848d2", - "sha256:47c40d7fe183a6f21403a199b3e4192cca5774656965b0a4988ad2f8feb5f009" + "sha256:3a7af91c3db40ec72dd9d154ae18e008c69efe8ca88dde4f9a731bb82fe2f9eb", + "sha256:972cfa31bc2fedd3fa838a51e9bc7e64b7fb725a8c00e7431554311f180e9980" ], "markers": "python_version >= '3.7'", - "version": "==3.8.1" + "version": "==3.9.0" } }, "develop": { @@ -1588,19 +1596,19 @@ }, "ansible-core": { "hashes": [ - "sha256:3b386e03ee8a81415a8bfd5e1dc629e949327c9ced953a0e5c9ad42b9c7ee38f", - "sha256:57d0858802c824fa2dcc0f7643a06abf82053a3b9e7eb82d1fd62f5ee1d75a1d" + "sha256:c383fb0cdef97614ec483a278147ba867142fc86f166b18d49ae9c25d7ab1f68", + "sha256:ecea23ec4a272f3c3acd80fb2ad5ca2dfcb2fea8c31f8cbe275dd163d5fab62c" ], "markers": "python_version >= '3.9'", - "version": "==2.14.0b2" + "version": "==2.14.0rc1" }, "ansible-lint": { "hashes": [ - "sha256:a9863ee15397d1510b8360afa3318cf5ff9f48cd10efb7a26ea126ae0f0d704b", - "sha256:e9bec571351781d538161a7507ded1852b13cccbc9ec342a58bba7593f43b015" + "sha256:17dfacb0d9139a435ccf2095234e20491d556f7adb97dedd891b40566e316553", + "sha256:dbfcf94ae4ed7c3b1747c5e4cbb8d9ba094821f28fee3d82f0ba06ef96bfccac" ], "index": "pypi", - "version": "==6.8.0b2" + "version": "==6.8.2" }, "appnope": { "hashes": [ @@ -1643,11 +1651,11 @@ }, "awscli": { "hashes": [ - "sha256:7dd805791aab17b90e27b5265b20d5e3510f2444a46960a0e9829710f5366be8", - "sha256:e49b5340822a57aecc9d5ba89c47acec5c937554501f0b3ff975e583e4b554c5" + "sha256:3caae534c15f519b5c5159176b060098a6bf6317e7bfac7a427d4684a69e37a9", + "sha256:f2755c995db186a990330f898fd280880d3e0bf915326486773035646b4f6bf4" ], "index": "pypi", - "version": "==1.25.86" + "version": "==1.25.94" }, "awscli-plugin-endpoint": { "hashes": [ @@ -1682,40 +1690,38 @@ }, "black": { "hashes": [ - "sha256:0a12e4e1353819af41df998b02c6742643cfef58282915f781d0e4dd7a200411", - "sha256:0ad827325a3a634bae88ae7747db1a395d5ee02cf05d9aa7a9bd77dfb10e940c", - "sha256:32a4b17f644fc288c6ee2bafdf5e3b045f4eff84693ac069d87b1a347d861497", - "sha256:3b2c25f8dea5e8444bdc6788a2f543e1fb01494e144480bc17f806178378005e", - "sha256:4a098a69a02596e1f2a58a2a1c8d5a05d5a74461af552b371e82f9fa4ada8342", - "sha256:5107ea36b2b61917956d018bd25129baf9ad1125e39324a9b18248d362156a27", - "sha256:53198e28a1fb865e9fe97f88220da2e44df6da82b18833b588b1883b16bb5d41", - "sha256:5594efbdc35426e35a7defa1ea1a1cb97c7dbd34c0e49af7fb593a36bd45edab", - "sha256:5b879eb439094751185d1cfdca43023bc6786bd3c60372462b6f051efa6281a5", - "sha256:78dd85caaab7c3153054756b9fe8c611efa63d9e7aecfa33e533060cb14b6d16", - "sha256:792f7eb540ba9a17e8656538701d3eb1afcb134e3b45b71f20b25c77a8db7e6e", - "sha256:8ce13ffed7e66dda0da3e0b2eb1bdfc83f5812f66e09aca2b0978593ed636b6c", - "sha256:a05da0430bd5ced89176db098567973be52ce175a55677436a271102d7eaa3fe", - "sha256:a983526af1bea1e4cf6768e649990f28ee4f4137266921c2c3cee8116ae42ec3", - "sha256:bc4d4123830a2d190e9cc42a2e43570f82ace35c3aeb26a512a2102bce5af7ec", - "sha256:c3a73f66b6d5ba7288cd5d6dad9b4c9b43f4e8a4b789a94bf5abfb878c663eb3", - "sha256:ce957f1d6b78a8a231b18e0dd2d94a33d2ba738cd88a7fe64f53f659eea49fdd", - "sha256:cea1b2542d4e2c02c332e83150e41e3ca80dc0fb8de20df3c5e98e242156222c", - "sha256:d2c21d439b2baf7aa80d6dd4e3659259be64c6f49dfd0f32091063db0e006db4", - "sha256:d839150f61d09e7217f52917259831fe2b689f5c8e5e32611736351b89bb2a90", - "sha256:dd82842bb272297503cbec1a2600b6bfb338dae017186f8f215c8958f8acf869", - "sha256:e8166b7bfe5dcb56d325385bd1d1e0f635f24aae14b3ae437102dedc0c186747", - "sha256:e981e20ec152dfb3e77418fb616077937378b322d7b26aa1ff87717fb18b4875" - ], - "index": "pypi", - "version": "==22.8.0" + "sha256:14ff67aec0a47c424bc99b71005202045dc09270da44a27848d534600ac64fc7", + "sha256:197df8509263b0b8614e1df1756b1dd41be6738eed2ba9e9769f3880c2b9d7b6", + "sha256:1e464456d24e23d11fced2bc8c47ef66d471f845c7b7a42f3bd77bf3d1789650", + "sha256:2039230db3c6c639bd84efe3292ec7b06e9214a2992cd9beb293d639c6402edb", + "sha256:21199526696b8f09c3997e2b4db8d0b108d801a348414264d2eb8eb2532e540d", + "sha256:2644b5d63633702bc2c5f3754b1b475378fbbfb481f62319388235d0cd104c2d", + "sha256:432247333090c8c5366e69627ccb363bc58514ae3e63f7fc75c54b1ea80fa7de", + "sha256:444ebfb4e441254e87bad00c661fe32df9969b2bf224373a448d8aca2132b395", + "sha256:5b9b29da4f564ba8787c119f37d174f2b69cdfdf9015b7d8c5c16121ddc054ae", + "sha256:5cc42ca67989e9c3cf859e84c2bf014f6633db63d1cbdf8fdb666dcd9e77e3fa", + "sha256:5d8f74030e67087b219b032aa33a919fae8806d49c867846bfacde57f43972ef", + "sha256:72ef3925f30e12a184889aac03d77d031056860ccae8a1e519f6cbb742736383", + "sha256:819dc789f4498ecc91438a7de64427c73b45035e2e3680c92e18795a839ebb66", + "sha256:915ace4ff03fdfff953962fa672d44be269deb2eaf88499a0f8805221bc68c87", + "sha256:9311e99228ae10023300ecac05be5a296f60d2fd10fff31cf5c1fa4ca4b1988d", + "sha256:974308c58d057a651d182208a484ce80a26dac0caef2895836a92dd6ebd725e0", + "sha256:b8b49776299fece66bffaafe357d929ca9451450f5466e997a7285ab0fe28e3b", + "sha256:c957b2b4ea88587b46cf49d1dc17681c1e672864fd7af32fc1e9664d572b3458", + "sha256:e41a86c6c650bcecc6633ee3180d80a025db041a8e2398dcc059b3afa8382cd4", + "sha256:f513588da599943e0cde4e32cc9879e825d58720d6557062d1098c5ad80080e1", + "sha256:fba8a281e570adafb79f7755ac8721b6cf1bbf691186a287e990c7929c7692ff" + ], + "index": "pypi", + "version": "==22.10.0" }, "botocore": { "hashes": [ - "sha256:06b3ebacbe9a532074db4ad68040235e3c7bd52741e3ff6710cef8ab144ecdfe", - "sha256:2b4c86178b5a5a2baa0a065aaf9ef9d33a7ac67bf3f30e39005de151b0408cac" + "sha256:7648891692c3c69038eef902f8d64af59f91211f713fe9072fb83ecdd310dbbc", + "sha256:d5200f7b9150cdb91c9d3994980870d7bb4554e19c4d9d847f64626d8ceacf95" ], "markers": "python_version >= '3.7'", - "version": "==1.27.85" + "version": "==1.27.93" }, "bracex": { "hashes": [ @@ -1951,11 +1957,11 @@ "bcrypt" ], "hashes": [ - "sha256:115baf5049d5cf4163e43492cdc7139c306ed6d451e7d3571fe9612903903713", - "sha256:f71934b1a822f14a86c9ac9634053689279cd04ae69cb6ade4a59471b886582b" + "sha256:18ba8efa36b69cfcd4b670d0fa187c6fe7506596f0ababe580e16909bcdec121", + "sha256:3adc285124244724a394fa9b9839cc8cd116faf7d159554c43ecdaa8cdf0b94d" ], "index": "pypi", - "version": "==3.2.15" + "version": "==3.2.16" }, "django-debug-toolbar": { "hashes": [ @@ -1973,14 +1979,6 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "version": "==0.16" }, - "enrich": { - "hashes": [ - "sha256:0a2ab0d2931dff8947012602d1234d2a3ee002d9a355b5d70be6bf5466008893", - "sha256:f29b2c8c124b4dbd7c975ab5c3568f6c7a47938ea3b7d2106c8a3bd346545e4f" - ], - "markers": "python_version >= '3.6'", - "version": "==1.2.7" - }, "exceptiongroup": { "hashes": [ "sha256:2e3c3fc1538a094aab74fad52d6c33fc94de3dfee3ee01f187c0e0c72aec5337", @@ -1999,10 +1997,10 @@ }, "executing": { "hashes": [ - "sha256:2c2c07d1ec4b2d8f9676b25170f1d8445c0ee2eb78901afb075a4b8d83608c6a", - "sha256:4a6d96ba89eb3dcc11483471061b42b9006d8c9f81c584dd04246944cd022530" + "sha256:236ea5f059a38781714a8bfba46a70fad3479c2f552abee3bbafadc57ed111b8", + "sha256:b0d7f8dcc2bac47ce6e39374397e7acecea6fdc380a6d5323e26185d70f38ea8" ], - "version": "==1.1.0" + "version": "==1.1.1" }, "factory-boy": { "hashes": [ @@ -2014,11 +2012,11 @@ }, "faker": { "hashes": [ - "sha256:245fc7d23470dc57164bd9a59b7b1126e16289ffcf813d88a6c8e9b8a37ea3fb", - "sha256:84c83f0ac1a2c8ecabd784c501aa0ef1d082d4aee52c3d797d586081c166434c" + "sha256:096c15e136adb365db24d8c3964fe26bfc68fe060c9385071a339f8c14e09c8a", + "sha256:a741b77f484215c3aab2604100669657189548f440fcb2ed0f8b7ee21c385629" ], "markers": "python_version >= '3.7'", - "version": "==15.0.0" + "version": "==15.1.1" }, "filelock": { "hashes": [ @@ -2046,11 +2044,11 @@ }, "hypothesis": { "hashes": [ - "sha256:38ac02dba2801e26b964da57950cbc574fb22a3e16951c227150282b986ed002", - "sha256:dc3a296f1c23fc402ec50a3d90785fcf4a6290156ad4c97e23b0f972b0c3e32c" + "sha256:15dae5d993339aefa57e00f5cb5a5817ff300eeb661d96d1c9d094eb62b04c9a", + "sha256:802d236d03dbd54e0e1c55c0daa2ec41aeaadc87a4dcbb41421b78bf3f7a7789" ], "index": "pypi", - "version": "==6.56.0" + "version": "==6.56.3" }, "idna": { "hashes": [ @@ -2111,7 +2109,7 @@ "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7", "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951" ], - "markers": "python_version < '4' and python_full_version >= '3.6.1'", + "markers": "python_version < '4.0' and python_full_version >= '3.6.1'", "version": "==5.10.1" }, "jedi": { @@ -2521,11 +2519,11 @@ }, "pytest-mock": { "hashes": [ - "sha256:1a1b9264224d026932d6685a0f9cef3b61d91563c3e74af9fe5afb2767e13812", - "sha256:c899a0dcc8a5f22930acd020b500abd5f956911f326864a3b979e4866e14da82" + "sha256:f4c973eeae0282963eb293eb173ce91b091a79c1334455acfac9ddee8a1c784b", + "sha256:fbbdb085ef7c252a326fd8cdcac0aa3b1333d8811f131bdcc701002e1be7ed4f" ], "index": "pypi", - "version": "==3.9.0" + "version": "==3.10.0" }, "pytest-xdist": { "hashes": [ @@ -2545,10 +2543,10 @@ }, "pytz": { "hashes": [ - "sha256:2c0784747071402c6e99f0bafdb7da0fa22645f06554c7ae06bf6358897e9c91", - "sha256:48ce799d83b6f8aab2020e369b627446696619e79645419610b9facd909b3174" + "sha256:335ab46900b1465e714b4fda4963d87363264eb662aab5e65da039c25f1f5b22", + "sha256:c4d88f472f54d615e9cd582a5004d1e5f624854a6a27a6211591c251f22a6914" ], - "version": "==2022.4" + "version": "==2022.5" }, "pyyaml": { "hashes": [ @@ -2624,7 +2622,7 @@ "sha256:78f9a9bf4e7be0c5ded4583326e7461e3a3c5aae24073648b4bdfa797d78c9d2", "sha256:9d689e6ca1b3038bc82bf8d23e944b6b6037bc02301a574935b2dd946e0353b9" ], - "markers": "python_version >= '3.5' and python_version < '4'", + "markers": "python_version >= '3.5' and python_version < '4.0'", "version": "==4.7.2" }, "ruamel.yaml": { @@ -2681,11 +2679,11 @@ }, "setuptools": { "hashes": [ - "sha256:1b6bdc6161661409c5f21508763dc63ab20a9ac2f8ba20029aaaa7fdb9118012", - "sha256:3050e338e5871e70c72983072fe34f6032ae1cdeeeb67338199c2f74e083a80e" + "sha256:512e5536220e38146176efb833d4a62aa726b7bbff82cfbc8ba9eaa3996e0b17", + "sha256:f62ea9da9ed6289bfe868cd6845968a2c854d1427f8548d52cae02a42b4f0356" ], "markers": "python_version >= '3.7'", - "version": "==65.4.1" + "version": "==65.5.0" }, "six": { "hashes": [ @@ -2719,11 +2717,11 @@ }, "sphinx": { "hashes": [ - "sha256:5b10cb1022dac8c035f75767799c39217a05fc0fe2d6fe5597560d38e44f0363", - "sha256:7abf6fabd7b58d0727b7317d5e2650ef68765bbe0ccb63c8795fa8683477eaa2" + "sha256:060ca5c9f7ba57a08a1219e547b269fadf125ae25b06b9fa7f66768efb652d6d", + "sha256:51026de0a9ff9fc13c05d74913ad66047e104f56a129ff73e174eb5c3ee794b5" ], "index": "pypi", - "version": "==5.2.3" + "version": "==5.3.0" }, "sphinx-autobuild": { "hashes": [ @@ -2773,6 +2771,14 @@ "markers": "python_version >= '3.5'", "version": "==1.0.1" }, + "sphinxcontrib-mermaid": { + "hashes": [ + "sha256:3e20de1937c30dfa807e446bf99983d73d0dd3dc5c6524addda59800fe928762", + "sha256:aa8a40b50ec86ad12824b62180240ca52a9bda8424455d7eb252eae9aa5d293c" + ], + "index": "pypi", + "version": "==0.7.1" + }, "sphinxcontrib-qthelp": { "hashes": [ "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72", @@ -2833,7 +2839,7 @@ "sha256:571854ebbb5eac89abcb4a2e47d7ea27b89bf29e09c35395da6f03dd4ae23d1c", "sha256:f2ef9da9cef846ee027947dc99a45d6b68a63b0ebc21944649505bf2e8bc5fe7" ], - "markers": "python_version >= '3.6' and python_version < '4'", + "markers": "python_version >= '3.6' and python_version < '4.0'", "version": "==0.11.5" }, "tornado": { @@ -2855,19 +2861,19 @@ }, "traitlets": { "hashes": [ - "sha256:3f2c4e435e271592fe4390f1746ea56836e3a080f84e7833f0f801d9613fec39", - "sha256:93663cc8236093d48150e2af5e2ed30fc7904a11a6195e21bab0408af4e6d6c8" + "sha256:1201b2c9f76097195989cdf7f65db9897593b0dfd69e4ac96016661bb6f0d30f", + "sha256:b122f9ff2f2f6c1709dab289a05555be011c87828e911c0cf4074b85cb780a79" ], "markers": "python_version >= '3.7'", - "version": "==5.4.0" + "version": "==5.5.0" }, "typing-extensions": { "hashes": [ - "sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02", - "sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6" + "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa", + "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e" ], "markers": "python_version >= '3.7'", - "version": "==4.3.0" + "version": "==4.4.0" }, "urllib3": { "hashes": [ @@ -2964,6 +2970,7 @@ }, "yamllint": { "hashes": [ + "sha256:89bb5b5ac33b1ade059743cf227de73daa34d5e5a474b06a5e17fc16583b0cf2", "sha256:9e3d8ddd16d0583214c5fdffe806c9344086721f107435f68bad990e5a88826b" ], "markers": "python_version >= '3.6'", @@ -2971,11 +2978,11 @@ }, "zipp": { "hashes": [ - "sha256:05b45f1ee8f807d0cc928485ca40a07cb491cf092ff587c0df9cb1fd154848d2", - "sha256:47c40d7fe183a6f21403a199b3e4192cca5774656965b0a4988ad2f8feb5f009" + "sha256:3a7af91c3db40ec72dd9d154ae18e008c69efe8ca88dde4f9a731bb82fe2f9eb", + "sha256:972cfa31bc2fedd3fa838a51e9bc7e64b7fb725a8c00e7431554311f180e9980" ], "markers": "python_version >= '3.7'", - "version": "==3.8.1" + "version": "==3.9.0" } } } diff --git a/docs/conf.py b/docs/conf.py index a313c719..d7eb20fe 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -29,7 +29,7 @@ # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = ["myst_parser"] +extensions = ["myst_parser", "sphinxcontrib.mermaid"] # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] @@ -51,4 +51,3 @@ # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ["_static"] - diff --git a/docs/deployment.md b/docs/deployment.md index 682cee34..b25dba62 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -1,13 +1,10 @@ ## Deployment -```{admonition} Draft -As a ... you probably want to deploy your work at some point. The following steps will guide you through the process of deploying your work. -``` ### Github conventions + .... ### Using Ansible -.... We use Ansible to deploy versions of the site to our servers. @@ -19,10 +16,191 @@ This runs through the following steps: - fetch dependencies for both cases - run build steps to generate static files for django and any artefacts needed in front end pipelines - reload the servers -- if necessary, update nginx +- if necessary, update nginx (for static files and webserver), gunicorn (for serving web requests), or dramatiq (for our queue and workers) + +See `deploy.yml` and `deploy-workers.yml`, for more information. + +Assumign you have ssh access set up for the correct servers, you deploy with the following command: + +``` +ansible-playbook -i ansible/inventories/prod.yml ./ansible/deploy.yml +``` + +Alternatively, merging code into master triggers the same ansible script via github actions. + +### how our web servers are deployed + +The chart below outlines a high level model of how different moving parts serve web requests. + +A request comes in, and normally a nginx either serves static files or reverse proxies the request to gunicorn, our django web server. Once the request reaches gunicorn, which is running at least one "worker" process, which actually services the request. Represented visually, it looks like so: + +The simplest example + +```{mermaid} + +flowchart LR + + request[http request] + request-->nginx + nginx-->master + master + + subgraph gunicorn + master-->worker1 + subgraph worker1[worker ] + %% render left to right + %% to make them stack + direction LR + end + end +``` + +One gunicorn master procss with one worker will not be able to serve that many requests by itself, so in production we use multiple workers. + +Gunicorn allows us to use multiple types of workers, to fit the workload we are serving, and the resources we have available, like available RAM, free cores, and CPU cycles. + +For a workload where we have free RAM, and CPU, we'd serve 4 sync workers, like so. + +#### A model closer to production - 4 sync workers + +```{mermaid} +flowchart LR + + request + request-->master + + subgraph gunicorn + + master-->worker1 + master-->worker2 + master-->worker3 + master-->worker4 + + subgraph worker1[worker 1] + end + + subgraph worker2[worker 2] + end + + subgraph worker3[worker 3] + end + + subgraph worker4[worker 4] + end + + end + + +``` + +Where ram is constrained, but we have spare CPU capacity, and work is IO bound, we can allocate multiple threads within a worker. This allows a single worker to serve multiple requests, and the threads within a worker are sharing memory, we can still serve multiple requests, without needing to allocate so much memory: +#### 2 workers, each with 2 threads +```{mermaid} +flowchart LR + request + request-->master + + subgraph gunicorn + + master-->worker1 + master-->worker2 + + subgraph worker1[worker 1] + %% render left to right + %% to make them stack + direction LR + thread1 + thread2 + end + + subgraph worker2[worker 2] + %% render left to right + %% to make them stack + direction LR + thread3 + thread4 + end + + end +``` + +As the workloads we serve change, we may need to update the numbers of workers and the kinds of workers,to make the best use of the resources available to serve the workloads facing us. + +**See the code** + +Run the code below from the project root, to run gunicorn: + +``` +# run gunicorn using the `greenweb.wsgi` for defining the behavior inside django, +# the file `gunicorn.conf.py` to define gunicorn's behaviour, +# and binding to port 8000 of the network address 0.0.0.0 +gunicorn greenweb.wsgi --bind 0.0.0.0:8000 -c gunicorn.conf.py +``` + +See `gunicorn.conf.py` in the code base for further informatinon about the workers in use, and `greenweb/wsgi.py` to see which django config file is used to define how the django application behaves. + +**Further reading** + +1. [More on using 'sync' gunicorn workers compared to other types](https://hackernoon.com/why-you-should-almost-always-choose-sync-gunicorn-over-workers-ze9c32wj) +2. [Different means of acheiving concurrency with gunicorn](https://medium.com/building-the-system/gunicorn-3-means-of-concurrency-efbb547674b7) +3. [Using the univorn.worker class to serve async requests with Gunicorn](https://www.uvicorn.org/deployment/) + +### Workers + +We use dramatiq to handle out of band requests, for actions that will take longer than we would like a user to wait to receive a response. + +Here use rabbit MQ as our queue system, and Dramatiq for managing workers. Dramatiq relies on an actor model for picking up work on a queue, and allocating enough workers. + +If you have a series of very heavy, computationally expensive jobs in the queue, there is a risk that all the workers will be stuck working on these, as lots of smaller jobs pile up. + +To avoid this, we have multiple queues - regular, fast finishing throughput work is allocated to the _default_ queue. Heavier, batch processing work to generate stats should be allocated to the stats_ queue. + +#### Typical queue operation - serving fast and slow responses + +```{mermaid} +flowchart LR + + subgraph dramatiq + worker[worker ] + worker2[worker 2] + worker3[worker 3] + worker4[worker 4] + + end + + subgraph rabbitmq + %% render left to right + %% to make them stack + direction LR + default + stats + end + + worker-->default + worker2-->default + worker3-->default + worker4-->stats +``` + +You can run a worker jobs with the following command: + +``` +# serve one worker, using one thread per worker, just for the default queue +manage.py rundramatiq --threads 1 --processes 1 --queues default + +# serve one worker, using one thread per worker, just for the stats queue +manage.py rundramatiq --threads 1 --processes 1 --queues stats + +# serve the default: as many workers as cores available, each with 8 threads, for all queues +manage.py rundramatiq +``` +Update the number of threads and processes accordingly to allocate the appropriate amounts of resources for the workloads. +**Further reading** +1. [The dramatiq guide](https://dramatiq.io/guide.html) +2. [Django Dramatiq, the pacakge we use for interfacing with dramatiq](https://github.com/Bogdanp/django_dramatiq) diff --git a/docs/requirements.txt b/docs/requirements.txt index 520125a4..89ebac5c 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,3 +1,4 @@ -i https://pypi.org/simple myst-parser==0.18.0 furo==2022.6.21 +sphinxcontrib-mermaid==0.7.1 diff --git a/makefile b/makefile index c68e343d..f2245512 100644 --- a/makefile +++ b/makefile @@ -33,7 +33,6 @@ test.only: flake: flake8 ./greenweb ./apps ./*.py --count --statistics - black: black ./greenweb ./apps ./*.py $(ARGS)