diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a7228ed8b..f150020e7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,9 +16,9 @@ on: env: # This is the version of pipenv all the steps will use # If changing this, change Dockerfile - DEFAULT_PIP_ENV_VERSION: "2024.0.1" + DEFAULT_PIP_ENV_VERSION: "2024.0.3" # This is the default version of Python to use in most steps which aren't specific - DEFAULT_PYTHON_VERSION: "3.10" + DEFAULT_PYTHON_VERSION: "3.11" jobs: pre-commit: @@ -100,7 +100,7 @@ jobs: - pre-commit strategy: matrix: - python-version: ['3.9', '3.10', '3.11'] + python-version: ['3.10', '3.11', '3.12'] fail-fast: false steps: - @@ -486,7 +486,7 @@ jobs: name: Patch whitenoise run: | curl --fail --silent --show-error --location --output 484.patch https://github.com/evansd/whitenoise/pull/484.patch - patch -d $(pipenv --venv)/lib/python3.10/site-packages --verbose -p2 < 484.patch + patch -d $(pipenv --venv)/lib/python3.11/site-packages --verbose -p2 < 484.patch rm 484.patch - name: Install system dependencies diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c39480421..fb38eaf26 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -48,7 +48,7 @@ repos: exclude: "(^Pipfile\\.lock$)" # Python hooks - repo: https://github.com/astral-sh/ruff-pre-commit - rev: 'v0.6.5' + rev: 'v0.6.8' hooks: - id: ruff - id: ruff-format @@ -62,6 +62,9 @@ repos: rev: v6.2.1 hooks: - id: beautysh + language_version: '3.10' + additional_dependencies: + - setuptools args: - "--tab" - repo: https://github.com/shellcheck-py/shellcheck-py diff --git a/.python-version b/.python-version index 8e34c8131..8cc1b46f5 100644 --- a/.python-version +++ b/.python-version @@ -1 +1 @@ -3.9.19 +3.10.15 diff --git a/.ruff.toml b/.ruff.toml index 6331bfaf5..2ac983fe8 100644 --- a/.ruff.toml +++ b/.ruff.toml @@ -2,7 +2,7 @@ fix = true line-length = 88 respect-gitignore = true src = ["src"] -target-version = "py39" +target-version = "py310" output-format = "grouped" show-fixes = true diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a7375000b..bbc017d36 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -11,7 +11,7 @@ If you want to implement something big: ## Python -Paperless supports python 3.9 - 3.11 at this time. We format Python code with [ruff](https://docs.astral.sh/ruff/formatter/). +Paperless supports python 3.10 - 3.12 at this time. We format Python code with [ruff](https://docs.astral.sh/ruff/formatter/). ## Branches diff --git a/Dockerfile b/Dockerfile index 4ef558712..cfc14554e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -39,7 +39,7 @@ COPY Pipfile* ./ RUN set -eux \ && echo "Installing pipenv" \ - && python3 -m pip install --no-cache-dir --upgrade pipenv==2024.0.1 \ + && python3 -m pip install --no-cache-dir --upgrade pipenv==2024.0.3 \ && echo "Generating requirement.txt" \ && pipenv requirements > requirements.txt @@ -233,11 +233,11 @@ RUN --mount=type=cache,target=/root/.cache/pip/,id=pip-cache \ && python3 -m pip install --no-cache-dir --upgrade wheel \ && echo "Installing Python requirements" \ && curl --fail --silent --show-error --location \ - --output psycopg_c-3.2.1-cp311-cp311-linux_x86_64.whl \ - https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.1/psycopg_c-3.2.1-cp311-cp311-linux_x86_64.whl \ + --output psycopg_c-3.2.2-cp311-cp311-linux_x86_64.whl \ + https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.2/psycopg_c-3.2.2-cp311-cp311-linux_x86_64.whl \ && curl --fail --silent --show-error --location \ - --output psycopg_c-3.2.1-cp311-cp311-linux_aarch64.whl \ - https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.1/psycopg_c-3.2.1-cp311-cp311-linux_aarch64.whl \ + --output psycopg_c-3.2.2-cp311-cp311-linux_aarch64.whl \ + https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.2/psycopg_c-3.2.2-cp311-cp311-linux_aarch64.whl \ && python3 -m pip install --default-timeout=1000 --find-links . --requirement requirements.txt \ && echo "Patching whitenoise for compression speedup" \ && curl --fail --silent --show-error --location --output 484.patch https://github.com/evansd/whitenoise/pull/484.patch \ diff --git a/Pipfile.lock b/Pipfile.lock index 4b94f6dfe..d9d14ba17 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -24,11 +24,11 @@ }, "anyio": { "hashes": [ - "sha256:5aadc6a1bbb7cdb0bede386cac5e2940f5e2ff3aa20277e991cf028e0585ce94", - "sha256:c1b2d8f46a8a812513012e1107cb0e68c17159a7a594208005a57dc776e1bdc7" + "sha256:137b4559cbb034c477165047febb6ff83f390fc3b20bf181c1fc0a728cb8beeb", + "sha256:c7d2e9d63e31599eeb636c8c5c03a7e108d73b345f064f1c19fdc87b79036a9a" ], - "markers": "python_version >= '3.8'", - "version": "==4.4.0" + "markers": "python_version >= '3.9'", + "version": "==4.6.0" }, "asgiref": { "hashes": [ @@ -48,11 +48,11 @@ }, "billiard": { "hashes": [ - "sha256:07aa978b308f334ff8282bd4a746e681b3513db5c9a514cbdd810cbbdc19714d", - "sha256:9a3c3184cb275aa17a732f93f65b20c525d3d9f253722d26a82194803ade5a2c" + "sha256:12b641b0c539073fc8d3f5b8b7be998956665c4233c7c1fcd66a7e677c4fb36f", + "sha256:40b59a4ac8806ba2c2369ea98d876bc6108b051c227baffd928c644d15d8f3cb" ], "markers": "python_version >= '3.7'", - "version": "==4.2.0" + "version": "==4.2.1" }, "bleach": { "hashes": [ @@ -476,10 +476,10 @@ "socialaccount" ], "hashes": [ - "sha256:54bf0af8dc5c334254dd56f9069447c19b9b623110a095b2a0dcb82a414e1055" + "sha256:1f8281e2dc17d3b977bcd95b99538f128c5cd2fb2551346a7f8ad31aa76f17cc" ], "markers": "python_version >= '3.8'", - "version": "==64.2.1" + "version": "==65.0.1" }, "django-auditlog": { "hashes": [ @@ -594,12 +594,12 @@ }, "filelock": { "hashes": [ - "sha256:81de9eb8453c769b63369f87f11131a7ab04e367f8d97ad39dc230daa07e3bec", - "sha256:f6ed4c963184f4c84dd5557ce8fece759a3724b37b80c6c4f20a2f63a4dc6609" + "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0", + "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==3.16.0" + "version": "==3.16.1" }, "flower": { "hashes": [ @@ -828,11 +828,11 @@ }, "idna": { "hashes": [ - "sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac", - "sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603" + "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", + "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3" ], "markers": "python_version >= '3.6'", - "version": "==3.8" + "version": "==3.10" }, "imap-tools": { "hashes": [ @@ -874,11 +874,11 @@ }, "kombu": { "hashes": [ - "sha256:ad200a8dbdaaa2bbc5f26d2ee7d707d9a1fded353a0f4bd751ce8c7d9f449c60", - "sha256:c8dd99820467610b4febbc7a9e8a0d3d7da2d35116b67184418b51cc520ea6b6" + "sha256:14212f5ccf022fc0a70453bb025a1dcc32782a588c49ea866884047d66e14763", + "sha256:eef572dd2fd9fc614b37580e3caeafdd5af46c1eff31e7fba89138cdb406f2cf" ], "markers": "python_version >= '3.8'", - "version": "==5.4.0" + "version": "==5.4.2" }, "langdetect": { "hashes": [ @@ -1050,65 +1050,73 @@ }, "msgpack": { "hashes": [ - "sha256:00e073efcba9ea99db5acef3959efa45b52bc67b61b00823d2a1a6944bf45982", - "sha256:0726c282d188e204281ebd8de31724b7d749adebc086873a59efb8cf7ae27df3", - "sha256:0ceea77719d45c839fd73abcb190b8390412a890df2f83fb8cf49b2a4b5c2f40", - "sha256:114be227f5213ef8b215c22dde19532f5da9652e56e8ce969bf0a26d7c419fee", - "sha256:13577ec9e247f8741c84d06b9ece5f654920d8365a4b636ce0e44f15e07ec693", - "sha256:1876b0b653a808fcd50123b953af170c535027bf1d053b59790eebb0aeb38950", - "sha256:1ab0bbcd4d1f7b6991ee7c753655b481c50084294218de69365f8f1970d4c151", - "sha256:1cce488457370ffd1f953846f82323cb6b2ad2190987cd4d70b2713e17268d24", - "sha256:26ee97a8261e6e35885c2ecd2fd4a6d38252246f94a2aec23665a4e66d066305", - "sha256:3528807cbbb7f315bb81959d5961855e7ba52aa60a3097151cb21956fbc7502b", - "sha256:374a8e88ddab84b9ada695d255679fb99c53513c0a51778796fcf0944d6c789c", - "sha256:376081f471a2ef24828b83a641a02c575d6103a3ad7fd7dade5486cad10ea659", - "sha256:3923a1778f7e5ef31865893fdca12a8d7dc03a44b33e2a5f3295416314c09f5d", - "sha256:4916727e31c28be8beaf11cf117d6f6f188dcc36daae4e851fee88646f5b6b18", - "sha256:493c5c5e44b06d6c9268ce21b302c9ca055c1fd3484c25ba41d34476c76ee746", - "sha256:505fe3d03856ac7d215dbe005414bc28505d26f0c128906037e66d98c4e95868", - "sha256:5845fdf5e5d5b78a49b826fcdc0eb2e2aa7191980e3d2cfd2a30303a74f212e2", - "sha256:5c330eace3dd100bdb54b5653b966de7f51c26ec4a7d4e87132d9b4f738220ba", - "sha256:5dbf059fb4b7c240c873c1245ee112505be27497e90f7c6591261c7d3c3a8228", - "sha256:5e390971d082dba073c05dbd56322427d3280b7cc8b53484c9377adfbae67dc2", - "sha256:5fbb160554e319f7b22ecf530a80a3ff496d38e8e07ae763b9e82fadfe96f273", - "sha256:64d0fcd436c5683fdd7c907eeae5e2cbb5eb872fafbc03a43609d7941840995c", - "sha256:69284049d07fce531c17404fcba2bb1df472bc2dcdac642ae71a2d079d950653", - "sha256:6a0e76621f6e1f908ae52860bdcb58e1ca85231a9b0545e64509c931dd34275a", - "sha256:73ee792784d48aa338bba28063e19a27e8d989344f34aad14ea6e1b9bd83f596", - "sha256:74398a4cf19de42e1498368c36eed45d9528f5fd0155241e82c4082b7e16cffd", - "sha256:7938111ed1358f536daf311be244f34df7bf3cdedb3ed883787aca97778b28d8", - "sha256:82d92c773fbc6942a7a8b520d22c11cfc8fd83bba86116bfcf962c2f5c2ecdaa", - "sha256:83b5c044f3eff2a6534768ccfd50425939e7a8b5cf9a7261c385de1e20dcfc85", - "sha256:8db8e423192303ed77cff4dce3a4b88dbfaf43979d280181558af5e2c3c71afc", - "sha256:9517004e21664f2b5a5fd6333b0731b9cf0817403a941b393d89a2f1dc2bd836", - "sha256:95c02b0e27e706e48d0e5426d1710ca78e0f0628d6e89d5b5a5b91a5f12274f3", - "sha256:99881222f4a8c2f641f25703963a5cefb076adffd959e0558dc9f803a52d6a58", - "sha256:9ee32dcb8e531adae1f1ca568822e9b3a738369b3b686d1477cbc643c4a9c128", - "sha256:a22e47578b30a3e199ab067a4d43d790249b3c0587d9a771921f86250c8435db", - "sha256:b5505774ea2a73a86ea176e8a9a4a7c8bf5d521050f0f6f8426afe798689243f", - "sha256:bd739c9251d01e0279ce729e37b39d49a08c0420d3fee7f2a4968c0576678f77", - "sha256:d16a786905034e7e34098634b184a7d81f91d4c3d246edc6bd7aefb2fd8ea6ad", - "sha256:d3420522057ebab1728b21ad473aa950026d07cb09da41103f8e597dfbfaeb13", - "sha256:d56fd9f1f1cdc8227d7b7918f55091349741904d9520c65f0139a9755952c9e8", - "sha256:d661dc4785affa9d0edfdd1e59ec056a58b3dbb9f196fa43587f3ddac654ac7b", - "sha256:dfe1f0f0ed5785c187144c46a292b8c34c1295c01da12e10ccddfc16def4448a", - "sha256:e1dd7839443592d00e96db831eddb4111a2a81a46b028f0facd60a09ebbdd543", - "sha256:e2872993e209f7ed04d963e4b4fbae72d034844ec66bc4ca403329db2074377b", - "sha256:e2f879ab92ce502a1e65fce390eab619774dda6a6ff719718069ac94084098ce", - "sha256:e3aa7e51d738e0ec0afbed661261513b38b3014754c9459508399baf14ae0c9d", - "sha256:e532dbd6ddfe13946de050d7474e3f5fb6ec774fbb1a188aaf469b08cf04189a", - "sha256:e6b7842518a63a9f17107eb176320960ec095a8ee3b4420b5f688e24bf50c53c", - "sha256:e75753aeda0ddc4c28dce4c32ba2f6ec30b1b02f6c0b14e547841ba5b24f753f", - "sha256:eadb9f826c138e6cf3c49d6f8de88225a3c0ab181a9b4ba792e006e5292d150e", - "sha256:ed59dd52075f8fc91da6053b12e8c89e37aa043f8986efd89e61fae69dc1b011", - "sha256:ef254a06bcea461e65ff0373d8a0dd1ed3aa004af48839f002a0c994a6f72d04", - "sha256:f3709997b228685fe53e8c433e2df9f0cdb5f4542bd5114ed17ac3c0129b0480", - "sha256:f51bab98d52739c50c56658cc303f190785f9a2cd97b823357e7aeae54c8f68a", - "sha256:f9904e24646570539a8950400602d66d2b2c492b9010ea7e965025cb71d0c86d", - "sha256:f9af38a89b6a5c04b7d18c492c8ccf2aee7048aff1ce8437c4683bb5a1df893d" + "sha256:06f5fd2f6bb2a7914922d935d3b8bb4a7fff3a9a91cfce6d06c13bc42bec975b", + "sha256:071603e2f0771c45ad9bc65719291c568d4edf120b44eb36324dcb02a13bfddf", + "sha256:0907e1a7119b337971a689153665764adc34e89175f9a34793307d9def08e6ca", + "sha256:0f92a83b84e7c0749e3f12821949d79485971f087604178026085f60ce109330", + "sha256:115a7af8ee9e8cddc10f87636767857e7e3717b7a2e97379dc2054712693e90f", + "sha256:13599f8829cfbe0158f6456374e9eea9f44eee08076291771d8ae93eda56607f", + "sha256:17fb65dd0bec285907f68b15734a993ad3fc94332b5bb21b0435846228de1f39", + "sha256:2137773500afa5494a61b1208619e3871f75f27b03bcfca7b3a7023284140247", + "sha256:3180065ec2abbe13a4ad37688b61b99d7f9e012a535b930e0e683ad6bc30155b", + "sha256:398b713459fea610861c8a7b62a6fec1882759f308ae0795b5413ff6a160cf3c", + "sha256:3d364a55082fb2a7416f6c63ae383fbd903adb5a6cf78c5b96cc6316dc1cedc7", + "sha256:3df7e6b05571b3814361e8464f9304c42d2196808e0119f55d0d3e62cd5ea044", + "sha256:41c991beebf175faf352fb940bf2af9ad1fb77fd25f38d9142053914947cdbf6", + "sha256:42f754515e0f683f9c79210a5d1cad631ec3d06cea5172214d2176a42e67e19b", + "sha256:452aff037287acb1d70a804ffd022b21fa2bb7c46bee884dbc864cc9024128a0", + "sha256:4676e5be1b472909b2ee6356ff425ebedf5142427842aa06b4dfd5117d1ca8a2", + "sha256:46c34e99110762a76e3911fc923222472c9d681f1094096ac4102c18319e6468", + "sha256:471e27a5787a2e3f974ba023f9e265a8c7cfd373632247deb225617e3100a3c7", + "sha256:4a1964df7b81285d00a84da4e70cb1383f2e665e0f1f2a7027e683956d04b734", + "sha256:4b51405e36e075193bc051315dbf29168d6141ae2500ba8cd80a522964e31434", + "sha256:4d1b7ff2d6146e16e8bd665ac726a89c74163ef8cd39fa8c1087d4e52d3a2325", + "sha256:53258eeb7a80fc46f62fd59c876957a2d0e15e6449a9e71842b6d24419d88ca1", + "sha256:534480ee5690ab3cbed89d4c8971a5c631b69a8c0883ecfea96c19118510c846", + "sha256:58638690ebd0a06427c5fe1a227bb6b8b9fdc2bd07701bec13c2335c82131a88", + "sha256:58dfc47f8b102da61e8949708b3eafc3504509a5728f8b4ddef84bd9e16ad420", + "sha256:59caf6a4ed0d164055ccff8fe31eddc0ebc07cf7326a2aaa0dbf7a4001cd823e", + "sha256:5dbad74103df937e1325cc4bfeaf57713be0b4f15e1c2da43ccdd836393e2ea2", + "sha256:5e1da8f11a3dd397f0a32c76165cf0c4eb95b31013a94f6ecc0b280c05c91b59", + "sha256:646afc8102935a388ffc3914b336d22d1c2d6209c773f3eb5dd4d6d3b6f8c1cb", + "sha256:64fc9068d701233effd61b19efb1485587560b66fe57b3e50d29c5d78e7fef68", + "sha256:65553c9b6da8166e819a6aa90ad15288599b340f91d18f60b2061f402b9a4915", + "sha256:685ec345eefc757a7c8af44a3032734a739f8c45d1b0ac45efc5d8977aa4720f", + "sha256:6ad622bf7756d5a497d5b6836e7fc3752e2dd6f4c648e24b1803f6048596f701", + "sha256:73322a6cc57fcee3c0c57c4463d828e9428275fb85a27aa2aa1a92fdc42afd7b", + "sha256:74bed8f63f8f14d75eec75cf3d04ad581da6b914001b474a5d3cd3372c8cc27d", + "sha256:79ec007767b9b56860e0372085f8504db5d06bd6a327a335449508bbee9648fa", + "sha256:7a946a8992941fea80ed4beae6bff74ffd7ee129a90b4dd5cf9c476a30e9708d", + "sha256:7ad442d527a7e358a469faf43fda45aaf4ac3249c8310a82f0ccff9164e5dccd", + "sha256:7c9a35ce2c2573bada929e0b7b3576de647b0defbd25f5139dcdaba0ae35a4cc", + "sha256:7e7b853bbc44fb03fbdba34feb4bd414322180135e2cb5164f20ce1c9795ee48", + "sha256:879a7b7b0ad82481c52d3c7eb99bf6f0645dbdec5134a4bddbd16f3506947feb", + "sha256:8a706d1e74dd3dea05cb54580d9bd8b2880e9264856ce5068027eed09680aa74", + "sha256:8a84efb768fb968381e525eeeb3d92857e4985aacc39f3c47ffd00eb4509315b", + "sha256:8cf9e8c3a2153934a23ac160cc4cba0ec035f6867c8013cc6077a79823370346", + "sha256:8da4bf6d54ceed70e8861f833f83ce0814a2b72102e890cbdfe4b34764cdd66e", + "sha256:8e59bca908d9ca0de3dc8684f21ebf9a690fe47b6be93236eb40b99af28b6ea6", + "sha256:914571a2a5b4e7606997e169f64ce53a8b1e06f2cf2c3a7273aa106236d43dd5", + "sha256:a51abd48c6d8ac89e0cfd4fe177c61481aca2d5e7ba42044fd218cfd8ea9899f", + "sha256:a52a1f3a5af7ba1c9ace055b659189f6c669cf3657095b50f9602af3a3ba0fe5", + "sha256:ad33e8400e4ec17ba782f7b9cf868977d867ed784a1f5f2ab46e7ba53b6e1e1b", + "sha256:b4c01941fd2ff87c2a934ee6055bda4ed353a7846b8d4f341c428109e9fcde8c", + "sha256:bce7d9e614a04d0883af0b3d4d501171fbfca038f12c77fa838d9f198147a23f", + "sha256:c40ffa9a15d74e05ba1fe2681ea33b9caffd886675412612d93ab17b58ea2fec", + "sha256:c5a91481a3cc573ac8c0d9aace09345d989dc4a0202b7fcb312c88c26d4e71a8", + "sha256:c921af52214dcbb75e6bdf6a661b23c3e6417f00c603dd2070bccb5c3ef499f5", + "sha256:d46cf9e3705ea9485687aa4001a76e44748b609d260af21c4ceea7f2212a501d", + "sha256:d8ce0b22b890be5d252de90d0e0d119f363012027cf256185fc3d474c44b1b9e", + "sha256:dd432ccc2c72b914e4cb77afce64aab761c1137cc698be3984eee260bcb2896e", + "sha256:e0856a2b7e8dcb874be44fea031d22e5b3a19121be92a1e098f46068a11b0870", + "sha256:e1f3c3d21f7cf67bcf2da8e494d30a75e4cf60041d98b3f79875afb5b96f3a3f", + "sha256:f1ba6136e650898082d9d5a5217d5906d1e138024f836ff48691784bbe1adf96", + "sha256:f3e9b4936df53b970513eac1758f3882c88658a220b58dcc1e39606dccaaf01c", + "sha256:f80bc7d47f76089633763f952e67f8214cb7b3ee6bfa489b3cb6a84cfac114cd", + "sha256:fd2906780f25c8ed5d7b323379f6138524ba793428db5d0e9d226d3fa6aa1788" ], "markers": "python_version >= '3.8'", - "version": "==1.0.8" + "version": "==1.1.0" }, "mysqlclient": { "hashes": [ @@ -1137,54 +1145,62 @@ }, "numpy": { "hashes": [ - "sha256:0123ffdaa88fa4ab64835dcbde75dcdf89c453c922f18dced6e27c90d1d0ec5a", - "sha256:11a76c372d1d37437857280aa142086476136a8c0f373b2e648ab2c8f18fb195", - "sha256:13e689d772146140a252c3a28501da66dfecd77490b498b168b501835041f951", - "sha256:1e795a8be3ddbac43274f18588329c72939870a16cae810c2b73461c40718ab1", - "sha256:26df23238872200f63518dd2aa984cfca675d82469535dc7162dc2ee52d9dd5c", - "sha256:286cd40ce2b7d652a6f22efdfc6d1edf879440e53e76a75955bc0c826c7e64dc", - "sha256:2b2955fa6f11907cf7a70dab0d0755159bca87755e831e47932367fc8f2f2d0b", - "sha256:2da5960c3cf0df7eafefd806d4e612c5e19358de82cb3c343631188991566ccd", - "sha256:312950fdd060354350ed123c0e25a71327d3711584beaef30cdaa93320c392d4", - "sha256:423e89b23490805d2a5a96fe40ec507407b8ee786d66f7328be214f9679df6dd", - "sha256:496f71341824ed9f3d2fd36cf3ac57ae2e0165c143b55c3a035ee219413f3318", - "sha256:49ca4decb342d66018b01932139c0961a8f9ddc7589611158cb3c27cbcf76448", - "sha256:51129a29dbe56f9ca83438b706e2e69a39892b5eda6cedcb6b0c9fdc9b0d3ece", - "sha256:5fec9451a7789926bcf7c2b8d187292c9f93ea30284802a0ab3f5be8ab36865d", - "sha256:671bec6496f83202ed2d3c8fdc486a8fc86942f2e69ff0e986140339a63bcbe5", - "sha256:7f0a0c6f12e07fa94133c8a67404322845220c06a9e80e85999afe727f7438b8", - "sha256:807ec44583fd708a21d4a11d94aedf2f4f3c3719035c76a2bbe1fe8e217bdc57", - "sha256:883c987dee1880e2a864ab0dc9892292582510604156762362d9326444636e78", - "sha256:8c5713284ce4e282544c68d1c3b2c7161d38c256d2eefc93c1d683cf47683e66", - "sha256:8cafab480740e22f8d833acefed5cc87ce276f4ece12fdaa2e8903db2f82897a", - "sha256:8df823f570d9adf0978347d1f926b2a867d5608f434a7cff7f7908c6570dcf5e", - "sha256:9059e10581ce4093f735ed23f3b9d283b9d517ff46009ddd485f1747eb22653c", - "sha256:905d16e0c60200656500c95b6b8dca5d109e23cb24abc701d41c02d74c6b3afa", - "sha256:9189427407d88ff25ecf8f12469d4d39d35bee1db5d39fc5c168c6f088a6956d", - "sha256:96a55f64139912d61de9137f11bf39a55ec8faec288c75a54f93dfd39f7eb40c", - "sha256:97032a27bd9d8988b9a97a8c4d2c9f2c15a81f61e2f21404d7e8ef00cb5be729", - "sha256:984d96121c9f9616cd33fbd0618b7f08e0cfc9600a7ee1d6fd9b239186d19d97", - "sha256:9a92ae5c14811e390f3767053ff54eaee3bf84576d99a2456391401323f4ec2c", - "sha256:9ea91dfb7c3d1c56a0e55657c0afb38cf1eeae4544c208dc465c3c9f3a7c09f9", - "sha256:a15f476a45e6e5a3a79d8a14e62161d27ad897381fecfa4a09ed5322f2085669", - "sha256:a392a68bd329eafac5817e5aefeb39038c48b671afd242710b451e76090e81f4", - "sha256:a3f4ab0caa7f053f6797fcd4e1e25caee367db3112ef2b6ef82d749530768c73", - "sha256:a46288ec55ebbd58947d31d72be2c63cbf839f0a63b49cb755022310792a3385", - "sha256:a61ec659f68ae254e4d237816e33171497e978140353c0c2038d46e63282d0c8", - "sha256:a842d573724391493a97a62ebbb8e731f8a5dcc5d285dfc99141ca15a3302d0c", - "sha256:becfae3ddd30736fe1889a37f1f580e245ba79a5855bff5f2a29cb3ccc22dd7b", - "sha256:c05e238064fc0610c840d1cf6a13bf63d7e391717d247f1bf0318172e759e692", - "sha256:c1c9307701fec8f3f7a1e6711f9089c06e6284b3afbbcd259f7791282d660a15", - "sha256:c7b0be4ef08607dd04da4092faee0b86607f111d5ae68036f16cc787e250a131", - "sha256:cfd41e13fdc257aa5778496b8caa5e856dc4896d4ccf01841daee1d96465467a", - "sha256:d731a1c6116ba289c1e9ee714b08a8ff882944d4ad631fd411106a30f083c326", - "sha256:df55d490dea7934f330006d0f81e8551ba6010a5bf035a249ef61a94f21c500b", - "sha256:ec9852fb39354b5a45a80bdab5ac02dd02b15f44b3804e9f00c556bf24b4bded", - "sha256:f15975dfec0cf2239224d80e32c3170b1d168335eaedee69da84fbe9f1f9cd04", - "sha256:f26b258c385842546006213344c50655ff1555a9338e2e5e02a0756dc3e803dd" + "sha256:046356b19d7ad1890c751b99acad5e82dc4a02232013bd9a9a712fddf8eb60f5", + "sha256:0b8cc2715a84b7c3b161f9ebbd942740aaed913584cae9cdc7f8ad5ad41943d0", + "sha256:0d07841fd284718feffe7dd17a63a2e6c78679b2d386d3e82f44f0108c905550", + "sha256:13cc11c00000848702322af4de0147ced365c81d66053a67c2e962a485b3717c", + "sha256:13ce49a34c44b6de5241f0b38b07e44c1b2dcacd9e36c30f9c2fcb1bb5135db7", + "sha256:24c2ad697bd8593887b019817ddd9974a7f429c14a5469d7fad413f28340a6d2", + "sha256:251105b7c42abe40e3a689881e1793370cc9724ad50d64b30b358bbb3a97553b", + "sha256:2ca4b53e1e0b279142113b8c5eb7d7a877e967c306edc34f3b58e9be12fda8df", + "sha256:3269c9eb8745e8d975980b3a7411a98976824e1fdef11f0aacf76147f662b15f", + "sha256:397bc5ce62d3fb73f304bec332171535c187e0643e176a6e9421a6e3eacef06d", + "sha256:3fc5eabfc720db95d68e6646e88f8b399bfedd235994016351b1d9e062c4b270", + "sha256:50a95ca3560a6058d6ea91d4629a83a897ee27c00630aed9d933dff191f170cd", + "sha256:52ac2e48f5ad847cd43c4755520a2317f3380213493b9d8a4c5e37f3b87df504", + "sha256:53e27293b3a2b661c03f79aa51c3987492bd4641ef933e366e0f9f6c9bf257ec", + "sha256:57eb525e7c2a8fdee02d731f647146ff54ea8c973364f3b850069ffb42799647", + "sha256:5889dd24f03ca5a5b1e8a90a33b5a0846d8977565e4ae003a63d22ecddf6782f", + "sha256:59ca673ad11d4b84ceb385290ed0ebe60266e356641428c845b39cd9df6713ab", + "sha256:6435c48250c12f001920f0751fe50c0348f5f240852cfddc5e2f97e007544cbe", + "sha256:6e5a9cb2be39350ae6c8f79410744e80154df658d5bea06e06e0ac5bb75480d5", + "sha256:7be6a07520b88214ea85d8ac8b7d6d8a1839b0b5cb87412ac9f49fa934eb15d5", + "sha256:7c803b7934a7f59563db459292e6aa078bb38b7ab1446ca38dd138646a38203e", + "sha256:7dd86dfaf7c900c0bbdcb8b16e2f6ddf1eb1fe39c6c8cca6e94844ed3152a8fd", + "sha256:8661c94e3aad18e1ea17a11f60f843a4933ccaf1a25a7c6a9182af70610b2313", + "sha256:8ae0fd135e0b157365ac7cc31fff27f07a5572bdfc38f9c2d43b2aff416cc8b0", + "sha256:910b47a6d0635ec1bd53b88f86120a52bf56dcc27b51f18c7b4a2e2224c29f0f", + "sha256:913cc1d311060b1d409e609947fa1b9753701dac96e6581b58afc36b7ee35af6", + "sha256:920b0911bb2e4414c50e55bd658baeb78281a47feeb064ab40c2b66ecba85553", + "sha256:950802d17a33c07cba7fd7c3dcfa7d64705509206be1606f196d179e539111ed", + "sha256:981707f6b31b59c0c24bcda52e5605f9701cb46da4b86c2e8023656ad3e833cb", + "sha256:98ce7fb5b8063cfdd86596b9c762bf2b5e35a2cdd7e967494ab78a1fa7f8b86e", + "sha256:99f4a9ee60eed1385a86e82288971a51e71df052ed0b2900ed30bc840c0f2e39", + "sha256:9a8e06c7a980869ea67bbf551283bbed2856915f0a792dc32dd0f9dd2fb56728", + "sha256:ae8ce252404cdd4de56dcfce8b11eac3c594a9c16c231d081fb705cf23bd4d9e", + "sha256:afd9c680df4de71cd58582b51e88a61feed4abcc7530bcd3d48483f20fc76f2a", + "sha256:b49742cdb85f1f81e4dc1b39dcf328244f4d8d1ded95dea725b316bd2cf18c95", + "sha256:b5613cfeb1adfe791e8e681128f5f49f22f3fcaa942255a6124d58ca59d9528f", + "sha256:bab7c09454460a487e631ffc0c42057e3d8f2a9ddccd1e60c7bb8ed774992480", + "sha256:c8a0e34993b510fc19b9a2ce7f31cb8e94ecf6e924a40c0c9dd4f62d0aac47d9", + "sha256:caf5d284ddea7462c32b8d4a6b8af030b6c9fd5332afb70e7414d7fdded4bfd0", + "sha256:cea427d1350f3fd0d2818ce7350095c1a2ee33e30961d2f0fef48576ddbbe90f", + "sha256:d0cf7d55b1051387807405b3898efafa862997b4cba8aa5dbe657be794afeafd", + "sha256:d10c39947a2d351d6d466b4ae83dad4c37cd6c3cdd6d5d0fa797da56f710a6ae", + "sha256:d2b9cd92c8f8e7b313b80e93cedc12c0112088541dcedd9197b5dee3738c1201", + "sha256:d4c57b68c8ef5e1ebf47238e99bf27657511ec3f071c465f6b1bccbef12d4136", + "sha256:d51fc141ddbe3f919e91a096ec739f49d686df8af254b2053ba21a910ae518bf", + "sha256:e097507396c0be4e547ff15b13dc3866f45f3680f789c1a1301b07dadd3fbc78", + "sha256:e30356d530528a42eeba51420ae8bf6c6c09559051887196599d96ee5f536468", + "sha256:e8d5f8a8e3bc87334f025194c6193e408903d21ebaeb10952264943a985066ca", + "sha256:e8dfa9e94fc127c40979c3eacbae1e61fda4fe71d84869cc129e2721973231ef", + "sha256:f212d4f46b67ff604d11fff7cc62d36b3e8714edf68e44e9760e19be38c03eb0", + "sha256:f7506387e191fe8cdb267f912469a3cccc538ab108471291636a96a54e599556", + "sha256:fac6e277a41163d27dfab5f4ec1f7a83fac94e170665a4a50191b545721c6521", + "sha256:fcd8f556cdc8cfe35e70efb92463082b7f43dd7e547eb071ffc36abc0ca4699b" ], - "markers": "python_version >= '3.9'", - "version": "==2.0.2" + "markers": "python_version >= '3.10'", + "version": "==2.1.1" }, "oauthlib": { "hashes": [ @@ -1391,36 +1407,36 @@ }, "prometheus-client": { "hashes": [ - "sha256:287629d00b147a32dcb2be0b9df905da599b2d82f80377083ec8463309a4bb89", - "sha256:cde524a85bce83ca359cc837f28b8c0db5cac7aa653a588fd7e84ba061c329e7" + "sha256:4fa6b4dd0ac16d58bb587c04b1caae65b8c5043e85f778f42f5f632f6af2e166", + "sha256:96c83c606b71ff2b0a433c98889d275f51ffec6c5e267de37c7a2b5c9aa9233e" ], "markers": "python_version >= '3.8'", - "version": "==0.20.0" + "version": "==0.21.0" }, "prompt-toolkit": { "hashes": [ - "sha256:0d7bfa67001d5e39d02c224b663abc33687405033a8c422d0d675a5a13361d10", - "sha256:1e1b29cb58080b1e69f207c893a1a7bf16d127a5c30c9d17a25a5d77792e5360" + "sha256:d6623ab0477a80df74e646bdbc93621143f5caf104206aa29294d53de1a03d90", + "sha256:f49a827f90062e411f1ce1f854f2aedb3c23353244f8108b89283587397ac10e" ], "markers": "python_full_version >= '3.7.0'", - "version": "==3.0.47" + "version": "==3.0.48" }, "psycopg": { "extras": [ "c" ], "hashes": [ - "sha256:dc8da6dc8729dacacda3cc2f17d2c9397a70a66cf0d2b69c91065d60d5f00cb7", - "sha256:ece385fb413a37db332f97c49208b36cf030ff02b199d7635ed2fbd378724175" + "sha256:8bad2e497ce22d556dac1464738cb948f8d6bab450d965cf1d8a8effd52412e0", + "sha256:babf565d459d8f72fb65da5e211dd0b58a52c51e4e1fa9cadecff42d6b7619b2" ], "markers": "python_version >= '3.8'", - "version": "==3.2.1" + "version": "==3.2.2" }, "psycopg-c": { "hashes": [ - "sha256:2d09943cc8a855c42c1e23b4298957b7ce8f27bf3683258c52fd139f601f7cda" + "sha256:de8cac75bc6640ef0f54ad9187b81e07c430206a83c566b73d4cca41ecccb7c8" ], - "version": "==3.2.1" + "version": "==3.2.2" }, "pycparser": { "hashes": [ @@ -1468,11 +1484,11 @@ }, "python-gnupg": { "hashes": [ - "sha256:01d8013931c9fa3f45824bbea7054c03d6e11f258a72e7e086e168dbcb91854c", - "sha256:72ce142af6da7f07e433fef148b445fb3e07854acd2f88739008838745c0e9f5" + "sha256:290d8ddb9cd63df96cfe9284b9b265f19fd6e145e5582dc58fd7271f026d0a47", + "sha256:2f8a4c6f63766feca6cc1416408f8b84e1b914fe7b54514e570fc5cbe92e9248" ], "index": "pypi", - "version": "==0.5.2" + "version": "==0.5.3" }, "python-ipware": { "hashes": [ @@ -1494,10 +1510,10 @@ }, "pytz": { "hashes": [ - "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812", - "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319" + "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a", + "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725" ], - "version": "==2024.1" + "version": "==2024.2" }, "pyyaml": { "hashes": [ @@ -1568,118 +1584,98 @@ }, "rapidfuzz": { "hashes": [ - "sha256:03126f9a040ff21d2a110610bfd6b93b79377ce8b4121edcb791d61b7df6eec5", - "sha256:048d55d36c02c6685a2b2741688503c3d15149694506655b6169dcfd3b6c2585", - "sha256:057bb03f39e285047d7e9412e01ecf31bb2d42b9466a5409d715d587460dd59b", - "sha256:0b1c2d504eddf97bc0f2eba422c8915576dbf025062ceaca2d68aecd66324ad9", - "sha256:0d1415a732ee75e74a90af12020b77a0b396b36c60afae1bde3208a78cd2c9fc", - "sha256:0e9125377fa3d21a8abd4fbdbcf1c27be73e8b1850f0b61b5b711364bf3b59db", - "sha256:110b6294396bc0a447648627479c9320f095c2034c0537f687592e0f58622638", - "sha256:111a20a3c090cf244d9406e60500b6c34b2375ba3a5009e2b38fd806fe38e337", - "sha256:13d8675a1fa7e2b19650ca7ef9a6ec01391d4bb12ab9e0793e8eb024538b4a34", - "sha256:18669bb6cdf7d40738526d37e550df09ba065b5a7560f3d802287988b6cb63cf", - "sha256:19c64d8ddb2940b42a4567b23f1681af77f50a5ff6c9b8e85daba079c210716e", - "sha256:1dc516ac6d32027be2b0196bedf6d977ac26debd09ca182376322ad620460feb", - "sha256:1de91e7fd7f525e10ea79a6e62c559d1b0278ec097ad83d9da378b6fab65a265", - "sha256:1ee2086f490cb501d86b7e386c1eb4e3a0ccbb0c99067089efaa8c79012c8952", - "sha256:1ef6a1a8f0b12f8722f595f15c62950c9a02d5abc64742561299ffd49f6c6944", - "sha256:1f1a33e84056b7892c721d84475d3bde49a145126bc4c6efe0d6d0d59cb31c29", - "sha256:22589c0b8ccc6c391ce7f776c93a8c92c96ab8d34e1a19f1bd2b12a235332632", - "sha256:2379e0b2578ad3ac7004f223251550f08bca873ff76c169b09410ec562ad78d8", - "sha256:268f8e1ca50fc61c0736f3fe9d47891424adf62d96ed30196f30f4bd8216b41f", - "sha256:3171653212218a162540a3c8eb8ae7d3dcc8548540b69eaecaf3b47c14d89c90", - "sha256:32532af1d70c6ec02ea5ac7ee2766dfff7c8ae8c761abfe8da9e527314e634e8", - "sha256:3445a35c4c8d288f2b2011eb61bce1227c633ce85a3154e727170f37c0266bb2", - "sha256:3492c7a42b7fa9f0051d7fcce9893e95ed91c97c9ec7fb64346f3e070dd318ed", - "sha256:35d3044cb635ca6b1b2b7b67b3597bd19f34f1753b129eb6d2ae04cf98cd3945", - "sha256:364587827d7cbd41afa0782adc2d2d19e3f07d355b0750a02a8e33ad27a9c368", - "sha256:3665b92e788578c3bb334bd5b5fa7ee1a84bafd68be438e3110861d1578c63a0", - "sha256:36dd6e820379c37a1ffefc8a52b648758e867cd9d78ee5b5dc0c9a6a10145378", - "sha256:3ed5adb752f4308fcc8f4fb6f8eb7aa4082f9d12676fda0a74fa5564242a8107", - "sha256:47e92c155a14f44511ea8ebcc6bc1535a1fe8d0a7d67ad3cc47ba61606df7bcf", - "sha256:4ff196996240db7075f62c7bc4506f40a3c80cd4ae3ab0e79ac6892283a90859", - "sha256:521c58c72ed8a612b25cda378ff10dee17e6deb4ee99a070b723519a345527b9", - "sha256:5551d68264c1bb6943f542da83a4dc8940ede52c5847ef158698799cc28d14f5", - "sha256:578302828dd97ee2ba507d2f71d62164e28d2fc7bc73aad0d2d1d2afc021a5d5", - "sha256:579d107102c0725f7c79b4e79f16d3cf4d7c9208f29c66b064fa1fd4641d5155", - "sha256:591908240f4085e2ade5b685c6e8346e2ed44932cffeaac2fb32ddac95b55c7f", - "sha256:5a93c9e60904cb76e7aefef67afffb8b37c4894f81415ed513db090f29d01101", - "sha256:5d1eff95362f993b0276fd3839aee48625b09aac8938bb0c23b40d219cba5dc5", - "sha256:5d5262383634626eb45c536017204b8163a03bc43bda880cf1bdd7885db9a163", - "sha256:5f8bf3f0d02935751d8660abda6044821a861f6229f7d359f98bcdcc7e66c39b", - "sha256:603f48f621272a448ff58bb556feb4371252a02156593303391f5c3281dfaeac", - "sha256:675b75412a943bb83f1f53e2e54fd18c80ef15ed642dc6eb0382d1949419d904", - "sha256:68bd888eafd07b09585dcc8bc2716c5ecdb7eed62827470664d25588982b2873", - "sha256:696a79018ef989bf1c9abd9005841cee18005ccad4748bad8a4c274c47b6241a", - "sha256:6c5b32875646cb7f60c193ade99b2e4b124f19583492115293cd00f6fb198b17", - "sha256:6f83221db5755b8f34222e40607d87f1176a8d5d4dbda4a55a0f0b67d588a69c", - "sha256:6fb76e5a21034f0307c51c5a2fc08856f698c53a4c593b17d291f7d6e9d09ca3", - "sha256:7abe2dbae81120a64bb4f8d3fcafe9122f328c9f86d7f327f174187a5af4ed86", - "sha256:7b702de95666a1f7d5c6b47eacadfe2d2794af3742d63d2134767d13e5d1c713", - "sha256:7c20c1474b068c4bd45bf2fd0ad548df284f74e9a14a68b06746c56e3aa8eb70", - "sha256:836f4d88b8bd0fff2ebe815dcaab8aa6c8d07d1d566a7e21dd137cf6fe11ed5b", - "sha256:8501000a5eb8037c4b56857724797fe5a8b01853c363de91c8d0d0ad56bef319", - "sha256:8772b745668260c5c4d069c678bbaa68812e6c69830f3771eaad521af7bc17f8", - "sha256:8b01153c7466d0bad48fba77a303d5a768e66f24b763853469f47220b3de4661", - "sha256:8d92c552c6b7577402afdd547dcf5d31ea6c8ae31ad03f78226e055cfa37f3c6", - "sha256:9030e7238c0df51aed5c9c5ed8eee2bdd47a2ae788e562c1454af2851c3d1906", - "sha256:90db86fa196eecf96cb6db09f1083912ea945c50c57188039392d810d0b784e1", - "sha256:948dcee7aaa1cd14358b2a7ef08bf0be42bf89049c3a906669874a715fc2c937", - "sha256:94baaeea0b4f8632a6da69348b1e741043eba18d4e3088d674d3f76586b6223d", - "sha256:953b3780765c8846866faf891ee4290f6a41a6dacf4fbcd3926f78c9de412ca6", - "sha256:95b8292383e717e10455f2c917df45032b611141e43d1adf70f71b1566136b11", - "sha256:965693c2e9efd425b0f059f5be50ef830129f82892fa1858e220e424d9d0160f", - "sha256:97f2ce529d2a70a60c290f6ab269a2bbf1d3b47b9724dccc84339b85f7afb044", - "sha256:9b6a5de507b9be6de688dae40143b656f7a93b10995fb8bd90deb555e7875c60", - "sha256:9dba13d86806fcf3fe9c9919f58575e0090eadfb89c058bde02bcc7ab24e4548", - "sha256:9dc86aa6b29d174713c5f4caac35ffb7f232e3e649113e8d13812b35ab078228", - "sha256:9dcd14cf4876f04b488f6e54a7abd3e9b31db5f5a6aba0ce90659917aaa8c088", - "sha256:a3b36e1c61b796ae1777f3e9e11fd39898b09d351c9384baf6e3b7e6191d8ced", - "sha256:a3c0783910911f4f24655826d007c9f4360f08107410952c01ee3df98c713eb2", - "sha256:a40184c67db8252593ec518e17fb8a6e86d7259dc9f2d6c0bf4ff4db8cf1ad4b", - "sha256:a4da514d13f4433e16960a17f05b67e0af30ac771719c9a9fb877e5004f74477", - "sha256:a8feac9006d5c9758438906f093befffc4290de75663dbb2098461df7c7d28dd", - "sha256:a93cd834b3c315ab437f0565ee3a2f42dd33768dc885ccbabf9710b131cf70d2", - "sha256:ae1a38bade755aa9dd95a81cda949e1bf9cd92b79341ccc5e2189c9e7bdfc5ec", - "sha256:b23806fbdd6b510ba9ac93bb72d503066263b0fba44b71b835be9f063a84025f", - "sha256:b4f86e09d3064dca0b014cd48688964036a904a2d28048f00c8f4640796d06a8", - "sha256:b997ff3b39d4cee9fb025d6c46b0a24bd67595ce5a5b652a97fb3a9d60beb651", - "sha256:be3a1fc3e2ab3bdf93dc0c83c00acca8afd2a80602297d96cf4a0ba028333cdf", - "sha256:c12d180b17a22d107c8747de9c68d0b9c1d15dcda5445ff9bf9f4ccfb67c3e16", - "sha256:c1318d42610c26dcd68bd3279a1bf9e3605377260867c9a8ed22eafc1bd93a7c", - "sha256:c33211cfff9aec425bb1bfedaf94afcf337063aa273754f22779d6dadebef4c2", - "sha256:c4eebf6c93af0ae866c22b403a84747580bb5c10f0d7b51c82a87f25405d4dcb", - "sha256:c4f28f1930b09a2c300357d8465b388cecb7e8b2f454a5d5425561710b7fd07f", - "sha256:ca66676c8ef6557f9b81c5b2b519097817a7c776a6599b8d6fcc3e16edd216fe", - "sha256:ccf68e30b80e903f2309f90a438dbd640dd98e878eeb5ad361a288051ee5b75c", - "sha256:cd9360e30041690912525a210e48a897b49b230768cc8af1c702e5395690464f", - "sha256:cfa74aac64c85898b93d9c80bb935a96bf64985e28d4ee0f1a3d1f3bf11a5106", - "sha256:d098ce6162eb5e48fceb0745455bc950af059df6113eec83e916c129fca11408", - "sha256:d1230e0f9026851a6a432beaa0ce575dda7b39fe689b576f99a0704fbb81fc9c", - "sha256:d4ba2318ef670ce505f42881a5d2af70f948124646947341a3c6ccb33cd70369", - "sha256:d4e049d5ad61448c9a020d1061eba20944c4887d720c4069724beb6ea1692507", - "sha256:d73ee2df41224c87336448d279b5b6a3a75f36e41dd3dcf538c0c9cce36360d8", - "sha256:d7df9c2194c7ec930b33c991c55dbd0c10951bd25800c0b7a7b571994ebbced5", - "sha256:d95751f505a301af1aaf086c19f34536056d6c8efa91b2240de532a3db57b543", - "sha256:dd5fa6e3c6e0333051c1f3a49f0807b3366f4131c8d6ac8c3e05fd0d0ce3755c", - "sha256:df596ddd3db38aa513d4c0995611267b3946e7cbe5a8761b50e9306dfec720ee", - "sha256:e2957fdad10bb83b1982b02deb3604a3f6911a5e545f518b59c741086f92d152", - "sha256:e3dcfbe7266e74a707173a12a7b355a531f2dcfbdb32f09468e664330da14874", - "sha256:e6d9db2fa4e9be171e9bb31cf2d2575574774966b43f5b951062bb2e67885852", - "sha256:e9012d86c6397edbc9da4ac0132de7f8ee9d6ce857f4194d5684c4ddbcdd1c5c", - "sha256:e9fbf659537d246086d0297628b3795dc3e4a384101ecc01e5791c827b8d7345", - "sha256:ecc24af7f905f3d6efb371a01680116ffea8d64e266618fb9ad1602a9b4f7934", - "sha256:ece45eb2af8b00f90d10f7419322e8804bd42fb1129026f9bfe712c37508b514", - "sha256:f1c7296534c1afb6f495aa95871f14ccdc197c6db42965854e483100df313030", - "sha256:f847fb0fbfb72482b1c05c59cbb275c58a55b73708a7f77a83f8035ee3c86497", - "sha256:fbda3dd68d8b28ccb20ffb6f756fefd9b5ba570a772bedd7643ed441f5793308", - "sha256:fc3e6081069eea61593f1d6839029da53d00c8c9b205c5534853eaa3f031085c", - "sha256:fcf79b686962d7bec458a0babc904cb4fa319808805e036b9d5a531ee6b9b835", - "sha256:fde81b1da9a947f931711febe2e2bee694e891f6d3e6aa6bc02c1884702aea19" + "sha256:094c26116d55bf9c53abd840d08422f20da78ec4c4723e5024322321caedca48", + "sha256:0ec338d5f4ad8d9339a88a08db5c23e7f7a52c2b2a10510c48a0cef1fb3f0ddc", + "sha256:10fdad800441b9c97d471a937ba7d42625f1b530db05e572f1cb7d401d95c893", + "sha256:116c71a81e046ba56551d8ab68067ca7034d94b617545316d460a452c5c3c289", + "sha256:1af60988d47534246d9525f77288fdd9de652608a4842815d9018570b959acc6", + "sha256:2026651761bf83a0f31495cc0f70840d5c0d54388f41316e3f9cb51bd85e49a5", + "sha256:20bd153aacc244e4c907d772c703fea82754c4db14f8aa64d75ff81b7b8ab92d", + "sha256:26de93e6495078b6af4c4d93a42ca067b16cc0e95699526c82ab7d1025b4d3bf", + "sha256:288f6f6e7410cacb115fb851f3f18bf0e4231eb3f6cb5bd1cec0e7b25c4d039d", + "sha256:2db9187f3acf3cd33424ecdbaad75414c298ecd1513470df7bda885dcb68cc15", + "sha256:2e9be5d05cd960914024412b5406fb75a82f8562f45912ff86255acbfdbfb78e", + "sha256:2fe5783676f0afba4a522c80b15e99dbf4e393c149ab610308a8ef1f04c6bcc8", + "sha256:3084161fc3e963056232ef8d937449a2943852e07101f5a136c8f3cfa4119217", + "sha256:34f213d59219a9c3ca14e94a825f585811a68ac56b4118b4dc388b5b14afc108", + "sha256:399b9b79ccfcf50ca3bad7692bc098bb8eade88d7d5e15773b7f866c91156d0c", + "sha256:43dfc5e733808962a822ff6d9c29f3039a3cfb3620706f5953e17cfe4496724c", + "sha256:457827ba82261aa2ae6ac06a46d0043ab12ba7216b82d87ae1434ec0f29736d6", + "sha256:47aca565a39c9a6067927871973ca827023e8b65ba6c5747f4c228c8d7ddc04f", + "sha256:4bd1a7676ee2a4c8e2f7f2550bece994f9f89e58afb96088964145a83af7408b", + "sha256:4dd3d8443970eaa02ab5ae45ce584b061f2799cd9f7e875190e2617440c1f9d4", + "sha256:4df75b3ebbb8cfdb9bf8b213b168620b88fd92d0c16a8bc9f9234630b282db59", + "sha256:50484d563f8bfa723c74c944b0bb15b9e054db9c889348c8c307abcbee75ab92", + "sha256:50e3d0c72ea15391ba9531ead7f2068a67c5b18a6a365fef3127583aaadd1725", + "sha256:545fc04f2d592e4350f59deb0818886c1b444ffba3bec535b4fbb97191aaf769", + "sha256:56fd15ea8f4c948864fa5ebd9261c67cf7b89a1c517a0caef4df75446a7af18c", + "sha256:5897242d455461f2c5b82d7397b29341fd11e85bf3608a522177071044784ee8", + "sha256:5d350864269d56f51ab81ab750c9259ae5cad3152c0680baef143dcec92206a1", + "sha256:5dd6eec15b13329abe66cc241b484002ecb0e17d694491c944a22410a6a9e5e2", + "sha256:63e4c175cbce8c3adc22dca5e6154588ae673f6c55374d156f3dac732c88d7de", + "sha256:69ef5b363afff7150a1fbe788007e307b9802a2eb6ad92ed51ab94e6ad2674c6", + "sha256:6b62af27e65bb39276a66533655a2fa3c60a487b03935721c45b7809527979be", + "sha256:6cd67d3d017296d98ff505529104299f78433e4b8af31b55003d901a62bbebe9", + "sha256:718c9bd369288aca5fa929df6dbf66fdbe9768d90940a940c0b5cdc96ade4309", + "sha256:76a35e9e19a7c883c422ffa378e9a04bc98cb3b29648c5831596401298ee51e6", + "sha256:7947a425d1be3e744707ee58c6cb318b93a56e08f080722dcc0347e0b7a1bb9a", + "sha256:79e7f98525b60b3c14524e0a4e1fedf7654657b6e02eb25f1be897ab097706f3", + "sha256:7c4c82b1689b23b1b5e6a603164ed2be41b6f6de292a698b98ba2381e889eb9d", + "sha256:7dc87073ba3a40dd65591a2100aa71602107443bf10770579ff9c8a3242edb94", + "sha256:7f3a6aa6e70fc27e4ff5c479f13cc9fc26a56347610f5f8b50396a0d344c5f55", + "sha256:803f255f10d63420979b1909ef976e7d30dec42025c9b067fc1d2040cc365a7e", + "sha256:884453860de029380dded8f3c1918af2d8eb5adf8010261645c7e5c88c2b5428", + "sha256:886882367dbc985f5736356105798f2ae6e794e671fc605476cbe2e73838a9bb", + "sha256:8a6405d34c394c65e4f73a1d300c001f304f08e529d2ed6413b46ee3037956eb", + "sha256:916a6abf3632e592b937c3d04c00a6efadd8fd30539cdcd4e6e4d92be7ca5d90", + "sha256:9178277f72d144a6c7704d7ae7fa15b7b86f0f0796f0e1049c7b4ef748a662ef", + "sha256:949b5e9eeaa4ecb4c7e9c2a4689dddce60929dd1ff9c76a889cdbabe8bbf2171", + "sha256:94c48b4a2a4b1d22246f48e2b11cae01ec7d23f0c9123f8bb822839ad79d0a88", + "sha256:96ad46f5f56f70fab2be9e5f3165a21be58d633b90bf6e67fc52a856695e4bcf", + "sha256:98f6ebe28831a482981ecfeedc8237047878424ad0c1add2c7f366ba44a20452", + "sha256:9eac95b4278bd53115903d89118a2c908398ee8bdfd977ae844f1bd2b02b917c", + "sha256:a425a0a868cf8e9c6e93e1cda4b758cdfd314bb9a4fc916c5742c934e3613480", + "sha256:a68e3724b7dab761c01816aaa64b0903734d999d5589daf97c14ef5cc0629a8e", + "sha256:a86d5d1d75e61df060c1e56596b6b0a4422a929dff19cc3dbfd5eee762c86b61", + "sha256:a9b8f51e08c3f983d857c3889930af9ddecc768453822076683664772d87e374", + "sha256:aadce42147fc09dcef1afa892485311e824c050352e1aa6e47f56b9b27af4cf0", + "sha256:ae7966f205b5a7fde93b44ca8fed37c1c8539328d7f179b1197de34eceaceb5f", + "sha256:b0445fa9880ead81f5a7d0efc0b9c977a947d8052c43519aceeaf56eabaf6843", + "sha256:b0732343cdc4273b5921268026dd7266f75466eb21873cb7635a200d9d9c3fac", + "sha256:b11a127ac590fc991e8a02c2d7e1ac86e8141c92f78546f18b5c904064a0552c", + "sha256:b33e13e537e3afd1627d421a142a12bbbe601543558a391a6fae593356842f6e", + "sha256:b5363932a5aab67010ae1a6205c567d1ef256fb333bc23c27582481606be480c", + "sha256:b54853c2371bf0e38d67da379519deb6fbe70055efb32f6607081641af3dc752", + "sha256:b67cc21a14327a0eb0f47bc3d7e59ec08031c7c55220ece672f9476e7a8068d3", + "sha256:bb0013795b40db5cf361e6f21ee7cda09627cf294977149b50e217d7fe9a2f03", + "sha256:bd393683129f446a75d8634306aed7e377627098a1286ff3af2a4f1736742820", + "sha256:c038b9939da3035afb6cb2f465f18163e8f070aba0482923ecff9443def67178", + "sha256:c50bc308fa29767ed8f53a8d33b7633a9e14718ced038ed89d41b886e301da32", + "sha256:c582c46b1bb0b19f1a5f4c1312f1b640c21d78c371a6615c34025b16ee56369b", + "sha256:c77a7330dd15c7eb5fd3631dc646fc96327f98db8181138766bd14d3e905f0ba", + "sha256:c9e29a13d2fd9be3e7d8c26c7ef4ba60b5bc7efbc9dbdf24454c7e9ebba31768", + "sha256:ca366c2e2a54e2f663f4529b189fdeb6e14d419b1c78b754ec1744f3c01070d4", + "sha256:ce19887268e90ee81a3957eef5e46a70ecc000713796639f83828b950343f49e", + "sha256:cffbc50e0767396ed483900900dd58ce4351bc0d40e64bced8694bd41864cc71", + "sha256:d29d1b9857c65f8cb3a29270732e1591b9bacf89de9d13fa764f79f07d8f1fd2", + "sha256:d4688862f957c8629d557d084f20b2d803f8738b6c4066802a0b1cc472e088d9", + "sha256:e5ddb2388610799fc46abe389600625058f2a73867e63e20107c5ad5ffa57c47", + "sha256:e89605afebbd2d4b045bccfdc12a14b16fe8ccbae05f64b4b4c64a97dad1c891", + "sha256:ea2da0459b951ee461bd4e02b8904890bd1c4263999d291c5cd01e6620177ad4", + "sha256:ec9139baa3f85b65adc700eafa03ed04995ca8533dd56c924f0e458ffec044ab", + "sha256:eda4c661e68dddd56c8fbfe1ca35e40dd2afd973f7ebb1605f4d151edc63dff8", + "sha256:f0a547e4350d1fa32624d3eab51eff8cf329f4cae110b4ea0402486b1da8be40", + "sha256:f39a2a5ded23b9b9194ec45740dce57177b80f86c6d8eba953d3ff1a25c97766", + "sha256:f3a0bda83c18195c361b5500377d0767749f128564ca95b42c8849fd475bb327", + "sha256:f744b5eb1469bf92dd143d36570d2bdbbdc88fe5cb0b5405e53dd34f479cbd8a", + "sha256:f9f0bbfb6787b97c51516f3ccf97737d504db5d239ad44527673b81f598b84ab", + "sha256:fa9720e56663cc3649d62b4b5f3145e94b8f5611e8a8e1b46507777249d46aad", + "sha256:fb6ec40cef63b1922083d33bfef2f91fc0b0bc07b5b09bfee0b0f1717d558292", + "sha256:fe5231e8afd069c742ac5b4f96344a0fe4aff52df8e53ef87faebf77f827822c" ], "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==3.9.7" + "markers": "python_version >= '3.9'", + "version": "==3.10.0" }, "redis": { "extras": [ @@ -1694,88 +1690,103 @@ }, "regex": { "hashes": [ - "sha256:01b689e887f612610c869421241e075c02f2e3d1ae93a037cb14f88ab6a8934c", - "sha256:04ce29e2c5fedf296b1a1b0acc1724ba93a36fb14031f3abfb7abda2806c1535", - "sha256:0ffe3f9d430cd37d8fa5632ff6fb36d5b24818c5c986893063b4e5bdb84cdf24", - "sha256:18300a1d78cf1290fa583cd8b7cde26ecb73e9f5916690cf9d42de569c89b1ce", - "sha256:185e029368d6f89f36e526764cf12bf8d6f0e3a2a7737da625a76f594bdfcbfc", - "sha256:19c65b00d42804e3fbea9708f0937d157e53429a39b7c61253ff15670ff62cb5", - "sha256:228b0d3f567fafa0633aee87f08b9276c7062da9616931382993c03808bb68ce", - "sha256:23acc72f0f4e1a9e6e9843d6328177ae3074b4182167e34119ec7233dfeccf53", - "sha256:25419b70ba00a16abc90ee5fce061228206173231f004437730b67ac77323f0d", - "sha256:2dfbb8baf8ba2c2b9aa2807f44ed272f0913eeeba002478c4577b8d29cde215c", - "sha256:2f1baff13cc2521bea83ab2528e7a80cbe0ebb2c6f0bfad15be7da3aed443908", - "sha256:33e2614a7ce627f0cdf2ad104797d1f68342d967de3695678c0cb84f530709f8", - "sha256:3426de3b91d1bc73249042742f45c2148803c111d1175b283270177fdf669024", - "sha256:382281306e3adaaa7b8b9ebbb3ffb43358a7bbf585fa93821300a418bb975281", - "sha256:3d974d24edb231446f708c455fd08f94c41c1ff4f04bcf06e5f36df5ef50b95a", - "sha256:3f3b6ca8eae6d6c75a6cff525c8530c60e909a71a15e1b731723233331de4169", - "sha256:3fac296f99283ac232d8125be932c5cd7644084a30748fda013028c815ba3364", - "sha256:416c0e4f56308f34cdb18c3f59849479dde5b19febdcd6e6fa4d04b6c31c9faa", - "sha256:438d9f0f4bc64e8dea78274caa5af971ceff0f8771e1a2333620969936ba10be", - "sha256:43affe33137fcd679bdae93fb25924979517e011f9dea99163f80b82eadc7e53", - "sha256:44fc61b99035fd9b3b9453f1713234e5a7c92a04f3577252b45feefe1b327759", - "sha256:45104baae8b9f67569f0f1dca5e1f1ed77a54ae1cd8b0b07aba89272710db61e", - "sha256:4fdd1384619f406ad9037fe6b6eaa3de2749e2e12084abc80169e8e075377d3b", - "sha256:538d30cd96ed7d1416d3956f94d54e426a8daf7c14527f6e0d6d425fcb4cca52", - "sha256:558a57cfc32adcf19d3f791f62b5ff564922942e389e3cfdb538a23d65a6b610", - "sha256:5eefee9bfe23f6df09ffb6dfb23809f4d74a78acef004aa904dc7c88b9944b05", - "sha256:64bd50cf16bcc54b274e20235bf8edbb64184a30e1e53873ff8d444e7ac656b2", - "sha256:65fd3d2e228cae024c411c5ccdffae4c315271eee4a8b839291f84f796b34eca", - "sha256:66b4c0731a5c81921e938dcf1a88e978264e26e6ac4ec96a4d21ae0354581ae0", - "sha256:68a8f8c046c6466ac61a36b65bb2395c74451df2ffb8458492ef49900efed293", - "sha256:6a1141a1dcc32904c47f6846b040275c6e5de0bf73f17d7a409035d55b76f289", - "sha256:6b9fc7e9cc983e75e2518496ba1afc524227c163e43d706688a6bb9eca41617e", - "sha256:6f51f9556785e5a203713f5efd9c085b4a45aecd2a42573e2b5041881b588d1f", - "sha256:7214477bf9bd195894cf24005b1e7b496f46833337b5dedb7b2a6e33f66d962c", - "sha256:731fcd76bbdbf225e2eb85b7c38da9633ad3073822f5ab32379381e8c3c12e94", - "sha256:74007a5b25b7a678459f06559504f1eec2f0f17bca218c9d56f6a0a12bfffdad", - "sha256:7a5486ca56c8869070a966321d5ab416ff0f83f30e0e2da1ab48815c8d165d46", - "sha256:7c479f5ae937ec9985ecaf42e2e10631551d909f203e31308c12d703922742f9", - "sha256:7df9ea48641da022c2a3c9c641650cd09f0cd15e8908bf931ad538f5ca7919c9", - "sha256:7e37e809b9303ec3a179085415cb5f418ecf65ec98cdfe34f6a078b46ef823ee", - "sha256:80c811cfcb5c331237d9bad3bea2c391114588cf4131707e84d9493064d267f9", - "sha256:836d3cc225b3e8a943d0b02633fb2f28a66e281290302a79df0e1eaa984ff7c1", - "sha256:84c312cdf839e8b579f504afcd7b65f35d60b6285d892b19adea16355e8343c9", - "sha256:86b17ba823ea76256b1885652e3a141a99a5c4422f4a869189db328321b73799", - "sha256:871e3ab2838fbcb4e0865a6e01233975df3a15e6fce93b6f99d75cacbd9862d1", - "sha256:88ecc3afd7e776967fa16c80f974cb79399ee8dc6c96423321d6f7d4b881c92b", - "sha256:8bc593dcce679206b60a538c302d03c29b18e3d862609317cb560e18b66d10cf", - "sha256:8fd5afd101dcf86a270d254364e0e8dddedebe6bd1ab9d5f732f274fa00499a5", - "sha256:945352286a541406f99b2655c973852da7911b3f4264e010218bbc1cc73168f2", - "sha256:973335b1624859cb0e52f96062a28aa18f3a5fc77a96e4a3d6d76e29811a0e6e", - "sha256:994448ee01864501912abf2bad9203bffc34158e80fe8bfb5b031f4f8e16da51", - "sha256:9cfd009eed1a46b27c14039ad5bbc5e71b6367c5b2e6d5f5da0ea91600817506", - "sha256:a2ec4419a3fe6cf8a4795752596dfe0adb4aea40d3683a132bae9c30b81e8d73", - "sha256:a4997716674d36a82eab3e86f8fa77080a5d8d96a389a61ea1d0e3a94a582cf7", - "sha256:a512eed9dfd4117110b1881ba9a59b31433caed0c4101b361f768e7bcbaf93c5", - "sha256:a82465ebbc9b1c5c50738536fdfa7cab639a261a99b469c9d4c7dcbb2b3f1e57", - "sha256:ae2757ace61bc4061b69af19e4689fa4416e1a04840f33b441034202b5cd02d4", - "sha256:b16582783f44fbca6fcf46f61347340c787d7530d88b4d590a397a47583f31dd", - "sha256:ba2537ef2163db9e6ccdbeb6f6424282ae4dea43177402152c67ef869cf3978b", - "sha256:bf7a89eef64b5455835f5ed30254ec19bf41f7541cd94f266ab7cbd463f00c41", - "sha256:c0abb5e4e8ce71a61d9446040c1e86d4e6d23f9097275c5bd49ed978755ff0fe", - "sha256:c414cbda77dbf13c3bc88b073a1a9f375c7b0cb5e115e15d4b73ec3a2fbc6f59", - "sha256:c51edc3541e11fbe83f0c4d9412ef6c79f664a3745fab261457e84465ec9d5a8", - "sha256:c5e69fd3eb0b409432b537fe3c6f44ac089c458ab6b78dcec14478422879ec5f", - "sha256:c918b7a1e26b4ab40409820ddccc5d49871a82329640f5005f73572d5eaa9b5e", - "sha256:c9bb87fdf2ab2370f21e4d5636e5317775e5d51ff32ebff2cf389f71b9b13750", - "sha256:ca5b2028c2f7af4e13fb9fc29b28d0ce767c38c7facdf64f6c2cd040413055f1", - "sha256:d0a07763776188b4db4c9c7fb1b8c494049f84659bb387b71c73bbc07f189e96", - "sha256:d33a0021893ede5969876052796165bab6006559ab845fd7b515a30abdd990dc", - "sha256:d55588cba7553f0b6ec33130bc3e114b355570b45785cebdc9daed8c637dd440", - "sha256:dac8e84fff5d27420f3c1e879ce9929108e873667ec87e0c8eeb413a5311adfe", - "sha256:eaef80eac3b4cfbdd6de53c6e108b4c534c21ae055d1dbea2de6b3b8ff3def38", - "sha256:eb462f0e346fcf41a901a126b50f8781e9a474d3927930f3490f38a6e73b6950", - "sha256:eb563dd3aea54c797adf513eeec819c4213d7dbfc311874eb4fd28d10f2ff0f2", - "sha256:f273674b445bcb6e4409bf8d1be67bc4b58e8b46fd0d560055d515b8830063cd", - "sha256:f6442f0f0ff81775eaa5b05af8a0ffa1dda36e9cf6ec1e0d3d245e8564b684ce", - "sha256:fb168b5924bef397b5ba13aabd8cf5df7d3d93f10218d7b925e360d436863f66", - "sha256:fbf8c2f00904eaf63ff37718eb13acf8e178cb940520e47b2f05027f5bb34ce3", - "sha256:fe4ebef608553aff8deb845c7f4f1d0740ff76fa672c011cc0bacb2a00fbde86" + "sha256:01c2acb51f8a7d6494c8c5eafe3d8e06d76563d8a8a4643b37e9b2dd8a2ff623", + "sha256:02087ea0a03b4af1ed6ebab2c54d7118127fee8d71b26398e8e4b05b78963199", + "sha256:040562757795eeea356394a7fb13076ad4f99d3c62ab0f8bdfb21f99a1f85664", + "sha256:042c55879cfeb21a8adacc84ea347721d3d83a159da6acdf1116859e2427c43f", + "sha256:079400a8269544b955ffa9e31f186f01d96829110a3bf79dc338e9910f794fca", + "sha256:07f45f287469039ffc2c53caf6803cd506eb5f5f637f1d4acb37a738f71dd066", + "sha256:09d77559e80dcc9d24570da3745ab859a9cf91953062e4ab126ba9d5993688ca", + "sha256:0cbff728659ce4bbf4c30b2a1be040faafaa9eca6ecde40aaff86f7889f4ab39", + "sha256:0e12c481ad92d129c78f13a2a3662317e46ee7ef96c94fd332e1c29131875b7d", + "sha256:0ea51dcc0835eea2ea31d66456210a4e01a076d820e9039b04ae8d17ac11dee6", + "sha256:0ffbcf9221e04502fc35e54d1ce9567541979c3fdfb93d2c554f0ca583a19b35", + "sha256:1494fa8725c285a81d01dc8c06b55287a1ee5e0e382d8413adc0a9197aac6408", + "sha256:16e13a7929791ac1216afde26f712802e3df7bf0360b32e4914dca3ab8baeea5", + "sha256:18406efb2f5a0e57e3a5881cd9354c1512d3bb4f5c45d96d110a66114d84d23a", + "sha256:18e707ce6c92d7282dfce370cd205098384b8ee21544e7cb29b8aab955b66fa9", + "sha256:220e92a30b426daf23bb67a7962900ed4613589bab80382be09b48896d211e92", + "sha256:23b30c62d0f16827f2ae9f2bb87619bc4fba2044911e2e6c2eb1af0161cdb766", + "sha256:23f9985c8784e544d53fc2930fc1ac1a7319f5d5332d228437acc9f418f2f168", + "sha256:297f54910247508e6e5cae669f2bc308985c60540a4edd1c77203ef19bfa63ca", + "sha256:2b08fce89fbd45664d3df6ad93e554b6c16933ffa9d55cb7e01182baaf971508", + "sha256:2cce2449e5927a0bf084d346da6cd5eb016b2beca10d0013ab50e3c226ffc0df", + "sha256:313ea15e5ff2a8cbbad96ccef6be638393041b0a7863183c2d31e0c6116688cf", + "sha256:323c1f04be6b2968944d730e5c2091c8c89767903ecaa135203eec4565ed2b2b", + "sha256:35f4a6f96aa6cb3f2f7247027b07b15a374f0d5b912c0001418d1d55024d5cb4", + "sha256:3b37fa423beefa44919e009745ccbf353d8c981516e807995b2bd11c2c77d268", + "sha256:3ce4f1185db3fbde8ed8aa223fc9620f276c58de8b0d4f8cc86fd1360829edb6", + "sha256:46989629904bad940bbec2106528140a218b4a36bb3042d8406980be1941429c", + "sha256:4838e24ee015101d9f901988001038f7f0d90dc0c3b115541a1365fb439add62", + "sha256:49b0e06786ea663f933f3710a51e9385ce0cba0ea56b67107fd841a55d56a231", + "sha256:4db21ece84dfeefc5d8a3863f101995de646c6cb0536952c321a2650aa202c36", + "sha256:54c4a097b8bc5bb0dfc83ae498061d53ad7b5762e00f4adaa23bee22b012e6ba", + "sha256:54d9ff35d4515debf14bc27f1e3b38bfc453eff3220f5bce159642fa762fe5d4", + "sha256:55b96e7ce3a69a8449a66984c268062fbaa0d8ae437b285428e12797baefce7e", + "sha256:57fdd2e0b2694ce6fc2e5ccf189789c3e2962916fb38779d3e3521ff8fe7a822", + "sha256:587d4af3979376652010e400accc30404e6c16b7df574048ab1f581af82065e4", + "sha256:5b513b6997a0b2f10e4fd3a1313568e373926e8c252bd76c960f96fd039cd28d", + "sha256:5ddcd9a179c0a6fa8add279a4444015acddcd7f232a49071ae57fa6e278f1f71", + "sha256:6113c008a7780792efc80f9dfe10ba0cd043cbf8dc9a76ef757850f51b4edc50", + "sha256:635a1d96665f84b292e401c3d62775851aedc31d4f8784117b3c68c4fcd4118d", + "sha256:64ce2799bd75039b480cc0360907c4fb2f50022f030bf9e7a8705b636e408fad", + "sha256:69dee6a020693d12a3cf892aba4808fe168d2a4cef368eb9bf74f5398bfd4ee8", + "sha256:6a2644a93da36c784e546de579ec1806bfd2763ef47babc1b03d765fe560c9f8", + "sha256:6b41e1adc61fa347662b09398e31ad446afadff932a24807d3ceb955ed865cc8", + "sha256:6c188c307e8433bcb63dc1915022deb553b4203a70722fc542c363bf120a01fd", + "sha256:6edd623bae6a737f10ce853ea076f56f507fd7726bee96a41ee3d68d347e4d16", + "sha256:73d6d2f64f4d894c96626a75578b0bf7d9e56dcda8c3d037a2118fdfe9b1c664", + "sha256:7a22ccefd4db3f12b526eccb129390942fe874a3a9fdbdd24cf55773a1faab1a", + "sha256:7fb89ee5d106e4a7a51bce305ac4efb981536301895f7bdcf93ec92ae0d91c7f", + "sha256:846bc79ee753acf93aef4184c040d709940c9d001029ceb7b7a52747b80ed2dd", + "sha256:85ab7824093d8f10d44330fe1e6493f756f252d145323dd17ab6b48733ff6c0a", + "sha256:8dee5b4810a89447151999428fe096977346cf2f29f4d5e29609d2e19e0199c9", + "sha256:8e5fb5f77c8745a60105403a774fe2c1759b71d3e7b4ca237a5e67ad066c7199", + "sha256:98eeee2f2e63edae2181c886d7911ce502e1292794f4c5ee71e60e23e8d26b5d", + "sha256:9d4a76b96f398697fe01117093613166e6aa8195d63f1b4ec3f21ab637632963", + "sha256:9e8719792ca63c6b8340380352c24dcb8cd7ec49dae36e963742a275dfae6009", + "sha256:a0b2b80321c2ed3fcf0385ec9e51a12253c50f146fddb2abbb10f033fe3d049a", + "sha256:a4cc92bb6db56ab0c1cbd17294e14f5e9224f0cc6521167ef388332604e92679", + "sha256:a738b937d512b30bf75995c0159c0ddf9eec0775c9d72ac0202076c72f24aa96", + "sha256:a8f877c89719d759e52783f7fe6e1c67121076b87b40542966c02de5503ace42", + "sha256:a906ed5e47a0ce5f04b2c981af1c9acf9e8696066900bf03b9d7879a6f679fc8", + "sha256:ae2941333154baff9838e88aa71c1d84f4438189ecc6021a12c7573728b5838e", + "sha256:b0d0a6c64fcc4ef9c69bd5b3b3626cc3776520a1637d8abaa62b9edc147a58f7", + "sha256:b5b029322e6e7b94fff16cd120ab35a253236a5f99a79fb04fda7ae71ca20ae8", + "sha256:b7aaa315101c6567a9a45d2839322c51c8d6e81f67683d529512f5bcfb99c802", + "sha256:be1c8ed48c4c4065ecb19d882a0ce1afe0745dfad8ce48c49586b90a55f02366", + "sha256:c0256beda696edcf7d97ef16b2a33a8e5a875affd6fa6567b54f7c577b30a137", + "sha256:c157bb447303070f256e084668b702073db99bbb61d44f85d811025fcf38f784", + "sha256:c57d08ad67aba97af57a7263c2d9006d5c404d721c5f7542f077f109ec2a4a29", + "sha256:c69ada171c2d0e97a4b5aa78fbb835e0ffbb6b13fc5da968c09811346564f0d3", + "sha256:c94bb0a9f1db10a1d16c00880bdebd5f9faf267273b8f5bd1878126e0fbde771", + "sha256:cb130fccd1a37ed894824b8c046321540263013da72745d755f2d35114b81a60", + "sha256:ced479f601cd2f8ca1fd7b23925a7e0ad512a56d6e9476f79b8f381d9d37090a", + "sha256:d05ac6fa06959c4172eccd99a222e1fbf17b5670c4d596cb1e5cde99600674c4", + "sha256:d552c78411f60b1fdaafd117a1fca2f02e562e309223b9d44b7de8be451ec5e0", + "sha256:dd4490a33eb909ef5078ab20f5f000087afa2a4daa27b4c072ccb3cb3050ad84", + "sha256:df5cbb1fbc74a8305b6065d4ade43b993be03dbe0f8b30032cced0d7740994bd", + "sha256:e28f9faeb14b6f23ac55bfbbfd3643f5c7c18ede093977f1df249f73fd22c7b1", + "sha256:e464b467f1588e2c42d26814231edecbcfe77f5ac414d92cbf4e7b55b2c2a776", + "sha256:e4c22e1ac1f1ec1e09f72e6c44d8f2244173db7eb9629cc3a346a8d7ccc31142", + "sha256:e53b5fbab5d675aec9f0c501274c467c0f9a5d23696cfc94247e1fb56501ed89", + "sha256:e93f1c331ca8e86fe877a48ad64e77882c0c4da0097f2212873a69bbfea95d0c", + "sha256:e997fd30430c57138adc06bba4c7c2968fb13d101e57dd5bb9355bf8ce3fa7e8", + "sha256:e9a091b0550b3b0207784a7d6d0f1a00d1d1c8a11699c1a4d93db3fbefc3ad35", + "sha256:eab4bb380f15e189d1313195b062a6aa908f5bd687a0ceccd47c8211e9cf0d4a", + "sha256:eb1ae19e64c14c7ec1995f40bd932448713d3c73509e82d8cd7744dc00e29e86", + "sha256:ecea58b43a67b1b79805f1a0255730edaf5191ecef84dbc4cc85eb30bc8b63b9", + "sha256:ee439691d8c23e76f9802c42a95cfeebf9d47cf4ffd06f18489122dbb0a7ad64", + "sha256:eee9130eaad130649fd73e5cd92f60e55708952260ede70da64de420cdcad554", + "sha256:f47cd43a5bfa48f86925fe26fbdd0a488ff15b62468abb5d2a1e092a4fb10e85", + "sha256:f6fff13ef6b5f29221d6904aa816c34701462956aa72a77f1f151a8ec4f56aeb", + "sha256:f745ec09bc1b0bd15cfc73df6fa4f726dcc26bb16c23a03f9e3367d357eeedd0", + "sha256:f8404bf61298bb6f8224bb9176c1424548ee1181130818fcd2cbffddc768bed8", + "sha256:f9268774428ec173654985ce55fc6caf4c6d11ade0f6f914d48ef4719eb05ebb", + "sha256:faa3c142464efec496967359ca99696c896c591c56c53506bac1ad465f66e919" ], "markers": "python_version >= '3.8'", - "version": "==2024.7.24" + "version": "==2024.9.11" }, "reportlab": { "hashes": [ @@ -1802,70 +1813,78 @@ }, "rich": { "hashes": [ - "sha256:2e85306a063b9492dffc86278197a60cbece75bcb766022f3436f567cae11bdc", - "sha256:a5ac1f1cd448ade0d59cc3356f7db7a7ccda2c8cbae9c7a90c28ff463d3e91f4" + "sha256:1760a3c0848469b97b558fc61c85233e3dafb69c7a071b4d60c38099d3cd4c06", + "sha256:8260cda28e3db6bf04d2d1ef4dbc03ba80a824c88b0e7668a0f23126a424844a" ], "markers": "python_full_version >= '3.7.0'", - "version": "==13.8.0" + "version": "==13.8.1" }, "scikit-learn": { "hashes": [ - "sha256:0828673c5b520e879f2af6a9e99eee0eefea69a2188be1ca68a6121b809055c1", - "sha256:0ea5d40c0e3951df445721927448755d3fe1d80833b0b7308ebff5d2a45e6414", - "sha256:10e49170691514a94bb2e03787aa921b82dbc507a4ea1f20fd95557862c98dc1", - "sha256:154297ee43c0b83af12464adeab378dee2d0a700ccd03979e2b821e7dd7cc1c2", - "sha256:161808750c267b77b4a9603cf9c93579c7a74ba8486b1336034c2f1579546d21", - "sha256:1bd8d3a19d4bd6dc5a7d4f358c8c3a60934dc058f363c34c0ac1e9e12a31421d", - "sha256:1ff4ba34c2abff5ec59c803ed1d97d61b036f659a17f55be102679e88f926fac", - "sha256:508907e5f81390e16d754e8815f7497e52139162fd69c4fdbd2dfa5d6cc88915", - "sha256:5944ce1faada31c55fb2ba20a5346b88e36811aab504ccafb9f0339e9f780395", - "sha256:5f57428de0c900a98389c4a433d4a3cf89de979b3aa24d1c1d251802aa15e44d", - "sha256:689b6f74b2c880276e365fe84fe4f1befd6a774f016339c65655eaff12e10cbf", - "sha256:781586c414f8cc58e71da4f3d7af311e0505a683e112f2f62919e3019abd3745", - "sha256:7b073a27797a283187a4ef4ee149959defc350b46cbf63a84d8514fe16b69855", - "sha256:88e0672c7ac21eb149d409c74cc29f1d611d5158175846e7a9c2427bd12b3956", - "sha256:909144d50f367a513cee6090873ae582dba019cb3fca063b38054fa42704c3a4", - "sha256:97625f217c5c0c5d0505fa2af28ae424bd37949bb2f16ace3ff5f2f81fb4498b", - "sha256:9a07f90846313a7639af6a019d849ff72baadfa4c74c778821ae0fad07b7275b", - "sha256:b59e3e62d2be870e5c74af4e793293753565c7383ae82943b83383fdcf5cc5c1", - "sha256:b5e865e9bd59396220de49cb4a57b17016256637c61b4c5cc81aaf16bc123bbe", - "sha256:da3f404e9e284d2b0a157e1b56b6566a34eb2798205cba35a211df3296ab7a74", - "sha256:f5b213bc29cc30a89a3130393b0e39c847a15d769d6e59539cd86b75d276b1a7" + "sha256:03b6158efa3faaf1feea3faa884c840ebd61b6484167c711548fce208ea09445", + "sha256:1ff45e26928d3b4eb767a8f14a9a6efbf1cbff7c05d1fb0f95f211a89fd4f5de", + "sha256:299406827fb9a4f862626d0fe6c122f5f87f8910b86fe5daa4c32dcd742139b6", + "sha256:2d4cad1119c77930b235579ad0dc25e65c917e756fe80cab96aa3b9428bd3fb0", + "sha256:394397841449853c2290a32050382edaec3da89e35b3e03d6cc966aebc6a8ae6", + "sha256:3a686885a4b3818d9e62904d91b57fa757fc2bed3e465c8b177be652f4dd37c8", + "sha256:3b923d119d65b7bd555c73be5423bf06c0105678ce7e1f558cb4b40b0a5502b1", + "sha256:3bed4909ba187aca80580fe2ef370d9180dcf18e621a27c4cf2ef10d279a7efe", + "sha256:52788f48b5d8bca5c0736c175fa6bdaab2ef00a8f536cda698db61bd89c551c1", + "sha256:57cc1786cfd6bd118220a92ede80270132aa353647684efa385a74244a41e3b1", + "sha256:643964678f4b5fbdc95cbf8aec638acc7aa70f5f79ee2cdad1eec3df4ba6ead8", + "sha256:6c16d84a0d45e4894832b3c4d0bf73050939e21b99b01b6fd59cbb0cf39163b6", + "sha256:757c7d514ddb00ae249832fe87100d9c73c6ea91423802872d9e74970a0e40b9", + "sha256:8c412ccc2ad9bf3755915e3908e677b367ebc8d010acbb3f182814524f2e5540", + "sha256:b4237ed7b3fdd0a4882792e68ef2545d5baa50aca3bb45aa7df468138ad8f94d", + "sha256:c15b1ca23d7c5f33cc2cb0a0d6aaacf893792271cddff0edbd6a40e8319bc113", + "sha256:ca64b3089a6d9b9363cd3546f8978229dcbb737aceb2c12144ee3f70f95684b7", + "sha256:f60021ec1574e56632be2a36b946f8143bf4e5e6af4a06d85281adc22938e0dd", + "sha256:f763897fe92d0e903aa4847b0aec0e68cadfff77e8a0687cabd946c89d17e675", + "sha256:f8b0ccd4a902836493e026c03256e8b206656f91fbcc4fde28c57a5b752561f1", + "sha256:f932a02c3f4956dfb981391ab24bda1dbd90fe3d628e4b42caef3e041c67707a" ], "index": "pypi", "markers": "python_version >= '3.9'", - "version": "==1.5.1" + "version": "==1.5.2" }, "scipy": { "hashes": [ - "sha256:017367484ce5498445aade74b1d5ab377acdc65e27095155e448c88497755a5d", - "sha256:095a87a0312b08dfd6a6155cbbd310a8c51800fc931b8c0b84003014b874ed3c", - "sha256:20335853b85e9a49ff7572ab453794298bcf0354d8068c5f6775a0eabf350aca", - "sha256:27e52b09c0d3a1d5b63e1105f24177e544a222b43611aaf5bc44d4a0979e32f9", - "sha256:2831f0dc9c5ea9edd6e51e6e769b655f08ec6db6e2e10f86ef39bd32eb11da54", - "sha256:2ac65fb503dad64218c228e2dc2d0a0193f7904747db43014645ae139c8fad16", - "sha256:392e4ec766654852c25ebad4f64e4e584cf19820b980bc04960bca0b0cd6eaa2", - "sha256:436bbb42a94a8aeef855d755ce5a465479c721e9d684de76bf61a62e7c2b81d5", - "sha256:45484bee6d65633752c490404513b9ef02475b4284c4cfab0ef946def50b3f59", - "sha256:54f430b00f0133e2224c3ba42b805bfd0086fe488835effa33fa291561932326", - "sha256:5713f62f781eebd8d597eb3f88b8bf9274e79eeabf63afb4a737abc6c84ad37b", - "sha256:5d72782f39716b2b3509cd7c33cdc08c96f2f4d2b06d51e52fb45a19ca0c86a1", - "sha256:637e98dcf185ba7f8e663e122ebf908c4702420477ae52a04f9908707456ba4d", - "sha256:8335549ebbca860c52bf3d02f80784e91a004b71b059e3eea9678ba994796a24", - "sha256:949ae67db5fa78a86e8fa644b9a6b07252f449dcf74247108c50e1d20d2b4627", - "sha256:a014c2b3697bde71724244f63de2476925596c24285c7a637364761f8710891c", - "sha256:a78b4b3345f1b6f68a763c6e25c0c9a23a9fd0f39f5f3d200efe8feda560a5fa", - "sha256:cdd7dacfb95fea358916410ec61bbc20440f7860333aee6d882bb8046264e949", - "sha256:cfa31f1def5c819b19ecc3a8b52d28ffdcc7ed52bb20c9a7589669dd3c250989", - "sha256:d533654b7d221a6a97304ab63c41c96473ff04459e404b83275b60aa8f4b7004", - "sha256:d605e9c23906d1994f55ace80e0125c587f96c020037ea6aa98d01b4bd2e222f", - "sha256:de3ade0e53bc1f21358aa74ff4830235d716211d7d077e340c7349bc3542e884", - "sha256:e89369d27f9e7b0884ae559a3a956e77c02114cc60a6058b4e5011572eea9299", - "sha256:eccfa1906eacc02de42d70ef4aecea45415f5be17e72b61bafcfd329bdc52e94", - "sha256:f26264b282b9da0952a024ae34710c2aff7d27480ee91a2e82b7b7073c24722f" + "sha256:0c2f95de3b04e26f5f3ad5bb05e74ba7f68b837133a4492414b3afd79dfe540e", + "sha256:1729560c906963fc8389f6aac023739ff3983e727b1a4d87696b7bf108316a79", + "sha256:278266012eb69f4a720827bdd2dc54b2271c97d84255b2faaa8f161a158c3b37", + "sha256:2843f2d527d9eebec9a43e6b406fb7266f3af25a751aa91d62ff416f54170bc5", + "sha256:2da0469a4ef0ecd3693761acbdc20f2fdeafb69e6819cc081308cc978153c675", + "sha256:2ff0a7e01e422c15739ecd64432743cf7aae2b03f3084288f399affcefe5222d", + "sha256:2ff38e22128e6c03ff73b6bb0f85f897d2362f8c052e3b8ad00532198fbdae3f", + "sha256:30ac8812c1d2aab7131a79ba62933a2a76f582d5dbbc695192453dae67ad6310", + "sha256:3a1b111fac6baec1c1d92f27e76511c9e7218f1695d61b59e05e0fe04dc59617", + "sha256:4079b90df244709e675cdc8b93bfd8a395d59af40b72e339c2287c91860deb8e", + "sha256:5149e3fd2d686e42144a093b206aef01932a0059c2a33ddfa67f5f035bdfe13e", + "sha256:5a275584e726026a5699459aa72f828a610821006228e841b94275c4a7c08417", + "sha256:631f07b3734d34aced009aaf6fedfd0eb3498a97e581c3b1e5f14a04164a456d", + "sha256:716e389b694c4bb564b4fc0c51bc84d381735e0d39d3f26ec1af2556ec6aad94", + "sha256:8426251ad1e4ad903a4514712d2fa8fdd5382c978010d1c6f5f37ef286a713ad", + "sha256:8475230e55549ab3f207bff11ebfc91c805dc3463ef62eda3ccf593254524ce8", + "sha256:8bddf15838ba768bb5f5083c1ea012d64c9a444e16192762bd858f1e126196d0", + "sha256:8e32dced201274bf96899e6491d9ba3e9a5f6b336708656466ad0522d8528f69", + "sha256:8f9ea80f2e65bdaa0b7627fb00cbeb2daf163caa015e59b7516395fe3bd1e066", + "sha256:97c5dddd5932bd2a1a31c927ba5e1463a53b87ca96b5c9bdf5dfd6096e27efc3", + "sha256:a49f6ed96f83966f576b33a44257d869756df6cf1ef4934f59dd58b25e0327e5", + "sha256:af29a935803cc707ab2ed7791c44288a682f9c8107bc00f0eccc4f92c08d6e07", + "sha256:b05d43735bb2f07d689f56f7b474788a13ed8adc484a85aa65c0fd931cf9ccd2", + "sha256:b28d2ca4add7ac16ae8bb6632a3c86e4b9e4d52d3e34267f6e1b0c1f8d87e389", + "sha256:b99722ea48b7ea25e8e015e8341ae74624f72e5f21fc2abd45f3a93266de4c5d", + "sha256:baff393942b550823bfce952bb62270ee17504d02a1801d7fd0719534dfb9c84", + "sha256:c0ee987efa6737242745f347835da2cc5bb9f1b42996a4d97d5c7ff7928cb6f2", + "sha256:d0d2821003174de06b69e58cef2316a6622b60ee613121199cb2852a873f8cf3", + "sha256:e0cf28db0f24a38b2a0ca33a85a54852586e43cf6fd876365c86e0657cfe7d73", + "sha256:e4f5a7c49323533f9103d4dacf4e4f07078f360743dec7f7596949149efeec06", + "sha256:eb58ca0abd96911932f688528977858681a59d61a7ce908ffd355957f7025cfc", + "sha256:edaf02b82cd7639db00dbff629995ef185c8df4c3ffa71a5562a595765a06ce1", + "sha256:fef8c87f8abfb884dac04e97824b61299880c43f4ce675dd2cbeadd3c9b466d2" ], - "markers": "python_version >= '3.9'", - "version": "==1.13.1" + "markers": "python_version >= '3.10'", + "version": "==1.14.1" }, "setproctitle": { "hashes": [ @@ -2039,11 +2058,11 @@ }, "tzdata": { "hashes": [ - "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd", - "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252" + "sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc", + "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd" ], "markers": "python_version >= '2'", - "version": "==2024.1" + "version": "==2024.2" }, "tzlocal": { "hashes": [ @@ -2055,11 +2074,11 @@ }, "urllib3": { "hashes": [ - "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472", - "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168" + "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac", + "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9" ], "markers": "python_version >= '3.8'", - "version": "==2.2.2" + "version": "==2.2.3" }, "uvicorn": { "extras": [ @@ -2262,94 +2281,94 @@ }, "websockets": { "hashes": [ - "sha256:00fd961943b6c10ee6f0b1130753e50ac5dcd906130dcd77b0003c3ab797d026", - "sha256:03d3f9ba172e0a53e37fa4e636b86cc60c3ab2cfee4935e66ed1d7acaa4625ad", - "sha256:0513c727fb8adffa6d9bf4a4463b2bade0186cbd8c3604ae5540fae18a90cb99", - "sha256:05e70fec7c54aad4d71eae8e8cab50525e899791fc389ec6f77b95312e4e9920", - "sha256:0617fd0b1d14309c7eab6ba5deae8a7179959861846cbc5cb528a7531c249448", - "sha256:06c0a667e466fcb56a0886d924b5f29a7f0886199102f0a0e1c60a02a3751cb4", - "sha256:0f52504023b1480d458adf496dc1c9e9811df4ba4752f0bc1f89ae92f4f07d0c", - "sha256:10a0dc7242215d794fb1918f69c6bb235f1f627aaf19e77f05336d147fce7c37", - "sha256:11f9976ecbc530248cf162e359a92f37b7b282de88d1d194f2167b5e7ad80ce3", - "sha256:132511bfd42e77d152c919147078460c88a795af16b50e42a0bd14f0ad71ddd2", - "sha256:139add0f98206cb74109faf3611b7783ceafc928529c62b389917a037d4cfdf4", - "sha256:14b9c006cac63772b31abbcd3e3abb6228233eec966bf062e89e7fa7ae0b7333", - "sha256:15c7d62ee071fa94a2fc52c2b472fed4af258d43f9030479d9c4a2de885fd543", - "sha256:165bedf13556f985a2aa064309baa01462aa79bf6112fbd068ae38993a0e1f1b", - "sha256:17118647c0ea14796364299e942c330d72acc4b248e07e639d34b75067b3cdd8", - "sha256:1841c9082a3ba4a05ea824cf6d99570a6a2d8849ef0db16e9c826acb28089e8f", - "sha256:1a678532018e435396e37422a95e3ab87f75028ac79570ad11f5bf23cd2a7d8c", - "sha256:1ee4cc030a4bdab482a37462dbf3ffb7e09334d01dd37d1063be1136a0d825fa", - "sha256:1f3cf6d6ec1142412d4535adabc6bd72a63f5f148c43fe559f06298bc21953c9", - "sha256:1f613289f4a94142f914aafad6c6c87903de78eae1e140fa769a7385fb232fdf", - "sha256:1fa082ea38d5de51dd409434edc27c0dcbd5fed2b09b9be982deb6f0508d25bc", - "sha256:249aab278810bee585cd0d4de2f08cfd67eed4fc75bde623be163798ed4db2eb", - "sha256:254ecf35572fca01a9f789a1d0f543898e222f7b69ecd7d5381d8d8047627bdb", - "sha256:2a02b0161c43cc9e0232711eff846569fad6ec836a7acab16b3cf97b2344c060", - "sha256:30d3a1f041360f029765d8704eae606781e673e8918e6b2c792e0775de51352f", - "sha256:3624fd8664f2577cf8de996db3250662e259bfbc870dd8ebdcf5d7c6ac0b5185", - "sha256:3f55b36d17ac50aa8a171b771e15fbe1561217510c8768af3d546f56c7576cdc", - "sha256:46af561eba6f9b0848b2c9d2427086cabadf14e0abdd9fde9d72d447df268418", - "sha256:47236c13be337ef36546004ce8c5580f4b1150d9538b27bf8a5ad8edf23ccfab", - "sha256:4a365bcb7be554e6e1f9f3ed64016e67e2fa03d7b027a33e436aecf194febb63", - "sha256:4d6ece65099411cfd9a48d13701d7438d9c34f479046b34c50ff60bb8834e43e", - "sha256:4e85f46ce287f5c52438bb3703d86162263afccf034a5ef13dbe4318e98d86e7", - "sha256:4f0426d51c8f0926a4879390f53c7f5a855e42d68df95fff6032c82c888b5f36", - "sha256:518f90e6dd089d34eaade01101fd8a990921c3ba18ebbe9b0165b46ebff947f0", - "sha256:52aed6ef21a0f1a2a5e310fb5c42d7555e9c5855476bbd7173c3aa3d8a0302f2", - "sha256:556e70e4f69be1082e6ef26dcb70efcd08d1850f5d6c5f4f2bcb4e397e68f01f", - "sha256:56a952fa2ae57a42ba7951e6b2605e08a24801a4931b5644dfc68939e041bc7f", - "sha256:59197afd478545b1f73367620407b0083303569c5f2d043afe5363676f2697c9", - "sha256:5df891c86fe68b2c38da55b7aea7095beca105933c697d719f3f45f4220a5e0e", - "sha256:63848cdb6fcc0bf09d4a155464c46c64ffdb5807ede4fb251da2c2692559ce75", - "sha256:64a11aae1de4c178fa653b07d90f2fb1a2ed31919a5ea2361a38760192e1858b", - "sha256:6724b554b70d6195ba19650fef5759ef11346f946c07dbbe390e039bcaa7cc3d", - "sha256:67494e95d6565bf395476e9d040037ff69c8b3fa356a886b21d8422ad86ae075", - "sha256:67648f5e50231b5a7f6d83b32f9c525e319f0ddc841be0de64f24928cd75a603", - "sha256:68264802399aed6fe9652e89761031acc734fc4c653137a5911c2bfa995d6d6d", - "sha256:699ba9dd6a926f82a277063603fc8d586b89f4cb128efc353b749b641fcddda7", - "sha256:6aa74a45d4cdc028561a7d6ab3272c8b3018e23723100b12e58be9dfa5a24491", - "sha256:6b41a1b3b561f1cba8321fb32987552a024a8f67f0d05f06fcf29f0090a1b956", - "sha256:71e6e5a3a3728886caee9ab8752e8113670936a193284be9d6ad2176a137f376", - "sha256:7d20516990d8ad557b5abeb48127b8b779b0b7e6771a265fa3e91767596d7d97", - "sha256:80e4ba642fc87fa532bac07e5ed7e19d56940b6af6a8c61d4429be48718a380f", - "sha256:872afa52a9f4c414d6955c365b6588bc4401272c629ff8321a55f44e3f62b553", - "sha256:8eb2b9a318542153674c6e377eb8cb9ca0fc011c04475110d3477862f15d29f0", - "sha256:9bbc525f4be3e51b89b2a700f5746c2a6907d2e2ef4513a8daafc98198b92237", - "sha256:a1a2e272d067030048e1fe41aa1ec8cfbbaabce733b3d634304fa2b19e5c897f", - "sha256:a5dc0c42ded1557cc7c3f0240b24129aefbad88af4f09346164349391dea8e58", - "sha256:acab3539a027a85d568c2573291e864333ec9d912675107d6efceb7e2be5d980", - "sha256:acbebec8cb3d4df6e2488fbf34702cbc37fc39ac7abf9449392cefb3305562e9", - "sha256:ad327ac80ba7ee61da85383ca8822ff808ab5ada0e4a030d66703cc025b021c4", - "sha256:b448a0690ef43db5ef31b3a0d9aea79043882b4632cfc3eaab20105edecf6097", - "sha256:b5a06d7f60bc2fc378a333978470dfc4e1415ee52f5f0fce4f7853eb10c1e9df", - "sha256:b74593e9acf18ea5469c3edaa6b27fa7ecf97b30e9dabd5a94c4c940637ab96e", - "sha256:b79915a1179a91f6c5f04ece1e592e2e8a6bd245a0e45d12fd56b2b59e559a32", - "sha256:b80f0c51681c517604152eb6a572f5a9378f877763231fddb883ba2f968e8817", - "sha256:b8ac5b46fd798bbbf2ac6620e0437c36a202b08e1f827832c4bf050da081b501", - "sha256:c3c493d0e5141ec055a7d6809a28ac2b88d5b878bb22df8c621ebe79a61123d0", - "sha256:c44ca9ade59b2e376612df34e837013e2b273e6c92d7ed6636d0556b6f4db93d", - "sha256:c4a6343e3b0714e80da0b0893543bf9a5b5fa71b846ae640e56e9abc6fbc4c83", - "sha256:c5870b4a11b77e4caa3937142b650fbbc0914a3e07a0cf3131f35c0587489c1c", - "sha256:ca48914cdd9f2ccd94deab5bcb5ac98025a5ddce98881e5cce762854a5de330b", - "sha256:cf2fae6d85e5dc384bf846f8243ddaa9197f3a1a70044f59399af001fd1f51d4", - "sha256:d450f5a7a35662a9b91a64aefa852f0c0308ee256122f5218a42f1d13577d71e", - "sha256:d6716c087e4aa0b9260c4e579bb82e068f84faddb9bfba9906cb87726fa2e870", - "sha256:d93572720d781331fb10d3da9ca1067817d84ad1e7c31466e9f5e59965618096", - "sha256:dbb0b697cc0655719522406c059eae233abaa3243821cfdfab1215d02ac10231", - "sha256:e33505534f3f673270dd67f81e73550b11de5b538c56fe04435d63c02c3f26b5", - "sha256:e801ca2f448850685417d723ec70298feff3ce4ff687c6f20922c7474b4746ae", - "sha256:e82db3756ccb66266504f5a3de05ac6b32f287faacff72462612120074103329", - "sha256:ef48e4137e8799998a343706531e656fdec6797b80efd029117edacb74b0a10a", - "sha256:f1d3d1f2eb79fe7b0fb02e599b2bf76a7619c79300fc55f0b5e2d382881d4f7f", - "sha256:f3fea72e4e6edb983908f0db373ae0732b275628901d909c382aae3b592589f2", - "sha256:f40de079779acbcdbb6ed4c65af9f018f8b77c5ec4e17a4b737c05c2db554491", - "sha256:f73e676a46b0fe9426612ce8caeca54c9073191a77c3e9d5c94697aef99296af", - "sha256:f9c9e258e3d5efe199ec23903f5da0eeaad58cf6fccb3547b74fd4750e5ac47a", - "sha256:fac2d146ff30d9dd2fcf917e5d147db037a5c573f0446c564f16f1f94cf87462", - "sha256:faef9ec6354fe4f9a2c0bbb52fb1ff852effc897e2a4501e25eb3a47cb0a4f89" + "sha256:004280a140f220c812e65f36944a9ca92d766b6cc4560be652a0a3883a79ed8a", + "sha256:035233b7531fb92a76beefcbf479504db8c72eb3bff41da55aecce3a0f729e54", + "sha256:149e622dc48c10ccc3d2760e5f36753db9cacf3ad7bc7bbbfd7d9c819e286f23", + "sha256:163e7277e1a0bd9fb3c8842a71661ad19c6aa7bb3d6678dc7f89b17fbcc4aeb7", + "sha256:18503d2c5f3943e93819238bf20df71982d193f73dcecd26c94514f417f6b135", + "sha256:1971e62d2caa443e57588e1d82d15f663b29ff9dfe7446d9964a4b6f12c1e700", + "sha256:204e5107f43095012b00f1451374693267adbb832d29966a01ecc4ce1db26faf", + "sha256:2510c09d8e8df777177ee3d40cd35450dc169a81e747455cc4197e63f7e7bfe5", + "sha256:25c35bf84bf7c7369d247f0b8cfa157f989862c49104c5cf85cb5436a641d93e", + "sha256:2f85cf4f2a1ba8f602298a853cec8526c2ca42a9a4b947ec236eaedb8f2dc80c", + "sha256:308e20f22c2c77f3f39caca508e765f8725020b84aa963474e18c59accbf4c02", + "sha256:325b1ccdbf5e5725fdcb1b0e9ad4d2545056479d0eee392c291c1bf76206435a", + "sha256:327b74e915cf13c5931334c61e1a41040e365d380f812513a255aa804b183418", + "sha256:346bee67a65f189e0e33f520f253d5147ab76ae42493804319b5716e46dddf0f", + "sha256:38377f8b0cdeee97c552d20cf1865695fcd56aba155ad1b4ca8779a5b6ef4ac3", + "sha256:3c78383585f47ccb0fcf186dcb8a43f5438bd7d8f47d69e0b56f71bf431a0a68", + "sha256:4059f790b6ae8768471cddb65d3c4fe4792b0ab48e154c9f0a04cefaabcd5978", + "sha256:459bf774c754c35dbb487360b12c5727adab887f1622b8aed5755880a21c4a20", + "sha256:463e1c6ec853202dd3657f156123d6b4dad0c546ea2e2e38be2b3f7c5b8e7295", + "sha256:4676df3fe46956fbb0437d8800cd5f2b6d41143b6e7e842e60554398432cf29b", + "sha256:485307243237328c022bc908b90e4457d0daa8b5cf4b3723fd3c4a8012fce4c6", + "sha256:48a2ef1381632a2f0cb4efeff34efa97901c9fbc118e01951ad7cfc10601a9bb", + "sha256:4b889dbd1342820cc210ba44307cf75ae5f2f96226c0038094455a96e64fb07a", + "sha256:586a356928692c1fed0eca68b4d1c2cbbd1ca2acf2ac7e7ebd3b9052582deefa", + "sha256:58cf7e75dbf7e566088b07e36ea2e3e2bd5676e22216e4cad108d4df4a7402a0", + "sha256:5993260f483d05a9737073be197371940c01b257cc45ae3f1d5d7adb371b266a", + "sha256:5dd6da9bec02735931fccec99d97c29f47cc61f644264eb995ad6c0c27667238", + "sha256:5f2e75431f8dc4a47f31565a6e1355fb4f2ecaa99d6b89737527ea917066e26c", + "sha256:5f9fee94ebafbc3117c30be1844ed01a3b177bb6e39088bc6b2fa1dc15572084", + "sha256:61fc0dfcda609cda0fc9fe7977694c0c59cf9d749fbb17f4e9483929e3c48a19", + "sha256:624459daabeb310d3815b276c1adef475b3e6804abaf2d9d2c061c319f7f187d", + "sha256:62d516c325e6540e8a57b94abefc3459d7dab8ce52ac75c96cad5549e187e3a7", + "sha256:6548f29b0e401eea2b967b2fdc1c7c7b5ebb3eeb470ed23a54cd45ef078a0db9", + "sha256:6d2aad13a200e5934f5a6767492fb07151e1de1d6079c003ab31e1823733ae79", + "sha256:6d6855bbe70119872c05107e38fbc7f96b1d8cb047d95c2c50869a46c65a8e96", + "sha256:70c5be9f416aa72aab7a2a76c90ae0a4fe2755c1816c153c1a2bcc3333ce4ce6", + "sha256:730f42125ccb14602f455155084f978bd9e8e57e89b569b4d7f0f0c17a448ffe", + "sha256:7a43cfdcddd07f4ca2b1afb459824dd3c6d53a51410636a2c7fc97b9a8cf4842", + "sha256:7bd6abf1e070a6b72bfeb71049d6ad286852e285f146682bf30d0296f5fbadfa", + "sha256:7c1e90228c2f5cdde263253fa5db63e6653f1c00e7ec64108065a0b9713fa1b3", + "sha256:7c65ffa900e7cc958cd088b9a9157a8141c991f8c53d11087e6fb7277a03f81d", + "sha256:80c421e07973a89fbdd93e6f2003c17d20b69010458d3a8e37fb47874bd67d51", + "sha256:82d0ba76371769d6a4e56f7e83bb8e81846d17a6190971e38b5de108bde9b0d7", + "sha256:83f91d8a9bb404b8c2c41a707ac7f7f75b9442a0a876df295de27251a856ad09", + "sha256:87c6e35319b46b99e168eb98472d6c7d8634ee37750d7693656dc766395df096", + "sha256:8d23b88b9388ed85c6faf0e74d8dec4f4d3baf3ecf20a65a47b836d56260d4b9", + "sha256:9156c45750b37337f7b0b00e6248991a047be4aa44554c9886fe6bdd605aab3b", + "sha256:91a0fa841646320ec0d3accdff5b757b06e2e5c86ba32af2e0815c96c7a603c5", + "sha256:95858ca14a9f6fa8413d29e0a585b31b278388aa775b8a81fa24830123874678", + "sha256:95df24ca1e1bd93bbca51d94dd049a984609687cb2fb08a7f2c56ac84e9816ea", + "sha256:9b37c184f8b976f0c0a231a5f3d6efe10807d41ccbe4488df8c74174805eea7d", + "sha256:9b6f347deb3dcfbfde1c20baa21c2ac0751afaa73e64e5b693bb2b848efeaa49", + "sha256:9d75baf00138f80b48f1eac72ad1535aac0b6461265a0bcad391fc5aba875cfc", + "sha256:9ef8aa8bdbac47f4968a5d66462a2a0935d044bf35c0e5a8af152d58516dbeb5", + "sha256:a11e38ad8922c7961447f35c7b17bffa15de4d17c70abd07bfbe12d6faa3e027", + "sha256:a1b54689e38d1279a51d11e3467dd2f3a50f5f2e879012ce8f2d6943f00e83f0", + "sha256:a3b3366087c1bc0a2795111edcadddb8b3b59509d5db5d7ea3fdd69f954a8878", + "sha256:a569eb1b05d72f9bce2ebd28a1ce2054311b66677fcd46cf36204ad23acead8c", + "sha256:a7affedeb43a70351bb811dadf49493c9cfd1ed94c9c70095fd177e9cc1541fa", + "sha256:a9a396a6ad26130cdae92ae10c36af09d9bfe6cafe69670fd3b6da9b07b4044f", + "sha256:a9ab1e71d3d2e54a0aa646ab6d4eebfaa5f416fe78dfe4da2839525dc5d765c6", + "sha256:a9cd1af7e18e5221d2878378fbc287a14cd527fdd5939ed56a18df8a31136bb2", + "sha256:a9dcaf8b0cc72a392760bb8755922c03e17a5a54e08cca58e8b74f6902b433cf", + "sha256:b9d7439d7fab4dce00570bb906875734df13d9faa4b48e261c440a5fec6d9708", + "sha256:bcc03c8b72267e97b49149e4863d57c2d77f13fae12066622dc78fe322490fe6", + "sha256:c11d4d16e133f6df8916cc5b7e3e96ee4c44c936717d684a94f48f82edb7c92f", + "sha256:c1dca61c6db1166c48b95198c0b7d9c990b30c756fc2923cc66f68d17dc558fd", + "sha256:c518e84bb59c2baae725accd355c8dc517b4a3ed8db88b4bc93c78dae2974bf2", + "sha256:c7934fd0e920e70468e676fe7f1b7261c1efa0d6c037c6722278ca0228ad9d0d", + "sha256:c7e72ce6bda6fb9409cc1e8164dd41d7c91466fb599eb047cfda72fe758a34a7", + "sha256:c90d6dec6be2c7d03378a574de87af9b1efea77d0c52a8301dd831ece938452f", + "sha256:ceec59f59d092c5007e815def4ebb80c2de330e9588e101cf8bd94c143ec78a5", + "sha256:cf1781ef73c073e6b0f90af841aaf98501f975d306bbf6221683dd594ccc52b6", + "sha256:d04f13a1d75cb2b8382bdc16ae6fa58c97337253826dfe136195b7f89f661557", + "sha256:d6d300f8ec35c24025ceb9b9019ae9040c1ab2f01cddc2bcc0b518af31c75c14", + "sha256:d8dbb1bf0c0a4ae8b40bdc9be7f644e2f3fb4e8a9aca7145bfa510d4a374eeb7", + "sha256:de58647e3f9c42f13f90ac7e5f58900c80a39019848c5547bc691693098ae1bd", + "sha256:deeb929efe52bed518f6eb2ddc00cc496366a14c726005726ad62c2dd9017a3c", + "sha256:df01aea34b6e9e33572c35cd16bae5a47785e7d5c8cb2b54b2acdb9678315a17", + "sha256:e2620453c075abeb0daa949a292e19f56de518988e079c36478bacf9546ced23", + "sha256:e4450fc83a3df53dec45922b576e91e94f5578d06436871dce3a6be38e40f5db", + "sha256:e54affdeb21026329fb0744ad187cf812f7d3c2aa702a5edb562b325191fcab6", + "sha256:e9875a0143f07d74dc5e1ded1c4581f0d9f7ab86c78994e2ed9e95050073c94d", + "sha256:f1c3cf67185543730888b20682fb186fc8d0fa6f07ccc3ef4390831ab4b388d9", + "sha256:f48c749857f8fb598fb890a75f540e3221d0976ed0bf879cf3c7eef34151acee", + "sha256:f779498eeec470295a2b1a5d97aa1bc9814ecd25e1eb637bd9d1c73a327387f6" ], - "version": "==13.0.1" + "version": "==13.1" }, "whitenoise": { "hashes": [ @@ -2584,11 +2603,11 @@ "develop": { "anyio": { "hashes": [ - "sha256:5aadc6a1bbb7cdb0bede386cac5e2940f5e2ff3aa20277e991cf028e0585ce94", - "sha256:c1b2d8f46a8a812513012e1107cb0e68c17159a7a594208005a57dc776e1bdc7" + "sha256:137b4559cbb034c477165047febb6ff83f390fc3b20bf181c1fc0a728cb8beeb", + "sha256:c7d2e9d63e31599eeb636c8c5c03a7e108d73b345f064f1c19fdc87b79036a9a" ], - "markers": "python_version >= '3.8'", - "version": "==4.4.0" + "markers": "python_version >= '3.9'", + "version": "==4.6.0" }, "asgiref": { "hashes": [ @@ -2996,20 +3015,20 @@ }, "faker": { "hashes": [ - "sha256:4294d169255a045990720d6f3fa4134b764a4cdf46ef0d3c7553d2506f1adaa1", - "sha256:e59c01d1e8b8e20a83255ab8232c143cb2af3b4f5ab6a3f5ce495f385ad8ab4c" + "sha256:bf0207af5777950054a2a3b43f4b5bdc33b585918d2b28f1dab52ac0ffe2bac0", + "sha256:f0a60009150736c1c033bea31aa19ae63071c9dcf10adfaf9f1a87a3add84bc8" ], "markers": "python_version >= '3.8'", - "version": "==28.4.1" + "version": "==30.0.0" }, "filelock": { "hashes": [ - "sha256:81de9eb8453c769b63369f87f11131a7ab04e367f8d97ad39dc230daa07e3bec", - "sha256:f6ed4c963184f4c84dd5557ce8fece759a3724b37b80c6c4f20a2f63a4dc6609" + "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0", + "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==3.16.0" + "version": "==3.16.1" }, "ghp-import": { "hashes": [ @@ -3054,19 +3073,19 @@ }, "identify": { "hashes": [ - "sha256:cb171c685bdc31bcc4c1734698736a7d5b6c8bf2e0c15117f4d469c8640ae5cf", - "sha256:e79ae4406387a9d300332b5fd366d8994f1525e8414984e1a59e058b2eda2dd0" + "sha256:53863bcac7caf8d2ed85bd20312ea5dcfc22226800f6d6881f232d861db5a8f0", + "sha256:91478c5fb7c3aac5ff7bf9b4344f803843dc586832d5f110d672b19aa1984c98" ], "markers": "python_version >= '3.8'", - "version": "==2.6.0" + "version": "==2.6.1" }, "idna": { "hashes": [ - "sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac", - "sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603" + "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", + "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3" ], "markers": "python_version >= '3.6'", - "version": "==3.8" + "version": "==3.10" }, "imagehash": { "hashes": [ @@ -3076,14 +3095,6 @@ "index": "pypi", "version": "==4.3.1" }, - "importlib-metadata": { - "hashes": [ - "sha256:66f342cc6ac9818fc6ff340576acd24d65ba0b3efabb2b4ac08b598965a4a2f1", - "sha256:9a547d3bc3608b025f93d403fdd1aae741c24fbb8314df4b155675742ce303c5" - ], - "markers": "python_version < '3.10'", - "version": "==8.4.0" - }, "incremental": { "hashes": [ "sha256:8cb2c3431530bec48ad70513931a760f446ad6c25e8333ca5d95e24b0ed7b8fe", @@ -3216,12 +3227,12 @@ }, "mkdocs-material": { "hashes": [ - "sha256:1e60ddf716cfb5679dfd65900b8a25d277064ed82d9a53cd5190e3f894df7840", - "sha256:54caa8be708de2b75167fd4d3b9f3d949579294f49cb242515d4653dbee9227e" + "sha256:1843c5171ad6b489550aeaf7358e5b7128cc03ddcf0fb4d91d19aa1e691a63b8", + "sha256:d4779051d52ba9f1e7e344b34de95449c7c366c212b388e4a2db9a3db043c228" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==9.5.34" + "version": "==9.5.38" }, "mkdocs-material-extensions": { "hashes": [ @@ -3241,54 +3252,62 @@ }, "numpy": { "hashes": [ - "sha256:0123ffdaa88fa4ab64835dcbde75dcdf89c453c922f18dced6e27c90d1d0ec5a", - "sha256:11a76c372d1d37437857280aa142086476136a8c0f373b2e648ab2c8f18fb195", - "sha256:13e689d772146140a252c3a28501da66dfecd77490b498b168b501835041f951", - "sha256:1e795a8be3ddbac43274f18588329c72939870a16cae810c2b73461c40718ab1", - "sha256:26df23238872200f63518dd2aa984cfca675d82469535dc7162dc2ee52d9dd5c", - "sha256:286cd40ce2b7d652a6f22efdfc6d1edf879440e53e76a75955bc0c826c7e64dc", - "sha256:2b2955fa6f11907cf7a70dab0d0755159bca87755e831e47932367fc8f2f2d0b", - "sha256:2da5960c3cf0df7eafefd806d4e612c5e19358de82cb3c343631188991566ccd", - "sha256:312950fdd060354350ed123c0e25a71327d3711584beaef30cdaa93320c392d4", - "sha256:423e89b23490805d2a5a96fe40ec507407b8ee786d66f7328be214f9679df6dd", - "sha256:496f71341824ed9f3d2fd36cf3ac57ae2e0165c143b55c3a035ee219413f3318", - "sha256:49ca4decb342d66018b01932139c0961a8f9ddc7589611158cb3c27cbcf76448", - "sha256:51129a29dbe56f9ca83438b706e2e69a39892b5eda6cedcb6b0c9fdc9b0d3ece", - "sha256:5fec9451a7789926bcf7c2b8d187292c9f93ea30284802a0ab3f5be8ab36865d", - "sha256:671bec6496f83202ed2d3c8fdc486a8fc86942f2e69ff0e986140339a63bcbe5", - "sha256:7f0a0c6f12e07fa94133c8a67404322845220c06a9e80e85999afe727f7438b8", - "sha256:807ec44583fd708a21d4a11d94aedf2f4f3c3719035c76a2bbe1fe8e217bdc57", - "sha256:883c987dee1880e2a864ab0dc9892292582510604156762362d9326444636e78", - "sha256:8c5713284ce4e282544c68d1c3b2c7161d38c256d2eefc93c1d683cf47683e66", - "sha256:8cafab480740e22f8d833acefed5cc87ce276f4ece12fdaa2e8903db2f82897a", - "sha256:8df823f570d9adf0978347d1f926b2a867d5608f434a7cff7f7908c6570dcf5e", - "sha256:9059e10581ce4093f735ed23f3b9d283b9d517ff46009ddd485f1747eb22653c", - "sha256:905d16e0c60200656500c95b6b8dca5d109e23cb24abc701d41c02d74c6b3afa", - "sha256:9189427407d88ff25ecf8f12469d4d39d35bee1db5d39fc5c168c6f088a6956d", - "sha256:96a55f64139912d61de9137f11bf39a55ec8faec288c75a54f93dfd39f7eb40c", - "sha256:97032a27bd9d8988b9a97a8c4d2c9f2c15a81f61e2f21404d7e8ef00cb5be729", - "sha256:984d96121c9f9616cd33fbd0618b7f08e0cfc9600a7ee1d6fd9b239186d19d97", - "sha256:9a92ae5c14811e390f3767053ff54eaee3bf84576d99a2456391401323f4ec2c", - "sha256:9ea91dfb7c3d1c56a0e55657c0afb38cf1eeae4544c208dc465c3c9f3a7c09f9", - "sha256:a15f476a45e6e5a3a79d8a14e62161d27ad897381fecfa4a09ed5322f2085669", - "sha256:a392a68bd329eafac5817e5aefeb39038c48b671afd242710b451e76090e81f4", - "sha256:a3f4ab0caa7f053f6797fcd4e1e25caee367db3112ef2b6ef82d749530768c73", - "sha256:a46288ec55ebbd58947d31d72be2c63cbf839f0a63b49cb755022310792a3385", - "sha256:a61ec659f68ae254e4d237816e33171497e978140353c0c2038d46e63282d0c8", - "sha256:a842d573724391493a97a62ebbb8e731f8a5dcc5d285dfc99141ca15a3302d0c", - "sha256:becfae3ddd30736fe1889a37f1f580e245ba79a5855bff5f2a29cb3ccc22dd7b", - "sha256:c05e238064fc0610c840d1cf6a13bf63d7e391717d247f1bf0318172e759e692", - "sha256:c1c9307701fec8f3f7a1e6711f9089c06e6284b3afbbcd259f7791282d660a15", - "sha256:c7b0be4ef08607dd04da4092faee0b86607f111d5ae68036f16cc787e250a131", - "sha256:cfd41e13fdc257aa5778496b8caa5e856dc4896d4ccf01841daee1d96465467a", - "sha256:d731a1c6116ba289c1e9ee714b08a8ff882944d4ad631fd411106a30f083c326", - "sha256:df55d490dea7934f330006d0f81e8551ba6010a5bf035a249ef61a94f21c500b", - "sha256:ec9852fb39354b5a45a80bdab5ac02dd02b15f44b3804e9f00c556bf24b4bded", - "sha256:f15975dfec0cf2239224d80e32c3170b1d168335eaedee69da84fbe9f1f9cd04", - "sha256:f26b258c385842546006213344c50655ff1555a9338e2e5e02a0756dc3e803dd" + "sha256:046356b19d7ad1890c751b99acad5e82dc4a02232013bd9a9a712fddf8eb60f5", + "sha256:0b8cc2715a84b7c3b161f9ebbd942740aaed913584cae9cdc7f8ad5ad41943d0", + "sha256:0d07841fd284718feffe7dd17a63a2e6c78679b2d386d3e82f44f0108c905550", + "sha256:13cc11c00000848702322af4de0147ced365c81d66053a67c2e962a485b3717c", + "sha256:13ce49a34c44b6de5241f0b38b07e44c1b2dcacd9e36c30f9c2fcb1bb5135db7", + "sha256:24c2ad697bd8593887b019817ddd9974a7f429c14a5469d7fad413f28340a6d2", + "sha256:251105b7c42abe40e3a689881e1793370cc9724ad50d64b30b358bbb3a97553b", + "sha256:2ca4b53e1e0b279142113b8c5eb7d7a877e967c306edc34f3b58e9be12fda8df", + "sha256:3269c9eb8745e8d975980b3a7411a98976824e1fdef11f0aacf76147f662b15f", + "sha256:397bc5ce62d3fb73f304bec332171535c187e0643e176a6e9421a6e3eacef06d", + "sha256:3fc5eabfc720db95d68e6646e88f8b399bfedd235994016351b1d9e062c4b270", + "sha256:50a95ca3560a6058d6ea91d4629a83a897ee27c00630aed9d933dff191f170cd", + "sha256:52ac2e48f5ad847cd43c4755520a2317f3380213493b9d8a4c5e37f3b87df504", + "sha256:53e27293b3a2b661c03f79aa51c3987492bd4641ef933e366e0f9f6c9bf257ec", + "sha256:57eb525e7c2a8fdee02d731f647146ff54ea8c973364f3b850069ffb42799647", + "sha256:5889dd24f03ca5a5b1e8a90a33b5a0846d8977565e4ae003a63d22ecddf6782f", + "sha256:59ca673ad11d4b84ceb385290ed0ebe60266e356641428c845b39cd9df6713ab", + "sha256:6435c48250c12f001920f0751fe50c0348f5f240852cfddc5e2f97e007544cbe", + "sha256:6e5a9cb2be39350ae6c8f79410744e80154df658d5bea06e06e0ac5bb75480d5", + "sha256:7be6a07520b88214ea85d8ac8b7d6d8a1839b0b5cb87412ac9f49fa934eb15d5", + "sha256:7c803b7934a7f59563db459292e6aa078bb38b7ab1446ca38dd138646a38203e", + "sha256:7dd86dfaf7c900c0bbdcb8b16e2f6ddf1eb1fe39c6c8cca6e94844ed3152a8fd", + "sha256:8661c94e3aad18e1ea17a11f60f843a4933ccaf1a25a7c6a9182af70610b2313", + "sha256:8ae0fd135e0b157365ac7cc31fff27f07a5572bdfc38f9c2d43b2aff416cc8b0", + "sha256:910b47a6d0635ec1bd53b88f86120a52bf56dcc27b51f18c7b4a2e2224c29f0f", + "sha256:913cc1d311060b1d409e609947fa1b9753701dac96e6581b58afc36b7ee35af6", + "sha256:920b0911bb2e4414c50e55bd658baeb78281a47feeb064ab40c2b66ecba85553", + "sha256:950802d17a33c07cba7fd7c3dcfa7d64705509206be1606f196d179e539111ed", + "sha256:981707f6b31b59c0c24bcda52e5605f9701cb46da4b86c2e8023656ad3e833cb", + "sha256:98ce7fb5b8063cfdd86596b9c762bf2b5e35a2cdd7e967494ab78a1fa7f8b86e", + "sha256:99f4a9ee60eed1385a86e82288971a51e71df052ed0b2900ed30bc840c0f2e39", + "sha256:9a8e06c7a980869ea67bbf551283bbed2856915f0a792dc32dd0f9dd2fb56728", + "sha256:ae8ce252404cdd4de56dcfce8b11eac3c594a9c16c231d081fb705cf23bd4d9e", + "sha256:afd9c680df4de71cd58582b51e88a61feed4abcc7530bcd3d48483f20fc76f2a", + "sha256:b49742cdb85f1f81e4dc1b39dcf328244f4d8d1ded95dea725b316bd2cf18c95", + "sha256:b5613cfeb1adfe791e8e681128f5f49f22f3fcaa942255a6124d58ca59d9528f", + "sha256:bab7c09454460a487e631ffc0c42057e3d8f2a9ddccd1e60c7bb8ed774992480", + "sha256:c8a0e34993b510fc19b9a2ce7f31cb8e94ecf6e924a40c0c9dd4f62d0aac47d9", + "sha256:caf5d284ddea7462c32b8d4a6b8af030b6c9fd5332afb70e7414d7fdded4bfd0", + "sha256:cea427d1350f3fd0d2818ce7350095c1a2ee33e30961d2f0fef48576ddbbe90f", + "sha256:d0cf7d55b1051387807405b3898efafa862997b4cba8aa5dbe657be794afeafd", + "sha256:d10c39947a2d351d6d466b4ae83dad4c37cd6c3cdd6d5d0fa797da56f710a6ae", + "sha256:d2b9cd92c8f8e7b313b80e93cedc12c0112088541dcedd9197b5dee3738c1201", + "sha256:d4c57b68c8ef5e1ebf47238e99bf27657511ec3f071c465f6b1bccbef12d4136", + "sha256:d51fc141ddbe3f919e91a096ec739f49d686df8af254b2053ba21a910ae518bf", + "sha256:e097507396c0be4e547ff15b13dc3866f45f3680f789c1a1301b07dadd3fbc78", + "sha256:e30356d530528a42eeba51420ae8bf6c6c09559051887196599d96ee5f536468", + "sha256:e8d5f8a8e3bc87334f025194c6193e408903d21ebaeb10952264943a985066ca", + "sha256:e8dfa9e94fc127c40979c3eacbae1e61fda4fe71d84869cc129e2721973231ef", + "sha256:f212d4f46b67ff604d11fff7cc62d36b3e8714edf68e44e9760e19be38c03eb0", + "sha256:f7506387e191fe8cdb267f912469a3cccc538ab108471291636a96a54e599556", + "sha256:fac6e277a41163d27dfab5f4ec1f7a83fac94e170665a4a50191b545721c6521", + "sha256:fcd8f556cdc8cfe35e70efb92463082b7f43dd7e547eb071ffc36abc0ca4699b" ], - "markers": "python_version >= '3.9'", - "version": "==2.0.2" + "markers": "python_version >= '3.10'", + "version": "==2.1.1" }, "packaging": { "hashes": [ @@ -3401,11 +3420,11 @@ }, "platformdirs": { "hashes": [ - "sha256:9e5e27a08aa095dd127b9f2e764d74254f482fef22b0970773bfba79d091ab8c", - "sha256:eb1c8582560b34ed4ba105009a4badf7f6f85768b30126f351328507b2beb617" + "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", + "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb" ], "markers": "python_version >= '3.8'", - "version": "==4.3.2" + "version": "==4.3.6" }, "pluggy": { "hashes": [ @@ -3426,19 +3445,19 @@ }, "pyasn1": { "hashes": [ - "sha256:3a35ab2c4b5ef98e17dfdec8ab074046fbda76e281c5a706ccd82328cfc8f64c", - "sha256:cca4bb0f2df5504f02f6f8a775b6e416ff9b0b3b16f7ee80b5a3153d9b804473" + "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629", + "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034" ], "markers": "python_version >= '3.8'", - "version": "==0.6.0" + "version": "==0.6.1" }, "pyasn1-modules": { "hashes": [ - "sha256:831dbcea1b177b28c9baddf4c6d1013c24c3accd14a1873fffaa6a2e905f17b6", - "sha256:be04f15b66c206eed667e0bb5ab27e2b1855ea54a842e5037738099e8ca4ae0b" + "sha256:49bfa96b45a292b711e986f222502c1c9a5e1f4e568fc30e2574a6c7d07838fd", + "sha256:c28e2dbf9c06ad61c71a075c7e0f9fd0f1b0bb2d2ad4377f240d33ac2ab60a7c" ], "markers": "python_version >= '3.8'", - "version": "==0.4.0" + "version": "==0.4.1" }, "pycparser": { "hashes": [ @@ -3458,11 +3477,11 @@ }, "pymdown-extensions": { "hashes": [ - "sha256:6ff740bcd99ec4172a938970d42b96128bdc9d4b9bcad72494f29921dc69b753", - "sha256:d323f7e90d83c86113ee78f3fe62fc9dee5f56b54d912660703ea1816fed5626" + "sha256:513a9e9432b197cf0539356c8f1fc376e0d10b70ad150cadeb649a5628aacd45", + "sha256:65d82324ef2497931bc858c8320540c6264ab0d9a292707edb61f4fe0cd56633" ], "markers": "python_version >= '3.8'", - "version": "==10.9" + "version": "==10.10.2" }, "pyopenssl": { "hashes": [ @@ -3500,12 +3519,12 @@ }, "pytest-env": { "hashes": [ - "sha256:86653658da8f11c6844975db955746c458a9c09f1e64957603161e2ff93f5133", - "sha256:a4212056d4d440febef311a98fdca56c31256d58fb453d103cba4e8a532b721d" + "sha256:91209840aa0e43385073ac464a554ad2947cc2fd663a9debf88d03b01e0cc1cf", + "sha256:ce90cf8772878515c24b31cd97c7fa1f4481cd68d588419fd45f10ecaee6bc30" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==1.1.4" + "version": "==1.1.5" }, "pytest-httpx": { "hashes": [ @@ -3562,42 +3581,45 @@ }, "pywavelets": { "hashes": [ - "sha256:058a750477dde633ac53b8806f835af3559d52db6532fb2b93c1f4b5441365b8", - "sha256:0595c51472c9c5724fe087cb73e2797053fd25c788d6553fdad6ff61abc60e91", - "sha256:138471513bc0a4cd2ddc4e50c7ec04e3468c268e101a0d02f698f6aedd1d5e79", - "sha256:274bc47b289585383aa65519b3fcae5b4dee5e31db3d4198d4fad701a70e59f7", - "sha256:32198de321892743c1a3d1957fe1cd8a8ecc078bfbba6b8f3982518e897271d7", - "sha256:3b5302edb6d1d1ff6636d37c9ff29c4892f2a3648d736cc1df01f3f36e25c8cf", - "sha256:4021ef69ec9f3862f66580fc4417be728bd78722914394594b48212fd1fcaf21", - "sha256:42a22e68e345b6de7d387ef752111ab4530c98048d2b4bdac8ceefb078b4ead6", - "sha256:47b0314a22616c5f3f08760f0e00b4a15b7c7dadca5e39bb701cf7869a4207c5", - "sha256:47de024ba4f9df97e98b5f540340e1a9edd82d2c477450bef8c9b5381487128e", - "sha256:48b3813c6d1a7a8194f37dbb5dbbdf2fe1112152c91445ea2e54f64ff6350c36", - "sha256:4ef15a63a72afa67ae9f4f3b06c95c5382730fb3075e668d49a880e65f2f089c", - "sha256:4ffb484d096a5eb10af7121e0203546a03e1369328df321a33ef91f67bac40cf", - "sha256:538795d9c4181152b414285b5a7f72ac52581ecdcdce74b6cca3fa0b8a5ab0aa", - "sha256:627df378e63e9c789b6f2e7060cb4264ebae6f6b0efc1da287a2c060de454a1f", - "sha256:67936491ae3e5f957c428e34fdaed21f131535b8d60c7c729a1b539ce8864837", - "sha256:78feab4e0c25fa32034b6b64cb854c6ce15663b4f0ffb25d8f0ee58915300f9b", - "sha256:8fbf7b61b28b5457693c034e58a01622756d1fd60a80ae13ac5888b1d3e57e80", - "sha256:97ea9613bd6b7108ebb44b709060adc7e2d5fac73be7152342bdd5513d75f84e", - "sha256:9ec7d69b746a0eaa327b829a3252a63619f2345e263177be5dd9bf30d7933c8d", - "sha256:a413b51dc19e05243fe0b0864a8e8a16b5ca9bf2e4713da00a95b1b5747a5367", - "sha256:ab652112d3932d21f020e281e06926a751354c2b5629fb716f5eb9d0104b84e5", - "sha256:be36f08efe9bc3abf40cf40cd2ee0aa0db26e4894e13ce5ac178442864161e8c", - "sha256:be615c6c1873e189c265d4a76d1751ec49b17e29725e6dd2e9c74f1868f590b7", - "sha256:c5e655446e37a3c87213d5c6386b86f65c4d61736b4432d720171e7dd6523d6a", - "sha256:d6ec113386a432e04103f95e351d2657b42145bd1e1ed26513423391bcb5f011", - "sha256:d91aaaf6de53b758bcdc96c81cdb5a8607758602be49f691188c0e108cf1e738", - "sha256:dd798cee3d28fb3d32a26a00d9831a20bf316c36d685e4ced01b4e4a8f36f5ce", - "sha256:ddc1ff5ad706313d930f857f9656f565dfb81b85bbe58a9db16ad8fa7d1537c5", - "sha256:e2c44760c0906ddf2176920a2613287f6eea947f166ce7eee9546081b06a6835", - "sha256:e772f7f0c16bfc3be8ac3cd10d29a9920bb7a39781358856223c491b899e6e79", - "sha256:ea027c70977122c5fc27b2510f0a0d9528f9c3df6ea3e4c577ca55fd00325a5b", - "sha256:f58ddbb0a6cd243928876edfc463b990763a24fb94498607d6fea690e32cca4c" + "sha256:05dc2930cf9b7f61a24b2fe52b18e9d6046012fc46fc360355222781a95a1378", + "sha256:097bd03ee1b687942fa2f82ad0d35849879eef0ac82fc6f757d6ef881c53db6d", + "sha256:0b37212b7524438f694cb619cc4a0a3dc54ad77b63a18d0e8e6364f525fffd91", + "sha256:0bd2611076f5d2c4ad940421bbb3c450b6a53d8ca24bde02662455dc67c70dac", + "sha256:0cd599c78fc240cbadb63344d73912fc79e8dccbb0db8a8bd5143df400c3a519", + "sha256:0f402424288178fd105a5cb76e1818649dc67e4a08d1b9974c8c7ef01dc5feb3", + "sha256:0fc5e0e592678e43c18dd169b0d8471e9a5ffb5eb7ff4bdc8f447c882f78aa8b", + "sha256:105249d2bf824bddfb286e4e08934ff1e8829aa3077dab74ce3b2921a09caa43", + "sha256:1a550fdbe134040c04f1bb46cfe13a1a903c5dce13090b681106e4db99feba81", + "sha256:259ccf233879cf0ed66052ffd174dcabe6314e92b53aa2de25f4ae50b08ea1e3", + "sha256:29a912c074977db6adf3782dfbd414945805039b755d0c23979bc823f1b4e9c3", + "sha256:3740c84de06fab5081c8f08994f12f9ee94dc2eb4d818eaeace3bdb0b838e2fc", + "sha256:392553248aed33eac6f38647acacdba94dd6a8f283319c2d9852de7a871d6d0f", + "sha256:3e3c8c0fa44f4de7bf05c5d12883b227aaf6dcf46deb3f6f5a9fa5bb79c33283", + "sha256:40ebb994b332d48db3b0564e3c335c4f8ba236283939f5167de099766cf16517", + "sha256:4a2a8cc39901f09d82fc94007026f9aed63876e334ae043eb26caa601aee2551", + "sha256:4ff81dd8288afdd5f2eae6c44f963152b41e14e2e5fc647b608c97bd6f8270fe", + "sha256:5b7e1a212269d3e48318388744684b702c6a649a70758e35e9a88614316e9b91", + "sha256:6a322607b8c2985997ea45317d36cab58f0223ccf4c5b6540b612ed067d099ff", + "sha256:6ad14d8b5a412a621406276b8ae8ee1e369ba7a7f8e517fb87355bcb8106820f", + "sha256:71918b973950c013c17ff28c3fc2958dfff68ec767ef60cd927a3ac4ff5a7345", + "sha256:74e838e0225783f37ae346e60a9f783b4a31adc5731b9cb6d687ee5c93bd87b7", + "sha256:8565de589f42283bca17ddca298f1188a26ef8ee75cadc4a4744cadf5a79cfdf", + "sha256:8bdab6b1781f01c087c54782d656a4fc1df77796c241f122445adcbb24892839", + "sha256:953b877c43f1fa53204b1b0eedd04efa6739378a873e79fa34ee5296d47a9ca1", + "sha256:a0d8c641aa26e040d62166cbe2052dd3cd575e3e0c78c00c52770be6d7dd386b", + "sha256:a469a7e73f5ab1d59b52a525a89a4a280426d1ba08eb081261f8bc6775f101d6", + "sha256:ae3ae86ba69d75327b1c5cd368138fb9329bc7eb7418d6b0ce9504c5070974ef", + "sha256:b47250e5bb853e37db5db423bafc82847f4cde0ffdf7aebb06336a993bc174f6", + "sha256:badb7dc70ecd8042ddd98fdd41803d5e5b28bf7c90910bb1751906812326ab54", + "sha256:c7b47d94aefe6e03085f4d9ce74f6133741164d470ac2839af9906686c6c2ed1", + "sha256:d5fc7fbad53379c30b2c9d46c235130a4b96e0597653e32e7680a310da06bd07", + "sha256:d81d2486e4f9b65f7c6cab252f3e706c8e8e72bbd0311f72c1a5ec56c947d257", + "sha256:d99156b461f914cafbe6ee3b511612a83e90061addbe1f2660f522e9841fbdc4", + "sha256:e0611ffb6ceeee1b677bd224e657895193eec03ad39538f5263ce61db465f836", + "sha256:eac60fdb28bd421f72eb18824bd2e4f36c3dab0d7f4802ebfe4bbf68744a524a" ], - "markers": "python_version >= '3.9'", - "version": "==1.6.0" + "markers": "python_version >= '3.10'", + "version": "==1.7.0" }, "pyyaml": { "hashes": [ @@ -3667,88 +3689,103 @@ }, "regex": { "hashes": [ - "sha256:01b689e887f612610c869421241e075c02f2e3d1ae93a037cb14f88ab6a8934c", - "sha256:04ce29e2c5fedf296b1a1b0acc1724ba93a36fb14031f3abfb7abda2806c1535", - "sha256:0ffe3f9d430cd37d8fa5632ff6fb36d5b24818c5c986893063b4e5bdb84cdf24", - "sha256:18300a1d78cf1290fa583cd8b7cde26ecb73e9f5916690cf9d42de569c89b1ce", - "sha256:185e029368d6f89f36e526764cf12bf8d6f0e3a2a7737da625a76f594bdfcbfc", - "sha256:19c65b00d42804e3fbea9708f0937d157e53429a39b7c61253ff15670ff62cb5", - "sha256:228b0d3f567fafa0633aee87f08b9276c7062da9616931382993c03808bb68ce", - "sha256:23acc72f0f4e1a9e6e9843d6328177ae3074b4182167e34119ec7233dfeccf53", - "sha256:25419b70ba00a16abc90ee5fce061228206173231f004437730b67ac77323f0d", - "sha256:2dfbb8baf8ba2c2b9aa2807f44ed272f0913eeeba002478c4577b8d29cde215c", - "sha256:2f1baff13cc2521bea83ab2528e7a80cbe0ebb2c6f0bfad15be7da3aed443908", - "sha256:33e2614a7ce627f0cdf2ad104797d1f68342d967de3695678c0cb84f530709f8", - "sha256:3426de3b91d1bc73249042742f45c2148803c111d1175b283270177fdf669024", - "sha256:382281306e3adaaa7b8b9ebbb3ffb43358a7bbf585fa93821300a418bb975281", - "sha256:3d974d24edb231446f708c455fd08f94c41c1ff4f04bcf06e5f36df5ef50b95a", - "sha256:3f3b6ca8eae6d6c75a6cff525c8530c60e909a71a15e1b731723233331de4169", - "sha256:3fac296f99283ac232d8125be932c5cd7644084a30748fda013028c815ba3364", - "sha256:416c0e4f56308f34cdb18c3f59849479dde5b19febdcd6e6fa4d04b6c31c9faa", - "sha256:438d9f0f4bc64e8dea78274caa5af971ceff0f8771e1a2333620969936ba10be", - "sha256:43affe33137fcd679bdae93fb25924979517e011f9dea99163f80b82eadc7e53", - "sha256:44fc61b99035fd9b3b9453f1713234e5a7c92a04f3577252b45feefe1b327759", - "sha256:45104baae8b9f67569f0f1dca5e1f1ed77a54ae1cd8b0b07aba89272710db61e", - "sha256:4fdd1384619f406ad9037fe6b6eaa3de2749e2e12084abc80169e8e075377d3b", - "sha256:538d30cd96ed7d1416d3956f94d54e426a8daf7c14527f6e0d6d425fcb4cca52", - "sha256:558a57cfc32adcf19d3f791f62b5ff564922942e389e3cfdb538a23d65a6b610", - "sha256:5eefee9bfe23f6df09ffb6dfb23809f4d74a78acef004aa904dc7c88b9944b05", - "sha256:64bd50cf16bcc54b274e20235bf8edbb64184a30e1e53873ff8d444e7ac656b2", - "sha256:65fd3d2e228cae024c411c5ccdffae4c315271eee4a8b839291f84f796b34eca", - "sha256:66b4c0731a5c81921e938dcf1a88e978264e26e6ac4ec96a4d21ae0354581ae0", - "sha256:68a8f8c046c6466ac61a36b65bb2395c74451df2ffb8458492ef49900efed293", - "sha256:6a1141a1dcc32904c47f6846b040275c6e5de0bf73f17d7a409035d55b76f289", - "sha256:6b9fc7e9cc983e75e2518496ba1afc524227c163e43d706688a6bb9eca41617e", - "sha256:6f51f9556785e5a203713f5efd9c085b4a45aecd2a42573e2b5041881b588d1f", - "sha256:7214477bf9bd195894cf24005b1e7b496f46833337b5dedb7b2a6e33f66d962c", - "sha256:731fcd76bbdbf225e2eb85b7c38da9633ad3073822f5ab32379381e8c3c12e94", - "sha256:74007a5b25b7a678459f06559504f1eec2f0f17bca218c9d56f6a0a12bfffdad", - "sha256:7a5486ca56c8869070a966321d5ab416ff0f83f30e0e2da1ab48815c8d165d46", - "sha256:7c479f5ae937ec9985ecaf42e2e10631551d909f203e31308c12d703922742f9", - "sha256:7df9ea48641da022c2a3c9c641650cd09f0cd15e8908bf931ad538f5ca7919c9", - "sha256:7e37e809b9303ec3a179085415cb5f418ecf65ec98cdfe34f6a078b46ef823ee", - "sha256:80c811cfcb5c331237d9bad3bea2c391114588cf4131707e84d9493064d267f9", - "sha256:836d3cc225b3e8a943d0b02633fb2f28a66e281290302a79df0e1eaa984ff7c1", - "sha256:84c312cdf839e8b579f504afcd7b65f35d60b6285d892b19adea16355e8343c9", - "sha256:86b17ba823ea76256b1885652e3a141a99a5c4422f4a869189db328321b73799", - "sha256:871e3ab2838fbcb4e0865a6e01233975df3a15e6fce93b6f99d75cacbd9862d1", - "sha256:88ecc3afd7e776967fa16c80f974cb79399ee8dc6c96423321d6f7d4b881c92b", - "sha256:8bc593dcce679206b60a538c302d03c29b18e3d862609317cb560e18b66d10cf", - "sha256:8fd5afd101dcf86a270d254364e0e8dddedebe6bd1ab9d5f732f274fa00499a5", - "sha256:945352286a541406f99b2655c973852da7911b3f4264e010218bbc1cc73168f2", - "sha256:973335b1624859cb0e52f96062a28aa18f3a5fc77a96e4a3d6d76e29811a0e6e", - "sha256:994448ee01864501912abf2bad9203bffc34158e80fe8bfb5b031f4f8e16da51", - "sha256:9cfd009eed1a46b27c14039ad5bbc5e71b6367c5b2e6d5f5da0ea91600817506", - "sha256:a2ec4419a3fe6cf8a4795752596dfe0adb4aea40d3683a132bae9c30b81e8d73", - "sha256:a4997716674d36a82eab3e86f8fa77080a5d8d96a389a61ea1d0e3a94a582cf7", - "sha256:a512eed9dfd4117110b1881ba9a59b31433caed0c4101b361f768e7bcbaf93c5", - "sha256:a82465ebbc9b1c5c50738536fdfa7cab639a261a99b469c9d4c7dcbb2b3f1e57", - "sha256:ae2757ace61bc4061b69af19e4689fa4416e1a04840f33b441034202b5cd02d4", - "sha256:b16582783f44fbca6fcf46f61347340c787d7530d88b4d590a397a47583f31dd", - "sha256:ba2537ef2163db9e6ccdbeb6f6424282ae4dea43177402152c67ef869cf3978b", - "sha256:bf7a89eef64b5455835f5ed30254ec19bf41f7541cd94f266ab7cbd463f00c41", - "sha256:c0abb5e4e8ce71a61d9446040c1e86d4e6d23f9097275c5bd49ed978755ff0fe", - "sha256:c414cbda77dbf13c3bc88b073a1a9f375c7b0cb5e115e15d4b73ec3a2fbc6f59", - "sha256:c51edc3541e11fbe83f0c4d9412ef6c79f664a3745fab261457e84465ec9d5a8", - "sha256:c5e69fd3eb0b409432b537fe3c6f44ac089c458ab6b78dcec14478422879ec5f", - "sha256:c918b7a1e26b4ab40409820ddccc5d49871a82329640f5005f73572d5eaa9b5e", - "sha256:c9bb87fdf2ab2370f21e4d5636e5317775e5d51ff32ebff2cf389f71b9b13750", - "sha256:ca5b2028c2f7af4e13fb9fc29b28d0ce767c38c7facdf64f6c2cd040413055f1", - "sha256:d0a07763776188b4db4c9c7fb1b8c494049f84659bb387b71c73bbc07f189e96", - "sha256:d33a0021893ede5969876052796165bab6006559ab845fd7b515a30abdd990dc", - "sha256:d55588cba7553f0b6ec33130bc3e114b355570b45785cebdc9daed8c637dd440", - "sha256:dac8e84fff5d27420f3c1e879ce9929108e873667ec87e0c8eeb413a5311adfe", - "sha256:eaef80eac3b4cfbdd6de53c6e108b4c534c21ae055d1dbea2de6b3b8ff3def38", - "sha256:eb462f0e346fcf41a901a126b50f8781e9a474d3927930f3490f38a6e73b6950", - "sha256:eb563dd3aea54c797adf513eeec819c4213d7dbfc311874eb4fd28d10f2ff0f2", - "sha256:f273674b445bcb6e4409bf8d1be67bc4b58e8b46fd0d560055d515b8830063cd", - "sha256:f6442f0f0ff81775eaa5b05af8a0ffa1dda36e9cf6ec1e0d3d245e8564b684ce", - "sha256:fb168b5924bef397b5ba13aabd8cf5df7d3d93f10218d7b925e360d436863f66", - "sha256:fbf8c2f00904eaf63ff37718eb13acf8e178cb940520e47b2f05027f5bb34ce3", - "sha256:fe4ebef608553aff8deb845c7f4f1d0740ff76fa672c011cc0bacb2a00fbde86" + "sha256:01c2acb51f8a7d6494c8c5eafe3d8e06d76563d8a8a4643b37e9b2dd8a2ff623", + "sha256:02087ea0a03b4af1ed6ebab2c54d7118127fee8d71b26398e8e4b05b78963199", + "sha256:040562757795eeea356394a7fb13076ad4f99d3c62ab0f8bdfb21f99a1f85664", + "sha256:042c55879cfeb21a8adacc84ea347721d3d83a159da6acdf1116859e2427c43f", + "sha256:079400a8269544b955ffa9e31f186f01d96829110a3bf79dc338e9910f794fca", + "sha256:07f45f287469039ffc2c53caf6803cd506eb5f5f637f1d4acb37a738f71dd066", + "sha256:09d77559e80dcc9d24570da3745ab859a9cf91953062e4ab126ba9d5993688ca", + "sha256:0cbff728659ce4bbf4c30b2a1be040faafaa9eca6ecde40aaff86f7889f4ab39", + "sha256:0e12c481ad92d129c78f13a2a3662317e46ee7ef96c94fd332e1c29131875b7d", + "sha256:0ea51dcc0835eea2ea31d66456210a4e01a076d820e9039b04ae8d17ac11dee6", + "sha256:0ffbcf9221e04502fc35e54d1ce9567541979c3fdfb93d2c554f0ca583a19b35", + "sha256:1494fa8725c285a81d01dc8c06b55287a1ee5e0e382d8413adc0a9197aac6408", + "sha256:16e13a7929791ac1216afde26f712802e3df7bf0360b32e4914dca3ab8baeea5", + "sha256:18406efb2f5a0e57e3a5881cd9354c1512d3bb4f5c45d96d110a66114d84d23a", + "sha256:18e707ce6c92d7282dfce370cd205098384b8ee21544e7cb29b8aab955b66fa9", + "sha256:220e92a30b426daf23bb67a7962900ed4613589bab80382be09b48896d211e92", + "sha256:23b30c62d0f16827f2ae9f2bb87619bc4fba2044911e2e6c2eb1af0161cdb766", + "sha256:23f9985c8784e544d53fc2930fc1ac1a7319f5d5332d228437acc9f418f2f168", + "sha256:297f54910247508e6e5cae669f2bc308985c60540a4edd1c77203ef19bfa63ca", + "sha256:2b08fce89fbd45664d3df6ad93e554b6c16933ffa9d55cb7e01182baaf971508", + "sha256:2cce2449e5927a0bf084d346da6cd5eb016b2beca10d0013ab50e3c226ffc0df", + "sha256:313ea15e5ff2a8cbbad96ccef6be638393041b0a7863183c2d31e0c6116688cf", + "sha256:323c1f04be6b2968944d730e5c2091c8c89767903ecaa135203eec4565ed2b2b", + "sha256:35f4a6f96aa6cb3f2f7247027b07b15a374f0d5b912c0001418d1d55024d5cb4", + "sha256:3b37fa423beefa44919e009745ccbf353d8c981516e807995b2bd11c2c77d268", + "sha256:3ce4f1185db3fbde8ed8aa223fc9620f276c58de8b0d4f8cc86fd1360829edb6", + "sha256:46989629904bad940bbec2106528140a218b4a36bb3042d8406980be1941429c", + "sha256:4838e24ee015101d9f901988001038f7f0d90dc0c3b115541a1365fb439add62", + "sha256:49b0e06786ea663f933f3710a51e9385ce0cba0ea56b67107fd841a55d56a231", + "sha256:4db21ece84dfeefc5d8a3863f101995de646c6cb0536952c321a2650aa202c36", + "sha256:54c4a097b8bc5bb0dfc83ae498061d53ad7b5762e00f4adaa23bee22b012e6ba", + "sha256:54d9ff35d4515debf14bc27f1e3b38bfc453eff3220f5bce159642fa762fe5d4", + "sha256:55b96e7ce3a69a8449a66984c268062fbaa0d8ae437b285428e12797baefce7e", + "sha256:57fdd2e0b2694ce6fc2e5ccf189789c3e2962916fb38779d3e3521ff8fe7a822", + "sha256:587d4af3979376652010e400accc30404e6c16b7df574048ab1f581af82065e4", + "sha256:5b513b6997a0b2f10e4fd3a1313568e373926e8c252bd76c960f96fd039cd28d", + "sha256:5ddcd9a179c0a6fa8add279a4444015acddcd7f232a49071ae57fa6e278f1f71", + "sha256:6113c008a7780792efc80f9dfe10ba0cd043cbf8dc9a76ef757850f51b4edc50", + "sha256:635a1d96665f84b292e401c3d62775851aedc31d4f8784117b3c68c4fcd4118d", + "sha256:64ce2799bd75039b480cc0360907c4fb2f50022f030bf9e7a8705b636e408fad", + "sha256:69dee6a020693d12a3cf892aba4808fe168d2a4cef368eb9bf74f5398bfd4ee8", + "sha256:6a2644a93da36c784e546de579ec1806bfd2763ef47babc1b03d765fe560c9f8", + "sha256:6b41e1adc61fa347662b09398e31ad446afadff932a24807d3ceb955ed865cc8", + "sha256:6c188c307e8433bcb63dc1915022deb553b4203a70722fc542c363bf120a01fd", + "sha256:6edd623bae6a737f10ce853ea076f56f507fd7726bee96a41ee3d68d347e4d16", + "sha256:73d6d2f64f4d894c96626a75578b0bf7d9e56dcda8c3d037a2118fdfe9b1c664", + "sha256:7a22ccefd4db3f12b526eccb129390942fe874a3a9fdbdd24cf55773a1faab1a", + "sha256:7fb89ee5d106e4a7a51bce305ac4efb981536301895f7bdcf93ec92ae0d91c7f", + "sha256:846bc79ee753acf93aef4184c040d709940c9d001029ceb7b7a52747b80ed2dd", + "sha256:85ab7824093d8f10d44330fe1e6493f756f252d145323dd17ab6b48733ff6c0a", + "sha256:8dee5b4810a89447151999428fe096977346cf2f29f4d5e29609d2e19e0199c9", + "sha256:8e5fb5f77c8745a60105403a774fe2c1759b71d3e7b4ca237a5e67ad066c7199", + "sha256:98eeee2f2e63edae2181c886d7911ce502e1292794f4c5ee71e60e23e8d26b5d", + "sha256:9d4a76b96f398697fe01117093613166e6aa8195d63f1b4ec3f21ab637632963", + "sha256:9e8719792ca63c6b8340380352c24dcb8cd7ec49dae36e963742a275dfae6009", + "sha256:a0b2b80321c2ed3fcf0385ec9e51a12253c50f146fddb2abbb10f033fe3d049a", + "sha256:a4cc92bb6db56ab0c1cbd17294e14f5e9224f0cc6521167ef388332604e92679", + "sha256:a738b937d512b30bf75995c0159c0ddf9eec0775c9d72ac0202076c72f24aa96", + "sha256:a8f877c89719d759e52783f7fe6e1c67121076b87b40542966c02de5503ace42", + "sha256:a906ed5e47a0ce5f04b2c981af1c9acf9e8696066900bf03b9d7879a6f679fc8", + "sha256:ae2941333154baff9838e88aa71c1d84f4438189ecc6021a12c7573728b5838e", + "sha256:b0d0a6c64fcc4ef9c69bd5b3b3626cc3776520a1637d8abaa62b9edc147a58f7", + "sha256:b5b029322e6e7b94fff16cd120ab35a253236a5f99a79fb04fda7ae71ca20ae8", + "sha256:b7aaa315101c6567a9a45d2839322c51c8d6e81f67683d529512f5bcfb99c802", + "sha256:be1c8ed48c4c4065ecb19d882a0ce1afe0745dfad8ce48c49586b90a55f02366", + "sha256:c0256beda696edcf7d97ef16b2a33a8e5a875affd6fa6567b54f7c577b30a137", + "sha256:c157bb447303070f256e084668b702073db99bbb61d44f85d811025fcf38f784", + "sha256:c57d08ad67aba97af57a7263c2d9006d5c404d721c5f7542f077f109ec2a4a29", + "sha256:c69ada171c2d0e97a4b5aa78fbb835e0ffbb6b13fc5da968c09811346564f0d3", + "sha256:c94bb0a9f1db10a1d16c00880bdebd5f9faf267273b8f5bd1878126e0fbde771", + "sha256:cb130fccd1a37ed894824b8c046321540263013da72745d755f2d35114b81a60", + "sha256:ced479f601cd2f8ca1fd7b23925a7e0ad512a56d6e9476f79b8f381d9d37090a", + "sha256:d05ac6fa06959c4172eccd99a222e1fbf17b5670c4d596cb1e5cde99600674c4", + "sha256:d552c78411f60b1fdaafd117a1fca2f02e562e309223b9d44b7de8be451ec5e0", + "sha256:dd4490a33eb909ef5078ab20f5f000087afa2a4daa27b4c072ccb3cb3050ad84", + "sha256:df5cbb1fbc74a8305b6065d4ade43b993be03dbe0f8b30032cced0d7740994bd", + "sha256:e28f9faeb14b6f23ac55bfbbfd3643f5c7c18ede093977f1df249f73fd22c7b1", + "sha256:e464b467f1588e2c42d26814231edecbcfe77f5ac414d92cbf4e7b55b2c2a776", + "sha256:e4c22e1ac1f1ec1e09f72e6c44d8f2244173db7eb9629cc3a346a8d7ccc31142", + "sha256:e53b5fbab5d675aec9f0c501274c467c0f9a5d23696cfc94247e1fb56501ed89", + "sha256:e93f1c331ca8e86fe877a48ad64e77882c0c4da0097f2212873a69bbfea95d0c", + "sha256:e997fd30430c57138adc06bba4c7c2968fb13d101e57dd5bb9355bf8ce3fa7e8", + "sha256:e9a091b0550b3b0207784a7d6d0f1a00d1d1c8a11699c1a4d93db3fbefc3ad35", + "sha256:eab4bb380f15e189d1313195b062a6aa908f5bd687a0ceccd47c8211e9cf0d4a", + "sha256:eb1ae19e64c14c7ec1995f40bd932448713d3c73509e82d8cd7744dc00e29e86", + "sha256:ecea58b43a67b1b79805f1a0255730edaf5191ecef84dbc4cc85eb30bc8b63b9", + "sha256:ee439691d8c23e76f9802c42a95cfeebf9d47cf4ffd06f18489122dbb0a7ad64", + "sha256:eee9130eaad130649fd73e5cd92f60e55708952260ede70da64de420cdcad554", + "sha256:f47cd43a5bfa48f86925fe26fbdd0a488ff15b62468abb5d2a1e092a4fb10e85", + "sha256:f6fff13ef6b5f29221d6904aa816c34701462956aa72a77f1f151a8ec4f56aeb", + "sha256:f745ec09bc1b0bd15cfc73df6fa4f726dcc26bb16c23a03f9e3367d357eeedd0", + "sha256:f8404bf61298bb6f8224bb9176c1424548ee1181130818fcd2cbffddc768bed8", + "sha256:f9268774428ec173654985ce55fc6caf4c6d11ade0f6f914d48ef4719eb05ebb", + "sha256:faa3c142464efec496967359ca99696c896c591c56c53506bac1ad465f66e919" ], "markers": "python_version >= '3.8'", - "version": "==2024.7.24" + "version": "==2024.9.11" }, "requests": { "hashes": [ @@ -3760,59 +3797,67 @@ }, "ruff": { "hashes": [ - "sha256:005256d977021790cc52aa23d78f06bb5090dc0bfbd42de46d49c201533982ae", - "sha256:09c72a833fd3551135ceddcba5ebdb68ff89225d30758027280968c9acdc7810", - "sha256:381413ec47f71ce1d1c614f7779d88886f406f1fd53d289c77e4e533dc6ea200", - "sha256:3a8d42d11fff8d3143ff4da41742a98f8f233bf8890e9fe23077826818f8d680", - "sha256:3e42a57b58e3612051a636bc1ac4e6b838679530235520e8f095f7c44f706ff9", - "sha256:482c1e6bfeb615eafc5899127b805d28e387bd87db38b2c0c41d271f5e58d8cc", - "sha256:4d32d87fab433c0cf285c3683dd4dae63be05fd7a1d65b3f5bf7cdd05a6b96fb", - "sha256:51935067740773afdf97493ba9b8231279e9beef0f2a8079188c4776c25688e0", - "sha256:52e75a82bbc9b42e63c08d22ad0ac525117e72aee9729a069d7c4f235fc4d276", - "sha256:7291e64d7129f24d1b0c947ec3ec4c0076e958d1475c61202497c6aced35dd19", - "sha256:794ada3400a0d0b89e3015f1a7e01f4c97320ac665b7bc3ade24b50b54cb2972", - "sha256:7e4e308f16e07c95fc7753fc1aaac690a323b2bb9f4ec5e844a97bb7fbebd748", - "sha256:800c50371bdcb99b3c1551d5691e14d16d6f07063a518770254227f7f6e8c178", - "sha256:8e25ddd9cd63ba1f3bd51c1f09903904a6adf8429df34f17d728a8fa11174253", - "sha256:932cd69eefe4daf8c7d92bd6689f7e8182571cb934ea720af218929da7bd7d69", - "sha256:9ad7dfbd138d09d9a7e6931e6a7e797651ce29becd688be8a0d4d5f8177b4b0c", - "sha256:a50af6e828ee692fb10ff2dfe53f05caecf077f4210fae9677e06a808275754f", - "sha256:cf4d3fa53644137f6a4a27a2b397381d16454a1566ae5335855c187fbf67e4f5" + "sha256:007dee844738c3d2e6c24ab5bc7d43c99ba3e1943bd2d95d598582e9c1b27750", + "sha256:1085c455d1b3fdb8021ad534379c60353b81ba079712bce7a900e834859182fa", + "sha256:27b87e1801e786cd6ede4ada3faa5e254ce774de835e6723fd94551464c56b8c", + "sha256:5fd0d4b7b1457c49e435ee1e437900ced9b35cb8dc5178921dfb7d98d65a08d0", + "sha256:677e03c00f37c66cea033274295a983c7c546edea5043d0c798833adf4cf4c6f", + "sha256:6cfb227b932ba8ef6e56c9f875d987973cd5e35bc5d05f5abf045af78ad8e098", + "sha256:6ef0411eccfc3909269fed47c61ffebdcb84a04504bafa6b6df9b85c27e813b0", + "sha256:6f5a2f17c7d32991169195d52a04c95b256378bbf0de8cb98478351eb70d526f", + "sha256:70edf6a93b19481affd287d696d9e311388d808671bc209fb8907b46a8c3af44", + "sha256:77944bca110ff0a43b768f05a529fecd0706aac7bcce36d7f1eeb4cbfca5f0f2", + "sha256:792213f7be25316f9b46b854df80a77e0da87ec66691e8f012f887b4a671ab5a", + "sha256:8d3bb2e3fbb9875172119021a13eed38849e762499e3cfde9588e4b4d70968dc", + "sha256:9f1476236b3eacfacfc0f66aa9e6cd39f2a624cb73ea99189556015f27c0bdeb", + "sha256:a5bf44b1aa0adaf6d9d20f86162b34f7c593bfedabc51239953e446aefc8ce18", + "sha256:cd48f945da2a6334f1793d7f701725a76ba93bf3d73c36f6b21fb04d5338dcf5", + "sha256:ce60058d3cdd8490e5e5471ef086b3f1e90ab872b548814e35930e21d848c9ce", + "sha256:ec0517dc0f37cad14a5319ba7bba6e7e339d03fbf967a6d69b0907d61be7a263", + "sha256:f8034b19b993e9601f2ddf2c517451e17a6ab5cdb1c13fdff50c1442a7171d87" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==0.6.5" + "version": "==0.6.8" }, "scipy": { "hashes": [ - "sha256:017367484ce5498445aade74b1d5ab377acdc65e27095155e448c88497755a5d", - "sha256:095a87a0312b08dfd6a6155cbbd310a8c51800fc931b8c0b84003014b874ed3c", - "sha256:20335853b85e9a49ff7572ab453794298bcf0354d8068c5f6775a0eabf350aca", - "sha256:27e52b09c0d3a1d5b63e1105f24177e544a222b43611aaf5bc44d4a0979e32f9", - "sha256:2831f0dc9c5ea9edd6e51e6e769b655f08ec6db6e2e10f86ef39bd32eb11da54", - "sha256:2ac65fb503dad64218c228e2dc2d0a0193f7904747db43014645ae139c8fad16", - "sha256:392e4ec766654852c25ebad4f64e4e584cf19820b980bc04960bca0b0cd6eaa2", - "sha256:436bbb42a94a8aeef855d755ce5a465479c721e9d684de76bf61a62e7c2b81d5", - "sha256:45484bee6d65633752c490404513b9ef02475b4284c4cfab0ef946def50b3f59", - "sha256:54f430b00f0133e2224c3ba42b805bfd0086fe488835effa33fa291561932326", - "sha256:5713f62f781eebd8d597eb3f88b8bf9274e79eeabf63afb4a737abc6c84ad37b", - "sha256:5d72782f39716b2b3509cd7c33cdc08c96f2f4d2b06d51e52fb45a19ca0c86a1", - "sha256:637e98dcf185ba7f8e663e122ebf908c4702420477ae52a04f9908707456ba4d", - "sha256:8335549ebbca860c52bf3d02f80784e91a004b71b059e3eea9678ba994796a24", - "sha256:949ae67db5fa78a86e8fa644b9a6b07252f449dcf74247108c50e1d20d2b4627", - "sha256:a014c2b3697bde71724244f63de2476925596c24285c7a637364761f8710891c", - "sha256:a78b4b3345f1b6f68a763c6e25c0c9a23a9fd0f39f5f3d200efe8feda560a5fa", - "sha256:cdd7dacfb95fea358916410ec61bbc20440f7860333aee6d882bb8046264e949", - "sha256:cfa31f1def5c819b19ecc3a8b52d28ffdcc7ed52bb20c9a7589669dd3c250989", - "sha256:d533654b7d221a6a97304ab63c41c96473ff04459e404b83275b60aa8f4b7004", - "sha256:d605e9c23906d1994f55ace80e0125c587f96c020037ea6aa98d01b4bd2e222f", - "sha256:de3ade0e53bc1f21358aa74ff4830235d716211d7d077e340c7349bc3542e884", - "sha256:e89369d27f9e7b0884ae559a3a956e77c02114cc60a6058b4e5011572eea9299", - "sha256:eccfa1906eacc02de42d70ef4aecea45415f5be17e72b61bafcfd329bdc52e94", - "sha256:f26264b282b9da0952a024ae34710c2aff7d27480ee91a2e82b7b7073c24722f" + "sha256:0c2f95de3b04e26f5f3ad5bb05e74ba7f68b837133a4492414b3afd79dfe540e", + "sha256:1729560c906963fc8389f6aac023739ff3983e727b1a4d87696b7bf108316a79", + "sha256:278266012eb69f4a720827bdd2dc54b2271c97d84255b2faaa8f161a158c3b37", + "sha256:2843f2d527d9eebec9a43e6b406fb7266f3af25a751aa91d62ff416f54170bc5", + "sha256:2da0469a4ef0ecd3693761acbdc20f2fdeafb69e6819cc081308cc978153c675", + "sha256:2ff0a7e01e422c15739ecd64432743cf7aae2b03f3084288f399affcefe5222d", + "sha256:2ff38e22128e6c03ff73b6bb0f85f897d2362f8c052e3b8ad00532198fbdae3f", + "sha256:30ac8812c1d2aab7131a79ba62933a2a76f582d5dbbc695192453dae67ad6310", + "sha256:3a1b111fac6baec1c1d92f27e76511c9e7218f1695d61b59e05e0fe04dc59617", + "sha256:4079b90df244709e675cdc8b93bfd8a395d59af40b72e339c2287c91860deb8e", + "sha256:5149e3fd2d686e42144a093b206aef01932a0059c2a33ddfa67f5f035bdfe13e", + "sha256:5a275584e726026a5699459aa72f828a610821006228e841b94275c4a7c08417", + "sha256:631f07b3734d34aced009aaf6fedfd0eb3498a97e581c3b1e5f14a04164a456d", + "sha256:716e389b694c4bb564b4fc0c51bc84d381735e0d39d3f26ec1af2556ec6aad94", + "sha256:8426251ad1e4ad903a4514712d2fa8fdd5382c978010d1c6f5f37ef286a713ad", + "sha256:8475230e55549ab3f207bff11ebfc91c805dc3463ef62eda3ccf593254524ce8", + "sha256:8bddf15838ba768bb5f5083c1ea012d64c9a444e16192762bd858f1e126196d0", + "sha256:8e32dced201274bf96899e6491d9ba3e9a5f6b336708656466ad0522d8528f69", + "sha256:8f9ea80f2e65bdaa0b7627fb00cbeb2daf163caa015e59b7516395fe3bd1e066", + "sha256:97c5dddd5932bd2a1a31c927ba5e1463a53b87ca96b5c9bdf5dfd6096e27efc3", + "sha256:a49f6ed96f83966f576b33a44257d869756df6cf1ef4934f59dd58b25e0327e5", + "sha256:af29a935803cc707ab2ed7791c44288a682f9c8107bc00f0eccc4f92c08d6e07", + "sha256:b05d43735bb2f07d689f56f7b474788a13ed8adc484a85aa65c0fd931cf9ccd2", + "sha256:b28d2ca4add7ac16ae8bb6632a3c86e4b9e4d52d3e34267f6e1b0c1f8d87e389", + "sha256:b99722ea48b7ea25e8e015e8341ae74624f72e5f21fc2abd45f3a93266de4c5d", + "sha256:baff393942b550823bfce952bb62270ee17504d02a1801d7fd0719534dfb9c84", + "sha256:c0ee987efa6737242745f347835da2cc5bb9f1b42996a4d97d5c7ff7928cb6f2", + "sha256:d0d2821003174de06b69e58cef2316a6622b60ee613121199cb2852a873f8cf3", + "sha256:e0cf28db0f24a38b2a0ca33a85a54852586e43cf6fd876365c86e0657cfe7d73", + "sha256:e4f5a7c49323533f9103d4dacf4e4f07078f360743dec7f7596949149efeec06", + "sha256:eb58ca0abd96911932f688528977858681a59d61a7ce908ffd355957f7025cfc", + "sha256:edaf02b82cd7639db00dbff629995ef185c8df4c3ffa71a5562a595765a06ce1", + "sha256:fef8c87f8abfb884dac04e97824b61299880c43f4ce675dd2cbeadd3c9b466d2" ], - "markers": "python_version >= '3.9'", - "version": "==1.13.1" + "markers": "python_version >= '3.10'", + "version": "==1.14.1" }, "service-identity": { "hashes": [ @@ -3823,11 +3868,11 @@ }, "setuptools": { "hashes": [ - "sha256:5f4c08aa4d3ebcb57a50c33b1b07e94315d7fc7230f7115e47fc99776c8ce308", - "sha256:95b40ed940a1c67eb70fc099094bd6e99c6ee7c23aa2306f4d2697ba7916f9c6" + "sha256:35ab7fd3bcd95e6b7fd704e4a1539513edad446c097797f2985e0e4b960772f2", + "sha256:d59a21b17a275fb872a9c3dae73963160ae079f1049ed956880cd7c09b120538" ], "markers": "python_version >= '3.8'", - "version": "==74.1.2" + "version": "==75.1.0" }, "six": { "hashes": [ @@ -3890,19 +3935,19 @@ }, "urllib3": { "hashes": [ - "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472", - "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168" + "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac", + "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9" ], "markers": "python_version >= '3.8'", - "version": "==2.2.2" + "version": "==2.2.3" }, "virtualenv": { "hashes": [ - "sha256:48f2695d9809277003f30776d155615ffc11328e6a0a8c1f0ec80188d7874a55", - "sha256:c17f4e0f3e6036e9f26700446f85c76ab11df65ff6d8a9cbfad9f71aabfcf23c" + "sha256:4f3ac17b81fba3ce3bd6f4ead2749a72da5929c01774948e243db9ba41df4ff6", + "sha256:ce489cac131aa58f4b25e321d6d186171f78e6cb13fafbf32a840cee67733ff4" ], "markers": "python_version >= '3.7'", - "version": "==20.26.4" + "version": "==20.26.5" }, "watchdog": { "hashes": [ @@ -3946,14 +3991,6 @@ "markers": "python_version >= '3.8'", "version": "==4.0.2" }, - "zipp": { - "hashes": [ - "sha256:9960cd8967c8f85a56f920d5d507274e74f9ff813a0ab8889a5b5be2daf44064", - "sha256:c22b14cc4763c5a5b04134207736c107db42e9d3ef2d9779d465f5f1bcba572b" - ], - "markers": "python_version >= '3.8'", - "version": "==3.20.1" - }, "zope-interface": { "hashes": [ "sha256:01e6e58078ad2799130c14a1d34ec89044ada0e1495329d72ee0407b9ae5100d", @@ -4190,20 +4227,6 @@ "markers": "python_full_version >= '3.7.0'", "version": "==3.3.2" }, - "coreapi": { - "hashes": [ - "sha256:46145fcc1f7017c076a2ef684969b641d18a2991051fddec9458ad3f78ffc1cb", - "sha256:bf39d118d6d3e171f10df9ede5666f63ad80bba9a29a8ec17726a66cf52ee6f3" - ], - "version": "==2.3.3" - }, - "coreschema": { - "hashes": [ - "sha256:5e6ef7bf38c1525d5e55a895934ab4273548629f16aed5c0a6caa74ebf45551f", - "sha256:9503506007d482ab0867ba14724b93c18a33b22b6d19fb419ef2d239dd4a1607" - ], - "version": "==0.0.4" - }, "cryptography": { "hashes": [ "sha256:014f58110f53237ace6a408b5beb6c427b64e084eb451ef25a28308270086494", @@ -4259,119 +4282,38 @@ "compatible-mypy" ], "hashes": [ - "sha256:78e3764488fdfd2695f12502136548ec22f8d4b1780541a835042b8238d11514", - "sha256:c2502f5ecbae50c68f9a86d52b5b2447d8648fd205036dad0ccb41e19a445927" + "sha256:86128c228b65e6c9a85e5dc56eb1c6f41125917dae0e21e6cfecdf1b27e630c5", + "sha256:b98d49a80aa4adf1433a97407102d068de26c739c405431d93faad96dd282c40" ], "markers": "python_version >= '3.8'", - "version": "==5.0.4" + "version": "==5.1.0" }, "django-stubs-ext": { "hashes": [ - "sha256:85da065224204774208be29c7d02b4482d5a69218a728465c2fbe41725fdc819", - "sha256:910cbaff3d1e8e806a5c27d5ddd4088535aae8371ea921b7fd680fdfa5f14e30" + "sha256:a455fc222c90b30b29ad8c53319559f5b54a99b4197205ddbb385aede03b395d", + "sha256:ed7d51c0b731651879fc75f331fb0806d98b67bfab464e96e2724db6b46ef926" ], "markers": "python_version >= '3.8'", - "version": "==5.0.4" + "version": "==5.1.0" }, "djangorestframework-stubs": { "extras": [ "compatible-mypy" ], "hashes": [ - "sha256:037f0582b1e6c79366b6a839da861474d59210c4bfa1d36291545cb6ede6a0da", - "sha256:f6ed5fb19c12aa752288ddc6ad28d4ca7c81681ca7f28a19aba9064b2a69489c" + "sha256:34539871895d66d382b6ae3655d9f95c1de7733cf50bc29097638d367ed3117d", + "sha256:79dc9018f5d5fa420f9981eec9f1e820ecbd04719791f144419cdc6c5b8e29bd" ], - "markers": "python_version >= '3.6'", - "version": "==1.4.0" + "markers": "python_version >= '3.8'", + "version": "==3.15.1" }, "idna": { "hashes": [ - "sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac", - "sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603" + "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", + "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3" ], "markers": "python_version >= '3.6'", - "version": "==3.8" - }, - "itypes": { - "hashes": [ - "sha256:03da6872ca89d29aef62773672b2d408f490f80db48b23079a4b194c86dd04c6", - "sha256:af886f129dea4a2a1e3d36595a2d139589e4dd287f5cab0b40e799ee81570ff1" - ], - "version": "==1.2.0" - }, - "jinja2": { - "hashes": [ - "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369", - "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d" - ], - "markers": "python_version >= '3.7'", - "version": "==3.1.4" - }, - "markupsafe": { - "hashes": [ - "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf", - "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff", - "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f", - "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3", - "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532", - "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f", - "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617", - "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df", - "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4", - "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906", - "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f", - "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4", - "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8", - "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371", - "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2", - "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465", - "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52", - "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6", - "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169", - "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad", - "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2", - "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0", - "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029", - "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f", - "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a", - "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced", - "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5", - "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c", - "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf", - "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9", - "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb", - "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad", - "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3", - "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1", - "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46", - "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc", - "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a", - "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee", - "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900", - "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5", - "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea", - "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f", - "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5", - "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e", - "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a", - "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f", - "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50", - "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a", - "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b", - "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4", - "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff", - "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2", - "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46", - "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b", - "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf", - "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5", - "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5", - "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab", - "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd", - "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68" - ], - "markers": "python_version >= '3.7'", - "version": "==2.1.5" + "version": "==3.10" }, "mypy": { "hashes": [ @@ -4544,11 +4486,11 @@ }, "types-pyyaml": { "hashes": [ - "sha256:b8f76ddbd7f65440a8bda5526a9607e4c7a322dc2f8e1a8c405644f9a6f4b9af", - "sha256:deda34c5c655265fc517b546c902aa6eed2ef8d3e921e4765fe606fe2afe8d35" + "sha256:392b267f1c0fe6022952462bf5d6523f31e37f6cea49b14cee7ad634b6301570", + "sha256:d1405a86f9576682234ef83bcb4e6fff7c9305c8b1fbad5e0bcd4f7dbdc9c587" ], "markers": "python_version >= '3.8'", - "version": "==6.0.12.20240808" + "version": "==6.0.12.20240917" }, "types-redis": { "hashes": [ @@ -4559,14 +4501,22 @@ "markers": "python_version >= '3.8'", "version": "==4.6.0.20240903" }, + "types-requests": { + "hashes": [ + "sha256:2850e178db3919d9bf809e434eef65ba49d0e7e33ac92d588f4a5e295fffd405", + "sha256:59c2f673eb55f32a99b2894faf6020e1a9f4a402ad0f192bfee0b64469054310" + ], + "markers": "python_version >= '3.8'", + "version": "==2.32.0.20240914" + }, "types-setuptools": { "hashes": [ - "sha256:0abdb082552ca966c1e5fc244e4853adc62971f6cd724fb1d8a3713b580e5a65", - "sha256:15b38c8e63ca34f42f6063ff4b1dd662ea20086166d5ad6a102e670a52574120" + "sha256:06f78307e68d1bbde6938072c57b81cf8a99bc84bd6dc7e4c5014730b097dc0c", + "sha256:12f12a165e7ed383f31def705e5c0fa1c26215dd466b0af34bd042f7d5331f55" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==74.1.0.20240907" + "version": "==75.1.0.20240917" }, "types-tqdm": { "hashes": [ @@ -4585,21 +4535,13 @@ "markers": "python_version < '3.11'", "version": "==4.12.2" }, - "uritemplate": { - "hashes": [ - "sha256:4346edfc5c3b79f694bccd6d6099a322bbeb628dbf2cd86eea55a456ce5124f0", - "sha256:830c08b8d99bdd312ea4ead05994a38e8936266f84b9a7878232db50b044e02e" - ], - "markers": "python_version >= '3.6'", - "version": "==4.1.1" - }, "urllib3": { "hashes": [ - "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472", - "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168" + "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac", + "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9" ], "markers": "python_version >= '3.8'", - "version": "==2.2.2" + "version": "==2.2.3" } } } diff --git a/docs/advanced_usage.md b/docs/advanced_usage.md index 30687680c..fe8d2e305 100644 --- a/docs/advanced_usage.md +++ b/docs/advanced_usage.md @@ -418,6 +418,11 @@ Insurances/ # Insurances Defining a storage path is optional. If no storage path is defined for a document, the global [`PAPERLESS_FILENAME_FORMAT`](configuration.md#PAPERLESS_FILENAME_FORMAT) is applied. +## Automatic recovery of invalid PDFs {#pdf-recovery} + +Paperless will attempt to "clean" certain invalid PDFs with `qpdf` before processing if, for example, the mime_type +detection is incorrect. This can happen if the PDF is not properly formatted or contains errors. + ## Celery Monitoring {#celery-monitoring} The monitoring tool diff --git a/docs/api.md b/docs/api.md index d15d6207b..e5da43a5c 100644 --- a/docs/api.md +++ b/docs/api.md @@ -54,6 +54,7 @@ fields: - `archived_file_name`: Verbose filename of the archived document. Read-only. Null if no archived document is available. - `notes`: Array of notes associated with the document. +- `page_count`: Number of pages. - `set_permissions`: Allows setting document permissions. Optional, write-only. See [below](#permissions). - `custom_fields`: Array of custom fields & values, specified as diff --git a/docs/setup.md b/docs/setup.md index 4fdd7227a..bb5c7ee65 100644 --- a/docs/setup.md +++ b/docs/setup.md @@ -250,7 +250,7 @@ a minimal installation of Debian/Buster, which is the current stable release at the time of writing. Windows is not and will never be supported. -Paperless requires Python 3. At this time, 3.9 - 3.11 are tested versions. +Paperless requires Python 3. At this time, 3.10 - 3.12 are tested versions. Newer versions may work, but some dependencies may not fully support newer versions. Support for older Python versions may be dropped as they reach end of life or as newer versions are released, dependency support is confirmed, etc. diff --git a/src-ui/messages.xlf b/src-ui/messages.xlf index 643b5f896..9165f41f6 100644 --- a/src-ui/messages.xlf +++ b/src-ui/messages.xlf @@ -1046,11 +1046,11 @@ src/app/data/document.ts - 62 + 63 src/app/data/document.ts - 95 + 100 @@ -1954,11 +1954,11 @@ src/app/data/document.ts - 38 + 39 src/app/data/document.ts - 92 + 97 @@ -2414,7 +2414,7 @@ src/app/components/document-list/document-card-small/document-card-small.component.html - 120 + 128 src/app/components/manage/custom-fields/custom-fields.component.html @@ -2760,7 +2760,7 @@ src/app/data/document.ts - 46 + 47 @@ -2972,7 +2972,7 @@ src/app/components/document-list/document-card-small/document-card-small.component.html - 131 + 139 @@ -3441,11 +3441,11 @@ src/app/data/document.ts - 42 + 43 src/app/data/document.ts - 93 + 98 @@ -5571,7 +5571,7 @@ src/app/components/document-list/document-list.component.html - 277 + 286 @@ -5586,7 +5586,7 @@ src/app/components/document-list/document-list.component.html - 312 + 321 @@ -5601,7 +5601,7 @@ src/app/components/document-list/document-list.component.html - 319 + 328 @@ -5899,11 +5899,11 @@ src/app/data/document.ts - 34 + 35 src/app/data/document.ts - 90 + 95 @@ -5940,11 +5940,11 @@ src/app/data/document.ts - 50 + 51 src/app/data/document.ts - 89 + 94 @@ -5967,11 +5967,11 @@ src/app/data/document.ts - 54 + 55 src/app/data/document.ts - 91 + 96 @@ -5994,7 +5994,7 @@ src/app/data/document.ts - 58 + 59 @@ -6783,7 +6783,7 @@ src/app/components/document-list/document-list.component.html - 288 + 297 @@ -6838,19 +6838,30 @@ 82,83 + + {VAR_PLURAL, plural, =1 {1 page} other { pages}} + + src/app/components/document-list/document-card-large/document-card-large.component.html + 117 + + + src/app/components/document-list/document-card-small/document-card-small.component.html + 95 + + Shared src/app/components/document-list/document-card-large/document-card-large.component.html - 121 + 127 src/app/components/document-list/document-card-small/document-card-small.component.html - 106 + 114 src/app/data/document.ts - 70 + 71 src/app/pipes/username.pipe.ts @@ -6861,7 +6872,7 @@ Score: src/app/components/document-list/document-card-large/document-card-large.component.html - 126 + 132 @@ -7000,11 +7011,11 @@ src/app/data/document.ts - 74 + 75 src/app/data/document.ts - 88 + 93 @@ -7036,11 +7047,11 @@ src/app/data/document.ts - 66 + 67 src/app/data/document.ts - 96 + 101 @@ -7078,25 +7089,51 @@ 243 + + Sort by number of pages + + src/app/components/document-list/document-list.component.html + 252 + + + + Pages + + src/app/components/document-list/document-list.component.html + 256 + + + src/app/data/document.ts + 79 + + + src/app/data/document.ts + 102 + + + src/app/data/paperless-config.ts + 90 + + Shared src/app/components/document-list/document-list.component.html - 250,252 + 259,261 Edit document src/app/components/document-list/document-list.component.html - 284 + 293 Yes src/app/components/document-list/document-list.component.html - 335 + 349 src/app/pipes/yes-no.pipe.ts @@ -7107,7 +7144,7 @@ No src/app/components/document-list/document-list.component.html - 335 + 349 src/app/pipes/yes-no.pipe.ts @@ -8111,14 +8148,14 @@ Modified src/app/data/document.ts - 94 + 99 Search score src/app/data/document.ts - 102 + 108 Score is a value returned by the full text search engine and specifies how well a result matches the given query @@ -8234,13 +8271,6 @@ 83 - - Pages - - src/app/data/paperless-config.ts - 90 - - Mode diff --git a/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.spec.ts b/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.spec.ts index cb120bb64..ef36b0806 100644 --- a/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.spec.ts +++ b/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.spec.ts @@ -65,6 +65,7 @@ const savedView: SavedView = { DisplayField.CORRESPONDENT, DisplayField.DOCUMENT_TYPE, DisplayField.STORAGE_PATH, + DisplayField.PAGE_COUNT, `${DisplayField.CUSTOM_FIELD}11` as any, `${DisplayField.CUSTOM_FIELD}15` as any, ], @@ -344,6 +345,7 @@ describe('SavedViewWidgetComponent', () => { expect(component.getColumnTitle(DisplayField.STORAGE_PATH)).toEqual( 'Storage path' ) + expect(component.getColumnTitle(DisplayField.PAGE_COUNT)).toEqual('Pages') }) it('should get correct column title for custom field', () => { diff --git a/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.html b/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.html index 1a8c7df82..f60056c42 100644 --- a/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.html +++ b/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.html @@ -111,6 +111,12 @@ } } + @if (displayFields.includes(DisplayField.PAGE_COUNT) && document.page_count) { +
+ + {document.page_count, plural, =1 {1 page} other {{{document.page_count}} pages}} +
+ } @if (displayFields.includes(DisplayField.OWNER) && document.owner && document.owner !== settingsService.currentUser.id) {
{{document.owner | username}} diff --git a/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.spec.ts b/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.spec.ts index a3f047f03..efd5076be 100644 --- a/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.spec.ts +++ b/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.spec.ts @@ -31,6 +31,7 @@ const doc = { correspondent: 8, document_type: 10, storage_path: null, + page_count: 8, notes: [ { id: 11, @@ -80,6 +81,7 @@ describe('DocumentCardLargeComponent', () => { it('should display a document', () => { expect(fixture.nativeElement.textContent).toContain('Document 10') expect(fixture.nativeElement.textContent).toContain('Cupcake ipsum') + expect(fixture.nativeElement.textContent).toContain('8 pages') }) it('should show preview on mouseover after delay to preload content', fakeAsync(() => { diff --git a/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.html b/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.html index 92449214e..26f71ee8b 100644 --- a/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.html +++ b/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.html @@ -88,6 +88,14 @@
} + @if (displayFields.includes(DisplayField.PAGE_COUNT) && document.page_count) { +
+
+ + {document.page_count, plural, =1 {1 page} other {{{document.page_count}} pages}} +
+
+ } @if (displayFields.includes(DisplayField.ASN) && document.archive_serial_number | isNumber) {
diff --git a/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.spec.ts b/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.spec.ts index fc15453be..b86453a25 100644 --- a/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.spec.ts +++ b/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.spec.ts @@ -34,6 +34,7 @@ const doc = { correspondent: 8, document_type: 10, storage_path: null, + page_count: 12, notes: [ { id: 11, @@ -91,6 +92,10 @@ describe('DocumentCardSmallComponent', () => { fixture.detectChanges() }) + it('should display page count', () => { + expect(fixture.nativeElement.textContent).toContain('12 pages') + }) + it('should display a document, limit tags to 5', () => { expect(fixture.nativeElement.textContent).toContain('Document 10') expect( diff --git a/src-ui/src/app/components/document-list/document-list.component.html b/src-ui/src/app/components/document-list/document-list.component.html index 5ba14631e..ed5e8646a 100644 --- a/src-ui/src/app/components/document-list/document-list.component.html +++ b/src-ui/src/app/components/document-list/document-list.component.html @@ -246,6 +246,15 @@ (sort)="onSort($event)" i18n>Added } + @if (activeDisplayFields.includes(DisplayField.PAGE_COUNT)) { + Pages + } @if (activeDisplayFields.includes(DisplayField.SHARED)) { Shared @@ -330,6 +339,11 @@ {{d.added | customDate}} } + @if (activeDisplayFields.includes(DisplayField.PAGE_COUNT)) { + + {{ d.page_count }} + + } @if (activeDisplayFields.includes(DisplayField.SHARED)) { @if (d.is_shared_by_requester) { Yes } @else { No } diff --git a/src-ui/src/app/components/document-list/document-list.component.spec.ts b/src-ui/src/app/components/document-list/document-list.component.spec.ts index 26758b3c0..ad85652b8 100644 --- a/src-ui/src/app/components/document-list/document-list.component.spec.ts +++ b/src-ui/src/app/components/document-list/document-list.component.spec.ts @@ -602,7 +602,7 @@ describe('DocumentListComponent', () => { expect( fixture.debugElement.queryAll(By.directive(SortableDirective)) - ).toHaveLength(9) + ).toHaveLength(10) expect(component.notesEnabled).toBeTruthy() settingsService.set(SETTINGS_KEYS.NOTES_ENABLED, false) @@ -610,14 +610,14 @@ describe('DocumentListComponent', () => { expect(component.notesEnabled).toBeFalsy() expect( fixture.debugElement.queryAll(By.directive(SortableDirective)) - ).toHaveLength(8) + ).toHaveLength(9) // insufficient perms jest.spyOn(permissionService, 'currentUserCan').mockReturnValue(false) fixture.detectChanges() expect( fixture.debugElement.queryAll(By.directive(SortableDirective)) - ).toHaveLength(4) + ).toHaveLength(5) }) it('should support toggle on document objects', () => { diff --git a/src-ui/src/app/data/document.ts b/src-ui/src/app/data/document.ts index 1571d2a53..0b630b8cd 100644 --- a/src-ui/src/app/data/document.ts +++ b/src-ui/src/app/data/document.ts @@ -26,6 +26,7 @@ export enum DisplayField { OWNER = 'owner', SHARED = 'shared', ASN = 'asn', + PAGE_COUNT = 'pagecount', } export const DEFAULT_DISPLAY_FIELDS = [ @@ -73,6 +74,10 @@ export const DEFAULT_DISPLAY_FIELDS = [ id: DisplayField.ASN, name: $localize`ASN`, }, + { + id: DisplayField.PAGE_COUNT, + name: $localize`Pages`, + }, ] export const DEFAULT_DASHBOARD_VIEW_PAGE_SIZE = 10 @@ -94,6 +99,7 @@ export const DOCUMENT_SORT_FIELDS = [ { field: 'modified', name: $localize`Modified` }, { field: 'num_notes', name: $localize`Notes` }, { field: 'owner', name: $localize`Owner` }, + { field: 'page_count', name: $localize`Pages` }, ] export const DOCUMENT_SORT_FIELDS_FULLTEXT = [ @@ -164,4 +170,6 @@ export interface Document extends ObjectWithPermissions { // write-only field remove_inbox_tags?: boolean + + page_count?: number } diff --git a/src-ui/src/app/services/settings.service.ts b/src-ui/src/app/services/settings.service.ts index 91d1cc320..c3ea3f856 100644 --- a/src-ui/src/app/services/settings.service.ts +++ b/src-ui/src/app/services/settings.service.ts @@ -345,6 +345,7 @@ export class SettingsService { DisplayField.CREATED, DisplayField.ADDED, DisplayField.ASN, + DisplayField.PAGE_COUNT, DisplayField.SHARED, ].includes(field.id) ) { diff --git a/src-ui/src/styles.scss b/src-ui/src/styles.scss index 412324142..c83ebd493 100644 --- a/src-ui/src/styles.scss +++ b/src-ui/src/styles.scss @@ -680,3 +680,13 @@ code { } } } + +// pdfjs +canvas.hiddenCanvasElement { + position: absolute; + left: 0; + bottom: 0; + z-index: -1; + height: 0; + width: 0; +} diff --git a/src/documents/barcodes.py b/src/documents/barcodes.py index 97177cbf6..746d6014d 100644 --- a/src/documents/barcodes.py +++ b/src/documents/barcodes.py @@ -3,7 +3,6 @@ import re import tempfile from dataclasses import dataclass from pathlib import Path -from typing import Optional from django.conf import settings from pdf2image import convert_from_path @@ -81,7 +80,7 @@ class BarcodePlugin(ConsumeTaskPlugin): self._tiff_conversion_done = False self.barcodes: list[Barcode] = [] - def run(self) -> Optional[str]: + def run(self) -> str | None: # Some operations may use PIL, override pixel setting if needed maybe_override_pixel_limit() @@ -299,7 +298,7 @@ class BarcodePlugin(ConsumeTaskPlugin): ) @property - def asn(self) -> Optional[int]: + def asn(self) -> int | None: """ Search the parsed barcodes for any ASNs. The first barcode that starts with CONSUMER_ASN_BARCODE_PREFIX @@ -334,7 +333,7 @@ class BarcodePlugin(ConsumeTaskPlugin): return asn @property - def tags(self) -> Optional[list[int]]: + def tags(self) -> list[int] | None: """ Search the parsed barcodes for any tags. Returns the detected tag ids (or empty list) diff --git a/src/documents/bulk_edit.py b/src/documents/bulk_edit.py index 1f7a2a403..1aba8f9ec 100644 --- a/src/documents/bulk_edit.py +++ b/src/documents/bulk_edit.py @@ -3,7 +3,6 @@ import itertools import logging import os import tempfile -from typing import Optional from celery import chain from celery import chord @@ -242,7 +241,7 @@ def rotate(doc_ids: list[int], degrees: int): def merge( doc_ids: list[int], - metadata_document_id: Optional[int] = None, + metadata_document_id: int | None = None, delete_originals: bool = False, user: User = None, ): @@ -387,6 +386,8 @@ def delete_pages(doc_ids: list[int], pages: list[int]): pdf.remove_unreferenced_resources() pdf.save() doc.checksum = hashlib.md5(doc.source_path.read_bytes()).hexdigest() + if doc.page_count is not None: + doc.page_count = doc.page_count - len(pages) doc.save() update_document_archive_file.delay(document_id=doc.id) logger.info(f"Deleted pages {pages} from document {doc.id}") diff --git a/src/documents/caching.py b/src/documents/caching.py index 4bcb22e21..6eb2b691f 100644 --- a/src/documents/caching.py +++ b/src/documents/caching.py @@ -19,8 +19,8 @@ logger = logging.getLogger("paperless.caching") class MetadataCacheData: original_checksum: str original_metadata: list - archive_checksum: Optional[str] - archive_metadata: Optional[list] + archive_checksum: str | None + archive_metadata: list | None @dataclass(frozen=True) @@ -46,7 +46,7 @@ def get_suggestion_cache_key(document_id: int) -> str: return f"doc_{document_id}_suggest" -def get_suggestion_cache(document_id: int) -> Optional[SuggestionCacheData]: +def get_suggestion_cache(document_id: int) -> SuggestionCacheData | None: """ If possible, return the cached suggestions for the given document ID. The classifier needs to be matching in format and hash and the suggestions need to @@ -121,13 +121,13 @@ def get_metadata_cache_key(document_id: int) -> str: return f"doc_{document_id}_metadata" -def get_metadata_cache(document_id: int) -> Optional[MetadataCacheData]: +def get_metadata_cache(document_id: int) -> MetadataCacheData | None: """ Returns the cached document metadata for the given document ID, as long as the metadata was cached once and the checksums have not changed """ doc_key = get_metadata_cache_key(document_id) - doc_metadata: Optional[MetadataCacheData] = cache.get(doc_key) + doc_metadata: MetadataCacheData | None = cache.get(doc_key) # The metadata exists in the cache if doc_metadata is not None: try: @@ -161,7 +161,7 @@ def get_metadata_cache(document_id: int) -> Optional[MetadataCacheData]: def set_metadata_cache( document: Document, original_metadata: list, - archive_metadata: Optional[list], + archive_metadata: list | None, *, timeout=CACHE_50_MINUTES, ) -> None: diff --git a/src/documents/classifier.py b/src/documents/classifier.py index 66b06d69d..26a1ae478 100644 --- a/src/documents/classifier.py +++ b/src/documents/classifier.py @@ -78,9 +78,9 @@ class DocumentClassifier: def __init__(self): # last time a document changed and therefore training might be required - self.last_doc_change_time: Optional[datetime] = None + self.last_doc_change_time: datetime | None = None # Hash of primary keys of AUTO matching values last used in training - self.last_auto_type_hash: Optional[bytes] = None + self.last_auto_type_hash: bytes | None = None self.data_vectorizer = None self.tags_binarizer = None @@ -408,7 +408,7 @@ class DocumentClassifier: return content - def predict_correspondent(self, content: str) -> Optional[int]: + def predict_correspondent(self, content: str) -> int | None: if self.correspondent_classifier: X = self.data_vectorizer.transform([self.preprocess_content(content)]) correspondent_id = self.correspondent_classifier.predict(X) @@ -419,7 +419,7 @@ class DocumentClassifier: else: return None - def predict_document_type(self, content: str) -> Optional[int]: + def predict_document_type(self, content: str) -> int | None: if self.document_type_classifier: X = self.data_vectorizer.transform([self.preprocess_content(content)]) document_type_id = self.document_type_classifier.predict(X) @@ -451,7 +451,7 @@ class DocumentClassifier: else: return [] - def predict_storage_path(self, content: str) -> Optional[int]: + def predict_storage_path(self, content: str) -> int | None: if self.storage_path_classifier: X = self.data_vectorizer.transform([self.preprocess_content(content)]) storage_path_id = self.storage_path_classifier.predict(X) diff --git a/src/documents/conditionals.py b/src/documents/conditionals.py index 14fe3096a..47d9bfe4b 100644 --- a/src/documents/conditionals.py +++ b/src/documents/conditionals.py @@ -1,6 +1,5 @@ from datetime import datetime from datetime import timezone -from typing import Optional from django.conf import settings from django.core.cache import cache @@ -15,7 +14,7 @@ from documents.classifier import DocumentClassifier from documents.models import Document -def suggestions_etag(request, pk: int) -> Optional[str]: +def suggestions_etag(request, pk: int) -> str | None: """ Returns an optional string for the ETag, allowing browser caching of suggestions if the classifier has not been changed and the suggested dates @@ -42,7 +41,7 @@ def suggestions_etag(request, pk: int) -> Optional[str]: return None -def suggestions_last_modified(request, pk: int) -> Optional[datetime]: +def suggestions_last_modified(request, pk: int) -> datetime | None: """ Returns the datetime of classifier last modification. This is slightly off, as there is not way to track the suggested date setting modification, but it seems @@ -67,7 +66,7 @@ def suggestions_last_modified(request, pk: int) -> Optional[datetime]: return None -def metadata_etag(request, pk: int) -> Optional[str]: +def metadata_etag(request, pk: int) -> str | None: """ Metadata is extracted from the original file, so use its checksum as the ETag @@ -80,7 +79,7 @@ def metadata_etag(request, pk: int) -> Optional[str]: return None -def metadata_last_modified(request, pk: int) -> Optional[datetime]: +def metadata_last_modified(request, pk: int) -> datetime | None: """ Metadata is extracted from the original file, so use its modified. Strictly speaking, this is not the modification of the original file, but of the database object, but might as well @@ -94,7 +93,7 @@ def metadata_last_modified(request, pk: int) -> Optional[datetime]: return None -def preview_etag(request, pk: int) -> Optional[str]: +def preview_etag(request, pk: int) -> str | None: """ ETag for the document preview, using the original or archive checksum, depending on the request """ @@ -110,7 +109,7 @@ def preview_etag(request, pk: int) -> Optional[str]: return None -def preview_last_modified(request, pk: int) -> Optional[datetime]: +def preview_last_modified(request, pk: int) -> datetime | None: """ Uses the documents modified time to set the Last-Modified header. Not strictly speaking correct, but close enough and quick @@ -123,7 +122,7 @@ def preview_last_modified(request, pk: int) -> Optional[datetime]: return None -def thumbnail_last_modified(request, pk: int) -> Optional[datetime]: +def thumbnail_last_modified(request, pk: int) -> datetime | None: """ Returns the filesystem last modified either from cache or from filesystem. Cache should be (slightly?) faster than filesystem diff --git a/src/documents/consumer.py b/src/documents/consumer.py index d90b88f5a..803d82510 100644 --- a/src/documents/consumer.py +++ b/src/documents/consumer.py @@ -5,8 +5,6 @@ import tempfile from enum import Enum from pathlib import Path from typing import TYPE_CHECKING -from typing import Optional -from typing import Union import magic from django.conf import settings @@ -61,7 +59,7 @@ class WorkflowTriggerPlugin( ): NAME: str = "WorkflowTriggerPlugin" - def run(self) -> Optional[str]: + def run(self) -> str | None: """ Get overrides from matching workflows """ @@ -278,7 +276,7 @@ class ConsumerPlugin( current_progress: int, max_progress: int, status: ProgressStatusOptions, - message: Optional[Union[ConsumerStatusShortMessage, str]] = None, + message: ConsumerStatusShortMessage | str | None = None, document_id=None, ): # pragma: no cover self.status_mgr.send_progress( @@ -294,10 +292,10 @@ class ConsumerPlugin( def _fail( self, - message: Union[ConsumerStatusShortMessage, str], - log_message: Optional[str] = None, + message: ConsumerStatusShortMessage | str, + log_message: str | None = None, exc_info=None, - exception: Optional[Exception] = None, + exception: Exception | None = None, ): self._send_progress(100, 100, ProgressStatusOptions.FAILED, message) self.log.error(log_message or message, exc_info=exc_info) @@ -532,6 +530,7 @@ class ConsumerPlugin( ) self.working_copy = Path(tempdir.name) / Path(self.filename) copy_file_with_basic_stats(self.input_doc.original_file, self.working_copy) + self.unmodified_original = None # Determine the parser class. @@ -539,11 +538,40 @@ class ConsumerPlugin( self.log.debug(f"Detected mime type: {mime_type}") + if ( + Path(self.filename).suffix.lower() == ".pdf" + and mime_type in settings.CONSUMER_PDF_RECOVERABLE_MIME_TYPES + ): + try: + # The file might be a pdf, but the mime type is wrong. + # Try to clean with qpdf + self.log.debug( + "Detected possible PDF with wrong mime type, trying to clean with qpdf", + ) + run_subprocess( + [ + "qpdf", + "--replace-input", + self.working_copy, + ], + logger=self.log, + ) + mime_type = magic.from_file(self.working_copy, mime=True) + self.log.debug(f"Detected mime type after qpdf: {mime_type}") + # Save the original file for later + self.unmodified_original = ( + Path(tempdir.name) / Path("uo") / Path(self.filename) + ) + copy_file_with_basic_stats( + self.input_doc.original_file, + self.unmodified_original, + ) + except Exception as e: + self.log.error(f"Error attempting to clean PDF: {e}") + # Based on the mime type, get the parser for that type - parser_class: Optional[type[DocumentParser]] = ( - get_parser_class_for_mime_type( - mime_type, - ) + parser_class: type[DocumentParser] | None = get_parser_class_for_mime_type( + mime_type, ) if not parser_class: tempdir.cleanup() @@ -586,6 +614,7 @@ class ConsumerPlugin( date = None thumbnail = None archive_path = None + page_count = None try: self._send_progress( @@ -621,6 +650,7 @@ class ConsumerPlugin( ) date = parse_date(self.filename, text) archive_path = document_parser.get_archive_path() + page_count = document_parser.get_page_count(self.working_copy, mime_type) except ParseError as e: document_parser.cleanup() @@ -662,7 +692,12 @@ class ConsumerPlugin( try: with transaction.atomic(): # store the document. - document = self._store(text=text, date=date, mime_type=mime_type) + document = self._store( + text=text, + date=date, + page_count=page_count, + mime_type=mime_type, + ) # If we get here, it was successful. Proceed with post-consume # hooks. If they fail, nothing will get changed. @@ -682,7 +717,9 @@ class ConsumerPlugin( self._write( document.storage_type, - self.working_copy, + self.unmodified_original + if self.unmodified_original is not None + else self.working_copy, document.source_path, ) @@ -718,6 +755,8 @@ class ConsumerPlugin( self.log.debug(f"Deleting file {self.working_copy}") self.input_doc.original_file.unlink() self.working_copy.unlink() + if self.unmodified_original is not None: # pragma: no cover + self.unmodified_original.unlink() # https://github.com/jonaswinkler/paperless-ng/discussions/1037 shadow_file = os.path.join( @@ -789,7 +828,8 @@ class ConsumerPlugin( def _store( self, text: str, - date: Optional[datetime.datetime], + date: datetime.datetime | None, + page_count: int | None, mime_type: str, ) -> Document: # If someone gave us the original filename, use it instead of doc. @@ -835,6 +875,7 @@ class ConsumerPlugin( created=create_date, modified=create_date, storage_type=storage_type, + page_count=page_count, original_filename=self.filename, ) @@ -916,7 +957,7 @@ def parse_doc_title_w_placeholders( owner_username: str, local_added: datetime.datetime, original_filename: str, - created: Optional[datetime.datetime] = None, + created: datetime.datetime | None = None, ) -> str: """ Available title placeholders for Workflows depend on what has already been assigned, diff --git a/src/documents/data_models.py b/src/documents/data_models.py index b99c8511d..231e59005 100644 --- a/src/documents/data_models.py +++ b/src/documents/data_models.py @@ -2,7 +2,6 @@ import dataclasses import datetime from enum import IntEnum from pathlib import Path -from typing import Optional import magic from guardian.shortcuts import get_groups_with_perms @@ -17,20 +16,20 @@ class DocumentMetadataOverrides: meaning no override is happening """ - filename: Optional[str] = None - title: Optional[str] = None - correspondent_id: Optional[int] = None - document_type_id: Optional[int] = None - tag_ids: Optional[list[int]] = None - storage_path_id: Optional[int] = None - created: Optional[datetime.datetime] = None - asn: Optional[int] = None - owner_id: Optional[int] = None - view_users: Optional[list[int]] = None - view_groups: Optional[list[int]] = None - change_users: Optional[list[int]] = None - change_groups: Optional[list[int]] = None - custom_field_ids: Optional[list[int]] = None + filename: str | None = None + title: str | None = None + correspondent_id: int | None = None + document_type_id: int | None = None + tag_ids: list[int] | None = None + storage_path_id: int | None = None + created: datetime.datetime | None = None + asn: int | None = None + owner_id: int | None = None + view_users: list[int] | None = None + view_groups: list[int] | None = None + change_users: list[int] | None = None + change_groups: list[int] | None = None + custom_field_ids: list[int] | None = None def update(self, other: "DocumentMetadataOverrides") -> "DocumentMetadataOverrides": """ @@ -156,7 +155,7 @@ class ConsumableDocument: source: DocumentSource original_file: Path - mailrule_id: Optional[int] = None + mailrule_id: int | None = None mime_type: str = dataclasses.field(init=False, default=None) def __post_init__(self): diff --git a/src/documents/double_sided.py b/src/documents/double_sided.py index bfe66f4fe..3c3ec4723 100644 --- a/src/documents/double_sided.py +++ b/src/documents/double_sided.py @@ -4,7 +4,6 @@ import os import shutil from pathlib import Path from typing import Final -from typing import Optional from django.conf import settings from pikepdf import Pdf @@ -37,7 +36,7 @@ class CollatePlugin(NoCleanupPluginMixin, NoSetupPluginMixin, ConsumeTaskPlugin) in self.input_doc.original_file.parts ) - def run(self) -> Optional[str]: + def run(self) -> str | None: """ Tries to collate pages from 2 single sided scans of a double sided document. diff --git a/src/documents/filters.py b/src/documents/filters.py index b6ac591fe..255e7d3d7 100644 --- a/src/documents/filters.py +++ b/src/documents/filters.py @@ -2,9 +2,8 @@ import functools import inspect import json import operator +from collections.abc import Callable from contextlib import contextmanager -from typing import Callable -from typing import Union from django.contrib.contenttypes.models import ContentType from django.db.models import CharField @@ -311,7 +310,7 @@ class CustomFieldQueryParser: `max_query_depth` and `max_atom_count` can be set to guard against generating arbitrarily complex SQL queries. """ - self._custom_fields: dict[Union[int, str], CustomField] = {} + self._custom_fields: dict[int | str, CustomField] = {} self._validation_prefix = validation_prefix # Dummy ModelSerializer used to convert a Django models.Field to serializers.Field. self._model_serializer = serializers.ModelSerializer() @@ -345,7 +344,7 @@ class CustomFieldQueryParser: Applies rule (1, 2, 3) or (4, 5, 6) based on the length of the expr. """ with self._track_query_depth(): - if isinstance(expr, (list, tuple)): + if isinstance(expr, list | tuple): if len(expr) == 2: return self._parse_logical_expr(*expr) elif len(expr) == 3: @@ -359,7 +358,7 @@ class CustomFieldQueryParser: """ Handles [`q0`, `q1`, ..., `qn`] in rule 4 & 5. """ - if not isinstance(exprs, (list, tuple)) or not exprs: + if not isinstance(exprs, list | tuple) or not exprs: raise serializers.ValidationError( [_("Invalid expression list. Must be nonempty.")], ) diff --git a/src/documents/index.py b/src/documents/index.py index d95a80213..eacd1f99b 100644 --- a/src/documents/index.py +++ b/src/documents/index.py @@ -6,7 +6,6 @@ from contextlib import contextmanager from datetime import datetime from datetime import timezone from shutil import rmtree -from typing import Optional from django.conf import settings from django.db.models import QuerySet @@ -80,6 +79,7 @@ def get_schema(): has_owner=BOOLEAN(), viewer_id=KEYWORD(commas=True), checksum=TEXT(), + page_count=NUMERIC(sortable=True), original_filename=TEXT(sortable=True), is_shared=BOOLEAN(), ) @@ -181,6 +181,7 @@ def update_document(writer: AsyncWriter, doc: Document): has_owner=doc.owner is not None, viewer_id=viewer_ids if viewer_ids else None, checksum=doc.checksum, + page_count=doc.page_count, original_filename=doc.original_filename, is_shared=len(viewer_ids) > 0, ) @@ -247,6 +248,7 @@ class DelayedQuery: "archive_serial_number": "asn", "num_notes": "num_notes", "owner": "owner", + "page_count": "page_count", } if field.startswith("-"): @@ -386,7 +388,7 @@ def autocomplete( ix: FileIndex, term: str, limit: int = 10, - user: Optional[User] = None, + user: User | None = None, ): """ Mimics whoosh.reading.IndexReader.most_distinctive_terms with permissions @@ -422,7 +424,7 @@ def autocomplete( return terms -def get_permissions_criterias(user: Optional[User] = None): +def get_permissions_criterias(user: User | None = None): user_criterias = [query.Term("has_owner", False)] if user is not None: if user.is_superuser: # superusers see all docs diff --git a/src/documents/management/commands/document_consumer.py b/src/documents/management/commands/document_consumer.py index 97f9fcc59..1eb2f6541 100644 --- a/src/documents/management/commands/document_consumer.py +++ b/src/documents/management/commands/document_consumer.py @@ -251,7 +251,7 @@ class Command(BaseCommand): self.handle_inotify(directory, recursive, options["testing"]) else: if INotify is None and settings.CONSUMER_POLLING == 0: # pragma: no cover - logger.warn("Using polling as INotify import failed") + logger.warning("Using polling as INotify import failed") self.handle_polling(directory, recursive, options["testing"]) logger.debug("Consumer exiting.") @@ -267,7 +267,7 @@ class Command(BaseCommand): polling_interval = settings.CONSUMER_POLLING if polling_interval == 0: # pragma: no cover # Only happens if INotify failed to import - logger.warn("Using polling of 10s, consider setting this") + logger.warning("Using polling of 10s, consider setting this") polling_interval = 10 with ThreadPoolExecutor(max_workers=4) as pool: diff --git a/src/documents/management/commands/document_exporter.py b/src/documents/management/commands/document_exporter.py index 618c1a4e5..3d7352c1a 100644 --- a/src/documents/management/commands/document_exporter.py +++ b/src/documents/management/commands/document_exporter.py @@ -6,7 +6,6 @@ import tempfile import time from pathlib import Path from typing import TYPE_CHECKING -from typing import Optional import tqdm from django.conf import settings @@ -183,7 +182,7 @@ class Command(CryptMixin, BaseCommand): self.zip_export: bool = options["zip"] self.data_only: bool = options["data_only"] self.no_progress_bar: bool = options["no_progress_bar"] - self.passphrase: Optional[str] = options.get("passphrase") + self.passphrase: str | None = options.get("passphrase") self.files_in_export_dir: set[Path] = set() self.exported_files: set[str] = set() @@ -427,7 +426,7 @@ class Command(CryptMixin, BaseCommand): document: Document, base_name: str, document_dict: dict, - ) -> tuple[Path, Optional[Path], Optional[Path]]: + ) -> tuple[Path, Path | None, Path | None]: """ Generates the targets for a given document, including the original file, archive file and thumbnail (depending on settings). """ @@ -461,8 +460,8 @@ class Command(CryptMixin, BaseCommand): self, document: Document, original_target: Path, - thumbnail_target: Optional[Path], - archive_target: Optional[Path], + thumbnail_target: Path | None, + archive_target: Path | None, ) -> None: """ Copies files from the document storage location to the specified target location. @@ -512,7 +511,7 @@ class Command(CryptMixin, BaseCommand): def check_and_copy( self, source: Path, - source_checksum: Optional[str], + source_checksum: str | None, target: Path, ): """ diff --git a/src/documents/management/commands/document_importer.py b/src/documents/management/commands/document_importer.py index 3535e1476..a402466f4 100644 --- a/src/documents/management/commands/document_importer.py +++ b/src/documents/management/commands/document_importer.py @@ -3,7 +3,6 @@ import logging import os from contextlib import contextmanager from pathlib import Path -from typing import Optional import tqdm from django.conf import settings @@ -228,8 +227,8 @@ class Command(CryptMixin, BaseCommand): self.data_only: bool = options["data_only"] self.no_progress_bar: bool = options["no_progress_bar"] self.passphrase: str | None = options.get("passphrase") - self.version: Optional[str] = None - self.salt: Optional[str] = None + self.version: str | None = None + self.salt: str | None = None self.manifest_paths = [] self.manifest = [] diff --git a/src/documents/management/commands/mixins.py b/src/documents/management/commands/mixins.py index 823631586..212ecf597 100644 --- a/src/documents/management/commands/mixins.py +++ b/src/documents/management/commands/mixins.py @@ -1,9 +1,7 @@ import base64 import os from argparse import ArgumentParser -from typing import Optional from typing import TypedDict -from typing import Union from cryptography.fernet import Fernet from cryptography.hazmat.primitives import hashes @@ -103,7 +101,7 @@ class CryptMixin: }, ] - def get_crypt_params(self) -> dict[str, dict[str, Union[str, int]]]: + def get_crypt_params(self) -> dict[str, dict[str, str | int]]: return { EXPORTER_CRYPTO_SETTINGS_NAME: { EXPORTER_CRYPTO_ALGO_NAME: self.kdf_algorithm, @@ -128,7 +126,7 @@ class CryptMixin: EXPORTER_CRYPTO_SALT_NAME ] - def setup_crypto(self, *, passphrase: str, salt: Optional[str] = None): + def setup_crypto(self, *, passphrase: str, salt: str | None = None): """ Constructs a class for encryption or decryption using the specified passphrase and salt diff --git a/src/documents/matching.py b/src/documents/matching.py index 586ca3a6a..36fa9a2c6 100644 --- a/src/documents/matching.py +++ b/src/documents/matching.py @@ -1,7 +1,6 @@ import logging import re from fnmatch import fnmatch -from typing import Union from documents.classifier import DocumentClassifier from documents.data_models import ConsumableDocument @@ -20,7 +19,7 @@ logger = logging.getLogger("paperless.matching") def log_reason( - matching_model: Union[MatchingModel, WorkflowTrigger], + matching_model: MatchingModel | WorkflowTrigger, document: Document, reason: str, ): @@ -386,7 +385,7 @@ def existing_document_matches_workflow( def document_matches_workflow( - document: Union[ConsumableDocument, Document], + document: ConsumableDocument | Document, workflow: Workflow, trigger_type: WorkflowTrigger.WorkflowTriggerType, ) -> bool: diff --git a/src/documents/migrations/1053_document_page_count.py b/src/documents/migrations/1053_document_page_count.py new file mode 100644 index 000000000..13549e00f --- /dev/null +++ b/src/documents/migrations/1053_document_page_count.py @@ -0,0 +1,62 @@ +# Generated by Django 4.2.16 on 2024-09-21 15:44 +from pathlib import Path + +import pikepdf +from django.conf import settings +from django.db import migrations +from django.db import models +from django.utils.termcolors import colorize as colourise + + +def source_path(self): + if self.filename: + fname = str(self.filename) + + return Path(settings.ORIGINALS_DIR / fname).resolve() + + +def add_number_of_pages_to_page_count(apps, schema_editor): + Document = apps.get_model("documents", "Document") + + if not Document.objects.all().exists(): + return + + for doc in Document.objects.filter(mime_type="application/pdf"): + print( + " {} {} {}".format( + colourise("*", fg="green"), + colourise("Calculating number of pages for", fg="white"), + colourise(doc.filename, fg="cyan"), + ), + ) + + try: + with pikepdf.Pdf.open(source_path(doc)) as pdf: + if pdf.pages is not None: + doc.page_count = len(pdf.pages) + doc.save() + except Exception as e: # pragma: no cover + print(f"Error retrieving number of pages for {doc.filename}: {e}") + + +class Migration(migrations.Migration): + dependencies = [ + ("documents", "1052_document_transaction_id"), + ] + + operations = [ + migrations.AddField( + model_name="document", + name="page_count", + field=models.PositiveIntegerField( + blank=False, + null=True, + unique=False, + db_index=False, + ), + ), + migrations.RunPython( + add_number_of_pages_to_page_count, + migrations.RunPython.noop, + ), + ] diff --git a/src/documents/models.py b/src/documents/models.py index 452c57c78..b0a6bdd61 100644 --- a/src/documents/models.py +++ b/src/documents/models.py @@ -5,7 +5,6 @@ import re from collections import OrderedDict from pathlib import Path from typing import Final -from typing import Optional import dateutil.parser import pathvalidate @@ -205,6 +204,18 @@ class Document(SoftDeleteModel, ModelWithOwner): help_text=_("The checksum of the archived document."), ) + page_count = models.PositiveIntegerField( + _("page count"), + blank=False, + null=True, + unique=False, + db_index=False, + validators=[MinValueValidator(1)], + help_text=_( + "The number of pages of the document.", + ), + ) + created = models.DateTimeField(_("created"), default=timezone.now, db_index=True) modified = models.DateTimeField( @@ -314,7 +325,7 @@ class Document(SoftDeleteModel, ModelWithOwner): return self.archive_filename is not None @property - def archive_path(self) -> Optional[Path]: + def archive_path(self) -> Path | None: if self.has_archive_version: return (settings.ARCHIVE_DIR / Path(str(self.archive_filename))).resolve() else: @@ -414,6 +425,7 @@ class SavedView(ModelWithOwner): OWNER = ("owner", _("Owner")) SHARED = ("shared", _("Shared")) ASN = ("asn", _("ASN")) + PAGE_COUNT = ("pagecount", _("Pages")) CUSTOM_FIELD = ("custom_field_%d", ("Custom Field")) name = models.CharField(_("name"), max_length=128) diff --git a/src/documents/parsers.py b/src/documents/parsers.py index 1297162e2..2d73dc63f 100644 --- a/src/documents/parsers.py +++ b/src/documents/parsers.py @@ -10,7 +10,6 @@ from collections.abc import Iterator from functools import lru_cache from pathlib import Path from re import Match -from typing import Optional from django.conf import settings from django.utils import timezone @@ -107,7 +106,7 @@ def get_supported_file_extensions() -> set[str]: return extensions -def get_parser_class_for_mime_type(mime_type: str) -> Optional[type["DocumentParser"]]: +def get_parser_class_for_mime_type(mime_type: str) -> type["DocumentParser"] | None: """ Returns the best parser (by weight) for the given mimetype or None if no parser exists @@ -252,7 +251,7 @@ def make_thumbnail_from_pdf(in_path, temp_dir, logging_group=None) -> Path: return out_path -def parse_date(filename, text) -> Optional[datetime.datetime]: +def parse_date(filename, text) -> datetime.datetime | None: return next(parse_date_generator(filename, text), None) @@ -277,7 +276,7 @@ def parse_date_generator(filename, text) -> Iterator[datetime.datetime]: }, ) - def __filter(date: datetime.datetime) -> Optional[datetime.datetime]: + def __filter(date: datetime.datetime) -> datetime.datetime | None: if ( date is not None and date.year > 1900 @@ -290,7 +289,7 @@ def parse_date_generator(filename, text) -> Iterator[datetime.datetime]: def __process_match( match: Match[str], date_order: str, - ) -> Optional[datetime.datetime]: + ) -> datetime.datetime | None: date_string = match.group(0) try: @@ -339,7 +338,7 @@ class DocumentParser(LoggingMixin): self.archive_path = None self.text = None - self.date: Optional[datetime.datetime] = None + self.date: datetime.datetime | None = None self.progress_callback = progress_callback def progress(self, current_progress, max_progress): @@ -367,6 +366,9 @@ class DocumentParser(LoggingMixin): def extract_metadata(self, document_path, mime_type): return [] + def get_page_count(self, document_path, mime_type): + return None + def parse(self, document_path, mime_type, file_name=None): raise NotImplementedError @@ -382,7 +384,7 @@ class DocumentParser(LoggingMixin): def get_text(self): return self.text - def get_date(self) -> Optional[datetime.datetime]: + def get_date(self) -> datetime.datetime | None: return self.date def cleanup(self): diff --git a/src/documents/plugins/base.py b/src/documents/plugins/base.py index 14d6ea696..81f0c86c3 100644 --- a/src/documents/plugins/base.py +++ b/src/documents/plugins/base.py @@ -1,7 +1,6 @@ import abc from pathlib import Path from typing import Final -from typing import Optional from documents.data_models import ConsumableDocument from documents.data_models import DocumentMetadataOverrides @@ -88,7 +87,7 @@ class ConsumeTaskPlugin(abc.ABC): """ @abc.abstractmethod - def run(self) -> Optional[str]: + def run(self) -> str | None: """ The bulk of plugin processing, this does whatever action the plugin is for. diff --git a/src/documents/plugins/helpers.py b/src/documents/plugins/helpers.py index 2d3686db4..20380b852 100644 --- a/src/documents/plugins/helpers.py +++ b/src/documents/plugins/helpers.py @@ -1,7 +1,5 @@ import enum from typing import TYPE_CHECKING -from typing import Optional -from typing import Union from asgiref.sync import async_to_sync from channels.layers import get_channel_layer @@ -23,9 +21,9 @@ class ProgressManager: of the open/close of the layer to ensure messages go out and everything is cleaned up """ - def __init__(self, filename: str, task_id: Optional[str] = None) -> None: + def __init__(self, filename: str, task_id: str | None = None) -> None: self.filename = filename - self._channel: Optional[RedisPubSubChannelLayer] = None + self._channel: RedisPubSubChannelLayer | None = None self.task_id = task_id def __enter__(self): @@ -57,7 +55,7 @@ class ProgressManager: message: str, current_progress: int, max_progress: int, - extra_args: Optional[dict[str, Union[str, int, None]]] = None, + extra_args: dict[str, str | int | None] | None = None, ) -> None: # Ensure the layer is open self.open() diff --git a/src/documents/serialisers.py b/src/documents/serialisers.py index 5218cbf8a..30f3dd26d 100644 --- a/src/documents/serialisers.py +++ b/src/documents/serialisers.py @@ -750,6 +750,7 @@ class DocumentSerializer( original_file_name = SerializerMethodField() archived_file_name = SerializerMethodField() created_date = serializers.DateField(required=False) + page_count = SerializerMethodField() custom_fields = CustomFieldInstanceSerializer( many=True, @@ -770,6 +771,9 @@ class DocumentSerializer( required=False, ) + def get_page_count(self, obj): + return obj.page_count + def get_original_file_name(self, obj): return obj.original_filename @@ -885,6 +889,7 @@ class DocumentSerializer( "notes", "custom_fields", "remove_inbox_tags", + "page_count", ) list_serializer_class = OwnedObjectListSerializer @@ -1384,9 +1389,18 @@ class PostDocumentSerializer(serializers.Serializer): mime_type = magic.from_buffer(document_data, mime=True) if not is_mime_type_supported(mime_type): - raise serializers.ValidationError( - _("File type %(type)s not supported") % {"type": mime_type}, - ) + if ( + mime_type in settings.CONSUMER_PDF_RECOVERABLE_MIME_TYPES + and document.name.endswith( + ".pdf", + ) + ): + # If the file is an invalid PDF, we can try to recover it later in the consumer + mime_type = "application/pdf" + else: + raise serializers.ValidationError( + _("File type %(type)s not supported") % {"type": mime_type}, + ) return document.name, document_data diff --git a/src/documents/signals/handlers.py b/src/documents/signals/handlers.py index 2cf73ca41..cf6733dd5 100644 --- a/src/documents/signals/handlers.py +++ b/src/documents/signals/handlers.py @@ -1,7 +1,6 @@ import logging import os import shutil -from typing import Optional from celery import states from celery.signals import before_task_publish @@ -62,7 +61,7 @@ def _suggestion_printer( suggestion_type: str, document: Document, selected: MatchingModel, - base_url: Optional[str] = None, + base_url: str | None = None, ): """ Smaller helper to reduce duplication when just outputting suggestions to the console @@ -80,7 +79,7 @@ def set_correspondent( sender, document: Document, logging_group=None, - classifier: Optional[DocumentClassifier] = None, + classifier: DocumentClassifier | None = None, replace=False, use_first=True, suggest=False, @@ -135,7 +134,7 @@ def set_document_type( sender, document: Document, logging_group=None, - classifier: Optional[DocumentClassifier] = None, + classifier: DocumentClassifier | None = None, replace=False, use_first=True, suggest=False, @@ -191,7 +190,7 @@ def set_tags( sender, document: Document, logging_group=None, - classifier: Optional[DocumentClassifier] = None, + classifier: DocumentClassifier | None = None, replace=False, suggest=False, base_url=None, @@ -246,7 +245,7 @@ def set_storage_path( sender, document: Document, logging_group=None, - classifier: Optional[DocumentClassifier] = None, + classifier: DocumentClassifier | None = None, replace=False, use_first=True, suggest=False, diff --git a/src/documents/tasks.py b/src/documents/tasks.py index 2f1bc2ee4..8f5ee51bc 100644 --- a/src/documents/tasks.py +++ b/src/documents/tasks.py @@ -5,7 +5,6 @@ import uuid from datetime import timedelta from pathlib import Path from tempfile import TemporaryDirectory -from typing import Optional import tqdm from celery import Task @@ -106,7 +105,7 @@ def train_classifier(): def consume_file( self: Task, input_doc: ConsumableDocument, - overrides: Optional[DocumentMetadataOverrides] = None, + overrides: DocumentMetadataOverrides | None = None, ): # Default no overrides if overrides is None: diff --git a/src/documents/tests/samples/invalid_pdf.pdf b/src/documents/tests/samples/invalid_pdf.pdf new file mode 100644 index 000000000..f226c2d84 Binary files /dev/null and b/src/documents/tests/samples/invalid_pdf.pdf differ diff --git a/src/documents/tests/test_api_documents.py b/src/documents/tests/test_api_documents.py index ee2e8ee1e..b1cd43932 100644 --- a/src/documents/tests/test_api_documents.py +++ b/src/documents/tests/test_api_documents.py @@ -1402,6 +1402,27 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase): self.assertEqual(overrides.filename, "simple.pdf") self.assertEqual(overrides.custom_field_ids, [custom_field.id]) + def test_upload_invalid_pdf(self): + """ + GIVEN: Invalid PDF named "*.pdf" that mime_type is in settings.CONSUMER_PDF_RECOVERABLE_MIME_TYPES + WHEN: Upload the file + THEN: The file is not rejected + """ + self.consume_file_mock.return_value = celery.result.AsyncResult( + id=str(uuid.uuid4()), + ) + + with open( + os.path.join(os.path.dirname(__file__), "samples", "invalid_pdf.pdf"), + "rb", + ) as f: + response = self.client.post( + "/api/documents/post_document/", + {"document": f}, + ) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + def test_get_metadata(self): doc = Document.objects.create( title="test", diff --git a/src/documents/tests/test_api_filter_by_custom_fields.py b/src/documents/tests/test_api_filter_by_custom_fields.py index 421376e44..327855c4c 100644 --- a/src/documents/tests/test_api_filter_by_custom_fields.py +++ b/src/documents/tests/test_api_filter_by_custom_fields.py @@ -1,6 +1,6 @@ import json +from collections.abc import Callable from datetime import date -from typing import Callable from unittest.mock import Mock from urllib.parse import quote diff --git a/src/documents/tests/test_bulk_edit.py b/src/documents/tests/test_bulk_edit.py index fed93cd01..d80116a80 100644 --- a/src/documents/tests/test_bulk_edit.py +++ b/src/documents/tests/test_bulk_edit.py @@ -389,6 +389,7 @@ class TestPDFActions(DirectoriesMixin, TestCase): title="B", filename=sample2, mime_type="application/pdf", + page_count=8, ) self.doc2.archive_filename = sample2_archive self.doc2.save() @@ -681,14 +682,20 @@ class TestPDFActions(DirectoriesMixin, TestCase): THEN: - Save should be called once - Archive file should be updated once + - The document's page_count should be reduced by the number of deleted pages """ doc_ids = [self.doc2.id] + initial_page_count = self.doc2.page_count pages = [1, 3] result = bulk_edit.delete_pages(doc_ids, pages) mock_pdf_save.assert_called_once() mock_update_archive_file.assert_called_once() self.assertEqual(result, "OK") + expected_page_count = initial_page_count - len(pages) + self.doc2.refresh_from_db() + self.assertEqual(self.doc2.page_count, expected_page_count) + @mock.patch("documents.tasks.update_document_archive_file.delay") @mock.patch("pikepdf.Pdf.save") def test_delete_pages_with_error(self, mock_pdf_save, mock_update_archive_file): diff --git a/src/documents/tests/test_consumer.py b/src/documents/tests/test_consumer.py index 5b56e2cca..aa452e15b 100644 --- a/src/documents/tests/test_consumer.py +++ b/src/documents/tests/test_consumer.py @@ -235,6 +235,8 @@ class FaultyGenericExceptionParser(_BaseTestParser): def fake_magic_from_file(file, mime=False): if mime: + if file.name.startswith("invalid_pdf"): + return "application/octet-stream" if os.path.splitext(file)[1] == ".pdf": return "application/pdf" elif os.path.splitext(file)[1] == ".png": @@ -952,6 +954,27 @@ class TestConsumer( sanity_check() + @mock.patch("documents.consumer.run_subprocess") + def test_try_to_clean_invalid_pdf(self, m): + shutil.copy( + Path(__file__).parent / "samples" / "invalid_pdf.pdf", + settings.CONSUMPTION_DIR / "invalid_pdf.pdf", + ) + with self.get_consumer( + settings.CONSUMPTION_DIR / "invalid_pdf.pdf", + ) as consumer: + # fails because no qpdf + self.assertRaises(ConsumerError, consumer.run) + + m.assert_called_once() + + args, _ = m.call_args + + command = args[0] + + self.assertEqual(command[0], "qpdf") + self.assertEqual(command[1], "--replace-input") + @mock.patch("documents.consumer.magic.from_file", fake_magic_from_file) class TestConsumerCreatedDate(DirectoriesMixin, GetConsumerMixin, TestCase): diff --git a/src/documents/tests/test_double_sided.py b/src/documents/tests/test_double_sided.py index 64cd7be48..5d068b735 100644 --- a/src/documents/tests/test_double_sided.py +++ b/src/documents/tests/test_double_sided.py @@ -2,7 +2,6 @@ import datetime as dt import os import shutil from pathlib import Path -from typing import Union from unittest import mock from django.test import TestCase @@ -34,7 +33,7 @@ class TestDoubleSided(DirectoriesMixin, FileSystemAssertsMixin, TestCase): self.dirs.double_sided_dir.mkdir() self.staging_file = self.dirs.scratch_dir / STAGING_FILE_NAME - def consume_file(self, srcname, dstname: Union[str, Path] = "foo.pdf"): + def consume_file(self, srcname, dstname: str | Path = "foo.pdf"): """ Starts the consume process and also ensures the destination file does not exist afterwards diff --git a/src/documents/tests/test_migration_archive_files.py b/src/documents/tests/test_migration_archive_files.py index 17dc8f040..5039e13de 100644 --- a/src/documents/tests/test_migration_archive_files.py +++ b/src/documents/tests/test_migration_archive_files.py @@ -3,7 +3,6 @@ import importlib import os import shutil from pathlib import Path -from typing import Optional from unittest import mock from django.conf import settings @@ -66,8 +65,8 @@ def make_test_document( mime_type: str, original: str, original_filename: str, - archive: Optional[str] = None, - archive_filename: Optional[str] = None, + archive: str | None = None, + archive_filename: str | None = None, ): doc = document_class() doc.filename = original_filename diff --git a/src/documents/tests/test_migration_document_pages_count.py b/src/documents/tests/test_migration_document_pages_count.py new file mode 100644 index 000000000..e656bf1b8 --- /dev/null +++ b/src/documents/tests/test_migration_document_pages_count.py @@ -0,0 +1,59 @@ +import os +import shutil +from pathlib import Path + +from django.conf import settings + +from documents.tests.utils import TestMigrations + + +def source_path_before(self): + if self.filename: + fname = str(self.filename) + + return os.path.join(settings.ORIGINALS_DIR, fname) + + +class TestMigrateDocumentPageCount(TestMigrations): + migrate_from = "1052_document_transaction_id" + migrate_to = "1053_document_page_count" + + def setUpBeforeMigration(self, apps): + Document = apps.get_model("documents", "Document") + doc = Document.objects.create( + title="test1", + mime_type="application/pdf", + filename="file1.pdf", + ) + self.doc_id = doc.id + shutil.copy( + Path(__file__).parent / "samples" / "simple.pdf", + source_path_before(doc), + ) + + def testDocumentPageCountMigrated(self): + Document = self.apps.get_model("documents", "Document") + + doc = Document.objects.get(id=self.doc_id) + self.assertEqual(doc.page_count, 1) + + +class TestMigrateDocumentPageCountBackwards(TestMigrations): + migrate_from = "1053_document_page_count" + migrate_to = "1052_document_transaction_id" + + def setUpBeforeMigration(self, apps): + Document = apps.get_model("documents", "Document") + doc = Document.objects.create( + title="test1", + mime_type="application/pdf", + filename="file1.pdf", + page_count=8, + ) + self.doc_id = doc.id + + def test_remove_number_of_pages_to_page_count(self): + Document = self.apps.get_model("documents", "Document") + self.assertFalse( + "page_count" in [field.name for field in Document._meta.get_fields()], + ) diff --git a/src/documents/tests/test_migration_encrypted_webp_conversion.py b/src/documents/tests/test_migration_encrypted_webp_conversion.py index 35a711901..0660df368 100644 --- a/src/documents/tests/test_migration_encrypted_webp_conversion.py +++ b/src/documents/tests/test_migration_encrypted_webp_conversion.py @@ -1,10 +1,9 @@ import importlib import shutil import tempfile +from collections.abc import Callable from collections.abc import Iterable from pathlib import Path -from typing import Callable -from typing import Union from unittest import mock from django.test import override_settings @@ -115,7 +114,7 @@ class TestMigrateToEncrytpedWebPThumbnails(TestMigrations): def assert_file_count_by_extension( self, ext: str, - dir: Union[str, Path], + dir: str | Path, expected_count: int, ): """ diff --git a/src/documents/tests/test_migration_webp_conversion.py b/src/documents/tests/test_migration_webp_conversion.py index cf0ee22a8..cd148ed6f 100644 --- a/src/documents/tests/test_migration_webp_conversion.py +++ b/src/documents/tests/test_migration_webp_conversion.py @@ -1,10 +1,9 @@ import importlib import shutil import tempfile +from collections.abc import Callable from collections.abc import Iterable from pathlib import Path -from typing import Callable -from typing import Union from unittest import mock from django.test import override_settings @@ -86,7 +85,7 @@ class TestMigrateWebPThumbnails(TestMigrations): def assert_file_count_by_extension( self, ext: str, - dir: Union[str, Path], + dir: str | Path, expected_count: int, ): """ diff --git a/src/documents/tests/utils.py b/src/documents/tests/utils.py index 4ec0851df..cd4db84e6 100644 --- a/src/documents/tests/utils.py +++ b/src/documents/tests/utils.py @@ -3,15 +3,13 @@ import tempfile import time import warnings from collections import namedtuple +from collections.abc import Callable from collections.abc import Generator from collections.abc import Iterator from contextlib import contextmanager from os import PathLike from pathlib import Path from typing import Any -from typing import Callable -from typing import Optional -from typing import Union from unittest import mock import httpx @@ -91,7 +89,7 @@ def paperless_environment(): def util_call_with_backoff( method_or_callable: Callable, - args: Union[list, tuple], + args: list | tuple, *, skip_on_50x_err=True, ) -> tuple[bool, Any]: @@ -170,22 +168,22 @@ class FileSystemAssertsMixin: Utilities for checks various state information of the file system """ - def assertIsFile(self, path: Union[PathLike, str]): + def assertIsFile(self, path: PathLike | str): self.assertTrue(Path(path).resolve().is_file(), f"File does not exist: {path}") - def assertIsNotFile(self, path: Union[PathLike, str]): + def assertIsNotFile(self, path: PathLike | str): self.assertFalse(Path(path).resolve().is_file(), f"File does exist: {path}") - def assertIsDir(self, path: Union[PathLike, str]): + def assertIsDir(self, path: PathLike | str): self.assertTrue(Path(path).resolve().is_dir(), f"Dir does not exist: {path}") - def assertIsNotDir(self, path: Union[PathLike, str]): + def assertIsNotDir(self, path: PathLike | str): self.assertFalse(Path(path).resolve().is_dir(), f"Dir does exist: {path}") def assertFilesEqual( self, - path1: Union[PathLike, str], - path2: Union[PathLike, str], + path1: PathLike | str, + path2: PathLike | str, ): path1 = Path(path1) path2 = Path(path2) @@ -196,7 +194,7 @@ class FileSystemAssertsMixin: self.assertEqual(hash1, hash2, "File SHA256 mismatch") - def assertFileCountInDir(self, path: Union[PathLike, str], count: int): + def assertFileCountInDir(self, path: PathLike | str, count: int): path = Path(path).resolve() self.assertTrue(path.is_dir(), f"Path {path} is not a directory") files = [x for x in path.iterdir() if x.is_file()] @@ -340,7 +338,7 @@ class GetConsumerMixin: def get_consumer( self, filepath: Path, - overrides: Union[DocumentMetadataOverrides, None] = None, + overrides: DocumentMetadataOverrides | None = None, source: DocumentSource = DocumentSource.ConsumeFolder, ) -> Generator[ConsumerPlugin, None, None]: # Store this for verification @@ -368,7 +366,7 @@ class DummyProgressManager: mock.patch("documents.tasks.ProgressManager", DummyProgressManager) """ - def __init__(self, filename: str, task_id: Optional[str] = None) -> None: + def __init__(self, filename: str, task_id: str | None = None) -> None: self.filename = filename self.task_id = task_id self.payloads = [] @@ -392,7 +390,7 @@ class DummyProgressManager: message: str, current_progress: int, max_progress: int, - extra_args: Optional[dict[str, Union[str, int]]] = None, + extra_args: dict[str, str | int] | None = None, ) -> None: # Ensure the layer is open self.open() diff --git a/src/documents/utils.py b/src/documents/utils.py index 0af1f54e3..d8a8e8ab2 100644 --- a/src/documents/utils.py +++ b/src/documents/utils.py @@ -4,21 +4,19 @@ from os import utime from pathlib import Path from subprocess import CompletedProcess from subprocess import run -from typing import Optional -from typing import Union from django.conf import settings from PIL import Image def _coerce_to_path( - source: Union[Path, str], - dest: Union[Path, str], + source: Path | str, + dest: Path | str, ) -> tuple[Path, Path]: return Path(source).resolve(), Path(dest).resolve() -def copy_basic_file_stats(source: Union[Path, str], dest: Union[Path, str]) -> None: +def copy_basic_file_stats(source: Path | str, dest: Path | str) -> None: """ Copies only the m_time and a_time attributes from source to destination. Both are expected to exist. @@ -33,8 +31,8 @@ def copy_basic_file_stats(source: Union[Path, str], dest: Union[Path, str]) -> N def copy_file_with_basic_stats( - source: Union[Path, str], - dest: Union[Path, str], + source: Path | str, + dest: Path | str, ) -> None: """ A sort of simpler copy2 that doesn't copy extended file attributes, @@ -53,7 +51,7 @@ def maybe_override_pixel_limit() -> None: """ Maybe overrides the PIL limit on pixel count, if configured to allow it """ - limit: Optional[Union[float, int]] = settings.MAX_IMAGE_PIXELS + limit: float | int | None = settings.MAX_IMAGE_PIXELS if limit is not None and limit >= 0: pixel_count = limit if pixel_count == 0: @@ -63,8 +61,8 @@ def maybe_override_pixel_limit() -> None: def run_subprocess( arguments: list[str], - env: Optional[dict[str, str]] = None, - logger: Optional[logging.Logger] = None, + env: dict[str, str] | None = None, + logger: logging.Logger | None = None, *, check_exit_code: bool = True, log_stdout: bool = True, diff --git a/src/documents/views.py b/src/documents/views.py index a8a5bf97d..c870c15b5 100644 --- a/src/documents/views.py +++ b/src/documents/views.py @@ -361,6 +361,7 @@ class DocumentViewSet( "archive_serial_number", "num_notes", "owner", + "page_count", ) def get_queryset(self): @@ -1637,9 +1638,8 @@ class RemoteVersionView(GenericAPIView): try: remote_json = json.loads(remote) remote_version = remote_json["tag_name"] - # Basically PEP 616 but that only went in 3.9 - if remote_version.startswith("ngx-"): - remote_version = remote_version[len("ngx-") :] + # Some early tags used ngx-x.y.z + remote_version = remote_version.removeprefix("ngx-") except ValueError: logger.debug("An error occurred parsing remote version json") except urllib.error.URLError: diff --git a/src/paperless/config.py b/src/paperless/config.py index 00afb2a13..8a40fc6c6 100644 --- a/src/paperless/config.py +++ b/src/paperless/config.py @@ -1,6 +1,5 @@ import dataclasses import json -from typing import Optional from django.conf import settings @@ -44,18 +43,18 @@ class OcrConfig(OutputTypeConfig): correspond almost directly to the OCRMyPDF options """ - pages: Optional[int] = dataclasses.field(init=False) + pages: int | None = dataclasses.field(init=False) language: str = dataclasses.field(init=False) mode: str = dataclasses.field(init=False) skip_archive_file: str = dataclasses.field(init=False) - image_dpi: Optional[int] = dataclasses.field(init=False) + image_dpi: int | None = dataclasses.field(init=False) clean: str = dataclasses.field(init=False) deskew: bool = dataclasses.field(init=False) rotate: bool = dataclasses.field(init=False) rotate_threshold: float = dataclasses.field(init=False) - max_image_pixel: Optional[float] = dataclasses.field(init=False) + max_image_pixel: float | None = dataclasses.field(init=False) color_conversion_strategy: str = dataclasses.field(init=False) - user_args: Optional[dict[str, str]] = dataclasses.field(init=False) + user_args: dict[str, str] | None = dataclasses.field(init=False) def __post_init__(self) -> None: super().__post_init__() diff --git a/src/paperless/settings.py b/src/paperless/settings.py index 95badde2f..ab943f30f 100644 --- a/src/paperless/settings.py +++ b/src/paperless/settings.py @@ -9,8 +9,6 @@ from os import PathLike from pathlib import Path from platform import machine from typing import Final -from typing import Optional -from typing import Union from urllib.parse import urlparse from celery.schedules import crontab @@ -57,7 +55,7 @@ def __get_int(key: str, default: int) -> int: return int(os.getenv(key, default)) -def __get_optional_int(key: str) -> Optional[int]: +def __get_optional_int(key: str) -> int | None: """ Returns None if the environment key is not present, otherwise an integer """ @@ -75,7 +73,7 @@ def __get_float(key: str, default: float) -> float: def __get_path( key: str, - default: Union[PathLike, str], + default: PathLike | str, ) -> Path: """ Return a normalized, absolute path based on the environment variable or a default, @@ -86,7 +84,7 @@ def __get_path( return Path(default).resolve() -def __get_optional_path(key: str) -> Optional[Path]: +def __get_optional_path(key: str) -> Path | None: """ Returns None if the environment key is not present, otherwise a fully resolved Path """ @@ -97,7 +95,7 @@ def __get_optional_path(key: str) -> Optional[Path]: def __get_list( key: str, - default: Optional[list[str]] = None, + default: list[str] | None = None, sep: str = ",", ) -> list[str]: """ @@ -112,7 +110,7 @@ def __get_list( return [] -def _parse_redis_url(env_redis: Optional[str]) -> tuple[str, str]: +def _parse_redis_url(env_redis: str | None) -> tuple[str, str]: """ Gets the Redis information from the environment or a default and handles converting from incompatible django_channels and celery formats. @@ -960,6 +958,8 @@ CONSUMER_COLLATE_DOUBLE_SIDED_TIFF_SUPPORT: Final[bool] = __get_boolean( "PAPERLESS_CONSUMER_COLLATE_DOUBLE_SIDED_TIFF_SUPPORT", ) +CONSUMER_PDF_RECOVERABLE_MIME_TYPES = ("application/octet-stream",) + OCR_PAGES = __get_optional_int("PAPERLESS_OCR_PAGES") # The default language that tesseract will attempt to use when parsing @@ -987,7 +987,7 @@ OCR_ROTATE_PAGES_THRESHOLD: Final[float] = __get_float( 12.0, ) -OCR_MAX_IMAGE_PIXELS: Final[Optional[int]] = __get_optional_int( +OCR_MAX_IMAGE_PIXELS: Final[int | None] = __get_optional_int( "PAPERLESS_OCR_MAX_IMAGE_PIXELS", ) @@ -998,7 +998,7 @@ OCR_COLOR_CONVERSION_STRATEGY = os.getenv( OCR_USER_ARGS = os.getenv("PAPERLESS_OCR_USER_ARGS") -MAX_IMAGE_PIXELS: Final[Optional[int]] = __get_optional_int( +MAX_IMAGE_PIXELS: Final[int | None] = __get_optional_int( "PAPERLESS_MAX_IMAGE_PIXELS", ) @@ -1126,7 +1126,7 @@ APP_LOGO = os.getenv("PAPERLESS_APP_LOGO", None) ############################################################################### -def _get_nltk_language_setting(ocr_lang: str) -> Optional[str]: +def _get_nltk_language_setting(ocr_lang: str) -> str | None: """ Maps an ISO-639-1 language code supported by Tesseract into an optional NLTK language name. This is the set of common supported @@ -1163,7 +1163,7 @@ def _get_nltk_language_setting(ocr_lang: str) -> Optional[str]: NLTK_ENABLED: Final[bool] = __get_boolean("PAPERLESS_ENABLE_NLTK", "yes") -NLTK_LANGUAGE: Optional[str] = _get_nltk_language_setting(OCR_LANGUAGE) +NLTK_LANGUAGE: str | None = _get_nltk_language_setting(OCR_LANGUAGE) ############################################################################### # Email (SMTP) Backend # @@ -1185,7 +1185,7 @@ if DEBUG: # pragma: no cover # Email Preprocessors # ############################################################################### -EMAIL_GNUPG_HOME: Final[Optional[str]] = os.getenv("PAPERLESS_EMAIL_GNUPG_HOME") +EMAIL_GNUPG_HOME: Final[str | None] = os.getenv("PAPERLESS_EMAIL_GNUPG_HOME") EMAIL_ENABLE_GPG_DECRYPTOR: Final[bool] = __get_boolean( "PAPERLESS_ENABLE_GPG_DECRYPTOR", ) diff --git a/src/paperless_mail/mail.py b/src/paperless_mail/mail.py index 4ecd44659..b52a2ebe4 100644 --- a/src/paperless_mail/mail.py +++ b/src/paperless_mail/mail.py @@ -10,8 +10,6 @@ from datetime import timedelta from fnmatch import fnmatch from pathlib import Path from typing import TYPE_CHECKING -from typing import Optional -from typing import Union import magic import pathvalidate @@ -84,7 +82,7 @@ class BaseMailAction: read mails when the action is to mark mails as read). """ - def get_criteria(self) -> Union[dict, LogicOperator]: + def get_criteria(self) -> dict | LogicOperator: """ Returns filtering criteria/query for this mail action. """ @@ -453,7 +451,7 @@ class MailAccountHandler(LoggingMixin): else: self.log.debug(f"Skipping mail preprocessor {preprocessor_type.NAME}") - def _correspondent_from_name(self, name: str) -> Optional[Correspondent]: + def _correspondent_from_name(self, name: str) -> Correspondent | None: try: return Correspondent.objects.get_or_create(name=name)[0] except DatabaseError as e: @@ -465,7 +463,7 @@ class MailAccountHandler(LoggingMixin): message: MailMessage, att: MailAttachment, rule: MailRule, - ) -> Optional[str]: + ) -> str | None: if rule.assign_title_from == MailRule.TitleSource.FROM_SUBJECT: return message.subject @@ -484,7 +482,7 @@ class MailAccountHandler(LoggingMixin): self, message: MailMessage, rule: MailRule, - ) -> Optional[Correspondent]: + ) -> Correspondent | None: c_from = rule.assign_correspondent_from if c_from == MailRule.CorrespondentSource.FROM_NOTHING: @@ -688,7 +686,7 @@ class MailAccountHandler(LoggingMixin): def filename_inclusion_matches( self, - filter_attachment_filename_include: Optional[str], + filter_attachment_filename_include: str | None, filename: str, ) -> bool: if filter_attachment_filename_include: @@ -707,7 +705,7 @@ class MailAccountHandler(LoggingMixin): def filename_exclusion_matches( self, - filter_attachment_filename_exclude: Optional[str], + filter_attachment_filename_exclude: str | None, filename: str, ) -> bool: if filter_attachment_filename_exclude: diff --git a/src/paperless_mail/parsers.py b/src/paperless_mail/parsers.py index 4e83844e2..d98fb7238 100644 --- a/src/paperless_mail/parsers.py +++ b/src/paperless_mail/parsers.py @@ -1,7 +1,6 @@ import re from html import escape from pathlib import Path -from typing import Optional from bleach import clean from bleach import linkify @@ -33,7 +32,7 @@ class MailDocumentParser(DocumentParser): logging_name = "paperless.parsing.mail" - def _settings_to_gotenberg_pdfa(self) -> Optional[PdfAFormat]: + def _settings_to_gotenberg_pdfa(self) -> PdfAFormat | None: """ Converts our requested PDF/A output into the Gotenberg API format @@ -44,7 +43,7 @@ class MailDocumentParser(DocumentParser): }: return PdfAFormat.A2b elif settings.OCR_OUTPUT_TYPE == OutputTypeChoices.PDF_A1: # pragma: no cover - self.log.warn( + self.log.warning( "Gotenberg does not support PDF/A-1a, choosing PDF/A-2b instead", ) return PdfAFormat.A2b diff --git a/src/paperless_mail/tests/test_mail.py b/src/paperless_mail/tests/test_mail.py index bdadc7450..c12b54ffe 100644 --- a/src/paperless_mail/tests/test_mail.py +++ b/src/paperless_mail/tests/test_mail.py @@ -4,8 +4,6 @@ import random import uuid from collections import namedtuple from contextlib import AbstractContextManager -from typing import Optional -from typing import Union from unittest import mock import pytest @@ -199,11 +197,11 @@ class MessageBuilder: def create_message( self, - attachments: Union[int, list[_AttachmentDef]] = 1, + attachments: int | list[_AttachmentDef] = 1, body: str = "", subject: str = "the subject", from_: str = "no_one@mail.com", - to: Optional[list[str]] = None, + to: list[str] | None = None, seen: bool = False, flagged: bool = False, processed: bool = False, @@ -622,8 +620,8 @@ class TestMail( @dataclasses.dataclass(frozen=True) class FilterTestCase: name: str - include_pattern: Optional[str] - exclude_pattern: Optional[str] + include_pattern: str | None + exclude_pattern: str | None expected_matches: list[str] tests = [ diff --git a/src/paperless_tesseract/parsers.py b/src/paperless_tesseract/parsers.py index 4e92990f1..6b9ec3d93 100644 --- a/src/paperless_tesseract/parsers.py +++ b/src/paperless_tesseract/parsers.py @@ -3,7 +3,6 @@ import re import tempfile from pathlib import Path from typing import TYPE_CHECKING -from typing import Optional from django.conf import settings from PIL import Image @@ -41,6 +40,15 @@ class RasterisedDocumentParser(DocumentParser): """ return OcrConfig() + def get_page_count(self, document_path, mime_type): + page_count = None + if mime_type == "application/pdf": + import pikepdf + + with pikepdf.Pdf.open(document_path) as pdf: + page_count = len(pdf.pages) + return page_count + def extract_metadata(self, document_path, mime_type): result = [] if mime_type == "application/pdf": @@ -115,7 +123,7 @@ class RasterisedDocumentParser(DocumentParser): ) return no_alpha_image - def get_dpi(self, image) -> Optional[int]: + def get_dpi(self, image) -> int | None: try: with Image.open(image) as im: x, y = im.info["dpi"] @@ -124,7 +132,7 @@ class RasterisedDocumentParser(DocumentParser): self.log.warning(f"Error while getting DPI from image {image}: {e}") return None - def calculate_a4_dpi(self, image) -> Optional[int]: + def calculate_a4_dpi(self, image) -> int | None: try: with Image.open(image) as im: width, height = im.size @@ -139,9 +147,9 @@ class RasterisedDocumentParser(DocumentParser): def extract_text( self, - sidecar_file: Optional[Path], + sidecar_file: Path | None, pdf_file: Path, - ) -> Optional[str]: + ) -> str | None: # When re-doing OCR, the sidecar contains ONLY the new text, not # the whole text, so do not utilize it in that case if ( diff --git a/src/paperless_tesseract/tests/test_parser.py b/src/paperless_tesseract/tests/test_parser.py index d63d965c5..45a5939ab 100644 --- a/src/paperless_tesseract/tests/test_parser.py +++ b/src/paperless_tesseract/tests/test_parser.py @@ -57,6 +57,30 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase): self.assertContainsStrings(text.strip(), ["This is a test document."]) + def test_get_page_count(self): + """ + GIVEN: + - PDF file with a single page + - PDF file with multiple pages + WHEN: + - The number of pages is requested + THEN: + - The method returns 1 as the expected number of pages + - The method returns the correct number of pages (6) + """ + parser = RasterisedDocumentParser(uuid.uuid4()) + page_count = parser.get_page_count( + os.path.join(self.SAMPLE_FILES, "simple-digital.pdf"), + "application/pdf", + ) + self.assertEqual(page_count, 1) + + page_count = parser.get_page_count( + os.path.join(self.SAMPLE_FILES, "multi-page-mixed.pdf"), + "application/pdf", + ) + self.assertEqual(page_count, 6) + def test_thumbnail(self): parser = RasterisedDocumentParser(uuid.uuid4()) thumb = parser.get_thumbnail( diff --git a/src/paperless_tika/parsers.py b/src/paperless_tika/parsers.py index 519f6c6ae..f51a03916 100644 --- a/src/paperless_tika/parsers.py +++ b/src/paperless_tika/parsers.py @@ -102,7 +102,7 @@ class TikaDocumentParser(DocumentParser): }: route.pdf_format(PdfAFormat.A2b) elif settings.OCR_OUTPUT_TYPE == OutputTypeChoices.PDF_A1: - self.log.warn( + self.log.warning( "Gotenberg does not support PDF/A-1a, choosing PDF/A-2b instead", ) route.pdf_format(PdfAFormat.A2b)