diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b5decde4519f7d4ca45a84e8f70aa23dd745d677..be605d93e176d6515afef3269bfa9fc19aca7c70 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -259,6 +259,7 @@ django-tests: - pdm sync -d --no-editable -G dev - pdm run test - pdm run coverage xml -i + - pdm run coverage report coverage: '/(?i)total(?:\s+\d+){4}\s+(\d+)%/' artifacts: reports: diff --git a/.pylintrc.toml b/.pylintrc.toml index 92b9f079fb5afc1610d22c1258eaab29e1988b71..a71b646c8e7171307309b5af0ab825da8491a5e0 100644 --- a/.pylintrc.toml +++ b/.pylintrc.toml @@ -19,11 +19,11 @@ ignore-patterns = ["^\\.#"] # Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the # number of processors available to use, and will cap the count on Windows to # avoid hangs. -jobs = 0 +#jobs = 0 # List of plugins (as comma separated values of python module names) to load, # usually to register additional checkers. -load-plugins = ["pylint_django", "pylint_per_file_ignores"] +load-plugins = ["pylint_django"] # Pickle collected data for later comparisons. persistent = true @@ -156,14 +156,10 @@ disable = [ "too-many-return-statements", "too-many-statements", + "unused-argument", + "protected-access", ] -# TODO: replace with TOML List after https://github.com/christopherpickering/pylint-per-file-ignores/issues/160 is fixec -per-file-ignores = """ - "src/hub/settings/*:unused-wildcard-import,wildcard-import,unused-import", - ".*/tests/.*:invalid-name", -""" - [tool.pylint.miscellaneous] # List of note tags to take in consideration, separated by a comma. diff --git a/pdm.lock b/pdm.lock index 80fa92196c5cf368447c3c03aec97b0f53e7aa64..e40d73c468e64fb34e85aa0c6c2a75aca70ddd24 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default", "dev", "lint", "local", "static-analysis", "typing", "watchfiles"] strategy = ["inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:fc86b394f3b33fdd867e661094a00a1b8cbb93ae926d2d1e8b1590fa54771159" +content_hash = "sha256:af6518bad6a153127d484181441f930ee00c15c1d0f704ea906e0fc5b4d15282" [[metadata.targets]] requires_python = "==3.13.*" @@ -138,23 +138,23 @@ files = [ [[package]] name = "boto3" -version = "1.35.57" +version = "1.35.68" requires_python = ">=3.8" summary = "The AWS SDK for Python" groups = ["default"] dependencies = [ - "botocore<1.36.0,>=1.35.57", + "botocore<1.36.0,>=1.35.68", "jmespath<2.0.0,>=0.7.1", "s3transfer<0.11.0,>=0.10.0", ] files = [ - {file = "boto3-1.35.57-py3-none-any.whl", hash = "sha256:9edf49640c79a05b0a72f4c2d1e24dfc164344b680535a645f455ac624dc3680"}, - {file = "boto3-1.35.57.tar.gz", hash = "sha256:db58348849a5af061f0f5ec9c3b699da5221ca83354059fdccb798e3ddb6b62a"}, + {file = "boto3-1.35.68-py3-none-any.whl", hash = "sha256:9b26fa31901da7793c1dcd65eee9bab7e897d8aa1ffed0b5e1c3bce93d2aefe4"}, + {file = "boto3-1.35.68.tar.gz", hash = "sha256:091d6bed1422370987a839bff3f8755df7404fc15e9fac2a48e8505356f07433"}, ] [[package]] name = "botocore" -version = "1.35.57" +version = "1.35.68" requires_python = ">=3.8" summary = "Low-level, data-driven core of boto 3." groups = ["default"] @@ -165,8 +165,8 @@ dependencies = [ "urllib3<1.27,>=1.25.4; python_version < \"3.10\"", ] files = [ - {file = "botocore-1.35.57-py3-none-any.whl", hash = "sha256:92ddd02469213766872cb2399269dd20948f90348b42bf08379881d5e946cc34"}, - {file = "botocore-1.35.57.tar.gz", hash = "sha256:d96306558085baf0bcb3b022d7a8c39c93494f031edb376694d2b2dcd0e81327"}, + {file = "botocore-1.35.68-py3-none-any.whl", hash = "sha256:599139d5564291f5be873800711f9e4e14a823395ae9ce7b142be775e9849b94"}, + {file = "botocore-1.35.68.tar.gz", hash = "sha256:42c3700583a82f2b5316281a073d644a521d6358837e2b446dc458ba5d990fb4"}, ] [[package]] @@ -292,32 +292,32 @@ files = [ [[package]] name = "coverage" -version = "7.6.4" +version = "7.6.8" requires_python = ">=3.9" summary = "Code coverage measurement for Python" groups = ["dev"] files = [ - {file = "coverage-7.6.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:023bf8ee3ec6d35af9c1c6ccc1d18fa69afa1cb29eaac57cb064dbb262a517f9"}, - {file = "coverage-7.6.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b0ac3d42cb51c4b12df9c5f0dd2f13a4f24f01943627120ec4d293c9181219ba"}, - {file = "coverage-7.6.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8fe4984b431f8621ca53d9380901f62bfb54ff759a1348cd140490ada7b693c"}, - {file = "coverage-7.6.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5fbd612f8a091954a0c8dd4c0b571b973487277d26476f8480bfa4b2a65b5d06"}, - {file = "coverage-7.6.4-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dacbc52de979f2823a819571f2e3a350a7e36b8cb7484cdb1e289bceaf35305f"}, - {file = "coverage-7.6.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:dab4d16dfef34b185032580e2f2f89253d302facba093d5fa9dbe04f569c4f4b"}, - {file = "coverage-7.6.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:862264b12ebb65ad8d863d51f17758b1684560b66ab02770d4f0baf2ff75da21"}, - {file = "coverage-7.6.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5beb1ee382ad32afe424097de57134175fea3faf847b9af002cc7895be4e2a5a"}, - {file = "coverage-7.6.4-cp313-cp313-win32.whl", hash = "sha256:bf20494da9653f6410213424f5f8ad0ed885e01f7e8e59811f572bdb20b8972e"}, - {file = "coverage-7.6.4-cp313-cp313-win_amd64.whl", hash = "sha256:182e6cd5c040cec0a1c8d415a87b67ed01193ed9ad458ee427741c7d8513d963"}, - {file = "coverage-7.6.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a181e99301a0ae128493a24cfe5cfb5b488c4e0bf2f8702091473d033494d04f"}, - {file = "coverage-7.6.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:df57bdbeffe694e7842092c5e2e0bc80fff7f43379d465f932ef36f027179806"}, - {file = "coverage-7.6.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bcd1069e710600e8e4cf27f65c90c7843fa8edfb4520fb0ccb88894cad08b11"}, - {file = "coverage-7.6.4-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99b41d18e6b2a48ba949418db48159d7a2e81c5cc290fc934b7d2380515bd0e3"}, - {file = "coverage-7.6.4-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6b1e54712ba3474f34b7ef7a41e65bd9037ad47916ccb1cc78769bae324c01a"}, - {file = "coverage-7.6.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:53d202fd109416ce011578f321460795abfe10bb901b883cafd9b3ef851bacfc"}, - {file = "coverage-7.6.4-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:c48167910a8f644671de9f2083a23630fbf7a1cb70ce939440cd3328e0919f70"}, - {file = "coverage-7.6.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cc8ff50b50ce532de2fa7a7daae9dd12f0a699bfcd47f20945364e5c31799fef"}, - {file = "coverage-7.6.4-cp313-cp313t-win32.whl", hash = "sha256:b8d3a03d9bfcaf5b0141d07a88456bb6a4c3ce55c080712fec8418ef3610230e"}, - {file = "coverage-7.6.4-cp313-cp313t-win_amd64.whl", hash = "sha256:f3ddf056d3ebcf6ce47bdaf56142af51bb7fad09e4af310241e9db7a3a8022e1"}, - {file = "coverage-7.6.4.tar.gz", hash = "sha256:29fc0f17b1d3fea332f8001d4558f8214af7f1d87a345f3a133c901d60347c73"}, + {file = "coverage-7.6.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0b0c69f4f724c64dfbfe79f5dfb503b42fe6127b8d479b2677f2b227478db2eb"}, + {file = "coverage-7.6.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c15b32a7aca8038ed7644f854bf17b663bc38e1671b5d6f43f9a2b2bd0c46f63"}, + {file = "coverage-7.6.8-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63068a11171e4276f6ece913bde059e77c713b48c3a848814a6537f35afb8365"}, + {file = "coverage-7.6.8-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f4548c5ead23ad13fb7a2c8ea541357474ec13c2b736feb02e19a3085fac002"}, + {file = "coverage-7.6.8-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b4b4299dd0d2c67caaaf286d58aef5e75b125b95615dda4542561a5a566a1e3"}, + {file = "coverage-7.6.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c9ebfb2507751f7196995142f057d1324afdab56db1d9743aab7f50289abd022"}, + {file = "coverage-7.6.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:c1b4474beee02ede1eef86c25ad4600a424fe36cff01a6103cb4533c6bf0169e"}, + {file = "coverage-7.6.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d9fd2547e6decdbf985d579cf3fc78e4c1d662b9b0ff7cc7862baaab71c9cc5b"}, + {file = "coverage-7.6.8-cp313-cp313-win32.whl", hash = "sha256:8aae5aea53cbfe024919715eca696b1a3201886ce83790537d1c3668459c7146"}, + {file = "coverage-7.6.8-cp313-cp313-win_amd64.whl", hash = "sha256:ae270e79f7e169ccfe23284ff5ea2d52a6f401dc01b337efb54b3783e2ce3f28"}, + {file = "coverage-7.6.8-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:de38add67a0af869b0d79c525d3e4588ac1ffa92f39116dbe0ed9753f26eba7d"}, + {file = "coverage-7.6.8-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b07c25d52b1c16ce5de088046cd2432b30f9ad5e224ff17c8f496d9cb7d1d451"}, + {file = "coverage-7.6.8-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62a66ff235e4c2e37ed3b6104d8b478d767ff73838d1222132a7a026aa548764"}, + {file = "coverage-7.6.8-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09b9f848b28081e7b975a3626e9081574a7b9196cde26604540582da60235fdf"}, + {file = "coverage-7.6.8-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:093896e530c38c8e9c996901858ac63f3d4171268db2c9c8b373a228f459bbc5"}, + {file = "coverage-7.6.8-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9a7b8ac36fd688c8361cbc7bf1cb5866977ece6e0b17c34aa0df58bda4fa18a4"}, + {file = "coverage-7.6.8-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:38c51297b35b3ed91670e1e4efb702b790002e3245a28c76e627478aa3c10d83"}, + {file = "coverage-7.6.8-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2e4e0f60cb4bd7396108823548e82fdab72d4d8a65e58e2c19bbbc2f1e2bfa4b"}, + {file = "coverage-7.6.8-cp313-cp313t-win32.whl", hash = "sha256:6535d996f6537ecb298b4e287a855f37deaf64ff007162ec0afb9ab8ba3b8b71"}, + {file = "coverage-7.6.8-cp313-cp313t-win_amd64.whl", hash = "sha256:c79c0685f142ca53256722a384540832420dff4ab15fec1863d7e5bc8691bdcc"}, + {file = "coverage-7.6.8.tar.gz", hash = "sha256:8b2b8503edb06822c86d82fa64a4a5cb0760bb8f31f26e138ec743f422f37cfc"}, ] [[package]] @@ -367,17 +367,17 @@ files = [ [[package]] name = "debugpy" -version = "1.8.8" +version = "1.8.9" requires_python = ">=3.8" summary = "An implementation of the Debug Adapter Protocol for Python" groups = ["local"] files = [ - {file = "debugpy-1.8.8-cp313-cp313-macosx_14_0_universal2.whl", hash = "sha256:705cd123a773d184860ed8dae99becd879dfec361098edbefb5fc0d3683eb804"}, - {file = "debugpy-1.8.8-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:890fd16803f50aa9cb1a9b9b25b5ec321656dd6b78157c74283de241993d086f"}, - {file = "debugpy-1.8.8-cp313-cp313-win32.whl", hash = "sha256:90244598214bbe704aa47556ec591d2f9869ff9e042e301a2859c57106649add"}, - {file = "debugpy-1.8.8-cp313-cp313-win_amd64.whl", hash = "sha256:4b93e4832fd4a759a0c465c967214ed0c8a6e8914bced63a28ddb0dd8c5f078b"}, - {file = "debugpy-1.8.8-py2.py3-none-any.whl", hash = "sha256:ec684553aba5b4066d4de510859922419febc710df7bba04fe9e7ef3de15d34f"}, - {file = "debugpy-1.8.8.zip", hash = "sha256:e6355385db85cbd666be703a96ab7351bc9e6c61d694893206f8001e22aee091"}, + {file = "debugpy-1.8.9-cp313-cp313-macosx_14_0_universal2.whl", hash = "sha256:957ecffff80d47cafa9b6545de9e016ae8c9547c98a538ee96ab5947115fb3dd"}, + {file = "debugpy-1.8.9-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1efbb3ff61487e2c16b3e033bc8595aea578222c08aaf3c4bf0f93fadbd662ee"}, + {file = "debugpy-1.8.9-cp313-cp313-win32.whl", hash = "sha256:7c4d65d03bee875bcb211c76c1d8f10f600c305dbd734beaed4077e902606fee"}, + {file = "debugpy-1.8.9-cp313-cp313-win_amd64.whl", hash = "sha256:e46b420dc1bea64e5bbedd678148be512442bc589b0111bd799367cde051e71a"}, + {file = "debugpy-1.8.9-py2.py3-none-any.whl", hash = "sha256:cc37a6c9987ad743d9c3a14fa1b1a14b7e4e6041f9dd0c8abf8895fe7a97b899"}, + {file = "debugpy-1.8.9.zip", hash = "sha256:1339e14c7d980407248f09824d1b25ff5c5616651689f1e0f0e51bdead3ea13e"}, ] [[package]] @@ -843,7 +843,7 @@ files = [ [[package]] name = "httpcore" -version = "1.0.6" +version = "1.0.7" requires_python = ">=3.8" summary = "A minimal low-level HTTP client." groups = ["default"] @@ -852,8 +852,8 @@ dependencies = [ "h11<0.15,>=0.13", ] files = [ - {file = "httpcore-1.0.6-py3-none-any.whl", hash = "sha256:27b59625743b85577a8c0e10e55b50b5368a4f2cfe8cc7bcfa9cf00829c2682f"}, - {file = "httpcore-1.0.6.tar.gz", hash = "sha256:73f6dbd6eb8c21bbf7ef8efad555481853f5f6acdeaff1edb0694289269ee17f"}, + {file = "httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd"}, + {file = "httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c"}, ] [[package]] @@ -990,13 +990,13 @@ files = [ [[package]] name = "json5" -version = "0.9.25" -requires_python = ">=3.8" +version = "0.9.28" +requires_python = ">=3.8.0" summary = "A Python implementation of the JSON5 data format." groups = ["lint"] files = [ - {file = "json5-0.9.25-py3-none-any.whl", hash = "sha256:34ed7d834b1341a86987ed52f3f76cd8ee184394906b6e22a1e0deb9ab294e8f"}, - {file = "json5-0.9.25.tar.gz", hash = "sha256:548e41b9be043f9426776f05df8635a00fe06104ea51ed24b67f908856e151ae"}, + {file = "json5-0.9.28-py3-none-any.whl", hash = "sha256:29c56f1accdd8bc2e037321237662034a7e07921e2b7223281a5ce2c46f0c4df"}, + {file = "json5-0.9.28.tar.gz", hash = "sha256:1f82f36e615bc5b42f1bbd49dbc94b12563c56408c6ffa06414ea310890e9a6e"}, ] [[package]] @@ -1453,7 +1453,7 @@ files = [ [[package]] name = "psycopg-pool" -version = "3.2.3" +version = "3.2.4" requires_python = ">=3.8" summary = "Connection Pool for Psycopg" groups = ["default"] @@ -1461,8 +1461,8 @@ dependencies = [ "typing-extensions>=4.6", ] files = [ - {file = "psycopg_pool-3.2.3-py3-none-any.whl", hash = "sha256:53bd8e640625e01b2927b2ad96df8ed8e8f91caea4597d45e7673fc7bbb85eb1"}, - {file = "psycopg_pool-3.2.3.tar.gz", hash = "sha256:bb942f123bef4b7fbe4d55421bd3fb01829903c95c0f33fd42b7e94e5ac9b52a"}, + {file = "psycopg_pool-3.2.4-py3-none-any.whl", hash = "sha256:f6a22cff0f21f06d72fb2f5cb48c618946777c49385358e0c88d062c59cbd224"}, + {file = "psycopg_pool-3.2.4.tar.gz", hash = "sha256:61774b5bbf23e8d22bedc7504707135aaf744679f8ef9b3fe29942920746a6ed"}, ] [[package]] @@ -1496,24 +1496,23 @@ files = [ [[package]] name = "pydantic" -version = "2.9.2" +version = "2.10.1" requires_python = ">=3.8" summary = "Data validation using Python type hints" groups = ["default"] dependencies = [ "annotated-types>=0.6.0", - "pydantic-core==2.23.4", - "typing-extensions>=4.12.2; python_version >= \"3.13\"", - "typing-extensions>=4.6.1; python_version < \"3.13\"", + "pydantic-core==2.27.1", + "typing-extensions>=4.12.2", ] files = [ - {file = "pydantic-2.9.2-py3-none-any.whl", hash = "sha256:f048cec7b26778210e28a0459867920654d48e5e62db0958433636cde4254f12"}, - {file = "pydantic-2.9.2.tar.gz", hash = "sha256:d155cef71265d1e9807ed1c32b4c8deec042a44a50a4188b25ac67ecd81a9c0f"}, + {file = "pydantic-2.10.1-py3-none-any.whl", hash = "sha256:a8d20db84de64cf4a7d59e899c2caf0fe9d660c7cfc482528e7020d7dd189a7e"}, + {file = "pydantic-2.10.1.tar.gz", hash = "sha256:a4daca2dc0aa429555e0656d6bf94873a7dc5f54ee42b1f5873d666fb3f35560"}, ] [[package]] name = "pydantic-core" -version = "2.23.4" +version = "2.27.1" requires_python = ">=3.8" summary = "Core functionality for Pydantic validation and serialization" groups = ["default"] @@ -1521,19 +1520,21 @@ dependencies = [ "typing-extensions!=4.7.0,>=4.6.0", ] files = [ - {file = "pydantic_core-2.23.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7530e201d10d7d14abce4fb54cfe5b94a0aefc87da539d0346a484ead376c3cc"}, - {file = "pydantic_core-2.23.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:df933278128ea1cd77772673c73954e53a1c95a4fdf41eef97c2b779271bd0bd"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cb3da3fd1b6a5d0279a01877713dbda118a2a4fc6f0d821a57da2e464793f05"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c6dcb030aefb668a2b7009c85b27f90e51e6a3b4d5c9bc4c57631292015b0d"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:696dd8d674d6ce621ab9d45b205df149399e4bb9aa34102c970b721554828510"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2971bb5ffe72cc0f555c13e19b23c85b654dd2a8f7ab493c262071377bfce9f6"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8394d940e5d400d04cad4f75c0598665cbb81aecefaca82ca85bd28264af7f9b"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0dff76e0602ca7d4cdaacc1ac4c005e0ce0dcfe095d5b5259163a80d3a10d327"}, - {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7d32706badfe136888bdea71c0def994644e09fff0bfe47441deaed8e96fdbc6"}, - {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ed541d70698978a20eb63d8c5d72f2cc6d7079d9d90f6b50bad07826f1320f5f"}, - {file = "pydantic_core-2.23.4-cp313-none-win32.whl", hash = "sha256:3d5639516376dce1940ea36edf408c554475369f5da2abd45d44621cb616f769"}, - {file = "pydantic_core-2.23.4-cp313-none-win_amd64.whl", hash = "sha256:5a1504ad17ba4210df3a045132a7baeeba5a200e930f57512ee02909fc5c4cb5"}, - {file = "pydantic_core-2.23.4.tar.gz", hash = "sha256:2584f7cf844ac4d970fba483a717dbe10c1c1c96a969bf65d61ffe94df1b2863"}, + {file = "pydantic_core-2.27.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f216dbce0e60e4d03e0c4353c7023b202d95cbaeff12e5fd2e82ea0a66905073"}, + {file = "pydantic_core-2.27.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a2e02889071850bbfd36b56fd6bc98945e23670773bc7a76657e90e6b6603c08"}, + {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42b0e23f119b2b456d07ca91b307ae167cc3f6c846a7b169fca5326e32fdc6cf"}, + {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:764be71193f87d460a03f1f7385a82e226639732214b402f9aa61f0d025f0737"}, + {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c00666a3bd2f84920a4e94434f5974d7bbc57e461318d6bb34ce9cdbbc1f6b2"}, + {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ccaa88b24eebc0f849ce0a4d09e8a408ec5a94afff395eb69baf868f5183107"}, + {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c65af9088ac534313e1963443d0ec360bb2b9cba6c2909478d22c2e363d98a51"}, + {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:206b5cf6f0c513baffaeae7bd817717140770c74528f3e4c3e1cec7871ddd61a"}, + {file = "pydantic_core-2.27.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:062f60e512fc7fff8b8a9d680ff0ddaaef0193dba9fa83e679c0c5f5fbd018bc"}, + {file = "pydantic_core-2.27.1-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:a0697803ed7d4af5e4c1adf1670af078f8fcab7a86350e969f454daf598c4960"}, + {file = "pydantic_core-2.27.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:58ca98a950171f3151c603aeea9303ef6c235f692fe555e883591103da709b23"}, + {file = "pydantic_core-2.27.1-cp313-none-win32.whl", hash = "sha256:8065914ff79f7eab1599bd80406681f0ad08f8e47c880f17b416c9f8f7a26d05"}, + {file = "pydantic_core-2.27.1-cp313-none-win_amd64.whl", hash = "sha256:ba630d5e3db74c79300d9a5bdaaf6200172b107f263c98a0539eeecb857b2337"}, + {file = "pydantic_core-2.27.1-cp313-none-win_arm64.whl", hash = "sha256:45cf8588c066860b623cd11c4ba687f8d7175d5f7ef65f7129df8a394c502de5"}, + {file = "pydantic_core-2.27.1.tar.gz", hash = "sha256:62a763352879b84aa31058fc931884055fd75089cccbd9d58bb6afd01141b235"}, ] [[package]] @@ -1597,20 +1598,6 @@ files = [ {file = "pylint_django-2.6.1-py3-none-any.whl", hash = "sha256:359f68fe8c810ee6bc8e1ab4c83c19b15a43b234a24b08978f47a23462b5ce28"}, ] -[[package]] -name = "pylint-per-file-ignores" -version = "1.3.2" -requires_python = ">=3.8.1,<4.0.0" -summary = "A pylint plugin to ignore error codes per file." -groups = ["static-analysis"] -dependencies = [ - "tomli<3.0.0,>=2.0.1; python_version < \"3.11\"", -] -files = [ - {file = "pylint_per_file_ignores-1.3.2-py3-none-any.whl", hash = "sha256:4a2a2d7b88484ef1d1b1170029e542954f70efbab13ac3b977606ea5617d04c1"}, - {file = "pylint_per_file_ignores-1.3.2.tar.gz", hash = "sha256:3c641f69c316770749a8a353556504dae7469541cdaef38e195fe2228841451e"}, -] - [[package]] name = "pylint-plugin-utils" version = "0.8.2" @@ -1804,29 +1791,29 @@ files = [ [[package]] name = "ruff" -version = "0.7.3" +version = "0.8.0" requires_python = ">=3.7" summary = "An extremely fast Python linter and code formatter, written in Rust." groups = ["lint"] files = [ - {file = "ruff-0.7.3-py3-none-linux_armv6l.whl", hash = "sha256:34f2339dc22687ec7e7002792d1f50712bf84a13d5152e75712ac08be565d344"}, - {file = "ruff-0.7.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:fb397332a1879b9764a3455a0bb1087bda876c2db8aca3a3cbb67b3dbce8cda0"}, - {file = "ruff-0.7.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:37d0b619546103274e7f62643d14e1adcbccb242efda4e4bdb9544d7764782e9"}, - {file = "ruff-0.7.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d59f0c3ee4d1a6787614e7135b72e21024875266101142a09a61439cb6e38a5"}, - {file = "ruff-0.7.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:44eb93c2499a169d49fafd07bc62ac89b1bc800b197e50ff4633aed212569299"}, - {file = "ruff-0.7.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6d0242ce53f3a576c35ee32d907475a8d569944c0407f91d207c8af5be5dae4e"}, - {file = "ruff-0.7.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:6b6224af8b5e09772c2ecb8dc9f3f344c1aa48201c7f07e7315367f6dd90ac29"}, - {file = "ruff-0.7.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c50f95a82b94421c964fae4c27c0242890a20fe67d203d127e84fbb8013855f5"}, - {file = "ruff-0.7.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7f3eff9961b5d2644bcf1616c606e93baa2d6b349e8aa8b035f654df252c8c67"}, - {file = "ruff-0.7.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8963cab06d130c4df2fd52c84e9f10d297826d2e8169ae0c798b6221be1d1d2"}, - {file = "ruff-0.7.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:61b46049d6edc0e4317fb14b33bd693245281a3007288b68a3f5b74a22a0746d"}, - {file = "ruff-0.7.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:10ebce7696afe4644e8c1a23b3cf8c0f2193a310c18387c06e583ae9ef284de2"}, - {file = "ruff-0.7.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:3f36d56326b3aef8eeee150b700e519880d1aab92f471eefdef656fd57492aa2"}, - {file = "ruff-0.7.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5d024301109a0007b78d57ab0ba190087b43dce852e552734ebf0b0b85e4fb16"}, - {file = "ruff-0.7.3-py3-none-win32.whl", hash = "sha256:4ba81a5f0c5478aa61674c5a2194de8b02652f17addf8dfc40c8937e6e7d79fc"}, - {file = "ruff-0.7.3-py3-none-win_amd64.whl", hash = "sha256:588a9ff2fecf01025ed065fe28809cd5a53b43505f48b69a1ac7707b1b7e4088"}, - {file = "ruff-0.7.3-py3-none-win_arm64.whl", hash = "sha256:1713e2c5545863cdbfe2cbce21f69ffaf37b813bfd1fb3b90dc9a6f1963f5a8c"}, - {file = "ruff-0.7.3.tar.gz", hash = "sha256:e1d1ba2e40b6e71a61b063354d04be669ab0d39c352461f3d789cac68b54a313"}, + {file = "ruff-0.8.0-py3-none-linux_armv6l.whl", hash = "sha256:fcb1bf2cc6706adae9d79c8d86478677e3bbd4ced796ccad106fd4776d395fea"}, + {file = "ruff-0.8.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:295bb4c02d58ff2ef4378a1870c20af30723013f441c9d1637a008baaf928c8b"}, + {file = "ruff-0.8.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7b1f1c76b47c18fa92ee78b60d2d20d7e866c55ee603e7d19c1e991fad933a9a"}, + {file = "ruff-0.8.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb0d4f250a7711b67ad513fde67e8870109e5ce590a801c3722580fe98c33a99"}, + {file = "ruff-0.8.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0e55cce9aa93c5d0d4e3937e47b169035c7e91c8655b0974e61bb79cf398d49c"}, + {file = "ruff-0.8.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3f4cd64916d8e732ce6b87f3f5296a8942d285bbbc161acee7fe561134af64f9"}, + {file = "ruff-0.8.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:c5c1466be2a2ebdf7c5450dd5d980cc87c8ba6976fb82582fea18823da6fa362"}, + {file = "ruff-0.8.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2dabfd05b96b7b8f2da00d53c514eea842bff83e41e1cceb08ae1966254a51df"}, + {file = "ruff-0.8.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:facebdfe5a5af6b1588a1d26d170635ead6892d0e314477e80256ef4a8470cf3"}, + {file = "ruff-0.8.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87a8e86bae0dbd749c815211ca11e3a7bd559b9710746c559ed63106d382bd9c"}, + {file = "ruff-0.8.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:85e654f0ded7befe2d61eeaf3d3b1e4ef3894469cd664ffa85006c7720f1e4a2"}, + {file = "ruff-0.8.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:83a55679c4cb449fa527b8497cadf54f076603cc36779b2170b24f704171ce70"}, + {file = "ruff-0.8.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:812e2052121634cf13cd6fddf0c1871d0ead1aad40a1a258753c04c18bb71bbd"}, + {file = "ruff-0.8.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:780d5d8523c04202184405e60c98d7595bdb498c3c6abba3b6d4cdf2ca2af426"}, + {file = "ruff-0.8.0-py3-none-win32.whl", hash = "sha256:5fdb6efecc3eb60bba5819679466471fd7d13c53487df7248d6e27146e985468"}, + {file = "ruff-0.8.0-py3-none-win_amd64.whl", hash = "sha256:582891c57b96228d146725975fbb942e1f30a0c4ba19722e692ca3eb25cc9b4f"}, + {file = "ruff-0.8.0-py3-none-win_arm64.whl", hash = "sha256:ba93e6294e9a737cd726b74b09a6972e36bb511f9a102f1d9a7e1ce94dd206a6"}, + {file = "ruff-0.8.0.tar.gz", hash = "sha256:a7ccfe6331bf8c8dad715753e157457faf7351c2b69f62f32c165c2dbcbacd44"}, ] [[package]] @@ -1841,7 +1828,7 @@ files = [ [[package]] name = "s3transfer" -version = "0.10.3" +version = "0.10.4" requires_python = ">=3.8" summary = "An Amazon S3 Transfer Manager" groups = ["default"] @@ -1849,8 +1836,8 @@ dependencies = [ "botocore<2.0a.0,>=1.33.2", ] files = [ - {file = "s3transfer-0.10.3-py3-none-any.whl", hash = "sha256:263ed587a5803c6c708d3ce44dc4dfedaab4c1a32e8329bab818933d79ddcf5d"}, - {file = "s3transfer-0.10.3.tar.gz", hash = "sha256:4f50ed74ab84d474ce614475e0b8d5047ff080810aac5d01ea25231cfc944b0c"}, + {file = "s3transfer-0.10.4-py3-none-any.whl", hash = "sha256:244a76a24355363a68164241438de1b72f8781664920260c48465896b712a41e"}, + {file = "s3transfer-0.10.4.tar.gz", hash = "sha256:29edc09801743c21eb5ecbc617a152df41d3c287f67b615f73e5f750583666a7"}, ] [[package]] @@ -1939,13 +1926,13 @@ files = [ [[package]] name = "sqlparse" -version = "0.5.1" +version = "0.5.2" requires_python = ">=3.8" summary = "A non-validating SQL parser." groups = ["default", "typing", "watchfiles"] files = [ - {file = "sqlparse-0.5.1-py3-none-any.whl", hash = "sha256:773dcbf9a5ab44a090f3441e2180efe2560220203dc2f8c0b0fa141e18b505e4"}, - {file = "sqlparse-0.5.1.tar.gz", hash = "sha256:bb6b4df465655ef332548e24f08e205afc81b9ab86cb1c45657a7ff173a3a00e"}, + {file = "sqlparse-0.5.2-py3-none-any.whl", hash = "sha256:e99bc85c78160918c3e1d9230834ab8d80fc06c59d03f8db2618f65f65dda55e"}, + {file = "sqlparse-0.5.2.tar.gz", hash = "sha256:9e37b35e16d1cc652a2545f0997c1deb23ea28fa1f3eefe609eee3063c3b105f"}, ] [[package]] @@ -2000,7 +1987,7 @@ files = [ [[package]] name = "tqdm" -version = "4.67.0" +version = "4.67.1" requires_python = ">=3.7" summary = "Fast, Extensible Progress Meter" groups = ["lint"] @@ -2008,8 +1995,8 @@ dependencies = [ "colorama; platform_system == \"Windows\"", ] files = [ - {file = "tqdm-4.67.0-py3-none-any.whl", hash = "sha256:0cd8af9d56911acab92182e88d763100d4788bdf421d251616040cc4d44863be"}, - {file = "tqdm-4.67.0.tar.gz", hash = "sha256:fe5a6f95e6fe0b9755e9469b77b9c3cf850048224ecaa8293d7d2d31f97d869a"}, + {file = "tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2"}, + {file = "tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2"}, ] [[package]] diff --git a/pyproject.toml b/pyproject.toml index f0cafc3d131a8b81c419630b31cf5b7949e5c1f5..9e4c869dfb62b52555721b72e47dabcd21e1f01c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -83,7 +83,6 @@ typing = [ static-analysis = [ "pylint>=3.3.1", "pylint-django>=2.6.1", - "pylint-per-file-ignores>=1.3.2", ] [tool.pdm.scripts] diff --git a/requirements.dev.txt b/requirements.dev.txt index 5a8bea86bc285d6212f7f943c2c0d07ac9349bac..7da485862cf49823ec2ef5f4c4e06a8dde9d9218 100644 --- a/requirements.dev.txt +++ b/requirements.dev.txt @@ -10,8 +10,8 @@ babel==2.16.0 beautifulsoup4==4.12.3 bleach==6.2.0 blinker==1.9.0 -boto3==1.35.57 -botocore==1.35.57 +boto3==1.35.68 +botocore==1.35.68 cachetools==5.5.0 certifi==2024.8.30 cffi==1.17.1; platform_python_implementation != "PyPy" @@ -20,10 +20,10 @@ chardet==5.2.0 charset-normalizer==3.4.0 click==8.1.7 colorama==0.4.6 -coverage==7.6.4 +coverage==7.6.8 cryptography==43.0.3 cssbeautifier==1.15.1 -debugpy==1.8.8 +debugpy==1.8.9 defusedxml==0.7.1 dep-logic==0.4.9 dill==0.3.9; python_version >= "3.11" @@ -55,7 +55,7 @@ freezegun==1.5.1 gunicorn==23.0.0 h11==0.14.0 hishel==0.0.33 -httpcore==1.0.6 +httpcore==1.0.7 httpx[socks]==0.27.2 icecream==2.1.3 identify==2.6.2 @@ -65,7 +65,7 @@ isort==5.13.2 jinja2==3.1.4 jmespath==1.0.1 jsbeautifier==1.15.1 -json5==0.9.25 +json5==0.9.28 jwcrypto==1.5.6 lxml==5.3.0 markdown-it-py==3.0.0 @@ -91,16 +91,15 @@ platformdirs==4.3.6 pluggy==1.5.0 pre-commit==4.0.1 psycopg-binary==3.2.3; implementation_name != "pypy" -psycopg-pool==3.2.3 +psycopg-pool==3.2.4 psycopg[binary,pool]==3.2.3 pycparser==2.22; platform_python_implementation != "PyPy" -pydantic==2.9.2 -pydantic-core==2.23.4 +pydantic==2.10.1 +pydantic-core==2.27.1 pygments==2.18.0 pyjwt==2.9.0 pylint==3.3.1 pylint-django==2.6.1 -pylint-per-file-ignores==1.3.2 pylint-plugin-utils==0.8.2 pyproject-api==1.8.0 pyproject-hooks==1.2.0 @@ -114,9 +113,9 @@ requests==2.31.0 requests-file==1.5.1 resolvelib==1.1.0 rich==13.9.4 -ruff==0.7.3 +ruff==0.8.0 rules==3.5 -s3transfer==0.10.3 +s3transfer==0.10.4 segno==1.6.1 sentry-sdk==2.17.0 shellingham==1.5.4 @@ -124,11 +123,11 @@ six==1.16.0 sniffio==1.3.1 socksio==1.0.0 soupsieve==2.6 -sqlparse==0.5.1 +sqlparse==0.5.2 tomlkit==0.13.2 tox==4.23.2 tox-pdm==0.7.2 -tqdm==4.67.0 +tqdm==4.67.1 truststore==0.10.0; python_version >= "3.10" types-pyyaml==6.0.12.20240917 typing-extensions==4.12.2 diff --git a/requirements.txt b/requirements.txt index 4d5e2a4460a0bb6a355cf8f4bf9975b1234ede8a..1467aba9ded099d2bcff8dfce66cd482ac459f7b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,8 +8,8 @@ babel==2.16.0 beautifulsoup4==4.12.3 bleach==6.2.0 blinker==1.9.0 -boto3==1.35.57 -botocore==1.35.57 +boto3==1.35.68 +botocore==1.35.68 certifi==2024.8.30 cffi==1.17.1; platform_python_implementation != "PyPy" charset-normalizer==3.4.0 @@ -38,7 +38,7 @@ freezegun==1.5.1 gunicorn==23.0.0 h11==0.14.0 hishel==0.0.33 -httpcore==1.0.6 +httpcore==1.0.7 httpx[socks]==0.27.2 idna==3.10 installer==0.7.0 @@ -64,11 +64,11 @@ pdm==2.20.1 pillow==11.0.0 platformdirs==4.3.6 psycopg-binary==3.2.3; implementation_name != "pypy" -psycopg-pool==3.2.3 +psycopg-pool==3.2.4 psycopg[binary,pool]==3.2.3 pycparser==2.22; platform_python_implementation != "PyPy" -pydantic==2.9.2 -pydantic-core==2.23.4 +pydantic==2.10.1 +pydantic-core==2.27.1 pygments==2.18.0 pyjwt==2.9.0 pyproject-hooks==1.2.0 @@ -81,7 +81,7 @@ requests-file==1.5.1 resolvelib==1.1.0 rich==13.9.4 rules==3.5 -s3transfer==0.10.3 +s3transfer==0.10.4 segno==1.6.1 sentry-sdk==2.17.0 shellingham==1.5.4 @@ -89,7 +89,7 @@ six==1.16.0 sniffio==1.3.1 socksio==1.0.0 soupsieve==2.6 -sqlparse==0.5.1 +sqlparse==0.5.2 tomlkit==0.13.2 truststore==0.10.0; python_version >= "3.10" typing-extensions==4.12.2 diff --git a/src/api/schedule.py b/src/api/schedule.py index cf26a3c40645dca72214468a74a38440c28ee055..d0f10416a626a71f442165014d1f5297b82c0a3c 100644 --- a/src/api/schedule.py +++ b/src/api/schedule.py @@ -4,7 +4,7 @@ import logging import re from collections import OrderedDict from datetime import datetime, timedelta -from typing import Optional, TYPE_CHECKING +from typing import TYPE_CHECKING from uuid import UUID from lxml import etree as ET @@ -91,7 +91,7 @@ class RoomDay: class ScheduleEncoder(json.JSONEncoder): tz = None - def encode_duration(self, duration: Optional[timedelta]) -> Optional[str]: + def encode_duration(self, duration: timedelta | None) -> str | None: """converts a python `timedelta` to the schedule xml timedelta string that represents this timedelta. ([d:]HH:mm)""" if duration is None: diff --git a/src/api/serializers.py b/src/api/serializers.py index f6a5ef970628ac7ce536c1bfb077dd660f40aad3..aa56d026e3d20cc78fe684addf6512f0281110e9 100644 --- a/src/api/serializers.py +++ b/src/api/serializers.py @@ -33,7 +33,7 @@ class ParameterisedHyperlinkedIdentityField(HyperlinkedIdentityField): self.lookup_fields = kwargs.pop('lookup_fields', self.lookup_fields) super().__init__(*args, **kwargs) - def get_url(self, obj, view_name, request, format): + def get_url(self, obj, view_name, request, format): # pylint: disable=redefined-builtin """ Given an object, return the URL that hyperlinks to the object. diff --git a/src/api/tests/badges/create_redeem_token.py b/src/api/tests/badges/create_redeem_token.py index aa74f060b0375cb5f4022d56cff1e1f1d4ec41dc..28931971e31803126209a4c87296cb27e393da16 100644 --- a/src/api/tests/badges/create_redeem_token.py +++ b/src/api/tests/badges/create_redeem_token.py @@ -1,9 +1,9 @@ import uuid from datetime import datetime from http import HTTPStatus +from zoneinfo import ZoneInfo from rest_framework.authtoken.models import Token -from zoneinfo import ZoneInfo from django.test import TestCase, override_settings from django.urls import reverse diff --git a/src/api/tests/map.py b/src/api/tests/map.py index 7261e596f05cd215e4e4ca9870cdf9a1c91ab706..9f6375eecc07b16dbecbf38cb9888c6da18bcb4f 100644 --- a/src/api/tests/map.py +++ b/src/api/tests/map.py @@ -1,7 +1,6 @@ import json import uuid from datetime import datetime - from zoneinfo import ZoneInfo from django.contrib.gis.geos import Point diff --git a/src/api/tests/permissions.py b/src/api/tests/permissions.py index 92fef7d754435b8bacbd57c7be073c0c93eb7e5b..87c9010ebc969fb1c338200eb04669074048f4e0 100644 --- a/src/api/tests/permissions.py +++ b/src/api/tests/permissions.py @@ -135,7 +135,7 @@ class ConferencePermissionTestCase(PermissionTestCase): self.conference = self.view.conference = Conference.objects.create(slug='conf', name='TestConf', is_public=True) -class ConferenceDetectionTestCase(PermissionTestCase): +class ConferenceDetectionTestCase(ConferencePermissionTestCase): def test_get_conference_view(self): self.assertEqual(ConferencePermission().get_conference(view=self.view), self.conference) diff --git a/src/api/tests/schedule.py b/src/api/tests/schedule.py index 3fa2c6d7e32b77ac512618798f806b370f897b64..5d3e5b045a5aab46e5e54c9515c0aae29f0724a8 100644 --- a/src/api/tests/schedule.py +++ b/src/api/tests/schedule.py @@ -2,9 +2,9 @@ import json import uuid import xml.etree.ElementTree as ET from datetime import datetime, timedelta +from zoneinfo import ZoneInfo from rest_framework.authtoken.models import Token -from zoneinfo import ZoneInfo from django.test import TestCase, override_settings from django.urls import reverse diff --git a/src/api/views/__init__.py b/src/api/views/__init__.py index 5758e553087046b1e6856d8ccf2cf742cca7f0c1..2e840c7ac53e1c48e7dd0ba2b1313f73c11e4a52 100644 --- a/src/api/views/__init__.py +++ b/src/api/views/__init__.py @@ -8,7 +8,7 @@ __all__ = [ @api_view(['GET']) -def api_root(request, format=None): +def api_root(request, format=None): # pylint: disable=redefined-builtin links = { 'conference': { 'info': reverse('api:conference-detail', request=request, format=format), diff --git a/src/api/views/badges.py b/src/api/views/badges.py index 066adb4786bbdfd7cf5d5aae67737a6cbc75aa57..41abd07579164cb59504dc3a6987da0cc723a88f 100644 --- a/src/api/views/badges.py +++ b/src/api/views/badges.py @@ -100,7 +100,7 @@ class RedeemBadgeMapTokenView(ConferenceSlugMixin, APIView): return super().initial(request, *args, **kwargs) - def post(self, request, *get, format=None, **kwargs): + def post(self, request, *get, format=None, **kwargs): # pylint: disable=redefined-builtin if self.target_user is None or not self.target_user.is_in_conference(self.conference): return HttpResponse(status=404) diff --git a/src/api/views/conferencemember.py b/src/api/views/conferencemember.py index 795ea8136a6b837b0af8a299a3df92f47be6f6d1..29d3cc9d274899f6710f2c6ef9ebab8ea557e660 100644 --- a/src/api/views/conferencemember.py +++ b/src/api/views/conferencemember.py @@ -28,14 +28,14 @@ class AngelView(ConferenceSlugMixin, APIView): return super().initial(request, *args, **kwargs) - def get(self, request, *args, format=None, **kwargs): + def get(self, request, *args, format=None, **kwargs): # pylint: disable=redefined-builtin data = { 'active_angel': self.target.active_angel, 'angel_types': self.target.roles, } return Response(data) - def post(self, request, *args, format=None, **kwargs): + def post(self, request, *args, format=None, **kwargs): # pylint: disable=redefined-builtin data = request.data if 'active_angel' in data: @@ -60,7 +60,7 @@ class WorkadventureView(ConferenceSlugMixin, APIView): return super().initial(request, *args, **kwargs) - def get(self, request, *args, format=None, **kwargs): + def get(self, request, *args, format=None, **kwargs): # pylint: disable=redefined-builtin if self.target_user is None: return Response({'active': False}, status=status.HTTP_400_BAD_REQUEST) @@ -74,7 +74,7 @@ class WorkadventureView(ConferenceSlugMixin, APIView): ) return Response(result) - def post(self, request, *get, format=None, **kwargs): + def post(self, request, *get, format=None, **kwargs): # pylint: disable=redefined-builtin if (u := self.target_user) is None: return Response({'error': 'No target user.'}, status=status.HTTP_400_BAD_REQUEST) diff --git a/src/api/views/maps.py b/src/api/views/maps.py index 41c83afe237adf4eea82e585cd31f9a987d96e5a..eba2387d775bb38af9f35b69c43c511dc70a6c03 100644 --- a/src/api/views/maps.py +++ b/src/api/views/maps.py @@ -59,7 +59,7 @@ class PoiExportView(ConferenceSlugMixin, APIView): return ConferenceExportCache.handle_http_request( request=request, conference=self.conference, - type=ConferenceExportCache.Type.MAP, + entry_type=ConferenceExportCache.Type.MAP, ident=cache_id, content_type='application/geo+json', result_func=lambda: json.dumps(self.get_geojson()), @@ -113,7 +113,7 @@ class AssembliesExportView(ConferenceSlugMixin, APIView, metaclass=abc.ABCMeta): return ConferenceExportCache.handle_http_request( request=request, conference=self.conference, - type=ConferenceExportCache.Type.MAP, + entry_type=ConferenceExportCache.Type.MAP, ident=cache_id, content_type='application/geo+json', result_func=lambda: json.dumps(self.get_geojson()), diff --git a/src/api/views/schedule.py b/src/api/views/schedule.py index b728ff43977d803200de257ab8ea958ee3642aa4..31bc747186d3bb0be5aa07d57834a2241e6eb334 100644 --- a/src/api/views/schedule.py +++ b/src/api/views/schedule.py @@ -60,7 +60,7 @@ class BaseScheduleView(ConferenceSlugMixin, View): return ConferenceExportCache.handle_http_request( request=self.request, conference=self.conference, - type=ConferenceExportCache.Type.SCHEDULE, + entry_type=ConferenceExportCache.Type.SCHEDULE, ident=cache_id, content_type=lambda: 'application/json' if req_format == 'json' else 'text/xml', result_func=gen_data, @@ -77,7 +77,7 @@ class ConferenceSchedule(BaseScheduleView): def get_cache_id(self, **kwargs): return '' - def get_events(self, filter=None, **kwargs): + def get_events(self, filter=None, **kwargs): # pylint: disable=redefined-builtin queryset = ( Event.objects.conference_accessible(conference=self.conference) .exclude(schedule_duration=None) @@ -95,7 +95,7 @@ class AssemblySchedule(BaseScheduleView): assembly_id = self.request.resolver_match.kwargs.get('assembly') return f'assembly_{assembly_id}' - def get_events(self, filter=None): + def get_events(self, filter=None): # pylint: disable=redefined-builtin assembly_id = self.request.resolver_match.kwargs.get('assembly') queryset = ( Event.objects.conference_accessible(conference=self.conference) @@ -113,7 +113,7 @@ class RoomSchedule(BaseScheduleView): room_id = self.request.resolver_match.kwargs.get('pk') return f'room-{room_id}' - def get_events(self, filter=None): + def get_events(self, filter=None): # pylint: disable=redefined-builtin room_id = self.request.resolver_match.kwargs.get('pk') queryset = Event.objects.conference_accessible(conference=self.conference).filter(room_id=room_id).order_by('schedule_start') if filter: @@ -126,11 +126,11 @@ class EventSchedule(ConferenceSlugMixin, APIView): authentication_classes = [authentication.TokenAuthentication] permission_classes = [IsApiUserOrReadOnly] - def get(self, request, pk, format=None, **kwargs): + def get(self, request, pk, format=None, **kwargs): # pylint: disable=redefined-builtin event = Event.objects.associated_with_user(conference=self.conference, user=self.request.user, show_public=True).get(pk=pk) return Response(ScheduleEncoder().encode_event(event, self.conference.timezone)) - def post(self, request, pk, format=None, **kwargs): + def post(self, request, pk, format=None, **kwargs): # pylint: disable=redefined-builtin event = request.data if len(event) == 0: return Response({'error': 'No data.'}, status=400) diff --git a/src/api/views/users.py b/src/api/views/users.py index 371269fc96912613a7b18e92e41b891c5a40ac89..b210f908e88bb8961264432640153ea5c08b087d 100644 --- a/src/api/views/users.py +++ b/src/api/views/users.py @@ -18,7 +18,7 @@ from api.serializers import UserTimelineEntrySerializer @api_view(['GET']) -def profile(request, format=None): +def profile(request, format=None): # pylint: disable=redefined-builtin u = request.user if not u.is_authenticated: @@ -34,7 +34,7 @@ def profile(request, format=None): @api_view(['GET']) -def friends(request, format=None): +def friends(request, format=None): # pylint: disable=redefined-builtin u = request.user if not u.is_authenticated: return Response([]) @@ -55,7 +55,7 @@ def friends(request, format=None): @api_view(['GET']) -def badges(request, format=None): +def badges(request, format=None): # pylint: disable=redefined-builtin u = request.user if not u.is_authenticated: return Response([]) diff --git a/src/api/views/workadventure.py b/src/api/views/workadventure.py index 4cc556758e114dd6822c9550e632c5115431ddd5..3b47e5cb6e43a7e00ab41dd449e546e6ef1a18c0 100644 --- a/src/api/views/workadventure.py +++ b/src/api/views/workadventure.py @@ -72,7 +72,7 @@ class MapServiceView(ConferenceSlugMixin, APIView): permission_classes = [IsConferenceService | IsSuperUser] required_service_classes = ['wa_mapservice'] - def get(self, request, format=None): + def get(self, request, format=None): # pylint: disable=redefined-builtin wa_rooms = [] for room in Room.objects.filter(conference=self.conference, room_type=Room.RoomType.WORKADVENTURE).select_related('assembly'): if room.assembly is not None and room.assembly.state_assembly not in Assembly.PUBLIC_STATES: @@ -82,7 +82,7 @@ class MapServiceView(ConferenceSlugMixin, APIView): return Response(wa_rooms) - def put(self, request, format=None): + def put(self, request, format=None): # pylint: disable=redefined-builtin data = request.data status_code, response = self.handle_mapservice_push_request(self.conference, data) @@ -272,7 +272,7 @@ class MapDetailView(ConferenceSlugMixin, APIView): permission_classes = [IsConferenceService | IsSuperUser] required_service_classes = ['wa_mapservice', 'wa_backend'] - def get(self, request, conference, assembly, world, room=None, format=None): + def get(self, request, conference, assembly, world, room=None, format=None): # pylint: disable=redefined-builtin a = self.conference.assemblies.get(slug=assembly) wa_room = a.rooms.get(room_type=Room.RoomType.WORKADVENTURE) # TODO:, slug=world) @@ -305,7 +305,7 @@ class UserInfoView(ConferenceSlugMixin, APIView): permission_classes = [IsConferenceService | IsSuperUser] required_service_classes = ['wa_backend'] - def get(self, request, conference, uid, format=None): + def get(self, request, conference, uid, format=None): # pylint: disable=redefined-builtin try: wa_session = WorkadventureSession.objects.get(conference=self.conference, pk=uid) except WorkadventureSession.DoesNotExist: @@ -313,7 +313,7 @@ class UserInfoView(ConferenceSlugMixin, APIView): return Response(wa_session.export_userdata()) - def post(self, request, conference, uid, format=None): + def post(self, request, conference, uid, format=None): # pylint: disable=redefined-builtin try: wa_session = WorkadventureSession.objects.get(conference=self.conference, pk=uid) except WorkadventureSession.DoesNotExist: @@ -335,7 +335,7 @@ class RegisterView(ConferenceSlugMixin, APIView): permission_classes = [IsConferenceService | IsSuperUser] required_service_classes = ['wa_backend'] - def get(self, request, conference, token, format=None): + def get(self, request, conference, token, format=None): # pylint: disable=redefined-builtin try: wa_session = WorkadventureSession.objects.get(conference=self.conference, token=token) if wa_session.token_expiry < timezone.now(): diff --git a/src/backoffice/forms/events.py b/src/backoffice/forms/events.py index 1248e7e07815d518efb7351abd41272df097c05b..819ca7fd2f641e63dced69cd11987e383572380a 100644 --- a/src/backoffice/forms/events.py +++ b/src/backoffice/forms/events.py @@ -1,5 +1,5 @@ import logging -from typing import Any, Optional +from typing import Any from django import forms from django.utils.translation import gettext_lazy as _ @@ -45,8 +45,8 @@ class EventForm(TranslatedFieldsForm): self, *args, conference, - assembly: Optional[Assembly] = None, - owner: Optional[PlatformUser] = None, + assembly: Assembly | None = None, + owner: PlatformUser | None = None, create: bool = False, publish: bool = False, **kwargs, diff --git a/src/backoffice/forms/rooms.py b/src/backoffice/forms/rooms.py index 5b94ea23c73702b5a53197f839667748e096acda..8e22909df8476d7079960b652bf4ae5c16b430b1 100644 --- a/src/backoffice/forms/rooms.py +++ b/src/backoffice/forms/rooms.py @@ -167,7 +167,7 @@ class AssemblyRoomEditForm(TranslatedFieldsForm): model = Room fields = ['name', 'slug', 'description', 'is_public_fahrplan', 'is_official', 'official_room_order', 'capacity'] - def __init__(self, with_capacity=False, channel_staff=False, *args, **kwargs): + def __init__(self, *args, with_capacity=False, channel_staff=False, **kwargs): super().__init__(*args, **kwargs) self.fields['slug'].disabled = True if self.instance.room_type in Room.BACKEND_ROOMTYPES: @@ -238,7 +238,7 @@ class AssemblyRoomEditWorkAdventureForm(TranslatedFieldsForm): model = Room fields = ['backend_status', 'backend_link', 'backend_link_branch'] - def __init__(self, with_capacity=False, channel_staff=False, *args, **kwargs): + def __init__(self, *args, with_capacity=False, channel_staff=False, **kwargs): super().__init__(*args, **kwargs) self.fields['backend_status'].disabled = True self.fields['backend_link'].label = _('Room-workadventure_backend_link') @@ -254,7 +254,7 @@ class AssemblyRoomEditHangarForm(TranslatedFieldsForm): model = Room fields = ['backend_status', 'backend_link'] - def __init__(self, with_capacity=False, channel_staff=False, *args, **kwargs): + def __init__(self, *args, with_capacity=False, channel_staff=False, **kwargs): super().__init__(*args, **kwargs) self.fields['backend_status'].disabled = True self.fields['backend_link'].label = _('Room-hangar_backend_link') diff --git a/src/backoffice/tests/auth.py b/src/backoffice/tests/auth.py index 7881dd10700257c4713ca7d36e3c2347fe711618..f70e9381fa1110bed026b6ebbb7c531d29db1e4f 100644 --- a/src/backoffice/tests/auth.py +++ b/src/backoffice/tests/auth.py @@ -1,10 +1,14 @@ import re +from datetime import timedelta +from django.contrib.auth.tokens import default_token_generator from django.contrib.auth.views import INTERNAL_RESET_SESSION_TOKEN from django.core import mail from django.http import SimpleCookie from django.test import override_settings from django.urls import reverse +from django.utils.encoding import force_bytes +from django.utils.http import urlsafe_base64_encode from django.utils.translation import override as override_locale from core.models import ( @@ -65,12 +69,6 @@ class PasswordResetTest(BackOfficeTestCase): @override_settings(LANGUAGE_CODE='en', AUTH_PASSWORD_VALIDATORS=[]) def test_invalid_password_reset_link(self): - from datetime import timedelta - - from django.contrib.auth.tokens import default_token_generator - from django.utils.encoding import force_bytes - from django.utils.http import urlsafe_base64_encode - uidb = urlsafe_base64_encode(force_bytes(self.user.pk)) expired_token = default_token_generator._make_token_with_timestamp( self.user, default_token_generator._num_seconds(default_token_generator._now() - timedelta(days=365)), secret=None @@ -85,11 +83,6 @@ class PasswordResetTest(BackOfficeTestCase): @override_settings(LANGUAGE_CODE='en', AUTH_PASSWORD_VALIDATORS=[]) def test_PasswordResetConfirmView(self): - from django.contrib.auth.tokens import default_token_generator - from django.contrib.auth.views import INTERNAL_RESET_SESSION_TOKEN - from django.utils.encoding import force_bytes - from django.utils.http import urlsafe_base64_encode - self.client.force_login(self.user) self.client.cookies = SimpleCookie() self.user.set_password('forgotten') diff --git a/src/backoffice/tests/invitations/habitat.py b/src/backoffice/tests/invitations/habitat.py index 021900da3f7cd9ba03acb06042464904a072d963..f911540364a14ca2cfae3545e8b43afca671fcc6 100644 --- a/src/backoffice/tests/invitations/habitat.py +++ b/src/backoffice/tests/invitations/habitat.py @@ -244,9 +244,10 @@ class InvitationHabitatSendViewTestCase(InvitationHabitatTestCase): self.assertEqual(Invitation.objects.count(), 1) # After the rejection has timed out, can send invitation again - with patch.object(timezone, 'now', return_value=(datetime(2042, 12, 27, 12, 34, 0, 0, tzinfo=UTC)) + timedelta(hours=4)), patch( - 'core.models.invitation.datetime' - ) as mock_datetime: + with ( + patch.object(timezone, 'now', return_value=(datetime(2042, 12, 27, 12, 34, 0, 0, tzinfo=UTC)) + timedelta(hours=4)), + patch('core.models.invitation.datetime') as mock_datetime, + ): mock_datetime.now.return_value = datetime(2042, 12, 27, 12, 34, 0, 0, tzinfo=UTC) + timedelta(hours=4) response = self.client.post( reverse( diff --git a/src/backoffice/views/assemblies/__init__.py b/src/backoffice/views/assemblies/__init__.py index b5411010f26dfb9b0683f5c286da67dbbd391de7..3cc45e5da6afb2dbc608425b9abb8200a6172e1c 100644 --- a/src/backoffice/views/assemblies/__init__.py +++ b/src/backoffice/views/assemblies/__init__.py @@ -32,20 +32,20 @@ __all__ = [ 'AssemblyCreateView', 'AssemblyDetailView', 'AssemblyLinksUpdateView', - 'AssemblyUpdateView', 'AssemblyListView', 'AssemblyParentLeaveView', + 'AssemblyRoomCreateView', + 'AssemblyRoomDeleteView', + 'AssemblyRoomUpdateView', + 'AssemblyUpdateView', 'AuthAppView', 'AuthGetTokenView', 'AuthView', 'MemberCreateView', 'MemberListView', 'MembersUpdateView', - 'VouchersView', - 'AssemblyRoomUpdateView', - 'AssemblyRoomCreateView', 'RoomLinkCreateView', - 'RoomNotAvailableError', 'RoomLinkDeleteView', - 'AssemblyRoomDeleteView', + 'RoomNotAvailableError', + 'VouchersView', ] diff --git a/src/backoffice/views/assemblies/assemblies.py b/src/backoffice/views/assemblies/assemblies.py index 9bffb30f54cbe3cea190b8086b8c34f945e759e1..06bbe853723f05ace7d2f6c7e6bebb492cd8b090 100644 --- a/src/backoffice/views/assemblies/assemblies.py +++ b/src/backoffice/views/assemblies/assemblies.py @@ -402,7 +402,7 @@ class AssemblyLinksUpdateView(AssemblyMixin, View): if link_created: messages.success(request, gettext('assemblyedit_addedlink').format(linked_name=linkee.name)) logger.info( - 'Assembly "%(assembly_name)s" (%(assembly_pk)s): added link to "%(linkee)s" (%(linkee_pk)s), requested by {%(user)s.' + 'Assembly "%(assembly_name)s" (%(assembly_pk)s): added link to "%(linkee)s" (%(linkee_pk)s), requested by {%(user)s}.' 'Not from this assembly', {'assembly_name': assembly.name, 'assembly_pk': assembly.pk, 'linkee': linkee, 'linkee_pk': linkee.pk, 'user': request.user.username}, ) diff --git a/src/backoffice/views/events/__init__.py b/src/backoffice/views/events/__init__.py index 485c41059cb2ac663bd4defae33c9c4e0ae30bf9..e7159fba6d7ede9bbf294b128c9c1a49fc9fd7aa 100644 --- a/src/backoffice/views/events/__init__.py +++ b/src/backoffice/views/events/__init__.py @@ -12,12 +12,12 @@ from backoffice.views.events.self_organized_sessions import ( ) __all__ = [ - 'AssemblyEventListView', 'AssemblyEventCreateView', - 'AssemblyEventUpdateView', 'AssemblyEventDeleteView', + 'AssemblyEventListView', + 'AssemblyEventUpdateView', 'SelfOrganizedContentListView', 'SoSCreateView', - 'SoSUpdateView', 'SoSDeleteView', + 'SoSUpdateView', ] diff --git a/src/backoffice/views/invitations/__init__.py b/src/backoffice/views/invitations/__init__.py index 2cb8e3459de1a4f3ed9c0e6a2bbdb67af9ee1fb7..696ed9a8bc5185dbced1abe5769830dc7ae22f99 100644 --- a/src/backoffice/views/invitations/__init__.py +++ b/src/backoffice/views/invitations/__init__.py @@ -44,7 +44,7 @@ class InvitationSendView(CreateView): __all__ = [ - 'InvitationUpdateView', 'InvitationListView', 'InvitationSendView', + 'InvitationUpdateView', ] diff --git a/src/backoffice/views/mixins.py b/src/backoffice/views/mixins.py index cb17f642f7b802b86ea08196addaf85d24524844..e6b187cf4b1b1b588744da4750112c060d419e70 100644 --- a/src/backoffice/views/mixins.py +++ b/src/backoffice/views/mixins.py @@ -73,10 +73,10 @@ class ConferenceRequiredMixinBase: return redirect('backoffice:conferences') return super().dispatch(request, *args, **kwargs) - def get_context_data(self, *args, **kwargs): + def get_context_data(self, **kwargs): # super() does not have to have get_context_data(), e.g. if it's a plain View if hasattr(super(), 'get_context_data'): - context = super().get_context_data(*args, **kwargs) + context = super().get_context_data(**kwargs) else: context = {} @@ -243,8 +243,8 @@ class AssemblyMixinBase: def staff_mode(self): return self._staff_mode - def get_context_data(self, *args, **kwargs): - context = super().get_context_data(*args, **kwargs) + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) context['assembly'] = assembly = self.assembly context['can_manage'] = can_manage = self.can_manage diff --git a/src/backoffice/views/moderation/__init__.py b/src/backoffice/views/moderation/__init__.py index efc4978be7f1e8d788e01837206a491c62cf9255..ec51583a5e27c7c0979abec38f0f13f2a98108ee 100644 --- a/src/backoffice/views/moderation/__init__.py +++ b/src/backoffice/views/moderation/__init__.py @@ -22,7 +22,7 @@ RE_UUID = re.compile(r'[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{ class IndexView(ModerationAdminMixin, TemplateView): template_name = 'backoffice/moderation_index.html' - def get(self, request): + def get(self, request, *args, **kwargs): ctx = self.get_context_data() ctx['results'] = results = {} @@ -34,21 +34,21 @@ class IndexView(ModerationAdminMixin, TemplateView): __all__ = [ - 'ModerationAdminMixin', 'IndexView', - 'ModerationAssemblyListView', + 'ModerationAdminMixin', 'ModerationAssemblyDetailView', - 'ModerationEventListView', - 'ModerationEventDetailView', - 'ModerationBadgeListView', + 'ModerationAssemblyListView', 'ModerationBadgeDetailView', - 'ModerationBoardListView', + 'ModerationBadgeListView', 'ModerationBoardDetailView', - 'ModerationProjectListView', + 'ModerationBoardListView', + 'ModerationEventDetailView', + 'ModerationEventListView', 'ModerationProjectDetailView', - 'ModerationUserListView', + 'ModerationProjectListView', 'ModerationUserDetailView', + 'ModerationUserListView', 'ModerationUserRenameView', - 'ModerationWikiListView', 'ModerationWikiDetailView', + 'ModerationWikiListView', ] diff --git a/src/backoffice/views/moderation/board_entry.py b/src/backoffice/views/moderation/board_entry.py index 77b4030383712907c7bb869190993b3fa793417e..9113d34560812b362d4ed249acbbbf414fe78a8a 100644 --- a/src/backoffice/views/moderation/board_entry.py +++ b/src/backoffice/views/moderation/board_entry.py @@ -25,26 +25,26 @@ class ModerationBoardDetailView(ModerationAdminMixin, DetailView): return qs def post(self, request, *args, **kwargs): - object = self.get_object() + current_object = self.get_object() action = request.POST.get('action') # TODO: add log entries if action == 'hide': - object.hidden = True - object.save(update_fields=['hidden']) - messages.success(request, f'board entry forced hidden: #{object.id}') + current_object.hidden = True + current_object.save(update_fields=['hidden']) + messages.success(request, f'board entry forced hidden: #{current_object.id}') elif action == 'show': - object.hidden = False - object.save(update_fields=['hidden']) - messages.success(request, f'board entry is not forced hidden any more: #{object.id}') + current_object.hidden = False + current_object.save(update_fields=['hidden']) + messages.success(request, f'board entry is not forced hidden any more: #{current_object.id}') elif action == 'delete': - object.delete() - messages.success(request, f'board entry deleted: #{object.id}') + current_object.delete() + messages.success(request, f'board entry deleted: #{current_object.id}') return redirect('backoffice:moderation-board-list') else: messages.error(request, f'UNKNOWN ACTION: {action}') - return redirect('backoffice:moderation-board-detail', pk=object.pk) + return redirect('backoffice:moderation-board-detail', pk=current_object.pk) diff --git a/src/backoffice/views/moderation/projects.py b/src/backoffice/views/moderation/projects.py index ff6d0b69878dd3ceb7b413f31965ecc680f108e6..a358416ebe2c8fe30a339e68d27c30c28c66a0a7 100644 --- a/src/backoffice/views/moderation/projects.py +++ b/src/backoffice/views/moderation/projects.py @@ -26,31 +26,31 @@ class ModerationProjectDetailView(ModerationAdminMixin, DetailView): return qs def post(self, request, *args, **kwargs): - object = self.get_object() + current_object = self.get_object() action = request.POST.get('action') # TODO: add log entries if action == 'hide': - object.is_public = False - object.save(update_fields=['is_public']) - messages.success(request, f'project hidden: #{object.id}') + current_object.is_public = False + current_object.save(update_fields=['is_public']) + messages.success(request, f'project hidden: #{current_object.id}') if action == 'block': - object.blocked = True - object.save(update_fields=['blocked']) - messages.success(request, f'project blocked: #{object.id}') + current_object.blocked = True + current_object.save(update_fields=['blocked']) + messages.success(request, f'project blocked: #{current_object.id}') elif action == 'unblock': - object.blocked = False - object.save(update_fields=['blocked']) - messages.success(request, f'project unblocked: #{object.id}') + current_object.blocked = False + current_object.save(update_fields=['blocked']) + messages.success(request, f'project unblocked: #{current_object.id}') elif action == 'delete': - object.delete() - messages.success(request, f'project deleted: #{object.id}') + current_object.delete() + messages.success(request, f'project deleted: #{current_object.id}') return redirect('backoffice:moderation-project-list') else: messages.error(request, f'UNKNOWN ACTION: {action}') - return redirect('backoffice:moderation-project-detail', pk=object.pk) + return redirect('backoffice:moderation-project-detail', pk=current_object.pk) diff --git a/src/backoffice/views/projects/__init__.py b/src/backoffice/views/projects/__init__.py index de750f8627149942d192df799a8ab7d8530405dc..e658c6ebac5b1a329abef12ee7f91b9c4a2445f3 100644 --- a/src/backoffice/views/projects/__init__.py +++ b/src/backoffice/views/projects/__init__.py @@ -68,7 +68,7 @@ __all__ = [ 'AssemblyProjectListView', 'AssemblyProjectUpdateView', 'ProjectCreateView', + 'ProjectDeleteView', 'SelfOrganizedProjectListView', 'SelfOrganizedProjectUpdateView', - 'ProjectDeleteView', ] diff --git a/src/core/admin.py b/src/core/admin.py index d25f7458f212b8b2892cd0cae7bcf44f1f51c931..1494e08a694ed197a187173fc6569e4a7692f65a 100644 --- a/src/core/admin.py +++ b/src/core/admin.py @@ -226,10 +226,10 @@ class ConferenceNavigationItemInline(admin.TabularInline): def has_add_permission(self, request, obj): return False - def has_change_permission(self, request, obj): + def has_change_permission(self, request, obj=None): return False - def has_delete_permission(self, request, obj): + def has_delete_permission(self, request, obj=None): return False diff --git a/src/core/forms/__init__.py b/src/core/forms/__init__.py index de1590280aeb4452df61677b1e084c37a06369d8..f7d33eb1b1124c803a3855000949e2b10ce1f342 100644 --- a/src/core/forms/__init__.py +++ b/src/core/forms/__init__.py @@ -5,13 +5,13 @@ from core.forms.links import LinkForm, LinkFormSet from core.forms.projects import ProjectForm __all__ = [ - 'LoginForm', - 'PasswordResetForm', - 'RegistrationForm', + 'ConferencePublicationForm', + 'ConferenceRegistrationForm', 'InvitationHabitatForm', 'LinkForm', 'LinkFormSet', - 'ConferencePublicationForm', - 'ConferenceRegistrationForm', + 'LoginForm', + 'PasswordResetForm', 'ProjectForm', + 'RegistrationForm', ] diff --git a/src/core/integrations/bigbluebutton.py b/src/core/integrations/bigbluebutton.py index 8c1326a098da9b9fd267b79cc602670afac4907d..374ab65f9838e137a0ee6e06d6f3bf6235bfefab 100644 --- a/src/core/integrations/bigbluebutton.py +++ b/src/core/integrations/bigbluebutton.py @@ -2,7 +2,6 @@ import logging import string from hashlib import sha1 from random import SystemRandom -from typing import Dict, Union from urllib.parse import quote, urlencode, urljoin from uuid import uuid4 from xml.etree import ElementTree as ET @@ -20,7 +19,7 @@ logger = logging.getLogger(__name__) PASSWORD_CHARS = string.ascii_letters + string.digits -def _params_to_str(data: Dict[str, Union[str, int, bool]]): +def _params_to_str(data: dict[str, str | int | bool]): res = {} for k, v in data.items(): if isinstance(v, bool): @@ -48,7 +47,8 @@ class BigBlueButtonIntegration: self._initial_presentation_url = initial_presentation_url self._session = requests.session() - def _send_request(self, resource: str, params: Dict[str, str] = {}, raw=False, post_body=None): + def _send_request(self, resource: str, params: dict[str, str] | None = None, raw=False, post_body=None): + params = params or {} encoded_params = urlencode(_params_to_str(params)) hash_input = resource + encoded_params + self._api_token hash_input = hash_input.encode('utf-8') @@ -89,7 +89,7 @@ class BigBlueButtonIntegration: return True def create_room(self, room: BackendMixin): - assert room is not None and (isinstance(room, Room) and room.room_type == Room.RoomType.BIGBLUEBUTTON or isinstance(room, Event)) + assert room is not None and ((isinstance(room, Room) and room.room_type == Room.RoomType.BIGBLUEBUTTON) or isinstance(room, Event)) if room.backend_status in {Room.BackendStatus.ACTIVE, Room.BackendStatus.FULL}: # room was already created, don't need to create it twice @@ -175,7 +175,7 @@ class BigBlueButtonIntegration: return result def remove_room(self, room: BackendMixin): - assert room is not None and (isinstance(room, Room) and room.room_type == Room.RoomType.BIGBLUEBUTTON) or isinstance(room, Event) + assert (room is not None and (isinstance(room, Room) and room.room_type == Room.RoomType.BIGBLUEBUTTON)) or isinstance(room, Event) if room.backend_status not in {Room.BackendStatus.ACTIVE, Room.BackendStatus.FULL, Room.BackendStatus.INACTIVE}: return @@ -202,7 +202,7 @@ class BigBlueButtonIntegration: return result def _room_status(self, room: BackendMixin, commit=True): - assert room is not None and (isinstance(room, Room) and room.room_type == Room.RoomType.BIGBLUEBUTTON) or isinstance(room, Event) + assert (room is not None and (isinstance(room, Room) and room.room_type == Room.RoomType.BIGBLUEBUTTON)) or isinstance(room, Event) if room.backend_status not in {Room.BackendStatus.ACTIVE, Room.BackendStatus.FULL, Room.BackendStatus.INACTIVE}: return False @@ -229,7 +229,7 @@ class BigBlueButtonIntegration: return self._room_status(room, True) def _join_room(self, room: BackendMixin, user: PlatformUser, anonymous: bool = False, retrying: bool = False): - assert room is not None and (isinstance(room, Room) and room.room_type == Room.RoomType.BIGBLUEBUTTON) or isinstance(room, Event) + assert (room is not None and (isinstance(room, Room) and room.room_type == Room.RoomType.BIGBLUEBUTTON)) or isinstance(room, Event) assert user is not None create_room = False diff --git a/src/core/integrations/workadventure.py b/src/core/integrations/workadventure.py index 094bed765356d0f6fae4d68f81e1cc5d9ea671c9..461339ba1cb49c33ffc3e49b3b3913ec1920be6b 100644 --- a/src/core/integrations/workadventure.py +++ b/src/core/integrations/workadventure.py @@ -264,6 +264,7 @@ class WorkAdventureIntegration: linter_errors = None linter_missingassets = None linter_missingentrypoints = None + linter_exitgraph = None publish_timestamp = None publish_commit = None has_mapinfo = False diff --git a/src/core/management/commands/create_conference.py b/src/core/management/commands/create_conference.py index 7023a6969c9f5a953e665434e46b43e52b389616..c2c28b3116c75792c81332832a7555db3aa7acce 100644 --- a/src/core/management/commands/create_conference.py +++ b/src/core/management/commands/create_conference.py @@ -1,8 +1,8 @@ from argparse import ArgumentTypeError, BooleanOptionalAction from datetime import datetime +from zoneinfo import ZoneInfo from rich.console import Console -from zoneinfo import ZoneInfo from django.contrib.auth.models import Group from django.core.management import call_command @@ -336,7 +336,7 @@ def seed_conference(conf: Conference, console: Console, year: int = 0): ) # create badge categories - badge_category_general, created = BadgeCategory.objects.get_or_create( + badge_category_general, _created = BadgeCategory.objects.get_or_create( slug='general', defaults={ 'name_de': 'Allgemein', @@ -346,7 +346,7 @@ def seed_conference(conf: Conference, console: Console, year: int = 0): 'description_en': 'Category for general badges', }, ) - badge_category_explore, created = BadgeCategory.objects.get_or_create( + badge_category_explore, _created = BadgeCategory.objects.get_or_create( slug='explore', defaults={ 'name_de': 'Erkunden', @@ -356,7 +356,7 @@ def seed_conference(conf: Conference, console: Console, year: int = 0): 'description_en': 'Category for badges related to discovering new things', }, ) - badge_category_help, created = BadgeCategory.objects.get_or_create( + badge_category_help, _created = BadgeCategory.objects.get_or_create( slug='help', defaults={ 'name_de': 'Hilfe', diff --git a/src/core/management/commands/sanitize_database.py b/src/core/management/commands/sanitize_database.py index 0b56489e4ad3ec4ccf8b78426d1ab96aab279d57..c9b268407ff695a13739b9ab62fd0229d5e35c5f 100644 --- a/src/core/management/commands/sanitize_database.py +++ b/src/core/management/commands/sanitize_database.py @@ -4,7 +4,8 @@ from django.conf import settings from django.core.management.base import BaseCommand from django.db.models import Max -from core.models.assemblies import Assembly, AssemblyLogEntry, AssemblyMember +from core.models.activitylog import ActivityLogEntry +from core.models.assemblies import Assembly, AssemblyMember from core.models.badges import UserBadge from core.models.conference import ( ConferenceMember, @@ -60,8 +61,8 @@ class Command(BaseCommand): print('Assembly(technical_user:=None): ', end='', flush=True) print(Assembly.objects.exclude(technical_user=None).update(technical_user=None)) - print('AssemblyLogEntry: ', end='', flush=True) - print_delete_stat(AssemblyLogEntry.objects.all().delete()) + print('ActivityLogEntry: ', end='', flush=True) + print_delete_stat(ActivityLogEntry.objects.all().delete()) print('ConferenceMembers: ', end='', flush=True) print_delete_stat(ConferenceMember.objects.all().delete()) diff --git a/src/core/markdown.py b/src/core/markdown.py index f5af1efc229edcb2a804b0477b2e567547437240..06e3365b4e0f1f3949aed2a5aad7faad5eaba16d 100644 --- a/src/core/markdown.py +++ b/src/core/markdown.py @@ -1,6 +1,5 @@ import html import re -from typing import Optional, Tuple from urllib.parse import quote, urlparse import bleach @@ -110,7 +109,7 @@ class MyHtmlRenderer(HTMLRenderer): result += '</div>\n' return result - def __init__(self, conf: 'conference.Conference', result: 'RenderResult', derefer_allowlist: bool = True, *extras, **kwargs): + def __init__(self, conf: 'conference.Conference', result: 'RenderResult', *extras, derefer_allowlist: bool = True, **kwargs): self.conf = conf self.result = result self.derefer_allowlist = derefer_allowlist @@ -131,7 +130,7 @@ class MyHtmlRenderer(HTMLRenderer): return redirect_via_dereferer(url) if do_derefer else url - def handle_link(self, url: str) -> Tuple[str, str]: + def handle_link(self, url: str) -> tuple[str, str]: from .utils import resolve_internal_url # attempt resolving an internal URL @@ -396,7 +395,7 @@ def refresh_linking_markdown(conf: 'conference.Conference', link_target): def compile_translated_markdown_fields( - obj: Model, conf: 'conference.Conference', field_name: str, dst_obj: Optional[Model] = None, dst_field_name: Optional[str] = None + obj: Model, conf: 'conference.Conference', field_name: str, dst_obj: Model | None = None, dst_field_name: str | None = None ) -> RenderResult: if dst_obj is None: dst_obj = obj diff --git a/src/core/models/__init__.py b/src/core/models/__init__.py index 15e500bcf527404b98c07484d50b58eca623da5d..7b37267d429446682446dada2212d09872d8edc1 100644 --- a/src/core/models/__init__.py +++ b/src/core/models/__init__.py @@ -27,8 +27,8 @@ from .voucher import Voucher, VoucherEntry from .workadventure import WorkadventureSession, WorkadventureTexture __all__ = [ - 'ActivityLogEntry', 'ActivityLogChange', + 'ActivityLogEntry', 'Application', 'Assembly', 'AssemblyLink', @@ -57,6 +57,7 @@ __all__ = [ 'Lock', 'MapFloor', 'MapPOI', + 'MarkdownMeta', 'MetaNavItem', 'PlatformUser', 'Project', @@ -67,13 +68,12 @@ __all__ = [ 'ScheduleSourceImport', 'ScheduleSourceMapping', 'StaticPage', - 'StaticPageRevision', 'StaticPageNamespace', - 'MarkdownMeta', + 'StaticPageRevision', 'TagItem', - 'UserContact', - 'UserCommunicationChannel', 'UserBadge', + 'UserCommunicationChannel', + 'UserContact', 'UserDereferrerAllowlist', 'Voucher', 'VoucherEntry', diff --git a/src/core/models/assemblies.py b/src/core/models/assemblies.py index 2db742f2799441021ccd0f91e707636ecd9e95ff..c0d6f7b60a83b4d5ba72da3c64f6613276f09405 100644 --- a/src/core/models/assemblies.py +++ b/src/core/models/assemblies.py @@ -2,7 +2,7 @@ import json import logging import re from pathlib import Path -from typing import Dict, Optional, TypeIs +from typing import TypeIs from uuid import uuid4 import rules @@ -295,7 +295,7 @@ class Assembly(TaggedItemMixin, ActivityLogMixin, RulesModel): return hub_absolute('plainui:assembly', assembly_slug=self.slug) - def ensure_single_type_conference_match(self) -> Dict: + def ensure_single_type_conference_match(self) -> dict[str, tuple[bool, bool]]: """ Checks if the conference only supports a single type of assembly and sets that field if it is not set. @@ -474,7 +474,7 @@ class Assembly(TaggedItemMixin, ActivityLogMixin, RulesModel): return super().save(*args, update_fields=update_fields, **kwargs) - def prepare_mail_to_managers(self, subject, message) -> tuple[str, Optional[str], str]: + def prepare_mail_to_managers(self, subject, message) -> tuple[str, str | None, str]: from core.templatetags.hub_absolute import hub_absolute # pylint: disable=import-outside-toplevel ctx = { @@ -514,7 +514,7 @@ class Assembly(TaggedItemMixin, ActivityLogMixin, RulesModel): result.append(recipient.member.username) return result - def get_voucher_count(self, with_always_public: bool = True) -> Optional[int]: + def get_voucher_count(self, with_always_public: bool = True) -> int | None: from .voucher import Voucher entries = Voucher.objects.for_assembly(self, include_public_ones=False).count() @@ -570,7 +570,7 @@ class AssemblyLinkManager(ConferenceManagerMixin['AssemblyLink']): staff_permissions = ['core.assembly_team'] conference_filter = 'a__conference' - def apply_public_filter(self, queryset: 'QuerySet[AssemblyLink]', member: ConferenceMember) -> 'QuerySet[AssemblyLink]': + def apply_public_filter(self, queryset: 'QuerySet[AssemblyLink]', member: ConferenceMember | None = None) -> 'QuerySet[AssemblyLink]': return queryset.filter(Type__in=self.model.PUBLIC_TYPES) diff --git a/src/core/models/badges.py b/src/core/models/badges.py index fcee09d49089064bf53110fd6062524835558607..2bfb38e2e545b493b1ddbffcc3eb058f6ff8faaf 100644 --- a/src/core/models/badges.py +++ b/src/core/models/badges.py @@ -184,11 +184,10 @@ class Badge(models.Model): def is_public(self): return self.state == self.State.PUBLIC - def save(self, update_fields=None, *args, **kwargs): - if self.conference_id is None and self.issuing_assembly_id is not None: - self.conference_id = self.issuing_assembly.conference_id - - elif self.conference_id != self.issuing_assembly.conference_id: + def save(self, *args, update_fields=None, **kwargs): + if not hasattr(self, 'conference') and hasattr(self, 'issuing_assembly'): + self.conference = self.issuing_assembly.conference + elif hasattr(self, 'conference') and self.conference != self.issuing_assembly.conference: raise ValidationError(_('Badge__issuing_assembly__wrong_conference')) if update_fields is None or 'description' in update_fields: render_results = compile_translated_markdown_fields(self, self.conference, 'description') diff --git a/src/core/models/conference.py b/src/core/models/conference.py index 1b4be0f04b2df1a2552ee6d51655018627d6439e..9bf0caa5f9f7921a9358f0a5da5a7ee93a1cb578 100644 --- a/src/core/models/conference.py +++ b/src/core/models/conference.py @@ -767,10 +767,10 @@ class ConferenceExportCache(models.Model): return super().save(*args, **kwargs) @classmethod - def _get_or_create_entry(cls, conference: Conference, type: Type, ident: str, result_func: typing.Callable[..., bytes]): + def _get_or_create_entry(cls, conference: Conference, entry_type: Type, ident: str, result_func: typing.Callable[..., bytes]): cache_entry, _ = cls.objects.get_or_create( conference=conference, - type=type, + type=entry_type, ident=ident, defaults={'needs_regeneration': True}, ) @@ -782,14 +782,14 @@ class ConferenceExportCache(models.Model): return cache_entry @classmethod - def fetch_entry(cls, conference: Conference, type: Type, ident: str, result_func, as_text: bool = False): - cache_entry = cls._get_or_create_entry(conference, type, ident, result_func) + def fetch_entry(cls, conference: Conference, entry_type: Type, ident: str, result_func, as_text: bool = False): + cache_entry = cls._get_or_create_entry(conference, entry_type, ident, result_func) content = cache_entry.data return content.decode('utf-8') if as_text else content @classmethod - def handle_http_request(cls, request: HttpRequest, conference: Conference, type: Type, ident: str, content_type, result_func): - cache_entry = cls._get_or_create_entry(conference, type, ident, result_func) + def handle_http_request(cls, request: HttpRequest, conference: Conference, entry_type: Type, ident: str, content_type, result_func): + cache_entry = cls._get_or_create_entry(conference, entry_type, ident, result_func) headers = { 'Content-Type': content_type if isinstance(content_type, str) else content_type(), diff --git a/src/core/models/messages.py b/src/core/models/messages.py index edcb87f3e5aa5710bcf96db1c2a42a334ae71da7..28e424ce9bac8c3be832245d62b3b2f2ed72875c 100644 --- a/src/core/models/messages.py +++ b/src/core/models/messages.py @@ -87,7 +87,7 @@ class DirectMessage(models.Model): ctx = { 'conference': self.conference, 'subject': self.subject, - 'message': self.message, + 'message': self.body, 'timestamp': self.timestamp, 'sender': str(self.sender) if self.sender is not None else None, 'recipient': str(self.recipient) if self.recipient is not None else None, @@ -96,7 +96,7 @@ class DirectMessage(models.Model): body_text = render_to_string('core/directmessage.txt', ctx) if settings.SUPPORT_HTML_MAILS: - ctx['message_html'] = render_markdown(self.body) + ctx['message_html'] = render_markdown(self.conference, self.body) body_html = render_to_string('core/directmessage.html', ctx) else: body_html = None diff --git a/src/core/models/pages.py b/src/core/models/pages.py index 1d5c480f93fddee344d22b24b9978c0cecbd8b1f..257ca1333fcafc5d67617ae4ff9a17e657390425 100644 --- a/src/core/models/pages.py +++ b/src/core/models/pages.py @@ -1,7 +1,6 @@ import logging import re import uuid -from typing import Dict, Optional, Tuple from urllib3.util import Url, parse_url @@ -65,7 +64,7 @@ class StaticPageNamespace(models.Model): return self.prefix @staticmethod - def _extract_doc_metadata(markup) -> Tuple[Dict[str, str], str]: + def _extract_doc_metadata(markup) -> tuple[dict[str, str], str]: if markup.startswith('---\n'): metadata_raw, content = markup[4:].split('---\n', maxsplit=1) metadata = {} @@ -174,7 +173,7 @@ class StaticPageNamespace(models.Model): class StaticPageManager(models.Manager): - def accessible_by_user(self, user: PlatformUser, conference: Conference, language: Optional[str] = ''): + def accessible_by_user(self, user: PlatformUser, conference: Conference, language: str | None = ''): if language is not None: qs = self.get_queryset().filter(language=language, conference=conference) else: @@ -195,7 +194,7 @@ class StaticPageManager(models.Manager): return qs.filter(public_revision__gt=0).filter(pages_filters) - def get_editable_page(self, user: PlatformUser, conference: Conference, language: str, slug: str, check=False) -> Tuple[Optional['StaticPage'], bool]: + def get_editable_page(self, user: PlatformUser, conference: Conference, language: str, slug: str, check=False) -> tuple['StaticPage | None', bool]: """ Request to edit the Static Page with slug `slug` in the context of `user` and `conference` and in the language `langguage`. @@ -327,7 +326,7 @@ class StaticPage(models.Model): def newest_revision(self): try: return self.revisions.order_by('-revision').first() - except models.DoesNotExist: + except StaticPageRevision.DoesNotExist: return None def clean(self, *args, **kwargs): diff --git a/src/core/models/rooms.py b/src/core/models/rooms.py index 8990529c366aa65353ffc2291e48ec2b67f9cd3d..e471f98450e89cca2f7339e1f38b5dc28a0d6e06 100644 --- a/src/core/models/rooms.py +++ b/src/core/models/rooms.py @@ -1,7 +1,7 @@ import uuid from datetime import datetime from pathlib import Path -from typing import Optional, TypeIs +from typing import TypeIs from uuid import uuid4 from django.conf import settings @@ -48,7 +48,7 @@ class RoomManager(ConferenceManagerMixin['Room']): Q(assembly__state_assembly__in=Assembly.PUBLIC_STATES) | Q(assembly__state_channel__in=Assembly.PUBLIC_STATES) ) - def assignable_in_timeframe(self, conference: Conference, start: datetime, end: datetime, assembly: Optional[Assembly] = None, is_sos: bool = False): + def assignable_in_timeframe(self, conference: Conference, start: datetime, end: datetime, assembly: Assembly | None = None, is_sos: bool = False): """ Fetch all rooms which are bookable for events in the given assembly (if any), in the given timeframe. Optionally, include rooms which are free for SOS usage. @@ -450,6 +450,7 @@ class RoomShare(models.Model): class RoomLinkManager(ConferenceManagerMixin['RoomLink']): + # TODO: Add public filter when switching to querysets staff_permissions = ['core.assembly_team'] conference_filter = 'room__assembly__conference' assembly_filter = 'room__assembly' diff --git a/src/core/models/schedules.py b/src/core/models/schedules.py index e712f8b4294e6b221e8457371633c804f55e562c..3e394cb9cfd7e64251ac33efe66eb9f6cef601fa 100644 --- a/src/core/models/schedules.py +++ b/src/core/models/schedules.py @@ -1,7 +1,7 @@ import logging from datetime import timedelta from hashlib import sha1 -from typing import TYPE_CHECKING, Dict, List, Optional +from typing import TYPE_CHECKING from uuid import UUID, uuid4 from django.core.exceptions import ObjectDoesNotExist @@ -92,7 +92,7 @@ class ScheduleSource(models.Model): """ # a frequency of 0 means: don't import automatically - if self.import_frequency.total_seconds == 0: + if self.import_frequency.total_seconds() == 0: return False # no imports yet? we're due! @@ -104,7 +104,7 @@ class ScheduleSource(models.Model): return timespan_since_last_import >= self.import_frequency @property - def latest_import(self) -> Optional['ScheduleSourceImport']: + def latest_import(self) -> 'ScheduleSourceImport | None': result = self.imports.order_by('-start').first() return result @@ -125,8 +125,8 @@ class ScheduleSource(models.Model): def _get_or_create_speaker( self, name: str, - mail_guid: Optional[str | UUID] = None, - addresses: Optional[List[str]] = None, + mail_guid: str | UUID | None = None, + addresses: list[str] | None = None, ): if not name: raise ValueError('You need to provide a name for the speaker.') @@ -448,7 +448,7 @@ class ScheduleSource(models.Model): ).values_list('local_id', flat=True) ) - def speaker_lookup(speaker_info: Dict[str, str]): + def speaker_lookup(speaker_info: dict[str, str]): """ Try to match the given speaker dict to a PlatformUser, if necessary creating a virtual one in the process. Returns None if the speaker shall be skipped (explicitly, using ScheduleSourceMapping.skip=True). @@ -562,7 +562,7 @@ class ScheduleSource(models.Model): from_dict_args={ 'allow_kind': self.assembly.is_official if self.assembly else False, # TODO: lookup assembly's room if not given 'allow_track': allow_track, # TODO - 'room_lookup': lambda r_source_id: rooms.get(r_source_id), + 'room_lookup': rooms.get, 'speaker_lookup': speaker_lookup, }, ) diff --git a/src/core/models/users.py b/src/core/models/users.py index 495500edd4384f407c78a27d4d3c6e7b7db3b7f6..65d89795a317dd8919f34ef60688b26a7b162e09 100644 --- a/src/core/models/users.py +++ b/src/core/models/users.py @@ -285,19 +285,19 @@ class PlatformUser(AbstractUser): """Counterpart to get_avatar_json() to update the settings.""" MIN_AVATAR_INDEX = -1 - """The lowest allowed avatar index for WorkAdventure, probably -1 (custom avatar).""" + # The lowest allowed avatar index for WorkAdventure, probably -1 (custom avatar). MAX_AVATAR_INDEX = 42 - """The largest allowed avatar index for WorkAdventure.""" + # The largest allowed avatar index for WorkAdventure. CUSTOM_LENGTH = 6 - """The length of the custom avatar array, these correspond to WorkAdventure's custom avatar selection window's row count.""" + # The length of the custom avatar array, these correspond to WorkAdventure's custom avatar selection window's row count. MIN_CUSTOM_INDEX = 0 - """The minimum value for each element in the custom avatar spec's array.""" + # The minimum value for each element in the custom avatar spec's array. MAX_CUSTOM_INDEX = 100 - """The maximum value for each element in the custom avatar spec's array.""" + # The maximum value for each element in the custom avatar spec's array. info = self.avatar_config or {} _UNSET = object() diff --git a/src/core/models/voucher.py b/src/core/models/voucher.py index b6f437a5091627b4d4738aff6e01d9d45bd846a3..6f5d007cb65b58cfd81bfadb817eb527400fdea9 100644 --- a/src/core/models/voucher.py +++ b/src/core/models/voucher.py @@ -1,5 +1,4 @@ import logging -from typing import List from uuid import uuid4 from django.core.exceptions import ValidationError @@ -134,7 +133,7 @@ class Voucher(models.Model): return html @classmethod - def do_auto_assignments(cls, conference: Conference = None, assemblies: List[Assembly] | None = None) -> int: + def do_auto_assignments(cls, conference: Conference = None, assemblies: list[Assembly] | None = None) -> int: qs = cls.objects.filter(conference=conference) if conference is not None else cls.objects.all() qs = qs.filter(enabled=True) qs = qs.filter(target__in=Voucher.ASSEMBLY_TARGETS) @@ -149,7 +148,7 @@ class Voucher(models.Model): return total - def do_auto_assignment(self, assemblies: List[Assembly]) -> int: + def do_auto_assignment(self, assemblies: list[Assembly]) -> int: if self.target not in self.ASSEMBLY_TARGETS: raise NotImplementedError('Auto-Assignment of non assemblies/channels not implemented yet.') diff --git a/src/core/paginators.py b/src/core/paginators.py index 4c60dd23e0186fbbcfa898860da13b14a9992092..addde934f9c8c8ea2abeb5e3c5c864844f5b501f 100644 --- a/src/core/paginators.py +++ b/src/core/paginators.py @@ -12,7 +12,7 @@ class AlphabetPaginator(Paginator): def __init__(self, *args, on: str | None = None, **kwargs) -> None: if on is None: - ImproperlyConfigured('on parameter required for NamePaginator') + raise ImproperlyConfigured('on parameter required for NamePaginator') self.on = on super().__init__(*args, **kwargs) self.item_dict = {} diff --git a/src/core/schedules/base.py b/src/core/schedules/base.py index 7d3d5cc01b4c03fcefdc7aec83a40c79ad6c4359..7735ba89ee0c2d2bfccceb47de4f512bc436042f 100644 --- a/src/core/schedules/base.py +++ b/src/core/schedules/base.py @@ -7,7 +7,8 @@ from django.utils import timezone from django.utils.module_loading import import_string -def filter_additional_data(data: dict, computed_fields={}) -> dict: +def filter_additional_data(data: dict, computed_fields: dict | None = None) -> dict: + computed_fields = computed_fields or {} return { k: v for k, v in data.items() if (v and k not in ['guid', 'room', 'start', 'date', 'duration', 'title', 'abstract', 'description', 'language']) } | computed_fields diff --git a/src/core/search.py b/src/core/search.py index baf83f2c1a72da3b955b7d3b618e94f8819563cb..23f5f663ffe6780cafee76782c3af25ae893c776 100644 --- a/src/core/search.py +++ b/src/core/search.py @@ -1,5 +1,5 @@ import shlex -from typing import Iterator, Union +from collections.abc import Iterator from django.contrib.postgres.search import SearchQuery @@ -12,7 +12,7 @@ from .models.users import PlatformUser def search( user: PlatformUser, conference: Conference, search_term: str, max_per_category: int = 10 -) -> Iterator[Union[ConferenceTag, ConferenceTrack, Assembly, Event]]: +) -> Iterator[ConferenceTag | ConferenceTrack | Assembly | Event]: """ Search assemblies, events, pages and tags for the search_term(s). Matches on the name are ranked higher than those only in the description. diff --git a/src/core/sso.py b/src/core/sso.py index 1a70b5a9e87424e3c692f25f0e693ca126c02a30..e723ab7326160595a2853f5ac6bf4efb1b5ca477 100644 --- a/src/core/sso.py +++ b/src/core/sso.py @@ -82,7 +82,7 @@ class OAuth2Scopes(BaseScopes): logger.warning('OAuth2Scopes.get_available_scopes() called for unexpected application user_type: %s', application.id) return result - def get_available_scopes(self, application=None, request=None, *args, **kwargs): + def get_available_scopes(self, *args, application=None, request=None, **kwargs): """ Return a list of scopes available for the current application/request. @@ -103,7 +103,7 @@ class OAuth2Scopes(BaseScopes): return [] - def get_default_scopes(self, application=None, request=None, *args, **kwargs): + def get_default_scopes(self, *args, application=None, request=None, **kwargs): """ Return a list of the default scopes for the current application/request. This MUST be a subset of the scopes returned by `get_available_scopes`. diff --git a/src/core/templatetags/hub_absolute.py b/src/core/templatetags/hub_absolute.py index f28467ad3afdf46afb004adffd7914b3f5380652..301aee5e863ed8fe3f28a6350360e1fa7f356f4d 100644 --- a/src/core/templatetags/hub_absolute.py +++ b/src/core/templatetags/hub_absolute.py @@ -1,5 +1,3 @@ -from typing import Optional - from django.conf import settings from django.conf.urls.i18n import i18n_patterns from django.contrib import admin @@ -56,7 +54,7 @@ def hub_absolute(url: str, *args, i18n: bool = True, lang: str | None = None, qu - *args: Positional arguments to pass to the URL resolver. - i18n (bool, optional): Whether to use internationalization. Defaults to True. - - lang (Optional[str], optional): Language code to switch to. Defaults to None. + - lang (str | None, optional): Language code to switch to. Defaults to None. - query_string (str, optional): Query string to append to the URL. Defaults to ''. - **kwargs: Keyword arguments to pass to the URL resolver. @@ -86,14 +84,14 @@ def hub_absolute(url: str, *args, i18n: bool = True, lang: str | None = None, qu @register.simple_tag -def hub_absolute_self(request: HttpRequest, i18n: bool = True, lang: Optional[str] = None): +def hub_absolute_self(request: HttpRequest, i18n: bool = True, lang: str | None = None): """ Generates an absolute URL for the current requests url. ### Args: - request (HttpRequest): The HTTP request object. - i18n (bool, optional): Flag to include internationalization in the URL. Defaults to True. - - lang (Optional[str], optional): Language code to include in the URL. Defaults to None. + - lang (str | None, optional): Language code to include in the URL. Defaults to None. ### Returns: - str: The absolute URL for the current requests url. diff --git a/src/core/tests/exportcache.py b/src/core/tests/exportcache.py index 72e57d7e46465b636be9dc6a0995b81b8985be14..a62ffc4c80d1f54d0caeb04b89469faf69061009 100644 --- a/src/core/tests/exportcache.py +++ b/src/core/tests/exportcache.py @@ -1,7 +1,6 @@ -from datetime import datetime, timedelta +from datetime import UTC, datetime, timedelta from django.test import TestCase, override_settings -from django.utils import timezone from django.utils.timezone import now from core.models.assemblies import Assembly @@ -13,8 +12,8 @@ class ScheduleTests(TestCase): self.conference = Conference( slug='foo', name='Foo Conference', - start=datetime(2021, 12, 1, 9, 0, 0, tzinfo=timezone.utc), - end=datetime(2021, 12, 31, 15, 00, 00, tzinfo=timezone.utc), + start=datetime(2021, 12, 1, 9, 0, 0, tzinfo=UTC), + end=datetime(2021, 12, 31, 15, 00, 00, tzinfo=UTC), ) self.conference.save() diff --git a/src/core/utils.py b/src/core/utils.py index 4df1eea55b26faf5eb433e044d4f64ae1dcc5d54..a39e515cbdd1f0560c81d6e65c6943f7c8adecf4 100644 --- a/src/core/utils.py +++ b/src/core/utils.py @@ -9,7 +9,6 @@ from datetime import UTC, datetime, timedelta from io import BytesIO from pathlib import Path from string import ascii_letters, digits -from typing import Dict, List, Optional, Tuple, Union from urllib.parse import parse_qs, urlparse, urlunparse import requests @@ -21,7 +20,7 @@ from django.utils.html import strip_tags logger = logging.getLogger(__name__) -def scheme_and_netloc_from_url(url: str) -> Optional[str]: +def scheme_and_netloc_from_url(url: str) -> str | None: parsed = urlparse(url) if not parsed.netloc: return None @@ -30,7 +29,7 @@ def scheme_and_netloc_from_url(url: str) -> Optional[str]: return scheme + '://' + parsed.netloc -def url_in_allowlist(scheme_and_netloc: str, patterns: List[Union[re.Pattern, str]]) -> bool: +def url_in_allowlist(scheme_and_netloc: str, patterns: list[re.Pattern | str]) -> bool: """ Checks whether the given scheme_and_netloc is present in the given list of patterns. This is used in dereferrer handling and (optionally) markdown generation. @@ -153,7 +152,7 @@ def mask_url(url): return urlunparse(masked) -def resolve_internal_url(url: str, fallback_as_is: bool = True) -> Optional[str]: +def resolve_internal_url(url: str, fallback_as_is: bool = True) -> str | None: """ Resolves special URLs like - conference://url_resolver_name @@ -195,9 +194,9 @@ def resolve_internal_url(url: str, fallback_as_is: bool = True) -> Optional[str] return url if fallback_as_is else None -def download_from_url(url: str) -> Tuple[str, bytes]: +def download_from_url(url: str) -> tuple[str, bytes]: # let requests library fetch the URL - r = requests.get(url) + r = requests.get(url, timeout=30) # bail out if response is not 200 r.raise_for_status() @@ -323,7 +322,7 @@ class GitRepo: return base_path - def get_documents(self, glob: str = '*.md', encoding: str = 'utf-8') -> Dict[str, str]: + def get_documents(self, glob: str = '*.md', encoding: str = 'utf-8') -> dict[str, str]: """ Read all documents matching the configured glob as text. :param glob: a filter to use when searching the documents to read, defaults to Markdown file extension diff --git a/src/core/validators.py b/src/core/validators.py index d7750f148b048c619d768f61796a184d2f1c8628..d288ef9c886111569de90f3bb1a3970dfe1b33e8 100644 --- a/src/core/validators.py +++ b/src/core/validators.py @@ -102,7 +102,7 @@ class ImageDimensionValidator: if errors: errors.append( ValidationError( - _('Validation__image_dimensions ' '%(min_width)s %(min_height)s ' '%(max_width)s %(max_height)s'), + _('Validation__image_dimensions %(min_width)s %(min_height)s %(max_width)s %(max_height)s'), code='image_dimensions', params={ 'min_width': self.min_size[0], diff --git a/src/core/views/__init__.py b/src/core/views/__init__.py index 627b74ea625b0fe66500626617a9700d6b30829d..76c258a3b05ed6086ca4df9b67949398dc280a50 100644 --- a/src/core/views/__init__.py +++ b/src/core/views/__init__.py @@ -1,9 +1,15 @@ -from core.views.auth import BaseLoginView, BasePasswordResetConfirmView, BasePasswordResetView, BaseRegistrationActivationView, BaseRegistrationView - -all = ( +from core.views.auth import ( BaseLoginView, + BasePasswordResetConfirmView, + BasePasswordResetView, BaseRegistrationActivationView, BaseRegistrationView, - BasePasswordResetView, - BasePasswordResetConfirmView, +) + +__all__ = ( + 'BaseLoginView', + 'BasePasswordResetConfirmView', + 'BasePasswordResetView', + 'BaseRegistrationActivationView', + 'BaseRegistrationView', ) diff --git a/src/hub/logging_utils.py b/src/hub/logging_utils.py index f2040793b54e6cbe6dd25beed0e3ca8b9355e6ff..6e311c51192b467188b7dfaf1cb69087255385d7 100644 --- a/src/hub/logging_utils.py +++ b/src/hub/logging_utils.py @@ -6,7 +6,6 @@ import logging from datetime import datetime from logging import LogRecord from pathlib import Path -from typing import Optional from dateutil import tz from rich.console import ConsoleRenderable @@ -81,14 +80,14 @@ class HubLoggingHandler(RichHandler): self, *, record: LogRecord, - traceback: Optional[Traceback], + traceback: Traceback | None, message_renderable: 'ConsoleRenderable', ) -> 'ConsoleRenderable': """Render log for display. Args: record (LogRecord): logging Record. - traceback (Optional[Traceback]): Traceback instance or None for no Traceback. + traceback (Traceback | None): Traceback instance or None for no Traceback. message_renderable (ConsoleRenderable): Renderable (typically Text) containing log message contents. Returns: diff --git a/src/hub/settings/base.py b/src/hub/settings/base.py index 192e7da89b6ad074f6031883538dfb4a778864b1..1c4d6482e7febf1f186368b549712a29e4884c34 100644 --- a/src/hub/settings/base.py +++ b/src/hub/settings/base.py @@ -173,7 +173,7 @@ DEBUG = env('DJANGO_DEBUG') == 'I_KNOW_WHAT_I_AM_DOING' ALLOWED_HOSTS = env('ALLOWED_HOSTS') # read application version from marker file -with BASE_DIR.joinpath('version.json').open() as version_file: +with BASE_DIR.joinpath('version.json').open(encoding='UTF-8') as version_file: APP_VERSION_INFO = json.load(version_file) # Application definition diff --git a/src/hub/settings/build.py b/src/hub/settings/build.py index bda3e5fa7ebeb64d1d43241c888b3d226e3cbb8a..6c4b906cef6c81389576fca9eb742b2ecd5c0e66 100644 --- a/src/hub/settings/build.py +++ b/src/hub/settings/build.py @@ -1,4 +1,4 @@ -from hub.settings.base import * # noqa: F403 +from hub.settings.base import * # noqa: F403 # pylint: disable=wildcard-import,unused-wildcard-import SECRET_KEY = 'BUILD_KEY' diff --git a/src/hub/settings/default.py b/src/hub/settings/default.py index 61f83b91bc2d4a2e15e11ce418f1f5724c549449..f7c1ee2ba33cd603c778d9cf26a0ce3034a4f5ed 100644 --- a/src/hub/settings/default.py +++ b/src/hub/settings/default.py @@ -2,7 +2,7 @@ import environ from django.urls import reverse_lazy -from hub.settings.base import * # noqa: F403 +from hub.settings.base import * # noqa: F403 # pylint: disable=wildcard-import,unused-wildcard-import default_env = environ.FileAwareEnv( # set casting, default value @@ -61,10 +61,10 @@ assert SECRET_KEY is None or not SECRET_KEY.startswith('/'), ( # Local Settings try: - from hub.local_settings import * # noqa: F403,F401 + from hub.local_settings import * # noqa: F403,F401 # pylint: disable=wildcard-import,unused-wildcard-import except ImportError: try: - from hub.settings.local_settings import * # noqa: F403,F401 + from hub.settings.local_settings import * # noqa: F403,F401 # pylint: disable=wildcard-import,unused-wildcard-import except ImportError: print('Unable to load (optional) local_settings.py.') @@ -72,12 +72,12 @@ except ImportError: if DEBUG and SECRET_KEY is None: # noqa: F405 SECRET_FILE = BASE_DIR.joinpath('hub', '.settings.secret') # noqa: F405 try: - with SECRET_FILE.open() as secret_file: + with SECRET_FILE.open(encoding='UTF-8') as secret_file: SECRET_KEY = secret_file.read().strip() except OSError: try: SECRET_KEY = gen_secret_key() # noqa: F405 - with SECRET_FILE.open('w') as secret: + with SECRET_FILE.open('w', encoding='UTF-8') as secret: secret.write(SECRET_KEY) print('*' * 72) print('*', 'Written SECRET_KEY file:', SECRET_FILE) diff --git a/src/hub/settings/dev.py b/src/hub/settings/dev.py index 4537146421af619835b976c267a77fadd37a41a6..640f276423aa28cd87cf5f42d2ea7223dd2d3e98 100644 --- a/src/hub/settings/dev.py +++ b/src/hub/settings/dev.py @@ -57,7 +57,7 @@ os.environ.setdefault('ABUSE_DEFAULT_RECIPIENT', 'report@hub.example.net') os.environ.setdefault('ABUSE_RECIPIENTS', 'abuse=abuse@hub.example.net,tech=hub@hub.example.net,foo=') # load all default settings -from .default import * # noqa: F401, E402, F403 +from .default import * # noqa: F401, E402, F403 # pylint: disable=wildcard-import,unused-wildcard-import # force DEBUG mode DEBUG = True diff --git a/src/hub/settings/test.py b/src/hub/settings/test.py index 207a156a37f651f102bd35c7c9d83b0458afba94..b2db9cf16393674bde51b52fb720a21d815c38c9 100644 --- a/src/hub/settings/test.py +++ b/src/hub/settings/test.py @@ -2,7 +2,7 @@ import os os.environ.setdefault('DJANGO_SECRET_KEY', 'Testing101') -from .default import * # noqa: F401, E402, F403 +from .default import * # noqa: F401, E402, F403 # pylint: disable=wildcard-import,unused-wildcard-import INTEGRATIONS_WORKADVENTURE = True INTEGRATIONS_BBB = True diff --git a/src/hub/urls.py b/src/hub/urls.py index a6be1c306a42cf1e4db15d745d81d55ca548d45c..be661c7d18b311216c63516f41bc4c0d8e1dea00 100644 --- a/src/hub/urls.py +++ b/src/hub/urls.py @@ -38,13 +38,13 @@ def is_only(service: str) -> bool: return not any(v for k, v in services.items() if k != service) -def add_service(service: str, prefix: str, urlpatterns: Any, path_name: str = '{prefix}', always_prefix: bool = False): +def add_service(service: str, prefix: str, service_patterns: Any, path_name: str = '{prefix}', always_prefix: bool = False): if not services[service]: return [] return [ path( prefix if always_prefix else path_name.format(prefix='' if is_only(service) else prefix), - urlpatterns, + service_patterns, ) ] @@ -74,9 +74,9 @@ services.pop('admin') urlpatterns += add_service( 'api', 'api/', - urlpatterns=include('api.urls'), + service_patterns=include('api.urls'), ) -urlpatterns += add_service('api', 'api/', path_name='{prefix}auth/', urlpatterns=include('rest_framework.urls', namespace='rest_framework')) +urlpatterns += add_service('api', 'api/', path_name='{prefix}auth/', service_patterns=include('rest_framework.urls', namespace='rest_framework')) urlpatterns += add_service('api', 'metrics/', include('api.urls_metrics', namespace='metrics'), always_prefix=True) services.pop('api') diff --git a/src/plainui/forms.py b/src/plainui/forms.py index 518904fda04b1c2ef317164d91e36af6d1ef5ae3..9444cef763028b621049dfb2c33561f1c68a679d 100644 --- a/src/plainui/forms.py +++ b/src/plainui/forms.py @@ -244,7 +244,7 @@ class TokenPasswortResetForm(auth_forms.SetPasswordForm): if user.email or user.communication_channels.filter(channel=UserCommunicationChannel.Channel.MAIL, is_verified=True).exists(): raise ValidationError(gettext('User can use password reset by email!')) - pretix_ident, ticket_data = ConferenceMemberTicket.validate_pretix_ticket(self.conf, self.user, self.cleaned_data['jwt'], validate_only=True) + pretix_ident, _ticket_data = ConferenceMemberTicket.validate_pretix_ticket(self.conf, self.user, self.cleaned_data['jwt'], validate_only=True) if not ConferenceMemberTicket.objects.filter(conference=self.conf, user=user, ident=pretix_ident).exists(): raise ValidationError(gettext("User didn't use this Ticket!")) diff --git a/src/plainui/jinja2.py b/src/plainui/jinja2.py index a3be9bc73fbdf87c8a048d17029686b85602a215..2677ce6ce025e927519f7457abe8663890209993 100644 --- a/src/plainui/jinja2.py +++ b/src/plainui/jinja2.py @@ -108,7 +108,7 @@ def custom_timedelta_short(tdelta: timedelta): s -= h * 3600 m = s // 60 s -= m * 60 - return '%02d:%02d:%02d' % (h, m, s) + return f'{h:02d}:{m:02d}:{s:02d}' def custom_strftime(date): diff --git a/src/plainui/tests/__init__.py b/src/plainui/tests/__init__.py index fc9875885ae34b0b8d9452769eda0e20fb9c8232..b84588ab6f34e4a5f49a96db5a85c6f69ff43726 100644 --- a/src/plainui/tests/__init__.py +++ b/src/plainui/tests/__init__.py @@ -1,9 +1,9 @@ from plainui.tests.test_auth import AuthViewTest, LoginViewTests, RegistrationTests from plainui.tests.test_views import ViewsTest -all = ( - AuthViewTest, - RegistrationTests, - LoginViewTests, - ViewsTest, -) +__all__ = [ + 'AuthViewTest', + 'LoginViewTests', + 'RegistrationTests', + 'ViewsTest', +] diff --git a/src/plainui/tests/test_views.py b/src/plainui/tests/test_views.py index a7ca15956bbcf25f442c22e407313fc1cd7735bc..e9002f0c704db719229956d1659aa6d71ed7b151 100644 --- a/src/plainui/tests/test_views.py +++ b/src/plainui/tests/test_views.py @@ -131,8 +131,8 @@ class ViewsTestBase(TestCase): This is the test pendant to `plainui.views.ConferenceRequiredMixin`. """ + old_cookies = self.client.cookies try: - old_cookies = self.client.cookies self.client.cookies = SimpleCookie() ConferenceRequiredMixin._test_cork = True _request = self.client.post if post else self.client.get @@ -192,8 +192,8 @@ class ViewsTestBase(TestCase): ConferenceRequiredMixin._conf = None def assertNeedsNoLogin(self, url, data=None, post=False): + old_cookies = self.client.cookies try: - old_cookies = self.client.cookies self.client.cookies = SimpleCookie() ConferenceRequiredMixin._test_cork = True _request = self.client.post if post else self.client.get diff --git a/src/plainui/utils.py b/src/plainui/utils.py index 608b35b83db6e9d4d52b0dc11a3c3dd49d851e01..08c243bd7634f39bf271c97af306ff950dadb61a 100644 --- a/src/plainui/utils.py +++ b/src/plainui/utils.py @@ -1,7 +1,6 @@ import logging import re from difflib import HtmlDiff -from typing import Optional, Tuple from django.conf import settings from django.urls import reverse @@ -17,8 +16,8 @@ logger = logging.getLogger(__name__) def fetch_wiki_page( - conference: Conference, slug: str, language: Optional[str] = None, default_title: Optional[str] = 'Page 404', default_body: Optional[str] = None -) -> Tuple[str, str]: + conference: Conference, slug: str, language: str | None = None, default_title: str | None = 'Page 404', default_body: str | None = None +) -> tuple[str, str]: """ Retrieves the wiki page with the given slug in the given language (defaults to current request's one). :param conference: the Conference in which to search @@ -63,6 +62,7 @@ def check_message_content(conf, request, text, kind, kind_data): report_form = ReportForm( conf=conf, + request=request, data={ 'kind': kind, 'kind_data': str(kind_data), @@ -83,11 +83,11 @@ class StaticPageDiff(HtmlDiff): # function from source code - removed `nowrap="nowrap"` def _format_line(self, side, flag, linenum, text): try: - linenum = '%d' % linenum - id = f' id="{self._prefix[side]}{linenum}"' + linenum = f'{linenum:d}' + change_id = f' id="{self._prefix[side]}{linenum}"' except TypeError: # handle blank lines where linenum is '>' or '' - id = '' + change_id = '' # replace those things that would get confused with HTML symbols text = text.replace('&', '&').replace('>', '>').replace('<', '<') @@ -96,4 +96,4 @@ class StaticPageDiff(HtmlDiff): text = text.replace(' ', ' ').rstrip() # vvv ---- removed `nowrap="nowrap"` --- vvv - return f'<td class="diff_header"{id}>{linenum}</td><td class="diff_content">{text}</td>' + return f'<td class="diff_header"{change_id}>{linenum}</td><td class="diff_content">{text}</td>' diff --git a/src/plainui/views/assemblies.py b/src/plainui/views/assemblies.py index 5ccfdeed76dd2fa42d230ec064fa723f0eede0dc..67037006d0666742ceb68e1b7900b5db0fb9b8cc 100644 --- a/src/plainui/views/assemblies.py +++ b/src/plainui/views/assemblies.py @@ -1,7 +1,7 @@ __all__ = ( - 'AssemblyView', - 'AssembliesView', 'AssembliesAllView', + 'AssembliesView', + 'AssemblyView', ) import logging @@ -114,10 +114,10 @@ class AssembliesAllView(ConferenceRequiredMixin, PrefetchTagsMixin, Alphabetical } ordering = ['name'] - def get_queryset(self) -> QuerySet[Assembly]: + def get_queryset(self, queryset: QuerySet[Assembly] | None = None) -> QuerySet[Assembly]: public_events = Event.objects.conference_accessible(self.conf) public_projects = Project.objects.conference_accessible(self.conf) - queryset: QuerySet[Assembly] = Assembly.objects.conference_accessible(self.conf) + queryset = queryset if queryset else Assembly.objects.conference_accessible(self.conf) queryset = queryset.annotate( public_events_count=Count('events', filter=Q(events__in=public_events), distinct=True), public_projects_count=Count('projects', Q(projects__in=public_projects), distinct=True), diff --git a/src/plainui/views/auth.py b/src/plainui/views/auth.py index 00c5dbca0593a2c7b93f69f786ca33372682cd01..a39f45b35d2e21feb2ca90772c6d293dab1e9ab6 100644 --- a/src/plainui/views/auth.py +++ b/src/plainui/views/auth.py @@ -1,16 +1,16 @@ __all__ = ( 'LoginView', - 'RegistrationView', - 'RegistrationDoneView', - 'RegistrationActivationView', + 'LogoutView', 'PasswordChangeView', - 'PasswordResetView', - 'PasswordResetDoneView', - 'PasswordResetConfirmView', 'PasswordResetCompleteView', - 'TokenPasswordResetView', - 'LogoutView', + 'PasswordResetConfirmView', + 'PasswordResetDoneView', + 'PasswordResetView', + 'RegistrationActivationView', + 'RegistrationDoneView', + 'RegistrationView', 'ShibboleetView', + 'TokenPasswordResetView', ) import logging diff --git a/src/plainui/views/badges.py b/src/plainui/views/badges.py index 047ab6ecf3c6032b1a4d2e19cc42238731971634..0aafbdd27b9f600c38ec06fa7fbd714cf723bc96 100644 --- a/src/plainui/views/badges.py +++ b/src/plainui/views/badges.py @@ -1,11 +1,11 @@ __all__ = ( + 'AwardBadgeView', 'BadgeDetailView', 'BadgeListView', - 'ManageBadgesView', + 'BadgeSettingsView', 'ManageBadgeView', - 'AwardBadgeView', + 'ManageBadgesView', 'RedeemTokenDetailView', - 'BadgeSettingsView', ) from typing import Any @@ -235,7 +235,7 @@ class ManageBadgesView(ConferenceRequiredMixin, TemplateView): return context def dispatch(self, request, *args, **kwargs): - if hasattr(request, 'GET') and request.GET.get('redeem_token') or hasattr(request, 'GET') and request.POST.get('purpose', None) == 'redeem_token': + if (hasattr(request, 'GET') and request.GET.get('redeem_token')) or (hasattr(request, 'GET') and request.POST.get('purpose', None) == 'redeem_token'): return RedeemBadgeView.as_view(external_context=self.get_context_data())(request, *args, **kwargs) return super().dispatch(request, *args, **kwargs) @@ -256,7 +256,7 @@ class ManageBadgeView(ConferenceRequiredMixin, UpdateView): return queryset.get(badge__id=self.kwargs.get('pk')) def dispatch(self, request, *args, **kwargs): - if hasattr(request, 'GET') and request.GET.get('redeem_token') or hasattr(request, 'GET') and request.POST.get('purpose', None) == 'redeem_token': + if (hasattr(request, 'GET') and request.GET.get('redeem_token')) or (hasattr(request, 'GET') and request.POST.get('purpose', None) == 'redeem_token'): return RedeemBadgeView.as_view(external_context=self.get_context_data())(request, *args, **kwargs) return super().dispatch(request, *args, **kwargs) diff --git a/src/plainui/views/board.py b/src/plainui/views/board.py index 55b27ec54045ee602c4cfa1d1f62b60cef10155f..25cf021c942e643f148ccdb5074a35c5ef62388f 100644 --- a/src/plainui/views/board.py +++ b/src/plainui/views/board.py @@ -1,10 +1,10 @@ __all__ = ( - 'BoardView', - 'BoardPrivateView', - 'BoardEntryView', 'BoardEntryCreateView', - 'BoardEntryEditView', 'BoardEntryDeleteView', + 'BoardEntryEditView', + 'BoardEntryView', + 'BoardPrivateView', + 'BoardView', ) diff --git a/src/plainui/views/dereferrer.py b/src/plainui/views/dereferrer.py index 503267e418f45544bdab954189667e75e8f881e7..4e874977b17594ecd2988b82c3100172d2aac958 100644 --- a/src/plainui/views/dereferrer.py +++ b/src/plainui/views/dereferrer.py @@ -1,9 +1,9 @@ __all__ = ( - 'DereferrerView', - 'DereferrerRedirectView', - 'WorkadventureDereferrerView', 'DereferrerAddToAllowlistView', + 'DereferrerRedirectView', 'DereferrerRemoveFromAllowlistView', + 'DereferrerView', + 'WorkadventureDereferrerView', ) from urllib.parse import ParseResult, unquote, urlparse diff --git a/src/plainui/views/events.py b/src/plainui/views/events.py index e483b54693880a1a42c9a7267e618fe03253c710..f6e7fc3c7377f4bc2b3eb84dd8f7ed78d0273e8d 100644 --- a/src/plainui/views/events.py +++ b/src/plainui/views/events.py @@ -1,10 +1,10 @@ __all__ = ( 'AssembliesEventsView', - 'EventView', - 'UpcomingView', 'ChannelEventsView', - 'SosList', + 'EventView', 'SosJoin', + 'SosList', + 'UpcomingView', ) from django.contrib import messages diff --git a/src/plainui/views/general.py b/src/plainui/views/general.py index 41a9b819acf0edf46b93c9b44fae86288aa96b87..7d935cb942d4d8f32a020ae60b36dc0276ecc642 100644 --- a/src/plainui/views/general.py +++ b/src/plainui/views/general.py @@ -1,10 +1,10 @@ __all__ = ( - 'LandingView', + 'ComponentGalleryView', 'IndexView', + 'LandingView', 'MetaNavView', - 'TagView', 'SearchView', - 'ComponentGalleryView', + 'TagView', 'UnderConstructionView', ) @@ -146,7 +146,7 @@ class TagView(ConferenceRequiredMixin, TemplateView): class SearchView(ConferenceRequiredMixin, TemplateView): template_name = 'plainui/search.html.j2' - def get(self, request, **kwargs): + def get(self, request, *args, **kwargs): return redirect(reverse('plainui:index')) def post(self, request, **kwargs): @@ -169,10 +169,11 @@ class SearchView(ConferenceRequiredMixin, TemplateView): class ComponentGalleryView(TemplateView): template_name = 'plainui/component_gallery.html.j2' - def get_context_data(self): + def get_context_data(self, **kwargs): form_invalid = ExampleForm({'text': '', 'textarea': ''}) form_invalid.full_clean() return super().get_context_data( + **kwargs, form_valid=ExampleForm({'text': 'lorem ipsum', 'password': 'lorem ipsum', 'checkbox': True, 'textarea': 'blork'}), form_invalid=form_invalid, conf={ diff --git a/src/plainui/views/personal_messages.py b/src/plainui/views/personal_messages.py index bfc0b681ee2f4fb742335aacea4790fefdc9404b..195d38beb6c25e802ddb1cef79bb5f3ed0554c20 100644 --- a/src/plainui/views/personal_messages.py +++ b/src/plainui/views/personal_messages.py @@ -1,8 +1,8 @@ __all__ = ( + 'PersonalMessageDeleteView', 'PersonalMessageListView', 'PersonalMessageSendView', 'PersonalMessageShowView', - 'PersonalMessageDeleteView', ) from django_ratelimit.decorators import ratelimit diff --git a/src/plainui/views/static_pages.py b/src/plainui/views/static_pages.py index 962ffa607a7750b3612e21f583885b575cf3118c..8a435aa25d3ddcbafb0dc8bb401f573ab292fbbd 100644 --- a/src/plainui/views/static_pages.py +++ b/src/plainui/views/static_pages.py @@ -1,15 +1,15 @@ __all__ = ( - 'StaticPageView', - 'StaticPageEditView', - 'StaticPageHistoryView', 'StaticPageDiffView', + 'StaticPageEditView', 'StaticPageGlobalHistoryView', + 'StaticPageHistoryView', 'StaticPageLockKeepalive', 'StaticPageRedirectToStart', + 'StaticPageView', ) from datetime import timedelta -from typing import Any, Dict +from typing import Any from django_ratelimit.decorators import ratelimit @@ -45,7 +45,7 @@ class StaticPageView(ConferenceRequiredMixin, TemplateView): require_conference_member = False @transaction.atomic - def get(self, request, page_slug, **kwargs): + def get(self, request, *args, page_slug, **kwargs): self.static_page = StaticPage.objects.conference_accessible(conference=self.conf, language=get_language()).filter(slug=page_slug).first() if 'release' in request.GET: @@ -63,7 +63,7 @@ class StaticPageView(ConferenceRequiredMixin, TemplateView): messages.error(self.request, gettext('You need an active Ticket to access this Page!')) return redirect(reverse('plainui:redeem_token')) - return super().get(request, page_slug=page_slug, **kwargs) + return super().get(request, *args, page_slug=page_slug, **kwargs) def get_context_data(self, page_slug, **kwargs): context = super().get_context_data(**kwargs) @@ -275,7 +275,7 @@ class StaticPageEditView(ConferenceRequiredMixin, TemplateView): lock = None if page_exists and not preview: lock = Lock.objects.filter(pk=lock_id, content_type=ContentType.objects.get_for_model(StaticPage), object_id=static_page.pk).first() - if not lock or lock.timeout >= self.now and lock.lock_holder != request.user: + if not lock or (lock.timeout >= self.now and lock.lock_holder != request.user): messages.error(request, gettext('Lock error, could not save page!')) lock_error = True @@ -336,7 +336,7 @@ class StaticPageHistoryView(ConferenceRequiredMixin, TemplateView): template_name = 'plainui/static_page_history.html.j2' require_user = True - def get(self, request, page_slug, **kwargs): + def get(self, request, *args, page_slug, **kwargs): self.static_page = StaticPage.objects.conference_accessible(conference=self.conf, language=get_language()).filter(slug=page_slug).first() if not self.static_page: return redirect(reverse('plainui:static_page', kwargs={'page_slug': page_slug})) @@ -348,9 +348,9 @@ class StaticPageHistoryView(ConferenceRequiredMixin, TemplateView): if not self.request.user.is_authenticated or not self.request.user.has_conference_staff_permission(self.conf, 'core.static_pages'): return redirect(reverse('plainui:static_page', kwargs={'page_slug': page_slug})) - return super().get(request, page_slug=page_slug, **kwargs) + return super().get(request, *args, page_slug=page_slug, **kwargs) - def get_context_data(self, page_slug, **kwargs) -> Dict[str, Any]: + def get_context_data(self, page_slug, **kwargs) -> dict[str, Any]: context = super().get_context_data(**kwargs) context['conf'] = self.conf context['page_slug'] = page_slug @@ -364,7 +364,7 @@ class StaticPageDiffView(ConferenceRequiredMixin, TemplateView): template_name = 'plainui/static_page_diff.html.j2' require_user = True - def get(self, request, page_slug, **kwargs): + def get(self, request, *args, page_slug, **kwargs): self.static_page = StaticPage.objects.conference_accessible(conference=self.conf, language=get_language()).filter(slug=page_slug).first() if not self.static_page: return redirect(reverse('plainui:static_page', kwargs={'page_slug': page_slug})) @@ -376,9 +376,9 @@ class StaticPageDiffView(ConferenceRequiredMixin, TemplateView): if not self.request.user.is_authenticated or not self.request.user.has_conference_staff_permission(self.conf, 'core.static_pages'): return redirect(reverse('plainui:static_page', kwargs={'page_slug': page_slug})) - return super().get(request, page_slug=page_slug, **kwargs) + return super().get(request, *args, page_slug=page_slug, **kwargs) - def get_context_data(self, page_slug, **kwargs) -> Dict[str, Any]: + def get_context_data(self, page_slug, **kwargs) -> dict[str, Any]: context = super().get_context_data(**kwargs) context['conf'] = self.conf context['page_slug'] = page_slug @@ -407,7 +407,7 @@ class StaticPageGlobalHistoryView(ConferenceRequiredMixin, TemplateView): template_name = 'plainui/static_page_global_history.html.j2' require_user = True - def get_context_data(self, **kwargs: Any) -> Dict[str, Any]: + def get_context_data(self, **kwargs: Any) -> dict[str, Any]: PAGE_SIZE = 30 try: page = int(self.request.GET.get('page', '0')) diff --git a/src/plainui/views/ticket_tokens.py b/src/plainui/views/ticket_tokens.py index a5bf8a13ad4c92e513319700bca26186781d4b88..648c02d5b52589a9f6ec4636f242b8674d8b4a5c 100644 --- a/src/plainui/views/ticket_tokens.py +++ b/src/plainui/views/ticket_tokens.py @@ -1,8 +1,8 @@ __all__ = ( - 'RedeemTokenView', 'RedeemTokenAddToUserView', - 'RedeemTokenUserCreateView', 'RedeemTokenLoggedIn', + 'RedeemTokenUserCreateView', + 'RedeemTokenView', ) from django_ratelimit.decorators import ratelimit diff --git a/src/plainui/views/user_profile.py b/src/plainui/views/user_profile.py index 0cfd977fa9d04838f1295f16ba2a78a2e429578e..04e5de43300cc2073c0fd92672d2a6b65e9fd373 100644 --- a/src/plainui/views/user_profile.py +++ b/src/plainui/views/user_profile.py @@ -1,7 +1,7 @@ __all__ = ( - 'ProfileView', - 'ModifyThemeView', 'ModifyFavoritesView', + 'ModifyThemeView', + 'ProfileView', ) import logging diff --git a/src/plainui/views/users.py b/src/plainui/views/users.py index d6729bf3b8d94fc7dac5e962c84fd38e75b5afc3..785e052416e2cc1e1ba9bac9141ec256702e2f74 100644 --- a/src/plainui/views/users.py +++ b/src/plainui/views/users.py @@ -1,6 +1,6 @@ __all__ = ( - 'UserView', 'UserByUuidView', + 'UserView', ) from django.db.models import Q diff --git a/src/plainui/views/utils.py b/src/plainui/views/utils.py index 4c150bd4a8886006d827b909573c799eb5633394..b38eaaf0f3a6892288c812ad9fe1b279fac6263f 100644 --- a/src/plainui/views/utils.py +++ b/src/plainui/views/utils.py @@ -1,6 +1,6 @@ import threading from datetime import UTC, datetime, timedelta -from typing import ClassVar, Optional +from typing import ClassVar from django.conf import settings from django.contrib import messages @@ -37,7 +37,7 @@ class ConferenceRequiredMixin(auth_mixins.AccessMixin): """ This view should only be shown when the conference is published.""" # cache for the conference used in this plainui instance - _conf: ClassVar[Optional[Conference]] = None + _conf: ClassVar[Conference | None] = None _conf_timeout: ClassVar[datetime] = datetime.min.replace(tzinfo=UTC) _conf_lock: ClassVar[threading.Lock] = threading.Lock() @@ -97,7 +97,7 @@ class ConferenceRequiredMixin(auth_mixins.AccessMixin): return redirect(reverse('plainui:under_construction')) if ( - self.require_login and conf.require_login or conf.require_ticket and self.require_conference_member or self.require_user + (self.require_login and conf.require_login) or (conf.require_ticket and self.require_conference_member) or self.require_user ) and not request.user.is_authenticated: return self.handle_no_permission() @@ -190,7 +190,7 @@ def event_filter( upcoming=False, calendar_mode=True, public_fahrplan=None, - is_recorded: Optional[bool] = None, + is_recorded: bool | None = None, ): min_date, max_date = conf.start, conf.end if min_date is None or max_date is None: @@ -275,22 +275,22 @@ _SESSION_TYPE_MAP = { } -def _session_refresh_favorite(session, user, type: str) -> list[str]: +def _session_refresh_favorite(session, user, object_type: str) -> list[str]: if not user.is_authenticated: return [] - favorites = [str(id_) for id_ in _SESSION_TYPE_MAP[type].objects.filter(favorite_of=user).values_list('id', flat=True)] - session[type] = favorites + favorites = [str(id_) for id_ in _SESSION_TYPE_MAP[object_type].objects.filter(favorite_of=user).values_list('id', flat=True)] + session[object_type] = favorites return favorites -def session_get_favorite(session, user, type: str) -> list[str]: +def session_get_favorite(session, user, object_type: str) -> list[str]: if not user.is_authenticated: return [] - favorites = session.get(type, None) + favorites = session.get(object_type, None) if favorites is None: - return _session_refresh_favorite(session, user, type) + return _session_refresh_favorite(session, user, object_type) return favorites @@ -303,9 +303,6 @@ def _session_refresh_favorite_assemblies(session, user) -> list[str]: def session_get_favorite_events(session, user) -> list[str]: - # TODO: remove this after 37c3, when no more session are active, this is to migrate the personal calendar data - if session.pop('sch_e', None): - session.pop('fav_e') return session_get_favorite(session, user, 'fav_e') diff --git a/src/plainui/views/work_adventure.py b/src/plainui/views/work_adventure.py index 49cb99e00c8cc9f85bcc83951444e691e8021816..dd522e3228e1583496ca9f6d67793ff4285c0d8a 100644 --- a/src/plainui/views/work_adventure.py +++ b/src/plainui/views/work_adventure.py @@ -1,9 +1,9 @@ __all__ = ( - 'WorldView', - 'WorkadventureEnterView', + 'AssemblyJoinBBBView', 'WorkadventureContactPageView', + 'WorkadventureEnterView', 'WorkadventureVCardView', - 'AssemblyJoinBBBView', + 'WorldView', ) from django.conf import settings