From e2cd855b5b02025e2946e849244c0bfba1e7b03e Mon Sep 17 00:00:00 2001 From: Peter Weber Date: Thu, 17 Feb 2022 15:37:35 +0100 Subject: [PATCH] module: invenio-record-resources MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Updates `invenio-record-resources` from version v.0.12.5 to v0.16.14. Versions greater then 17 are not working for `invenio 3.4`. Co-Authored-by: Peter Weber Co-Authored-by: Johnny MarieĢthoz --- poetry.lock | 295 ++++++++++-------- pyproject.toml | 19 +- scripts/test | 3 +- sonar/dedicated/hepvs/projects/resource.py | 41 +-- sonar/ext.py | 22 +- sonar/modules/documents/receivers.py | 10 +- sonar/modules/pdf_extractor/pdf_extractor.py | 2 +- sonar/resources/projects/resource.py | 53 ++-- sonar/resources/projects/service.py | 114 ++++--- sonar/resources/resource.py | 15 +- sonar/resources/resources/responses.py | 49 +-- sonar/resources/service.py | 9 +- sonar/suggestions/rest.py | 10 +- tests/api/projects/test_projects_filters.py | 6 +- .../api/projects/test_projects_permissions.py | 90 +++--- .../api/suggestions/test_suggestions_rest.py | 2 +- tests/api/test_ext_before_request.py | 4 +- tests/conftest.py | 2 +- tests/ui/deposits/test_deposits_cli.py | 2 +- .../ui/documents/test_documents_receivers.py | 2 +- tests/ui/pdf_extractor/conftest.py | 1 - .../unit/resources/test_resources_resource.py | 8 +- 22 files changed, 420 insertions(+), 339 deletions(-) diff --git a/poetry.lock b/poetry.lock index f7f4bf42..88dcf391 100644 --- a/poetry.lock +++ b/poetry.lock @@ -958,14 +958,14 @@ Flask = "*" [[package]] name = "flask-resources" -version = "0.6.0" +version = "0.8.1" description = "Flask Resources module to create REST APIs." category = "main" optional = false python-versions = "*" [package.dependencies] -Flask = ">=1.1.2,<1.2.0" +Flask = ">=1.1.4" marshmallow = ">=3.0,<4.0" speaklater = ">=1.3,<2.0" @@ -1905,16 +1905,17 @@ tests = ["pytest-invenio (>=1.4.0)"] [[package]] name = "invenio-records" -version = "1.7.0" +version = "1.6.2" description = "Invenio-Records is a metadata storage module." category = "main" optional = false -python-versions = ">=3.6" +python-versions = "*" [package.dependencies] arrow = ">=0.16.0" -invenio-celery = ">=1.2.4" -invenio-i18n = ">=1.3.2" +invenio-base = ">=1.2.11" +invenio-celery = ">=1.2.2" +invenio-i18n = ">=1.2.0" jsonpatch = ">=1.26" jsonref = ">=0.2" jsonresolver = ">=0.3.1" @@ -1922,10 +1923,12 @@ jsonschema = ">=4.3.0,<5.0.0" [package.extras] admin = ["invenio-admin (>=1.2.1)"] -mysql = ["invenio-db[versioning,mysql] (>=1.0.14,<2.0)"] -postgresql = ["invenio-db[versioning,postgresql] (>=1.0.14,<2.0)"] -sqlite = ["invenio-db[versioning] (>=1.0.14,<2.0)"] -tests = ["invenio-admin (>=1.2.1)", "pytest-invenio (>=1.4.6)", "sphinx (>=4.2.0,<5)"] +all = ["Sphinx (==4.2.0)", "invenio-admin (>=1.2.1)", "pytest-invenio (>=1.4.1)"] +docs = ["Sphinx (==4.2.0)"] +mysql = ["invenio-db[versioning,mysql] (>=1.0.14,<1.1.0)"] +postgresql = ["invenio-db[versioning,postgresql] (>=1.0.14,<1.1.0)"] +sqlite = ["invenio-db[versioning] (>=1.0.14,<1.1.0)"] +tests = ["pytest-invenio (>=1.4.1)"] [[package]] name = "invenio-records-files" @@ -1951,7 +1954,7 @@ tests = ["check-manifest (>=0.25)", "coverage (>=4.0)", "invenio-indexer (>=1.1. [[package]] name = "invenio-records-permissions" -version = "0.11.0" +version = "0.13.0" description = "Permission policies for Invenio records." category = "main" optional = false @@ -1965,44 +1968,46 @@ invenio-records = ">=1.4.0" [package.extras] all = ["Sphinx (>=3)", "pytest-mock (>=1.6.0)", "pytest-invenio (>=1.4.1)", "invenio-accounts (>=1.4.3)", "invenio-app (>=1.3.0)"] docs = ["Sphinx (>=3)"] -elasticsearch6 = ["invenio-search[elasticsearch6] (>=1.4.0,<2.0.0)"] -elasticsearch7 = ["invenio-search[elasticsearch7] (>=1.4.0,<2.0.0)"] -mysql = ["invenio-db[versioning,mysql] (>=1.0.5,<2.0.0)"] -postgresql = ["invenio-db[versioning,postgresql] (>=1.0.5,<2.0.0)"] -sqlite = ["invenio-db[versioning] (>=1.0.5,<2.0.0)"] +elasticsearch6 = ["invenio-search[elasticsearch6] (>=1.4.1,<2.0.0)"] +elasticsearch7 = ["invenio-search[elasticsearch7] (>=1.4.1,<2.0.0)"] +mysql = ["invenio-db[versioning,mysql] (>=1.0.9,<2.0.0)"] +postgresql = ["invenio-db[versioning,postgresql] (>=1.0.9,<2.0.0)"] +sqlite = ["invenio-db[versioning] (>=1.0.9,<2.0.0)"] tests = ["pytest-mock (>=1.6.0)", "pytest-invenio (>=1.4.1)", "invenio-accounts (>=1.4.3)", "invenio-app (>=1.3.0)", "Sphinx (>=3)"] [[package]] name = "invenio-records-resources" -version = "0.12.5" +version = "0.17.1" description = "Invenio Resources module to create REST APIs." category = "main" optional = false python-versions = "*" [package.dependencies] -flask-resources = ">=0.6.0,<0.7.0" -invenio-accounts = ">=1.4.3" -invenio-base = ">=1.2.3" +flask-resources = ">=0.8.0,<0.9.0" +invenio-accounts = ">=1.4.7" +invenio-base = ">=1.2.4" invenio-files-rest = ">=1.2.0" -invenio-i18n = ">=1.2.0" -invenio-indexer = ">=1.2.0" -invenio-jsonschemas = ">=1.1.0" +invenio-i18n = ">=1.3.0" +invenio-indexer = ">=1.2.1" +invenio-jsonschemas = ">=1.1.3" invenio-pidstore = ">=1.2.2" -invenio-records = ">=1.5.0a2" -invenio-records-permissions = ">=0.11.0,<0.12.0" -marshmallow-utils = ">=0.3.4,<0.4.0" +invenio-records = ">=1.5.0b1" +invenio-records-permissions = ">=0.13.0,<0.14.0" +luqum = ">=0.11.0" +marshmallow-utils = ">=0.5.2,<0.6.0" uritemplate = ">=3.0.1" +wand = ">=0.6.6,<0.7.0" xmltodict = ">=0.12.0,<0.13.0" [package.extras] -all = ["Sphinx (>=2.4,<3)", "invenio-db[versioning,mysql] (>=1.0.5,<2.0.0)", "invenio-db[versioning,postgresql] (>=1.0.5,<2.0.0)", "invenio-db[versioning] (>=1.0.5,<2.0.0)", "invenio-app (>=1.3.0)", "pytest-invenio (>=1.4.1)"] +all = ["Sphinx (>=2.4,<3)", "invenio-db[versioning,mysql] (>=1.0.9,<2.0.0)", "invenio-db[versioning,postgresql] (>=1.0.9,<2.0.0)", "invenio-db[versioning] (>=1.0.9,<2.0.0)", "invenio-app (>=1.3.0)", "pytest-invenio (>=1.4.1)"] docs = ["Sphinx (>=2.4,<3)"] -elasticsearch6 = ["invenio-search[elasticsearch6] (>=1.4.1,<2.0.0)"] -elasticsearch7 = ["invenio-search[elasticsearch7] (>=1.4.1,<2.0.0)"] -mysql = ["invenio-db[versioning,mysql] (>=1.0.5,<2.0.0)"] -postgresql = ["invenio-db[versioning,postgresql] (>=1.0.5,<2.0.0)"] -sqlite = ["invenio-db[versioning] (>=1.0.5,<2.0.0)"] +elasticsearch6 = ["invenio-search[elasticsearch6] (>=1.4.2,<2.0.0)"] +elasticsearch7 = ["invenio-search[elasticsearch7] (>=1.4.2,<2.0.0)"] +mysql = ["invenio-db[versioning,mysql] (>=1.0.9,<2.0.0)"] +postgresql = ["invenio-db[versioning,postgresql] (>=1.0.9,<2.0.0)"] +sqlite = ["invenio-db[versioning] (>=1.0.9,<2.0.0)"] tests = ["invenio-app (>=1.3.0)", "pytest-invenio (>=1.4.1)"] [[package]] @@ -2203,6 +2208,14 @@ parallel = ["ipyparallel"] qtconsole = ["qtconsole"] test = ["nose (>=0.10.1)", "requests", "testpath", "pygments", "nbformat", "ipykernel", "numpy (>=1.17)"] +[[package]] +name = "ipython-genutils" +version = "0.2.0" +description = "Vestigial utilities from IPython" +category = "main" +optional = false +python-versions = "*" + [[package]] name = "isbnlib" version = "3.10.10" @@ -2432,6 +2445,17 @@ python-versions = "*" [package.dependencies] six = ">=1.4.1" +[[package]] +name = "luqum" +version = "0.11.0" +description = "A Lucene query parser generating ElasticSearch queries and more !" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +ply = ">=3.11" + [[package]] name = "lxml" version = "4.7.0" @@ -2490,11 +2514,11 @@ Markdown = ">=3.0.1" [[package]] name = "markupsafe" -version = "1.1.1" +version = "2.0.1" description = "Safely add untrusted strings to HTML/XML markup." category = "main" optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" +python-versions = ">=3.6" [[package]] name = "marshmallow" @@ -2531,7 +2555,7 @@ tests = ["pytest", "mock"] [[package]] name = "marshmallow-utils" -version = "0.3.10" +version = "0.5.5" description = "Extras and utilities for Marshmallow" category = "main" optional = false @@ -2552,8 +2576,8 @@ uritemplate = ">=3.0.1" werkzeug = ">=1.0.0" [package.extras] -all = ["Sphinx (>=3.0.0)", "check-manifest (>=0.42)", "coverage (>=5.2.1)", "pytest-cov (>=2.10.1)", "pytest-isort (>=1.2.0)", "pytest-pycodestyle (>=2.2.0)", "pytest-pydocstyle (>=2.2.0)", "pytest (>=6.0)"] -docs = ["Sphinx (>=3.0.0)"] +all = ["Sphinx (>=4.2.0)", "check-manifest (>=0.42)", "coverage (>=5.2.1)", "pytest-cov (>=2.10.1)", "pytest-isort (>=1.2.0)", "pytest-pycodestyle (>=2.2.0)", "pytest-pydocstyle (>=2.2.0)", "pytest (>=6.0)"] +docs = ["Sphinx (>=4.2.0)"] tests = ["check-manifest (>=0.42)", "coverage (>=5.2.1)", "pytest-cov (>=2.10.1)", "pytest-isort (>=1.2.0)", "pytest-pycodestyle (>=2.2.0)", "pytest-pydocstyle (>=2.2.0)", "pytest (>=6.0)"] [[package]] @@ -2635,7 +2659,7 @@ test = ["ipython (<8.0.0)", "ipykernel", "ipywidgets (<8.0.0)", "pytest (>=4.1)" [[package]] name = "nbconvert" -version = "6.4.4" +version = "6.4.5" description = "Converting Jupyter Notebooks" category = "main" optional = false @@ -2649,6 +2673,7 @@ entrypoints = ">=0.2.2" jinja2 = ">=2.4" jupyter-core = "*" jupyterlab-pygments = "*" +MarkupSafe = ">=2.0" mistune = ">=0.8.1,<2" nbclient = ">=0.5.0,<0.6.0" nbformat = ">=4.4" @@ -2781,14 +2806,6 @@ bcrypt = ["bcrypt (>=3.1.0)"] build_docs = ["sphinx (>=1.6)", "sphinxcontrib-fulltoc (>=1.2.0)", "cloud-sptheme (>=1.10.1)"] totp = ["cryptography"] -[[package]] -name = "pastel" -version = "0.2.1" -description = "Bring colors to your terminal." -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - [[package]] name = "pep517" version = "0.12.0" @@ -2848,19 +2865,12 @@ importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} dev = ["pre-commit", "tox"] [[package]] -name = "poethepoet" -version = "0.13.1" -description = "A task runner that works well with poetry." +name = "ply" +version = "3.11" +description = "Python Lex & Yacc" category = "main" optional = false -python-versions = ">=3.6.2" - -[package.dependencies] -pastel = ">=0.2.1,<0.3.0" -tomli = ">=1.2.2" - -[package.extras] -poetry_plugin = ["poetry (>=1.0,<2.0)"] +python-versions = "*" [[package]] name = "polib" @@ -3267,7 +3277,7 @@ tests = ["bottle", "celery (>=2.5)", "coverage (<4)", "exam (>=0.5.2)", "flake8 [[package]] name = "redis" -version = "4.3.2" +version = "4.3.3" description = "Python client for Redis database and key-value store" category = "main" optional = false @@ -3926,7 +3936,7 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest- [metadata] lock-version = "1.1" python-versions = ">= 3.7, <3.10" -content-hash = "2b08838fe70e8176871f8a8eee683ada13b2471160e3e3f89cdcf343952008e2" +content-hash = "098161b65069802959fec8fdaba5983793a871229a1a8c9da74aef398c331b3a" [metadata.files] alabaster = [ @@ -4352,8 +4362,8 @@ flask-principal = [ {file = "Flask-Principal-0.4.0.tar.gz", hash = "sha256:f5d6134b5caebfdbb86f32d56d18ee44b080876a27269560a96ea35f75c99453"}, ] flask-resources = [ - {file = "flask-resources-0.6.0.tar.gz", hash = "sha256:ca29486e492b6638c6bb916b0bae00ae590f5e0db9030f90f020d55c7a87c43a"}, - {file = "flask_resources-0.6.0-py2.py3-none-any.whl", hash = "sha256:64f6b5c20f96075c1f6a965d0f7a1eda1961abbd854a2f78f30c73702aa801f0"}, + {file = "flask-resources-0.8.1.tar.gz", hash = "sha256:78a8c59a1ec7ee5ed62964349032a67d5935c6b1dc8c7394f1afcba634a524f5"}, + {file = "flask_resources-0.8.1-py2.py3-none-any.whl", hash = "sha256:50a2128ec3cc9aadad707edb55c8bb2ad218233a4bf3d4287635d90891a9fda1"}, ] flask-restful = [ {file = "Flask-RESTful-0.3.9.tar.gz", hash = "sha256:ccec650b835d48192138c85329ae03735e6ced58e9b2d9c2146d6c84c06fa53e"}, @@ -4539,20 +4549,20 @@ invenio-queues = [ {file = "invenio_queues-1.0.0a3-py2.py3-none-any.whl", hash = "sha256:15cf3ea8487a387c07c6cce7f955aa9d64570884f2a6907dbabe8660d186e9e4"}, ] invenio-records = [ - {file = "invenio-records-1.7.0.tar.gz", hash = "sha256:41e2e3c212f98d7954da2cd40fd6f1cde2bbba488c6690815ec4bbe2e77c16bf"}, - {file = "invenio_records-1.7.0-py2.py3-none-any.whl", hash = "sha256:c889a855af1c9e973da64c48850a58c74a339c8edd90e95f3eace58cd61a86d5"}, + {file = "invenio-records-1.6.2.tar.gz", hash = "sha256:cf408aed82812ee03431c1e9936c6f3933f895586474654195f3c5ebd83bcb24"}, + {file = "invenio_records-1.6.2-py2.py3-none-any.whl", hash = "sha256:c8f44197b9ae7da33acdeaa804840bf9cb3bb0971173f5611f2522f954997875"}, ] invenio-records-files = [ {file = "invenio-records-files-1.2.1.tar.gz", hash = "sha256:a08da459517f6354cb99bb0005f32fab3894f2852fe3a1a602bda3b6dcad4082"}, {file = "invenio_records_files-1.2.1-py2.py3-none-any.whl", hash = "sha256:46155d8a21b7b9ef7ba0665b824c35c86b44e06b940953990473c252a8d9e18b"}, ] invenio-records-permissions = [ - {file = "invenio-records-permissions-0.11.0.tar.gz", hash = "sha256:f8e4fb95aabadc39e07c9247ee33661a194d57e281478bf670be392128f603a7"}, - {file = "invenio_records_permissions-0.11.0-py2.py3-none-any.whl", hash = "sha256:0e069f303b8afbbaa96f347fcd2e83989d8678896abcae779ca1f70948b12b8c"}, + {file = "invenio-records-permissions-0.13.0.tar.gz", hash = "sha256:f061be7633f80fac626a5da1f8116b01dfff5146105e7faf12fda9f8241e84bd"}, + {file = "invenio_records_permissions-0.13.0-py2.py3-none-any.whl", hash = "sha256:381a37b4c6d7f9900fbdc59f81370b0b566a91c5bcf8bacf763abe088c353437"}, ] invenio-records-resources = [ - {file = "invenio-records-resources-0.12.5.tar.gz", hash = "sha256:5932c9cbb8c4ba94ae50dd40e1c0c34b7df3da19801fcd9a85fa023a8522251f"}, - {file = "invenio_records_resources-0.12.5-py2.py3-none-any.whl", hash = "sha256:d597941a0f55f3ef89139a96d6a114c5846a8dcb667eceb82d45579a6edcfa98"}, + {file = "invenio-records-resources-0.17.1.tar.gz", hash = "sha256:4872fdf2df271947a9abab4b75b6ab16a0b073840351b4c37bbd1f4c3a0c405c"}, + {file = "invenio_records_resources-0.17.1-py2.py3-none-any.whl", hash = "sha256:fd63267ffefbff6fa79dae80bb97813509fd1334a5e793457b1af48268cde5ca"}, ] invenio-records-rest = [ {file = "invenio-records-rest-1.8.0.tar.gz", hash = "sha256:70ba741f19f8c9a1ae14a700d82c632175e881fd786ffdc4692f2718482e8dd1"}, @@ -4586,6 +4596,10 @@ ipython = [ {file = "ipython-7.34.0-py3-none-any.whl", hash = "sha256:c175d2440a1caff76116eb719d40538fbb316e214eda85c5515c303aacbfb23e"}, {file = "ipython-7.34.0.tar.gz", hash = "sha256:af3bdb46aa292bce5615b1b2ebc76c2080c5f77f54bda2ec72461317273e7cd6"}, ] +ipython-genutils = [ + {file = "ipython_genutils-0.2.0-py2.py3-none-any.whl", hash = "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8"}, + {file = "ipython_genutils-0.2.0.tar.gz", hash = "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"}, +] isbnlib = [ {file = "isbnlib-3.10.10-py2.py3-none-any.whl", hash = "sha256:623a09329e8ec7049edf15dd412db042bf4f8236a428bf7a22d84a125584f52d"}, {file = "isbnlib-3.10.10.tar.gz", hash = "sha256:c9e6c1dcaa9dff195429373cf2beb3117f30b3fca43d7db5aec5a2d1f6f59784"}, @@ -4653,6 +4667,10 @@ limits = [ {file = "limits-1.6-py3-none-any.whl", hash = "sha256:12ae4449cf7daadee43edf4096acd9cb9f4bfdec3a995aa9fbd0f72b0b9af762"}, {file = "limits-1.6.tar.gz", hash = "sha256:6c0a57b42647f1141f5a7a0a8479b49e4367c24937a01bd9d4063a595c2dd48a"}, ] +luqum = [ + {file = "luqum-0.11.0-py3-none-any.whl", hash = "sha256:f7518558bcfd219771083fcca9c2c877c3ff621beb2f5197d569e14ec2c6fe6f"}, + {file = "luqum-0.11.0.linux-x86_64.tar.gz", hash = "sha256:2910a6d1eaba62a7fbaf3ea867b0da9b0d80739f87eb804906dc6bc4cbf6647f"}, +] lxml = [ {file = "lxml-4.7.0-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:fd2d46aa9f379553ff30fd915f039c0296837e99b91e75e77f36a0819331a724"}, {file = "lxml-4.7.0-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b584fa08ff85e441c70ea8310e274eda0b6fb522a86679d07839737629c0b891"}, @@ -4728,58 +4746,75 @@ markdown-captions = [ {file = "markdown_captions-2.1.1-py3-none-any.whl", hash = "sha256:67e785d6a288cf951e218e555026b9591e699c2572fab97b55fe833a477e1654"}, ] markupsafe = [ - {file = "MarkupSafe-1.1.1-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161"}, - {file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"}, - {file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183"}, - {file = "MarkupSafe-1.1.1-cp27-cp27m-win32.whl", hash = "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b"}, - {file = "MarkupSafe-1.1.1-cp27-cp27m-win_amd64.whl", hash = "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e"}, - {file = "MarkupSafe-1.1.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f"}, - {file = "MarkupSafe-1.1.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1"}, - {file = "MarkupSafe-1.1.1-cp34-cp34m-macosx_10_6_intel.whl", hash = "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5"}, - {file = "MarkupSafe-1.1.1-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1"}, - {file = "MarkupSafe-1.1.1-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735"}, - {file = "MarkupSafe-1.1.1-cp34-cp34m-win32.whl", hash = "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21"}, - {file = "MarkupSafe-1.1.1-cp34-cp34m-win_amd64.whl", hash = "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235"}, - {file = "MarkupSafe-1.1.1-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b"}, - {file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f"}, - {file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905"}, - {file = "MarkupSafe-1.1.1-cp35-cp35m-win32.whl", hash = "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1"}, - {file = "MarkupSafe-1.1.1-cp35-cp35m-win_amd64.whl", hash = "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d53bc011414228441014aa71dbec320c66468c1030aae3a6e29778a3382d96e5"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:3b8a6499709d29c2e2399569d96719a1b21dcd94410a586a18526b143ec8470f"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:84dee80c15f1b560d55bcfe6d47b27d070b4681c699c572af2e3c7cc90a3b8e0"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:b1dba4527182c95a0db8b6060cc98ac49b9e2f5e64320e2b56e47cb2831978c7"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-win32.whl", hash = "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:bf5aa3cbcfdf57fa2ee9cd1822c862ef23037f5c832ad09cfea57fa846dec193"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:6fffc775d90dcc9aed1b89219549b329a9250d918fd0b8fa8d93d154918422e1"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:a6a744282b7718a2a62d2ed9d993cad6f5f585605ad352c11de459f4108df0a1"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:195d7d2c4fbb0ee8139a6cf67194f3973a6b3042d742ebe0a9ed36d8b6f0c07f"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-win32.whl", hash = "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:acf08ac40292838b3cbbb06cfe9b2cb9ec78fce8baca31ddb87aaac2e2dc3bc2"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d9be0ba6c527163cbed5e0857c451fcd092ce83947944d6c14bc95441203f032"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:caabedc8323f1e93231b52fc32bdcde6db817623d33e100708d9a68e1f53b26b"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-win32.whl", hash = "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be"}, - {file = "MarkupSafe-1.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d73a845f227b0bfe8a7455ee623525ee656a9e2e749e4742706d80a6065d5e2c"}, - {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:98bae9582248d6cf62321dcb52aaf5d9adf0bad3b40582925ef7c7f0ed85fceb"}, - {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:2beec1e0de6924ea551859edb9e7679da6e4870d32cb766240ce17e0a0ba2014"}, - {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:7fed13866cf14bba33e7176717346713881f56d9d2bcebab207f7a036f41b850"}, - {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:6f1e273a344928347c1290119b493a1f0303c52f5a5eae5f16d74f48c15d4a85"}, - {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:feb7b34d6325451ef96bc0e36e1a6c0c1c64bc1fbec4b854f4529e51887b1621"}, - {file = "MarkupSafe-1.1.1-cp39-cp39-win32.whl", hash = "sha256:22c178a091fc6630d0d045bdb5992d2dfe14e3259760e713c490da5323866c39"}, - {file = "MarkupSafe-1.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:b7d644ddb4dbd407d31ffb699f1d140bc35478da613b441c582aeb7c43838dd8"}, - {file = "MarkupSafe-1.1.1.tar.gz", hash = "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4dc8f9fb58f7364b63fd9f85013b780ef83c11857ae79f2feda41e270468dd9b"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:20dca64a3ef2d6e4d5d615a3fd418ad3bde77a47ec8a23d984a12b5b4c74491a"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cdfba22ea2f0029c9261a4bd07e830a8da012291fbe44dc794e488b6c9bb353a"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-win32.whl", hash = "sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:deb993cacb280823246a026e3b2d81c493c53de6acfd5e6bfe31ab3402bb37dd"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:63f3268ba69ace99cab4e3e3b5840b03340efed0948ab8f78d2fd87ee5442a4f"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:8d206346619592c6200148b01a2142798c989edcb9c896f9ac9722a99d4e77e6"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-win32.whl", hash = "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d6c7ebd4e944c85e2c3421e612a7057a2f48d478d79e61800d81468a8d842207"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f0567c4dc99f264f49fe27da5f735f414c4e7e7dd850cfd8e69f0862d7c74ea9"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:89c687013cb1cd489a0f0ac24febe8c7a666e6e221b783e53ac50ebf68e45d86"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:aca6377c0cb8a8253e493c6b451565ac77e98c2951c45f913e0b52facdcff83f"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:04635854b943835a6ea959e948d19dcd311762c5c0c6e1f0e16ee57022669194"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6300b8454aa6930a24b9618fbb54b5a68135092bc666f7b06901f897fa5c2fee"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-win32.whl", hash = "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4296f2b1ce8c86a6aea78613c34bb1a672ea0e3de9c6ba08a960efe0b0a09047"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f02365d4e99430a12647f09b6cc8bab61a6564363f313126f775eb4f6ef798e"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5b6d930f030f8ed98e3e6c98ffa0652bdb82601e7a016ec2ab5d7ff23baa78d1"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-win32.whl", hash = "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"}, + {file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"}, ] marshmallow = [ {file = "marshmallow-3.16.0-py3-none-any.whl", hash = "sha256:53a1e0ee69f79e1f3e80d17393b25cfc917eda52f859e8183b4af72c3390c1f1"}, @@ -4790,8 +4825,8 @@ marshmallow-oneofschema = [ {file = "marshmallow_oneofschema-3.0.1-py2.py3-none-any.whl", hash = "sha256:bd29410a9f2f7457a2b428286e2a80ef76b8ddc3701527dc1f935a88914b02f2"}, ] marshmallow-utils = [ - {file = "marshmallow-utils-0.3.10.tar.gz", hash = "sha256:2f32e4b12f0a74fb3dcb24b11f464bb70ba24d4a10d95d606206771629b79274"}, - {file = "marshmallow_utils-0.3.10-py2.py3-none-any.whl", hash = "sha256:50c74cd89f2d2a977b7c122009c35de2fef65db733cdaf3720340e6d61861710"}, + {file = "marshmallow-utils-0.5.5.tar.gz", hash = "sha256:3c24569fd3cd9cb7d89400bef09d2ec3c4012067f07b4bdda4129b07a3c4add0"}, + {file = "marshmallow_utils-0.5.5-py2.py3-none-any.whl", hash = "sha256:7d3aa62463882e4e8dc19ad8a68dc450f3f90405073bb75bf87b6f03be48504f"}, ] matplotlib-inline = [ {file = "matplotlib-inline-0.1.3.tar.gz", hash = "sha256:a04bfba22e0d1395479f866853ec1ee28eea1485c1d69a6faf00dc3e24ff34ee"}, @@ -4852,8 +4887,8 @@ nbclient = [ {file = "nbclient-0.5.13.tar.gz", hash = "sha256:40c52c9b5e3c31faecaee69f202b3f53e38d7c1c563de0fadde9d7eda0fdafe8"}, ] nbconvert = [ - {file = "nbconvert-6.4.4-py3-none-any.whl", hash = "sha256:c0c13d11378e13f72b9cd509c008383dca4051c228e4985f75023b2a5d82fc9f"}, - {file = "nbconvert-6.4.4.tar.gz", hash = "sha256:ee0dfe34bbd1082ac9bfc750aae3c73fcbc34a70c5574c6986ff83c10a3541fd"}, + {file = "nbconvert-6.4.5-py3-none-any.whl", hash = "sha256:e01d219f55cc79f9701c834d605e8aa3acf35725345d3942e3983937f368ce14"}, + {file = "nbconvert-6.4.5.tar.gz", hash = "sha256:21163a8e2073c07109ca8f398836e45efdba2aacea68d6f75a8a545fef070d4e"}, ] nbformat = [ {file = "nbformat-5.4.0-py3-none-any.whl", hash = "sha256:0d6072aaec95dddc39735c144ee8bbc6589c383fb462e4058abc855348152dad"}, @@ -4893,10 +4928,6 @@ passlib = [ {file = "passlib-1.7.4-py2.py3-none-any.whl", hash = "sha256:aa6bca462b8d8bda89c70b382f0c298a20b5560af6cbfa2dce410c0a2fb669f1"}, {file = "passlib-1.7.4.tar.gz", hash = "sha256:defd50f72b65c5402ab2c573830a6978e5f202ad0d984793c8dde2c4152ebe04"}, ] -pastel = [ - {file = "pastel-0.2.1-py2.py3-none-any.whl", hash = "sha256:4349225fcdf6c2bb34d483e523475de5bb04a5c10ef711263452cb37d7dd4364"}, - {file = "pastel-0.2.1.tar.gz", hash = "sha256:e6581ac04e973cac858828c6202c1e1e81fee1dc7de7683f3e1ffe0bfd8a573d"}, -] pep517 = [ {file = "pep517-0.12.0-py2.py3-none-any.whl", hash = "sha256:dd884c326898e2c6e11f9e0b64940606a93eb10ea022a2e067959f3a110cf161"}, {file = "pep517-0.12.0.tar.gz", hash = "sha256:931378d93d11b298cf511dd634cf5ea4cb249a28ef84160b3247ee9afb4e8ab0"}, @@ -4953,9 +4984,9 @@ pluggy = [ {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, ] -poethepoet = [ - {file = "poethepoet-0.13.1-py3-none-any.whl", hash = "sha256:47e7b38c8b5412f840447f7612a9330697e8633e7500a7340b6734f50e26e380"}, - {file = "poethepoet-0.13.1.tar.gz", hash = "sha256:4f6962f17f5d5a453fd7fa66e3e7897e9191d4289148433efe441c81f2451a46"}, +ply = [ + {file = "ply-3.11-py2.py3-none-any.whl", hash = "sha256:096f9b8350b65ebd2fd1346b12452efe5b9607f7482813ffca50c22722a807ce"}, + {file = "ply-3.11.tar.gz", hash = "sha256:00c7c1aaa88358b9c765b6d3000c6eec0ba42abca5351b095321aef446081da3"}, ] polib = [ {file = "polib-1.1.1-py2.py3-none-any.whl", hash = "sha256:d3ee85e0c6788f789353416b1612c6c92d75fe6ccfac0029711974d6abd0f86d"}, @@ -5262,8 +5293,8 @@ raven = [ {file = "raven-6.10.0.tar.gz", hash = "sha256:3fa6de6efa2493a7c827472e984ce9b020797d0da16f1db67197bcc23c8fae54"}, ] redis = [ - {file = "redis-4.3.2-py3-none-any.whl", hash = "sha256:b30a2dc3f27cab69284829d6d423643beee88f279967c42e9014a2ffb7c2a84c"}, - {file = "redis-4.3.2.tar.gz", hash = "sha256:b989406ea786a9d151066a683b32a437a48f043c3ffd3795ec5ad9d344ee9e8c"}, + {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"}, diff --git a/pyproject.toml b/pyproject.toml index fcbfd4c1..867139a7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,12 +19,11 @@ invenio-oaiserver = ">1.4.0,<1.5.0" invenio-pidstore = ">=1.2.1,<1.3.0" invenio-records-rest = ">=1.8.0,<1.9.0" invenio-records-ui= ">=1.2.0,<1.3.0" -invenio-records = "<=1.7.0" +invenio-records = ">=1.4.0,<1.7.0" invenio-stats = "^1.0.0a18" -# TODO: update invenio-records-resourcces -invenio-records-resources = "==0.12.5" -invenio = {version = ">=3.4.0,<3.5.0", extras = ["base", "files", "postgresql", "auth", "elasticsearch7", "docs", "tests"]} +invenio-records-resources = "*" invenio-previewer = ">=1.3.5" +invenio = {version = ">=3.4.0,<3.5.0", extras = ["base", "files", "postgresql", "auth", "elasticsearch7", "docs", "tests"]} uwsgi = ">=2.0" uwsgitop = ">=0.11" @@ -37,7 +36,7 @@ marshmallow = ">=3.0.0,<4.0.0" pycountry = "*" markdown-captions = "*" bleach = ">3.11" -wand = ">0.6.0" +wand = ">=0.6.6,<0.7.0" python-dotenv = "*" flask-cors = ">3.0.8" cryptography = ">=3.2" @@ -50,17 +49,19 @@ python-Levenshtein = ">=0.12.0" polib = ">=1.1.0" idutils = ">=1.1.8" urllib3 = ">=1.26.4" -coverage = "*" pillow = ">=9.0.0" celery = ">=5.0.0" -jedi = "<0.18.0" wtforms = "<3.0.0" flask-wtf = "<1.0.0" zipp = "*" pycparser = "*" -poethepoet = "*" +sqlalchemy = "<1.4.0" +sqlalchemy_continuum = "<2.0.0" +psycopg2-binary = "<3.0.0" +MarkupSafe = "<2.1.0" +jedi = "<0.18.0" +ipython_genutils = "^0.2.0" Flask = "<2.0.0" -MarkupSafe = "<2.0.0" SQLAlchemy = "<1.4.0" flask-wiki = {git = "https://github.com/rero/flask-wiki.git", rev = "v0.1.0"} diff --git a/scripts/test b/scripts/test index a1817a30..12dbfbbb 100755 --- a/scripts/test +++ b/scripts/test @@ -41,8 +41,9 @@ fi # | click | 7.1.2 | <8.0.0 | 47833 | # | celery | 5.1.2 | <5.2.0 | 42498 | # | celery | 5.1.2 | <5.2.2 | 43738 | +# | click | 7.1.2 | <8.0.0 | 47833 | # +============================+===========+==========================+==========+ -safety check -i 42852 -i 42194 -i 45183 -i 44501 -i 40459 -i 47833 -i 42498 -i 43738 +safety check -i 42852 -i 42050 -i 42194 -i 45183 -i 44501 -i 40459 -i 42498 -i 43738 -i 47833 pydocstyle sonar tests docs isort --check-only --diff "${SCRIPT_PATH}/.." autoflake -c -r --remove-all-unused-imports --ignore-init-module-imports . &> /dev/null || { diff --git a/sonar/dedicated/hepvs/projects/resource.py b/sonar/dedicated/hepvs/projects/resource.py index 9b3f0899..1f9238c3 100644 --- a/sonar/dedicated/hepvs/projects/resource.py +++ b/sonar/dedicated/hepvs/projects/resource.py @@ -17,36 +17,39 @@ """HEP Valais Projects resource.""" +from flask_resources import ResponseHandler from flask_resources.serializers import JSONSerializer from invenio_records_resources.resources import \ RecordResourceConfig as BaseRecordResourceConfig -from invenio_records_resources.resources.records.response import RecordResponse +from invenio_records_resources.resources.records.headers import etag_headers from sonar.dedicated.hepvs.projects.serializers.csv import CSVSerializer from sonar.resources.projects.resource import \ RecordResourceConfig as BaseRecordResourceConfig -from sonar.resources.resources.responses import StreamResponse +from sonar.resources.resources.responses import StreamResponseHandler class RecordResourceConfig(BaseRecordResourceConfig): """HEP Valais Projects resource configuration.""" response_handlers = { - 'application/json': - RecordResponse(JSONSerializer()), - 'text/csv': - StreamResponse(CSVSerializer(csv_included_fields=[ - 'pid', 'name', 'approvalDate', 'projectSponsor', 'statusHep', - 'mainTeam', 'innerSearcher', 'secondaryTeam', 'externalPartners', - 'status', 'startDate', 'endDate', 'description', 'keywords', - 'realizationFramework', 'funding_funder_type', - 'funding_funder_name', 'funding_funder_number', - 'funding_fundingReceived', 'actorsInvolved', 'benefits', - 'impactOnFormation', 'impactOnProfessionalEnvironment', - 'impactOnPublicAction', 'promoteInnovation', - 'relatedToMandate_mandate', 'relatedToMandate_name', - 'relatedToMandate_briefDescription', 'educationalDocument', - 'searchResultsValorised' - ]), - filename='projects.csv') + 'application/json': ResponseHandler( + JSONSerializer(), + headers=etag_headers), + 'text/csv': StreamResponseHandler( + CSVSerializer(csv_included_fields=[ + 'pid', 'name', 'approvalDate', 'projectSponsor', 'statusHep', + 'mainTeam', 'innerSearcher', 'secondaryTeam', + 'externalPartners', 'status', 'startDate', 'endDate', + 'description', 'keywords', 'realizationFramework', + 'funding_funder_type', 'funding_funder_name', + 'funding_funder_number', 'funding_fundingReceived', + 'actorsInvolved', 'benefits', 'impactOnFormation', + 'impactOnProfessionalEnvironment', 'impactOnPublicAction', + 'promoteInnovation', 'relatedToMandate_mandate', + 'relatedToMandate_name', 'relatedToMandate_briefDescription', + 'educationalDocument', 'searchResultsValorised' + ]), + filename='projects.csv', + headers=etag_headers) } diff --git a/sonar/ext.py b/sonar/ext.py index 067a57f3..f6c4ed52 100644 --- a/sonar/ext.py +++ b/sonar/ext.py @@ -40,10 +40,10 @@ from sonar.modules.users.signals import add_full_name, user_registered_handler from sonar.modules.utils import get_language_value, get_specific_theme, \ get_switch_aai_providers, get_view_code -from sonar.resources.projects.resource import \ - RecordResource as ProjectRecordResource -from sonar.resources.projects.service import \ - RecordService as ProjectRecordService +from sonar.resources.projects.resource import ProjectsRecordResource, \ + ProjectsRecordResourceConfig +from sonar.resources.projects.service import ProjectsRecordService, \ + ProjectsRecordServiceConfig from sonar.signals import file_download_proxy from . import config_sonar @@ -122,6 +122,8 @@ def init_views(self, app): @app.route('/', defaults={ 'view': app.config.get('SONAR_APP_DEFAULT_ORGANISATION')}) + + @app.route('//') def index(view): """Homepage.""" @@ -206,8 +208,10 @@ def favicon(org): def create_resources(self): """Create resources.""" # Initialize the project resource with the corresponding service. - projects_resource = ProjectRecordResource( - service=ProjectRecordService()) + project_service = ProjectsRecordService(ProjectsRecordServiceConfig()) + projects_resource = ProjectsRecordResource( + service=project_service, + config=ProjectsRecordResourceConfig) self.resources['projects'] = projects_resource def get_endpoints(self): @@ -243,7 +247,7 @@ def register_blueprints(self, app): """Register the blueprints.""" # Register REST endpoint for projects resource. app.register_blueprint( - self.resources['projects'].as_blueprint('projects')) + self.resources['projects'].as_blueprint()) @app.before_request def set_accept_mimetype(): @@ -253,5 +257,5 @@ def set_accept_mimetype(): in flask_resources: https://github.com/inveniosoftware/flask-resources/blob/master/flask_resources/content_negotiation.py#L105 """ if request.args.get('format'): - request.accept_mimetypes = MIMEAccept([(request.args['format'], - 1)]) + request.accept_mimetypes = MIMEAccept([ + (request.args['format'],1)]) diff --git a/sonar/modules/documents/receivers.py b/sonar/modules/documents/receivers.py index ebc4e0ef..ff7347a4 100644 --- a/sonar/modules/documents/receivers.py +++ b/sonar/modules/documents/receivers.py @@ -23,6 +23,7 @@ from os import makedirs from os.path import exists, join +import click import pytz from flask import current_app @@ -57,7 +58,8 @@ def transform_harvested_records(sender=None, records=None, **kwargs): max_records = None if kwargs.get('name'): - print('Harvesting records from "{set}"'.format(set=kwargs.get('name'))) + click.echo( + 'Harvesting records from "{set}"'.format(set=kwargs.get('name'))) harvested_records = list(records) @@ -80,7 +82,7 @@ def transform_harvested_records(sender=None, records=None, **kwargs): for chunk in list(chunks(records, CHUNK_SIZE)): import_records.delay(chunk) - print('{count} records harvested in {time} seconds'.format( + click.echo('{count} records harvested in {time} seconds'.format( count=len(records), time=time.time() - start_time)) @@ -175,7 +177,7 @@ def export_json(sender=None, records=None, **kwargs): records_to_export = [] - print('{count} records harvested'.format(count=len(records))) + click.echo('{count} records harvested'.format(count=len(records))) for record in records: loader_schema = LoaderSchemaFactory.create(kwargs['name']) @@ -192,4 +194,4 @@ def export_json(sender=None, records=None, **kwargs): client = HegClient() client.upload_file(file_name, file_path) - print('{count} records exported'.format(count=len(records_to_export))) + click.echo('{count} records exported'.format(count=len(records_to_export))) diff --git a/sonar/modules/pdf_extractor/pdf_extractor.py b/sonar/modules/pdf_extractor/pdf_extractor.py index 48923b1d..26daf449 100644 --- a/sonar/modules/pdf_extractor/pdf_extractor.py +++ b/sonar/modules/pdf_extractor/pdf_extractor.py @@ -52,7 +52,7 @@ def api_is_alive(self): """ try: response, status = self.do_request('isalive', 'get') - except Exception: + except Exception as err: return False if status != 200: diff --git a/sonar/resources/projects/resource.py b/sonar/resources/projects/resource.py index ab837f4c..73c4246d 100644 --- a/sonar/resources/projects/resource.py +++ b/sonar/resources/projects/resource.py @@ -17,35 +17,52 @@ """Projects resource.""" +from flask_resources import ResponseHandler from flask_resources.serializers import JSONSerializer -from invenio_records_resources.resources import \ - RecordResourceConfig as BaseRecordResourceConfig -from invenio_records_resources.resources.records.response import RecordResponse +from invenio_records_resources.resources import RecordResourceConfig +from invenio_records_resources.resources.records.headers import etag_headers from sonar.resources.projects.serializers.csv import CSVSerializer -from sonar.resources.resource import RecordResource as BaseRecordResource -from sonar.resources.resources.responses import StreamResponse +from sonar.resources.resource import RecordResource +from sonar.resources.resources.responses import StreamResponseHandler -class RecordResourceConfig(BaseRecordResourceConfig): +class ProjectsRecordResourceConfig(RecordResourceConfig): """Projects resource configuration.""" + blueprint_name = 'projects' + url_prefix = "/projects/" resource_name = 'projects' - list_route = '/projects/' - item_route = f'{list_route}/' response_handlers = { - 'application/json': - RecordResponse(JSONSerializer()), - 'text/csv': - StreamResponse(CSVSerializer(csv_included_fields=[ - 'pid', 'name', 'description', 'startDate', 'endDate' - ]), - filename='projects.csv') + 'application/json': ResponseHandler( + JSONSerializer(), + headers=etag_headers), + 'text/csv': StreamResponseHandler( + CSVSerializer(csv_included_fields=[ + 'pid', 'name', 'description', 'startDate', 'endDate' + ]), + filename='projects.csv', + headers=etag_headers) } + # # Request parsing + # request_read_args = {} + # request_view_args = {"pid_value": ma.fields.Str()} + # request_search_args = SearchRequestArgsSchema + # request_headers = {"if_match": ma.fields.Int()} + # request_body_parsers = { + # "application/json": RequestBodyParser(JSONDeserializer()) + # } + # default_content_type = "application/json" + + # # Response handling + # response_handlers = { + # "application/json": ResponseHandler( + # JSONSerializer(), headers=etag_headers) + # } + # default_accept_mimetype = "application/json" -class RecordResource(BaseRecordResource): - """Projects resource".""" - default_config = RecordResourceConfig +class ProjectsRecordResource(RecordResource): + """Projects resource".""" diff --git a/sonar/resources/projects/service.py b/sonar/resources/projects/service.py index e6f12a27..24dcb626 100644 --- a/sonar/resources/projects/service.py +++ b/sonar/resources/projects/service.py @@ -17,31 +17,49 @@ """Projects service.""" +from invenio_records_resources.services import \ + SearchOptions as BaseSearchOptions +from invenio_records_resources.services.records.facets import TermsFacet +from invenio_records_resources.services.records.params import FacetsParam, \ + PaginationParam, QueryStrParam, SortParam +from invenio_records_resources.services.records.params.querystr import \ + SuggestQueryParser from invenio_records_resources.services.records.schema import \ - MarshmallowServiceSchema + ServiceSchemaWrapper from invenio_records_rest.utils import obj_or_import_string -from sonar.config import DEFAULT_AGGREGATION_SIZE from sonar.modules.organisations.api import current_organisation -from sonar.modules.query import and_term_filter from sonar.modules.utils import has_custom_resource -from ..service import RecordService as BaseRecordService -from ..service import RecordServiceConfig as BaseRecordServiceConfig +from ..service import RecordService, RecordServiceConfig from .api import Record, RecordComponent from .permissions import RecordPermissionPolicy from .results import RecordList -class RecordServiceConfig(BaseRecordServiceConfig): - """Projects service configuration.""" +class PreFacetsParam(FacetsParam): + """.""" - permission_policy_cls = RecordPermissionPolicy - record_cls = Record - result_list_cls = RecordList - search_sort_default = 'relevance' - search_sort_default_no_query = 'newest' - search_sort_options = { + def filter(self, search): + """Apply a pre filter on the search.""" + if not self._filters: + return search + + filters = list(self._filters.values()) + + post_filter = filters[0] + for f in filters[1:]: + post_filter |= f + + return search.filter(post_filter) + + +class SearchOptions(BaseSearchOptions): + """Search options.""" + + sort_default = 'relevance' + sort_default_no_query = 'newest' + sort_options = { 'relevance': { 'fields': ['_score'], }, @@ -55,40 +73,50 @@ class RecordServiceConfig(BaseRecordServiceConfig): 'fields': ['metadata.startDate'] } } - search_facets_options = { - 'aggs': { - 'user': { - 'terms': { - 'field': 'metadata.user.pid', - 'size': DEFAULT_AGGREGATION_SIZE - } - }, - 'organisation': { - 'terms': { - 'field': 'metadata.organisation.pid', - 'size': DEFAULT_AGGREGATION_SIZE - } - }, - 'status': { - 'terms': { - 'field': 'metadata.validation.status', - 'size': DEFAULT_AGGREGATION_SIZE - } - }, - }, - 'filters': { - 'user': and_term_filter('metadata.user.pid'), - 'organisation': and_term_filter('metadata.organisation.pid'), - 'status': and_term_filter('metadata.validation.status'), - } + + pagination_options = { + "default_results_per_page": 10, + "default_max_results": 10000 } - components = BaseRecordServiceConfig.components + [RecordComponent] + + params_interpreters_cls = [ + QueryStrParam, + PaginationParam, + SortParam, + PreFacetsParam + ] + + facets = { + 'user': TermsFacet(field='metadata.user.pid'), + 'organisation': TermsFacet(field='metadata.organisation.pid'), + 'status': TermsFacet(field='metadata.validation.status') + } + + suggest_parser_cls = SuggestQueryParser.factory(fields=[ + "metadata.name.suggest", "metadata.projectSponsor.suggest", + "metadata.innerSearcher.suggest", "metadata.keywords.suggest" + ]) + + + +class ProjectsRecordServiceConfig(RecordServiceConfig): + """Projects service configuration.""" + + permission_policy_cls = RecordPermissionPolicy + + record_cls = Record + + result_list_cls = RecordList + + search = SearchOptions + + components = RecordServiceConfig.components + [RecordComponent] -class RecordService(BaseRecordService): +class ProjectsRecordService(RecordService): """Projects service.""" - default_config = RecordServiceConfig + default_config = ProjectsRecordServiceConfig @property def schema(self): @@ -101,4 +129,4 @@ def schema(self): schema = obj_or_import_string(schema_path) - return MarshmallowServiceSchema(self, schema=schema) + return ServiceSchemaWrapper(self, schema=schema) diff --git a/sonar/resources/resource.py b/sonar/resources/resource.py index 5d50835c..075efaa8 100644 --- a/sonar/resources/resource.py +++ b/sonar/resources/resource.py @@ -28,17 +28,6 @@ class RecordResource(BaseRecordResource): """Base record resource.""" - def __init__(self, service=None): - """Constructor. - - Needed to override parent constructor because `config` must not be set - as it is calculated when calling the property. - - :param service: Record service. - """ - self.bp_name = None - self.service = service - @property def config(self): """Get the configuration depending on the orgnanisation. @@ -56,3 +45,7 @@ def config(self): f'{resource_name}.resource:RecordResourceConfig') return obj_or_import_string(resource_config) + + @config.setter + def config(self, value): + self.default_config = value diff --git a/sonar/resources/resources/responses.py b/sonar/resources/resources/responses.py index 814c22d6..76b9b235 100644 --- a/sonar/resources/resources/responses.py +++ b/sonar/resources/resources/responses.py @@ -17,38 +17,45 @@ """SONAR resources responses.""" -from flask import Response, make_response -from flask_resources.responses import Response as ResourceResponse +from flask import Response +from flask import make_response as flask_make_response +from flask_resources.responses import ResponseHandler -class StreamResponse(ResourceResponse): +class StreamResponseHandler(ResponseHandler): """Stream response.""" filename = None - def __init__(self, serializer=None, **kwargs): + def __init__(self, serializer, filename='records', headers=None): """Stream response initialization. - Sets the filename for attachment if present in kwargs. - + :param filename: File name. :param serializer: Record serializer. """ - self.filename = kwargs.pop('filename', 'records') - super().__init__(serializer) - - def make_list_response(self, content, code=200): - """Builds a response for a list of objects. - - :param content: Content to send. - :param code: Status code. - :returns: Response object. - """ - response = make_response( - "" if content is None else Response( - self.serializer.serialize_object_list(content)), - code, - self.make_headers(), + self.filename = filename + super().__init__(serializer=serializer, headers=headers) + + + def make_response(self, obj_or_list, code, many=False): + """Builds a response for one object.""" + # If view returns a response, bypass the serialization. + if isinstance(obj_or_list, Response): + return obj_or_list + + # https://flask.palletsprojects.com/en/1.1.x/api/#flask.Flask.make_response + # (body, status, header) + if many: + serialize = self.serializer.serialize_object_list + else: + serialize = self.serializer.serialize_object + + response = flask_make_response( + "" if obj_or_list is None else Response( + self.serializer.serialize_object_list(obj_or_list)), + code ) + response.headers[ 'Content-Disposition'] = f'attachment; filename={self.filename}' return response diff --git a/sonar/resources/service.py b/sonar/resources/service.py index c1e352bf..db582b4d 100644 --- a/sonar/resources/service.py +++ b/sonar/resources/service.py @@ -24,20 +24,15 @@ from invenio_records_resources.services.records import \ RecordService as BaseRecordService -from .params import FilterParams - class RecordServiceConfig(BaseRecordServiceConfig): """Service factory configuration.""" - search_params_interpreters_cls = \ - [FilterParams] + BaseRecordServiceConfig.search_params_interpreters_cls - class RecordService(BaseRecordService): """SONAR resources base service class.""" - def create(self, identity, data, links_config=None): + def create(self, identity, data): """Create a record. :param identity: Identity of user creating the record. @@ -48,7 +43,7 @@ def create(self, identity, data, links_config=None): identity.provides.add(UserNeed(1)) identity.provides.add(Need(method='system_role', value='any_user')) - return super().create(identity, data, links_config) + return super().create(identity, data) def bulk_reindex(self): """Send all records to the index queue and process indexing.""" diff --git a/sonar/suggestions/rest.py b/sonar/suggestions/rest.py index b8346599..97704244 100644 --- a/sonar/suggestions/rest.py +++ b/sonar/suggestions/rest.py @@ -46,10 +46,10 @@ def completion(): search = None try: service = sonar.service(resource) - search = service.config.search_cls(index=resource) - except Exception: - for doc_type, config in current_app.config.get( - 'RECORDS_REST_ENDPOINTS').items(): + search = service.config.search.search_cls(index=resource) + except Exception as err: + endpoints = current_app.config.get('RECORDS_REST_ENDPOINTS') + for doc_type, config in endpoints.items(): if config.get('search_index') == resource: search = config['search_class']() @@ -68,7 +68,7 @@ def completion(): 'field': field, 'skip_duplicates': True }) - for i, suggestion in search.execute().suggest.to_dict().items(): + for _, suggestion in search.execute().suggest.to_dict().items(): results = results + [ option['text'] for option in suggestion[0]['options'] ] diff --git a/tests/api/projects/test_projects_filters.py b/tests/api/projects/test_projects_filters.py index b65c1c61..f29cae28 100644 --- a/tests/api/projects/test_projects_filters.py +++ b/tests/api/projects/test_projects_filters.py @@ -28,20 +28,20 @@ def test_filters(client, superuser, project, make_project): login_user_via_session(client, email=superuser['email']) # Without filter - res = client.get(url_for('projects.projects_list')) + res = client.get(url_for('projects.search')) assert res.status_code == 200 assert res.json['hits']['total'] == 2 assert len(res.json['aggregations']['user']['buckets']) == 2 # Existing user filter res = client.get( - url_for('projects.projects_list', user='orgadmin')) + url_for('projects.search', user='orgadmin')) assert res.status_code == 200 assert res.json['hits']['total'] == 1 assert len(res.json['aggregations']['user']['buckets']) == 1 # Non existing organisation filter - res = client.get(url_for('projects.projects_list', user='unknown')) + res = client.get(url_for('projects.search', user='unknown')) assert res.status_code == 200 assert res.json['hits']['total'] == 0 assert not res.json['aggregations']['user']['buckets'] diff --git a/tests/api/projects/test_projects_permissions.py b/tests/api/projects/test_projects_permissions.py index 9f0e4fa7..16fa9583 100644 --- a/tests/api/projects/test_projects_permissions.py +++ b/tests/api/projects/test_projects_permissions.py @@ -36,17 +36,17 @@ def test_list(app, client, make_project, superuser, admin, moderator, time.sleep(1) # Not logged - res = client.get(url_for('projects.projects_list')) + res = client.get(url_for('projects.search')) assert res.status_code == 403 # Logged as user login_user_via_session(client, email=user['email']) - res = client.get(url_for('projects.projects_list')) + res = client.get(url_for('projects.search')) assert res.status_code == 403 # Logged as superuser login_user_via_session(client, email=superuser['email']) - res = client.get(url_for('projects.projects_list')) + res = client.get(url_for('projects.search')) assert res.status_code == 200 assert res.json['hits']['total'] == 3 assert 'organisation' in res.json['aggregations'] @@ -54,7 +54,7 @@ def test_list(app, client, make_project, superuser, admin, moderator, # Logged as submitter login_user_via_session(client, email=submitter['email']) - res = client.get(url_for('projects.projects_list')) + res = client.get(url_for('projects.search')) assert res.status_code == 200 assert res.json['hits']['total'] == 1 assert 'organisation' not in res.json['aggregations'] @@ -62,13 +62,13 @@ def test_list(app, client, make_project, superuser, admin, moderator, # Query string res = client.get( - url_for('projects.projects_list', q='metadata.name.suggest:Proj')) + url_for('projects.search', q='metadata.name.suggest:Proj')) assert res.status_code == 200 assert res.json['hits']['total'] == 2 # Logged as moderator login_user_via_session(client, email=moderator['email']) - res = client.get(url_for('projects.projects_list')) + res = client.get(url_for('projects.search')) assert res.status_code == 200 assert res.json['hits']['total'] == 2 assert 'organisation' not in res.json['aggregations'] @@ -76,7 +76,7 @@ def test_list(app, client, make_project, superuser, admin, moderator, # Logged as admin login_user_via_session(client, email=admin['email']) - res = client.get(url_for('projects.projects_list')) + res = client.get(url_for('projects.search')) assert res.status_code == 200 assert res.json['hits']['total'] == 2 assert 'organisation' not in res.json['aggregations'] @@ -87,7 +87,7 @@ def test_list(app, client, make_project, superuser, admin, moderator, lambda *args, **kwargs: [any_user]) new_user = make_user('user', 'org', 'admin_access') login_user_via_session(client, email=new_user['email']) - res = client.get(url_for('projects.projects_list')) + res = client.get(url_for('projects.search')) assert res.status_code == 200 assert res.json['hits']['total'] == 3 @@ -101,42 +101,42 @@ def test_create(client, project_json, superuser, admin, moderator, submitter, } # Not logged - res = client.post(url_for('projects.projects_list'), + res = client.post(url_for('projects.search'), data=json.dumps(project_json), headers=headers) assert res.status_code == 403 # User login_user_via_session(client, email=user['email']) - res = client.post(url_for('projects.projects_list'), + res = client.post(url_for('projects.search'), data=json.dumps(project_json), headers=headers) assert res.status_code == 403 # submitter login_user_via_session(client, email=submitter['email']) - res = client.post(url_for('projects.projects_list'), + res = client.post(url_for('projects.search'), data=json.dumps(project_json), headers=headers) assert res.status_code == 201 # Moderator login_user_via_session(client, email=moderator['email']) - res = client.post(url_for('projects.projects_list'), + res = client.post(url_for('projects.search'), data=json.dumps(project_json), headers=headers) assert res.status_code == 201 # Admin login_user_via_session(client, email=admin['email']) - res = client.post(url_for('projects.projects_list'), + res = client.post(url_for('projects.search'), data=json.dumps(project_json), headers=headers) assert res.status_code == 201 # Super user login_user_via_session(client, email=superuser['email']) - res = client.post(url_for('projects.projects_list'), + res = client.post(url_for('projects.search'), data=json.dumps(project_json), headers=headers) assert res.status_code == 201 @@ -150,56 +150,56 @@ def test_read(client, make_project, make_user, superuser, admin, moderator, # Not logged res = client.get( - url_for('projects.projects_item', pid_value=project1['id'])) + url_for('projects.read', pid_value=project1['id'])) assert res.status_code == 403 # Logged as user login_user_via_session(client, email=user['email']) res = client.get( - url_for('projects.projects_item', pid_value=project1['id'])) + url_for('projects.read', pid_value=project1['id'])) assert res.status_code == 403 # Logged as submitter login_user_via_session(client, email=submitter['email']) res = client.get( - url_for('projects.projects_item', pid_value=project1['id'])) + url_for('projects.read', pid_value=project1['id'])) assert res.status_code == 200 res = client.get( - url_for('projects.projects_item', pid_value=project2['id'])) + url_for('projects.read', pid_value=project2['id'])) assert res.status_code == 200 # Logged as moderator login_user_via_session(client, email=moderator['email']) res = client.get( - url_for('projects.projects_item', pid_value=project1['id'])) + url_for('projects.read', pid_value=project1['id'])) assert res.status_code == 200 res = client.get( - url_for('projects.projects_item', pid_value=project2['id'])) + url_for('projects.read', pid_value=project2['id'])) assert res.status_code == 200 # Logged as admin login_user_via_session(client, email=admin['email']) res = client.get( - url_for('projects.projects_item', pid_value=project1['id'])) + url_for('projects.read', pid_value=project1['id'])) assert res.status_code == 200 res = client.get( - url_for('projects.projects_item', pid_value=project2['id'])) + url_for('projects.read', pid_value=project2['id'])) assert res.status_code == 200 # Logged as admin of other organisation other_admin = make_user('admin', 'org2', access='admin-access') login_user_via_session(client, email=other_admin['email']) res = client.get( - url_for('projects.projects_item', pid_value=project1['id'])) + url_for('projects.read', pid_value=project1['id'])) assert res.status_code == 200 # Logged as superuser login_user_via_session(client, email=superuser['email']) res = client.get( - url_for('projects.projects_item', pid_value=project1['id'])) + url_for('projects.read', pid_value=project1['id'])) assert res.status_code == 200 @@ -215,7 +215,7 @@ def test_update(client, make_project, superuser, admin, moderator, submitter, } # Not logged - res = client.put(url_for('projects.projects_item', + res = client.put(url_for('projects.read', pid_value=project1['id']), data=json.dumps(project1.data), headers=headers) @@ -223,7 +223,7 @@ def test_update(client, make_project, superuser, admin, moderator, submitter, # Logged as user login_user_via_session(client, email=user['email']) - res = client.put(url_for('projects.projects_item', + res = client.put(url_for('projects.read', pid_value=project1['id']), data=json.dumps(project1.data), headers=headers) @@ -231,13 +231,13 @@ def test_update(client, make_project, superuser, admin, moderator, submitter, # Logged as submitter login_user_via_session(client, email=submitter['email']) - res = client.put(url_for('projects.projects_item', + res = client.put(url_for('projects.read', pid_value=project1['id']), data=json.dumps(project1.data), headers=headers) assert res.status_code == 200 - res = client.put(url_for('projects.projects_item', + res = client.put(url_for('projects.read', pid_value=project2['id']), data=json.dumps(project2.data), headers=headers) @@ -245,13 +245,13 @@ def test_update(client, make_project, superuser, admin, moderator, submitter, # Logged as moderator login_user_via_session(client, email=moderator['email']) - res = client.put(url_for('projects.projects_item', + res = client.put(url_for('projects.read', pid_value=project1['id']), data=json.dumps(project1.data), headers=headers) assert res.status_code == 200 - res = client.put(url_for('projects.projects_item', + res = client.put(url_for('projects.read', pid_value=project2['id']), data=json.dumps(project2.data), headers=headers) @@ -259,13 +259,13 @@ def test_update(client, make_project, superuser, admin, moderator, submitter, # Logged as admin login_user_via_session(client, email=admin['email']) - res = client.put(url_for('projects.projects_item', + res = client.put(url_for('projects.read', pid_value=project1['id']), data=json.dumps(project1.data), headers=headers) assert res.status_code == 200 - res = client.put(url_for('projects.projects_item', + res = client.put(url_for('projects.read', pid_value=project2['id']), data=json.dumps(project2.data), headers=headers) @@ -273,13 +273,13 @@ def test_update(client, make_project, superuser, admin, moderator, submitter, # Logged as superuser login_user_via_session(client, email=superuser['email']) - res = client.put(url_for('projects.projects_item', + res = client.put(url_for('projects.read', pid_value=project1['id']), data=json.dumps(project1.data), headers=headers) assert res.status_code == 200 - res = client.put(url_for('projects.projects_item', + res = client.put(url_for('projects.read', pid_value=project2['id']), data=json.dumps(project2.data), headers=headers) @@ -287,7 +287,7 @@ def test_update(client, make_project, superuser, admin, moderator, submitter, # Save status rejected --> OK project2.data['metadata']['validation']['status'] = 'rejected' - res = client.put(url_for('projects.projects_item', + res = client.put(url_for('projects.read', pid_value=project2['id']), data=json.dumps(project2.data), headers=headers) @@ -295,7 +295,7 @@ def test_update(client, make_project, superuser, admin, moderator, submitter, # Record is rejected, new save is not possible project2.data['metadata']['validation']['status'] = 'validated' - res = client.put(url_for('projects.projects_item', + res = client.put(url_for('projects.read', pid_value=project2['id']), data=json.dumps(project2.data), headers=headers) @@ -310,19 +310,19 @@ def test_delete(client, db, document, make_project, superuser, admin, # Not logged res = client.delete( - url_for('projects.projects_item', pid_value=project1['id'])) + url_for('projects.read', pid_value=project1['id'])) assert res.status_code == 403 # Logged as user login_user_via_session(client, email=user['email']) res = client.delete( - url_for('projects.projects_item', pid_value=project1['id'])) + url_for('projects.read', pid_value=project1['id'])) assert res.status_code == 403 # Logged as submitter login_user_via_session(client, email=submitter['email']) res = client.delete( - url_for('projects.projects_item', pid_value=project2['id'])) + url_for('projects.read', pid_value=project2['id'])) assert res.status_code == 403 project1 = make_project('submitter', 'org') @@ -330,11 +330,11 @@ def test_delete(client, db, document, make_project, superuser, admin, # Logged as moderator login_user_via_session(client, email=moderator['email']) res = client.delete( - url_for('projects.projects_item', pid_value=project2['id'])) + url_for('projects.read', pid_value=project2['id'])) assert res.status_code == 403 res = client.delete( - url_for('projects.projects_item', pid_value=project1['id'])) + url_for('projects.read', pid_value=project1['id'])) assert res.status_code == 204 project1 = make_project('submitter', 'org') @@ -342,11 +342,11 @@ def test_delete(client, db, document, make_project, superuser, admin, # Logged as admin login_user_via_session(client, email=admin['email']) res = client.delete( - url_for('projects.projects_item', pid_value=project2['id'])) + url_for('projects.read', pid_value=project2['id'])) assert res.status_code == 403 res = client.delete( - url_for('projects.projects_item', pid_value=project1['id'])) + url_for('projects.read', pid_value=project1['id'])) assert res.status_code == 204 project1 = make_project('submitter', 'org') @@ -354,7 +354,7 @@ def test_delete(client, db, document, make_project, superuser, admin, # Logged as superuser login_user_via_session(client, email=superuser['email']) res = client.delete( - url_for('projects.projects_item', pid_value=project1['id'])) + url_for('projects.read', pid_value=project1['id'])) assert res.status_code == 204 project1 = make_project('submitter', 'org') @@ -369,5 +369,5 @@ def test_delete(client, db, document, make_project, superuser, admin, document.reindex() res = client.delete( - url_for('projects.projects_item', pid_value=project1['id'])) + url_for('projects.read', pid_value=project1['id'])) assert res.status_code == 403 diff --git a/tests/api/suggestions/test_suggestions_rest.py b/tests/api/suggestions/test_suggestions_rest.py index 0e0642c0..cbb32267 100644 --- a/tests/api/suggestions/test_suggestions_rest.py +++ b/tests/api/suggestions/test_suggestions_rest.py @@ -30,7 +30,7 @@ def test_completion(client, project_json, make_user): login_user_via_session(client, email=user['email']) project_json['metadata']['projectSponsor'] = 'Sponsor 1' - res = client.post(url_for('projects.projects_list'), + res = client.post(url_for('projects.search'), data=json.dumps(project_json)) # Ensure project is indexed current_search.flush_and_refresh(index='projects') diff --git a/tests/api/test_ext_before_request.py b/tests/api/test_ext_before_request.py index 9b16c9ae..0bd3a5c8 100644 --- a/tests/api/test_ext_before_request.py +++ b/tests/api/test_ext_before_request.py @@ -25,10 +25,10 @@ def test_ext_before_request(client, superuser): """Test before request hook in extension.""" login_user_via_session(client, email=superuser['email']) - res = client.get(url_for('projects.projects_list')) + res = client.get(url_for('projects.search')) assert res.status_code == 200 assert request.accept_mimetypes.find('text/csv') == -1 - res = client.get(url_for('projects.projects_list', format='text/csv')) + res = client.get(url_for('projects.search', format='text/csv')) assert res.status_code == 200 assert request.accept_mimetypes.find('text/csv') == 0 diff --git a/tests/conftest.py b/tests/conftest.py index 31952e0e..827e708f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1001,7 +1001,7 @@ def mock_thumbnail_creation(monkeypatch): b'Fake thumbnail image content') -@pytest.yield_fixture +@pytest.fixture def without_oaiset_signals(app): """Temporary disable oaiset signals.""" from invenio_oaiserver import current_oaiserver diff --git a/tests/ui/deposits/test_deposits_cli.py b/tests/ui/deposits/test_deposits_cli.py index ab957784..15c56080 100644 --- a/tests/ui/deposits/test_deposits_cli.py +++ b/tests/ui/deposits/test_deposits_cli.py @@ -29,7 +29,7 @@ def test_create(app, script_info): directory = os.path.join(app.instance_path, 'files') - os.mkdir(directory, 0o755) + os.makedirs(directory, 0o755, exist_ok=True) result = runner.invoke(cli.create, obj=script_info) assert 'Location #1 created successfully' in result.output diff --git a/tests/ui/documents/test_documents_receivers.py b/tests/ui/documents/test_documents_receivers.py index 43e8a896..3e0e1b4a 100644 --- a/tests/ui/documents/test_documents_receivers.py +++ b/tests/ui/documents/test_documents_receivers.py @@ -77,7 +77,7 @@ def test_enrich_document_data(app, db, document, pdf_file): enrich_document_data(record=document, index='documents', json=json) assert len(json['fulltext']) == 1 - assert json['fulltext'][0].startswith('PHYSICAL REVIEW B 99') + assert 'PHYSICAL REVIEW B 99' in json['fulltext'][0] def test_chunks(): diff --git a/tests/ui/pdf_extractor/conftest.py b/tests/ui/pdf_extractor/conftest.py index f3197899..c087d7ce 100644 --- a/tests/ui/pdf_extractor/conftest.py +++ b/tests/ui/pdf_extractor/conftest.py @@ -38,7 +38,6 @@ class MockResponse: status_code = 200 text = output - monkeypatch.setattr(requests, 'post', lambda *args, **kwargs: MockResponse) diff --git a/tests/unit/resources/test_resources_resource.py b/tests/unit/resources/test_resources_resource.py index 882675e7..a6ba409b 100644 --- a/tests/unit/resources/test_resources_resource.py +++ b/tests/unit/resources/test_resources_resource.py @@ -22,7 +22,7 @@ from sonar.dedicated.hepvs.projects.resource import \ RecordResourceConfig as HEPVSRecordResourceConfig -from sonar.resources.projects.resource import RecordResourceConfig +from sonar.resources.projects.resource import ProjectsRecordResourceConfig def test_config(app, client, make_user, admin): @@ -31,12 +31,12 @@ def test_config(app, client, make_user, admin): # Not logged assert isinstance(app.extensions['sonar'].resources['projects'].config(), - RecordResourceConfig) + ProjectsRecordResourceConfig) # Logged as user not belonging to HEPVS. login_user_via_view(client, email=admin['email'], password='123456') assert isinstance(app.extensions['sonar'].resources['projects'].config(), - RecordResourceConfig) + ProjectsRecordResourceConfig) client.get(url_for_security('logout')) @@ -51,4 +51,4 @@ def test_config(app, client, make_user, admin): delattr(app.extensions['sonar'].resources['projects'].default_config, 'resource_name') assert isinstance(app.extensions['sonar'].resources['projects'].config(), - RecordResourceConfig) + ProjectsRecordResourceConfig)