From 126bfb3fe9b89be6821e6afd5a5e7505af0f2c20 Mon Sep 17 00:00:00 2001 From: Eduardo Martin Rojo Date: Mon, 14 Mar 2022 14:28:48 +0000 Subject: [PATCH 01/33] Added log lines to monitor baracoda accesss --- baracoda/barcodes.py | 3 +++ baracoda/config/logging.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/baracoda/barcodes.py b/baracoda/barcodes.py index ebe94ff..69da014 100644 --- a/baracoda/barcodes.py +++ b/baracoda/barcodes.py @@ -17,6 +17,7 @@ @bp.post("/barcodes_group//new") # type: ignore def get_new_barcode_group(prefix: str) -> Tuple[Any, int]: try: + logger.debug(f"Creating a barcode group for '{ prefix }'") count = get_count_param() operator = BarcodeOperations(prefix=prefix) @@ -37,6 +38,7 @@ def get_new_barcode_group(prefix: str) -> Tuple[Any, int]: @bp.post("/barcodes//new") # type: ignore def get_new_barcode(prefix: str) -> Tuple[Any, int]: try: + logger.debug(f"Creating a barcode for '{ prefix }'") operator = BarcodeOperations(prefix=prefix) barcode = operator.create_barcode() @@ -51,6 +53,7 @@ def get_new_barcode(prefix: str) -> Tuple[Any, int]: @bp.get("/barcodes//last") # type: ignore def get_last_barcode(prefix: str) -> Tuple[Any, int]: try: + logger.debug(f"Obtaining last from '{ prefix }'") operator = BarcodeOperations(prefix=prefix) barcode = operator.get_last_barcode(prefix) diff --git a/baracoda/config/logging.py b/baracoda/config/logging.py index 1a31c91..242d1f9 100644 --- a/baracoda/config/logging.py +++ b/baracoda/config/logging.py @@ -37,7 +37,7 @@ "filters": ["package_path"], }, "console": { - "level": "INFO", + "level": "DEBUG", "class": "logging.StreamHandler", "formatter": "verbose", }, From 2cdad5e4e7ec58fdb8584634483239212f9db0a0 Mon Sep 17 00:00:00 2001 From: Eduardo Martin Rojo Date: Mon, 14 Mar 2022 14:33:58 +0000 Subject: [PATCH 02/33] Change logs so it will log debug --- baracoda/config/logging.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/baracoda/config/logging.py b/baracoda/config/logging.py index 242d1f9..b55c9cd 100644 --- a/baracoda/config/logging.py +++ b/baracoda/config/logging.py @@ -50,7 +50,7 @@ "loggers": { "baracoda": { "handlers": ["console", "slack"], - "level": "INFO", + "level": "DEBUG", "propagate": True, }, }, From 221a1f5aec54fdd0d1d01d34fd9d4ca1ee351a3f Mon Sep 17 00:00:00 2001 From: Eduardo Martin Rojo Date: Wed, 23 Mar 2022 17:06:46 +0000 Subject: [PATCH 03/33] First version --- Pipfile | 1 + Pipfile.lock | 381 +++++++++--------- .../c742377339a6_add_seq_dnaplate_sequence.py | 24 ++ baracoda/__init__.py | 2 + baracoda/config/defaults.py | 107 ++--- baracoda/config/test.py | 1 + baracoda/formats.py | 44 +- baracoda/operations.py | 9 +- baracoda/plate_barcodes.py | 32 ++ docker-compose.yml | 2 +- tests/conftest.py | 8 +- tests/data/fixture_data.py | 13 +- tests/test_formats.py | 10 +- tests/test_helpers.py | 3 +- 14 files changed, 370 insertions(+), 267 deletions(-) create mode 100644 alembic/versions/c742377339a6_add_seq_dnaplate_sequence.py create mode 100644 baracoda/plate_barcodes.py diff --git a/Pipfile b/Pipfile index 8e9a62f..70da577 100644 --- a/Pipfile +++ b/Pipfile @@ -25,6 +25,7 @@ psycopg2 = "~=2.9" python-dotenv = "~=0.19" slackclient = "~=2.5" sqlalchemy = "~=1.4" +dicttoxml = "*" [requires] python_version = "3.8" diff --git a/Pipfile.lock b/Pipfile.lock index 573bc77..8f3b743 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "4c8942d64138982f0fc190d3baf1af0d537595da7fee0c47b339a23fce107cd9" + "sha256": "a4cdc3e7010217ec72763b8d48950899b2c0dff1193c2900e1b970645a4f83af" }, "pipfile-spec": 6, "requires": { @@ -104,11 +104,11 @@ }, "alembic": { "hashes": [ - "sha256:a21fedebb3fb8f6bbbba51a11114f08c78709377051384c9c5ead5705ee93a51", - "sha256:e78be5b919f5bb184e3e0e2dd1ca986f2362e29a2bc933c446fe89f39dbe4e9c" + "sha256:29be0856ec7591c39f4e1cb10f198045d890e6e2274cf8da80cb5e721a09642b", + "sha256:4961248173ead7ce8a21efb3de378f13b8398e6630fab0eb258dc74a8af24c58" ], "index": "pypi", - "version": "==1.6.5" + "version": "==1.7.7" }, "async-timeout": { "hashes": [ @@ -157,6 +157,13 @@ "index": "pypi", "version": "==6.6.0" }, + "dicttoxml": { + "hashes": [ + "sha256:ea44cc4ec6c0f85098c57a431a1ee891b3549347b07b7414c8a24611ecf37e45" + ], + "index": "pypi", + "version": "==1.7.4" + }, "flask": { "hashes": [ "sha256:59da8a3170004800a2837844bfa84d49b022550616070f7cb1a659682b2e7c9f", @@ -317,13 +324,29 @@ "markers": "python_version >= '3.5'", "version": "==3.3" }, + "importlib-metadata": { + "hashes": [ + "sha256:1208431ca90a8cca1a6b8af391bb53c1a2db74e5d1cef6ddced95d4b2062edc6", + "sha256:ea4c597ebf37142f827b8f39299579e31685c31d3a438b59f469406afd0f2539" + ], + "markers": "python_version < '3.9'", + "version": "==4.11.3" + }, + "importlib-resources": { + "hashes": [ + "sha256:33a95faed5fc19b4bc16b29a6eeae248a3fe69dd55d4d229d2b480e23eeaad45", + "sha256:d756e2f85dd4de2ba89be0b21dba2a3bbec2e871a42a3a16719258a11f87506b" + ], + "markers": "python_version < '3.9'", + "version": "==5.4.0" + }, "itsdangerous": { "hashes": [ - "sha256:29285842166554469a56d427addc0843914172343784cb909695fdbe90a3e129", - "sha256:d848fcb8bc7d507c4546b448574e8a44fc4ea2ba84ebf8d783290d53e81992f5" + "sha256:7b7d3023cd35d9cb0c1fd91392f8c95c6fa02c59bf8ad64b8849be3401b95afb", + "sha256:935642cd4b987cdbee7210080004033af76306757ff8b4c0a506a4b6e06f02cf" ], "markers": "python_version >= '3.7'", - "version": "==2.1.0" + "version": "==2.1.1" }, "jinja2": { "hashes": [ @@ -335,57 +358,57 @@ }, "mako": { "hashes": [ - "sha256:4e9e345a41924a954251b95b4b28e14a301145b544901332e658907a7464b6b2", - "sha256:afaf8e515d075b22fad7d7b8b30e4a1c90624ff2f3733a06ec125f5a5f043a57" + "sha256:23aab11fdbbb0f1051b93793a58323ff937e98e34aece1c4219675122e57e4ba", + "sha256:9a7c7e922b87db3686210cf49d5d767033a41d4010b284e747682c92bddd8b39" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.1.6" + "markers": "python_version >= '3.7'", + "version": "==1.2.0" }, "markupsafe": { "hashes": [ - "sha256:023af8c54fe63530545f70dd2a2a7eed18d07a9a77b94e8bf1e2ff7f252db9a3", - "sha256:09c86c9643cceb1d87ca08cdc30160d1b7ab49a8a21564868921959bd16441b8", - "sha256:142119fb14a1ef6d758912b25c4e803c3ff66920635c44078666fe7cc3f8f759", - "sha256:1d1fb9b2eec3c9714dd936860850300b51dbaa37404209c8d4cb66547884b7ed", - "sha256:204730fd5fe2fe3b1e9ccadb2bd18ba8712b111dcabce185af0b3b5285a7c989", - "sha256:24c3be29abb6b34052fd26fc7a8e0a49b1ee9d282e3665e8ad09a0a68faee5b3", - "sha256:290b02bab3c9e216da57c1d11d2ba73a9f73a614bbdcc027d299a60cdfabb11a", - "sha256:3028252424c72b2602a323f70fbf50aa80a5d3aa616ea6add4ba21ae9cc9da4c", - "sha256:30c653fde75a6e5eb814d2a0a89378f83d1d3f502ab710904ee585c38888816c", - "sha256:3cace1837bc84e63b3fd2dfce37f08f8c18aeb81ef5cf6bb9b51f625cb4e6cd8", - "sha256:4056f752015dfa9828dce3140dbadd543b555afb3252507348c493def166d454", - "sha256:454ffc1cbb75227d15667c09f164a0099159da0c1f3d2636aa648f12675491ad", - "sha256:598b65d74615c021423bd45c2bc5e9b59539c875a9bdb7e5f2a6b92dfcfc268d", - "sha256:599941da468f2cf22bf90a84f6e2a65524e87be2fce844f96f2dd9a6c9d1e635", - "sha256:5ddea4c352a488b5e1069069f2f501006b1a4362cb906bee9a193ef1245a7a61", - "sha256:62c0285e91414f5c8f621a17b69fc0088394ccdaa961ef469e833dbff64bd5ea", - "sha256:679cbb78914ab212c49c67ba2c7396dc599a8479de51b9a87b174700abd9ea49", - "sha256:6e104c0c2b4cd765b4e83909cde7ec61a1e313f8a75775897db321450e928cce", - "sha256:736895a020e31b428b3382a7887bfea96102c529530299f426bf2e636aacec9e", - "sha256:75bb36f134883fdbe13d8e63b8675f5f12b80bb6627f7714c7d6c5becf22719f", - "sha256:7d2f5d97fcbd004c03df8d8fe2b973fe2b14e7bfeb2cfa012eaa8759ce9a762f", - "sha256:80beaf63ddfbc64a0452b841d8036ca0611e049650e20afcb882f5d3c266d65f", - "sha256:84ad5e29bf8bab3ad70fd707d3c05524862bddc54dc040982b0dbcff36481de7", - "sha256:8da5924cb1f9064589767b0f3fc39d03e3d0fb5aa29e0cb21d43106519bd624a", - "sha256:961eb86e5be7d0973789f30ebcf6caab60b844203f4396ece27310295a6082c7", - "sha256:96de1932237abe0a13ba68b63e94113678c379dca45afa040a17b6e1ad7ed076", - "sha256:a0a0abef2ca47b33fb615b491ce31b055ef2430de52c5b3fb19a4042dbc5cadb", - "sha256:b2a5a856019d2833c56a3dcac1b80fe795c95f401818ea963594b345929dffa7", - "sha256:b8811d48078d1cf2a6863dafb896e68406c5f513048451cd2ded0473133473c7", - "sha256:c532d5ab79be0199fa2658e24a02fce8542df196e60665dd322409a03db6a52c", - "sha256:d3b64c65328cb4cd252c94f83e66e3d7acf8891e60ebf588d7b493a55a1dbf26", - "sha256:d4e702eea4a2903441f2735799d217f4ac1b55f7d8ad96ab7d4e25417cb0827c", - "sha256:d5653619b3eb5cbd35bfba3c12d575db2a74d15e0e1c08bf1db788069d410ce8", - "sha256:d66624f04de4af8bbf1c7f21cc06649c1c69a7f84109179add573ce35e46d448", - "sha256:e67ec74fada3841b8c5f4c4f197bea916025cb9aa3fe5abf7d52b655d042f956", - "sha256:e6f7f3f41faffaea6596da86ecc2389672fa949bd035251eab26dc6697451d05", - "sha256:f02cf7221d5cd915d7fa58ab64f7ee6dd0f6cddbb48683debf5d04ae9b1c2cc1", - "sha256:f0eddfcabd6936558ec020130f932d479930581171368fd728efcfb6ef0dd357", - "sha256:fabbe18087c3d33c5824cb145ffca52eccd053061df1d79d4b66dafa5ad2a5ea", - "sha256:fc3150f85e2dbcf99e65238c842d1cfe69d3e7649b19864c1cc043213d9cd730" + "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003", + "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88", + "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5", + "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7", + "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a", + "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603", + "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1", + "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135", + "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247", + "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6", + "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601", + "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77", + "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02", + "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e", + "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63", + "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f", + "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980", + "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b", + "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812", + "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff", + "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96", + "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1", + "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925", + "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a", + "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6", + "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e", + "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f", + "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4", + "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f", + "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3", + "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c", + "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a", + "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417", + "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a", + "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a", + "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37", + "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452", + "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933", + "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a", + "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7" ], "markers": "python_version >= '3.7'", - "version": "==2.1.0" + "version": "==2.1.1" }, "multidict": { "hashes": [ @@ -469,14 +492,6 @@ "index": "pypi", "version": "==2.9.3" }, - "python-dateutil": { - "hashes": [ - "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", - "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.8.2" - }, "python-dotenv": { "hashes": [ "sha256:32b2bdc1873fd3a3c346da1c6db83d0053c3c62f28f1f38516070c4c8971b1d3", @@ -485,23 +500,13 @@ "index": "pypi", "version": "==0.19.2" }, - "python-editor": { - "hashes": [ - "sha256:1bf6e860a8ad52a14c3ee1252d5dc25b2030618ed80c022598f00176adc8367d", - "sha256:51fda6bcc5ddbbb7063b2af7509e43bd84bfc32a4ff71349ec7847713882327b", - "sha256:5f98b069316ea1c2ed3f67e7f5df6c0d8f10b689964a4a811ff64f0106819ec8", - "sha256:c3da2053dbab6b29c94e43c486ff67206eafbe7eb52dbec7390b5e2fb05aac77", - "sha256:ea87e17f6ec459e780e4221f295411462e0d0810858e055fc514684350a2f522" - ], - "version": "==1.0.4" - }, "setuptools": { "hashes": [ - "sha256:2347b2b432c891a863acadca2da9ac101eae6169b1d3dfee2ec605ecd50dbfe5", - "sha256:e4f30b9f84e5ab3decf945113119649fec09c1fc3507c6ebffec75646c56e62b" + "sha256:6599055eeb23bfef457d5605d33a4d68804266e6cb430b0fb12417c5efeae36c", + "sha256:782ef48d58982ddb49920c11a0c5c9c0b02e7d7d1c2ad0aa44e1a1e133051c96" ], "markers": "python_version >= '3.7'", - "version": "==60.9.3" + "version": "==60.10.0" }, "six": { "hashes": [ @@ -521,45 +526,44 @@ }, "sqlalchemy": { "hashes": [ - "sha256:05fa14f279d43df68964ad066f653193187909950aa0163320b728edfc400167", - "sha256:0ddc5e5ccc0160e7ad190e5c61eb57560f38559e22586955f205e537cda26034", - "sha256:15a03261aa1e68f208e71ae3cd845b00063d242cbf8c87348a0c2c0fc6e1f2ac", - "sha256:289465162b1fa1e7a982f8abe59d26a8331211cad4942e8031d2b7db1f75e649", - "sha256:2e216c13ecc7fcdcbb86bb3225425b3ed338e43a8810c7089ddb472676124b9b", - "sha256:2fd4d3ca64c41dae31228b80556ab55b6489275fb204827f6560b65f95692cf3", - "sha256:330eb45395874cc7787214fdd4489e2afb931bc49e0a7a8f9cd56d6e9c5b1639", - "sha256:3c7ed6c69debaf6198fadb1c16ae1253a29a7670bbf0646f92582eb465a0b999", - "sha256:4ad31cec8b49fd718470328ad9711f4dc703507d434fd45461096da0a7135ee0", - "sha256:57205844f246bab9b666a32f59b046add8995c665d9ecb2b7b837b087df90639", - "sha256:582b59d1e5780a447aada22b461e50b404a9dc05768da1d87368ad8190468418", - "sha256:5e9c7b3567edbc2183607f7d9f3e7e89355b8f8984eec4d2cd1e1513c8f7b43f", - "sha256:6a01ec49ca54ce03bc14e10de55dfc64187a2194b3b0e5ac0fdbe9b24767e79e", - "sha256:6f22c040d196f841168b1456e77c30a18a3dc16b336ddbc5a24ce01ab4e95ae0", - "sha256:81f2dd355b57770fdf292b54f3e0a9823ec27a543f947fa2eb4ec0df44f35f0d", - "sha256:85e4c244e1de056d48dae466e9baf9437980c19fcde493e0db1a0a986e6d75b4", - "sha256:8d0949b11681380b4a50ac3cd075e4816afe9fa4a8c8ae006c1ca26f0fa40ad8", - "sha256:975f5c0793892c634c4920057da0de3a48bbbbd0a5c86f5fcf2f2fedf41b76da", - "sha256:9e4fb2895b83993831ba2401b6404de953fdbfa9d7d4fa6a4756294a83bbc94f", - "sha256:b35dca159c1c9fa8a5f9005e42133eed82705bf8e243da371a5e5826440e65ca", - "sha256:b7b20c88873675903d6438d8b33fba027997193e274b9367421e610d9da76c08", - "sha256:bb4b15fb1f0aafa65cbdc62d3c2078bea1ceecbfccc9a1f23a2113c9ac1191fa", - "sha256:c0c7171aa5a57e522a04a31b84798b6c926234cb559c0939840c3235cf068813", - "sha256:c317ddd7c586af350a6aef22b891e84b16bff1a27886ed5b30f15c1ed59caeaa", - "sha256:c3abc34fed19fdeaead0ced8cf56dd121f08198008c033596aa6aae7cc58f59f", - "sha256:ca68c52e3cae491ace2bf39b35fef4ce26c192fd70b4cd90f040d419f70893b5", - "sha256:cf2cd387409b12d0a8b801610d6336ee7d24043b6dd965950eaec09b73e7262f", - "sha256:d046a9aeba9bc53e88a41e58beb72b6205abb9a20f6c136161adf9128e589db5", - "sha256:d5c20c8415173b119762b6110af64448adccd4d11f273fb9f718a9865b88a99c", - "sha256:d86132922531f0dc5a4f424c7580a472a924dd737602638e704841c9cb24aea2", - "sha256:dccff41478050e823271642837b904d5f9bda3f5cf7d371ce163f00a694118d6", - "sha256:de85c26a5a1c72e695ab0454e92f60213b4459b8d7c502e0be7a6369690eeb1a", - "sha256:e3a86b59b6227ef72ffc10d4b23f0fe994bef64d4667eab4fb8cd43de4223bec", - "sha256:e79e73d5ee24196d3057340e356e6254af4d10e1fc22d3207ea8342fc5ffb977", - "sha256:ea8210090a816d48a4291a47462bac750e3bc5c2442e6d64f7b8137a7c3f9ac5", - "sha256:f3b7ec97e68b68cb1f9ddb82eda17b418f19a034fa8380a0ac04e8fe01532875" + "sha256:04164e0063feb7aedd9d073db0fd496edb244be40d46ea1f0d8990815e4b8c34", + "sha256:159c2f69dd6efd28e894f261ffca1100690f28210f34cfcd70b895e0ea7a64f3", + "sha256:199dc6d0068753b6a8c0bd3aceb86a3e782df118260ebc1fa981ea31ee054674", + "sha256:1bbac3e8293b34c4403d297e21e8f10d2a57756b75cff101dc62186adec725f5", + "sha256:20e9eba7fd86ef52e0df25bea83b8b518dfdf0bce09b336cfe51671f52aaaa3f", + "sha256:290cbdf19129ae520d4bdce392648c6fcdbee763bc8f750b53a5ab51880cb9c9", + "sha256:316270e5867566376e69a0ac738b863d41396e2b63274616817e1d34156dff0e", + "sha256:3f88a4ee192142eeed3fe173f673ea6ab1f5a863810a9d85dbf6c67a9bd08f97", + "sha256:4aa96e957141006181ca58e792e900ee511085b8dae06c2d08c00f108280fb8a", + "sha256:4b2bcab3a914715d332ca783e9bda13bc570d8b9ef087563210ba63082c18c16", + "sha256:576684771456d02e24078047c2567025f2011977aa342063468577d94e194b00", + "sha256:5a2e73508f939175363d8a4be9dcdc84cf16a92578d7fa86e6e4ca0e6b3667b2", + "sha256:5ba59761c19b800bc2e1c9324da04d35ef51e4ee9621ff37534bc2290d258f71", + "sha256:5dc9801ae9884e822ba942ca493642fb50f049c06b6dbe3178691fce48ceb089", + "sha256:6fdd2dc5931daab778c2b65b03df6ae68376e028a3098eb624d0909d999885bc", + "sha256:708973b5d9e1e441188124aaf13c121e5b03b6054c2df59b32219175a25aa13e", + "sha256:7ff72b3cc9242d1a1c9b84bd945907bf174d74fc2519efe6184d6390a8df478b", + "sha256:8679f9aba5ac22e7bce54ccd8a77641d3aea3e2d96e73e4356c887ebf8ff1082", + "sha256:8b9a395122770a6f08ebfd0321546d7379f43505882c7419d7886856a07caa13", + "sha256:8e1e5d96b744a4f91163290b01045430f3f32579e46d87282449e5b14d27d4ac", + "sha256:9a0195af6b9050c9322a97cf07514f66fe511968e623ca87b2df5e3cf6349615", + "sha256:9cb5698c896fa72f88e7ef04ef62572faf56809093180771d9be8d9f2e264a13", + "sha256:b3f1d9b3aa09ab9adc7f8c4b40fc3e081eb903054c9a6f9ae1633fe15ae503b4", + "sha256:bb42f9b259c33662c6a9b866012f6908a91731a419e69304e1261ba3ab87b8d1", + "sha256:bca714d831e5b8860c3ab134c93aec63d1a4f493bed20084f54e3ce9f0a3bf99", + "sha256:bedd89c34ab62565d44745212814e4b57ef1c24ad4af9b29c504ce40f0dc6558", + "sha256:bfec934aac7f9fa95fc82147a4ba5db0a8bdc4ebf1e33b585ab8860beb10232f", + "sha256:c7046f7aa2db445daccc8424f50b47a66c4039c9f058246b43796aa818f8b751", + "sha256:d7e483f4791fbda60e23926b098702340504f7684ce7e1fd2c1bf02029288423", + "sha256:dd93162615870c976dba43963a24bb418b28448fef584f30755990c134a06a55", + "sha256:e4607d2d16330757818c9d6fba322c2e80b4b112ff24295d1343a80b876eb0ed", + "sha256:e9a680d9665f88346ed339888781f5236347933906c5a56348abb8261282ec48", + "sha256:edfcf93fd92e2f9eef640b3a7a40db20fe3c1d7c2c74faa41424c63dead61b76", + "sha256:f7e4a3c0c3c596296b37f8427c467c8e4336dc8d50f8ed38042e8ba79507b2c9", + "sha256:fff677fa4522dafb5a5e2c0cf909790d5d367326321aeabc0dffc9047cb235bd" ], "index": "pypi", - "version": "==1.4.31" + "version": "==1.4.32" }, "werkzeug": { "hashes": [ @@ -646,6 +650,14 @@ ], "markers": "python_version >= '3.6'", "version": "==1.7.2" + }, + "zipp": { + "hashes": [ + "sha256:9f50f446828eb9d45b267433fd3e9da8d801f614129124863f9c51ebceafb87d", + "sha256:b47250dd24f92b7dd6a0a8fc5244da14608f3ca90a5efcd37a3b1642fac9a375" + ], + "markers": "python_version >= '3.7'", + "version": "==3.7.0" } }, "develop": { @@ -699,56 +711,50 @@ "toml" ], "hashes": [ - "sha256:01774a2c2c729619760320270e42cd9e797427ecfddd32c2a7b639cdc481f3c0", - "sha256:03b20e52b7d31be571c9c06b74746746d4eb82fc260e594dc662ed48145e9efd", - "sha256:0a7726f74ff63f41e95ed3a89fef002916c828bb5fcae83b505b49d81a066884", - "sha256:1219d760ccfafc03c0822ae2e06e3b1248a8e6d1a70928966bafc6838d3c9e48", - "sha256:13362889b2d46e8d9f97c421539c97c963e34031ab0cb89e8ca83a10cc71ac76", - "sha256:174cf9b4bef0db2e8244f82059a5a72bd47e1d40e71c68ab055425172b16b7d0", - "sha256:17e6c11038d4ed6e8af1407d9e89a2904d573be29d51515f14262d7f10ef0a64", - "sha256:215f8afcc02a24c2d9a10d3790b21054b58d71f4b3c6f055d4bb1b15cecce685", - "sha256:22e60a3ca5acba37d1d4a2ee66e051f5b0e1b9ac950b5b0cf4aa5366eda41d47", - "sha256:2641f803ee9f95b1f387f3e8f3bf28d83d9b69a39e9911e5bfee832bea75240d", - "sha256:276651978c94a8c5672ea60a2656e95a3cce2a3f31e9fb2d5ebd4c215d095840", - "sha256:3f7c17209eef285c86f819ff04a6d4cbee9b33ef05cbcaae4c0b4e8e06b3ec8f", - "sha256:3feac4084291642165c3a0d9eaebedf19ffa505016c4d3db15bfe235718d4971", - "sha256:49dbff64961bc9bdd2289a2bda6a3a5a331964ba5497f694e2cbd540d656dc1c", - "sha256:4e547122ca2d244f7c090fe3f4b5a5861255ff66b7ab6d98f44a0222aaf8671a", - "sha256:5829192582c0ec8ca4a2532407bc14c2f338d9878a10442f5d03804a95fac9de", - "sha256:5d6b09c972ce9200264c35a1d53d43ca55ef61836d9ec60f0d44273a31aa9f17", - "sha256:600617008aa82032ddeace2535626d1bc212dfff32b43989539deda63b3f36e4", - "sha256:619346d57c7126ae49ac95b11b0dc8e36c1dd49d148477461bb66c8cf13bb521", - "sha256:63c424e6f5b4ab1cf1e23a43b12f542b0ec2e54f99ec9f11b75382152981df57", - "sha256:6dbc1536e105adda7a6312c778f15aaabe583b0e9a0b0a324990334fd458c94b", - "sha256:6e1394d24d5938e561fbeaa0cd3d356207579c28bd1792f25a068743f2d5b282", - "sha256:86f2e78b1eff847609b1ca8050c9e1fa3bd44ce755b2ec30e70f2d3ba3844644", - "sha256:8bdfe9ff3a4ea37d17f172ac0dff1e1c383aec17a636b9b35906babc9f0f5475", - "sha256:8e2c35a4c1f269704e90888e56f794e2d9c0262fb0c1b1c8c4ee44d9b9e77b5d", - "sha256:92b8c845527eae547a2a6617d336adc56394050c3ed8a6918683646328fbb6da", - "sha256:9365ed5cce5d0cf2c10afc6add145c5037d3148585b8ae0e77cc1efdd6aa2953", - "sha256:9a29311bd6429be317c1f3fe4bc06c4c5ee45e2fa61b2a19d4d1d6111cb94af2", - "sha256:9a2b5b52be0a8626fcbffd7e689781bf8c2ac01613e77feda93d96184949a98e", - "sha256:a4bdeb0a52d1d04123b41d90a4390b096f3ef38eee35e11f0b22c2d031222c6c", - "sha256:a9c8c4283e17690ff1a7427123ffb428ad6a52ed720d550e299e8291e33184dc", - "sha256:b637c57fdb8be84e91fac60d9325a66a5981f8086c954ea2772efe28425eaf64", - "sha256:bf154ba7ee2fd613eb541c2bc03d3d9ac667080a737449d1a3fb342740eb1a74", - "sha256:c254b03032d5a06de049ce8bca8338a5185f07fb76600afff3c161e053d88617", - "sha256:c332d8f8d448ded473b97fefe4a0983265af21917d8b0cdcb8bb06b2afe632c3", - "sha256:c7912d1526299cb04c88288e148c6c87c0df600eca76efd99d84396cfe00ef1d", - "sha256:cfd9386c1d6f13b37e05a91a8583e802f8059bebfccde61a418c5808dea6bbfa", - "sha256:d5d2033d5db1d58ae2d62f095e1aefb6988af65b4b12cb8987af409587cc0739", - "sha256:dca38a21e4423f3edb821292e97cec7ad38086f84313462098568baedf4331f8", - "sha256:e2cad8093172b7d1595b4ad66f24270808658e11acf43a8f95b41276162eb5b8", - "sha256:e3db840a4dee542e37e09f30859f1612da90e1c5239a6a2498c473183a50e781", - "sha256:edcada2e24ed68f019175c2b2af2a8b481d3d084798b8c20d15d34f5c733fa58", - "sha256:f467bbb837691ab5a8ca359199d3429a11a01e6dfb3d9dcc676dc035ca93c0a9", - "sha256:f506af4f27def639ba45789fa6fde45f9a217da0be05f8910458e4557eed020c", - "sha256:f614fc9956d76d8a88a88bb41ddc12709caa755666f580af3a688899721efecd", - "sha256:f9afb5b746781fc2abce26193d1c817b7eb0e11459510fba65d2bd77fe161d9e", - "sha256:fb8b8ee99b3fffe4fd86f4c81b35a6bf7e4462cba019997af2fe679365db0c49" + "sha256:03e2a7826086b91ef345ff18742ee9fc47a6839ccd517061ef8fa1976e652ce9", + "sha256:07e6db90cd9686c767dcc593dff16c8c09f9814f5e9c51034066cad3373b914d", + "sha256:18d520c6860515a771708937d2f78f63cc47ab3b80cb78e86573b0a760161faf", + "sha256:1ebf730d2381158ecf3dfd4453fbca0613e16eaa547b4170e2450c9707665ce7", + "sha256:21b7745788866028adeb1e0eca3bf1101109e2dc58456cb49d2d9b99a8c516e6", + "sha256:26e2deacd414fc2f97dd9f7676ee3eaecd299ca751412d89f40bc01557a6b1b4", + "sha256:2c6dbb42f3ad25760010c45191e9757e7dce981cbfb90e42feef301d71540059", + "sha256:2fea046bfb455510e05be95e879f0e768d45c10c11509e20e06d8fcaa31d9e39", + "sha256:34626a7eee2a3da12af0507780bb51eb52dca0e1751fd1471d0810539cefb536", + "sha256:37d1141ad6b2466a7b53a22e08fe76994c2d35a5b6b469590424a9953155afac", + "sha256:46191097ebc381fbf89bdce207a6c107ac4ec0890d8d20f3360345ff5976155c", + "sha256:4dd8bafa458b5c7d061540f1ee9f18025a68e2d8471b3e858a9dad47c8d41903", + "sha256:4e21876082ed887baed0146fe222f861b5815455ada3b33b890f4105d806128d", + "sha256:58303469e9a272b4abdb9e302a780072c0633cdcc0165db7eec0f9e32f901e05", + "sha256:5ca5aeb4344b30d0bec47481536b8ba1181d50dbe783b0e4ad03c95dc1296684", + "sha256:68353fe7cdf91f109fc7d474461b46e7f1f14e533e911a2a2cbb8b0fc8613cf1", + "sha256:6f89d05e028d274ce4fa1a86887b071ae1755082ef94a6740238cd7a8178804f", + "sha256:7a15dc0a14008f1da3d1ebd44bdda3e357dbabdf5a0b5034d38fcde0b5c234b7", + "sha256:8bdde1177f2311ee552f47ae6e5aa7750c0e3291ca6b75f71f7ffe1f1dab3dca", + "sha256:8ce257cac556cb03be4a248d92ed36904a59a4a5ff55a994e92214cde15c5bad", + "sha256:8cf5cfcb1521dc3255d845d9dca3ff204b3229401994ef8d1984b32746bb45ca", + "sha256:8fbbdc8d55990eac1b0919ca69eb5a988a802b854488c34b8f37f3e2025fa90d", + "sha256:9548f10d8be799551eb3a9c74bbf2b4934ddb330e08a73320123c07f95cc2d92", + "sha256:96f8a1cb43ca1422f36492bebe63312d396491a9165ed3b9231e778d43a7fca4", + "sha256:9b27d894748475fa858f9597c0ee1d4829f44683f3813633aaf94b19cb5453cf", + "sha256:9baff2a45ae1f17c8078452e9e5962e518eab705e50a0aa8083733ea7d45f3a6", + "sha256:a2a8b8bcc399edb4347a5ca8b9b87e7524c0967b335fbb08a83c8421489ddee1", + "sha256:acf53bc2cf7282ab9b8ba346746afe703474004d9e566ad164c91a7a59f188a4", + "sha256:b0be84e5a6209858a1d3e8d1806c46214e867ce1b0fd32e4ea03f4bd8b2e3359", + "sha256:b31651d018b23ec463e95cf10070d0b2c548aa950a03d0b559eaa11c7e5a6fa3", + "sha256:b78e5afb39941572209f71866aa0b206c12f0109835aa0d601e41552f9b3e620", + "sha256:c76aeef1b95aff3905fb2ae2d96e319caca5b76fa41d3470b19d4e4a3a313512", + "sha256:dd035edafefee4d573140a76fdc785dc38829fe5a455c4bb12bac8c20cfc3d69", + "sha256:dd6fe30bd519694b356cbfcaca9bd5c1737cddd20778c6a581ae20dc8c04def2", + "sha256:e5f4e1edcf57ce94e5475fe09e5afa3e3145081318e5fd1a43a6b4539a97e518", + "sha256:ec6bc7fe73a938933d4178c9b23c4e0568e43e220aef9472c4f6044bfc6dd0f0", + "sha256:f1555ea6d6da108e1999b2463ea1003fe03f29213e459145e70edbaf3e004aaa", + "sha256:f5fa5803f47e095d7ad8443d28b01d48c0359484fec1b9d8606d0e3282084bc4", + "sha256:f7331dbf301b7289013175087636bbaf5b2405e57259dd2c42fdcc9fcc47325e", + "sha256:f9987b0354b06d4df0f4d3e0ec1ae76d7ce7cbca9a2f98c25041eb79eec766f1", + "sha256:fd9e830e9d8d89b20ab1e5af09b32d33e1a08ef4c4e14411e559556fd788e6b2" ], "index": "pypi", - "version": "==6.2" + "version": "==6.3.2" }, "flake8": { "hashes": [ @@ -774,29 +780,32 @@ }, "mypy": { "hashes": [ - "sha256:0038b21890867793581e4cb0d810829f5fd4441aa75796b53033af3aa30430ce", - "sha256:1171f2e0859cfff2d366da2c7092b06130f232c636a3f7301e3feb8b41f6377d", - "sha256:1b06268df7eb53a8feea99cbfff77a6e2b205e70bf31743e786678ef87ee8069", - "sha256:1b65714dc296a7991000b6ee59a35b3f550e0073411ac9d3202f6516621ba66c", - "sha256:1bf752559797c897cdd2c65f7b60c2b6969ffe458417b8d947b8340cc9cec08d", - "sha256:300717a07ad09525401a508ef5d105e6b56646f7942eb92715a1c8d610149714", - "sha256:3c5b42d0815e15518b1f0990cff7a705805961613e701db60387e6fb663fe78a", - "sha256:4365c60266b95a3f216a3047f1d8e3f895da6c7402e9e1ddfab96393122cc58d", - "sha256:50c7346a46dc76a4ed88f3277d4959de8a2bd0a0fa47fa87a4cde36fe247ac05", - "sha256:5b56154f8c09427bae082b32275a21f500b24d93c88d69a5e82f3978018a0266", - "sha256:74f7eccbfd436abe9c352ad9fb65872cc0f1f0a868e9d9c44db0893440f0c697", - "sha256:7b3f6f557ba4afc7f2ce6d3215d5db279bcf120b3cfd0add20a5d4f4abdae5bc", - "sha256:8c11003aaeaf7cc2d0f1bc101c1cc9454ec4cc9cb825aef3cafff8a5fdf4c799", - "sha256:8ca7f8c4b1584d63c9a0f827c37ba7a47226c19a23a753d52e5b5eddb201afcd", - "sha256:c89702cac5b302f0c5d33b172d2b55b5df2bede3344a2fbed99ff96bddb2cf00", - "sha256:d8f1ff62f7a879c9fe5917b3f9eb93a79b78aad47b533911b853a757223f72e7", - "sha256:d9d2b84b2007cea426e327d2483238f040c49405a6bf4074f605f0156c91a47a", - "sha256:e839191b8da5b4e5d805f940537efcaa13ea5dd98418f06dc585d2891d228cf0", - "sha256:f9fe20d0872b26c4bba1c1be02c5340de1019530302cf2dcc85c7f9fc3252ae0", - "sha256:ff3bf387c14c805ab1388185dd22d6b210824e164d4bb324b195ff34e322d166" + "sha256:080097eee5393fd740f32c63f9343580aaa0fb1cda0128fd859dfcf081321c3d", + "sha256:0d3bcbe146247997e03bf030122000998b076b3ac6925b0b6563f46d1ce39b50", + "sha256:0dd441fbacf48e19dc0c5c42fafa72b8e1a0ba0a39309c1af9c84b9397d9b15a", + "sha256:108f3c7e14a038cf097d2444fa0155462362c6316e3ecb2d70f6dd99cd36084d", + "sha256:3bada0cf7b6965627954b3a128903a87cac79a79ccd83b6104912e723ef16c7b", + "sha256:3cf77f138efb31727ee7197bc824c9d6d7039204ed96756cc0f9ca7d8e8fc2a4", + "sha256:42c216a33d2bdba08098acaf5bae65b0c8196afeb535ef4b870919a788a27259", + "sha256:465a6ce9ca6268cadfbc27a2a94ddf0412568a6b27640ced229270be4f5d394d", + "sha256:6a8e1f63357851444940351e98fb3252956a15f2cabe3d698316d7a2d1f1f896", + "sha256:745071762f32f65e77de6df699366d707fad6c132a660d1342077cbf671ef589", + "sha256:818cfc51c25a5dbfd0705f3ac1919fff6971eb0c02e6f1a1f6a017a42405a7c0", + "sha256:8e5974583a77d630a5868eee18f85ac3093caf76e018c510aeb802b9973304ce", + "sha256:8eaf55fdf99242a1c8c792247c455565447353914023878beadb79600aac4a2a", + "sha256:98f61aad0bb54f797b17da5b82f419e6ce214de0aa7e92211ebee9e40eb04276", + "sha256:b2ce2788df0c066c2ff4ba7190fa84f18937527c477247e926abeb9b1168b8cc", + "sha256:b30d29251dff4c59b2e5a1fa1bab91ff3e117b4658cb90f76d97702b7a2ae699", + "sha256:bf446223b2e0e4f0a4792938e8d885e8a896834aded5f51be5c3c69566495540", + "sha256:cbcc691d8b507d54cb2b8521f0a2a3d4daa477f62fe77f0abba41e5febb377b7", + "sha256:d051ce0946521eba48e19b25f27f98e5ce4dbc91fff296de76240c46b4464df0", + "sha256:d61b73c01fc1de799226963f2639af831307fe1556b04b7c25e2b6c267a3bc76", + "sha256:eea10982b798ff0ccc3b9e7e42628f932f552c5845066970e67cd6858655d52c", + "sha256:f79137d012ff3227866222049af534f25354c07a0d6b9a171dba9f1d6a1fdef4", + "sha256:fc5ecff5a3bbfbe20091b1cad82815507f5ae9c380a3a9bf40f740c70ce30a9b" ], "index": "pypi", - "version": "==0.931" + "version": "==0.941" }, "mypy-extensions": { "hashes": [ @@ -870,11 +879,11 @@ }, "pytest": { "hashes": [ - "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89", - "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134" + "sha256:841132caef6b1ad17a9afde46dc4f6cfa59a05f9555aae5151f73bdf2820ca63", + "sha256:92f723789a8fdd7180b6b06483874feca4c48a5c76968e03bb3e7f806a1869ea" ], "index": "pypi", - "version": "==6.2.5" + "version": "==7.1.1" }, "pytest-cov": { "hashes": [ @@ -892,14 +901,6 @@ "index": "pypi", "version": "==0.4" }, - "toml": { - "hashes": [ - "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", - "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" - ], - "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==0.10.2" - }, "tomli": { "hashes": [ "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", diff --git a/alembic/versions/c742377339a6_add_seq_dnaplate_sequence.py b/alembic/versions/c742377339a6_add_seq_dnaplate_sequence.py new file mode 100644 index 0000000..75f4a13 --- /dev/null +++ b/alembic/versions/c742377339a6_add_seq_dnaplate_sequence.py @@ -0,0 +1,24 @@ +"""Add SEQ_DNAPLATE sequence + +Revision ID: c742377339a6 +Revises: bc442d63d7d3 +Create Date: 2022-03-23 15:23:22.964360 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = "c742377339a6" +down_revision = "bc442d63d7d3" +branch_labels = None +depends_on = None + + +def upgrade(): + op.execute("CREATE SEQUENCE SEQ_DNAPLATE START 1;") + + +def downgrade(): + op.execute("DROP SEQUENCE IF EXISTS SEQ_DNAPLATE;") diff --git a/baracoda/__init__.py b/baracoda/__init__.py index 64ab192..044dd30 100644 --- a/baracoda/__init__.py +++ b/baracoda/__init__.py @@ -7,6 +7,7 @@ from flask.cli import with_appcontext from baracoda import barcodes +from baracoda import plate_barcodes from baracoda.config.logging import LOGGING from baracoda.db import db, reset_db @@ -41,6 +42,7 @@ def create_app(test_config=None): app.cli.add_command(init_db_command) app.register_blueprint(barcodes.bp) + app.register_blueprint(plate_barcodes.bp) @app.route("/health") def health_check(): diff --git a/baracoda/config/defaults.py b/baracoda/config/defaults.py index a6a35f4..f7f4fca 100644 --- a/baracoda/config/defaults.py +++ b/baracoda/config/defaults.py @@ -1,10 +1,12 @@ from typing import Any, Dict, List +from baracoda.formats import HeronCogUkIdFormatter, HeronPlateCherrypickedFormatter, SequencescapePlateBarcodeFormatter + ### # database config ### DB_DBNAME = "baracoda_dev" -DB_HOST = "127.0.0.1" +DB_HOST = "host.docker.internal" DB_PASSWORD = "postgres" DB_PORT = "5432" DB_USER = "postgres" @@ -26,58 +28,59 @@ # prefix for barcodes returned from the respective sequece ### PREFIXES: List[Dict[str, Any]] = [ - {"prefix": "ALDP", "sequence_name": "heron", "convert": True}, - {"prefix": "BHRT", "sequence_name": "heron", "convert": True}, - {"prefix": "BIRM", "sequence_name": "heron", "convert": True}, - {"prefix": "BRBR", "sequence_name": "heron", "convert": True}, - {"prefix": "BRIG", "sequence_name": "heron", "convert": True}, - {"prefix": "BRIS", "sequence_name": "heron", "convert": True}, - {"prefix": "CAMB", "sequence_name": "heron", "convert": True}, - {"prefix": "CAMC", "sequence_name": "heron", "convert": True}, - {"prefix": "CPTD", "sequence_name": "heron", "convert": True}, - {"prefix": "CWAR", "sequence_name": "heron", "convert": True}, - {"prefix": "EDIN", "sequence_name": "heron", "convert": True}, - {"prefix": "EKHU", "sequence_name": "heron", "convert": True}, - {"prefix": "EXET", "sequence_name": "heron", "convert": True}, - {"prefix": "GCVR", "sequence_name": "heron", "convert": True}, - {"prefix": "GLOU", "sequence_name": "heron", "convert": True}, - {"prefix": "GSTT", "sequence_name": "heron", "convert": True}, - {"prefix": "HECH", "sequence_name": "heron", "convert": True}, - {"prefix": "HSLL", "sequence_name": "heron", "convert": True}, - {"prefix": "KGHT", "sequence_name": "heron", "convert": True}, - {"prefix": "LCST", "sequence_name": "heron", "convert": True}, - {"prefix": "LEED", "sequence_name": "heron", "convert": True}, - {"prefix": "LIVE", "sequence_name": "heron", "convert": True}, - {"prefix": "LOND", "sequence_name": "heron", "convert": True}, - {"prefix": "LSPA", "sequence_name": "heron", "convert": True}, - {"prefix": "MILK", "sequence_name": "heron", "convert": True}, - {"prefix": "MTUN", "sequence_name": "heron", "convert": True}, - {"prefix": "NEWC", "sequence_name": "heron", "convert": True}, - {"prefix": "NIRE", "sequence_name": "heron", "convert": True}, - {"prefix": "NORT", "sequence_name": "heron", "convert": True}, - {"prefix": "NORW", "sequence_name": "heron", "convert": True}, - {"prefix": "NOTT", "sequence_name": "heron", "convert": True}, - {"prefix": "NWGH", "sequence_name": "heron", "convert": True}, - {"prefix": "OXON", "sequence_name": "heron", "convert": True}, - {"prefix": "PAHT", "sequence_name": "heron", "convert": True}, - {"prefix": "PHEC", "sequence_name": "heron", "convert": True}, - {"prefix": "PHWC", "sequence_name": "heron", "convert": True}, - {"prefix": "PLYM", "sequence_name": "heron", "convert": True}, - {"prefix": "PORT", "sequence_name": "heron", "convert": True}, - {"prefix": "PRIN", "sequence_name": "heron", "convert": True}, - {"prefix": "QEUH", "sequence_name": "heron", "convert": True}, - {"prefix": "RAND", "sequence_name": "heron", "convert": True}, - {"prefix": "RSCH", "sequence_name": "heron", "convert": True}, - {"prefix": "SANG", "sequence_name": "heron", "convert": True}, - {"prefix": "SHEF", "sequence_name": "heron", "convert": True}, - {"prefix": "TBSD", "sequence_name": "heron", "convert": True}, - {"prefix": "TFCI", "sequence_name": "heron", "convert": True}, - {"prefix": "WAHH", "sequence_name": "heron", "convert": True}, - {"prefix": "WSFT", "sequence_name": "heron", "convert": True}, - {"prefix": "HT", "sequence_name": "ht", "convert": False}, + {"prefix": "ALDP", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, + {"prefix": "BHRT", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, + {"prefix": "BIRM", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, + {"prefix": "BRBR", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, + {"prefix": "BRIG", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, + {"prefix": "BRIS", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, + {"prefix": "CAMB", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, + {"prefix": "CAMC", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, + {"prefix": "CPTD", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, + {"prefix": "CWAR", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, + {"prefix": "EDIN", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, + {"prefix": "EKHU", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, + {"prefix": "EXET", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, + {"prefix": "GCVR", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, + {"prefix": "GLOU", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, + {"prefix": "GSTT", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, + {"prefix": "HECH", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, + {"prefix": "HSLL", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, + {"prefix": "KGHT", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, + {"prefix": "LCST", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, + {"prefix": "LEED", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, + {"prefix": "LIVE", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, + {"prefix": "LOND", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, + {"prefix": "LSPA", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, + {"prefix": "MILK", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, + {"prefix": "MTUN", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, + {"prefix": "NEWC", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, + {"prefix": "NIRE", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, + {"prefix": "NORT", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, + {"prefix": "NORW", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, + {"prefix": "NOTT", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, + {"prefix": "NWGH", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, + {"prefix": "OXON", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, + {"prefix": "PAHT", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, + {"prefix": "PHEC", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, + {"prefix": "PHWC", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, + {"prefix": "PLYM", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, + {"prefix": "PORT", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, + {"prefix": "PRIN", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, + {"prefix": "QEUH", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, + {"prefix": "RAND", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, + {"prefix": "RSCH", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, + {"prefix": "SANG", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, + {"prefix": "SHEF", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, + {"prefix": "TBSD", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, + {"prefix": "TFCI", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, + {"prefix": "WAHH", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, + {"prefix": "WSFT", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, + {"prefix": "HT", "sequence_name": "ht", "formatter_class": HeronPlateCherrypickedFormatter}, + {"prefix": "DN", "sequence_name": "SEQ_DNAPLATE", "formatter_class": SequencescapePlateBarcodeFormatter}, ] for prefix_item in PREFIXES: - for key in ["prefix", "sequence_name", "convert"]: + for key in ["prefix", "sequence_name", "formatter_class"]: if not (key in prefix_item): - raise KeyError("PREFIXES must all contain a prefix, sequence_name and convert key.") + raise KeyError("PREFIXES must all contain a prefix, sequence_name and formatter_class key.") diff --git a/baracoda/config/test.py b/baracoda/config/test.py index ac27742..f0b157e 100644 --- a/baracoda/config/test.py +++ b/baracoda/config/test.py @@ -11,4 +11,5 @@ # database config ### DB_DBNAME = "baracoda_test" +DB_HOST = "host.docker.internal" SQLALCHEMY_DATABASE_URI = f"postgresql+psycopg2://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_DBNAME}" diff --git a/baracoda/formats.py b/baracoda/formats.py index 469c947..cbfedda 100644 --- a/baracoda/formats.py +++ b/baracoda/formats.py @@ -3,14 +3,48 @@ logger = logging.getLogger(__name__) -class HeronFormatter: - def __init__(self, prefix: str, convert: bool = True): +class FormatterInterface: + def barcode(self): + pass + + +class SequencescapePlateBarcodeFormatter(FormatterInterface): + def __init__(self, prefix: str): + logger.debug(f"Instantiate formatter PlateBarcodeFormatter") + self.prefix = prefix + + def barcode(self, value: int) -> str: + return value + + +class HeronPlateCherrypickedFormatter(FormatterInterface): + def __init__(self, prefix: str): logger.debug(f"Instantiate formatter with {prefix}") self.prefix = prefix - # tells the formatter whether barcode needs to be mashed up - self.convert = convert + def barcode(self, value: int) -> str: + """ + Method which returns a barcode with a prefix. + If the barcode needs to be converted it is formatted otherwise it is returned as is + + Arguments: + value {str} -- the value of the barcode from the sequence + + Returns: + str -- formatted barcode with prefix and checksum + """ + + formatted_value = value + + return f"{self.prefix}-{formatted_value}" + + +class HeronCogUkIdFormatter(FormatterInterface): + def __init__(self, prefix: str): + logger.debug(f"Instantiate formatter with {prefix}") + + self.prefix = prefix def hex_to_int(self, hex_str: str) -> int: """Convert a hex string to integer. @@ -67,7 +101,7 @@ def barcode(self, value: int) -> str: str -- formatted barcode with prefix and checksum """ - formatted_value = self.format_barcode_number(value) if self.convert else value + formatted_value = self.format_barcode_number(value) return f"{self.prefix}-{formatted_value}" diff --git a/baracoda/operations.py b/baracoda/operations.py index b558971..f965ecf 100644 --- a/baracoda/operations.py +++ b/baracoda/operations.py @@ -5,7 +5,6 @@ from baracoda.db import db from baracoda.exceptions import InvalidPrefixError -from baracoda.formats import HeronFormatter from baracoda.helpers import get_prefix_item from baracoda.orm.barcode import Barcode from baracoda.orm.barcodes_group import BarcodesGroup @@ -21,6 +20,7 @@ def __init__(self, prefix: str): self.__check_prefix() + logger.debug(f"Setting prefix item from prefix {self.prefix}") self.__set_prefix_item() # if the prefix item does not exist the prefix is not valid @@ -28,9 +28,11 @@ def __init__(self, prefix: str): raise InvalidPrefixError() # saves pulling it out of object every time + logger.debug("Accessing sequence_name") self.sequence_name = self.prefix_item["sequence_name"] - self.formatter = HeronFormatter(prefix=self.prefix, convert=self.prefix_item["convert"]) # type: ignore + def formatter(self): + return self.prefix_item["formatter_class"](self.prefix) def create_barcode_group(self, count: int) -> BarcodesGroup: """Creates a new barcode group and the associated barcodes. @@ -66,6 +68,7 @@ def create_barcode(self) -> Barcode: Returns: str -- the generated barcode in the Heron format """ + logger.debug(f"Calling create_barcode for sequence name {self.sequence_name}") try: next_value = self.__get_next_value(self.sequence_name) # type: ignore barcode = self.__build_barcode(self.prefix, next_value, barcodes_group=None) @@ -121,7 +124,7 @@ def __validate_prefix(self) -> bool: return bool(pattern.match(self.prefix)) def __build_barcode(self, prefix: str, next_value: int, barcodes_group: Optional[BarcodesGroup]) -> Barcode: - barcode = self.formatter.barcode(next_value) + barcode = self.formatter().barcode(next_value) return Barcode( prefix=prefix, barcode=barcode, diff --git a/baracoda/plate_barcodes.py b/baracoda/plate_barcodes.py new file mode 100644 index 0000000..ad7406e --- /dev/null +++ b/baracoda/plate_barcodes.py @@ -0,0 +1,32 @@ +import logging +from flask import Blueprint +from http import HTTPStatus +from typing import Any, Tuple +from baracoda.exceptions import InvalidPrefixError +from baracoda.operations import BarcodeOperations +from dicttoxml import dicttoxml + +bp = Blueprint("plate_barcode_creation", __name__) + +logger = logging.getLogger(__name__) + +SEQ_DNAPLATE_PREFIX = "DN" + + +@bp.post("/plate_barcodes.xml") # type: ignore +def get_new_plate_barcode() -> Tuple[Any, int]: + try: + logger.debug(f"Creating a plate_barcode.xml") + operator = BarcodeOperations(prefix=SEQ_DNAPLATE_PREFIX) + barcode = operator.create_barcode() + response = dicttoxml(barcode.to_dict(), attr_type=False, custom_root="plate_barcode") + + return response, HTTPStatus.OK + + except InvalidPrefixError as e: + return dicttoxml([f"{type(e).__name__}"], attr_type=False, custom_root="errors"), HTTPStatus.BAD_REQUEST + except Exception as e: + return ( + dicttoxml([f"{type(e).__name__}"], attr_type=False, custom_root="errors"), + HTTPStatus.INTERNAL_SERVER_ERROR, + ) diff --git a/docker-compose.yml b/docker-compose.yml index 4b82c38..9e3350a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,4 @@ -version: "3.8" +version: "3.3" services: # A Postgresql service as close to production as currently possible # To run it as a standalone container: diff --git a/tests/conftest.py b/tests/conftest.py index 1e7047c..305354f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -2,7 +2,7 @@ from baracoda import create_app from baracoda.db import db, reset_db -from baracoda.formats import HeronFormatter +from baracoda.formats import HeronCogUkIdFormatter from tests.data.fixture_data import PREFIXES @@ -11,7 +11,7 @@ def app(): app = create_app( { - "DB_HOST": "localhost", + "DB_HOST": "host.docker.internal", "DB_PORT": 5432, "DB_USER": "postgres", "DB_PASSWORD": "postgres", @@ -21,7 +21,7 @@ def app(): "SEQUENCE_RESET": True, "SLACK_API_TOKEN": "", "SLACK_CHANNEL_ID": "", - "SQLALCHEMY_DATABASE_URI": "postgresql+psycopg2://postgres:postgres@localhost:5432/baracoda_test", + "SQLALCHEMY_DATABASE_URI": "postgresql+psycopg2://postgres:postgres@host.docker.internal:5432/baracoda_test", "PREFIXES": PREFIXES, } ) @@ -40,7 +40,7 @@ def client(app): @pytest.fixture def heron_formatter(): - return HeronFormatter(prefix="SANG") + return HeronCogUkIdFormatter(prefix="SANG") @pytest.fixture diff --git a/tests/data/fixture_data.py b/tests/data/fixture_data.py index bf3c50d..e79c759 100644 --- a/tests/data/fixture_data.py +++ b/tests/data/fixture_data.py @@ -1,34 +1,35 @@ from typing import Dict, List, Union +from baracoda.formats import HeronCogUkIdFormatter, HeronPlateCherrypickedFormatter PREFIXES: List[Dict[str, Union[str, bool]]] = [ { "prefix": "SANG", "sequence_name": "heron", - "convert": True, + "formatter_class": HeronCogUkIdFormatter, }, { "prefix": "CAMB", "sequence_name": "heron", - "convert": True, + "formatter_class": HeronCogUkIdFormatter, }, { "prefix": "NORW", "sequence_name": "heron", - "convert": True, + "formatter_class": HeronCogUkIdFormatter, }, { "prefix": "NOTT", "sequence_name": "heron", - "convert": True, + "formatter_class": HeronCogUkIdFormatter, }, { "prefix": "LEED", "sequence_name": "heron", - "convert": True, + "formatter_class": HeronCogUkIdFormatter, }, { "prefix": "HT", "sequence_name": "ht", - "convert": False, + "formatter_class": HeronPlateCherrypickedFormatter, }, ] diff --git a/tests/test_formats.py b/tests/test_formats.py index cac5ba4..a95a6fd 100644 --- a/tests/test_formats.py +++ b/tests/test_formats.py @@ -1,4 +1,4 @@ -from baracoda.formats import HeronFormatter +from baracoda.formats import HeronCogUkIdFormatter, HeronPlateCherrypickedFormatter def test_checksum_conversion(heron_formatter): @@ -19,12 +19,12 @@ def test_barcode_example_1(heron_formatter): def test_barcode_example_2(): - formatter = HeronFormatter(prefix="NIRE") + formatter = HeronCogUkIdFormatter(prefix="NIRE") assert formatter.barcode(111111) == "NIRE-1B2075" -def test_barcode_example_when_no_conversion_needed(): - formatter = HeronFormatter(prefix="HT", convert=False) +def test_barcode_example_plate_cherrypicked(): + formatter = HeronPlateCherrypickedFormatter(prefix="HT") assert formatter.barcode(111111) == "HT-111111" @@ -40,7 +40,7 @@ def barcode_for(barcode: str) -> str: """ prefix, number_and_checksum = barcode.split("-") number = number_and_checksum[:-1] - formatter = HeronFormatter(prefix=prefix) + formatter = HeronCogUkIdFormatter(prefix=prefix) return formatter.barcode(int(number, 16)) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index f145da7..8decdb1 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -1,10 +1,11 @@ +from baracoda.formats import HeronCogUkIdFormatter from baracoda.helpers import get_prefix_item def test_correct_prefix_item_is_returned(app, prefixes): with app.app_context(): prefix_item = get_prefix_item("LEED") - assert prefix_item == {"prefix": "LEED", "sequence_name": "heron", "convert": True} + assert prefix_item == {"prefix": "LEED", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter} def test_none_is_returned_for_invalid_prefix(app): From 499f4f2a21b0531a3cbbc5afc2aae4bc48ddfd83 Mon Sep 17 00:00:00 2001 From: Ben Topping Date: Mon, 4 Apr 2022 15:33:28 +0100 Subject: [PATCH 04/33] Added new prefix type, changes to barcode formatters, added tests --- .../c742377339a6_add_seq_dnaplate_sequence.py | 24 -------------- .../versions/e501839465f6_add_sqp_sequence.py | 23 +++++++++++++ baracoda/__init__.py | 2 -- baracoda/config/defaults.py | 7 ++-- baracoda/config/development.py | 4 +++ baracoda/config/test.py | 2 +- baracoda/formats.py | 11 +------ baracoda/plate_barcodes.py | 32 ------------------- baracoda/sql/schema.sql | 4 +++ tests/conftest.py | 4 +-- tests/data/fixture_data.py | 9 ++++-- tests/test_barcodes.py | 7 ++++ tests/test_formats.py | 9 ++++-- tests/test_operations.py | 6 ++++ 14 files changed, 65 insertions(+), 79 deletions(-) delete mode 100644 alembic/versions/c742377339a6_add_seq_dnaplate_sequence.py create mode 100644 alembic/versions/e501839465f6_add_sqp_sequence.py delete mode 100644 baracoda/plate_barcodes.py diff --git a/alembic/versions/c742377339a6_add_seq_dnaplate_sequence.py b/alembic/versions/c742377339a6_add_seq_dnaplate_sequence.py deleted file mode 100644 index 75f4a13..0000000 --- a/alembic/versions/c742377339a6_add_seq_dnaplate_sequence.py +++ /dev/null @@ -1,24 +0,0 @@ -"""Add SEQ_DNAPLATE sequence - -Revision ID: c742377339a6 -Revises: bc442d63d7d3 -Create Date: 2022-03-23 15:23:22.964360 - -""" -from alembic import op -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision = "c742377339a6" -down_revision = "bc442d63d7d3" -branch_labels = None -depends_on = None - - -def upgrade(): - op.execute("CREATE SEQUENCE SEQ_DNAPLATE START 1;") - - -def downgrade(): - op.execute("DROP SEQUENCE IF EXISTS SEQ_DNAPLATE;") diff --git a/alembic/versions/e501839465f6_add_sqp_sequence.py b/alembic/versions/e501839465f6_add_sqp_sequence.py new file mode 100644 index 0000000..7b0bf61 --- /dev/null +++ b/alembic/versions/e501839465f6_add_sqp_sequence.py @@ -0,0 +1,23 @@ +"""Add sqp sequence + +Revision ID: e501839465f6 +Revises: bc442d63d7d3 +Create Date: 2022-04-04 14:38:35.746245 + +""" +from alembic import op + + +# revision identifiers, used by Alembic. +revision = "e501839465f6" +down_revision = "bc442d63d7d3" +branch_labels = None +depends_on = None + + +def upgrade(): + op.execute("CREATE SEQUENCE sqp START 1;") + + +def downgrade(): + op.execute("DROP SEQUENCE IF EXISTS sqp;") diff --git a/baracoda/__init__.py b/baracoda/__init__.py index 044dd30..64ab192 100644 --- a/baracoda/__init__.py +++ b/baracoda/__init__.py @@ -7,7 +7,6 @@ from flask.cli import with_appcontext from baracoda import barcodes -from baracoda import plate_barcodes from baracoda.config.logging import LOGGING from baracoda.db import db, reset_db @@ -42,7 +41,6 @@ def create_app(test_config=None): app.cli.add_command(init_db_command) app.register_blueprint(barcodes.bp) - app.register_blueprint(plate_barcodes.bp) @app.route("/health") def health_check(): diff --git a/baracoda/config/defaults.py b/baracoda/config/defaults.py index f7f4fca..661194d 100644 --- a/baracoda/config/defaults.py +++ b/baracoda/config/defaults.py @@ -1,12 +1,12 @@ from typing import Any, Dict, List -from baracoda.formats import HeronCogUkIdFormatter, HeronPlateCherrypickedFormatter, SequencescapePlateBarcodeFormatter +from baracoda.formats import HeronCogUkIdFormatter, GenericBarcodeFormatter ### # database config ### DB_DBNAME = "baracoda_dev" -DB_HOST = "host.docker.internal" +DB_HOST = "localhost" DB_PASSWORD = "postgres" DB_PORT = "5432" DB_USER = "postgres" @@ -76,8 +76,7 @@ {"prefix": "TFCI", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, {"prefix": "WAHH", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, {"prefix": "WSFT", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, - {"prefix": "HT", "sequence_name": "ht", "formatter_class": HeronPlateCherrypickedFormatter}, - {"prefix": "DN", "sequence_name": "SEQ_DNAPLATE", "formatter_class": SequencescapePlateBarcodeFormatter}, + {"prefix": "HT", "sequence_name": "ht", "formatter_class": GenericBarcodeFormatter}, ] for prefix_item in PREFIXES: diff --git a/baracoda/config/development.py b/baracoda/config/development.py index f0301e0..53a84fd 100644 --- a/baracoda/config/development.py +++ b/baracoda/config/development.py @@ -1,4 +1,8 @@ # flake8: noqa from baracoda.config.defaults import * +from baracoda.formats import GenericBarcodeFormatter + +# Adds a development prefix for sqp sequence +PREFIXES.append({"prefix": "SQPD", "sequence_name": "sqp", "formatter_class": GenericBarcodeFormatter}) # settings here overwrite those in 'defaults.py' diff --git a/baracoda/config/test.py b/baracoda/config/test.py index f0b157e..01829b6 100644 --- a/baracoda/config/test.py +++ b/baracoda/config/test.py @@ -11,5 +11,5 @@ # database config ### DB_DBNAME = "baracoda_test" -DB_HOST = "host.docker.internal" +DB_HOST = "localhost" SQLALCHEMY_DATABASE_URI = f"postgresql+psycopg2://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_DBNAME}" diff --git a/baracoda/formats.py b/baracoda/formats.py index cbfedda..8eee530 100644 --- a/baracoda/formats.py +++ b/baracoda/formats.py @@ -8,16 +8,7 @@ def barcode(self): pass -class SequencescapePlateBarcodeFormatter(FormatterInterface): - def __init__(self, prefix: str): - logger.debug(f"Instantiate formatter PlateBarcodeFormatter") - self.prefix = prefix - - def barcode(self, value: int) -> str: - return value - - -class HeronPlateCherrypickedFormatter(FormatterInterface): +class GenericBarcodeFormatter(FormatterInterface): def __init__(self, prefix: str): logger.debug(f"Instantiate formatter with {prefix}") diff --git a/baracoda/plate_barcodes.py b/baracoda/plate_barcodes.py deleted file mode 100644 index ad7406e..0000000 --- a/baracoda/plate_barcodes.py +++ /dev/null @@ -1,32 +0,0 @@ -import logging -from flask import Blueprint -from http import HTTPStatus -from typing import Any, Tuple -from baracoda.exceptions import InvalidPrefixError -from baracoda.operations import BarcodeOperations -from dicttoxml import dicttoxml - -bp = Blueprint("plate_barcode_creation", __name__) - -logger = logging.getLogger(__name__) - -SEQ_DNAPLATE_PREFIX = "DN" - - -@bp.post("/plate_barcodes.xml") # type: ignore -def get_new_plate_barcode() -> Tuple[Any, int]: - try: - logger.debug(f"Creating a plate_barcode.xml") - operator = BarcodeOperations(prefix=SEQ_DNAPLATE_PREFIX) - barcode = operator.create_barcode() - response = dicttoxml(barcode.to_dict(), attr_type=False, custom_root="plate_barcode") - - return response, HTTPStatus.OK - - except InvalidPrefixError as e: - return dicttoxml([f"{type(e).__name__}"], attr_type=False, custom_root="errors"), HTTPStatus.BAD_REQUEST - except Exception as e: - return ( - dicttoxml([f"{type(e).__name__}"], attr_type=False, custom_root="errors"), - HTTPStatus.INTERNAL_SERVER_ERROR, - ) diff --git a/baracoda/sql/schema.sql b/baracoda/sql/schema.sql index 2d74164..0e594f2 100644 --- a/baracoda/sql/schema.sql +++ b/baracoda/sql/schema.sql @@ -6,6 +6,10 @@ DROP SEQUENCE IF EXISTS ht; CREATE SEQUENCE ht START 111111; +DROP SEQUENCE IF EXISTS sqp; +CREATE SEQUENCE sqp +START 1; + DROP TABLE IF EXISTS barcodes; CREATE TABLE barcodes diff --git a/tests/conftest.py b/tests/conftest.py index 305354f..97dffcc 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -11,7 +11,7 @@ def app(): app = create_app( { - "DB_HOST": "host.docker.internal", + "DB_HOST": "localhost", "DB_PORT": 5432, "DB_USER": "postgres", "DB_PASSWORD": "postgres", @@ -21,7 +21,7 @@ def app(): "SEQUENCE_RESET": True, "SLACK_API_TOKEN": "", "SLACK_CHANNEL_ID": "", - "SQLALCHEMY_DATABASE_URI": "postgresql+psycopg2://postgres:postgres@host.docker.internal:5432/baracoda_test", + "SQLALCHEMY_DATABASE_URI": "postgresql+psycopg2://postgres:postgres@localhost:5432/baracoda_test", "PREFIXES": PREFIXES, } ) diff --git a/tests/data/fixture_data.py b/tests/data/fixture_data.py index e79c759..61f8245 100644 --- a/tests/data/fixture_data.py +++ b/tests/data/fixture_data.py @@ -1,5 +1,5 @@ from typing import Dict, List, Union -from baracoda.formats import HeronCogUkIdFormatter, HeronPlateCherrypickedFormatter +from baracoda.formats import HeronCogUkIdFormatter, GenericBarcodeFormatter PREFIXES: List[Dict[str, Union[str, bool]]] = [ { @@ -30,6 +30,11 @@ { "prefix": "HT", "sequence_name": "ht", - "formatter_class": HeronPlateCherrypickedFormatter, + "formatter_class": GenericBarcodeFormatter, + }, + { + "prefix": "SQPD", + "sequence_name": "sqp", + "formatter_class": GenericBarcodeFormatter, }, ] diff --git a/tests/test_barcodes.py b/tests/test_barcodes.py index 4011ed7..e851a74 100644 --- a/tests/test_barcodes.py +++ b/tests/test_barcodes.py @@ -3,6 +3,7 @@ # sequences # starts at 2000000 for heron # starts at 111111 for ht +# starts at 1 for sqp def test_param_empty_prefix_value(client): @@ -27,6 +28,12 @@ def test_get_new_barcode_for_ht(client): assert response.status_code == HTTPStatus.CREATED +def test_get_new_barcode_for_sqp(client): + response = client.post("/barcodes/SQPD/new") + assert response.json == {"barcode": "SQPD-1"} + assert response.status_code == HTTPStatus.CREATED + + def test_get_new_barcodes_group_as_url_param(client): response = client.post("/barcodes_group/SANG/new?count=3") assert response.json == {"barcodes_group": {"barcodes": ["SANG-30D404", "SANG-30D413", "SANG-30D422"], "id": 1}} diff --git a/tests/test_formats.py b/tests/test_formats.py index a95a6fd..c0a1825 100644 --- a/tests/test_formats.py +++ b/tests/test_formats.py @@ -1,4 +1,4 @@ -from baracoda.formats import HeronCogUkIdFormatter, HeronPlateCherrypickedFormatter +from baracoda.formats import HeronCogUkIdFormatter, GenericBarcodeFormatter def test_checksum_conversion(heron_formatter): @@ -24,10 +24,15 @@ def test_barcode_example_2(): def test_barcode_example_plate_cherrypicked(): - formatter = HeronPlateCherrypickedFormatter(prefix="HT") + formatter = GenericBarcodeFormatter(prefix="HT") assert formatter.barcode(111111) == "HT-111111" +def test_barcode_example_plate_sequencescape(): + formatter = GenericBarcodeFormatter(prefix="SQPD") + assert formatter.barcode(1) == "SQPD-1" + + def barcode_for(barcode: str) -> str: """Exract the prefix and checksum from a given barcode then recreates the barcode given the number and prefix. diff --git a/tests/test_operations.py b/tests/test_operations.py index c156b78..2af02d6 100644 --- a/tests/test_operations.py +++ b/tests/test_operations.py @@ -23,6 +23,12 @@ def test_sequence_is_correct_for_ht_plates(app): assert barcode_operations.sequence_name == "ht" +def test_sequence_is_correct_for_SQPD_plates(app): + with app.app_context(): + barcode_operations = BarcodeOperations(prefix="SQPD") + assert barcode_operations.sequence_name == "sqp" + + def test_error_is_raised_if_prefix_is_not_valid(app): with app.app_context(): with pytest.raises(InvalidPrefixError): From c125a5906621451f2daad8d18c876c4f9de192fd Mon Sep 17 00:00:00 2001 From: Ben Topping Date: Mon, 4 Apr 2022 15:42:24 +0100 Subject: [PATCH 05/33] removed unused dicttoxml package --- Pipfile | 1 - Pipfile.lock | 355 +++++++++++++++++++++++++-------------------------- 2 files changed, 177 insertions(+), 179 deletions(-) diff --git a/Pipfile b/Pipfile index 70da577..8e9a62f 100644 --- a/Pipfile +++ b/Pipfile @@ -25,7 +25,6 @@ psycopg2 = "~=2.9" python-dotenv = "~=0.19" slackclient = "~=2.5" sqlalchemy = "~=1.4" -dicttoxml = "*" [requires] python_version = "3.8" diff --git a/Pipfile.lock b/Pipfile.lock index 8f3b743..6e78833 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "a4cdc3e7010217ec72763b8d48950899b2c0dff1193c2900e1b970645a4f83af" + "sha256": "4c8942d64138982f0fc190d3baf1af0d537595da7fee0c47b339a23fce107cd9" }, "pipfile-spec": 6, "requires": { @@ -143,11 +143,11 @@ }, "click": { "hashes": [ - "sha256:6a7a62563bbfabfda3a38f3023a1db4a35978c0abd76f6c9605ecd6554d6d9b1", - "sha256:8458d7b1287c5fb128c90e23381cf99dcde74beaf6c7ff6384ce84d6fe090adb" + "sha256:24e1a4a9ec5bf6299411369b208c1df2188d9eb8d916302fe6bf03faed227f1e", + "sha256:479707fe14d9ec9a0757618b7a100a0ae4c4e236fac5b7f80ca68028141a1a72" ], - "markers": "python_version >= '3.6'", - "version": "==8.0.4" + "markers": "python_version >= '3.7'", + "version": "==8.1.2" }, "colorlog": { "hashes": [ @@ -157,20 +157,13 @@ "index": "pypi", "version": "==6.6.0" }, - "dicttoxml": { - "hashes": [ - "sha256:ea44cc4ec6c0f85098c57a431a1ee891b3549347b07b7414c8a24611ecf37e45" - ], - "index": "pypi", - "version": "==1.7.4" - }, "flask": { "hashes": [ - "sha256:59da8a3170004800a2837844bfa84d49b022550616070f7cb1a659682b2e7c9f", - "sha256:e1120c228ca2f553b470df4a5fa927ab66258467526069981b3eb0a91902687d" + "sha256:8a4cf32d904cf5621db9f0c9fbcd7efabf3003f22a04e4d0ce790c7137ec5264", + "sha256:a8c9bd3e558ec99646d177a9739c41df1ded0629480b4c8d2975412f3c9519c8" ], "index": "pypi", - "version": "==2.0.3" + "version": "==2.1.1" }, "flask-cors": { "hashes": [ @@ -255,58 +248,63 @@ }, "greenlet": { "hashes": [ - "sha256:03e6e40a1c6d523e59e4b80173986dfb4bdefbbd14104a6f1a9d321bb4dc0226", - "sha256:045a6cdd8d7ba5fffb82b9d4d35ae99bbc57afdd03a8b8eb161ec6fd0d2fc15b", - "sha256:04828fcd02de1fa606560ac13eaa026a3f1859bbb6098cbb4e2c9471bce73e17", - "sha256:063c55ae93b19dcbd077182d34ab7e70838d16edae8a5ba9fe1c8f9a6530201d", - "sha256:08f03790cd6105a5b0f452d798a22bd72944bda41dc674d39dc8e485b730056e", - "sha256:22b2111811abbd2af884426b2286b41ad3dbec15dcd362f68fc319abdf2d6f36", - "sha256:44440890e79d8bded5893fa4c322c3d8bf18fdf87657fd6523252ec247be6f4b", - "sha256:4c74283a777414ea1382448f70340096b360e3dc4bedc64259b0e355328f2f5a", - "sha256:4c79da840373815c8ebbde0e64aa0b74fbfe74394665dcf318cfd7220ca9ed8b", - "sha256:4cedd60664090cc7407119fd40b91c9a2a640909c8c59273b180ba61b1ff9210", - "sha256:53f90311f1779fac5641a75c01ba3b9d088688518915b7138b2f0636f151c20a", - "sha256:5667b436e4a365f9bc9bb98c0d5356c1929a62a51d78373c92870a138ada273a", - "sha256:5aa13f7f2650f39653c096cead3346e0a69cc17ad29a91a3a6776801f124b963", - "sha256:5f9a1fdd3339cd2fcaa2e768fb297429601888ef28ab9509a34cb3dd4497c88f", - "sha256:61c2fbbf1cebb7cbaa35690143b5dc3212d764c7975957cc5aeacfd5686ad534", - "sha256:63e0620aec7dc22fd988ec2c62c7e678921d13cf8eaefc2d2df6cc065998cc7b", - "sha256:676d9c5f0428da9f69621ff71091b4c02d952a2f7e71a47891cde93b9114f048", - "sha256:678065b2bc9a2fb804a61cfddac6b44cbddb1787e619e47eae3cb25c57709516", - "sha256:68ebc87166fc0a13d9fb0b7f231790369ea03ca0b7efa6d100ea2f701dfb6a6a", - "sha256:69a681b219c1208f0dd40c29c142e44a436f1f7add8ae9ac93470c1764b38980", - "sha256:6bcfa9dd47645ea7ab072422405858ab8afb8420014bd2dfde0fed78d4026e08", - "sha256:757ddbadc18515d28018c53f98bf85ea7af9fb7accff0d7b5d681bcbfdca9a74", - "sha256:7bf83a6b7f068e631cfae0a0fc9f7ca73bc8115c0d6240131b1d5c5d7a65bbc2", - "sha256:800c0e9f13df16c36df8b040bb1693cea4e8375b8ddc730013c87ed656d3659c", - "sha256:80e8f41031b26856b8e4a0f65a395bb57cb3fcc22a810f87994a266f63f22fd0", - "sha256:81e8c96d0f590c11ffcbed42293dc3e10be1639a57b0e531a937ac61fb400224", - "sha256:85a8b1bfccf88326cf36a43a6cd7990afa22f0a02da624ca18ac7eb16bc14edd", - "sha256:9ad8a0b3542747b4311f03784a87ba2f1c2a0bf15e7e95ecf06d97ccea2f81e9", - "sha256:9ae7b519fbacaa5bf7e9ba71d582da463ce049e493568be6da80f444504bc951", - "sha256:9d531a58f3feb283f120bacc817e4d75289bde8263363fbfeedf96376a1c68a8", - "sha256:9db3f35c9c493dbbf053fb13285822bccbf2a76049328d10edf0daf4591b65a0", - "sha256:9dcc10e86164853c5267bfac43816048a12dcf4c898c1b83a2eb8d5933da9ee0", - "sha256:a172161ec09ef67f8b88fce873f3b6b37ca99b4ddd2536147dc611d4d645bc28", - "sha256:a34b6d63ebaab722f79df4f79cbceab6fac2f96546561848a8ce1513a4478e16", - "sha256:a74c507dbef45ba85f8565a1bc297f97342a246078af5ea05330992d5a26e72c", - "sha256:ab6385c0a16c1d33ab45593f0cad49834918ad83d0406b58cd62f1454a1778c6", - "sha256:ab951ebc9f4c63d9ba0518d533f358519e3633c3a263bfba8674ee0c5043bd1a", - "sha256:b3ff97e9761e3c392eaba4b7f17da9deb9e1177b372313611ee2358a19f235bb", - "sha256:b68ff6925dd210a4eac47df411316168a2448178f837fcb01597d9a183ad8603", - "sha256:b904cbb4b69fa959674043d39aa34ec84abca917037ec931d72003b60b311174", - "sha256:be681332594c3361a32198fe591f966e6114c67bf62e1cbb1f6fe700e7f809b3", - "sha256:c006d07a64777466b4ecf75a3fc86f7c687021e9b1fae0a0c3157ae93ce44563", - "sha256:c9525a82b0ff1bb35253ec2d2e9430c53479274e65bddefbfd5db74972c7202d", - "sha256:cb1f258971419b4b34f71736b09ce7d3daeea71a5660eb28b34f8a80520ee20b", - "sha256:ce1f6e65a3b9b6a8b5b1f64ef75e49fa159721c8882027b50cfaa82472bd1c10", - "sha256:ce2ec312bcb516a83780b659fd008fdd26af449b653c8ffa52e203a397765cc2", - "sha256:da809e3861a8f697727b6bdf4c735184912215d2eb9df15eb7ad3d6c9d533a52", - "sha256:edacb7c0f8a42e6df031ef675e29156ca933403f1cb8027e8f2c82ad8d68bc67", - "sha256:fa04d0419b2ed61125bb8a4a0a810cad428546adeee87cab6f2beee50259fecc" + "sha256:004aed447382d80a56ecc354a6d807f305e6c808714ce6ccbca4839c94fae81d", + "sha256:068d68fad6bd623e29a2d36e74538c9b9d6dc6464931cd27d93da6cfc6a7f242", + "sha256:06fd4075754009c9817c6b4e1dc0af4616de52757b6ca973a81c3c1aadc28257", + "sha256:1004cb542451814b12a4f38e835a47734e2b2c683acbf463d5ae76282a3974cf", + "sha256:10c358633a8b27bfc32d27114ef2ca2ddc9f1f89f1643d1157b85e1fdd695315", + "sha256:115bc25fefbdc692c4483e9ddb9011ccd0251590ed59dbfff0f4eb7050bf99c4", + "sha256:1d987a2579336792f73ae6b106c2f087e32afc8573fbf9566f123ac6d8cfb72f", + "sha256:2128d727fd1e8afba8e68feb2cdcf88c90163b69ddc9707722a3e491c5280720", + "sha256:230132c241fe284f93f2e7b3969e9b22bbd76ef98cf93e382c945d378907f5a4", + "sha256:23558f7bd08a663386c032ab8d302d613d2d02ae0c9758ad410bab6035b58d3d", + "sha256:255d520d3e4a5f16883b182e1a94219fe455ab4f50aaaf534bfd6d64ee728397", + "sha256:2a6bc19a728f6f643cfc89b876159a1a25a8f7d8700c013d48a73691f80b4550", + "sha256:379bed346ef8ba0a0e698b3c5975a44d15dd4a5bbff40bbd7fd548b445d5550b", + "sha256:3b12d0866759db93b0a893b4e50a7d7d1681519d2346c26695bb8bb2c652230e", + "sha256:40d491944f69e350e1e8b25f6ca49459824ede1678ec0cd4b5541f41edc06614", + "sha256:471484c7b9d7b7867263051aa81cdeed6e06b455e629a7f05eb91a6cb8bd0836", + "sha256:488c557080557bc01aabb3e1bda7225c68455b853733a8652857ac0d810dad1b", + "sha256:49c2e76e7aa81ba889b3c183e2341af3cc6161ee38852085110ae49d5b5d9a40", + "sha256:52d13ec90236e5935ed6da044e78faa1371d5116cc43fe6d7ca8994dd619ef96", + "sha256:57898c69a253d81f487787bdd538629fabd671fab8a9e31b041ca30965fd9556", + "sha256:5d577eef5beb5730ef01ab39983eb852a97c359b7a546809adf70c409f4b2ecc", + "sha256:6a41987c1474c9158a0c0c96611530a8f299bc547d35bee8add981b8b2534f74", + "sha256:6ae67b7df8db3626af8e042e9c6949cfa27d1a3bbbfdff29e45b72bb6673a650", + "sha256:6c42c27e9d12e8a481aff469ffe8dd4ce0484c354a418470960f760f6ae41e7c", + "sha256:6c4a90c9f6128b4d0905a89930bd325e0491574e5cb453f606bb7094a3197587", + "sha256:6e64518e5833ac2d9359b6d9bd4df2c0cf441a0f3a4eca9e735fbea99009fa70", + "sha256:6fd3a270c23c5b42d86a9c7c6b0229f23ee4a7a4cabdaaa1693ad7a0982d13cb", + "sha256:70db73351e0fcf11a76288c47a0469d9a330bcb2e7618c5eb57432b8caa82403", + "sha256:771f401692046845626cbdf1dd0f04e999413ede0ee9ad39033fe30b5fa2e845", + "sha256:7935026ec61b967cbc6b746c0ca75c1651ea118d7fee4d259cff9e6866153374", + "sha256:7b76b1cac9baac1980210e29145800954e7b42e91ef69c4d695de1cab87ce41f", + "sha256:7e3f37c11b6699b1a1e0fcc0e88829dba4f2866546381b05ab8b3f4db645a823", + "sha256:8370fa65ad421484894f559055f951843754153b72b9bca2ebdc5288efe2e3f0", + "sha256:8ae9c443d44a4e23252632e4d7775f419f992d0df3eff923e23775f5cc551d39", + "sha256:8b31d85f2781e44f1ffaaf7ea07f484e7d42317c677c355fa77b4a1a4bea7394", + "sha256:8b450336b27f3b375cadc474c6704838eaa8dd3ca312aac3bb69d92264a8e638", + "sha256:9ce84357388a76d886febff4e50e321c212ffd3248b590960b2da6e02404a5c9", + "sha256:a23e986fb0ba8e7407286add41fa0d4207be44e3dce1b04789f4757800eca1cf", + "sha256:a81610ee00d0da9cd2c8679479b7791149365b6dfb3971b01b22ee29b04787ce", + "sha256:b4e40444975e5ab0ed3004369209c39a28e084951daaeee4919f164b6b849b14", + "sha256:b66600de16702b9dfa74bea34524b55183a2183e5fd92f20fe6c2fcae550a64c", + "sha256:ba6ee18694d3673796b7a31b7d21254e87e9e43ca5be56f323fd396111255315", + "sha256:bd03837da28293baa39bdfc3cada69e2f8807f423ae06168aa28d2b32c63a6b6", + "sha256:bd2192070f88c0778ae1d68a0980fdece3473498c1db37f3794e3454f91e3ecf", + "sha256:c1f6f1a3cc013012cd1da913c40b13e6d721046a8c8a0ea0cde94069645a75db", + "sha256:ce10a8e7e067bde3c1fbf494d2b8859db510206030b0b67bc3af90b0eb1887b9", + "sha256:d31386d208303a5a6cf0819ef9f6db6680bab9e4ca8e48adb3d4b26ead89beb7", + "sha256:d83b3af53b201970973c5574b39df226746194063bb248a53fd12b470ac34319", + "sha256:df9657b212c054ac6d803290d7c4bcd7790af0b725984fce1eeb0a1e3f2d9798", + "sha256:e576e5fd3f129e6b3595dc734ac7f2b8c548f19ef07781194bc538dc9c0cdbbc", + "sha256:e7400358558094c1bcedc75f3b3c4f400c53130b44833848890a99968dee6a64", + "sha256:eb6a385f8577d30e4cb43dd555fb134ddaae1edeb84205e09dabec332bf49fd0", + "sha256:f27f0875e0873f6bf5df09a456bfcac0667824cabac4cad30b43f36e0382ffe7", + "sha256:fcd4a6d04995f1d66bc78b503e4e59ae72fd32aaec4f661657fe5ae5c1aa4ce3" ], "markers": "python_version >= '3' and platform_machine == 'aarch64' or (platform_machine == 'ppc64le' or (platform_machine == 'x86_64' or (platform_machine == 'amd64' or (platform_machine == 'AMD64' or (platform_machine == 'win32' or platform_machine == 'WIN32')))))", - "version": "==2.0.0a1" + "version": "==2.0.0a2" }, "gunicorn": { "hashes": [ @@ -334,27 +332,27 @@ }, "importlib-resources": { "hashes": [ - "sha256:33a95faed5fc19b4bc16b29a6eeae248a3fe69dd55d4d229d2b480e23eeaad45", - "sha256:d756e2f85dd4de2ba89be0b21dba2a3bbec2e871a42a3a16719258a11f87506b" + "sha256:1b93238cbf23b4cde34240dd8321d99e9bf2eb4bc91c0c99b2886283e7baad85", + "sha256:a9dd72f6cc106aeb50f6e66b86b69b454766dd6e39b69ac68450253058706bcc" ], "markers": "python_version < '3.9'", - "version": "==5.4.0" + "version": "==5.6.0" }, "itsdangerous": { "hashes": [ - "sha256:7b7d3023cd35d9cb0c1fd91392f8c95c6fa02c59bf8ad64b8849be3401b95afb", - "sha256:935642cd4b987cdbee7210080004033af76306757ff8b4c0a506a4b6e06f02cf" + "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44", + "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a" ], "markers": "python_version >= '3.7'", - "version": "==2.1.1" + "version": "==2.1.2" }, "jinja2": { "hashes": [ - "sha256:077ce6014f7b40d03b47d1f1ca4b0fc8328a692bd284016f806ed0eaca390ad8", - "sha256:611bb273cd68f3b993fabdc4064fc858c5b47a973cb5aa7999ec1ba405c87cd7" + "sha256:539835f51a74a69f41b848a9645dbdc35b4f20a3b601e2d9a7e22947b15ff119", + "sha256:640bed4bb501cbd17194b3cace1dc2126f5b619cf068a726b98192a0fde74ae9" ], - "markers": "python_version >= '3.6'", - "version": "==3.0.3" + "markers": "python_version >= '3.7'", + "version": "==3.1.1" }, "mako": { "hashes": [ @@ -494,19 +492,19 @@ }, "python-dotenv": { "hashes": [ - "sha256:32b2bdc1873fd3a3c346da1c6db83d0053c3c62f28f1f38516070c4c8971b1d3", - "sha256:a5de49a31e953b45ff2d2fd434bbc2670e8db5273606c1e737cc6b93eff3655f" + "sha256:b7e3b04a59693c42c36f9ab1cc2acc46fa5df8c78e178fc33a8d4cd05c8d498f", + "sha256:d92a187be61fe482e4fd675b6d52200e7be63a12b724abbf931a40ce4fa92938" ], "index": "pypi", - "version": "==0.19.2" + "version": "==0.20.0" }, "setuptools": { "hashes": [ - "sha256:6599055eeb23bfef457d5605d33a4d68804266e6cb430b0fb12417c5efeae36c", - "sha256:782ef48d58982ddb49920c11a0c5c9c0b02e7d7d1c2ad0aa44e1a1e133051c96" + "sha256:7999cbd87f1b6e1f33bf47efa368b224bed5e27b5ef2c4d46580186cbcb1a86a", + "sha256:a65e3802053e99fc64c6b3b29c11132943d5b8c8facbcc461157511546510967" ], "markers": "python_version >= '3.7'", - "version": "==60.10.0" + "version": "==62.0.0" }, "six": { "hashes": [ @@ -526,52 +524,53 @@ }, "sqlalchemy": { "hashes": [ - "sha256:04164e0063feb7aedd9d073db0fd496edb244be40d46ea1f0d8990815e4b8c34", - "sha256:159c2f69dd6efd28e894f261ffca1100690f28210f34cfcd70b895e0ea7a64f3", - "sha256:199dc6d0068753b6a8c0bd3aceb86a3e782df118260ebc1fa981ea31ee054674", - "sha256:1bbac3e8293b34c4403d297e21e8f10d2a57756b75cff101dc62186adec725f5", - "sha256:20e9eba7fd86ef52e0df25bea83b8b518dfdf0bce09b336cfe51671f52aaaa3f", - "sha256:290cbdf19129ae520d4bdce392648c6fcdbee763bc8f750b53a5ab51880cb9c9", - "sha256:316270e5867566376e69a0ac738b863d41396e2b63274616817e1d34156dff0e", - "sha256:3f88a4ee192142eeed3fe173f673ea6ab1f5a863810a9d85dbf6c67a9bd08f97", - "sha256:4aa96e957141006181ca58e792e900ee511085b8dae06c2d08c00f108280fb8a", - "sha256:4b2bcab3a914715d332ca783e9bda13bc570d8b9ef087563210ba63082c18c16", - "sha256:576684771456d02e24078047c2567025f2011977aa342063468577d94e194b00", - "sha256:5a2e73508f939175363d8a4be9dcdc84cf16a92578d7fa86e6e4ca0e6b3667b2", - "sha256:5ba59761c19b800bc2e1c9324da04d35ef51e4ee9621ff37534bc2290d258f71", - "sha256:5dc9801ae9884e822ba942ca493642fb50f049c06b6dbe3178691fce48ceb089", - "sha256:6fdd2dc5931daab778c2b65b03df6ae68376e028a3098eb624d0909d999885bc", - "sha256:708973b5d9e1e441188124aaf13c121e5b03b6054c2df59b32219175a25aa13e", - "sha256:7ff72b3cc9242d1a1c9b84bd945907bf174d74fc2519efe6184d6390a8df478b", - "sha256:8679f9aba5ac22e7bce54ccd8a77641d3aea3e2d96e73e4356c887ebf8ff1082", - "sha256:8b9a395122770a6f08ebfd0321546d7379f43505882c7419d7886856a07caa13", - "sha256:8e1e5d96b744a4f91163290b01045430f3f32579e46d87282449e5b14d27d4ac", - "sha256:9a0195af6b9050c9322a97cf07514f66fe511968e623ca87b2df5e3cf6349615", - "sha256:9cb5698c896fa72f88e7ef04ef62572faf56809093180771d9be8d9f2e264a13", - "sha256:b3f1d9b3aa09ab9adc7f8c4b40fc3e081eb903054c9a6f9ae1633fe15ae503b4", - "sha256:bb42f9b259c33662c6a9b866012f6908a91731a419e69304e1261ba3ab87b8d1", - "sha256:bca714d831e5b8860c3ab134c93aec63d1a4f493bed20084f54e3ce9f0a3bf99", - "sha256:bedd89c34ab62565d44745212814e4b57ef1c24ad4af9b29c504ce40f0dc6558", - "sha256:bfec934aac7f9fa95fc82147a4ba5db0a8bdc4ebf1e33b585ab8860beb10232f", - "sha256:c7046f7aa2db445daccc8424f50b47a66c4039c9f058246b43796aa818f8b751", - "sha256:d7e483f4791fbda60e23926b098702340504f7684ce7e1fd2c1bf02029288423", - "sha256:dd93162615870c976dba43963a24bb418b28448fef584f30755990c134a06a55", - "sha256:e4607d2d16330757818c9d6fba322c2e80b4b112ff24295d1343a80b876eb0ed", - "sha256:e9a680d9665f88346ed339888781f5236347933906c5a56348abb8261282ec48", - "sha256:edfcf93fd92e2f9eef640b3a7a40db20fe3c1d7c2c74faa41424c63dead61b76", - "sha256:f7e4a3c0c3c596296b37f8427c467c8e4336dc8d50f8ed38042e8ba79507b2c9", - "sha256:fff677fa4522dafb5a5e2c0cf909790d5d367326321aeabc0dffc9047cb235bd" + "sha256:045d6a26c262929af0b9cb25441aae675ac04db4ea8bd2446b355617cd6b6b7d", + "sha256:07f4dab2deb6d34618a2ccfff3971a85923ad7c3a9a45401818870fc51d3f0cc", + "sha256:08aaad905aba8940f27aeb9f1f851bf63f18ef97b0062ca41f64afc4b64e0e8c", + "sha256:27a42894a2751e438eaed12fc0dcfe741ff2f66c14760d081222c5adc5460064", + "sha256:2a3e4dc7c452ba3c0f3175ad5a8e0ba49c2b0570a8d07272cf50844c8d78e74f", + "sha256:345306707bb0e51e7cd6e7573adafbce018894ee5e3b9c31134545f704936db0", + "sha256:36f08d94670315ca04c8139bd80b3e02b9dd9cc66fc11bcb96fd10ad51a051ab", + "sha256:3ebb97ed96f4506e2f212e1fcf0ec07a103bb194938627660a5acb4d9feae49c", + "sha256:40b995d7aeeb6f88a1927ce6692c0f626b59d8effd3e1d597f125e141707b37c", + "sha256:4414ace6e3a5e39523e55a5d9f3b215699b2ead4ff91fca98f1b659b7ab2d92a", + "sha256:50107d8183da3fbe5715957aa3954cd9d82aed555c5b4d3fd37fac861af422fa", + "sha256:50174e173d03209c34e07e7b57cca48d0082ac2390edf927aafc706c111da11e", + "sha256:5e88912bf192e7b5739c446d2276e1cba74cfa6c1c93eea2b2534404f6be1dbd", + "sha256:621d3f6c0ba2407bb97e82b649be5ca7d5b6c201dcfb964ce13f517bf1cb6305", + "sha256:623bac2d6bdca3f3e61cf1e1c466c5fb9f5cf08735736ee1111187b7a4108891", + "sha256:671f61c3db4595b0e86cc4b30f675a7c0206d9ce99f041b4f6761c7ddd1e0074", + "sha256:67c1c27c48875afc950bee5ee24582794f20b545e64e4f9ca94071a9b514d6ed", + "sha256:6a6cfd468f54d65324fd3847cfd0148b0610efa6a43e5f5fcc89f455696ae9e7", + "sha256:70048a83f0a1ece1fcd7189891c888e20af2c57fbd33eb760d8cece9843b896c", + "sha256:7ee14a7f9f76d1ef9d5e5b760c9252617c839b87eee04d1ce8325ac66ae155c4", + "sha256:804cf491437f3e4ce31247ab4b309b181f06ecc97d309b746d10f09439b4eb85", + "sha256:878c7beaafa365602762c19f638282e1885454fed1aed86f8fae038933c7c671", + "sha256:954ea8c527c4322afb6885944904714893af81fe9167e421273770991bf08a4a", + "sha256:a47bf6b7ca6c28e4f4e262fabcf5be6b907af81be36de77839c9eeda2cdf3bb3", + "sha256:a4fb5c6ee84a6bba4ff6f9f5379f0b3a0ffe9de7ba5a0945659b3da8d519709b", + "sha256:b34bbc683789559f1bc9bb685fc162e0956dbbdfbe2fbd6755a9f5982c113610", + "sha256:c025d45318b73c0601cca451532556cbab532b2742839ebb8cb58f9ebf06811e", + "sha256:c3ad7f5b61ba014f5045912aea15b03c473bb02b1c07fd92c9d2c794fa183276", + "sha256:c9218e3519398129e364121e0d89823e6ba2a2b77c28bfc661face0829c41433", + "sha256:cd5cffd1dd753828f1069f33062f3896e51c990acd957c264f40e051b3e19887", + "sha256:d8efcaa709ea8e7c08c3d3e7639c39b36083f5a995f397f9e6eedf5f5e4e4946", + "sha256:e297a5cc625e3f1367a82deedf2d48ee4d2b2bd263b8b8d2efbaaf5608b5229e", + "sha256:e67278ceb63270cdac0a7b89fc3c29a56f7dac9616a7ee48e7ad6b52e3b631e5", + "sha256:eb6558ba07409dafa18c793c34292b3265be455904966f0724c10198829477e3", + "sha256:f197c66663ed0f9e1178d51141d864688fb244a83f6b17f667d521e482537b2e", + "sha256:f47996b1810894f766c9ee689607077c6c0e0fd6761e04c12ba13efb56d50c1d" ], "index": "pypi", - "version": "==1.4.32" + "version": "==1.4.34" }, "werkzeug": { "hashes": [ - "sha256:1421ebfc7648a39a5c58c601b154165d05cf47a3cd0ccb70857cbdacf6c8f2b8", - "sha256:b863f8ff057c522164b6067c9e28b041161b4be5ba4d0daceeaa50a163822d3c" + "sha256:3c5493ece8268fecdcdc9c0b112211acd006354723b280d643ec732b6d4063d6", + "sha256:f8e89a20aeabbe8a893c24a461d3ee5dad2123b05cc6abd73ceed01d39c3ae74" ], - "markers": "python_version >= '3.6'", - "version": "==2.0.3" + "markers": "python_version >= '3.7'", + "version": "==2.1.1" }, "yarl": { "hashes": [ @@ -653,11 +652,11 @@ }, "zipp": { "hashes": [ - "sha256:9f50f446828eb9d45b267433fd3e9da8d801f614129124863f9c51ebceafb87d", - "sha256:b47250dd24f92b7dd6a0a8fc5244da14608f3ca90a5efcd37a3b1642fac9a375" + "sha256:56bf8aadb83c24db6c4b577e13de374ccfb67da2078beba1d037c17980bf43ad", + "sha256:c4f6e5bbf48e74f7a38e7cc5b0480ff42b0ae5178957d564d18932525d5cf099" ], "markers": "python_version >= '3.7'", - "version": "==3.7.0" + "version": "==3.8.0" } }, "develop": { @@ -671,40 +670,40 @@ }, "black": { "hashes": [ - "sha256:07e5c049442d7ca1a2fc273c79d1aecbbf1bc858f62e8184abe1ad175c4f7cc2", - "sha256:0e21e1f1efa65a50e3960edd068b6ae6d64ad6235bd8bfea116a03b21836af71", - "sha256:1297c63b9e1b96a3d0da2d85d11cd9bf8664251fd69ddac068b98dc4f34f73b6", - "sha256:228b5ae2c8e3d6227e4bde5920d2fc66cc3400fde7bcc74f480cb07ef0b570d5", - "sha256:2d6f331c02f0f40aa51a22e479c8209d37fcd520c77721c034517d44eecf5912", - "sha256:2ff96450d3ad9ea499fc4c60e425a1439c2120cbbc1ab959ff20f7c76ec7e866", - "sha256:3524739d76b6b3ed1132422bf9d82123cd1705086723bc3e235ca39fd21c667d", - "sha256:35944b7100af4a985abfcaa860b06af15590deb1f392f06c8683b4381e8eeaf0", - "sha256:373922fc66676133ddc3e754e4509196a8c392fec3f5ca4486673e685a421321", - "sha256:5fa1db02410b1924b6749c245ab38d30621564e658297484952f3d8a39fce7e8", - "sha256:6f2f01381f91c1efb1451998bd65a129b3ed6f64f79663a55fe0e9b74a5f81fd", - "sha256:742ce9af3086e5bd07e58c8feb09dbb2b047b7f566eb5f5bc63fd455814979f3", - "sha256:7835fee5238fc0a0baf6c9268fb816b5f5cd9b8793423a75e8cd663c48d073ba", - "sha256:8871fcb4b447206904932b54b567923e5be802b9b19b744fdff092bd2f3118d0", - "sha256:a7c0192d35635f6fc1174be575cb7915e92e5dd629ee79fdaf0dcfa41a80afb5", - "sha256:b1a5ed73ab4c482208d20434f700d514f66ffe2840f63a6252ecc43a9bc77e8a", - "sha256:c8226f50b8c34a14608b848dc23a46e5d08397d009446353dad45e04af0c8e28", - "sha256:ccad888050f5393f0d6029deea2a33e5ae371fd182a697313bdbd835d3edaf9c", - "sha256:dae63f2dbf82882fa3b2a3c49c32bffe144970a573cd68d247af6560fc493ae1", - "sha256:e2f69158a7d120fd641d1fa9a921d898e20d52e44a74a6fbbcc570a62a6bc8ab", - "sha256:efbadd9b52c060a8fc3b9658744091cb33c31f830b3f074422ed27bad2b18e8f", - "sha256:f5660feab44c2e3cb24b2419b998846cbb01c23c7fe645fee45087efa3da2d61", - "sha256:fdb8754b453fb15fad3f72cd9cad3e16776f0964d67cf30ebcbf10327a3777a3" + "sha256:06f9d8846f2340dfac80ceb20200ea5d1b3f181dd0556b47af4e8e0b24fa0a6b", + "sha256:10dbe6e6d2988049b4655b2b739f98785a884d4d6b85bc35133a8fb9a2233176", + "sha256:2497f9c2386572e28921fa8bec7be3e51de6801f7459dffd6e62492531c47e09", + "sha256:30d78ba6bf080eeaf0b7b875d924b15cd46fec5fd044ddfbad38c8ea9171043a", + "sha256:328efc0cc70ccb23429d6be184a15ce613f676bdfc85e5fe8ea2a9354b4e9015", + "sha256:35020b8886c022ced9282b51b5a875b6d1ab0c387b31a065b84db7c33085ca79", + "sha256:5795a0375eb87bfe902e80e0c8cfaedf8af4d49694d69161e5bd3206c18618bb", + "sha256:5891ef8abc06576985de8fa88e95ab70641de6c1fca97e2a15820a9b69e51b20", + "sha256:637a4014c63fbf42a692d22b55d8ad6968a946b4a6ebc385c5505d9625b6a464", + "sha256:67c8301ec94e3bcc8906740fe071391bce40a862b7be0b86fb5382beefecd968", + "sha256:6d2fc92002d44746d3e7db7cf9313cf4452f43e9ea77a2c939defce3b10b5c82", + "sha256:6ee227b696ca60dd1c507be80a6bc849a5a6ab57ac7352aad1ffec9e8b805f21", + "sha256:863714200ada56cbc366dc9ae5291ceb936573155f8bf8e9de92aef51f3ad0f0", + "sha256:9b542ced1ec0ceeff5b37d69838106a6348e60db7b8fdd245294dc1d26136265", + "sha256:a6342964b43a99dbc72f72812bf88cad8f0217ae9acb47c0d4f141a6416d2d7b", + "sha256:ad4efa5fad66b903b4a5f96d91461d90b9507a812b3c5de657d544215bb7877a", + "sha256:bc58025940a896d7e5356952228b68f793cf5fcb342be703c3a2669a1488cb72", + "sha256:cc1e1de68c8e5444e8f94c3670bb48a2beef0e91dddfd4fcc29595ebd90bb9ce", + "sha256:cee3e11161dde1b2a33a904b850b0899e0424cc331b7295f2a9698e79f9a69a0", + "sha256:e3556168e2e5c49629f7b0f377070240bd5511e45e25a4497bb0073d9dda776a", + "sha256:e8477ec6bbfe0312c128e74644ac8a02ca06bcdb8982d4ee06f209be28cdf163", + "sha256:ee8f1f7228cce7dffc2b464f07ce769f478968bfb3dd1254a4c2eeed84928aad", + "sha256:fd57160949179ec517d32ac2ac898b5f20d68ed1a9c977346efbac9c2f1e779d" ], "index": "pypi", - "version": "==22.1.0" + "version": "==22.3.0" }, "click": { "hashes": [ - "sha256:6a7a62563bbfabfda3a38f3023a1db4a35978c0abd76f6c9605ecd6554d6d9b1", - "sha256:8458d7b1287c5fb128c90e23381cf99dcde74beaf6c7ff6384ce84d6fe090adb" + "sha256:24e1a4a9ec5bf6299411369b208c1df2188d9eb8d916302fe6bf03faed227f1e", + "sha256:479707fe14d9ec9a0757618b7a100a0ae4c4e236fac5b7f80ca68028141a1a72" ], - "markers": "python_version >= '3.6'", - "version": "==8.0.4" + "markers": "python_version >= '3.7'", + "version": "==8.1.2" }, "coverage": { "extras": [ @@ -780,32 +779,32 @@ }, "mypy": { "hashes": [ - "sha256:080097eee5393fd740f32c63f9343580aaa0fb1cda0128fd859dfcf081321c3d", - "sha256:0d3bcbe146247997e03bf030122000998b076b3ac6925b0b6563f46d1ce39b50", - "sha256:0dd441fbacf48e19dc0c5c42fafa72b8e1a0ba0a39309c1af9c84b9397d9b15a", - "sha256:108f3c7e14a038cf097d2444fa0155462362c6316e3ecb2d70f6dd99cd36084d", - "sha256:3bada0cf7b6965627954b3a128903a87cac79a79ccd83b6104912e723ef16c7b", - "sha256:3cf77f138efb31727ee7197bc824c9d6d7039204ed96756cc0f9ca7d8e8fc2a4", - "sha256:42c216a33d2bdba08098acaf5bae65b0c8196afeb535ef4b870919a788a27259", - "sha256:465a6ce9ca6268cadfbc27a2a94ddf0412568a6b27640ced229270be4f5d394d", - "sha256:6a8e1f63357851444940351e98fb3252956a15f2cabe3d698316d7a2d1f1f896", - "sha256:745071762f32f65e77de6df699366d707fad6c132a660d1342077cbf671ef589", - "sha256:818cfc51c25a5dbfd0705f3ac1919fff6971eb0c02e6f1a1f6a017a42405a7c0", - "sha256:8e5974583a77d630a5868eee18f85ac3093caf76e018c510aeb802b9973304ce", - "sha256:8eaf55fdf99242a1c8c792247c455565447353914023878beadb79600aac4a2a", - "sha256:98f61aad0bb54f797b17da5b82f419e6ce214de0aa7e92211ebee9e40eb04276", - "sha256:b2ce2788df0c066c2ff4ba7190fa84f18937527c477247e926abeb9b1168b8cc", - "sha256:b30d29251dff4c59b2e5a1fa1bab91ff3e117b4658cb90f76d97702b7a2ae699", - "sha256:bf446223b2e0e4f0a4792938e8d885e8a896834aded5f51be5c3c69566495540", - "sha256:cbcc691d8b507d54cb2b8521f0a2a3d4daa477f62fe77f0abba41e5febb377b7", - "sha256:d051ce0946521eba48e19b25f27f98e5ce4dbc91fff296de76240c46b4464df0", - "sha256:d61b73c01fc1de799226963f2639af831307fe1556b04b7c25e2b6c267a3bc76", - "sha256:eea10982b798ff0ccc3b9e7e42628f932f552c5845066970e67cd6858655d52c", - "sha256:f79137d012ff3227866222049af534f25354c07a0d6b9a171dba9f1d6a1fdef4", - "sha256:fc5ecff5a3bbfbe20091b1cad82815507f5ae9c380a3a9bf40f740c70ce30a9b" + "sha256:0e2dd88410937423fba18e57147dd07cd8381291b93d5b1984626f173a26543e", + "sha256:10daab80bc40f84e3f087d896cdb53dc811a9f04eae4b3f95779c26edee89d16", + "sha256:17e44649fec92e9f82102b48a3bf7b4a5510ad0cd22fa21a104826b5db4903e2", + "sha256:1a0459c333f00e6a11cbf6b468b870c2b99a906cb72d6eadf3d1d95d38c9352c", + "sha256:246e1aa127d5b78488a4a0594bd95f6d6fb9d63cf08a66dafbff8595d8891f67", + "sha256:2b184db8c618c43c3a31b32ff00cd28195d39e9c24e7c3b401f3db7f6e5767f5", + "sha256:2bc249409a7168d37c658e062e1ab5173300984a2dada2589638568ddc1db02b", + "sha256:3841b5433ff936bff2f4dc8d54cf2cdbfea5d8e88cedfac45c161368e5770ba6", + "sha256:4c3e497588afccfa4334a9986b56f703e75793133c4be3a02d06a3df16b67a58", + "sha256:5bf44840fb43ac4074636fd47ee476d73f0039f4f54e86d7265077dc199be24d", + "sha256:64235137edc16bee6f095aba73be5334677d6f6bdb7fa03cfab90164fa294a17", + "sha256:6776e5fa22381cc761df53e7496a805801c1a751b27b99a9ff2f0ca848c7eca0", + "sha256:6ce34a118d1a898f47def970a2042b8af6bdcc01546454726c7dd2171aa6dfca", + "sha256:6f6ad963172152e112b87cc7ec103ba0f2db2f1cd8997237827c052a3903eaa6", + "sha256:6f7106cbf9cc2f403693bf50ed7c9fa5bb3dfa9007b240db3c910929abe2a322", + "sha256:7742d2c4e46bb5017b51c810283a6a389296cda03df805a4f7869a6f41246534", + "sha256:9521c1265ccaaa1791d2c13582f06facf815f426cd8b07c3a485f486a8ffc1f3", + "sha256:a1b383fe99678d7402754fe90448d4037f9512ce70c21f8aee3b8bf48ffc51db", + "sha256:b840cfe89c4ab6386c40300689cd8645fc8d2d5f20101c7f8bd23d15fca14904", + "sha256:d8d3ba77e56b84cd47a8ee45b62c84b6d80d32383928fe2548c9a124ea0a725c", + "sha256:dcd955f36e0180258a96f880348fbca54ce092b40fbb4b37372ae3b25a0b0a46", + "sha256:e865fec858d75b78b4d63266c9aff770ecb6a39dfb6d6b56c47f7f8aba6baba8", + "sha256:edf7237137a1a9330046dbb14796963d734dd740a98d5e144a3eb1d267f5f9ee" ], "index": "pypi", - "version": "==0.941" + "version": "==0.942" }, "mypy-extensions": { "hashes": [ @@ -906,7 +905,7 @@ "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f" ], - "markers": "python_version >= '3.7'", + "markers": "python_version < '3.11'", "version": "==2.0.1" }, "types-click": { @@ -950,7 +949,7 @@ "sha256:1a9462dcc3347a79b1f1c0271fbe79e844580bb598bafa1ed208b94da3cdcd42", "sha256:21c85e0fe4b9a155d0799430b0ad741cdce7e359660ccbd8b530613e8df88ce2" ], - "markers": "python_version >= '3.6'", + "markers": "python_version < '3.10'", "version": "==4.1.1" } } From a83b43316165784050cd063bf89ac0bd3e151cf1 Mon Sep 17 00:00:00 2001 From: Ben Topping Date: Mon, 4 Apr 2022 15:44:51 +0100 Subject: [PATCH 06/33] fixed merge conflicts --- Pipfile.lock | 574 +++++++++++++++++++++++++-------------------------- 1 file changed, 287 insertions(+), 287 deletions(-) diff --git a/Pipfile.lock b/Pipfile.lock index 6e78833..573bc77 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -104,11 +104,11 @@ }, "alembic": { "hashes": [ - "sha256:29be0856ec7591c39f4e1cb10f198045d890e6e2274cf8da80cb5e721a09642b", - "sha256:4961248173ead7ce8a21efb3de378f13b8398e6630fab0eb258dc74a8af24c58" + "sha256:a21fedebb3fb8f6bbbba51a11114f08c78709377051384c9c5ead5705ee93a51", + "sha256:e78be5b919f5bb184e3e0e2dd1ca986f2362e29a2bc933c446fe89f39dbe4e9c" ], "index": "pypi", - "version": "==1.7.7" + "version": "==1.6.5" }, "async-timeout": { "hashes": [ @@ -143,11 +143,11 @@ }, "click": { "hashes": [ - "sha256:24e1a4a9ec5bf6299411369b208c1df2188d9eb8d916302fe6bf03faed227f1e", - "sha256:479707fe14d9ec9a0757618b7a100a0ae4c4e236fac5b7f80ca68028141a1a72" + "sha256:6a7a62563bbfabfda3a38f3023a1db4a35978c0abd76f6c9605ecd6554d6d9b1", + "sha256:8458d7b1287c5fb128c90e23381cf99dcde74beaf6c7ff6384ce84d6fe090adb" ], - "markers": "python_version >= '3.7'", - "version": "==8.1.2" + "markers": "python_version >= '3.6'", + "version": "==8.0.4" }, "colorlog": { "hashes": [ @@ -159,11 +159,11 @@ }, "flask": { "hashes": [ - "sha256:8a4cf32d904cf5621db9f0c9fbcd7efabf3003f22a04e4d0ce790c7137ec5264", - "sha256:a8c9bd3e558ec99646d177a9739c41df1ded0629480b4c8d2975412f3c9519c8" + "sha256:59da8a3170004800a2837844bfa84d49b022550616070f7cb1a659682b2e7c9f", + "sha256:e1120c228ca2f553b470df4a5fa927ab66258467526069981b3eb0a91902687d" ], "index": "pypi", - "version": "==2.1.1" + "version": "==2.0.3" }, "flask-cors": { "hashes": [ @@ -248,63 +248,58 @@ }, "greenlet": { "hashes": [ - "sha256:004aed447382d80a56ecc354a6d807f305e6c808714ce6ccbca4839c94fae81d", - "sha256:068d68fad6bd623e29a2d36e74538c9b9d6dc6464931cd27d93da6cfc6a7f242", - "sha256:06fd4075754009c9817c6b4e1dc0af4616de52757b6ca973a81c3c1aadc28257", - "sha256:1004cb542451814b12a4f38e835a47734e2b2c683acbf463d5ae76282a3974cf", - "sha256:10c358633a8b27bfc32d27114ef2ca2ddc9f1f89f1643d1157b85e1fdd695315", - "sha256:115bc25fefbdc692c4483e9ddb9011ccd0251590ed59dbfff0f4eb7050bf99c4", - "sha256:1d987a2579336792f73ae6b106c2f087e32afc8573fbf9566f123ac6d8cfb72f", - "sha256:2128d727fd1e8afba8e68feb2cdcf88c90163b69ddc9707722a3e491c5280720", - "sha256:230132c241fe284f93f2e7b3969e9b22bbd76ef98cf93e382c945d378907f5a4", - "sha256:23558f7bd08a663386c032ab8d302d613d2d02ae0c9758ad410bab6035b58d3d", - "sha256:255d520d3e4a5f16883b182e1a94219fe455ab4f50aaaf534bfd6d64ee728397", - "sha256:2a6bc19a728f6f643cfc89b876159a1a25a8f7d8700c013d48a73691f80b4550", - "sha256:379bed346ef8ba0a0e698b3c5975a44d15dd4a5bbff40bbd7fd548b445d5550b", - "sha256:3b12d0866759db93b0a893b4e50a7d7d1681519d2346c26695bb8bb2c652230e", - "sha256:40d491944f69e350e1e8b25f6ca49459824ede1678ec0cd4b5541f41edc06614", - "sha256:471484c7b9d7b7867263051aa81cdeed6e06b455e629a7f05eb91a6cb8bd0836", - "sha256:488c557080557bc01aabb3e1bda7225c68455b853733a8652857ac0d810dad1b", - "sha256:49c2e76e7aa81ba889b3c183e2341af3cc6161ee38852085110ae49d5b5d9a40", - "sha256:52d13ec90236e5935ed6da044e78faa1371d5116cc43fe6d7ca8994dd619ef96", - "sha256:57898c69a253d81f487787bdd538629fabd671fab8a9e31b041ca30965fd9556", - "sha256:5d577eef5beb5730ef01ab39983eb852a97c359b7a546809adf70c409f4b2ecc", - "sha256:6a41987c1474c9158a0c0c96611530a8f299bc547d35bee8add981b8b2534f74", - "sha256:6ae67b7df8db3626af8e042e9c6949cfa27d1a3bbbfdff29e45b72bb6673a650", - "sha256:6c42c27e9d12e8a481aff469ffe8dd4ce0484c354a418470960f760f6ae41e7c", - "sha256:6c4a90c9f6128b4d0905a89930bd325e0491574e5cb453f606bb7094a3197587", - "sha256:6e64518e5833ac2d9359b6d9bd4df2c0cf441a0f3a4eca9e735fbea99009fa70", - "sha256:6fd3a270c23c5b42d86a9c7c6b0229f23ee4a7a4cabdaaa1693ad7a0982d13cb", - "sha256:70db73351e0fcf11a76288c47a0469d9a330bcb2e7618c5eb57432b8caa82403", - "sha256:771f401692046845626cbdf1dd0f04e999413ede0ee9ad39033fe30b5fa2e845", - "sha256:7935026ec61b967cbc6b746c0ca75c1651ea118d7fee4d259cff9e6866153374", - "sha256:7b76b1cac9baac1980210e29145800954e7b42e91ef69c4d695de1cab87ce41f", - "sha256:7e3f37c11b6699b1a1e0fcc0e88829dba4f2866546381b05ab8b3f4db645a823", - "sha256:8370fa65ad421484894f559055f951843754153b72b9bca2ebdc5288efe2e3f0", - "sha256:8ae9c443d44a4e23252632e4d7775f419f992d0df3eff923e23775f5cc551d39", - "sha256:8b31d85f2781e44f1ffaaf7ea07f484e7d42317c677c355fa77b4a1a4bea7394", - "sha256:8b450336b27f3b375cadc474c6704838eaa8dd3ca312aac3bb69d92264a8e638", - "sha256:9ce84357388a76d886febff4e50e321c212ffd3248b590960b2da6e02404a5c9", - "sha256:a23e986fb0ba8e7407286add41fa0d4207be44e3dce1b04789f4757800eca1cf", - "sha256:a81610ee00d0da9cd2c8679479b7791149365b6dfb3971b01b22ee29b04787ce", - "sha256:b4e40444975e5ab0ed3004369209c39a28e084951daaeee4919f164b6b849b14", - "sha256:b66600de16702b9dfa74bea34524b55183a2183e5fd92f20fe6c2fcae550a64c", - "sha256:ba6ee18694d3673796b7a31b7d21254e87e9e43ca5be56f323fd396111255315", - "sha256:bd03837da28293baa39bdfc3cada69e2f8807f423ae06168aa28d2b32c63a6b6", - "sha256:bd2192070f88c0778ae1d68a0980fdece3473498c1db37f3794e3454f91e3ecf", - "sha256:c1f6f1a3cc013012cd1da913c40b13e6d721046a8c8a0ea0cde94069645a75db", - "sha256:ce10a8e7e067bde3c1fbf494d2b8859db510206030b0b67bc3af90b0eb1887b9", - "sha256:d31386d208303a5a6cf0819ef9f6db6680bab9e4ca8e48adb3d4b26ead89beb7", - "sha256:d83b3af53b201970973c5574b39df226746194063bb248a53fd12b470ac34319", - "sha256:df9657b212c054ac6d803290d7c4bcd7790af0b725984fce1eeb0a1e3f2d9798", - "sha256:e576e5fd3f129e6b3595dc734ac7f2b8c548f19ef07781194bc538dc9c0cdbbc", - "sha256:e7400358558094c1bcedc75f3b3c4f400c53130b44833848890a99968dee6a64", - "sha256:eb6a385f8577d30e4cb43dd555fb134ddaae1edeb84205e09dabec332bf49fd0", - "sha256:f27f0875e0873f6bf5df09a456bfcac0667824cabac4cad30b43f36e0382ffe7", - "sha256:fcd4a6d04995f1d66bc78b503e4e59ae72fd32aaec4f661657fe5ae5c1aa4ce3" + "sha256:03e6e40a1c6d523e59e4b80173986dfb4bdefbbd14104a6f1a9d321bb4dc0226", + "sha256:045a6cdd8d7ba5fffb82b9d4d35ae99bbc57afdd03a8b8eb161ec6fd0d2fc15b", + "sha256:04828fcd02de1fa606560ac13eaa026a3f1859bbb6098cbb4e2c9471bce73e17", + "sha256:063c55ae93b19dcbd077182d34ab7e70838d16edae8a5ba9fe1c8f9a6530201d", + "sha256:08f03790cd6105a5b0f452d798a22bd72944bda41dc674d39dc8e485b730056e", + "sha256:22b2111811abbd2af884426b2286b41ad3dbec15dcd362f68fc319abdf2d6f36", + "sha256:44440890e79d8bded5893fa4c322c3d8bf18fdf87657fd6523252ec247be6f4b", + "sha256:4c74283a777414ea1382448f70340096b360e3dc4bedc64259b0e355328f2f5a", + "sha256:4c79da840373815c8ebbde0e64aa0b74fbfe74394665dcf318cfd7220ca9ed8b", + "sha256:4cedd60664090cc7407119fd40b91c9a2a640909c8c59273b180ba61b1ff9210", + "sha256:53f90311f1779fac5641a75c01ba3b9d088688518915b7138b2f0636f151c20a", + "sha256:5667b436e4a365f9bc9bb98c0d5356c1929a62a51d78373c92870a138ada273a", + "sha256:5aa13f7f2650f39653c096cead3346e0a69cc17ad29a91a3a6776801f124b963", + "sha256:5f9a1fdd3339cd2fcaa2e768fb297429601888ef28ab9509a34cb3dd4497c88f", + "sha256:61c2fbbf1cebb7cbaa35690143b5dc3212d764c7975957cc5aeacfd5686ad534", + "sha256:63e0620aec7dc22fd988ec2c62c7e678921d13cf8eaefc2d2df6cc065998cc7b", + "sha256:676d9c5f0428da9f69621ff71091b4c02d952a2f7e71a47891cde93b9114f048", + "sha256:678065b2bc9a2fb804a61cfddac6b44cbddb1787e619e47eae3cb25c57709516", + "sha256:68ebc87166fc0a13d9fb0b7f231790369ea03ca0b7efa6d100ea2f701dfb6a6a", + "sha256:69a681b219c1208f0dd40c29c142e44a436f1f7add8ae9ac93470c1764b38980", + "sha256:6bcfa9dd47645ea7ab072422405858ab8afb8420014bd2dfde0fed78d4026e08", + "sha256:757ddbadc18515d28018c53f98bf85ea7af9fb7accff0d7b5d681bcbfdca9a74", + "sha256:7bf83a6b7f068e631cfae0a0fc9f7ca73bc8115c0d6240131b1d5c5d7a65bbc2", + "sha256:800c0e9f13df16c36df8b040bb1693cea4e8375b8ddc730013c87ed656d3659c", + "sha256:80e8f41031b26856b8e4a0f65a395bb57cb3fcc22a810f87994a266f63f22fd0", + "sha256:81e8c96d0f590c11ffcbed42293dc3e10be1639a57b0e531a937ac61fb400224", + "sha256:85a8b1bfccf88326cf36a43a6cd7990afa22f0a02da624ca18ac7eb16bc14edd", + "sha256:9ad8a0b3542747b4311f03784a87ba2f1c2a0bf15e7e95ecf06d97ccea2f81e9", + "sha256:9ae7b519fbacaa5bf7e9ba71d582da463ce049e493568be6da80f444504bc951", + "sha256:9d531a58f3feb283f120bacc817e4d75289bde8263363fbfeedf96376a1c68a8", + "sha256:9db3f35c9c493dbbf053fb13285822bccbf2a76049328d10edf0daf4591b65a0", + "sha256:9dcc10e86164853c5267bfac43816048a12dcf4c898c1b83a2eb8d5933da9ee0", + "sha256:a172161ec09ef67f8b88fce873f3b6b37ca99b4ddd2536147dc611d4d645bc28", + "sha256:a34b6d63ebaab722f79df4f79cbceab6fac2f96546561848a8ce1513a4478e16", + "sha256:a74c507dbef45ba85f8565a1bc297f97342a246078af5ea05330992d5a26e72c", + "sha256:ab6385c0a16c1d33ab45593f0cad49834918ad83d0406b58cd62f1454a1778c6", + "sha256:ab951ebc9f4c63d9ba0518d533f358519e3633c3a263bfba8674ee0c5043bd1a", + "sha256:b3ff97e9761e3c392eaba4b7f17da9deb9e1177b372313611ee2358a19f235bb", + "sha256:b68ff6925dd210a4eac47df411316168a2448178f837fcb01597d9a183ad8603", + "sha256:b904cbb4b69fa959674043d39aa34ec84abca917037ec931d72003b60b311174", + "sha256:be681332594c3361a32198fe591f966e6114c67bf62e1cbb1f6fe700e7f809b3", + "sha256:c006d07a64777466b4ecf75a3fc86f7c687021e9b1fae0a0c3157ae93ce44563", + "sha256:c9525a82b0ff1bb35253ec2d2e9430c53479274e65bddefbfd5db74972c7202d", + "sha256:cb1f258971419b4b34f71736b09ce7d3daeea71a5660eb28b34f8a80520ee20b", + "sha256:ce1f6e65a3b9b6a8b5b1f64ef75e49fa159721c8882027b50cfaa82472bd1c10", + "sha256:ce2ec312bcb516a83780b659fd008fdd26af449b653c8ffa52e203a397765cc2", + "sha256:da809e3861a8f697727b6bdf4c735184912215d2eb9df15eb7ad3d6c9d533a52", + "sha256:edacb7c0f8a42e6df031ef675e29156ca933403f1cb8027e8f2c82ad8d68bc67", + "sha256:fa04d0419b2ed61125bb8a4a0a810cad428546adeee87cab6f2beee50259fecc" ], "markers": "python_version >= '3' and platform_machine == 'aarch64' or (platform_machine == 'ppc64le' or (platform_machine == 'x86_64' or (platform_machine == 'amd64' or (platform_machine == 'AMD64' or (platform_machine == 'win32' or platform_machine == 'WIN32')))))", - "version": "==2.0.0a2" + "version": "==2.0.0a1" }, "gunicorn": { "hashes": [ @@ -322,91 +317,75 @@ "markers": "python_version >= '3.5'", "version": "==3.3" }, - "importlib-metadata": { - "hashes": [ - "sha256:1208431ca90a8cca1a6b8af391bb53c1a2db74e5d1cef6ddced95d4b2062edc6", - "sha256:ea4c597ebf37142f827b8f39299579e31685c31d3a438b59f469406afd0f2539" - ], - "markers": "python_version < '3.9'", - "version": "==4.11.3" - }, - "importlib-resources": { - "hashes": [ - "sha256:1b93238cbf23b4cde34240dd8321d99e9bf2eb4bc91c0c99b2886283e7baad85", - "sha256:a9dd72f6cc106aeb50f6e66b86b69b454766dd6e39b69ac68450253058706bcc" - ], - "markers": "python_version < '3.9'", - "version": "==5.6.0" - }, "itsdangerous": { "hashes": [ - "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44", - "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a" + "sha256:29285842166554469a56d427addc0843914172343784cb909695fdbe90a3e129", + "sha256:d848fcb8bc7d507c4546b448574e8a44fc4ea2ba84ebf8d783290d53e81992f5" ], "markers": "python_version >= '3.7'", - "version": "==2.1.2" + "version": "==2.1.0" }, "jinja2": { "hashes": [ - "sha256:539835f51a74a69f41b848a9645dbdc35b4f20a3b601e2d9a7e22947b15ff119", - "sha256:640bed4bb501cbd17194b3cace1dc2126f5b619cf068a726b98192a0fde74ae9" + "sha256:077ce6014f7b40d03b47d1f1ca4b0fc8328a692bd284016f806ed0eaca390ad8", + "sha256:611bb273cd68f3b993fabdc4064fc858c5b47a973cb5aa7999ec1ba405c87cd7" ], - "markers": "python_version >= '3.7'", - "version": "==3.1.1" + "markers": "python_version >= '3.6'", + "version": "==3.0.3" }, "mako": { "hashes": [ - "sha256:23aab11fdbbb0f1051b93793a58323ff937e98e34aece1c4219675122e57e4ba", - "sha256:9a7c7e922b87db3686210cf49d5d767033a41d4010b284e747682c92bddd8b39" + "sha256:4e9e345a41924a954251b95b4b28e14a301145b544901332e658907a7464b6b2", + "sha256:afaf8e515d075b22fad7d7b8b30e4a1c90624ff2f3733a06ec125f5a5f043a57" ], - "markers": "python_version >= '3.7'", - "version": "==1.2.0" + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==1.1.6" }, "markupsafe": { "hashes": [ - "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003", - "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88", - "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5", - "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7", - "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a", - "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603", - "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1", - "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135", - "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247", - "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6", - "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601", - "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77", - "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02", - "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e", - "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63", - "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f", - "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980", - "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b", - "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812", - "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff", - "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96", - "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1", - "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925", - "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a", - "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6", - "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e", - "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f", - "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4", - "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f", - "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3", - "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c", - "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a", - "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417", - "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a", - "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a", - "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37", - "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452", - "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933", - "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a", - "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7" + "sha256:023af8c54fe63530545f70dd2a2a7eed18d07a9a77b94e8bf1e2ff7f252db9a3", + "sha256:09c86c9643cceb1d87ca08cdc30160d1b7ab49a8a21564868921959bd16441b8", + "sha256:142119fb14a1ef6d758912b25c4e803c3ff66920635c44078666fe7cc3f8f759", + "sha256:1d1fb9b2eec3c9714dd936860850300b51dbaa37404209c8d4cb66547884b7ed", + "sha256:204730fd5fe2fe3b1e9ccadb2bd18ba8712b111dcabce185af0b3b5285a7c989", + "sha256:24c3be29abb6b34052fd26fc7a8e0a49b1ee9d282e3665e8ad09a0a68faee5b3", + "sha256:290b02bab3c9e216da57c1d11d2ba73a9f73a614bbdcc027d299a60cdfabb11a", + "sha256:3028252424c72b2602a323f70fbf50aa80a5d3aa616ea6add4ba21ae9cc9da4c", + "sha256:30c653fde75a6e5eb814d2a0a89378f83d1d3f502ab710904ee585c38888816c", + "sha256:3cace1837bc84e63b3fd2dfce37f08f8c18aeb81ef5cf6bb9b51f625cb4e6cd8", + "sha256:4056f752015dfa9828dce3140dbadd543b555afb3252507348c493def166d454", + "sha256:454ffc1cbb75227d15667c09f164a0099159da0c1f3d2636aa648f12675491ad", + "sha256:598b65d74615c021423bd45c2bc5e9b59539c875a9bdb7e5f2a6b92dfcfc268d", + "sha256:599941da468f2cf22bf90a84f6e2a65524e87be2fce844f96f2dd9a6c9d1e635", + "sha256:5ddea4c352a488b5e1069069f2f501006b1a4362cb906bee9a193ef1245a7a61", + "sha256:62c0285e91414f5c8f621a17b69fc0088394ccdaa961ef469e833dbff64bd5ea", + "sha256:679cbb78914ab212c49c67ba2c7396dc599a8479de51b9a87b174700abd9ea49", + "sha256:6e104c0c2b4cd765b4e83909cde7ec61a1e313f8a75775897db321450e928cce", + "sha256:736895a020e31b428b3382a7887bfea96102c529530299f426bf2e636aacec9e", + "sha256:75bb36f134883fdbe13d8e63b8675f5f12b80bb6627f7714c7d6c5becf22719f", + "sha256:7d2f5d97fcbd004c03df8d8fe2b973fe2b14e7bfeb2cfa012eaa8759ce9a762f", + "sha256:80beaf63ddfbc64a0452b841d8036ca0611e049650e20afcb882f5d3c266d65f", + "sha256:84ad5e29bf8bab3ad70fd707d3c05524862bddc54dc040982b0dbcff36481de7", + "sha256:8da5924cb1f9064589767b0f3fc39d03e3d0fb5aa29e0cb21d43106519bd624a", + "sha256:961eb86e5be7d0973789f30ebcf6caab60b844203f4396ece27310295a6082c7", + "sha256:96de1932237abe0a13ba68b63e94113678c379dca45afa040a17b6e1ad7ed076", + "sha256:a0a0abef2ca47b33fb615b491ce31b055ef2430de52c5b3fb19a4042dbc5cadb", + "sha256:b2a5a856019d2833c56a3dcac1b80fe795c95f401818ea963594b345929dffa7", + "sha256:b8811d48078d1cf2a6863dafb896e68406c5f513048451cd2ded0473133473c7", + "sha256:c532d5ab79be0199fa2658e24a02fce8542df196e60665dd322409a03db6a52c", + "sha256:d3b64c65328cb4cd252c94f83e66e3d7acf8891e60ebf588d7b493a55a1dbf26", + "sha256:d4e702eea4a2903441f2735799d217f4ac1b55f7d8ad96ab7d4e25417cb0827c", + "sha256:d5653619b3eb5cbd35bfba3c12d575db2a74d15e0e1c08bf1db788069d410ce8", + "sha256:d66624f04de4af8bbf1c7f21cc06649c1c69a7f84109179add573ce35e46d448", + "sha256:e67ec74fada3841b8c5f4c4f197bea916025cb9aa3fe5abf7d52b655d042f956", + "sha256:e6f7f3f41faffaea6596da86ecc2389672fa949bd035251eab26dc6697451d05", + "sha256:f02cf7221d5cd915d7fa58ab64f7ee6dd0f6cddbb48683debf5d04ae9b1c2cc1", + "sha256:f0eddfcabd6936558ec020130f932d479930581171368fd728efcfb6ef0dd357", + "sha256:fabbe18087c3d33c5824cb145ffca52eccd053061df1d79d4b66dafa5ad2a5ea", + "sha256:fc3150f85e2dbcf99e65238c842d1cfe69d3e7649b19864c1cc043213d9cd730" ], "markers": "python_version >= '3.7'", - "version": "==2.1.1" + "version": "==2.1.0" }, "multidict": { "hashes": [ @@ -490,21 +469,39 @@ "index": "pypi", "version": "==2.9.3" }, + "python-dateutil": { + "hashes": [ + "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", + "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==2.8.2" + }, "python-dotenv": { "hashes": [ - "sha256:b7e3b04a59693c42c36f9ab1cc2acc46fa5df8c78e178fc33a8d4cd05c8d498f", - "sha256:d92a187be61fe482e4fd675b6d52200e7be63a12b724abbf931a40ce4fa92938" + "sha256:32b2bdc1873fd3a3c346da1c6db83d0053c3c62f28f1f38516070c4c8971b1d3", + "sha256:a5de49a31e953b45ff2d2fd434bbc2670e8db5273606c1e737cc6b93eff3655f" ], "index": "pypi", - "version": "==0.20.0" + "version": "==0.19.2" + }, + "python-editor": { + "hashes": [ + "sha256:1bf6e860a8ad52a14c3ee1252d5dc25b2030618ed80c022598f00176adc8367d", + "sha256:51fda6bcc5ddbbb7063b2af7509e43bd84bfc32a4ff71349ec7847713882327b", + "sha256:5f98b069316ea1c2ed3f67e7f5df6c0d8f10b689964a4a811ff64f0106819ec8", + "sha256:c3da2053dbab6b29c94e43c486ff67206eafbe7eb52dbec7390b5e2fb05aac77", + "sha256:ea87e17f6ec459e780e4221f295411462e0d0810858e055fc514684350a2f522" + ], + "version": "==1.0.4" }, "setuptools": { "hashes": [ - "sha256:7999cbd87f1b6e1f33bf47efa368b224bed5e27b5ef2c4d46580186cbcb1a86a", - "sha256:a65e3802053e99fc64c6b3b29c11132943d5b8c8facbcc461157511546510967" + "sha256:2347b2b432c891a863acadca2da9ac101eae6169b1d3dfee2ec605ecd50dbfe5", + "sha256:e4f30b9f84e5ab3decf945113119649fec09c1fc3507c6ebffec75646c56e62b" ], "markers": "python_version >= '3.7'", - "version": "==62.0.0" + "version": "==60.9.3" }, "six": { "hashes": [ @@ -524,53 +521,53 @@ }, "sqlalchemy": { "hashes": [ - "sha256:045d6a26c262929af0b9cb25441aae675ac04db4ea8bd2446b355617cd6b6b7d", - "sha256:07f4dab2deb6d34618a2ccfff3971a85923ad7c3a9a45401818870fc51d3f0cc", - "sha256:08aaad905aba8940f27aeb9f1f851bf63f18ef97b0062ca41f64afc4b64e0e8c", - "sha256:27a42894a2751e438eaed12fc0dcfe741ff2f66c14760d081222c5adc5460064", - "sha256:2a3e4dc7c452ba3c0f3175ad5a8e0ba49c2b0570a8d07272cf50844c8d78e74f", - "sha256:345306707bb0e51e7cd6e7573adafbce018894ee5e3b9c31134545f704936db0", - "sha256:36f08d94670315ca04c8139bd80b3e02b9dd9cc66fc11bcb96fd10ad51a051ab", - "sha256:3ebb97ed96f4506e2f212e1fcf0ec07a103bb194938627660a5acb4d9feae49c", - "sha256:40b995d7aeeb6f88a1927ce6692c0f626b59d8effd3e1d597f125e141707b37c", - "sha256:4414ace6e3a5e39523e55a5d9f3b215699b2ead4ff91fca98f1b659b7ab2d92a", - "sha256:50107d8183da3fbe5715957aa3954cd9d82aed555c5b4d3fd37fac861af422fa", - "sha256:50174e173d03209c34e07e7b57cca48d0082ac2390edf927aafc706c111da11e", - "sha256:5e88912bf192e7b5739c446d2276e1cba74cfa6c1c93eea2b2534404f6be1dbd", - "sha256:621d3f6c0ba2407bb97e82b649be5ca7d5b6c201dcfb964ce13f517bf1cb6305", - "sha256:623bac2d6bdca3f3e61cf1e1c466c5fb9f5cf08735736ee1111187b7a4108891", - "sha256:671f61c3db4595b0e86cc4b30f675a7c0206d9ce99f041b4f6761c7ddd1e0074", - "sha256:67c1c27c48875afc950bee5ee24582794f20b545e64e4f9ca94071a9b514d6ed", - "sha256:6a6cfd468f54d65324fd3847cfd0148b0610efa6a43e5f5fcc89f455696ae9e7", - "sha256:70048a83f0a1ece1fcd7189891c888e20af2c57fbd33eb760d8cece9843b896c", - "sha256:7ee14a7f9f76d1ef9d5e5b760c9252617c839b87eee04d1ce8325ac66ae155c4", - "sha256:804cf491437f3e4ce31247ab4b309b181f06ecc97d309b746d10f09439b4eb85", - "sha256:878c7beaafa365602762c19f638282e1885454fed1aed86f8fae038933c7c671", - "sha256:954ea8c527c4322afb6885944904714893af81fe9167e421273770991bf08a4a", - "sha256:a47bf6b7ca6c28e4f4e262fabcf5be6b907af81be36de77839c9eeda2cdf3bb3", - "sha256:a4fb5c6ee84a6bba4ff6f9f5379f0b3a0ffe9de7ba5a0945659b3da8d519709b", - "sha256:b34bbc683789559f1bc9bb685fc162e0956dbbdfbe2fbd6755a9f5982c113610", - "sha256:c025d45318b73c0601cca451532556cbab532b2742839ebb8cb58f9ebf06811e", - "sha256:c3ad7f5b61ba014f5045912aea15b03c473bb02b1c07fd92c9d2c794fa183276", - "sha256:c9218e3519398129e364121e0d89823e6ba2a2b77c28bfc661face0829c41433", - "sha256:cd5cffd1dd753828f1069f33062f3896e51c990acd957c264f40e051b3e19887", - "sha256:d8efcaa709ea8e7c08c3d3e7639c39b36083f5a995f397f9e6eedf5f5e4e4946", - "sha256:e297a5cc625e3f1367a82deedf2d48ee4d2b2bd263b8b8d2efbaaf5608b5229e", - "sha256:e67278ceb63270cdac0a7b89fc3c29a56f7dac9616a7ee48e7ad6b52e3b631e5", - "sha256:eb6558ba07409dafa18c793c34292b3265be455904966f0724c10198829477e3", - "sha256:f197c66663ed0f9e1178d51141d864688fb244a83f6b17f667d521e482537b2e", - "sha256:f47996b1810894f766c9ee689607077c6c0e0fd6761e04c12ba13efb56d50c1d" + "sha256:05fa14f279d43df68964ad066f653193187909950aa0163320b728edfc400167", + "sha256:0ddc5e5ccc0160e7ad190e5c61eb57560f38559e22586955f205e537cda26034", + "sha256:15a03261aa1e68f208e71ae3cd845b00063d242cbf8c87348a0c2c0fc6e1f2ac", + "sha256:289465162b1fa1e7a982f8abe59d26a8331211cad4942e8031d2b7db1f75e649", + "sha256:2e216c13ecc7fcdcbb86bb3225425b3ed338e43a8810c7089ddb472676124b9b", + "sha256:2fd4d3ca64c41dae31228b80556ab55b6489275fb204827f6560b65f95692cf3", + "sha256:330eb45395874cc7787214fdd4489e2afb931bc49e0a7a8f9cd56d6e9c5b1639", + "sha256:3c7ed6c69debaf6198fadb1c16ae1253a29a7670bbf0646f92582eb465a0b999", + "sha256:4ad31cec8b49fd718470328ad9711f4dc703507d434fd45461096da0a7135ee0", + "sha256:57205844f246bab9b666a32f59b046add8995c665d9ecb2b7b837b087df90639", + "sha256:582b59d1e5780a447aada22b461e50b404a9dc05768da1d87368ad8190468418", + "sha256:5e9c7b3567edbc2183607f7d9f3e7e89355b8f8984eec4d2cd1e1513c8f7b43f", + "sha256:6a01ec49ca54ce03bc14e10de55dfc64187a2194b3b0e5ac0fdbe9b24767e79e", + "sha256:6f22c040d196f841168b1456e77c30a18a3dc16b336ddbc5a24ce01ab4e95ae0", + "sha256:81f2dd355b57770fdf292b54f3e0a9823ec27a543f947fa2eb4ec0df44f35f0d", + "sha256:85e4c244e1de056d48dae466e9baf9437980c19fcde493e0db1a0a986e6d75b4", + "sha256:8d0949b11681380b4a50ac3cd075e4816afe9fa4a8c8ae006c1ca26f0fa40ad8", + "sha256:975f5c0793892c634c4920057da0de3a48bbbbd0a5c86f5fcf2f2fedf41b76da", + "sha256:9e4fb2895b83993831ba2401b6404de953fdbfa9d7d4fa6a4756294a83bbc94f", + "sha256:b35dca159c1c9fa8a5f9005e42133eed82705bf8e243da371a5e5826440e65ca", + "sha256:b7b20c88873675903d6438d8b33fba027997193e274b9367421e610d9da76c08", + "sha256:bb4b15fb1f0aafa65cbdc62d3c2078bea1ceecbfccc9a1f23a2113c9ac1191fa", + "sha256:c0c7171aa5a57e522a04a31b84798b6c926234cb559c0939840c3235cf068813", + "sha256:c317ddd7c586af350a6aef22b891e84b16bff1a27886ed5b30f15c1ed59caeaa", + "sha256:c3abc34fed19fdeaead0ced8cf56dd121f08198008c033596aa6aae7cc58f59f", + "sha256:ca68c52e3cae491ace2bf39b35fef4ce26c192fd70b4cd90f040d419f70893b5", + "sha256:cf2cd387409b12d0a8b801610d6336ee7d24043b6dd965950eaec09b73e7262f", + "sha256:d046a9aeba9bc53e88a41e58beb72b6205abb9a20f6c136161adf9128e589db5", + "sha256:d5c20c8415173b119762b6110af64448adccd4d11f273fb9f718a9865b88a99c", + "sha256:d86132922531f0dc5a4f424c7580a472a924dd737602638e704841c9cb24aea2", + "sha256:dccff41478050e823271642837b904d5f9bda3f5cf7d371ce163f00a694118d6", + "sha256:de85c26a5a1c72e695ab0454e92f60213b4459b8d7c502e0be7a6369690eeb1a", + "sha256:e3a86b59b6227ef72ffc10d4b23f0fe994bef64d4667eab4fb8cd43de4223bec", + "sha256:e79e73d5ee24196d3057340e356e6254af4d10e1fc22d3207ea8342fc5ffb977", + "sha256:ea8210090a816d48a4291a47462bac750e3bc5c2442e6d64f7b8137a7c3f9ac5", + "sha256:f3b7ec97e68b68cb1f9ddb82eda17b418f19a034fa8380a0ac04e8fe01532875" ], "index": "pypi", - "version": "==1.4.34" + "version": "==1.4.31" }, "werkzeug": { "hashes": [ - "sha256:3c5493ece8268fecdcdc9c0b112211acd006354723b280d643ec732b6d4063d6", - "sha256:f8e89a20aeabbe8a893c24a461d3ee5dad2123b05cc6abd73ceed01d39c3ae74" + "sha256:1421ebfc7648a39a5c58c601b154165d05cf47a3cd0ccb70857cbdacf6c8f2b8", + "sha256:b863f8ff057c522164b6067c9e28b041161b4be5ba4d0daceeaa50a163822d3c" ], - "markers": "python_version >= '3.7'", - "version": "==2.1.1" + "markers": "python_version >= '3.6'", + "version": "==2.0.3" }, "yarl": { "hashes": [ @@ -649,14 +646,6 @@ ], "markers": "python_version >= '3.6'", "version": "==1.7.2" - }, - "zipp": { - "hashes": [ - "sha256:56bf8aadb83c24db6c4b577e13de374ccfb67da2078beba1d037c17980bf43ad", - "sha256:c4f6e5bbf48e74f7a38e7cc5b0480ff42b0ae5178957d564d18932525d5cf099" - ], - "markers": "python_version >= '3.7'", - "version": "==3.8.0" } }, "develop": { @@ -670,90 +659,96 @@ }, "black": { "hashes": [ - "sha256:06f9d8846f2340dfac80ceb20200ea5d1b3f181dd0556b47af4e8e0b24fa0a6b", - "sha256:10dbe6e6d2988049b4655b2b739f98785a884d4d6b85bc35133a8fb9a2233176", - "sha256:2497f9c2386572e28921fa8bec7be3e51de6801f7459dffd6e62492531c47e09", - "sha256:30d78ba6bf080eeaf0b7b875d924b15cd46fec5fd044ddfbad38c8ea9171043a", - "sha256:328efc0cc70ccb23429d6be184a15ce613f676bdfc85e5fe8ea2a9354b4e9015", - "sha256:35020b8886c022ced9282b51b5a875b6d1ab0c387b31a065b84db7c33085ca79", - "sha256:5795a0375eb87bfe902e80e0c8cfaedf8af4d49694d69161e5bd3206c18618bb", - "sha256:5891ef8abc06576985de8fa88e95ab70641de6c1fca97e2a15820a9b69e51b20", - "sha256:637a4014c63fbf42a692d22b55d8ad6968a946b4a6ebc385c5505d9625b6a464", - "sha256:67c8301ec94e3bcc8906740fe071391bce40a862b7be0b86fb5382beefecd968", - "sha256:6d2fc92002d44746d3e7db7cf9313cf4452f43e9ea77a2c939defce3b10b5c82", - "sha256:6ee227b696ca60dd1c507be80a6bc849a5a6ab57ac7352aad1ffec9e8b805f21", - "sha256:863714200ada56cbc366dc9ae5291ceb936573155f8bf8e9de92aef51f3ad0f0", - "sha256:9b542ced1ec0ceeff5b37d69838106a6348e60db7b8fdd245294dc1d26136265", - "sha256:a6342964b43a99dbc72f72812bf88cad8f0217ae9acb47c0d4f141a6416d2d7b", - "sha256:ad4efa5fad66b903b4a5f96d91461d90b9507a812b3c5de657d544215bb7877a", - "sha256:bc58025940a896d7e5356952228b68f793cf5fcb342be703c3a2669a1488cb72", - "sha256:cc1e1de68c8e5444e8f94c3670bb48a2beef0e91dddfd4fcc29595ebd90bb9ce", - "sha256:cee3e11161dde1b2a33a904b850b0899e0424cc331b7295f2a9698e79f9a69a0", - "sha256:e3556168e2e5c49629f7b0f377070240bd5511e45e25a4497bb0073d9dda776a", - "sha256:e8477ec6bbfe0312c128e74644ac8a02ca06bcdb8982d4ee06f209be28cdf163", - "sha256:ee8f1f7228cce7dffc2b464f07ce769f478968bfb3dd1254a4c2eeed84928aad", - "sha256:fd57160949179ec517d32ac2ac898b5f20d68ed1a9c977346efbac9c2f1e779d" + "sha256:07e5c049442d7ca1a2fc273c79d1aecbbf1bc858f62e8184abe1ad175c4f7cc2", + "sha256:0e21e1f1efa65a50e3960edd068b6ae6d64ad6235bd8bfea116a03b21836af71", + "sha256:1297c63b9e1b96a3d0da2d85d11cd9bf8664251fd69ddac068b98dc4f34f73b6", + "sha256:228b5ae2c8e3d6227e4bde5920d2fc66cc3400fde7bcc74f480cb07ef0b570d5", + "sha256:2d6f331c02f0f40aa51a22e479c8209d37fcd520c77721c034517d44eecf5912", + "sha256:2ff96450d3ad9ea499fc4c60e425a1439c2120cbbc1ab959ff20f7c76ec7e866", + "sha256:3524739d76b6b3ed1132422bf9d82123cd1705086723bc3e235ca39fd21c667d", + "sha256:35944b7100af4a985abfcaa860b06af15590deb1f392f06c8683b4381e8eeaf0", + "sha256:373922fc66676133ddc3e754e4509196a8c392fec3f5ca4486673e685a421321", + "sha256:5fa1db02410b1924b6749c245ab38d30621564e658297484952f3d8a39fce7e8", + "sha256:6f2f01381f91c1efb1451998bd65a129b3ed6f64f79663a55fe0e9b74a5f81fd", + "sha256:742ce9af3086e5bd07e58c8feb09dbb2b047b7f566eb5f5bc63fd455814979f3", + "sha256:7835fee5238fc0a0baf6c9268fb816b5f5cd9b8793423a75e8cd663c48d073ba", + "sha256:8871fcb4b447206904932b54b567923e5be802b9b19b744fdff092bd2f3118d0", + "sha256:a7c0192d35635f6fc1174be575cb7915e92e5dd629ee79fdaf0dcfa41a80afb5", + "sha256:b1a5ed73ab4c482208d20434f700d514f66ffe2840f63a6252ecc43a9bc77e8a", + "sha256:c8226f50b8c34a14608b848dc23a46e5d08397d009446353dad45e04af0c8e28", + "sha256:ccad888050f5393f0d6029deea2a33e5ae371fd182a697313bdbd835d3edaf9c", + "sha256:dae63f2dbf82882fa3b2a3c49c32bffe144970a573cd68d247af6560fc493ae1", + "sha256:e2f69158a7d120fd641d1fa9a921d898e20d52e44a74a6fbbcc570a62a6bc8ab", + "sha256:efbadd9b52c060a8fc3b9658744091cb33c31f830b3f074422ed27bad2b18e8f", + "sha256:f5660feab44c2e3cb24b2419b998846cbb01c23c7fe645fee45087efa3da2d61", + "sha256:fdb8754b453fb15fad3f72cd9cad3e16776f0964d67cf30ebcbf10327a3777a3" ], "index": "pypi", - "version": "==22.3.0" + "version": "==22.1.0" }, "click": { "hashes": [ - "sha256:24e1a4a9ec5bf6299411369b208c1df2188d9eb8d916302fe6bf03faed227f1e", - "sha256:479707fe14d9ec9a0757618b7a100a0ae4c4e236fac5b7f80ca68028141a1a72" + "sha256:6a7a62563bbfabfda3a38f3023a1db4a35978c0abd76f6c9605ecd6554d6d9b1", + "sha256:8458d7b1287c5fb128c90e23381cf99dcde74beaf6c7ff6384ce84d6fe090adb" ], - "markers": "python_version >= '3.7'", - "version": "==8.1.2" + "markers": "python_version >= '3.6'", + "version": "==8.0.4" }, "coverage": { "extras": [ "toml" ], "hashes": [ - "sha256:03e2a7826086b91ef345ff18742ee9fc47a6839ccd517061ef8fa1976e652ce9", - "sha256:07e6db90cd9686c767dcc593dff16c8c09f9814f5e9c51034066cad3373b914d", - "sha256:18d520c6860515a771708937d2f78f63cc47ab3b80cb78e86573b0a760161faf", - "sha256:1ebf730d2381158ecf3dfd4453fbca0613e16eaa547b4170e2450c9707665ce7", - "sha256:21b7745788866028adeb1e0eca3bf1101109e2dc58456cb49d2d9b99a8c516e6", - "sha256:26e2deacd414fc2f97dd9f7676ee3eaecd299ca751412d89f40bc01557a6b1b4", - "sha256:2c6dbb42f3ad25760010c45191e9757e7dce981cbfb90e42feef301d71540059", - "sha256:2fea046bfb455510e05be95e879f0e768d45c10c11509e20e06d8fcaa31d9e39", - "sha256:34626a7eee2a3da12af0507780bb51eb52dca0e1751fd1471d0810539cefb536", - "sha256:37d1141ad6b2466a7b53a22e08fe76994c2d35a5b6b469590424a9953155afac", - "sha256:46191097ebc381fbf89bdce207a6c107ac4ec0890d8d20f3360345ff5976155c", - "sha256:4dd8bafa458b5c7d061540f1ee9f18025a68e2d8471b3e858a9dad47c8d41903", - "sha256:4e21876082ed887baed0146fe222f861b5815455ada3b33b890f4105d806128d", - "sha256:58303469e9a272b4abdb9e302a780072c0633cdcc0165db7eec0f9e32f901e05", - "sha256:5ca5aeb4344b30d0bec47481536b8ba1181d50dbe783b0e4ad03c95dc1296684", - "sha256:68353fe7cdf91f109fc7d474461b46e7f1f14e533e911a2a2cbb8b0fc8613cf1", - "sha256:6f89d05e028d274ce4fa1a86887b071ae1755082ef94a6740238cd7a8178804f", - "sha256:7a15dc0a14008f1da3d1ebd44bdda3e357dbabdf5a0b5034d38fcde0b5c234b7", - "sha256:8bdde1177f2311ee552f47ae6e5aa7750c0e3291ca6b75f71f7ffe1f1dab3dca", - "sha256:8ce257cac556cb03be4a248d92ed36904a59a4a5ff55a994e92214cde15c5bad", - "sha256:8cf5cfcb1521dc3255d845d9dca3ff204b3229401994ef8d1984b32746bb45ca", - "sha256:8fbbdc8d55990eac1b0919ca69eb5a988a802b854488c34b8f37f3e2025fa90d", - "sha256:9548f10d8be799551eb3a9c74bbf2b4934ddb330e08a73320123c07f95cc2d92", - "sha256:96f8a1cb43ca1422f36492bebe63312d396491a9165ed3b9231e778d43a7fca4", - "sha256:9b27d894748475fa858f9597c0ee1d4829f44683f3813633aaf94b19cb5453cf", - "sha256:9baff2a45ae1f17c8078452e9e5962e518eab705e50a0aa8083733ea7d45f3a6", - "sha256:a2a8b8bcc399edb4347a5ca8b9b87e7524c0967b335fbb08a83c8421489ddee1", - "sha256:acf53bc2cf7282ab9b8ba346746afe703474004d9e566ad164c91a7a59f188a4", - "sha256:b0be84e5a6209858a1d3e8d1806c46214e867ce1b0fd32e4ea03f4bd8b2e3359", - "sha256:b31651d018b23ec463e95cf10070d0b2c548aa950a03d0b559eaa11c7e5a6fa3", - "sha256:b78e5afb39941572209f71866aa0b206c12f0109835aa0d601e41552f9b3e620", - "sha256:c76aeef1b95aff3905fb2ae2d96e319caca5b76fa41d3470b19d4e4a3a313512", - "sha256:dd035edafefee4d573140a76fdc785dc38829fe5a455c4bb12bac8c20cfc3d69", - "sha256:dd6fe30bd519694b356cbfcaca9bd5c1737cddd20778c6a581ae20dc8c04def2", - "sha256:e5f4e1edcf57ce94e5475fe09e5afa3e3145081318e5fd1a43a6b4539a97e518", - "sha256:ec6bc7fe73a938933d4178c9b23c4e0568e43e220aef9472c4f6044bfc6dd0f0", - "sha256:f1555ea6d6da108e1999b2463ea1003fe03f29213e459145e70edbaf3e004aaa", - "sha256:f5fa5803f47e095d7ad8443d28b01d48c0359484fec1b9d8606d0e3282084bc4", - "sha256:f7331dbf301b7289013175087636bbaf5b2405e57259dd2c42fdcc9fcc47325e", - "sha256:f9987b0354b06d4df0f4d3e0ec1ae76d7ce7cbca9a2f98c25041eb79eec766f1", - "sha256:fd9e830e9d8d89b20ab1e5af09b32d33e1a08ef4c4e14411e559556fd788e6b2" + "sha256:01774a2c2c729619760320270e42cd9e797427ecfddd32c2a7b639cdc481f3c0", + "sha256:03b20e52b7d31be571c9c06b74746746d4eb82fc260e594dc662ed48145e9efd", + "sha256:0a7726f74ff63f41e95ed3a89fef002916c828bb5fcae83b505b49d81a066884", + "sha256:1219d760ccfafc03c0822ae2e06e3b1248a8e6d1a70928966bafc6838d3c9e48", + "sha256:13362889b2d46e8d9f97c421539c97c963e34031ab0cb89e8ca83a10cc71ac76", + "sha256:174cf9b4bef0db2e8244f82059a5a72bd47e1d40e71c68ab055425172b16b7d0", + "sha256:17e6c11038d4ed6e8af1407d9e89a2904d573be29d51515f14262d7f10ef0a64", + "sha256:215f8afcc02a24c2d9a10d3790b21054b58d71f4b3c6f055d4bb1b15cecce685", + "sha256:22e60a3ca5acba37d1d4a2ee66e051f5b0e1b9ac950b5b0cf4aa5366eda41d47", + "sha256:2641f803ee9f95b1f387f3e8f3bf28d83d9b69a39e9911e5bfee832bea75240d", + "sha256:276651978c94a8c5672ea60a2656e95a3cce2a3f31e9fb2d5ebd4c215d095840", + "sha256:3f7c17209eef285c86f819ff04a6d4cbee9b33ef05cbcaae4c0b4e8e06b3ec8f", + "sha256:3feac4084291642165c3a0d9eaebedf19ffa505016c4d3db15bfe235718d4971", + "sha256:49dbff64961bc9bdd2289a2bda6a3a5a331964ba5497f694e2cbd540d656dc1c", + "sha256:4e547122ca2d244f7c090fe3f4b5a5861255ff66b7ab6d98f44a0222aaf8671a", + "sha256:5829192582c0ec8ca4a2532407bc14c2f338d9878a10442f5d03804a95fac9de", + "sha256:5d6b09c972ce9200264c35a1d53d43ca55ef61836d9ec60f0d44273a31aa9f17", + "sha256:600617008aa82032ddeace2535626d1bc212dfff32b43989539deda63b3f36e4", + "sha256:619346d57c7126ae49ac95b11b0dc8e36c1dd49d148477461bb66c8cf13bb521", + "sha256:63c424e6f5b4ab1cf1e23a43b12f542b0ec2e54f99ec9f11b75382152981df57", + "sha256:6dbc1536e105adda7a6312c778f15aaabe583b0e9a0b0a324990334fd458c94b", + "sha256:6e1394d24d5938e561fbeaa0cd3d356207579c28bd1792f25a068743f2d5b282", + "sha256:86f2e78b1eff847609b1ca8050c9e1fa3bd44ce755b2ec30e70f2d3ba3844644", + "sha256:8bdfe9ff3a4ea37d17f172ac0dff1e1c383aec17a636b9b35906babc9f0f5475", + "sha256:8e2c35a4c1f269704e90888e56f794e2d9c0262fb0c1b1c8c4ee44d9b9e77b5d", + "sha256:92b8c845527eae547a2a6617d336adc56394050c3ed8a6918683646328fbb6da", + "sha256:9365ed5cce5d0cf2c10afc6add145c5037d3148585b8ae0e77cc1efdd6aa2953", + "sha256:9a29311bd6429be317c1f3fe4bc06c4c5ee45e2fa61b2a19d4d1d6111cb94af2", + "sha256:9a2b5b52be0a8626fcbffd7e689781bf8c2ac01613e77feda93d96184949a98e", + "sha256:a4bdeb0a52d1d04123b41d90a4390b096f3ef38eee35e11f0b22c2d031222c6c", + "sha256:a9c8c4283e17690ff1a7427123ffb428ad6a52ed720d550e299e8291e33184dc", + "sha256:b637c57fdb8be84e91fac60d9325a66a5981f8086c954ea2772efe28425eaf64", + "sha256:bf154ba7ee2fd613eb541c2bc03d3d9ac667080a737449d1a3fb342740eb1a74", + "sha256:c254b03032d5a06de049ce8bca8338a5185f07fb76600afff3c161e053d88617", + "sha256:c332d8f8d448ded473b97fefe4a0983265af21917d8b0cdcb8bb06b2afe632c3", + "sha256:c7912d1526299cb04c88288e148c6c87c0df600eca76efd99d84396cfe00ef1d", + "sha256:cfd9386c1d6f13b37e05a91a8583e802f8059bebfccde61a418c5808dea6bbfa", + "sha256:d5d2033d5db1d58ae2d62f095e1aefb6988af65b4b12cb8987af409587cc0739", + "sha256:dca38a21e4423f3edb821292e97cec7ad38086f84313462098568baedf4331f8", + "sha256:e2cad8093172b7d1595b4ad66f24270808658e11acf43a8f95b41276162eb5b8", + "sha256:e3db840a4dee542e37e09f30859f1612da90e1c5239a6a2498c473183a50e781", + "sha256:edcada2e24ed68f019175c2b2af2a8b481d3d084798b8c20d15d34f5c733fa58", + "sha256:f467bbb837691ab5a8ca359199d3429a11a01e6dfb3d9dcc676dc035ca93c0a9", + "sha256:f506af4f27def639ba45789fa6fde45f9a217da0be05f8910458e4557eed020c", + "sha256:f614fc9956d76d8a88a88bb41ddc12709caa755666f580af3a688899721efecd", + "sha256:f9afb5b746781fc2abce26193d1c817b7eb0e11459510fba65d2bd77fe161d9e", + "sha256:fb8b8ee99b3fffe4fd86f4c81b35a6bf7e4462cba019997af2fe679365db0c49" ], "index": "pypi", - "version": "==6.3.2" + "version": "==6.2" }, "flake8": { "hashes": [ @@ -779,32 +774,29 @@ }, "mypy": { "hashes": [ - "sha256:0e2dd88410937423fba18e57147dd07cd8381291b93d5b1984626f173a26543e", - "sha256:10daab80bc40f84e3f087d896cdb53dc811a9f04eae4b3f95779c26edee89d16", - "sha256:17e44649fec92e9f82102b48a3bf7b4a5510ad0cd22fa21a104826b5db4903e2", - "sha256:1a0459c333f00e6a11cbf6b468b870c2b99a906cb72d6eadf3d1d95d38c9352c", - "sha256:246e1aa127d5b78488a4a0594bd95f6d6fb9d63cf08a66dafbff8595d8891f67", - "sha256:2b184db8c618c43c3a31b32ff00cd28195d39e9c24e7c3b401f3db7f6e5767f5", - "sha256:2bc249409a7168d37c658e062e1ab5173300984a2dada2589638568ddc1db02b", - "sha256:3841b5433ff936bff2f4dc8d54cf2cdbfea5d8e88cedfac45c161368e5770ba6", - "sha256:4c3e497588afccfa4334a9986b56f703e75793133c4be3a02d06a3df16b67a58", - "sha256:5bf44840fb43ac4074636fd47ee476d73f0039f4f54e86d7265077dc199be24d", - "sha256:64235137edc16bee6f095aba73be5334677d6f6bdb7fa03cfab90164fa294a17", - "sha256:6776e5fa22381cc761df53e7496a805801c1a751b27b99a9ff2f0ca848c7eca0", - "sha256:6ce34a118d1a898f47def970a2042b8af6bdcc01546454726c7dd2171aa6dfca", - "sha256:6f6ad963172152e112b87cc7ec103ba0f2db2f1cd8997237827c052a3903eaa6", - "sha256:6f7106cbf9cc2f403693bf50ed7c9fa5bb3dfa9007b240db3c910929abe2a322", - "sha256:7742d2c4e46bb5017b51c810283a6a389296cda03df805a4f7869a6f41246534", - "sha256:9521c1265ccaaa1791d2c13582f06facf815f426cd8b07c3a485f486a8ffc1f3", - "sha256:a1b383fe99678d7402754fe90448d4037f9512ce70c21f8aee3b8bf48ffc51db", - "sha256:b840cfe89c4ab6386c40300689cd8645fc8d2d5f20101c7f8bd23d15fca14904", - "sha256:d8d3ba77e56b84cd47a8ee45b62c84b6d80d32383928fe2548c9a124ea0a725c", - "sha256:dcd955f36e0180258a96f880348fbca54ce092b40fbb4b37372ae3b25a0b0a46", - "sha256:e865fec858d75b78b4d63266c9aff770ecb6a39dfb6d6b56c47f7f8aba6baba8", - "sha256:edf7237137a1a9330046dbb14796963d734dd740a98d5e144a3eb1d267f5f9ee" + "sha256:0038b21890867793581e4cb0d810829f5fd4441aa75796b53033af3aa30430ce", + "sha256:1171f2e0859cfff2d366da2c7092b06130f232c636a3f7301e3feb8b41f6377d", + "sha256:1b06268df7eb53a8feea99cbfff77a6e2b205e70bf31743e786678ef87ee8069", + "sha256:1b65714dc296a7991000b6ee59a35b3f550e0073411ac9d3202f6516621ba66c", + "sha256:1bf752559797c897cdd2c65f7b60c2b6969ffe458417b8d947b8340cc9cec08d", + "sha256:300717a07ad09525401a508ef5d105e6b56646f7942eb92715a1c8d610149714", + "sha256:3c5b42d0815e15518b1f0990cff7a705805961613e701db60387e6fb663fe78a", + "sha256:4365c60266b95a3f216a3047f1d8e3f895da6c7402e9e1ddfab96393122cc58d", + "sha256:50c7346a46dc76a4ed88f3277d4959de8a2bd0a0fa47fa87a4cde36fe247ac05", + "sha256:5b56154f8c09427bae082b32275a21f500b24d93c88d69a5e82f3978018a0266", + "sha256:74f7eccbfd436abe9c352ad9fb65872cc0f1f0a868e9d9c44db0893440f0c697", + "sha256:7b3f6f557ba4afc7f2ce6d3215d5db279bcf120b3cfd0add20a5d4f4abdae5bc", + "sha256:8c11003aaeaf7cc2d0f1bc101c1cc9454ec4cc9cb825aef3cafff8a5fdf4c799", + "sha256:8ca7f8c4b1584d63c9a0f827c37ba7a47226c19a23a753d52e5b5eddb201afcd", + "sha256:c89702cac5b302f0c5d33b172d2b55b5df2bede3344a2fbed99ff96bddb2cf00", + "sha256:d8f1ff62f7a879c9fe5917b3f9eb93a79b78aad47b533911b853a757223f72e7", + "sha256:d9d2b84b2007cea426e327d2483238f040c49405a6bf4074f605f0156c91a47a", + "sha256:e839191b8da5b4e5d805f940537efcaa13ea5dd98418f06dc585d2891d228cf0", + "sha256:f9fe20d0872b26c4bba1c1be02c5340de1019530302cf2dcc85c7f9fc3252ae0", + "sha256:ff3bf387c14c805ab1388185dd22d6b210824e164d4bb324b195ff34e322d166" ], "index": "pypi", - "version": "==0.942" + "version": "==0.931" }, "mypy-extensions": { "hashes": [ @@ -878,11 +870,11 @@ }, "pytest": { "hashes": [ - "sha256:841132caef6b1ad17a9afde46dc4f6cfa59a05f9555aae5151f73bdf2820ca63", - "sha256:92f723789a8fdd7180b6b06483874feca4c48a5c76968e03bb3e7f806a1869ea" + "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89", + "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134" ], "index": "pypi", - "version": "==7.1.1" + "version": "==6.2.5" }, "pytest-cov": { "hashes": [ @@ -900,12 +892,20 @@ "index": "pypi", "version": "==0.4" }, + "toml": { + "hashes": [ + "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", + "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" + ], + "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==0.10.2" + }, "tomli": { "hashes": [ "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f" ], - "markers": "python_version < '3.11'", + "markers": "python_version >= '3.7'", "version": "==2.0.1" }, "types-click": { @@ -949,7 +949,7 @@ "sha256:1a9462dcc3347a79b1f1c0271fbe79e844580bb598bafa1ed208b94da3cdcd42", "sha256:21c85e0fe4b9a155d0799430b0ad741cdce7e359660ccbd8b530613e8df88ce2" ], - "markers": "python_version < '3.10'", + "markers": "python_version >= '3.6'", "version": "==4.1.1" } } From 1a200543c6616d2ef1df2a89e779c34c13e56829 Mon Sep 17 00:00:00 2001 From: Ben Topping Date: Thu, 7 Apr 2022 16:15:14 +0100 Subject: [PATCH 07/33] Initial work for adding new child barcodes feature --- ...2e15499_add_child_barcode_counter_table.py | 33 ++++++++++++++ baracoda/__init__.py | 2 + baracoda/child_barcodes.py | 37 ++++++++++++++++ baracoda/operations.py | 44 ++++++++++++++++++- baracoda/orm/child_barcode.py | 18 ++++++++ 5 files changed, 132 insertions(+), 2 deletions(-) create mode 100644 alembic/versions/e505f2e15499_add_child_barcode_counter_table.py create mode 100644 baracoda/child_barcodes.py create mode 100644 baracoda/orm/child_barcode.py diff --git a/alembic/versions/e505f2e15499_add_child_barcode_counter_table.py b/alembic/versions/e505f2e15499_add_child_barcode_counter_table.py new file mode 100644 index 0000000..e698e4b --- /dev/null +++ b/alembic/versions/e505f2e15499_add_child_barcode_counter_table.py @@ -0,0 +1,33 @@ +"""add child barcode counter table + + +Revision ID: e505f2e15499 +Revises: e501839465f6 +Create Date: 2022-04-06 14:58:18.990940 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = "e505f2e15499" +down_revision = "e501839465f6" +branch_labels = None +depends_on = None + + +def upgrade(): + op.create_table( + "child_barcode_counter", + # ID + sa.Column("barcode", sa.String(50), nullable=False), + sa.Column("child_count", sa.Integer(), nullable=True), + # Created_at + sa.PrimaryKeyConstraint("barcode"), + ) + + +def downgrade(): + op.drop_table("child_barcode_counter") + # ### end Alembic commands ### diff --git a/baracoda/__init__.py b/baracoda/__init__.py index 64ab192..298201d 100644 --- a/baracoda/__init__.py +++ b/baracoda/__init__.py @@ -7,6 +7,7 @@ from flask.cli import with_appcontext from baracoda import barcodes +from baracoda import child_barcodes from baracoda.config.logging import LOGGING from baracoda.db import db, reset_db @@ -41,6 +42,7 @@ def create_app(test_config=None): app.cli.add_command(init_db_command) app.register_blueprint(barcodes.bp) + app.register_blueprint(child_barcodes.bp) @app.route("/health") def health_check(): diff --git a/baracoda/child_barcodes.py b/baracoda/child_barcodes.py new file mode 100644 index 0000000..3f5dca5 --- /dev/null +++ b/baracoda/child_barcodes.py @@ -0,0 +1,37 @@ +import logging +from http import HTTPStatus +from typing import Any, Tuple + +from flask import Blueprint, request +from sqlalchemy import exc +from flask_cors import CORS +import pdb +from baracoda.operations import ChildBarcodeOperations +from baracoda.exceptions import InvalidCountError + +bp = Blueprint("child_barcode_creation", __name__) +CORS(bp) + +logger = logging.getLogger(__name__) + + +@bp.post("/child-barcodes/create") # type: ignore +def create_child_barcodes() -> Tuple[Any, int]: + try: + count = request.args.get('count', default=1, type=int) + barcode = request.args.get('barcode', default=1, type=str) + logger.debug(f"Creating child barcode(s) for '{barcode}'") + + child_barcodes = ChildBarcodeOperations.create_child_barcodes(barcode, count) + + return ( + { "barcodes": child_barcodes }, + HTTPStatus.CREATED, + ) + except InvalidCountError as e: + return {"errors": [f"{type(e).__name__}"]}, HTTPStatus.UNPROCESSABLE_ENTITY + except exc.IntegrityError as e: + logger.error(f"{type(e).__name__}: Two creation requests recieved for the same barcode") + return {"errors": [f"{type(e).__name__}"]}, HTTPStatus.INTERNAL_SERVER_ERROR + except Exception as e: + return {"errors": [f"{type(e).__name__}"]}, HTTPStatus.INTERNAL_SERVER_ERROR \ No newline at end of file diff --git a/baracoda/operations.py b/baracoda/operations.py index f965ecf..1e7da46 100644 --- a/baracoda/operations.py +++ b/baracoda/operations.py @@ -2,11 +2,12 @@ import re from datetime import datetime from typing import List, Optional, cast - +import pdb from baracoda.db import db -from baracoda.exceptions import InvalidPrefixError +from baracoda.exceptions import InvalidPrefixError, InvalidCountError from baracoda.helpers import get_prefix_item from baracoda.orm.barcode import Barcode +from baracoda.orm.child_barcode import ChildBarcode from baracoda.orm.barcodes_group import BarcodesGroup logger = logging.getLogger(__name__) @@ -170,3 +171,42 @@ def __set_prefix_item(self): prefix item or None if prefix does not exist """ self.prefix_item = get_prefix_item(self.prefix) + +class ChildBarcodeOperations: + def create_child_barcodes(barcode: str, count: int) -> ChildBarcode: + """Retrieve the next child barcodes for a given barcode + + Returns: + [str] -- The generated child barcodes + """ + + try: + if count < 1: + raise InvalidCountError() + + # Check barcode exists + barcode_record = db.session.query(ChildBarcode).with_for_update().filter_by(barcode=barcode).first() + + # If no record, then create one + if barcode_record is None: + old_count = 0 + barcode_record = ChildBarcode(barcode=barcode,child_count=count) + db.session.add(barcode_record) + else: + old_count = barcode_record.child_count + barcode_record.child_count = old_count + count + + db.session.commit() + + # We want the new count to start at the next number + new_count = old_count + 1 + + # Format child barcodes + child_barcodes = [] + for x in range(new_count, barcode_record.child_count + 1): + child_barcodes.append(f"{barcode}-{x}") + + return child_barcodes + except Exception as e: + db.session.rollback() + raise e diff --git a/baracoda/orm/child_barcode.py b/baracoda/orm/child_barcode.py new file mode 100644 index 0000000..6ba76ba --- /dev/null +++ b/baracoda/orm/child_barcode.py @@ -0,0 +1,18 @@ +from sqlalchemy import Column, Integer, String +from baracoda.orm.base import Base + + +class ChildBarcode(Base): + __tablename__ = "child_barcode_counter" + + barcode = Column(String(50), nullable=False, primary_key=True) + child_count = Column(Integer, nullable=True) + + def __repr__(self): + return "" % ( + self.barcode, + self.child_count, + ) + + def to_dict(self): + return {"barcode": self.barcode, "child_count": self.child_count} From 8bf8e6ccd19b30f8a11df3cbaa26bc1f70795e06 Mon Sep 17 00:00:00 2001 From: Ben Topping Date: Mon, 11 Apr 2022 12:35:18 +0100 Subject: [PATCH 08/33] Linted, added tests, added exception handling for child barcodes --- baracoda/child_barcodes.py | 41 +++++++++++++++++++++++++------ baracoda/exceptions.py | 17 ++++++++++++- baracoda/formats.py | 2 +- baracoda/operations.py | 12 ++++----- baracoda/sql/schema.sql | 9 ++++++- tests/data/fixture_data.py | 2 +- tests/test_barcodes.py | 11 +++++++-- tests/test_child_barcodes.py | 47 ++++++++++++++++++++++++++++++++++++ tests/test_operations.py | 29 +++++++++++++++++++++- 9 files changed, 148 insertions(+), 22 deletions(-) create mode 100644 tests/test_child_barcodes.py diff --git a/baracoda/child_barcodes.py b/baracoda/child_barcodes.py index 3f5dca5..b99ff99 100644 --- a/baracoda/child_barcodes.py +++ b/baracoda/child_barcodes.py @@ -5,9 +5,8 @@ from flask import Blueprint, request from sqlalchemy import exc from flask_cors import CORS -import pdb from baracoda.operations import ChildBarcodeOperations -from baracoda.exceptions import InvalidCountError +from baracoda.exceptions import InvalidCountError, InvalidBarcodeError bp = Blueprint("child_barcode_creation", __name__) CORS(bp) @@ -15,23 +14,49 @@ logger = logging.getLogger(__name__) -@bp.post("/child-barcodes/create") # type: ignore +@bp.post("/child-barcodes/new") # type: ignore def create_child_barcodes() -> Tuple[Any, int]: try: - count = request.args.get('count', default=1, type=int) - barcode = request.args.get('barcode', default=1, type=str) - logger.debug(f"Creating child barcode(s) for '{barcode}'") + count = get_count_param() + barcode = get_barcode_param() + logger.debug(f"Creating child barcode(s) for '{barcode}'") child_barcodes = ChildBarcodeOperations.create_child_barcodes(barcode, count) return ( - { "barcodes": child_barcodes }, + {"barcodes": child_barcodes}, HTTPStatus.CREATED, ) except InvalidCountError as e: return {"errors": [f"{type(e).__name__}"]}, HTTPStatus.UNPROCESSABLE_ENTITY + except InvalidBarcodeError as e: + return {"errors": [f"{type(e).__name__}"]}, HTTPStatus.UNPROCESSABLE_ENTITY except exc.IntegrityError as e: logger.error(f"{type(e).__name__}: Two creation requests recieved for the same barcode") return {"errors": [f"{type(e).__name__}"]}, HTTPStatus.INTERNAL_SERVER_ERROR except Exception as e: - return {"errors": [f"{type(e).__name__}"]}, HTTPStatus.INTERNAL_SERVER_ERROR \ No newline at end of file + return {"errors": [f"{type(e).__name__}"]}, HTTPStatus.INTERNAL_SERVER_ERROR + + +def get_count_param(): + count = 1 # Default count + if "count" in request.values: + count = int(request.values["count"]) + else: + if request.json and ("count" in request.json): + count = int(request.json["count"]) + if count > 0: + return count + raise InvalidCountError() + + +def get_barcode_param(): + barcode = "" + if "barcode" in request.values: + barcode = str(request.values["barcode"]) + else: + if request.json and ("barcode" in request.json): + barcode = str(request.json["barcode"]) + if len(barcode.strip()) > 0: + return barcode + raise InvalidBarcodeError() diff --git a/baracoda/exceptions.py b/baracoda/exceptions.py index 3564437..0ec6e4c 100644 --- a/baracoda/exceptions.py +++ b/baracoda/exceptions.py @@ -20,7 +20,7 @@ def __str__(self): class InvalidCountError(Error): - """Raised when a param count for a Heron barcode group is not found.""" + """Raised when a param count for a Heron barcode group or child barcode is not found.""" def __init__(self, message: str = ""): self.message = message @@ -32,3 +32,18 @@ def __str__(self): return f"InvalidCountError: {self.message}" else: return f"InvalidCountError: {default_message}" + + +class InvalidBarcodeError(Error): + """Raised when a barcode param is not given for a child barcode""" + + def __init__(self, message: str = ""): + self.message = message + + def __str__(self): + default_message = "Please add the 'barcode' param to the request" + + if self.message: + return f"InvalidBarcodeError: {self.message}" + else: + return f"InvalidBarcodeError: {default_message}" diff --git a/baracoda/formats.py b/baracoda/formats.py index 8eee530..e111c90 100644 --- a/baracoda/formats.py +++ b/baracoda/formats.py @@ -4,7 +4,7 @@ class FormatterInterface: - def barcode(self): + def barcode(self, value: int) -> str: pass diff --git a/baracoda/operations.py b/baracoda/operations.py index 1e7da46..f6726fa 100644 --- a/baracoda/operations.py +++ b/baracoda/operations.py @@ -2,9 +2,8 @@ import re from datetime import datetime from typing import List, Optional, cast -import pdb from baracoda.db import db -from baracoda.exceptions import InvalidPrefixError, InvalidCountError +from baracoda.exceptions import InvalidPrefixError from baracoda.helpers import get_prefix_item from baracoda.orm.barcode import Barcode from baracoda.orm.child_barcode import ChildBarcode @@ -172,8 +171,10 @@ def __set_prefix_item(self): """ self.prefix_item = get_prefix_item(self.prefix) + class ChildBarcodeOperations: - def create_child_barcodes(barcode: str, count: int) -> ChildBarcode: + @staticmethod + def create_child_barcodes(barcode: str, count: int) -> List[str]: """Retrieve the next child barcodes for a given barcode Returns: @@ -181,16 +182,13 @@ def create_child_barcodes(barcode: str, count: int) -> ChildBarcode: """ try: - if count < 1: - raise InvalidCountError() - # Check barcode exists barcode_record = db.session.query(ChildBarcode).with_for_update().filter_by(barcode=barcode).first() # If no record, then create one if barcode_record is None: old_count = 0 - barcode_record = ChildBarcode(barcode=barcode,child_count=count) + barcode_record = ChildBarcode(barcode=barcode, child_count=count) db.session.add(barcode_record) else: old_count = barcode_record.child_count diff --git a/baracoda/sql/schema.sql b/baracoda/sql/schema.sql index 0e594f2..45f17a2 100644 --- a/baracoda/sql/schema.sql +++ b/baracoda/sql/schema.sql @@ -11,7 +11,6 @@ CREATE SEQUENCE sqp START 1; DROP TABLE IF EXISTS barcodes; - CREATE TABLE barcodes ( id SERIAL, @@ -30,6 +29,14 @@ CREATE TABLE barcodes_groups PRIMARY KEY (id) ); +DROP TABLE IF EXISTS child_barcode_counter; +CREATE TABLE child_barcode_counter +( + barcode VARCHAR(50) NOT NULL, + child_count integer, + PRIMARY KEY (barcode) +); + DROP TABLE IF EXISTS alembic_version; CREATE TABLE alembic_version ( diff --git a/tests/data/fixture_data.py b/tests/data/fixture_data.py index 61f8245..87ae614 100644 --- a/tests/data/fixture_data.py +++ b/tests/data/fixture_data.py @@ -1,7 +1,7 @@ from typing import Dict, List, Union from baracoda.formats import HeronCogUkIdFormatter, GenericBarcodeFormatter -PREFIXES: List[Dict[str, Union[str, bool]]] = [ +PREFIXES: List[Dict[str, Union[str, object]]] = [ { "prefix": "SANG", "sequence_name": "heron", diff --git a/tests/test_barcodes.py b/tests/test_barcodes.py index e851a74..cae3cd0 100644 --- a/tests/test_barcodes.py +++ b/tests/test_barcodes.py @@ -1,4 +1,5 @@ from http import HTTPStatus +import json # sequences # starts at 2000000 for heron @@ -41,12 +42,18 @@ def test_get_new_barcodes_group_as_url_param(client): def test_get_new_barcodes_group_without_count(client): - response = client.post("/barcodes_group/SANG/new") + # Since the count is no longer a param the get_count_param method looks at the json body + # this response.json method will internal error unless we pass the correct headers and data + response = client.post( + "/barcodes_group/SANG/new", data=json.dumps({}), headers={"Content-Type": "application/json"} + ) assert response.status_code == HTTPStatus.UNPROCESSABLE_ENTITY def test_get_new_barcodes_group_as_json_param(client): - response = client.post("/barcodes_group/SANG/new", data={"count": 2}) + response = client.post( + "/barcodes_group/SANG/new", data=json.dumps({"count": 2}), headers={"Content-Type": "application/json"} + ) assert response.json == {"barcodes_group": {"barcodes": ["SANG-30D404", "SANG-30D413"], "id": 1}} assert response.status_code == HTTPStatus.CREATED diff --git a/tests/test_child_barcodes.py b/tests/test_child_barcodes.py new file mode 100644 index 0000000..4cde5bc --- /dev/null +++ b/tests/test_child_barcodes.py @@ -0,0 +1,47 @@ +from http import HTTPStatus +import json + +# sequences +# starts at 1 for sqp + + +def test_new_child_barcode(client): + response = client.post( + "/child-barcodes/new", data=json.dumps({"barcode": "test"}), headers={"Content-Type": "application/json"} + ) + assert response.json == {"barcodes": ["test-1"]} + assert response.status_code == HTTPStatus.CREATED + + +def test_new_child_barcode_with_count(client): + response = client.post( + "/child-barcodes/new", + data=json.dumps({"barcode": "test", "count": 3}), + headers={"Content-Type": "application/json"}, + ) + assert response.json == {"barcodes": ["test-1", "test-2", "test-3"]} + assert response.status_code == HTTPStatus.CREATED + + +def test_no_child_barcode(client): + response = client.post("/child-barcodes/new", data=json.dumps({}), headers={"Content-Type": "application/json"}) + assert response.json == {"errors": ["InvalidBarcodeError"]} + assert response.status_code == HTTPStatus.UNPROCESSABLE_ENTITY + + +def test_bad_child_barcode(client): + response = client.post( + "/child-barcodes/new", data=json.dumps({"barcode": " "}), headers={"Content-Type": "application/json"} + ) + assert response.json == {"errors": ["InvalidBarcodeError"]} + assert response.status_code == HTTPStatus.UNPROCESSABLE_ENTITY + + +def test_bad_count(client): + response = client.post( + "/child-barcodes/new", + data=json.dumps({"barcode": "test", "count": 0}), + headers={"Content-Type": "application/json"}, + ) + assert response.json == {"errors": ["InvalidCountError"]} + assert response.status_code == HTTPStatus.UNPROCESSABLE_ENTITY diff --git a/tests/test_operations.py b/tests/test_operations.py index 2af02d6..60f05ed 100644 --- a/tests/test_operations.py +++ b/tests/test_operations.py @@ -2,7 +2,10 @@ from baracoda.exceptions import InvalidPrefixError from baracoda.helpers import get_prefix_item -from baracoda.operations import BarcodeOperations +from baracoda.operations import BarcodeOperations, ChildBarcodeOperations + + +# BarcodeOperations def test_correct_prefix_obj_is_created(app, prefixes): @@ -33,3 +36,27 @@ def test_error_is_raised_if_prefix_is_not_valid(app): with app.app_context(): with pytest.raises(InvalidPrefixError): _ = BarcodeOperations(prefix="MOON") + + +# ChildBarcodeOperations + + +def test_child_barcodes_are_created_when_new_barcode(app): + with app.app_context(): + expected_child_barcodes = ["test-1"] + assert ChildBarcodeOperations.create_child_barcodes("test", 1) == expected_child_barcodes + + +def test_child_barcodes_are_created_when_existing_barcode(app): + with app.app_context(): + # Create a barcode record in the database + ChildBarcodeOperations.create_child_barcodes("test", 5) + # Expect child barcode to have correct when suffix when same barcode is used + expected_child_barcodes = ["test-6"] + assert ChildBarcodeOperations.create_child_barcodes("test", 1) == expected_child_barcodes + + +def test_correct_number_of_child_barcodes_are_created(app): + with app.app_context(): + expected_child_barcodes = ["test-1", "test-2", "test-3"] + assert ChildBarcodeOperations.create_child_barcodes("test", 3) == expected_child_barcodes From c7b277f4ffcaad900caa62d04c72db18ff5af668 Mon Sep 17 00:00:00 2001 From: Eduardo Martin Rojo Date: Mon, 11 Apr 2022 16:23:56 +0100 Subject: [PATCH 09/33] Added typing --- baracoda/config/defaults.py | 8 +++++--- baracoda/helpers.py | 3 ++- baracoda/operations.py | 13 ++++++++----- baracoda/types.py | 13 +++++++++++++ 4 files changed, 28 insertions(+), 9 deletions(-) create mode 100644 baracoda/types.py diff --git a/baracoda/config/defaults.py b/baracoda/config/defaults.py index 661194d..c681f6f 100644 --- a/baracoda/config/defaults.py +++ b/baracoda/config/defaults.py @@ -1,6 +1,7 @@ -from typing import Any, Dict, List +from typing import Any, Dict, List, Union, TypedDict, Type +from baracoda.types import PrefixesType -from baracoda.formats import HeronCogUkIdFormatter, GenericBarcodeFormatter +from baracoda.formats import HeronCogUkIdFormatter, GenericBarcodeFormatter, FormatterInterface ### # database config @@ -27,7 +28,8 @@ ### # prefix for barcodes returned from the respective sequece ### -PREFIXES: List[Dict[str, Any]] = [ + +PREFIXES: List[PrefixesType] = [ {"prefix": "ALDP", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, {"prefix": "BHRT", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, {"prefix": "BIRM", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, diff --git a/baracoda/helpers.py b/baracoda/helpers.py index 7232509..397991f 100644 --- a/baracoda/helpers.py +++ b/baracoda/helpers.py @@ -1,12 +1,13 @@ import logging from typing import Dict, Optional, Union +from baracoda.types import PrefixesType from flask import current_app logger = logging.getLogger(__name__) -def get_prefix_item(prefix: str) -> Optional[Dict[str, Union[str, bool]]]: +def get_prefix_item(prefix: str) -> Optional[PrefixesType]: """ Method which returns prefix object. This will contain the prefix, the sequence and whether any barcode for that prefix needs to be formatted. diff --git a/baracoda/operations.py b/baracoda/operations.py index f6726fa..2a9b17c 100644 --- a/baracoda/operations.py +++ b/baracoda/operations.py @@ -1,13 +1,15 @@ import logging import re from datetime import datetime -from typing import List, Optional, cast +from typing import List, Optional, cast, Dict, Union from baracoda.db import db from baracoda.exceptions import InvalidPrefixError from baracoda.helpers import get_prefix_item from baracoda.orm.barcode import Barcode from baracoda.orm.child_barcode import ChildBarcode from baracoda.orm.barcodes_group import BarcodesGroup +from baracoda.formats import FormatterInterface +from baracoda.types import PrefixesType logger = logging.getLogger(__name__) @@ -31,8 +33,9 @@ def __init__(self, prefix: str): logger.debug("Accessing sequence_name") self.sequence_name = self.prefix_item["sequence_name"] - def formatter(self): - return self.prefix_item["formatter_class"](self.prefix) + def formatter(self) -> FormatterInterface: + formatter_class = cast(PrefixesType, self.prefix_item)["formatter_class"] + return formatter_class(self.prefix) def create_barcode_group(self, count: int) -> BarcodesGroup: """Creates a new barcode group and the associated barcodes. @@ -44,7 +47,7 @@ def create_barcode_group(self, count: int) -> BarcodesGroup: BarcodeGroup -- the barcode group created """ try: - next_values = self.__get_next_values(self.sequence_name, count) # type: ignore + next_values = self.__get_next_values(self.sequence_name, count) barcodes_group = self.__build_barcodes_group() db.session.add(barcodes_group) @@ -70,7 +73,7 @@ def create_barcode(self) -> Barcode: """ logger.debug(f"Calling create_barcode for sequence name {self.sequence_name}") try: - next_value = self.__get_next_value(self.sequence_name) # type: ignore + next_value = self.__get_next_value(self.sequence_name) barcode = self.__build_barcode(self.prefix, next_value, barcodes_group=None) db.session.add(barcode) diff --git a/baracoda/types.py b/baracoda/types.py new file mode 100644 index 0000000..66f4ca8 --- /dev/null +++ b/baracoda/types.py @@ -0,0 +1,13 @@ +from typing import List, Union, TypedDict, Type +from baracoda.formats import HeronCogUkIdFormatter, GenericBarcodeFormatter, FormatterInterface + +FormatterClassType = Union[Type[HeronCogUkIdFormatter], Type[GenericBarcodeFormatter]] + +PrefixesType = TypedDict('PrefixesType', { + 'prefix': str, + 'sequence_name': str, + 'formatter_class': FormatterClassType +}) + + +FormatterInterfaceType = List[PrefixesType] From 43bf6e297e308eb5b339e2606d17859ff11b70b6 Mon Sep 17 00:00:00 2001 From: Eduardo Martin Rojo Date: Mon, 11 Apr 2022 16:57:48 +0100 Subject: [PATCH 10/33] Fix config --- baracoda/config/development.py | 2 +- baracoda/helpers.py | 2 +- baracoda/static/index.html | 98 +++++++++++++++++----------------- 3 files changed, 51 insertions(+), 51 deletions(-) diff --git a/baracoda/config/development.py b/baracoda/config/development.py index 5e996d7..598bb98 100644 --- a/baracoda/config/development.py +++ b/baracoda/config/development.py @@ -9,4 +9,4 @@ # adding an additional barcode prefix for use in source plate data setup # for beckman and biosero, sharing the ht sequence -PREFIXES.append({"prefix": "TEST", "sequence_name": "ht", "convert": False}) +PREFIXES.append({"prefix": "TEST", "sequence_name": "ht", "formatter_class": GenericBarcodeFormatter}) diff --git a/baracoda/helpers.py b/baracoda/helpers.py index 397991f..8f73d87 100644 --- a/baracoda/helpers.py +++ b/baracoda/helpers.py @@ -16,7 +16,7 @@ def get_prefix_item(prefix: str) -> Optional[PrefixesType]: value {str} -- the actual prefix Returns: - Dict -- prefix object e.g. { "prefix": "abc", "sequence_name": "seq", "convert": False} or + Dict -- prefix object e.g. { "prefix": "abc", "sequence_name": "seq" } or None -- if the prefix item is not available """ return next((item for item in current_app.config["PREFIXES"] if item["prefix"] == prefix), None) diff --git a/baracoda/static/index.html b/baracoda/static/index.html index 1d40055..12c54d5 100644 --- a/baracoda/static/index.html +++ b/baracoda/static/index.html @@ -44,55 +44,55 @@ } var prefixes = [ - { "prefix": "ALDP", "sequence_name": "heron", "convert": true }, - { "prefix": "BHRT", "sequence_name": "heron", "convert": true }, - { "prefix": "BIRM", "sequence_name": "heron", "convert": true }, - { "prefix": "BRBR", "sequence_name": "heron", "convert": true }, - { "prefix": "BRIG", "sequence_name": "heron", "convert": true }, - { "prefix": "BRIS", "sequence_name": "heron", "convert": true }, - { "prefix": "CAMB", "sequence_name": "heron", "convert": true }, - { "prefix": "CAMC", "sequence_name": "heron", "convert": true }, - { "prefix": "CPTD", "sequence_name": "heron", "convert": true }, - { "prefix": "CWAR", "sequence_name": "heron", "convert": true }, - { "prefix": "EDIN", "sequence_name": "heron", "convert": true }, - { "prefix": "EKHU", "sequence_name": "heron", "convert": true }, - { "prefix": "EXET", "sequence_name": "heron", "convert": true }, - { "prefix": "GCVR", "sequence_name": "heron", "convert": true }, - { "prefix": "GLOU", "sequence_name": "heron", "convert": true }, - { "prefix": "GSTT", "sequence_name": "heron", "convert": true }, - { "prefix": "HECH", "sequence_name": "heron", "convert": true }, - { "prefix": "HSLL", "sequence_name": "heron", "convert": true }, - { "prefix": "KGHT", "sequence_name": "heron", "convert": true }, - { "prefix": "LCST", "sequence_name": "heron", "convert": true }, - { "prefix": "LEED", "sequence_name": "heron", "convert": true }, - { "prefix": "LIVE", "sequence_name": "heron", "convert": true }, - { "prefix": "LOND", "sequence_name": "heron", "convert": true }, - { "prefix": "LSPA", "sequence_name": "heron", "convert": true }, - { "prefix": "MILK", "sequence_name": "heron", "convert": true }, - { "prefix": "MTUN", "sequence_name": "heron", "convert": true }, - { "prefix": "NEWC", "sequence_name": "heron", "convert": true }, - { "prefix": "NIRE", "sequence_name": "heron", "convert": true }, - { "prefix": "NORT", "sequence_name": "heron", "convert": true }, - { "prefix": "NORW", "sequence_name": "heron", "convert": true }, - { "prefix": "NOTT", "sequence_name": "heron", "convert": true }, - { "prefix": "NWGH", "sequence_name": "heron", "convert": true }, - { "prefix": "OXON", "sequence_name": "heron", "convert": true }, - { "prefix": "PAHT", "sequence_name": "heron", "convert": true }, - { "prefix": "PHEC", "sequence_name": "heron", "convert": true }, - { "prefix": "PHWC", "sequence_name": "heron", "convert": true }, - { "prefix": "PLYM", "sequence_name": "heron", "convert": true }, - { "prefix": "PORT", "sequence_name": "heron", "convert": true }, - { "prefix": "PRIN", "sequence_name": "heron", "convert": true }, - { "prefix": "QEUH", "sequence_name": "heron", "convert": true }, - { "prefix": "RAND", "sequence_name": "heron", "convert": true }, - { "prefix": "RSCH", "sequence_name": "heron", "convert": true }, - { "prefix": "SANG", "sequence_name": "heron", "convert": true }, - { "prefix": "SHEF", "sequence_name": "heron", "convert": true }, - { "prefix": "TBSD", "sequence_name": "heron", "convert": true }, - { "prefix": "TFCI", "sequence_name": "heron", "convert": true }, - { "prefix": "WAHH", "sequence_name": "heron", "convert": true }, - { "prefix": "WSFT", "sequence_name": "heron", "convert": true }, - { "prefix": "HT", "sequence_name": "ht", "convert": false } + { "prefix": "ALDP", "sequence_name": "heron" }, + { "prefix": "BHRT", "sequence_name": "heron" }, + { "prefix": "BIRM", "sequence_name": "heron" }, + { "prefix": "BRBR", "sequence_name": "heron" }, + { "prefix": "BRIG", "sequence_name": "heron" }, + { "prefix": "BRIS", "sequence_name": "heron" }, + { "prefix": "CAMB", "sequence_name": "heron" }, + { "prefix": "CAMC", "sequence_name": "heron" }, + { "prefix": "CPTD", "sequence_name": "heron" }, + { "prefix": "CWAR", "sequence_name": "heron" }, + { "prefix": "EDIN", "sequence_name": "heron" }, + { "prefix": "EKHU", "sequence_name": "heron" }, + { "prefix": "EXET", "sequence_name": "heron" }, + { "prefix": "GCVR", "sequence_name": "heron" }, + { "prefix": "GLOU", "sequence_name": "heron" }, + { "prefix": "GSTT", "sequence_name": "heron" }, + { "prefix": "HECH", "sequence_name": "heron" }, + { "prefix": "HSLL", "sequence_name": "heron" }, + { "prefix": "KGHT", "sequence_name": "heron" }, + { "prefix": "LCST", "sequence_name": "heron" }, + { "prefix": "LEED", "sequence_name": "heron" }, + { "prefix": "LIVE", "sequence_name": "heron" }, + { "prefix": "LOND", "sequence_name": "heron" }, + { "prefix": "LSPA", "sequence_name": "heron" }, + { "prefix": "MILK", "sequence_name": "heron" }, + { "prefix": "MTUN", "sequence_name": "heron" }, + { "prefix": "NEWC", "sequence_name": "heron" }, + { "prefix": "NIRE", "sequence_name": "heron" }, + { "prefix": "NORT", "sequence_name": "heron" }, + { "prefix": "NORW", "sequence_name": "heron" }, + { "prefix": "NOTT", "sequence_name": "heron" }, + { "prefix": "NWGH", "sequence_name": "heron" }, + { "prefix": "OXON", "sequence_name": "heron" }, + { "prefix": "PAHT", "sequence_name": "heron" }, + { "prefix": "PHEC", "sequence_name": "heron" }, + { "prefix": "PHWC", "sequence_name": "heron" }, + { "prefix": "PLYM", "sequence_name": "heron" }, + { "prefix": "PORT", "sequence_name": "heron" }, + { "prefix": "PRIN", "sequence_name": "heron" }, + { "prefix": "QEUH", "sequence_name": "heron" }, + { "prefix": "RAND", "sequence_name": "heron" }, + { "prefix": "RSCH", "sequence_name": "heron" }, + { "prefix": "SANG", "sequence_name": "heron" }, + { "prefix": "SHEF", "sequence_name": "heron" }, + { "prefix": "TBSD", "sequence_name": "heron" }, + { "prefix": "TFCI", "sequence_name": "heron" }, + { "prefix": "WAHH", "sequence_name": "heron" }, + { "prefix": "WSFT", "sequence_name": "heron" }, + { "prefix": "HT", "sequence_name": "ht" } ] function buildPrefixSelector() { From 0241cdcbac218b9c63a45fbc304271d395358634 Mon Sep 17 00:00:00 2001 From: Eduardo Martin Rojo Date: Mon, 11 Apr 2022 16:58:44 +0100 Subject: [PATCH 11/33] Reformatted --- baracoda/types.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/baracoda/types.py b/baracoda/types.py index 66f4ca8..783c800 100644 --- a/baracoda/types.py +++ b/baracoda/types.py @@ -3,11 +3,7 @@ FormatterClassType = Union[Type[HeronCogUkIdFormatter], Type[GenericBarcodeFormatter]] -PrefixesType = TypedDict('PrefixesType', { - 'prefix': str, - 'sequence_name': str, - 'formatter_class': FormatterClassType -}) +PrefixesType = TypedDict("PrefixesType", {"prefix": str, "sequence_name": str, "formatter_class": FormatterClassType}) FormatterInterfaceType = List[PrefixesType] From 5253863b6f8e8e3b32220548f9a45965480e0f85 Mon Sep 17 00:00:00 2001 From: Eduardo Martin Rojo Date: Mon, 11 Apr 2022 17:00:58 +0100 Subject: [PATCH 12/33] Linting --- baracoda/config/defaults.py | 4 ++-- baracoda/helpers.py | 2 +- baracoda/operations.py | 2 +- baracoda/types.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/baracoda/config/defaults.py b/baracoda/config/defaults.py index c681f6f..fc9114c 100644 --- a/baracoda/config/defaults.py +++ b/baracoda/config/defaults.py @@ -1,7 +1,7 @@ -from typing import Any, Dict, List, Union, TypedDict, Type +from typing import List from baracoda.types import PrefixesType -from baracoda.formats import HeronCogUkIdFormatter, GenericBarcodeFormatter, FormatterInterface +from baracoda.formats import HeronCogUkIdFormatter, GenericBarcodeFormatter ### # database config diff --git a/baracoda/helpers.py b/baracoda/helpers.py index 8f73d87..1b3efb2 100644 --- a/baracoda/helpers.py +++ b/baracoda/helpers.py @@ -1,5 +1,5 @@ import logging -from typing import Dict, Optional, Union +from typing import Optional from baracoda.types import PrefixesType from flask import current_app diff --git a/baracoda/operations.py b/baracoda/operations.py index 2a9b17c..70c8df1 100644 --- a/baracoda/operations.py +++ b/baracoda/operations.py @@ -1,7 +1,7 @@ import logging import re from datetime import datetime -from typing import List, Optional, cast, Dict, Union +from typing import List, Optional, cast from baracoda.db import db from baracoda.exceptions import InvalidPrefixError from baracoda.helpers import get_prefix_item diff --git a/baracoda/types.py b/baracoda/types.py index 783c800..2feb663 100644 --- a/baracoda/types.py +++ b/baracoda/types.py @@ -1,5 +1,5 @@ from typing import List, Union, TypedDict, Type -from baracoda.formats import HeronCogUkIdFormatter, GenericBarcodeFormatter, FormatterInterface +from baracoda.formats import HeronCogUkIdFormatter, GenericBarcodeFormatter FormatterClassType = Union[Type[HeronCogUkIdFormatter], Type[GenericBarcodeFormatter]] From cf0a821e431321a07c60d989b2d135762265b607 Mon Sep 17 00:00:00 2001 From: Ben Topping Date: Tue, 12 Apr 2022 10:51:43 +0100 Subject: [PATCH 13/33] changed create_child_barcodes method to be part of operations module and removed ability to create child barcodes using params --- baracoda/child_barcodes.py | 21 +++++-------- baracoda/operations.py | 61 +++++++++++++++++++------------------- tests/test_operations.py | 12 ++++---- 3 files changed, 44 insertions(+), 50 deletions(-) diff --git a/baracoda/child_barcodes.py b/baracoda/child_barcodes.py index b99ff99..d7eb187 100644 --- a/baracoda/child_barcodes.py +++ b/baracoda/child_barcodes.py @@ -1,11 +1,10 @@ import logging from http import HTTPStatus from typing import Any, Tuple - from flask import Blueprint, request from sqlalchemy import exc from flask_cors import CORS -from baracoda.operations import ChildBarcodeOperations +from baracoda.operations import create_child_barcodes from baracoda.exceptions import InvalidCountError, InvalidBarcodeError bp = Blueprint("child_barcode_creation", __name__) @@ -15,13 +14,13 @@ @bp.post("/child-barcodes/new") # type: ignore -def create_child_barcodes() -> Tuple[Any, int]: +def new_child_barcodes() -> Tuple[Any, int]: try: count = get_count_param() barcode = get_barcode_param() logger.debug(f"Creating child barcode(s) for '{barcode}'") - child_barcodes = ChildBarcodeOperations.create_child_barcodes(barcode, count) + child_barcodes = create_child_barcodes(barcode, count) return ( {"barcodes": child_barcodes}, @@ -40,11 +39,8 @@ def create_child_barcodes() -> Tuple[Any, int]: def get_count_param(): count = 1 # Default count - if "count" in request.values: - count = int(request.values["count"]) - else: - if request.json and ("count" in request.json): - count = int(request.json["count"]) + if request.json and ("count" in request.json): + count = int(request.json["count"]) if count > 0: return count raise InvalidCountError() @@ -52,11 +48,8 @@ def get_count_param(): def get_barcode_param(): barcode = "" - if "barcode" in request.values: - barcode = str(request.values["barcode"]) - else: - if request.json and ("barcode" in request.json): - barcode = str(request.json["barcode"]) + if request.json and ("barcode" in request.json): + barcode = str(request.json["barcode"]) if len(barcode.strip()) > 0: return barcode raise InvalidBarcodeError() diff --git a/baracoda/operations.py b/baracoda/operations.py index 70c8df1..fdce132 100644 --- a/baracoda/operations.py +++ b/baracoda/operations.py @@ -175,39 +175,40 @@ def __set_prefix_item(self): self.prefix_item = get_prefix_item(self.prefix) -class ChildBarcodeOperations: - @staticmethod - def create_child_barcodes(barcode: str, count: int) -> List[str]: - """Retrieve the next child barcodes for a given barcode +# Child barcode operations - Returns: - [str] -- The generated child barcodes - """ - try: - # Check barcode exists - barcode_record = db.session.query(ChildBarcode).with_for_update().filter_by(barcode=barcode).first() - - # If no record, then create one - if barcode_record is None: - old_count = 0 - barcode_record = ChildBarcode(barcode=barcode, child_count=count) - db.session.add(barcode_record) - else: - old_count = barcode_record.child_count - barcode_record.child_count = old_count + count +def create_child_barcodes(barcode: str, count: int) -> List[str]: + """Retrieve the next child barcodes for a given barcode - db.session.commit() + Returns: + [str] -- The generated child barcodes + """ - # We want the new count to start at the next number - new_count = old_count + 1 + try: + # Check barcode exists + barcode_record = db.session.query(ChildBarcode).with_for_update().filter_by(barcode=barcode).first() - # Format child barcodes - child_barcodes = [] - for x in range(new_count, barcode_record.child_count + 1): - child_barcodes.append(f"{barcode}-{x}") + # If no record, then create one + if barcode_record is None: + old_count = 0 + barcode_record = ChildBarcode(barcode=barcode, child_count=count) + db.session.add(barcode_record) + else: + old_count = barcode_record.child_count + barcode_record.child_count = old_count + count - return child_barcodes - except Exception as e: - db.session.rollback() - raise e + db.session.commit() + + # We want the new count to start at the next number + new_count = old_count + 1 + + # Format child barcodes + child_barcodes = [] + for x in range(new_count, barcode_record.child_count + 1): + child_barcodes.append(f"{barcode}-{x}") + + return child_barcodes + except Exception as e: + db.session.rollback() + raise e diff --git a/tests/test_operations.py b/tests/test_operations.py index 60f05ed..160bf8c 100644 --- a/tests/test_operations.py +++ b/tests/test_operations.py @@ -2,7 +2,7 @@ from baracoda.exceptions import InvalidPrefixError from baracoda.helpers import get_prefix_item -from baracoda.operations import BarcodeOperations, ChildBarcodeOperations +from baracoda.operations import BarcodeOperations, create_child_barcodes # BarcodeOperations @@ -38,25 +38,25 @@ def test_error_is_raised_if_prefix_is_not_valid(app): _ = BarcodeOperations(prefix="MOON") -# ChildBarcodeOperations +# Child barcode operations def test_child_barcodes_are_created_when_new_barcode(app): with app.app_context(): expected_child_barcodes = ["test-1"] - assert ChildBarcodeOperations.create_child_barcodes("test", 1) == expected_child_barcodes + assert create_child_barcodes("test", 1) == expected_child_barcodes def test_child_barcodes_are_created_when_existing_barcode(app): with app.app_context(): # Create a barcode record in the database - ChildBarcodeOperations.create_child_barcodes("test", 5) + create_child_barcodes("test", 5) # Expect child barcode to have correct when suffix when same barcode is used expected_child_barcodes = ["test-6"] - assert ChildBarcodeOperations.create_child_barcodes("test", 1) == expected_child_barcodes + assert create_child_barcodes("test", 1) == expected_child_barcodes def test_correct_number_of_child_barcodes_are_created(app): with app.app_context(): expected_child_barcodes = ["test-1", "test-2", "test-3"] - assert ChildBarcodeOperations.create_child_barcodes("test", 3) == expected_child_barcodes + assert create_child_barcodes("test", 3) == expected_child_barcodes From bd488cfc8c4db9fb4c5896111da50ab427ac8ccc Mon Sep 17 00:00:00 2001 From: Eduardo Martin Rojo Date: Thu, 12 May 2022 00:07:55 +0100 Subject: [PATCH 14/33] Changes to create barcode groups for child barcodes --- baracoda/child_barcodes.py | 21 ++- baracoda/migrations/script.py.mako | 24 +++ baracoda/operations.py | 162 +++++++++++------ tests/test_child_barcodes.py | 279 +++++++++++++++++++++++++++-- tests/test_operations.py | 59 +++++- 5 files changed, 471 insertions(+), 74 deletions(-) create mode 100644 baracoda/migrations/script.py.mako diff --git a/baracoda/child_barcodes.py b/baracoda/child_barcodes.py index d7eb187..7eb415f 100644 --- a/baracoda/child_barcodes.py +++ b/baracoda/child_barcodes.py @@ -4,7 +4,9 @@ from flask import Blueprint, request from sqlalchemy import exc from flask_cors import CORS -from baracoda.operations import create_child_barcodes + +# from baracoda.operations import create_child_barcodes +from baracoda.operations import BarcodeOperations, InvalidParentBarcode from baracoda.exceptions import InvalidCountError, InvalidBarcodeError bp = Blueprint("child_barcode_creation", __name__) @@ -13,17 +15,24 @@ logger = logging.getLogger(__name__) -@bp.post("/child-barcodes/new") # type: ignore -def new_child_barcodes() -> Tuple[Any, int]: +@bp.post("/child-barcodes//new") # type: ignore +def new_child_barcodes(prefix: str) -> Tuple[Any, int]: try: count = get_count_param() barcode = get_barcode_param() logger.debug(f"Creating child barcode(s) for '{barcode}'") - child_barcodes = create_child_barcodes(barcode, count) + operator = BarcodeOperations(prefix=prefix) + + if not operator.is_valid_parent_barcode(barcode): + barcode_group = operator.create_barcode_group(count) + else: + info = operator.extract_barcode_parent_information(barcode) + operator.validate_barcode_parent_information(info) + barcode_group = operator.create_children_barcode_group(info["parent_barcode"], count) return ( - {"barcodes": child_barcodes}, + barcode_group.to_dict(), HTTPStatus.CREATED, ) except InvalidCountError as e: @@ -33,6 +42,8 @@ def new_child_barcodes() -> Tuple[Any, int]: except exc.IntegrityError as e: logger.error(f"{type(e).__name__}: Two creation requests recieved for the same barcode") return {"errors": [f"{type(e).__name__}"]}, HTTPStatus.INTERNAL_SERVER_ERROR + except InvalidParentBarcode as e: + return {"errors": [f"{type(e).__name__}"]}, HTTPStatus.INTERNAL_SERVER_ERROR except Exception as e: return {"errors": [f"{type(e).__name__}"]}, HTTPStatus.INTERNAL_SERVER_ERROR diff --git a/baracoda/migrations/script.py.mako b/baracoda/migrations/script.py.mako new file mode 100644 index 0000000..2c01563 --- /dev/null +++ b/baracoda/migrations/script.py.mako @@ -0,0 +1,24 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision = ${repr(up_revision)} +down_revision = ${repr(down_revision)} +branch_labels = ${repr(branch_labels)} +depends_on = ${repr(depends_on)} + + +def upgrade(): + ${upgrades if upgrades else "pass"} + + +def downgrade(): + ${downgrades if downgrades else "pass"} diff --git a/baracoda/operations.py b/baracoda/operations.py index fdce132..5315e82 100644 --- a/baracoda/operations.py +++ b/baracoda/operations.py @@ -1,7 +1,8 @@ import logging import re from datetime import datetime -from typing import List, Optional, cast +from typing import List, Optional, cast, Dict +from xmlrpc.client import Boolean from baracoda.db import db from baracoda.exceptions import InvalidPrefixError from baracoda.helpers import get_prefix_item @@ -14,6 +15,10 @@ logger = logging.getLogger(__name__) +class InvalidParentBarcode(BaseException): + pass + + class BarcodeOperations: def __init__(self, prefix: str): logger.debug("Instantiate....") @@ -37,6 +42,10 @@ def formatter(self) -> FormatterInterface: formatter_class = cast(PrefixesType, self.prefix_item)["formatter_class"] return formatter_class(self.prefix) + def create_barcodes(self, count: int) -> List[str]: + next_values = self.__get_next_values(self.sequence_name, count) + return [self.formatter().barcode(next_value) for next_value in next_values] + def create_barcode_group(self, count: int) -> BarcodesGroup: """Creates a new barcode group and the associated barcodes. @@ -46,24 +55,21 @@ def create_barcode_group(self, count: int) -> BarcodesGroup: Returns: BarcodeGroup -- the barcode group created """ - try: - next_values = self.__get_next_values(self.sequence_name, count) - - barcodes_group = self.__build_barcodes_group() - db.session.add(barcodes_group) + next_values = self.__get_next_values(self.sequence_name, count) + barcodes = [self.formatter().barcode(next_value) for next_value in next_values] + return self.__create_barcode_group(barcodes) - barcodes = [ - self.__build_barcode(self.prefix, next_value, barcodes_group=barcodes_group) - for next_value in next_values - ] - db.session.add_all(barcodes) + def create_children_barcode_group(self, parent_barcode: str, count: int) -> BarcodesGroup: + """Creates a new barcode group and the associated barcodes. - db.session.commit() + Arguments: + count {int} -- number of barcodes to create in the group - return barcodes_group - except Exception as e: - db.session.rollback() - raise e + Returns: + BarcodeGroup -- the barcode group created + """ + barcodes = self.create_child_barcodes(parent_barcode, count) + return self.__create_barcode_group(barcodes) def create_barcode(self) -> Barcode: """Generate and store a barcode using the Heron formatter. @@ -74,7 +80,9 @@ def create_barcode(self) -> Barcode: logger.debug(f"Calling create_barcode for sequence name {self.sequence_name}") try: next_value = self.__get_next_value(self.sequence_name) - barcode = self.__build_barcode(self.prefix, next_value, barcodes_group=None) + barcode = self.__build_barcode( + prefix=self.prefix, barcode=self.formatter().barcode(next_value), barcodes_group=None + ) db.session.add(barcode) @@ -126,8 +134,7 @@ def __validate_prefix(self) -> bool: return bool(pattern.match(self.prefix)) - def __build_barcode(self, prefix: str, next_value: int, barcodes_group: Optional[BarcodesGroup]) -> Barcode: - barcode = self.formatter().barcode(next_value) + def __build_barcode(self, prefix: str, barcode: str, barcodes_group: Optional[BarcodesGroup]) -> Barcode: return Barcode( prefix=prefix, barcode=barcode, @@ -135,6 +142,32 @@ def __build_barcode(self, prefix: str, next_value: int, barcodes_group: Optional barcodes_group=barcodes_group, ) + def __create_barcode_group(self, barcodes: List[str]) -> BarcodesGroup: + """Creates a new barcode group and the associated barcodes. + + Arguments: + count {int} -- number of barcodes to create in the group + + Returns: + BarcodeGroup -- the barcode group created + """ + try: + barcodes_group = self.__build_barcodes_group() + db.session.add(barcodes_group) + + barcodes_instances = [ + self.__build_barcode(prefix=self.prefix, barcode=barcode, barcodes_group=barcodes_group) + for barcode in barcodes + ] + db.session.add_all(barcodes_instances) + + db.session.commit() + + return barcodes_group + except Exception as e: + db.session.rollback() + raise e + def __build_barcodes_group(self) -> BarcodesGroup: return BarcodesGroup(created_at=datetime.now()) @@ -174,41 +207,68 @@ def __set_prefix_item(self): """ self.prefix_item = get_prefix_item(self.prefix) + # Child barcode operations + def is_valid_parent_barcode(self, barcode: str) -> Boolean: + return not self.extract_barcode_parent_information(barcode) is None -# Child barcode operations + def extract_barcode_parent_information(self, barcode): + pattern = re.compile(f"^(?P{self.prefix}-\\d+)(?:-(?P\\d+))?$") + found = pattern.search(barcode) + if not found: + return None + return { + "parent_barcode": found.group("parent_barcode"), + "child": found.group("child"), + } + def validate_barcode_parent_information(self, info: Dict[str, str]) -> None: + if not info: + raise InvalidParentBarcode("The barcode provided is not valid for generating child barcodes") -def create_child_barcodes(barcode: str, count: int) -> List[str]: - """Retrieve the next child barcodes for a given barcode + barcode_record = db.session.query(ChildBarcode).filter_by(barcode=info["parent_barcode"]).first() - Returns: - [str] -- The generated child barcodes - """ + if barcode_record is None: + if info["child"]: + raise InvalidParentBarcode("The barcode provided is an impostor barcode. It has no parent.") + return - try: - # Check barcode exists - barcode_record = db.session.query(ChildBarcode).with_for_update().filter_by(barcode=barcode).first() + if info["child"]: + child_position = int(info["child"]) + if barcode_record.child_count < child_position: + raise InvalidParentBarcode( + "The barcode provided is an impostor barcode. Its parent has not generated this position yet." + ) - # If no record, then create one - if barcode_record is None: - old_count = 0 - barcode_record = ChildBarcode(barcode=barcode, child_count=count) - db.session.add(barcode_record) - else: - old_count = barcode_record.child_count - barcode_record.child_count = old_count + count - - db.session.commit() - - # We want the new count to start at the next number - new_count = old_count + 1 - - # Format child barcodes - child_barcodes = [] - for x in range(new_count, barcode_record.child_count + 1): - child_barcodes.append(f"{barcode}-{x}") - - return child_barcodes - except Exception as e: - db.session.rollback() - raise e + def create_child_barcodes(self, parent_barcode: str, count: int) -> List[str]: + """Retrieve the next child barcodes for a given barcode + + Returns: + [str] -- The generated child barcodes + """ + try: + # Check barcode exists + barcode_record = db.session.query(ChildBarcode).with_for_update().filter_by(barcode=parent_barcode).first() + + # If no record, then create one + if barcode_record is None: + old_count = 0 + barcode_record = ChildBarcode(barcode=parent_barcode, child_count=count) + db.session.add(barcode_record) + else: + old_count = barcode_record.child_count + barcode_record.child_count = old_count + count + + db.session.commit() + + # We want the new count to start at the next number + new_count = old_count + 1 + + # Format child barcodes + child_barcodes = [] + for x in range(new_count, barcode_record.child_count + 1): + child_barcodes.append(f"{parent_barcode}-{x}") + + return child_barcodes + except Exception as e: + db.session.rollback() + raise e diff --git a/tests/test_child_barcodes.py b/tests/test_child_barcodes.py index 4cde5bc..adc5fb5 100644 --- a/tests/test_child_barcodes.py +++ b/tests/test_child_barcodes.py @@ -5,33 +5,266 @@ # starts at 1 for sqp -def test_new_child_barcode(client): +def test_new_child_barcode_can_create_single_barcode(client): response = client.post( - "/child-barcodes/new", data=json.dumps({"barcode": "test"}), headers={"Content-Type": "application/json"} + "/child-barcodes/HT/new", data=json.dumps({"barcode": "HT-1"}), headers={"Content-Type": "application/json"} ) - assert response.json == {"barcodes": ["test-1"]} + assert response.json == {"barcodes_group": {"id": 1, "barcodes": ["HT-1-1"]}} assert response.status_code == HTTPStatus.CREATED -def test_new_child_barcode_with_count(client): +def test_new_child_barcode_can_create_several_barcodes(client): response = client.post( - "/child-barcodes/new", - data=json.dumps({"barcode": "test", "count": 3}), + "/child-barcodes/HT/new", + data=json.dumps({"barcode": "HT-1", "count": 3}), headers={"Content-Type": "application/json"}, ) - assert response.json == {"barcodes": ["test-1", "test-2", "test-3"]} + assert response.json == {"barcodes_group": {"id": 1, "barcodes": ["HT-1-1", "HT-1-2", "HT-1-3"]}} assert response.status_code == HTTPStatus.CREATED +def test_new_child_barcode_different_parent_keep_their_own_counting_of_children(client): + # Parent 1 + response = client.post( + "/child-barcodes/HT/new", + data=json.dumps({"barcode": "HT-1", "count": 3}), + headers={"Content-Type": "application/json"}, + ) + assert response.json == {"barcodes_group": {"id": 1, "barcodes": ["HT-1-1", "HT-1-2", "HT-1-3"]}} + assert response.status_code == HTTPStatus.CREATED + + # Parent 2 + response = client.post( + "/child-barcodes/HT/new", + data=json.dumps({"barcode": "HT-2", "count": 2}), + headers={"Content-Type": "application/json"}, + ) + assert response.json == {"barcodes_group": {"id": 2, "barcodes": ["HT-2-1", "HT-2-2"]}} + assert response.status_code == HTTPStatus.CREATED + + # Parent 1 again + response = client.post( + "/child-barcodes/HT/new", + data=json.dumps({"barcode": "HT-1", "count": 1}), + headers={"Content-Type": "application/json"}, + ) + assert response.json == {"barcodes_group": {"id": 3, "barcodes": ["HT-1-4"]}} + assert response.status_code == HTTPStatus.CREATED + + +def test_new_child_barcode_incorrect_valid_prefixed_parent_can_create_unattributed_children(client): + response = client.post( + "/child-barcodes/HT/new", + data=json.dumps({"barcode": "SANG-1", "count": 3}), + headers={"Content-Type": "application/json"}, + ) + assert response.json == {"barcodes_group": {"id": 1, "barcodes": ["HT-111111", "HT-111112", "HT-111113"]}} + assert response.status_code == HTTPStatus.CREATED + + +def test_new_child_barcode_invalid_prefixed_parent_can_create_unattributed_children(client): + response = client.post( + "/child-barcodes/HT/new", + data=json.dumps({"barcode": "test-123", "count": 3}), + headers={"Content-Type": "application/json"}, + ) + assert response.json == {"barcodes_group": {"barcodes": ["HT-111111", "HT-111112", "HT-111113"], "id": 1}} + assert response.status_code == HTTPStatus.CREATED + + +def test_new_child_barcode_valid_parent_can_create_attributed_children_and_descendants_can_continue_lineage(client): + response = client.post( + "/child-barcodes/HT/new", + data=json.dumps({"barcode": "HT-1", "count": 3}), + headers={"Content-Type": "application/json"}, + ) + assert response.json == {"barcodes_group": {"barcodes": ["HT-1-1", "HT-1-2", "HT-1-3"], "id": 1}} + assert response.status_code == HTTPStatus.CREATED + + response = client.post( + "/child-barcodes/HT/new", + data=json.dumps({"barcode": "HT-1-1", "count": 3}), + headers={"Content-Type": "application/json"}, + ) + assert response.json == {"barcodes_group": {"barcodes": ["HT-1-4", "HT-1-5", "HT-1-6"], "id": 2}} + assert response.status_code == HTTPStatus.CREATED + + response = client.post( + "/child-barcodes/HT/new", + data=json.dumps({"barcode": "HT-1-5", "count": 1}), + headers={"Content-Type": "application/json"}, + ) + assert response.json == {"barcodes_group": {"barcodes": ["HT-1-7"], "id": 3}} + assert response.status_code == HTTPStatus.CREATED + + +def test_new_child_barcode_valid_parent_can_create_attributed_children_and_children_and_parent_can_continue_lineage( + client, +): + # Parent + response = client.post( + "/child-barcodes/HT/new", + data=json.dumps({"barcode": "HT-1", "count": 3}), + headers={"Content-Type": "application/json"}, + ) + assert response.json == {"barcodes_group": {"barcodes": ["HT-1-1", "HT-1-2", "HT-1-3"], "id": 1}} + assert response.status_code == HTTPStatus.CREATED + + # Children 1 + response = client.post( + "/child-barcodes/HT/new", + data=json.dumps({"barcode": "HT-1-1", "count": 3}), + headers={"Content-Type": "application/json"}, + ) + assert response.json == {"barcodes_group": {"barcodes": ["HT-1-4", "HT-1-5", "HT-1-6"], "id": 2}} + assert response.status_code == HTTPStatus.CREATED + + # Children 2 + response = client.post( + "/child-barcodes/HT/new", + data=json.dumps({"barcode": "HT-1-2", "count": 2}), + headers={"Content-Type": "application/json"}, + ) + assert response.json == {"barcodes_group": {"barcodes": ["HT-1-7", "HT-1-8"], "id": 3}} + assert response.status_code == HTTPStatus.CREATED + + # Parent again + response = client.post( + "/child-barcodes/HT/new", + data=json.dumps({"barcode": "HT-1", "count": 2}), + headers={"Content-Type": "application/json"}, + ) + assert response.json == {"barcodes_group": {"barcodes": ["HT-1-9", "HT-1-10"], "id": 4}} + assert response.status_code == HTTPStatus.CREATED + + +def test_new_child_barcode_invalid_parent_can_create_unattributed_children(client): + response = client.post( + "/child-barcodes/HT/new", + data=json.dumps({"barcode": "HT1-1", "count": 3}), + headers={"Content-Type": "application/json"}, + ) + assert response.json == {"barcodes_group": {"barcodes": ["HT-111111", "HT-111112", "HT-111113"], "id": 1}} + assert response.status_code == HTTPStatus.CREATED + + +def test_new_child_barcode_invalid_parent_can_create_unattributed_children_several_times(client): + response = client.post( + "/child-barcodes/HT/new", + data=json.dumps({"barcode": "HT1-1", "count": 3}), + headers={"Content-Type": "application/json"}, + ) + assert response.json == {"barcodes_group": {"barcodes": ["HT-111111", "HT-111112", "HT-111113"], "id": 1}} + assert response.status_code == HTTPStatus.CREATED + + response = client.post( + "/child-barcodes/HT/new", + data=json.dumps({"barcode": "HT1-1", "count": 3}), + headers={"Content-Type": "application/json"}, + ) + assert response.json == {"barcodes_group": {"barcodes": ["HT-111114", "HT-111115", "HT-111116"], "id": 2}} + assert response.status_code == HTTPStatus.CREATED + + +def test_new_child_barcode_unattributed_children_of_invalid_parent_can_start_own_lineage(client): + # invalid parent + response = client.post( + "/child-barcodes/HT/new", + data=json.dumps({"barcode": "HT1-1", "count": 3}), + headers={"Content-Type": "application/json"}, + ) + assert response.json == {"barcodes_group": {"barcodes": ["HT-111111", "HT-111112", "HT-111113"], "id": 1}} + assert response.status_code == HTTPStatus.CREATED + + # unattributed_children + response = client.post( + "/child-barcodes/HT/new", + data=json.dumps({"barcode": "HT-111111", "count": 2}), + headers={"Content-Type": "application/json"}, + ) + assert response.json == {"barcodes_group": {"barcodes": ["HT-111111-1", "HT-111111-2"], "id": 2}} + assert response.status_code == HTTPStatus.CREATED + + # new lineage + response = client.post( + "/child-barcodes/HT/new", + data=json.dumps({"barcode": "HT-111111-2", "count": 2}), + headers={"Content-Type": "application/json"}, + ) + assert response.json == {"barcodes_group": {"barcodes": ["HT-111111-3", "HT-111111-4"], "id": 3}} + assert response.status_code == HTTPStatus.CREATED + + +def test_new_child_barcode_impostor_children_with_possible_parent_can_be_stopped(client): + # invalid parent + response = client.post( + "/child-barcodes/HT/new", + data=json.dumps({"barcode": "HT-1", "count": 2}), + headers={"Content-Type": "application/json"}, + ) + assert response.json == {"barcodes_group": {"barcodes": ["HT-1-1", "HT-1-2"], "id": 1}} + assert response.status_code == HTTPStatus.CREATED + + # Hacking children + response = client.post( + "/child-barcodes/HT/new", + data=json.dumps({"barcode": "HT-1-3", "count": 1}), + headers={"Content-Type": "application/json"}, + ) + assert response.json == {"errors": ["InvalidParentBarcode"]} + assert response.status_code == HTTPStatus.INTERNAL_SERVER_ERROR + + +def test_new_child_barcode_impostor_children_without_possible_parent_can_be_stopped(client): + # Hacking children + response = client.post( + "/child-barcodes/HT/new", + data=json.dumps({"barcode": "HT-1-3", "count": 1}), + headers={"Content-Type": "application/json"}, + ) + assert response.json == {"errors": ["InvalidParentBarcode"]} + assert response.status_code == HTTPStatus.INTERNAL_SERVER_ERROR + + +def test_new_child_barcode_children_of_invalid_parent_can_create_children(client): + response = client.post( + "/child-barcodes/HT/new", + data=json.dumps({"barcode": "HT1-1", "count": 3}), + headers={"Content-Type": "application/json"}, + ) + assert response.json == {"barcodes_group": {"barcodes": ["HT-111111", "HT-111112", "HT-111113"], "id": 1}} + assert response.status_code == HTTPStatus.CREATED + + response = client.post( + "/child-barcodes/HT/new", + data=json.dumps({"barcode": "HT-111111", "count": 3}), + headers={"Content-Type": "application/json"}, + ) + assert response.json == {"barcodes_group": {"barcodes": ["HT-111111-1", "HT-111111-2", "HT-111111-3"], "id": 2}} + assert response.status_code == HTTPStatus.CREATED + + +def test_new_child_barcode_with_unknown_prefix_rejects_request(client): + response = client.post( + "/child-barcodes/unknown/new", + data=json.dumps({"barcode": "SANG-1", "count": 3}), + headers={"Content-Type": "application/json"}, + ) + assert response.json == {"errors": ["InvalidPrefixError"]} + assert response.status_code == HTTPStatus.INTERNAL_SERVER_ERROR + + def test_no_child_barcode(client): - response = client.post("/child-barcodes/new", data=json.dumps({}), headers={"Content-Type": "application/json"}) + response = client.post( + "/child-barcodes/test/new", data=json.dumps({}), headers={"Content-Type": "application/json"} + ) assert response.json == {"errors": ["InvalidBarcodeError"]} assert response.status_code == HTTPStatus.UNPROCESSABLE_ENTITY def test_bad_child_barcode(client): response = client.post( - "/child-barcodes/new", data=json.dumps({"barcode": " "}), headers={"Content-Type": "application/json"} + "/child-barcodes/test/new", data=json.dumps({"barcode": " "}), headers={"Content-Type": "application/json"} ) assert response.json == {"errors": ["InvalidBarcodeError"]} assert response.status_code == HTTPStatus.UNPROCESSABLE_ENTITY @@ -39,9 +272,35 @@ def test_bad_child_barcode(client): def test_bad_count(client): response = client.post( - "/child-barcodes/new", + "/child-barcodes/test/new", data=json.dumps({"barcode": "test", "count": 0}), headers={"Content-Type": "application/json"}, ) assert response.json == {"errors": ["InvalidCountError"]} assert response.status_code == HTTPStatus.UNPROCESSABLE_ENTITY + + +# def test_child_barcodes_use_previous_parent_when_present_allowed(app): +# with app.app_context(): +# # Create a barcode record in the database +# create_child_barcodes("SQPD-1234", 5) +# # Expect child barcode to have correct when suffix when same barcode is used +# assert create_child_barcodes("SQPD-1234", 1) == ["SQPD-1234-6"] +# assert create_child_barcodes("DN1234", 2) == ["DN1234-1", "DN1234-2"] + + +# def test_child_barcodes_use_child_format_when_parent_was_a_child(app): +# with app.app_context(): +# # Create a barcode record in the database +# create_child_barcodes("SQPD-1234", 5) +# # Expect child barcode to have correct when suffix when same barcode is used +# expected_child_barcodes = ["SQPD-1234-6"] +# assert create_child_barcodes("SQPD-1234-5", 1) == expected_child_barcodes + +# def test_child_barcodes_use_standard_format_when_parent_not_allowed(app): +# with app.app_context(): +# # Create a barcode record in the database +# create_child_barcodes("SQPD-1234", 5) +# # Expect child barcode to have correct when suffix when same barcode is used +# expected_child_barcodes = ["SQPD-1"] +# assert create_child_barcodes("AA-12345", 1) == expected_child_barcodes diff --git a/tests/test_operations.py b/tests/test_operations.py index 160bf8c..7f8cbc0 100644 --- a/tests/test_operations.py +++ b/tests/test_operations.py @@ -2,7 +2,7 @@ from baracoda.exceptions import InvalidPrefixError from baracoda.helpers import get_prefix_item -from baracoda.operations import BarcodeOperations, create_child_barcodes +from baracoda.operations import BarcodeOperations, InvalidParentBarcode # BarcodeOperations @@ -43,20 +43,63 @@ def test_error_is_raised_if_prefix_is_not_valid(app): def test_child_barcodes_are_created_when_new_barcode(app): with app.app_context(): - expected_child_barcodes = ["test-1"] - assert create_child_barcodes("test", 1) == expected_child_barcodes + barcode_operations = BarcodeOperations(prefix="SQPD") + expected_child_barcodes = ["SQPD-1"] + assert barcode_operations.create_child_barcodes("SQPD", 1) == expected_child_barcodes def test_child_barcodes_are_created_when_existing_barcode(app): with app.app_context(): # Create a barcode record in the database - create_child_barcodes("test", 5) + barcode_operations = BarcodeOperations(prefix="SQPD") + barcode_operations.create_child_barcodes("SQPD", 5) # Expect child barcode to have correct when suffix when same barcode is used - expected_child_barcodes = ["test-6"] - assert create_child_barcodes("test", 1) == expected_child_barcodes + expected_child_barcodes = ["SQPD-6"] + assert barcode_operations.create_child_barcodes("SQPD", 1) == expected_child_barcodes def test_correct_number_of_child_barcodes_are_created(app): with app.app_context(): - expected_child_barcodes = ["test-1", "test-2", "test-3"] - assert create_child_barcodes("test", 3) == expected_child_barcodes + barcode_operations = BarcodeOperations(prefix="SQPD") + expected_child_barcodes = ["SQPD-1", "SQPD-2", "SQPD-3"] + assert barcode_operations.create_child_barcodes("SQPD", 3) == expected_child_barcodes + + +def test_is_valid_parent_barcode(app): + with app.app_context(): + barcode_operations = BarcodeOperations(prefix="HT") + assert barcode_operations.is_valid_parent_barcode("HT-1234") == True + assert barcode_operations.is_valid_parent_barcode("SQPD-1234") == False + assert barcode_operations.is_valid_parent_barcode("HT-1234-1") == True + assert barcode_operations.is_valid_parent_barcode("HT-1234-1-1") == False + assert barcode_operations.is_valid_parent_barcode("HT-1234-1-1-1") == False + assert barcode_operations.is_valid_parent_barcode("HT1234") == False + assert barcode_operations.is_valid_parent_barcode("HT") == False + assert barcode_operations.is_valid_parent_barcode("HT-") == False + assert barcode_operations.is_valid_parent_barcode("") == False + + +def test_validate_barcode_parent_information(app): + with app.app_context(): + barcode_operations = BarcodeOperations(prefix="SQPD") + with pytest.raises(InvalidParentBarcode): + barcode_operations.validate_barcode_parent_information({"parent_barcode": "SQPD-1", "child": "1"}) + + +def test_extract_barcode_parent_information(app): + with app.app_context(): + barcode_operations = BarcodeOperations(prefix="SQPD") + + assert barcode_operations.extract_barcode_parent_information("SQPD-1") == { + "parent_barcode": "SQPD-1", + "child": None, + } + + assert barcode_operations.extract_barcode_parent_information("SQPD-11-22") == { + "parent_barcode": "SQPD-11", + "child": "22", + } + + assert barcode_operations.extract_barcode_parent_information("SQPD-11-22-33") == None + + assert barcode_operations.extract_barcode_parent_information("DN-11-22") == None From 79be4fb569b78deeec809fb66b927fe16405ea03 Mon Sep 17 00:00:00 2001 From: Eduardo Martin Rojo Date: Thu, 12 May 2022 00:09:42 +0100 Subject: [PATCH 15/33] Linting --- tests/test_operations.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/test_operations.py b/tests/test_operations.py index 7f8cbc0..b3971a2 100644 --- a/tests/test_operations.py +++ b/tests/test_operations.py @@ -68,15 +68,15 @@ def test_correct_number_of_child_barcodes_are_created(app): def test_is_valid_parent_barcode(app): with app.app_context(): barcode_operations = BarcodeOperations(prefix="HT") - assert barcode_operations.is_valid_parent_barcode("HT-1234") == True - assert barcode_operations.is_valid_parent_barcode("SQPD-1234") == False - assert barcode_operations.is_valid_parent_barcode("HT-1234-1") == True - assert barcode_operations.is_valid_parent_barcode("HT-1234-1-1") == False - assert barcode_operations.is_valid_parent_barcode("HT-1234-1-1-1") == False - assert barcode_operations.is_valid_parent_barcode("HT1234") == False - assert barcode_operations.is_valid_parent_barcode("HT") == False - assert barcode_operations.is_valid_parent_barcode("HT-") == False - assert barcode_operations.is_valid_parent_barcode("") == False + assert barcode_operations.is_valid_parent_barcode("HT-1234") is True + assert barcode_operations.is_valid_parent_barcode("SQPD-1234") is False + assert barcode_operations.is_valid_parent_barcode("HT-1234-1") is True + assert barcode_operations.is_valid_parent_barcode("HT-1234-1-1") is False + assert barcode_operations.is_valid_parent_barcode("HT-1234-1-1-1") is False + assert barcode_operations.is_valid_parent_barcode("HT1234") is False + assert barcode_operations.is_valid_parent_barcode("HT") is False + assert barcode_operations.is_valid_parent_barcode("HT-") is False + assert barcode_operations.is_valid_parent_barcode("") is False def test_validate_barcode_parent_information(app): @@ -100,6 +100,6 @@ def test_extract_barcode_parent_information(app): "child": "22", } - assert barcode_operations.extract_barcode_parent_information("SQPD-11-22-33") == None + assert barcode_operations.extract_barcode_parent_information("SQPD-11-22-33") is None - assert barcode_operations.extract_barcode_parent_information("DN-11-22") == None + assert barcode_operations.extract_barcode_parent_information("DN-11-22") is None From 0af843d391ae488f0040f5a90d80939fb8050f44 Mon Sep 17 00:00:00 2001 From: Eduardo Martin Rojo Date: Thu, 12 May 2022 17:01:28 +0100 Subject: [PATCH 16/33] Support config for enable childre prefix --- baracoda/child_barcodes.py | 5 +- baracoda/operations.py | 13 +- tests/conftest.py | 12 ++ tests/test_child_barcodes.py | 267 ++++++++++++++++++++--------------- 4 files changed, 179 insertions(+), 118 deletions(-) diff --git a/baracoda/child_barcodes.py b/baracoda/child_barcodes.py index 7eb415f..3de520f 100644 --- a/baracoda/child_barcodes.py +++ b/baracoda/child_barcodes.py @@ -6,7 +6,7 @@ from flask_cors import CORS # from baracoda.operations import create_child_barcodes -from baracoda.operations import BarcodeOperations, InvalidParentBarcode +from baracoda.operations import BarcodeOperations, InvalidParentBarcode, InvalidPrefixForChildrenCreation from baracoda.exceptions import InvalidCountError, InvalidBarcodeError bp = Blueprint("child_barcode_creation", __name__) @@ -28,6 +28,7 @@ def new_child_barcodes(prefix: str) -> Tuple[Any, int]: if not operator.is_valid_parent_barcode(barcode): barcode_group = operator.create_barcode_group(count) else: + operator.validate_prefix_for_child_creation() info = operator.extract_barcode_parent_information(barcode) operator.validate_barcode_parent_information(info) barcode_group = operator.create_children_barcode_group(info["parent_barcode"], count) @@ -44,6 +45,8 @@ def new_child_barcodes(prefix: str) -> Tuple[Any, int]: return {"errors": [f"{type(e).__name__}"]}, HTTPStatus.INTERNAL_SERVER_ERROR except InvalidParentBarcode as e: return {"errors": [f"{type(e).__name__}"]}, HTTPStatus.INTERNAL_SERVER_ERROR + except InvalidPrefixForChildrenCreation as e: + return {"errors": [f"{type(e).__name__}"]}, HTTPStatus.INTERNAL_SERVER_ERROR except Exception as e: return {"errors": [f"{type(e).__name__}"]}, HTTPStatus.INTERNAL_SERVER_ERROR diff --git a/baracoda/operations.py b/baracoda/operations.py index 5315e82..3143cab 100644 --- a/baracoda/operations.py +++ b/baracoda/operations.py @@ -3,6 +3,7 @@ from datetime import datetime from typing import List, Optional, cast, Dict from xmlrpc.client import Boolean +from baracoda.constants import CONFIG_ENABLE_CHILDREN_CREATION, CONFIG_SEQUENCE_NAME from baracoda.db import db from baracoda.exceptions import InvalidPrefixError from baracoda.helpers import get_prefix_item @@ -19,6 +20,10 @@ class InvalidParentBarcode(BaseException): pass +class InvalidPrefixForChildrenCreation(BaseException): + pass + + class BarcodeOperations: def __init__(self, prefix: str): logger.debug("Instantiate....") @@ -36,10 +41,10 @@ def __init__(self, prefix: str): # saves pulling it out of object every time logger.debug("Accessing sequence_name") - self.sequence_name = self.prefix_item["sequence_name"] + self.sequence_name = self.prefix_item[CONFIG_SEQUENCE_NAME] def formatter(self) -> FormatterInterface: - formatter_class = cast(PrefixesType, self.prefix_item)["formatter_class"] + formatter_class = cast(PrefixesType, self.prefix_item)[CONFIG_FORMATTER_CLASS] return formatter_class(self.prefix) def create_barcodes(self, count: int) -> List[str]: @@ -221,6 +226,10 @@ def extract_barcode_parent_information(self, barcode): "child": found.group("child"), } + def validate_prefix_for_child_creation(self) -> None: + if not self.prefix_item.get(CONFIG_ENABLE_CHILDREN_CREATION): + raise InvalidPrefixForChildrenCreation() + def validate_barcode_parent_information(self, info: Dict[str, str]) -> None: if not info: raise InvalidParentBarcode("The barcode provided is not valid for generating child barcodes") diff --git a/tests/conftest.py b/tests/conftest.py index 97dffcc..6e336f6 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -3,8 +3,11 @@ from baracoda import create_app from baracoda.db import db, reset_db from baracoda.formats import HeronCogUkIdFormatter +from baracoda.helpers import get_prefix_item +from baracoda.constants import ENABLE_CHILDREN_CREATION from tests.data.fixture_data import PREFIXES +from unittest.mock import patch @pytest.fixture @@ -46,3 +49,12 @@ def heron_formatter(): @pytest.fixture def prefixes(): return PREFIXES + + +@pytest.fixture +def enable_children_for_prefix(app, prefix): + with app.app_context(): + item = get_prefix_item(prefix) + item[ENABLE_CHILDREN_CREATION] = True + with patch("baracoda.operations.get_prefix_item", return_value=item): + yield diff --git a/tests/test_child_barcodes.py b/tests/test_child_barcodes.py index adc5fb5..21d9a7e 100644 --- a/tests/test_child_barcodes.py +++ b/tests/test_child_barcodes.py @@ -1,250 +1,310 @@ from http import HTTPStatus import json +import pytest -# sequences -# starts at 1 for sqp +CHILD_BARCODE_PREFIXES = ["SQPD"] -def test_new_child_barcode_can_create_single_barcode(client): +@pytest.mark.parametrize("prefix", CHILD_BARCODE_PREFIXES) +def test_new_child_barcode_can_create_single_barcode(client, prefix, enable_children_for_prefix): response = client.post( - "/child-barcodes/HT/new", data=json.dumps({"barcode": "HT-1"}), headers={"Content-Type": "application/json"} + f"/child-barcodes/{ prefix }/new", + data=json.dumps({"barcode": f"{ prefix }-1"}), + headers={"Content-Type": "application/json"}, ) - assert response.json == {"barcodes_group": {"id": 1, "barcodes": ["HT-1-1"]}} + assert response.json == {"barcodes_group": {"id": 1, "barcodes": [f"{ prefix }-1-1"]}} assert response.status_code == HTTPStatus.CREATED -def test_new_child_barcode_can_create_several_barcodes(client): +@pytest.mark.parametrize("prefix", CHILD_BARCODE_PREFIXES) +def test_new_child_barcode_can_create_several_barcodes(client, prefix, enable_children_for_prefix): response = client.post( - "/child-barcodes/HT/new", - data=json.dumps({"barcode": "HT-1", "count": 3}), + f"/child-barcodes/{ prefix }/new", + data=json.dumps({"barcode": f"{ prefix }-1", "count": 3}), headers={"Content-Type": "application/json"}, ) - assert response.json == {"barcodes_group": {"id": 1, "barcodes": ["HT-1-1", "HT-1-2", "HT-1-3"]}} + assert response.json == { + "barcodes_group": {"id": 1, "barcodes": [f"{ prefix }-1-1", f"{ prefix }-1-2", f"{ prefix }-1-3"]} + } assert response.status_code == HTTPStatus.CREATED -def test_new_child_barcode_different_parent_keep_their_own_counting_of_children(client): +@pytest.mark.parametrize("prefix", CHILD_BARCODE_PREFIXES) +def test_new_child_barcode_different_parent_keep_their_own_counting_of_children( + client, prefix, enable_children_for_prefix +): # Parent 1 response = client.post( - "/child-barcodes/HT/new", - data=json.dumps({"barcode": "HT-1", "count": 3}), + f"/child-barcodes/{ prefix }/new", + data=json.dumps({"barcode": f"{ prefix }-1", "count": 3}), headers={"Content-Type": "application/json"}, ) - assert response.json == {"barcodes_group": {"id": 1, "barcodes": ["HT-1-1", "HT-1-2", "HT-1-3"]}} + assert response.json == { + "barcodes_group": {"id": 1, "barcodes": [f"{ prefix }-1-1", f"{ prefix }-1-2", f"{ prefix }-1-3"]} + } assert response.status_code == HTTPStatus.CREATED # Parent 2 response = client.post( - "/child-barcodes/HT/new", - data=json.dumps({"barcode": "HT-2", "count": 2}), + f"/child-barcodes/{ prefix }/new", + data=json.dumps({"barcode": f"{ prefix }-2", "count": 2}), headers={"Content-Type": "application/json"}, ) - assert response.json == {"barcodes_group": {"id": 2, "barcodes": ["HT-2-1", "HT-2-2"]}} + assert response.json == {"barcodes_group": {"id": 2, "barcodes": [f"{ prefix }-2-1", f"{ prefix }-2-2"]}} assert response.status_code == HTTPStatus.CREATED # Parent 1 again response = client.post( - "/child-barcodes/HT/new", - data=json.dumps({"barcode": "HT-1", "count": 1}), + f"/child-barcodes/{ prefix }/new", + data=json.dumps({"barcode": f"{ prefix }-1", "count": 1}), headers={"Content-Type": "application/json"}, ) - assert response.json == {"barcodes_group": {"id": 3, "barcodes": ["HT-1-4"]}} + assert response.json == {"barcodes_group": {"id": 3, "barcodes": [f"{ prefix }-1-4"]}} assert response.status_code == HTTPStatus.CREATED -def test_new_child_barcode_incorrect_valid_prefixed_parent_can_create_unattributed_children(client): +@pytest.mark.parametrize("prefix", CHILD_BARCODE_PREFIXES) +def test_new_child_barcode_incorrect_valid_prefixed_parent_can_create_unattributed_children( + client, prefix, enable_children_for_prefix +): response = client.post( - "/child-barcodes/HT/new", + f"/child-barcodes/{ prefix }/new", data=json.dumps({"barcode": "SANG-1", "count": 3}), headers={"Content-Type": "application/json"}, ) - assert response.json == {"barcodes_group": {"id": 1, "barcodes": ["HT-111111", "HT-111112", "HT-111113"]}} + assert response.json == { + "barcodes_group": {"id": 1, "barcodes": [f"{ prefix }-1", f"{ prefix }-2", f"{ prefix }-3"]} + } assert response.status_code == HTTPStatus.CREATED -def test_new_child_barcode_invalid_prefixed_parent_can_create_unattributed_children(client): +@pytest.mark.parametrize("prefix", CHILD_BARCODE_PREFIXES) +def test_new_child_barcode_invalid_prefixed_parent_can_create_unattributed_children( + client, prefix, enable_children_for_prefix +): response = client.post( - "/child-barcodes/HT/new", + f"/child-barcodes/{ prefix }/new", data=json.dumps({"barcode": "test-123", "count": 3}), headers={"Content-Type": "application/json"}, ) - assert response.json == {"barcodes_group": {"barcodes": ["HT-111111", "HT-111112", "HT-111113"], "id": 1}} + assert response.json == { + "barcodes_group": {"barcodes": [f"{ prefix }-1", f"{ prefix }-2", f"{ prefix }-3"], "id": 1} + } assert response.status_code == HTTPStatus.CREATED -def test_new_child_barcode_valid_parent_can_create_attributed_children_and_descendants_can_continue_lineage(client): +@pytest.mark.parametrize("prefix", CHILD_BARCODE_PREFIXES) +def test_new_child_barcode_valid_parent_can_create_attributed_children_and_descendants_can_continue_lineage( + client, prefix, enable_children_for_prefix +): response = client.post( - "/child-barcodes/HT/new", - data=json.dumps({"barcode": "HT-1", "count": 3}), + f"/child-barcodes/{ prefix }/new", + data=json.dumps({"barcode": f"{ prefix }-1", "count": 3}), headers={"Content-Type": "application/json"}, ) - assert response.json == {"barcodes_group": {"barcodes": ["HT-1-1", "HT-1-2", "HT-1-3"], "id": 1}} + assert response.json == { + "barcodes_group": {"barcodes": [f"{ prefix }-1-1", f"{ prefix }-1-2", f"{ prefix }-1-3"], "id": 1} + } assert response.status_code == HTTPStatus.CREATED response = client.post( - "/child-barcodes/HT/new", - data=json.dumps({"barcode": "HT-1-1", "count": 3}), + f"/child-barcodes/{ prefix }/new", + data=json.dumps({"barcode": f"{ prefix }-1-1", "count": 3}), headers={"Content-Type": "application/json"}, ) - assert response.json == {"barcodes_group": {"barcodes": ["HT-1-4", "HT-1-5", "HT-1-6"], "id": 2}} + assert response.json == { + "barcodes_group": {"barcodes": [f"{ prefix }-1-4", f"{ prefix }-1-5", f"{ prefix }-1-6"], "id": 2} + } assert response.status_code == HTTPStatus.CREATED response = client.post( - "/child-barcodes/HT/new", - data=json.dumps({"barcode": "HT-1-5", "count": 1}), + f"/child-barcodes/{ prefix }/new", + data=json.dumps({"barcode": f"{ prefix }-1-5", "count": 1}), headers={"Content-Type": "application/json"}, ) - assert response.json == {"barcodes_group": {"barcodes": ["HT-1-7"], "id": 3}} + assert response.json == {"barcodes_group": {"barcodes": [f"{ prefix }-1-7"], "id": 3}} assert response.status_code == HTTPStatus.CREATED +@pytest.mark.parametrize("prefix", CHILD_BARCODE_PREFIXES) def test_new_child_barcode_valid_parent_can_create_attributed_children_and_children_and_parent_can_continue_lineage( - client, + client, prefix ): # Parent response = client.post( - "/child-barcodes/HT/new", - data=json.dumps({"barcode": "HT-1", "count": 3}), + f"/child-barcodes/{ prefix }/new", + data=json.dumps({"barcode": f"{ prefix }-1", "count": 3}), headers={"Content-Type": "application/json"}, ) - assert response.json == {"barcodes_group": {"barcodes": ["HT-1-1", "HT-1-2", "HT-1-3"], "id": 1}} + assert response.json == { + "barcodes_group": {"barcodes": [f"{ prefix }-1-1", f"{ prefix }-1-2", f"{ prefix }-1-3"], "id": 1} + } assert response.status_code == HTTPStatus.CREATED # Children 1 response = client.post( - "/child-barcodes/HT/new", - data=json.dumps({"barcode": "HT-1-1", "count": 3}), + f"/child-barcodes/{ prefix }/new", + data=json.dumps({"barcode": f"{ prefix }-1-1", "count": 3}), headers={"Content-Type": "application/json"}, ) - assert response.json == {"barcodes_group": {"barcodes": ["HT-1-4", "HT-1-5", "HT-1-6"], "id": 2}} + assert response.json == { + "barcodes_group": {"barcodes": [f"{ prefix }-1-4", f"{ prefix }-1-5", f"{ prefix }-1-6"], "id": 2} + } assert response.status_code == HTTPStatus.CREATED # Children 2 response = client.post( - "/child-barcodes/HT/new", - data=json.dumps({"barcode": "HT-1-2", "count": 2}), + f"/child-barcodes/{ prefix }/new", + data=json.dumps({"barcode": f"{ prefix }-1-2", "count": 2}), headers={"Content-Type": "application/json"}, ) - assert response.json == {"barcodes_group": {"barcodes": ["HT-1-7", "HT-1-8"], "id": 3}} + assert response.json == {"barcodes_group": {"barcodes": [f"{ prefix }-1-7", f"{ prefix }-1-8"], "id": 3}} assert response.status_code == HTTPStatus.CREATED # Parent again response = client.post( - "/child-barcodes/HT/new", - data=json.dumps({"barcode": "HT-1", "count": 2}), + f"/child-barcodes/{ prefix }/new", + data=json.dumps({"barcode": f"{ prefix }-1", "count": 2}), headers={"Content-Type": "application/json"}, ) - assert response.json == {"barcodes_group": {"barcodes": ["HT-1-9", "HT-1-10"], "id": 4}} + assert response.json == {"barcodes_group": {"barcodes": [f"{ prefix }-1-9", f"{ prefix }-1-10"], "id": 4}} assert response.status_code == HTTPStatus.CREATED -def test_new_child_barcode_invalid_parent_can_create_unattributed_children(client): +@pytest.mark.parametrize("prefix", CHILD_BARCODE_PREFIXES) +def test_new_child_barcode_invalid_parent_can_create_unattributed_children(client, prefix, enable_children_for_prefix): response = client.post( - "/child-barcodes/HT/new", - data=json.dumps({"barcode": "HT1-1", "count": 3}), + f"/child-barcodes/{ prefix }/new", + data=json.dumps({"barcode": f"{ prefix }1-1", "count": 3}), headers={"Content-Type": "application/json"}, ) - assert response.json == {"barcodes_group": {"barcodes": ["HT-111111", "HT-111112", "HT-111113"], "id": 1}} + assert response.json == { + "barcodes_group": {"barcodes": [f"{ prefix }-1", f"{ prefix }-2", f"{ prefix }-3"], "id": 1} + } assert response.status_code == HTTPStatus.CREATED -def test_new_child_barcode_invalid_parent_can_create_unattributed_children_several_times(client): +@pytest.mark.parametrize("prefix", CHILD_BARCODE_PREFIXES) +def test_new_child_barcode_invalid_parent_can_create_unattributed_children_several_times( + client, prefix, enable_children_for_prefix +): response = client.post( - "/child-barcodes/HT/new", - data=json.dumps({"barcode": "HT1-1", "count": 3}), + f"/child-barcodes/{ prefix }/new", + data=json.dumps({"barcode": f"{ prefix }1-1", "count": 3}), headers={"Content-Type": "application/json"}, ) - assert response.json == {"barcodes_group": {"barcodes": ["HT-111111", "HT-111112", "HT-111113"], "id": 1}} + assert response.json == { + "barcodes_group": {"barcodes": [f"{ prefix }-1", f"{ prefix }-2", f"{ prefix }-3"], "id": 1} + } assert response.status_code == HTTPStatus.CREATED response = client.post( - "/child-barcodes/HT/new", - data=json.dumps({"barcode": "HT1-1", "count": 3}), + f"/child-barcodes/{ prefix }/new", + data=json.dumps({"barcode": f"{ prefix }1-1", "count": 3}), headers={"Content-Type": "application/json"}, ) - assert response.json == {"barcodes_group": {"barcodes": ["HT-111114", "HT-111115", "HT-111116"], "id": 2}} + assert response.json == { + "barcodes_group": {"barcodes": [f"{ prefix }-4", f"{ prefix }-5", f"{ prefix }-6"], "id": 2} + } assert response.status_code == HTTPStatus.CREATED -def test_new_child_barcode_unattributed_children_of_invalid_parent_can_start_own_lineage(client): +@pytest.mark.parametrize("prefix", CHILD_BARCODE_PREFIXES) +def test_new_child_barcode_unattributed_children_of_invalid_parent_can_start_own_lineage( + client, prefix, enable_children_for_prefix +): # invalid parent response = client.post( - "/child-barcodes/HT/new", - data=json.dumps({"barcode": "HT1-1", "count": 3}), + f"/child-barcodes/{ prefix }/new", + data=json.dumps({"barcode": f"{ prefix }1-1", "count": 3}), headers={"Content-Type": "application/json"}, ) - assert response.json == {"barcodes_group": {"barcodes": ["HT-111111", "HT-111112", "HT-111113"], "id": 1}} + assert response.json == { + "barcodes_group": {"barcodes": [f"{ prefix }-1", f"{ prefix }-2", f"{ prefix }-3"], "id": 1} + } assert response.status_code == HTTPStatus.CREATED # unattributed_children response = client.post( - "/child-barcodes/HT/new", - data=json.dumps({"barcode": "HT-111111", "count": 2}), + f"/child-barcodes/{ prefix }/new", + data=json.dumps({"barcode": f"{ prefix }-1", "count": 2}), headers={"Content-Type": "application/json"}, ) - assert response.json == {"barcodes_group": {"barcodes": ["HT-111111-1", "HT-111111-2"], "id": 2}} + assert response.json == {"barcodes_group": {"barcodes": [f"{ prefix }-1-1", f"{ prefix }-1-2"], "id": 2}} assert response.status_code == HTTPStatus.CREATED # new lineage response = client.post( - "/child-barcodes/HT/new", - data=json.dumps({"barcode": "HT-111111-2", "count": 2}), + f"/child-barcodes/{ prefix }/new", + data=json.dumps({"barcode": f"{ prefix }-1-2", "count": 2}), headers={"Content-Type": "application/json"}, ) - assert response.json == {"barcodes_group": {"barcodes": ["HT-111111-3", "HT-111111-4"], "id": 3}} + assert response.json == {"barcodes_group": {"barcodes": [f"{ prefix }-1-3", f"{ prefix }-1-4"], "id": 3}} assert response.status_code == HTTPStatus.CREATED -def test_new_child_barcode_impostor_children_with_possible_parent_can_be_stopped(client): +@pytest.mark.parametrize("prefix", CHILD_BARCODE_PREFIXES) +def test_new_child_barcode_impostor_children_with_possible_parent_can_be_stopped( + client, prefix, enable_children_for_prefix +): # invalid parent response = client.post( - "/child-barcodes/HT/new", - data=json.dumps({"barcode": "HT-1", "count": 2}), + f"/child-barcodes/{ prefix }/new", + data=json.dumps({"barcode": f"{ prefix }-1", "count": 2}), headers={"Content-Type": "application/json"}, ) - assert response.json == {"barcodes_group": {"barcodes": ["HT-1-1", "HT-1-2"], "id": 1}} + assert response.json == {"barcodes_group": {"barcodes": [f"{ prefix }-1-1", f"{ prefix }-1-2"], "id": 1}} assert response.status_code == HTTPStatus.CREATED # Hacking children response = client.post( - "/child-barcodes/HT/new", - data=json.dumps({"barcode": "HT-1-3", "count": 1}), + f"/child-barcodes/{ prefix }/new", + data=json.dumps({"barcode": f"{ prefix }-1-3", "count": 1}), headers={"Content-Type": "application/json"}, ) assert response.json == {"errors": ["InvalidParentBarcode"]} assert response.status_code == HTTPStatus.INTERNAL_SERVER_ERROR -def test_new_child_barcode_impostor_children_without_possible_parent_can_be_stopped(client): +@pytest.mark.parametrize("prefix", CHILD_BARCODE_PREFIXES) +def test_new_child_barcode_impostor_children_without_possible_parent_can_be_stopped( + client, prefix, enable_children_for_prefix +): # Hacking children response = client.post( - "/child-barcodes/HT/new", - data=json.dumps({"barcode": "HT-1-3", "count": 1}), + f"/child-barcodes/{ prefix }/new", + data=json.dumps({"barcode": f"{ prefix }-1-3", "count": 1}), headers={"Content-Type": "application/json"}, ) assert response.json == {"errors": ["InvalidParentBarcode"]} assert response.status_code == HTTPStatus.INTERNAL_SERVER_ERROR -def test_new_child_barcode_children_of_invalid_parent_can_create_children(client): +@pytest.mark.parametrize("prefix", CHILD_BARCODE_PREFIXES) +def test_new_child_barcode_children_of_invalid_parent_can_create_children(client, prefix, enable_children_for_prefix): response = client.post( - "/child-barcodes/HT/new", - data=json.dumps({"barcode": "HT1-1", "count": 3}), + f"/child-barcodes/{ prefix }/new", + data=json.dumps({"barcode": f"{ prefix }1-1", "count": 3}), headers={"Content-Type": "application/json"}, ) - assert response.json == {"barcodes_group": {"barcodes": ["HT-111111", "HT-111112", "HT-111113"], "id": 1}} + assert response.json == { + "barcodes_group": {"barcodes": [f"{ prefix }-1", f"{ prefix }-2", f"{ prefix }-3"], "id": 1} + } assert response.status_code == HTTPStatus.CREATED response = client.post( - "/child-barcodes/HT/new", - data=json.dumps({"barcode": "HT-111111", "count": 3}), + f"/child-barcodes/{ prefix }/new", + data=json.dumps({"barcode": f"{ prefix }-1", "count": 3}), headers={"Content-Type": "application/json"}, ) - assert response.json == {"barcodes_group": {"barcodes": ["HT-111111-1", "HT-111111-2", "HT-111111-3"], "id": 2}} + assert response.json == { + "barcodes_group": {"barcodes": [f"{ prefix }-1-1", f"{ prefix }-1-2", f"{ prefix }-1-3"], "id": 2} + } assert response.status_code == HTTPStatus.CREATED -def test_new_child_barcode_with_unknown_prefix_rejects_request(client): +@pytest.mark.parametrize("prefix", CHILD_BARCODE_PREFIXES) +def test_new_child_barcode_with_unknown_prefix_rejects_request(client, prefix, enable_children_for_prefix): response = client.post( "/child-barcodes/unknown/new", data=json.dumps({"barcode": "SANG-1", "count": 3}), @@ -254,7 +314,8 @@ def test_new_child_barcode_with_unknown_prefix_rejects_request(client): assert response.status_code == HTTPStatus.INTERNAL_SERVER_ERROR -def test_no_child_barcode(client): +@pytest.mark.parametrize("prefix", CHILD_BARCODE_PREFIXES) +def test_no_child_barcode(client, prefix, enable_children_for_prefix): response = client.post( "/child-barcodes/test/new", data=json.dumps({}), headers={"Content-Type": "application/json"} ) @@ -262,7 +323,8 @@ def test_no_child_barcode(client): assert response.status_code == HTTPStatus.UNPROCESSABLE_ENTITY -def test_bad_child_barcode(client): +@pytest.mark.parametrize("prefix", CHILD_BARCODE_PREFIXES) +def test_bad_child_barcode(client, prefix, enable_children_for_prefix): response = client.post( "/child-barcodes/test/new", data=json.dumps({"barcode": " "}), headers={"Content-Type": "application/json"} ) @@ -270,7 +332,8 @@ def test_bad_child_barcode(client): assert response.status_code == HTTPStatus.UNPROCESSABLE_ENTITY -def test_bad_count(client): +@pytest.mark.parametrize("prefix", CHILD_BARCODE_PREFIXES) +def test_bad_count(client, prefix, enable_children_for_prefix): response = client.post( "/child-barcodes/test/new", data=json.dumps({"barcode": "test", "count": 0}), @@ -278,29 +341,3 @@ def test_bad_count(client): ) assert response.json == {"errors": ["InvalidCountError"]} assert response.status_code == HTTPStatus.UNPROCESSABLE_ENTITY - - -# def test_child_barcodes_use_previous_parent_when_present_allowed(app): -# with app.app_context(): -# # Create a barcode record in the database -# create_child_barcodes("SQPD-1234", 5) -# # Expect child barcode to have correct when suffix when same barcode is used -# assert create_child_barcodes("SQPD-1234", 1) == ["SQPD-1234-6"] -# assert create_child_barcodes("DN1234", 2) == ["DN1234-1", "DN1234-2"] - - -# def test_child_barcodes_use_child_format_when_parent_was_a_child(app): -# with app.app_context(): -# # Create a barcode record in the database -# create_child_barcodes("SQPD-1234", 5) -# # Expect child barcode to have correct when suffix when same barcode is used -# expected_child_barcodes = ["SQPD-1234-6"] -# assert create_child_barcodes("SQPD-1234-5", 1) == expected_child_barcodes - -# def test_child_barcodes_use_standard_format_when_parent_not_allowed(app): -# with app.app_context(): -# # Create a barcode record in the database -# create_child_barcodes("SQPD-1234", 5) -# # Expect child barcode to have correct when suffix when same barcode is used -# expected_child_barcodes = ["SQPD-1"] -# assert create_child_barcodes("AA-12345", 1) == expected_child_barcodes From 64c13e3ade74d085ac435e1a860c2c8de2c347ce Mon Sep 17 00:00:00 2001 From: Eduardo Martin Rojo Date: Thu, 12 May 2022 17:02:08 +0100 Subject: [PATCH 17/33] Add constants --- baracoda/constants.py | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 baracoda/constants.py diff --git a/baracoda/constants.py b/baracoda/constants.py new file mode 100644 index 0000000..2084e63 --- /dev/null +++ b/baracoda/constants.py @@ -0,0 +1,7 @@ +from typing import Final + +# Prefix Config keys +CONFIG_ENABLE_CHILDREN_CREATION: str = "enableChildrenCreation" +CONFIG_PREFIX: str = "prefix" +CONFIG_SEQUENCE_NAME: str = "sequence_name" +CONFIG_FORMATTER_CLASS: str = "formatter_class" From 5492d07aa3aec98d8965a23d1d1c16ee06e5e85d Mon Sep 17 00:00:00 2001 From: Eduardo Martin Rojo Date: Thu, 12 May 2022 17:31:42 +0100 Subject: [PATCH 18/33] Added config setting for enable children creation --- baracoda/config/defaults.py | 343 ++++++++++++++++++++++++++++----- baracoda/config/development.py | 18 +- baracoda/constants.py | 7 - baracoda/operations.py | 7 +- baracoda/types.py | 5 +- tests/conftest.py | 12 -- tests/data/fixture_data.py | 7 + tests/test_child_barcodes.py | 46 ++--- tests/test_helpers.py | 7 +- 9 files changed, 346 insertions(+), 106 deletions(-) delete mode 100644 baracoda/constants.py diff --git a/baracoda/config/defaults.py b/baracoda/config/defaults.py index fc9114c..d34db25 100644 --- a/baracoda/config/defaults.py +++ b/baracoda/config/defaults.py @@ -30,55 +30,300 @@ ### PREFIXES: List[PrefixesType] = [ - {"prefix": "ALDP", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, - {"prefix": "BHRT", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, - {"prefix": "BIRM", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, - {"prefix": "BRBR", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, - {"prefix": "BRIG", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, - {"prefix": "BRIS", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, - {"prefix": "CAMB", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, - {"prefix": "CAMC", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, - {"prefix": "CPTD", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, - {"prefix": "CWAR", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, - {"prefix": "EDIN", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, - {"prefix": "EKHU", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, - {"prefix": "EXET", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, - {"prefix": "GCVR", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, - {"prefix": "GLOU", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, - {"prefix": "GSTT", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, - {"prefix": "HECH", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, - {"prefix": "HSLL", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, - {"prefix": "KGHT", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, - {"prefix": "LCST", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, - {"prefix": "LEED", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, - {"prefix": "LIVE", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, - {"prefix": "LOND", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, - {"prefix": "LSPA", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, - {"prefix": "MILK", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, - {"prefix": "MTUN", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, - {"prefix": "NEWC", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, - {"prefix": "NIRE", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, - {"prefix": "NORT", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, - {"prefix": "NORW", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, - {"prefix": "NOTT", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, - {"prefix": "NWGH", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, - {"prefix": "OXON", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, - {"prefix": "PAHT", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, - {"prefix": "PHEC", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, - {"prefix": "PHWC", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, - {"prefix": "PLYM", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, - {"prefix": "PORT", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, - {"prefix": "PRIN", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, - {"prefix": "QEUH", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, - {"prefix": "RAND", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, - {"prefix": "RSCH", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, - {"prefix": "SANG", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, - {"prefix": "SHEF", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, - {"prefix": "TBSD", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, - {"prefix": "TFCI", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, - {"prefix": "WAHH", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, - {"prefix": "WSFT", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter}, - {"prefix": "HT", "sequence_name": "ht", "formatter_class": GenericBarcodeFormatter}, + { + "prefix": "ALDP", + "sequence_name": "heron", + "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, + }, + { + "prefix": "BHRT", + "sequence_name": "heron", + "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, + }, + { + "prefix": "BIRM", + "sequence_name": "heron", + "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, + }, + { + "prefix": "BRBR", + "sequence_name": "heron", + "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, + }, + { + "prefix": "BRIG", + "sequence_name": "heron", + "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, + }, + { + "prefix": "BRIS", + "sequence_name": "heron", + "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, + }, + { + "prefix": "CAMB", + "sequence_name": "heron", + "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, + }, + { + "prefix": "CAMC", + "sequence_name": "heron", + "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, + }, + { + "prefix": "CPTD", + "sequence_name": "heron", + "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, + }, + { + "prefix": "CWAR", + "sequence_name": "heron", + "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, + }, + { + "prefix": "EDIN", + "sequence_name": "heron", + "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, + }, + { + "prefix": "EKHU", + "sequence_name": "heron", + "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, + }, + { + "prefix": "EXET", + "sequence_name": "heron", + "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, + }, + { + "prefix": "GCVR", + "sequence_name": "heron", + "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, + }, + { + "prefix": "GLOU", + "sequence_name": "heron", + "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, + }, + { + "prefix": "GSTT", + "sequence_name": "heron", + "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, + }, + { + "prefix": "HECH", + "sequence_name": "heron", + "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, + }, + { + "prefix": "HSLL", + "sequence_name": "heron", + "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, + }, + { + "prefix": "KGHT", + "sequence_name": "heron", + "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, + }, + { + "prefix": "LCST", + "sequence_name": "heron", + "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, + }, + { + "prefix": "LEED", + "sequence_name": "heron", + "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, + }, + { + "prefix": "LIVE", + "sequence_name": "heron", + "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, + }, + { + "prefix": "LOND", + "sequence_name": "heron", + "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, + }, + { + "prefix": "LSPA", + "sequence_name": "heron", + "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, + }, + { + "prefix": "MILK", + "sequence_name": "heron", + "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, + }, + { + "prefix": "MTUN", + "sequence_name": "heron", + "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, + }, + { + "prefix": "NEWC", + "sequence_name": "heron", + "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, + }, + { + "prefix": "NIRE", + "sequence_name": "heron", + "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, + }, + { + "prefix": "NORT", + "sequence_name": "heron", + "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, + }, + { + "prefix": "NORW", + "sequence_name": "heron", + "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, + }, + { + "prefix": "NOTT", + "sequence_name": "heron", + "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, + }, + { + "prefix": "NWGH", + "sequence_name": "heron", + "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, + }, + { + "prefix": "OXON", + "sequence_name": "heron", + "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, + }, + { + "prefix": "PAHT", + "sequence_name": "heron", + "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, + }, + { + "prefix": "PHEC", + "sequence_name": "heron", + "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, + }, + { + "prefix": "PHWC", + "sequence_name": "heron", + "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, + }, + { + "prefix": "PLYM", + "sequence_name": "heron", + "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, + }, + { + "prefix": "PORT", + "sequence_name": "heron", + "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, + }, + { + "prefix": "PRIN", + "sequence_name": "heron", + "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, + }, + { + "prefix": "QEUH", + "sequence_name": "heron", + "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, + }, + { + "prefix": "RAND", + "sequence_name": "heron", + "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, + }, + { + "prefix": "RSCH", + "sequence_name": "heron", + "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, + }, + { + "prefix": "SANG", + "sequence_name": "heron", + "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, + }, + { + "prefix": "SHEF", + "sequence_name": "heron", + "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, + }, + { + "prefix": "TBSD", + "sequence_name": "heron", + "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, + }, + { + "prefix": "TFCI", + "sequence_name": "heron", + "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, + }, + { + "prefix": "WAHH", + "sequence_name": "heron", + "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, + }, + { + "prefix": "WSFT", + "sequence_name": "heron", + "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, + }, + { + "prefix": "HT", + "sequence_name": "ht", + "formatter_class": GenericBarcodeFormatter, + "enableChildrenCreation": False, + }, ] for prefix_item in PREFIXES: diff --git a/baracoda/config/development.py b/baracoda/config/development.py index 598bb98..c88a224 100644 --- a/baracoda/config/development.py +++ b/baracoda/config/development.py @@ -3,10 +3,24 @@ from baracoda.formats import GenericBarcodeFormatter # Adds a development prefix for sqp sequence -PREFIXES.append({"prefix": "SQPD", "sequence_name": "sqp", "formatter_class": GenericBarcodeFormatter}) +PREFIXES.append( + { + "prefix": "SQPD", + "sequence_name": "sqp", + "formatter_class": GenericBarcodeFormatter, + "enableChildrenCreation": True, + } +) # settings here overwrite those in 'defaults.py' # adding an additional barcode prefix for use in source plate data setup # for beckman and biosero, sharing the ht sequence -PREFIXES.append({"prefix": "TEST", "sequence_name": "ht", "formatter_class": GenericBarcodeFormatter}) +PREFIXES.append( + { + "prefix": "TEST", + "sequence_name": "ht", + "formatter_class": GenericBarcodeFormatter, + "enableChildrenCreation": True, + } +) diff --git a/baracoda/constants.py b/baracoda/constants.py deleted file mode 100644 index 2084e63..0000000 --- a/baracoda/constants.py +++ /dev/null @@ -1,7 +0,0 @@ -from typing import Final - -# Prefix Config keys -CONFIG_ENABLE_CHILDREN_CREATION: str = "enableChildrenCreation" -CONFIG_PREFIX: str = "prefix" -CONFIG_SEQUENCE_NAME: str = "sequence_name" -CONFIG_FORMATTER_CLASS: str = "formatter_class" diff --git a/baracoda/operations.py b/baracoda/operations.py index 3143cab..afe5ae0 100644 --- a/baracoda/operations.py +++ b/baracoda/operations.py @@ -3,7 +3,6 @@ from datetime import datetime from typing import List, Optional, cast, Dict from xmlrpc.client import Boolean -from baracoda.constants import CONFIG_ENABLE_CHILDREN_CREATION, CONFIG_SEQUENCE_NAME from baracoda.db import db from baracoda.exceptions import InvalidPrefixError from baracoda.helpers import get_prefix_item @@ -41,10 +40,10 @@ def __init__(self, prefix: str): # saves pulling it out of object every time logger.debug("Accessing sequence_name") - self.sequence_name = self.prefix_item[CONFIG_SEQUENCE_NAME] + self.sequence_name = self.prefix_item["sequence_name"] def formatter(self) -> FormatterInterface: - formatter_class = cast(PrefixesType, self.prefix_item)[CONFIG_FORMATTER_CLASS] + formatter_class = cast(PrefixesType, self.prefix_item)["formatter_class"] return formatter_class(self.prefix) def create_barcodes(self, count: int) -> List[str]: @@ -227,7 +226,7 @@ def extract_barcode_parent_information(self, barcode): } def validate_prefix_for_child_creation(self) -> None: - if not self.prefix_item.get(CONFIG_ENABLE_CHILDREN_CREATION): + if not cast(PrefixesType, self.prefix_item)["enableChildrenCreation"]: raise InvalidPrefixForChildrenCreation() def validate_barcode_parent_information(self, info: Dict[str, str]) -> None: diff --git a/baracoda/types.py b/baracoda/types.py index 2feb663..cdf2550 100644 --- a/baracoda/types.py +++ b/baracoda/types.py @@ -3,7 +3,10 @@ FormatterClassType = Union[Type[HeronCogUkIdFormatter], Type[GenericBarcodeFormatter]] -PrefixesType = TypedDict("PrefixesType", {"prefix": str, "sequence_name": str, "formatter_class": FormatterClassType}) +PrefixesType = TypedDict( + "PrefixesType", + {"prefix": str, "sequence_name": str, "formatter_class": FormatterClassType, "enableChildrenCreation": bool}, +) FormatterInterfaceType = List[PrefixesType] diff --git a/tests/conftest.py b/tests/conftest.py index 6e336f6..97dffcc 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -3,11 +3,8 @@ from baracoda import create_app from baracoda.db import db, reset_db from baracoda.formats import HeronCogUkIdFormatter -from baracoda.helpers import get_prefix_item -from baracoda.constants import ENABLE_CHILDREN_CREATION from tests.data.fixture_data import PREFIXES -from unittest.mock import patch @pytest.fixture @@ -49,12 +46,3 @@ def heron_formatter(): @pytest.fixture def prefixes(): return PREFIXES - - -@pytest.fixture -def enable_children_for_prefix(app, prefix): - with app.app_context(): - item = get_prefix_item(prefix) - item[ENABLE_CHILDREN_CREATION] = True - with patch("baracoda.operations.get_prefix_item", return_value=item): - yield diff --git a/tests/data/fixture_data.py b/tests/data/fixture_data.py index 87ae614..fff82a9 100644 --- a/tests/data/fixture_data.py +++ b/tests/data/fixture_data.py @@ -6,35 +6,42 @@ "prefix": "SANG", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, }, { "prefix": "CAMB", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, }, { "prefix": "NORW", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, }, { "prefix": "NOTT", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, }, { "prefix": "LEED", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, }, { "prefix": "HT", "sequence_name": "ht", "formatter_class": GenericBarcodeFormatter, + "enableChildrenCreation": False, }, { "prefix": "SQPD", "sequence_name": "sqp", "formatter_class": GenericBarcodeFormatter, + "enableChildrenCreation": True, }, ] diff --git a/tests/test_child_barcodes.py b/tests/test_child_barcodes.py index 21d9a7e..6671aa4 100644 --- a/tests/test_child_barcodes.py +++ b/tests/test_child_barcodes.py @@ -6,7 +6,7 @@ @pytest.mark.parametrize("prefix", CHILD_BARCODE_PREFIXES) -def test_new_child_barcode_can_create_single_barcode(client, prefix, enable_children_for_prefix): +def test_new_child_barcode_can_create_single_barcode(client, prefix): response = client.post( f"/child-barcodes/{ prefix }/new", data=json.dumps({"barcode": f"{ prefix }-1"}), @@ -17,7 +17,7 @@ def test_new_child_barcode_can_create_single_barcode(client, prefix, enable_chil @pytest.mark.parametrize("prefix", CHILD_BARCODE_PREFIXES) -def test_new_child_barcode_can_create_several_barcodes(client, prefix, enable_children_for_prefix): +def test_new_child_barcode_can_create_several_barcodes(client, prefix): response = client.post( f"/child-barcodes/{ prefix }/new", data=json.dumps({"barcode": f"{ prefix }-1", "count": 3}), @@ -30,9 +30,7 @@ def test_new_child_barcode_can_create_several_barcodes(client, prefix, enable_ch @pytest.mark.parametrize("prefix", CHILD_BARCODE_PREFIXES) -def test_new_child_barcode_different_parent_keep_their_own_counting_of_children( - client, prefix, enable_children_for_prefix -): +def test_new_child_barcode_different_parent_keep_their_own_counting_of_children(client, prefix): # Parent 1 response = client.post( f"/child-barcodes/{ prefix }/new", @@ -64,9 +62,7 @@ def test_new_child_barcode_different_parent_keep_their_own_counting_of_children( @pytest.mark.parametrize("prefix", CHILD_BARCODE_PREFIXES) -def test_new_child_barcode_incorrect_valid_prefixed_parent_can_create_unattributed_children( - client, prefix, enable_children_for_prefix -): +def test_new_child_barcode_incorrect_valid_prefixed_parent_can_create_unattributed_children(client, prefix): response = client.post( f"/child-barcodes/{ prefix }/new", data=json.dumps({"barcode": "SANG-1", "count": 3}), @@ -79,9 +75,7 @@ def test_new_child_barcode_incorrect_valid_prefixed_parent_can_create_unattribut @pytest.mark.parametrize("prefix", CHILD_BARCODE_PREFIXES) -def test_new_child_barcode_invalid_prefixed_parent_can_create_unattributed_children( - client, prefix, enable_children_for_prefix -): +def test_new_child_barcode_invalid_prefixed_parent_can_create_unattributed_children(client, prefix): response = client.post( f"/child-barcodes/{ prefix }/new", data=json.dumps({"barcode": "test-123", "count": 3}), @@ -95,7 +89,7 @@ def test_new_child_barcode_invalid_prefixed_parent_can_create_unattributed_child @pytest.mark.parametrize("prefix", CHILD_BARCODE_PREFIXES) def test_new_child_barcode_valid_parent_can_create_attributed_children_and_descendants_can_continue_lineage( - client, prefix, enable_children_for_prefix + client, prefix ): response = client.post( f"/child-barcodes/{ prefix }/new", @@ -172,7 +166,7 @@ def test_new_child_barcode_valid_parent_can_create_attributed_children_and_child @pytest.mark.parametrize("prefix", CHILD_BARCODE_PREFIXES) -def test_new_child_barcode_invalid_parent_can_create_unattributed_children(client, prefix, enable_children_for_prefix): +def test_new_child_barcode_invalid_parent_can_create_unattributed_children(client, prefix): response = client.post( f"/child-barcodes/{ prefix }/new", data=json.dumps({"barcode": f"{ prefix }1-1", "count": 3}), @@ -185,9 +179,7 @@ def test_new_child_barcode_invalid_parent_can_create_unattributed_children(clien @pytest.mark.parametrize("prefix", CHILD_BARCODE_PREFIXES) -def test_new_child_barcode_invalid_parent_can_create_unattributed_children_several_times( - client, prefix, enable_children_for_prefix -): +def test_new_child_barcode_invalid_parent_can_create_unattributed_children_several_times(client, prefix): response = client.post( f"/child-barcodes/{ prefix }/new", data=json.dumps({"barcode": f"{ prefix }1-1", "count": 3}), @@ -210,9 +202,7 @@ def test_new_child_barcode_invalid_parent_can_create_unattributed_children_sever @pytest.mark.parametrize("prefix", CHILD_BARCODE_PREFIXES) -def test_new_child_barcode_unattributed_children_of_invalid_parent_can_start_own_lineage( - client, prefix, enable_children_for_prefix -): +def test_new_child_barcode_unattributed_children_of_invalid_parent_can_start_own_lineage(client, prefix): # invalid parent response = client.post( f"/child-barcodes/{ prefix }/new", @@ -244,9 +234,7 @@ def test_new_child_barcode_unattributed_children_of_invalid_parent_can_start_own @pytest.mark.parametrize("prefix", CHILD_BARCODE_PREFIXES) -def test_new_child_barcode_impostor_children_with_possible_parent_can_be_stopped( - client, prefix, enable_children_for_prefix -): +def test_new_child_barcode_impostor_children_with_possible_parent_can_be_stopped(client, prefix): # invalid parent response = client.post( f"/child-barcodes/{ prefix }/new", @@ -267,9 +255,7 @@ def test_new_child_barcode_impostor_children_with_possible_parent_can_be_stopped @pytest.mark.parametrize("prefix", CHILD_BARCODE_PREFIXES) -def test_new_child_barcode_impostor_children_without_possible_parent_can_be_stopped( - client, prefix, enable_children_for_prefix -): +def test_new_child_barcode_impostor_children_without_possible_parent_can_be_stopped(client, prefix): # Hacking children response = client.post( f"/child-barcodes/{ prefix }/new", @@ -281,7 +267,7 @@ def test_new_child_barcode_impostor_children_without_possible_parent_can_be_stop @pytest.mark.parametrize("prefix", CHILD_BARCODE_PREFIXES) -def test_new_child_barcode_children_of_invalid_parent_can_create_children(client, prefix, enable_children_for_prefix): +def test_new_child_barcode_children_of_invalid_parent_can_create_children(client, prefix): response = client.post( f"/child-barcodes/{ prefix }/new", data=json.dumps({"barcode": f"{ prefix }1-1", "count": 3}), @@ -304,7 +290,7 @@ def test_new_child_barcode_children_of_invalid_parent_can_create_children(client @pytest.mark.parametrize("prefix", CHILD_BARCODE_PREFIXES) -def test_new_child_barcode_with_unknown_prefix_rejects_request(client, prefix, enable_children_for_prefix): +def test_new_child_barcode_with_unknown_prefix_rejects_request(client, prefix): response = client.post( "/child-barcodes/unknown/new", data=json.dumps({"barcode": "SANG-1", "count": 3}), @@ -315,7 +301,7 @@ def test_new_child_barcode_with_unknown_prefix_rejects_request(client, prefix, e @pytest.mark.parametrize("prefix", CHILD_BARCODE_PREFIXES) -def test_no_child_barcode(client, prefix, enable_children_for_prefix): +def test_no_child_barcode(client, prefix): response = client.post( "/child-barcodes/test/new", data=json.dumps({}), headers={"Content-Type": "application/json"} ) @@ -324,7 +310,7 @@ def test_no_child_barcode(client, prefix, enable_children_for_prefix): @pytest.mark.parametrize("prefix", CHILD_BARCODE_PREFIXES) -def test_bad_child_barcode(client, prefix, enable_children_for_prefix): +def test_bad_child_barcode(client, prefix): response = client.post( "/child-barcodes/test/new", data=json.dumps({"barcode": " "}), headers={"Content-Type": "application/json"} ) @@ -333,7 +319,7 @@ def test_bad_child_barcode(client, prefix, enable_children_for_prefix): @pytest.mark.parametrize("prefix", CHILD_BARCODE_PREFIXES) -def test_bad_count(client, prefix, enable_children_for_prefix): +def test_bad_count(client, prefix): response = client.post( "/child-barcodes/test/new", data=json.dumps({"barcode": "test", "count": 0}), diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 8decdb1..100a71b 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -5,7 +5,12 @@ def test_correct_prefix_item_is_returned(app, prefixes): with app.app_context(): prefix_item = get_prefix_item("LEED") - assert prefix_item == {"prefix": "LEED", "sequence_name": "heron", "formatter_class": HeronCogUkIdFormatter} + assert prefix_item == { + "prefix": "LEED", + "sequence_name": "heron", + "formatter_class": HeronCogUkIdFormatter, + "enableChildrenCreation": False, + } def test_none_is_returned_for_invalid_prefix(app): From 0020915ba472e6678d8fd165eee507db4c578459 Mon Sep 17 00:00:00 2001 From: Eduardo Martin Rojo Date: Fri, 13 May 2022 10:56:31 +0100 Subject: [PATCH 19/33] Added some comments Reduce level of logs --- baracoda/barcodes.py | 53 ++++++++++++++++++++++++++++++++++++++ baracoda/config/logging.py | 2 +- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/baracoda/barcodes.py b/baracoda/barcodes.py index 69da014..699a784 100644 --- a/baracoda/barcodes.py +++ b/baracoda/barcodes.py @@ -16,6 +16,24 @@ @bp.post("/barcodes_group//new") # type: ignore def get_new_barcode_group(prefix: str) -> Tuple[Any, int]: + """Endpoint that creates a new group of barcodes that are related in one request + + Arguments: + - prefix : str - URL extracted argument, that defines the Prefix to use for + the barcodes generated. It has to be one of the prefixes defined in + baracoda.config PREFIXES variable + - count : str - URL or BODY extracted argument. It represents the number of + barcodes we want to create inside the group. + If specified in URL it can be defined as url parameter: + Eg: /barcodes_group/TEST/new?count=14 + If specified in BODY it has to be defined as jSON: + Eg: { "count": 14 } + Result: + - Success: HTTP 201 with JSON representation of BarcodeGroup instance + - InvalidPrefixError: HTTP 400 with JSON representation of error. + - InvalidCountError: HTTP 422 with JSON representation of error. + - OtherError: HTTP 500 with JSON representation of error. + """ try: logger.debug(f"Creating a barcode group for '{ prefix }'") count = get_count_param() @@ -37,6 +55,17 @@ def get_new_barcode_group(prefix: str) -> Tuple[Any, int]: @bp.post("/barcodes//new") # type: ignore def get_new_barcode(prefix: str) -> Tuple[Any, int]: + """Endpoint that creates one single barcode for a prefix + + Arguments: + - prefix : str - URL extracted argument, that defines the Prefix to use for + the barcode generated. It has to be one of the prefixes defined in + baracoda.config PREFIXES variable + Result: + - Success: HTTP 201 with JSON representation of Barcode instance + - InvalidPrefixError: HTTP 400 with JSON representation of error. + - OtherError: HTTP 500 with JSON representation of error. + """ try: logger.debug(f"Creating a barcode for '{ prefix }'") operator = BarcodeOperations(prefix=prefix) @@ -52,6 +81,18 @@ def get_new_barcode(prefix: str) -> Tuple[Any, int]: @bp.get("/barcodes//last") # type: ignore def get_last_barcode(prefix: str) -> Tuple[Any, int]: + """Endpoint that returns the last generated barcode for a specific prefix + + Arguments: + - prefix : str - URL extracted argument, that defines the Prefix we want + to queryu. It has to be one of the prefixes defined in + baracoda.config PREFIXES variable + Result: + - Success: HTTP 200 with JSON representation of barcode instance + - NotFound: HTTP 404 with empty body + - InvalidPrefixError: HTTP 400 with JSON representation of error. + - OtherError: HTTP 500 with JSON representation of error. + """ try: logger.debug(f"Obtaining last from '{ prefix }'") operator = BarcodeOperations(prefix=prefix) @@ -68,6 +109,18 @@ def get_last_barcode(prefix: str) -> Tuple[Any, int]: def get_count_param(): + """Extracts the count argument from the HTTP request received. + If specified in URL it can be defined as url parameter: + Eg: /barcodes_group/TEST/new?count=14 + If specified in BODY it has to be defined as jSON: + Eg: { "count": 14 } + + Arguments: No + Returns one of this: + int - value of the 'count' argument extracted + InvalidCountError - Exception raised when argument could not be extracted + + """ if "count" in request.values: return int(request.values["count"]) else: diff --git a/baracoda/config/logging.py b/baracoda/config/logging.py index b55c9cd..06cc699 100644 --- a/baracoda/config/logging.py +++ b/baracoda/config/logging.py @@ -50,7 +50,7 @@ "loggers": { "baracoda": { "handlers": ["console", "slack"], - "level": "DEBUG", + "level": "ERROR", "propagate": True, }, }, From 9fb154949488939273a496139a2170d70bd7c400 Mon Sep 17 00:00:00 2001 From: Eduardo Martin Rojo Date: Fri, 13 May 2022 10:57:22 +0100 Subject: [PATCH 20/33] Lint --- baracoda/barcodes.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/baracoda/barcodes.py b/baracoda/barcodes.py index 699a784..684b2b0 100644 --- a/baracoda/barcodes.py +++ b/baracoda/barcodes.py @@ -110,15 +110,15 @@ def get_last_barcode(prefix: str) -> Tuple[Any, int]: def get_count_param(): """Extracts the count argument from the HTTP request received. - If specified in URL it can be defined as url parameter: - Eg: /barcodes_group/TEST/new?count=14 - If specified in BODY it has to be defined as jSON: - Eg: { "count": 14 } - - Arguments: No - Returns one of this: - int - value of the 'count' argument extracted - InvalidCountError - Exception raised when argument could not be extracted + If specified in URL it can be defined as url parameter: + Eg: /barcodes_group/TEST/new?count=14 + If specified in BODY it has to be defined as jSON: + Eg: { "count": 14 } + + Arguments: No + Returns one of this: + int - value of the 'count' argument extracted + InvalidCountError - Exception raised when argument could not be extracted """ if "count" in request.values: From 0df8ee03c816e54cf7d6f41320a486bc1c292bce Mon Sep 17 00:00:00 2001 From: Eduardo Martin Rojo Date: Fri, 13 May 2022 14:42:39 +0100 Subject: [PATCH 21/33] Added more documentation --- README.md | 42 ++++++++++++++++++++++++++++++++++++++ baracoda/child_barcodes.py | 19 +++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/README.md b/README.md index 3d9fc1f..248d22f 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,20 @@ Barcode generation using postgres sequences and pre-defined prefixes. +## Features + +Baracoda is a JSON-based microservice written in Python and backed in a +PostgreSQL database, with the purpose of handling the creation +of new barcodes for the LIMS application supported currently in PSD. + +These are some of the key features currently supported: + +* Creation of single barcodes +* Creation of group of barcodes +* Support for children barcodes creation +* Retrieval of the last barcode created for a prefix +* Support for different barcode formats + ## Table of Contents @@ -13,6 +27,7 @@ Barcode generation using postgres sequences and pre-defined prefixes. - [Requirements for Development](#requirements-for-development) * [Setup Steps](#setup-steps) - [Running](#running) +- [Configuration](#configuration) - [Testing](#testing) * [Testing Requirements](#testing-requirements) * [Running Tests](#running-tests) @@ -156,6 +171,33 @@ The following routes are available from this service: ## Miscellaneous +### Configuration + +The default configuration of the currently supported prefixes is specified in the +```baracoda/config/defaults.py``` module. For example: +```json + { + "prefix": "HT", + "sequence_name": "ht", + "formatter_class": GenericBarcodeFormatter, + "enableChildrenCreation": False, + } +``` +These are the allowed keywords that we can specify to configure a prefix: + +- ```prefix```: This is the string that represents the prefix we are configuring for +supporting new barcodes. +- ```sequence_name```: This is the sequence name in the PostgreSQL database with will +keep record of the last index created for a barcode. Prefixes can share the same +sequence. +- ```formatter_class```: Defines the class that will generate the string that represents +a new barcode by using the prefix and the new value obtained from the sequence. +If we want to support a new formatter class we have to provide a class that implements +the interface ```baracoda.formats.FormatterInterface```. +- ```enableChildrenCreation```: Defines if the prefix has enabled the children creation. +If true, the prefix will support creating barcodes based on a parent barcode +If it is False, the prefix will reject any children creation request for that prefix. + ### Troubleshooting #### Installing psycopg2 diff --git a/baracoda/child_barcodes.py b/baracoda/child_barcodes.py index 3de520f..d900610 100644 --- a/baracoda/child_barcodes.py +++ b/baracoda/child_barcodes.py @@ -17,6 +17,25 @@ @bp.post("/child-barcodes//new") # type: ignore def new_child_barcodes(prefix: str) -> Tuple[Any, int]: + """Endpoint that creates a new group of child barcodes from a parent barcode + provided, all in one single request. + + Arguments: + - prefix : str - URL extracted argument, that defines the Prefix to use for + the barcodes generated. It has to be one of the prefixes defined in + baracoda.config PREFIXES variable + - count : str - URL or BODY extracted argument. It represents the number of + barcodes we want to create inside the group. + If specified in URL it can be defined as url parameter: + Eg: /barcodes_group/TEST/new?count=14 + If specified in BODY it has to be defined as jSON: + Eg: { "count": 14 } + Result: + - Success: HTTP 201 with JSON representation of BarcodeGroup instance + - InvalidPrefixError: HTTP 400 with JSON representation of error. + - InvalidCountError: HTTP 422 with JSON representation of error. + - OtherError: HTTP 500 with JSON representation of error. + """ try: count = get_count_param() barcode = get_barcode_param() From f559d96adc68c4516dc256694fc0a47699521597 Mon Sep 17 00:00:00 2001 From: Eduardo Martin Rojo Date: Fri, 13 May 2022 15:29:46 +0100 Subject: [PATCH 22/33] More readme changes --- README.md | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/README.md b/README.md index 248d22f..d6e5492 100644 --- a/README.md +++ b/README.md @@ -198,6 +198,54 @@ the interface ```baracoda.formats.FormatterInterface```. If true, the prefix will support creating barcodes based on a parent barcode If it is False, the prefix will reject any children creation request for that prefix. +### Child barcode creation logic + +New children barcodes for a barcode can be created after receiving a POST request to: + +```/child-barcodes/-(-)? +where the last number part is optional. Valid barcodes would be HT-11111-13 and HT-11112-24 +but not HT-1-1-1 or HT12341-1, for example. +- Child: this is the part of the Parent barcode string that would identify if the parent was +a child before (the last number). For example for the parent barcode HT-11111-14, Child +would be 14; but for the parent barcode HT-11111, child would have no value defined. +- Count`: number of children barcodes to create. + +The following diagram describes the workflow of how this endpoint will behave depending on +the inputs declared before: + + +```mermaid +graph TD; + Prefix(Prefix is extracted from URL) --> PrefixEnabled[[Is 'Prefix' enabled by config to create children]]; + ParentBarcode(Parent barcode is extracted from URL arg) --> PrefixEnabled[[Is 'Prefix' enabled by config to create children]]; + Child(Child is extracted from Parent barcode) --> PrefixEnabled[[Is 'Prefix' enabled by config to create children]]; + PrefixEnabled -->|Yes|ValidBarcode[[Is 'Parent Barcode' a valid parent?]]; + PrefixEnabled -->|No|Rejected([HTTP 422 - Rejected]); + ValidBarcode -->|Yes|ParentPresent[[Is 'Parent barcode' present in database?]]; + ValidBarcode -->|No|NormalBarcode(Generate normal barcodes for the Prefix); + NormalBarcode -->NormalAccept([HTTP 201 - Created]) + ParentPresent -->|Yes|ChildExist[[Do we have a value for 'Child'?]]; + ParentPresent -->|No|ChildExist2[[Do we have a value for 'Child'?]]; + ChildExist -->|Yes|ChildConstraint[[Is 'Child' bigger than the last child generated by the 'Parent barcode' in previous requests?]]; + ChildConstraint -->|Yes|Impostor([HTTP 500 - Parent barcode is an impostor]); + ChildExist2 -->|Yes|Impostor; + ChildExist -->|No|ChildrenBarcodes(Generate children barcodes); + ChildrenBarcodes --> NormalAccept; + ChildConstraint -->|No|ChildrenBarcodes + ChildExist2 -->|No|ChildrenBarcodes; + +``` + ### Troubleshooting #### Installing psycopg2 From 459704b1120b898d3cbdd38b120d01d3aef503fa Mon Sep 17 00:00:00 2001 From: Eduardo Martin Rojo Date: Fri, 13 May 2022 15:37:53 +0100 Subject: [PATCH 23/33] More readme changes. --- README.md | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index d6e5492..8d9f7df 100644 --- a/README.md +++ b/README.md @@ -198,27 +198,36 @@ the interface ```baracoda.formats.FormatterInterface```. If true, the prefix will support creating barcodes based on a parent barcode If it is False, the prefix will reject any children creation request for that prefix. -### Child barcode creation logic +### About Children barcode creation -New children barcodes for a barcode can be created after receiving a POST request to: +Children barcodes from a parent barcode can be created with a POST request to the +endpoint with a JSON body: ```/child-barcodes/-(-)? -where the last number part is optional. Valid barcodes would be HT-11111-13 and HT-11112-24 -but not HT-1-1-1 or HT12341-1, for example. -- Child: this is the part of the Parent barcode string that would identify if the parent was -a child before (the last number). For example for the parent barcode HT-11111-14, Child -would be 14; but for the parent barcode HT-11111, child would have no value defined. -- Count`: number of children barcodes to create. +where the last number part is optional (it represents if the barcode was a child). +For example, valid barcodes would be ```HT-11111-13``` (normal parent) and ```HT-11112-24``` +(parent that was a child) but not ```HT-1-1-1``` or ```HT12341-1```. +- *Child* : part of the Parent barcode string that would identify if the parent was +a child before (the last number). For example for the *Parent barcode* ```HT-11111-14```, +*Child* would be 14; but for the *Parent barcode* ```HT-11111```, *Child* would have no +value defined. +- *Count* : number of children barcodes to create. + +There is the possibility that the parent barcode received is in the wrong format, in +which case this endpoint will generate a normal barcode. +There is the possibility that the parent barcode receied has a children barcode format +that is logically incorrect from database perspective, in that case the request +will be rejected as 'Impostor' barcode. The following diagram describes the workflow of how this endpoint will behave depending on the inputs declared before: From 086315abec455a766996f236c4bc591dd452014c Mon Sep 17 00:00:00 2001 From: Eduardo Martin Rojo Date: Fri, 13 May 2022 15:40:56 +0100 Subject: [PATCH 24/33] More readme changes --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8d9f7df..71b937b 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,6 @@ These are some of the key features currently supported: - [Requirements for Development](#requirements-for-development) * [Setup Steps](#setup-steps) - [Running](#running) -- [Configuration](#configuration) - [Testing](#testing) * [Testing Requirements](#testing-requirements) * [Running Tests](#running-tests) @@ -39,6 +38,8 @@ These are some of the key features currently supported: - [Autogenerating Migrations](#autogenerating-migrations) - [Routes](#routes) - [Miscellaneous](#miscellaneous) + * [Configuration](#configuration) + * [Children barcode creation](#children-barcode-creation) * [Troubleshooting](#troubleshooting) + [Installing psycopg2](#installing-psycopg2) * [Updating the Table of Contents](#updating-the-table-of-contents) @@ -198,7 +199,7 @@ the interface ```baracoda.formats.FormatterInterface```. If true, the prefix will support creating barcodes based on a parent barcode If it is False, the prefix will reject any children creation request for that prefix. -### About Children barcode creation +### Children barcode creation Children barcodes from a parent barcode can be created with a POST request to the endpoint with a JSON body: From d55351bd68deb7edcabdbe9e4c73f9624fd7aa1e Mon Sep 17 00:00:00 2001 From: Eduardo Martin Rojo Date: Fri, 13 May 2022 15:43:24 +0100 Subject: [PATCH 25/33] More changes --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 71b937b..ec97937 100644 --- a/README.md +++ b/README.md @@ -188,7 +188,7 @@ These are the allowed keywords that we can specify to configure a prefix: - ```prefix```: This is the string that represents the prefix we are configuring for supporting new barcodes. -- ```sequence_name```: This is the sequence name in the PostgreSQL database with will +- ```sequence_name```: This is the sequence name in the PostgreSQL database which will keep record of the last index created for a barcode. Prefixes can share the same sequence. - ```formatter_class```: Defines the class that will generate the string that represents @@ -209,12 +209,12 @@ endpoint with a JSON body: The inputs for this request will be: - *Prefix* : prefix where we want to create the children under. This argument will be -extracted from the URL ```/child-barcodes//new``` All barcodes for the children will have this prefix (example, prefix HT will generate children like HT-11111-1, HT-11111-2, etc) - *Parent Barcode* : barcode that will act as parent of the children. This argument will be extracted from the Body of the request, eg: ```{'barcode': 'HT-1-1', 'count': 2}```. -To be considered valid, the barcode needs to follow the format -(-)? +To be considered valid, the barcode needs to follow the format ```-(-)?``` where the last number part is optional (it represents if the barcode was a child). For example, valid barcodes would be ```HT-11111-13``` (normal parent) and ```HT-11112-24``` (parent that was a child) but not ```HT-1-1-1``` or ```HT12341-1```. From 980ed589b8d6accf100b30ecaf103700e0cefdf7 Mon Sep 17 00:00:00 2001 From: Eduardo Martin Rojo Date: Fri, 13 May 2022 15:48:59 +0100 Subject: [PATCH 26/33] More changes --- README.md | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index ec97937..56f0432 100644 --- a/README.md +++ b/README.md @@ -224,11 +224,20 @@ a child before (the last number). For example for the *Parent barcode* ```HT-111 value defined. - *Count* : number of children barcodes to create. -There is the possibility that the parent barcode received is in the wrong format, in -which case this endpoint will generate a normal barcode. -There is the possibility that the parent barcode receied has a children barcode format -that is logically incorrect from database perspective, in that case the request -will be rejected as 'Impostor' barcode. +#### Wrong parent barcodes + +A request with parent barcode that does not follow the format defined like: +``` +-(-)? +``` +will create normal barcodes instead of suffixed children barcodes (normal barcode creation). + +A request that follows the right format but does not comply with current database will be +rejected as impostor. For example, if we receive the parent barcode HT-1111-14, but in the +database the parent HT-1111 has only created 12 child barcodes yet, so HT-1111-14 is +impossible to have been generated by Baracoda. + +#### Logic Workflow The following diagram describes the workflow of how this endpoint will behave depending on the inputs declared before: From 2ed5ace51b397ac1b6b6fe9205ec7bf9e5a2e111 Mon Sep 17 00:00:00 2001 From: Eduardo Martin Rojo Date: Fri, 13 May 2022 15:50:42 +0100 Subject: [PATCH 27/33] More changes --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 56f0432..1ef63e4 100644 --- a/README.md +++ b/README.md @@ -233,8 +233,8 @@ A request with parent barcode that does not follow the format defined like: will create normal barcodes instead of suffixed children barcodes (normal barcode creation). A request that follows the right format but does not comply with current database will be -rejected as impostor. For example, if we receive the parent barcode HT-1111-14, but in the -database the parent HT-1111 has only created 12 child barcodes yet, so HT-1111-14 is +rejected as impostor barcode. For example, if we receive the parent barcode ```HT-1111-14```, but in the +database the parent ```HT-1111``` has only created 12 child barcodes yet, so ```HT-1111-14``` is impossible to have been generated by Baracoda. #### Logic Workflow From bcc8104461aa6cfa9d1bdb9bf1d9861ce9fb7737 Mon Sep 17 00:00:00 2001 From: Eduardo Martin Rojo Date: Fri, 13 May 2022 15:53:00 +0100 Subject: [PATCH 28/33] More changes --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 1ef63e4..9650f4e 100644 --- a/README.md +++ b/README.md @@ -167,6 +167,7 @@ The following routes are available from this service: barcode_creation.get_last_barcode GET /barcodes//last barcode_creation.get_new_barcode POST /barcodes//new barcode_creation.get_new_barcode_group POST /barcodes_group//new + child_barcodes.new_child_barcodes POST /child-barcodes//new health_check GET /health static GET /static/ From b08f8fd5621ffb6ad5c221f331a94085a8075d5b Mon Sep 17 00:00:00 2001 From: Eduardo Martin Rojo Date: Fri, 13 May 2022 16:09:44 +0100 Subject: [PATCH 29/33] Added more code documentation --- baracoda/child_barcodes.py | 6 ++++- baracoda/operations.py | 48 ++++++++++++++++++++++++++++++++++---- baracoda/types.py | 5 ++++ 3 files changed, 54 insertions(+), 5 deletions(-) diff --git a/baracoda/child_barcodes.py b/baracoda/child_barcodes.py index d900610..5873933 100644 --- a/baracoda/child_barcodes.py +++ b/baracoda/child_barcodes.py @@ -8,6 +8,8 @@ # from baracoda.operations import create_child_barcodes from baracoda.operations import BarcodeOperations, InvalidParentBarcode, InvalidPrefixForChildrenCreation from baracoda.exceptions import InvalidCountError, InvalidBarcodeError +from baracoda.types import BarcodeParentInfoType +from typing import cast bp = Blueprint("child_barcode_creation", __name__) CORS(bp) @@ -50,7 +52,9 @@ def new_child_barcodes(prefix: str) -> Tuple[Any, int]: operator.validate_prefix_for_child_creation() info = operator.extract_barcode_parent_information(barcode) operator.validate_barcode_parent_information(info) - barcode_group = operator.create_children_barcode_group(info["parent_barcode"], count) + barcode_group = operator.create_children_barcode_group( + cast(BarcodeParentInfoType, info)["parent_barcode"], count + ) return ( barcode_group.to_dict(), HTTPStatus.CREATED, diff --git a/baracoda/operations.py b/baracoda/operations.py index afe5ae0..feba092 100644 --- a/baracoda/operations.py +++ b/baracoda/operations.py @@ -1,7 +1,7 @@ import logging import re from datetime import datetime -from typing import List, Optional, cast, Dict +from typing import List, Optional, cast from xmlrpc.client import Boolean from baracoda.db import db from baracoda.exceptions import InvalidPrefixError @@ -10,7 +10,7 @@ from baracoda.orm.child_barcode import ChildBarcode from baracoda.orm.barcodes_group import BarcodesGroup from baracoda.formats import FormatterInterface -from baracoda.types import PrefixesType +from baracoda.types import PrefixesType, BarcodeParentInfoType logger = logging.getLogger(__name__) @@ -213,9 +213,31 @@ def __set_prefix_item(self): # Child barcode operations def is_valid_parent_barcode(self, barcode: str) -> Boolean: + """Boolean function that identifies if a barcode can act as a parent barcode. + It checks that the barcode matches the format of the regexp declared in the + #extract_barcode_parent_information method. + + Arguments: + barcode - str : Barcode that we want to check if it is valid parent + + Returns: + bool indicating if the barcode was a valid parent + """ return not self.extract_barcode_parent_information(barcode) is None - def extract_barcode_parent_information(self, barcode): + def extract_barcode_parent_information(self, barcode: str) -> Optional[BarcodeParentInfoType]: + """Extracts the parent and child information from a barcode string by following the regexp + defined. If the input does not match it will return None. + Eg: barcode HT-1111-23 it will extract parent: HT-1111 and child: 23 + barcode HT-1111 it will extract parent: HT-1111 and child: None + + Arguments: + barcode - str : Barcode string where we want to extract data from + + Returns: + BarcodeParentInfoType object with the fields parent_barcode and child, or + None if the barcode string from input did not match the regexp. + """ pattern = re.compile(f"^(?P{self.prefix}-\\d+)(?:-(?P\\d+))?$") found = pattern.search(barcode) if not found: @@ -226,10 +248,28 @@ def extract_barcode_parent_information(self, barcode): } def validate_prefix_for_child_creation(self) -> None: + """Validates if self.prefix is declared as children creation enabled and if not + it will raise an exception. + + Returns: + None if prefix has children creation enabled + Raise InvalidPrefixForChildrenCreation if not enabled + """ if not cast(PrefixesType, self.prefix_item)["enableChildrenCreation"]: raise InvalidPrefixForChildrenCreation() - def validate_barcode_parent_information(self, info: Dict[str, str]) -> None: + def validate_barcode_parent_information(self, info: Optional[BarcodeParentInfoType]) -> None: + """Validates if barcode has all the correct information to generate children barcodes. + It will check that: + - The barcode was correctly parsed in the object as input, otherwise it will raise + InvalidParentBarcode + - The parent barcode, if is a child barcode, it was generated as a child before by Baracoda + otherwise it will be rejected and raise InvalidParentBarcode + + Returns: + None if checks were ok + Raise InvalidParentBarcode if not correct + """ if not info: raise InvalidParentBarcode("The barcode provided is not valid for generating child barcodes") diff --git a/baracoda/types.py b/baracoda/types.py index cdf2550..87ae168 100644 --- a/baracoda/types.py +++ b/baracoda/types.py @@ -8,5 +8,10 @@ {"prefix": str, "sequence_name": str, "formatter_class": FormatterClassType, "enableChildrenCreation": bool}, ) +BarcodeParentInfoType = TypedDict( + "BarcodeParentInfoType", + {"parent_barcode": str, "child": str}, +) + FormatterInterfaceType = List[PrefixesType] From fd37119f4c77b859674d0bce2df2bc68f9a19ef3 Mon Sep 17 00:00:00 2001 From: Eduardo Martin Rojo Date: Fri, 13 May 2022 16:11:27 +0100 Subject: [PATCH 30/33] Change logging level for console --- baracoda/child_barcodes.py | 1 - baracoda/config/logging.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/baracoda/child_barcodes.py b/baracoda/child_barcodes.py index 5873933..7ba801e 100644 --- a/baracoda/child_barcodes.py +++ b/baracoda/child_barcodes.py @@ -5,7 +5,6 @@ from sqlalchemy import exc from flask_cors import CORS -# from baracoda.operations import create_child_barcodes from baracoda.operations import BarcodeOperations, InvalidParentBarcode, InvalidPrefixForChildrenCreation from baracoda.exceptions import InvalidCountError, InvalidBarcodeError from baracoda.types import BarcodeParentInfoType diff --git a/baracoda/config/logging.py b/baracoda/config/logging.py index 06cc699..a9c85fa 100644 --- a/baracoda/config/logging.py +++ b/baracoda/config/logging.py @@ -37,7 +37,7 @@ "filters": ["package_path"], }, "console": { - "level": "DEBUG", + "level": "INFO", "class": "logging.StreamHandler", "formatter": "verbose", }, From 90ab047041889a6228e87876d725044f2b7432b9 Mon Sep 17 00:00:00 2001 From: Eduardo Martin Rojo Date: Fri, 13 May 2022 16:22:16 +0100 Subject: [PATCH 31/33] Added more comments --- baracoda/operations.py | 46 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/baracoda/operations.py b/baracoda/operations.py index feba092..bf50400 100644 --- a/baracoda/operations.py +++ b/baracoda/operations.py @@ -16,10 +16,19 @@ class InvalidParentBarcode(BaseException): + """The barcode provided did not match the right format, or + it was an impostor barcode (created outside of Baracoda but + using the same format). + """ + pass class InvalidPrefixForChildrenCreation(BaseException): + """The prefix provided is not currently enabled for children + creation. + """ + pass @@ -43,10 +52,28 @@ def __init__(self, prefix: str): self.sequence_name = self.prefix_item["sequence_name"] def formatter(self) -> FormatterInterface: + """Factory method that will create a new formatter instance + from the prefix declared. + + Returns: + FormatterInterface instance that can be used to format a new + barcode string + """ formatter_class = cast(PrefixesType, self.prefix_item)["formatter_class"] return formatter_class(self.prefix) def create_barcodes(self, count: int) -> List[str]: + """Create a list of barcodes, not inside a group. + It requests a new list of ids from the sequence associated with the current prefix + and formats those ids into new barcode strings. The sequence is incremented with this + request. + + Arguments: + count - int : number of barcodes to create + + Returns: + List[str] - List with the string of barcodes created + """ next_values = self.__get_next_values(self.sequence_name, count) return [self.formatter().barcode(next_value) for next_value in next_values] @@ -139,6 +166,17 @@ def __validate_prefix(self) -> bool: return bool(pattern.match(self.prefix)) def __build_barcode(self, prefix: str, barcode: str, barcodes_group: Optional[BarcodesGroup]) -> Barcode: + """Creates a new instance for Barcode with the arguments received, relating them to a + BarcodeGroup if provided, and setting a created_at timestamp. + + Arguments: + prefix : str - prefix of the barcode + barcode : str - string with the barcode value + barcodes_group : Optional[BarcodesGroup] - instance of BarcodesGroup or None if not needed + + Returns: + Barcode instance with the arguments set and a created_at timestamp attached + """ return Barcode( prefix=prefix, barcode=barcode, @@ -173,6 +211,14 @@ def __create_barcode_group(self, barcodes: List[str]) -> BarcodesGroup: raise e def __build_barcodes_group(self) -> BarcodesGroup: + """Creates a new instance for BarcodesGroup, and sets + a created_at timestamp. + + Arguments: None + + Returns: + BarcodesGroup instance with a created_at timestamp attached + """ return BarcodesGroup(created_at=datetime.now()) def __get_next_value(self, sequence_name: str) -> int: From 3c8c8225effcb3d8e267d0e9a6b5140b4b260695 Mon Sep 17 00:00:00 2001 From: Eduardo Martin Rojo Date: Fri, 13 May 2022 16:25:34 +0100 Subject: [PATCH 32/33] More comments --- baracoda/child_barcodes.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/baracoda/child_barcodes.py b/baracoda/child_barcodes.py index 7ba801e..1fc1fc8 100644 --- a/baracoda/child_barcodes.py +++ b/baracoda/child_barcodes.py @@ -74,6 +74,13 @@ def new_child_barcodes(prefix: str) -> Tuple[Any, int]: def get_count_param(): + """Extracts the count param from the Body of the request. If count + was not defined it will return 1. + + Returns: + int with the count value or + InvalidCountError if it could not be extracted + """ count = 1 # Default count if request.json and ("count" in request.json): count = int(request.json["count"]) @@ -83,6 +90,13 @@ def get_count_param(): def get_barcode_param(): + """Extracts the barcode param from the Body of the request. If barcode + was not defined it will raise InvalidBarcodeError. + + Returns: + str with the barcode value or + InvalidBarcodeError if it was not present + """ barcode = "" if request.json and ("barcode" in request.json): barcode = str(request.json["barcode"]) From 18678f90c501aa5398b93d1fb3b4137dee9e5010 Mon Sep 17 00:00:00 2001 From: Eduardo Martin Rojo Date: Fri, 13 May 2022 16:31:00 +0100 Subject: [PATCH 33/33] More changes --- baracoda/orm/child_barcode.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/baracoda/orm/child_barcode.py b/baracoda/orm/child_barcode.py index 6ba76ba..dd3eb14 100644 --- a/baracoda/orm/child_barcode.py +++ b/baracoda/orm/child_barcode.py @@ -3,6 +3,11 @@ class ChildBarcode(Base): + """Class that will define the ORM mapping with the database. It will + store a list of all parent barcodes with an index of the last created + child for the parent for each entry. + """ + __tablename__ = "child_barcode_counter" barcode = Column(String(50), nullable=False, primary_key=True)