From 4b1d7d2cc64c45b105427ffc8453beb857900ba3 Mon Sep 17 00:00:00 2001 From: yoldas Date: Wed, 3 Jan 2024 00:31:58 +0000 Subject: [PATCH 01/40] bump tj-actions/changed-files from 23 to 41 in /.github/workflows Bumps tj-actions/changed-files from 23 to 41. --- .github/workflows/check_release_version.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check_release_version.yml b/.github/workflows/check_release_version.yml index e5db52d7..3f441c5f 100644 --- a/.github/workflows/check_release_version.yml +++ b/.github/workflows/check_release_version.yml @@ -14,7 +14,7 @@ jobs: - name: Get specific changed files id: changed-files-specific - uses: tj-actions/changed-files@v23 + uses: tj-actions/changed-files@v41 with: files: | .release-version From cfd550671cf706d724053d0a42b21851bcb6243d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Jan 2024 11:30:32 +0000 Subject: [PATCH 02/40] Bump requests from 2.28.2 to 2.31.0 Bumps [requests](https://github.com/psf/requests) from 2.28.2 to 2.31.0. - [Release notes](https://github.com/psf/requests/releases) - [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md) - [Commits](https://github.com/psf/requests/compare/v2.28.2...v2.31.0) --- updated-dependencies: - dependency-name: requests dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- Pipfile | 2 +- Pipfile.lock | 125 +++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 108 insertions(+), 19 deletions(-) diff --git a/Pipfile b/Pipfile index 517b5d29..aa55e1bb 100644 --- a/Pipfile +++ b/Pipfile @@ -20,7 +20,7 @@ responses = "*" colorlog = "~=6.7" more-itertools = "~=9.0" python-dotenv = "~=0.21" -requests = "~=2.28" +requests = "~=2.31" slackclient = "~=2.9" lab-share-lib = {editable = false,git = 'https://github.com/sanger/lab-share-lib.git',ref = 'v0.1.6'} requests-mock = "*" diff --git a/Pipfile.lock b/Pipfile.lock index 34e445b7..20141880 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "9943c70cc967f872b684c4f5edc4da62311d36b5f515abf35d0dccbe6b451340" + "sha256": "1758e5526bc765996440cdc1d3179fc09c15f9d1622dfb6595ebc64106577a1a" }, "pipfile-spec": 6, "requires": { @@ -135,19 +135,107 @@ }, "certifi": { "hashes": [ - "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3", - "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18" + "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1", + "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474" ], "markers": "python_version >= '3.6'", - "version": "==2022.12.7" + "version": "==2023.11.17" }, "charset-normalizer": { "hashes": [ - "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845", - "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f" + "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027", + "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087", + "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786", + "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", + "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09", + "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185", + "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", + "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e", + "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519", + "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898", + "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269", + "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3", + "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f", + "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6", + "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8", + "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a", + "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73", + "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", + "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714", + "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2", + "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc", + "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", + "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d", + "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", + "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", + "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269", + "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", + "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d", + "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a", + "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", + "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", + "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d", + "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0", + "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", + "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", + "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac", + "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25", + "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8", + "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", + "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", + "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2", + "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", + "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", + "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5", + "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99", + "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c", + "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", + "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811", + "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", + "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", + "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03", + "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", + "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04", + "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c", + "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", + "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458", + "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", + "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99", + "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985", + "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537", + "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238", + "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f", + "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d", + "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796", + "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a", + "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", + "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8", + "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c", + "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5", + "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5", + "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711", + "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4", + "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6", + "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c", + "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", + "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4", + "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", + "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", + "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12", + "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c", + "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae", + "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8", + "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887", + "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b", + "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", + "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", + "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", + "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33", + "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519", + "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561" ], - "markers": "python_full_version >= '3.6.0'", - "version": "==2.1.1" + "markers": "python_full_version >= '3.7.0'", + "version": "==3.3.2" }, "colorlog": { "hashes": [ @@ -266,11 +354,11 @@ }, "idna": { "hashes": [ - "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4", - "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2" + "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca", + "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f" ], "markers": "python_version >= '3.5'", - "version": "==3.4" + "version": "==3.6" }, "lab-share-lib": { "git": "https://github.com/sanger/lab-share-lib.git", @@ -436,11 +524,12 @@ }, "requests": { "hashes": [ - "sha256:64299f4909223da747622c030b781c0d7811e359c37124b4bd368fb8c6518baa", - "sha256:98b1b2782e3c6c4904938b84c0eb932721069dfdb9134313beff7c83c2df24bf" + "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", + "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1" ], "index": "pypi", - "version": "==2.28.2" + "markers": "python_version >= '3.7'", + "version": "==2.31.0" }, "requests-mock": { "hashes": [ @@ -483,11 +572,11 @@ }, "urllib3": { "hashes": [ - "sha256:076907bf8fd355cde77728471316625a4d2f7e713c125f51953bb5b3eecf4f72", - "sha256:75edcdc2f7d85b137124a6c3c9fc3933cdeaa12ecb9a6a959f22797a0feca7e1" + "sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3", + "sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==1.26.14" + "markers": "python_version >= '3.8'", + "version": "==2.1.0" }, "yarl": { "hashes": [ From ec56b63420ee6e9fed28c78c05a21da7c2e770ca Mon Sep 17 00:00:00 2001 From: Stephen <519327+stevieing@users.noreply.github.com> Date: Tue, 16 Jan 2024 11:31:45 +0000 Subject: [PATCH 03/40] Updated readme to include snappy fix. --- README.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/README.md b/README.md index 841ec181..d4959497 100644 --- a/README.md +++ b/README.md @@ -58,3 +58,31 @@ This project uses a Docker image as the unit of deployment. Update `.release-ver major/minor/patch. On merging a pull request into *develop* or *master*, a release will be created along with the Docker image associated to that release. +## Snappy + +``` +If when you install the dependencies and you see the following error: + +[pipenv.exceptions.InstallError]: src/snappy/snappymodule.cc:33:10: fatal error: 'snappy-c.h' file not found +[pipenv.exceptions.InstallError]: #include +[pipenv.exceptions.InstallError]: ^~~~~~~~~~~~ +[pipenv.exceptions.InstallError]: 1 error generated. +[pipenv.exceptions.InstallError]: error: command '/usr/bin/clang' failed with exit code 1 +[pipenv.exceptions.InstallError]: [end of output] +[pipenv.exceptions.InstallError]: +[pipenv.exceptions.InstallError]: note: This error originates from a subprocess, and is likely not a problem with pip. +[pipenv.exceptions.InstallError]: ERROR: Failed building wheel for python-snappy +[pipenv.exceptions.InstallError]: ERROR: Could not build wheels for python-snappy, which is required to install pyproject.toml-based projects +ERROR: Couldn't install package: {} + Package installation failed... +``` + +You need to install snappy and set the path: + +``` +brew install snappy +``` + +``` +pip install --global-option=build_ext --global-option='-I/opt/homebrew/include' --global-option='-L/opt/homebrew/lib' python-snappy +``` From 0d2856870e852838dcfeb1b243deb642dc0a3a71 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Jan 2024 11:55:49 +0000 Subject: [PATCH 04/40] Bump certifi from 2022.12.7 to 2023.7.22 Bumps [certifi](https://github.com/certifi/python-certifi) from 2022.12.7 to 2023.7.22. - [Commits](https://github.com/certifi/python-certifi/compare/2022.12.07...2023.07.22) --- updated-dependencies: - dependency-name: certifi dependency-type: indirect ... Signed-off-by: dependabot[bot] --- Pipfile.lock | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Pipfile.lock b/Pipfile.lock index 20141880..04344761 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -135,11 +135,12 @@ }, "certifi": { "hashes": [ - "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1", - "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474" + "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082", + "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9" ], + "index": "pypi", "markers": "python_version >= '3.6'", - "version": "==2023.11.17" + "version": "==2023.7.22" }, "charset-normalizer": { "hashes": [ From f5e1121e285a72ff9cb99dd8d3f15721226b7b5b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Jan 2024 11:55:50 +0000 Subject: [PATCH 05/40] Bump urllib3 from 1.26.14 to 1.26.18 Bumps [urllib3](https://github.com/urllib3/urllib3) from 1.26.14 to 1.26.18. - [Release notes](https://github.com/urllib3/urllib3/releases) - [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst) - [Commits](https://github.com/urllib3/urllib3/compare/1.26.14...1.26.18) --- updated-dependencies: - dependency-name: urllib3 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- Pipfile.lock | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Pipfile.lock b/Pipfile.lock index 20141880..25da0802 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -572,11 +572,12 @@ }, "urllib3": { "hashes": [ - "sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3", - "sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54" + "sha256:34b97092d7e0a3a8cf7cd10e386f401b3737364026c45e622aa02903dffe0f07", + "sha256:f8ecc1bba5667413457c529ab955bf8c67b45db799d159066261719e328580a0" ], - "markers": "python_version >= '3.8'", - "version": "==2.1.0" + "index": "pypi", + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", + "version": "==1.26.18" }, "yarl": { "hashes": [ From 5110edb7ea6402151c0a9cad00927b5b3a9c9d83 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Jan 2024 11:55:56 +0000 Subject: [PATCH 06/40] Bump aiohttp from 3.8.3 to 3.9.0 Bumps [aiohttp](https://github.com/aio-libs/aiohttp) from 3.8.3 to 3.9.0. - [Release notes](https://github.com/aio-libs/aiohttp/releases) - [Changelog](https://github.com/aio-libs/aiohttp/blob/master/CHANGES.rst) - [Commits](https://github.com/aio-libs/aiohttp/compare/v3.8.3...v3.9.0) --- updated-dependencies: - dependency-name: aiohttp dependency-type: indirect ... Signed-off-by: dependabot[bot] --- Pipfile.lock | 497 ++++++++++++++++++++++++++------------------------- 1 file changed, 253 insertions(+), 244 deletions(-) diff --git a/Pipfile.lock b/Pipfile.lock index 20141880..6b9b9521 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -18,96 +18,86 @@ "default": { "aiohttp": { "hashes": [ - "sha256:02f9a2c72fc95d59b881cf38a4b2be9381b9527f9d328771e90f72ac76f31ad8", - "sha256:059a91e88f2c00fe40aed9031b3606c3f311414f86a90d696dd982e7aec48142", - "sha256:05a3c31c6d7cd08c149e50dc7aa2568317f5844acd745621983380597f027a18", - "sha256:08c78317e950e0762c2983f4dd58dc5e6c9ff75c8a0efeae299d363d439c8e34", - "sha256:09e28f572b21642128ef31f4e8372adb6888846f32fecb288c8b0457597ba61a", - "sha256:0d2c6d8c6872df4a6ec37d2ede71eff62395b9e337b4e18efd2177de883a5033", - "sha256:16c121ba0b1ec2b44b73e3a8a171c4f999b33929cd2397124a8c7fcfc8cd9e06", - "sha256:1d90043c1882067f1bd26196d5d2db9aa6d268def3293ed5fb317e13c9413ea4", - "sha256:1e56b9cafcd6531bab5d9b2e890bb4937f4165109fe98e2b98ef0dcfcb06ee9d", - "sha256:20acae4f268317bb975671e375493dbdbc67cddb5f6c71eebdb85b34444ac46b", - "sha256:21b30885a63c3f4ff5b77a5d6caf008b037cb521a5f33eab445dc566f6d092cc", - "sha256:21d69797eb951f155026651f7e9362877334508d39c2fc37bd04ff55b2007091", - "sha256:256deb4b29fe5e47893fa32e1de2d73c3afe7407738bd3c63829874661d4822d", - "sha256:25892c92bee6d9449ffac82c2fe257f3a6f297792cdb18ad784737d61e7a9a85", - "sha256:2ca9af5f8f5812d475c5259393f52d712f6d5f0d7fdad9acdb1107dd9e3cb7eb", - "sha256:2d252771fc85e0cf8da0b823157962d70639e63cb9b578b1dec9868dd1f4f937", - "sha256:2dea10edfa1a54098703cb7acaa665c07b4e7568472a47f4e64e6319d3821ccf", - "sha256:2df5f139233060578d8c2c975128fb231a89ca0a462b35d4b5fcf7c501ebdbe1", - "sha256:2feebbb6074cdbd1ac276dbd737b40e890a1361b3cc30b74ac2f5e24aab41f7b", - "sha256:309aa21c1d54b8ef0723181d430347d7452daaff93e8e2363db8e75c72c2fb2d", - "sha256:3828fb41b7203176b82fe5d699e0d845435f2374750a44b480ea6b930f6be269", - "sha256:398701865e7a9565d49189f6c90868efaca21be65c725fc87fc305906be915da", - "sha256:43046a319664a04b146f81b40e1545d4c8ac7b7dd04c47e40bf09f65f2437346", - "sha256:437399385f2abcd634865705bdc180c8314124b98299d54fe1d4c8990f2f9494", - "sha256:45d88b016c849d74ebc6f2b6e8bc17cabf26e7e40c0661ddd8fae4c00f015697", - "sha256:47841407cc89a4b80b0c52276f3cc8138bbbfba4b179ee3acbd7d77ae33f7ac4", - "sha256:4a4fbc769ea9b6bd97f4ad0b430a6807f92f0e5eb020f1e42ece59f3ecfc4585", - "sha256:4ab94426ddb1ecc6a0b601d832d5d9d421820989b8caa929114811369673235c", - "sha256:4b0f30372cef3fdc262f33d06e7b411cd59058ce9174ef159ad938c4a34a89da", - "sha256:4e3a23ec214e95c9fe85a58470b660efe6534b83e6cbe38b3ed52b053d7cb6ad", - "sha256:512bd5ab136b8dc0ffe3fdf2dfb0c4b4f49c8577f6cae55dca862cd37a4564e2", - "sha256:527b3b87b24844ea7865284aabfab08eb0faf599b385b03c2aa91fc6edd6e4b6", - "sha256:54d107c89a3ebcd13228278d68f1436d3f33f2dd2af5415e3feaeb1156e1a62c", - "sha256:5835f258ca9f7c455493a57ee707b76d2d9634d84d5d7f62e77be984ea80b849", - "sha256:598adde339d2cf7d67beaccda3f2ce7c57b3b412702f29c946708f69cf8222aa", - "sha256:599418aaaf88a6d02a8c515e656f6faf3d10618d3dd95866eb4436520096c84b", - "sha256:5bf651afd22d5f0c4be16cf39d0482ea494f5c88f03e75e5fef3a85177fecdeb", - "sha256:5c59fcd80b9049b49acd29bd3598cada4afc8d8d69bd4160cd613246912535d7", - "sha256:653acc3880459f82a65e27bd6526e47ddf19e643457d36a2250b85b41a564715", - "sha256:66bd5f950344fb2b3dbdd421aaa4e84f4411a1a13fca3aeb2bcbe667f80c9f76", - "sha256:6f3553510abdbec67c043ca85727396ceed1272eef029b050677046d3387be8d", - "sha256:7018ecc5fe97027214556afbc7c502fbd718d0740e87eb1217b17efd05b3d276", - "sha256:713d22cd9643ba9025d33c4af43943c7a1eb8547729228de18d3e02e278472b6", - "sha256:73a4131962e6d91109bca6536416aa067cf6c4efb871975df734f8d2fd821b37", - "sha256:75880ed07be39beff1881d81e4a907cafb802f306efd6d2d15f2b3c69935f6fb", - "sha256:75e14eac916f024305db517e00a9252714fce0abcb10ad327fb6dcdc0d060f1d", - "sha256:8135fa153a20d82ffb64f70a1b5c2738684afa197839b34cc3e3c72fa88d302c", - "sha256:84b14f36e85295fe69c6b9789b51a0903b774046d5f7df538176516c3e422446", - "sha256:86fc24e58ecb32aee09f864cb11bb91bc4c1086615001647dbfc4dc8c32f4008", - "sha256:87f44875f2804bc0511a69ce44a9595d5944837a62caecc8490bbdb0e18b1342", - "sha256:88c70ed9da9963d5496d38320160e8eb7e5f1886f9290475a881db12f351ab5d", - "sha256:88e5be56c231981428f4f506c68b6a46fa25c4123a2e86d156c58a8369d31ab7", - "sha256:89d2e02167fa95172c017732ed7725bc8523c598757f08d13c5acca308e1a061", - "sha256:8d6aaa4e7155afaf994d7924eb290abbe81a6905b303d8cb61310a2aba1c68ba", - "sha256:92a2964319d359f494f16011e23434f6f8ef0434acd3cf154a6b7bec511e2fb7", - "sha256:96372fc29471646b9b106ee918c8eeb4cca423fcbf9a34daa1b93767a88a2290", - "sha256:978b046ca728073070e9abc074b6299ebf3501e8dee5e26efacb13cec2b2dea0", - "sha256:9c7149272fb5834fc186328e2c1fa01dda3e1fa940ce18fded6d412e8f2cf76d", - "sha256:a0239da9fbafd9ff82fd67c16704a7d1bccf0d107a300e790587ad05547681c8", - "sha256:ad5383a67514e8e76906a06741febd9126fc7c7ff0f599d6fcce3e82b80d026f", - "sha256:ad61a9639792fd790523ba072c0555cd6be5a0baf03a49a5dd8cfcf20d56df48", - "sha256:b29bfd650ed8e148f9c515474a6ef0ba1090b7a8faeee26b74a8ff3b33617502", - "sha256:b97decbb3372d4b69e4d4c8117f44632551c692bb1361b356a02b97b69e18a62", - "sha256:ba71c9b4dcbb16212f334126cc3d8beb6af377f6703d9dc2d9fb3874fd667ee9", - "sha256:c37c5cce780349d4d51739ae682dec63573847a2a8dcb44381b174c3d9c8d403", - "sha256:c971bf3786b5fad82ce5ad570dc6ee420f5b12527157929e830f51c55dc8af77", - "sha256:d1fde0f44029e02d02d3993ad55ce93ead9bb9b15c6b7ccd580f90bd7e3de476", - "sha256:d24b8bb40d5c61ef2d9b6a8f4528c2f17f1c5d2d31fed62ec860f6006142e83e", - "sha256:d5ba88df9aa5e2f806650fcbeedbe4f6e8736e92fc0e73b0400538fd25a4dd96", - "sha256:d6f76310355e9fae637c3162936e9504b4767d5c52ca268331e2756e54fd4ca5", - "sha256:d737fc67b9a970f3234754974531dc9afeea11c70791dcb7db53b0cf81b79784", - "sha256:da22885266bbfb3f78218dc40205fed2671909fbd0720aedba39b4515c038091", - "sha256:da37dcfbf4b7f45d80ee386a5f81122501ec75672f475da34784196690762f4b", - "sha256:db19d60d846283ee275d0416e2a23493f4e6b6028825b51290ac05afc87a6f97", - "sha256:db4c979b0b3e0fa7e9e69ecd11b2b3174c6963cebadeecfb7ad24532ffcdd11a", - "sha256:e164e0a98e92d06da343d17d4e9c4da4654f4a4588a20d6c73548a29f176abe2", - "sha256:e168a7560b7c61342ae0412997b069753f27ac4862ec7867eff74f0fe4ea2ad9", - "sha256:e381581b37db1db7597b62a2e6b8b57c3deec95d93b6d6407c5b61ddc98aca6d", - "sha256:e65bc19919c910127c06759a63747ebe14f386cda573d95bcc62b427ca1afc73", - "sha256:e7b8813be97cab8cb52b1375f41f8e6804f6507fe4660152e8ca5c48f0436017", - "sha256:e8a78079d9a39ca9ca99a8b0ac2fdc0c4d25fc80c8a8a82e5c8211509c523363", - "sha256:ebf909ea0a3fc9596e40d55d8000702a85e27fd578ff41a5500f68f20fd32e6c", - "sha256:ec40170327d4a404b0d91855d41bfe1fe4b699222b2b93e3d833a27330a87a6d", - "sha256:f178d2aadf0166be4df834c4953da2d7eef24719e8aec9a65289483eeea9d618", - "sha256:f88df3a83cf9df566f171adba39d5bd52814ac0b94778d2448652fc77f9eb491", - "sha256:f973157ffeab5459eefe7b97a804987876dd0a55570b8fa56b4e1954bf11329b", - "sha256:ff25f48fc8e623d95eca0670b8cc1469a83783c924a602e0fbd47363bb54aaca" + "sha256:05857848da443c8c12110d99285d499b4e84d59918a21132e45c3f0804876994", + "sha256:05a183f1978802588711aed0dea31e697d760ce9055292db9dc1604daa9a8ded", + "sha256:09f23292d29135025e19e8ff4f0a68df078fe4ee013bca0105b2e803989de92d", + "sha256:11ca808f9a6b63485059f5f6e164ef7ec826483c1212a44f268b3653c91237d8", + "sha256:1736d87dad8ef46a8ec9cddd349fa9f7bd3a064c47dd6469c0d6763d3d49a4fc", + "sha256:1df43596b826022b14998f0460926ce261544fedefe0d2f653e1b20f49e96454", + "sha256:23170247ef89ffa842a02bbfdc425028574d9e010611659abeb24d890bc53bb8", + "sha256:2779f5e7c70f7b421915fd47db332c81de365678180a9f3ab404088f87ba5ff9", + "sha256:28185e36a78d247c55e9fbea2332d16aefa14c5276a582ce7a896231c6b1c208", + "sha256:2cbc14a13fb6b42d344e4f27746a4b03a2cb0c1c3c5b932b0d6ad8881aa390e3", + "sha256:2d71abc15ff7047412ef26bf812dfc8d0d1020d664617f4913df2df469f26b76", + "sha256:2d820162c8c2bdbe97d328cd4f417c955ca370027dce593345e437b2e9ffdc4d", + "sha256:317719d7f824eba55857fe0729363af58e27c066c731bc62cd97bc9c3d9c7ea4", + "sha256:35a68cd63ca6aaef5707888f17a70c36efe62b099a4e853d33dc2e9872125be8", + "sha256:3607375053df58ed6f23903aa10cf3112b1240e8c799d243bbad0f7be0666986", + "sha256:366bc870d7ac61726f32a489fbe3d1d8876e87506870be66b01aeb84389e967e", + "sha256:3abf0551874fecf95f93b58f25ef4fc9a250669a2257753f38f8f592db85ddea", + "sha256:3d7f6235c7475658acfc1769d968e07ab585c79f6ca438ddfecaa9a08006aee2", + "sha256:3dd8119752dd30dd7bca7d4bc2a92a59be6a003e4e5c2cf7e248b89751b8f4b7", + "sha256:42fe4fd9f0dfcc7be4248c162d8056f1d51a04c60e53366b0098d1267c4c9da8", + "sha256:45820ddbb276113ead8d4907a7802adb77548087ff5465d5c554f9aa3928ae7d", + "sha256:4790e44f46a4aa07b64504089def5744d3b6780468c4ec3a1a36eb7f2cae9814", + "sha256:4afa8f71dba3a5a2e1e1282a51cba7341ae76585345c43d8f0e624882b622218", + "sha256:4b777c9286b6c6a94f50ddb3a6e730deec327e9e2256cb08b5530db0f7d40fd8", + "sha256:4ee1b4152bc3190cc40ddd6a14715e3004944263ea208229ab4c297712aa3075", + "sha256:51a4cd44788ea0b5e6bb8fa704597af3a30be75503a7ed1098bc5b8ffdf6c982", + "sha256:536b01513d67d10baf6f71c72decdf492fb7433c5f2f133e9a9087379d4b6f31", + "sha256:571760ad7736b34d05597a1fd38cbc7d47f7b65deb722cb8e86fd827404d1f6b", + "sha256:5a2eb5311a37fe105aa35f62f75a078537e1a9e4e1d78c86ec9893a3c97d7a30", + "sha256:5ab16c254e2312efeb799bc3c06897f65a133b38b69682bf75d1f1ee1a9c43a9", + "sha256:65b0a70a25456d329a5e1426702dde67be0fb7a4ead718005ba2ca582d023a94", + "sha256:673343fbc0c1ac44d0d2640addc56e97a052504beacd7ade0dc5e76d3a4c16e8", + "sha256:6777a390e41e78e7c45dab43a4a0196c55c3b8c30eebe017b152939372a83253", + "sha256:6896b8416be9ada4d22cd359d7cb98955576ce863eadad5596b7cdfbf3e17c6c", + "sha256:694df243f394629bcae2d8ed94c589a181e8ba8604159e6e45e7b22e58291113", + "sha256:70e851f596c00f40a2f00a46126c95c2e04e146015af05a9da3e4867cfc55911", + "sha256:7276fe0017664414fdc3618fca411630405f1aaf0cc3be69def650eb50441787", + "sha256:76a86a9989ebf82ee61e06e2bab408aec4ea367dc6da35145c3352b60a112d11", + "sha256:7a94bde005a8f926d0fa38b88092a03dea4b4875a61fbcd9ac6f4351df1b57cd", + "sha256:7ae5f99a32c53731c93ac3075abd3e1e5cfbe72fc3eaac4c27c9dd64ba3b19fe", + "sha256:7e8a3b79b6d186a9c99761fd4a5e8dd575a48d96021f220ac5b5fa856e5dd029", + "sha256:816f4db40555026e4cdda604a1088577c1fb957d02f3f1292e0221353403f192", + "sha256:8303531e2c17b1a494ffaeba48f2da655fe932c4e9a2626c8718403c83e5dd2b", + "sha256:8488519aa05e636c5997719fe543c8daf19f538f4fa044f3ce94bee608817cff", + "sha256:87c8b0a6487e8109427ccf638580865b54e2e3db4a6e0e11c02639231b41fc0f", + "sha256:8c9e5f4d7208cda1a2bb600e29069eecf857e6980d0ccc922ccf9d1372c16f4b", + "sha256:94697c7293199c2a2551e3e3e18438b4cba293e79c6bc2319f5fd652fccb7456", + "sha256:9623cfd9e85b76b83ef88519d98326d4731f8d71869867e47a0b979ffec61c73", + "sha256:98d21092bf2637c5fa724a428a69e8f5955f2182bff61f8036827cf6ce1157bf", + "sha256:99ae01fb13a618b9942376df77a1f50c20a281390dad3c56a6ec2942e266220d", + "sha256:9c196b30f1b1aa3363a69dd69079ae9bec96c2965c4707eaa6914ba099fb7d4f", + "sha256:a00ce44c21612d185c5275c5cba4bab8d7c1590f248638b667ed8a782fa8cd6f", + "sha256:a1b66dbb8a7d5f50e9e2ea3804b01e766308331d0cac76eb30c563ac89c95985", + "sha256:a1d7edf74a36de0e5ca50787e83a77cf352f5504eb0ffa3f07000a911ba353fb", + "sha256:a1e3b3c107ccb0e537f309f719994a55621acd2c8fdf6d5ce5152aed788fb940", + "sha256:a486ddf57ab98b6d19ad36458b9f09e6022de0381674fe00228ca7b741aacb2f", + "sha256:ac9669990e2016d644ba8ae4758688534aabde8dbbc81f9af129c3f5f01ca9cd", + "sha256:b1a2ea8252cacc7fd51df5a56d7a2bb1986ed39be9397b51a08015727dfb69bd", + "sha256:c5b7bf8fe4d39886adc34311a233a2e01bc10eb4e842220235ed1de57541a896", + "sha256:c67a51ea415192c2e53e4e048c78bab82d21955b4281d297f517707dc836bf3d", + "sha256:ca4fddf84ac7d8a7d0866664936f93318ff01ee33e32381a115b19fb5a4d1202", + "sha256:d5b9345ab92ebe6003ae11d8092ce822a0242146e6fa270889b9ba965457ca40", + "sha256:d97c3e286d0ac9af6223bc132dc4bad6540b37c8d6c0a15fe1e70fb34f9ec411", + "sha256:db04d1de548f7a62d1dd7e7cdf7c22893ee168e22701895067a28a8ed51b3735", + "sha256:dcf71c55ec853826cd70eadb2b6ac62ec577416442ca1e0a97ad875a1b3a0305", + "sha256:de3cc86f4ea8b4c34a6e43a7306c40c1275e52bfa9748d869c6b7d54aa6dad80", + "sha256:deac0a32aec29608eb25d730f4bc5a261a65b6c48ded1ed861d2a1852577c932", + "sha256:e18d92c3e9e22553a73e33784fcb0ed484c9874e9a3e96c16a8d6a1e74a0217b", + "sha256:eb6dfd52063186ac97b4caa25764cdbcdb4b10d97f5c5f66b0fa95052e744eb7", + "sha256:f09960b5bb1017d16c0f9e9f7fc42160a5a49fa1e87a175fd4a2b1a1833ea0af", + "sha256:f1e4f254e9c35d8965d377e065c4a8a55d396fe87c8e7e8429bcfdeeb229bfb3", + "sha256:f32c86dc967ab8c719fd229ce71917caad13cc1e8356ee997bf02c5b368799bf", + "sha256:f50b4663c3e0262c3a361faf440761fbef60ccdde5fe8545689a4b3a3c149fb4", + "sha256:f8e05f5163528962ce1d1806fce763ab893b1c5b7ace0a3538cd81a90622f844", + "sha256:f929f4c9b9a00f3e6cc0587abb95ab9c05681f8b14e0fe1daecfa83ea90f8318", + "sha256:f9e09a1c83521d770d170b3801eea19b89f41ccaa61d53026ed111cb6f088887" ], - "markers": "python_version >= '3.6'", - "version": "==3.8.3" + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==3.9.0" }, "aiosignal": { "hashes": [ @@ -127,11 +117,11 @@ }, "attrs": { "hashes": [ - "sha256:29e95c7f6778868dbd49170f98f8818f78f3dc5e0e37c0b1f474e3561b240836", - "sha256:c9227bfc2f01993c03f68db37d1d15c9690188323c067c641f1a35ca58185f99" + "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30", + "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1" ], - "markers": "python_version >= '3.6'", - "version": "==22.2.0" + "markers": "python_version >= '3.7'", + "version": "==23.2.0" }, "certifi": { "hashes": [ @@ -274,83 +264,86 @@ }, "frozenlist": { "hashes": [ - "sha256:008a054b75d77c995ea26629ab3a0c0d7281341f2fa7e1e85fa6153ae29ae99c", - "sha256:02c9ac843e3390826a265e331105efeab489ffaf4dd86384595ee8ce6d35ae7f", - "sha256:034a5c08d36649591be1cbb10e09da9f531034acfe29275fc5454a3b101ce41a", - "sha256:05cdb16d09a0832eedf770cb7bd1fe57d8cf4eaf5aced29c4e41e3f20b30a784", - "sha256:0693c609e9742c66ba4870bcee1ad5ff35462d5ffec18710b4ac89337ff16e27", - "sha256:0771aed7f596c7d73444c847a1c16288937ef988dc04fb9f7be4b2aa91db609d", - "sha256:0af2e7c87d35b38732e810befb9d797a99279cbb85374d42ea61c1e9d23094b3", - "sha256:14143ae966a6229350021384870458e4777d1eae4c28d1a7aa47f24d030e6678", - "sha256:180c00c66bde6146a860cbb81b54ee0df350d2daf13ca85b275123bbf85de18a", - "sha256:1841e200fdafc3d51f974d9d377c079a0694a8f06de2e67b48150328d66d5483", - "sha256:23d16d9f477bb55b6154654e0e74557040575d9d19fe78a161bd33d7d76808e8", - "sha256:2b07ae0c1edaa0a36339ec6cce700f51b14a3fc6545fdd32930d2c83917332cf", - "sha256:2c926450857408e42f0bbc295e84395722ce74bae69a3b2aa2a65fe22cb14b99", - "sha256:2e24900aa13212e75e5b366cb9065e78bbf3893d4baab6052d1aca10d46d944c", - "sha256:303e04d422e9b911a09ad499b0368dc551e8c3cd15293c99160c7f1f07b59a48", - "sha256:352bd4c8c72d508778cf05ab491f6ef36149f4d0cb3c56b1b4302852255d05d5", - "sha256:3843f84a6c465a36559161e6c59dce2f2ac10943040c2fd021cfb70d58c4ad56", - "sha256:394c9c242113bfb4b9aa36e2b80a05ffa163a30691c7b5a29eba82e937895d5e", - "sha256:3bbdf44855ed8f0fbcd102ef05ec3012d6a4fd7c7562403f76ce6a52aeffb2b1", - "sha256:40de71985e9042ca00b7953c4f41eabc3dc514a2d1ff534027f091bc74416401", - "sha256:41fe21dc74ad3a779c3d73a2786bdf622ea81234bdd4faf90b8b03cad0c2c0b4", - "sha256:47df36a9fe24054b950bbc2db630d508cca3aa27ed0566c0baf661225e52c18e", - "sha256:4ea42116ceb6bb16dbb7d526e242cb6747b08b7710d9782aa3d6732bd8d27649", - "sha256:58bcc55721e8a90b88332d6cd441261ebb22342e238296bb330968952fbb3a6a", - "sha256:5c11e43016b9024240212d2a65043b70ed8dfd3b52678a1271972702d990ac6d", - "sha256:5cf820485f1b4c91e0417ea0afd41ce5cf5965011b3c22c400f6d144296ccbc0", - "sha256:5d8860749e813a6f65bad8285a0520607c9500caa23fea6ee407e63debcdbef6", - "sha256:6327eb8e419f7d9c38f333cde41b9ae348bec26d840927332f17e887a8dcb70d", - "sha256:65a5e4d3aa679610ac6e3569e865425b23b372277f89b5ef06cf2cdaf1ebf22b", - "sha256:66080ec69883597e4d026f2f71a231a1ee9887835902dbe6b6467d5a89216cf6", - "sha256:783263a4eaad7c49983fe4b2e7b53fa9770c136c270d2d4bbb6d2192bf4d9caf", - "sha256:7f44e24fa70f6fbc74aeec3e971f60a14dde85da364aa87f15d1be94ae75aeef", - "sha256:7fdfc24dcfce5b48109867c13b4cb15e4660e7bd7661741a391f821f23dfdca7", - "sha256:810860bb4bdce7557bc0febb84bbd88198b9dbc2022d8eebe5b3590b2ad6c842", - "sha256:841ea19b43d438a80b4de62ac6ab21cfe6827bb8a9dc62b896acc88eaf9cecba", - "sha256:84610c1502b2461255b4c9b7d5e9c48052601a8957cd0aea6ec7a7a1e1fb9420", - "sha256:899c5e1928eec13fd6f6d8dc51be23f0d09c5281e40d9cf4273d188d9feeaf9b", - "sha256:8bae29d60768bfa8fb92244b74502b18fae55a80eac13c88eb0b496d4268fd2d", - "sha256:8df3de3a9ab8325f94f646609a66cbeeede263910c5c0de0101079ad541af332", - "sha256:8fa3c6e3305aa1146b59a09b32b2e04074945ffcfb2f0931836d103a2c38f936", - "sha256:924620eef691990dfb56dc4709f280f40baee568c794b5c1885800c3ecc69816", - "sha256:9309869032abb23d196cb4e4db574232abe8b8be1339026f489eeb34a4acfd91", - "sha256:9545a33965d0d377b0bc823dcabf26980e77f1b6a7caa368a365a9497fb09420", - "sha256:9ac5995f2b408017b0be26d4a1d7c61bce106ff3d9e3324374d66b5964325448", - "sha256:9bbbcedd75acdfecf2159663b87f1bb5cfc80e7cd99f7ddd9d66eb98b14a8411", - "sha256:a4ae8135b11652b08a8baf07631d3ebfe65a4c87909dbef5fa0cdde440444ee4", - "sha256:a6394d7dadd3cfe3f4b3b186e54d5d8504d44f2d58dcc89d693698e8b7132b32", - "sha256:a97b4fe50b5890d36300820abd305694cb865ddb7885049587a5678215782a6b", - "sha256:ae4dc05c465a08a866b7a1baf360747078b362e6a6dbeb0c57f234db0ef88ae0", - "sha256:b1c63e8d377d039ac769cd0926558bb7068a1f7abb0f003e3717ee003ad85530", - "sha256:b1e2c1185858d7e10ff045c496bbf90ae752c28b365fef2c09cf0fa309291669", - "sha256:b4395e2f8d83fbe0c627b2b696acce67868793d7d9750e90e39592b3626691b7", - "sha256:b756072364347cb6aa5b60f9bc18e94b2f79632de3b0190253ad770c5df17db1", - "sha256:ba64dc2b3b7b158c6660d49cdb1d872d1d0bf4e42043ad8d5006099479a194e5", - "sha256:bed331fe18f58d844d39ceb398b77d6ac0b010d571cba8267c2e7165806b00ce", - "sha256:c188512b43542b1e91cadc3c6c915a82a5eb95929134faf7fd109f14f9892ce4", - "sha256:c21b9aa40e08e4f63a2f92ff3748e6b6c84d717d033c7b3438dd3123ee18f70e", - "sha256:ca713d4af15bae6e5d79b15c10c8522859a9a89d3b361a50b817c98c2fb402a2", - "sha256:cd4210baef299717db0a600d7a3cac81d46ef0e007f88c9335db79f8979c0d3d", - "sha256:cfe33efc9cb900a4c46f91a5ceba26d6df370ffddd9ca386eb1d4f0ad97b9ea9", - "sha256:d5cd3ab21acbdb414bb6c31958d7b06b85eeb40f66463c264a9b343a4e238642", - "sha256:dfbac4c2dfcc082fcf8d942d1e49b6aa0766c19d3358bd86e2000bf0fa4a9cf0", - "sha256:e235688f42b36be2b6b06fc37ac2126a73b75fb8d6bc66dd632aa35286238703", - "sha256:eb82dbba47a8318e75f679690190c10a5e1f447fbf9df41cbc4c3afd726d88cb", - "sha256:ebb86518203e12e96af765ee89034a1dbb0c3c65052d1b0c19bbbd6af8a145e1", - "sha256:ee78feb9d293c323b59a6f2dd441b63339a30edf35abcb51187d2fc26e696d13", - "sha256:eedab4c310c0299961ac285591acd53dc6723a1ebd90a57207c71f6e0c2153ab", - "sha256:efa568b885bca461f7c7b9e032655c0c143d305bf01c30caf6db2854a4532b38", - "sha256:efce6ae830831ab6a22b9b4091d411698145cb9b8fc869e1397ccf4b4b6455cb", - "sha256:f163d2fd041c630fed01bc48d28c3ed4a3b003c00acd396900e11ee5316b56bb", - "sha256:f20380df709d91525e4bee04746ba612a4df0972c1b8f8e1e8af997e678c7b81", - "sha256:f30f1928162e189091cf4d9da2eac617bfe78ef907a761614ff577ef4edfb3c8", - "sha256:f470c92737afa7d4c3aacc001e335062d582053d4dbe73cda126f2d7031068dd", - "sha256:ff8bf625fe85e119553b5383ba0fb6aa3d0ec2ae980295aaefa552374926b3f4" + "sha256:04ced3e6a46b4cfffe20f9ae482818e34eba9b5fb0ce4056e4cc9b6e212d09b7", + "sha256:0633c8d5337cb5c77acbccc6357ac49a1770b8c487e5b3505c57b949b4b82e98", + "sha256:068b63f23b17df8569b7fdca5517edef76171cf3897eb68beb01341131fbd2ad", + "sha256:0c250a29735d4f15321007fb02865f0e6b6a41a6b88f1f523ca1596ab5f50bd5", + "sha256:1979bc0aeb89b33b588c51c54ab0161791149f2461ea7c7c946d95d5f93b56ae", + "sha256:1a4471094e146b6790f61b98616ab8e44f72661879cc63fa1049d13ef711e71e", + "sha256:1b280e6507ea8a4fa0c0a7150b4e526a8d113989e28eaaef946cc77ffd7efc0a", + "sha256:1d0ce09d36d53bbbe566fe296965b23b961764c0bcf3ce2fa45f463745c04701", + "sha256:20b51fa3f588ff2fe658663db52a41a4f7aa6c04f6201449c6c7c476bd255c0d", + "sha256:23b2d7679b73fe0e5a4560b672a39f98dfc6f60df63823b0a9970525325b95f6", + "sha256:23b701e65c7b36e4bf15546a89279bd4d8675faabc287d06bbcfac7d3c33e1e6", + "sha256:2471c201b70d58a0f0c1f91261542a03d9a5e088ed3dc6c160d614c01649c106", + "sha256:27657df69e8801be6c3638054e202a135c7f299267f1a55ed3a598934f6c0d75", + "sha256:29acab3f66f0f24674b7dc4736477bcd4bc3ad4b896f5f45379a67bce8b96868", + "sha256:32453c1de775c889eb4e22f1197fe3bdfe457d16476ea407472b9442e6295f7a", + "sha256:3a670dc61eb0d0eb7080890c13de3066790f9049b47b0de04007090807c776b0", + "sha256:3e0153a805a98f5ada7e09826255ba99fb4f7524bb81bf6b47fb702666484ae1", + "sha256:410478a0c562d1a5bcc2f7ea448359fcb050ed48b3c6f6f4f18c313a9bdb1826", + "sha256:442acde1e068288a4ba7acfe05f5f343e19fac87bfc96d89eb886b0363e977ec", + "sha256:48f6a4533887e189dae092f1cf981f2e3885175f7a0f33c91fb5b7b682b6bab6", + "sha256:4f57dab5fe3407b6c0c1cc907ac98e8a189f9e418f3b6e54d65a718aaafe3950", + "sha256:4f9c515e7914626b2a2e1e311794b4c35720a0be87af52b79ff8e1429fc25f19", + "sha256:55fdc093b5a3cb41d420884cdaf37a1e74c3c37a31f46e66286d9145d2063bd0", + "sha256:5667ed53d68d91920defdf4035d1cdaa3c3121dc0b113255124bcfada1cfa1b8", + "sha256:590344787a90ae57d62511dd7c736ed56b428f04cd8c161fcc5e7232c130c69a", + "sha256:5a7d70357e7cee13f470c7883a063aae5fe209a493c57d86eb7f5a6f910fae09", + "sha256:5c3894db91f5a489fc8fa6a9991820f368f0b3cbdb9cd8849547ccfab3392d86", + "sha256:5c849d495bf5154cd8da18a9eb15db127d4dba2968d88831aff6f0331ea9bd4c", + "sha256:64536573d0a2cb6e625cf309984e2d873979709f2cf22839bf2d61790b448ad5", + "sha256:693945278a31f2086d9bf3df0fe8254bbeaef1fe71e1351c3bd730aa7d31c41b", + "sha256:6db4667b187a6742b33afbbaf05a7bc551ffcf1ced0000a571aedbb4aa42fc7b", + "sha256:6eb73fa5426ea69ee0e012fb59cdc76a15b1283d6e32e4f8dc4482ec67d1194d", + "sha256:722e1124aec435320ae01ee3ac7bec11a5d47f25d0ed6328f2273d287bc3abb0", + "sha256:7268252af60904bf52c26173cbadc3a071cece75f873705419c8681f24d3edea", + "sha256:74fb4bee6880b529a0c6560885fce4dc95936920f9f20f53d99a213f7bf66776", + "sha256:780d3a35680ced9ce682fbcf4cb9c2bad3136eeff760ab33707b71db84664e3a", + "sha256:82e8211d69a4f4bc360ea22cd6555f8e61a1bd211d1d5d39d3d228b48c83a897", + "sha256:89aa2c2eeb20957be2d950b85974b30a01a762f3308cd02bb15e1ad632e22dc7", + "sha256:8aefbba5f69d42246543407ed2461db31006b0f76c4e32dfd6f42215a2c41d09", + "sha256:96ec70beabbd3b10e8bfe52616a13561e58fe84c0101dd031dc78f250d5128b9", + "sha256:9750cc7fe1ae3b1611bb8cfc3f9ec11d532244235d75901fb6b8e42ce9229dfe", + "sha256:9acbb16f06fe7f52f441bb6f413ebae6c37baa6ef9edd49cdd567216da8600cd", + "sha256:9d3e0c25a2350080e9319724dede4f31f43a6c9779be48021a7f4ebde8b2d742", + "sha256:a06339f38e9ed3a64e4c4e43aec7f59084033647f908e4259d279a52d3757d09", + "sha256:a0cb6f11204443f27a1628b0e460f37fb30f624be6051d490fa7d7e26d4af3d0", + "sha256:a7496bfe1da7fb1a4e1cc23bb67c58fab69311cc7d32b5a99c2007b4b2a0e932", + "sha256:a828c57f00f729620a442881cc60e57cfcec6842ba38e1b19fd3e47ac0ff8dc1", + "sha256:a9b2de4cf0cdd5bd2dee4c4f63a653c61d2408055ab77b151c1957f221cabf2a", + "sha256:b46c8ae3a8f1f41a0d2ef350c0b6e65822d80772fe46b653ab6b6274f61d4a49", + "sha256:b7e3ed87d4138356775346e6845cccbe66cd9e207f3cd11d2f0b9fd13681359d", + "sha256:b7f2f9f912dca3934c1baec2e4585a674ef16fe00218d833856408c48d5beee7", + "sha256:ba60bb19387e13597fb059f32cd4d59445d7b18b69a745b8f8e5db0346f33480", + "sha256:beee944ae828747fd7cb216a70f120767fc9f4f00bacae8543c14a6831673f89", + "sha256:bfa4a17e17ce9abf47a74ae02f32d014c5e9404b6d9ac7f729e01562bbee601e", + "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b", + "sha256:c302220494f5c1ebeb0912ea782bcd5e2f8308037b3c7553fad0e48ebad6ad82", + "sha256:c6321c9efe29975232da3bd0af0ad216800a47e93d763ce64f291917a381b8eb", + "sha256:c757a9dd70d72b076d6f68efdbb9bc943665ae954dad2801b874c8c69e185068", + "sha256:c99169d4ff810155ca50b4da3b075cbde79752443117d89429595c2e8e37fed8", + "sha256:c9c92be9fd329ac801cc420e08452b70e7aeab94ea4233a4804f0915c14eba9b", + "sha256:cc7b01b3754ea68a62bd77ce6020afaffb44a590c2289089289363472d13aedb", + "sha256:db9e724bebd621d9beca794f2a4ff1d26eed5965b004a97f1f1685a173b869c2", + "sha256:dca69045298ce5c11fd539682cff879cc1e664c245d1c64da929813e54241d11", + "sha256:dd9b1baec094d91bf36ec729445f7769d0d0cf6b64d04d86e45baf89e2b9059b", + "sha256:e02a0e11cf6597299b9f3bbd3f93d79217cb90cfd1411aec33848b13f5c656cc", + "sha256:e6a20a581f9ce92d389a8c7d7c3dd47c81fd5d6e655c8dddf341e14aa48659d0", + "sha256:e7004be74cbb7d9f34553a5ce5fb08be14fb33bc86f332fb71cbe5216362a497", + "sha256:e774d53b1a477a67838a904131c4b0eef6b3d8a651f8b138b04f748fccfefe17", + "sha256:edb678da49d9f72c9f6c609fbe41a5dfb9a9282f9e6a2253d5a91e0fc382d7c0", + "sha256:f146e0911cb2f1da549fc58fc7bcd2b836a44b79ef871980d605ec392ff6b0d2", + "sha256:f56e2333dda1fe0f909e7cc59f021eba0d2307bc6f012a1ccf2beca6ba362439", + "sha256:f9a3ea26252bd92f570600098783d1371354d89d5f6b7dfd87359d669f2109b5", + "sha256:f9aa1878d1083b276b0196f2dfbe00c9b7e752475ed3b682025ff20c1c1f51ac", + "sha256:fb3c2db03683b5767dedb5769b8a40ebb47d6f7f45b1b3e3b4b51ec8ad9d9825", + "sha256:fbeb989b5cc29e8daf7f976b421c220f1b8c731cbf22b9130d8815418ea45887", + "sha256:fde5bd59ab5357e3853313127f4d3565fc7dad314a74d7b5d43c22c6a5ed2ced", + "sha256:fe1a06da377e3a1062ae5fe0926e12b84eceb8a50b350ddca72dc85015873f74" ], - "markers": "python_version >= '3.7'", - "version": "==1.3.3" + "markers": "python_version >= '3.8'", + "version": "==1.4.1" }, "idna": { "hashes": [ @@ -580,83 +573,99 @@ }, "yarl": { "hashes": [ - "sha256:009a028127e0a1755c38b03244c0bea9d5565630db9c4cf9572496e947137a87", - "sha256:0414fd91ce0b763d4eadb4456795b307a71524dbacd015c657bb2a39db2eab89", - "sha256:0978f29222e649c351b173da2b9b4665ad1feb8d1daa9d971eb90df08702668a", - "sha256:0ef8fb25e52663a1c85d608f6dd72e19bd390e2ecaf29c17fb08f730226e3a08", - "sha256:10b08293cda921157f1e7c2790999d903b3fd28cd5c208cf8826b3b508026996", - "sha256:1684a9bd9077e922300ecd48003ddae7a7474e0412bea38d4631443a91d61077", - "sha256:1b372aad2b5f81db66ee7ec085cbad72c4da660d994e8e590c997e9b01e44901", - "sha256:1e21fb44e1eff06dd6ef971d4bdc611807d6bd3691223d9c01a18cec3677939e", - "sha256:2305517e332a862ef75be8fad3606ea10108662bc6fe08509d5ca99503ac2aee", - "sha256:24ad1d10c9db1953291f56b5fe76203977f1ed05f82d09ec97acb623a7976574", - "sha256:272b4f1599f1b621bf2aabe4e5b54f39a933971f4e7c9aa311d6d7dc06965165", - "sha256:2a1fca9588f360036242f379bfea2b8b44cae2721859b1c56d033adfd5893634", - "sha256:2b4fa2606adf392051d990c3b3877d768771adc3faf2e117b9de7eb977741229", - "sha256:3150078118f62371375e1e69b13b48288e44f6691c1069340081c3fd12c94d5b", - "sha256:326dd1d3caf910cd26a26ccbfb84c03b608ba32499b5d6eeb09252c920bcbe4f", - "sha256:34c09b43bd538bf6c4b891ecce94b6fa4f1f10663a8d4ca589a079a5018f6ed7", - "sha256:388a45dc77198b2460eac0aca1efd6a7c09e976ee768b0d5109173e521a19daf", - "sha256:3adeef150d528ded2a8e734ebf9ae2e658f4c49bf413f5f157a470e17a4a2e89", - "sha256:3edac5d74bb3209c418805bda77f973117836e1de7c000e9755e572c1f7850d0", - "sha256:3f6b4aca43b602ba0f1459de647af954769919c4714706be36af670a5f44c9c1", - "sha256:3fc056e35fa6fba63248d93ff6e672c096f95f7836938241ebc8260e062832fe", - "sha256:418857f837347e8aaef682679f41e36c24250097f9e2f315d39bae3a99a34cbf", - "sha256:42430ff511571940d51e75cf42f1e4dbdded477e71c1b7a17f4da76c1da8ea76", - "sha256:44ceac0450e648de86da8e42674f9b7077d763ea80c8ceb9d1c3e41f0f0a9951", - "sha256:47d49ac96156f0928f002e2424299b2c91d9db73e08c4cd6742923a086f1c863", - "sha256:48dd18adcf98ea9cd721a25313aef49d70d413a999d7d89df44f469edfb38a06", - "sha256:49d43402c6e3013ad0978602bf6bf5328535c48d192304b91b97a3c6790b1562", - "sha256:4d04acba75c72e6eb90745447d69f84e6c9056390f7a9724605ca9c56b4afcc6", - "sha256:57a7c87927a468e5a1dc60c17caf9597161d66457a34273ab1760219953f7f4c", - "sha256:58a3c13d1c3005dbbac5c9f0d3210b60220a65a999b1833aa46bd6677c69b08e", - "sha256:5df5e3d04101c1e5c3b1d69710b0574171cc02fddc4b23d1b2813e75f35a30b1", - "sha256:63243b21c6e28ec2375f932a10ce7eda65139b5b854c0f6b82ed945ba526bff3", - "sha256:64dd68a92cab699a233641f5929a40f02a4ede8c009068ca8aa1fe87b8c20ae3", - "sha256:6604711362f2dbf7160df21c416f81fac0de6dbcf0b5445a2ef25478ecc4c778", - "sha256:6c4fcfa71e2c6a3cb568cf81aadc12768b9995323186a10827beccf5fa23d4f8", - "sha256:6d88056a04860a98341a0cf53e950e3ac9f4e51d1b6f61a53b0609df342cc8b2", - "sha256:705227dccbe96ab02c7cb2c43e1228e2826e7ead880bb19ec94ef279e9555b5b", - "sha256:728be34f70a190566d20aa13dc1f01dc44b6aa74580e10a3fb159691bc76909d", - "sha256:74dece2bfc60f0f70907c34b857ee98f2c6dd0f75185db133770cd67300d505f", - "sha256:75c16b2a900b3536dfc7014905a128a2bea8fb01f9ee26d2d7d8db0a08e7cb2c", - "sha256:77e913b846a6b9c5f767b14dc1e759e5aff05502fe73079f6f4176359d832581", - "sha256:7a66c506ec67eb3159eea5096acd05f5e788ceec7b96087d30c7d2865a243918", - "sha256:8c46d3d89902c393a1d1e243ac847e0442d0196bbd81aecc94fcebbc2fd5857c", - "sha256:93202666046d9edadfe9f2e7bf5e0782ea0d497b6d63da322e541665d65a044e", - "sha256:97209cc91189b48e7cfe777237c04af8e7cc51eb369004e061809bcdf4e55220", - "sha256:a48f4f7fea9a51098b02209d90297ac324241bf37ff6be6d2b0149ab2bd51b37", - "sha256:a783cd344113cb88c5ff7ca32f1f16532a6f2142185147822187913eb989f739", - "sha256:ae0eec05ab49e91a78700761777f284c2df119376e391db42c38ab46fd662b77", - "sha256:ae4d7ff1049f36accde9e1ef7301912a751e5bae0a9d142459646114c70ecba6", - "sha256:b05df9ea7496df11b710081bd90ecc3a3db6adb4fee36f6a411e7bc91a18aa42", - "sha256:baf211dcad448a87a0d9047dc8282d7de59473ade7d7fdf22150b1d23859f946", - "sha256:bb81f753c815f6b8e2ddd2eef3c855cf7da193b82396ac013c661aaa6cc6b0a5", - "sha256:bcd7bb1e5c45274af9a1dd7494d3c52b2be5e6bd8d7e49c612705fd45420b12d", - "sha256:bf071f797aec5b96abfc735ab97da9fd8f8768b43ce2abd85356a3127909d146", - "sha256:c15163b6125db87c8f53c98baa5e785782078fbd2dbeaa04c6141935eb6dab7a", - "sha256:cb6d48d80a41f68de41212f3dfd1a9d9898d7841c8f7ce6696cf2fd9cb57ef83", - "sha256:ceff9722e0df2e0a9e8a79c610842004fa54e5b309fe6d218e47cd52f791d7ef", - "sha256:cfa2bbca929aa742b5084fd4663dd4b87c191c844326fcb21c3afd2d11497f80", - "sha256:d617c241c8c3ad5c4e78a08429fa49e4b04bedfc507b34b4d8dceb83b4af3588", - "sha256:d881d152ae0007809c2c02e22aa534e702f12071e6b285e90945aa3c376463c5", - "sha256:da65c3f263729e47351261351b8679c6429151ef9649bba08ef2528ff2c423b2", - "sha256:de986979bbd87272fe557e0a8fcb66fd40ae2ddfe28a8b1ce4eae22681728fef", - "sha256:df60a94d332158b444301c7f569659c926168e4d4aad2cfbf4bce0e8fb8be826", - "sha256:dfef7350ee369197106805e193d420b75467b6cceac646ea5ed3049fcc950a05", - "sha256:e59399dda559688461762800d7fb34d9e8a6a7444fd76ec33220a926c8be1516", - "sha256:e6f3515aafe0209dd17fb9bdd3b4e892963370b3de781f53e1746a521fb39fc0", - "sha256:e7fd20d6576c10306dea2d6a5765f46f0ac5d6f53436217913e952d19237efc4", - "sha256:ebb78745273e51b9832ef90c0898501006670d6e059f2cdb0e999494eb1450c2", - "sha256:efff27bd8cbe1f9bd127e7894942ccc20c857aa8b5a0327874f30201e5ce83d0", - "sha256:f37db05c6051eff17bc832914fe46869f8849de5b92dc4a3466cd63095d23dfd", - "sha256:f8ca8ad414c85bbc50f49c0a106f951613dfa5f948ab69c10ce9b128d368baf8", - "sha256:fb742dcdd5eec9f26b61224c23baea46c9055cf16f62475e11b9b15dfd5c117b", - "sha256:fc77086ce244453e074e445104f0ecb27530d6fd3a46698e33f6c38951d5a0f1", - "sha256:ff205b58dc2929191f68162633d5e10e8044398d7a45265f90a0f1d51f85f72c" + "sha256:008d3e808d03ef28542372d01057fd09168419cdc8f848efe2804f894ae03e51", + "sha256:03caa9507d3d3c83bca08650678e25364e1843b484f19986a527630ca376ecce", + "sha256:07574b007ee20e5c375a8fe4a0789fad26db905f9813be0f9fef5a68080de559", + "sha256:09efe4615ada057ba2d30df871d2f668af661e971dfeedf0c159927d48bbeff0", + "sha256:0d2454f0aef65ea81037759be5ca9947539667eecebca092733b2eb43c965a81", + "sha256:0e9d124c191d5b881060a9e5060627694c3bdd1fe24c5eecc8d5d7d0eb6faabc", + "sha256:18580f672e44ce1238b82f7fb87d727c4a131f3a9d33a5e0e82b793362bf18b4", + "sha256:1f23e4fe1e8794f74b6027d7cf19dc25f8b63af1483d91d595d4a07eca1fb26c", + "sha256:206a55215e6d05dbc6c98ce598a59e6fbd0c493e2de4ea6cc2f4934d5a18d130", + "sha256:23d32a2594cb5d565d358a92e151315d1b2268bc10f4610d098f96b147370136", + "sha256:26a1dc6285e03f3cc9e839a2da83bcbf31dcb0d004c72d0730e755b33466c30e", + "sha256:29e0f83f37610f173eb7e7b5562dd71467993495e568e708d99e9d1944f561ec", + "sha256:2b134fd795e2322b7684155b7855cc99409d10b2e408056db2b93b51a52accc7", + "sha256:2d47552b6e52c3319fede1b60b3de120fe83bde9b7bddad11a69fb0af7db32f1", + "sha256:357495293086c5b6d34ca9616a43d329317feab7917518bc97a08f9e55648455", + "sha256:35a2b9396879ce32754bd457d31a51ff0a9d426fd9e0e3c33394bf4b9036b099", + "sha256:3777ce5536d17989c91696db1d459574e9a9bd37660ea7ee4d3344579bb6f129", + "sha256:3986b6f41ad22988e53d5778f91855dc0399b043fc8946d4f2e68af22ee9ff10", + "sha256:44d8ffbb9c06e5a7f529f38f53eda23e50d1ed33c6c869e01481d3fafa6b8142", + "sha256:49a180c2e0743d5d6e0b4d1a9e5f633c62eca3f8a86ba5dd3c471060e352ca98", + "sha256:4aa9741085f635934f3a2583e16fcf62ba835719a8b2b28fb2917bb0537c1dfa", + "sha256:4b21516d181cd77ebd06ce160ef8cc2a5e9ad35fb1c5930882baff5ac865eee7", + "sha256:4b3c1ffe10069f655ea2d731808e76e0f452fc6c749bea04781daf18e6039525", + "sha256:4c7d56b293cc071e82532f70adcbd8b61909eec973ae9d2d1f9b233f3d943f2c", + "sha256:4e9035df8d0880b2f1c7f5031f33f69e071dfe72ee9310cfc76f7b605958ceb9", + "sha256:54525ae423d7b7a8ee81ba189f131054defdb122cde31ff17477951464c1691c", + "sha256:549d19c84c55d11687ddbd47eeb348a89df9cb30e1993f1b128f4685cd0ebbf8", + "sha256:54beabb809ffcacbd9d28ac57b0db46e42a6e341a030293fb3185c409e626b8b", + "sha256:566db86717cf8080b99b58b083b773a908ae40f06681e87e589a976faf8246bf", + "sha256:5a2e2433eb9344a163aced6a5f6c9222c0786e5a9e9cac2c89f0b28433f56e23", + "sha256:5aef935237d60a51a62b86249839b51345f47564208c6ee615ed2a40878dccdd", + "sha256:604f31d97fa493083ea21bd9b92c419012531c4e17ea6da0f65cacdcf5d0bd27", + "sha256:63b20738b5aac74e239622d2fe30df4fca4942a86e31bf47a81a0e94c14df94f", + "sha256:686a0c2f85f83463272ddffd4deb5e591c98aac1897d65e92319f729c320eece", + "sha256:6a962e04b8f91f8c4e5917e518d17958e3bdee71fd1d8b88cdce74dd0ebbf434", + "sha256:6ad6d10ed9b67a382b45f29ea028f92d25bc0bc1daf6c5b801b90b5aa70fb9ec", + "sha256:6f5cb257bc2ec58f437da2b37a8cd48f666db96d47b8a3115c29f316313654ff", + "sha256:6fe79f998a4052d79e1c30eeb7d6c1c1056ad33300f682465e1b4e9b5a188b78", + "sha256:7855426dfbddac81896b6e533ebefc0af2f132d4a47340cee6d22cac7190022d", + "sha256:7d5aaac37d19b2904bb9dfe12cdb08c8443e7ba7d2852894ad448d4b8f442863", + "sha256:801e9264d19643548651b9db361ce3287176671fb0117f96b5ac0ee1c3530d53", + "sha256:81eb57278deb6098a5b62e88ad8281b2ba09f2f1147c4767522353eaa6260b31", + "sha256:824d6c50492add5da9374875ce72db7a0733b29c2394890aef23d533106e2b15", + "sha256:8397a3817d7dcdd14bb266283cd1d6fc7264a48c186b986f32e86d86d35fbac5", + "sha256:848cd2a1df56ddbffeb375535fb62c9d1645dde33ca4d51341378b3f5954429b", + "sha256:84fc30f71689d7fc9168b92788abc977dc8cefa806909565fc2951d02f6b7d57", + "sha256:8619d6915b3b0b34420cf9b2bb6d81ef59d984cb0fde7544e9ece32b4b3043c3", + "sha256:8a854227cf581330ffa2c4824d96e52ee621dd571078a252c25e3a3b3d94a1b1", + "sha256:8be9e837ea9113676e5754b43b940b50cce76d9ed7d2461df1af39a8ee674d9f", + "sha256:928cecb0ef9d5a7946eb6ff58417ad2fe9375762382f1bf5c55e61645f2c43ad", + "sha256:957b4774373cf6f709359e5c8c4a0af9f6d7875db657adb0feaf8d6cb3c3964c", + "sha256:992f18e0ea248ee03b5a6e8b3b4738850ae7dbb172cc41c966462801cbf62cf7", + "sha256:9fc5fc1eeb029757349ad26bbc5880557389a03fa6ada41703db5e068881e5f2", + "sha256:a00862fb23195b6b8322f7d781b0dc1d82cb3bcac346d1e38689370cc1cc398b", + "sha256:a3a6ed1d525bfb91b3fc9b690c5a21bb52de28c018530ad85093cc488bee2dd2", + "sha256:a6327976c7c2f4ee6816eff196e25385ccc02cb81427952414a64811037bbc8b", + "sha256:a7409f968456111140c1c95301cadf071bd30a81cbd7ab829169fb9e3d72eae9", + "sha256:a825ec844298c791fd28ed14ed1bffc56a98d15b8c58a20e0e08c1f5f2bea1be", + "sha256:a8c1df72eb746f4136fe9a2e72b0c9dc1da1cbd23b5372f94b5820ff8ae30e0e", + "sha256:a9bd00dc3bc395a662900f33f74feb3e757429e545d831eef5bb280252631984", + "sha256:aa102d6d280a5455ad6a0f9e6d769989638718e938a6a0a2ff3f4a7ff8c62cc4", + "sha256:aaaea1e536f98754a6e5c56091baa1b6ce2f2700cc4a00b0d49eca8dea471074", + "sha256:ad4d7a90a92e528aadf4965d685c17dacff3df282db1121136c382dc0b6014d2", + "sha256:b8477c1ee4bd47c57d49621a062121c3023609f7a13b8a46953eb6c9716ca392", + "sha256:ba6f52cbc7809cd8d74604cce9c14868306ae4aa0282016b641c661f981a6e91", + "sha256:bac8d525a8dbc2a1507ec731d2867025d11ceadcb4dd421423a5d42c56818541", + "sha256:bef596fdaa8f26e3d66af846bbe77057237cb6e8efff8cd7cc8dff9a62278bbf", + "sha256:c0ec0ed476f77db9fb29bca17f0a8fcc7bc97ad4c6c1d8959c507decb22e8572", + "sha256:c38c9ddb6103ceae4e4498f9c08fac9b590c5c71b0370f98714768e22ac6fa66", + "sha256:c7224cab95645c7ab53791022ae77a4509472613e839dab722a72abe5a684575", + "sha256:c74018551e31269d56fab81a728f683667e7c28c04e807ba08f8c9e3bba32f14", + "sha256:ca06675212f94e7a610e85ca36948bb8fc023e458dd6c63ef71abfd482481aa5", + "sha256:d1d2532b340b692880261c15aee4dc94dd22ca5d61b9db9a8a361953d36410b1", + "sha256:d25039a474c4c72a5ad4b52495056f843a7ff07b632c1b92ea9043a3d9950f6e", + "sha256:d5ff2c858f5f6a42c2a8e751100f237c5e869cbde669a724f2062d4c4ef93551", + "sha256:d7d7f7de27b8944f1fee2c26a88b4dabc2409d2fea7a9ed3df79b67277644e17", + "sha256:d7eeb6d22331e2fd42fce928a81c697c9ee2d51400bd1a28803965883e13cead", + "sha256:d8a1c6c0be645c745a081c192e747c5de06e944a0d21245f4cf7c05e457c36e0", + "sha256:d8b889777de69897406c9fb0b76cdf2fd0f31267861ae7501d93003d55f54fbe", + "sha256:d9e09c9d74f4566e905a0b8fa668c58109f7624db96a2171f21747abc7524234", + "sha256:db8e58b9d79200c76956cefd14d5c90af54416ff5353c5bfd7cbe58818e26ef0", + "sha256:ddb2a5c08a4eaaba605340fdee8fc08e406c56617566d9643ad8bf6852778fc7", + "sha256:e0381b4ce23ff92f8170080c97678040fc5b08da85e9e292292aba67fdac6c34", + "sha256:e23a6d84d9d1738dbc6e38167776107e63307dfc8ad108e580548d1f2c587f42", + "sha256:e516dc8baf7b380e6c1c26792610230f37147bb754d6426462ab115a02944385", + "sha256:ea65804b5dc88dacd4a40279af0cdadcfe74b3e5b4c897aa0d81cf86927fee78", + "sha256:ec61d826d80fc293ed46c9dd26995921e3a82146feacd952ef0757236fc137be", + "sha256:ee04010f26d5102399bd17f8df8bc38dc7ccd7701dc77f4a68c5b8d733406958", + "sha256:f3bc6af6e2b8f92eced34ef6a96ffb248e863af20ef4fde9448cc8c9b858b749", + "sha256:f7d6b36dd2e029b6bcb8a13cf19664c7b8e19ab3a58e0fefbb5b8461447ed5ec" ], "markers": "python_version >= '3.7'", - "version": "==1.8.2" + "version": "==1.9.4" } }, "develop": { From a9a2b7469a50c96fc449adcecb42f310e7c46ed4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 30 Jan 2024 00:15:58 +0000 Subject: [PATCH 07/40] Bump aiohttp from 3.9.0 to 3.9.2 Bumps [aiohttp](https://github.com/aio-libs/aiohttp) from 3.9.0 to 3.9.2. - [Release notes](https://github.com/aio-libs/aiohttp/releases) - [Changelog](https://github.com/aio-libs/aiohttp/blob/master/CHANGES.rst) - [Commits](https://github.com/aio-libs/aiohttp/compare/v3.9.0...v3.9.2) --- updated-dependencies: - dependency-name: aiohttp dependency-type: indirect ... Signed-off-by: dependabot[bot] --- Pipfile.lock | 154 +++++++++++++++++++++++++-------------------------- 1 file changed, 77 insertions(+), 77 deletions(-) diff --git a/Pipfile.lock b/Pipfile.lock index e4f481f6..950c3ddf 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -18,86 +18,86 @@ "default": { "aiohttp": { "hashes": [ - "sha256:05857848da443c8c12110d99285d499b4e84d59918a21132e45c3f0804876994", - "sha256:05a183f1978802588711aed0dea31e697d760ce9055292db9dc1604daa9a8ded", - "sha256:09f23292d29135025e19e8ff4f0a68df078fe4ee013bca0105b2e803989de92d", - "sha256:11ca808f9a6b63485059f5f6e164ef7ec826483c1212a44f268b3653c91237d8", - "sha256:1736d87dad8ef46a8ec9cddd349fa9f7bd3a064c47dd6469c0d6763d3d49a4fc", - "sha256:1df43596b826022b14998f0460926ce261544fedefe0d2f653e1b20f49e96454", - "sha256:23170247ef89ffa842a02bbfdc425028574d9e010611659abeb24d890bc53bb8", - "sha256:2779f5e7c70f7b421915fd47db332c81de365678180a9f3ab404088f87ba5ff9", - "sha256:28185e36a78d247c55e9fbea2332d16aefa14c5276a582ce7a896231c6b1c208", - "sha256:2cbc14a13fb6b42d344e4f27746a4b03a2cb0c1c3c5b932b0d6ad8881aa390e3", - "sha256:2d71abc15ff7047412ef26bf812dfc8d0d1020d664617f4913df2df469f26b76", - "sha256:2d820162c8c2bdbe97d328cd4f417c955ca370027dce593345e437b2e9ffdc4d", - "sha256:317719d7f824eba55857fe0729363af58e27c066c731bc62cd97bc9c3d9c7ea4", - "sha256:35a68cd63ca6aaef5707888f17a70c36efe62b099a4e853d33dc2e9872125be8", - "sha256:3607375053df58ed6f23903aa10cf3112b1240e8c799d243bbad0f7be0666986", - "sha256:366bc870d7ac61726f32a489fbe3d1d8876e87506870be66b01aeb84389e967e", - "sha256:3abf0551874fecf95f93b58f25ef4fc9a250669a2257753f38f8f592db85ddea", - "sha256:3d7f6235c7475658acfc1769d968e07ab585c79f6ca438ddfecaa9a08006aee2", - "sha256:3dd8119752dd30dd7bca7d4bc2a92a59be6a003e4e5c2cf7e248b89751b8f4b7", - "sha256:42fe4fd9f0dfcc7be4248c162d8056f1d51a04c60e53366b0098d1267c4c9da8", - "sha256:45820ddbb276113ead8d4907a7802adb77548087ff5465d5c554f9aa3928ae7d", - "sha256:4790e44f46a4aa07b64504089def5744d3b6780468c4ec3a1a36eb7f2cae9814", - "sha256:4afa8f71dba3a5a2e1e1282a51cba7341ae76585345c43d8f0e624882b622218", - "sha256:4b777c9286b6c6a94f50ddb3a6e730deec327e9e2256cb08b5530db0f7d40fd8", - "sha256:4ee1b4152bc3190cc40ddd6a14715e3004944263ea208229ab4c297712aa3075", - "sha256:51a4cd44788ea0b5e6bb8fa704597af3a30be75503a7ed1098bc5b8ffdf6c982", - "sha256:536b01513d67d10baf6f71c72decdf492fb7433c5f2f133e9a9087379d4b6f31", - "sha256:571760ad7736b34d05597a1fd38cbc7d47f7b65deb722cb8e86fd827404d1f6b", - "sha256:5a2eb5311a37fe105aa35f62f75a078537e1a9e4e1d78c86ec9893a3c97d7a30", - "sha256:5ab16c254e2312efeb799bc3c06897f65a133b38b69682bf75d1f1ee1a9c43a9", - "sha256:65b0a70a25456d329a5e1426702dde67be0fb7a4ead718005ba2ca582d023a94", - "sha256:673343fbc0c1ac44d0d2640addc56e97a052504beacd7ade0dc5e76d3a4c16e8", - "sha256:6777a390e41e78e7c45dab43a4a0196c55c3b8c30eebe017b152939372a83253", - "sha256:6896b8416be9ada4d22cd359d7cb98955576ce863eadad5596b7cdfbf3e17c6c", - "sha256:694df243f394629bcae2d8ed94c589a181e8ba8604159e6e45e7b22e58291113", - "sha256:70e851f596c00f40a2f00a46126c95c2e04e146015af05a9da3e4867cfc55911", - "sha256:7276fe0017664414fdc3618fca411630405f1aaf0cc3be69def650eb50441787", - "sha256:76a86a9989ebf82ee61e06e2bab408aec4ea367dc6da35145c3352b60a112d11", - "sha256:7a94bde005a8f926d0fa38b88092a03dea4b4875a61fbcd9ac6f4351df1b57cd", - "sha256:7ae5f99a32c53731c93ac3075abd3e1e5cfbe72fc3eaac4c27c9dd64ba3b19fe", - "sha256:7e8a3b79b6d186a9c99761fd4a5e8dd575a48d96021f220ac5b5fa856e5dd029", - "sha256:816f4db40555026e4cdda604a1088577c1fb957d02f3f1292e0221353403f192", - "sha256:8303531e2c17b1a494ffaeba48f2da655fe932c4e9a2626c8718403c83e5dd2b", - "sha256:8488519aa05e636c5997719fe543c8daf19f538f4fa044f3ce94bee608817cff", - "sha256:87c8b0a6487e8109427ccf638580865b54e2e3db4a6e0e11c02639231b41fc0f", - "sha256:8c9e5f4d7208cda1a2bb600e29069eecf857e6980d0ccc922ccf9d1372c16f4b", - "sha256:94697c7293199c2a2551e3e3e18438b4cba293e79c6bc2319f5fd652fccb7456", - "sha256:9623cfd9e85b76b83ef88519d98326d4731f8d71869867e47a0b979ffec61c73", - "sha256:98d21092bf2637c5fa724a428a69e8f5955f2182bff61f8036827cf6ce1157bf", - "sha256:99ae01fb13a618b9942376df77a1f50c20a281390dad3c56a6ec2942e266220d", - "sha256:9c196b30f1b1aa3363a69dd69079ae9bec96c2965c4707eaa6914ba099fb7d4f", - "sha256:a00ce44c21612d185c5275c5cba4bab8d7c1590f248638b667ed8a782fa8cd6f", - "sha256:a1b66dbb8a7d5f50e9e2ea3804b01e766308331d0cac76eb30c563ac89c95985", - "sha256:a1d7edf74a36de0e5ca50787e83a77cf352f5504eb0ffa3f07000a911ba353fb", - "sha256:a1e3b3c107ccb0e537f309f719994a55621acd2c8fdf6d5ce5152aed788fb940", - "sha256:a486ddf57ab98b6d19ad36458b9f09e6022de0381674fe00228ca7b741aacb2f", - "sha256:ac9669990e2016d644ba8ae4758688534aabde8dbbc81f9af129c3f5f01ca9cd", - "sha256:b1a2ea8252cacc7fd51df5a56d7a2bb1986ed39be9397b51a08015727dfb69bd", - "sha256:c5b7bf8fe4d39886adc34311a233a2e01bc10eb4e842220235ed1de57541a896", - "sha256:c67a51ea415192c2e53e4e048c78bab82d21955b4281d297f517707dc836bf3d", - "sha256:ca4fddf84ac7d8a7d0866664936f93318ff01ee33e32381a115b19fb5a4d1202", - "sha256:d5b9345ab92ebe6003ae11d8092ce822a0242146e6fa270889b9ba965457ca40", - "sha256:d97c3e286d0ac9af6223bc132dc4bad6540b37c8d6c0a15fe1e70fb34f9ec411", - "sha256:db04d1de548f7a62d1dd7e7cdf7c22893ee168e22701895067a28a8ed51b3735", - "sha256:dcf71c55ec853826cd70eadb2b6ac62ec577416442ca1e0a97ad875a1b3a0305", - "sha256:de3cc86f4ea8b4c34a6e43a7306c40c1275e52bfa9748d869c6b7d54aa6dad80", - "sha256:deac0a32aec29608eb25d730f4bc5a261a65b6c48ded1ed861d2a1852577c932", - "sha256:e18d92c3e9e22553a73e33784fcb0ed484c9874e9a3e96c16a8d6a1e74a0217b", - "sha256:eb6dfd52063186ac97b4caa25764cdbcdb4b10d97f5c5f66b0fa95052e744eb7", - "sha256:f09960b5bb1017d16c0f9e9f7fc42160a5a49fa1e87a175fd4a2b1a1833ea0af", - "sha256:f1e4f254e9c35d8965d377e065c4a8a55d396fe87c8e7e8429bcfdeeb229bfb3", - "sha256:f32c86dc967ab8c719fd229ce71917caad13cc1e8356ee997bf02c5b368799bf", - "sha256:f50b4663c3e0262c3a361faf440761fbef60ccdde5fe8545689a4b3a3c149fb4", - "sha256:f8e05f5163528962ce1d1806fce763ab893b1c5b7ace0a3538cd81a90622f844", - "sha256:f929f4c9b9a00f3e6cc0587abb95ab9c05681f8b14e0fe1daecfa83ea90f8318", - "sha256:f9e09a1c83521d770d170b3801eea19b89f41ccaa61d53026ed111cb6f088887" + "sha256:00a9abcea793c81e7f8778ca195a1714a64f6d7436c4c0bb168ad2a212627000", + "sha256:04fd8ffd2be73d42bcf55fd78cde7958eeee6d4d8f73c3846b7cba491ecdb570", + "sha256:07205ae0015e05c78b3288c1517afa000823a678a41594b3fdc870878d645305", + "sha256:07be2be7071723c3509ab5c08108d3a74f2181d4964e869f2504aaab68f8d3e8", + "sha256:0b500c5ad9c07639d48615a770f49618130e61be36608fc9bc2d9bae31732b8f", + "sha256:0e4ee4df741670560b1bc393672035418bf9063718fee05e1796bf867e995fad", + "sha256:103daf41ff3b53ba6fa09ad410793e2e76c9d0269151812e5aba4b9dd674a7e8", + "sha256:114da29f39eccd71b93a0fcacff178749a5c3559009b4a4498c2c173a6d74dff", + "sha256:122468f6fee5fcbe67cb07014a08c195b3d4c41ff71e7b5160a7bcc41d585a5f", + "sha256:1543e7fb00214fb4ccead42e6a7d86f3bb7c34751ec7c605cca7388e525fd0b4", + "sha256:158564d0d1020e0d3fe919a81d97aadad35171e13e7b425b244ad4337fc6793a", + "sha256:16a967685907003765855999af11a79b24e70b34dc710f77a38d21cd9fc4f5fe", + "sha256:186e94570433a004e05f31f632726ae0f2c9dee4762a9ce915769ce9c0a23d89", + "sha256:193cc1ccd69d819562cc7f345c815a6fc51d223b2ef22f23c1a0f67a88de9a72", + "sha256:1956e3ac376b1711c1533266dec4efd485f821d84c13ce1217d53e42c9e65f08", + "sha256:1c45e4e815ac6af3b72ca2bde9b608d2571737bb1e2d42299fc1ffdf60f6f9a1", + "sha256:200dc0246f0cb5405c80d18ac905c8350179c063ea1587580e3335bfc243ba6a", + "sha256:2dec87a556f300d3211decf018bfd263424f0690fcca00de94a837949fbcea02", + "sha256:328918a6c2835861ff7afa8c6d2c70c35fdaf996205d5932351bdd952f33fa2f", + "sha256:3cc158466f6a980a6095ee55174d1de5730ad7dec251be655d9a6a9dd7ea1ff9", + "sha256:3d8d962b439a859b3ded9a1e111a4615357b01620a546bc601f25b0211f2da81", + "sha256:3e1a800f988ce7c4917f34096f81585a73dbf65b5c39618b37926b1238cf9bc4", + "sha256:3f17999ae3927d8a9a823a1283b201344a0627272f92d4f3e3a4efe276972fe8", + "sha256:4112d8ba61fbd0abd5d43a9cb312214565b446d926e282a6d7da3f5a5aa71d36", + "sha256:4bd9d5b989d57b41e4ff56ab250c5ddf259f32db17159cce630fd543376bd96b", + "sha256:4c189b64bd6d9a403a1a3f86a3ab3acbc3dc41a68f73a268a4f683f89a4dec1f", + "sha256:4deae2c165a5db1ed97df2868ef31ca3cc999988812e82386d22937d9d6fed52", + "sha256:5264d7327c9464786f74e4ec9342afbbb6ee70dfbb2ec9e3dfce7a54c8043aa3", + "sha256:5422cd9a4a00f24c7244e1b15aa9b87935c85fb6a00c8ac9b2527b38627a9211", + "sha256:54287bcb74d21715ac8382e9de146d9442b5f133d9babb7e5d9e453faadd005e", + "sha256:54ec82f45d57c9a65a1ead3953b51c704f9587440e6682f689da97f3e8defa35", + "sha256:5bb3d05569aa83011fcb346b5266e00b04180105fcacc63743fc2e4a1862a891", + "sha256:61c47ab8ef629793c086378b1df93d18438612d3ed60dca76c3422f4fbafa792", + "sha256:68bbee9e17d66f17bb0010aa15a22c6eb28583edcc8b3212e2b8e3f77f3ebe2a", + "sha256:6aaa6f99256dd1b5756a50891a20f0d252bd7bdb0854c5d440edab4495c9f973", + "sha256:6f4cdba12539215aaecf3c310ce9d067b0081a0795dd8a8805fdb67a65c0572a", + "sha256:6fa3ee92cd441d5c2d07ca88d7a9cef50f7ec975f0117cd0c62018022a184308", + "sha256:772fbe371788e61c58d6d3d904268e48a594ba866804d08c995ad71b144f94cb", + "sha256:7a75307ffe31329928a8d47eae0692192327c599113d41b278d4c12b54e1bd11", + "sha256:7a9825fdd64ecac5c670234d80bb52bdcaa4139d1f839165f548208b3779c6c6", + "sha256:7d579dcd5d82a86a46f725458418458fa43686f6a7b252f2966d359033ffc8ab", + "sha256:8008d0f451d66140a5aa1c17e3eedc9d56e14207568cd42072c9d6b92bf19b52", + "sha256:84e843b33d5460a5c501c05539809ff3aee07436296ff9fbc4d327e32aa3a326", + "sha256:8a7876f794523123bca6d44bfecd89c9fec9ec897a25f3dd202ee7fc5c6525b7", + "sha256:8ceb658afd12b27552597cf9a65d9807d58aef45adbb58616cdd5ad4c258c39e", + "sha256:972b63d589ff8f305463593050a31b5ce91638918da38139b9d8deaba9e0fed7", + "sha256:9a87aa0b13bbee025faa59fa58861303c2b064b9855d4c0e45ec70182bbeba1b", + "sha256:a1c3a4d0ab2f75f22ec80bca62385db2e8810ee12efa8c9e92efea45c1849133", + "sha256:a27d8c70ad87bcfce2e97488652075a9bdd5b70093f50b10ae051dfe5e6baf37", + "sha256:aa906b9bdfd4a7972dd0628dbbd6413d2062df5b431194486a78f0d2ae87bd55", + "sha256:abeb813a18eb387f0d835ef51f88568540ad0325807a77a6e501fed4610f864e", + "sha256:ae0a1e638cffc3ec4d4784b8b4fd1cf28968febc4bd2718ffa25b99b96a741bd", + "sha256:b0ad0a5e86ce73f5368a164c10ada10504bf91869c05ab75d982c6048217fbf7", + "sha256:b141753be581fab842a25cb319f79536d19c2a51995d7d8b29ee290169868eab", + "sha256:b65e861f4bebfb660f7f0f40fa3eb9f2ab9af10647d05dac824390e7af8f75b7", + "sha256:b9f1cb839b621f84a5b006848e336cf1496688059d2408e617af33e3470ba204", + "sha256:bc71f748e12284312f140eaa6599a520389273174b42c345d13c7e07792f4f57", + "sha256:bda42eb410be91b349fb4ee3a23a30ee301c391e503996a638d05659d76ea4c2", + "sha256:c07327b368745b1ce2393ae9e1aafed7073d9199e1dcba14e035cc646c7941bf", + "sha256:c4ad4241b52bb2eb7a4d2bde060d31c2b255b8c6597dd8deac2f039168d14fd7", + "sha256:c8534e7d69bb8e8d134fe2be9890d1b863518582f30c9874ed7ed12e48abe3c4", + "sha256:cc7d6502c23a0ec109687bf31909b3fb7b196faf198f8cff68c81b49eb316ea9", + "sha256:cc91d07280d7d169f3a0f9179d8babd0ee05c79d4d891447629ff0d7d8089ec2", + "sha256:cfee9287778399fdef6f8a11c9e425e1cb13cc9920fd3a3df8f122500978292b", + "sha256:d22a0931848b8c7a023c695fa2057c6aaac19085f257d48baa24455e67df97ec", + "sha256:d23fba734e3dd7b1d679b9473129cd52e4ec0e65a4512b488981a56420e708db", + "sha256:d43302a30ba1166325974858e6ef31727a23bdd12db40e725bec0f759abce505", + "sha256:d52d20832ac1560f4510d68e7ba8befbc801a2b77df12bd0cd2bcf3b049e52a4", + "sha256:da1346cd0ccb395f0ed16b113ebb626fa43b7b07fd7344fce33e7a4f04a8897a", + "sha256:e2cc0d04688b9f4a7854c56c18aa7af9e5b0a87a28f934e2e596ba7e14783192", + "sha256:ea510718a41b95c236c992b89fdfc3d04cc7ca60281f93aaada497c2b4e05c46", + "sha256:eaa9256de26ea0334ffa25f1913ae15a51e35c529a1ed9af8e6286dd44312554", + "sha256:edd4f1af2253f227ae311ab3d403d0c506c9b4410c7fc8d9573dec6d9740369f", + "sha256:ee2661a3f5b529f4fc8a8ffee9f736ae054adfb353a0d2f78218be90617194b3", + "sha256:f31df6a32217a34ae2f813b152a6f348154f948c83213b690e59d9e84020925c", + "sha256:fa6904088e6642609981f919ba775838ebf7df7fe64998b1a954fb411ffb4663" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==3.9.0" + "version": "==3.9.2" }, "aiosignal": { "hashes": [ From 44020c62f81b2cf6284555481c47cd812c1df290 Mon Sep 17 00:00:00 2001 From: Stuart McHattie Date: Mon, 5 Feb 2024 15:26:54 +0000 Subject: [PATCH 08/40] Update instructions around snappy installation --- README.md | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index d4959497..0b99c7fc 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,6 @@ Rabbitmq consumer for TOL data input - ## Getting Started The following tools are required for development: @@ -16,23 +15,22 @@ defined in the `Pipfile`: brew install pyenv pyenv install ``` - + Use pipenv to install the required python packages for the application and development: ```bash pipenv install --dev ``` - ### Setting up with Docker If you want to setup a local development environment with Docker please check the instructions in [SETUP_DOCKER.md](SETUP_DOCKER.md) - ## Running 1. Enter the python virtual environment using: + ```bash pipenv shell ``` @@ -51,7 +49,6 @@ Run the tests using pytest (flags are for verbose and exit early): python -m pytest -vx ``` - ## Deployment This project uses a Docker image as the unit of deployment. Update `.release-version` with @@ -60,7 +57,7 @@ along with the Docker image associated to that release. ## Snappy -``` +```stdout If when you install the dependencies and you see the following error: [pipenv.exceptions.InstallError]: src/snappy/snappymodule.cc:33:10: fatal error: 'snappy-c.h' file not found @@ -79,10 +76,14 @@ ERROR: Couldn't install package: {} You need to install snappy and set the path: -``` +```bash brew install snappy ``` -``` -pip install --global-option=build_ext --global-option='-I/opt/homebrew/include' --global-option='-L/opt/homebrew/lib' python-snappy +Ensure the `include` and `lib` directories of `homebrew` are set in environment variables. +You might want to add these to your `~/.zshrc` file: + +```bash +export CPPFLAGS="-I$(brew --prefix)/include" +export LDFLAGS="-L$(brew --prefix)/lib" ``` From fc8bb1807bb5f487aa4c8de66e5e7cd3fcbdd90f Mon Sep 17 00:00:00 2001 From: Stuart McHattie Date: Mon, 5 Feb 2024 15:32:56 +0000 Subject: [PATCH 09/40] Fix Markdown formatting --- README.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 0b99c7fc..f9b52ea0 100644 --- a/README.md +++ b/README.md @@ -12,14 +12,14 @@ Use pyenv or something similar to install the version of python defined in the `Pipfile`: ```bash - brew install pyenv - pyenv install +brew install pyenv +pyenv install ``` Use pipenv to install the required python packages for the application and development: ```bash - pipenv install --dev +pipenv install --dev ``` ### Setting up with Docker @@ -31,22 +31,22 @@ the instructions in [SETUP_DOCKER.md](SETUP_DOCKER.md) 1. Enter the python virtual environment using: -```bash + ```bash pipenv shell -``` + ``` 1. Run the app using: -```bash + ```bash python main.py -``` + ``` ## Testing Run the tests using pytest (flags are for verbose and exit early): ```bash - python -m pytest -vx +python -m pytest -vx ``` ## Deployment @@ -57,9 +57,9 @@ along with the Docker image associated to that release. ## Snappy -```stdout If when you install the dependencies and you see the following error: +```stdout [pipenv.exceptions.InstallError]: src/snappy/snappymodule.cc:33:10: fatal error: 'snappy-c.h' file not found [pipenv.exceptions.InstallError]: #include [pipenv.exceptions.InstallError]: ^~~~~~~~~~~~ @@ -74,7 +74,7 @@ ERROR: Couldn't install package: {} Package installation failed... ``` -You need to install snappy and set the path: +You need to install snappy: ```bash brew install snappy From ef835669551468ce3bedf8fa2d28c50e324de66c Mon Sep 17 00:00:00 2001 From: Stuart McHattie Date: Mon, 5 Feb 2024 17:01:01 +0000 Subject: [PATCH 10/40] Improve documentation markdown files --- SETUP_DOCKER.md | 56 ++++++++++++++++++++++++++--------------------- schemas/README.md | 31 +++++++++++++++----------- 2 files changed, 49 insertions(+), 38 deletions(-) diff --git a/SETUP_DOCKER.md b/SETUP_DOCKER.md index 40f33fea..0ecb6590 100644 --- a/SETUP_DOCKER.md +++ b/SETUP_DOCKER.md @@ -1,41 +1,48 @@ ## Setting up a complete development environment from scratch 1. Start dependent services: RabbitMQ and RedPanda -```bash - ./docker/dependencies/up.sh -``` -2. Setup RabbitMQ configuration (queues, etc). You may need to wait 30 seconds from the previous command + ```bash + ./docker/dependencies/up.sh + ``` + +1. Setup RabbitMQ configuration (queues, etc). You may need to wait 30 seconds from the previous command to run this one as it requires Rabbitmq to have started completely: -```bash + + ```bash python setup_dev_rabbit.py -``` + ``` -3. Load Redpanda schemas: -```bash +1. Load Redpanda schemas: + + ```bash ./schemas/push.sh http://localhost:8081 redpanda-test -``` + ``` + +1. Build docker image -4. Build docker image -```bash + ```bash docker build . -t tol-lab-share:develop -``` + ``` -5. Create .env file with contents -``` -SETTINGS_MODULE=tol_lab_share.config.defaults -LOCALHOST=host.docker.internal -``` +1. Create .env file with contents -6. Start interactive bash in docker container -```bash + ```text + SETTINGS_MODULE=tol_lab_share.config.defaults + LOCALHOST=host.docker.internal + ``` + +1. Start interactive bash in docker container + + ```bash docker run -ti -v $(pwd):/code --env-file=.env --entrypoint bash tol-lab-share:develop -``` + ``` -7. Start the consumer service (inside the previous bash) -```bash +1. Start the consumer service (inside the previous bash) + + ```bash pipenv run python main.py -``` + ``` After this you should have: @@ -43,5 +50,4 @@ After this you should have: * Rabbitmq service running in http://localhost:8080/ with user/password: admin/development * Redpanda API service running in local in http://localhost:8081/ - -If you want to perform any changes in code, you can kill the consumer with Control-C, modify the code in local and then restart the consumer again using the same command \ No newline at end of file +If you want to perform any changes in code, you can kill the consumer with Control-C, modify the code in local and then restart the consumer again using the same command diff --git a/schemas/README.md b/schemas/README.md index d5110da6..97adad83 100644 --- a/schemas/README.md +++ b/schemas/README.md @@ -1,32 +1,37 @@ How to publish schemas ---------------------- +1. Ensure `jq` is installed: + + ```bash + brew install jq + ``` 1. Run the command: -```bash - push.sh -``` + ```bash + push.sh + ``` -where: + where: -``` - : URL to connect to RedPanda where the schemas will be uploaded - : secret key with write permission for redpanda -``` + ```text + : URL to connect to RedPanda where the schemas will be uploaded + : secret key with write permission for redpanda + ``` How to remove last schemas created ---------------------------------- -1. Run the command: +Run the command: ```bash - remove_all.sh +remove_all.sh ``` where: -``` - : URL to connect to RedPanda where the schemas will be uploaded - : secret key with write permission for redpanda +```text +: URL to connect to RedPanda where the schemas will be uploaded +: secret key with write permission for redpanda ``` From 674c6f77f48b9fc1da078cd23bbd6fd8375e6b51 Mon Sep 17 00:00:00 2001 From: Stuart McHattie Date: Tue, 6 Feb 2024 18:34:09 +0000 Subject: [PATCH 11/40] Fix typo in docstring Also lay the docstring out according to Google's style guide --- tol_lab_share/messages/traction_qc_message.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tol_lab_share/messages/traction_qc_message.py b/tol_lab_share/messages/traction_qc_message.py index 4034811c..904cc002 100644 --- a/tol_lab_share/messages/traction_qc_message.py +++ b/tol_lab_share/messages/traction_qc_message.py @@ -198,12 +198,14 @@ def validators(self) -> List[Callable]: return [self.check_has_requests, self.check_requests_have_all_content, self.check_no_errors] def requests(self, position: int) -> TractionQcMessageRequestInterface: - """Returns the request at position position from this message. If there is no requesrt there it + """Returns the request at position position from this message. If there is no request there it will create a new instance and return it. - Parameters: - position (int) position that we want to return + + Args: + position (int) position that we want to return. + Returns: - OutputTractionMessageRequest with the request required + TractionQcMessageRequest at the specified position. """ if position not in self._requests: self._requests[position] = TractionQcMessageRequest() From 83592ef3ed44a98fbb8ab585eabd20a73fd44586 Mon Sep 17 00:00:00 2001 From: Stuart McHattie Date: Tue, 6 Feb 2024 18:34:34 +0000 Subject: [PATCH 12/40] Switch Traction payload to receive Sanger Sample ID --- .../definitions/test_labware.py | 2 +- tests/messages/test_traction_qc_message.py | 8 ++++---- .../message_properties/definitions/labware.py | 4 ++-- tol_lab_share/messages/interfaces.py | 6 +++--- tol_lab_share/messages/traction_qc_message.py | 20 +++++++++---------- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/tests/message_properties/definitions/test_labware.py b/tests/message_properties/definitions/test_labware.py index d834dbfb..b2f6dc6f 100644 --- a/tests/message_properties/definitions/test_labware.py +++ b/tests/message_properties/definitions/test_labware.py @@ -182,7 +182,7 @@ def test_add_to_traction_qc_message(valid_sample): traction_qc_message = TractionQcMessage() instance.add_to_traction_qc_message(traction_qc_message) - assert traction_qc_message.requests(0).supplier_sample_name == "SampleSupplied1" + assert traction_qc_message.requests(0).sanger_sample_id == "cichlid_pacbio8196429" assert traction_qc_message.requests(0).container_barcode == "BARCODE001" assert traction_qc_message.requests(0).sheared_femto_fragment_size == "8" assert traction_qc_message.requests(0).post_spri_concentration == "9" diff --git a/tests/messages/test_traction_qc_message.py b/tests/messages/test_traction_qc_message.py index 5ece46db..cca2f054 100644 --- a/tests/messages/test_traction_qc_message.py +++ b/tests/messages/test_traction_qc_message.py @@ -22,7 +22,7 @@ class TestTractionQcMessage: @pytest.fixture() def valid_traction_qc_message(self): traction_qc_message = TractionQcMessage() - traction_qc_message.requests(0).supplier_sample_name = "supplier_sample_name_DDD" + traction_qc_message.requests(0).sanger_sample_id = "sanger_sample_id_DDD" traction_qc_message.requests(0).container_barcode = "FD20706500" traction_qc_message.requests(0).sheared_femto_fragment_size = "5" traction_qc_message.requests(0).post_spri_concentration = "10" @@ -33,7 +33,7 @@ def valid_traction_qc_message(self): traction_qc_message.requests(0).shearing_qc_comments = "Comments" traction_qc_message.requests(0).date_submitted_utc = datetime.now().timestamp() * 1000 - traction_qc_message.requests(1).supplier_sample_name = "supplier_sample_name_DDD2" + traction_qc_message.requests(1).sanger_sample_id = "sanger_sample_id_DDD2" traction_qc_message.requests(1).container_barcode = "FD20706501" traction_qc_message.requests(1).sheared_femto_fragment_size = "9" traction_qc_message.requests(1).post_spri_concentration = "10" @@ -79,7 +79,7 @@ def test_can_generate_correct_payload(self, valid_traction_qc_message): "shearing_qc_comments": "Comments", "date_submitted": datetime.now().timestamp() * 1000, "labware_barcode": "FD20706500", - "sample_external_id": "supplier_sample_name_DDD", + "sample_external_id": "sanger_sample_id_DDD", }, { "final_nano_drop": "100", @@ -90,7 +90,7 @@ def test_can_generate_correct_payload(self, valid_traction_qc_message): "sheared_femto_fragment_size": "9", "date_submitted": datetime.now().timestamp() * 1000, "labware_barcode": "FD20706501", - "sample_external_id": "supplier_sample_name_DDD2", + "sample_external_id": "sanger_sample_id_DDD2", }, ], "source": "tol-lab-share.tol", diff --git a/tol_lab_share/message_properties/definitions/labware.py b/tol_lab_share/message_properties/definitions/labware.py index 3a7a83bc..a81a744b 100644 --- a/tol_lab_share/message_properties/definitions/labware.py +++ b/tol_lab_share/message_properties/definitions/labware.py @@ -133,6 +133,6 @@ def add_to_traction_qc_message(self, traction_qc_message: TractionQcMessageInter ).value traction_qc_message.requests(sample_pos).date_submitted_utc = sample.properties("date_submitted_utc").value traction_qc_message.requests(sample_pos).container_barcode = self.properties("barcode").value - traction_qc_message.requests(sample_pos).supplier_sample_name = sample.properties( - "supplier_sample_name" + traction_qc_message.requests(sample_pos).sanger_sample_id = sample.properties( + "sanger_sample_id" ).value diff --git a/tol_lab_share/messages/interfaces.py b/tol_lab_share/messages/interfaces.py index 47318218..0ff40cf5 100644 --- a/tol_lab_share/messages/interfaces.py +++ b/tol_lab_share/messages/interfaces.py @@ -279,12 +279,12 @@ def container_barcode(self, value: Optional[str]) -> None: @property @abstractmethod - def supplier_sample_name(self) -> Optional[str]: + def sanger_sample_id(self) -> Optional[str]: ... - @supplier_sample_name.setter + @sanger_sample_id.setter @abstractmethod - def supplier_sample_name(self, value: Optional[str]) -> None: + def sanger_sample_id(self, value: Optional[str]) -> None: ... @property diff --git a/tol_lab_share/messages/traction_qc_message.py b/tol_lab_share/messages/traction_qc_message.py index 904cc002..10ff6e13 100644 --- a/tol_lab_share/messages/traction_qc_message.py +++ b/tol_lab_share/messages/traction_qc_message.py @@ -24,7 +24,7 @@ class TractionQcMessageRequest(TractionQcMessageRequestInterface): def __init__(self): """Constructor to initialize the info for the request""" - self._supplier_sample_name = None + self._sanger_sample_id = None self._container_barcode = None self._sheared_femto_fragment_size = None self._post_spri_concentration = None @@ -39,7 +39,7 @@ def validate(self) -> bool: """Checks that we have all required information and that it is valid before marking this request as valid.""" return ( - (self._supplier_sample_name is not None) + (self._sanger_sample_id is not None) and (self._container_barcode is not None) and (self._sheared_femto_fragment_size is not None) and (self._post_spri_concentration is not None) @@ -62,14 +62,14 @@ def container_barcode(self, value: Optional[str]) -> None: self._container_barcode = value @property - def supplier_sample_name(self) -> Optional[str]: - """Gets the supplier sample name for this request.""" - return self._supplier_sample_name + def sanger_sample_id(self) -> Optional[str]: + """Gets the Sanger sample id for this request.""" + return self._sanger_sample_id - @supplier_sample_name.setter - def supplier_sample_name(self, value: Optional[str]) -> None: - """Sets the supplier sample name for this request.""" - self._supplier_sample_name = value + @sanger_sample_id.setter + def sanger_sample_id(self, value: Optional[str]) -> None: + """Sets the Sanger sample id for this request.""" + self._sanger_sample_id = value @property def sheared_femto_fragment_size(self) -> Optional[str]: @@ -166,7 +166,7 @@ def payload(self) -> Dict[str, Any]: "final_nano_drop_230": self.instance.final_nano_drop_230, "final_nano_drop": self.instance.final_nano_drop, "shearing_qc_comments": self.instance.shearing_qc_comments, - "sample_external_id": self.instance.supplier_sample_name, + "sample_external_id": self.instance.sanger_sample_id, "labware_barcode": self.instance.container_barcode, "date_submitted": self.instance.date_submitted_utc, } From 31068c864aecbff1acaad05cbffa8934fd23b08e Mon Sep 17 00:00:00 2001 From: Stuart McHattie Date: Tue, 6 Feb 2024 18:43:50 +0000 Subject: [PATCH 13/40] Accept black reformat --- tol_lab_share/message_properties/definitions/labware.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tol_lab_share/message_properties/definitions/labware.py b/tol_lab_share/message_properties/definitions/labware.py index a81a744b..2ea84eb9 100644 --- a/tol_lab_share/message_properties/definitions/labware.py +++ b/tol_lab_share/message_properties/definitions/labware.py @@ -133,6 +133,4 @@ def add_to_traction_qc_message(self, traction_qc_message: TractionQcMessageInter ).value traction_qc_message.requests(sample_pos).date_submitted_utc = sample.properties("date_submitted_utc").value traction_qc_message.requests(sample_pos).container_barcode = self.properties("barcode").value - traction_qc_message.requests(sample_pos).sanger_sample_id = sample.properties( - "sanger_sample_id" - ).value + traction_qc_message.requests(sample_pos).sanger_sample_id = sample.properties("sanger_sample_id").value From 412108d435311d4aebec95935ceab931cb54be8c Mon Sep 17 00:00:00 2001 From: Stuart McHattie Date: Fri, 9 Feb 2024 10:56:19 +0000 Subject: [PATCH 14/40] Do some tidy up of docstrings --- tol_lab_share/messages/output_traction_message.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/tol_lab_share/messages/output_traction_message.py b/tol_lab_share/messages/output_traction_message.py index 84b0fc92..2a116d4f 100644 --- a/tol_lab_share/messages/output_traction_message.py +++ b/tol_lab_share/messages/output_traction_message.py @@ -25,7 +25,7 @@ class OutputTractionMessageRequest(OutputTractionMessageRequestInterface): """ def __init__(self): - """Constructor that initializes all info for a single request""" + """Constructor that initializes all info for a single request.""" self._library_type = None self._study_uuid = None self._sample_name = None @@ -46,8 +46,7 @@ def __init__(self): self._date_of_sample_collection = None def validate(self) -> bool: - """Checks that we have all required information and that it is valid before - marking this request as valid.""" + """Checks that we have all required information and that it is valid before marking this request as valid.""" return ( (self._library_type is not None) and (self._study_uuid is not None) @@ -251,16 +250,18 @@ class RequestSerializer: """Class to manage the serialization to JSON of the request received as argument in the constructor.""" def __init__(self, instance: OutputTractionMessageRequest): - """Constructor that sets initiali state of the instance. - Parameters: - instance (OutputTractionMessageRequest) request that we want to serialize + """Constructor that sets the initial state of the instance. + + Args: + instance (OutputTractionMessageRequest): The request that we want to serialize. """ self.instance = instance def is_ont_library_type(self) -> bool: """Flag boolean method that identifies if the library type is ONT. + Returns: - boolean indicating if the library type is ONT or not + bool: True if the library type is ONT; otherwise false. """ return bool(self.instance.library_type and ("ONT" in self.instance.library_type)) From cb58c410769b07bd2f4750b35f6bd8c369a12527 Mon Sep 17 00:00:00 2001 From: Stuart McHattie Date: Fri, 9 Feb 2024 11:57:38 +0000 Subject: [PATCH 15/40] Change requests to a list It doesn't make sense for it to be a dictionary when we iterate over it by expecting all keys in a range the same size as the length of the entries in the dictionary! --- .../definitions/test_labware.py | 46 +-- .../messages/test_output_traction_message.py | 275 ++++++++++-------- .../message_properties/definitions/labware.py | 37 +-- tol_lab_share/messages/interfaces.py | 2 +- .../messages/output_traction_message.py | 45 ++- 5 files changed, 213 insertions(+), 192 deletions(-) diff --git a/tests/message_properties/definitions/test_labware.py b/tests/message_properties/definitions/test_labware.py index b2f6dc6f..cb03df1e 100644 --- a/tests/message_properties/definitions/test_labware.py +++ b/tests/message_properties/definitions/test_labware.py @@ -114,17 +114,17 @@ def test_labware_add_to_traction_message_wells(valid_sample): traction_message = OutputTractionMessage() instance.add_to_traction_message(traction_message) - assert traction_message.requests(0).study_uuid == "dd490ee5-fd1d-456d-99fd-eb9d3861e014" - assert traction_message.requests(0).sample_name == "cichlid_pacbio8196429" - assert traction_message.requests(0).public_name == "SamplePublicName1" - assert traction_message.requests(0).sanger_sample_id == "cichlid_pacbio8196429" - assert traction_message.requests(0).sample_uuid == "dd490ee5-fd1d-456d-99fd-eb9d3861e0f6" - assert traction_message.requests(0).library_type == "Library1" - assert traction_message.requests(0).species == "Mus musculus" - assert traction_message.requests(0).container_barcode == "BARCODE001" - assert traction_message.requests(0).container_location == "A1" - assert traction_message.requests(0).container_type == "wells" - assert traction_message.requests(0).cost_code == "S1234" + assert traction_message._requests[0].study_uuid == "dd490ee5-fd1d-456d-99fd-eb9d3861e014" + assert traction_message._requests[0].sample_name == "cichlid_pacbio8196429" + assert traction_message._requests[0].public_name == "SamplePublicName1" + assert traction_message._requests[0].sanger_sample_id == "cichlid_pacbio8196429" + assert traction_message._requests[0].sample_uuid == "dd490ee5-fd1d-456d-99fd-eb9d3861e0f6" + assert traction_message._requests[0].library_type == "Library1" + assert traction_message._requests[0].species == "Mus musculus" + assert traction_message._requests[0].container_barcode == "BARCODE001" + assert traction_message._requests[0].container_location == "A1" + assert traction_message._requests[0].container_type == "wells" + assert traction_message._requests[0].cost_code == "S1234" def test_labware_add_to_traction_message_tubes(valid_sample): @@ -139,17 +139,17 @@ def test_labware_add_to_traction_message_tubes(valid_sample): traction_message = OutputTractionMessage() instance.add_to_traction_message(traction_message) - assert traction_message.requests(0).study_uuid == "dd490ee5-fd1d-456d-99fd-eb9d3861e014" - assert traction_message.requests(0).sample_name == "cichlid_pacbio8196429" - assert traction_message.requests(0).sanger_sample_id == "cichlid_pacbio8196429" - assert traction_message.requests(0).public_name == "SamplePublicName1" - assert traction_message.requests(0).sample_uuid == "dd490ee5-fd1d-456d-99fd-eb9d3861e0f6" - assert traction_message.requests(0).library_type == "Library1" - assert traction_message.requests(0).species == "Mus musculus" - assert traction_message.requests(0).container_barcode == "BARCODE001" - assert traction_message.requests(0).container_location == "A1" - assert traction_message.requests(0).container_type == "tubes" - assert traction_message.requests(0).cost_code == "S1234" + assert traction_message._requests[0].study_uuid == "dd490ee5-fd1d-456d-99fd-eb9d3861e014" + assert traction_message._requests[0].sample_name == "cichlid_pacbio8196429" + assert traction_message._requests[0].sanger_sample_id == "cichlid_pacbio8196429" + assert traction_message._requests[0].public_name == "SamplePublicName1" + assert traction_message._requests[0].sample_uuid == "dd490ee5-fd1d-456d-99fd-eb9d3861e0f6" + assert traction_message._requests[0].library_type == "Library1" + assert traction_message._requests[0].species == "Mus musculus" + assert traction_message._requests[0].container_barcode == "BARCODE001" + assert traction_message._requests[0].container_location == "A1" + assert traction_message._requests[0].container_type == "tubes" + assert traction_message._requests[0].cost_code == "S1234" def test_labware_add_to_traction_message_uses_unpadded_location(valid_sample): @@ -163,7 +163,7 @@ def test_labware_add_to_traction_message_uses_unpadded_location(valid_sample): instance.properties("samples")[0].add_property("location", Location(Input("B01"))) traction_message = OutputTractionMessage() instance.add_to_traction_message(traction_message) - assert traction_message.requests(0).container_location == "B1" + assert traction_message._requests[0].container_location == "B1" def test_add_to_traction_qc_message(valid_sample): diff --git a/tests/messages/test_output_traction_message.py b/tests/messages/test_output_traction_message.py index c8d75e7d..f4727ced 100644 --- a/tests/messages/test_output_traction_message.py +++ b/tests/messages/test_output_traction_message.py @@ -9,14 +9,16 @@ def valid_traction_message(): instance = OutputTractionMessage() - instance.requests(0).container_barcode = "1" - instance.requests(0).container_type = OUTPUT_TRACTION_MESSAGE_CREATE_REQUEST_CONTAINER_TYPE_WELLS - instance.requests(0).library_type = "library" - instance.requests(0).sample_name = "test1" - instance.requests(0).study_uuid = "dd490ee5-fd1d-456d-99fd-eb9d3861e014" - instance.requests(0).sample_uuid = "8860a6b4-82e2-451c-aba2-a3129c38c0fc" - instance.requests(0).species = "test species" - instance.requests(0).cost_code = "S1234" + request = instance.create_request() + request.container_barcode = "1" + request.container_type = OUTPUT_TRACTION_MESSAGE_CREATE_REQUEST_CONTAINER_TYPE_WELLS + request.library_type = "library" + request.sample_name = "test1" + request.study_uuid = "dd490ee5-fd1d-456d-99fd-eb9d3861e014" + request.sample_uuid = "8860a6b4-82e2-451c-aba2-a3129c38c0fc" + request.species = "test species" + request.cost_code = "S1234" + return instance @@ -29,9 +31,10 @@ def test_output_traction_message_can_validate(): assert not instance.validate() instance = OutputTractionMessage() - instance.requests(0).container_barcode = "1" - instance.requests(0).container_type = "wells" - instance.requests(0).library_type = "library" + request = instance.create_request() + request.container_barcode = "1" + request.container_type = "wells" + request.library_type = "library" assert not instance.validate() assert valid_traction_message().validate() @@ -40,50 +43,53 @@ def test_output_traction_message_can_validate(): def test_output_traction_message_can_generate_payload_for_plates(): my_date = datetime.now() instance = OutputTractionMessage() - instance.requests(0).container_barcode = "1" - instance.requests(0).container_type = OUTPUT_TRACTION_MESSAGE_CREATE_REQUEST_CONTAINER_TYPE_WELLS - instance.requests(1).container_location = "A1" - instance.requests(0).library_type = "library" - instance.requests(0).sample_name = "test1" - instance.requests(0).study_uuid = "dd490ee5-fd1d-456d-99fd-eb9d3861e014" - instance.requests(0).sample_uuid = "8860a6b4-82e2-451c-aba2-a3129c38c0fc" - instance.requests(0).species = "test species" - instance.requests(0).public_name = "Public1" - instance.requests(0).cost_code = "S1234" - instance.requests(0).priority_level = None - instance.requests(0).sanger_sample_id = "sample1" - instance.requests(0).supplier_name = "supplier1" - instance.requests(0).taxon_id = "9606" - instance.requests(0).donor_id = "donor1" - instance.requests(0).country_of_origin = "United Kingdom" - instance.requests(0).accession_number = "AN1234" - instance.requests(0).date_of_sample_collection = my_date - - instance.requests(1).container_barcode = "1" - instance.requests(1).container_type = OUTPUT_TRACTION_MESSAGE_CREATE_REQUEST_CONTAINER_TYPE_WELLS - instance.requests(1).container_location = "B1" - instance.requests(1).library_type = "library" - instance.requests(1).sample_name = "test1" - instance.requests(1).study_uuid = "dd490ee5-fd1d-456d-99fd-eb9d3861e014" - instance.requests(1).sample_uuid = "8860a6b4-82e2-451c-aba2-a3129c38c0fc" - instance.requests(1).species = "test species" - instance.requests(1).public_name = "Public2" - instance.requests(1).cost_code = "S4567" - instance.requests(1).priority_level = None - instance.requests(1).sanger_sample_id = "sample2" - instance.requests(1).supplier_name = "supplier2" - instance.requests(1).taxon_id = "9606" - instance.requests(1).donor_id = "donor2" - instance.requests(1).country_of_origin = "United Kingdom" - instance.requests(1).accession_number = "AN1235" - instance.requests(1).date_of_sample_collection = my_date + + request = instance.create_request() + request.container_barcode = "1" + request.container_type = OUTPUT_TRACTION_MESSAGE_CREATE_REQUEST_CONTAINER_TYPE_WELLS + request.container_location = "A1" + request.library_type = "library" + request.sample_name = "test1" + request.study_uuid = "dd490ee5-fd1d-456d-99fd-eb9d3861e014" + request.sample_uuid = "8860a6b4-82e2-451c-aba2-a3129c38c0fc" + request.species = "test species" + request.public_name = "Public1" + request.cost_code = "S1234" + request.priority_level = None + request.sanger_sample_id = "sample1" + request.supplier_name = "supplier1" + request.taxon_id = "9606" + request.donor_id = "donor1" + request.country_of_origin = "United Kingdom" + request.accession_number = "AN1234" + request.date_of_sample_collection = my_date + + request = instance.create_request() + request.container_barcode = "1" + request.container_type = OUTPUT_TRACTION_MESSAGE_CREATE_REQUEST_CONTAINER_TYPE_WELLS + request.container_location = "B1" + request.library_type = "library" + request.sample_name = "test1" + request.study_uuid = "dd490ee5-fd1d-456d-99fd-eb9d3861e014" + request.sample_uuid = "8860a6b4-82e2-451c-aba2-a3129c38c0fc" + request.species = "test species" + request.public_name = "Public2" + request.cost_code = "S4567" + request.priority_level = None + request.sanger_sample_id = "sample2" + request.supplier_name = "supplier2" + request.taxon_id = "9606" + request.donor_id = "donor2" + request.country_of_origin = "United Kingdom" + request.accession_number = "AN1235" + request.date_of_sample_collection = my_date assert instance.payload() == { "data": { "attributes": { "request_attributes": [ { - "container": {"barcode": "1", "position": None, "type": "wells"}, + "container": {"barcode": "1", "position": "A1", "type": "wells"}, "request": { "external_study_id": "dd490ee5-fd1d-456d-99fd-eb9d3861e014", "library_type": "library", @@ -137,50 +143,53 @@ def test_output_traction_message_can_generate_payload_for_plates(): def test_output_traction_message_can_generate_payload_for_ont_library_types(): my_date = datetime.now() instance = OutputTractionMessage() - instance.requests(0).container_barcode = "1" - instance.requests(0).container_type = OUTPUT_TRACTION_MESSAGE_CREATE_REQUEST_CONTAINER_TYPE_WELLS - instance.requests(1).container_location = "A1" - instance.requests(0).library_type = "ONT_mylib" - instance.requests(0).sample_name = "test1" - instance.requests(0).study_uuid = "dd490ee5-fd1d-456d-99fd-eb9d3861e014" - instance.requests(0).sample_uuid = "8860a6b4-82e2-451c-aba2-a3129c38c0fc" - instance.requests(0).species = "test species" - instance.requests(0).public_name = "Public1" - instance.requests(0).cost_code = "S1234" - instance.requests(0).priority_level = "Medium" - instance.requests(0).sanger_sample_id = "sample1" - instance.requests(0).supplier_name = "supplier1" - instance.requests(0).taxon_id = "9606" - instance.requests(0).donor_id = "donor1" - instance.requests(0).country_of_origin = "United Kingdom" - instance.requests(0).accession_number = "AN1234" - instance.requests(0).date_of_sample_collection = my_date - - instance.requests(1).container_barcode = "1" - instance.requests(1).container_type = OUTPUT_TRACTION_MESSAGE_CREATE_REQUEST_CONTAINER_TYPE_WELLS - instance.requests(1).container_location = "B1" - instance.requests(1).library_type = "ONT_mylib" - instance.requests(1).sample_name = "test1" - instance.requests(1).study_uuid = "dd490ee5-fd1d-456d-99fd-eb9d3861e014" - instance.requests(1).sample_uuid = "8860a6b4-82e2-451c-aba2-a3129c38c0fc" - instance.requests(1).species = "test species" - instance.requests(1).public_name = "Public2" - instance.requests(1).cost_code = "S4567" - instance.requests(1).priority_level = None - instance.requests(1).sanger_sample_id = "sample2" - instance.requests(1).supplier_name = "supplier2" - instance.requests(1).taxon_id = "9606" - instance.requests(1).donor_id = "donor2" - instance.requests(1).country_of_origin = "United Kingdom" - instance.requests(1).accession_number = "AN1235" - instance.requests(1).date_of_sample_collection = my_date + + request = instance.create_request() + request.container_barcode = "1" + request.container_type = OUTPUT_TRACTION_MESSAGE_CREATE_REQUEST_CONTAINER_TYPE_WELLS + request.container_location = "A1" + request.library_type = "ONT_mylib" + request.sample_name = "test1" + request.study_uuid = "dd490ee5-fd1d-456d-99fd-eb9d3861e014" + request.sample_uuid = "8860a6b4-82e2-451c-aba2-a3129c38c0fc" + request.species = "test species" + request.public_name = "Public1" + request.cost_code = "S1234" + request.priority_level = "Medium" + request.sanger_sample_id = "sample1" + request.supplier_name = "supplier1" + request.taxon_id = "9606" + request.donor_id = "donor1" + request.country_of_origin = "United Kingdom" + request.accession_number = "AN1234" + request.date_of_sample_collection = my_date + + request = instance.create_request() + request.container_barcode = "1" + request.container_type = OUTPUT_TRACTION_MESSAGE_CREATE_REQUEST_CONTAINER_TYPE_WELLS + request.container_location = "B1" + request.library_type = "ONT_mylib" + request.sample_name = "test1" + request.study_uuid = "dd490ee5-fd1d-456d-99fd-eb9d3861e014" + request.sample_uuid = "8860a6b4-82e2-451c-aba2-a3129c38c0fc" + request.species = "test species" + request.public_name = "Public2" + request.cost_code = "S4567" + request.priority_level = None + request.sanger_sample_id = "sample2" + request.supplier_name = "supplier2" + request.taxon_id = "9606" + request.donor_id = "donor2" + request.country_of_origin = "United Kingdom" + request.accession_number = "AN1235" + request.date_of_sample_collection = my_date assert instance.payload() == { "data": { "attributes": { "request_attributes": [ { - "container": {"barcode": "1", "position": None, "type": "wells"}, + "container": {"barcode": "1", "position": "A1", "type": "wells"}, "request": { "external_study_id": "dd490ee5-fd1d-456d-99fd-eb9d3861e014", "library_type": "ONT_mylib", @@ -236,41 +245,44 @@ def test_output_traction_message_can_generate_payload_for_ont_library_types(): def test_output_traction_message_can_generate_payload_for_tubes(): my_date = datetime.now() instance = OutputTractionMessage() - instance.requests(0).container_barcode = "1" - instance.requests(0).container_type = OUTPUT_TRACTION_MESSAGE_CREATE_REQUEST_CONTAINER_TYPE_TUBES - instance.requests(0).library_type = "library" - instance.requests(0).sample_name = "test1" - instance.requests(0).study_uuid = "dd490ee5-fd1d-456d-99fd-eb9d3861e014" - instance.requests(0).sample_uuid = "8860a6b4-82e2-451c-aba2-a3129c38c0fc" - instance.requests(0).species = "test species" - instance.requests(0).public_name = "Public1" - instance.requests(0).cost_code = "S1234" - instance.requests(0).priority_level = "High" - instance.requests(0).sanger_sample_id = "sample1" - instance.requests(0).supplier_name = "supplier1" - instance.requests(0).taxon_id = "9606" - instance.requests(0).donor_id = "donor1" - instance.requests(0).country_of_origin = "United Kingdom" - instance.requests(0).accession_number = "AN1234" - instance.requests(0).date_of_sample_collection = my_date - - instance.requests(1).container_barcode = "1" - instance.requests(1).container_type = OUTPUT_TRACTION_MESSAGE_CREATE_REQUEST_CONTAINER_TYPE_TUBES - instance.requests(1).library_type = "library" - instance.requests(1).sample_name = "test1" - instance.requests(1).study_uuid = "dd490ee5-fd1d-456d-99fd-eb9d3861e014" - instance.requests(1).sample_uuid = "8860a6b4-82e2-451c-aba2-a3129c38c0fc" - instance.requests(1).species = "test species" - instance.requests(1).public_name = "Public2" - instance.requests(1).cost_code = "S4567" - instance.requests(1).priority_level = "Low" - instance.requests(1).sanger_sample_id = "sample2" - instance.requests(1).supplier_name = "supplier2" - instance.requests(1).taxon_id = "9606" - instance.requests(1).donor_id = "donor2" - instance.requests(1).country_of_origin = "United Kingdom" - instance.requests(1).accession_number = "AN1235" - instance.requests(1).date_of_sample_collection = my_date + + request = instance.create_request() + request.container_barcode = "1" + request.container_type = OUTPUT_TRACTION_MESSAGE_CREATE_REQUEST_CONTAINER_TYPE_TUBES + request.library_type = "library" + request.sample_name = "test1" + request.study_uuid = "dd490ee5-fd1d-456d-99fd-eb9d3861e014" + request.sample_uuid = "8860a6b4-82e2-451c-aba2-a3129c38c0fc" + request.species = "test species" + request.public_name = "Public1" + request.cost_code = "S1234" + request.priority_level = "High" + request.sanger_sample_id = "sample1" + request.supplier_name = "supplier1" + request.taxon_id = "9606" + request.donor_id = "donor1" + request.country_of_origin = "United Kingdom" + request.accession_number = "AN1234" + request.date_of_sample_collection = my_date + + request = instance.create_request() + request.container_barcode = "1" + request.container_type = OUTPUT_TRACTION_MESSAGE_CREATE_REQUEST_CONTAINER_TYPE_TUBES + request.library_type = "library" + request.sample_name = "test1" + request.study_uuid = "dd490ee5-fd1d-456d-99fd-eb9d3861e014" + request.sample_uuid = "8860a6b4-82e2-451c-aba2-a3129c38c0fc" + request.species = "test species" + request.public_name = "Public2" + request.cost_code = "S4567" + request.priority_level = "Low" + request.sanger_sample_id = "sample2" + request.supplier_name = "supplier2" + request.taxon_id = "9606" + request.donor_id = "donor2" + request.country_of_origin = "United Kingdom" + request.accession_number = "AN1235" + request.date_of_sample_collection = my_date assert instance.payload() == { "data": { @@ -330,20 +342,24 @@ def test_output_traction_message_can_generate_payload_for_tubes(): def test_output_traction_message_can_detect_errors_on_sent(config): vt = valid_traction_message() + with requests_mock.Mocker() as m: m.post(config.TRACTION_URL, text="Error", status_code=422) vt.send(config.TRACTION_URL) + assert not vt.validate() assert len(vt.errors) > 0 def test_output_traction_message_can_add_to_feedback_message_when_not_sent(valid_feedback_message): vt = valid_traction_message() + assert vt.validate() + feedback = valid_feedback_message vt.add_to_feedback_message(feedback) + assert len(feedback.errors) == 0 - # When message not sent assert not feedback.operation_was_error_free @@ -352,21 +368,28 @@ def test_output_traction_message_can_add_to_feedback_message_when_sent( ): vt = valid_traction_message() feedback = valid_feedback_message - # When message sent + with requests_mock.Mocker() as m: m.post(config.TRACTION_URL, json=traction_success_creation_response, status_code=201) vt.send(config.TRACTION_URL) + vt.add_to_feedback_message(feedback) + assert len(feedback.errors) == 0 assert feedback.operation_was_error_free def test_output_traction_message_can_add_to_feedback_message_when_errors(valid_feedback_message): instance = OutputTractionMessage() - instance.requests(0).container_barcode = "1" - instance.requests(0).container_type = OUTPUT_TRACTION_MESSAGE_CREATE_REQUEST_CONTAINER_TYPE_TUBES + + request = instance.create_request() + request.container_barcode = "1" + request.container_type = OUTPUT_TRACTION_MESSAGE_CREATE_REQUEST_CONTAINER_TYPE_TUBES + assert not instance.validate() + feedback = valid_feedback_message instance.add_to_feedback_message(feedback) + assert len(feedback.errors) > 0 assert not feedback.operation_was_error_free diff --git a/tol_lab_share/message_properties/definitions/labware.py b/tol_lab_share/message_properties/definitions/labware.py index 2ea84eb9..c687a774 100644 --- a/tol_lab_share/message_properties/definitions/labware.py +++ b/tol_lab_share/message_properties/definitions/labware.py @@ -87,24 +87,25 @@ def add_to_traction_message(self, traction_message: OutputTractionMessageInterfa super().add_to_traction_message(traction_message) for sample_pos in range(len(self.properties("samples"))): sample = self.properties("samples")[sample_pos] - traction_message.requests(sample_pos).cost_code = sample.properties("cost_code").value - traction_message.requests(sample_pos).study_uuid = sample.properties("study_uuid").value - traction_message.requests(sample_pos).sample_name = sample.properties("sanger_sample_id").value - traction_message.requests(sample_pos).public_name = sample.properties("public_name").value - traction_message.requests(sample_pos).sample_uuid = sample.properties("uuid").value - traction_message.requests(sample_pos).library_type = sample.properties("library_type").value - traction_message.requests(sample_pos).species = sample.properties("scientific_name").value - traction_message.requests(sample_pos).container_barcode = self.properties("barcode").value - traction_message.requests(sample_pos).container_location = sample.properties("location").value - traction_message.requests(sample_pos).container_type = self.traction_container_type() - traction_message.requests(sample_pos).priority_level = sample.properties("priority_level").value - traction_message.requests(sample_pos).taxon_id = sample.properties("taxon_id").value - traction_message.requests(sample_pos).sanger_sample_id = sample.properties("sanger_sample_id").value - traction_message.requests(sample_pos).donor_id = sample.properties("donor_id").value - traction_message.requests(sample_pos).country_of_origin = sample.properties("country_of_origin").value - traction_message.requests(sample_pos).accession_number = sample.properties("accession_number").value - traction_message.requests(sample_pos).supplier_name = sample.properties("supplier_sample_name").value - traction_message.requests(sample_pos).date_of_sample_collection = sample.properties("collection_date").value + request = traction_message.create_request() + request.cost_code = sample.properties("cost_code").value + request.study_uuid = sample.properties("study_uuid").value + request.sample_name = sample.properties("sanger_sample_id").value + request.public_name = sample.properties("public_name").value + request.sample_uuid = sample.properties("uuid").value + request.library_type = sample.properties("library_type").value + request.species = sample.properties("scientific_name").value + request.container_barcode = self.properties("barcode").value + request.container_location = sample.properties("location").value + request.container_type = self.traction_container_type() + request.priority_level = sample.properties("priority_level").value + request.taxon_id = sample.properties("taxon_id").value + request.sanger_sample_id = sample.properties("sanger_sample_id").value + request.donor_id = sample.properties("donor_id").value + request.country_of_origin = sample.properties("country_of_origin").value + request.accession_number = sample.properties("accession_number").value + request.supplier_name = sample.properties("supplier_sample_name").value + request.date_of_sample_collection = sample.properties("collection_date").value def add_to_traction_qc_message(self, traction_qc_message: TractionQcMessageInterface) -> None: """Given a traction qc message instance, it adds the qc data. diff --git a/tol_lab_share/messages/interfaces.py b/tol_lab_share/messages/interfaces.py index 0ff40cf5..a5108adc 100644 --- a/tol_lab_share/messages/interfaces.py +++ b/tol_lab_share/messages/interfaces.py @@ -258,7 +258,7 @@ def add_error(self, error: ErrorCode) -> None: ... @abstractmethod - def requests(self, position: int) -> OutputTractionMessageRequestInterface: + def create_request(self) -> OutputTractionMessageRequestInterface: ... diff --git a/tol_lab_share/messages/output_traction_message.py b/tol_lab_share/messages/output_traction_message.py index 2a116d4f..13d4b953 100644 --- a/tol_lab_share/messages/output_traction_message.py +++ b/tol_lab_share/messages/output_traction_message.py @@ -1,4 +1,4 @@ -from typing import Dict, Optional, List, Callable, Any +from typing import Optional, Callable, Any from json import dumps from datetime import datetime from requests import post, codes @@ -265,7 +265,7 @@ def is_ont_library_type(self) -> bool: """ return bool(self.instance.library_type and ("ONT" in self.instance.library_type)) - def request_payload(self) -> Dict[str, Any]: + def request_payload(self) -> dict[str, Any]: """Generates the correct payload depending on the library type for the current request. Returns: Dic[str,str] with the required information to send for this request to Traction @@ -284,7 +284,7 @@ def request_payload(self) -> Dict[str, Any]: "cost_code": self.instance.cost_code, } - def sample_payload(self) -> Dict[str, Any]: + def sample_payload(self) -> dict[str, Any]: """Generates the correct payload for the sample defined in this request Returns: Dic[str,str] with the required sample information to send for this request to Traction @@ -307,7 +307,7 @@ def sample_payload(self) -> Dict[str, Any]: "date_of_sample_collection": collection_date, } - def container_payload(self) -> Dict[str, Any]: + def container_payload(self) -> dict[str, Any]: """Generates the correct payload for the container defined in this request depending on the container type (tubes or wells) Returns: @@ -322,7 +322,7 @@ def container_payload(self) -> Dict[str, Any]: "position": self.instance.container_location, } - def payload(self) -> Dict[str, Any]: + def payload(self) -> dict[str, Any]: """Main builder of the payload required to describe all information required for Traction: sample, request and container. Returns: @@ -336,12 +336,12 @@ def payload(self) -> Dict[str, Any]: class OutputTractionMessage(MessageProperty, OutputTractionMessageInterface): - """Class that handle the generation and publishing of a message to Traction""" + """Class that handles the generation and publishing of a message to Traction.""" def __init__(self): - """Resets initial data""" + """Reset initial data.""" super().__init__(Input(self)) - self._requests: Dict[int, OutputTractionMessageRequest] = {} + self._requests: list[OutputTractionMessageRequest] = [] self._sent = False self._validate_certificates = get_config().CERTIFICATES_VALIDATION_ENABLED @@ -352,32 +352,28 @@ def origin(self) -> str: return "OutputFeedbackMessage" @property - def validators(self) -> List[Callable]: + def validators(self) -> list[Callable]: """List of validators to check the message is correct before sending""" return [self.check_has_requests, self.check_requests_have_all_content, self.check_no_errors] - def requests(self, position: int) -> OutputTractionMessageRequestInterface: - """Returns the request at position position from this message. If there is no requesrt there it - will create a new instance and return it. - Parameters: - position (int) position that we want to return + def create_request(self) -> OutputTractionMessageRequestInterface: + """Creates a new request and returns it. It will be appended to the list of requests. + Returns: - OutputTractionMessageRequest with the request required + OutputTractionMessageRequestInterface: The newly created request. """ - if position not in self._requests: - self._requests[position] = OutputTractionMessageRequest() + self._requests.append(OutputTractionMessageRequest()) + return self._requests[-1] - return self._requests[position] - - def request_attributes(self) -> List[Dict[str, Any]]: + def request_attributes(self) -> list[dict[str, Any]]: """Returns a list with all the payloads for every request Returns: List[Dict[str,Any]] with all payloads for all the requests """ - return [self._requests[position].serializer().payload() for position in range(len(self._requests))] + return [request.serializer().payload() for request in self._requests] @property - def errors(self) -> List[ErrorCode]: + def errors(self) -> list[ErrorCode]: """List of errors defined for this message""" return self._errors @@ -405,12 +401,13 @@ def check_requests_have_all_content(self) -> bool: Returns: bool indicating that all requests have valid content inside. """ - if all([self.requests(key).validate() for key in self._requests]): + if all([request.validate() for request in self._requests]): return True + self.trigger_error(error_codes.ERROR_24_TRACTION_MESSAGE_REQUESTS_HAVE_MISSING_DATA) return False - def payload(self) -> Dict[str, Any]: + def payload(self) -> dict[str, Any]: """Returns the valid payload to send to traction""" return { "data": { From 8897f9eb95843e0ee1028968c2d244cb036e5a9d Mon Sep 17 00:00:00 2001 From: Stuart McHattie Date: Fri, 9 Feb 2024 16:01:27 +0000 Subject: [PATCH 16/40] Remove interfaces around MessageProperty This also includes those for OutputTractionMessageInterface and TractionQcMessageInterface --- .../definitions/test_labware.py | 16 ++-- .../message_properties/definitions/labware.py | 62 +++++++-------- .../definitions/message_property.py | 40 ++++------ .../message_properties/interfaces.py | 77 ------------------- tol_lab_share/messages/interfaces.py | 36 --------- .../messages/output_traction_message.py | 3 +- tol_lab_share/messages/traction_qc_message.py | 3 +- .../processors/create_labware_processor.py | 4 +- 8 files changed, 59 insertions(+), 182 deletions(-) delete mode 100644 tol_lab_share/message_properties/interfaces.py diff --git a/tests/message_properties/definitions/test_labware.py b/tests/message_properties/definitions/test_labware.py index cb03df1e..54ac46e6 100644 --- a/tests/message_properties/definitions/test_labware.py +++ b/tests/message_properties/definitions/test_labware.py @@ -101,7 +101,7 @@ def test_count_of_valid_samples(valid_sample, invalid_sample): assert instance.count_of_valid_samples() == 1 -def test_labware_add_to_traction_message_wells(valid_sample): +def test_labware_add_to_message_wells(valid_sample): data = { "labwareType": "Plate12x8", "labwareUuid": "dd490ee5-fd1d-456d-99fd-eb9d3861e0f9".encode(), @@ -112,7 +112,7 @@ def test_labware_add_to_traction_message_wells(valid_sample): assert instance.validate() traction_message = OutputTractionMessage() - instance.add_to_traction_message(traction_message) + instance.add_to_message_property(traction_message) assert traction_message._requests[0].study_uuid == "dd490ee5-fd1d-456d-99fd-eb9d3861e014" assert traction_message._requests[0].sample_name == "cichlid_pacbio8196429" @@ -127,7 +127,7 @@ def test_labware_add_to_traction_message_wells(valid_sample): assert traction_message._requests[0].cost_code == "S1234" -def test_labware_add_to_traction_message_tubes(valid_sample): +def test_labware_add_to_message_tubes(valid_sample): data = { "labwareType": "Tube", "labwareUuid": "dd490ee5-fd1d-456d-99fd-eb9d3861e0f9".encode(), @@ -137,7 +137,7 @@ def test_labware_add_to_traction_message_tubes(valid_sample): instance = Labware(Input(data)) traction_message = OutputTractionMessage() - instance.add_to_traction_message(traction_message) + instance.add_to_message_property(traction_message) assert traction_message._requests[0].study_uuid == "dd490ee5-fd1d-456d-99fd-eb9d3861e014" assert traction_message._requests[0].sample_name == "cichlid_pacbio8196429" @@ -152,7 +152,7 @@ def test_labware_add_to_traction_message_tubes(valid_sample): assert traction_message._requests[0].cost_code == "S1234" -def test_labware_add_to_traction_message_uses_unpadded_location(valid_sample): +def test_labware_add_to_message_uses_unpadded_location(valid_sample): data = { "labwareType": "Plate12x8", "labwareUuid": "dd490ee5-fd1d-456d-99fd-eb9d3861e0f9".encode(), @@ -162,7 +162,7 @@ def test_labware_add_to_traction_message_uses_unpadded_location(valid_sample): instance = Labware(Input(data)) instance.properties("samples")[0].add_property("location", Location(Input("B01"))) traction_message = OutputTractionMessage() - instance.add_to_traction_message(traction_message) + instance.add_to_message_property(traction_message) assert traction_message._requests[0].container_location == "B1" @@ -177,10 +177,10 @@ def test_add_to_traction_qc_message(valid_sample): assert instance.validate() traction_message = OutputTractionMessage() - instance.add_to_traction_message(traction_message) + instance.add_to_message_property(traction_message) traction_qc_message = TractionQcMessage() - instance.add_to_traction_qc_message(traction_qc_message) + instance.add_to_message_property(traction_qc_message) assert traction_qc_message.requests(0).sanger_sample_id == "cichlid_pacbio8196429" assert traction_qc_message.requests(0).container_barcode == "BARCODE001" diff --git a/tol_lab_share/message_properties/definitions/labware.py b/tol_lab_share/message_properties/definitions/labware.py index c687a774..f3877be4 100644 --- a/tol_lab_share/message_properties/definitions/labware.py +++ b/tol_lab_share/message_properties/definitions/labware.py @@ -10,12 +10,12 @@ from tol_lab_share.message_properties.definitions.dict_input import DictInput from tol_lab_share.message_properties.definitions.labware_type import LabwareType from tol_lab_share.message_properties.definitions.sample import Sample -from tol_lab_share.message_properties.interfaces import MessagePropertyInterface from tol_lab_share.messages.interfaces import OutputFeedbackMessageInterface -from tol_lab_share.messages.output_traction_message import OutputTractionMessageInterface -from tol_lab_share.messages.traction_qc_message import TractionQcMessageInterface +from tol_lab_share.messages.output_traction_message import OutputTractionMessage +from tol_lab_share.messages.traction_qc_message import TractionQcMessage from .message_property import MessageProperty +from functools import singledispatchmethod logger = logging.getLogger(__name__) @@ -30,11 +30,11 @@ def __init__(self, input: MessageProperty): self.add_property("barcode", Barcode(DictInput(input, BARCODE))) self.add_property("samples", self._parse_samples(input)) - def _parse_samples(self, input: MessageProperty) -> List[MessagePropertyInterface]: + def _parse_samples(self, input: MessageProperty) -> List[MessageProperty]: """Parses the samples section and creates a sample for each position.""" samples_dict = DictInput(input, SAMPLES) if samples_dict.validate(): - samples_list_dict: List[MessagePropertyInterface] = [] + samples_list_dict: List[MessageProperty] = [] for position in range(len(samples_dict.value)): sample = samples_dict.value[position] samples_list_dict.append(Sample(sample)) @@ -78,16 +78,23 @@ def traction_container_type(self) -> str: else: return OUTPUT_TRACTION_MESSAGE_CREATE_REQUEST_CONTAINER_TYPE_WELLS - def add_to_traction_message(self, traction_message: OutputTractionMessageInterface) -> None: + @singledispatchmethod + def add_to_message_property(self, message_property: MessageProperty) -> None: + raise NotImplementedError(f"No registered method for message property type '{type(message_property)}'.") + + @add_to_message_property.register + def _(self, message: OutputTractionMessage) -> None: """Given a traction message instance, it adds to the instance all the relevant information from the labware. + Returns: - None + None """ - super().add_to_traction_message(traction_message) + super().add_to_message_property(message) + for sample_pos in range(len(self.properties("samples"))): sample = self.properties("samples")[sample_pos] - request = traction_message.create_request() + request = message.create_request() request.cost_code = sample.properties("cost_code").value request.study_uuid = sample.properties("study_uuid").value request.sample_name = sample.properties("sanger_sample_id").value @@ -107,31 +114,26 @@ def add_to_traction_message(self, traction_message: OutputTractionMessageInterfa request.supplier_name = sample.properties("supplier_sample_name").value request.date_of_sample_collection = sample.properties("collection_date").value - def add_to_traction_qc_message(self, traction_qc_message: TractionQcMessageInterface) -> None: + @add_to_message_property.register + def _(self, message: TractionQcMessage) -> None: """Given a traction qc message instance, it adds the qc data. + Returns: - None + None """ - super().add_to_traction_qc_message(traction_qc_message) + super().add_to_message_property(message) + for sample_pos in range(len(self.properties("samples"))): sample = self.properties("samples")[sample_pos] - traction_qc_message.requests(sample_pos).sheared_femto_fragment_size = sample.properties( + message.requests(sample_pos).sheared_femto_fragment_size = sample.properties( "sheared_femto_fragment_size" ).value - traction_qc_message.requests(sample_pos).post_spri_concentration = sample.properties( - "post_spri_concentration" - ).value - traction_qc_message.requests(sample_pos).post_spri_volume = sample.properties("post_spri_volume").value - traction_qc_message.requests(sample_pos).final_nano_drop_280 = sample.properties( - "final_nano_drop_280" - ).value - traction_qc_message.requests(sample_pos).final_nano_drop_230 = sample.properties( - "final_nano_drop_230" - ).value - traction_qc_message.requests(sample_pos).final_nano_drop = sample.properties("final_nano_drop").value - traction_qc_message.requests(sample_pos).shearing_qc_comments = sample.properties( - "shearing_qc_comments" - ).value - traction_qc_message.requests(sample_pos).date_submitted_utc = sample.properties("date_submitted_utc").value - traction_qc_message.requests(sample_pos).container_barcode = self.properties("barcode").value - traction_qc_message.requests(sample_pos).sanger_sample_id = sample.properties("sanger_sample_id").value + message.requests(sample_pos).post_spri_concentration = sample.properties("post_spri_concentration").value + message.requests(sample_pos).post_spri_volume = sample.properties("post_spri_volume").value + message.requests(sample_pos).final_nano_drop_280 = sample.properties("final_nano_drop_280").value + message.requests(sample_pos).final_nano_drop_230 = sample.properties("final_nano_drop_230").value + message.requests(sample_pos).final_nano_drop = sample.properties("final_nano_drop").value + message.requests(sample_pos).shearing_qc_comments = sample.properties("shearing_qc_comments").value + message.requests(sample_pos).date_submitted_utc = sample.properties("date_submitted_utc").value + message.requests(sample_pos).container_barcode = self.properties("barcode").value + message.requests(sample_pos).sanger_sample_id = sample.properties("sanger_sample_id").value diff --git a/tol_lab_share/message_properties/definitions/message_property.py b/tol_lab_share/message_properties/definitions/message_property.py index 36d8c3f8..ac9cf3c2 100644 --- a/tol_lab_share/message_properties/definitions/message_property.py +++ b/tol_lab_share/message_properties/definitions/message_property.py @@ -1,17 +1,13 @@ +from __future__ import annotations import datetime import logging -from functools import cached_property +from functools import cached_property, singledispatchmethod from itertools import chain from typing import Any, Callable, Dict, List, Optional, Union, cast from tol_lab_share import error_codes from tol_lab_share.error_codes import ErrorCode -from tol_lab_share.message_properties.interfaces import MessagePropertyInterface -from tol_lab_share.messages.interfaces import ( - OutputFeedbackMessageInterface, - OutputTractionMessageInterface, - TractionQcMessageInterface, -) +from tol_lab_share.messages.interfaces import OutputFeedbackMessageInterface logger = logging.getLogger(__name__) @@ -19,7 +15,7 @@ PROPERTY_TYPE_ARRAY = "Array" -class MessageProperty(MessagePropertyInterface): +class MessageProperty: """Base class for MessageProperty that provides the core functionality of running validations, managing properties, triggering errors and some common validation checks. @@ -59,7 +55,7 @@ def has_property(self, key): """Returns a boolean indicating if the property exists for the key provided""" return key in self._properties - def _add_property_instance(self, property_name: str, instance: MessagePropertyInterface) -> None: + def _add_property_instance(self, property_name: str, instance: MessageProperty) -> None: """Given a property name and an instance of MessageProperty, it configures this instance as the value for the property. Parameters: @@ -83,7 +79,7 @@ def _property_name_for_list_instance(self, property_name: str, pos: int) -> str: """ return f"{property_name}[{pos}]" - def _add_property_list(self, property_name: str, input: List[MessagePropertyInterface]) -> None: + def _add_property_list(self, property_name: str, input: List[MessageProperty]) -> None: """Given a property name and a list of message properties, it stores this list of MessageProperty as the value of the property and includes the position in each of them. Parameters: @@ -101,9 +97,7 @@ def _add_property_list(self, property_name: str, input: List[MessagePropertyInte instance.property_type = PROPERTY_TYPE_ARRAY self._properties[property_name].append(instance) - def add_property( - self, property_name: str, input: Union[MessagePropertyInterface, List[MessagePropertyInterface]] - ) -> None: + def add_property(self, property_name: str, input: Union[MessageProperty, List[MessageProperty]]) -> None: """Given an property name and an input it adds the input as the value of the property Parameters: property_name (str) name of the property @@ -134,7 +128,7 @@ def property_source(self) -> Any: return self._property_source @property_source.setter - def property_source(self, value: MessagePropertyInterface) -> None: + def property_source(self, value: MessageProperty) -> None: """Sets the property source for this property""" self._property_source = value @@ -196,19 +190,15 @@ def add_to_feedback_message(self, feedback_message: OutputFeedbackMessageInterfa for property in self._properties_instances: property.add_to_feedback_message(feedback_message) - def add_to_traction_message(self, traction_message: OutputTractionMessageInterface) -> None: - """Calls the method add_to_traction_message in all the properties defined inside this property - (if there are any). - """ - for property in self._properties_instances: - property.add_to_traction_message(traction_message) + @singledispatchmethod + def add_to_message_property(self, message_property: MessageProperty) -> None: + """Adds the information from child properties of this property to the message provided. - def add_to_traction_qc_message(self, traction_qc_message: TractionQcMessageInterface) -> None: - """Calls the method add_to_traction_qc_message for all the properties defined inside this property - (if there are any). + Args: + message_property (MessageProperty): The message property to add the information to. """ for property in self._properties_instances: - property.add_to_traction_qc_message(traction_qc_message) + property.add_to_message_property(message_property) @property def errors(self) -> List[ErrorCode]: @@ -367,7 +357,7 @@ def _errors_properties(self) -> List[ErrorCode]: return error_list @cached_property - def _properties_instances(self) -> List[MessagePropertyInterface]: + def _properties_instances(self) -> List[MessageProperty]: """Returns a list that contains all the properties inside this instance. Returns: diff --git a/tol_lab_share/message_properties/interfaces.py b/tol_lab_share/message_properties/interfaces.py deleted file mode 100644 index 30eb0abc..00000000 --- a/tol_lab_share/message_properties/interfaces.py +++ /dev/null @@ -1,77 +0,0 @@ -from abc import ABC, abstractmethod -from functools import cached_property -from typing import Any, Optional - -from tol_lab_share.messages.interfaces import ( - OutputFeedbackMessageInterface, - OutputTractionMessageInterface, - TractionQcMessageInterface, -) - - -class MessagePropertyInterface(ABC): - @abstractmethod - def validate(self): - ... - - @property - @abstractmethod - def errors(self) -> Any: - ... - - @cached_property - @abstractmethod - def value(self) -> Optional[Any]: - ... - - @abstractmethod - def add_to_feedback_message(self, feedback_message: OutputFeedbackMessageInterface) -> None: - ... - - @abstractmethod - def add_to_traction_message(self, traction_message: OutputTractionMessageInterface) -> None: - ... - - @abstractmethod - def add_to_traction_qc_message(self, traction_qc_message: TractionQcMessageInterface) -> None: - ... - - @property - @abstractmethod - def property_name(self) -> Optional[str]: - ... - - @property_name.setter - @abstractmethod - def property_name(self, value: str) -> None: - ... - - @property - @abstractmethod - def property_source(self) -> Optional[Any]: - ... - - @property_source.setter - @abstractmethod - def property_source(self, value: Any) -> None: - ... - - @property - @abstractmethod - def property_position(self) -> Optional[int]: - ... - - @property_position.setter - @abstractmethod - def property_position(self, value: Any) -> None: - ... - - @property - @abstractmethod - def property_type(self) -> Optional[str]: - ... - - @property_type.setter - @abstractmethod - def property_type(self, value: Any) -> None: - ... diff --git a/tol_lab_share/messages/interfaces.py b/tol_lab_share/messages/interfaces.py index a5108adc..ba2ef2ae 100644 --- a/tol_lab_share/messages/interfaces.py +++ b/tol_lab_share/messages/interfaces.py @@ -244,24 +244,6 @@ def priority_level(self, value: Optional[str]) -> None: ... -class OutputTractionMessageInterface(ABC): - @abstractmethod - def payload(self): - ... - - @abstractmethod - def send(self, url): - ... - - @abstractmethod - def add_error(self, error: ErrorCode) -> None: - ... - - @abstractmethod - def create_request(self) -> OutputTractionMessageRequestInterface: - ... - - class TractionQcMessageRequestInterface: @abstractmethod def validate(self) -> bool: @@ -366,21 +348,3 @@ def date_submitted_utc(self) -> Optional[float]: @abstractmethod def date_submitted_utc(self, value: Optional[float]) -> None: ... - - -class TractionQcMessageInterface(ABC): - @abstractmethod - def payload(self): - ... - - @abstractmethod - def send(self, url): - ... - - @abstractmethod - def add_error(self, error: ErrorCode) -> None: - ... - - @abstractmethod - def requests(self, position: int) -> TractionQcMessageRequestInterface: - ... diff --git a/tol_lab_share/messages/output_traction_message.py b/tol_lab_share/messages/output_traction_message.py index 13d4b953..9a71c322 100644 --- a/tol_lab_share/messages/output_traction_message.py +++ b/tol_lab_share/messages/output_traction_message.py @@ -3,7 +3,6 @@ from datetime import datetime from requests import post, codes from tol_lab_share.messages.interfaces import ( - OutputTractionMessageInterface, OutputFeedbackMessageInterface, OutputTractionMessageRequestInterface, ) @@ -335,7 +334,7 @@ def payload(self) -> dict[str, Any]: } -class OutputTractionMessage(MessageProperty, OutputTractionMessageInterface): +class OutputTractionMessage(MessageProperty): """Class that handles the generation and publishing of a message to Traction.""" def __init__(self): diff --git a/tol_lab_share/messages/traction_qc_message.py b/tol_lab_share/messages/traction_qc_message.py index 10ff6e13..ed180446 100644 --- a/tol_lab_share/messages/traction_qc_message.py +++ b/tol_lab_share/messages/traction_qc_message.py @@ -12,7 +12,6 @@ from tol_lab_share.message_properties.definitions.message_property import MessageProperty from tol_lab_share.messages.interfaces import ( OutputFeedbackMessageInterface, - TractionQcMessageInterface, TractionQcMessageRequestInterface, ) @@ -176,7 +175,7 @@ def clear_empty_value_keys(self, obj: Dict[str, Any]) -> Dict[str, Any]: return {k: v for k, v in obj.items() if v} -class TractionQcMessage(MessageProperty, TractionQcMessageInterface): +class TractionQcMessage(MessageProperty): """Class that handle the generation and publishing of a QC message to Traction""" def __init__(self): diff --git a/tol_lab_share/processors/create_labware_processor.py b/tol_lab_share/processors/create_labware_processor.py index 9b1bb4e4..8b0e3539 100644 --- a/tol_lab_share/processors/create_labware_processor.py +++ b/tol_lab_share/processors/create_labware_processor.py @@ -58,7 +58,7 @@ def process(self, message: RabbitMessage) -> bool: if validation: output_traction_message = OutputTractionMessage() - input.add_to_traction_message(output_traction_message) + input.add_to_message_property(output_traction_message) logger.info("Attempting to send to traction") output_traction_message.send(url=self._config.TRACTION_URL) output_traction_message.add_to_feedback_message(output_feedback_message) @@ -90,7 +90,7 @@ def send_qc_data_to_traction( ) -> bool: """Send qc data to traction, if there is any error, add to feedback message""" traction_qc_message = TractionQcMessage() - input.add_to_traction_qc_message(traction_qc_message) + input.add_to_message_property(traction_qc_message) logger.info("Attempting to send qc message to traction") traction_qc_message.send(url=self._config.TRACTION_QC_URL) traction_qc_message.add_to_feedback_message(feedback_message) From cd0bb8d2775796a34d528e8994715e05b1996372 Mon Sep 17 00:00:00 2001 From: Stuart McHattie Date: Fri, 9 Feb 2024 16:55:34 +0000 Subject: [PATCH 17/40] Remove remaining unneeded interfaces --- .../definitions/test_message_uuid.py | 4 +- .../test_input_create_labware_message.py | 8 +- .../messages/test_output_traction_message.py | 12 +- tests/messages/test_traction_qc_message.py | 8 +- .../message_properties/definitions/labware.py | 44 +-- .../definitions/message_property.py | 9 - .../definitions/message_uuid.py | 19 +- .../messages/input_create_labware_message.py | 32 +- tol_lab_share/messages/interfaces.py | 350 ------------------ .../messages/output_feedback_message.py | 3 +- .../messages/output_traction_message.py | 57 +-- tol_lab_share/messages/traction_qc_message.py | 25 +- .../processors/create_labware_processor.py | 6 +- 13 files changed, 129 insertions(+), 448 deletions(-) delete mode 100644 tol_lab_share/messages/interfaces.py diff --git a/tests/message_properties/definitions/test_message_uuid.py b/tests/message_properties/definitions/test_message_uuid.py index 8fe9e99f..df79b6e6 100644 --- a/tests/message_properties/definitions/test_message_uuid.py +++ b/tests/message_properties/definitions/test_message_uuid.py @@ -3,12 +3,12 @@ from tol_lab_share.message_properties.definitions.input import Input -def test_uuid_add_to_feedback_message(): +def test_uuid_add_to_message_property(): instance = MessageUuid(Input(b"dd490ee5-fd1d-456d-99fd-eb9d3861e0f9")) feedback = OutputFeedbackMessage() instance.validate() - instance.add_to_feedback_message(feedback) + instance.add_to_message_property(feedback) assert feedback.operation_was_error_free is True assert feedback.source_message_uuid == "dd490ee5-fd1d-456d-99fd-eb9d3861e0f9" diff --git a/tests/messages/test_input_create_labware_message.py b/tests/messages/test_input_create_labware_message.py index d3e8e9e4..fe0e200f 100644 --- a/tests/messages/test_input_create_labware_message.py +++ b/tests/messages/test_input_create_labware_message.py @@ -19,23 +19,23 @@ def test_input_create_labware_message_can_validate_when_invalid(invalid_create_l assert len(instance.errors) == 5 -def test_input_create_labware_message_can_add_to_feedback_message_if_invalid(invalid_create_labware_message): +def test_input_create_labware_message_can_add_to_message_property_if_invalid(invalid_create_labware_message): subject = InputCreateLabwareMessage(invalid_create_labware_message) feedback_message = OutputFeedbackMessage() assert subject.validate() is False - subject.add_to_feedback_message(feedback_message) + subject.add_to_message_property(feedback_message) assert len(feedback_message.errors) > 0 -def test_input_create_labware_message_can_add_to_feedback_message_if_valid(valid_create_labware_message): +def test_input_create_labware_message_can_add_to_message_property_if_valid(valid_create_labware_message): subject = InputCreateLabwareMessage(valid_create_labware_message) subject.validate() feedback_message = OutputFeedbackMessage() assert feedback_message.source_message_uuid is None - subject.add_to_feedback_message(feedback_message) + subject.add_to_message_property(feedback_message) assert feedback_message.source_message_uuid == "b01aa0ad-7b19-4f94-87e9-70d74fb8783c" diff --git a/tests/messages/test_output_traction_message.py b/tests/messages/test_output_traction_message.py index f4727ced..abfc0f19 100644 --- a/tests/messages/test_output_traction_message.py +++ b/tests/messages/test_output_traction_message.py @@ -351,19 +351,19 @@ def test_output_traction_message_can_detect_errors_on_sent(config): assert len(vt.errors) > 0 -def test_output_traction_message_can_add_to_feedback_message_when_not_sent(valid_feedback_message): +def test_output_traction_message_can_add_to_message_property_when_not_sent(valid_feedback_message): vt = valid_traction_message() assert vt.validate() feedback = valid_feedback_message - vt.add_to_feedback_message(feedback) + vt.add_to_message_property(feedback) assert len(feedback.errors) == 0 assert not feedback.operation_was_error_free -def test_output_traction_message_can_add_to_feedback_message_when_sent( +def test_output_traction_message_can_add_to_message_property_when_sent( config, traction_success_creation_response, valid_feedback_message ): vt = valid_traction_message() @@ -373,13 +373,13 @@ def test_output_traction_message_can_add_to_feedback_message_when_sent( m.post(config.TRACTION_URL, json=traction_success_creation_response, status_code=201) vt.send(config.TRACTION_URL) - vt.add_to_feedback_message(feedback) + vt.add_to_message_property(feedback) assert len(feedback.errors) == 0 assert feedback.operation_was_error_free -def test_output_traction_message_can_add_to_feedback_message_when_errors(valid_feedback_message): +def test_output_traction_message_can_add_to_message_property_when_errors(valid_feedback_message): instance = OutputTractionMessage() request = instance.create_request() @@ -389,7 +389,7 @@ def test_output_traction_message_can_add_to_feedback_message_when_errors(valid_f assert not instance.validate() feedback = valid_feedback_message - instance.add_to_feedback_message(feedback) + instance.add_to_message_property(feedback) assert len(feedback.errors) > 0 assert not feedback.operation_was_error_free diff --git a/tests/messages/test_traction_qc_message.py b/tests/messages/test_traction_qc_message.py index cca2f054..e31d02c5 100644 --- a/tests/messages/test_traction_qc_message.py +++ b/tests/messages/test_traction_qc_message.py @@ -101,21 +101,21 @@ def test_can_generate_correct_payload(self, valid_traction_qc_message): assert payload == expected_payload - def test_can_add_to_feedback_message_on_success( + def test_can_add_to_message_property_on_success( self, config, valid_traction_qc_message, valid_feedback_message, traction_qc_success_response ): feedback = valid_feedback_message with requests_mock.Mocker() as m: m.post(config.TRACTION_QC_URL, json=traction_qc_success_response, status_code=201) valid_traction_qc_message.send(config.TRACTION_QC_URL) - valid_traction_qc_message.add_to_feedback_message(feedback) + valid_traction_qc_message.add_to_message_property(feedback) assert len(feedback.errors) == 0 assert feedback.operation_was_error_free - def test_can_add_to_feedback_message_when_errors(self, invalid_traction_qc_message, valid_feedback_message): + def test_can_add_to_message_property_when_errors(self, invalid_traction_qc_message, valid_feedback_message): assert not invalid_traction_qc_message.validate() feedback = valid_feedback_message - invalid_traction_qc_message.add_to_feedback_message(feedback) + invalid_traction_qc_message.add_to_message_property(feedback) assert len(feedback.errors) > 0 assert not feedback.operation_was_error_free diff --git a/tol_lab_share/message_properties/definitions/labware.py b/tol_lab_share/message_properties/definitions/labware.py index f3877be4..954fffdb 100644 --- a/tol_lab_share/message_properties/definitions/labware.py +++ b/tol_lab_share/message_properties/definitions/labware.py @@ -10,7 +10,7 @@ from tol_lab_share.message_properties.definitions.dict_input import DictInput from tol_lab_share.message_properties.definitions.labware_type import LabwareType from tol_lab_share.message_properties.definitions.sample import Sample -from tol_lab_share.messages.interfaces import OutputFeedbackMessageInterface +from tol_lab_share.messages.output_feedback_message import OutputFeedbackMessage from tol_lab_share.messages.output_traction_message import OutputTractionMessage from tol_lab_share.messages.traction_qc_message import TractionQcMessage @@ -54,19 +54,6 @@ def count_of_valid_samples(self) -> int: """Returns the number of samples that are valid from this labware""" return [sample.validate() for sample in self._properties["samples"]].count(True) - def add_to_feedback_message(self, feedback_message: OutputFeedbackMessageInterface) -> None: - """Adds this labware information to the feedback message. It updates there the number - of total samples and the number of valid samples. - Parameters: - feedback_message (OutputFeedbackInterface) Instance of the feedback message where we want - to add the new data - Returns: - None""" - logger.debug("Labware::add_to_feedback_message") - super().add_to_feedback_message(feedback_message) - feedback_message.count_of_total_samples = self.count_of_total_samples() - feedback_message.count_of_valid_samples = self.count_of_valid_samples() - def traction_container_type(self) -> str: """It converts the labware type to a valid container type value for Traction. @@ -80,15 +67,28 @@ def traction_container_type(self) -> str: @singledispatchmethod def add_to_message_property(self, message_property: MessageProperty) -> None: - raise NotImplementedError(f"No registered method for message property type '{type(message_property)}'.") + super().add_to_message_property(message_property) + + @add_to_message_property.register + def _(self, feedback_message: OutputFeedbackMessage) -> None: + """Adds the labware information to an OutputFeedbackMessage. + This includes the number of total samples and the number of valid samples. + + Args: + feedback_message (OutputFeedbackMessage): The OutputFeedbackMessage instance to add the data to. + """ + logger.debug("Labware::add_to_message_property") + super().add_to_message_property(feedback_message) + + feedback_message.count_of_total_samples = self.count_of_total_samples() + feedback_message.count_of_valid_samples = self.count_of_valid_samples() @add_to_message_property.register def _(self, message: OutputTractionMessage) -> None: - """Given a traction message instance, it adds to the instance all the relevant information - from the labware. + """Adds the labware information to an OutputTractionMessage. - Returns: - None + Args: + message (OutputTractionMessage): The OutputTractionMessage instance to add the data to. """ super().add_to_message_property(message) @@ -116,10 +116,10 @@ def _(self, message: OutputTractionMessage) -> None: @add_to_message_property.register def _(self, message: TractionQcMessage) -> None: - """Given a traction qc message instance, it adds the qc data. + """Adds the QC data for this labware to a TractionQcMessage. - Returns: - None + Args: + message (TractionQcMessage): The TractionsQcMessage instance to add the data to. """ super().add_to_message_property(message) diff --git a/tol_lab_share/message_properties/definitions/message_property.py b/tol_lab_share/message_properties/definitions/message_property.py index ac9cf3c2..4572a08a 100644 --- a/tol_lab_share/message_properties/definitions/message_property.py +++ b/tol_lab_share/message_properties/definitions/message_property.py @@ -7,7 +7,6 @@ from tol_lab_share import error_codes from tol_lab_share.error_codes import ErrorCode -from tol_lab_share.messages.interfaces import OutputFeedbackMessageInterface logger = logging.getLogger(__name__) @@ -182,14 +181,6 @@ def trigger_error(self, error_code: ErrorCode, text: Optional[str] = None) -> No """ self.add_error(error_code.trigger(instance=self, origin=self.origin, field=self.field, text=text)) - def add_to_feedback_message(self, feedback_message: OutputFeedbackMessageInterface) -> None: - """Calls the method add_to_feedback_message in all the properties defined inside this property - (if there are any). - """ - logger.debug("MessageProperty::add_to_feedback_message") - for property in self._properties_instances: - property.add_to_feedback_message(feedback_message) - @singledispatchmethod def add_to_message_property(self, message_property: MessageProperty) -> None: """Adds the information from child properties of this property to the message provided. diff --git a/tol_lab_share/message_properties/definitions/message_uuid.py b/tol_lab_share/message_properties/definitions/message_uuid.py index 6031aafc..bf9a1a63 100644 --- a/tol_lab_share/message_properties/definitions/message_uuid.py +++ b/tol_lab_share/message_properties/definitions/message_uuid.py @@ -1,8 +1,11 @@ +from functools import singledispatchmethod +from tol_lab_share.message_properties.definitions.message_property import MessageProperty from tol_lab_share.message_properties.definitions.uuid import Uuid -from tol_lab_share.messages.interfaces import OutputFeedbackMessageInterface import logging +from tol_lab_share.messages.output_feedback_message import OutputFeedbackMessage + logger = logging.getLogger(__name__) @@ -11,11 +14,17 @@ class MessageUuid(Uuid): Uuid. """ - def add_to_feedback_message(self, feedback_message: OutputFeedbackMessageInterface) -> None: + @singledispatchmethod + def add_to_message_property(self, message_property: MessageProperty) -> None: + super().add_to_message_property(message_property) + + @add_to_message_property.register + def _(self, feedback_message: OutputFeedbackMessage) -> None: """Adds the source message uuid to the feedback message passed as parameter + Parameters: - feedback_message (OutputFeedbacMessageInterface) message where we want to add the info + feedback_message (OutputFeedbacMessage) message where we want to add the info """ - logger.debug("MessageUuid::add_to_feedback_message") - super().add_to_feedback_message(feedback_message) + logger.debug("MessageUuid::add_to_message_property") + super().add_to_message_property(feedback_message) feedback_message.source_message_uuid = self.value diff --git a/tol_lab_share/messages/input_create_labware_message.py b/tol_lab_share/messages/input_create_labware_message.py index 99d67f60..e613deb5 100644 --- a/tol_lab_share/messages/input_create_labware_message.py +++ b/tol_lab_share/messages/input_create_labware_message.py @@ -1,3 +1,4 @@ +from functools import singledispatchmethod from lab_share_lib.processing.rabbit_message import RabbitMessage from tol_lab_share.constants.input_create_labware_message import ( MESSAGE_UUID, @@ -9,10 +10,11 @@ from tol_lab_share.message_properties.definitions.date_utc import DateUtc from tol_lab_share.message_properties.definitions.message_property import MessageProperty from tol_lab_share.message_properties.definitions.dict_input import DictInput -from tol_lab_share.messages.interfaces import OutputFeedbackMessageInterface import logging +from tol_lab_share.messages.output_feedback_message import OutputFeedbackMessage + logger = logging.getLogger(__name__) @@ -20,9 +22,10 @@ class InputCreateLabwareMessage(MessageProperty): """Class that handles parsing a TOL lab share message received""" def __init__(self, m: RabbitMessage): - """Constructor that receives a RabbitMessage and parses it using the properties defined - Parameters: - m (RabbitMessage) message we want to parse + """Constructor that receives a RabbitMessage and parses it using the properties defined. + + Args: + m (RabbitMessage): The message to parse. """ super().__init__(m) self._message = m.message @@ -31,14 +34,21 @@ def __init__(self, m: RabbitMessage): self.add_property("labware", Labware(DictInput(self._message, LABWARE))) self.add_property("create_date_utc", DateUtc(DictInput(self._message, CREATED_DATE_UTC))) - def add_to_feedback_message(self, feedback_message: OutputFeedbackMessageInterface) -> None: - """Given a feedback message, it adds all errors from this message into it. If there are - any errors it changes the flag setting to indicate it. - Parameters: - feedback_message (OutputFeedbackMessageInterface) feedback message + @singledispatchmethod + def add_to_message_property(self, message_property: MessageProperty) -> None: + super().add_to_message_property(message_property) + + @add_to_message_property.register + def _(self, feedback_message: OutputFeedbackMessage) -> None: + """Adds errors from this message into an OutputFeedbackMessage. + If errors are added, it will change the operation_was_error_free flag to False. + + Args: + feedback_message (OutputFeedbackMessage): The OutputFeedbackMessage to add errors to. """ - logger.debug("InputCreateLabware::add_to_feedback_message") - super().add_to_feedback_message(feedback_message) + logger.debug("InputCreateLabware::add_to_message_property") + super().add_to_message_property(feedback_message) + if len(self.errors) > 0: for error in self.errors: feedback_message.add_error(error) diff --git a/tol_lab_share/messages/interfaces.py b/tol_lab_share/messages/interfaces.py deleted file mode 100644 index ba2ef2ae..00000000 --- a/tol_lab_share/messages/interfaces.py +++ /dev/null @@ -1,350 +0,0 @@ -from abc import ABC, abstractmethod -from typing import Optional -from datetime import datetime -from tol_lab_share.error_codes import ErrorCode - - -class OutputFeedbackMessageInterface(ABC): - @property - @abstractmethod - def source_message_uuid(self) -> Optional[bytes]: - ... - - @source_message_uuid.setter - @abstractmethod - def source_message_uuid(self, value: bytes) -> None: - ... - - @property - @abstractmethod - def count_of_total_samples(self) -> Optional[int]: - ... - - @count_of_total_samples.setter - @abstractmethod - def count_of_total_samples(self, value: int) -> None: - ... - - @property - @abstractmethod - def count_of_valid_samples(self) -> Optional[int]: - ... - - @count_of_valid_samples.setter - @abstractmethod - def count_of_valid_samples(self, value: int) -> None: - ... - - @property - @abstractmethod - def operation_was_error_free(self) -> Optional[bool]: - ... - - @operation_was_error_free.setter - @abstractmethod - def operation_was_error_free(self, value: bool) -> None: - ... - - @abstractmethod - def to_json(self): - ... - - @abstractmethod - def publish(self, publisher, schema_registry, exchange): - ... - - @abstractmethod - def add_error(self, error: ErrorCode) -> None: - ... - - -class OutputTractionMessageRequestInterface: - @abstractmethod - def validate(self) -> bool: - ... - - @property - @abstractmethod - def supplier_name(self) -> Optional[str]: - ... - - @supplier_name.setter - @abstractmethod - def supplier_name(self, value: Optional[str]) -> None: - ... - - @property - @abstractmethod - def date_of_sample_collection(self) -> Optional[datetime]: - ... - - @date_of_sample_collection.setter - @abstractmethod - def date_of_sample_collection(self, value: Optional[datetime]) -> None: - ... - - @property - @abstractmethod - def taxon_id(self) -> Optional[str]: - ... - - @taxon_id.setter - @abstractmethod - def taxon_id(self, value: Optional[str]) -> None: - ... - - @property - @abstractmethod - def accession_number(self) -> Optional[str]: - ... - - @accession_number.setter - @abstractmethod - def accession_number(self, value: Optional[str]) -> None: - ... - - @property - @abstractmethod - def country_of_origin(self) -> Optional[str]: - ... - - @country_of_origin.setter - @abstractmethod - def country_of_origin(self, value: Optional[str]) -> None: - ... - - @property - @abstractmethod - def donor_id(self) -> Optional[str]: - ... - - @donor_id.setter - @abstractmethod - def donor_id(self, value: Optional[str]) -> None: - ... - - @property - @abstractmethod - def sanger_sample_id(self) -> Optional[str]: - ... - - @sanger_sample_id.setter - @abstractmethod - def sanger_sample_id(self, value: Optional[str]) -> None: - ... - - @property - @abstractmethod - def species(self) -> Optional[str]: - ... - - @species.setter - @abstractmethod - def species(self, value: Optional[str]) -> None: - ... - - @property - @abstractmethod - def container_type(self) -> Optional[str]: - ... - - @container_type.setter - @abstractmethod - def container_type(self, value: Optional[str]) -> None: - ... - - @property - @abstractmethod - def container_location(self) -> Optional[str]: - ... - - @container_location.setter - @abstractmethod - def container_location(self, value: Optional[str]) -> None: - ... - - @property - @abstractmethod - def container_barcode(self) -> Optional[str]: - ... - - @container_barcode.setter - @abstractmethod - def container_barcode(self, value: Optional[str]) -> None: - ... - - @property - @abstractmethod - def sample_uuid(self) -> Optional[str]: - ... - - @sample_uuid.setter - @abstractmethod - def sample_uuid(self, value: Optional[str]) -> None: - ... - - @property - @abstractmethod - def sample_name(self) -> Optional[str]: - ... - - @sample_name.setter - @abstractmethod - def sample_name(self, value: Optional[str]) -> None: - ... - - @property - @abstractmethod - def public_name(self) -> Optional[str]: - ... - - @public_name.setter - @abstractmethod - def public_name(self, value: Optional[str]) -> None: - ... - - @property - @abstractmethod - def library_type(self) -> Optional[str]: - ... - - @library_type.setter - @abstractmethod - def library_type(self, value: Optional[str]) -> None: - ... - - @property - @abstractmethod - def study_uuid(self) -> Optional[str]: - ... - - @study_uuid.setter - @abstractmethod - def study_uuid(self, value: Optional[str]) -> None: - ... - - @property - @abstractmethod - def cost_code(self) -> Optional[str]: - ... - - @cost_code.setter - @abstractmethod - def cost_code(self, value: Optional[str]) -> None: - ... - - @property - @abstractmethod - def priority_level(self) -> Optional[str]: - ... - - @priority_level.setter - @abstractmethod - def priority_level(self, value: Optional[str]) -> None: - ... - - -class TractionQcMessageRequestInterface: - @abstractmethod - def validate(self) -> bool: - ... - - @property - @abstractmethod - def container_barcode(self) -> Optional[str]: - ... - - @container_barcode.setter - @abstractmethod - def container_barcode(self, value: Optional[str]) -> None: - ... - - @property - @abstractmethod - def sanger_sample_id(self) -> Optional[str]: - ... - - @sanger_sample_id.setter - @abstractmethod - def sanger_sample_id(self, value: Optional[str]) -> None: - ... - - @property - @abstractmethod - def sheared_femto_fragment_size(self) -> Optional[str]: - ... - - @sheared_femto_fragment_size.setter - @abstractmethod - def sheared_femto_fragment_size(self, value: Optional[str]) -> None: - ... - - @property - @abstractmethod - def post_spri_concentration(self) -> Optional[str]: - ... - - @post_spri_concentration.setter - @abstractmethod - def post_spri_concentration(self, value: Optional[str]) -> None: - ... - - @property - @abstractmethod - def post_spri_volume(self) -> Optional[str]: - ... - - @post_spri_volume.setter - @abstractmethod - def post_spri_volume(self, value: Optional[str]) -> None: - ... - - @property - @abstractmethod - def final_nano_drop_280(self) -> Optional[str]: - ... - - @final_nano_drop_280.setter - @abstractmethod - def final_nano_drop_280(self, value: Optional[str]) -> None: - ... - - @property - @abstractmethod - def final_nano_drop_230(self) -> Optional[str]: - ... - - @final_nano_drop_230.setter - @abstractmethod - def final_nano_drop_230(self, value: Optional[str]) -> None: - ... - - @property - @abstractmethod - def final_nano_drop(self) -> Optional[str]: - ... - - @final_nano_drop.setter - @abstractmethod - def final_nano_drop(self, value: Optional[str]) -> None: - ... - - @property - @abstractmethod - def shearing_qc_comments(self) -> Optional[str]: - ... - - @shearing_qc_comments.setter - @abstractmethod - def shearing_qc_comments(self, value: Optional[str]) -> None: - ... - - @property - @abstractmethod - def date_submitted_utc(self) -> Optional[float]: - ... - - @date_submitted_utc.setter - @abstractmethod - def date_submitted_utc(self, value: Optional[float]) -> None: - ... diff --git a/tol_lab_share/messages/output_feedback_message.py b/tol_lab_share/messages/output_feedback_message.py index bc97f781..5a1c9a96 100644 --- a/tol_lab_share/messages/output_feedback_message.py +++ b/tol_lab_share/messages/output_feedback_message.py @@ -7,7 +7,6 @@ ) from tol_lab_share import error_codes from tol_lab_share.message_properties.definitions.message_property import MessageProperty -from tol_lab_share.messages.interfaces import OutputFeedbackMessageInterface import logging from tol_lab_share.helpers import get_config from tol_lab_share.message_properties.definitions.input import Input @@ -19,7 +18,7 @@ logger = logging.getLogger(__name__) -class OutputFeedbackMessage(MessageProperty, OutputFeedbackMessageInterface): +class OutputFeedbackMessage(MessageProperty): """Class that handles the feedback message parsing for TOL lab share""" def __init__(self): diff --git a/tol_lab_share/messages/output_traction_message.py b/tol_lab_share/messages/output_traction_message.py index 9a71c322..b8fda26f 100644 --- a/tol_lab_share/messages/output_traction_message.py +++ b/tol_lab_share/messages/output_traction_message.py @@ -1,11 +1,8 @@ +from functools import singledispatchmethod from typing import Optional, Callable, Any from json import dumps from datetime import datetime from requests import post, codes -from tol_lab_share.messages.interfaces import ( - OutputFeedbackMessageInterface, - OutputTractionMessageRequestInterface, -) from tol_lab_share.message_properties.definitions.message_property import MessageProperty from tol_lab_share.message_properties.definitions.input import Input from tol_lab_share.constants import ( @@ -16,9 +13,10 @@ from tol_lab_share import error_codes from tol_lab_share.error_codes import ErrorCode from tol_lab_share.helpers import get_config +from tol_lab_share.messages.output_feedback_message import OutputFeedbackMessage -class OutputTractionMessageRequest(OutputTractionMessageRequestInterface): +class OutputTractionMessageRequest: """Class that manages the information of a single Traction request instance as part of the Traction message """ @@ -346,8 +344,9 @@ def __init__(self): @property def origin(self) -> str: - """Default origin identifier. This will be appended to any errors generated to know - where it was originated when we received""" + """Default origin identifier. This will be appended to any errors generated to know where it was originated + when we received it. + """ return "OutputFeedbackMessage" @property @@ -355,7 +354,7 @@ def validators(self) -> list[Callable]: """List of validators to check the message is correct before sending""" return [self.check_has_requests, self.check_requests_have_all_content, self.check_no_errors] - def create_request(self) -> OutputTractionMessageRequestInterface: + def create_request(self) -> OutputTractionMessageRequest: """Creates a new request and returns it. It will be appended to the list of requests. Returns: @@ -366,8 +365,9 @@ def create_request(self) -> OutputTractionMessageRequestInterface: def request_attributes(self) -> list[dict[str, Any]]: """Returns a list with all the payloads for every request + Returns: - List[Dict[str,Any]] with all payloads for all the requests + List[Dict[str,Any]] with all payloads for all the requests """ return [request.serializer().payload() for request in self._requests] @@ -379,8 +379,9 @@ def errors(self) -> list[ErrorCode]: def check_has_requests(self) -> bool: """Returns a bool identifying if the message has requests. If not it will trigger an error and return false. + Returns - bool saying if the message has requests + bool saying if the message has requests """ if len(self._requests) > 0: return True @@ -390,15 +391,17 @@ def check_has_requests(self) -> bool: def check_no_errors(self) -> bool: """Checks that a message has no errors + Returns: - bool indicating if there is no errors + bool indicating if there is no errors """ return len(self.errors) == 0 def check_requests_have_all_content(self) -> bool: """Checks that all requests provided are valid. Triggers an error if any is not. + Returns: - bool indicating that all requests have valid content inside. + bool indicating that all requests have valid content inside. """ if all([request.validate() for request in self._requests]): return True @@ -420,9 +423,10 @@ def payload(self) -> dict[str, Any]: def error_code_traction_problem(self, status_code: int, error_str: str) -> None: """Triggers an error indicating that traction failed. - Parameters: - status_code (int) HTTP status code when sending to traction (422, 500, etc) - error_str (str) contents received by Traction endpoint on the request + + Args: + status_code (int) HTTP status code when sending to traction (422, 500, etc) + error_str (str) contents received by Traction endpoint on the request """ self.trigger_error( error_codes.ERROR_13_TRACTION_REQUEST_FAILED, text=f"HTTP CODE: { status_code }, MSG: {error_str}" @@ -431,10 +435,12 @@ def error_code_traction_problem(self, status_code: int, error_str: str) -> None: def send(self, url: str) -> bool: """Sends a request to Traction. If is correct returns true, if not it will trigger an error and return False - Parameters: - url (str) url where it will send the Traction request + + Args: + url (str) url where it will send the Traction request + Returns: - bool indicating if the request was successful + bool indicating if the request was successful """ headers = {"Content-type": "application/vnd.api+json", "Accept": "application/vnd.api+json"} @@ -447,9 +453,18 @@ def send(self, url: str) -> bool: return self._sent - def add_to_feedback_message(self, feedback_message: OutputFeedbackMessageInterface) -> None: - """Adds the relevant information about this Traction sent to the feedback message, indicating - if there has been any errors""" + @singledispatchmethod + def add_to_message_property(self, message_property: MessageProperty) -> None: + super().add_to_message_property(message_property) + + @add_to_message_property.register + def _(self, feedback_message: OutputFeedbackMessage) -> None: + """Adds error information to the an OutputFeedbackMessage. + Also sets the operation_was_error_free to False if the message has not been sent. + + Args: + feedback_message (OutputFeedbackMessage): The OutputFeedbackMessage to add errors to. + """ if not self._sent: feedback_message.operation_was_error_free = False if len(self._errors) > 0: diff --git a/tol_lab_share/messages/traction_qc_message.py b/tol_lab_share/messages/traction_qc_message.py index ed180446..503717b3 100644 --- a/tol_lab_share/messages/traction_qc_message.py +++ b/tol_lab_share/messages/traction_qc_message.py @@ -1,3 +1,4 @@ +from functools import singledispatchmethod import logging from json import dumps from typing import Any, Callable, Dict, List, Optional @@ -10,15 +11,12 @@ from tol_lab_share.helpers import get_config from tol_lab_share.message_properties.definitions.input import Input from tol_lab_share.message_properties.definitions.message_property import MessageProperty -from tol_lab_share.messages.interfaces import ( - OutputFeedbackMessageInterface, - TractionQcMessageRequestInterface, -) +from tol_lab_share.messages.output_feedback_message import OutputFeedbackMessage logger = logging.getLogger(__name__) -class TractionQcMessageRequest(TractionQcMessageRequestInterface): +class TractionQcMessageRequest: """Class that holds the information for a traction Qc message request""" def __init__(self): @@ -196,7 +194,7 @@ def validators(self) -> List[Callable]: """List of validators to check the message is correct before sending""" return [self.check_has_requests, self.check_requests_have_all_content, self.check_no_errors] - def requests(self, position: int) -> TractionQcMessageRequestInterface: + def requests(self, position: int) -> TractionQcMessageRequest: """Returns the request at position position from this message. If there is no request there it will create a new instance and return it. @@ -293,9 +291,18 @@ def send(self, url: str) -> bool: return self._sent - def add_to_feedback_message(self, feedback_message: OutputFeedbackMessageInterface) -> None: - """Adds the relevant information about this Traction sent to the feedback message, indicating - if there has been any errors""" + @singledispatchmethod + def add_to_message_property(self, message_property: MessageProperty) -> None: + super().add_to_message_property(message_property) + + @add_to_message_property.register + def _(self, feedback_message: OutputFeedbackMessage) -> None: + """Adds errors about this TractionQcMessage to an OutputFeedbackMessage. + Also sets the operation_was_error_free to False if the message was not sent. + + Args: + feedback_message (OutputFeedbackMessage): The OutputFeedbackMessage to add the errors to. + """ if not self._sent: feedback_message.operation_was_error_free = False if len(self._errors) > 0: diff --git a/tol_lab_share/processors/create_labware_processor.py b/tol_lab_share/processors/create_labware_processor.py index 8b0e3539..61ad23f8 100644 --- a/tol_lab_share/processors/create_labware_processor.py +++ b/tol_lab_share/processors/create_labware_processor.py @@ -54,14 +54,14 @@ def process(self, message: RabbitMessage) -> bool: input = InputCreateLabwareMessage(message) validation = input.validate() - input.add_to_feedback_message(output_feedback_message) + input.add_to_message_property(output_feedback_message) if validation: output_traction_message = OutputTractionMessage() input.add_to_message_property(output_traction_message) logger.info("Attempting to send to traction") output_traction_message.send(url=self._config.TRACTION_URL) - output_traction_message.add_to_feedback_message(output_feedback_message) + output_traction_message.add_to_message_property(output_feedback_message) if len(output_traction_message.errors) > 0: error_codes.ERROR_16_PROBLEM_TALKING_WITH_TRACTION.trigger( @@ -93,7 +93,7 @@ def send_qc_data_to_traction( input.add_to_message_property(traction_qc_message) logger.info("Attempting to send qc message to traction") traction_qc_message.send(url=self._config.TRACTION_QC_URL) - traction_qc_message.add_to_feedback_message(feedback_message) + traction_qc_message.add_to_message_property(feedback_message) if len(traction_qc_message.errors) > 0: error_codes.ERROR_28_PROBLEM_TALKING_TO_TRACTION.trigger( From 0cd5e6922faf16114314abd8f140889194b4182b Mon Sep 17 00:00:00 2001 From: Stuart McHattie Date: Fri, 9 Feb 2024 17:04:15 +0000 Subject: [PATCH 18/40] Remove unnecessary property boilerplate on MessageProperty --- .../definitions/message_property.py | 50 ++----------------- 1 file changed, 4 insertions(+), 46 deletions(-) diff --git a/tol_lab_share/message_properties/definitions/message_property.py b/tol_lab_share/message_properties/definitions/message_property.py index 4572a08a..7a0febb0 100644 --- a/tol_lab_share/message_properties/definitions/message_property.py +++ b/tol_lab_share/message_properties/definitions/message_property.py @@ -29,10 +29,10 @@ def __init__(self, input: Any): self._input = input self._errors: List[ErrorCode] = [] self._properties: Dict[str, Any] = {} - self.property_name = None - self.property_source = None - self.property_position = None - self.property_type = PROPERTY_TYPE_PROPERTY + self.property_name: str | None = None + self.property_source: MessageProperty | None = None + self.property_position: int | None = None + self.property_type: str = PROPERTY_TYPE_PROPERTY def validate(self) -> bool: return self._validation_status @@ -110,48 +110,6 @@ def add_property(self, property_name: str, input: Union[MessageProperty, List[Me else: self._add_property_instance(property_name, input) - @property - def property_name(self) -> Optional[str]: - """Returns the property name for this property""" - return self._property_name - - @property_name.setter - def property_name(self, value: str) -> None: - """Sets the property name for this property""" - self._property_name = value - - @property - def property_source(self) -> Any: - """Returns the property source for this property (the property that is the container of - this property""" - return self._property_source - - @property_source.setter - def property_source(self, value: MessageProperty) -> None: - """Sets the property source for this property""" - self._property_source = value - - @property - def property_position(self) -> Optional[int]: - """Returns the property position for this property that is the place in the list where this - property is defined if it was defined as part of a list of properties""" - return self._property_position - - @property_position.setter - def property_position(self, value: int) -> None: - """Sets the property position for this property""" - self._property_position = value - - @property - def property_type(self) -> Optional[str]: - """Returns the property type for this property. The type can be Property or List""" - return self._property_type - - @property_type.setter - def property_type(self, value: str) -> None: - """Sets the property type for this property.""" - self._property_type = value - @cached_property def value(self) -> Any: """Returns the value representing this property.""" From a989b6093f520492399078118b60cddb653015da Mon Sep 17 00:00:00 2001 From: Stuart McHattie Date: Fri, 9 Feb 2024 17:40:09 +0000 Subject: [PATCH 19/40] Remove boilerplate for instance variables in all message types --- .../messages/test_output_traction_message.py | 20 +- tol_lab_share/constants/__init__.py | 7 +- .../message_properties/definitions/labware.py | 17 +- .../messages/output_feedback_message.py | 49 +--- .../messages/output_traction_message.py | 236 ++---------------- tol_lab_share/messages/traction_qc_message.py | 130 ++-------- 6 files changed, 74 insertions(+), 385 deletions(-) diff --git a/tests/messages/test_output_traction_message.py b/tests/messages/test_output_traction_message.py index abfc0f19..8550e99e 100644 --- a/tests/messages/test_output_traction_message.py +++ b/tests/messages/test_output_traction_message.py @@ -1,9 +1,5 @@ from tol_lab_share.messages.output_traction_message import OutputTractionMessage from datetime import datetime -from tol_lab_share.constants import ( - OUTPUT_TRACTION_MESSAGE_CREATE_REQUEST_CONTAINER_TYPE_WELLS, - OUTPUT_TRACTION_MESSAGE_CREATE_REQUEST_CONTAINER_TYPE_TUBES, -) import requests_mock @@ -11,7 +7,7 @@ def valid_traction_message(): instance = OutputTractionMessage() request = instance.create_request() request.container_barcode = "1" - request.container_type = OUTPUT_TRACTION_MESSAGE_CREATE_REQUEST_CONTAINER_TYPE_WELLS + request.container_type = "wells" request.library_type = "library" request.sample_name = "test1" request.study_uuid = "dd490ee5-fd1d-456d-99fd-eb9d3861e014" @@ -46,7 +42,7 @@ def test_output_traction_message_can_generate_payload_for_plates(): request = instance.create_request() request.container_barcode = "1" - request.container_type = OUTPUT_TRACTION_MESSAGE_CREATE_REQUEST_CONTAINER_TYPE_WELLS + request.container_type = "wells" request.container_location = "A1" request.library_type = "library" request.sample_name = "test1" @@ -66,7 +62,7 @@ def test_output_traction_message_can_generate_payload_for_plates(): request = instance.create_request() request.container_barcode = "1" - request.container_type = OUTPUT_TRACTION_MESSAGE_CREATE_REQUEST_CONTAINER_TYPE_WELLS + request.container_type = "wells" request.container_location = "B1" request.library_type = "library" request.sample_name = "test1" @@ -146,7 +142,7 @@ def test_output_traction_message_can_generate_payload_for_ont_library_types(): request = instance.create_request() request.container_barcode = "1" - request.container_type = OUTPUT_TRACTION_MESSAGE_CREATE_REQUEST_CONTAINER_TYPE_WELLS + request.container_type = "wells" request.container_location = "A1" request.library_type = "ONT_mylib" request.sample_name = "test1" @@ -166,7 +162,7 @@ def test_output_traction_message_can_generate_payload_for_ont_library_types(): request = instance.create_request() request.container_barcode = "1" - request.container_type = OUTPUT_TRACTION_MESSAGE_CREATE_REQUEST_CONTAINER_TYPE_WELLS + request.container_type = "wells" request.container_location = "B1" request.library_type = "ONT_mylib" request.sample_name = "test1" @@ -248,7 +244,7 @@ def test_output_traction_message_can_generate_payload_for_tubes(): request = instance.create_request() request.container_barcode = "1" - request.container_type = OUTPUT_TRACTION_MESSAGE_CREATE_REQUEST_CONTAINER_TYPE_TUBES + request.container_type = "tubes" request.library_type = "library" request.sample_name = "test1" request.study_uuid = "dd490ee5-fd1d-456d-99fd-eb9d3861e014" @@ -267,7 +263,7 @@ def test_output_traction_message_can_generate_payload_for_tubes(): request = instance.create_request() request.container_barcode = "1" - request.container_type = OUTPUT_TRACTION_MESSAGE_CREATE_REQUEST_CONTAINER_TYPE_TUBES + request.container_type = "tubes" request.library_type = "library" request.sample_name = "test1" request.study_uuid = "dd490ee5-fd1d-456d-99fd-eb9d3861e014" @@ -384,7 +380,7 @@ def test_output_traction_message_can_add_to_message_property_when_errors(valid_f request = instance.create_request() request.container_barcode = "1" - request.container_type = OUTPUT_TRACTION_MESSAGE_CREATE_REQUEST_CONTAINER_TYPE_TUBES + request.container_type = "tubes" assert not instance.validate() diff --git a/tol_lab_share/constants/__init__.py b/tol_lab_share/constants/__init__.py index c7d399af..6a3a08cd 100644 --- a/tol_lab_share/constants/__init__.py +++ b/tol_lab_share/constants/__init__.py @@ -1,3 +1,6 @@ +from typing import Literal + + RABBITMQ_SUBJECT_CREATE_LABWARE = "create-labware" RABBITMQ_SUBJECT_UPDATE_LABWARE = "update-labware" RABBITMQ_FEEDBACK_EXCHANGE = "psd.tol" @@ -7,6 +10,6 @@ RABBITMQ_SUBJECT_UPDATE_LABWARE_FEEDBACK = "update-labware-feedback" -OUTPUT_TRACTION_MESSAGE_CREATE_REQUEST_CONTAINER_TYPE_WELLS = "wells" -OUTPUT_TRACTION_MESSAGE_CREATE_REQUEST_CONTAINER_TYPE_TUBES = "tubes" OUTPUT_TRACTION_MESSAGE_SOURCE = "tol-lab-share.tol" + +OUTPUT_TRACTION_MESSAGE_CONTAINER_TYPES = Literal["tubes", "wells"] diff --git a/tol_lab_share/message_properties/definitions/labware.py b/tol_lab_share/message_properties/definitions/labware.py index 954fffdb..d450aa24 100644 --- a/tol_lab_share/message_properties/definitions/labware.py +++ b/tol_lab_share/message_properties/definitions/labware.py @@ -1,10 +1,7 @@ import logging from typing import Any, List -from tol_lab_share.constants import ( - OUTPUT_TRACTION_MESSAGE_CREATE_REQUEST_CONTAINER_TYPE_TUBES, - OUTPUT_TRACTION_MESSAGE_CREATE_REQUEST_CONTAINER_TYPE_WELLS, -) +from tol_lab_share.constants import OUTPUT_TRACTION_MESSAGE_CONTAINER_TYPES from tol_lab_share.constants.input_create_labware_message import BARCODE, LABWARE_TYPE, SAMPLES from tol_lab_share.message_properties.definitions.barcode import Barcode from tol_lab_share.message_properties.definitions.dict_input import DictInput @@ -54,16 +51,16 @@ def count_of_valid_samples(self) -> int: """Returns the number of samples that are valid from this labware""" return [sample.validate() for sample in self._properties["samples"]].count(True) - def traction_container_type(self) -> str: - """It converts the labware type to a valid container type value for - Traction. + def traction_container_type(self) -> OUTPUT_TRACTION_MESSAGE_CONTAINER_TYPES: + """It converts the labware type to a valid container type value for Traction. + Returns: - str with a container type value for Traction + str with a container type value for Traction """ if self.labware_type().value == "Tube": - return OUTPUT_TRACTION_MESSAGE_CREATE_REQUEST_CONTAINER_TYPE_TUBES + return "tubes" else: - return OUTPUT_TRACTION_MESSAGE_CREATE_REQUEST_CONTAINER_TYPE_WELLS + return "wells" @singledispatchmethod def add_to_message_property(self, message_property: MessageProperty) -> None: diff --git a/tol_lab_share/messages/output_feedback_message.py b/tol_lab_share/messages/output_feedback_message.py index 5a1c9a96..be3169de 100644 --- a/tol_lab_share/messages/output_feedback_message.py +++ b/tol_lab_share/messages/output_feedback_message.py @@ -1,4 +1,3 @@ -from typing import Optional from lab_share_lib.rabbit.avro_encoder import AvroEncoderJson, AvroEncoderBinary from lab_share_lib.constants import RABBITMQ_HEADER_VALUE_ENCODER_TYPE_BINARY, RABBITMQ_HEADER_VALUE_ENCODER_TYPE_JSON from tol_lab_share.constants import ( @@ -25,10 +24,10 @@ def __init__(self): """Constructor that resets the state of a feedback message""" super().__init__(Input(self)) - self._source_message_uuid: Optional[bytes] = None - self._count_of_total_samples: Optional[int] = None - self._count_of_valid_samples: Optional[int] = None - self._operation_was_error_free: Optional[bool] = True + self.source_message_uuid: bytes | None = None + self.count_of_total_samples: int | None = None + self.count_of_valid_samples: int | None = None + self.operation_was_error_free: bool = True @property def validators(self): @@ -40,46 +39,6 @@ def origin(self): """ "Name of the origin for this property, set as a constant value.""" return "OutputFeedbackMessage" - @property - def source_message_uuid(self) -> Optional[bytes]: - """Returns the uuid of the source message""" - return self._source_message_uuid - - @source_message_uuid.setter - def source_message_uuid(self, value: bytes) -> None: - """Sets the uuid of the source message""" - self._source_message_uuid = value - - @property - def count_of_total_samples(self) -> Optional[int]: - """Returns the count of total samples""" - return self._count_of_total_samples - - @count_of_total_samples.setter - def count_of_total_samples(self, value: int) -> None: - """Sets the count of total samples""" - self._count_of_total_samples = value - - @property - def count_of_valid_samples(self) -> Optional[int]: - """Returns the count of valid samples""" - return self._count_of_valid_samples - - @count_of_valid_samples.setter - def count_of_valid_samples(self, value: int) -> None: - """Sets the count of valid samples""" - self._count_of_valid_samples = value - - @property - def operation_was_error_free(self) -> Optional[bool]: - """Returns the flag indicating if the operation was error free""" - return self._operation_was_error_free - - @operation_was_error_free.setter - def operation_was_error_free(self, value: bool) -> None: - """Sets the flag indicating if the operation was error free""" - self._operation_was_error_free = value - def to_json(self) -> Dict[str, Any]: """Returns a Dict with the JSON-like representation of the message.""" return { diff --git a/tol_lab_share/messages/output_traction_message.py b/tol_lab_share/messages/output_traction_message.py index b8fda26f..2dd45e11 100644 --- a/tol_lab_share/messages/output_traction_message.py +++ b/tol_lab_share/messages/output_traction_message.py @@ -1,13 +1,12 @@ from functools import singledispatchmethod -from typing import Optional, Callable, Any +from typing import Callable, Any from json import dumps from datetime import datetime from requests import post, codes from tol_lab_share.message_properties.definitions.message_property import MessageProperty from tol_lab_share.message_properties.definitions.input import Input from tol_lab_share.constants import ( - OUTPUT_TRACTION_MESSAGE_CREATE_REQUEST_CONTAINER_TYPE_WELLS, - OUTPUT_TRACTION_MESSAGE_CREATE_REQUEST_CONTAINER_TYPE_TUBES, + OUTPUT_TRACTION_MESSAGE_CONTAINER_TYPES, OUTPUT_TRACTION_MESSAGE_SOURCE, ) from tol_lab_share import error_codes @@ -23,218 +22,37 @@ class OutputTractionMessageRequest: def __init__(self): """Constructor that initializes all info for a single request.""" - self._library_type = None - self._study_uuid = None - self._sample_name = None - self._sample_uuid = None - self._container_barcode = None - self._container_location = None - self._container_type = None - self._species = None - self._cost_code = None - self._priority_level = None - self._sanger_sample_id = None - self._public_name = None - self._supplier_name = None - self._taxon_id = None - self._donor_id = None - self._country_of_origin = None - self._accession_number = None - self._date_of_sample_collection = None + self.accession_number: str | None = None + self.container_barcode: str | None = None + self.container_location: str | None = None + self.container_type: OUTPUT_TRACTION_MESSAGE_CONTAINER_TYPES | None = None + self.cost_code: str | None = None + self.country_of_origin: str | None = None + self.date_of_sample_collection: datetime | None = None + self.donor_id: str | None = None + self.library_type: str | None = None + self.priority_level: str | None = None + self.public_name: str | None = None + self.sample_name: str | None = None + self.sample_uuid: str | None = None + self.sanger_sample_id: str | None = None + self.species: str | None = None + self.study_uuid: str | None = None + self.supplier_name: str | None = None + self.taxon_id: str | None = None def validate(self) -> bool: """Checks that we have all required information and that it is valid before marking this request as valid.""" return ( - (self._library_type is not None) - and (self._study_uuid is not None) - and (self._sample_name is not None) - and (self._container_barcode is not None) - and (self._species is not None) - and ( - (self._container_type == OUTPUT_TRACTION_MESSAGE_CREATE_REQUEST_CONTAINER_TYPE_WELLS) - or (self._container_type == OUTPUT_TRACTION_MESSAGE_CREATE_REQUEST_CONTAINER_TYPE_TUBES) - ) - and (self._species is not None) + (self.library_type is not None) + and (self.study_uuid is not None) + and (self.sample_name is not None) + and (self.container_barcode is not None) + and (self.species is not None) + and (self.container_type is not None) + and (self.species is not None) ) - @property - def sanger_sample_id(self) -> Optional[str]: - """Gets the sanger_sample_id value for this request""" - return self._sanger_sample_id - - @sanger_sample_id.setter - def sanger_sample_id(self, value: Optional[str]) -> None: - """Sets the sanger_sample_id value for this request""" - self._sanger_sample_id = value - - @property - def public_name(self) -> Optional[str]: - """Gets the public_name value for this request""" - return self._public_name - - @public_name.setter - def public_name(self, value: Optional[str]) -> None: - """Sets the public_name value for this request""" - self._public_name = value - - @property - def date_of_sample_collection(self) -> Optional[datetime]: - """Gets the date_of_sample_collection value for this request""" - return self._date_of_sample_collection - - @date_of_sample_collection.setter - def date_of_sample_collection(self, value: Optional[datetime]) -> None: - """Sets the date_of_sample_collection value for this request""" - self._date_of_sample_collection = value - - @property - def supplier_name(self) -> Optional[str]: - """Gets the supplier_name value for this request""" - return self._supplier_name - - @supplier_name.setter - def supplier_name(self, value: Optional[str]) -> None: - """Sets the supplier_name value for this request""" - self._supplier_name = value - - @property - def taxon_id(self) -> Optional[str]: - """Gets the taxon_id value for this request""" - return self._taxon_id - - @taxon_id.setter - def taxon_id(self, value: Optional[str]) -> None: - """Sets the taxon_id value for this request""" - self._taxon_id = value - - @property - def donor_id(self) -> Optional[str]: - """Gets the donor_id value for this request""" - return self._donor_id - - @donor_id.setter - def donor_id(self, value: Optional[str]) -> None: - """Sets the donor_id value for this request""" - self._donor_id = value - - @property - def country_of_origin(self) -> Optional[str]: - """Gets the country_of_origin value for this request""" - return self._country_of_origin - - @country_of_origin.setter - def country_of_origin(self, value: Optional[str]) -> None: - """Sets the country_of_origin value for this request""" - self._country_of_origin = value - - @property - def accession_number(self) -> Optional[str]: - """Gets the accession_number value for this request""" - return self._accession_number - - @accession_number.setter - def accession_number(self, value: Optional[str]) -> None: - """Sets the accession_number value for this request""" - self._accession_number = value - - @property - def species(self) -> Optional[str]: - """Gets the species value for this request""" - return self._species - - @species.setter - def species(self, value: Optional[str]) -> None: - """Sets the species value for this request""" - self._species = value - - @property - def cost_code(self) -> Optional[str]: - """Gets the cost_code value for this request""" - return self._cost_code - - @cost_code.setter - def cost_code(self, value: Optional[str]) -> None: - """Sets the cost_code value for this request""" - self._cost_code = value - - @property - def container_type(self) -> Optional[str]: - """Gets the container type value for this request. ('wells' or 'tubes')""" - return self._container_type - - @container_type.setter - def container_type(self, value: Optional[str]) -> None: - """Sets the container type value for this request. ('wells' or 'tubes')""" - self._container_type = value - - @property - def container_location(self) -> Optional[str]: - """Gets the container location for this request. Eg: 'A01'""" - return self._container_location - - @container_location.setter - def container_location(self, value: Optional[str]) -> None: - """Sets the container location for this request. Eg: 'A01'""" - self._container_location = value - - @property - def container_barcode(self) -> Optional[str]: - """Gets the container barcode for this request.""" - return self._container_barcode - - @container_barcode.setter - def container_barcode(self, value: Optional[str]) -> None: - """Sets the container barcode for this request.""" - self._container_barcode = value - - @property - def sample_uuid(self) -> Optional[str]: - """Gets the sample uuid for this request.""" - return self._sample_uuid - - @sample_uuid.setter - def sample_uuid(self, value: Optional[str]) -> None: - """Sets the sample uuid for this request.""" - self._sample_uuid = value - - @property - def sample_name(self) -> Optional[str]: - """Gets the sample name for this request.""" - return self._sample_name - - @sample_name.setter - def sample_name(self, value: Optional[str]) -> None: - """Sets the sample name for this request.""" - self._sample_name = value - - @property - def library_type(self) -> Optional[str]: - """Gets the library type for this request.""" - return self._library_type - - @library_type.setter - def library_type(self, value: Optional[str]) -> None: - """Sets the library type for this request.""" - self._library_type = value - - @property - def study_uuid(self) -> Optional[str]: - """Gets the study uuid for this request.""" - return self._study_uuid - - @study_uuid.setter - def study_uuid(self, value: Optional[str]) -> None: - """Sets the study uuid for this request.""" - self._study_uuid = value - - @property - def priority_level(self) -> Optional[str]: - return self._priority_level - - @priority_level.setter - def priority_level(self, value: Optional[str]) -> None: - self._priority_level = value - def serializer(self): """Returns a serializer instance to handle the generation of the message for this request. Returns: diff --git a/tol_lab_share/messages/traction_qc_message.py b/tol_lab_share/messages/traction_qc_message.py index 503717b3..f8e3a327 100644 --- a/tol_lab_share/messages/traction_qc_message.py +++ b/tol_lab_share/messages/traction_qc_message.py @@ -1,7 +1,7 @@ from functools import singledispatchmethod import logging from json import dumps -from typing import Any, Callable, Dict, List, Optional +from typing import Any, Callable, Dict, List from requests import codes, post @@ -17,121 +17,37 @@ class TractionQcMessageRequest: - """Class that holds the information for a traction Qc message request""" + """Class that holds the information for a traction Qc message request.""" def __init__(self): - """Constructor to initialize the info for the request""" - self._sanger_sample_id = None - self._container_barcode = None - self._sheared_femto_fragment_size = None - self._post_spri_concentration = None - self._post_spri_volume = None - self._final_nano_drop_280 = None - self._final_nano_drop_230 = None - self._final_nano_drop = None - self._shearing_qc_comments = None - self._date_submitted_utc = None + """Constructor to initialize the info for the request.""" + self.container_barcode: str | None = None + self.date_submitted_utc: float | None = None + self.final_nano_drop: str | None = None + self.final_nano_drop_230: str | None = None + self.final_nano_drop_280: str | None = None + self.post_spri_concentration: str | None = None + self.post_spri_volume: str | None = None + self.sanger_sample_id: str | None = None + self.sheared_femto_fragment_size: str | None = None + self.shearing_qc_comments: str | None = None def validate(self) -> bool: """Checks that we have all required information and that it is valid before marking this request as valid.""" return ( - (self._sanger_sample_id is not None) - and (self._container_barcode is not None) - and (self._sheared_femto_fragment_size is not None) - and (self._post_spri_concentration is not None) - and (self._post_spri_volume is not None) - and (self._final_nano_drop_280 is not None) - and (self._final_nano_drop_230 is not None) - and (self._final_nano_drop is not None) - and (self._shearing_qc_comments is not None) - and (self._date_submitted_utc is not None) + (self.sanger_sample_id is not None) + and (self.container_barcode is not None) + and (self.sheared_femto_fragment_size is not None) + and (self.post_spri_concentration is not None) + and (self.post_spri_volume is not None) + and (self.final_nano_drop_280 is not None) + and (self.final_nano_drop_230 is not None) + and (self.final_nano_drop is not None) + and (self.shearing_qc_comments is not None) + and (self.date_submitted_utc is not None) ) - @property - def container_barcode(self) -> Optional[str]: - """Gets the container barcode for this request.""" - return self._container_barcode - - @container_barcode.setter - def container_barcode(self, value: Optional[str]) -> None: - """Sets the container barcode for this request.""" - self._container_barcode = value - - @property - def sanger_sample_id(self) -> Optional[str]: - """Gets the Sanger sample id for this request.""" - return self._sanger_sample_id - - @sanger_sample_id.setter - def sanger_sample_id(self, value: Optional[str]) -> None: - """Sets the Sanger sample id for this request.""" - self._sanger_sample_id = value - - @property - def sheared_femto_fragment_size(self) -> Optional[str]: - return self._sheared_femto_fragment_size - - @sheared_femto_fragment_size.setter - def sheared_femto_fragment_size(self, value: Optional[str]) -> None: - self._sheared_femto_fragment_size = value - - @property - def post_spri_concentration(self) -> Optional[str]: - return self._post_spri_concentration - - @post_spri_concentration.setter - def post_spri_concentration(self, value: Optional[str]) -> None: - self._post_spri_concentration = value - - @property - def post_spri_volume(self) -> Optional[str]: - return self._post_spri_volume - - @post_spri_volume.setter - def post_spri_volume(self, value: Optional[str]) -> None: - self._post_spri_volume = value - - @property - def final_nano_drop_280(self) -> Optional[str]: - return self._final_nano_drop_280 - - @final_nano_drop_280.setter - def final_nano_drop_280(self, value: Optional[str]) -> None: - self._final_nano_drop_280 = value - - @property - def final_nano_drop_230(self) -> Optional[str]: - return self._final_nano_drop_230 - - @final_nano_drop_230.setter - def final_nano_drop_230(self, value: Optional[str]) -> None: - self._final_nano_drop_230 = value - - @property - def final_nano_drop(self) -> Optional[str]: - return self._final_nano_drop - - @final_nano_drop.setter - def final_nano_drop(self, value: Optional[str]) -> None: - self._final_nano_drop = value - - @property - def shearing_qc_comments(self) -> Optional[str]: - return self._shearing_qc_comments - - @shearing_qc_comments.setter - def shearing_qc_comments(self, value: Optional[str]) -> None: - self._shearing_qc_comments = value - - @property - def date_submitted_utc(self) -> Optional[float]: - return self._date_submitted_utc - - @date_submitted_utc.setter - def date_submitted_utc(self, value: Optional[float]) -> None: - self._date_submitted_utc = value - def serializer(self): """Returns a serializer instance to handle the generation of the message for this request. Returns: From 1d702b41e87661d1445a6f359ca3d74656e64462 Mon Sep 17 00:00:00 2001 From: Stuart McHattie Date: Fri, 9 Feb 2024 17:54:01 +0000 Subject: [PATCH 20/40] Change Dict and List imports to built in types --- .../data/examples_create_labware_messages.py | 6 +- .../definitions/test_dict_input.py | 24 +++----- .../definitions/test_message_property.py | 6 +- tol_lab_share/config/logging.py | 4 +- tol_lab_share/config/processors.py | 4 +- tol_lab_share/error_codes.py | 14 ++--- .../definitions/accession_number.py | 4 +- .../message_properties/definitions/barcode.py | 4 +- .../definitions/common_name.py | 4 +- .../definitions/concentration.py | 4 +- .../definitions/cost_code.py | 4 +- .../definitions/country_of_origin.py | 4 +- .../definitions/date_utc.py | 4 +- .../definitions/dict_input.py | 14 ++--- .../definitions/donor_id.py | 4 +- .../definitions/final_nano_drop.py | 4 +- .../definitions/final_nano_drop_230.py | 4 +- .../definitions/final_nano_drop_280.py | 4 +- .../definitions/genome_size.py | 4 +- .../message_properties/definitions/input.py | 4 +- .../message_properties/definitions/labware.py | 14 ++--- .../definitions/labware_type.py | 12 ++-- .../definitions/library_type.py | 4 +- .../definitions/location.py | 4 +- .../definitions/message_property.py | 26 ++++----- .../definitions/post_spri_concentration.py | 4 +- .../definitions/post_spri_volume.py | 4 +- .../definitions/priority_level.py | 4 +- .../definitions/public_name.py | 4 +- .../message_properties/definitions/sample.py | 56 +++++++++---------- .../definitions/sanger_sample_id.py | 4 +- .../scientific_name_from_taxon_id.py | 8 +-- .../sheared_femto_fragment_size.py | 4 +- .../definitions/shearing_qc_comments.py | 4 +- .../definitions/supplier_sample_name.py | 4 +- .../definitions/taxon_id.py | 4 +- .../message_properties/definitions/uuid.py | 4 +- .../message_properties/definitions/volume.py | 4 +- .../messages/input_create_labware_message.py | 8 +-- .../messages/output_feedback_message.py | 10 ++-- .../messages/output_traction_message.py | 6 +- tol_lab_share/messages/traction_qc_message.py | 22 ++++---- tol_lab_share/types.py | 6 +- 43 files changed, 169 insertions(+), 175 deletions(-) diff --git a/tests/data/examples_create_labware_messages.py b/tests/data/examples_create_labware_messages.py index 0997b9be..2ec57687 100644 --- a/tests/data/examples_create_labware_messages.py +++ b/tests/data/examples_create_labware_messages.py @@ -1,7 +1,7 @@ from datetime import datetime -from typing import Any, Dict +from typing import Any -TEST_CREATE_LABWARE_MSG_OBJECT: Dict[str, Any] = { +TEST_CREATE_LABWARE_MSG_OBJECT: dict[str, Any] = { "messageUuid": "b01aa0ad-7b19-4f94-87e9-70d74fb8783c".encode(), "messageCreateDateUtc": datetime.now(), "labware": { @@ -69,7 +69,7 @@ } -TEST_INVALID_CREATE_LABWARE_MSG_OBJECT: Dict[str, Any] = { +TEST_INVALID_CREATE_LABWARE_MSG_OBJECT: dict[str, Any] = { "messageUuid": "b01aa0ad7b19-4f94-87e9-70d74fb8783c".encode(), "messageCreateDateUtc": datetime.now(), "labware": { diff --git a/tests/message_properties/definitions/test_dict_input.py b/tests/message_properties/definitions/test_dict_input.py index d3a7217b..6231f366 100644 --- a/tests/message_properties/definitions/test_dict_input.py +++ b/tests/message_properties/definitions/test_dict_input.py @@ -1,39 +1,33 @@ -from tol_lab_share.message_properties.definitions.dict_input import DictInput +from tol_lab_share.message_properties.definitions.dict_input import dictInput def test_dict_input_can_validate_when_valid(): - input = DictInput({"name": 1}, "name") + input = dictInput({"name": 1}, "name") assert input.validate() - input = DictInput(DictInput({"name": {"first": "James"}}, "name"), "first") + input = dictInput(dictInput({"name": {"first": "James"}}, "name"), "first") assert input.validate() def test_dict_input_can_get_value_when_valid(): - input = DictInput({"name": 1}, "name") + input = dictInput({"name": 1}, "name") assert input.validate() def test_dict_input_can_get_chained_value_when_valid(): - input = DictInput(DictInput({"name": {"first": "James"}}, "name"), "first") + input = dictInput(dictInput({"name": {"first": "James"}}, "name"), "first") assert input.value == "James" def test_dict_input_can_validate_when_invalid(): - input = DictInput({}, "name") + input = dictInput({}, "name") assert not input.validate() - input = DictInput(None, "name") # type: ignore + input = dictInput({"other": 1}, "name") assert not input.validate() - input = DictInput([], "name") # type: ignore + input = dictInput(dictInput({"name": {"first": "James"}}, "name"), "second") assert not input.validate() - input = DictInput({"other": 1}, "name") - assert not input.validate() - - input = DictInput(DictInput({"name": {"first": "James"}}, "name"), "second") - assert not input.validate() - - input = DictInput(DictInput({"name": {"first": "James"}}, "WRONG!!!"), "second") + input = dictInput(dictInput({"name": {"first": "James"}}, "WRONG!!!"), "second") assert not input.validate() diff --git a/tests/message_properties/definitions/test_message_property.py b/tests/message_properties/definitions/test_message_property.py index b77ffe25..a1dc6dd5 100644 --- a/tests/message_properties/definitions/test_message_property.py +++ b/tests/message_properties/definitions/test_message_property.py @@ -1,7 +1,7 @@ from tol_lab_share.message_properties.definitions.message_property import MessageProperty from unittest import mock from tol_lab_share.message_properties.definitions.input import Input -from tol_lab_share.message_properties.definitions.dict_input import DictInput +from tol_lab_share.message_properties.definitions.dict_input import dictInput from tol_lab_share import error_codes import pytest from datetime import datetime @@ -64,7 +64,7 @@ def test_message_property_check_is_integer(): assert instance.check_is_integer() is False assert len(instance.errors) > 0 - instance = MessageProperty(DictInput({"test": 1234}, "wrong!!")) + instance = MessageProperty(dictInput({"test": 1234}, "wrong!!")) assert instance.check_is_integer() is False assert len(instance.errors) > 0 @@ -116,7 +116,7 @@ def test_message_property_check_is_float(): assert instance.check_is_float() is True assert len(instance.errors) == 0 - instance = MessageProperty(DictInput({"test": 1234}, "wrong!!")) + instance = MessageProperty(dictInput({"test": 1234}, "wrong!!")) assert instance.check_is_float() is False assert len(instance.errors) > 0 diff --git a/tol_lab_share/config/logging.py b/tol_lab_share/config/logging.py index 19bd5ac1..fd2572b7 100644 --- a/tol_lab_share/config/logging.py +++ b/tol_lab_share/config/logging.py @@ -1,8 +1,8 @@ -from typing import Any, Dict +from typing import Any DEFAULT_LOGGING_LEVEL = "INFO" -LOGGING: Dict[str, Any] = { +LOGGING: dict[str, Any] = { "version": 1, "disable_existing_loggers": False, "formatters": { diff --git a/tol_lab_share/config/processors.py b/tol_lab_share/config/processors.py index 525b1b36..2a6b3af5 100644 --- a/tol_lab_share/config/processors.py +++ b/tol_lab_share/config/processors.py @@ -1,11 +1,11 @@ -from typing import Dict, cast +from typing import cast from lab_share_lib.processing.base_processor import BaseProcessor from tol_lab_share.processors.create_labware_processor import CreateLabwareProcessor from tol_lab_share.processors.update_labware_processor import UpdateLabwareProcessor from tol_lab_share.constants import RABBITMQ_SUBJECT_CREATE_LABWARE, RABBITMQ_SUBJECT_UPDATE_LABWARE -PROCESSORS: Dict[str, BaseProcessor] = { +PROCESSORS: dict[str, BaseProcessor] = { RABBITMQ_SUBJECT_CREATE_LABWARE: cast(BaseProcessor, CreateLabwareProcessor), RABBITMQ_SUBJECT_UPDATE_LABWARE: cast(BaseProcessor, UpdateLabwareProcessor), } diff --git a/tol_lab_share/error_codes.py b/tol_lab_share/error_codes.py index 2cfdebfb..1599442d 100644 --- a/tol_lab_share/error_codes.py +++ b/tol_lab_share/error_codes.py @@ -1,6 +1,6 @@ import copy import logging -from typing import Any, Dict, Optional +from typing import Any LEVEL_FATAL = "level_fatal" LEVEL_ERROR = "leverl_error" @@ -75,7 +75,7 @@ def origin_for_json(self) -> Any: return "sample" return "root" - def json(self) -> Dict[str, Any]: + def json(self) -> dict[str, Any]: """Returns a JSON-like representation of the ErrorCode""" return { "typeId": self.type_id, @@ -84,7 +84,7 @@ def json(self) -> Dict[str, Any]: "description": self.description, } - def message_for_trigger(self, text: Optional[str] = None, instance: Any = None) -> str: + def message_for_trigger(self, text: str | None = None, instance: Any = None) -> str: """Generates a user-friendly message to attach to the error triggered Parameters: text (str) user friendly description of the error @@ -102,10 +102,10 @@ def message_for_trigger(self, text: Optional[str] = None, instance: Any = None) def trigger( self, - text: Optional[str] = None, + text: str | None = None, instance: Any = None, - origin: Optional[str] = None, - field: Optional[str] = None, + origin: str | None = None, + field: str | None = None, ) -> Any: """Triggers the action defined for this message. By default, if will log all messages to the level of criticality defined in the @@ -158,7 +158,7 @@ def trigger( ERROR_9_INVALID_INPUT = ErrorCode(9, "plate", "input", "Not valid input") ERROR_10_DICT_WRONG_KEY = ErrorCode(10, "plate", "dict", "Not valid key") ERROR_11_PARENT_DICT_WRONG = ErrorCode(11, "plate", "dict", "Parent dict is wrong") -ERROR_12_DICT_NOT_ITERABLE = ErrorCode(12, "plate", "dict", "Dict is not iterable") +ERROR_12_DICT_NOT_ITERABLE = ErrorCode(12, "plate", "dict", "dict is not iterable") ERROR_13_TRACTION_REQUEST_FAILED = ErrorCode(13, "plate", "dict", "Traction send request failed") ERROR_14_PROBLEM_ACCESSING_TAXON_ID = ErrorCode( 14, "plate", "taxon_id", "Problem when accessing the taxon id service", level=LEVEL_FATAL, handler=HANDLER_RAISE diff --git a/tol_lab_share/message_properties/definitions/accession_number.py b/tol_lab_share/message_properties/definitions/accession_number.py index 65b4fb8e..77f0e583 100644 --- a/tol_lab_share/message_properties/definitions/accession_number.py +++ b/tol_lab_share/message_properties/definitions/accession_number.py @@ -1,5 +1,5 @@ from .message_property import MessageProperty -from typing import List, Callable +from typing import Callable class AccessionNumber(MessageProperty): @@ -10,6 +10,6 @@ class AccessionNumber(MessageProperty): """ @property - def validators(self) -> List[Callable]: + def validators(self) -> list[Callable]: """Defines the list of validators""" return [self.check_is_string] diff --git a/tol_lab_share/message_properties/definitions/barcode.py b/tol_lab_share/message_properties/definitions/barcode.py index 54148716..eaf73f47 100644 --- a/tol_lab_share/message_properties/definitions/barcode.py +++ b/tol_lab_share/message_properties/definitions/barcode.py @@ -1,6 +1,6 @@ import logging from .message_property import MessageProperty -from typing import List, Callable +from typing import Callable logger = logging.getLogger(__name__) @@ -13,6 +13,6 @@ class Barcode(MessageProperty): """ @property - def validators(self) -> List[Callable]: + def validators(self) -> list[Callable]: """Defines the list of validators""" return [self.check_is_string] diff --git a/tol_lab_share/message_properties/definitions/common_name.py b/tol_lab_share/message_properties/definitions/common_name.py index 63f49d06..76a412bf 100644 --- a/tol_lab_share/message_properties/definitions/common_name.py +++ b/tol_lab_share/message_properties/definitions/common_name.py @@ -1,5 +1,5 @@ from .message_property import MessageProperty -from typing import List, Callable +from typing import Callable class CommonName(MessageProperty): @@ -10,6 +10,6 @@ class CommonName(MessageProperty): """ @property - def validators(self) -> List[Callable]: + def validators(self) -> list[Callable]: """Defines the list of validators""" return [self.check_is_string] diff --git a/tol_lab_share/message_properties/definitions/concentration.py b/tol_lab_share/message_properties/definitions/concentration.py index ae7ea84a..8b35c544 100644 --- a/tol_lab_share/message_properties/definitions/concentration.py +++ b/tol_lab_share/message_properties/definitions/concentration.py @@ -1,5 +1,5 @@ from .message_property import MessageProperty -from typing import List, Callable +from typing import Callable class Concentration(MessageProperty): @@ -9,6 +9,6 @@ class Concentration(MessageProperty): """ @property - def validators(self) -> List[Callable]: + def validators(self) -> list[Callable]: """Defines the list of validators""" return [self.check_is_string] diff --git a/tol_lab_share/message_properties/definitions/cost_code.py b/tol_lab_share/message_properties/definitions/cost_code.py index 3d4dd650..035c54b9 100644 --- a/tol_lab_share/message_properties/definitions/cost_code.py +++ b/tol_lab_share/message_properties/definitions/cost_code.py @@ -1,5 +1,5 @@ from .message_property import MessageProperty -from typing import List, Callable +from typing import Callable class CostCode(MessageProperty): @@ -10,6 +10,6 @@ class CostCode(MessageProperty): """ @property - def validators(self) -> List[Callable]: + def validators(self) -> list[Callable]: """Defines the list of validators""" return [self.check_is_string] diff --git a/tol_lab_share/message_properties/definitions/country_of_origin.py b/tol_lab_share/message_properties/definitions/country_of_origin.py index ff4b6e4d..bce808a4 100644 --- a/tol_lab_share/message_properties/definitions/country_of_origin.py +++ b/tol_lab_share/message_properties/definitions/country_of_origin.py @@ -2,7 +2,7 @@ from tol_lab_share.config.insdc import COUNTRIES from tol_lab_share import error_codes import logging -from typing import List, Callable +from typing import Callable logger = logging.getLogger(__name__) @@ -15,7 +15,7 @@ class CountryOfOrigin(MessageProperty): """ @property - def validators(self) -> List[Callable]: + def validators(self) -> list[Callable]: """Defines the list of validators""" return [self.check_is_string, self.check_is_valid_country] diff --git a/tol_lab_share/message_properties/definitions/date_utc.py b/tol_lab_share/message_properties/definitions/date_utc.py index fce1f805..04675a8d 100644 --- a/tol_lab_share/message_properties/definitions/date_utc.py +++ b/tol_lab_share/message_properties/definitions/date_utc.py @@ -1,6 +1,6 @@ from .message_property import MessageProperty import logging -from typing import List, Callable +from typing import Callable from functools import cached_property from typing import Any @@ -13,7 +13,7 @@ class DateUtc(MessageProperty): """ @property - def validators(self) -> List[Callable]: + def validators(self) -> list[Callable]: """Defines the list of validators""" return [self.check_is_date_utc] diff --git a/tol_lab_share/message_properties/definitions/dict_input.py b/tol_lab_share/message_properties/definitions/dict_input.py index 17a03c64..4eff92ad 100644 --- a/tol_lab_share/message_properties/definitions/dict_input.py +++ b/tol_lab_share/message_properties/definitions/dict_input.py @@ -3,25 +3,25 @@ from tol_lab_share import error_codes import logging from tol_lab_share.message_properties.definitions.input import Input -from typing import List, Callable, Any, Union, Dict +from typing import Callable, Any, Union logger = logging.getLogger(__name__) -class DictInput(MessageProperty): +class dictInput(MessageProperty): """MessageProperty subclass to manage parsing of the access to the key of a - dictionary. The input can be a valid Dict or another MessageProperty that provides - as value a valid Dict. If the input is not a valid Dict, or if the key does not + dictionary. The input can be a valid dict or another MessageProperty that provides + as value a valid dict. If the input is not a valid dict, or if the key does not exist, it will trigger an error on validation. """ - def __init__(self, input: Union[MessageProperty, Dict[str, Any]], key: str): + def __init__(self, input: Union[MessageProperty, dict[str, Any]], key: str): """Constructor that will create an instance to manage the access of the input dictionary using the key provided as argument. Parameters: input (MessageProperty): The dictionary we want to access, wrapped inside another - MessageProperty class; normally it will be an Input or another DictInput. + MessageProperty class; normally it will be an Input or another dictInput. key (str): The key we want to access inside the dictionary provided. Returns: @@ -35,7 +35,7 @@ def __init__(self, input: Union[MessageProperty, Dict[str, Any]], key: str): self._key = key @property - def validators(self) -> List[Callable]: + def validators(self) -> list[Callable]: """Defines the list of validators""" return [self.check_has_key] diff --git a/tol_lab_share/message_properties/definitions/donor_id.py b/tol_lab_share/message_properties/definitions/donor_id.py index 5b859054..6963fa26 100644 --- a/tol_lab_share/message_properties/definitions/donor_id.py +++ b/tol_lab_share/message_properties/definitions/donor_id.py @@ -1,5 +1,5 @@ from .message_property import MessageProperty -from typing import List, Callable +from typing import Callable class DonorId(MessageProperty): @@ -10,6 +10,6 @@ class DonorId(MessageProperty): """ @property - def validators(self) -> List[Callable]: + def validators(self) -> list[Callable]: """Defines the list of validators""" return [self.check_is_string] diff --git a/tol_lab_share/message_properties/definitions/final_nano_drop.py b/tol_lab_share/message_properties/definitions/final_nano_drop.py index 342a9831..668da2f0 100644 --- a/tol_lab_share/message_properties/definitions/final_nano_drop.py +++ b/tol_lab_share/message_properties/definitions/final_nano_drop.py @@ -1,5 +1,5 @@ from .message_property import MessageProperty -from typing import List, Callable +from typing import Callable class FinalNanoDrop(MessageProperty): @@ -10,6 +10,6 @@ class FinalNanoDrop(MessageProperty): """ @property - def validators(self) -> List[Callable]: + def validators(self) -> list[Callable]: """Defines the list of validators""" return [self.check_is_string] diff --git a/tol_lab_share/message_properties/definitions/final_nano_drop_230.py b/tol_lab_share/message_properties/definitions/final_nano_drop_230.py index d6dfd63a..a4cdb474 100644 --- a/tol_lab_share/message_properties/definitions/final_nano_drop_230.py +++ b/tol_lab_share/message_properties/definitions/final_nano_drop_230.py @@ -1,5 +1,5 @@ from .message_property import MessageProperty -from typing import List, Callable +from typing import Callable class FinalNanoDrop230(MessageProperty): @@ -10,6 +10,6 @@ class FinalNanoDrop230(MessageProperty): """ @property - def validators(self) -> List[Callable]: + def validators(self) -> list[Callable]: """Defines the list of validators""" return [self.check_is_string] diff --git a/tol_lab_share/message_properties/definitions/final_nano_drop_280.py b/tol_lab_share/message_properties/definitions/final_nano_drop_280.py index 4e0e3fbd..08341171 100644 --- a/tol_lab_share/message_properties/definitions/final_nano_drop_280.py +++ b/tol_lab_share/message_properties/definitions/final_nano_drop_280.py @@ -1,5 +1,5 @@ from .message_property import MessageProperty -from typing import List, Callable +from typing import Callable class FinalNanoDrop280(MessageProperty): @@ -10,6 +10,6 @@ class FinalNanoDrop280(MessageProperty): """ @property - def validators(self) -> List[Callable]: + def validators(self) -> list[Callable]: """Defines the list of validators""" return [self.check_is_string] diff --git a/tol_lab_share/message_properties/definitions/genome_size.py b/tol_lab_share/message_properties/definitions/genome_size.py index c783d591..365211de 100644 --- a/tol_lab_share/message_properties/definitions/genome_size.py +++ b/tol_lab_share/message_properties/definitions/genome_size.py @@ -1,5 +1,5 @@ from .message_property import MessageProperty -from typing import List, Callable +from typing import Callable class GenomeSize(MessageProperty): @@ -10,6 +10,6 @@ class GenomeSize(MessageProperty): """ @property - def validators(self) -> List[Callable]: + def validators(self) -> list[Callable]: """Defines the list of validators""" return [self.check_is_string] diff --git a/tol_lab_share/message_properties/definitions/input.py b/tol_lab_share/message_properties/definitions/input.py index 4a7c1fdb..0b73801b 100644 --- a/tol_lab_share/message_properties/definitions/input.py +++ b/tol_lab_share/message_properties/definitions/input.py @@ -1,7 +1,7 @@ from .message_property import MessageProperty from functools import cached_property import logging -from typing import List, Callable +from typing import Callable logger = logging.getLogger(__name__) @@ -16,6 +16,6 @@ def value(self): return self._input @property - def validators(self) -> List[Callable]: + def validators(self) -> list[Callable]: """Defines the list of validators""" return [] diff --git a/tol_lab_share/message_properties/definitions/labware.py b/tol_lab_share/message_properties/definitions/labware.py index d450aa24..4295245e 100644 --- a/tol_lab_share/message_properties/definitions/labware.py +++ b/tol_lab_share/message_properties/definitions/labware.py @@ -1,10 +1,10 @@ import logging -from typing import Any, List +from typing import Any from tol_lab_share.constants import OUTPUT_TRACTION_MESSAGE_CONTAINER_TYPES from tol_lab_share.constants.input_create_labware_message import BARCODE, LABWARE_TYPE, SAMPLES from tol_lab_share.message_properties.definitions.barcode import Barcode -from tol_lab_share.message_properties.definitions.dict_input import DictInput +from tol_lab_share.message_properties.definitions.dict_input import dictInput from tol_lab_share.message_properties.definitions.labware_type import LabwareType from tol_lab_share.message_properties.definitions.sample import Sample from tol_lab_share.messages.output_feedback_message import OutputFeedbackMessage @@ -23,15 +23,15 @@ class Labware(MessageProperty): def __init__(self, input: MessageProperty): super().__init__(input) - self.add_property("labware_type", LabwareType(DictInput(input, LABWARE_TYPE))) - self.add_property("barcode", Barcode(DictInput(input, BARCODE))) + self.add_property("labware_type", LabwareType(dictInput(input, LABWARE_TYPE))) + self.add_property("barcode", Barcode(dictInput(input, BARCODE))) self.add_property("samples", self._parse_samples(input)) - def _parse_samples(self, input: MessageProperty) -> List[MessageProperty]: + def _parse_samples(self, input: MessageProperty) -> list[MessageProperty]: """Parses the samples section and creates a sample for each position.""" - samples_dict = DictInput(input, SAMPLES) + samples_dict = dictInput(input, SAMPLES) if samples_dict.validate(): - samples_list_dict: List[MessageProperty] = [] + samples_list_dict: list[MessageProperty] = [] for position in range(len(samples_dict.value)): sample = samples_dict.value[position] samples_list_dict.append(Sample(sample)) diff --git a/tol_lab_share/message_properties/definitions/labware_type.py b/tol_lab_share/message_properties/definitions/labware_type.py index 926f4f4a..1e3dc00f 100644 --- a/tol_lab_share/message_properties/definitions/labware_type.py +++ b/tol_lab_share/message_properties/definitions/labware_type.py @@ -1,7 +1,7 @@ from .message_property import MessageProperty from tol_lab_share import error_codes import logging -from typing import List, Callable +from typing import Callable logger = logging.getLogger(__name__) @@ -15,7 +15,7 @@ class LabwareType(MessageProperty): """ @property - def validators(self) -> List[Callable]: + def validators(self) -> list[Callable]: """Defines the list of validators""" return [self.check_is_string, self.check_labware_type] @@ -44,11 +44,11 @@ def pad_number(self, number: int) -> str: padded_number = f"0{padded_number}" return padded_number - def _locations_for_plate12x8_column_order(self) -> List[str]: + def _locations_for_plate12x8_column_order(self) -> list[str]: """It generates the list of all valid locations for a labware type following column order. Eg: ['A01', 'B01', 'C01', .... 'H12'] Returns: - List[str] with al the valid locations + list[str] with al the valid locations """ locations = [] for number in range(12): @@ -56,12 +56,12 @@ def _locations_for_plate12x8_column_order(self) -> List[str]: locations.append(f"{chr(letter_ord)}{self.pad_number(number+1)}") return locations - def valid_locations(self) -> List[str]: + def valid_locations(self) -> list[str]: """It returns the list of valid locations for the labware type. If the labware type is a tube, it returns ane empty list. If it is a plate 12x8 it returns all locations in column order. Returns: - List[str] with all the valid locations + list[str] with all the valid locations """ if self.value == "Plate12x8": return self._locations_for_plate12x8_column_order() diff --git a/tol_lab_share/message_properties/definitions/library_type.py b/tol_lab_share/message_properties/definitions/library_type.py index 49966e89..bfad9bcb 100644 --- a/tol_lab_share/message_properties/definitions/library_type.py +++ b/tol_lab_share/message_properties/definitions/library_type.py @@ -1,5 +1,5 @@ from .message_property import MessageProperty -from typing import List, Callable +from typing import Callable class LibraryType(MessageProperty): @@ -10,6 +10,6 @@ class LibraryType(MessageProperty): """ @property - def validators(self) -> List[Callable]: + def validators(self) -> list[Callable]: """Defines the list of validators""" return [self.check_is_string] diff --git a/tol_lab_share/message_properties/definitions/location.py b/tol_lab_share/message_properties/definitions/location.py index 4bb13608..146c0406 100644 --- a/tol_lab_share/message_properties/definitions/location.py +++ b/tol_lab_share/message_properties/definitions/location.py @@ -2,7 +2,7 @@ from tol_lab_share import error_codes from functools import cached_property from typing import Any -from typing import List, Callable +from typing import Callable import logging @@ -27,7 +27,7 @@ def value(self): return self.unpadded() @property - def validators(self) -> List[Callable]: + def validators(self) -> list[Callable]: """Defines the list of validators""" return [self.check_is_location] diff --git a/tol_lab_share/message_properties/definitions/message_property.py b/tol_lab_share/message_properties/definitions/message_property.py index 7a0febb0..19425381 100644 --- a/tol_lab_share/message_properties/definitions/message_property.py +++ b/tol_lab_share/message_properties/definitions/message_property.py @@ -3,7 +3,7 @@ import logging from functools import cached_property, singledispatchmethod from itertools import chain -from typing import Any, Callable, Dict, List, Optional, Union, cast +from typing import Any, Callable, Optional, Union, cast from tol_lab_share import error_codes from tol_lab_share.error_codes import ErrorCode @@ -27,8 +27,8 @@ def __init__(self, input: Any): input (MessagePropertyInterface) An instance of message property that we will use as base """ self._input = input - self._errors: List[ErrorCode] = [] - self._properties: Dict[str, Any] = {} + self._errors: list[ErrorCode] = [] + self._properties: dict[str, Any] = {} self.property_name: str | None = None self.property_source: MessageProperty | None = None self.property_position: int | None = None @@ -78,12 +78,12 @@ def _property_name_for_list_instance(self, property_name: str, pos: int) -> str: """ return f"{property_name}[{pos}]" - def _add_property_list(self, property_name: str, input: List[MessageProperty]) -> None: + def _add_property_list(self, property_name: str, input: list[MessageProperty]) -> None: """Given a property name and a list of message properties, it stores this list of MessageProperty as the value of the property and includes the position in each of them. Parameters: property_name (str) name of the property - input (List[MessagePropertyInterface]) list of message properties + input (list[MessagePropertyInterface]) list of message properties Returns: None """ @@ -96,11 +96,11 @@ def _add_property_list(self, property_name: str, input: List[MessageProperty]) - instance.property_type = PROPERTY_TYPE_ARRAY self._properties[property_name].append(instance) - def add_property(self, property_name: str, input: Union[MessageProperty, List[MessageProperty]]) -> None: + def add_property(self, property_name: str, input: Union[MessageProperty, list[MessageProperty]]) -> None: """Given an property name and an input it adds the input as the value of the property Parameters: property_name (str) name of the property - input (MessagePropertyInterface | List[MessagePropertyInterface]) property or list of + input (MessagePropertyInterface | list[MessagePropertyInterface]) property or list of properties that we want to add for the name provided Returns: None @@ -150,7 +150,7 @@ def add_to_message_property(self, message_property: MessageProperty) -> None: property.add_to_message_property(message_property) @property - def errors(self) -> List[ErrorCode]: + def errors(self) -> list[ErrorCode]: """Returns an aggregation of all errors from the current instance and all errors from the properties it contains. """ @@ -164,7 +164,7 @@ def add_error(self, error: ErrorCode) -> None: self._errors.append(error) @property - def validators(self) -> List[Callable]: + def validators(self) -> list[Callable]: """Defines the list of validators""" return [] @@ -292,11 +292,11 @@ def _validate_instance(self) -> bool: return all(list([validator() for validator in self.validators])) @property - def _errors_properties(self) -> List[ErrorCode]: + def _errors_properties(self) -> list[ErrorCode]: """Returns a list that contains all the errors for the properties inside this instance. Returns: - List[ErrorCode] with all the errors from the properties + list[ErrorCode] with all the errors from the properties """ error_list = [] for property in self._properties_instances: @@ -306,11 +306,11 @@ def _errors_properties(self) -> List[ErrorCode]: return error_list @cached_property - def _properties_instances(self) -> List[MessageProperty]: + def _properties_instances(self) -> list[MessageProperty]: """Returns a list that contains all the properties inside this instance. Returns: - List[MessagePropertyInterface] with all the properties + list[MessagePropertyInterface] with all the properties """ prop_list = [] for property in list(self._properties.values()): diff --git a/tol_lab_share/message_properties/definitions/post_spri_concentration.py b/tol_lab_share/message_properties/definitions/post_spri_concentration.py index 76ca6176..58d706ee 100644 --- a/tol_lab_share/message_properties/definitions/post_spri_concentration.py +++ b/tol_lab_share/message_properties/definitions/post_spri_concentration.py @@ -1,5 +1,5 @@ from .message_property import MessageProperty -from typing import List, Callable +from typing import Callable class PostSPRIConcentration(MessageProperty): @@ -10,6 +10,6 @@ class PostSPRIConcentration(MessageProperty): """ @property - def validators(self) -> List[Callable]: + def validators(self) -> list[Callable]: """Defines the list of validators""" return [self.check_is_string] diff --git a/tol_lab_share/message_properties/definitions/post_spri_volume.py b/tol_lab_share/message_properties/definitions/post_spri_volume.py index 610be591..a6290bc1 100644 --- a/tol_lab_share/message_properties/definitions/post_spri_volume.py +++ b/tol_lab_share/message_properties/definitions/post_spri_volume.py @@ -1,5 +1,5 @@ from .message_property import MessageProperty -from typing import List, Callable +from typing import Callable class PostSPRIVolume(MessageProperty): @@ -10,6 +10,6 @@ class PostSPRIVolume(MessageProperty): """ @property - def validators(self) -> List[Callable]: + def validators(self) -> list[Callable]: """Defines the list of validators""" return [self.check_is_string] diff --git a/tol_lab_share/message_properties/definitions/priority_level.py b/tol_lab_share/message_properties/definitions/priority_level.py index 5c05bd3e..c231b1c8 100644 --- a/tol_lab_share/message_properties/definitions/priority_level.py +++ b/tol_lab_share/message_properties/definitions/priority_level.py @@ -1,4 +1,4 @@ -from typing import Callable, List +from typing import Callable from .message_property import MessageProperty @@ -12,6 +12,6 @@ class PriorityLevel(MessageProperty): """ @property - def validators(self) -> List[Callable]: + def validators(self) -> list[Callable]: """Defines the list of validators""" return [self.check_is_string] diff --git a/tol_lab_share/message_properties/definitions/public_name.py b/tol_lab_share/message_properties/definitions/public_name.py index 80922ff8..abba6230 100644 --- a/tol_lab_share/message_properties/definitions/public_name.py +++ b/tol_lab_share/message_properties/definitions/public_name.py @@ -1,5 +1,5 @@ from .message_property import MessageProperty -from typing import List, Callable +from typing import Callable class PublicName(MessageProperty): @@ -10,6 +10,6 @@ class PublicName(MessageProperty): """ @property - def validators(self) -> List[Callable]: + def validators(self) -> list[Callable]: """Defines the list of validators""" return [self.check_is_string] diff --git a/tol_lab_share/message_properties/definitions/sample.py b/tol_lab_share/message_properties/definitions/sample.py index 64271ff5..42a84c32 100644 --- a/tol_lab_share/message_properties/definitions/sample.py +++ b/tol_lab_share/message_properties/definitions/sample.py @@ -36,7 +36,7 @@ from tol_lab_share.message_properties.definitions.cost_code import CostCode from tol_lab_share.message_properties.definitions.country_of_origin import CountryOfOrigin from tol_lab_share.message_properties.definitions.date_utc import DateUtc -from tol_lab_share.message_properties.definitions.dict_input import DictInput +from tol_lab_share.message_properties.definitions.dict_input import dictInput from tol_lab_share.message_properties.definitions.donor_id import DonorId from tol_lab_share.message_properties.definitions.final_nano_drop import FinalNanoDrop from tol_lab_share.message_properties.definitions.final_nano_drop_230 import FinalNanoDrop230 @@ -68,39 +68,39 @@ class Sample(MessageProperty): def __init__(self, input: Any): super().__init__(input) - self.add_property("cost_code", CostCode(DictInput(input, SAMPLE_COST_CODE))) - self.add_property("study_uuid", Uuid(DictInput(input, SAMPLE_STUDY_UUID))) - self.add_property("common_name", CommonName(DictInput(input, SAMPLE_COMMON_NAME))) - self.add_property("concentration", Concentration(DictInput(input, SAMPLE_CONCENTRATION))) - self.add_property("volume", Volume(DictInput(input, SAMPLE_VOLUME))) + self.add_property("cost_code", CostCode(dictInput(input, SAMPLE_COST_CODE))) + self.add_property("study_uuid", Uuid(dictInput(input, SAMPLE_STUDY_UUID))) + self.add_property("common_name", CommonName(dictInput(input, SAMPLE_COMMON_NAME))) + self.add_property("concentration", Concentration(dictInput(input, SAMPLE_CONCENTRATION))) + self.add_property("volume", Volume(dictInput(input, SAMPLE_VOLUME))) self.add_property( "country_of_origin", - CountryOfOrigin(DictInput(input, SAMPLE_COUNTRY_OF_ORIGIN)), + CountryOfOrigin(dictInput(input, SAMPLE_COUNTRY_OF_ORIGIN)), ) - self.add_property("donor_id", DonorId(DictInput(input, SAMPLE_DONOR_ID))) - self.add_property("taxon_id", TaxonId(DictInput(input, SAMPLE_TAXON_ID))) - self.add_property("library_type", LibraryType(DictInput(input, SAMPLE_LIBRARY_TYPE))) - self.add_property("location", Location(DictInput(input, SAMPLE_LOCATION))) - self.add_property("public_name", PublicName(DictInput(input, SAMPLE_PUBLIC_NAME))) - self.add_property("sanger_sample_id", SangerSampleId(DictInput(input, SAMPLE_SANGER_SAMPLE_ID))) + self.add_property("donor_id", DonorId(dictInput(input, SAMPLE_DONOR_ID))) + self.add_property("taxon_id", TaxonId(dictInput(input, SAMPLE_TAXON_ID))) + self.add_property("library_type", LibraryType(dictInput(input, SAMPLE_LIBRARY_TYPE))) + self.add_property("location", Location(dictInput(input, SAMPLE_LOCATION))) + self.add_property("public_name", PublicName(dictInput(input, SAMPLE_PUBLIC_NAME))) + self.add_property("sanger_sample_id", SangerSampleId(dictInput(input, SAMPLE_SANGER_SAMPLE_ID))) self.add_property( "scientific_name", - ScientificNameFromTaxonId(TaxonId(DictInput(input, SAMPLE_SANGER_TAXON_ID))), + ScientificNameFromTaxonId(TaxonId(dictInput(input, SAMPLE_SANGER_TAXON_ID))), ) - self.add_property("uuid", Uuid(DictInput(input, SAMPLE_SANGER_UUID))) - self.add_property("accession_number", AccessionNumber(DictInput(input, SAMPLE_ACCESSION_NUMBER))) - self.add_property("genome_size", GenomeSize(DictInput(input, SAMPLE_GENOME_SIZE))) - self.add_property("collection_date", DateUtc(DictInput(input, SAMPLE_COLLECTION_DATE))) - self.add_property("supplier_sample_name", SupplierSampleName(DictInput(input, SUPPLIER_SAMPLE_NAME))) + self.add_property("uuid", Uuid(dictInput(input, SAMPLE_SANGER_UUID))) + self.add_property("accession_number", AccessionNumber(dictInput(input, SAMPLE_ACCESSION_NUMBER))) + self.add_property("genome_size", GenomeSize(dictInput(input, SAMPLE_GENOME_SIZE))) + self.add_property("collection_date", DateUtc(dictInput(input, SAMPLE_COLLECTION_DATE))) + self.add_property("supplier_sample_name", SupplierSampleName(dictInput(input, SUPPLIER_SAMPLE_NAME))) self.add_property( - "sheared_femto_fragment_size", ShearedFemtoFragmentSize(DictInput(input, SHEARED_FEMTO_FRAGMENT_SIZE)) + "sheared_femto_fragment_size", ShearedFemtoFragmentSize(dictInput(input, SHEARED_FEMTO_FRAGMENT_SIZE)) ) - self.add_property("post_spri_concentration", PostSPRIConcentration(DictInput(input, POST_SPRI_CONCENTRATION))) - self.add_property("post_spri_volume", PostSPRIVolume(DictInput(input, POST_SPRI_VOLUME))) - self.add_property("final_nano_drop_280", FinalNanoDrop280(DictInput(input, FINAL_NANODROP_280))) - self.add_property("final_nano_drop_230", FinalNanoDrop230(DictInput(input, FINAL_NANODROP_230))) - self.add_property("final_nano_drop", FinalNanoDrop(DictInput(input, FINAL_NANODROP))) - self.add_property("shearing_qc_comments", ShearingAndQCComments(DictInput(input, SHEARING_QC_COMMENTS))) - self.add_property("date_submitted_utc", DateUtc(DictInput(input, DATE_SUBMITTED_UTC))) - self.add_property("priority_level", PriorityLevel(DictInput(input, PRIORITY_LEVEL))) + self.add_property("post_spri_concentration", PostSPRIConcentration(dictInput(input, POST_SPRI_CONCENTRATION))) + self.add_property("post_spri_volume", PostSPRIVolume(dictInput(input, POST_SPRI_VOLUME))) + self.add_property("final_nano_drop_280", FinalNanoDrop280(dictInput(input, FINAL_NANODROP_280))) + self.add_property("final_nano_drop_230", FinalNanoDrop230(dictInput(input, FINAL_NANODROP_230))) + self.add_property("final_nano_drop", FinalNanoDrop(dictInput(input, FINAL_NANODROP))) + self.add_property("shearing_qc_comments", ShearingAndQCComments(dictInput(input, SHEARING_QC_COMMENTS))) + self.add_property("date_submitted_utc", DateUtc(dictInput(input, DATE_SUBMITTED_UTC))) + self.add_property("priority_level", PriorityLevel(dictInput(input, PRIORITY_LEVEL))) diff --git a/tol_lab_share/message_properties/definitions/sanger_sample_id.py b/tol_lab_share/message_properties/definitions/sanger_sample_id.py index 3d18bc85..fa975e22 100644 --- a/tol_lab_share/message_properties/definitions/sanger_sample_id.py +++ b/tol_lab_share/message_properties/definitions/sanger_sample_id.py @@ -1,5 +1,5 @@ from .message_property import MessageProperty -from typing import List, Callable +from typing import Callable class SangerSampleId(MessageProperty): @@ -10,6 +10,6 @@ class SangerSampleId(MessageProperty): """ @property - def validators(self) -> List[Callable]: + def validators(self) -> list[Callable]: """Defines the list of validators""" return [self.check_is_string] diff --git a/tol_lab_share/message_properties/definitions/scientific_name_from_taxon_id.py b/tol_lab_share/message_properties/definitions/scientific_name_from_taxon_id.py index cd17be18..2f0b8e58 100644 --- a/tol_lab_share/message_properties/definitions/scientific_name_from_taxon_id.py +++ b/tol_lab_share/message_properties/definitions/scientific_name_from_taxon_id.py @@ -3,10 +3,10 @@ from functools import cached_property from tol_lab_share.helpers import get_config from tol_lab_share import error_codes -from typing import Any, Dict, cast -from typing import List, Callable +from typing import Any, cast +from typing import Callable -CACHE_TAXON_IDS: Dict[str, str] = {} +CACHE_TAXON_IDS: dict[str, str] = {} MAX_CACHE_SIZE = 1000 @@ -16,7 +16,7 @@ class ScientificNameFromTaxonId(MessageProperty): """ @property - def validators(self) -> List[Callable]: + def validators(self) -> list[Callable]: """Checks the input is a string""" return [self.check_is_string] diff --git a/tol_lab_share/message_properties/definitions/sheared_femto_fragment_size.py b/tol_lab_share/message_properties/definitions/sheared_femto_fragment_size.py index db9d829c..cc60b0ee 100644 --- a/tol_lab_share/message_properties/definitions/sheared_femto_fragment_size.py +++ b/tol_lab_share/message_properties/definitions/sheared_femto_fragment_size.py @@ -1,5 +1,5 @@ from .message_property import MessageProperty -from typing import List, Callable +from typing import Callable class ShearedFemtoFragmentSize(MessageProperty): @@ -10,6 +10,6 @@ class ShearedFemtoFragmentSize(MessageProperty): """ @property - def validators(self) -> List[Callable]: + def validators(self) -> list[Callable]: """Defines the list of validators""" return [self.check_is_string] diff --git a/tol_lab_share/message_properties/definitions/shearing_qc_comments.py b/tol_lab_share/message_properties/definitions/shearing_qc_comments.py index 22c01a23..882898f5 100644 --- a/tol_lab_share/message_properties/definitions/shearing_qc_comments.py +++ b/tol_lab_share/message_properties/definitions/shearing_qc_comments.py @@ -1,5 +1,5 @@ from .message_property import MessageProperty -from typing import List, Callable +from typing import Callable class ShearingAndQCComments(MessageProperty): @@ -10,6 +10,6 @@ class ShearingAndQCComments(MessageProperty): """ @property - def validators(self) -> List[Callable]: + def validators(self) -> list[Callable]: """Defines the list of validators""" return [self.check_is_string] diff --git a/tol_lab_share/message_properties/definitions/supplier_sample_name.py b/tol_lab_share/message_properties/definitions/supplier_sample_name.py index 53f576cb..034ea856 100644 --- a/tol_lab_share/message_properties/definitions/supplier_sample_name.py +++ b/tol_lab_share/message_properties/definitions/supplier_sample_name.py @@ -1,5 +1,5 @@ from .message_property import MessageProperty -from typing import List, Callable +from typing import Callable class SupplierSampleName(MessageProperty): @@ -10,6 +10,6 @@ class SupplierSampleName(MessageProperty): """ @property - def validators(self) -> List[Callable]: + def validators(self) -> list[Callable]: """Defines the list of validators""" return [self.check_is_string] diff --git a/tol_lab_share/message_properties/definitions/taxon_id.py b/tol_lab_share/message_properties/definitions/taxon_id.py index 20b0a23d..f4108a1b 100644 --- a/tol_lab_share/message_properties/definitions/taxon_id.py +++ b/tol_lab_share/message_properties/definitions/taxon_id.py @@ -1,5 +1,5 @@ from .message_property import MessageProperty -from typing import List, Callable +from typing import Callable class TaxonId(MessageProperty): @@ -10,6 +10,6 @@ class TaxonId(MessageProperty): """ @property - def validators(self) -> List[Callable]: + def validators(self) -> list[Callable]: """Defines the list of validators""" return [self.check_is_string] diff --git a/tol_lab_share/message_properties/definitions/uuid.py b/tol_lab_share/message_properties/definitions/uuid.py index 45f5bdfd..aba5d656 100644 --- a/tol_lab_share/message_properties/definitions/uuid.py +++ b/tol_lab_share/message_properties/definitions/uuid.py @@ -5,7 +5,7 @@ from typing import Optional, Any from functools import cached_property import logging -from typing import List, Callable +from typing import Callable logger = logging.getLogger(__name__) @@ -16,7 +16,7 @@ class Uuid(MessageProperty): The uuid has to be binary defined in version UUID v4 and encoded using utf-8.""" @property - def validators(self) -> List[Callable]: + def validators(self) -> list[Callable]: """Defines the list of validators""" return [self.check_is_binary, self.check_is_uuid] diff --git a/tol_lab_share/message_properties/definitions/volume.py b/tol_lab_share/message_properties/definitions/volume.py index 20c2ef29..34494b30 100644 --- a/tol_lab_share/message_properties/definitions/volume.py +++ b/tol_lab_share/message_properties/definitions/volume.py @@ -1,5 +1,5 @@ from .message_property import MessageProperty -from typing import List, Callable +from typing import Callable class Volume(MessageProperty): @@ -10,6 +10,6 @@ class Volume(MessageProperty): """ @property - def validators(self) -> List[Callable]: + def validators(self) -> list[Callable]: """Defines the list of validators""" return [self.check_is_string] diff --git a/tol_lab_share/messages/input_create_labware_message.py b/tol_lab_share/messages/input_create_labware_message.py index e613deb5..06b43586 100644 --- a/tol_lab_share/messages/input_create_labware_message.py +++ b/tol_lab_share/messages/input_create_labware_message.py @@ -9,7 +9,7 @@ from tol_lab_share.message_properties.definitions.labware import Labware from tol_lab_share.message_properties.definitions.date_utc import DateUtc from tol_lab_share.message_properties.definitions.message_property import MessageProperty -from tol_lab_share.message_properties.definitions.dict_input import DictInput +from tol_lab_share.message_properties.definitions.dict_input import dictInput import logging @@ -30,9 +30,9 @@ def __init__(self, m: RabbitMessage): super().__init__(m) self._message = m.message - self.add_property("message_uuid", MessageUuid(DictInput(self._message, MESSAGE_UUID))) - self.add_property("labware", Labware(DictInput(self._message, LABWARE))) - self.add_property("create_date_utc", DateUtc(DictInput(self._message, CREATED_DATE_UTC))) + self.add_property("message_uuid", MessageUuid(dictInput(self._message, MESSAGE_UUID))) + self.add_property("labware", Labware(dictInput(self._message, LABWARE))) + self.add_property("create_date_utc", DateUtc(dictInput(self._message, CREATED_DATE_UTC))) @singledispatchmethod def add_to_message_property(self, message_property: MessageProperty) -> None: diff --git a/tol_lab_share/messages/output_feedback_message.py b/tol_lab_share/messages/output_feedback_message.py index be3169de..fc5064ae 100644 --- a/tol_lab_share/messages/output_feedback_message.py +++ b/tol_lab_share/messages/output_feedback_message.py @@ -9,7 +9,7 @@ import logging from tol_lab_share.helpers import get_config from tol_lab_share.message_properties.definitions.input import Input -from typing import Dict, Any +from typing import Any from lab_share_lib.rabbit.basic_publisher import BasicPublisher from lab_share_lib.rabbit.schema_registry import SchemaRegistry @@ -31,7 +31,7 @@ def __init__(self): @property def validators(self): - """List of validators to apply to the message""" + """list of validators to apply to the message""" return [self.check_defined_keys, self.check_errors_correct] @property @@ -39,8 +39,8 @@ def origin(self): """ "Name of the origin for this property, set as a constant value.""" return "OutputFeedbackMessage" - def to_json(self) -> Dict[str, Any]: - """Returns a Dict with the JSON-like representation of the message.""" + def to_json(self) -> dict[str, Any]: + """Returns a dict with the JSON-like representation of the message.""" return { "sourceMessageUuid": str(self.source_message_uuid), "countOfTotalSamples": self.count_of_total_samples, @@ -49,7 +49,7 @@ def to_json(self) -> Dict[str, Any]: "errors": [error.json() for error in self.errors], } - def encoder_config_for(self, encoder_type_selection: str) -> Dict[str, Any]: + def encoder_config_for(self, encoder_type_selection: str) -> dict[str, Any]: """Returns a config object with the encoder class and encoder type depending of the encoder selected.""" if encoder_type_selection == "json": return {"encoder_class": AvroEncoderJson, "encoder_type": RABBITMQ_HEADER_VALUE_ENCODER_TYPE_JSON} diff --git a/tol_lab_share/messages/output_traction_message.py b/tol_lab_share/messages/output_traction_message.py index 2dd45e11..aeea097f 100644 --- a/tol_lab_share/messages/output_traction_message.py +++ b/tol_lab_share/messages/output_traction_message.py @@ -169,7 +169,7 @@ def origin(self) -> str: @property def validators(self) -> list[Callable]: - """List of validators to check the message is correct before sending""" + """list of validators to check the message is correct before sending""" return [self.check_has_requests, self.check_requests_have_all_content, self.check_no_errors] def create_request(self) -> OutputTractionMessageRequest: @@ -185,13 +185,13 @@ def request_attributes(self) -> list[dict[str, Any]]: """Returns a list with all the payloads for every request Returns: - List[Dict[str,Any]] with all payloads for all the requests + list[dict[str,Any]] with all payloads for all the requests """ return [request.serializer().payload() for request in self._requests] @property def errors(self) -> list[ErrorCode]: - """List of errors defined for this message""" + """list of errors defined for this message""" return self._errors def check_has_requests(self) -> bool: diff --git a/tol_lab_share/messages/traction_qc_message.py b/tol_lab_share/messages/traction_qc_message.py index f8e3a327..11504980 100644 --- a/tol_lab_share/messages/traction_qc_message.py +++ b/tol_lab_share/messages/traction_qc_message.py @@ -1,7 +1,7 @@ from functools import singledispatchmethod import logging from json import dumps -from typing import Any, Callable, Dict, List +from typing import Any, Callable from requests import codes, post @@ -66,7 +66,7 @@ def __init__(self, instance: TractionQcMessageRequest): """ self.instance = instance - def payload(self) -> Dict[str, Any]: + def payload(self) -> dict[str, Any]: """Constructs the payload with qc data. Returns: Dic[str,str] with all the required payload information for Traction @@ -85,7 +85,7 @@ def payload(self) -> Dict[str, Any]: } return self.clear_empty_value_keys(obj) - def clear_empty_value_keys(self, obj: Dict[str, Any]) -> Dict[str, Any]: + def clear_empty_value_keys(self, obj: dict[str, Any]) -> dict[str, Any]: return {k: v for k, v in obj.items() if v} @@ -95,7 +95,7 @@ class TractionQcMessage(MessageProperty): def __init__(self): """Resets initial data""" super().__init__(Input(self)) - self._requests: Dict[int, TractionQcMessageRequest] = {} + self._requests: dict[int, TractionQcMessageRequest] = {} self._sent = False self._validate_certificates = get_config().CERTIFICATES_VALIDATION_ENABLED @@ -106,8 +106,8 @@ def origin(self) -> str: return "TractionQcMessage" @property - def validators(self) -> List[Callable]: - """List of validators to check the message is correct before sending""" + def validators(self) -> list[Callable]: + """list of validators to check the message is correct before sending""" return [self.check_has_requests, self.check_requests_have_all_content, self.check_no_errors] def requests(self, position: int) -> TractionQcMessageRequest: @@ -125,16 +125,16 @@ def requests(self, position: int) -> TractionQcMessageRequest: return self._requests[position] - def request_attributes(self) -> List[Dict[str, Any]]: + def request_attributes(self) -> list[dict[str, Any]]: """Returns a list with all the qc data payload for every request Returns: - List[Dict[str,Any]] with all payload for all the requests + list[dict[str,Any]] with all payload for all the requests """ return [self._requests[position].serializer().payload() for position in range(len(self._requests))] @property - def errors(self) -> List[ErrorCode]: - """List of errors defined for this message""" + def errors(self) -> list[ErrorCode]: + """list of errors defined for this message""" return self._errors def check_has_requests(self) -> bool: @@ -166,7 +166,7 @@ def check_requests_have_all_content(self) -> bool: self.trigger_error(error_codes.ERROR_26_TRACTION_QC_MESSAGE_REQUESTS_HAVE_MISSING_DATA) return False - def payload(self) -> Dict[str, Any]: + def payload(self) -> dict[str, Any]: """Returns the valid payload to send to traction""" return { "data": { diff --git a/tol_lab_share/types.py b/tol_lab_share/types.py index 387fa823..50e73418 100644 --- a/tol_lab_share/types.py +++ b/tol_lab_share/types.py @@ -1,11 +1,11 @@ from types import ModuleType -from typing import Dict, Any, Literal +from typing import Any, Literal class Config(ModuleType): """ModuleType class for the app config.""" - LOGGING: Dict[str, Any] + LOGGING: dict[str, Any] # RabbitMQ RABBITMQ_HOST: str @@ -24,7 +24,7 @@ class Config(ModuleType): REDPANDA_BASE_URI: str REDPANDA_API_KEY: str - PROCESSORS: Dict[str, str] + PROCESSORS: dict[str, str] CERTIFICATES_VALIDATION_ENABLED: bool TRACTION_URL: str From 55ce33460e15261d38619deec08c1e36fb514b72 Mon Sep 17 00:00:00 2001 From: Stuart McHattie Date: Fri, 9 Feb 2024 17:56:15 +0000 Subject: [PATCH 21/40] Remove remaining Optionals --- .../message_properties/definitions/message_property.py | 4 ++-- tol_lab_share/message_properties/definitions/uuid.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tol_lab_share/message_properties/definitions/message_property.py b/tol_lab_share/message_properties/definitions/message_property.py index 19425381..51cd6535 100644 --- a/tol_lab_share/message_properties/definitions/message_property.py +++ b/tol_lab_share/message_properties/definitions/message_property.py @@ -3,7 +3,7 @@ import logging from functools import cached_property, singledispatchmethod from itertools import chain -from typing import Any, Callable, Optional, Union, cast +from typing import Any, Callable, Union, cast from tol_lab_share import error_codes from tol_lab_share.error_codes import ErrorCode @@ -128,7 +128,7 @@ def field(self): """Alias to property name""" return self.property_name - def trigger_error(self, error_code: ErrorCode, text: Optional[str] = None) -> None: + def trigger_error(self, error_code: ErrorCode, text: str | None = None) -> None: """Given an error instance, it performs the action associated with it and after that it adds the error to the list of errors defined for the current property. Parameters: diff --git a/tol_lab_share/message_properties/definitions/uuid.py b/tol_lab_share/message_properties/definitions/uuid.py index aba5d656..9650a62b 100644 --- a/tol_lab_share/message_properties/definitions/uuid.py +++ b/tol_lab_share/message_properties/definitions/uuid.py @@ -2,7 +2,7 @@ from uuid import UUID from tol_lab_share import error_codes -from typing import Optional, Any +from typing import Any from functools import cached_property import logging from typing import Callable @@ -54,6 +54,6 @@ def check_is_uuid(self): return False @cached_property - def value(self) -> Optional[Any]: + def value(self) -> Any: """Returns the string representation of the uuid""" return self._input.value.decode("utf-8") From be40f3849db9abd788eb4f7b94366e0651a17249 Mon Sep 17 00:00:00 2001 From: Stuart McHattie Date: Fri, 9 Feb 2024 17:56:56 +0000 Subject: [PATCH 22/40] Remove Unions --- tol_lab_share/message_properties/definitions/dict_input.py | 4 ++-- .../message_properties/definitions/message_property.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tol_lab_share/message_properties/definitions/dict_input.py b/tol_lab_share/message_properties/definitions/dict_input.py index 4eff92ad..f89facb2 100644 --- a/tol_lab_share/message_properties/definitions/dict_input.py +++ b/tol_lab_share/message_properties/definitions/dict_input.py @@ -3,7 +3,7 @@ from tol_lab_share import error_codes import logging from tol_lab_share.message_properties.definitions.input import Input -from typing import Callable, Any, Union +from typing import Callable, Any logger = logging.getLogger(__name__) @@ -15,7 +15,7 @@ class dictInput(MessageProperty): exist, it will trigger an error on validation. """ - def __init__(self, input: Union[MessageProperty, dict[str, Any]], key: str): + def __init__(self, input: MessageProperty | dict[str, Any], key: str): """Constructor that will create an instance to manage the access of the input dictionary using the key provided as argument. diff --git a/tol_lab_share/message_properties/definitions/message_property.py b/tol_lab_share/message_properties/definitions/message_property.py index 51cd6535..422647c5 100644 --- a/tol_lab_share/message_properties/definitions/message_property.py +++ b/tol_lab_share/message_properties/definitions/message_property.py @@ -3,7 +3,7 @@ import logging from functools import cached_property, singledispatchmethod from itertools import chain -from typing import Any, Callable, Union, cast +from typing import Any, Callable, cast from tol_lab_share import error_codes from tol_lab_share.error_codes import ErrorCode @@ -96,7 +96,7 @@ def _add_property_list(self, property_name: str, input: list[MessageProperty]) - instance.property_type = PROPERTY_TYPE_ARRAY self._properties[property_name].append(instance) - def add_property(self, property_name: str, input: Union[MessageProperty, list[MessageProperty]]) -> None: + def add_property(self, property_name: str, input: MessageProperty | list[MessageProperty]) -> None: """Given an property name and an input it adds the input as the value of the property Parameters: property_name (str) name of the property From f6d7d297c5389fbc59e804037f877d3cd5740dc8 Mon Sep 17 00:00:00 2001 From: Stuart McHattie Date: Mon, 12 Feb 2024 10:28:37 +0000 Subject: [PATCH 23/40] Change TractionQcMessage requests to a list Code elsewhere assumes the indices are non-sparse so we should be using a list --- .../definitions/test_labware.py | 18 +++---- tests/messages/test_traction_qc_message.py | 48 ++++++++++--------- .../message_properties/definitions/labware.py | 27 +++++------ .../messages/output_traction_message.py | 12 ++--- tol_lab_share/messages/traction_qc_message.py | 31 +++++------- 5 files changed, 67 insertions(+), 69 deletions(-) diff --git a/tests/message_properties/definitions/test_labware.py b/tests/message_properties/definitions/test_labware.py index 54ac46e6..43b35b36 100644 --- a/tests/message_properties/definitions/test_labware.py +++ b/tests/message_properties/definitions/test_labware.py @@ -182,12 +182,12 @@ def test_add_to_traction_qc_message(valid_sample): traction_qc_message = TractionQcMessage() instance.add_to_message_property(traction_qc_message) - assert traction_qc_message.requests(0).sanger_sample_id == "cichlid_pacbio8196429" - assert traction_qc_message.requests(0).container_barcode == "BARCODE001" - assert traction_qc_message.requests(0).sheared_femto_fragment_size == "8" - assert traction_qc_message.requests(0).post_spri_concentration == "9" - assert traction_qc_message.requests(0).post_spri_volume == "10" - assert traction_qc_message.requests(0).final_nano_drop_280 == "200" - assert traction_qc_message.requests(0).final_nano_drop_230 == "200" - assert traction_qc_message.requests(0).final_nano_drop == "150" - assert traction_qc_message.requests(0).shearing_qc_comments == "" + assert traction_qc_message._requests[0].sanger_sample_id == "cichlid_pacbio8196429" + assert traction_qc_message._requests[0].container_barcode == "BARCODE001" + assert traction_qc_message._requests[0].sheared_femto_fragment_size == "8" + assert traction_qc_message._requests[0].post_spri_concentration == "9" + assert traction_qc_message._requests[0].post_spri_volume == "10" + assert traction_qc_message._requests[0].final_nano_drop_280 == "200" + assert traction_qc_message._requests[0].final_nano_drop_230 == "200" + assert traction_qc_message._requests[0].final_nano_drop == "150" + assert traction_qc_message._requests[0].shearing_qc_comments == "" diff --git a/tests/messages/test_traction_qc_message.py b/tests/messages/test_traction_qc_message.py index e31d02c5..678e2533 100644 --- a/tests/messages/test_traction_qc_message.py +++ b/tests/messages/test_traction_qc_message.py @@ -22,34 +22,38 @@ class TestTractionQcMessage: @pytest.fixture() def valid_traction_qc_message(self): traction_qc_message = TractionQcMessage() - traction_qc_message.requests(0).sanger_sample_id = "sanger_sample_id_DDD" - traction_qc_message.requests(0).container_barcode = "FD20706500" - traction_qc_message.requests(0).sheared_femto_fragment_size = "5" - traction_qc_message.requests(0).post_spri_concentration = "10" - traction_qc_message.requests(0).post_spri_volume = "20" - traction_qc_message.requests(0).final_nano_drop_280 = "280" - traction_qc_message.requests(0).final_nano_drop_230 = "230" - traction_qc_message.requests(0).final_nano_drop = "200" - traction_qc_message.requests(0).shearing_qc_comments = "Comments" - traction_qc_message.requests(0).date_submitted_utc = datetime.now().timestamp() * 1000 - - traction_qc_message.requests(1).sanger_sample_id = "sanger_sample_id_DDD2" - traction_qc_message.requests(1).container_barcode = "FD20706501" - traction_qc_message.requests(1).sheared_femto_fragment_size = "9" - traction_qc_message.requests(1).post_spri_concentration = "10" - traction_qc_message.requests(1).post_spri_volume = "30" - traction_qc_message.requests(1).final_nano_drop_280 = "180" - traction_qc_message.requests(1).final_nano_drop_230 = "130" - traction_qc_message.requests(1).final_nano_drop = "100" - traction_qc_message.requests(1).shearing_qc_comments = "" - traction_qc_message.requests(1).date_submitted_utc = datetime.now().timestamp() * 1000 + + request = traction_qc_message.create_request() + request.sanger_sample_id = "sanger_sample_id_DDD" + request.container_barcode = "FD20706500" + request.sheared_femto_fragment_size = "5" + request.post_spri_concentration = "10" + request.post_spri_volume = "20" + request.final_nano_drop_280 = "280" + request.final_nano_drop_230 = "230" + request.final_nano_drop = "200" + request.shearing_qc_comments = "Comments" + request.date_submitted_utc = datetime.now().timestamp() * 1000 + + request = traction_qc_message.create_request() + request.sanger_sample_id = "sanger_sample_id_DDD2" + request.container_barcode = "FD20706501" + request.sheared_femto_fragment_size = "9" + request.post_spri_concentration = "10" + request.post_spri_volume = "30" + request.final_nano_drop_280 = "180" + request.final_nano_drop_230 = "130" + request.final_nano_drop = "100" + request.shearing_qc_comments = "" + request.date_submitted_utc = datetime.now().timestamp() * 1000 return traction_qc_message @pytest.fixture() def invalid_traction_qc_message(self): traction_qc_message = TractionQcMessage() - traction_qc_message.requests(0).sheared_femto_fragment_size = "5" + request = traction_qc_message.create_request() + request.sheared_femto_fragment_size = "5" return traction_qc_message diff --git a/tol_lab_share/message_properties/definitions/labware.py b/tol_lab_share/message_properties/definitions/labware.py index 4295245e..a1cc93eb 100644 --- a/tol_lab_share/message_properties/definitions/labware.py +++ b/tol_lab_share/message_properties/definitions/labware.py @@ -89,8 +89,7 @@ def _(self, message: OutputTractionMessage) -> None: """ super().add_to_message_property(message) - for sample_pos in range(len(self.properties("samples"))): - sample = self.properties("samples")[sample_pos] + for sample in self.properties("samples"): request = message.create_request() request.cost_code = sample.properties("cost_code").value request.study_uuid = sample.properties("study_uuid").value @@ -120,17 +119,17 @@ def _(self, message: TractionQcMessage) -> None: """ super().add_to_message_property(message) - for sample_pos in range(len(self.properties("samples"))): - sample = self.properties("samples")[sample_pos] - message.requests(sample_pos).sheared_femto_fragment_size = sample.properties( + for sample in self.properties("samples"): + request = message.create_request() + request.sheared_femto_fragment_size = sample.properties( "sheared_femto_fragment_size" ).value - message.requests(sample_pos).post_spri_concentration = sample.properties("post_spri_concentration").value - message.requests(sample_pos).post_spri_volume = sample.properties("post_spri_volume").value - message.requests(sample_pos).final_nano_drop_280 = sample.properties("final_nano_drop_280").value - message.requests(sample_pos).final_nano_drop_230 = sample.properties("final_nano_drop_230").value - message.requests(sample_pos).final_nano_drop = sample.properties("final_nano_drop").value - message.requests(sample_pos).shearing_qc_comments = sample.properties("shearing_qc_comments").value - message.requests(sample_pos).date_submitted_utc = sample.properties("date_submitted_utc").value - message.requests(sample_pos).container_barcode = self.properties("barcode").value - message.requests(sample_pos).sanger_sample_id = sample.properties("sanger_sample_id").value + request.post_spri_concentration = sample.properties("post_spri_concentration").value + request.post_spri_volume = sample.properties("post_spri_volume").value + request.final_nano_drop_280 = sample.properties("final_nano_drop_280").value + request.final_nano_drop_230 = sample.properties("final_nano_drop_230").value + request.final_nano_drop = sample.properties("final_nano_drop").value + request.shearing_qc_comments = sample.properties("shearing_qc_comments").value + request.date_submitted_utc = sample.properties("date_submitted_utc").value + request.container_barcode = self.properties("barcode").value + request.sanger_sample_id = sample.properties("sanger_sample_id").value diff --git a/tol_lab_share/messages/output_traction_message.py b/tol_lab_share/messages/output_traction_message.py index aeea097f..38b78fd4 100644 --- a/tol_lab_share/messages/output_traction_message.py +++ b/tol_lab_share/messages/output_traction_message.py @@ -176,7 +176,7 @@ def create_request(self) -> OutputTractionMessageRequest: """Creates a new request and returns it. It will be appended to the list of requests. Returns: - OutputTractionMessageRequestInterface: The newly created request. + OutputTractionMessageRequest: The newly created request. """ self._requests.append(OutputTractionMessageRequest()) return self._requests[-1] @@ -201,11 +201,11 @@ def check_has_requests(self) -> bool: Returns bool saying if the message has requests """ - if len(self._requests) > 0: + if self._requests: return True - else: - self.trigger_error(error_codes.ERROR_23_TRACTION_MESSAGE_HAS_NO_REQUESTS) - return False + + self.trigger_error(error_codes.ERROR_23_TRACTION_MESSAGE_HAS_NO_REQUESTS) + return False def check_no_errors(self) -> bool: """Checks that a message has no errors @@ -213,7 +213,7 @@ def check_no_errors(self) -> bool: Returns: bool indicating if there is no errors """ - return len(self.errors) == 0 + return not self.errors def check_requests_have_all_content(self) -> bool: """Checks that all requests provided are valid. Triggers an error if any is not. diff --git a/tol_lab_share/messages/traction_qc_message.py b/tol_lab_share/messages/traction_qc_message.py index 11504980..ab8169a8 100644 --- a/tol_lab_share/messages/traction_qc_message.py +++ b/tol_lab_share/messages/traction_qc_message.py @@ -95,7 +95,7 @@ class TractionQcMessage(MessageProperty): def __init__(self): """Resets initial data""" super().__init__(Input(self)) - self._requests: dict[int, TractionQcMessageRequest] = {} + self._requests: list[TractionQcMessageRequest] = [] self._sent = False self._validate_certificates = get_config().CERTIFICATES_VALIDATION_ENABLED @@ -110,20 +110,14 @@ def validators(self) -> list[Callable]: """list of validators to check the message is correct before sending""" return [self.check_has_requests, self.check_requests_have_all_content, self.check_no_errors] - def requests(self, position: int) -> TractionQcMessageRequest: - """Returns the request at position position from this message. If there is no request there it - will create a new instance and return it. - - Args: - position (int) position that we want to return. + def create_request(self) -> TractionQcMessageRequest: + """Creates a new request and returns it. It will be appended to the list of requests. Returns: - TractionQcMessageRequest at the specified position. + TractionQcMessageRequest: The newly created request. """ - if position not in self._requests: - self._requests[position] = TractionQcMessageRequest() - - return self._requests[position] + self._requests.append(TractionQcMessageRequest()) + return self._requests[-1] def request_attributes(self) -> list[dict[str, Any]]: """Returns a list with all the qc data payload for every request @@ -143,26 +137,27 @@ def check_has_requests(self) -> bool: Returns bool saying if the message has requests """ - if len(self._requests) > 0: + if self._requests: return True - else: - self.trigger_error(error_codes.ERROR_25_TRACTION_QC_MESSAGE_HAS_NO_REQUESTS) - return False + + self.trigger_error(error_codes.ERROR_25_TRACTION_QC_MESSAGE_HAS_NO_REQUESTS) + return False def check_no_errors(self) -> bool: """Checks that a message has no errors Returns: bool indicating if there is no errors """ - return len(self.errors) == 0 + return not self.errors def check_requests_have_all_content(self) -> bool: """Checks that all requests provided are valid. Triggers an error if any is not. Returns: bool indicating that all requests have valid content inside. """ - if all([self.requests(key).validate() for key in self._requests]): + if all([request.validate() for request in self._requests]): return True + self.trigger_error(error_codes.ERROR_26_TRACTION_QC_MESSAGE_REQUESTS_HAVE_MISSING_DATA) return False From ddc5ca243de13f2492527077ea27f08459712473 Mon Sep 17 00:00:00 2001 From: Stuart McHattie Date: Mon, 12 Feb 2024 10:30:50 +0000 Subject: [PATCH 24/40] Simplify shortened line layout --- tol_lab_share/message_properties/definitions/labware.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tol_lab_share/message_properties/definitions/labware.py b/tol_lab_share/message_properties/definitions/labware.py index a1cc93eb..0055cf44 100644 --- a/tol_lab_share/message_properties/definitions/labware.py +++ b/tol_lab_share/message_properties/definitions/labware.py @@ -121,9 +121,7 @@ def _(self, message: TractionQcMessage) -> None: for sample in self.properties("samples"): request = message.create_request() - request.sheared_femto_fragment_size = sample.properties( - "sheared_femto_fragment_size" - ).value + request.sheared_femto_fragment_size = sample.properties("sheared_femto_fragment_size").value request.post_spri_concentration = sample.properties("post_spri_concentration").value request.post_spri_volume = sample.properties("post_spri_volume").value request.final_nano_drop_280 = sample.properties("final_nano_drop_280").value From 51858902753c3b46046c2dcd0dfdafb83e74e59c Mon Sep 17 00:00:00 2001 From: Stuart McHattie Date: Mon, 12 Feb 2024 16:04:55 +0000 Subject: [PATCH 25/40] Tidy up docstrings for message types --- .../messages/output_traction_message.py | 96 +++++++++++-------- tol_lab_share/messages/traction_qc_message.py | 76 +++++++++------ 2 files changed, 105 insertions(+), 67 deletions(-) diff --git a/tol_lab_share/messages/output_traction_message.py b/tol_lab_share/messages/output_traction_message.py index 38b78fd4..6c79fd5f 100644 --- a/tol_lab_share/messages/output_traction_message.py +++ b/tol_lab_share/messages/output_traction_message.py @@ -1,3 +1,4 @@ +from __future__ import annotations from functools import singledispatchmethod from typing import Callable, Any from json import dumps @@ -16,9 +17,7 @@ class OutputTractionMessageRequest: - """Class that manages the information of a single Traction request instance - as part of the Traction message - """ + """Class that manages the information of a single Traction request instance as part of the Traction message.""" def __init__(self): """Constructor that initializes all info for a single request.""" @@ -42,7 +41,11 @@ def __init__(self): self.taxon_id: str | None = None def validate(self) -> bool: - """Checks that we have all required information and that it is valid before marking this request as valid.""" + """Validate the information in this request. + + Returns: + bool: True if the request is valid; otherwise False. + """ return ( (self.library_type is not None) and (self.study_uuid is not None) @@ -53,10 +56,11 @@ def validate(self) -> bool: and (self.species is not None) ) - def serializer(self): + def serializer(self) -> RequestSerializer: """Returns a serializer instance to handle the generation of the message for this request. + Returns: - RequestSerializer instance for this request + RequestSerializer: instance for this request """ return RequestSerializer(self) @@ -81,9 +85,10 @@ def is_ont_library_type(self) -> bool: return bool(self.instance.library_type and ("ONT" in self.instance.library_type)) def request_payload(self) -> dict[str, Any]: - """Generates the correct payload depending on the library type for the current request. + """Generate the payload for the request in this message. + Returns: - Dic[str,str] with the required information to send for this request to Traction + dict[str, Any] A dictionary containing the Traction payload for the request. """ if self.is_ont_library_type(): return { @@ -100,13 +105,15 @@ def request_payload(self) -> dict[str, Any]: } def sample_payload(self) -> dict[str, Any]: - """Generates the correct payload for the sample defined in this request + """Generate the payload for the sample in this request. + Returns: - Dic[str,str] with the required sample information to send for this request to Traction + dict[str, Any]: A dictionary containing the Traction payload for the sample. """ collection_date = None if self.instance.date_of_sample_collection is not None: collection_date = self.instance.date_of_sample_collection.strftime("%Y-%m-%d") + return { "name": self.instance.sample_name, "external_id": self.instance.sample_uuid, @@ -123,10 +130,11 @@ def sample_payload(self) -> dict[str, Any]: } def container_payload(self) -> dict[str, Any]: - """Generates the correct payload for the container defined in this request depending on - the container type (tubes or wells) + """Generate the payload for the container in this request. + The contents of the payload varies depending on the container type (tubes or wells). + Returns: - Dic[str,str] with the required container information to send for this request to Traction + dict[str, Any]: A dictionary containing the Traction payload for the container. """ if self.instance.container_type == "tubes": return {"type": self.instance.container_type, "barcode": self.instance.container_barcode} @@ -138,10 +146,10 @@ def container_payload(self) -> dict[str, Any]: } def payload(self) -> dict[str, Any]: - """Main builder of the payload required to describe all information required for Traction: - sample, request and container. + """Generate a payload with the information required by Traction. + Returns: - Dic[str,str] with all the required payload information for Traction + dict[str, Any]: A dictionary containing the overall payload. """ return { "request": self.request_payload(), @@ -162,14 +170,21 @@ def __init__(self): @property def origin(self) -> str: - """Default origin identifier. This will be appended to any errors generated to know where it was originated - when we received it. + """The origin identifier for this message type. + This will be appended to any errors generated via the `trigger_error` method. + + Returns: + str: The origin identifier. """ return "OutputFeedbackMessage" @property def validators(self) -> list[Callable]: - """list of validators to check the message is correct before sending""" + """A list of validators to check the message is correct. + + Returns: + list[Callable]: The list of methods to call for complete validation of this message. + """ return [self.check_has_requests, self.check_requests_have_all_content, self.check_no_errors] def create_request(self) -> OutputTractionMessageRequest: @@ -182,24 +197,24 @@ def create_request(self) -> OutputTractionMessageRequest: return self._requests[-1] def request_attributes(self) -> list[dict[str, Any]]: - """Returns a list with all the payloads for every request + """Prepare a payload for all the requests. Returns: - list[dict[str,Any]] with all payloads for all the requests + list[dict[str, Any]]: The payload for all the requests. """ return [request.serializer().payload() for request in self._requests] @property def errors(self) -> list[ErrorCode]: - """list of errors defined for this message""" + """A list of errors defined for this message.""" return self._errors def check_has_requests(self) -> bool: - """Returns a bool identifying if the message has requests. If not it will trigger an error and - return false. + """Check that this message has associated requests. + Even if there are no requests, this will not trigger an error. - Returns - bool saying if the message has requests + Returns: + bool: True if there are registered requests; otherwise False. """ if self._requests: return True @@ -208,18 +223,19 @@ def check_has_requests(self) -> bool: return False def check_no_errors(self) -> bool: - """Checks that a message has no errors + """Check that the message has no errors. Returns: - bool indicating if there is no errors + bool: True if there are no errors registered; otherwise False. """ return not self.errors def check_requests_have_all_content(self) -> bool: - """Checks that all requests provided are valid. Triggers an error if any is not. + """Checks that all requests provided have valid content. + Triggers an error if any is not. Returns: - bool indicating that all requests have valid content inside. + bool: True if all requests validate successfully; otherwise False. """ if all([request.validate() for request in self._requests]): return True @@ -228,7 +244,11 @@ def check_requests_have_all_content(self) -> bool: return False def payload(self) -> dict[str, Any]: - """Returns the valid payload to send to traction""" + """Generates the payload to send to Traction. + + Returns: + dict[str, Any]: The payload dictionary. + """ return { "data": { "type": "receptions", @@ -240,25 +260,25 @@ def payload(self) -> dict[str, Any]: } def error_code_traction_problem(self, status_code: int, error_str: str) -> None: - """Triggers an error indicating that traction failed. + """Triggers an error indicating that Traction failed. Args: - status_code (int) HTTP status code when sending to traction (422, 500, etc) - error_str (str) contents received by Traction endpoint on the request + status_code (int): HTTP status code when sending to Traction (422, 500, etc) + error_str (str): Error message received from the Traction endpoint. """ self.trigger_error( error_codes.ERROR_13_TRACTION_REQUEST_FAILED, text=f"HTTP CODE: { status_code }, MSG: {error_str}" ) def send(self, url: str) -> bool: - """Sends a request to Traction. If is correct returns true, if not it will trigger an error and - return False + """Sends a Traction API request to the provided URL. + If the send fails, an error code will be recorded. Args: - url (str) url where it will send the Traction request + url (str): The URL to submit the Traction request to. Returns: - bool indicating if the request was successful + bool: True if the request was sent successfully; otherwise False. """ headers = {"Content-type": "application/vnd.api+json", "Accept": "application/vnd.api+json"} diff --git a/tol_lab_share/messages/traction_qc_message.py b/tol_lab_share/messages/traction_qc_message.py index ab8169a8..a42493af 100644 --- a/tol_lab_share/messages/traction_qc_message.py +++ b/tol_lab_share/messages/traction_qc_message.py @@ -1,3 +1,4 @@ +from __future__ import annotations from functools import singledispatchmethod import logging from json import dumps @@ -17,7 +18,7 @@ class TractionQcMessageRequest: - """Class that holds the information for a traction Qc message request.""" + """Class that holds the information for a Traction QC message request.""" def __init__(self): """Constructor to initialize the info for the request.""" @@ -33,8 +34,11 @@ def __init__(self): self.shearing_qc_comments: str | None = None def validate(self) -> bool: - """Checks that we have all required information and that it is valid before - marking this request as valid.""" + """Validate that the request has all the required information. + + Returns: + bool: True if the request is valid; otherwise False. + """ return ( (self.sanger_sample_id is not None) and (self.container_barcode is not None) @@ -48,10 +52,11 @@ def validate(self) -> bool: and (self.date_submitted_utc is not None) ) - def serializer(self): - """Returns a serializer instance to handle the generation of the message for this request. + def serializer(self) -> QcRequestSerializer: + """A serializer instance to handle the generation of the payload for this request. + Returns: - RequestSerializer instance for this request + RequestSerializer: The serializer for this request. """ return QcRequestSerializer(self) @@ -61,15 +66,17 @@ class QcRequestSerializer: def __init__(self, instance: TractionQcMessageRequest): """Constructor that sets initial state of the instance. - Parameters: - instance (TractionQcMessageRequest) request that we want to serialize + + Args: + instance (TractionQcMessageRequest): The request to serialize. """ self.instance = instance def payload(self) -> dict[str, Any]: - """Constructs the payload with qc data. + """Prepare a payload for the TractionQcMessageRequest. + Returns: - Dic[str,str] with all the required payload information for Traction + dict[str, Any]: The payload for the request. No entries for empty values will be included. """ obj = { "sheared_femto_fragment_size": self.instance.sheared_femto_fragment_size, @@ -102,12 +109,13 @@ def __init__(self): @property def origin(self) -> str: """Default origin identifier. This will be appended to any errors generated to know - where it was originated when we received""" + where it was originated when we received + """ return "TractionQcMessage" @property def validators(self) -> list[Callable]: - """list of validators to check the message is correct before sending""" + """A list of validators to check the message is correct before sending.""" return [self.check_has_requests, self.check_requests_have_all_content, self.check_no_errors] def create_request(self) -> TractionQcMessageRequest: @@ -120,22 +128,23 @@ def create_request(self) -> TractionQcMessageRequest: return self._requests[-1] def request_attributes(self) -> list[dict[str, Any]]: - """Returns a list with all the qc data payload for every request + """Prepare a payload for all the QC data of every request. + Returns: - list[dict[str,Any]] with all payload for all the requests + list[dict[str,Any]]: The payload for all the requests. """ - return [self._requests[position].serializer().payload() for position in range(len(self._requests))] + return [request.serializer().payload() for request in self._requests] @property def errors(self) -> list[ErrorCode]: - """list of errors defined for this message""" + """The list of errors defined for this message""" return self._errors def check_has_requests(self) -> bool: - """Returns a bool identifying if the message has requests. If not it will trigger an error and - return false. + """Check whether the message has requests. If not an error will be triggered. + Returns - bool saying if the message has requests + bool: True if the message has requests; otherwise False. """ if self._requests: return True @@ -144,16 +153,18 @@ def check_has_requests(self) -> bool: return False def check_no_errors(self) -> bool: - """Checks that a message has no errors + """Checks that a message has no errors. + Returns: - bool indicating if there is no errors + bool: True if there are no errors; otherwise False. """ return not self.errors def check_requests_have_all_content(self) -> bool: """Checks that all requests provided are valid. Triggers an error if any is not. + Returns: - bool indicating that all requests have valid content inside. + bool: True if all requests validate; otherwise False. """ if all([request.validate() for request in self._requests]): return True @@ -162,7 +173,11 @@ def check_requests_have_all_content(self) -> bool: return False def payload(self) -> dict[str, Any]: - """Returns the valid payload to send to traction""" + """Prepare a valid payload to send to traction. + + Returns: + dict[str, Any]: The payload. + """ return { "data": { "type": "qc_receptions", @@ -175,9 +190,10 @@ def payload(self) -> dict[str, Any]: def error_code_traction_problem(self, status_code: int, error_str: str) -> None: """Triggers an error indicating that traction failed. - Parameters: - status_code (int) HTTP status code when sending to traction (422, 500, etc) - error_str (str) contents received by Traction endpoint on the request + + Args: + status_code (int): HTTP status code when sending to traction (422, 500, etc) + error_str (str): contents received by Traction endpoint on the request """ self.trigger_error( error_codes.ERROR_27_TRACTION_QC_REQUEST_FAILED, text=f"HTTP CODE: { status_code }, MSG: {error_str}" @@ -186,10 +202,12 @@ def error_code_traction_problem(self, status_code: int, error_str: str) -> None: def send(self, url: str) -> bool: """Sends a request to Traction. If is correct returns true, if not it will trigger an error and return False - Parameters: - url (str) url where it will send the Traction qc request + + Args: + url (str): url where it will send the Traction qc request + Returns: - bool indicating if the request was successful + bool indicating if the request was successful """ headers = {"Content-type": "application/vnd.api+json", "Accept": "application/vnd.api+json"} From 4072e4aa4f56f9b57c2c444dd2d713ccd8fef8f6 Mon Sep 17 00:00:00 2001 From: Stuart McHattie Date: Mon, 12 Feb 2024 16:20:42 +0000 Subject: [PATCH 26/40] Undo accidental lowercasing of DictInput class --- .../definitions/test_dict_input.py | 18 +++--- .../definitions/test_message_property.py | 6 +- .../definitions/dict_input.py | 4 +- .../message_properties/definitions/labware.py | 8 +-- .../message_properties/definitions/sample.py | 56 +++++++++---------- .../messages/input_create_labware_message.py | 8 +-- 6 files changed, 50 insertions(+), 50 deletions(-) diff --git a/tests/message_properties/definitions/test_dict_input.py b/tests/message_properties/definitions/test_dict_input.py index 6231f366..f939862c 100644 --- a/tests/message_properties/definitions/test_dict_input.py +++ b/tests/message_properties/definitions/test_dict_input.py @@ -1,33 +1,33 @@ -from tol_lab_share.message_properties.definitions.dict_input import dictInput +from tol_lab_share.message_properties.definitions.dict_input import DictInput def test_dict_input_can_validate_when_valid(): - input = dictInput({"name": 1}, "name") + input = DictInput({"name": 1}, "name") assert input.validate() - input = dictInput(dictInput({"name": {"first": "James"}}, "name"), "first") + input = DictInput(DictInput({"name": {"first": "James"}}, "name"), "first") assert input.validate() def test_dict_input_can_get_value_when_valid(): - input = dictInput({"name": 1}, "name") + input = DictInput({"name": 1}, "name") assert input.validate() def test_dict_input_can_get_chained_value_when_valid(): - input = dictInput(dictInput({"name": {"first": "James"}}, "name"), "first") + input = DictInput(DictInput({"name": {"first": "James"}}, "name"), "first") assert input.value == "James" def test_dict_input_can_validate_when_invalid(): - input = dictInput({}, "name") + input = DictInput({}, "name") assert not input.validate() - input = dictInput({"other": 1}, "name") + input = DictInput({"other": 1}, "name") assert not input.validate() - input = dictInput(dictInput({"name": {"first": "James"}}, "name"), "second") + input = DictInput(DictInput({"name": {"first": "James"}}, "name"), "second") assert not input.validate() - input = dictInput(dictInput({"name": {"first": "James"}}, "WRONG!!!"), "second") + input = DictInput(DictInput({"name": {"first": "James"}}, "WRONG!!!"), "second") assert not input.validate() diff --git a/tests/message_properties/definitions/test_message_property.py b/tests/message_properties/definitions/test_message_property.py index a1dc6dd5..b77ffe25 100644 --- a/tests/message_properties/definitions/test_message_property.py +++ b/tests/message_properties/definitions/test_message_property.py @@ -1,7 +1,7 @@ from tol_lab_share.message_properties.definitions.message_property import MessageProperty from unittest import mock from tol_lab_share.message_properties.definitions.input import Input -from tol_lab_share.message_properties.definitions.dict_input import dictInput +from tol_lab_share.message_properties.definitions.dict_input import DictInput from tol_lab_share import error_codes import pytest from datetime import datetime @@ -64,7 +64,7 @@ def test_message_property_check_is_integer(): assert instance.check_is_integer() is False assert len(instance.errors) > 0 - instance = MessageProperty(dictInput({"test": 1234}, "wrong!!")) + instance = MessageProperty(DictInput({"test": 1234}, "wrong!!")) assert instance.check_is_integer() is False assert len(instance.errors) > 0 @@ -116,7 +116,7 @@ def test_message_property_check_is_float(): assert instance.check_is_float() is True assert len(instance.errors) == 0 - instance = MessageProperty(dictInput({"test": 1234}, "wrong!!")) + instance = MessageProperty(DictInput({"test": 1234}, "wrong!!")) assert instance.check_is_float() is False assert len(instance.errors) > 0 diff --git a/tol_lab_share/message_properties/definitions/dict_input.py b/tol_lab_share/message_properties/definitions/dict_input.py index f89facb2..52b8be8b 100644 --- a/tol_lab_share/message_properties/definitions/dict_input.py +++ b/tol_lab_share/message_properties/definitions/dict_input.py @@ -8,7 +8,7 @@ logger = logging.getLogger(__name__) -class dictInput(MessageProperty): +class DictInput(MessageProperty): """MessageProperty subclass to manage parsing of the access to the key of a dictionary. The input can be a valid dict or another MessageProperty that provides as value a valid dict. If the input is not a valid dict, or if the key does not @@ -21,7 +21,7 @@ def __init__(self, input: MessageProperty | dict[str, Any], key: str): Parameters: input (MessageProperty): The dictionary we want to access, wrapped inside another - MessageProperty class; normally it will be an Input or another dictInput. + MessageProperty class; normally it will be an Input or another DictInput. key (str): The key we want to access inside the dictionary provided. Returns: diff --git a/tol_lab_share/message_properties/definitions/labware.py b/tol_lab_share/message_properties/definitions/labware.py index 0055cf44..8ce1c8bc 100644 --- a/tol_lab_share/message_properties/definitions/labware.py +++ b/tol_lab_share/message_properties/definitions/labware.py @@ -4,7 +4,7 @@ from tol_lab_share.constants import OUTPUT_TRACTION_MESSAGE_CONTAINER_TYPES from tol_lab_share.constants.input_create_labware_message import BARCODE, LABWARE_TYPE, SAMPLES from tol_lab_share.message_properties.definitions.barcode import Barcode -from tol_lab_share.message_properties.definitions.dict_input import dictInput +from tol_lab_share.message_properties.definitions.dict_input import DictInput from tol_lab_share.message_properties.definitions.labware_type import LabwareType from tol_lab_share.message_properties.definitions.sample import Sample from tol_lab_share.messages.output_feedback_message import OutputFeedbackMessage @@ -23,13 +23,13 @@ class Labware(MessageProperty): def __init__(self, input: MessageProperty): super().__init__(input) - self.add_property("labware_type", LabwareType(dictInput(input, LABWARE_TYPE))) - self.add_property("barcode", Barcode(dictInput(input, BARCODE))) + self.add_property("labware_type", LabwareType(DictInput(input, LABWARE_TYPE))) + self.add_property("barcode", Barcode(DictInput(input, BARCODE))) self.add_property("samples", self._parse_samples(input)) def _parse_samples(self, input: MessageProperty) -> list[MessageProperty]: """Parses the samples section and creates a sample for each position.""" - samples_dict = dictInput(input, SAMPLES) + samples_dict = DictInput(input, SAMPLES) if samples_dict.validate(): samples_list_dict: list[MessageProperty] = [] for position in range(len(samples_dict.value)): diff --git a/tol_lab_share/message_properties/definitions/sample.py b/tol_lab_share/message_properties/definitions/sample.py index 42a84c32..64271ff5 100644 --- a/tol_lab_share/message_properties/definitions/sample.py +++ b/tol_lab_share/message_properties/definitions/sample.py @@ -36,7 +36,7 @@ from tol_lab_share.message_properties.definitions.cost_code import CostCode from tol_lab_share.message_properties.definitions.country_of_origin import CountryOfOrigin from tol_lab_share.message_properties.definitions.date_utc import DateUtc -from tol_lab_share.message_properties.definitions.dict_input import dictInput +from tol_lab_share.message_properties.definitions.dict_input import DictInput from tol_lab_share.message_properties.definitions.donor_id import DonorId from tol_lab_share.message_properties.definitions.final_nano_drop import FinalNanoDrop from tol_lab_share.message_properties.definitions.final_nano_drop_230 import FinalNanoDrop230 @@ -68,39 +68,39 @@ class Sample(MessageProperty): def __init__(self, input: Any): super().__init__(input) - self.add_property("cost_code", CostCode(dictInput(input, SAMPLE_COST_CODE))) - self.add_property("study_uuid", Uuid(dictInput(input, SAMPLE_STUDY_UUID))) - self.add_property("common_name", CommonName(dictInput(input, SAMPLE_COMMON_NAME))) - self.add_property("concentration", Concentration(dictInput(input, SAMPLE_CONCENTRATION))) - self.add_property("volume", Volume(dictInput(input, SAMPLE_VOLUME))) + self.add_property("cost_code", CostCode(DictInput(input, SAMPLE_COST_CODE))) + self.add_property("study_uuid", Uuid(DictInput(input, SAMPLE_STUDY_UUID))) + self.add_property("common_name", CommonName(DictInput(input, SAMPLE_COMMON_NAME))) + self.add_property("concentration", Concentration(DictInput(input, SAMPLE_CONCENTRATION))) + self.add_property("volume", Volume(DictInput(input, SAMPLE_VOLUME))) self.add_property( "country_of_origin", - CountryOfOrigin(dictInput(input, SAMPLE_COUNTRY_OF_ORIGIN)), + CountryOfOrigin(DictInput(input, SAMPLE_COUNTRY_OF_ORIGIN)), ) - self.add_property("donor_id", DonorId(dictInput(input, SAMPLE_DONOR_ID))) - self.add_property("taxon_id", TaxonId(dictInput(input, SAMPLE_TAXON_ID))) - self.add_property("library_type", LibraryType(dictInput(input, SAMPLE_LIBRARY_TYPE))) - self.add_property("location", Location(dictInput(input, SAMPLE_LOCATION))) - self.add_property("public_name", PublicName(dictInput(input, SAMPLE_PUBLIC_NAME))) - self.add_property("sanger_sample_id", SangerSampleId(dictInput(input, SAMPLE_SANGER_SAMPLE_ID))) + self.add_property("donor_id", DonorId(DictInput(input, SAMPLE_DONOR_ID))) + self.add_property("taxon_id", TaxonId(DictInput(input, SAMPLE_TAXON_ID))) + self.add_property("library_type", LibraryType(DictInput(input, SAMPLE_LIBRARY_TYPE))) + self.add_property("location", Location(DictInput(input, SAMPLE_LOCATION))) + self.add_property("public_name", PublicName(DictInput(input, SAMPLE_PUBLIC_NAME))) + self.add_property("sanger_sample_id", SangerSampleId(DictInput(input, SAMPLE_SANGER_SAMPLE_ID))) self.add_property( "scientific_name", - ScientificNameFromTaxonId(TaxonId(dictInput(input, SAMPLE_SANGER_TAXON_ID))), + ScientificNameFromTaxonId(TaxonId(DictInput(input, SAMPLE_SANGER_TAXON_ID))), ) - self.add_property("uuid", Uuid(dictInput(input, SAMPLE_SANGER_UUID))) - self.add_property("accession_number", AccessionNumber(dictInput(input, SAMPLE_ACCESSION_NUMBER))) - self.add_property("genome_size", GenomeSize(dictInput(input, SAMPLE_GENOME_SIZE))) - self.add_property("collection_date", DateUtc(dictInput(input, SAMPLE_COLLECTION_DATE))) - self.add_property("supplier_sample_name", SupplierSampleName(dictInput(input, SUPPLIER_SAMPLE_NAME))) + self.add_property("uuid", Uuid(DictInput(input, SAMPLE_SANGER_UUID))) + self.add_property("accession_number", AccessionNumber(DictInput(input, SAMPLE_ACCESSION_NUMBER))) + self.add_property("genome_size", GenomeSize(DictInput(input, SAMPLE_GENOME_SIZE))) + self.add_property("collection_date", DateUtc(DictInput(input, SAMPLE_COLLECTION_DATE))) + self.add_property("supplier_sample_name", SupplierSampleName(DictInput(input, SUPPLIER_SAMPLE_NAME))) self.add_property( - "sheared_femto_fragment_size", ShearedFemtoFragmentSize(dictInput(input, SHEARED_FEMTO_FRAGMENT_SIZE)) + "sheared_femto_fragment_size", ShearedFemtoFragmentSize(DictInput(input, SHEARED_FEMTO_FRAGMENT_SIZE)) ) - self.add_property("post_spri_concentration", PostSPRIConcentration(dictInput(input, POST_SPRI_CONCENTRATION))) - self.add_property("post_spri_volume", PostSPRIVolume(dictInput(input, POST_SPRI_VOLUME))) - self.add_property("final_nano_drop_280", FinalNanoDrop280(dictInput(input, FINAL_NANODROP_280))) - self.add_property("final_nano_drop_230", FinalNanoDrop230(dictInput(input, FINAL_NANODROP_230))) - self.add_property("final_nano_drop", FinalNanoDrop(dictInput(input, FINAL_NANODROP))) - self.add_property("shearing_qc_comments", ShearingAndQCComments(dictInput(input, SHEARING_QC_COMMENTS))) - self.add_property("date_submitted_utc", DateUtc(dictInput(input, DATE_SUBMITTED_UTC))) - self.add_property("priority_level", PriorityLevel(dictInput(input, PRIORITY_LEVEL))) + self.add_property("post_spri_concentration", PostSPRIConcentration(DictInput(input, POST_SPRI_CONCENTRATION))) + self.add_property("post_spri_volume", PostSPRIVolume(DictInput(input, POST_SPRI_VOLUME))) + self.add_property("final_nano_drop_280", FinalNanoDrop280(DictInput(input, FINAL_NANODROP_280))) + self.add_property("final_nano_drop_230", FinalNanoDrop230(DictInput(input, FINAL_NANODROP_230))) + self.add_property("final_nano_drop", FinalNanoDrop(DictInput(input, FINAL_NANODROP))) + self.add_property("shearing_qc_comments", ShearingAndQCComments(DictInput(input, SHEARING_QC_COMMENTS))) + self.add_property("date_submitted_utc", DateUtc(DictInput(input, DATE_SUBMITTED_UTC))) + self.add_property("priority_level", PriorityLevel(DictInput(input, PRIORITY_LEVEL))) diff --git a/tol_lab_share/messages/input_create_labware_message.py b/tol_lab_share/messages/input_create_labware_message.py index 06b43586..e613deb5 100644 --- a/tol_lab_share/messages/input_create_labware_message.py +++ b/tol_lab_share/messages/input_create_labware_message.py @@ -9,7 +9,7 @@ from tol_lab_share.message_properties.definitions.labware import Labware from tol_lab_share.message_properties.definitions.date_utc import DateUtc from tol_lab_share.message_properties.definitions.message_property import MessageProperty -from tol_lab_share.message_properties.definitions.dict_input import dictInput +from tol_lab_share.message_properties.definitions.dict_input import DictInput import logging @@ -30,9 +30,9 @@ def __init__(self, m: RabbitMessage): super().__init__(m) self._message = m.message - self.add_property("message_uuid", MessageUuid(dictInput(self._message, MESSAGE_UUID))) - self.add_property("labware", Labware(dictInput(self._message, LABWARE))) - self.add_property("create_date_utc", DateUtc(dictInput(self._message, CREATED_DATE_UTC))) + self.add_property("message_uuid", MessageUuid(DictInput(self._message, MESSAGE_UUID))) + self.add_property("labware", Labware(DictInput(self._message, LABWARE))) + self.add_property("create_date_utc", DateUtc(DictInput(self._message, CREATED_DATE_UTC))) @singledispatchmethod def add_to_message_property(self, message_property: MessageProperty) -> None: From 1d50f78a8480e06248103340f5e82d5e67f0baf1 Mon Sep 17 00:00:00 2001 From: Stuart McHattie Date: Mon, 12 Feb 2024 17:15:53 +0000 Subject: [PATCH 27/40] Update expected payload in unit tests for V2 Traction API --- .../messages/test_output_traction_message.py | 211 ++++++++++-------- 1 file changed, 114 insertions(+), 97 deletions(-) diff --git a/tests/messages/test_output_traction_message.py b/tests/messages/test_output_traction_message.py index 8550e99e..3d196a24 100644 --- a/tests/messages/test_output_traction_message.py +++ b/tests/messages/test_output_traction_message.py @@ -82,56 +82,63 @@ def test_output_traction_message_can_generate_payload_for_plates(): assert instance.payload() == { "data": { + "type": "receptions", "attributes": { - "request_attributes": [ + "source": "tol-lab-share.tol", + "plates_attributes": [ { - "container": {"barcode": "1", "position": "A1", "type": "wells"}, - "request": { - "external_study_id": "dd490ee5-fd1d-456d-99fd-eb9d3861e014", - "library_type": "library", - "cost_code": "S1234", - }, - "sample": { - "external_id": "8860a6b4-82e2-451c-aba2-a3129c38c0fc", - "name": "test1", - "species": "test " "species", - "priority_level": None, - "sanger_sample_id": "sample1", - "public_name": "Public1", - "supplier_name": "supplier1", - "taxon_id": "9606", - "donor_id": "donor1", - "country_of_origin": "United Kingdom", - "accession_number": "AN1234", - "date_of_sample_collection": my_date.strftime("%Y-%m-%d"), - }, - }, - { - "container": {"barcode": "1", "position": "B1", "type": "wells"}, - "request": { - "external_study_id": "dd490ee5-fd1d-456d-99fd-eb9d3861e014", - "library_type": "library", - "cost_code": "S4567", - }, - "sample": { - "external_id": "8860a6b4-82e2-451c-aba2-a3129c38c0fc", - "name": "test1", - "species": "test " "species", - "priority_level": None, - "sanger_sample_id": "sample2", - "public_name": "Public2", - "supplier_name": "supplier2", - "taxon_id": "9606", - "donor_id": "donor2", - "country_of_origin": "United Kingdom", - "accession_number": "AN1235", - "date_of_sample_collection": my_date.strftime("%Y-%m-%d"), - }, + "barcode": "1", + "type": "wells", + "wells_attributes": [ + { + "position": "A1", + "request": { + "external_study_id": "dd490ee5-fd1d-456d-99fd-eb9d3861e014", + "library_type": "ONT_mylib", + "cost_code": "S1234", + }, + "sample": { + "external_id": "8860a6b4-82e2-451c-aba2-a3129c38c0fc", + "name": "test1", + "species": "test " "species", + "priority_level": "Medium", + "sanger_sample_id": "sample1", + "public_name": "Public1", + "supplier_name": "supplier1", + "taxon_id": "9606", + "donor_id": "donor1", + "country_of_origin": "United Kingdom", + "accession_number": "AN1234", + "date_of_sample_collection": my_date.strftime("%Y-%m-%d"), + }, + }, + { + "position": "B1", + "request": { + "external_study_id": "dd490ee5-fd1d-456d-99fd-eb9d3861e014", + "library_type": "ONT_mylib", + "cost_code": "S4567", + }, + "sample": { + "external_id": "8860a6b4-82e2-451c-aba2-a3129c38c0fc", + "name": "test1", + "species": "test " "species", + "priority_level": None, + "sanger_sample_id": "sample2", + "public_name": "Public2", + "supplier_name": "supplier2", + "taxon_id": "9606", + "donor_id": "donor2", + "country_of_origin": "United Kingdom", + "accession_number": "AN1235", + "date_of_sample_collection": my_date.strftime("%Y-%m-%d"), + }, + }, + ], }, ], - "source": "tol-lab-share.tol", + "tubes_attributes": [], }, - "type": "receptions", } } @@ -182,58 +189,65 @@ def test_output_traction_message_can_generate_payload_for_ont_library_types(): assert instance.payload() == { "data": { + "type": "receptions", "attributes": { - "request_attributes": [ + "source": "tol-lab-share.tol", + "plates_attributes": [ { - "container": {"barcode": "1", "position": "A1", "type": "wells"}, - "request": { - "external_study_id": "dd490ee5-fd1d-456d-99fd-eb9d3861e014", - "library_type": "ONT_mylib", - "cost_code": "S1234", - "data_type": "basecalls", - }, - "sample": { - "external_id": "8860a6b4-82e2-451c-aba2-a3129c38c0fc", - "name": "test1", - "species": "test " "species", - "priority_level": "Medium", - "sanger_sample_id": "sample1", - "public_name": "Public1", - "supplier_name": "supplier1", - "taxon_id": "9606", - "donor_id": "donor1", - "country_of_origin": "United Kingdom", - "accession_number": "AN1234", - "date_of_sample_collection": my_date.strftime("%Y-%m-%d"), - }, - }, - { - "container": {"barcode": "1", "position": "B1", "type": "wells"}, - "request": { - "external_study_id": "dd490ee5-fd1d-456d-99fd-eb9d3861e014", - "library_type": "ONT_mylib", - "cost_code": "S4567", - "data_type": "basecalls", - }, - "sample": { - "external_id": "8860a6b4-82e2-451c-aba2-a3129c38c0fc", - "name": "test1", - "species": "test " "species", - "priority_level": None, - "sanger_sample_id": "sample2", - "public_name": "Public2", - "supplier_name": "supplier2", - "taxon_id": "9606", - "donor_id": "donor2", - "country_of_origin": "United Kingdom", - "accession_number": "AN1235", - "date_of_sample_collection": my_date.strftime("%Y-%m-%d"), - }, + "barcode": "1", + "type": "wells", + "wells_attributes": [ + { + "position": "A1", + "request": { + "external_study_id": "dd490ee5-fd1d-456d-99fd-eb9d3861e014", + "library_type": "ONT_mylib", + "cost_code": "S1234", + "data_type": "basecalls", + }, + "sample": { + "external_id": "8860a6b4-82e2-451c-aba2-a3129c38c0fc", + "name": "test1", + "species": "test " "species", + "priority_level": "Medium", + "sanger_sample_id": "sample1", + "public_name": "Public1", + "supplier_name": "supplier1", + "taxon_id": "9606", + "donor_id": "donor1", + "country_of_origin": "United Kingdom", + "accession_number": "AN1234", + "date_of_sample_collection": my_date.strftime("%Y-%m-%d"), + }, + }, + { + "position": "B1", + "request": { + "external_study_id": "dd490ee5-fd1d-456d-99fd-eb9d3861e014", + "library_type": "ONT_mylib", + "cost_code": "S4567", + "data_type": "basecalls", + }, + "sample": { + "external_id": "8860a6b4-82e2-451c-aba2-a3129c38c0fc", + "name": "test1", + "species": "test " "species", + "priority_level": None, + "sanger_sample_id": "sample2", + "public_name": "Public2", + "supplier_name": "supplier2", + "taxon_id": "9606", + "donor_id": "donor2", + "country_of_origin": "United Kingdom", + "accession_number": "AN1235", + "date_of_sample_collection": my_date.strftime("%Y-%m-%d"), + }, + }, + ], }, ], - "source": "tol-lab-share.tol", + "tubes_attributes": [], }, - "type": "receptions", } } @@ -282,10 +296,14 @@ def test_output_traction_message_can_generate_payload_for_tubes(): assert instance.payload() == { "data": { + "type": "receptions", "attributes": { - "request_attributes": [ + "source": "tol-lab-share.tol", + "plates_attributes": [], + "tubes_attributes": [ { - "container": {"barcode": "1", "type": "tubes"}, + "barcode": "1", + "type": "tubes", "request": { "external_study_id": "dd490ee5-fd1d-456d-99fd-eb9d3861e014", "library_type": "library", @@ -307,7 +325,8 @@ def test_output_traction_message_can_generate_payload_for_tubes(): }, }, { - "container": {"barcode": "1", "type": "tubes"}, + "barcode": "1", + "type": "tubes", "request": { "external_study_id": "dd490ee5-fd1d-456d-99fd-eb9d3861e014", "library_type": "library", @@ -329,9 +348,7 @@ def test_output_traction_message_can_generate_payload_for_tubes(): }, }, ], - "source": "tol-lab-share.tol", }, - "type": "receptions", } } From 0b9fc6cfe08249be30c206e86dc065badf4891a3 Mon Sep 17 00:00:00 2001 From: Stuart McHattie Date: Mon, 12 Feb 2024 18:01:23 +0000 Subject: [PATCH 28/40] Update the serializer for tube requests --- .../messages/output_traction_message.py | 154 ++++++++++-------- tol_lab_share/messages/traction_qc_message.py | 11 +- 2 files changed, 84 insertions(+), 81 deletions(-) diff --git a/tol_lab_share/messages/output_traction_message.py b/tol_lab_share/messages/output_traction_message.py index 6c79fd5f..5eed2fe6 100644 --- a/tol_lab_share/messages/output_traction_message.py +++ b/tol_lab_share/messages/output_traction_message.py @@ -1,4 +1,3 @@ -from __future__ import annotations from functools import singledispatchmethod from typing import Callable, Any from json import dumps @@ -56,94 +55,97 @@ def validate(self) -> bool: and (self.species is not None) ) - def serializer(self) -> RequestSerializer: - """Returns a serializer instance to handle the generation of the message for this request. - Returns: - RequestSerializer: instance for this request - """ - return RequestSerializer(self) - - -class RequestSerializer: +class Serializer: """Class to manage the serialization to JSON of the request received as argument in the constructor.""" - - def __init__(self, instance: OutputTractionMessageRequest): - """Constructor that sets the initial state of the instance. + @staticmethod + def request_payload(request: OutputTractionMessageRequest) -> dict[str, Any]: + """Generate the request payload for the given request. + The payload varies depending on whether the library type is ONT or not. Args: - instance (OutputTractionMessageRequest): The request that we want to serialize. - """ - self.instance = instance - - def is_ont_library_type(self) -> bool: - """Flag boolean method that identifies if the library type is ONT. - - Returns: - bool: True if the library type is ONT; otherwise false. - """ - return bool(self.instance.library_type and ("ONT" in self.instance.library_type)) - - def request_payload(self) -> dict[str, Any]: - """Generate the payload for the request in this message. + request (OutputTractionMessageRequest): The request to generate the payload for. Returns: - dict[str, Any] A dictionary containing the Traction payload for the request. + dict[str, Any]: A dictionary containing the Traction "request" payload for the request. """ - if self.is_ont_library_type(): + is_ont = request.library_type and ("ONT" in request.library_type) + if is_ont: return { "data_type": "basecalls", - "library_type": self.instance.library_type, - "external_study_id": self.instance.study_uuid, - "cost_code": self.instance.cost_code, + "library_type": request.library_type, + "external_study_id": request.study_uuid, + "cost_code": request.cost_code, } else: return { - "library_type": self.instance.library_type, - "external_study_id": self.instance.study_uuid, - "cost_code": self.instance.cost_code, + "library_type": request.library_type, + "external_study_id": request.study_uuid, + "cost_code": request.cost_code, } - def sample_payload(self) -> dict[str, Any]: - """Generate the payload for the sample in this request. + @staticmethod + def sample_payload(request: OutputTractionMessageRequest) -> dict[str, Any]: + """Generate the sample payload for the given request. + + Args: + request (OutputTractionMessageRequest): The request to generate the payload for. Returns: - dict[str, Any]: A dictionary containing the Traction payload for the sample. + dict[str, Any]: A dictionary containing the Traction "sample" payload for the request. """ collection_date = None - if self.instance.date_of_sample_collection is not None: - collection_date = self.instance.date_of_sample_collection.strftime("%Y-%m-%d") + if request.date_of_sample_collection is not None: + collection_date = request.date_of_sample_collection.strftime("%Y-%m-%d") return { - "name": self.instance.sample_name, - "external_id": self.instance.sample_uuid, - "species": self.instance.species, - "priority_level": self.instance.priority_level, - "sanger_sample_id": self.instance.sanger_sample_id, - "supplier_name": self.instance.supplier_name, - "public_name": self.instance.public_name, - "taxon_id": self.instance.taxon_id, - "donor_id": self.instance.donor_id, - "country_of_origin": self.instance.country_of_origin, - "accession_number": self.instance.accession_number, + "name": request.sample_name, + "external_id": request.sample_uuid, + "species": request.species, + "priority_level": request.priority_level, + "sanger_sample_id": request.sanger_sample_id, + "supplier_name": request.supplier_name, + "public_name": request.public_name, + "taxon_id": request.taxon_id, + "donor_id": request.donor_id, + "country_of_origin": request.country_of_origin, + "accession_number": request.accession_number, "date_of_sample_collection": collection_date, } - def container_payload(self) -> dict[str, Any]: - """Generate the payload for the container in this request. - The contents of the payload varies depending on the container type (tubes or wells). + +class PlateSerializer(Serializer): + """Class to manage the serialization of plate requests.""" + + def __init__(self, plate_requests: list[OutputTractionMessageRequest]): + """Constructor. + + Args: + plate_requests (list[OutputTractionMessageRequest]): The list of requests to serialize for a single plate. + """ + self._requests = plate_requests + + def payload(self) -> dict[str, Any]: + """Generate a payload with the information required by Traction. Returns: - dict[str, Any]: A dictionary containing the Traction payload for the container. + dict[str, Any]: A dictionary containing the overall payload. """ - if self.instance.container_type == "tubes": - return {"type": self.instance.container_type, "barcode": self.instance.container_barcode} - else: - return { - "type": self.instance.container_type, - "barcode": self.instance.container_barcode, - "position": self.instance.container_location, - } + return { + "request": self.request_payload(self._requests[0]), + "sample": self.sample_payload(self._requests[0]), + } + +class TubeSerializer(Serializer): + """Class to manage the serialization of plate requests.""" + + def __init__(self, tube_request: OutputTractionMessageRequest): + """Constructor. + + Args: + tube_request (OutputTractionMessageRequest): The request to serialize for a single tube. + """ + self._request = tube_request def payload(self) -> dict[str, Any]: """Generate a payload with the information required by Traction. @@ -152,9 +154,10 @@ def payload(self) -> dict[str, Any]: dict[str, Any]: A dictionary containing the overall payload. """ return { - "request": self.request_payload(), - "sample": self.sample_payload(), - "container": self.container_payload(), + "barcode": self._request.container_barcode, + "type": self._request.container_type, + "request": self.request_payload(self._request), + "sample": self.sample_payload(self._request), } @@ -196,13 +199,21 @@ def create_request(self) -> OutputTractionMessageRequest: self._requests.append(OutputTractionMessageRequest()) return self._requests[-1] - def request_attributes(self) -> list[dict[str, Any]]: - """Prepare a payload for all the requests. + def plates_attributes(self) -> list[dict[str, Any]]: + """Prepare a payload for all the request related to plates. + + Returns: + list[dict[str, Any]]: A list containing the payload for all the plates. + """ + return [PlateSerializer([request]).payload() for request in self._requests if request.container_type == "wells"] + + def tubes_attributes(self) -> list[dict[str, Any]]: + """Prepare a payload for all the requests related to tubes. Returns: - list[dict[str, Any]]: The payload for all the requests. + list[dict[str, Any]]: A list containing the payload for all the tubes. """ - return [request.serializer().payload() for request in self._requests] + return [TubeSerializer(request).payload() for request in self._requests if request.container_type == "tubes"] @property def errors(self) -> list[ErrorCode]: @@ -254,7 +265,8 @@ def payload(self) -> dict[str, Any]: "type": "receptions", "attributes": { "source": OUTPUT_TRACTION_MESSAGE_SOURCE, - "request_attributes": self.request_attributes(), + "plates_attributes": self.plates_attributes(), + "tubes_attributes": self.tubes_attributes(), }, } } diff --git a/tol_lab_share/messages/traction_qc_message.py b/tol_lab_share/messages/traction_qc_message.py index a42493af..6a8c0161 100644 --- a/tol_lab_share/messages/traction_qc_message.py +++ b/tol_lab_share/messages/traction_qc_message.py @@ -1,4 +1,3 @@ -from __future__ import annotations from functools import singledispatchmethod import logging from json import dumps @@ -52,14 +51,6 @@ def validate(self) -> bool: and (self.date_submitted_utc is not None) ) - def serializer(self) -> QcRequestSerializer: - """A serializer instance to handle the generation of the payload for this request. - - Returns: - RequestSerializer: The serializer for this request. - """ - return QcRequestSerializer(self) - class QcRequestSerializer: """Class to manage the serialization to JSON of the request received as argument in the constructor.""" @@ -133,7 +124,7 @@ def request_attributes(self) -> list[dict[str, Any]]: Returns: list[dict[str,Any]]: The payload for all the requests. """ - return [request.serializer().payload() for request in self._requests] + return [QcRequestSerializer(request).payload() for request in self._requests] @property def errors(self) -> list[ErrorCode]: From d55759d5c2552a1c43839a905091f810d42f035e Mon Sep 17 00:00:00 2001 From: Stuart McHattie Date: Mon, 12 Feb 2024 18:22:16 +0000 Subject: [PATCH 29/40] Implement plates_attributes for Traction message --- .../messages/test_output_traction_message.py | 10 +++---- .../messages/output_traction_message.py | 30 ++++++++++++++++--- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/tests/messages/test_output_traction_message.py b/tests/messages/test_output_traction_message.py index 3d196a24..bcdbac72 100644 --- a/tests/messages/test_output_traction_message.py +++ b/tests/messages/test_output_traction_message.py @@ -88,20 +88,20 @@ def test_output_traction_message_can_generate_payload_for_plates(): "plates_attributes": [ { "barcode": "1", - "type": "wells", + "type": "plates", "wells_attributes": [ { "position": "A1", "request": { "external_study_id": "dd490ee5-fd1d-456d-99fd-eb9d3861e014", - "library_type": "ONT_mylib", + "library_type": "library", "cost_code": "S1234", }, "sample": { "external_id": "8860a6b4-82e2-451c-aba2-a3129c38c0fc", "name": "test1", "species": "test " "species", - "priority_level": "Medium", + "priority_level": None, "sanger_sample_id": "sample1", "public_name": "Public1", "supplier_name": "supplier1", @@ -116,7 +116,7 @@ def test_output_traction_message_can_generate_payload_for_plates(): "position": "B1", "request": { "external_study_id": "dd490ee5-fd1d-456d-99fd-eb9d3861e014", - "library_type": "ONT_mylib", + "library_type": "library", "cost_code": "S4567", }, "sample": { @@ -195,7 +195,7 @@ def test_output_traction_message_can_generate_payload_for_ont_library_types(): "plates_attributes": [ { "barcode": "1", - "type": "wells", + "type": "plates", "wells_attributes": [ { "position": "A1", diff --git a/tol_lab_share/messages/output_traction_message.py b/tol_lab_share/messages/output_traction_message.py index 5eed2fe6..af574c4e 100644 --- a/tol_lab_share/messages/output_traction_message.py +++ b/tol_lab_share/messages/output_traction_message.py @@ -1,4 +1,5 @@ from functools import singledispatchmethod +import itertools from typing import Callable, Any from json import dumps from datetime import datetime @@ -125,6 +126,21 @@ def __init__(self, plate_requests: list[OutputTractionMessageRequest]): """ self._requests = plate_requests + def well_attributes_payload(self) -> list[dict[str, Any]]: + """Generate the well attributes payload for each of the plate's requests. + + Returns: + list[dict[str, Any]]: A list containing the well attributes for all the request. + """ + return [ + { + "position": request.container_location, + "request": self.request_payload(request), + "sample": self.sample_payload(request), + } + for request in self._requests + ] + def payload(self) -> dict[str, Any]: """Generate a payload with the information required by Traction. @@ -132,8 +148,9 @@ def payload(self) -> dict[str, Any]: dict[str, Any]: A dictionary containing the overall payload. """ return { - "request": self.request_payload(self._requests[0]), - "sample": self.sample_payload(self._requests[0]), + "barcode": self._requests[0].container_barcode, + "type": "plates", + "wells_attributes": self.well_attributes_payload(), } class TubeSerializer(Serializer): @@ -155,7 +172,7 @@ def payload(self) -> dict[str, Any]: """ return { "barcode": self._request.container_barcode, - "type": self._request.container_type, + "type": "tubes", "request": self.request_payload(self._request), "sample": self.sample_payload(self._request), } @@ -205,7 +222,12 @@ def plates_attributes(self) -> list[dict[str, Any]]: Returns: list[dict[str, Any]]: A list containing the payload for all the plates. """ - return [PlateSerializer([request]).payload() for request in self._requests if request.container_type == "wells"] + # Group well requests by plate barcode + well_requests = [request for request in self._requests if request.container_type == "wells"] + well_requests.sort(key=lambda request: request.container_barcode) + plate_requests = itertools.groupby(well_requests, lambda request: request.container_barcode) + + return [PlateSerializer(list(requests)).payload() for _, requests in plate_requests] def tubes_attributes(self) -> list[dict[str, Any]]: """Prepare a payload for all the requests related to tubes. From b1f8cd737491508f3dd6b6d941a7421941b34b04 Mon Sep 17 00:00:00 2001 From: Stuart McHattie Date: Mon, 12 Feb 2024 18:25:34 +0000 Subject: [PATCH 30/40] Fix linting issues --- tol_lab_share/messages/output_traction_message.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tol_lab_share/messages/output_traction_message.py b/tol_lab_share/messages/output_traction_message.py index af574c4e..63f13ebf 100644 --- a/tol_lab_share/messages/output_traction_message.py +++ b/tol_lab_share/messages/output_traction_message.py @@ -59,6 +59,7 @@ def validate(self) -> bool: class Serializer: """Class to manage the serialization to JSON of the request received as argument in the constructor.""" + @staticmethod def request_payload(request: OutputTractionMessageRequest) -> dict[str, Any]: """Generate the request payload for the given request. @@ -153,6 +154,7 @@ def payload(self) -> dict[str, Any]: "wells_attributes": self.well_attributes_payload(), } + class TubeSerializer(Serializer): """Class to manage the serialization of plate requests.""" @@ -224,7 +226,7 @@ def plates_attributes(self) -> list[dict[str, Any]]: """ # Group well requests by plate barcode well_requests = [request for request in self._requests if request.container_type == "wells"] - well_requests.sort(key=lambda request: request.container_barcode) + well_requests.sort(key=lambda request: request.container_barcode or "") plate_requests = itertools.groupby(well_requests, lambda request: request.container_barcode) return [PlateSerializer(list(requests)).payload() for _, requests in plate_requests] From 972022dc4da6376308e214c2f0c9f5273d09a02d Mon Sep 17 00:00:00 2001 From: Stephen Inglis <519327+stevieing@users.noreply.github.com> Date: Tue, 13 Feb 2024 10:47:30 +0000 Subject: [PATCH 31/40] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index f9b52ea0..b21b6959 100644 --- a/README.md +++ b/README.md @@ -86,4 +86,8 @@ You might want to add these to your `~/.zshrc` file: ```bash export CPPFLAGS="-I$(brew --prefix)/include" export LDFLAGS="-L$(brew --prefix)/lib" + + ``` + +![TOL Labware Production Flow - Architecture](https://github.com/sanger/tol-lab-share/assets/519327/5356846a-6d9b-4b8d-8ffb-af26d0776222) From a40196fd28b1675e0cb0992d3fef880bfeb251fd Mon Sep 17 00:00:00 2001 From: Stuart McHattie Date: Tue, 13 Feb 2024 10:56:45 +0000 Subject: [PATCH 32/40] Add a test for multiple plates appearing in the same message --- .../messages/test_output_traction_message.py | 204 ++++++++++++++++++ 1 file changed, 204 insertions(+) diff --git a/tests/messages/test_output_traction_message.py b/tests/messages/test_output_traction_message.py index bcdbac72..1a27bce6 100644 --- a/tests/messages/test_output_traction_message.py +++ b/tests/messages/test_output_traction_message.py @@ -143,6 +143,210 @@ def test_output_traction_message_can_generate_payload_for_plates(): } +def test_output_traction_message_can_generate_payload_for_multiple_plates(): + my_date = datetime.now() + instance = OutputTractionMessage() + + # Mix the order of the requests to ensure the grouping for plates works. + # Well order is still maintained since the payload keeps the order of the requests. + + # First plate, first well (A1). + request = instance.create_request() + request.container_barcode = "123" + request.container_type = "wells" + request.container_location = "A1" + request.library_type = "library" + request.sample_name = "test1" + request.study_uuid = "dd490ee5-fd1d-456d-99fd-eb9d3861e014" + request.sample_uuid = "8860a6b4-1234-451c-aba2-a3129c38c0fc" + request.species = "test species" + request.public_name = "Public1" + request.cost_code = "S1234" + request.priority_level = None + request.sanger_sample_id = "sample1" + request.supplier_name = "supplier1" + request.taxon_id = "9606" + request.donor_id = "donor1" + request.country_of_origin = "United Kingdom" + request.accession_number = "AN1234" + request.date_of_sample_collection = my_date + + # Second plate, first well (B1). + request = instance.create_request() + request.container_barcode = "456" + request.container_type = "wells" + request.container_location = "B1" + request.library_type = "library" + request.sample_name = "test3" + request.study_uuid = "dd490ee5-fd1d-456d-99fd-eb9d3861e014" + request.sample_uuid = "8860a6b4-1236-451c-aba2-a3129c38c0fc" + request.species = "test species" + request.public_name = "Public3" + request.cost_code = "S1234" + request.priority_level = None + request.sanger_sample_id = "sample3" + request.supplier_name = "supplier1" + request.taxon_id = "9606" + request.donor_id = "donor3" + request.country_of_origin = "United Kingdom" + request.accession_number = "AN1236" + request.date_of_sample_collection = my_date + + # First plate, second well (B1). + request = instance.create_request() + request.container_barcode = "123" + request.container_type = "wells" + request.container_location = "B1" + request.library_type = "library" + request.sample_name = "test2" + request.study_uuid = "dd490ee5-fd1d-456d-99fd-eb9d3861e014" + request.sample_uuid = "8860a6b4-1235-451c-aba2-a3129c38c0fc" + request.species = "test species" + request.public_name = "Public2" + request.cost_code = "S4567" + request.priority_level = None + request.sanger_sample_id = "sample2" + request.supplier_name = "supplier2" + request.taxon_id = "9606" + request.donor_id = "donor2" + request.country_of_origin = "United Kingdom" + request.accession_number = "AN1235" + request.date_of_sample_collection = my_date + + # Second plate, second well (C1). + request = instance.create_request() + request.container_barcode = "456" + request.container_type = "wells" + request.container_location = "C1" + request.library_type = "library" + request.sample_name = "test4" + request.study_uuid = "dd490ee5-fd1d-456d-99fd-eb9d3861e014" + request.sample_uuid = "8860a6b4-1237-451c-aba2-a3129c38c0fc" + request.species = "test species" + request.public_name = "Public4" + request.cost_code = "S4567" + request.priority_level = None + request.sanger_sample_id = "sample4" + request.supplier_name = "supplier2" + request.taxon_id = "9606" + request.donor_id = "donor4" + request.country_of_origin = "United Kingdom" + request.accession_number = "AN1237" + request.date_of_sample_collection = my_date + + assert instance.payload() == { + "data": { + "type": "receptions", + "attributes": { + "source": "tol-lab-share.tol", + "plates_attributes": [ + { + "barcode": "123", + "type": "plates", + "wells_attributes": [ + { + "position": "A1", + "request": { + "external_study_id": "dd490ee5-fd1d-456d-99fd-eb9d3861e014", + "library_type": "library", + "cost_code": "S1234", + }, + "sample": { + "external_id": "8860a6b4-1234-451c-aba2-a3129c38c0fc", + "name": "test1", + "species": "test " "species", + "priority_level": None, + "sanger_sample_id": "sample1", + "public_name": "Public1", + "supplier_name": "supplier1", + "taxon_id": "9606", + "donor_id": "donor1", + "country_of_origin": "United Kingdom", + "accession_number": "AN1234", + "date_of_sample_collection": my_date.strftime("%Y-%m-%d"), + }, + }, + { + "position": "B1", + "request": { + "external_study_id": "dd490ee5-fd1d-456d-99fd-eb9d3861e014", + "library_type": "library", + "cost_code": "S4567", + }, + "sample": { + "external_id": "8860a6b4-1235-451c-aba2-a3129c38c0fc", + "name": "test2", + "species": "test " "species", + "priority_level": None, + "sanger_sample_id": "sample2", + "public_name": "Public2", + "supplier_name": "supplier2", + "taxon_id": "9606", + "donor_id": "donor2", + "country_of_origin": "United Kingdom", + "accession_number": "AN1235", + "date_of_sample_collection": my_date.strftime("%Y-%m-%d"), + }, + }, + ], + }, + { + "barcode": "456", + "type": "plates", + "wells_attributes": [ + { + "position": "B1", + "request": { + "external_study_id": "dd490ee5-fd1d-456d-99fd-eb9d3861e014", + "library_type": "library", + "cost_code": "S1234", + }, + "sample": { + "external_id": "8860a6b4-1236-451c-aba2-a3129c38c0fc", + "name": "test3", + "species": "test " "species", + "priority_level": None, + "sanger_sample_id": "sample3", + "public_name": "Public3", + "supplier_name": "supplier1", + "taxon_id": "9606", + "donor_id": "donor3", + "country_of_origin": "United Kingdom", + "accession_number": "AN1236", + "date_of_sample_collection": my_date.strftime("%Y-%m-%d"), + }, + }, + { + "position": "C1", + "request": { + "external_study_id": "dd490ee5-fd1d-456d-99fd-eb9d3861e014", + "library_type": "library", + "cost_code": "S4567", + }, + "sample": { + "external_id": "8860a6b4-1237-451c-aba2-a3129c38c0fc", + "name": "test4", + "species": "test " "species", + "priority_level": None, + "sanger_sample_id": "sample4", + "public_name": "Public4", + "supplier_name": "supplier2", + "taxon_id": "9606", + "donor_id": "donor4", + "country_of_origin": "United Kingdom", + "accession_number": "AN1237", + "date_of_sample_collection": my_date.strftime("%Y-%m-%d"), + }, + }, + ], + }, + ], + "tubes_attributes": [], + }, + } + } + + def test_output_traction_message_can_generate_payload_for_ont_library_types(): my_date = datetime.now() instance = OutputTractionMessage() From 6156a405553ad4dcb65c09d876948b6782ce91b4 Mon Sep 17 00:00:00 2001 From: Stuart McHattie Date: Tue, 13 Feb 2024 11:13:34 +0000 Subject: [PATCH 33/40] Add test for a mix of plates and tubes in traction messages --- .../messages/test_output_traction_message.py | 192 ++++++++++++++++++ 1 file changed, 192 insertions(+) diff --git a/tests/messages/test_output_traction_message.py b/tests/messages/test_output_traction_message.py index 1a27bce6..4380fa42 100644 --- a/tests/messages/test_output_traction_message.py +++ b/tests/messages/test_output_traction_message.py @@ -557,6 +557,198 @@ def test_output_traction_message_can_generate_payload_for_tubes(): } +def test_output_traction_message_can_generate_payload_for_mix_of_plate_and_tubes(): + my_date = datetime.now() + instance = OutputTractionMessage() + + request = instance.create_request() + request.container_barcode = "123" + request.container_type = "wells" + request.container_location = "A1" + request.library_type = "library" + request.sample_name = "test1" + request.study_uuid = "dd490ee5-fd1d-456d-99fd-eb9d3861e014" + request.sample_uuid = "8860a6b4-1234-451c-aba2-a3129c38c0fc" + request.species = "test species" + request.public_name = "Public1" + request.cost_code = "S1234" + request.priority_level = None + request.sanger_sample_id = "sample1" + request.supplier_name = "supplier1" + request.taxon_id = "9606" + request.donor_id = "donor1" + request.country_of_origin = "United Kingdom" + request.accession_number = "AN1234" + request.date_of_sample_collection = my_date + + request = instance.create_request() + request.container_barcode = "123" + request.container_type = "wells" + request.container_location = "B1" + request.library_type = "library" + request.sample_name = "test2" + request.study_uuid = "dd490ee5-fd1d-456d-99fd-eb9d3861e014" + request.sample_uuid = "8860a6b4-1235-451c-aba2-a3129c38c0fc" + request.species = "test species" + request.public_name = "Public2" + request.cost_code = "S4567" + request.priority_level = None + request.sanger_sample_id = "sample2" + request.supplier_name = "supplier2" + request.taxon_id = "9606" + request.donor_id = "donor2" + request.country_of_origin = "United Kingdom" + request.accession_number = "AN1235" + request.date_of_sample_collection = my_date + + request = instance.create_request() + request.container_barcode = "987" + request.container_type = "tubes" + request.library_type = "library" + request.sample_name = "test3" + request.study_uuid = "dd490ee5-fd1d-456d-99fd-eb9d3861e014" + request.sample_uuid = "8860a6b4-1236-451c-aba2-a3129c38c0fc" + request.species = "test species" + request.public_name = "Public3" + request.cost_code = "S1234" + request.priority_level = "High" + request.sanger_sample_id = "sample3" + request.supplier_name = "supplier3" + request.taxon_id = "9606" + request.donor_id = "donor3" + request.country_of_origin = "United Kingdom" + request.accession_number = "AN1236" + request.date_of_sample_collection = my_date + + request = instance.create_request() + request.container_barcode = "876" + request.container_type = "tubes" + request.library_type = "library" + request.sample_name = "test4" + request.study_uuid = "dd490ee5-fd1d-456d-99fd-eb9d3861e014" + request.sample_uuid = "8860a6b4-1237-451c-aba2-a3129c38c0fc" + request.species = "test species" + request.public_name = "Public4" + request.cost_code = "S4567" + request.priority_level = "Low" + request.sanger_sample_id = "sample4" + request.supplier_name = "supplier4" + request.taxon_id = "9606" + request.donor_id = "donor4" + request.country_of_origin = "United Kingdom" + request.accession_number = "AN1237" + request.date_of_sample_collection = my_date + + assert instance.payload() == { + "data": { + "type": "receptions", + "attributes": { + "source": "tol-lab-share.tol", + "plates_attributes": [ + { + "barcode": "123", + "type": "plates", + "wells_attributes": [ + { + "position": "A1", + "request": { + "external_study_id": "dd490ee5-fd1d-456d-99fd-eb9d3861e014", + "library_type": "library", + "cost_code": "S1234", + }, + "sample": { + "external_id": "8860a6b4-1234-451c-aba2-a3129c38c0fc", + "name": "test1", + "species": "test " "species", + "priority_level": None, + "sanger_sample_id": "sample1", + "public_name": "Public1", + "supplier_name": "supplier1", + "taxon_id": "9606", + "donor_id": "donor1", + "country_of_origin": "United Kingdom", + "accession_number": "AN1234", + "date_of_sample_collection": my_date.strftime("%Y-%m-%d"), + }, + }, + { + "position": "B1", + "request": { + "external_study_id": "dd490ee5-fd1d-456d-99fd-eb9d3861e014", + "library_type": "library", + "cost_code": "S4567", + }, + "sample": { + "external_id": "8860a6b4-1235-451c-aba2-a3129c38c0fc", + "name": "test2", + "species": "test " "species", + "priority_level": None, + "sanger_sample_id": "sample2", + "public_name": "Public2", + "supplier_name": "supplier2", + "taxon_id": "9606", + "donor_id": "donor2", + "country_of_origin": "United Kingdom", + "accession_number": "AN1235", + "date_of_sample_collection": my_date.strftime("%Y-%m-%d"), + }, + }, + ], + }, + ], + "tubes_attributes": [ + { + "barcode": "987", + "type": "tubes", + "request": { + "external_study_id": "dd490ee5-fd1d-456d-99fd-eb9d3861e014", + "library_type": "library", + "cost_code": "S1234", + }, + "sample": { + "external_id": "8860a6b4-1236-451c-aba2-a3129c38c0fc", + "name": "test3", + "species": "test " "species", + "priority_level": "High", + "sanger_sample_id": "sample3", + "public_name": "Public3", + "supplier_name": "supplier3", + "taxon_id": "9606", + "donor_id": "donor3", + "country_of_origin": "United Kingdom", + "accession_number": "AN1236", + "date_of_sample_collection": my_date.strftime("%Y-%m-%d"), + }, + }, + { + "barcode": "876", + "type": "tubes", + "request": { + "external_study_id": "dd490ee5-fd1d-456d-99fd-eb9d3861e014", + "library_type": "library", + "cost_code": "S4567", + }, + "sample": { + "external_id": "8860a6b4-1237-451c-aba2-a3129c38c0fc", + "name": "test4", + "species": "test " "species", + "priority_level": "Low", + "sanger_sample_id": "sample4", + "public_name": "Public4", + "supplier_name": "supplier4", + "taxon_id": "9606", + "donor_id": "donor4", + "country_of_origin": "United Kingdom", + "accession_number": "AN1237", + "date_of_sample_collection": my_date.strftime("%Y-%m-%d"), + }, + }, + ], + }, + } + } + + def test_output_traction_message_can_detect_errors_on_sent(config): vt = valid_traction_message() From 7044f67ca66eb2c1aca329026b020a39e2b8d1d2 Mon Sep 17 00:00:00 2001 From: Stuart McHattie Date: Tue, 13 Feb 2024 11:17:41 +0000 Subject: [PATCH 34/40] Fix some documentation inaccuracies --- tol_lab_share/messages/output_traction_message.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tol_lab_share/messages/output_traction_message.py b/tol_lab_share/messages/output_traction_message.py index 63f13ebf..e224599a 100644 --- a/tol_lab_share/messages/output_traction_message.py +++ b/tol_lab_share/messages/output_traction_message.py @@ -58,7 +58,7 @@ def validate(self) -> bool: class Serializer: - """Class to manage the serialization to JSON of the request received as argument in the constructor.""" + """Class to manage the serialization of requests.""" @staticmethod def request_payload(request: OutputTractionMessageRequest) -> dict[str, Any]: @@ -156,7 +156,7 @@ def payload(self) -> dict[str, Any]: class TubeSerializer(Serializer): - """Class to manage the serialization of plate requests.""" + """Class to manage the serialization of tube requests.""" def __init__(self, tube_request: OutputTractionMessageRequest): """Constructor. From 608340edd479521b773dd90a3b116ff50cacd231 Mon Sep 17 00:00:00 2001 From: Stuart McHattie Date: Tue, 13 Feb 2024 11:20:28 +0000 Subject: [PATCH 35/40] Remove unneeded repetition for request_payload --- .../messages/output_traction_message.py | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/tol_lab_share/messages/output_traction_message.py b/tol_lab_share/messages/output_traction_message.py index e224599a..4e481167 100644 --- a/tol_lab_share/messages/output_traction_message.py +++ b/tol_lab_share/messages/output_traction_message.py @@ -71,20 +71,16 @@ def request_payload(request: OutputTractionMessageRequest) -> dict[str, Any]: Returns: dict[str, Any]: A dictionary containing the Traction "request" payload for the request. """ - is_ont = request.library_type and ("ONT" in request.library_type) - if is_ont: - return { - "data_type": "basecalls", - "library_type": request.library_type, - "external_study_id": request.study_uuid, - "cost_code": request.cost_code, - } - else: - return { - "library_type": request.library_type, - "external_study_id": request.study_uuid, - "cost_code": request.cost_code, - } + request_payload = { + "library_type": request.library_type, + "external_study_id": request.study_uuid, + "cost_code": request.cost_code, + } + + if request.library_type and ("ONT" in request.library_type): + request_payload["data_type"] = "basecalls" + + return request_payload @staticmethod def sample_payload(request: OutputTractionMessageRequest) -> dict[str, Any]: From 83eb9979e0eb6be8a292efa4bab5f8f341ecb7cb Mon Sep 17 00:00:00 2001 From: Stuart McHattie Date: Wed, 14 Feb 2024 11:53:11 +0000 Subject: [PATCH 36/40] Remove unneeded blank lines in code block --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index b21b6959..bbd6ebb2 100644 --- a/README.md +++ b/README.md @@ -86,8 +86,6 @@ You might want to add these to your `~/.zshrc` file: ```bash export CPPFLAGS="-I$(brew --prefix)/include" export LDFLAGS="-L$(brew --prefix)/lib" - - ``` ![TOL Labware Production Flow - Architecture](https://github.com/sanger/tol-lab-share/assets/519327/5356846a-6d9b-4b8d-8ffb-af26d0776222) From 115fa0568b76bc1842ab4dc9fc83a3693819b98d Mon Sep 17 00:00:00 2001 From: Stuart McHattie Date: Wed, 14 Feb 2024 16:13:44 +0000 Subject: [PATCH 37/40] Update release version to 0.4.0 --- .release-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.release-version b/.release-version index 0d91a54c..1d0ba9ea 100644 --- a/.release-version +++ b/.release-version @@ -1 +1 @@ -0.3.0 +0.4.0 From d7e226f7d593a157657b1fec1dafc0a31ca4918d Mon Sep 17 00:00:00 2001 From: Stuart McHattie Date: Thu, 15 Feb 2024 09:39:53 +0000 Subject: [PATCH 38/40] Set version to 1.0.0 --- .release-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.release-version b/.release-version index 1d0ba9ea..3eefcb9d 100644 --- a/.release-version +++ b/.release-version @@ -1 +1 @@ -0.4.0 +1.0.0 From cd8101e821942341a03f6800a200df96f617e74d Mon Sep 17 00:00:00 2001 From: Stuart McHattie Date: Thu, 15 Feb 2024 16:41:06 +0000 Subject: [PATCH 39/40] Simplify location creation code Remove the padding method and turn the location creation into a list comprehension --- .../definitions/labware_type.py | 39 +++++-------------- 1 file changed, 9 insertions(+), 30 deletions(-) diff --git a/tol_lab_share/message_properties/definitions/labware_type.py b/tol_lab_share/message_properties/definitions/labware_type.py index 1e3dc00f..0d71c006 100644 --- a/tol_lab_share/message_properties/definitions/labware_type.py +++ b/tol_lab_share/message_properties/definitions/labware_type.py @@ -1,3 +1,4 @@ +import string from .message_property import MessageProperty from tol_lab_share import error_codes import logging @@ -31,39 +32,17 @@ def check_labware_type(self): self.trigger_error(error_codes.ERROR_6_LABWARE_TYPE, text=f"input: {self._input.value}") return result - def pad_number(self, number: int) -> str: - """Given a number, it generates a string of size 2 padded with zeros at the left representing - the same value. Eg: 3 would be converted to '03', while 12 would be converted to '12'. - Parameters: - number (int) value we want to convert - Returns: - str with the padded value - """ - padded_number = str(number) - if len(padded_number) == 1: - padded_number = f"0{padded_number}" - return padded_number - - def _locations_for_plate12x8_column_order(self) -> list[str]: - """It generates the list of all valid locations for a labware type following column - order. Eg: ['A01', 'B01', 'C01', .... 'H12'] - Returns: - list[str] with al the valid locations - """ - locations = [] - for number in range(12): - for letter_ord in range(ord("A"), ord("H") + 1): - locations.append(f"{chr(letter_ord)}{self.pad_number(number+1)}") - return locations - def valid_locations(self) -> list[str]: - """It returns the list of valid locations for the labware type. If the labware type is - a tube, it returns ane empty list. If it is a plate 12x8 it returns all locations in - column order. + """It returns the list of valid locations for the labware type. + If the labware type is a tube, it returns ane empty list. + If it is a plate 12x8 it returns all locations in column order. i.e. ['A01', 'B01', 'C01', .... 'H12'] + Returns: - list[str] with all the valid locations + list[str]: A list with all the valid locations. """ if self.value == "Plate12x8": - return self._locations_for_plate12x8_column_order() + return [ + f"{letter}{str(number).zfill(2)}" for number in range(1, 13) for letter in string.ascii_uppercase[:8] + ] else: return [] From d6c81b1f32e07f9bc2b47b583bbe60314efa1cea Mon Sep 17 00:00:00 2001 From: Stuart McHattie Date: Thu, 15 Feb 2024 16:47:46 +0000 Subject: [PATCH 40/40] Use datetime.utcnow() in tests Not strictly necessary, but it's better practice than the locale affected now() method --- tests/conftest.py | 8 +++---- .../data/examples_create_labware_messages.py | 20 +++++++++--------- .../definitions/test_message_property.py | 2 +- .../messages/test_output_traction_message.py | 10 ++++----- tests/messages/test_traction_qc_message.py | 21 ++++++------------- tools/demo/testing_data.py | 14 ++++++------- 6 files changed, 33 insertions(+), 42 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 34f82b79..ec02422b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -138,7 +138,7 @@ def valid_sample(): "countryOfOrigin": "United Kingdom", "genomeSize": "14", "accessionNumber": "EE1234", - "sampleCollectionDateUtc": datetime.now(), + "sampleCollectionDateUtc": datetime.utcnow(), "costCode": "S1234", "shearedFemtoFragmentSize": "8", "postSPRIConcentration": "9", @@ -147,7 +147,7 @@ def valid_sample(): "finalNanoDrop230": "200", "finalNanoDrop": "150", "shearingAndQCComments": "", - "dateSubmittedUTC": datetime.now(), + "dateSubmittedUTC": datetime.utcnow(), "priorityLevel": "Medium", } @@ -171,7 +171,7 @@ def invalid_sample(): "genomeSize": "14", "accessionNumber": "EE1234", "costCode": 1234, - "sampleCollectionDateUtc": datetime.now(), + "sampleCollectionDateUtc": datetime.utcnow(), "shearedFemtoFragmentSize": "8", "postSPRIConcentration": "9", "postSPRIVolume": "10", @@ -179,7 +179,7 @@ def invalid_sample(): "finalNanoDrop230": 200, "finalNanoDrop": "150", "shearingAndQCComments": "", - "dateSubmittedUTC": datetime.now(), + "dateSubmittedUTC": datetime.utcnow(), "priorityLevel": "Medium", } diff --git a/tests/data/examples_create_labware_messages.py b/tests/data/examples_create_labware_messages.py index 2ec57687..6592ee21 100644 --- a/tests/data/examples_create_labware_messages.py +++ b/tests/data/examples_create_labware_messages.py @@ -3,7 +3,7 @@ TEST_CREATE_LABWARE_MSG_OBJECT: dict[str, Any] = { "messageUuid": "b01aa0ad-7b19-4f94-87e9-70d74fb8783c".encode(), - "messageCreateDateUtc": datetime.now(), + "messageCreateDateUtc": datetime.utcnow(), "labware": { "labwareType": "Plate12x8", "barcode": "BARCODE001", @@ -24,7 +24,7 @@ "countryOfOrigin": "United Kingdom", "genomeSize": "14", "accessionNumber": "EE3383", - "sampleCollectionDateUtc": datetime.now(), + "sampleCollectionDateUtc": datetime.utcnow(), "costCode": "S1234", "shearedFemtoFragmentSize": "5", "postSPRIConcentration": "10", @@ -33,7 +33,7 @@ "finalNanoDrop230": "6", "finalNanoDrop": "7", "shearingAndQCComments": "Comments", - "dateSubmittedUTC": datetime.now(), + "dateSubmittedUTC": datetime.utcnow(), "priorityLevel": "Medium", }, { @@ -52,7 +52,7 @@ "countryOfOrigin": "United Kingdom", "genomeSize": "14", "accessionNumber": "EE3383", - "sampleCollectionDateUtc": datetime.now(), + "sampleCollectionDateUtc": datetime.utcnow(), "costCode": "S1234", "shearedFemtoFragmentSize": "5", "postSPRIConcentration": "10", @@ -61,7 +61,7 @@ "finalNanoDrop230": "6", "finalNanoDrop": "7", "shearingAndQCComments": "Comments", - "dateSubmittedUTC": datetime.now(), + "dateSubmittedUTC": datetime.utcnow(), "priorityLevel": "Medium", }, ], @@ -71,7 +71,7 @@ TEST_INVALID_CREATE_LABWARE_MSG_OBJECT: dict[str, Any] = { "messageUuid": "b01aa0ad7b19-4f94-87e9-70d74fb8783c".encode(), - "messageCreateDateUtc": datetime.now(), + "messageCreateDateUtc": datetime.utcnow(), "labware": { "labwareType": "Plate12x8", "barcode": "BARCODE001", @@ -92,7 +92,7 @@ "countryOfOrigin": "United Kingdom", "genomeSize": "14", "accessionNumber": "EE3383", - "sampleCollectionDateUtc": datetime.now(), + "sampleCollectionDateUtc": datetime.utcnow(), "costCode": "S1234", "shearedFemtoFragmentSize": "5", "postSPRIConcentration": "10", @@ -101,7 +101,7 @@ "finalNanoDrop230": "6", "finalNanoDrop": 7, "shearingAndQCComments": "Comments", - "dateSubmittedUTC": datetime.now(), + "dateSubmittedUTC": datetime.utcnow(), "priorityLevel": "Medium", }, { @@ -120,7 +120,7 @@ "countryOfOrigin": "United Kingdom", "genomeSize": "14", "accessionNumber": "EE3383", - "sampleCollectionDateUtc": datetime.now(), + "sampleCollectionDateUtc": datetime.utcnow(), "costCode": "S1234", "shearedFemtoFragmentSize": "5", "postSPRIConcentration": "10", @@ -129,7 +129,7 @@ "finalNanoDrop230": "6", "finalNanoDrop": "7", "shearingAndQCComments": "Comments", - "dateSubmittedUTC": datetime.now(), + "dateSubmittedUTC": datetime.utcnow(), "priorityLevel": "Medium", }, ], diff --git a/tests/message_properties/definitions/test_message_property.py b/tests/message_properties/definitions/test_message_property.py index b77ffe25..d009f192 100644 --- a/tests/message_properties/definitions/test_message_property.py +++ b/tests/message_properties/definitions/test_message_property.py @@ -134,7 +134,7 @@ def test_message_property_check_is_date_utc(): instance = MessageProperty(Input(None)) assert instance.check_is_date_utc() is False - instance = MessageProperty(Input(datetime.now())) + instance = MessageProperty(Input(datetime.utcnow())) assert instance.check_is_date_utc() is True instance = MessageProperty(Input({})) diff --git a/tests/messages/test_output_traction_message.py b/tests/messages/test_output_traction_message.py index 4380fa42..b6aeb57c 100644 --- a/tests/messages/test_output_traction_message.py +++ b/tests/messages/test_output_traction_message.py @@ -37,7 +37,7 @@ def test_output_traction_message_can_validate(): def test_output_traction_message_can_generate_payload_for_plates(): - my_date = datetime.now() + my_date = datetime.utcnow() instance = OutputTractionMessage() request = instance.create_request() @@ -144,7 +144,7 @@ def test_output_traction_message_can_generate_payload_for_plates(): def test_output_traction_message_can_generate_payload_for_multiple_plates(): - my_date = datetime.now() + my_date = datetime.utcnow() instance = OutputTractionMessage() # Mix the order of the requests to ensure the grouping for plates works. @@ -348,7 +348,7 @@ def test_output_traction_message_can_generate_payload_for_multiple_plates(): def test_output_traction_message_can_generate_payload_for_ont_library_types(): - my_date = datetime.now() + my_date = datetime.utcnow() instance = OutputTractionMessage() request = instance.create_request() @@ -457,7 +457,7 @@ def test_output_traction_message_can_generate_payload_for_ont_library_types(): def test_output_traction_message_can_generate_payload_for_tubes(): - my_date = datetime.now() + my_date = datetime.utcnow() instance = OutputTractionMessage() request = instance.create_request() @@ -558,7 +558,7 @@ def test_output_traction_message_can_generate_payload_for_tubes(): def test_output_traction_message_can_generate_payload_for_mix_of_plate_and_tubes(): - my_date = datetime.now() + my_date = datetime.utcnow() instance = OutputTractionMessage() request = instance.create_request() diff --git a/tests/messages/test_traction_qc_message.py b/tests/messages/test_traction_qc_message.py index 678e2533..3d4499ff 100644 --- a/tests/messages/test_traction_qc_message.py +++ b/tests/messages/test_traction_qc_message.py @@ -9,18 +9,9 @@ logger = logging.getLogger(__name__) -class FreezeDate(datetime): - @classmethod - def now(cls): - return cls(2023, 7, 11, 12, 29, 11, 564246) - - -datetime = FreezeDate # type: ignore - - class TestTractionQcMessage: @pytest.fixture() - def valid_traction_qc_message(self): + def valid_traction_qc_message(self, freezer): traction_qc_message = TractionQcMessage() request = traction_qc_message.create_request() @@ -33,7 +24,7 @@ def valid_traction_qc_message(self): request.final_nano_drop_230 = "230" request.final_nano_drop = "200" request.shearing_qc_comments = "Comments" - request.date_submitted_utc = datetime.now().timestamp() * 1000 + request.date_submitted_utc = datetime.utcnow().timestamp() * 1000 request = traction_qc_message.create_request() request.sanger_sample_id = "sanger_sample_id_DDD2" @@ -45,7 +36,7 @@ def valid_traction_qc_message(self): request.final_nano_drop_230 = "130" request.final_nano_drop = "100" request.shearing_qc_comments = "" - request.date_submitted_utc = datetime.now().timestamp() * 1000 + request.date_submitted_utc = datetime.utcnow().timestamp() * 1000 return traction_qc_message @@ -66,7 +57,7 @@ def test_validate_traction_qc_message(self, valid_traction_qc_message, invalid_t assert not invalid_traction_qc_message.validate() assert valid_traction_qc_message.validate() - def test_can_generate_correct_payload(self, valid_traction_qc_message): + def test_can_generate_correct_payload(self, valid_traction_qc_message, freezer): payload = valid_traction_qc_message.payload() expected_payload = { @@ -81,7 +72,7 @@ def test_can_generate_correct_payload(self, valid_traction_qc_message): "post_spri_volume": "20", "sheared_femto_fragment_size": "5", "shearing_qc_comments": "Comments", - "date_submitted": datetime.now().timestamp() * 1000, + "date_submitted": datetime.utcnow().timestamp() * 1000, "labware_barcode": "FD20706500", "sample_external_id": "sanger_sample_id_DDD", }, @@ -92,7 +83,7 @@ def test_can_generate_correct_payload(self, valid_traction_qc_message): "post_spri_concentration": "10", "post_spri_volume": "30", "sheared_femto_fragment_size": "9", - "date_submitted": datetime.now().timestamp() * 1000, + "date_submitted": datetime.utcnow().timestamp() * 1000, "labware_barcode": "FD20706501", "sample_external_id": "sanger_sample_id_DDD2", }, diff --git a/tools/demo/testing_data.py b/tools/demo/testing_data.py index 2e91a4f7..0fa05d90 100644 --- a/tools/demo/testing_data.py +++ b/tools/demo/testing_data.py @@ -20,7 +20,7 @@ def build_create_labware_96_msg(unique_id, num_msg): unique_id_lab = f"TOLTESTING-PLATE-{unique_id}-{num_msg}" return { "messageUuid": str(uuid4()).encode(), - "messageCreateDateUtc": datetime.now().timestamp() * 1000, + "messageCreateDateUtc": datetime.utcnow().timestamp() * 1000, "labware": { "labwareType": "Plate12x8", "barcode": barcode_for_unique_id("PLATE", unique_id, num_msg), @@ -42,7 +42,7 @@ def build_create_labware_96_msg(unique_id, num_msg): "genomeSize": "1", "accessionNumber": "A1234", "costCode": "S1234", - "sampleCollectionDateUtc": datetime.now().timestamp() * 1000, + "sampleCollectionDateUtc": datetime.utcnow().timestamp() * 1000, "shearedFemtoFragmentSize": "5", "postSPRIConcentration": "10", "postSPRIVolume": "20", @@ -50,7 +50,7 @@ def build_create_labware_96_msg(unique_id, num_msg): "finalNanoDrop230": "6", "finalNanoDrop": "7", "shearingAndQCComments": "Comments", - "dateSubmittedUTC": datetime.now().timestamp() * 1000, + "dateSubmittedUTC": datetime.utcnow().timestamp() * 1000, "priorityLevel": "Medium", } for letter in range(ord("A"), ord("H") + 1) @@ -64,7 +64,7 @@ def build_create_tube_msg(unique_id, num_msg): unique_id_lab = f"TOLTESTING-TUBE-{unique_id}-{num_msg}" return { "messageUuid": str(uuid4()).encode(), - "messageCreateDateUtc": datetime.now().timestamp() * 1000, + "messageCreateDateUtc": datetime.utcnow().timestamp() * 1000, "labware": { "labwareType": "Tube", "barcode": barcode_for_unique_id("TUBE", unique_id, num_msg), @@ -86,7 +86,7 @@ def build_create_tube_msg(unique_id, num_msg): "genomeSize": "1", "accessionNumber": "A1234", "costCode": "S1234", - "sampleCollectionDateUtc": datetime.now().timestamp() * 1000, + "sampleCollectionDateUtc": datetime.utcnow().timestamp() * 1000, "shearedFemtoFragmentSize": "5", "postSPRIConcentration": "10", "postSPRIVolume": "20", @@ -94,7 +94,7 @@ def build_create_tube_msg(unique_id, num_msg): "finalNanoDrop230": "6", "finalNanoDrop": "7", "shearingAndQCComments": "Comments", - "dateSubmittedUTC": datetime.now().timestamp() * 1000, + "dateSubmittedUTC": datetime.utcnow().timestamp() * 1000, "priorityLevel": "Medium", } ], @@ -105,7 +105,7 @@ def build_create_tube_msg(unique_id, num_msg): def build_update_labware_msg(sample_msg): return { "messageUuid": str(uuid4()).encode(), - "messageCreateDateUtc": datetime.now().timestamp() * 1000, + "messageCreateDateUtc": datetime.utcnow().timestamp() * 1000, "sampleUpdates": [ { "sampleUuid": sample_msg["labware"]["samples"][3]["sampleUuid"],