diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index a6d012e3f0..dfa5fb20de 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -3,12 +3,12 @@ name: Docs on: push: branches: - - master + - develop paths: - "docs/**" pull_request: branches: - - master + - develop paths: - "docs/**" workflow_dispatch: @@ -46,7 +46,7 @@ jobs: retention-days: 30 deploy-docs: - if: github.repository == 'coronasafe/care' && github.ref == 'refs/heads/master' + if: github.repository == 'coronasafe/care' && github.ref == 'refs/heads/develop' name: Deploy docs runs-on: ubuntu-latest needs: build-docs diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 3c3b3136da..d0d5cc2f00 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -2,7 +2,9 @@ name: Lint Code Base on: pull_request: - branches: [master] + branches: + - develop + - staging merge_group: jobs: @@ -24,7 +26,7 @@ jobs: - name: Lint Code Base uses: github/super-linter/slim@v5 env: - DEFAULT_BRANCH: master + DEFAULT_BRANCH: develop GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} VALIDATE_ALL_CODEBASE: false VALIDATE_PYTHON_BLACK: true diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b4561b2650..a8fe2cd73c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,7 +5,7 @@ repos: rev: v4.4.0 hooks: - id: no-commit-to-branch - args: [--branch, master, --branch, production] + args: [--branch, develop, --branch, staging, --branch, production] - id: check-merge-conflict - id: check-builtin-literals - id: mixed-line-ending diff --git a/.vscode/settings.json b/.vscode/settings.json index c2df4bc2a8..88b18e9481 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -18,7 +18,7 @@ }, "files.trimFinalNewlines": true, "files.trimTrailingWhitespace": true, - "githubPullRequests.ignoredPullRequestBranches": ["master"], + "githubPullRequests.ignoredPullRequestBranches": ["develop", "staging"], "python.formatting.blackPath": "${workspaceFolder}/.venv/bin/black", "python.formatting.provider": "black", "python.languageServer": "Pylance", diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cab4ef6ccc..cd950287fc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -23,7 +23,7 @@ pre-commit install to run pre-commit on your branch: ```bash -pre-commit run --files $(git diff --name-only master...HEAD) +pre-commit run --files $(git diff --name-only develop...HEAD) ``` #### Using Docker diff --git a/Pipfile b/Pipfile index 590a56effd..61b01767b0 100644 --- a/Pipfile +++ b/Pipfile @@ -6,7 +6,7 @@ name = "pypi" [packages] argon2-cffi = "==23.1.0" authlib = "==1.2.1" -boto3 = "==1.34.27" +boto3 = "==1.34.65" celery = "==5.3.6" django = "==4.2.10" django-environ = "==0.11.2" @@ -23,7 +23,7 @@ django-rest-passwordreset = "==1.3.0" django-simple-history = "==3.3.0" djangoql = "==0.17.1" djangorestframework = "==3.14.0" -djangorestframework-simplejwt = "==5.3.0" +djangorestframework-simplejwt = "==5.3.1" dry-rest-permissions = "==0.1.10" drf-nested-routers = "==0.93.4" drf-spectacular = "==0.26.4" @@ -31,10 +31,10 @@ drf-spectacular = "==0.26.4" gunicorn = "==21.2.0" healthy-django = "==0.1.0" jsonschema = "==4.20.0" -jwcrypto = "==1.5.1" +jwcrypto = "==1.5.6" newrelic = "==9.3.0" pillow = "==10.2.0" -psycopg = "==3.1.14" +psycopg = "==3.1.18" pycryptodome = "==3.20.0" pydantic = "==1.10.12" # fix for fhir.resources < 7.0.2 pyjwt = "==2.8.0" @@ -48,9 +48,9 @@ redis-om = "==0.2.1" [dev-packages] black = "==23.9.1" -boto3-stubs = {extras = ["s3", "boto3"], version = "==1.34.27"} +boto3-stubs = {extras = ["s3", "boto3"], version = "==1.34.65"} coverage = "==7.4.0" -debugpy = "==1.7.0" +debugpy = "==1.8.1" django-coverage-plugin = "==3.1.0" django-debug-toolbar = "==4.2.0" django-extensions = "==3.2.3" @@ -58,7 +58,7 @@ django-silk = "==5.0.3" django-stubs = "==4.2.4" djangorestframework-stubs = "==3.14.2" factory-boy = "==3.3.0" -flake8 = "==6.1.0" +flake8 = "==7.0.0" freezegun = "==1.2.2" ipython = "==8.15.0" isort = "==5.12.0" diff --git a/Pipfile.lock b/Pipfile.lock index 6cdb09ff6a..90f6080148 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "e46c7dd4bb313bf57fc2b800581aab91c31c2fdf7599dafebac09b66bb22208c" + "sha256": "ea4fe23094588fde2796a4107473d14dc5a47a9795a08582da5182dac2b5c4fb" }, "pipfile-spec": 6, "requires": { @@ -94,20 +94,20 @@ }, "boto3": { "hashes": [ - "sha256:3626db4ba9fbb1b58c8fe923da5ed670873b3d881a102956ea19d3b69cd097cc", - "sha256:ebdd938019f3df2e7b50585353963d4553faf3fbb7b2085c440107fa6caa233b" + "sha256:b611de58ab28940a36c77d7ef9823427ebf25d5ee8277b802f9979b14e780534", + "sha256:db97f9c29f1806cf9020679be0dd5ffa2aff2670e28e0e2046f98b979be498a4" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==1.34.27" + "version": "==1.34.65" }, "botocore": { "hashes": [ - "sha256:9f00bd5e4698bcdd37ce6e224a896baf58d209678ed92834944b767de9061cc5", - "sha256:e175360445424b83b0e28ae20d301b99cf44ff2c9d5ab1d8670899bec05a9753" + "sha256:92560f8fbdaa9dd221212a3d3a7609219ba0bbf308c13571674c0cda9d8f39e1", + "sha256:fd7d8742007c220f897cb126b8916ca0cf3724a739d4d716aa5385d7f9d8aeb1" ], "markers": "python_version >= '3.8'", - "version": "==1.34.39" + "version": "==1.34.66" }, "celery": { "hashes": [ @@ -313,49 +313,41 @@ }, "cryptography": { "hashes": [ - "sha256:087887e55e0b9c8724cf05361357875adb5c20dec27e5816b653492980d20380", - "sha256:09a77e5b2e8ca732a19a90c5bca2d124621a1edb5438c5daa2d2738bfeb02589", - "sha256:130c0f77022b2b9c99d8cebcdd834d81705f61c68e91ddd614ce74c657f8b3ea", - "sha256:141e2aa5ba100d3788c0ad7919b288f89d1fe015878b9659b307c9ef867d3a65", - "sha256:28cb2c41f131a5758d6ba6a0504150d644054fd9f3203a1e8e8d7ac3aea7f73a", - "sha256:2f9f14185962e6a04ab32d1abe34eae8a9001569ee4edb64d2304bf0d65c53f3", - "sha256:320948ab49883557a256eab46149df79435a22d2fefd6a66fe6946f1b9d9d008", - "sha256:36d4b7c4be6411f58f60d9ce555a73df8406d484ba12a63549c88bd64f7967f1", - "sha256:3b15c678f27d66d247132cbf13df2f75255627bcc9b6a570f7d2fd08e8c081d2", - "sha256:3dbd37e14ce795b4af61b89b037d4bc157f2cb23e676fa16932185a04dfbf635", - "sha256:4383b47f45b14459cab66048d384614019965ba6c1a1a141f11b5a551cace1b2", - "sha256:44c95c0e96b3cb628e8452ec060413a49002a247b2b9938989e23a2c8291fc90", - "sha256:4b063d3413f853e056161eb0c7724822a9740ad3caa24b8424d776cebf98e7ee", - "sha256:52ed9ebf8ac602385126c9a2fe951db36f2cb0c2538d22971487f89d0de4065a", - "sha256:55d1580e2d7e17f45d19d3b12098e352f3a37fe86d380bf45846ef257054b242", - "sha256:5ef9bc3d046ce83c4bbf4c25e1e0547b9c441c01d30922d812e887dc5f125c12", - "sha256:5fa82a26f92871eca593b53359c12ad7949772462f887c35edaf36f87953c0e2", - "sha256:61321672b3ac7aade25c40449ccedbc6db72c7f5f0fdf34def5e2f8b51ca530d", - "sha256:701171f825dcab90969596ce2af253143b93b08f1a716d4b2a9d2db5084ef7be", - "sha256:841ec8af7a8491ac76ec5a9522226e287187a3107e12b7d686ad354bb78facee", - "sha256:8a06641fb07d4e8f6c7dda4fc3f8871d327803ab6542e33831c7ccfdcb4d0ad6", - "sha256:8e88bb9eafbf6a4014d55fb222e7360eef53e613215085e65a13290577394529", - "sha256:a00aee5d1b6c20620161984f8ab2ab69134466c51f58c052c11b076715e72929", - "sha256:a047682d324ba56e61b7ea7c7299d51e61fd3bca7dad2ccc39b72bd0118d60a1", - "sha256:a7ef8dd0bf2e1d0a27042b231a3baac6883cdd5557036f5e8df7139255feaac6", - "sha256:ad28cff53f60d99a928dfcf1e861e0b2ceb2bc1f08a074fdd601b314e1cc9e0a", - "sha256:b9097a208875fc7bbeb1286d0125d90bdfed961f61f214d3f5be62cd4ed8a446", - "sha256:b97fe7d7991c25e6a31e5d5e795986b18fbbb3107b873d5f3ae6dc9a103278e9", - "sha256:e0ec52ba3c7f1b7d813cd52649a5b3ef1fc0d433219dc8c93827c57eab6cf888", - "sha256:ea2c3ffb662fec8bbbfce5602e2c159ff097a4631d96235fcf0fb00e59e3ece4", - "sha256:fa3dec4ba8fb6e662770b74f62f1a0c7d4e37e25b58b2bf2c1be4c95372b4a33", - "sha256:fbeb725c9dc799a574518109336acccaf1303c30d45c075c665c0793c2f79a7f" + "sha256:0270572b8bd2c833c3981724b8ee9747b3ec96f699a9665470018594301439ee", + "sha256:111a0d8553afcf8eb02a4fea6ca4f59d48ddb34497aa8706a6cf536f1a5ec576", + "sha256:16a48c23a62a2f4a285699dba2e4ff2d1cff3115b9df052cdd976a18856d8e3d", + "sha256:1b95b98b0d2af784078fa69f637135e3c317091b615cd0905f8b8a087e86fa30", + "sha256:1f71c10d1e88467126f0efd484bd44bca5e14c664ec2ede64c32f20875c0d413", + "sha256:2424ff4c4ac7f6b8177b53c17ed5d8fa74ae5955656867f5a8affaca36a27abb", + "sha256:2bce03af1ce5a5567ab89bd90d11e7bbdff56b8af3acbbec1faded8f44cb06da", + "sha256:329906dcc7b20ff3cad13c069a78124ed8247adcac44b10bea1130e36caae0b4", + "sha256:37dd623507659e08be98eec89323469e8c7b4c1407c85112634ae3dbdb926fdd", + "sha256:3eaafe47ec0d0ffcc9349e1708be2aaea4c6dd4978d76bf6eb0cb2c13636c6fc", + "sha256:5e6275c09d2badf57aea3afa80d975444f4be8d3bc58f7f80d2a484c6f9485c8", + "sha256:6fe07eec95dfd477eb9530aef5bead34fec819b3aaf6c5bd6d20565da607bfe1", + "sha256:7367d7b2eca6513681127ebad53b2582911d1736dc2ffc19f2c3ae49997496bc", + "sha256:7cde5f38e614f55e28d831754e8a3bacf9ace5d1566235e39d91b35502d6936e", + "sha256:9481ffe3cf013b71b2428b905c4f7a9a4f76ec03065b05ff499bb5682a8d9ad8", + "sha256:98d8dc6d012b82287f2c3d26ce1d2dd130ec200c8679b6213b3c73c08b2b7940", + "sha256:a011a644f6d7d03736214d38832e030d8268bcff4a41f728e6030325fea3e400", + "sha256:a2913c5375154b6ef2e91c10b5720ea6e21007412f6437504ffea2109b5a33d7", + "sha256:a30596bae9403a342c978fb47d9b0ee277699fa53bbafad14706af51fe543d16", + "sha256:b03c2ae5d2f0fc05f9a2c0c997e1bc18c8229f392234e8a0194f202169ccd278", + "sha256:b6cd2203306b63e41acdf39aa93b86fb566049aeb6dc489b70e34bcd07adca74", + "sha256:b7ffe927ee6531c78f81aa17e684e2ff617daeba7f189f911065b2ea2d526dec", + "sha256:b8cac287fafc4ad485b8a9b67d0ee80c66bf3574f655d3b97ef2e1082360faf1", + "sha256:ba334e6e4b1d92442b75ddacc615c5476d4ad55cc29b15d590cc6b86efa487e2", + "sha256:ba3e4a42397c25b7ff88cdec6e2a16c2be18720f317506ee25210f6d31925f9c", + "sha256:c41fb5e6a5fe9ebcd58ca3abfeb51dffb5d83d6775405305bfa8715b76521922", + "sha256:cd2030f6650c089aeb304cf093f3244d34745ce0cfcc39f20c6fbfe030102e2a", + "sha256:cd65d75953847815962c84a4654a84850b2bb4aed3f26fadcc1c13892e1e29f6", + "sha256:e4985a790f921508f36f81831817cbc03b102d643b5fcb81cd33df3fa291a1a1", + "sha256:e807b3188f9eb0eaa7bbb579b462c5ace579f1cedb28107ce8b48a9f7ad3679e", + "sha256:f12764b8fffc7a123f641d7d049d382b73f96a34117e0b637b80643169cec8ac", + "sha256:f8837fe1d6ac4a8052a9a8ddab256bc006242696f03368a4009be7ee3075cdb7" ], "markers": "python_version >= '3.7'", - "version": "==42.0.2" - }, - "deprecated": { - "hashes": [ - "sha256:6fac8b097794a90302bdbb17b9b815e732d3c4720583ff1b198499d78470466c", - "sha256:e5323eb936458dccc2582dc6f9c322c852a775a27065ff2b0c4970b9d53d01b3" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.2.14" + "version": "==42.0.5" }, "django": { "hashes": [ @@ -486,20 +478,20 @@ }, "djangorestframework-simplejwt": { "hashes": [ - "sha256:631d7ae2ed4365d7196a35d3cc0f6d382f7bd3361fb24c894f8f92b4da5db27d", - "sha256:8e4c5dfca8d11c0b8a66dfd8a4e3fc1c6aa7ea188d10907ff91c942f4b52ed66" + "sha256:381bc966aa46913905629d472cd72ad45faa265509764e20ffd440164c88d220", + "sha256:6c4bd37537440bc439564ebf7d6085e74c5411485197073f508ebdfa34bc9fae" ], "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==5.3.0" + "markers": "python_version >= '3.8'", + "version": "==5.3.1" }, "dnspython": { "hashes": [ - "sha256:6facdf76b73c742ccf2d07add296f178e629da60be23ce4b0a9c927b1e02c3a6", - "sha256:a0034815a59ba9ae888946be7ccca8f7c157b286f8455b379c692efb51022a15" + "sha256:5ef3b9680161f6fa89daf8ad451b5f1a33b18ae8a1c6778cdf4b43f08c0a6e50", + "sha256:e8f0f9c23a7b7cb99ded64e6c3a6f3e701d78f50c55e002b839dea7225cff7cc" ], "markers": "python_version >= '3.8'", - "version": "==2.5.0" + "version": "==2.6.1" }, "drf-nested-routers": { "hashes": [ @@ -529,10 +521,10 @@ }, "email-validator": { "hashes": [ - "sha256:a4b0bd1cf55f073b924258d19321b1f3aa74b4b5a71a42c305575dba920e1a44", - "sha256:c973053efbeddfef924dc0bd93f6e77a1ea7ee0fce935aea7103c7a3d6d2d637" + "sha256:200a70680ba08904be6d1eef729205cc0d687634399a5924d842533efb824b84", + "sha256:97d882d174e2a65732fb43bfce81a3a834cbc1bde8bf419e30ef5ea976370a05" ], - "version": "==2.1.0.post1" + "version": "==2.1.1" }, "fhir.resources": { "hashes": [ @@ -723,11 +715,12 @@ }, "jwcrypto": { "hashes": [ - "sha256:48bb9bf433777136253579e52b75ffe0f9a4a721d133d01f45a0b91ed5f4f1ae" + "sha256:150d2b0ebbdb8f40b77f543fb44ffd2baeff48788be71f67f03566692fd55789", + "sha256:771a87762a0c081ae6166958a954f80848820b2ab066937dc8b8379d65b1b039" ], "index": "pypi", - "markers": "python_version >= '3.6'", - "version": "==1.5.1" + "markers": "python_version >= '3.8'", + "version": "==1.5.6" }, "kombu": { "hashes": [ @@ -769,11 +762,11 @@ }, "packaging": { "hashes": [ - "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5", - "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7" + "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5", + "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9" ], "markers": "python_version >= '3.7'", - "version": "==23.2" + "version": "==24.0" }, "pillow": { "hashes": [ @@ -867,12 +860,12 @@ }, "psycopg": { "hashes": [ - "sha256:7a63249f52e9c312d2d3978df5f170d21a0defd3a0c950d7859d226b7cfbfad5", - "sha256:f5bce37d357578b230ede15fb461e2c601122986f6dd590e94283aaca8958b14" + "sha256:31144d3fb4c17d78094d9e579826f047d4af1da6a10427d91dfcfb6ecdf6f12b", + "sha256:4d5a0a5a8590906daa58ebd5f3cfc34091377354a1acced269dd10faf55da60e" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==3.1.14" + "version": "==3.1.18" }, "py-vapid": { "hashes": [ @@ -983,18 +976,18 @@ }, "python-dateutil": { "hashes": [ - "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", - "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" + "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", + "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.8.2" + "version": "==2.9.0.post0" }, "python-fsutil": { "hashes": [ - "sha256:302b0a1739b85151df6e6fddb1593a8fb9648c655fad0fac6c66c21bce900c13", - "sha256:b4ad4c57c243ba46ee5aacebe58ce7a6f62ddeff20ea53e52afc420884b5e544" + "sha256:0d45e623f0f4403f674bdd8ae7aa7d24a4b3132ea45c65416bd2865e6b20b035", + "sha256:8fb204fa8059f37bdeee8a1dc0fff010170202ea47c4225ee71bb3c26f3997be" ], - "version": "==0.13.1" + "version": "==0.14.1" }, "python-slugify": { "hashes": [ @@ -1106,11 +1099,11 @@ }, "referencing": { "hashes": [ - "sha256:39240f2ecc770258f28b642dd47fd74bc8b02484de54e1882b74b35ebd779bd5", - "sha256:c775fedf74bc0f9189c2a3be1c12fd03e8c23f4d371dce795df44e06c5b412f7" + "sha256:5773bd84ef41799a5a8ca72dc34590c041eb01bf9aa02632b4a973fb0181a844", + "sha256:d53ae300ceddd3169f1ffa9caf2cb7b769e92657e4fafb23d34b93679116dfd4" ], "markers": "python_version >= '3.8'", - "version": "==0.33.0" + "version": "==0.34.0" }, "requests": { "hashes": [ @@ -1123,116 +1116,116 @@ }, "rpds-py": { "hashes": [ - "sha256:01f58a7306b64e0a4fe042047dd2b7d411ee82e54240284bab63e325762c1147", - "sha256:0210b2668f24c078307260bf88bdac9d6f1093635df5123789bfee4d8d7fc8e7", - "sha256:02866e060219514940342a1f84303a1ef7a1dad0ac311792fbbe19b521b489d2", - "sha256:0387ce69ba06e43df54e43968090f3626e231e4bc9150e4c3246947567695f68", - "sha256:060f412230d5f19fc8c8b75f315931b408d8ebf56aec33ef4168d1b9e54200b1", - "sha256:071bc28c589b86bc6351a339114fb7a029f5cddbaca34103aa573eba7b482382", - "sha256:0bfb09bf41fe7c51413f563373e5f537eaa653d7adc4830399d4e9bdc199959d", - "sha256:10162fe3f5f47c37ebf6d8ff5a2368508fe22007e3077bf25b9c7d803454d921", - "sha256:149c5cd24f729e3567b56e1795f74577aa3126c14c11e457bec1b1c90d212e38", - "sha256:1701fc54460ae2e5efc1dd6350eafd7a760f516df8dbe51d4a1c79d69472fbd4", - "sha256:1957a2ab607f9added64478a6982742eb29f109d89d065fa44e01691a20fc20a", - "sha256:1a746a6d49665058a5896000e8d9d2f1a6acba8a03b389c1e4c06e11e0b7f40d", - "sha256:1bfcad3109c1e5ba3cbe2f421614e70439f72897515a96c462ea657261b96518", - "sha256:1d36b2b59e8cc6e576f8f7b671e32f2ff43153f0ad6d0201250a7c07f25d570e", - "sha256:1db228102ab9d1ff4c64148c96320d0be7044fa28bd865a9ce628ce98da5973d", - "sha256:1dc29db3900cb1bb40353772417800f29c3d078dbc8024fd64655a04ee3c4bdf", - "sha256:1e626b365293a2142a62b9a614e1f8e331b28f3ca57b9f05ebbf4cf2a0f0bdc5", - "sha256:1f3c3461ebb4c4f1bbc70b15d20b565759f97a5aaf13af811fcefc892e9197ba", - "sha256:20de7b7179e2031a04042e85dc463a93a82bc177eeba5ddd13ff746325558aa6", - "sha256:24e4900a6643f87058a27320f81336d527ccfe503984528edde4bb660c8c8d59", - "sha256:2528ff96d09f12e638695f3a2e0c609c7b84c6df7c5ae9bfeb9252b6fa686253", - "sha256:25f071737dae674ca8937a73d0f43f5a52e92c2d178330b4c0bb6ab05586ffa6", - "sha256:270987bc22e7e5a962b1094953ae901395e8c1e1e83ad016c5cfcfff75a15a3f", - "sha256:292f7344a3301802e7c25c53792fae7d1593cb0e50964e7bcdcc5cf533d634e3", - "sha256:2953937f83820376b5979318840f3ee47477d94c17b940fe31d9458d79ae7eea", - "sha256:2a792b2e1d3038daa83fa474d559acfd6dc1e3650ee93b2662ddc17dbff20ad1", - "sha256:2a7b2f2f56a16a6d62e55354dd329d929560442bd92e87397b7a9586a32e3e76", - "sha256:2f4eb548daf4836e3b2c662033bfbfc551db58d30fd8fe660314f86bf8510b93", - "sha256:3664d126d3388a887db44c2e293f87d500c4184ec43d5d14d2d2babdb4c64cad", - "sha256:3677fcca7fb728c86a78660c7fb1b07b69b281964673f486ae72860e13f512ad", - "sha256:380e0df2e9d5d5d339803cfc6d183a5442ad7ab3c63c2a0982e8c824566c5ccc", - "sha256:3ac732390d529d8469b831949c78085b034bff67f584559340008d0f6041a049", - "sha256:4128980a14ed805e1b91a7ed551250282a8ddf8201a4e9f8f5b7e6225f54170d", - "sha256:4341bd7579611cf50e7b20bb8c2e23512a3dc79de987a1f411cb458ab670eb90", - "sha256:436474f17733c7dca0fbf096d36ae65277e8645039df12a0fa52445ca494729d", - "sha256:4dc889a9d8a34758d0fcc9ac86adb97bab3fb7f0c4d29794357eb147536483fd", - "sha256:4e21b76075c01d65d0f0f34302b5a7457d95721d5e0667aea65e5bb3ab415c25", - "sha256:516fb8c77805159e97a689e2f1c80655c7658f5af601c34ffdb916605598cda2", - "sha256:5576ee2f3a309d2bb403ec292d5958ce03953b0e57a11d224c1f134feaf8c40f", - "sha256:5a024fa96d541fd7edaa0e9d904601c6445e95a729a2900c5aec6555fe921ed6", - "sha256:5d0e8a6434a3fbf77d11448c9c25b2f25244226cfbec1a5159947cac5b8c5fa4", - "sha256:5e7d63ec01fe7c76c2dbb7e972fece45acbb8836e72682bde138e7e039906e2c", - "sha256:60e820ee1004327609b28db8307acc27f5f2e9a0b185b2064c5f23e815f248f8", - "sha256:637b802f3f069a64436d432117a7e58fab414b4e27a7e81049817ae94de45d8d", - "sha256:65dcf105c1943cba45d19207ef51b8bc46d232a381e94dd38719d52d3980015b", - "sha256:698ea95a60c8b16b58be9d854c9f993c639f5c214cf9ba782eca53a8789d6b19", - "sha256:70fcc6c2906cfa5c6a552ba7ae2ce64b6c32f437d8f3f8eea49925b278a61453", - "sha256:720215373a280f78a1814becb1312d4e4d1077b1202a56d2b0815e95ccb99ce9", - "sha256:7450dbd659fed6dd41d1a7d47ed767e893ba402af8ae664c157c255ec6067fde", - "sha256:7b7d9ca34542099b4e185b3c2a2b2eda2e318a7dbde0b0d83357a6d4421b5296", - "sha256:7fbd70cb8b54fe745301921b0816c08b6d917593429dfc437fd024b5ba713c58", - "sha256:81038ff87a4e04c22e1d81f947c6ac46f122e0c80460b9006e6517c4d842a6ec", - "sha256:810685321f4a304b2b55577c915bece4c4a06dfe38f6e62d9cc1d6ca8ee86b99", - "sha256:82ada4a8ed9e82e443fcef87e22a3eed3654dd3adf6e3b3a0deb70f03e86142a", - "sha256:841320e1841bb53fada91c9725e766bb25009cfd4144e92298db296fb6c894fb", - "sha256:8587fd64c2a91c33cdc39d0cebdaf30e79491cc029a37fcd458ba863f8815383", - "sha256:8ffe53e1d8ef2520ebcf0c9fec15bb721da59e8ef283b6ff3079613b1e30513d", - "sha256:9051e3d2af8f55b42061603e29e744724cb5f65b128a491446cc029b3e2ea896", - "sha256:91e5a8200e65aaac342a791272c564dffcf1281abd635d304d6c4e6b495f29dc", - "sha256:93432e747fb07fa567ad9cc7aaadd6e29710e515aabf939dfbed8046041346c6", - "sha256:938eab7323a736533f015e6069a7d53ef2dcc841e4e533b782c2bfb9fb12d84b", - "sha256:9584f8f52010295a4a417221861df9bea4c72d9632562b6e59b3c7b87a1522b7", - "sha256:9737bdaa0ad33d34c0efc718741abaafce62fadae72c8b251df9b0c823c63b22", - "sha256:99da0a4686ada4ed0f778120a0ea8d066de1a0a92ab0d13ae68492a437db78bf", - "sha256:99f567dae93e10be2daaa896e07513dd4bf9c2ecf0576e0533ac36ba3b1d5394", - "sha256:9bdf1303df671179eaf2cb41e8515a07fc78d9d00f111eadbe3e14262f59c3d0", - "sha256:9f0e4dc0f17dcea4ab9d13ac5c666b6b5337042b4d8f27e01b70fae41dd65c57", - "sha256:a000133a90eea274a6f28adc3084643263b1e7c1a5a66eb0a0a7a36aa757ed74", - "sha256:a3264e3e858de4fc601741498215835ff324ff2482fd4e4af61b46512dd7fc83", - "sha256:a71169d505af63bb4d20d23a8fbd4c6ce272e7bce6cc31f617152aa784436f29", - "sha256:a967dd6afda7715d911c25a6ba1517975acd8d1092b2f326718725461a3d33f9", - "sha256:aa5bfb13f1e89151ade0eb812f7b0d7a4d643406caaad65ce1cbabe0a66d695f", - "sha256:ae35e8e6801c5ab071b992cb2da958eee76340e6926ec693b5ff7d6381441745", - "sha256:b686f25377f9c006acbac63f61614416a6317133ab7fafe5de5f7dc8a06d42eb", - "sha256:b760a56e080a826c2e5af09002c1a037382ed21d03134eb6294812dda268c811", - "sha256:b86b21b348f7e5485fae740d845c65a880f5d1eda1e063bc59bef92d1f7d0c55", - "sha256:b9412abdf0ba70faa6e2ee6c0cc62a8defb772e78860cef419865917d86c7342", - "sha256:bd345a13ce06e94c753dab52f8e71e5252aec1e4f8022d24d56decd31e1b9b23", - "sha256:be22ae34d68544df293152b7e50895ba70d2a833ad9566932d750d3625918b82", - "sha256:bf046179d011e6114daf12a534d874958b039342b347348a78b7cdf0dd9d6041", - "sha256:c3d2010656999b63e628a3c694f23020322b4178c450dc478558a2b6ef3cb9bb", - "sha256:c64602e8be701c6cfe42064b71c84ce62ce66ddc6422c15463fd8127db3d8066", - "sha256:d65e6b4f1443048eb7e833c2accb4fa7ee67cc7d54f31b4f0555b474758bee55", - "sha256:d8bbd8e56f3ba25a7d0cf980fc42b34028848a53a0e36c9918550e0280b9d0b6", - "sha256:da1ead63368c04a9bded7904757dfcae01eba0e0f9bc41d3d7f57ebf1c04015a", - "sha256:dbbb95e6fc91ea3102505d111b327004d1c4ce98d56a4a02e82cd451f9f57140", - "sha256:dbc56680ecf585a384fbd93cd42bc82668b77cb525343170a2d86dafaed2a84b", - "sha256:df3b6f45ba4515632c5064e35ca7f31d51d13d1479673185ba8f9fefbbed58b9", - "sha256:dfe07308b311a8293a0d5ef4e61411c5c20f682db6b5e73de6c7c8824272c256", - "sha256:e796051f2070f47230c745d0a77a91088fbee2cc0502e9b796b9c6471983718c", - "sha256:efa767c220d94aa4ac3a6dd3aeb986e9f229eaf5bce92d8b1b3018d06bed3772", - "sha256:f0b8bf5b8db49d8fd40f54772a1dcf262e8be0ad2ab0206b5a2ec109c176c0a4", - "sha256:f175e95a197f6a4059b50757a3dca33b32b61691bdbd22c29e8a8d21d3914cae", - "sha256:f2f3b28b40fddcb6c1f1f6c88c6f3769cd933fa493ceb79da45968a21dccc920", - "sha256:f6c43b6f97209e370124baf2bf40bb1e8edc25311a158867eb1c3a5d449ebc7a", - "sha256:f7f4cb1f173385e8a39c29510dd11a78bf44e360fb75610594973f5ea141028b", - "sha256:fad059a4bd14c45776600d223ec194e77db6c20255578bb5bcdd7c18fd169361", - "sha256:ff1dcb8e8bc2261a088821b2595ef031c91d499a0c1b031c152d43fe0a6ecec8", - "sha256:ffee088ea9b593cc6160518ba9bd319b5475e5f3e578e4552d63818773c6f56a" + "sha256:01e36a39af54a30f28b73096dd39b6802eddd04c90dbe161c1b8dbe22353189f", + "sha256:044a3e61a7c2dafacae99d1e722cc2d4c05280790ec5a05031b3876809d89a5c", + "sha256:08231ac30a842bd04daabc4d71fddd7e6d26189406d5a69535638e4dcb88fe76", + "sha256:08f9ad53c3f31dfb4baa00da22f1e862900f45908383c062c27628754af2e88e", + "sha256:0ab39c1ba9023914297dd88ec3b3b3c3f33671baeb6acf82ad7ce883f6e8e157", + "sha256:0af039631b6de0397ab2ba16eaf2872e9f8fca391b44d3d8cac317860a700a3f", + "sha256:0b8612cd233543a3781bc659c731b9d607de65890085098986dfd573fc2befe5", + "sha256:11a8c85ef4a07a7638180bf04fe189d12757c696eb41f310d2426895356dcf05", + "sha256:1374f4129f9bcca53a1bba0bb86bf78325a0374577cf7e9e4cd046b1e6f20e24", + "sha256:1d4acf42190d449d5e89654d5c1ed3a4f17925eec71f05e2a41414689cda02d1", + "sha256:1d9a5be316c15ffb2b3c405c4ff14448c36b4435be062a7f578ccd8b01f0c4d8", + "sha256:1df3659d26f539ac74fb3b0c481cdf9d725386e3552c6fa2974f4d33d78e544b", + "sha256:22806714311a69fd0af9b35b7be97c18a0fc2826e6827dbb3a8c94eac6cf7eeb", + "sha256:2644e47de560eb7bd55c20fc59f6daa04682655c58d08185a9b95c1970fa1e07", + "sha256:2e6d75ab12b0bbab7215e5d40f1e5b738aa539598db27ef83b2ec46747df90e1", + "sha256:30f43887bbae0d49113cbaab729a112251a940e9b274536613097ab8b4899cf6", + "sha256:34b18ba135c687f4dac449aa5157d36e2cbb7c03cbea4ddbd88604e076aa836e", + "sha256:36b3ee798c58ace201289024b52788161e1ea133e4ac93fba7d49da5fec0ef9e", + "sha256:39514da80f971362f9267c600b6d459bfbbc549cffc2cef8e47474fddc9b45b1", + "sha256:39f5441553f1c2aed4de4377178ad8ff8f9d733723d6c66d983d75341de265ab", + "sha256:3a96e0c6a41dcdba3a0a581bbf6c44bb863f27c541547fb4b9711fd8cf0ffad4", + "sha256:3f26b5bd1079acdb0c7a5645e350fe54d16b17bfc5e71f371c449383d3342e17", + "sha256:41ef53e7c58aa4ef281da975f62c258950f54b76ec8e45941e93a3d1d8580594", + "sha256:42821446ee7a76f5d9f71f9e33a4fb2ffd724bb3e7f93386150b61a43115788d", + "sha256:43fbac5f22e25bee1d482c97474f930a353542855f05c1161fd804c9dc74a09d", + "sha256:4457a94da0d5c53dc4b3e4de1158bdab077db23c53232f37a3cb7afdb053a4e3", + "sha256:465a3eb5659338cf2a9243e50ad9b2296fa15061736d6e26240e713522b6235c", + "sha256:482103aed1dfe2f3b71a58eff35ba105289b8d862551ea576bd15479aba01f66", + "sha256:4832d7d380477521a8c1644bbab6588dfedea5e30a7d967b5fb75977c45fd77f", + "sha256:4901165d170a5fde6f589acb90a6b33629ad1ec976d4529e769c6f3d885e3e80", + "sha256:5307def11a35f5ae4581a0b658b0af8178c65c530e94893345bebf41cc139d33", + "sha256:5417558f6887e9b6b65b4527232553c139b57ec42c64570569b155262ac0754f", + "sha256:56a737287efecafc16f6d067c2ea0117abadcd078d58721f967952db329a3e5c", + "sha256:586f8204935b9ec884500498ccc91aa869fc652c40c093bd9e1471fbcc25c022", + "sha256:5b4e7d8d6c9b2e8ee2d55c90b59c707ca59bc30058269b3db7b1f8df5763557e", + "sha256:5ddcba87675b6d509139d1b521e0c8250e967e63b5909a7e8f8944d0f90ff36f", + "sha256:618a3d6cae6ef8ec88bb76dd80b83cfe415ad4f1d942ca2a903bf6b6ff97a2da", + "sha256:635dc434ff724b178cb192c70016cc0ad25a275228f749ee0daf0eddbc8183b1", + "sha256:661d25cbffaf8cc42e971dd570d87cb29a665f49f4abe1f9e76be9a5182c4688", + "sha256:66e6a3af5a75363d2c9a48b07cb27c4ea542938b1a2e93b15a503cdfa8490795", + "sha256:67071a6171e92b6da534b8ae326505f7c18022c6f19072a81dcf40db2638767c", + "sha256:685537e07897f173abcf67258bee3c05c374fa6fff89d4c7e42fb391b0605e98", + "sha256:69e64831e22a6b377772e7fb337533c365085b31619005802a79242fee620bc1", + "sha256:6b0817e34942b2ca527b0e9298373e7cc75f429e8da2055607f4931fded23e20", + "sha256:6c81e5f372cd0dc5dc4809553d34f832f60a46034a5f187756d9b90586c2c307", + "sha256:6d7faa6f14017c0b1e69f5e2c357b998731ea75a442ab3841c0dbbbfe902d2c4", + "sha256:6ef0befbb5d79cf32d0266f5cff01545602344eda89480e1dd88aca964260b18", + "sha256:6ef687afab047554a2d366e112dd187b62d261d49eb79b77e386f94644363294", + "sha256:7223a2a5fe0d217e60a60cdae28d6949140dde9c3bcc714063c5b463065e3d66", + "sha256:77f195baa60a54ef9d2de16fbbfd3ff8b04edc0c0140a761b56c267ac11aa467", + "sha256:793968759cd0d96cac1e367afd70c235867831983f876a53389ad869b043c948", + "sha256:7bd339195d84439cbe5771546fe8a4e8a7a045417d8f9de9a368c434e42a721e", + "sha256:7cd863afe7336c62ec78d7d1349a2f34c007a3cc6c2369d667c65aeec412a5b1", + "sha256:7f2facbd386dd60cbbf1a794181e6aa0bd429bd78bfdf775436020172e2a23f0", + "sha256:84ffab12db93b5f6bad84c712c92060a2d321b35c3c9960b43d08d0f639d60d7", + "sha256:8c8370641f1a7f0e0669ddccca22f1da893cef7628396431eb445d46d893e5cd", + "sha256:8db715ebe3bb7d86d77ac1826f7d67ec11a70dbd2376b7cc214199360517b641", + "sha256:8e8916ae4c720529e18afa0b879473049e95949bf97042e938530e072fde061d", + "sha256:8f03bccbd8586e9dd37219bce4d4e0d3ab492e6b3b533e973fa08a112cb2ffc9", + "sha256:8f2fc11e8fe034ee3c34d316d0ad8808f45bc3b9ce5857ff29d513f3ff2923a1", + "sha256:923d39efa3cfb7279a0327e337a7958bff00cc447fd07a25cddb0a1cc9a6d2da", + "sha256:93df1de2f7f7239dc9cc5a4a12408ee1598725036bd2dedadc14d94525192fc3", + "sha256:998e33ad22dc7ec7e030b3df701c43630b5bc0d8fbc2267653577e3fec279afa", + "sha256:99f70b740dc04d09e6b2699b675874367885217a2e9f782bdf5395632ac663b7", + "sha256:9a00312dea9310d4cb7dbd7787e722d2e86a95c2db92fbd7d0155f97127bcb40", + "sha256:9d54553c1136b50fd12cc17e5b11ad07374c316df307e4cfd6441bea5fb68496", + "sha256:9dbbeb27f4e70bfd9eec1be5477517365afe05a9b2c441a0b21929ee61048124", + "sha256:a1ce3ba137ed54f83e56fb983a5859a27d43a40188ba798993812fed73c70836", + "sha256:a34d557a42aa28bd5c48a023c570219ba2593bcbbb8dc1b98d8cf5d529ab1434", + "sha256:a5f446dd5055667aabaee78487f2b5ab72e244f9bc0b2ffebfeec79051679984", + "sha256:ad36cfb355e24f1bd37cac88c112cd7730873f20fb0bdaf8ba59eedf8216079f", + "sha256:aec493917dd45e3c69d00a8874e7cbed844efd935595ef78a0f25f14312e33c6", + "sha256:b316144e85316da2723f9d8dc75bada12fa58489a527091fa1d5a612643d1a0e", + "sha256:b34ae4636dfc4e76a438ab826a0d1eed2589ca7d9a1b2d5bb546978ac6485461", + "sha256:b34b7aa8b261c1dbf7720b5d6f01f38243e9b9daf7e6b8bc1fd4657000062f2c", + "sha256:bc362ee4e314870a70f4ae88772d72d877246537d9f8cb8f7eacf10884862432", + "sha256:bed88b9a458e354014d662d47e7a5baafd7ff81c780fd91584a10d6ec842cb73", + "sha256:c0013fe6b46aa496a6749c77e00a3eb07952832ad6166bd481c74bda0dcb6d58", + "sha256:c0b5dcf9193625afd8ecc92312d6ed78781c46ecbf39af9ad4681fc9f464af88", + "sha256:c4325ff0442a12113a6379af66978c3fe562f846763287ef66bdc1d57925d337", + "sha256:c463ed05f9dfb9baebef68048aed8dcdc94411e4bf3d33a39ba97e271624f8f7", + "sha256:c8362467a0fdeccd47935f22c256bec5e6abe543bf0d66e3d3d57a8fb5731863", + "sha256:cd5bf1af8efe569654bbef5a3e0a56eca45f87cfcffab31dd8dde70da5982475", + "sha256:cf1ea2e34868f6fbf070e1af291c8180480310173de0b0c43fc38a02929fc0e3", + "sha256:d62dec4976954a23d7f91f2f4530852b0c7608116c257833922a896101336c51", + "sha256:d68c93e381010662ab873fea609bf6c0f428b6d0bb00f2c6939782e0818d37bf", + "sha256:d7c36232a90d4755b720fbd76739d8891732b18cf240a9c645d75f00639a9024", + "sha256:dd18772815d5f008fa03d2b9a681ae38d5ae9f0e599f7dda233c439fcaa00d40", + "sha256:ddc2f4dfd396c7bfa18e6ce371cba60e4cf9d2e5cdb71376aa2da264605b60b9", + "sha256:e003b002ec72c8d5a3e3da2989c7d6065b47d9eaa70cd8808b5384fbb970f4ec", + "sha256:e32a92116d4f2a80b629778280103d2a510a5b3f6314ceccd6e38006b5e92dcb", + "sha256:e4461d0f003a0aa9be2bdd1b798a041f177189c1a0f7619fe8c95ad08d9a45d7", + "sha256:e541ec6f2ec456934fd279a3120f856cd0aedd209fc3852eca563f81738f6861", + "sha256:e546e768d08ad55b20b11dbb78a745151acbd938f8f00d0cfbabe8b0199b9880", + "sha256:ea7d4a99f3b38c37eac212dbd6ec42b7a5ec51e2c74b5d3223e43c811609e65f", + "sha256:ed4eb745efbff0a8e9587d22a84be94a5eb7d2d99c02dacf7bd0911713ed14dd", + "sha256:f8a2f084546cc59ea99fda8e070be2fd140c3092dc11524a71aa8f0f3d5a55ca", + "sha256:fcb25daa9219b4cf3a0ab24b0eb9a5cc8949ed4dc72acb8fa16b7e1681aa3c58", + "sha256:fdea4952db2793c4ad0bdccd27c1d8fdd1423a92f04598bc39425bcc2b8ee46e" ], "markers": "python_version >= '3.8'", - "version": "==0.17.1" + "version": "==0.18.0" }, "s3transfer": { "hashes": [ - "sha256:3cdb40f5cfa6966e812209d0994f2a4709b561c88e90cf00c2696d2df4e56b2e", - "sha256:d0c8bbf672d5eebbe4e57945e23b972d963f07d82f661cabf678a5c88831595b" + "sha256:5683916b4c724f799e600f41dd9e10a9ff19871bf87623cc8f491cb4f5fa0a19", + "sha256:ceb252b11bcf87080fb7850a224fb6e05c8a776bab8f2b64b7f25b969464839d" ], "markers": "python_version >= '3.8'", - "version": "==0.10.0" + "version": "==0.10.1" }, "sentry-sdk": { "hashes": [ @@ -1267,27 +1260,27 @@ }, "types-pyopenssl": { "hashes": [ - "sha256:24a255458b5b8a7fca8139cf56f2a8ad5a4f1a5f711b73a5bb9cb50dc688fab5", - "sha256:c812e5c1c35249f75ef5935708b2a997d62abf9745be222e5f94b9595472ab25" + "sha256:6e8e8bfad34924067333232c93f7fc4b369856d8bea0d5c9d1808cb290ab1972", + "sha256:7bca00cfc4e7ef9c5d2663c6a1c068c35798e59670595439f6296e7ba3d58083" ], "markers": "python_version >= '3.8'", - "version": "==24.0.0.20240130" + "version": "==24.0.0.20240311" }, "types-redis": { "hashes": [ - "sha256:2b2fa3a78f84559616242d23f86de5f4130dfd6c3b83fb2d8ce3329e503f756e", - "sha256:912de6507b631934bd225cdac310b04a58def94391003ba83939e5a10e99568d" + "sha256:6b9d68a29aba1ee400c823d8e5fe88675282eb69d7211e72fe65dbe54b33daca", + "sha256:e049bbdff0e0a1f8e701b64636811291d21bff79bf1e7850850a44055224a85f" ], "markers": "python_version >= '3.8'", - "version": "==4.6.0.20240106" + "version": "==4.6.0.20240311" }, "typing-extensions": { "hashes": [ - "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783", - "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd" + "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475", + "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb" ], "markers": "python_version >= '3.8'", - "version": "==4.9.0" + "version": "==4.10.0" }, "tzdata": { "hashes": [ @@ -1313,11 +1306,11 @@ }, "urllib3": { "hashes": [ - "sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84", - "sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e" + "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d", + "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19" ], - "markers": "python_version >= '3.6'", - "version": "==2.0.7" + "markers": "python_version >= '3.8'", + "version": "==2.2.1" }, "vine": { "hashes": [ @@ -1342,93 +1335,9 @@ "index": "pypi", "markers": "python_version >= '3.7'", "version": "==6.5.0" - }, - "wrapt": { - "hashes": [ - "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc", - "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81", - "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09", - "sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e", - "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca", - "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0", - "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb", - "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487", - "sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40", - "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c", - "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060", - "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202", - "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41", - "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9", - "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b", - "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664", - "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d", - "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362", - "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00", - "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc", - "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1", - "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267", - "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956", - "sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966", - "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1", - "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228", - "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72", - "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d", - "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292", - "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0", - "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0", - "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36", - "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c", - "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5", - "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f", - "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73", - "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b", - "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2", - "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593", - "sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39", - "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389", - "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf", - "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf", - "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89", - "sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c", - "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c", - "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f", - "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440", - "sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465", - "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136", - "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b", - "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8", - "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3", - "sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8", - "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6", - "sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e", - "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f", - "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c", - "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e", - "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8", - "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2", - "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020", - "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35", - "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d", - "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3", - "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537", - "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809", - "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d", - "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a", - "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4" - ], - "markers": "python_version >= '3.6'", - "version": "==1.16.0" } }, "develop": { - "appnope": { - "hashes": [ - "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee", - "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c" - ], - "markers": "sys_platform == 'darwin'", - "version": "==0.1.4" - }, "asgiref": { "hashes": [ "sha256:89b2ef2247e3b562a16eef663bc0e2e703ec6468e2fa8a5cd61cd449786d4f6e", @@ -1446,11 +1355,11 @@ }, "autopep8": { "hashes": [ - "sha256:067959ca4a07b24dbd5345efa8325f5f58da4298dab0dde0443d5ed765de80cb", - "sha256:2913064abd97b3419d1cc83ea71f042cb821f87e45b9c88cad5ad3c4ea87fe0c" + "sha256:1fa8964e4618929488f4ec36795c7ff12924a68b8bf01366c094fc52f770b6e7", + "sha256:2bb76888c5edbcafe6aabab3c47ba534f5a2c2d245c2eddced4a30c4b4946357" ], - "markers": "python_version >= '3.6'", - "version": "==2.0.4" + "markers": "python_version >= '3.8'", + "version": "==2.1.0" }, "backcall": { "hashes": [ @@ -1490,12 +1399,12 @@ }, "boto3": { "hashes": [ - "sha256:3626db4ba9fbb1b58c8fe923da5ed670873b3d881a102956ea19d3b69cd097cc", - "sha256:ebdd938019f3df2e7b50585353963d4553faf3fbb7b2085c440107fa6caa233b" + "sha256:b611de58ab28940a36c77d7ef9823427ebf25d5ee8277b802f9979b14e780534", + "sha256:db97f9c29f1806cf9020679be0dd5ffa2aff2670e28e0e2046f98b979be498a4" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==1.34.27" + "version": "==1.34.65" }, "boto3-stubs": { "extras": [ @@ -1503,27 +1412,27 @@ "s3" ], "hashes": [ - "sha256:a7e264794673b4fdecaed6d09575b94af5f23f1bf1bc6865366b59a5cd102b00", - "sha256:fd84678e3b1237103410690e68bc7a630641138ee26adeeecfd679894514d869" + "sha256:105da4a04dcb5e4ddc90f21ab8b24a3423ecfacb4775b8ccd3879574e5dce358", + "sha256:2afd696c8bb4daf8890ecd75a720e1733cd8b8556eaecc92c36f9b56fc6013bd" ], "markers": "python_version >= '3.8'", - "version": "==1.34.27" + "version": "==1.34.65" }, "botocore": { "hashes": [ - "sha256:9f00bd5e4698bcdd37ce6e224a896baf58d209678ed92834944b767de9061cc5", - "sha256:e175360445424b83b0e28ae20d301b99cf44ff2c9d5ab1d8670899bec05a9753" + "sha256:92560f8fbdaa9dd221212a3d3a7609219ba0bbf308c13571674c0cda9d8f39e1", + "sha256:fd7d8742007c220f897cb126b8916ca0cf3724a739d4d716aa5385d7f9d8aeb1" ], "markers": "python_version >= '3.8'", - "version": "==1.34.39" + "version": "==1.34.66" }, "botocore-stubs": { "hashes": [ - "sha256:2caf0a0d547ded49306408ea959d94aba39dd960b7de119d906c2829d149ece6", - "sha256:f1d689abbaaf4ba50aa5534417ee0be3513acab785b8f8fbd1107639ae2c0ae5" + "sha256:530ea7d66022ec6aa0ba0c5200a2aede5d30b839c632d00962f0cf4f806c6a51", + "sha256:a5aa1240c3c8ccc62d43916395943896afa81399dc5d4203127cc0ffba20f999" ], "markers": "python_version >= '3.8' and python_version < '4.0'", - "version": "==1.34.39" + "version": "==1.34.66" }, "certifi": { "hashes": [ @@ -1706,32 +1615,32 @@ }, "debugpy": { "hashes": [ - "sha256:0e90314a078d4e3f009520c8387aba8f74c3034645daa7a332a3d1bb81335756", - "sha256:1285920a3f9a75f5d1acf59ab1b9da9ae6eb9a05884cd7674f95170c9cafa4de", - "sha256:1565fd904f9571c430adca597771255cff4f92171486fced6f765dcbdfc8ec8d", - "sha256:17ad9a681aca1704c55b9a5edcb495fa8f599e4655c9872b7f9cf3dc25890d48", - "sha256:18a69f8e142a716310dd0af6d7db08992aed99e2606108732efde101e7c65e2a", - "sha256:18bca8429d6632e2d3435055416d2d88f0309cc39709f4f6355c8d412cc61f24", - "sha256:2b0e489613bc066051439df04c56777ec184b957d6810cb65f235083aef7a0dc", - "sha256:538765a41198aa88cc089295b39c7322dd598f9ef1d52eaae12145c63bf9430a", - "sha256:6516f36a2e95b3be27f171f12b641e443863f4ad5255d0fdcea6ae0be29bb912", - "sha256:676911c710e85567b17172db934a71319ed9d995104610ce23fd74a07f66e6f6", - "sha256:7515a5ba5ee9bfe956685909c5f28734c1cecd4ee813523363acfe3ca824883a", - "sha256:7bf0b4bbd841b2397b6a8de15da9227f1164f6d43ceee971c50194eaed930a9d", - "sha256:9e9571d831ad3c75b5fb6f3efcb71c471cf2a74ba84af6ac1c79ce00683bed4b", - "sha256:a5036e918c6ba8fc4c4f1fd0207d81db634431a02f0dc2ba51b12fd793c8c9de", - "sha256:a6f43a681c5025db1f1c0568069d1d1bad306a02e7c36144912b26d9c90e4724", - "sha256:ad22e1095b9977af432465c1e09132ba176e18df3834b1efcab1a449346b350b", - "sha256:bc8da67ade39d9e75608cdb8601d07e63a4e85966e0572c981f14e2cf42bcdef", - "sha256:c7e8cf91f8f3f9b5fad844dd88427b85d398bda1e2a0cd65d5a21312fcbc0c6f", - "sha256:d5be95b3946a4d7b388e45068c7b75036ac5a610f41014aee6cafcd5506423ad", - "sha256:dc8a12ac8b97ef3d6973c6679a093138c7c9b03eb685f0e253269a195f651559", - "sha256:f625e427f21423e5874139db529e18cb2966bdfcc1cb87a195538c5b34d163d1", - "sha256:f6de2e6f24f62969e0f0ef682d78c98161c4dca29e9fb05df4d2989005005502" + "sha256:016a9fcfc2c6b57f939673c874310d8581d51a0fe0858e7fac4e240c5eb743cb", + "sha256:0de56aba8249c28a300bdb0672a9b94785074eb82eb672db66c8144fff673146", + "sha256:1a9fe0829c2b854757b4fd0a338d93bc17249a3bf69ecf765c61d4c522bb92a8", + "sha256:28acbe2241222b87e255260c76741e1fbf04fdc3b6d094fcf57b6c6f75ce1242", + "sha256:3a79c6f62adef994b2dbe9fc2cc9cc3864a23575b6e387339ab739873bea53d0", + "sha256:3bda0f1e943d386cc7a0e71bfa59f4137909e2ed947fb3946c506e113000f741", + "sha256:3ebb70ba1a6524d19fa7bb122f44b74170c447d5746a503e36adc244a20ac539", + "sha256:58911e8521ca0c785ac7a0539f1e77e0ce2df753f786188f382229278b4cdf23", + "sha256:6df9aa9599eb05ca179fb0b810282255202a66835c6efb1d112d21ecb830ddd3", + "sha256:7a3afa222f6fd3d9dfecd52729bc2e12c93e22a7491405a0ecbf9e1d32d45b39", + "sha256:7eb7bd2b56ea3bedb009616d9e2f64aab8fc7000d481faec3cd26c98a964bcdd", + "sha256:92116039b5500633cc8d44ecc187abe2dfa9b90f7a82bbf81d079fcdd506bae9", + "sha256:a2e658a9630f27534e63922ebf655a6ab60c370f4d2fc5c02a5b19baf4410ace", + "sha256:bfb20cb57486c8e4793d41996652e5a6a885b4d9175dd369045dad59eaacea42", + "sha256:caad2846e21188797a1f17fc09c31b84c7c3c23baf2516fed5b40b378515bbf0", + "sha256:d915a18f0597ef685e88bb35e5d7ab968964b7befefe1aaea1eb5b2640b586c7", + "sha256:dda73bf69ea479c8577a0448f8c707691152e6c4de7f0c4dec5a4bc11dee516e", + "sha256:e38beb7992b5afd9d5244e96ad5fa9135e94993b0c551ceebf3fe1a5d9beb234", + "sha256:edcc9f58ec0fd121a25bc950d4578df47428d72e1a0d66c07403b04eb93bcf98", + "sha256:efd3fdd3f67a7e576dd869c184c5dd71d9aaa36ded271939da352880c012e703", + "sha256:f696d6be15be87aef621917585f9bb94b1dc9e8aced570db1b8a6fc14e8f9b42", + "sha256:fd97ed11a4c7f6d042d320ce03d83b20c3fb40da892f994bc041bbc415d7a099" ], "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==1.7.0" + "markers": "python_version >= '3.8'", + "version": "==1.8.1" }, "decorator": { "hashes": [ @@ -1837,11 +1746,11 @@ }, "faker": { "hashes": [ - "sha256:60e89e5c0b584e285a7db05eceba35011a241954afdab2853cb246c8a56700a2", - "sha256:b7f76bb1b2ac4cdc54442d955e36e477c387000f31ce46887fb9722a041be60b" + "sha256:5fb5aa9749d09971e04a41281ae3ceda9414f683d4810a694f8a8eebb8f9edec", + "sha256:9978025e765ba79f8bf6154c9630a9c2b7f9c9b0f175d4ad5e04b19a82a8d8d6" ], "markers": "python_version >= '3.8'", - "version": "==23.1.0" + "version": "==24.3.0" }, "filelock": { "hashes": [ @@ -1853,12 +1762,12 @@ }, "flake8": { "hashes": [ - "sha256:d5b3857f07c030bdb5bf41c7f53799571d75c4491748a3adcd47de929e34cd23", - "sha256:ffdfce58ea94c6580c77888a86506937f9a1a227dfcd15f245d694ae20a6b6e5" + "sha256:33f96621059e65eec474169085dc92bf26e7b2d47366b70be2f67ab80dc25132", + "sha256:a6dfbb75e03252917f2473ea9653f7cd799c3064e54d4c8140044c5c065f53c3" ], "index": "pypi", "markers": "python_full_version >= '3.8.1'", - "version": "==6.1.0" + "version": "==7.0.0" }, "freezegun": { "hashes": [ @@ -1879,11 +1788,11 @@ }, "identify": { "hashes": [ - "sha256:a4316013779e433d08b96e5eabb7f641e6c7942e4ab5d4c509ebd2e7a8994aed", - "sha256:ee17bc9d499899bc9eaec1ac7bf2dc9eedd480db9d88b96d123d3b64a9d34f5d" + "sha256:10a7ca245cfcd756a554a7288159f72ff105ad233c7c4b9c6f0f4d108f5f6791", + "sha256:c4de0081837b211594f8e877a6b4fad7ca32bbfc1a9307fdd61c28bfe923f13e" ], "markers": "python_version >= '3.8'", - "version": "==2.5.34" + "version": "==2.5.35" }, "idna": { "hashes": [ @@ -2045,10 +1954,10 @@ }, "mypy-boto3-s3": { "hashes": [ - "sha256:71c39ab0623cdb442d225b71c1783f6a513cff4c4a13505a2efbb2e3aff2e965", - "sha256:f9669ecd182d5bf3532f5f2dcc5e5237776afe157ad5a0b37b26d6bec5fcc432" + "sha256:2aecfbe1c00654bc21f839068218d60123366954bf43a708baa50f9543e3f205", + "sha256:2fcdf412ce2924b2f0b34db59abf06a9c0bbe4cd3361f14f0d2c1e211c0f7ddd" ], - "version": "==1.34.14" + "version": "==1.34.65" }, "mypy-extensions": { "hashes": [ @@ -2068,11 +1977,11 @@ }, "packaging": { "hashes": [ - "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5", - "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7" + "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5", + "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9" ], "markers": "python_version >= '3.7'", - "version": "==23.2" + "version": "==24.0" }, "parso": { "hashes": [ @@ -2154,11 +2063,11 @@ }, "pyflakes": { "hashes": [ - "sha256:4132f6d49cb4dae6819e5379898f2b8cce3c5f23994194c24b77d5da2e36f774", - "sha256:a0aae034c444db0071aa077972ba4768d40c830d9539fd45bf4cd3f8f6992efc" + "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f", + "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a" ], "markers": "python_version >= '3.8'", - "version": "==3.1.0" + "version": "==3.2.0" }, "pygments": { "hashes": [ @@ -2170,11 +2079,11 @@ }, "python-dateutil": { "hashes": [ - "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", - "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" + "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", + "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.8.2" + "version": "==2.9.0.post0" }, "pyyaml": { "hashes": [ @@ -2244,19 +2153,19 @@ }, "s3transfer": { "hashes": [ - "sha256:3cdb40f5cfa6966e812209d0994f2a4709b561c88e90cf00c2696d2df4e56b2e", - "sha256:d0c8bbf672d5eebbe4e57945e23b972d963f07d82f661cabf678a5c88831595b" + "sha256:5683916b4c724f799e600f41dd9e10a9ff19871bf87623cc8f491cb4f5fa0a19", + "sha256:ceb252b11bcf87080fb7850a224fb6e05c8a776bab8f2b64b7f25b969464839d" ], "markers": "python_version >= '3.8'", - "version": "==0.10.0" + "version": "==0.10.1" }, "setuptools": { "hashes": [ - "sha256:850894c4195f09c4ed30dba56213bf7c3f21d86ed6bdaafb5df5972593bfc401", - "sha256:c054629b81b946d63a9c6e732bc8b2513a7c3ea645f11d0139a2191d735c60c6" + "sha256:0ff4183f8f42cd8fa3acea16c45205521a4ef28f73c6391d8a25e92893134f2e", + "sha256:c21c49fb1042386df081cb5d86759792ab89efca84cf114889191cd09aacc80c" ], "markers": "python_version >= '3.8'", - "version": "==69.1.0" + "version": "==69.2.0" }, "six": { "hashes": [ @@ -2292,19 +2201,19 @@ }, "traitlets": { "hashes": [ - "sha256:2e5a030e6eff91737c643231bfcf04a65b0132078dad75e4936700b213652e74", - "sha256:8585105b371a04b8316a43d5ce29c098575c2e477850b62b848b964f1444527e" + "sha256:8cdd83c040dab7d1dee822678e5f5d100b514f7b72b01615b26fc5718916fdf9", + "sha256:fcdf85684a772ddeba87db2f398ce00b40ff550d1528c03c14dbf6a02003cd80" ], "markers": "python_version >= '3.8'", - "version": "==5.14.1" + "version": "==5.14.2" }, "types-awscrt": { "hashes": [ - "sha256:06a859189a329ca8e66d56ceeef2391488e39b878fbd2141f115eab4d416fe22", - "sha256:f61a120d3e98ee1387bc5ca4b93437f258cc5c2af1f55f8634ec4cee5729f178" + "sha256:61811bbf4de95248939f9276a434be93d2b95f6ccfe8aa94e56999e9778cfcc2", + "sha256:79d5bfb01f64701b6cf442e89a37d9c4dc6dbb79a46f2f611739b2418d30ecfd" ], "markers": "python_version >= '3.7' and python_version < '4.0'", - "version": "==0.20.3" + "version": "==0.20.5" }, "types-pytz": { "hashes": [ @@ -2316,18 +2225,19 @@ }, "types-pyyaml": { "hashes": [ - "sha256:334373d392fde0fdf95af5c3f1661885fa10c52167b14593eb856289e1855062", - "sha256:c05bc6c158facb0676674b7f11fe3960db4f389718e19e62bd2b84d6205cfd24" + "sha256:a9e0f0f88dc835739b0c1ca51ee90d04ca2a897a71af79de9aec5f38cb0a5342", + "sha256:b845b06a1c7e54b8e5b4c683043de0d9caf205e7434b3edc678ff2411979b8f6" ], - "version": "==6.0.12.12" + "markers": "python_version >= '3.8'", + "version": "==6.0.12.20240311" }, "types-requests": { "hashes": [ - "sha256:03a28ce1d7cd54199148e043b2079cdded22d6795d19a2c2a6791a4b2b5e2eb5", - "sha256:9592a9a4cb92d6d75d9b491a41477272b710e021011a2a3061157e2fb1f1a5d1" + "sha256:47872893d65a38e282ee9f277a4ee50d1b28bd592040df7d1fdaffdf3779937d", + "sha256:b1c1b66abfb7fa79aae09097a811c4aa97130eb8831c60e47aee4ca344731ca5" ], "markers": "python_version >= '3.8'", - "version": "==2.31.0.20240125" + "version": "==2.31.0.20240311" }, "types-s3transfer": { "hashes": [ @@ -2339,27 +2249,27 @@ }, "typing-extensions": { "hashes": [ - "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783", - "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd" + "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475", + "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb" ], "markers": "python_version >= '3.8'", - "version": "==4.9.0" + "version": "==4.10.0" }, "urllib3": { "hashes": [ - "sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84", - "sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e" + "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d", + "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19" ], - "markers": "python_version >= '3.6'", - "version": "==2.0.7" + "markers": "python_version >= '3.8'", + "version": "==2.2.1" }, "virtualenv": { "hashes": [ - "sha256:4238949c5ffe6876362d9c0180fc6c3a824a7b12b80604eeb8085f2ed7460de3", - "sha256:bf51c0d9c7dd63ea8e44086fa1e4fb1093a31e963b86959257378aef020e1f1b" + "sha256:961c026ac520bac5f69acb8ea063e8a4f071bcc9457b9c1f28f6b085c511583a", + "sha256:e08e13ecdca7a0bd53798f356d5831434afa5b07b93f0abdf0797b7a06ffe197" ], "markers": "python_version >= '3.7'", - "version": "==20.25.0" + "version": "==20.25.1" }, "watchdog": { "hashes": [ @@ -2650,11 +2560,11 @@ }, "packaging": { "hashes": [ - "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5", - "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7" + "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5", + "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9" ], "markers": "python_version >= '3.7'", - "version": "==23.2" + "version": "==24.0" }, "pygments": { "hashes": [ @@ -2755,11 +2665,11 @@ }, "urllib3": { "hashes": [ - "sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84", - "sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e" + "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d", + "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19" ], - "markers": "python_version >= '3.6'", - "version": "==2.0.7" + "markers": "python_version >= '3.8'", + "version": "==2.2.1" } } } diff --git a/README.md b/README.md index b1950e85ac..ce601d095b 100644 --- a/README.md +++ b/README.md @@ -36,10 +36,11 @@ You can find the docs at https://care-be-docs.coronasafe.network ### Staging Deployments -Staging instances for testing are automatically deployed on every commit to the `master` branch. The staging instances -are available at: +Dev and staging instances for testing are automatically deployed on every commit to the `develop` and `staging` branches. +The staging instances are available at: - https://careapi.ohc.network +- https://careapi-staging.ohc.network ### Self hosting diff --git a/care/facility/api/serializers/patient_consultation.py b/care/facility/api/serializers/patient_consultation.py index 6b992b414c..f67b1cc49e 100644 --- a/care/facility/api/serializers/patient_consultation.py +++ b/care/facility/api/serializers/patient_consultation.py @@ -24,6 +24,7 @@ Facility, PatientRegistration, Prescription, + PrescriptionDosageType, PrescriptionType, ) from care.facility.models.asset import AssetLocation @@ -151,17 +152,20 @@ class PatientConsultationSerializer(serializers.ModelSerializer): medico_legal_case = serializers.BooleanField(default=False, required=False) def get_discharge_prescription(self, consultation): - return Prescription.objects.filter( - consultation=consultation, - prescription_type=PrescriptionType.DISCHARGE.value, - is_prn=False, - ).values() + return ( + Prescription.objects.filter( + consultation=consultation, + prescription_type=PrescriptionType.DISCHARGE.value, + ) + .exclude(dosage_type=PrescriptionDosageType.PRN.value) + .values() + ) def get_discharge_prn_prescription(self, consultation): return Prescription.objects.filter( consultation=consultation, prescription_type=PrescriptionType.DISCHARGE.value, - is_prn=True, + dosage_type=PrescriptionDosageType.PRN.value, ).values() class Meta: @@ -641,17 +645,20 @@ class PatientConsultationDischargeSerializer(serializers.ModelSerializer): ) def get_discharge_prescription(self, consultation): - return Prescription.objects.filter( - consultation=consultation, - prescription_type=PrescriptionType.DISCHARGE.value, - is_prn=False, - ).values() + return ( + Prescription.objects.filter( + consultation=consultation, + prescription_type=PrescriptionType.DISCHARGE.value, + ) + .exclude(dosage_type=PrescriptionDosageType.PRN.value) + .values() + ) def get_discharge_prn_prescription(self, consultation): return Prescription.objects.filter( consultation=consultation, prescription_type=PrescriptionType.DISCHARGE.value, - is_prn=True, + dosage_type=PrescriptionDosageType.PRN.value, ).values() class Meta: diff --git a/care/facility/api/serializers/prescription.py b/care/facility/api/serializers/prescription.py index 49ad3d9913..ed68e772a7 100644 --- a/care/facility/api/serializers/prescription.py +++ b/care/facility/api/serializers/prescription.py @@ -2,7 +2,12 @@ from django.utils import timezone from rest_framework import serializers -from care.facility.models import MedibaseMedicine, MedicineAdministration, Prescription +from care.facility.models import ( + MedibaseMedicine, + MedicineAdministration, + Prescription, + PrescriptionDosageType, +) from care.users.api.serializers.user import UserBaseMinimumSerializer @@ -19,23 +24,60 @@ class Meta: ) +class MedicineAdministrationSerializer(serializers.ModelSerializer): + id = serializers.UUIDField(source="external_id", read_only=True) + + administered_by = UserBaseMinimumSerializer(read_only=True) + archived_by = UserBaseMinimumSerializer(read_only=True) + + def validate_administered_date(self, value): + if value > timezone.now(): + raise serializers.ValidationError( + "Administered Date cannot be in the future." + ) + if self.context["prescription"].created_date > value: + raise serializers.ValidationError( + "Administered Date cannot be before Prescription Date." + ) + return value + + def validate(self, attrs): + if ( + not attrs.get("dosage") + and self.context["prescription"].dosage_type + == PrescriptionDosageType.TITRATED + ): + raise serializers.ValidationError( + {"dosage": "Dosage is required for titrated prescriptions."} + ) + elif ( + self.context["prescription"].dosage_type != PrescriptionDosageType.TITRATED + ): + attrs.pop("dosage", None) + + return super().validate(attrs) + + class Meta: + model = MedicineAdministration + exclude = ("deleted",) + read_only_fields = ( + "external_id", + "administered_by", + "archived_by", + "archived_on", + "created_date", + "modified_date", + "prescription", + ) + + class PrescriptionSerializer(serializers.ModelSerializer): id = serializers.UUIDField(source="external_id", read_only=True) prescribed_by = UserBaseMinimumSerializer(read_only=True) - last_administered_on = serializers.SerializerMethodField() + last_administration = MedicineAdministrationSerializer(read_only=True) medicine_object = MedibaseMedicineSerializer(read_only=True, source="medicine") medicine = serializers.UUIDField(write_only=True) - def get_last_administered_on(self, obj): - last_administration = ( - MedicineAdministration.objects.filter(prescription=obj) - .order_by("-administered_date") - .first() - ) - if last_administration: - return last_administration.administered_date - return None - class Meta: model = Prescription exclude = ( @@ -75,47 +117,36 @@ def validate(self, attrs): } ) - if attrs.get("is_prn"): + if not attrs.get("base_dosage"): + raise serializers.ValidationError( + {"base_dosage": "Base dosage is required."} + ) + + if attrs.get("dosage_type") == PrescriptionDosageType.PRN: if not attrs.get("indicator"): raise serializers.ValidationError( {"indicator": "Indicator should be set for PRN prescriptions."} ) + attrs.pop("frequency", None) + attrs.pop("days", None) else: if not attrs.get("frequency"): raise serializers.ValidationError( {"frequency": "Frequency should be set for prescriptions."} ) + attrs.pop("indicator", None) + attrs.pop("max_dosage", None) + attrs.pop("min_hours_between_doses", None) + + if attrs.get("dosage_type") == PrescriptionDosageType.TITRATED: + if not attrs.get("target_dosage"): + raise serializers.ValidationError( + { + "target_dosage": "Target dosage should be set for titrated prescriptions." + } + ) + else: + attrs.pop("target_dosage", None) + return super().validate(attrs) # TODO: Ensure that this medicine is not already prescribed to the same patient and is currently active. - - -class MedicineAdministrationSerializer(serializers.ModelSerializer): - id = serializers.UUIDField(source="external_id", read_only=True) - - administered_by = UserBaseMinimumSerializer(read_only=True) - prescription = PrescriptionSerializer(read_only=True) - archived_by = UserBaseMinimumSerializer(read_only=True) - - def validate_administered_date(self, value): - if value > timezone.now(): - raise serializers.ValidationError( - "Administered Date cannot be in the future." - ) - if self.context["prescription"].created_date > value: - raise serializers.ValidationError( - "Administered Date cannot be before Prescription Date." - ) - return value - - class Meta: - model = MedicineAdministration - exclude = ("deleted",) - read_only_fields = ( - "external_id", - "administered_by", - "archived_by", - "archived_on", - "created_date", - "modified_date", - "prescription", - ) diff --git a/care/facility/api/serializers/resources.py b/care/facility/api/serializers/resources.py index e94cc8f05f..efc5df3294 100644 --- a/care/facility/api/serializers/resources.py +++ b/care/facility/api/serializers/resources.py @@ -13,6 +13,7 @@ ) from care.facility.models.resources import RESOURCE_SUB_CATEGORY_CHOICES from care.users.api.serializers.user import UserBaseMinimumSerializer +from care.utils.serializer.external_id_field import ExternalIdSerializerField from config.serializers import ChoiceField @@ -61,14 +62,16 @@ class ResourceRequestSerializer(serializers.ModelSerializer): category = ChoiceField(choices=RESOURCE_CATEGORY_CHOICES) sub_category = ChoiceField(choices=RESOURCE_SUB_CATEGORY_CHOICES) - origin_facility = serializers.UUIDField( - source="origin_facility.external_id", allow_null=False, required=True + origin_facility = ExternalIdSerializerField( + queryset=Facility.objects.all(), required=True ) - approving_facility = serializers.UUIDField( - source="approving_facility.external_id", allow_null=False, required=True + + approving_facility = ExternalIdSerializerField( + queryset=Facility.objects.all(), required=True ) - assigned_facility = serializers.UUIDField( - source="assigned_facility.external_id", allow_null=True, required=False + + assigned_facility = ExternalIdSerializerField( + queryset=Facility.objects.all(), required=False ) assigned_to_object = UserBaseMinimumSerializer(source="assigned_to", read_only=True) @@ -115,24 +118,6 @@ def update(self, instance, validated_data): if "origin_facility" in validated_data: validated_data.pop("origin_facility") - if "approving_facility" in validated_data: - approving_facility_external_id = validated_data.pop("approving_facility")[ - "external_id" - ] - if approving_facility_external_id: - validated_data["approving_facility_id"] = Facility.objects.get( - external_id=approving_facility_external_id - ).id - - if "assigned_facility" in validated_data: - assigned_facility_external_id = validated_data.pop("assigned_facility")[ - "external_id" - ] - if assigned_facility_external_id: - validated_data["assigned_facility_id"] = Facility.objects.get( - external_id=assigned_facility_external_id - ).id - instance.last_edited_by = self.context["request"].user new_instance = super().update(instance, validated_data) @@ -144,29 +129,6 @@ def create(self, validated_data): if "status" in validated_data: validated_data.pop("status") - origin_facility_external_id = validated_data.pop("origin_facility")[ - "external_id" - ] - validated_data["origin_facility_id"] = Facility.objects.get( - external_id=origin_facility_external_id - ).id - - request_approving_facility_external_id = validated_data.pop( - "approving_facility" - )["external_id"] - validated_data["approving_facility_id"] = Facility.objects.get( - external_id=request_approving_facility_external_id - ).id - - if "assigned_facility" in validated_data: - assigned_facility_external_id = validated_data.pop("assigned_facility")[ - "external_id" - ] - if assigned_facility_external_id: - validated_data["assigned_facility_id"] = Facility.objects.get( - external_id=assigned_facility_external_id - ).id - validated_data["created_by"] = self.context["request"].user validated_data["last_edited_by"] = self.context["request"].user diff --git a/care/facility/api/viewsets/prescription.py b/care/facility/api/viewsets/prescription.py index 779287ef7d..537faae98c 100644 --- a/care/facility/api/viewsets/prescription.py +++ b/care/facility/api/viewsets/prescription.py @@ -17,11 +17,13 @@ from care.facility.models import ( MedicineAdministration, Prescription, + PrescriptionDosageType, PrescriptionType, generate_choices, ) from care.facility.static_data.medibase import MedibaseMedicine from care.utils.filters.choicefilter import CareChoiceFilter +from care.utils.filters.multiselect import MultiSelectFilter from care.utils.queryset.consultation import get_consultation_queryset from care.utils.static_data.helpers import query_builder, token_escaper @@ -34,6 +36,9 @@ def inverse_choices(choices): inverse_prescription_type = inverse_choices(generate_choices(PrescriptionType)) +inverse_prescription_dosage_type = inverse_choices( + generate_choices(PrescriptionDosageType) +) class MedicineAdminstrationFilter(filters.FilterSet): @@ -83,7 +88,7 @@ def archive(self, request, *args, **kwargs): class ConsultationPrescriptionFilter(filters.FilterSet): - is_prn = filters.BooleanFilter() + dosage_type = MultiSelectFilter() prescription_type = CareChoiceFilter(choice_dict=inverse_prescription_type) discontinued = filters.BooleanFilter() diff --git a/care/facility/migrations/0415_alter_dailyround_consciousness_level_and_more.py b/care/facility/migrations/0415_alter_dailyround_consciousness_level_and_more.py new file mode 100644 index 0000000000..8b2f523587 --- /dev/null +++ b/care/facility/migrations/0415_alter_dailyround_consciousness_level_and_more.py @@ -0,0 +1,225 @@ +# Generated by Django 4.2.8 on 2024-01-20 10:02 + +from django.db import migrations, models +from django.db.models import Q + + +def forwards_func(apps, schema_editor): + DailyRound = apps.get_model("facility", "DailyRound") + DailyRound.objects.filter(consciousness_level=0).update(consciousness_level=None) + DailyRound.objects.filter(left_pupil_light_reaction=0).update( + left_pupil_light_reaction=None + ) + DailyRound.objects.filter(right_pupil_light_reaction=0).update( + right_pupil_light_reaction=None + ) + DailyRound.objects.filter(limb_response_upper_extremity_left=0).update( + limb_response_upper_extremity_left=None + ) + DailyRound.objects.filter(limb_response_lower_extremity_right=0).update( + limb_response_lower_extremity_right=None + ) + DailyRound.objects.filter(rhythm=0).update(rhythm=None) + DailyRound.objects.filter(ventilator_mode=0).update(ventilator_mode=None) + DailyRound.objects.filter(ventilator_interface=0).update(ventilator_interface=None) + DailyRound.objects.filter(ventilator_oxygen_modality=0).update( + ventilator_oxygen_modality=None + ) + DailyRound.objects.filter(insulin_intake_frequency=0).update( + insulin_intake_frequency=None + ) + DailyRound.objects.filter(Q(bp__systolic__lt=0) | Q(bp__diastolic__lt=0)).update( + bp={"systolic": None, "diastolic": None} + ) + + +class Migration(migrations.Migration): + dependencies = [ + ("facility", "0414_remove_bed_old_name"), + ] + + operations = [ + migrations.AlterField( + model_name="dailyround", + name="consciousness_level", + field=models.IntegerField( + choices=[ + (0, "UNKNOWN"), + (5, "ALERT"), + (10, "RESPONDS_TO_VOICE"), + (15, "RESPONDS_TO_PAIN"), + (20, "UNRESPONSIVE"), + (25, "AGITATED_OR_CONFUSED"), + (30, "ONSET_OF_AGITATION_AND_CONFUSION"), + ], + default=None, + null=True, + ), + ), + migrations.AlterField( + model_name="dailyround", + name="left_pupil_light_reaction", + field=models.IntegerField( + choices=[ + (0, "UNKNOWN"), + (5, "BRISK"), + (10, "SLUGGISH"), + (15, "FIXED"), + (20, "CANNOT_BE_ASSESSED"), + ], + default=None, + null=True, + ), + ), + migrations.AlterField( + model_name="dailyround", + name="limb_response_lower_extremity_left", + field=models.IntegerField( + choices=[ + (0, "UNKNOWN"), + (5, "STRONG"), + (10, "MODERATE"), + (15, "WEAK"), + (20, "FLEXION"), + (25, "EXTENSION"), + (30, "NONE"), + ], + default=None, + null=True, + ), + ), + migrations.AlterField( + model_name="dailyround", + name="limb_response_lower_extremity_right", + field=models.IntegerField( + choices=[ + (0, "UNKNOWN"), + (5, "STRONG"), + (10, "MODERATE"), + (15, "WEAK"), + (20, "FLEXION"), + (25, "EXTENSION"), + (30, "NONE"), + ], + default=None, + null=True, + ), + ), + migrations.AlterField( + model_name="dailyround", + name="limb_response_upper_extremity_left", + field=models.IntegerField( + choices=[ + (0, "UNKNOWN"), + (5, "STRONG"), + (10, "MODERATE"), + (15, "WEAK"), + (20, "FLEXION"), + (25, "EXTENSION"), + (30, "NONE"), + ], + default=None, + null=True, + ), + ), + migrations.AlterField( + model_name="dailyround", + name="limb_response_upper_extremity_right", + field=models.IntegerField( + choices=[ + (0, "UNKNOWN"), + (5, "STRONG"), + (10, "MODERATE"), + (15, "WEAK"), + (20, "FLEXION"), + (25, "EXTENSION"), + (30, "NONE"), + ], + default=None, + null=True, + ), + ), + migrations.AlterField( + model_name="dailyround", + name="rhythm", + field=models.IntegerField( + choices=[(0, "UNKNOWN"), (5, "REGULAR"), (10, "IRREGULAR")], + default=None, + null=True, + ), + ), + migrations.AlterField( + model_name="dailyround", + name="right_pupil_light_reaction", + field=models.IntegerField( + choices=[ + (0, "UNKNOWN"), + (5, "BRISK"), + (10, "SLUGGISH"), + (15, "FIXED"), + (20, "CANNOT_BE_ASSESSED"), + ], + default=None, + null=True, + ), + ), + migrations.AlterField( + model_name="dailyround", + name="ventilator_interface", + field=models.IntegerField( + choices=[ + (0, "UNKNOWN"), + (5, "INVASIVE"), + (10, "NON_INVASIVE"), + (15, "OXYGEN_SUPPORT"), + ], + default=None, + null=True, + ), + ), + migrations.AlterField( + model_name="dailyround", + name="ventilator_mode", + field=models.IntegerField( + choices=[ + (0, "UNKNOWN"), + (5, "VCV"), + (10, "PCV"), + (15, "PRVC"), + (20, "APRV"), + (25, "VC_SIMV"), + (30, "PC_SIMV"), + (40, "PRVC_SIMV"), + (45, "ASV"), + (50, "PSV"), + ], + default=None, + null=True, + ), + ), + migrations.AlterField( + model_name="dailyround", + name="ventilator_oxygen_modality", + field=models.IntegerField( + choices=[ + (0, "UNKNOWN"), + (5, "NASAL_PRONGS"), + (10, "SIMPLE_FACE_MASK"), + (15, "NON_REBREATHING_MASK"), + (20, "HIGH_FLOW_NASAL_CANNULA"), + ], + default=None, + null=True, + ), + ), + migrations.AlterField( + model_name="dailyround", + name="insulin_intake_frequency", + field=models.IntegerField( + choices=[(0, "UNKNOWN"), (5, "OD"), (10, "BD"), (15, "TD")], + default=None, + null=True, + ), + ), + migrations.RunPython(forwards_func, migrations.RunPython.noop), + ] diff --git a/care/facility/migrations/0415_consultationclinician_and_more.py b/care/facility/migrations/0415_consultationclinician_and_more.py new file mode 100644 index 0000000000..0165cef66b --- /dev/null +++ b/care/facility/migrations/0415_consultationclinician_and_more.py @@ -0,0 +1,52 @@ +# Generated by Django 4.2.8 on 2024-02-23 06:38 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ("facility", "0414_remove_bed_old_name"), + ] + + operations = [ + migrations.CreateModel( + name="ConsultationClinician", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "clinician", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "consultation", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + to="facility.patientconsultation", + ), + ), + ], + ), + migrations.AddField( + model_name="patientconsultation", + name="assigned_clinicians", + field=models.ManyToManyField( + related_name="patient_assigned_clinician", + through="facility.ConsultationClinician", + to=settings.AUTH_USER_MODEL, + ), + ), + ] diff --git a/care/facility/migrations/0415_rename_dosage_prescription_base_dosage_and_more.py b/care/facility/migrations/0415_rename_dosage_prescription_base_dosage_and_more.py new file mode 100644 index 0000000000..1c9638643b --- /dev/null +++ b/care/facility/migrations/0415_rename_dosage_prescription_base_dosage_and_more.py @@ -0,0 +1,88 @@ +# Generated by Django 4.2.8 on 2024-02-10 10:05 + +from django.db import migrations, models + +import care.utils.models.validators + + +class Migration(migrations.Migration): + dependencies = [ + ("facility", "0414_remove_bed_old_name"), + ] + + def set_prn_prescriptions_dosage_type(apps, schema_editor): + Prescription = apps.get_model("facility", "Prescription") + Prescription.objects.filter(is_prn=True).update(dosage_type="PRN") + + def reverse_set_prn_prescriptions_dosage_type(apps, schema_editor): + Prescription = apps.get_model("facility", "Prescription") + Prescription.objects.filter(dosage_type="PRN").update(is_prn=True) + + operations = [ + migrations.RenameField( + model_name="prescription", + old_name="dosage", + new_name="base_dosage", + ), + migrations.AddField( + model_name="prescription", + name="dosage_type", + field=models.CharField( + choices=[ + ("REGULAR", "REGULAR"), + ("TITRATED", "TITRATED"), + ("PRN", "PRN"), + ], + default="REGULAR", + max_length=100, + ), + ), + migrations.RunPython( + set_prn_prescriptions_dosage_type, reverse_set_prn_prescriptions_dosage_type + ), + migrations.RemoveField( + model_name="prescription", + name="is_prn", + ), + migrations.AddField( + model_name="medicineadministration", + name="dosage", + field=models.CharField( + blank=True, + max_length=100, + null=True, + validators=[ + care.utils.models.validators.DenominationValidator( + allow_floats=True, + max_amount=5000, + min_amount=0.0001, + precision=4, + units={"mg", "ml", "drop(s)", "ampule(s)", "g", "tsp"}, + ) + ], + ), + ), + migrations.AddField( + model_name="prescription", + name="instruction_on_titration", + field=models.TextField(blank=True, null=True), + ), + migrations.AddField( + model_name="prescription", + name="target_dosage", + field=models.CharField( + blank=True, + max_length=100, + null=True, + validators=[ + care.utils.models.validators.DenominationValidator( + allow_floats=True, + max_amount=5000, + min_amount=0.0001, + precision=4, + units={"mg", "ml", "drop(s)", "ampule(s)", "g", "tsp"}, + ) + ], + ), + ), + ] diff --git a/care/facility/migrations/0421_merge_20240318_1434.py b/care/facility/migrations/0421_merge_20240318_1434.py new file mode 100644 index 0000000000..454921ccb3 --- /dev/null +++ b/care/facility/migrations/0421_merge_20240318_1434.py @@ -0,0 +1,14 @@ +# Generated by Django 4.2.10 on 2024-03-18 09:04 + +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("facility", "0415_alter_dailyround_consciousness_level_and_more"), + ("facility", "0415_consultationclinician_and_more"), + ("facility", "0415_rename_dosage_prescription_base_dosage_and_more"), + ("facility", "0420_migrate_shifting_facility_type"), + ] + + operations = [] diff --git a/care/facility/models/daily_round.py b/care/facility/models/daily_round.py index 7140623e37..b9ae08eedf 100644 --- a/care/facility/models/daily_round.py +++ b/care/facility/models/daily_round.py @@ -191,7 +191,7 @@ class InsulinIntakeFrequencyType(enum.Enum): # Critical Care Attributes consciousness_level = models.IntegerField( - choices=ConsciousnessChoice, default=ConsciousnessType.UNKNOWN.value + choices=ConsciousnessChoice, default=None, null=True ) consciousness_level_detail = models.TextField(default=None, null=True, blank=True) @@ -205,7 +205,7 @@ class InsulinIntakeFrequencyType(enum.Enum): ) left_pupil_size_detail = models.TextField(default=None, null=True, blank=True) left_pupil_light_reaction = models.IntegerField( - choices=PupilReactionChoice, default=PupilReactionType.UNKNOWN.value + choices=PupilReactionChoice, default=None, null=True ) left_pupil_light_reaction_detail = models.TextField( default=None, null=True, blank=True @@ -218,7 +218,7 @@ class InsulinIntakeFrequencyType(enum.Enum): ) right_pupil_size_detail = models.TextField(default=None, null=True, blank=True) right_pupil_light_reaction = models.IntegerField( - choices=PupilReactionChoice, default=PupilReactionType.UNKNOWN.value + choices=PupilReactionChoice, default=None, null=True ) right_pupil_light_reaction_detail = models.TextField( default=None, null=True, blank=True @@ -244,16 +244,16 @@ class InsulinIntakeFrequencyType(enum.Enum): validators=[MinValueValidator(3), MaxValueValidator(15)], ) limb_response_upper_extremity_right = models.IntegerField( - choices=LimbResponseChoice, default=LimbResponseType.UNKNOWN.value + choices=LimbResponseChoice, default=None, null=True ) limb_response_upper_extremity_left = models.IntegerField( - choices=LimbResponseChoice, default=LimbResponseType.UNKNOWN.value + choices=LimbResponseChoice, default=None, null=True ) limb_response_lower_extremity_left = models.IntegerField( - choices=LimbResponseChoice, default=LimbResponseType.UNKNOWN.value + choices=LimbResponseChoice, default=None, null=True ) limb_response_lower_extremity_right = models.IntegerField( - choices=LimbResponseChoice, default=LimbResponseType.UNKNOWN.value + choices=LimbResponseChoice, default=None, null=True ) bp = JSONField(default=dict, validators=[JSONFieldSchemaValidator(BLOOD_PRESSURE)]) pulse = models.IntegerField( @@ -266,14 +266,15 @@ class InsulinIntakeFrequencyType(enum.Enum): null=True, validators=[MinValueValidator(0), MaxValueValidator(150)], ) - rhythm = models.IntegerField(choices=RythmnChoice, default=RythmnType.UNKNOWN.value) + rhythm = models.IntegerField(choices=RythmnChoice, default=None, null=True) rhythm_detail = models.TextField(default=None, null=True, blank=True) ventilator_interface = models.IntegerField( choices=VentilatorInterfaceChoice, - default=VentilatorInterfaceType.UNKNOWN.value, + default=None, + null=True, ) ventilator_mode = models.IntegerField( - choices=VentilatorModeChoice, default=VentilatorModeType.UNKNOWN.value + choices=VentilatorModeChoice, default=None, null=True ) ventilator_peep = models.DecimalField( decimal_places=2, @@ -309,8 +310,7 @@ class InsulinIntakeFrequencyType(enum.Enum): validators=[MinValueValidator(0), MaxValueValidator(1000)], ) ventilator_oxygen_modality = models.IntegerField( - choices=VentilatorOxygenModalityChoice, - default=VentilatorOxygenModalityType.UNKNOWN.value, + choices=VentilatorOxygenModalityChoice, default=None, null=True ) ventilator_oxygen_modality_oxygen_rate = models.IntegerField( default=None, @@ -417,7 +417,8 @@ class InsulinIntakeFrequencyType(enum.Enum): ) insulin_intake_frequency = models.IntegerField( choices=InsulinIntakeFrequencyChoice, - default=InsulinIntakeFrequencyType.UNKNOWN.value, + default=None, + null=True, ) infusions = JSONField( default=list, validators=[JSONFieldSchemaValidator(INFUSIONS)] @@ -504,18 +505,27 @@ def set_push_score(item): def save(self, *args, **kwargs): # Calculate all automated columns and populate them - self.glasgow_total_calculated = ( - self.cztn(self.glasgow_eye_open) - + self.cztn(self.glasgow_motor_response) - + self.cztn(self.glasgow_verbal_response) - ) - self.total_intake_calculated = sum([x["quantity"] for x in self.infusions]) - self.total_intake_calculated += sum([x["quantity"] for x in self.iv_fluids]) - self.total_intake_calculated += sum([x["quantity"] for x in self.feeds]) - - self.total_output_calculated = sum([x["quantity"] for x in self.output]) - - # self.pressure_sore = self.update_pressure_sore() + if ( + self.glasgow_eye_open is not None + and self.glasgow_motor_response is not None + and self.glasgow_verbal_response is not None + ): + self.glasgow_total_calculated = ( + self.cztn(self.glasgow_eye_open) + + self.cztn(self.glasgow_motor_response) + + self.cztn(self.glasgow_verbal_response) + ) + if ( + self.infusions is not None + and self.iv_fluids is not None + and self.feeds is not None + ): + self.total_intake_calculated = sum([x["quantity"] for x in self.infusions]) + self.total_intake_calculated += sum([x["quantity"] for x in self.iv_fluids]) + self.total_intake_calculated += sum([x["quantity"] for x in self.feeds]) + + if self.output is not None: + self.total_output_calculated = sum([x["quantity"] for x in self.output]) super(DailyRound, self).save(*args, **kwargs) diff --git a/care/facility/models/file_upload.py b/care/facility/models/file_upload.py index 584cda5e05..4aec7366ca 100644 --- a/care/facility/models/file_upload.py +++ b/care/facility/models/file_upload.py @@ -38,8 +38,10 @@ class FileCategory(enum.Enum): FileTypeChoices = [(e.value, e.name) for e in FileType] FileCategoryChoices = [(e.value, e.name) for e in FileCategory] - name = models.CharField(max_length=2000) - internal_name = models.CharField(max_length=2000) + name = models.CharField(max_length=2000) # name should not contain file extension + internal_name = models.CharField( + max_length=2000 + ) # internal_name should include file extension associating_id = models.CharField(max_length=100, blank=False, null=False) upload_completed = models.BooleanField(default=False) is_archived = models.BooleanField(default=False) diff --git a/care/facility/models/patient_consultation.py b/care/facility/models/patient_consultation.py index 5c9d8802f3..91e18dd351 100644 --- a/care/facility/models/patient_consultation.py +++ b/care/facility/models/patient_consultation.py @@ -168,6 +168,12 @@ class PatientConsultation(PatientBaseModel, ConsultationRelatedPermissionMixin): related_name="patient_assigned_to", ) + assigned_clinicians = models.ManyToManyField( + User, + related_name="patient_assigned_clinician", + through="ConsultationClinician", + ) + medico_legal_case = models.BooleanField(default=False) deprecated_verified_by = models.TextField( @@ -343,3 +349,14 @@ def has_object_email_discharge_summary_permission(self, request): def has_object_generate_discharge_summary_permission(self, request): return self.has_object_read_permission(request) + + +class ConsultationClinician(models.Model): + consultation = models.ForeignKey( + PatientConsultation, + on_delete=models.PROTECT, + ) + clinician = models.ForeignKey( + User, + on_delete=models.PROTECT, + ) diff --git a/care/facility/models/prescription.py b/care/facility/models/prescription.py index 5c34ab0e18..3c9bdedba2 100644 --- a/care/facility/models/prescription.py +++ b/care/facility/models/prescription.py @@ -42,6 +42,12 @@ class PrescriptionType(enum.Enum): REGULAR = "REGULAR" +class PrescriptionDosageType(models.TextChoices): + REGULAR = "REGULAR", "REGULAR" + TITRATED = "TITRATED", "TITRATED" + PRN = "PRN", "PRN" + + def generate_choices(enum_class): return [(tag.name, tag.value) for tag in enum_class] @@ -101,11 +107,20 @@ class Prescription(BaseModel, ConsultationRelatedPermissionMixin): blank=True, null=True, ) - dosage = models.CharField( + base_dosage = models.CharField( max_length=100, blank=True, null=True, validators=[dosage_validator] ) + dosage_type = models.CharField( + max_length=100, + choices=PrescriptionDosageType.choices, + default=PrescriptionDosageType.REGULAR.value, + ) - is_prn = models.BooleanField(default=False) + # titrated fields + target_dosage = models.CharField( + max_length=100, blank=True, null=True, validators=[dosage_validator] + ) + instruction_on_titration = models.TextField(blank=True, null=True) # non prn fields frequency = models.CharField( @@ -151,6 +166,10 @@ def save(self, *args, **kwargs) -> None: def medicine_name(self): return str(self.medicine) if self.medicine else self.medicine_old + @property + def last_administration(self): + return self.administrations.order_by("-administered_date").first() + def has_object_write_permission(self, request): return ConsultationRelatedPermissionMixin.has_write_permission(request) @@ -164,6 +183,9 @@ class MedicineAdministration(BaseModel, ConsultationRelatedPermissionMixin): on_delete=models.PROTECT, related_name="administrations", ) + dosage = models.CharField( + max_length=100, blank=True, null=True, validators=[dosage_validator] + ) notes = models.TextField(default="", blank=True) administered_by = models.ForeignKey( "users.User", diff --git a/care/facility/models/tests/test_resource.py b/care/facility/models/tests/test_resource.py new file mode 100644 index 0000000000..51e349a75b --- /dev/null +++ b/care/facility/models/tests/test_resource.py @@ -0,0 +1,36 @@ +from rest_framework import status +from rest_framework.test import APITestCase + +from care.utils.tests.test_utils import TestUtils + + +class ResourceTransferTest(TestUtils, APITestCase): + @classmethod + def setUpTestData(cls): + cls.state = cls.create_state() + cls.district = cls.create_district(cls.state) + cls.local_body = cls.create_local_body(cls.district) + cls.super_user = cls.create_super_user("su", cls.district) + cls.facility = cls.create_facility(cls.super_user, cls.district, cls.local_body) + cls.user = cls.create_user("staff1", cls.district, home_facility=cls.facility) + cls.patient = cls.create_patient(cls.district, cls.facility) + + def test_with_invalid_facilityid_input(self): + dist_admin = self.create_user("dist_admin", self.district, user_type=30) + sample_data = { + "approving_facility": self.facility.external_id, + "category": "OXYGEN", + "emergency": "false", + "origin_facility": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", # invalid facility id + "reason": "adadasa", + "refering_facility_contact_name": "rash", + "refering_facility_contact_number": "+918888888889", + "requested_quantity": "10", + "status": "PENDING", + "sub_category": 110, + "title": "a", + } + self.client.force_authenticate(user=dist_admin) + response = self.client.post("/api/v1/resource/", sample_data, format="json") + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertIn("origin_facility", response.data) diff --git a/care/facility/tests/test_medicine_api.py b/care/facility/tests/test_medicine_api.py index a34e73c43b..b683a6c22e 100644 --- a/care/facility/tests/test_medicine_api.py +++ b/care/facility/tests/test_medicine_api.py @@ -2,7 +2,7 @@ from rest_framework import status from rest_framework.test import APITestCase -from care.facility.models import MedibaseMedicine, Prescription +from care.facility.models import MedibaseMedicine, Prescription, PrescriptionDosageType from care.utils.tests.test_utils import TestUtils @@ -29,94 +29,96 @@ def prescription_data(self, **kwargs): data = { "medicine": self.medicine1, "prescription_type": "REGULAR", - "dosage": "1 mg", + "base_dosage": "1 mg", "frequency": "OD", - "is_prn": False, + "dosage_type": kwargs.get( + "dosage_type", PrescriptionDosageType.REGULAR.value + ), } return {**data, **kwargs} def test_invalid_dosage(self): - data = self.prescription_data(dosage="abc") + data = self.prescription_data(base_dosage="abc") res = self.client.post( f"/api/v1/consultation/{self.consultation.external_id}/prescriptions/", data, ) self.assertEqual(res.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual( - res.json()["dosage"][0], + res.json()["base_dosage"][0], "Invalid Input, must be in the format: ", ) def test_dosage_out_of_range(self): - data = self.prescription_data(dosage="10000 mg") + data = self.prescription_data(base_dosage="10000 mg") res = self.client.post( f"/api/v1/consultation/{self.consultation.external_id}/prescriptions/", data, ) self.assertEqual(res.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual( - res.json()["dosage"][0], + res.json()["base_dosage"][0], "Input amount must be between 0.0001 and 5000", ) - data = self.prescription_data(dosage="-1 mg") + data = self.prescription_data(base_dosage="-1 mg") res = self.client.post( f"/api/v1/consultation/{self.consultation.external_id}/prescriptions/", data, ) self.assertEqual(res.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual( - res.json()["dosage"][0], + res.json()["base_dosage"][0], "Input amount must be between 0.0001 and 5000", ) def test_dosage_precision(self): - data = self.prescription_data(dosage="0.300003 mg") + data = self.prescription_data(base_dosage="0.300003 mg") res = self.client.post( f"/api/v1/consultation/{self.consultation.external_id}/prescriptions/", data, ) self.assertEqual(res.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual( - res.json()["dosage"][0], + res.json()["base_dosage"][0], "Input amount must have at most 4 decimal places", ) def test_dosage_unit_invalid(self): - data = self.prescription_data(dosage="1 abc") + data = self.prescription_data(base_dosage="1 abc") res = self.client.post( f"/api/v1/consultation/{self.consultation.external_id}/prescriptions/", data, ) self.assertEqual(res.status_code, status.HTTP_400_BAD_REQUEST) - self.assertTrue(res.json()["dosage"][0].startswith("Unit must be one of")) + self.assertTrue(res.json()["base_dosage"][0].startswith("Unit must be one of")) def test_dosage_leading_zero(self): - data = self.prescription_data(dosage="01 mg") + data = self.prescription_data(base_dosage="01 mg") res = self.client.post( f"/api/v1/consultation/{self.consultation.external_id}/prescriptions/", data, ) self.assertEqual(res.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual( - res.json()["dosage"][0], + res.json()["base_dosage"][0], "Input amount must be a valid number without leading or trailing zeroes", ) def test_dosage_trailing_zero(self): - data = self.prescription_data(dosage="1.0 mg") + data = self.prescription_data(base_dosage="1.0 mg") res = self.client.post( f"/api/v1/consultation/{self.consultation.external_id}/prescriptions/", data, ) self.assertEqual(res.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual( - res.json()["dosage"][0], + res.json()["base_dosage"][0], "Input amount must be a valid number without leading or trailing zeroes", ) def test_dosage_validator_clean(self): - data = self.prescription_data(dosage=" 1 mg ") + data = self.prescription_data(base_dosage=" 1 mg ") res = self.client.post( f"/api/v1/consultation/{self.consultation.external_id}/prescriptions/", data, @@ -124,13 +126,54 @@ def test_dosage_validator_clean(self): self.assertEqual(res.status_code, status.HTTP_201_CREATED) def test_valid_dosage(self): - data = self.prescription_data(dosage="1 mg") + data = self.prescription_data(base_dosage="1 mg") res = self.client.post( f"/api/v1/consultation/{self.consultation.external_id}/prescriptions/", data, ) self.assertEqual(res.status_code, status.HTTP_201_CREATED) + def test_create_titrated_prescription(self): + titrated_prescription_data = self.prescription_data( + dosage_type=PrescriptionDosageType.TITRATED.value, + target_dosage="2 mg", + instruction_on_titration="Test Instruction", + ) + response = self.client.post( + f"/api/v1/consultation/{self.consultation.external_id}/prescriptions/", + titrated_prescription_data, + ) + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + + titrated_prescription_data = self.prescription_data( + dosage_type=PrescriptionDosageType.TITRATED.value, + ) + response = self.client.post( + f"/api/v1/consultation/{self.consultation.external_id}/prescriptions/", + titrated_prescription_data, + ) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + def test_create_prn_prescription(self): + prn_prescription_data = self.prescription_data( + dosage_type=PrescriptionDosageType.PRN.value, + indicator="Test Indicator", + ) + response = self.client.post( + f"/api/v1/consultation/{self.consultation.external_id}/prescriptions/", + prn_prescription_data, + ) + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + + prn_prescription_data = self.prescription_data( + dosage_type=PrescriptionDosageType.PRN.value, + ) + response = self.client.post( + f"/api/v1/consultation/{self.consultation.external_id}/prescriptions/", + prn_prescription_data, + ) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + class MedicineAdministrationsApiTestCase(TestUtils, APITestCase): @classmethod @@ -154,9 +197,11 @@ def create_prescription(self, **kwargs): "consultation": self.create_consultation(self.patient, self.facility), "medicine": MedibaseMedicine.objects.first(), "prescription_type": "REGULAR", - "dosage": "1 mg", + "base_dosage": "1 mg", "frequency": "OD", - "is_prn": False, + "dosage_type": kwargs.get( + "dosage_type", PrescriptionDosageType.REGULAR.value + ), } return Prescription.objects.create( **{**data, **kwargs, "prescribed_by": self.user} @@ -234,3 +279,20 @@ def test_administer_discontinued(self): {"notes": "Test Notes"}, ) self.assertEqual(res.status_code, status.HTTP_400_BAD_REQUEST) + + def test_administer_titrated_dosage(self): + prescription = self.create_prescription( + dosage_type=PrescriptionDosageType.TITRATED.value, target_dosage="10 mg" + ) + res = self.client.post( + f"/api/v1/consultation/{prescription.consultation.external_id}/prescriptions/{prescription.external_id}/administer/", + {"notes": "Test Notes"}, + ) + self.assertEqual(res.status_code, status.HTTP_400_BAD_REQUEST) + + res = self.client.post( + f"/api/v1/consultation/{prescription.consultation.external_id}/prescriptions/{prescription.external_id}/administer/", + {"notes": "Test Notes", "dosage": "1 mg"}, + ) + + self.assertEqual(res.status_code, status.HTTP_201_CREATED) diff --git a/care/facility/tests/test_patient_consultation_api.py b/care/facility/tests/test_patient_consultation_api.py index 758163f404..5a4104b1ec 100644 --- a/care/facility/tests/test_patient_consultation_api.py +++ b/care/facility/tests/test_patient_consultation_api.py @@ -1,10 +1,12 @@ import datetime +from unittest.mock import patch from django.utils.timezone import make_aware from rest_framework import status from rest_framework.test import APITestCase from care.facility.api.serializers.patient_consultation import MIN_ENCOUNTER_DATE +from care.facility.models.file_upload import FileUpload from care.facility.models.icd11_diagnosis import ( ConditionVerificationStatus, ICD11Diagnosis, @@ -224,6 +226,32 @@ def test_discharge_as_recovered_with_expired_fields(self): self.assertIsNone(consultation.death_datetime) self.assertIsNot(consultation.death_confirmed_doctor, "Dr. Test") + def discharge_summary(self, consultation, **kwargs): + return self.client.post( + f"{self.get_url(consultation)}generate_discharge_summary/", kwargs, "json" + ) + + def test_discharge_summary(self): + consultation = self.create_admission_consultation( + suggestion=SuggestionChoices.A, + encounter_date=make_aware(datetime.datetime(2020, 4, 1, 15, 30, 00)), + ) + with patch.object(FileUpload, "put_object"): + self.discharge_summary( + consultation, + new_discharge_reason=NewDischargeReasonEnum.RECOVERED, + discharge_date="2020-04-02T15:30:00Z", + discharge_notes="Discharge as recovered after admission before future", + ) + + file_res = FileUpload.objects.filter( + associating_id=consultation.external_id, + upload_completed=True, + is_archived=False, + ) + uploaded_file = file_res[0] + self.assertFalse(uploaded_file.name.endswith(".pdf")) + def test_referred_to_external_null(self): consultation = self.create_admission_consultation( suggestion=SuggestionChoices.A, diff --git a/care/facility/tests/test_prescriptions_api.py b/care/facility/tests/test_prescriptions_api.py index 35c2a198da..87d5fa3c1d 100644 --- a/care/facility/tests/test_prescriptions_api.py +++ b/care/facility/tests/test_prescriptions_api.py @@ -24,9 +24,9 @@ def setUp(self) -> None: self.normal_prescription_data = { "medicine": self.medicine.external_id, "prescription_type": "REGULAR", - "dosage": "1 mg", + "base_dosage": "1 mg", "frequency": "OD", - "is_prn": False, + "dosage_type": "REGULAR", } def test_create_normal_prescription(self): @@ -69,3 +69,47 @@ def test_prescribe_duplicate_active_medicine_and_discontinue(self): self.normal_prescription_data, ) self.assertEqual(res.status_code, status.HTTP_201_CREATED) + + def test_create_titrated_prescription(self): + titrated_prescription_data = { + **self.normal_prescription_data, + "dosage_type": "TITRATED", + "target_dosage": "2 mg", + } + response = self.client.post( + f"/api/v1/consultation/{self.consultation.external_id}/prescriptions/", + titrated_prescription_data, + ) + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + + titrated_prescription_data = { + **self.normal_prescription_data, + "dosage_type": "TITRATED", + } + response = self.client.post( + f"/api/v1/consultation/{self.consultation.external_id}/prescriptions/", + titrated_prescription_data, + ) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + def test_create_prn_prescription(self): + prn_prescription_data = { + **self.normal_prescription_data, + "dosage_type": "PRN", + "indicator": "Test Indicator", + } + response = self.client.post( + f"/api/v1/consultation/{self.consultation.external_id}/prescriptions/", + prn_prescription_data, + ) + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + + prn_prescription_data = { + **self.normal_prescription_data, + "dosage_type": "PRN", + } + response = self.client.post( + f"/api/v1/consultation/{self.consultation.external_id}/prescriptions/", + prn_prescription_data, + ) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) diff --git a/care/facility/utils/reports/discharge_summary.py b/care/facility/utils/reports/discharge_summary.py index 48af9e6c66..ac3afd1665 100644 --- a/care/facility/utils/reports/discharge_summary.py +++ b/care/facility/utils/reports/discharge_summary.py @@ -18,6 +18,7 @@ PatientConsultation, PatientSample, Prescription, + PrescriptionDosageType, PrescriptionType, ) from care.facility.models.file_upload import FileUpload @@ -105,22 +106,21 @@ def get_discharge_summary_data(consultation: PatientConsultation): prescriptions = Prescription.objects.filter( consultation=consultation, prescription_type=PrescriptionType.REGULAR.value, - is_prn=False, - ) + ).exclude(dosage_type=PrescriptionDosageType.PRN.value) prn_prescriptions = Prescription.objects.filter( consultation=consultation, prescription_type=PrescriptionType.REGULAR.value, - is_prn=True, + dosage_type=PrescriptionDosageType.PRN.value, ) discharge_prescriptions = Prescription.objects.filter( consultation=consultation, prescription_type=PrescriptionType.DISCHARGE.value, - is_prn=False, - ) + ).exclude(dosage_type=PrescriptionDosageType.PRN.value) + discharge_prn_prescriptions = Prescription.objects.filter( consultation=consultation, prescription_type=PrescriptionType.DISCHARGE.value, - is_prn=True, + dosage_type=PrescriptionDosageType.PRN.value, ) files = FileUpload.objects.filter( associating_id=consultation.id, @@ -178,7 +178,7 @@ def generate_and_upload_discharge_summary(consultation: PatientConsultation): try: current_date = timezone.now() summary_file = FileUpload( - name=f"discharge_summary-{consultation.patient.name}-{current_date}.pdf", + name=f"discharge_summary-{consultation.patient.name}-{current_date}", internal_name=f"{uuid4()}.pdf", file_type=FileUpload.FileType.DISCHARGE_SUMMARY.value, associating_id=consultation.external_id, diff --git a/care/templates/reports/patient_discharge_summary_pdf.html b/care/templates/reports/patient_discharge_summary_pdf.html index 1fbc572e26..8884e1d43a 100644 --- a/care/templates/reports/patient_discharge_summary_pdf.html +++ b/care/templates/reports/patient_discharge_summary_pdf.html @@ -480,8 +480,12 @@

