From 68d2582bfe68b9fb89c47b098aa3943be671e084 Mon Sep 17 00:00:00 2001 From: Sam Date: Sat, 11 Jun 2022 11:32:12 +0200 Subject: [PATCH 1/3] update python dependencies --- poetry.lock | 258 ++++++++++++++++++++++++------------------------- pyproject.toml | 4 +- 2 files changed, 131 insertions(+), 131 deletions(-) diff --git a/poetry.lock b/poetry.lock index ff8f4c4b..7b840248 100644 --- a/poetry.lock +++ b/poetry.lock @@ -8,11 +8,11 @@ python-versions = "*" [[package]] name = "alembic" -version = "1.7.7" +version = "1.8.0" description = "A database migration tool for SQLAlchemy." category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.dependencies] importlib-metadata = {version = "*", markers = "python_version < \"3.9\""} @@ -195,14 +195,14 @@ test = ["flake8 (==3.7.8)", "hypothesis (==3.55.3)"] [[package]] name = "coverage" -version = "6.4" +version = "6.4.1" description = "Code coverage measurement for Python" category = "dev" optional = false python-versions = ">=3.7" [package.dependencies] -tomli = {version = "*", optional = true, markers = "python_version < \"3.11\" and extra == \"toml\""} +tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} [package.extras] toml = ["tomli"] @@ -569,7 +569,7 @@ python-versions = "*" [[package]] name = "mypy" -version = "0.960" +version = "0.961" description = "Optional static typing for Python" category = "dev" optional = false @@ -990,7 +990,7 @@ sphinx = ">=1.3.1" [[package]] name = "redis" -version = "4.3.1" +version = "4.3.3" description = "Python client for Redis database and key-value store" category = "main" optional = false @@ -1009,20 +1009,20 @@ ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)" [[package]] name = "requests" -version = "2.27.1" +version = "2.28.0" description = "Python HTTP for Humans." category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +python-versions = ">=3.7, <4" [package.dependencies] certifi = ">=2017.4.17" -charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""} -idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""} +charset-normalizer = ">=2.0.0,<2.1.0" +idna = ">=2.5,<4" urllib3 = ">=1.21.1,<1.27" [package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] [[package]] @@ -1227,7 +1227,7 @@ test = ["pytest"] [[package]] name = "sqlalchemy" -version = "1.4.36" +version = "1.4.37" description = "Database Abstraction Library" category = "main" optional = false @@ -1252,7 +1252,7 @@ mysql_connector = ["mysql-connector-python"] oracle = ["cx_oracle (>=7,<8)", "cx_oracle (>=7)"] postgresql = ["psycopg2 (>=2.7)"] postgresql_asyncpg = ["greenlet (!=0.4.17)", "asyncpg"] -postgresql_pg8000 = ["pg8000 (>=1.16.6)"] +postgresql_pg8000 = ["pg8000 (>=1.16.6,!=1.29.0)"] postgresql_psycopg2binary = ["psycopg2-binary"] postgresql_psycopg2cffi = ["psycopg2cffi"] pymysql = ["pymysql (<1)", "pymysql"] @@ -1314,7 +1314,7 @@ python-versions = ">=3.7" [[package]] name = "trio" -version = "0.20.0" +version = "0.21.0" description = "A friendly Python library for async concurrency and I/O" category = "dev" optional = false @@ -1352,7 +1352,7 @@ python-versions = ">=3.6" [[package]] name = "types-freezegun" -version = "1.1.9" +version = "1.1.10" description = "Typing stubs for freezegun" category = "dev" optional = false @@ -1368,7 +1368,7 @@ python-versions = "*" [[package]] name = "types-requests" -version = "2.27.29" +version = "2.27.30" description = "Typing stubs for requests" category = "dev" optional = false @@ -1466,7 +1466,7 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest- [metadata] lock-version = "1.1" python-versions = "^3.7" -content-hash = "e3bc153f918e57b66b117505b29466555da5ed642b0bbdc58bb5bcb9bd2ea688" +content-hash = "fa5be5cc59de72ae3ddb18acb2c0291a7d77296de6427492b46c40d794800335" [metadata.files] alabaster = [ @@ -1474,8 +1474,8 @@ alabaster = [ {file = "alabaster-0.7.12.tar.gz", hash = "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"}, ] alembic = [ - {file = "alembic-1.7.7-py3-none-any.whl", hash = "sha256:29be0856ec7591c39f4e1cb10f198045d890e6e2274cf8da80cb5e721a09642b"}, - {file = "alembic-1.7.7.tar.gz", hash = "sha256:4961248173ead7ce8a21efb3de378f13b8398e6630fab0eb258dc74a8af24c58"}, + {file = "alembic-1.8.0-py3-none-any.whl", hash = "sha256:b5ae4bbfc7d1302ed413989d39474d102e7cfa158f6d5969d2497955ffe85a30"}, + {file = "alembic-1.8.0.tar.gz", hash = "sha256:a2d4d90da70b30e70352cd9455e35873a255a31402a438fe24815758d7a0e5e1"}, ] async-generator = [ {file = "async_generator-1.10-py3-none-any.whl", hash = "sha256:01c7bf666359b4967d2cda0000cc2e4af16a0ae098cbffcb8472fb9e8ad6585b"}, @@ -1612,47 +1612,47 @@ commonmark = [ {file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"}, ] coverage = [ - {file = "coverage-6.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:50ed480b798febce113709846b11f5d5ed1e529c88d8ae92f707806c50297abf"}, - {file = "coverage-6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:26f8f92699756cb7af2b30720de0c5bb8d028e923a95b6d0c891088025a1ac8f"}, - {file = "coverage-6.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:60c2147921da7f4d2d04f570e1838db32b95c5509d248f3fe6417e91437eaf41"}, - {file = "coverage-6.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:750e13834b597eeb8ae6e72aa58d1d831b96beec5ad1d04479ae3772373a8088"}, - {file = "coverage-6.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af5b9ee0fc146e907aa0f5fb858c3b3da9199d78b7bb2c9973d95550bd40f701"}, - {file = "coverage-6.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:a022394996419142b33a0cf7274cb444c01d2bb123727c4bb0b9acabcb515dea"}, - {file = "coverage-6.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5a78cf2c43b13aa6b56003707c5203f28585944c277c1f3f109c7b041b16bd39"}, - {file = "coverage-6.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9229d074e097f21dfe0643d9d0140ee7433814b3f0fc3706b4abffd1e3038632"}, - {file = "coverage-6.4-cp310-cp310-win32.whl", hash = "sha256:fb45fe08e1abc64eb836d187b20a59172053999823f7f6ef4f18a819c44ba16f"}, - {file = "coverage-6.4-cp310-cp310-win_amd64.whl", hash = "sha256:3cfd07c5889ddb96a401449109a8b97a165be9d67077df6802f59708bfb07720"}, - {file = "coverage-6.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:03014a74023abaf5a591eeeaf1ac66a73d54eba178ff4cb1fa0c0a44aae70383"}, - {file = "coverage-6.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c82f2cd69c71698152e943f4a5a6b83a3ab1db73b88f6e769fabc86074c3b08"}, - {file = "coverage-6.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b546cf2b1974ddc2cb222a109b37c6ed1778b9be7e6b0c0bc0cf0438d9e45a6"}, - {file = "coverage-6.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc173f1ce9ffb16b299f51c9ce53f66a62f4d975abe5640e976904066f3c835d"}, - {file = "coverage-6.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c53ad261dfc8695062fc8811ac7c162bd6096a05a19f26097f411bdf5747aee7"}, - {file = "coverage-6.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:eef5292b60b6de753d6e7f2d128d5841c7915fb1e3321c3a1fe6acfe76c38052"}, - {file = "coverage-6.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:543e172ce4c0de533fa892034cce260467b213c0ea8e39da2f65f9a477425211"}, - {file = "coverage-6.4-cp37-cp37m-win32.whl", hash = "sha256:00c8544510f3c98476bbd58201ac2b150ffbcce46a8c3e4fb89ebf01998f806a"}, - {file = "coverage-6.4-cp37-cp37m-win_amd64.whl", hash = "sha256:b84ab65444dcc68d761e95d4d70f3cfd347ceca5a029f2ffec37d4f124f61311"}, - {file = "coverage-6.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d548edacbf16a8276af13063a2b0669d58bbcfca7c55a255f84aac2870786a61"}, - {file = "coverage-6.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:033ebec282793bd9eb988d0271c211e58442c31077976c19c442e24d827d356f"}, - {file = "coverage-6.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:742fb8b43835078dd7496c3c25a1ec8d15351df49fb0037bffb4754291ef30ce"}, - {file = "coverage-6.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d55fae115ef9f67934e9f1103c9ba826b4c690e4c5bcf94482b8b2398311bf9c"}, - {file = "coverage-6.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5cd698341626f3c77784858427bad0cdd54a713115b423d22ac83a28303d1d95"}, - {file = "coverage-6.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:62d382f7d77eeeaff14b30516b17bcbe80f645f5cf02bb755baac376591c653c"}, - {file = "coverage-6.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:016d7f5cf1c8c84f533a3c1f8f36126fbe00b2ec0ccca47cc5731c3723d327c6"}, - {file = "coverage-6.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:69432946f154c6add0e9ede03cc43b96e2ef2733110a77444823c053b1ff5166"}, - {file = "coverage-6.4-cp38-cp38-win32.whl", hash = "sha256:83bd142cdec5e4a5c4ca1d4ff6fa807d28460f9db919f9f6a31babaaa8b88426"}, - {file = "coverage-6.4-cp38-cp38-win_amd64.whl", hash = "sha256:4002f9e8c1f286e986fe96ec58742b93484195defc01d5cc7809b8f7acb5ece3"}, - {file = "coverage-6.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e4f52c272fdc82e7c65ff3f17a7179bc5f710ebc8ce8a5cadac81215e8326740"}, - {file = "coverage-6.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b5578efe4038be02d76c344007b13119b2b20acd009a88dde8adec2de4f630b5"}, - {file = "coverage-6.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8099ea680201c2221f8468c372198ceba9338a5fec0e940111962b03b3f716a"}, - {file = "coverage-6.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a00441f5ea4504f5abbc047589d09e0dc33eb447dc45a1a527c8b74bfdd32c65"}, - {file = "coverage-6.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e76bd16f0e31bc2b07e0fb1379551fcd40daf8cdf7e24f31a29e442878a827c"}, - {file = "coverage-6.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8d2e80dd3438e93b19e1223a9850fa65425e77f2607a364b6fd134fcd52dc9df"}, - {file = "coverage-6.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:341e9c2008c481c5c72d0e0dbf64980a4b2238631a7f9780b0fe2e95755fb018"}, - {file = "coverage-6.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:21e6686a95025927775ac501e74f5940cdf6fe052292f3a3f7349b0abae6d00f"}, - {file = "coverage-6.4-cp39-cp39-win32.whl", hash = "sha256:968ed5407f9460bd5a591cefd1388cc00a8f5099de9e76234655ae48cfdbe2c3"}, - {file = "coverage-6.4-cp39-cp39-win_amd64.whl", hash = "sha256:e35217031e4b534b09f9b9a5841b9344a30a6357627761d4218818b865d45055"}, - {file = "coverage-6.4-pp36.pp37.pp38-none-any.whl", hash = "sha256:e637ae0b7b481905358624ef2e81d7fb0b1af55f5ff99f9ba05442a444b11e45"}, - {file = "coverage-6.4.tar.gz", hash = "sha256:727dafd7f67a6e1cad808dc884bd9c5a2f6ef1f8f6d2f22b37b96cb0080d4f49"}, + {file = "coverage-6.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f1d5aa2703e1dab4ae6cf416eb0095304f49d004c39e9db1d86f57924f43006b"}, + {file = "coverage-6.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4ce1b258493cbf8aec43e9b50d89982346b98e9ffdfaae8ae5793bc112fb0068"}, + {file = "coverage-6.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:83c4e737f60c6936460c5be330d296dd5b48b3963f48634c53b3f7deb0f34ec4"}, + {file = "coverage-6.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:84e65ef149028516c6d64461b95a8dbcfce95cfd5b9eb634320596173332ea84"}, + {file = "coverage-6.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f69718750eaae75efe506406c490d6fc5a6161d047206cc63ce25527e8a3adad"}, + {file = "coverage-6.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e57816f8ffe46b1df8f12e1b348f06d164fd5219beba7d9433ba79608ef011cc"}, + {file = "coverage-6.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:01c5615d13f3dd3aa8543afc069e5319cfa0c7d712f6e04b920431e5c564a749"}, + {file = "coverage-6.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:75ab269400706fab15981fd4bd5080c56bd5cc07c3bccb86aab5e1d5a88dc8f4"}, + {file = "coverage-6.4.1-cp310-cp310-win32.whl", hash = "sha256:a7f3049243783df2e6cc6deafc49ea123522b59f464831476d3d1448e30d72df"}, + {file = "coverage-6.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:ee2ddcac99b2d2aec413e36d7a429ae9ebcadf912946b13ffa88e7d4c9b712d6"}, + {file = "coverage-6.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fb73e0011b8793c053bfa85e53129ba5f0250fdc0392c1591fd35d915ec75c46"}, + {file = "coverage-6.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:106c16dfe494de3193ec55cac9640dd039b66e196e4641fa8ac396181578b982"}, + {file = "coverage-6.4.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:87f4f3df85aa39da00fd3ec4b5abeb7407e82b68c7c5ad181308b0e2526da5d4"}, + {file = "coverage-6.4.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:961e2fb0680b4f5ad63234e0bf55dfb90d302740ae9c7ed0120677a94a1590cb"}, + {file = "coverage-6.4.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:cec3a0f75c8f1031825e19cd86ee787e87cf03e4fd2865c79c057092e69e3a3b"}, + {file = "coverage-6.4.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:129cd05ba6f0d08a766d942a9ed4b29283aff7b2cccf5b7ce279d50796860bb3"}, + {file = "coverage-6.4.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:bf5601c33213d3cb19d17a796f8a14a9eaa5e87629a53979a5981e3e3ae166f6"}, + {file = "coverage-6.4.1-cp37-cp37m-win32.whl", hash = "sha256:269eaa2c20a13a5bf17558d4dc91a8d078c4fa1872f25303dddcbba3a813085e"}, + {file = "coverage-6.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:f02cbbf8119db68455b9d763f2f8737bb7db7e43720afa07d8eb1604e5c5ae28"}, + {file = "coverage-6.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ffa9297c3a453fba4717d06df579af42ab9a28022444cae7fa605af4df612d54"}, + {file = "coverage-6.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:145f296d00441ca703a659e8f3eb48ae39fb083baba2d7ce4482fb2723e050d9"}, + {file = "coverage-6.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d44996140af8b84284e5e7d398e589574b376fb4de8ccd28d82ad8e3bea13"}, + {file = "coverage-6.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2bd9a6fc18aab8d2e18f89b7ff91c0f34ff4d5e0ba0b33e989b3cd4194c81fd9"}, + {file = "coverage-6.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3384f2a3652cef289e38100f2d037956194a837221edd520a7ee5b42d00cc605"}, + {file = "coverage-6.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9b3e07152b4563722be523e8cd0b209e0d1a373022cfbde395ebb6575bf6790d"}, + {file = "coverage-6.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1480ff858b4113db2718848d7b2d1b75bc79895a9c22e76a221b9d8d62496428"}, + {file = "coverage-6.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:865d69ae811a392f4d06bde506d531f6a28a00af36f5c8649684a9e5e4a85c83"}, + {file = "coverage-6.4.1-cp38-cp38-win32.whl", hash = "sha256:664a47ce62fe4bef9e2d2c430306e1428ecea207ffd68649e3b942fa8ea83b0b"}, + {file = "coverage-6.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:26dff09fb0d82693ba9e6231248641d60ba606150d02ed45110f9ec26404ed1c"}, + {file = "coverage-6.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d9c80df769f5ec05ad21ea34be7458d1dc51ff1fb4b2219e77fe24edf462d6df"}, + {file = "coverage-6.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:39ee53946bf009788108b4dd2894bf1349b4e0ca18c2016ffa7d26ce46b8f10d"}, + {file = "coverage-6.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5b66caa62922531059bc5ac04f836860412f7f88d38a476eda0a6f11d4724f4"}, + {file = "coverage-6.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd180ed867e289964404051a958f7cccabdeed423f91a899829264bb7974d3d3"}, + {file = "coverage-6.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84631e81dd053e8a0d4967cedab6db94345f1c36107c71698f746cb2636c63e3"}, + {file = "coverage-6.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8c08da0bd238f2970230c2a0d28ff0e99961598cb2e810245d7fc5afcf1254e8"}, + {file = "coverage-6.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d42c549a8f41dc103a8004b9f0c433e2086add8a719da00e246e17cbe4056f72"}, + {file = "coverage-6.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:309ce4a522ed5fca432af4ebe0f32b21d6d7ccbb0f5fcc99290e71feba67c264"}, + {file = "coverage-6.4.1-cp39-cp39-win32.whl", hash = "sha256:fdb6f7bd51c2d1714cea40718f6149ad9be6a2ee7d93b19e9f00934c0f2a74d9"}, + {file = "coverage-6.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:342d4aefd1c3e7f620a13f4fe563154d808b69cccef415415aece4c786665397"}, + {file = "coverage-6.4.1-pp36.pp37.pp38-none-any.whl", hash = "sha256:4803e7ccf93230accb928f3a68f00ffa80a88213af98ed338a57ad021ef06815"}, + {file = "coverage-6.4.1.tar.gz", hash = "sha256:4321f075095a096e70aff1d002030ee612b65a205a0a0f5b815280d5dc58100c"}, ] cryptography = [ {file = "cryptography-37.0.2-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:ef15c2df7656763b4ff20a9bc4381d8352e6640cfeb95c2972c38ef508e75181"}, @@ -1875,29 +1875,29 @@ mccabe = [ {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, ] mypy = [ - {file = "mypy-0.960-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3a3e525cd76c2c4f90f1449fd034ba21fcca68050ff7c8397bb7dd25dd8b8248"}, - {file = "mypy-0.960-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7a76dc4f91e92db119b1be293892df8379b08fd31795bb44e0ff84256d34c251"}, - {file = "mypy-0.960-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ffdad80a92c100d1b0fe3d3cf1a4724136029a29afe8566404c0146747114382"}, - {file = "mypy-0.960-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7d390248ec07fa344b9f365e6ed9d205bd0205e485c555bed37c4235c868e9d5"}, - {file = "mypy-0.960-cp310-cp310-win_amd64.whl", hash = "sha256:925aa84369a07846b7f3b8556ccade1f371aa554f2bd4fb31cb97a24b73b036e"}, - {file = "mypy-0.960-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:239d6b2242d6c7f5822163ee082ef7a28ee02e7ac86c35593ef923796826a385"}, - {file = "mypy-0.960-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f1ba54d440d4feee49d8768ea952137316d454b15301c44403db3f2cb51af024"}, - {file = "mypy-0.960-cp36-cp36m-win_amd64.whl", hash = "sha256:cb7752b24528c118a7403ee955b6a578bfcf5879d5ee91790667c8ea511d2085"}, - {file = "mypy-0.960-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:826a2917c275e2ee05b7c7b736c1e6549a35b7ea5a198ca457f8c2ebea2cbecf"}, - {file = "mypy-0.960-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3eabcbd2525f295da322dff8175258f3fc4c3eb53f6d1929644ef4d99b92e72d"}, - {file = "mypy-0.960-cp37-cp37m-win_amd64.whl", hash = "sha256:f47322796c412271f5aea48381a528a613f33e0a115452d03ae35d673e6064f8"}, - {file = "mypy-0.960-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2c7f8bb9619290836a4e167e2ef1f2cf14d70e0bc36c04441e41487456561409"}, - {file = "mypy-0.960-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:fbfb873cf2b8d8c3c513367febde932e061a5f73f762896826ba06391d932b2a"}, - {file = "mypy-0.960-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cc537885891382e08129d9862553b3d00d4be3eb15b8cae9e2466452f52b0117"}, - {file = "mypy-0.960-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:481f98c6b24383188c928f33dd2f0776690807e12e9989dd0419edd5c74aa53b"}, - {file = "mypy-0.960-cp38-cp38-win_amd64.whl", hash = "sha256:29dc94d9215c3eb80ac3c2ad29d0c22628accfb060348fd23d73abe3ace6c10d"}, - {file = "mypy-0.960-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:33d53a232bb79057f33332dbbb6393e68acbcb776d2f571ba4b1d50a2c8ba873"}, - {file = "mypy-0.960-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8d645e9e7f7a5da3ec3bbcc314ebb9bb22c7ce39e70367830eb3c08d0140b9ce"}, - {file = "mypy-0.960-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:85cf2b14d32b61db24ade8ac9ae7691bdfc572a403e3cb8537da936e74713275"}, - {file = "mypy-0.960-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a85a20b43fa69efc0b955eba1db435e2ffecb1ca695fe359768e0503b91ea89f"}, - {file = "mypy-0.960-cp39-cp39-win_amd64.whl", hash = "sha256:0ebfb3f414204b98c06791af37a3a96772203da60636e2897408517fcfeee7a8"}, - {file = "mypy-0.960-py3-none-any.whl", hash = "sha256:bfd4f6536bd384c27c392a8b8f790fd0ed5c0cf2f63fc2fed7bce56751d53026"}, - {file = "mypy-0.960.tar.gz", hash = "sha256:d4fccf04c1acf750babd74252e0f2db6bd2ac3aa8fe960797d9f3ef41cf2bfd4"}, + {file = "mypy-0.961-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:697540876638ce349b01b6786bc6094ccdaba88af446a9abb967293ce6eaa2b0"}, + {file = "mypy-0.961-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b117650592e1782819829605a193360a08aa99f1fc23d1d71e1a75a142dc7e15"}, + {file = "mypy-0.961-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bdd5ca340beffb8c44cb9dc26697628d1b88c6bddf5c2f6eb308c46f269bb6f3"}, + {file = "mypy-0.961-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3e09f1f983a71d0672bbc97ae33ee3709d10c779beb613febc36805a6e28bb4e"}, + {file = "mypy-0.961-cp310-cp310-win_amd64.whl", hash = "sha256:e999229b9f3198c0c880d5e269f9f8129c8862451ce53a011326cad38b9ccd24"}, + {file = "mypy-0.961-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b24be97351084b11582fef18d79004b3e4db572219deee0212078f7cf6352723"}, + {file = "mypy-0.961-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f4a21d01fc0ba4e31d82f0fff195682e29f9401a8bdb7173891070eb260aeb3b"}, + {file = "mypy-0.961-cp36-cp36m-win_amd64.whl", hash = "sha256:439c726a3b3da7ca84a0199a8ab444cd8896d95012c4a6c4a0d808e3147abf5d"}, + {file = "mypy-0.961-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5a0b53747f713f490affdceef835d8f0cb7285187a6a44c33821b6d1f46ed813"}, + {file = "mypy-0.961-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0e9f70df36405c25cc530a86eeda1e0867863d9471fe76d1273c783df3d35c2e"}, + {file = "mypy-0.961-cp37-cp37m-win_amd64.whl", hash = "sha256:b88f784e9e35dcaa075519096dc947a388319cb86811b6af621e3523980f1c8a"}, + {file = "mypy-0.961-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:d5aaf1edaa7692490f72bdb9fbd941fbf2e201713523bdb3f4038be0af8846c6"}, + {file = "mypy-0.961-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9f5f5a74085d9a81a1f9c78081d60a0040c3efb3f28e5c9912b900adf59a16e6"}, + {file = "mypy-0.961-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f4b794db44168a4fc886e3450201365c9526a522c46ba089b55e1f11c163750d"}, + {file = "mypy-0.961-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:64759a273d590040a592e0f4186539858c948302c653c2eac840c7a3cd29e51b"}, + {file = "mypy-0.961-cp38-cp38-win_amd64.whl", hash = "sha256:63e85a03770ebf403291ec50097954cc5caf2a9205c888ce3a61bd3f82e17569"}, + {file = "mypy-0.961-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5f1332964963d4832a94bebc10f13d3279be3ce8f6c64da563d6ee6e2eeda932"}, + {file = "mypy-0.961-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:006be38474216b833eca29ff6b73e143386f352e10e9c2fbe76aa8549e5554f5"}, + {file = "mypy-0.961-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9940e6916ed9371809b35b2154baf1f684acba935cd09928952310fbddaba648"}, + {file = "mypy-0.961-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a5ea0875a049de1b63b972456542f04643daf320d27dc592d7c3d9cd5d9bf950"}, + {file = "mypy-0.961-cp39-cp39-win_amd64.whl", hash = "sha256:1ece702f29270ec6af25db8cf6185c04c02311c6bb21a69f423d40e527b75c56"}, + {file = "mypy-0.961-py3-none-any.whl", hash = "sha256:03c6cc893e7563e7b2949b969e63f02c000b32502a1b4d1314cabe391aa87d66"}, + {file = "mypy-0.961.tar.gz", hash = "sha256:f730d56cb924d371c26b8eaddeea3cc07d78ff51c521c6d04899ac6904b75492"}, ] mypy-extensions = [ {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, @@ -2160,12 +2160,12 @@ recommonmark = [ {file = "recommonmark-0.7.1.tar.gz", hash = "sha256:bdb4db649f2222dcd8d2d844f0006b958d627f732415d399791ee436a3686d67"}, ] redis = [ - {file = "redis-4.3.1-py3-none-any.whl", hash = "sha256:84316970995a7adb907a56754d2b92d88fc2d252963dc5ac34c88f0f1a22c25d"}, - {file = "redis-4.3.1.tar.gz", hash = "sha256:94b617b4cd296e94991146f66fc5559756fbefe9493604f0312e4d3298ac63e9"}, + {file = "redis-4.3.3-py3-none-any.whl", hash = "sha256:f57f8df5d238a8ecf92f499b6b21467bfee6c13d89953c27edf1e2bc673622e7"}, + {file = "redis-4.3.3.tar.gz", hash = "sha256:2f7a57cf4af15cd543c4394bcbe2b9148db2606a37edba755368836e3a1d053e"}, ] requests = [ - {file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"}, - {file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"}, + {file = "requests-2.28.0-py3-none-any.whl", hash = "sha256:bc7861137fbce630f17b03d3ad02ad0bf978c844f3536d0edda6499dafce2b6f"}, + {file = "requests-2.28.0.tar.gz", hash = "sha256:d568723a7ebd25875d8d1eaf5dfa068cd2fc8194b2e483d7b1f7c81918dbec6b"}, ] responses = [ {file = "responses-0.21.0-py3-none-any.whl", hash = "sha256:2dcc863ba63963c0c3d9ee3fa9507cbe36b7d7b0fccb4f0bdfd9e96c539b1487"}, @@ -2235,42 +2235,42 @@ sphinxcontrib-serializinghtml = [ {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"}, ] sqlalchemy = [ - {file = "SQLAlchemy-1.4.36-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:81e53bd383c2c33de9d578bfcc243f559bd3801a0e57f2bcc9a943c790662e0c"}, - {file = "SQLAlchemy-1.4.36-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6e1fe00ee85c768807f2a139b83469c1e52a9ffd58a6eb51aa7aeb524325ab18"}, - {file = "SQLAlchemy-1.4.36-cp27-cp27m-win32.whl", hash = "sha256:d57ac32f8dc731fddeb6f5d1358b4ca5456e72594e664769f0a9163f13df2a31"}, - {file = "SQLAlchemy-1.4.36-cp27-cp27m-win_amd64.whl", hash = "sha256:fca8322e04b2dde722fcb0558682740eebd3bd239bea7a0d0febbc190e99dc15"}, - {file = "SQLAlchemy-1.4.36-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:53d2d9ee93970c969bc4e3c78b1277d7129554642f6ffea039c282c7dc4577bc"}, - {file = "SQLAlchemy-1.4.36-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:f0394a3acfb8925db178f7728adb38c027ed7e303665b225906bfa8099dc1ce8"}, - {file = "SQLAlchemy-1.4.36-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09c606d8238feae2f360b8742ffbe67741937eb0a05b57f536948d198a3def96"}, - {file = "SQLAlchemy-1.4.36-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8d07fe2de0325d06e7e73281e9a9b5e259fbd7cbfbe398a0433cbb0082ad8fa7"}, - {file = "SQLAlchemy-1.4.36-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5041474dcab7973baa91ec1f3112049a9dd4652898d6a95a6a895ff5c58beb6b"}, - {file = "SQLAlchemy-1.4.36-cp310-cp310-win32.whl", hash = "sha256:be094460930087e50fd08297db9d7aadaed8408ad896baf758e9190c335632da"}, - {file = "SQLAlchemy-1.4.36-cp310-cp310-win_amd64.whl", hash = "sha256:64d796e9af522162f7f2bf7a3c5531a0a550764c426782797bbeed809d0646c5"}, - {file = "SQLAlchemy-1.4.36-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:a0ae3aa2e86a4613f2d4c49eb7da23da536e6ce80b2bfd60bbb2f55fc02b0b32"}, - {file = "SQLAlchemy-1.4.36-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d50cb71c1dbed70646d521a0975fb0f92b7c3f84c61fa59e07be23a1aaeecfc"}, - {file = "SQLAlchemy-1.4.36-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:16abf35af37a3d5af92725fc9ec507dd9e9183d261c2069b6606d60981ed1c6e"}, - {file = "SQLAlchemy-1.4.36-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5864a83bd345871ad9699ce466388f836db7572003d67d9392a71998092210e3"}, - {file = "SQLAlchemy-1.4.36-cp36-cp36m-win32.whl", hash = "sha256:fbf8c09fe9728168f8cc1b40c239eab10baf9c422c18be7f53213d70434dea43"}, - {file = "SQLAlchemy-1.4.36-cp36-cp36m-win_amd64.whl", hash = "sha256:6e859fa96605027bd50d8e966db1c4e1b03e7b3267abbc4b89ae658c99393c58"}, - {file = "SQLAlchemy-1.4.36-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:166a3887ec355f7d2f12738f7fa25dc8ac541867147a255f790f2f41f614cb44"}, - {file = "SQLAlchemy-1.4.36-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e885548da361aa3f8a9433db4cfb335b2107e533bf314359ae3952821d84b3e"}, - {file = "SQLAlchemy-1.4.36-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5c90ef955d429966d84326d772eb34333178737ebb669845f1d529eb00c75e72"}, - {file = "SQLAlchemy-1.4.36-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a052bd9f53004f8993c624c452dfad8ec600f572dd0ed0445fbe64b22f5570e"}, - {file = "SQLAlchemy-1.4.36-cp37-cp37m-win32.whl", hash = "sha256:dce3468bf1fc12374a1a732c9efd146ce034f91bb0482b602a9311cb6166a920"}, - {file = "SQLAlchemy-1.4.36-cp37-cp37m-win_amd64.whl", hash = "sha256:6cb4c4f57a20710cea277edf720d249d514e587f796b75785ad2c25e1c0fed26"}, - {file = "SQLAlchemy-1.4.36-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:e74ce103b81c375c3853b436297952ef8d7863d801dcffb6728d01544e5191b5"}, - {file = "SQLAlchemy-1.4.36-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b20c4178ead9bc398be479428568ff31b6c296eb22e75776273781a6551973f"}, - {file = "SQLAlchemy-1.4.36-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:af2587ae11400157753115612d6c6ad255143efba791406ad8a0cbcccf2edcb3"}, - {file = "SQLAlchemy-1.4.36-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83cf3077712be9f65c9aaa0b5bc47bc1a44789fd45053e2e3ecd59ff17c63fe9"}, - {file = "SQLAlchemy-1.4.36-cp38-cp38-win32.whl", hash = "sha256:ce20f5da141f8af26c123ebaa1b7771835ca6c161225ce728962a79054f528c3"}, - {file = "SQLAlchemy-1.4.36-cp38-cp38-win_amd64.whl", hash = "sha256:316c7e5304dda3e3ad711569ac5d02698bbc71299b168ac56a7076b86259f7ea"}, - {file = "SQLAlchemy-1.4.36-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:f522214f6749bc073262529c056f7dfd660f3b5ec4180c5354d985eb7219801e"}, - {file = "SQLAlchemy-1.4.36-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ecac4db8c1aa4a269f5829df7e706639a24b780d2ac46b3e485cbbd27ec0028"}, - {file = "SQLAlchemy-1.4.36-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b3db741beaa983d4cbf9087558620e7787106319f7e63a066990a70657dd6b35"}, - {file = "SQLAlchemy-1.4.36-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ec89bf98cc6a0f5d1e28e3ad28e9be6f3b4bdbd521a4053c7ae8d5e1289a8a1"}, - {file = "SQLAlchemy-1.4.36-cp39-cp39-win32.whl", hash = "sha256:e12532c4d3f614678623da5d852f038ace1f01869b89f003ed6fe8c793f0c6a3"}, - {file = "SQLAlchemy-1.4.36-cp39-cp39-win_amd64.whl", hash = "sha256:cb441ca461bf97d00877b607f132772644b623518b39ced54da433215adce691"}, - {file = "SQLAlchemy-1.4.36.tar.gz", hash = "sha256:64678ac321d64a45901ef2e24725ec5e783f1f4a588305e196431447e7ace243"}, + {file = "SQLAlchemy-1.4.37-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:d9050b0c4a7f5538650c74aaba5c80cd64450e41c206f43ea6d194ae6d060ff9"}, + {file = "SQLAlchemy-1.4.37-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:b4c92823889cf9846b972ee6db30c0e3a92c0ddfc76c6060a6cda467aa5fb694"}, + {file = "SQLAlchemy-1.4.37-cp27-cp27m-win32.whl", hash = "sha256:b55932fd0e81b43f4aff397c8ad0b3c038f540af37930423ab8f47a20b117e4c"}, + {file = "SQLAlchemy-1.4.37-cp27-cp27m-win_amd64.whl", hash = "sha256:4a17c1a1152ca4c29d992714aa9df3054da3af1598e02134f2e7314a32ef69d8"}, + {file = "SQLAlchemy-1.4.37-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:ffe487570f47536b96eff5ef2b84034a8ba4e19aab5ab7647e677d94a119ea55"}, + {file = "SQLAlchemy-1.4.37-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:78363f400fbda80f866e8e91d37d36fe6313ff847ded08674e272873c1377ea5"}, + {file = "SQLAlchemy-1.4.37-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ee34c85cbda7779d66abac392c306ec78c13f5c73a1f01b8b767916d4895d23"}, + {file = "SQLAlchemy-1.4.37-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8b38e088659b30c2ca0af63e5d139fad1779a7925d75075a08717a21c406c0f6"}, + {file = "SQLAlchemy-1.4.37-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6629c79967a6c92e33fad811599adf9bc5cee6e504a1027bbf9cc1b6fb2d276d"}, + {file = "SQLAlchemy-1.4.37-cp310-cp310-win32.whl", hash = "sha256:2aac2a685feb9882d09f457f4e5586c885d578af4e97a2b759e91e8c457cbce5"}, + {file = "SQLAlchemy-1.4.37-cp310-cp310-win_amd64.whl", hash = "sha256:7a44683cf97744a405103ef8fdd31199e9d7fc41b4a67e9044523b29541662b0"}, + {file = "SQLAlchemy-1.4.37-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:cffc67cdd07f0e109a1fc83e333972ae423ea5ad414585b63275b66b870ea62b"}, + {file = "SQLAlchemy-1.4.37-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:17417327b87a0f703c9a20180f75e953315207d048159aff51822052f3e33e69"}, + {file = "SQLAlchemy-1.4.37-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:aaa0e90e527066409c2ea5676282cf4afb4a40bb9dce0f56c8ec2768bff22a6e"}, + {file = "SQLAlchemy-1.4.37-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c1d9fb3931e27d59166bb5c4dcc911400fee51082cfba66ceb19ac954ade068"}, + {file = "SQLAlchemy-1.4.37-cp36-cp36m-win32.whl", hash = "sha256:0e7fd52e48e933771f177c2a1a484b06ea03774fc7741651ebdf19985a34037c"}, + {file = "SQLAlchemy-1.4.37-cp36-cp36m-win_amd64.whl", hash = "sha256:eec39a17bab3f69c44c9df4e0ed87c7306f2d2bf1eca3070af644927ec4199fa"}, + {file = "SQLAlchemy-1.4.37-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:caca6acf3f90893d7712ae2c6616ecfeac3581b4cc677c928a330ce6fbad4319"}, + {file = "SQLAlchemy-1.4.37-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50c8eaf44c3fed5ba6758d375de25f163e46137c39fda3a72b9ee1d1bb327dfc"}, + {file = "SQLAlchemy-1.4.37-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:139c50b9384e6d32a74fc4dcd0e9717f343ed38f95dbacf832c782c68e3862f3"}, + {file = "SQLAlchemy-1.4.37-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4c3b009c9220ae6e33f17b45f43fb46b9a1d281d76118405af13e26376f2e11"}, + {file = "SQLAlchemy-1.4.37-cp37-cp37m-win32.whl", hash = "sha256:9785d6f962d2c925aeb06a7539ac9d16608877da6aeaaf341984b3693ae80a02"}, + {file = "SQLAlchemy-1.4.37-cp37-cp37m-win_amd64.whl", hash = "sha256:3197441772dc3b1c6419f13304402f2418a18d7fe78000aa5a026e7100836739"}, + {file = "SQLAlchemy-1.4.37-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:3862a069a24f354145e01a76c7c720c263d62405fe5bed038c46a7ce900f5dd6"}, + {file = "SQLAlchemy-1.4.37-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e8706919829d455a9fa687c6bbd1b048e36fec3919a59f2d366247c2bfdbd9c"}, + {file = "SQLAlchemy-1.4.37-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:06ec11a5e6a4b6428167d3ce33b5bd455c020c867dabe3e6951fa98836e0741d"}, + {file = "SQLAlchemy-1.4.37-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d58f2d9d1a4b1459e8956a0153a4119da80f54ee5a9ea623cd568e99459a3ef1"}, + {file = "SQLAlchemy-1.4.37-cp38-cp38-win32.whl", hash = "sha256:d6927c9e3965b194acf75c8e0fb270b4d54512db171f65faae15ef418721996e"}, + {file = "SQLAlchemy-1.4.37-cp38-cp38-win_amd64.whl", hash = "sha256:a91d0668cada27352432f15b92ac3d43e34d8f30973fa8b86f5e9fddee928f3b"}, + {file = "SQLAlchemy-1.4.37-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:f9940528bf9c4df9e3c3872d23078b6b2da6431c19565637c09f1b88a427a684"}, + {file = "SQLAlchemy-1.4.37-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29a742c29fea12259f1d2a9ee2eb7fe4694a85d904a4ac66d15e01177b17ad7f"}, + {file = "SQLAlchemy-1.4.37-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7e579d6e281cc937bdb59917017ab98e618502067e04efb1d24ac168925e1d2a"}, + {file = "SQLAlchemy-1.4.37-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a940c551cfbd2e1e646ceea2777944425f5c3edff914bc808fe734d9e66f8d71"}, + {file = "SQLAlchemy-1.4.37-cp39-cp39-win32.whl", hash = "sha256:5e4e517ce72fad35cce364a01aff165f524449e9c959f1837dc71088afa2824c"}, + {file = "SQLAlchemy-1.4.37-cp39-cp39-win_amd64.whl", hash = "sha256:c37885f83b59e248bebe2b35beabfbea398cb40960cdc6d3a76eac863d4e1938"}, + {file = "SQLAlchemy-1.4.37.tar.gz", hash = "sha256:3688f92c62db6c5df268e2264891078f17ecb91e3141b400f2e28d0f75796dea"}, ] staticmap = [ {file = "staticmap-0.5.5.tar.gz", hash = "sha256:007c507b4d42e00eaba179649753f2f8d69d4ece3028736e18d9e86493044387"}, @@ -2292,8 +2292,8 @@ tomli = [ {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] trio = [ - {file = "trio-0.20.0-py3-none-any.whl", hash = "sha256:fb2d48e4eab0dfb786a472cd514aaadc71e3445b203bc300bad93daa75d77c1a"}, - {file = "trio-0.20.0.tar.gz", hash = "sha256:670a52d3115d0e879e1ac838a4eb999af32f858163e3a704fe4839de2a676070"}, + {file = "trio-0.21.0-py3-none-any.whl", hash = "sha256:4dc0bf9d5cc78767fc4516325b6d80cc0968705a31d0eec2ecd7cdda466265b0"}, + {file = "trio-0.21.0.tar.gz", hash = "sha256:523f39b7b69eef73501cebfe1aafd400a9aad5b03543a0eded52952488ff1c13"}, ] trio-websocket = [ {file = "trio-websocket-0.9.2.tar.gz", hash = "sha256:a3d34de8fac26023eee701ed1e7bf4da9a8326b61a62934ec9e53b64970fd8fe"}, @@ -2326,16 +2326,16 @@ typed-ast = [ {file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"}, ] types-freezegun = [ - {file = "types-freezegun-1.1.9.tar.gz", hash = "sha256:6f05108d468baecadf999873bd37e57b25ceb35d35d3f83e7a742f25d6fe8b0e"}, - {file = "types_freezegun-1.1.9-py3-none-any.whl", hash = "sha256:fe1dd73372d96358dcb93e3aeb66d39f6ac63749e0724f13554cc145e2120efe"}, + {file = "types-freezegun-1.1.10.tar.gz", hash = "sha256:cb3a2d2eee950eacbaac0673ab50499823365ceb8c655babb1544a41446409ec"}, + {file = "types_freezegun-1.1.10-py3-none-any.whl", hash = "sha256:fadebe72213e0674036153366205038e1f95c8ca96deb4ef9b71ddc15413543e"}, ] types-pytz = [ {file = "types-pytz-2021.3.8.tar.gz", hash = "sha256:41253a3a2bf028b6a3f17b58749a692d955af0f74e975de94f6f4d2d3cd01dbd"}, {file = "types_pytz-2021.3.8-py3-none-any.whl", hash = "sha256:aef4a917ab28c585d3f474bfce4f4b44b91e95d9d47d4de29dd845e0db8e3910"}, ] types-requests = [ - {file = "types-requests-2.27.29.tar.gz", hash = "sha256:fb453b3a76a48eca66381cea8004feaaea12835e838196f5c7ac87c75c5c19ef"}, - {file = "types_requests-2.27.29-py3-none-any.whl", hash = "sha256:014f4f82db7b96c41feea9adaea30e68cd64c230eeab34b70c29bebb26ec74ac"}, + {file = "types-requests-2.27.30.tar.gz", hash = "sha256:ca8d7cc549c3d10dbcb3c69c1b53e3ffd1270089c1001a65c1e9e1017eb5e704"}, + {file = "types_requests-2.27.30-py3-none-any.whl", hash = "sha256:b9b6cd0a6e5d500e56419b79f44ec96f316e9375ff6c8ee566c39d25e9612621"}, ] types-urllib3 = [ {file = "types-urllib3-1.26.15.tar.gz", hash = "sha256:c89283541ef92e344b7f59f83ea9b5a295b16366ceee3f25ecfc5593c79f794e"}, diff --git a/pyproject.toml b/pyproject.toml index ef2b778a..1dcfdfff 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,14 +38,14 @@ python-forecastio = "^1.4" pytz = "^2022.1" shortuuid = "^1.0.9" staticmap = "^0.5.4" -SQLAlchemy = "1.4.36" +SQLAlchemy = "1.4.37" pyOpenSSL = "^22.0" ua-parser = "^0.10.0" [tool.poetry.dev-dependencies] black = "^22.3" freezegun = "^1.2" -mypy = "^0.960" +mypy = "^0.961" pytest = "^7.1" pytest-black = "^0.3.12" pytest-cov = "^3.0" From 5874933643e4f3e4efd0b3974cc82fb4fad8c90d Mon Sep 17 00:00:00 2001 From: Sam Date: Sat, 11 Jun 2022 11:33:08 +0200 Subject: [PATCH 2/3] API - fix typo --- .../tests/workouts/test_workouts_api_0_get.py | 6 ++-- .../workouts/test_workouts_api_1_post.py | 22 +++++++-------- .../workouts/test_workouts_api_2_patch.py | 28 +++++++++---------- .../workouts/test_workouts_api_3_delete.py | 18 ++++++------ fittrackee/tests/workouts/utils.py | 2 +- fittrackee/workouts/workouts.py | 20 ++++++------- 6 files changed, 48 insertions(+), 48 deletions(-) diff --git a/fittrackee/tests/workouts/test_workouts_api_0_get.py b/fittrackee/tests/workouts/test_workouts_api_0_get.py index beba7c44..cafcb620 100644 --- a/fittrackee/tests/workouts/test_workouts_api_0_get.py +++ b/fittrackee/tests/workouts/test_workouts_api_0_get.py @@ -969,7 +969,7 @@ class TestGetWorkoutsWithFiltersAndPagination(ApiTestCaseMixin): class TestGetWorkout(ApiTestCaseMixin): - def test_it_gets_an_workout( + def test_it_gets_a_workout( self, app: Flask, user_1: User, @@ -1105,7 +1105,7 @@ class TestGetWorkout(ApiTestCaseMixin): response, f'no gpx file for this workout (id: {workout_short_id})' ) - def test_it_returns_500_on_getting_gpx_if_an_workout_has_invalid_gpx_pathname( # noqa + def test_it_returns_500_on_getting_gpx_if_a_workout_has_invalid_gpx_pathname( # noqa self, app: Flask, user_1: User, @@ -1125,7 +1125,7 @@ class TestGetWorkout(ApiTestCaseMixin): data = self.assert_500(response) assert 'data' not in data - def test_it_returns_500_on_getting_chart_data_if_an_workout_has_invalid_gpx_pathname( # noqa + def test_it_returns_500_on_getting_chart_data_if_a_workout_has_invalid_gpx_pathname( # noqa self, app: Flask, user_1: User, diff --git a/fittrackee/tests/workouts/test_workouts_api_1_post.py b/fittrackee/tests/workouts/test_workouts_api_1_post.py index f6ff5cac..5e56f003 100644 --- a/fittrackee/tests/workouts/test_workouts_api_1_post.py +++ b/fittrackee/tests/workouts/test_workouts_api_1_post.py @@ -222,7 +222,7 @@ class TestPostWorkoutWithGpx(ApiTestCaseMixin, CallArgsMixin): self.assert_401(response) - def test_it_adds_an_workout_with_gpx_file( + def test_it_adds_a_workout_with_gpx_file( self, app: Flask, user_1: User, sport_1_cycling: Sport, gpx_file: str ) -> None: client, auth_token = self.get_test_client_and_auth_token( @@ -248,7 +248,7 @@ class TestPostWorkoutWithGpx(ApiTestCaseMixin, CallArgsMixin): assert 'just a workout' == data['data']['workouts'][0]['title'] assert_workout_data_with_gpx(data) - def test_it_adds_an_workout_with_gpx_without_name( + def test_it_adds_a_workout_with_gpx_without_name( self, app: Flask, user_1: User, @@ -281,7 +281,7 @@ class TestPostWorkoutWithGpx(ApiTestCaseMixin, CallArgsMixin): ) assert_workout_data_with_gpx(data) - def test_it_adds_an_workout_with_gpx_without_name_timezone( + def test_it_adds_a_workout_with_gpx_without_name_timezone( self, app: Flask, user_1: User, @@ -606,7 +606,7 @@ class TestPostWorkoutWithoutGpx(ApiTestCaseMixin): self.assert_401(response) - def test_it_adds_an_workout_without_gpx( + def test_it_adds_a_workout_without_gpx( self, app: Flask, user_1: User, sport_1_cycling: Sport ) -> None: client, auth_token = self.get_test_client_and_auth_token( @@ -953,12 +953,12 @@ class TestPostAndGetWorkoutWithGpx(ApiTestCaseMixin): self.assert_500(response) - def test_it_gets_an_workout_created_with_gpx( + def test_it_gets_a_workout_created_with_gpx( self, app: Flask, user_1: User, sport_1_cycling: Sport, gpx_file: str ) -> None: return self.workout_assertion(app, user_1, gpx_file, False) - def test_it_gets_an_workout_created_with_gpx_with_segments( + def test_it_gets_a_workout_created_with_gpx_with_segments( self, app: Flask, user_1: User, @@ -969,7 +969,7 @@ class TestPostAndGetWorkoutWithGpx(ApiTestCaseMixin): app, user_1, gpx_file_with_segments, True ) - def test_it_gets_chart_data_for_an_workout_created_with_gpx( + def test_it_gets_chart_data_for_a_workout_created_with_gpx( self, app: Flask, user_1: User, sport_1_cycling: Sport, gpx_file: str ) -> None: client, auth_token = self.get_test_client_and_auth_token( @@ -1000,7 +1000,7 @@ class TestPostAndGetWorkoutWithGpx(ApiTestCaseMixin): assert data['message'] == '' assert data['data']['chart_data'] != '' - def test_it_gets_segment_chart_data_for_an_workout_created_with_gpx( + def test_it_gets_segment_chart_data_for_a_workout_created_with_gpx( self, app: Flask, user_1: User, sport_1_cycling: Sport, gpx_file: str ) -> None: client, auth_token = self.get_test_client_and_auth_token( @@ -1125,7 +1125,7 @@ class TestPostAndGetWorkoutWithGpx(ApiTestCaseMixin): class TestPostAndGetWorkoutWithoutGpx(ApiTestCaseMixin): - def test_it_add_and_gets_an_workout_wo_gpx( + def test_it_add_and_gets_a_workout_wo_gpx( self, app: Flask, user_1: User, sport_1_cycling: Sport ) -> None: client, auth_token = self.get_test_client_and_auth_token( @@ -1158,7 +1158,7 @@ class TestPostAndGetWorkoutWithoutGpx(ApiTestCaseMixin): assert len(data['data']['workouts']) == 1 assert_workout_data_wo_gpx(data) - def test_it_adds_and_gets_an_workout_wo_gpx_notes( + def test_it_adds_and_gets_a_workout_wo_gpx_notes( self, app: Flask, user_1: User, sport_1_cycling: Sport ) -> None: client, auth_token = self.get_test_client_and_auth_token( @@ -1194,7 +1194,7 @@ class TestPostAndGetWorkoutWithoutGpx(ApiTestCaseMixin): class TestPostAndGetWorkoutUsingTimezones(ApiTestCaseMixin): - def test_it_add_and_gets_an_workout_wo_gpx_with_timezone( + def test_it_add_and_gets_a_workout_wo_gpx_with_timezone( self, app: Flask, user_1: User, sport_1_cycling: Sport ) -> None: user_1.timezone = 'Europe/Paris' diff --git a/fittrackee/tests/workouts/test_workouts_api_2_patch.py b/fittrackee/tests/workouts/test_workouts_api_2_patch.py index 63bd919f..cf3143bc 100644 --- a/fittrackee/tests/workouts/test_workouts_api_2_patch.py +++ b/fittrackee/tests/workouts/test_workouts_api_2_patch.py @@ -9,7 +9,7 @@ from fittrackee.users.models import User from fittrackee.workouts.models import Sport, Workout from ..mixins import ApiTestCaseMixin -from .utils import get_random_short_id, post_an_workout +from .utils import get_random_short_id, post_a_workout def assert_workout_data_with_gpx(data: Dict, sport_id: int) -> None: @@ -56,7 +56,7 @@ def assert_workout_data_with_gpx(data: Dict, sport_id: int) -> None: class TestEditWorkoutWithGpx(ApiTestCaseMixin): - def test_it_updates_title_for_an_workout_with_gpx( + def test_it_updates_title_for_a_workout_with_gpx( self, app: Flask, user_1: User, @@ -64,7 +64,7 @@ class TestEditWorkoutWithGpx(ApiTestCaseMixin): sport_2_running: Sport, gpx_file: str, ) -> None: - token, workout_short_id = post_an_workout(app, gpx_file) + token, workout_short_id = post_a_workout(app, gpx_file) client = app.test_client() response = client.patch( @@ -100,7 +100,7 @@ class TestEditWorkoutWithGpx(ApiTestCaseMixin): sport_2_running: Sport, gpx_file: str, ) -> None: - token, workout_short_id = post_an_workout(app, gpx_file) + token, workout_short_id = post_a_workout(app, gpx_file) client = app.test_client() response = client.patch( @@ -124,7 +124,7 @@ class TestEditWorkoutWithGpx(ApiTestCaseMixin): sport_2_running: Sport, gpx_file: str, ) -> None: - token, workout_short_id = post_an_workout( + token, workout_short_id = post_a_workout( app, gpx_file, notes=uuid4().hex ) client = app.test_client() @@ -142,7 +142,7 @@ class TestEditWorkoutWithGpx(ApiTestCaseMixin): assert len(data['data']['workouts']) == 1 assert data['data']['workouts'][0]['notes'] == '' - def test_it_raises_403_when_editing_an_workout_from_different_user( + def test_it_raises_403_when_editing_a_workout_from_different_user( self, app: Flask, user_1: User, @@ -151,7 +151,7 @@ class TestEditWorkoutWithGpx(ApiTestCaseMixin): sport_2_running: Sport, gpx_file: str, ) -> None: - _, workout_short_id = post_an_workout(app, gpx_file) + _, workout_short_id = post_a_workout(app, gpx_file) client, auth_token = self.get_test_client_and_auth_token( app, user_2.email ) @@ -173,7 +173,7 @@ class TestEditWorkoutWithGpx(ApiTestCaseMixin): sport_2_running: Sport, gpx_file: str, ) -> None: - token, workout_short_id = post_an_workout(app, gpx_file) + token, workout_short_id = post_a_workout(app, gpx_file) client = app.test_client() response = client.patch( @@ -194,7 +194,7 @@ class TestEditWorkoutWithGpx(ApiTestCaseMixin): def test_it_returns_400_if_payload_is_empty( self, app: Flask, user_1: User, sport_1_cycling: Sport, gpx_file: str ) -> None: - token, workout_short_id = post_an_workout(app, gpx_file) + token, workout_short_id = post_a_workout(app, gpx_file) client = app.test_client() response = client.patch( @@ -209,7 +209,7 @@ class TestEditWorkoutWithGpx(ApiTestCaseMixin): def test_it_raises_500_if_sport_does_not_exists( self, app: Flask, user_1: User, sport_1_cycling: Sport, gpx_file: str ) -> None: - token, workout_short_id = post_an_workout(app, gpx_file) + token, workout_short_id = post_a_workout(app, gpx_file) client = app.test_client() response = client.patch( @@ -223,7 +223,7 @@ class TestEditWorkoutWithGpx(ApiTestCaseMixin): class TestEditWorkoutWithoutGpx(ApiTestCaseMixin): - def test_it_updates_an_workout_wo_gpx( + def test_it_updates_a_workout_wo_gpx( self, app: Flask, user_1: User, @@ -364,7 +364,7 @@ class TestEditWorkoutWithoutGpx(ApiTestCaseMixin): assert len(data['data']['workouts']) == 1 assert data['data']['workouts'][0]['notes'] == '' - def test_returns_403_when_editing_an_workout_wo_gpx_from_different_user( + def test_returns_403_when_editing_a_workout_wo_gpx_from_different_user( self, app: Flask, user_1: User, @@ -393,7 +393,7 @@ class TestEditWorkoutWithoutGpx(ApiTestCaseMixin): self.assert_403(response) - def test_it_updates_an_workout_wo_gpx_with_timezone( + def test_it_updates_a_workout_wo_gpx_with_timezone( self, app: Flask, user_1_paris: User, @@ -468,7 +468,7 @@ class TestEditWorkoutWithoutGpx(ApiTestCaseMixin): assert records[3]['workout_date'] == 'Tue, 15 May 2018 13:05:00 GMT' assert records[3]['value'] == 8.0 - def test_it_updates_only_sport_and_distance_an_workout_wo_gpx( + def test_it_updates_only_sport_and_distance_a_workout_wo_gpx( self, app: Flask, user_1: User, diff --git a/fittrackee/tests/workouts/test_workouts_api_3_delete.py b/fittrackee/tests/workouts/test_workouts_api_3_delete.py index d6c9adfe..86c5684f 100644 --- a/fittrackee/tests/workouts/test_workouts_api_3_delete.py +++ b/fittrackee/tests/workouts/test_workouts_api_3_delete.py @@ -7,7 +7,7 @@ from fittrackee.users.models import User from fittrackee.workouts.models import Sport, Workout from ..mixins import ApiTestCaseMixin -from .utils import get_random_short_id, post_an_workout +from .utils import get_random_short_id, post_a_workout def get_gpx_filepath(workout_id: int) -> str: @@ -16,10 +16,10 @@ def get_gpx_filepath(workout_id: int) -> str: class TestDeleteWorkoutWithGpx(ApiTestCaseMixin): - def test_it_deletes_an_workout_with_gpx( + def test_it_deletes_a_workout_with_gpx( self, app: Flask, user_1: User, sport_1_cycling: Sport, gpx_file: str ) -> None: - token, workout_short_id = post_an_workout(app, gpx_file) + token, workout_short_id = post_a_workout(app, gpx_file) client = app.test_client() response = client.delete( @@ -29,7 +29,7 @@ class TestDeleteWorkoutWithGpx(ApiTestCaseMixin): assert response.status_code == 204 - def test_it_returns_403_when_deleting_an_workout_from_different_user( + def test_it_returns_403_when_deleting_a_workout_from_different_user( self, app: Flask, user_1: User, @@ -37,7 +37,7 @@ class TestDeleteWorkoutWithGpx(ApiTestCaseMixin): sport_1_cycling: Sport, gpx_file: str, ) -> None: - _, workout_short_id = post_an_workout(app, gpx_file) + _, workout_short_id = post_a_workout(app, gpx_file) client, auth_token = self.get_test_client_and_auth_token( app, user_2.email ) @@ -64,10 +64,10 @@ class TestDeleteWorkoutWithGpx(ApiTestCaseMixin): data = self.assert_404(response) assert 'not found' in data['status'] - def test_it_returns_500_when_deleting_an_workout_with_gpx_invalid_file( + def test_it_returns_500_when_deleting_a_workout_with_gpx_invalid_file( self, app: Flask, user_1: User, sport_1_cycling: Sport, gpx_file: str ) -> None: - token, workout_short_id = post_an_workout(app, gpx_file) + token, workout_short_id = post_a_workout(app, gpx_file) client = app.test_client() gpx_filepath = get_gpx_filepath(1) gpx_filepath = get_absolute_file_path(gpx_filepath) @@ -82,7 +82,7 @@ class TestDeleteWorkoutWithGpx(ApiTestCaseMixin): class TestDeleteWorkoutWithoutGpx(ApiTestCaseMixin): - def test_it_deletes_an_workout_wo_gpx( + def test_it_deletes_a_workout_wo_gpx( self, app: Flask, user_1: User, @@ -98,7 +98,7 @@ class TestDeleteWorkoutWithoutGpx(ApiTestCaseMixin): ) assert response.status_code == 204 - def test_it_returns_403_when_deleting_an_workout_from_different_user( + def test_it_returns_403_when_deleting_a_workout_from_different_user( self, app: Flask, user_1: User, diff --git a/fittrackee/tests/workouts/utils.py b/fittrackee/tests/workouts/utils.py index 7aaea2f6..43bc65a9 100644 --- a/fittrackee/tests/workouts/utils.py +++ b/fittrackee/tests/workouts/utils.py @@ -12,7 +12,7 @@ def get_random_short_id() -> str: return encode_uuid(uuid4()) -def post_an_workout( +def post_a_workout( app: Flask, gpx_file: str, notes: Optional[str] = None ) -> Tuple[str, str]: client = app.test_client() diff --git a/fittrackee/workouts/workouts.py b/fittrackee/workouts/workouts.py index d747c128..b5d90a32 100644 --- a/fittrackee/workouts/workouts.py +++ b/fittrackee/workouts/workouts.py @@ -303,7 +303,7 @@ def get_workout( auth_user: User, workout_short_id: str ) -> Union[Dict, HttpResponse]: """ - Get an workout + Get a workout **Example request**: @@ -405,7 +405,7 @@ def get_workout_data( data_type: str, segment_id: Optional[int] = None, ) -> Union[Dict, HttpResponse]: - """Get data from an workout gpx file""" + """Get data from a workout gpx file""" workout_uuid = decode_short_id(workout_short_id) workout = Workout.query.filter_by(uuid=workout_uuid).first() if not workout: @@ -467,7 +467,7 @@ def get_workout_gpx( auth_user: User, workout_short_id: str ) -> Union[Dict, HttpResponse]: """ - Get gpx file for an workout displayed on map with Leaflet + Get gpx file for a workout displayed on map with Leaflet **Example request**: @@ -517,7 +517,7 @@ def get_workout_chart_data( auth_user: User, workout_short_id: str ) -> Union[Dict, HttpResponse]: """ - Get chart data from an workout gpx file, to display it with Recharts + Get chart data from a workout gpx file, to display it with Recharts **Example request**: @@ -587,7 +587,7 @@ def get_segment_gpx( auth_user: User, workout_short_id: str, segment_id: int ) -> Union[Dict, HttpResponse]: """ - Get gpx file for an workout segment displayed on map with Leaflet + Get gpx file for a workout segment displayed on map with Leaflet **Example request**: @@ -639,7 +639,7 @@ def get_segment_chart_data( auth_user: User, workout_short_id: str, segment_id: int ) -> Union[Dict, HttpResponse]: """ - Get chart data from an workout gpx file, to display it with Recharts + Get chart data from a workout gpx file, to display it with Recharts **Example request**: @@ -851,7 +851,7 @@ def get_map_tile(s: str, z: str, x: str, y: str) -> Tuple[Response, int]: @authenticate def post_workout(auth_user: User) -> Union[Tuple[Dict, int], HttpResponse]: """ - Post an workout with a gpx file + Post a workout with a gpx file **Example request**: @@ -1021,7 +1021,7 @@ def post_workout_no_gpx( auth_user: User, ) -> Union[Tuple[Dict, int], HttpResponse]: """ - Post an workout without gpx file + Post a workout without gpx file **Example request**: @@ -1169,7 +1169,7 @@ def update_workout( auth_user: User, workout_short_id: str ) -> Union[Dict, HttpResponse]: """ - Update an workout + Update a workout **Example request**: @@ -1316,7 +1316,7 @@ def delete_workout( auth_user: User, workout_short_id: str ) -> Union[Tuple[Dict, int], HttpResponse]: """ - Delete an workout + Delete a workout **Example request**: From 4288c3c3871cfcc1f689bf87c209aca2cb400216 Mon Sep 17 00:00:00 2001 From: Sam Date: Sat, 11 Jun 2022 13:10:02 +0200 Subject: [PATCH 3/3] API - handle gpx files with offset --- .../tests/fixtures/fixtures_workouts.py | 114 ++++++++++++++++++ .../tests/workouts/test_utils/test_gpx.py | 16 ++- .../workouts/test_utils/test_workouts.py | 87 ++++++++++++- .../workouts/test_workouts_api_1_post.py | 64 ++++++++-- fittrackee/workouts/utils/gpx.py | 86 ++++++++----- fittrackee/workouts/utils/weather.py | 12 +- fittrackee/workouts/utils/workouts.py | 99 ++++++++------- fittrackee/workouts/workouts.py | 6 +- poetry.lock | 8 +- pyproject.toml | 2 +- 10 files changed, 392 insertions(+), 102 deletions(-) diff --git a/fittrackee/tests/fixtures/fixtures_workouts.py b/fittrackee/tests/fixtures/fixtures_workouts.py index 46402c61..e2e11b21 100644 --- a/fittrackee/tests/fixtures/fixtures_workouts.py +++ b/fittrackee/tests/fixtures/fixtures_workouts.py @@ -452,6 +452,120 @@ def gpx_file_wo_name() -> str: ) +@pytest.fixture() +def gpx_file_with_offset() -> str: + return ( + '' + '' # noqa + ' ' + ' ' + ' ' + ' ' + ' 998' + ' ' + ' ' + ' ' + ' 998' + ' ' + ' ' + ' ' + ' 994' + ' ' + ' ' + ' ' + ' 994' + ' ' + ' ' + ' ' + ' 994' + ' ' + ' ' + ' ' + ' 993' + ' ' + ' ' + ' ' + ' 992' + ' ' + ' ' + ' ' + ' 992' + ' ' + ' ' + ' ' + ' 987' + ' ' + ' ' + ' ' + ' 987' + ' ' + ' ' + ' ' + ' 987' + ' ' + ' ' + ' ' + ' 987' + ' ' + ' ' + ' ' + ' 986' + ' ' + ' ' + ' ' + ' 986' + ' ' + ' ' + ' ' + ' 986' + ' ' + ' ' + ' ' + ' 985' + ' ' + ' ' + ' ' + ' 980' + ' ' + ' ' + ' ' + ' 980' + ' ' + ' ' + ' ' + ' 980' + ' ' + ' ' + ' ' + ' 979' + ' ' + ' ' + ' ' + ' 981' + ' ' + ' ' + ' ' + ' 980' + ' ' + ' ' + ' ' + ' 979' + ' ' + ' ' + ' ' + ' 979' + ' ' + ' ' + ' ' + ' 975' + ' ' + ' ' + ' ' + ' ' + '' + ) + + @pytest.fixture() def gpx_file_wo_track() -> str: return ( diff --git a/fittrackee/tests/workouts/test_utils/test_gpx.py b/fittrackee/tests/workouts/test_utils/test_gpx.py index 06d655a0..167b8bba 100644 --- a/fittrackee/tests/workouts/test_utils/test_gpx.py +++ b/fittrackee/tests/workouts/test_utils/test_gpx.py @@ -2,7 +2,7 @@ from unittest.mock import call, patch import pytest from flask import Flask -from gpxpy.gpx import MovingData +from gpxpy.gpx import IGNORE_TOP_SPEED_PERCENTILES, MovingData from werkzeug.datastructures import FileStorage from fittrackee.users.models import User, UserSportPreference @@ -55,7 +55,12 @@ class TestStoppedSpeedThreshold: assert gpx_track_segment_mock.call_args_list[0] == call( stopped_speed_threshold=expected_threshold ) - gpx_track_segment_mock.assert_called_with(expected_threshold) + gpx_track_segment_mock.assert_called_with( + expected_threshold, # stopped_speed_threshold + False, # raw + IGNORE_TOP_SPEED_PERCENTILES, # speed_extreemes_percentiles + True, # ignore_nonstandard_distances + ) def test_it_calls_get_moving_data_with_threshold_depending_from_user_preference( # noqa self, @@ -85,4 +90,9 @@ class TestStoppedSpeedThreshold: assert gpx_track_segment_mock.call_args_list[0] == call( stopped_speed_threshold=expected_threshold ) - gpx_track_segment_mock.assert_called_with(expected_threshold) + gpx_track_segment_mock.assert_called_with( + expected_threshold, # stopped_speed_threshold + False, # raw + IGNORE_TOP_SPEED_PERCENTILES, # speed_extreemes_percentiles + True, # ignore_nonstandard_distances + ) diff --git a/fittrackee/tests/workouts/test_utils/test_workouts.py b/fittrackee/tests/workouts/test_utils/test_workouts.py index 0e325878..16a90145 100644 --- a/fittrackee/tests/workouts/test_utils/test_workouts.py +++ b/fittrackee/tests/workouts/test_utils/test_workouts.py @@ -1,9 +1,27 @@ +from datetime import datetime from statistics import mean -from typing import List +from typing import List, Union import pytest +import pytz +from gpxpy.gpxfield import SimpleTZ -from fittrackee.workouts.utils.workouts import get_average_speed +from fittrackee.workouts.utils.workouts import ( + get_average_speed, + get_workout_datetime, +) + +utc_datetime = datetime( + year=2022, month=6, day=11, hour=10, minute=23, second=00, tzinfo=pytz.utc +) +input_workout_dates = [ + utc_datetime, + utc_datetime.replace(tzinfo=None), + utc_datetime.replace(tzinfo=SimpleTZ('Z')), + utc_datetime.astimezone(pytz.timezone('Europe/Paris')), + utc_datetime.astimezone(pytz.timezone('America/Toronto')), + '2022-06-11 12:23:00', +] class TestWorkoutAverageSpeed: @@ -30,3 +48,68 @@ class TestWorkoutAverageSpeed: assert get_average_speed( nb_workouts, total_average_speed, workout_average_speed ) == mean(ave_speeds_list) + + +class TestWorkoutGetWorkoutDatetime: + @pytest.mark.parametrize('input_workout_date', input_workout_dates) + def test_it_returns_naive_datetime( + self, input_workout_date: Union[datetime, str] + ) -> None: + naive_workout_date, _ = get_workout_datetime( + workout_date=input_workout_date, user_timezone='Europe/Paris' + ) + + assert naive_workout_date == datetime( + year=2022, month=6, day=11, hour=10, minute=23, second=00 + ) + + def test_it_return_naive_datetime_when_no_user_timezone(self) -> None: + naive_workout_date, _ = get_workout_datetime( + workout_date='2022-06-11 12:23:00', user_timezone=None + ) + + assert naive_workout_date == datetime( + year=2022, month=6, day=11, hour=12, minute=23, second=00 + ) + + @pytest.mark.parametrize('input_workout_date', input_workout_dates) + def test_it_returns_datetime_with_user_timezone( + self, input_workout_date: Union[datetime, str] + ) -> None: + timezone = 'Europe/Paris' + + _, workout_date_with_tz = get_workout_datetime( + input_workout_date, user_timezone=timezone, with_timezone=True + ) + + assert workout_date_with_tz == datetime( + year=2022, + month=6, + day=11, + hour=10, + minute=23, + second=00, + tzinfo=pytz.utc, + ).astimezone(pytz.timezone(timezone)) + + def test_it_does_not_return_datetime_with_user_timezone_when_no_user_tz( + self, + ) -> None: + _, workout_date_with_tz = get_workout_datetime( + workout_date='2022-06-11 12:23:00', + user_timezone=None, + with_timezone=True, + ) + + assert workout_date_with_tz is None + + def test_it_does_not_return_datetime_with_user_timezone_when_with_timezone_to_false( # noqa + self, + ) -> None: + _, workout_date_with_tz = get_workout_datetime( + workout_date='2022-06-11 12:23:00', + user_timezone='Europe/Paris', + with_timezone=False, + ) + + assert workout_date_with_tz is None diff --git a/fittrackee/tests/workouts/test_workouts_api_1_post.py b/fittrackee/tests/workouts/test_workouts_api_1_post.py index 5e56f003..f75320ec 100644 --- a/fittrackee/tests/workouts/test_workouts_api_1_post.py +++ b/fittrackee/tests/workouts/test_workouts_api_1_post.py @@ -2,7 +2,7 @@ import json import os from datetime import datetime from io import BytesIO -from typing import Dict +from typing import Dict, Optional from unittest.mock import Mock import pytest @@ -92,9 +92,7 @@ def assert_workout_data_with_gpx_segments(data: Dict) -> None: assert data['data']['workouts'][0]['descent'] == 23.4 assert data['data']['workouts'][0]['distance'] == 0.3 assert data['data']['workouts'][0]['max_alt'] == 998.0 - assert ( - data['data']['workouts'][0]['max_speed'] is None - ) # not enough points + assert data['data']['workouts'][0]['max_speed'] == 5.25 assert data['data']['workouts'][0]['min_alt'] == 975.0 assert data['data']['workouts'][0]['moving'] == '0:03:55' assert data['data']['workouts'][0]['pauses'] == '0:00:15' @@ -114,7 +112,7 @@ def assert_workout_data_with_gpx_segments(data: Dict) -> None: assert segment['descent'] == 11.0 assert segment['distance'] == 0.113 assert segment['max_alt'] == 998.0 - assert segment['max_speed'] is None + assert segment['max_speed'] == 5.25 assert segment['min_alt'] == 987.0 assert segment['moving'] == '0:01:30' assert segment['pauses'] is None @@ -128,28 +126,33 @@ def assert_workout_data_with_gpx_segments(data: Dict) -> None: assert segment['descent'] == 12.4 assert segment['distance'] == 0.186 assert segment['max_alt'] == 987.0 - assert segment['max_speed'] is None + assert segment['max_speed'] == 5.12 assert segment['min_alt'] == 975.0 assert segment['moving'] == '0:02:25' assert segment['pauses'] is None records = data['data']['workouts'][0]['records'] - assert len(records) == 3 + assert len(records) == 4 assert records[0]['sport_id'] == 1 assert records[0]['workout_id'] == data['data']['workouts'][0]['id'] - assert records[0]['record_type'] == 'LD' + assert records[0]['record_type'] == 'MS' assert records[0]['workout_date'] == 'Tue, 13 Mar 2018 12:44:45 GMT' - assert records[0]['value'] == '0:03:55' + assert records[0]['value'] == 5.25 assert records[1]['sport_id'] == 1 assert records[1]['workout_id'] == data['data']['workouts'][0]['id'] - assert records[1]['record_type'] == 'FD' + assert records[1]['record_type'] == 'LD' assert records[1]['workout_date'] == 'Tue, 13 Mar 2018 12:44:45 GMT' - assert records[1]['value'] == 0.3 + assert records[1]['value'] == '0:03:55' assert records[2]['sport_id'] == 1 assert records[2]['workout_id'] == data['data']['workouts'][0]['id'] - assert records[2]['record_type'] == 'AS' + assert records[2]['record_type'] == 'FD' assert records[2]['workout_date'] == 'Tue, 13 Mar 2018 12:44:45 GMT' - assert records[2]['value'] == 4.59 + assert records[2]['value'] == 0.3 + assert records[3]['sport_id'] == 1 + assert records[3]['workout_id'] == data['data']['workouts'][0]['id'] + assert records[3]['record_type'] == 'AS' + assert records[3]['workout_date'] == 'Tue, 13 Mar 2018 12:44:45 GMT' + assert records[3]['value'] == 4.59 def assert_workout_data_wo_gpx(data: Dict) -> None: @@ -315,6 +318,41 @@ class TestPostWorkoutWithGpx(ApiTestCaseMixin, CallArgsMixin): ) assert_workout_data_with_gpx(data) + @pytest.mark.parametrize('input_user_timezone', [None, 'Europe/Paris']) + def test_it_adds_a_workout_with_gpx_with_offset( + self, + app: Flask, + user_1: User, + sport_1_cycling: Sport, + gpx_file_with_offset: str, + input_user_timezone: Optional[str], + ) -> None: + user_1.timezone = input_user_timezone + client, auth_token = self.get_test_client_and_auth_token( + app, user_1.email + ) + + response = client.post( + '/api/workouts', + data=dict( + file=( + BytesIO(str.encode(gpx_file_with_offset)), + 'example.gpx', + ), + data='{"sport_id": 1}', + ), + headers=dict( + content_type='multipart/form-data', + Authorization=f'Bearer {auth_token}', + ), + ) + + data = json.loads(response.data.decode()) + assert response.status_code == 201 + assert 'created' in data['status'] + assert len(data['data']['workouts']) == 1 + assert_workout_data_with_gpx(data) + @pytest.mark.parametrize( 'input_description,input_notes', [ diff --git a/fittrackee/workouts/utils/gpx.py b/fittrackee/workouts/utils/gpx.py index f549014f..367c1c3c 100644 --- a/fittrackee/workouts/utils/gpx.py +++ b/fittrackee/workouts/utils/gpx.py @@ -1,5 +1,5 @@ -from datetime import timedelta -from typing import Any, Dict, List, Optional, Tuple +from datetime import datetime, timedelta, timezone +from typing import Any, Dict, List, Optional, Tuple, Union import gpxpy.gpx @@ -16,9 +16,9 @@ def open_gpx_file(gpx_file: str) -> Optional[gpxpy.gpx.GPX]: def get_gpx_data( - parsed_gpx: gpxpy.gpx, + parsed_gpx: Union[gpxpy.gpx.GPX, gpxpy.gpx.GPXTrackSegment], max_speed: float, - start: int, + start: Union[datetime, None], stopped_time_between_seg: timedelta, stopped_speed_threshold: float, ) -> Dict: @@ -32,7 +32,8 @@ def get_gpx_data( duration = parsed_gpx.get_duration() gpx_data['duration'] = ( - timedelta(seconds=duration) + stopped_time_between_seg + timedelta(seconds=duration if duration else 0) + + stopped_time_between_seg ) ele = parsed_gpx.get_elevation_extremes() @@ -43,18 +44,24 @@ def get_gpx_data( gpx_data['uphill'] = hill.uphill gpx_data['downhill'] = hill.downhill - mv = parsed_gpx.get_moving_data( + moving_data = parsed_gpx.get_moving_data( stopped_speed_threshold=stopped_speed_threshold ) - gpx_data['moving_time'] = timedelta(seconds=mv.moving_time) - gpx_data['stop_time'] = ( - timedelta(seconds=mv.stopped_time) + stopped_time_between_seg - ) - distance = mv.moving_distance + mv.stopped_distance - gpx_data['distance'] = distance / 1000 + if moving_data: + gpx_data['moving_time'] = timedelta(seconds=moving_data.moving_time) + gpx_data['stop_time'] = ( + timedelta(seconds=moving_data.stopped_time) + + stopped_time_between_seg + ) + distance = moving_data.moving_distance + moving_data.stopped_distance + gpx_data['distance'] = distance / 1000 - average_speed = distance / mv.moving_time if mv.moving_time > 0 else 0 - gpx_data['average_speed'] = (average_speed / 1000) * 3600 + average_speed = ( + distance / moving_data.moving_time + if moving_data.moving_time > 0 + else 0 + ) + gpx_data['average_speed'] = (average_speed / 1000) * 3600 return gpx_data @@ -72,9 +79,9 @@ def get_gpx_info( if gpx is None: raise WorkoutGPXException('not found', 'No gpx file') - gpx_data = {'name': gpx.tracks[0].name, 'segments': []} - max_speed = 0 - start = 0 + gpx_data: Dict = {'name': gpx.tracks[0].name, 'segments': []} + max_speed = 0.0 + start: Optional[datetime] = None map_data = [] weather_data = [] segments_nb = len(gpx.tracks[0].segments) @@ -83,14 +90,15 @@ def get_gpx_info( stopped_time_between_seg = no_stopped_time for segment_idx, segment in enumerate(gpx.tracks[0].segments): - segment_start = 0 + segment_start: Optional[datetime] = None segment_points_nb = len(segment.points) for point_idx, point in enumerate(segment.points): if point_idx == 0: + segment_start = point.time # first gpx point => get weather - if start == 0: + if start is None: start = point.time - if update_weather_data: + if point.time and update_weather_data: weather_data.append(get_weather(point)) # if a previous segment exists, calculate stopped time between @@ -108,13 +116,19 @@ def get_gpx_info( if update_map_data: map_data.append([point.longitude, point.latitude]) - calculated_max_speed = segment.get_moving_data( + moving_data = segment.get_moving_data( stopped_speed_threshold=stopped_speed_threshold - ).max_speed - segment_max_speed = calculated_max_speed if calculated_max_speed else 0 + ) + if moving_data: + calculated_max_speed = moving_data.max_speed + segment_max_speed = ( + calculated_max_speed if calculated_max_speed else 0 + ) - if segment_max_speed > max_speed: - max_speed = segment_max_speed + if segment_max_speed > max_speed: + max_speed = segment_max_speed + else: + segment_max_speed = 0.0 segment_data = get_gpx_data( segment, @@ -137,12 +151,16 @@ def get_gpx_info( if update_map_data: bounds = gpx.get_bounds() - gpx_data['bounds'] = [ - bounds.min_latitude, - bounds.min_longitude, - bounds.max_latitude, - bounds.max_longitude, - ] + gpx_data['bounds'] = ( + [ + bounds.min_latitude, + bounds.min_longitude, + bounds.max_latitude, + bounds.max_longitude, + ] + if bounds + else [] + ) return gpx_data, map_data, weather_data @@ -222,7 +240,11 @@ def get_chart_data( 'latitude': point.latitude, 'longitude': point.longitude, 'speed': speed, - 'time': point.time, + # workaround + # https://github.com/tkrajina/gpxpy/issues/209 + 'time': point.time.replace( + tzinfo=timezone(point.time.utcoffset()) + ), } ) previous_point = point diff --git a/fittrackee/workouts/utils/weather.py b/fittrackee/workouts/utils/weather.py index 8d580ad7..79a22932 100644 --- a/fittrackee/workouts/utils/weather.py +++ b/fittrackee/workouts/utils/weather.py @@ -3,18 +3,22 @@ from typing import Dict, Optional import forecastio import pytz -from gpxpy.gpx import GPXRoutePoint +from gpxpy.gpx import GPXTrackPoint from fittrackee import appLog API_KEY = os.getenv('WEATHER_API_KEY') -def get_weather(point: GPXRoutePoint) -> Optional[Dict]: - if not API_KEY or API_KEY == '': +def get_weather(point: GPXTrackPoint) -> Optional[Dict]: + if not API_KEY or not point.time: return None try: - point_time = pytz.utc.localize(point.time) + point_time = ( + pytz.utc.localize(point.time) + if point.time.tzinfo is None + else point.time.astimezone(pytz.utc) + ) forecast = forecastio.load_forecast( API_KEY, point.latitude, diff --git a/fittrackee/workouts/utils/workouts.py b/fittrackee/workouts/utils/workouts.py index e9159423..dea4c946 100644 --- a/fittrackee/workouts/utils/workouts.py +++ b/fittrackee/workouts/utils/workouts.py @@ -22,31 +22,42 @@ from .gpx import get_gpx_info from .maps import generate_map, get_map_hash -def get_datetime_with_tz( - timezone: str, workout_date: datetime, gpx_data: Optional[Dict] = None -) -> Tuple[Optional[datetime], datetime]: +def get_workout_datetime( + workout_date: Union[datetime, str], + user_timezone: Optional[str], + date_str_format: Optional[str] = None, + with_timezone: bool = False, +) -> Tuple[datetime, Optional[datetime]]: """ - Return naive datetime and datetime with user timezone + Return naive datetime and datetime with user timezone if with_timezone """ - workout_date_tz = None - if timezone: - user_tz = pytz.timezone(timezone) - utc_tz = pytz.utc - if gpx_data: - # workout date in gpx is in UTC, but in naive datetime - fmt = '%Y-%m-%d %H:%M:%S' - workout_date_string = workout_date.strftime(fmt) - workout_date_tmp = utc_tz.localize( - datetime.strptime(workout_date_string, fmt) - ) - workout_date_tz = workout_date_tmp.astimezone(user_tz) - else: - workout_date_tz = user_tz.localize(workout_date) - workout_date = workout_date_tz.astimezone(utc_tz) - # make datetime 'naive' like in gpx file - workout_date = workout_date.replace(tzinfo=None) + workout_date_with_user_tz = None - return workout_date_tz, workout_date + # workout w/o gpx + if isinstance(workout_date, str): + if not date_str_format: + date_str_format = '%Y-%m-%d %H:%M:%S' + workout_date = datetime.strptime(workout_date, date_str_format) + if user_timezone: + workout_date = pytz.timezone(user_timezone).localize(workout_date) + + if workout_date.tzinfo is None: + naive_workout_date = workout_date + if user_timezone and with_timezone: + pytz.utc.localize(naive_workout_date) + workout_date_with_user_tz = pytz.utc.localize( + naive_workout_date + ).astimezone(pytz.timezone(user_timezone)) + else: + naive_workout_date = workout_date.astimezone(pytz.utc).replace( + tzinfo=None + ) + if user_timezone and with_timezone: + workout_date_with_user_tz = workout_date.astimezone( + pytz.timezone(user_timezone) + ) + + return naive_workout_date, workout_date_with_user_tz def get_datetime_from_request_args( @@ -57,25 +68,32 @@ def get_datetime_from_request_args( date_from_str = params.get('from') if date_from_str: - date_from = datetime.strptime(date_from_str, '%Y-%m-%d') - _, date_from = get_datetime_with_tz(user.timezone, date_from) + date_from, _ = get_workout_datetime( + workout_date=date_from_str, + user_timezone=user.timezone, + date_str_format='%Y-%m-%d', + ) date_to_str = params.get('to') if date_to_str: - date_to = datetime.strptime( - f'{date_to_str} 23:59:59', '%Y-%m-%d %H:%M:%S' + date_to, _ = get_workout_datetime( + workout_date=f'{date_to_str} 23:59:59', + user_timezone=user.timezone, ) - _, date_to = get_datetime_with_tz(user.timezone, date_to) return date_from, date_to +def _remove_microseconds(delta: timedelta) -> timedelta: + return delta - timedelta(microseconds=delta.microseconds) + + def update_workout_data( workout: Union[Workout, WorkoutSegment], gpx_data: Dict ) -> Union[Workout, WorkoutSegment]: """ Update workout or workout segment with data from gpx file """ - workout.pauses = gpx_data['stop_time'] - workout.moving = gpx_data['moving_time'] + workout.pauses = _remove_microseconds(gpx_data['stop_time']) + workout.moving = _remove_microseconds(gpx_data['moving_time']) workout.min_alt = gpx_data['elevation_min'] workout.max_alt = gpx_data['elevation_max'] workout.descent = gpx_data['downhill'] @@ -92,17 +110,17 @@ def create_workout( Create Workout from data entered by user and from gpx if a gpx file is provided """ - workout_date = ( - gpx_data['start'] + workout_date, workout_date_tz = get_workout_datetime( + workout_date=gpx_data['start'] if gpx_data - else datetime.strptime(workout_data['workout_date'], '%Y-%m-%d %H:%M') - ) - workout_date_tz, workout_date = get_datetime_with_tz( - user.timezone, workout_date, gpx_data + else workout_data['workout_date'], + date_str_format=None if gpx_data else '%Y-%m-%d %H:%M', + user_timezone=user.timezone, + with_timezone=True, ) duration = ( - gpx_data['duration'] + _remove_microseconds(gpx_data['duration']) if gpx_data else timedelta(seconds=workout_data['duration']) ) @@ -202,11 +220,10 @@ def edit_workout( workout.notes = workout_data.get('notes') if not workout.gpx: if workout_data.get('workout_date'): - workout_date = datetime.strptime( - workout_data['workout_date'], '%Y-%m-%d %H:%M' - ) - _, workout.workout_date = get_datetime_with_tz( - auth_user.timezone, workout_date + workout.workout_date, _ = get_workout_datetime( + workout_date=workout_data.get('workout_date', ''), + date_str_format='%Y-%m-%d %H:%M', + user_timezone=auth_user.timezone, ) if workout_data.get('duration'): diff --git a/fittrackee/workouts/workouts.py b/fittrackee/workouts/workouts.py index b5d90a32..a6152189 100644 --- a/fittrackee/workouts/workouts.py +++ b/fittrackee/workouts/workouts.py @@ -1111,7 +1111,8 @@ def post_workout_no_gpx( "status": "success" } - :=3.7.4.3", markers = "python_version < \"3.8\"" [[package]] name = "gpxpy" -version = "1.3.4" +version = "1.5.0" description = "GPX file parser and GPS track manipulation library" category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=3.6" [[package]] name = "greenlet" @@ -1466,7 +1466,7 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest- [metadata] lock-version = "1.1" python-versions = "^3.7" -content-hash = "fa5be5cc59de72ae3ddb18acb2c0291a7d77296de6427492b46c40d794800335" +content-hash = "40b8491c8b82a7b29f57a2845209965c6351b527aebb7d504f5f8ec0ace4141e" [metadata.files] alabaster = [ @@ -1727,7 +1727,7 @@ gitpython = [ {file = "GitPython-3.1.27.tar.gz", hash = "sha256:1c885ce809e8ba2d88a29befeb385fcea06338d3640712b59ca623c220bb5704"}, ] gpxpy = [ - {file = "gpxpy-1.3.4.tar.gz", hash = "sha256:4a0f072ae5bdf9270c7450e452f93a6c5c91d888114e8d78868a8f163b0dbb15"}, + {file = "gpxpy-1.5.0.tar.gz", hash = "sha256:e6993a8945eae07a833cd304b88bbc6c3c132d63b2bf4a9b0a5d9097616b8708"}, ] greenlet = [ {file = "greenlet-1.1.2-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:58df5c2a0e293bf665a51f8a100d3e9956febfbf1d9aaf8c0677cf70218910c6"}, diff --git a/pyproject.toml b/pyproject.toml index 1dcfdfff..6d13c3b5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,7 +29,7 @@ flask = "^2.1" flask-bcrypt = "^1.0" flask-dramatiq = "^0.6.0" flask-migrate = "^3.1" -gpxpy = "=1.3.4" +gpxpy = "=1.5.0" gunicorn = "^20.1" humanize = "^4.1" psycopg2-binary = "^2.9"