{{ prescription.medicine_name }} - - {{ prescription.dosage }} + + {{ prescription.base_dosage }} + {% if prescription.dosage_type == 'TITRATED' %} + to {{ prescription.target_dosage }} +

Instruction on titration: {{ prescription.instruction_on_titration }}

+ {% endif %} {{ prescription.route }} @@ -538,7 +542,7 @@

{{ prescription.medicine_name }} - {{ prescription.dosage }} + {{ prescription.base_dosage }} {{ prescription.max_dosage }} @@ -885,7 +889,7 @@

{{ prescription.medicine_name }} - {{ prescription.dosage }} + {{ prescription.base_dosage }} {{ prescription.route }} @@ -942,7 +946,7 @@

{{ prescription.medicine_name }} - {{ prescription.dosage }} + {{ prescription.base_dosage }} {{ prescription.max_dosage }} diff --git a/care/utils/serializer/external_id_field.py b/care/utils/serializer/external_id_field.py index b71718c524..88d711731c 100644 --- a/care/utils/serializer/external_id_field.py +++ b/care/utils/serializer/external_id_field.py @@ -13,7 +13,7 @@ def __call__(self, value): raise serializers.ValidationError("invalid uuid") -class ExternalIdSerializerField(serializers.Field): +class ExternalIdSerializerField(serializers.UUIDField): def __init__(self, queryset=None, *args, **kwargs): super().__init__(*args, **kwargs) self.queryset = queryset diff --git a/data/dummy/facility.json b/data/dummy/facility.json index 39b690712b..16e4c2fab2 100644 --- a/data/dummy/facility.json +++ b/data/dummy/facility.json @@ -6864,8 +6864,8 @@ "medicine": 2, "medicine_old": null, "route": null, - "dosage": "3 mg", - "is_prn": false, + "base_dosage": "3 mg", + "dosage_type": "REGULAR", "frequency": "BD", "days": null, "indicator": null, @@ -6893,8 +6893,8 @@ "medicine": 2, "medicine_old": null, "route": null, - "dosage": "3 mg", - "is_prn": false, + "base_dosage": "3 mg", + "dosage_type": "REGULAR", "frequency": "BD", "days": null, "indicator": null, @@ -6922,8 +6922,8 @@ "medicine": 2, "medicine_old": null, "route": null, - "dosage": "3 mg", - "is_prn": false, + "base_dosage": "3 mg", + "dosage_type": "REGULAR", "frequency": "BD", "days": null, "indicator": null, @@ -6951,8 +6951,8 @@ "medicine": 2, "medicine_old": null, "route": null, - "dosage": "3 mg", - "is_prn": false, + "base_dosage": "3 mg", + "dosage_type": "REGULAR", "frequency": "BD", "days": null, "indicator": null, @@ -6980,8 +6980,8 @@ "medicine": 2, "medicine_old": null, "route": null, - "dosage": "3 mg", - "is_prn": false, + "base_dosage": "3 mg", + "dosage_type": "REGULAR", "frequency": "BD", "days": null, "indicator": null, @@ -7009,8 +7009,8 @@ "medicine": 2, "medicine_old": null, "route": null, - "dosage": "3 mg", - "is_prn": false, + "base_dosage": "3 mg", + "dosage_type": "REGULAR", "frequency": "BD", "days": null, "indicator": null, @@ -7038,8 +7038,8 @@ "medicine": 2, "medicine_old": null, "route": null, - "dosage": "3 mg", - "is_prn": false, + "base_dosage": "3 mg", + "dosage_type": "REGULAR", "frequency": "BD", "days": null, "indicator": null, @@ -7067,8 +7067,8 @@ "medicine": 2, "medicine_old": null, "route": null, - "dosage": "3 mg", - "is_prn": false, + "base_dosage": "3 mg", + "dosage_type": "REGULAR", "frequency": "BD", "days": null, "indicator": null, @@ -7096,8 +7096,8 @@ "medicine": 2, "medicine_old": null, "route": null, - "dosage": "3 mg", - "is_prn": false, + "base_dosage": "3 mg", + "dosage_type": "REGULAR", "frequency": "BD", "days": null, "indicator": null, @@ -7125,8 +7125,8 @@ "medicine": 2, "medicine_old": null, "route": null, - "dosage": "3 mg", - "is_prn": false, + "base_dosage": "3 mg", + "dosage_type": "REGULAR", "frequency": "BD", "days": null, "indicator": null, @@ -7154,8 +7154,8 @@ "medicine": 2, "medicine_old": null, "route": null, - "dosage": "3 mg", - "is_prn": false, + "base_dosage": "3 mg", + "dosage_type": "REGULAR", "frequency": "BD", "days": null, "indicator": null, @@ -7183,8 +7183,8 @@ "medicine": 2, "medicine_old": null, "route": null, - "dosage": "3 mg", - "is_prn": false, + "base_dosage": "3 mg", + "dosage_type": "REGULAR", "frequency": "BD", "days": null, "indicator": null, @@ -7212,8 +7212,8 @@ "medicine": 2, "medicine_old": null, "route": null, - "dosage": "3 mg", - "is_prn": false, + "base_dosage": "3 mg", + "dosage_type": "REGULAR", "frequency": "BD", "days": null, "indicator": null, @@ -7241,8 +7241,8 @@ "medicine": 2, "medicine_old": null, "route": null, - "dosage": "3 mg", - "is_prn": false, + "base_dosage": "3 mg", + "dosage_type": "REGULAR", "frequency": "BD", "days": null, "indicator": null, @@ -7270,8 +7270,8 @@ "medicine": 2, "medicine_old": null, "route": null, - "dosage": "3 mg", - "is_prn": false, + "base_dosage": "3 mg", + "dosage_type": "REGULAR", "frequency": "BD", "days": null, "indicator": null, @@ -7299,8 +7299,8 @@ "medicine": 2, "medicine_old": null, "route": null, - "dosage": "3 mg", - "is_prn": false, + "base_dosage": "3 mg", + "dosage_type": "REGULAR", "frequency": "BD", "days": null, "indicator": null, @@ -7328,8 +7328,8 @@ "medicine": 2, "medicine_old": null, "route": null, - "dosage": "3 mg", - "is_prn": false, + "base_dosage": "3 mg", + "dosage_type": "REGULAR", "frequency": "BD", "days": null, "indicator": null, diff --git a/data/dummy/users.json b/data/dummy/users.json index 70fdf67fe0..cf28ec50c4 100644 --- a/data/dummy/users.json +++ b/data/dummy/users.json @@ -768,5 +768,75 @@ "groups": [], "user_permissions": [] } + }, + { + "model": "users.user", + "pk": 23, + "fields": { + "password": "argon2$argon2id$v=19$m=102400,t=2,p=8$WDE0YXVzR1dIV09zNWJZalF4QzdXSw$ZTpeDj5sbB3TKfu0DEJ5EkQnL3EmM2vGgvdx+Qt2sAw", + "last_login": "2024-03-18 17:04:08.753272+00", + "is_superuser": false, + "first_name": "Dummy", + "last_name": "Nurse", + "email": "dummynurse1@test.com", + "is_staff": false, + "is_active": true, + "date_joined": "2024-03-08 06:31:23.199154+00", + "username": "dummynurse1", + "user_type": 14, + "created_by": 1, + "ward": null, + "local_body": null, + "district": 7, + "state": 1, + "phone_number": "+918878825662", + "alt_phone_number": "+918878825662", + "gender": 2, + "age": 21, + "home_facility": 1, + "verified": true, + "deleted": false, + "pf_endpoint": null, + "pf_p256dh": null, + "pf_auth": null, + "asset": null, + "groups": [], + "user_permissions": [] + } + }, + { + "model": "users.user", + "pk": 24, + "fields": { + "password": "argon2$argon2id$v=19$m=102400,t=2,p=8$WlVSNkdVTEd4Vkgzako3SHUxT0xJag$b5A8WE7JdwlysGuiJPwoQneBQHtzjWTs3Br0VnX3XRc", + "last_login": "2024-03-12 16:34:18.859463+00", + "is_superuser": false, + "first_name": "Dummy", + "last_name": "Nurse", + "email": "dummynurse2@test.com", + "is_staff": false, + "is_active": true, + "date_joined": "2024-03-08 06:32:12.270135+00", + "username": "dummynurse2", + "user_type": 14, + "created_by": 1, + "ward": null, + "local_body": null, + "district": 7, + "state": 1, + "phone_number": "+918744587566", + "alt_phone_number": "+918744587566", + "gender": 1, + "age": 35, + "home_facility": 1, + "verified": true, + "deleted": false, + "pf_endpoint": null, + "pf_p256dh": null, + "pf_auth": null, + "asset": null, + "groups": [], + "user_permissions": [] + } } ] diff --git a/docs/github-repo/configuration.rst b/docs/github-repo/configuration.rst index bf29224cc9..6729d8a268 100644 --- a/docs/github-repo/configuration.rst +++ b/docs/github-repo/configuration.rst @@ -3,7 +3,9 @@ GitHub Repository The Github Repo available here_ contains the source code for the care project, Apart from the secrets configured at runtime, the exact copy is deployed in production. -The :code:`master` branch auto deploys to the Development instance and is regarded as the Beta version of the application. +The :code:`develop` branch auto deploys to the Development instance and is regarded as the Beta version of the application. + +The :code:`staging` branch auto deploys to the Staging instance and is regarded as the Release Candidate version of the application. The :code:`production` branch auto deploys to Production instance and is regarded as the Stable version of the application.