From 4398f7ef314b95f723bfcb9a9e89e7eaa33f0bed Mon Sep 17 00:00:00 2001 From: jcadam14 <41971533+jcadam14@users.noreply.github.com> Date: Wed, 6 Nov 2024 09:18:07 -0700 Subject: [PATCH] Refactor to use polars (#244) Closes #243 Lots of changes, lots of comments added. Please ask questions. --------- Co-authored-by: Adam <41971533+jcadam@users.noreply.github.com> --- poetry.lock | 1716 ++++++++++++++--- pyproject.toml | 6 + src/regtech_data_validator/check_functions.py | 627 +++--- src/regtech_data_validator/checks.py | 4 +- src/regtech_data_validator/cli.py | 76 +- src/regtech_data_validator/create_schemas.py | 255 --- src/regtech_data_validator/data_formatters.py | 455 +++-- .../phase_validations.py | 245 ++- src/regtech_data_validator/schema_template.py | 17 +- .../validation_results.py | 10 +- src/regtech_data_validator/validator.py | 323 ++++ tests/data/all_logic_errors.csv | 9 + tests/data/all_logic_warnings.csv | 9 + tests/data/all_syntax_errors.csv | 140 ++ tests/data/sbl-validations-fail.csv | 370 ---- tests/data/sbl-validations-pass.csv | 528 ----- tests/data/sblar_no_findings.csv | 11 + tests/test_check_functions.py | 604 +++--- tests/test_cli.py | 51 +- tests/test_csv_to_code_differences.py | 7 +- tests/test_fig_anchors.py | 39 - tests/test_fig_links.py | 13 +- tests/test_output_formats.py | 337 ++-- tests/test_sample_data.py | 124 +- tests/test_schema_functions.py | 417 ++-- 25 files changed, 3531 insertions(+), 2862 deletions(-) delete mode 100644 src/regtech_data_validator/create_schemas.py create mode 100644 src/regtech_data_validator/validator.py create mode 100644 tests/data/all_logic_errors.csv create mode 100644 tests/data/all_logic_warnings.csv create mode 100644 tests/data/all_syntax_errors.csv delete mode 100644 tests/data/sbl-validations-fail.csv delete mode 100644 tests/data/sbl-validations-pass.csv create mode 100644 tests/data/sblar_no_findings.csv delete mode 100644 tests/test_fig_anchors.py diff --git a/poetry.lock b/poetry.lock index f2e9b584..51c46b9f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,5 +1,177 @@ # This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +[[package]] +name = "aiobotocore" +version = "2.13.3" +description = "Async client for aws services using botocore and aiohttp" +optional = false +python-versions = ">=3.8" +files = [ + {file = "aiobotocore-2.13.3-py3-none-any.whl", hash = "sha256:1272f765fd9414e1a68f8add71978367db94e17e36c3bf629cf1153eb5141fb9"}, + {file = "aiobotocore-2.13.3.tar.gz", hash = "sha256:ac5620f93cc3e7c2aef7c67ba2bb74035ff8d49ee2325821daed13b3dd82a473"}, +] + +[package.dependencies] +aiohttp = ">=3.9.2,<4.0.0" +aioitertools = ">=0.5.1,<1.0.0" +botocore = ">=1.34.70,<1.34.163" +wrapt = ">=1.10.10,<2.0.0" + +[package.extras] +awscli = ["awscli (>=1.32.70,<1.33.45)"] +boto3 = ["boto3 (>=1.34.70,<1.34.163)"] + +[[package]] +name = "aiohappyeyeballs" +version = "2.4.3" +description = "Happy Eyeballs for asyncio" +optional = false +python-versions = ">=3.8" +files = [ + {file = "aiohappyeyeballs-2.4.3-py3-none-any.whl", hash = "sha256:8a7a83727b2756f394ab2895ea0765a0a8c475e3c71e98d43d76f22b4b435572"}, + {file = "aiohappyeyeballs-2.4.3.tar.gz", hash = "sha256:75cf88a15106a5002a8eb1dab212525c00d1f4c0fa96e551c9fbe6f09a621586"}, +] + +[[package]] +name = "aiohttp" +version = "3.10.8" +description = "Async http client/server framework (asyncio)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "aiohttp-3.10.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a1ba7bc139592339ddeb62c06486d0fa0f4ca61216e14137a40d626c81faf10c"}, + {file = "aiohttp-3.10.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:85e4d7bd05d18e4b348441e7584c681eff646e3bf38f68b2626807f3add21aa2"}, + {file = "aiohttp-3.10.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:69de056022e7abf69cb9fec795515973cc3eeaff51e3ea8d72a77aa933a91c52"}, + {file = "aiohttp-3.10.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee3587506898d4a404b33bd19689286ccf226c3d44d7a73670c8498cd688e42c"}, + {file = "aiohttp-3.10.8-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fe285a697c851734285369614443451462ce78aac2b77db23567507484b1dc6f"}, + {file = "aiohttp-3.10.8-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:10c7932337285a6bfa3a5fe1fd4da90b66ebfd9d0cbd1544402e1202eb9a8c3e"}, + {file = "aiohttp-3.10.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd9716ef0224fe0d0336997eb242f40619f9f8c5c57e66b525a1ebf9f1d8cebe"}, + {file = "aiohttp-3.10.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ceacea31f8a55cdba02bc72c93eb2e1b77160e91f8abd605969c168502fd71eb"}, + {file = "aiohttp-3.10.8-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9721554bfa9e15f6e462da304374c2f1baede3cb06008c36c47fa37ea32f1dc4"}, + {file = "aiohttp-3.10.8-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:22cdeb684d8552490dd2697a5138c4ecb46f844892df437aaf94f7eea99af879"}, + {file = "aiohttp-3.10.8-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e56bb7e31c4bc79956b866163170bc89fd619e0581ce813330d4ea46921a4881"}, + {file = "aiohttp-3.10.8-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:3a95d2686bc4794d66bd8de654e41b5339fab542b2bca9238aa63ed5f4f2ce82"}, + {file = "aiohttp-3.10.8-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d82404a0e7b10e0d7f022cf44031b78af8a4f99bd01561ac68f7c24772fed021"}, + {file = "aiohttp-3.10.8-cp310-cp310-win32.whl", hash = "sha256:4e10b04542d27e21538e670156e88766543692a0a883f243ba8fad9ddea82e53"}, + {file = "aiohttp-3.10.8-cp310-cp310-win_amd64.whl", hash = "sha256:680dbcff5adc7f696ccf8bf671d38366a1f620b5616a1d333d0cb33956065395"}, + {file = "aiohttp-3.10.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:33a68011a38020ed4ff41ae0dbf4a96a202562ecf2024bdd8f65385f1d07f6ef"}, + {file = "aiohttp-3.10.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6c7efa6616a95e3bd73b8a69691012d2ef1f95f9ea0189e42f338fae080c2fc6"}, + {file = "aiohttp-3.10.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ddb9b9764cfb4459acf01c02d2a59d3e5066b06a846a364fd1749aa168efa2be"}, + {file = "aiohttp-3.10.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c7f270f4ca92760f98a42c45a58674fff488e23b144ec80b1cc6fa2effed377"}, + {file = "aiohttp-3.10.8-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6984dda9d79064361ab58d03f6c1e793ea845c6cfa89ffe1a7b9bb400dfd56bd"}, + {file = "aiohttp-3.10.8-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3f6d47e392c27206701565c8df4cac6ebed28fdf6dcaea5b1eea7a4631d8e6db"}, + {file = "aiohttp-3.10.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a72f89aea712c619b2ca32c6f4335c77125ede27530ad9705f4f349357833695"}, + {file = "aiohttp-3.10.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c36074b26f3263879ba8e4dbd33db2b79874a3392f403a70b772701363148b9f"}, + {file = "aiohttp-3.10.8-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e32148b4a745e70a255a1d44b5664de1f2e24fcefb98a75b60c83b9e260ddb5b"}, + {file = "aiohttp-3.10.8-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5aa1a073514cf59c81ad49a4ed9b5d72b2433638cd53160fd2f3a9cfa94718db"}, + {file = "aiohttp-3.10.8-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d3a79200a9d5e621c4623081ddb25380b713c8cf5233cd11c1aabad990bb9381"}, + {file = "aiohttp-3.10.8-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e45fdfcb2d5bcad83373e4808825b7512953146d147488114575780640665027"}, + {file = "aiohttp-3.10.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f78e2a78432c537ae876a93013b7bc0027ba5b93ad7b3463624c4b6906489332"}, + {file = "aiohttp-3.10.8-cp311-cp311-win32.whl", hash = "sha256:f8179855a4e4f3b931cb1764ec87673d3fbdcca2af496c8d30567d7b034a13db"}, + {file = "aiohttp-3.10.8-cp311-cp311-win_amd64.whl", hash = "sha256:ef9b484604af05ca745b6108ca1aaa22ae1919037ae4f93aaf9a37ba42e0b835"}, + {file = "aiohttp-3.10.8-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:ab2d6523575fc98896c80f49ac99e849c0b0e69cc80bf864eed6af2ae728a52b"}, + {file = "aiohttp-3.10.8-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f5d5d5401744dda50b943d8764508d0e60cc2d3305ac1e6420935861a9d544bc"}, + {file = "aiohttp-3.10.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de23085cf90911600ace512e909114385026b16324fa203cc74c81f21fd3276a"}, + {file = "aiohttp-3.10.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4618f0d2bf523043866a9ff8458900d8eb0a6d4018f251dae98e5f1fb699f3a8"}, + {file = "aiohttp-3.10.8-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:21c1925541ca84f7b5e0df361c0a813a7d6a56d3b0030ebd4b220b8d232015f9"}, + {file = "aiohttp-3.10.8-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:497a7d20caea8855c5429db3cdb829385467217d7feb86952a6107e033e031b9"}, + {file = "aiohttp-3.10.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c887019dbcb4af58a091a45ccf376fffe800b5531b45c1efccda4bedf87747ea"}, + {file = "aiohttp-3.10.8-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40d2d719c3c36a7a65ed26400e2b45b2d9ed7edf498f4df38b2ae130f25a0d01"}, + {file = "aiohttp-3.10.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:57359785f27394a8bcab0da6dcd46706d087dfebf59a8d0ad2e64a4bc2f6f94f"}, + {file = "aiohttp-3.10.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a961ee6f2cdd1a2be4735333ab284691180d40bad48f97bb598841bfcbfb94ec"}, + {file = "aiohttp-3.10.8-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:fe3d79d6af839ffa46fdc5d2cf34295390894471e9875050eafa584cb781508d"}, + {file = "aiohttp-3.10.8-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9a281cba03bdaa341c70b7551b2256a88d45eead149f48b75a96d41128c240b3"}, + {file = "aiohttp-3.10.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c6769d71bfb1ed60321363a9bc05e94dcf05e38295ef41d46ac08919e5b00d19"}, + {file = "aiohttp-3.10.8-cp312-cp312-win32.whl", hash = "sha256:a3081246bab4d419697ee45e555cef5cd1def7ac193dff6f50be761d2e44f194"}, + {file = "aiohttp-3.10.8-cp312-cp312-win_amd64.whl", hash = "sha256:ab1546fc8e00676febc81c548a876c7bde32f881b8334b77f84719ab2c7d28dc"}, + {file = "aiohttp-3.10.8-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:b1a012677b8e0a39e181e218de47d6741c5922202e3b0b65e412e2ce47c39337"}, + {file = "aiohttp-3.10.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2df786c96c57cd6b87156ba4c5f166af7b88f3fc05f9d592252fdc83d8615a3c"}, + {file = "aiohttp-3.10.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8885ca09d3a9317219c0831276bfe26984b17b2c37b7bf70dd478d17092a4772"}, + {file = "aiohttp-3.10.8-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4dbf252ac19860e0ab56cd480d2805498f47c5a2d04f5995d8d8a6effd04b48c"}, + {file = "aiohttp-3.10.8-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b2036479b6b94afaaca7d07b8a68dc0e67b0caf5f6293bb6a5a1825f5923000"}, + {file = "aiohttp-3.10.8-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:365783e1b7c40b59ed4ce2b5a7491bae48f41cd2c30d52647a5b1ee8604c68ad"}, + {file = "aiohttp-3.10.8-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:270e653b5a4b557476a1ed40e6b6ce82f331aab669620d7c95c658ef976c9c5e"}, + {file = "aiohttp-3.10.8-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8960fabc20bfe4fafb941067cda8e23c8c17c98c121aa31c7bf0cdab11b07842"}, + {file = "aiohttp-3.10.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f21e8f2abed9a44afc3d15bba22e0dfc71e5fa859bea916e42354c16102b036f"}, + {file = "aiohttp-3.10.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:fecd55e7418fabd297fd836e65cbd6371aa4035a264998a091bbf13f94d9c44d"}, + {file = "aiohttp-3.10.8-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:badb51d851358cd7535b647bb67af4854b64f3c85f0d089c737f75504d5910ec"}, + {file = "aiohttp-3.10.8-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:e860985f30f3a015979e63e7ba1a391526cdac1b22b7b332579df7867848e255"}, + {file = "aiohttp-3.10.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:71462f8eeca477cbc0c9700a9464e3f75f59068aed5e9d4a521a103692da72dc"}, + {file = "aiohttp-3.10.8-cp313-cp313-win32.whl", hash = "sha256:177126e971782769b34933e94fddd1089cef0fe6b82fee8a885e539f5b0f0c6a"}, + {file = "aiohttp-3.10.8-cp313-cp313-win_amd64.whl", hash = "sha256:98a4eb60e27033dee9593814ca320ee8c199489fbc6b2699d0f710584db7feb7"}, + {file = "aiohttp-3.10.8-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ffef3d763e4c8fc97e740da5b4d0f080b78630a3914f4e772a122bbfa608c1db"}, + {file = "aiohttp-3.10.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:597128cb7bc5f068181b49a732961f46cb89f85686206289d6ccb5e27cb5fbe2"}, + {file = "aiohttp-3.10.8-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f23a6c1d09de5de89a33c9e9b229106cb70dcfdd55e81a3a3580eaadaa32bc92"}, + {file = "aiohttp-3.10.8-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da57af0c54a302b7c655fa1ccd5b1817a53739afa39924ef1816e7b7c8a07ccb"}, + {file = "aiohttp-3.10.8-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e7a6af57091056a79a35104d6ec29d98ec7f1fb7270ad9c6fff871b678d1ff8"}, + {file = "aiohttp-3.10.8-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:32710d6b3b6c09c60c794d84ca887a3a2890131c0b02b3cefdcc6709a2260a7c"}, + {file = "aiohttp-3.10.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b91f4f62ad39a8a42d511d66269b46cb2fb7dea9564c21ab6c56a642d28bff5"}, + {file = "aiohttp-3.10.8-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:471a8c47344b9cc309558b3fcc469bd2c12b49322b4b31eb386c4a2b2d44e44a"}, + {file = "aiohttp-3.10.8-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:fc0e7f91705445d79beafba9bb3057dd50830e40fe5417017a76a214af54e122"}, + {file = "aiohttp-3.10.8-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:85431c9131a9a0f65260dc7a65c800ca5eae78c4c9931618f18c8e0933a0e0c1"}, + {file = "aiohttp-3.10.8-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:b91557ee0893da52794b25660d4f57bb519bcad8b7df301acd3898f7197c5d81"}, + {file = "aiohttp-3.10.8-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:4954e6b06dd0be97e1a5751fc606be1f9edbdc553c5d9b57d72406a8fbd17f9d"}, + {file = "aiohttp-3.10.8-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:a087c84b4992160ffef7afd98ef24177c8bd4ad61c53607145a8377457385100"}, + {file = "aiohttp-3.10.8-cp38-cp38-win32.whl", hash = "sha256:e1f0f7b27171b2956a27bd8f899751d0866ddabdd05cbddf3520f945130a908c"}, + {file = "aiohttp-3.10.8-cp38-cp38-win_amd64.whl", hash = "sha256:c4916070e12ae140110aa598031876c1bf8676a36a750716ea0aa5bd694aa2e7"}, + {file = "aiohttp-3.10.8-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5284997e3d88d0dfb874c43e51ae8f4a6f4ca5b90dcf22995035187253d430db"}, + {file = "aiohttp-3.10.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9443d9ebc5167ce1fbb552faf2d666fb22ef5716a8750be67efd140a7733738c"}, + {file = "aiohttp-3.10.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b667e2a03407d79a76c618dc30cedebd48f082d85880d0c9c4ec2faa3e10f43e"}, + {file = "aiohttp-3.10.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98fae99d5c2146f254b7806001498e6f9ffb0e330de55a35e72feb7cb2fa399b"}, + {file = "aiohttp-3.10.8-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8296edd99d0dd9d0eb8b9e25b3b3506eef55c1854e9cc230f0b3f885f680410b"}, + {file = "aiohttp-3.10.8-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1ce46dfb49cfbf9e92818be4b761d4042230b1f0e05ffec0aad15b3eb162b905"}, + {file = "aiohttp-3.10.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c38cfd355fd86c39b2d54651bd6ed7d63d4fe3b5553f364bae3306e2445f847"}, + {file = "aiohttp-3.10.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:713dff3f87ceec3bde4f3f484861464e722cf7533f9fa6b824ec82bb5a9010a7"}, + {file = "aiohttp-3.10.8-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:21a72f4a9c69a8567a0aca12042f12bba25d3139fd5dd8eeb9931f4d9e8599cd"}, + {file = "aiohttp-3.10.8-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:6d1ad868624f6cea77341ef2877ad4e71f7116834a6cd7ec36ec5c32f94ee6ae"}, + {file = "aiohttp-3.10.8-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:a78ba86d5a08207d1d1ad10b97aed6ea48b374b3f6831d02d0b06545ac0f181e"}, + {file = "aiohttp-3.10.8-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:aff048793d05e1ce05b62e49dccf81fe52719a13f4861530706619506224992b"}, + {file = "aiohttp-3.10.8-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d088ca05381fd409793571d8e34eca06daf41c8c50a05aeed358d2d340c7af81"}, + {file = "aiohttp-3.10.8-cp39-cp39-win32.whl", hash = "sha256:ee97c4e54f457c366e1f76fbbf3e8effee9de57dae671084a161c00f481106ce"}, + {file = "aiohttp-3.10.8-cp39-cp39-win_amd64.whl", hash = "sha256:d95ae4420669c871667aad92ba8cce6251d61d79c1a38504621094143f94a8b4"}, + {file = "aiohttp-3.10.8.tar.gz", hash = "sha256:21f8225f7dc187018e8433c9326be01477fb2810721e048b33ac49091b19fb4a"}, +] + +[package.dependencies] +aiohappyeyeballs = ">=2.3.0" +aiosignal = ">=1.1.2" +attrs = ">=17.3.0" +frozenlist = ">=1.1.1" +multidict = ">=4.5,<7.0" +yarl = ">=1.12.0,<2.0" + +[package.extras] +speedups = ["Brotli", "aiodns (>=3.2.0)", "brotlicffi"] + +[[package]] +name = "aioitertools" +version = "0.12.0" +description = "itertools and builtins for AsyncIO and mixed iterables" +optional = false +python-versions = ">=3.8" +files = [ + {file = "aioitertools-0.12.0-py3-none-any.whl", hash = "sha256:fc1f5fac3d737354de8831cbba3eb04f79dd649d8f3afb4c5b114925e662a796"}, + {file = "aioitertools-0.12.0.tar.gz", hash = "sha256:c2a9055b4fbb7705f561b9d86053e8af5d10cc845d22c32008c43490b2d8dd6b"}, +] + +[package.extras] +dev = ["attribution (==1.8.0)", "black (==24.8.0)", "build (>=1.2)", "coverage (==7.6.1)", "flake8 (==7.1.1)", "flit (==3.9.0)", "mypy (==1.11.2)", "ufmt (==2.7.1)", "usort (==1.0.8.post1)"] +docs = ["sphinx (==8.0.2)", "sphinx-mdinclude (==0.6.2)"] + +[[package]] +name = "aiosignal" +version = "1.3.1" +description = "aiosignal: a list of registered asynchronous callbacks" +optional = false +python-versions = ">=3.7" +files = [ + {file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"}, + {file = "aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"}, +] + +[package.dependencies] +frozenlist = ">=1.1.0" + [[package]] name = "annotated-types" version = "0.7.0" @@ -11,6 +183,25 @@ files = [ {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, ] +[[package]] +name = "attrs" +version = "24.2.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.7" +files = [ + {file = "attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2"}, + {file = "attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346"}, +] + +[package.extras] +benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] +tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] + [[package]] name = "beautifulsoup4" version = "4.12.3" @@ -76,15 +267,53 @@ d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] uvloop = ["uvloop (>=0.15.2)"] +[[package]] +name = "boto3" +version = "1.34.162" +description = "The AWS SDK for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "boto3-1.34.162-py3-none-any.whl", hash = "sha256:d6f6096bdab35a0c0deff469563b87d184a28df7689790f7fe7be98502b7c590"}, + {file = "boto3-1.34.162.tar.gz", hash = "sha256:873f8f5d2f6f85f1018cbb0535b03cceddc7b655b61f66a0a56995238804f41f"}, +] + +[package.dependencies] +botocore = ">=1.34.162,<1.35.0" +jmespath = ">=0.7.1,<2.0.0" +s3transfer = ">=0.10.0,<0.11.0" + +[package.extras] +crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] + +[[package]] +name = "botocore" +version = "1.34.162" +description = "Low-level, data-driven core of boto 3." +optional = false +python-versions = ">=3.8" +files = [ + {file = "botocore-1.34.162-py3-none-any.whl", hash = "sha256:2d918b02db88d27a75b48275e6fb2506e9adaaddbec1ffa6a8a0898b34e769be"}, + {file = "botocore-1.34.162.tar.gz", hash = "sha256:adc23be4fb99ad31961236342b7cbf3c0bfc62532cd02852196032e8c0d682f3"}, +] + +[package.dependencies] +jmespath = ">=0.7.1,<2.0.0" +python-dateutil = ">=2.1,<3.0.0" +urllib3 = {version = ">=1.25.4,<2.2.0 || >2.2.0,<3", markers = "python_version >= \"3.10\""} + +[package.extras] +crt = ["awscrt (==0.21.2)"] + [[package]] name = "certifi" -version = "2024.7.4" +version = "2024.8.30" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"}, - {file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"}, + {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, + {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, ] [[package]] @@ -211,70 +440,189 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +[[package]] +name = "contourpy" +version = "1.3.0" +description = "Python library for calculating contours of 2D quadrilateral grids" +optional = false +python-versions = ">=3.9" +files = [ + {file = "contourpy-1.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:880ea32e5c774634f9fcd46504bf9f080a41ad855f4fef54f5380f5133d343c7"}, + {file = "contourpy-1.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:76c905ef940a4474a6289c71d53122a4f77766eef23c03cd57016ce19d0f7b42"}, + {file = "contourpy-1.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:92f8557cbb07415a4d6fa191f20fd9d2d9eb9c0b61d1b2f52a8926e43c6e9af7"}, + {file = "contourpy-1.3.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:36f965570cff02b874773c49bfe85562b47030805d7d8360748f3eca570f4cab"}, + {file = "contourpy-1.3.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cacd81e2d4b6f89c9f8a5b69b86490152ff39afc58a95af002a398273e5ce589"}, + {file = "contourpy-1.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:69375194457ad0fad3a839b9e29aa0b0ed53bb54db1bfb6c3ae43d111c31ce41"}, + {file = "contourpy-1.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a52040312b1a858b5e31ef28c2e865376a386c60c0e248370bbea2d3f3b760d"}, + {file = "contourpy-1.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3faeb2998e4fcb256542e8a926d08da08977f7f5e62cf733f3c211c2a5586223"}, + {file = "contourpy-1.3.0-cp310-cp310-win32.whl", hash = "sha256:36e0cff201bcb17a0a8ecc7f454fe078437fa6bda730e695a92f2d9932bd507f"}, + {file = "contourpy-1.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:87ddffef1dbe5e669b5c2440b643d3fdd8622a348fe1983fad7a0f0ccb1cd67b"}, + {file = "contourpy-1.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0fa4c02abe6c446ba70d96ece336e621efa4aecae43eaa9b030ae5fb92b309ad"}, + {file = "contourpy-1.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:834e0cfe17ba12f79963861e0f908556b2cedd52e1f75e6578801febcc6a9f49"}, + {file = "contourpy-1.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dbc4c3217eee163fa3984fd1567632b48d6dfd29216da3ded3d7b844a8014a66"}, + {file = "contourpy-1.3.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4865cd1d419e0c7a7bf6de1777b185eebdc51470800a9f42b9e9decf17762081"}, + {file = "contourpy-1.3.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:303c252947ab4b14c08afeb52375b26781ccd6a5ccd81abcdfc1fafd14cf93c1"}, + {file = "contourpy-1.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:637f674226be46f6ba372fd29d9523dd977a291f66ab2a74fbeb5530bb3f445d"}, + {file = "contourpy-1.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:76a896b2f195b57db25d6b44e7e03f221d32fe318d03ede41f8b4d9ba1bff53c"}, + {file = "contourpy-1.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e1fd23e9d01591bab45546c089ae89d926917a66dceb3abcf01f6105d927e2cb"}, + {file = "contourpy-1.3.0-cp311-cp311-win32.whl", hash = "sha256:d402880b84df3bec6eab53cd0cf802cae6a2ef9537e70cf75e91618a3801c20c"}, + {file = "contourpy-1.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:6cb6cc968059db9c62cb35fbf70248f40994dfcd7aa10444bbf8b3faeb7c2d67"}, + {file = "contourpy-1.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:570ef7cf892f0afbe5b2ee410c507ce12e15a5fa91017a0009f79f7d93a1268f"}, + {file = "contourpy-1.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:da84c537cb8b97d153e9fb208c221c45605f73147bd4cadd23bdae915042aad6"}, + {file = "contourpy-1.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0be4d8425bfa755e0fd76ee1e019636ccc7c29f77a7c86b4328a9eb6a26d0639"}, + {file = "contourpy-1.3.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9c0da700bf58f6e0b65312d0a5e695179a71d0163957fa381bb3c1f72972537c"}, + {file = "contourpy-1.3.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eb8b141bb00fa977d9122636b16aa67d37fd40a3d8b52dd837e536d64b9a4d06"}, + {file = "contourpy-1.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3634b5385c6716c258d0419c46d05c8aa7dc8cb70326c9a4fb66b69ad2b52e09"}, + {file = "contourpy-1.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0dce35502151b6bd35027ac39ba6e5a44be13a68f55735c3612c568cac3805fd"}, + {file = "contourpy-1.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:aea348f053c645100612b333adc5983d87be69acdc6d77d3169c090d3b01dc35"}, + {file = "contourpy-1.3.0-cp312-cp312-win32.whl", hash = "sha256:90f73a5116ad1ba7174341ef3ea5c3150ddf20b024b98fb0c3b29034752c8aeb"}, + {file = "contourpy-1.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:b11b39aea6be6764f84360fce6c82211a9db32a7c7de8fa6dd5397cf1d079c3b"}, + {file = "contourpy-1.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3e1c7fa44aaae40a2247e2e8e0627f4bea3dd257014764aa644f319a5f8600e3"}, + {file = "contourpy-1.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:364174c2a76057feef647c802652f00953b575723062560498dc7930fc9b1cb7"}, + {file = "contourpy-1.3.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32b238b3b3b649e09ce9aaf51f0c261d38644bdfa35cbaf7b263457850957a84"}, + {file = "contourpy-1.3.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d51fca85f9f7ad0b65b4b9fe800406d0d77017d7270d31ec3fb1cc07358fdea0"}, + {file = "contourpy-1.3.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:732896af21716b29ab3e988d4ce14bc5133733b85956316fb0c56355f398099b"}, + {file = "contourpy-1.3.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d73f659398a0904e125280836ae6f88ba9b178b2fed6884f3b1f95b989d2c8da"}, + {file = "contourpy-1.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c6c7c2408b7048082932cf4e641fa3b8ca848259212f51c8c59c45aa7ac18f14"}, + {file = "contourpy-1.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f317576606de89da6b7e0861cf6061f6146ead3528acabff9236458a6ba467f8"}, + {file = "contourpy-1.3.0-cp313-cp313-win32.whl", hash = "sha256:31cd3a85dbdf1fc002280c65caa7e2b5f65e4a973fcdf70dd2fdcb9868069294"}, + {file = "contourpy-1.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:4553c421929ec95fb07b3aaca0fae668b2eb5a5203d1217ca7c34c063c53d087"}, + {file = "contourpy-1.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:345af746d7766821d05d72cb8f3845dfd08dd137101a2cb9b24de277d716def8"}, + {file = "contourpy-1.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3bb3808858a9dc68f6f03d319acd5f1b8a337e6cdda197f02f4b8ff67ad2057b"}, + {file = "contourpy-1.3.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:420d39daa61aab1221567b42eecb01112908b2cab7f1b4106a52caaec8d36973"}, + {file = "contourpy-1.3.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4d63ee447261e963af02642ffcb864e5a2ee4cbfd78080657a9880b8b1868e18"}, + {file = "contourpy-1.3.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:167d6c890815e1dac9536dca00828b445d5d0df4d6a8c6adb4a7ec3166812fa8"}, + {file = "contourpy-1.3.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:710a26b3dc80c0e4febf04555de66f5fd17e9cf7170a7b08000601a10570bda6"}, + {file = "contourpy-1.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:75ee7cb1a14c617f34a51d11fa7524173e56551646828353c4af859c56b766e2"}, + {file = "contourpy-1.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:33c92cdae89ec5135d036e7218e69b0bb2851206077251f04a6c4e0e21f03927"}, + {file = "contourpy-1.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a11077e395f67ffc2c44ec2418cfebed032cd6da3022a94fc227b6faf8e2acb8"}, + {file = "contourpy-1.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e8134301d7e204c88ed7ab50028ba06c683000040ede1d617298611f9dc6240c"}, + {file = "contourpy-1.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e12968fdfd5bb45ffdf6192a590bd8ddd3ba9e58360b29683c6bb71a7b41edca"}, + {file = "contourpy-1.3.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fd2a0fc506eccaaa7595b7e1418951f213cf8255be2600f1ea1b61e46a60c55f"}, + {file = "contourpy-1.3.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4cfb5c62ce023dfc410d6059c936dcf96442ba40814aefbfa575425a3a7f19dc"}, + {file = "contourpy-1.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68a32389b06b82c2fdd68276148d7b9275b5f5cf13e5417e4252f6d1a34f72a2"}, + {file = "contourpy-1.3.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:94e848a6b83da10898cbf1311a815f770acc9b6a3f2d646f330d57eb4e87592e"}, + {file = "contourpy-1.3.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d78ab28a03c854a873787a0a42254a0ccb3cb133c672f645c9f9c8f3ae9d0800"}, + {file = "contourpy-1.3.0-cp39-cp39-win32.whl", hash = "sha256:81cb5ed4952aae6014bc9d0421dec7c5835c9c8c31cdf51910b708f548cf58e5"}, + {file = "contourpy-1.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:14e262f67bd7e6eb6880bc564dcda30b15e351a594657e55b7eec94b6ef72843"}, + {file = "contourpy-1.3.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:fe41b41505a5a33aeaed2a613dccaeaa74e0e3ead6dd6fd3a118fb471644fd6c"}, + {file = "contourpy-1.3.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eca7e17a65f72a5133bdbec9ecf22401c62bcf4821361ef7811faee695799779"}, + {file = "contourpy-1.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:1ec4dc6bf570f5b22ed0d7efba0dfa9c5b9e0431aeea7581aa217542d9e809a4"}, + {file = "contourpy-1.3.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:00ccd0dbaad6d804ab259820fa7cb0b8036bda0686ef844d24125d8287178ce0"}, + {file = "contourpy-1.3.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ca947601224119117f7c19c9cdf6b3ab54c5726ef1d906aa4a69dfb6dd58102"}, + {file = "contourpy-1.3.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c6ec93afeb848a0845a18989da3beca3eec2c0f852322efe21af1931147d12cb"}, + {file = "contourpy-1.3.0.tar.gz", hash = "sha256:7ffa0db17717a8ffb127efd0c95a4362d996b892c2904db72428d5b52e1938a4"}, +] + +[package.dependencies] +numpy = ">=1.23" + +[package.extras] +bokeh = ["bokeh", "selenium"] +docs = ["furo", "sphinx (>=7.2)", "sphinx-copybutton"] +mypy = ["contourpy[bokeh,docs]", "docutils-stubs", "mypy (==1.11.1)", "types-Pillow"] +test = ["Pillow", "contourpy[test-no-images]", "matplotlib"] +test-no-images = ["pytest", "pytest-cov", "pytest-rerunfailures", "pytest-xdist", "wurlitzer"] + [[package]] name = "coverage" -version = "7.5.3" +version = "7.6.1" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.5.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a6519d917abb15e12380406d721e37613e2a67d166f9fb7e5a8ce0375744cd45"}, - {file = "coverage-7.5.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aea7da970f1feccf48be7335f8b2ca64baf9b589d79e05b9397a06696ce1a1ec"}, - {file = "coverage-7.5.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:923b7b1c717bd0f0f92d862d1ff51d9b2b55dbbd133e05680204465f454bb286"}, - {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62bda40da1e68898186f274f832ef3e759ce929da9a9fd9fcf265956de269dbc"}, - {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8b7339180d00de83e930358223c617cc343dd08e1aa5ec7b06c3a121aec4e1d"}, - {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:25a5caf742c6195e08002d3b6c2dd6947e50efc5fc2c2205f61ecb47592d2d83"}, - {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:05ac5f60faa0c704c0f7e6a5cbfd6f02101ed05e0aee4d2822637a9e672c998d"}, - {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:239a4e75e09c2b12ea478d28815acf83334d32e722e7433471fbf641c606344c"}, - {file = "coverage-7.5.3-cp310-cp310-win32.whl", hash = "sha256:a5812840d1d00eafae6585aba38021f90a705a25b8216ec7f66aebe5b619fb84"}, - {file = "coverage-7.5.3-cp310-cp310-win_amd64.whl", hash = "sha256:33ca90a0eb29225f195e30684ba4a6db05dbef03c2ccd50b9077714c48153cac"}, - {file = "coverage-7.5.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f81bc26d609bf0fbc622c7122ba6307993c83c795d2d6f6f6fd8c000a770d974"}, - {file = "coverage-7.5.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7cec2af81f9e7569280822be68bd57e51b86d42e59ea30d10ebdbb22d2cb7232"}, - {file = "coverage-7.5.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55f689f846661e3f26efa535071775d0483388a1ccfab899df72924805e9e7cd"}, - {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50084d3516aa263791198913a17354bd1dc627d3c1639209640b9cac3fef5807"}, - {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:341dd8f61c26337c37988345ca5c8ccabeff33093a26953a1ac72e7d0103c4fb"}, - {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ab0b028165eea880af12f66086694768f2c3139b2c31ad5e032c8edbafca6ffc"}, - {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5bc5a8c87714b0c67cfeb4c7caa82b2d71e8864d1a46aa990b5588fa953673b8"}, - {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:38a3b98dae8a7c9057bd91fbf3415c05e700a5114c5f1b5b0ea5f8f429ba6614"}, - {file = "coverage-7.5.3-cp311-cp311-win32.whl", hash = "sha256:fcf7d1d6f5da887ca04302db8e0e0cf56ce9a5e05f202720e49b3e8157ddb9a9"}, - {file = "coverage-7.5.3-cp311-cp311-win_amd64.whl", hash = "sha256:8c836309931839cca658a78a888dab9676b5c988d0dd34ca247f5f3e679f4e7a"}, - {file = "coverage-7.5.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:296a7d9bbc598e8744c00f7a6cecf1da9b30ae9ad51c566291ff1314e6cbbed8"}, - {file = "coverage-7.5.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:34d6d21d8795a97b14d503dcaf74226ae51eb1f2bd41015d3ef332a24d0a17b3"}, - {file = "coverage-7.5.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e317953bb4c074c06c798a11dbdd2cf9979dbcaa8ccc0fa4701d80042d4ebf1"}, - {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:705f3d7c2b098c40f5b81790a5fedb274113373d4d1a69e65f8b68b0cc26f6db"}, - {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1196e13c45e327d6cd0b6e471530a1882f1017eb83c6229fc613cd1a11b53cd"}, - {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:015eddc5ccd5364dcb902eaecf9515636806fa1e0d5bef5769d06d0f31b54523"}, - {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fd27d8b49e574e50caa65196d908f80e4dff64d7e592d0c59788b45aad7e8b35"}, - {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:33fc65740267222fc02975c061eb7167185fef4cc8f2770267ee8bf7d6a42f84"}, - {file = "coverage-7.5.3-cp312-cp312-win32.whl", hash = "sha256:7b2a19e13dfb5c8e145c7a6ea959485ee8e2204699903c88c7d25283584bfc08"}, - {file = "coverage-7.5.3-cp312-cp312-win_amd64.whl", hash = "sha256:0bbddc54bbacfc09b3edaec644d4ac90c08ee8ed4844b0f86227dcda2d428fcb"}, - {file = "coverage-7.5.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f78300789a708ac1f17e134593f577407d52d0417305435b134805c4fb135adb"}, - {file = "coverage-7.5.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b368e1aee1b9b75757942d44d7598dcd22a9dbb126affcbba82d15917f0cc155"}, - {file = "coverage-7.5.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f836c174c3a7f639bded48ec913f348c4761cbf49de4a20a956d3431a7c9cb24"}, - {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:244f509f126dc71369393ce5fea17c0592c40ee44e607b6d855e9c4ac57aac98"}, - {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4c2872b3c91f9baa836147ca33650dc5c172e9273c808c3c3199c75490e709d"}, - {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dd4b3355b01273a56b20c219e74e7549e14370b31a4ffe42706a8cda91f19f6d"}, - {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:f542287b1489c7a860d43a7d8883e27ca62ab84ca53c965d11dac1d3a1fab7ce"}, - {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:75e3f4e86804023e991096b29e147e635f5e2568f77883a1e6eed74512659ab0"}, - {file = "coverage-7.5.3-cp38-cp38-win32.whl", hash = "sha256:c59d2ad092dc0551d9f79d9d44d005c945ba95832a6798f98f9216ede3d5f485"}, - {file = "coverage-7.5.3-cp38-cp38-win_amd64.whl", hash = "sha256:fa21a04112c59ad54f69d80e376f7f9d0f5f9123ab87ecd18fbb9ec3a2beed56"}, - {file = "coverage-7.5.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f5102a92855d518b0996eb197772f5ac2a527c0ec617124ad5242a3af5e25f85"}, - {file = "coverage-7.5.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d1da0a2e3b37b745a2b2a678a4c796462cf753aebf94edcc87dcc6b8641eae31"}, - {file = "coverage-7.5.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8383a6c8cefba1b7cecc0149415046b6fc38836295bc4c84e820872eb5478b3d"}, - {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9aad68c3f2566dfae84bf46295a79e79d904e1c21ccfc66de88cd446f8686341"}, - {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e079c9ec772fedbade9d7ebc36202a1d9ef7291bc9b3a024ca395c4d52853d7"}, - {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bde997cac85fcac227b27d4fb2c7608a2c5f6558469b0eb704c5726ae49e1c52"}, - {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:990fb20b32990b2ce2c5f974c3e738c9358b2735bc05075d50a6f36721b8f303"}, - {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3d5a67f0da401e105753d474369ab034c7bae51a4c31c77d94030d59e41df5bd"}, - {file = "coverage-7.5.3-cp39-cp39-win32.whl", hash = "sha256:e08c470c2eb01977d221fd87495b44867a56d4d594f43739a8028f8646a51e0d"}, - {file = "coverage-7.5.3-cp39-cp39-win_amd64.whl", hash = "sha256:1d2a830ade66d3563bb61d1e3c77c8def97b30ed91e166c67d0632c018f380f0"}, - {file = "coverage-7.5.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:3538d8fb1ee9bdd2e2692b3b18c22bb1c19ffbefd06880f5ac496e42d7bb3884"}, - {file = "coverage-7.5.3.tar.gz", hash = "sha256:04aefca5190d1dc7a53a4c1a5a7f8568811306d7a8ee231c42fb69215571944f"}, + {file = "coverage-7.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b06079abebbc0e89e6163b8e8f0e16270124c154dc6e4a47b413dd538859af16"}, + {file = "coverage-7.6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cf4b19715bccd7ee27b6b120e7e9dd56037b9c0681dcc1adc9ba9db3d417fa36"}, + {file = "coverage-7.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61c0abb4c85b095a784ef23fdd4aede7a2628478e7baba7c5e3deba61070a02"}, + {file = "coverage-7.6.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd21f6ae3f08b41004dfb433fa895d858f3f5979e7762d052b12aef444e29afc"}, + {file = "coverage-7.6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f59d57baca39b32db42b83b2a7ba6f47ad9c394ec2076b084c3f029b7afca23"}, + {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a1ac0ae2b8bd743b88ed0502544847c3053d7171a3cff9228af618a068ed9c34"}, + {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e6a08c0be454c3b3beb105c0596ebdc2371fab6bb90c0c0297f4e58fd7e1012c"}, + {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f5796e664fe802da4f57a168c85359a8fbf3eab5e55cd4e4569fbacecc903959"}, + {file = "coverage-7.6.1-cp310-cp310-win32.whl", hash = "sha256:7bb65125fcbef8d989fa1dd0e8a060999497629ca5b0efbca209588a73356232"}, + {file = "coverage-7.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:3115a95daa9bdba70aea750db7b96b37259a81a709223c8448fa97727d546fe0"}, + {file = "coverage-7.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7dea0889685db8550f839fa202744652e87c60015029ce3f60e006f8c4462c93"}, + {file = "coverage-7.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed37bd3c3b063412f7620464a9ac1314d33100329f39799255fb8d3027da50d3"}, + {file = "coverage-7.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d85f5e9a5f8b73e2350097c3756ef7e785f55bd71205defa0bfdaf96c31616ff"}, + {file = "coverage-7.6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bc572be474cafb617672c43fe989d6e48d3c83af02ce8de73fff1c6bb3c198d"}, + {file = "coverage-7.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0420b573964c760df9e9e86d1a9a622d0d27f417e1a949a8a66dd7bcee7bc6"}, + {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1f4aa8219db826ce6be7099d559f8ec311549bfc4046f7f9fe9b5cea5c581c56"}, + {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:fc5a77d0c516700ebad189b587de289a20a78324bc54baee03dd486f0855d234"}, + {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b48f312cca9621272ae49008c7f613337c53fadca647d6384cc129d2996d1133"}, + {file = "coverage-7.6.1-cp311-cp311-win32.whl", hash = "sha256:1125ca0e5fd475cbbba3bb67ae20bd2c23a98fac4e32412883f9bcbaa81c314c"}, + {file = "coverage-7.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:8ae539519c4c040c5ffd0632784e21b2f03fc1340752af711f33e5be83a9d6c6"}, + {file = "coverage-7.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:95cae0efeb032af8458fc27d191f85d1717b1d4e49f7cb226cf526ff28179778"}, + {file = "coverage-7.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5621a9175cf9d0b0c84c2ef2b12e9f5f5071357c4d2ea6ca1cf01814f45d2391"}, + {file = "coverage-7.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:260933720fdcd75340e7dbe9060655aff3af1f0c5d20f46b57f262ab6c86a5e8"}, + {file = "coverage-7.6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07e2ca0ad381b91350c0ed49d52699b625aab2b44b65e1b4e02fa9df0e92ad2d"}, + {file = "coverage-7.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c44fee9975f04b33331cb8eb272827111efc8930cfd582e0320613263ca849ca"}, + {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:877abb17e6339d96bf08e7a622d05095e72b71f8afd8a9fefc82cf30ed944163"}, + {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3e0cadcf6733c09154b461f1ca72d5416635e5e4ec4e536192180d34ec160f8a"}, + {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c3c02d12f837d9683e5ab2f3d9844dc57655b92c74e286c262e0fc54213c216d"}, + {file = "coverage-7.6.1-cp312-cp312-win32.whl", hash = "sha256:e05882b70b87a18d937ca6768ff33cc3f72847cbc4de4491c8e73880766718e5"}, + {file = "coverage-7.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:b5d7b556859dd85f3a541db6a4e0167b86e7273e1cdc973e5b175166bb634fdb"}, + {file = "coverage-7.6.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a4acd025ecc06185ba2b801f2de85546e0b8ac787cf9d3b06e7e2a69f925b106"}, + {file = "coverage-7.6.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a6d3adcf24b624a7b778533480e32434a39ad8fa30c315208f6d3e5542aeb6e9"}, + {file = "coverage-7.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0c212c49b6c10e6951362f7c6df3329f04c2b1c28499563d4035d964ab8e08c"}, + {file = "coverage-7.6.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e81d7a3e58882450ec4186ca59a3f20a5d4440f25b1cff6f0902ad890e6748a"}, + {file = "coverage-7.6.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78b260de9790fd81e69401c2dc8b17da47c8038176a79092a89cb2b7d945d060"}, + {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a78d169acd38300060b28d600344a803628c3fd585c912cacc9ea8790fe96862"}, + {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2c09f4ce52cb99dd7505cd0fc8e0e37c77b87f46bc9c1eb03fe3bc9991085388"}, + {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6878ef48d4227aace338d88c48738a4258213cd7b74fd9a3d4d7582bb1d8a155"}, + {file = "coverage-7.6.1-cp313-cp313-win32.whl", hash = "sha256:44df346d5215a8c0e360307d46ffaabe0f5d3502c8a1cefd700b34baf31d411a"}, + {file = "coverage-7.6.1-cp313-cp313-win_amd64.whl", hash = "sha256:8284cf8c0dd272a247bc154eb6c95548722dce90d098c17a883ed36e67cdb129"}, + {file = "coverage-7.6.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d3296782ca4eab572a1a4eca686d8bfb00226300dcefdf43faa25b5242ab8a3e"}, + {file = "coverage-7.6.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:502753043567491d3ff6d08629270127e0c31d4184c4c8d98f92c26f65019962"}, + {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a89ecca80709d4076b95f89f308544ec8f7b4727e8a547913a35f16717856cb"}, + {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a318d68e92e80af8b00fa99609796fdbcdfef3629c77c6283566c6f02c6d6704"}, + {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13b0a73a0896988f053e4fbb7de6d93388e6dd292b0d87ee51d106f2c11b465b"}, + {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4421712dbfc5562150f7554f13dde997a2e932a6b5f352edcce948a815efee6f"}, + {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:166811d20dfea725e2e4baa71fffd6c968a958577848d2131f39b60043400223"}, + {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:225667980479a17db1048cb2bf8bfb39b8e5be8f164b8f6628b64f78a72cf9d3"}, + {file = "coverage-7.6.1-cp313-cp313t-win32.whl", hash = "sha256:170d444ab405852903b7d04ea9ae9b98f98ab6d7e63e1115e82620807519797f"}, + {file = "coverage-7.6.1-cp313-cp313t-win_amd64.whl", hash = "sha256:b9f222de8cded79c49bf184bdbc06630d4c58eec9459b939b4a690c82ed05657"}, + {file = "coverage-7.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6db04803b6c7291985a761004e9060b2bca08da6d04f26a7f2294b8623a0c1a0"}, + {file = "coverage-7.6.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f1adfc8ac319e1a348af294106bc6a8458a0f1633cc62a1446aebc30c5fa186a"}, + {file = "coverage-7.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a95324a9de9650a729239daea117df21f4b9868ce32e63f8b650ebe6cef5595b"}, + {file = "coverage-7.6.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b43c03669dc4618ec25270b06ecd3ee4fa94c7f9b3c14bae6571ca00ef98b0d3"}, + {file = "coverage-7.6.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8929543a7192c13d177b770008bc4e8119f2e1f881d563fc6b6305d2d0ebe9de"}, + {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:a09ece4a69cf399510c8ab25e0950d9cf2b42f7b3cb0374f95d2e2ff594478a6"}, + {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:9054a0754de38d9dbd01a46621636689124d666bad1936d76c0341f7d71bf569"}, + {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0dbde0f4aa9a16fa4d754356a8f2e36296ff4d83994b2c9d8398aa32f222f989"}, + {file = "coverage-7.6.1-cp38-cp38-win32.whl", hash = "sha256:da511e6ad4f7323ee5702e6633085fb76c2f893aaf8ce4c51a0ba4fc07580ea7"}, + {file = "coverage-7.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:3f1156e3e8f2872197af3840d8ad307a9dd18e615dc64d9ee41696f287c57ad8"}, + {file = "coverage-7.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:abd5fd0db5f4dc9289408aaf34908072f805ff7792632250dcb36dc591d24255"}, + {file = "coverage-7.6.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:547f45fa1a93154bd82050a7f3cddbc1a7a4dd2a9bf5cb7d06f4ae29fe94eaf8"}, + {file = "coverage-7.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:645786266c8f18a931b65bfcefdbf6952dd0dea98feee39bd188607a9d307ed2"}, + {file = "coverage-7.6.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e0b2df163b8ed01d515807af24f63de04bebcecbd6c3bfeff88385789fdf75a"}, + {file = "coverage-7.6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:609b06f178fe8e9f89ef676532760ec0b4deea15e9969bf754b37f7c40326dbc"}, + {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:702855feff378050ae4f741045e19a32d57d19f3e0676d589df0575008ea5004"}, + {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:2bdb062ea438f22d99cba0d7829c2ef0af1d768d1e4a4f528087224c90b132cb"}, + {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9c56863d44bd1c4fe2abb8a4d6f5371d197f1ac0ebdee542f07f35895fc07f36"}, + {file = "coverage-7.6.1-cp39-cp39-win32.whl", hash = "sha256:6e2cd258d7d927d09493c8df1ce9174ad01b381d4729a9d8d4e38670ca24774c"}, + {file = "coverage-7.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:06a737c882bd26d0d6ee7269b20b12f14a8704807a01056c80bb881a4b2ce6ca"}, + {file = "coverage-7.6.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:e9a6e0eb86070e8ccaedfbd9d38fec54864f3125ab95419970575b42af7541df"}, + {file = "coverage-7.6.1.tar.gz", hash = "sha256:953510dfb7b12ab69d20135a0662397f077c59b1e6379a768e97c59d852ee51d"}, ] [package.extras] toml = ["tomli"] +[[package]] +name = "cycler" +version = "0.12.1" +description = "Composable style cycles" +optional = false +python-versions = ">=3.8" +files = [ + {file = "cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30"}, + {file = "cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c"}, +] + +[package.extras] +docs = ["ipython", "matplotlib", "numpydoc", "sphinx"] +tests = ["pytest", "pytest-cov", "pytest-xdist"] + [[package]] name = "et-xmlfile" version = "1.1.0" @@ -286,17 +634,216 @@ files = [ {file = "et_xmlfile-1.1.0.tar.gz", hash = "sha256:8eb9e2bc2f8c97e37a2dc85a09ecdcdec9d8a396530a6d5a33b30b9a92da0c5c"}, ] +[[package]] +name = "fonttools" +version = "4.54.1" +description = "Tools to manipulate font files" +optional = false +python-versions = ">=3.8" +files = [ + {file = "fonttools-4.54.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7ed7ee041ff7b34cc62f07545e55e1468808691dddfd315d51dd82a6b37ddef2"}, + {file = "fonttools-4.54.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:41bb0b250c8132b2fcac148e2e9198e62ff06f3cc472065dff839327945c5882"}, + {file = "fonttools-4.54.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7965af9b67dd546e52afcf2e38641b5be956d68c425bef2158e95af11d229f10"}, + {file = "fonttools-4.54.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:278913a168f90d53378c20c23b80f4e599dca62fbffae4cc620c8eed476b723e"}, + {file = "fonttools-4.54.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0e88e3018ac809b9662615072dcd6b84dca4c2d991c6d66e1970a112503bba7e"}, + {file = "fonttools-4.54.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:4aa4817f0031206e637d1e685251ac61be64d1adef111060df84fdcbc6ab6c44"}, + {file = "fonttools-4.54.1-cp310-cp310-win32.whl", hash = "sha256:7e3b7d44e18c085fd8c16dcc6f1ad6c61b71ff463636fcb13df7b1b818bd0c02"}, + {file = "fonttools-4.54.1-cp310-cp310-win_amd64.whl", hash = "sha256:dd9cc95b8d6e27d01e1e1f1fae8559ef3c02c76317da650a19047f249acd519d"}, + {file = "fonttools-4.54.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5419771b64248484299fa77689d4f3aeed643ea6630b2ea750eeab219588ba20"}, + {file = "fonttools-4.54.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:301540e89cf4ce89d462eb23a89464fef50915255ece765d10eee8b2bf9d75b2"}, + {file = "fonttools-4.54.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76ae5091547e74e7efecc3cbf8e75200bc92daaeb88e5433c5e3e95ea8ce5aa7"}, + {file = "fonttools-4.54.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82834962b3d7c5ca98cb56001c33cf20eb110ecf442725dc5fdf36d16ed1ab07"}, + {file = "fonttools-4.54.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d26732ae002cc3d2ecab04897bb02ae3f11f06dd7575d1df46acd2f7c012a8d8"}, + {file = "fonttools-4.54.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:58974b4987b2a71ee08ade1e7f47f410c367cdfc5a94fabd599c88165f56213a"}, + {file = "fonttools-4.54.1-cp311-cp311-win32.whl", hash = "sha256:ab774fa225238986218a463f3fe151e04d8c25d7de09df7f0f5fce27b1243dbc"}, + {file = "fonttools-4.54.1-cp311-cp311-win_amd64.whl", hash = "sha256:07e005dc454eee1cc60105d6a29593459a06321c21897f769a281ff2d08939f6"}, + {file = "fonttools-4.54.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:54471032f7cb5fca694b5f1a0aaeba4af6e10ae989df408e0216f7fd6cdc405d"}, + {file = "fonttools-4.54.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8fa92cb248e573daab8d032919623cc309c005086d743afb014c836636166f08"}, + {file = "fonttools-4.54.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a911591200114969befa7f2cb74ac148bce5a91df5645443371aba6d222e263"}, + {file = "fonttools-4.54.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93d458c8a6a354dc8b48fc78d66d2a8a90b941f7fec30e94c7ad9982b1fa6bab"}, + {file = "fonttools-4.54.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5eb2474a7c5be8a5331146758debb2669bf5635c021aee00fd7c353558fc659d"}, + {file = "fonttools-4.54.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c9c563351ddc230725c4bdf7d9e1e92cbe6ae8553942bd1fb2b2ff0884e8b714"}, + {file = "fonttools-4.54.1-cp312-cp312-win32.whl", hash = "sha256:fdb062893fd6d47b527d39346e0c5578b7957dcea6d6a3b6794569370013d9ac"}, + {file = "fonttools-4.54.1-cp312-cp312-win_amd64.whl", hash = "sha256:e4564cf40cebcb53f3dc825e85910bf54835e8a8b6880d59e5159f0f325e637e"}, + {file = "fonttools-4.54.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6e37561751b017cf5c40fce0d90fd9e8274716de327ec4ffb0df957160be3bff"}, + {file = "fonttools-4.54.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:357cacb988a18aace66e5e55fe1247f2ee706e01debc4b1a20d77400354cddeb"}, + {file = "fonttools-4.54.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8e953cc0bddc2beaf3a3c3b5dd9ab7554677da72dfaf46951e193c9653e515a"}, + {file = "fonttools-4.54.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:58d29b9a294573d8319f16f2f79e42428ba9b6480442fa1836e4eb89c4d9d61c"}, + {file = "fonttools-4.54.1-cp313-cp313-win32.whl", hash = "sha256:9ef1b167e22709b46bf8168368b7b5d3efeaaa746c6d39661c1b4405b6352e58"}, + {file = "fonttools-4.54.1-cp313-cp313-win_amd64.whl", hash = "sha256:262705b1663f18c04250bd1242b0515d3bbae177bee7752be67c979b7d47f43d"}, + {file = "fonttools-4.54.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ed2f80ca07025551636c555dec2b755dd005e2ea8fbeb99fc5cdff319b70b23b"}, + {file = "fonttools-4.54.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9dc080e5a1c3b2656caff2ac2633d009b3a9ff7b5e93d0452f40cd76d3da3b3c"}, + {file = "fonttools-4.54.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d152d1be65652fc65e695e5619e0aa0982295a95a9b29b52b85775243c06556"}, + {file = "fonttools-4.54.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8583e563df41fdecef31b793b4dd3af8a9caa03397be648945ad32717a92885b"}, + {file = "fonttools-4.54.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:0d1d353ef198c422515a3e974a1e8d5b304cd54a4c2eebcae708e37cd9eeffb1"}, + {file = "fonttools-4.54.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:fda582236fee135d4daeca056c8c88ec5f6f6d88a004a79b84a02547c8f57386"}, + {file = "fonttools-4.54.1-cp38-cp38-win32.whl", hash = "sha256:e7d82b9e56716ed32574ee106cabca80992e6bbdcf25a88d97d21f73a0aae664"}, + {file = "fonttools-4.54.1-cp38-cp38-win_amd64.whl", hash = "sha256:ada215fd079e23e060157aab12eba0d66704316547f334eee9ff26f8c0d7b8ab"}, + {file = "fonttools-4.54.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f5b8a096e649768c2f4233f947cf9737f8dbf8728b90e2771e2497c6e3d21d13"}, + {file = "fonttools-4.54.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4e10d2e0a12e18f4e2dd031e1bf7c3d7017be5c8dbe524d07706179f355c5dac"}, + {file = "fonttools-4.54.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:31c32d7d4b0958600eac75eaf524b7b7cb68d3a8c196635252b7a2c30d80e986"}, + {file = "fonttools-4.54.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c39287f5c8f4a0c5a55daf9eaf9ccd223ea59eed3f6d467133cc727d7b943a55"}, + {file = "fonttools-4.54.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:a7a310c6e0471602fe3bf8efaf193d396ea561486aeaa7adc1f132e02d30c4b9"}, + {file = "fonttools-4.54.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d3b659d1029946f4ff9b6183984578041b520ce0f8fb7078bb37ec7445806b33"}, + {file = "fonttools-4.54.1-cp39-cp39-win32.whl", hash = "sha256:e96bc94c8cda58f577277d4a71f51c8e2129b8b36fd05adece6320dd3d57de8a"}, + {file = "fonttools-4.54.1-cp39-cp39-win_amd64.whl", hash = "sha256:e8a4b261c1ef91e7188a30571be6ad98d1c6d9fa2427244c545e2fa0a2494dd7"}, + {file = "fonttools-4.54.1-py3-none-any.whl", hash = "sha256:37cddd62d83dc4f72f7c3f3c2bcf2697e89a30efb152079896544a93907733bd"}, + {file = "fonttools-4.54.1.tar.gz", hash = "sha256:957f669d4922f92c171ba01bef7f29410668db09f6c02111e22b2bce446f3285"}, +] + +[package.extras] +all = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "fs (>=2.2.0,<3)", "lxml (>=4.0)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres", "pycairo", "scipy", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.23.0)", "unicodedata2 (>=15.1.0)", "xattr", "zopfli (>=0.1.4)"] +graphite = ["lz4 (>=1.7.4.2)"] +interpolatable = ["munkres", "pycairo", "scipy"] +lxml = ["lxml (>=4.0)"] +pathops = ["skia-pathops (>=0.5.0)"] +plot = ["matplotlib"] +repacker = ["uharfbuzz (>=0.23.0)"] +symfont = ["sympy"] +type1 = ["xattr"] +ufo = ["fs (>=2.2.0,<3)"] +unicode = ["unicodedata2 (>=15.1.0)"] +woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"] + +[[package]] +name = "frozenlist" +version = "1.4.1" +description = "A list-like structure which implements collections.abc.MutableSequence" +optional = false +python-versions = ">=3.8" +files = [ + {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f9aa1878d1083b276b0196f2dfbe00c9b7e752475ed3b682025ff20c1c1f51ac"}, + {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29acab3f66f0f24674b7dc4736477bcd4bc3ad4b896f5f45379a67bce8b96868"}, + {file = "frozenlist-1.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74fb4bee6880b529a0c6560885fce4dc95936920f9f20f53d99a213f7bf66776"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:590344787a90ae57d62511dd7c736ed56b428f04cd8c161fcc5e7232c130c69a"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:068b63f23b17df8569b7fdca5517edef76171cf3897eb68beb01341131fbd2ad"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c849d495bf5154cd8da18a9eb15db127d4dba2968d88831aff6f0331ea9bd4c"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9750cc7fe1ae3b1611bb8cfc3f9ec11d532244235d75901fb6b8e42ce9229dfe"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9b2de4cf0cdd5bd2dee4c4f63a653c61d2408055ab77b151c1957f221cabf2a"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0633c8d5337cb5c77acbccc6357ac49a1770b8c487e5b3505c57b949b4b82e98"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:27657df69e8801be6c3638054e202a135c7f299267f1a55ed3a598934f6c0d75"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:f9a3ea26252bd92f570600098783d1371354d89d5f6b7dfd87359d669f2109b5"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:4f57dab5fe3407b6c0c1cc907ac98e8a189f9e418f3b6e54d65a718aaafe3950"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e02a0e11cf6597299b9f3bbd3f93d79217cb90cfd1411aec33848b13f5c656cc"}, + {file = "frozenlist-1.4.1-cp310-cp310-win32.whl", hash = "sha256:a828c57f00f729620a442881cc60e57cfcec6842ba38e1b19fd3e47ac0ff8dc1"}, + {file = "frozenlist-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:f56e2333dda1fe0f909e7cc59f021eba0d2307bc6f012a1ccf2beca6ba362439"}, + {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a0cb6f11204443f27a1628b0e460f37fb30f624be6051d490fa7d7e26d4af3d0"}, + {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b46c8ae3a8f1f41a0d2ef350c0b6e65822d80772fe46b653ab6b6274f61d4a49"}, + {file = "frozenlist-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fde5bd59ab5357e3853313127f4d3565fc7dad314a74d7b5d43c22c6a5ed2ced"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:722e1124aec435320ae01ee3ac7bec11a5d47f25d0ed6328f2273d287bc3abb0"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2471c201b70d58a0f0c1f91261542a03d9a5e088ed3dc6c160d614c01649c106"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c757a9dd70d72b076d6f68efdbb9bc943665ae954dad2801b874c8c69e185068"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f146e0911cb2f1da549fc58fc7bcd2b836a44b79ef871980d605ec392ff6b0d2"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9c515e7914626b2a2e1e311794b4c35720a0be87af52b79ff8e1429fc25f19"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c302220494f5c1ebeb0912ea782bcd5e2f8308037b3c7553fad0e48ebad6ad82"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:442acde1e068288a4ba7acfe05f5f343e19fac87bfc96d89eb886b0363e977ec"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:1b280e6507ea8a4fa0c0a7150b4e526a8d113989e28eaaef946cc77ffd7efc0a"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:fe1a06da377e3a1062ae5fe0926e12b84eceb8a50b350ddca72dc85015873f74"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:db9e724bebd621d9beca794f2a4ff1d26eed5965b004a97f1f1685a173b869c2"}, + {file = "frozenlist-1.4.1-cp311-cp311-win32.whl", hash = "sha256:e774d53b1a477a67838a904131c4b0eef6b3d8a651f8b138b04f748fccfefe17"}, + {file = "frozenlist-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:fb3c2db03683b5767dedb5769b8a40ebb47d6f7f45b1b3e3b4b51ec8ad9d9825"}, + {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1979bc0aeb89b33b588c51c54ab0161791149f2461ea7c7c946d95d5f93b56ae"}, + {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cc7b01b3754ea68a62bd77ce6020afaffb44a590c2289089289363472d13aedb"}, + {file = "frozenlist-1.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9c92be9fd329ac801cc420e08452b70e7aeab94ea4233a4804f0915c14eba9b"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c3894db91f5a489fc8fa6a9991820f368f0b3cbdb9cd8849547ccfab3392d86"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba60bb19387e13597fb059f32cd4d59445d7b18b69a745b8f8e5db0346f33480"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8aefbba5f69d42246543407ed2461db31006b0f76c4e32dfd6f42215a2c41d09"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780d3a35680ced9ce682fbcf4cb9c2bad3136eeff760ab33707b71db84664e3a"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9acbb16f06fe7f52f441bb6f413ebae6c37baa6ef9edd49cdd567216da8600cd"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:23b701e65c7b36e4bf15546a89279bd4d8675faabc287d06bbcfac7d3c33e1e6"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3e0153a805a98f5ada7e09826255ba99fb4f7524bb81bf6b47fb702666484ae1"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:dd9b1baec094d91bf36ec729445f7769d0d0cf6b64d04d86e45baf89e2b9059b"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:1a4471094e146b6790f61b98616ab8e44f72661879cc63fa1049d13ef711e71e"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5667ed53d68d91920defdf4035d1cdaa3c3121dc0b113255124bcfada1cfa1b8"}, + {file = "frozenlist-1.4.1-cp312-cp312-win32.whl", hash = "sha256:beee944ae828747fd7cb216a70f120767fc9f4f00bacae8543c14a6831673f89"}, + {file = "frozenlist-1.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:64536573d0a2cb6e625cf309984e2d873979709f2cf22839bf2d61790b448ad5"}, + {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:20b51fa3f588ff2fe658663db52a41a4f7aa6c04f6201449c6c7c476bd255c0d"}, + {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:410478a0c562d1a5bcc2f7ea448359fcb050ed48b3c6f6f4f18c313a9bdb1826"}, + {file = "frozenlist-1.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c6321c9efe29975232da3bd0af0ad216800a47e93d763ce64f291917a381b8eb"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48f6a4533887e189dae092f1cf981f2e3885175f7a0f33c91fb5b7b682b6bab6"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6eb73fa5426ea69ee0e012fb59cdc76a15b1283d6e32e4f8dc4482ec67d1194d"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbeb989b5cc29e8daf7f976b421c220f1b8c731cbf22b9130d8815418ea45887"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32453c1de775c889eb4e22f1197fe3bdfe457d16476ea407472b9442e6295f7a"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:693945278a31f2086d9bf3df0fe8254bbeaef1fe71e1351c3bd730aa7d31c41b"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1d0ce09d36d53bbbe566fe296965b23b961764c0bcf3ce2fa45f463745c04701"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3a670dc61eb0d0eb7080890c13de3066790f9049b47b0de04007090807c776b0"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:dca69045298ce5c11fd539682cff879cc1e664c245d1c64da929813e54241d11"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a06339f38e9ed3a64e4c4e43aec7f59084033647f908e4259d279a52d3757d09"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b7f2f9f912dca3934c1baec2e4585a674ef16fe00218d833856408c48d5beee7"}, + {file = "frozenlist-1.4.1-cp38-cp38-win32.whl", hash = "sha256:e7004be74cbb7d9f34553a5ce5fb08be14fb33bc86f332fb71cbe5216362a497"}, + {file = "frozenlist-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:5a7d70357e7cee13f470c7883a063aae5fe209a493c57d86eb7f5a6f910fae09"}, + {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bfa4a17e17ce9abf47a74ae02f32d014c5e9404b6d9ac7f729e01562bbee601e"}, + {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b7e3ed87d4138356775346e6845cccbe66cd9e207f3cd11d2f0b9fd13681359d"}, + {file = "frozenlist-1.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c99169d4ff810155ca50b4da3b075cbde79752443117d89429595c2e8e37fed8"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edb678da49d9f72c9f6c609fbe41a5dfb9a9282f9e6a2253d5a91e0fc382d7c0"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6db4667b187a6742b33afbbaf05a7bc551ffcf1ced0000a571aedbb4aa42fc7b"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55fdc093b5a3cb41d420884cdaf37a1e74c3c37a31f46e66286d9145d2063bd0"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82e8211d69a4f4bc360ea22cd6555f8e61a1bd211d1d5d39d3d228b48c83a897"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89aa2c2eeb20957be2d950b85974b30a01a762f3308cd02bb15e1ad632e22dc7"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9d3e0c25a2350080e9319724dede4f31f43a6c9779be48021a7f4ebde8b2d742"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7268252af60904bf52c26173cbadc3a071cece75f873705419c8681f24d3edea"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:0c250a29735d4f15321007fb02865f0e6b6a41a6b88f1f523ca1596ab5f50bd5"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:96ec70beabbd3b10e8bfe52616a13561e58fe84c0101dd031dc78f250d5128b9"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:23b2d7679b73fe0e5a4560b672a39f98dfc6f60df63823b0a9970525325b95f6"}, + {file = "frozenlist-1.4.1-cp39-cp39-win32.whl", hash = "sha256:a7496bfe1da7fb1a4e1cc23bb67c58fab69311cc7d32b5a99c2007b4b2a0e932"}, + {file = "frozenlist-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:e6a20a581f9ce92d389a8c7d7c3dd47c81fd5d6e655c8dddf341e14aa48659d0"}, + {file = "frozenlist-1.4.1-py3-none-any.whl", hash = "sha256:04ced3e6a46b4cfffe20f9ae482818e34eba9b5fb0ce4056e4cc9b6e212d09b7"}, + {file = "frozenlist-1.4.1.tar.gz", hash = "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b"}, +] + +[[package]] +name = "fsspec" +version = "2024.9.0" +description = "File-system specification" +optional = false +python-versions = ">=3.8" +files = [ + {file = "fsspec-2024.9.0-py3-none-any.whl", hash = "sha256:a0947d552d8a6efa72cc2c730b12c41d043509156966cca4fb157b0f2a0c574b"}, + {file = "fsspec-2024.9.0.tar.gz", hash = "sha256:4b0afb90c2f21832df142f292649035d80b421f60a9e1c027802e5a0da2b04e8"}, +] + +[package.extras] +abfs = ["adlfs"] +adl = ["adlfs"] +arrow = ["pyarrow (>=1)"] +dask = ["dask", "distributed"] +dev = ["pre-commit", "ruff"] +doc = ["numpydoc", "sphinx", "sphinx-design", "sphinx-rtd-theme", "yarl"] +dropbox = ["dropbox", "dropboxdrivefs", "requests"] +full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "dask", "distributed", "dropbox", "dropboxdrivefs", "fusepy", "gcsfs", "libarchive-c", "ocifs", "panel", "paramiko", "pyarrow (>=1)", "pygit2", "requests", "s3fs", "smbprotocol", "tqdm"] +fuse = ["fusepy"] +gcs = ["gcsfs"] +git = ["pygit2"] +github = ["requests"] +gs = ["gcsfs"] +gui = ["panel"] +hdfs = ["pyarrow (>=1)"] +http = ["aiohttp (!=4.0.0a0,!=4.0.0a1)"] +libarchive = ["libarchive-c"] +oci = ["ocifs"] +s3 = ["s3fs"] +sftp = ["paramiko"] +smb = ["smbprotocol"] +ssh = ["paramiko"] +test = ["aiohttp (!=4.0.0a0,!=4.0.0a1)", "numpy", "pytest", "pytest-asyncio (!=0.22.0)", "pytest-benchmark", "pytest-cov", "pytest-mock", "pytest-recording", "pytest-rerunfailures", "requests"] +test-downstream = ["aiobotocore (>=2.5.4,<3.0.0)", "dask-expr", "dask[dataframe,test]", "moto[server] (>4,<5)", "pytest-timeout", "xarray"] +test-full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "cloudpickle", "dask", "distributed", "dropbox", "dropboxdrivefs", "fastparquet", "fusepy", "gcsfs", "jinja2", "kerchunk", "libarchive-c", "lz4", "notebook", "numpy", "ocifs", "pandas", "panel", "paramiko", "pyarrow", "pyarrow (>=1)", "pyftpdlib", "pygit2", "pytest", "pytest-asyncio (!=0.22.0)", "pytest-benchmark", "pytest-cov", "pytest-mock", "pytest-recording", "pytest-rerunfailures", "python-snappy", "requests", "smbprotocol", "tqdm", "urllib3", "zarr", "zstandard"] +tqdm = ["tqdm"] + [[package]] name = "idna" -version = "3.7" +version = "3.10" description = "Internationalized Domain Names in Applications (IDNA)" optional = false -python-versions = ">=3.5" +python-versions = ">=3.6" files = [ - {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, - {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, + {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, + {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, ] +[package.extras] +all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] + [[package]] name = "iniconfig" version = "2.0.0" @@ -308,6 +855,140 @@ files = [ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] +[[package]] +name = "jmespath" +version = "1.0.1" +description = "JSON Matching Expressions" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980"}, + {file = "jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe"}, +] + +[[package]] +name = "kiwisolver" +version = "1.4.7" +description = "A fast implementation of the Cassowary constraint solver" +optional = false +python-versions = ">=3.8" +files = [ + {file = "kiwisolver-1.4.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8a9c83f75223d5e48b0bc9cb1bf2776cf01563e00ade8775ffe13b0b6e1af3a6"}, + {file = "kiwisolver-1.4.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:58370b1ffbd35407444d57057b57da5d6549d2d854fa30249771775c63b5fe17"}, + {file = "kiwisolver-1.4.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aa0abdf853e09aff551db11fce173e2177d00786c688203f52c87ad7fcd91ef9"}, + {file = "kiwisolver-1.4.7-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:8d53103597a252fb3ab8b5845af04c7a26d5e7ea8122303dd7a021176a87e8b9"}, + {file = "kiwisolver-1.4.7-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:88f17c5ffa8e9462fb79f62746428dd57b46eb931698e42e990ad63103f35e6c"}, + {file = "kiwisolver-1.4.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88a9ca9c710d598fd75ee5de59d5bda2684d9db36a9f50b6125eaea3969c2599"}, + {file = "kiwisolver-1.4.7-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f4d742cb7af1c28303a51b7a27aaee540e71bb8e24f68c736f6f2ffc82f2bf05"}, + {file = "kiwisolver-1.4.7-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e28c7fea2196bf4c2f8d46a0415c77a1c480cc0724722f23d7410ffe9842c407"}, + {file = "kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e968b84db54f9d42046cf154e02911e39c0435c9801681e3fc9ce8a3c4130278"}, + {file = "kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0c18ec74c0472de033e1bebb2911c3c310eef5649133dd0bedf2a169a1b269e5"}, + {file = "kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8f0ea6da6d393d8b2e187e6a5e3fb81f5862010a40c3945e2c6d12ae45cfb2ad"}, + {file = "kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:f106407dda69ae456dd1227966bf445b157ccc80ba0dff3802bb63f30b74e895"}, + {file = "kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:84ec80df401cfee1457063732d90022f93951944b5b58975d34ab56bb150dfb3"}, + {file = "kiwisolver-1.4.7-cp310-cp310-win32.whl", hash = "sha256:71bb308552200fb2c195e35ef05de12f0c878c07fc91c270eb3d6e41698c3bcc"}, + {file = "kiwisolver-1.4.7-cp310-cp310-win_amd64.whl", hash = "sha256:44756f9fd339de0fb6ee4f8c1696cfd19b2422e0d70b4cefc1cc7f1f64045a8c"}, + {file = "kiwisolver-1.4.7-cp310-cp310-win_arm64.whl", hash = "sha256:78a42513018c41c2ffd262eb676442315cbfe3c44eed82385c2ed043bc63210a"}, + {file = "kiwisolver-1.4.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d2b0e12a42fb4e72d509fc994713d099cbb15ebf1103545e8a45f14da2dfca54"}, + {file = "kiwisolver-1.4.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2a8781ac3edc42ea4b90bc23e7d37b665d89423818e26eb6df90698aa2287c95"}, + {file = "kiwisolver-1.4.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:46707a10836894b559e04b0fd143e343945c97fd170d69a2d26d640b4e297935"}, + {file = "kiwisolver-1.4.7-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef97b8df011141c9b0f6caf23b29379f87dd13183c978a30a3c546d2c47314cb"}, + {file = "kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ab58c12a2cd0fc769089e6d38466c46d7f76aced0a1f54c77652446733d2d02"}, + {file = "kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:803b8e1459341c1bb56d1c5c010406d5edec8a0713a0945851290a7930679b51"}, + {file = "kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f9a9e8a507420fe35992ee9ecb302dab68550dedc0da9e2880dd88071c5fb052"}, + {file = "kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18077b53dc3bb490e330669a99920c5e6a496889ae8c63b58fbc57c3d7f33a18"}, + {file = "kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6af936f79086a89b3680a280c47ea90b4df7047b5bdf3aa5c524bbedddb9e545"}, + {file = "kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:3abc5b19d24af4b77d1598a585b8a719beb8569a71568b66f4ebe1fb0449460b"}, + {file = "kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:933d4de052939d90afbe6e9d5273ae05fb836cc86c15b686edd4b3560cc0ee36"}, + {file = "kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:65e720d2ab2b53f1f72fb5da5fb477455905ce2c88aaa671ff0a447c2c80e8e3"}, + {file = "kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3bf1ed55088f214ba6427484c59553123fdd9b218a42bbc8c6496d6754b1e523"}, + {file = "kiwisolver-1.4.7-cp311-cp311-win32.whl", hash = "sha256:4c00336b9dd5ad96d0a558fd18a8b6f711b7449acce4c157e7343ba92dd0cf3d"}, + {file = "kiwisolver-1.4.7-cp311-cp311-win_amd64.whl", hash = "sha256:929e294c1ac1e9f615c62a4e4313ca1823ba37326c164ec720a803287c4c499b"}, + {file = "kiwisolver-1.4.7-cp311-cp311-win_arm64.whl", hash = "sha256:e33e8fbd440c917106b237ef1a2f1449dfbb9b6f6e1ce17c94cd6a1e0d438376"}, + {file = "kiwisolver-1.4.7-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:5360cc32706dab3931f738d3079652d20982511f7c0ac5711483e6eab08efff2"}, + {file = "kiwisolver-1.4.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:942216596dc64ddb25adb215c3c783215b23626f8d84e8eff8d6d45c3f29f75a"}, + {file = "kiwisolver-1.4.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:48b571ecd8bae15702e4f22d3ff6a0f13e54d3d00cd25216d5e7f658242065ee"}, + {file = "kiwisolver-1.4.7-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ad42ba922c67c5f219097b28fae965e10045ddf145d2928bfac2eb2e17673640"}, + {file = "kiwisolver-1.4.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:612a10bdae23404a72941a0fc8fa2660c6ea1217c4ce0dbcab8a8f6543ea9e7f"}, + {file = "kiwisolver-1.4.7-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9e838bba3a3bac0fe06d849d29772eb1afb9745a59710762e4ba3f4cb8424483"}, + {file = "kiwisolver-1.4.7-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:22f499f6157236c19f4bbbd472fa55b063db77a16cd74d49afe28992dff8c258"}, + {file = "kiwisolver-1.4.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:693902d433cf585133699972b6d7c42a8b9f8f826ebcaf0132ff55200afc599e"}, + {file = "kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4e77f2126c3e0b0d055f44513ed349038ac180371ed9b52fe96a32aa071a5107"}, + {file = "kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:657a05857bda581c3656bfc3b20e353c232e9193eb167766ad2dc58b56504948"}, + {file = "kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4bfa75a048c056a411f9705856abfc872558e33c055d80af6a380e3658766038"}, + {file = "kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:34ea1de54beef1c104422d210c47c7d2a4999bdecf42c7b5718fbe59a4cac383"}, + {file = "kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:90da3b5f694b85231cf93586dad5e90e2d71b9428f9aad96952c99055582f520"}, + {file = "kiwisolver-1.4.7-cp312-cp312-win32.whl", hash = "sha256:18e0cca3e008e17fe9b164b55735a325140a5a35faad8de92dd80265cd5eb80b"}, + {file = "kiwisolver-1.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:58cb20602b18f86f83a5c87d3ee1c766a79c0d452f8def86d925e6c60fbf7bfb"}, + {file = "kiwisolver-1.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:f5a8b53bdc0b3961f8b6125e198617c40aeed638b387913bf1ce78afb1b0be2a"}, + {file = "kiwisolver-1.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2e6039dcbe79a8e0f044f1c39db1986a1b8071051efba3ee4d74f5b365f5226e"}, + {file = "kiwisolver-1.4.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a1ecf0ac1c518487d9d23b1cd7139a6a65bc460cd101ab01f1be82ecf09794b6"}, + {file = "kiwisolver-1.4.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7ab9ccab2b5bd5702ab0803676a580fffa2aa178c2badc5557a84cc943fcf750"}, + {file = "kiwisolver-1.4.7-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f816dd2277f8d63d79f9c8473a79fe54047bc0467754962840782c575522224d"}, + {file = "kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf8bcc23ceb5a1b624572a1623b9f79d2c3b337c8c455405ef231933a10da379"}, + {file = "kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dea0bf229319828467d7fca8c7c189780aa9ff679c94539eed7532ebe33ed37c"}, + {file = "kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c06a4c7cf15ec739ce0e5971b26c93638730090add60e183530d70848ebdd34"}, + {file = "kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:913983ad2deb14e66d83c28b632fd35ba2b825031f2fa4ca29675e665dfecbe1"}, + {file = "kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5337ec7809bcd0f424c6b705ecf97941c46279cf5ed92311782c7c9c2026f07f"}, + {file = "kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4c26ed10c4f6fa6ddb329a5120ba3b6db349ca192ae211e882970bfc9d91420b"}, + {file = "kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c619b101e6de2222c1fcb0531e1b17bbffbe54294bfba43ea0d411d428618c27"}, + {file = "kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:073a36c8273647592ea332e816e75ef8da5c303236ec0167196793eb1e34657a"}, + {file = "kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3ce6b2b0231bda412463e152fc18335ba32faf4e8c23a754ad50ffa70e4091ee"}, + {file = "kiwisolver-1.4.7-cp313-cp313-win32.whl", hash = "sha256:f4c9aee212bc89d4e13f58be11a56cc8036cabad119259d12ace14b34476fd07"}, + {file = "kiwisolver-1.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:8a3ec5aa8e38fc4c8af308917ce12c536f1c88452ce554027e55b22cbbfbff76"}, + {file = "kiwisolver-1.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:76c8094ac20ec259471ac53e774623eb62e6e1f56cd8690c67ce6ce4fcb05650"}, + {file = "kiwisolver-1.4.7-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5d5abf8f8ec1f4e22882273c423e16cae834c36856cac348cfbfa68e01c40f3a"}, + {file = "kiwisolver-1.4.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:aeb3531b196ef6f11776c21674dba836aeea9d5bd1cf630f869e3d90b16cfade"}, + {file = "kiwisolver-1.4.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b7d755065e4e866a8086c9bdada157133ff466476a2ad7861828e17b6026e22c"}, + {file = "kiwisolver-1.4.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08471d4d86cbaec61f86b217dd938a83d85e03785f51121e791a6e6689a3be95"}, + {file = "kiwisolver-1.4.7-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7bbfcb7165ce3d54a3dfbe731e470f65739c4c1f85bb1018ee912bae139e263b"}, + {file = "kiwisolver-1.4.7-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d34eb8494bea691a1a450141ebb5385e4b69d38bb8403b5146ad279f4b30fa3"}, + {file = "kiwisolver-1.4.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9242795d174daa40105c1d86aba618e8eab7bf96ba8c3ee614da8302a9f95503"}, + {file = "kiwisolver-1.4.7-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a0f64a48bb81af7450e641e3fe0b0394d7381e342805479178b3d335d60ca7cf"}, + {file = "kiwisolver-1.4.7-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:8e045731a5416357638d1700927529e2b8ab304811671f665b225f8bf8d8f933"}, + {file = "kiwisolver-1.4.7-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:4322872d5772cae7369f8351da1edf255a604ea7087fe295411397d0cfd9655e"}, + {file = "kiwisolver-1.4.7-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:e1631290ee9271dffe3062d2634c3ecac02c83890ada077d225e081aca8aab89"}, + {file = "kiwisolver-1.4.7-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:edcfc407e4eb17e037bca59be0e85a2031a2ac87e4fed26d3e9df88b4165f92d"}, + {file = "kiwisolver-1.4.7-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:4d05d81ecb47d11e7f8932bd8b61b720bf0b41199358f3f5e36d38e28f0532c5"}, + {file = "kiwisolver-1.4.7-cp38-cp38-win32.whl", hash = "sha256:b38ac83d5f04b15e515fd86f312479d950d05ce2368d5413d46c088dda7de90a"}, + {file = "kiwisolver-1.4.7-cp38-cp38-win_amd64.whl", hash = "sha256:d83db7cde68459fc803052a55ace60bea2bae361fc3b7a6d5da07e11954e4b09"}, + {file = "kiwisolver-1.4.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3f9362ecfca44c863569d3d3c033dbe8ba452ff8eed6f6b5806382741a1334bd"}, + {file = "kiwisolver-1.4.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e8df2eb9b2bac43ef8b082e06f750350fbbaf2887534a5be97f6cf07b19d9583"}, + {file = "kiwisolver-1.4.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f32d6edbc638cde7652bd690c3e728b25332acbadd7cad670cc4a02558d9c417"}, + {file = "kiwisolver-1.4.7-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e2e6c39bd7b9372b0be21456caab138e8e69cc0fc1190a9dfa92bd45a1e6e904"}, + {file = "kiwisolver-1.4.7-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:dda56c24d869b1193fcc763f1284b9126550eaf84b88bbc7256e15028f19188a"}, + {file = "kiwisolver-1.4.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79849239c39b5e1fd906556c474d9b0439ea6792b637511f3fe3a41158d89ca8"}, + {file = "kiwisolver-1.4.7-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5e3bc157fed2a4c02ec468de4ecd12a6e22818d4f09cde2c31ee3226ffbefab2"}, + {file = "kiwisolver-1.4.7-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3da53da805b71e41053dc670f9a820d1157aae77b6b944e08024d17bcd51ef88"}, + {file = "kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8705f17dfeb43139a692298cb6637ee2e59c0194538153e83e9ee0c75c2eddde"}, + {file = "kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:82a5c2f4b87c26bb1a0ef3d16b5c4753434633b83d365cc0ddf2770c93829e3c"}, + {file = "kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce8be0466f4c0d585cdb6c1e2ed07232221df101a4c6f28821d2aa754ca2d9e2"}, + {file = "kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:409afdfe1e2e90e6ee7fc896f3df9a7fec8e793e58bfa0d052c8a82f99c37abb"}, + {file = "kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5b9c3f4ee0b9a439d2415012bd1b1cc2df59e4d6a9939f4d669241d30b414327"}, + {file = "kiwisolver-1.4.7-cp39-cp39-win32.whl", hash = "sha256:a79ae34384df2b615eefca647a2873842ac3b596418032bef9a7283675962644"}, + {file = "kiwisolver-1.4.7-cp39-cp39-win_amd64.whl", hash = "sha256:cf0438b42121a66a3a667de17e779330fc0f20b0d97d59d2f2121e182b0505e4"}, + {file = "kiwisolver-1.4.7-cp39-cp39-win_arm64.whl", hash = "sha256:764202cc7e70f767dab49e8df52c7455e8de0df5d858fa801a11aa0d882ccf3f"}, + {file = "kiwisolver-1.4.7-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:94252291e3fe68001b1dd747b4c0b3be12582839b95ad4d1b641924d68fd4643"}, + {file = "kiwisolver-1.4.7-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5b7dfa3b546da08a9f622bb6becdb14b3e24aaa30adba66749d38f3cc7ea9706"}, + {file = "kiwisolver-1.4.7-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd3de6481f4ed8b734da5df134cd5a6a64fe32124fe83dde1e5b5f29fe30b1e6"}, + {file = "kiwisolver-1.4.7-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a91b5f9f1205845d488c928e8570dcb62b893372f63b8b6e98b863ebd2368ff2"}, + {file = "kiwisolver-1.4.7-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40fa14dbd66b8b8f470d5fc79c089a66185619d31645f9b0773b88b19f7223c4"}, + {file = "kiwisolver-1.4.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:eb542fe7933aa09d8d8f9d9097ef37532a7df6497819d16efe4359890a2f417a"}, + {file = "kiwisolver-1.4.7-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:bfa1acfa0c54932d5607e19a2c24646fb4c1ae2694437789129cf099789a3b00"}, + {file = "kiwisolver-1.4.7-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:eee3ea935c3d227d49b4eb85660ff631556841f6e567f0f7bda972df6c2c9935"}, + {file = "kiwisolver-1.4.7-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f3160309af4396e0ed04db259c3ccbfdc3621b5559b5453075e5de555e1f3a1b"}, + {file = "kiwisolver-1.4.7-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a17f6a29cf8935e587cc8a4dbfc8368c55edc645283db0ce9801016f83526c2d"}, + {file = "kiwisolver-1.4.7-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:10849fb2c1ecbfae45a693c070e0320a91b35dd4bcf58172c023b994283a124d"}, + {file = "kiwisolver-1.4.7-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:ac542bf38a8a4be2dc6b15248d36315ccc65f0743f7b1a76688ffb6b5129a5c2"}, + {file = "kiwisolver-1.4.7-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:8b01aac285f91ca889c800042c35ad3b239e704b150cfd3382adfc9dcc780e39"}, + {file = "kiwisolver-1.4.7-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:48be928f59a1f5c8207154f935334d374e79f2b5d212826307d072595ad76a2e"}, + {file = "kiwisolver-1.4.7-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f37cfe618a117e50d8c240555331160d73d0411422b59b5ee217843d7b693608"}, + {file = "kiwisolver-1.4.7-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:599b5c873c63a1f6ed7eead644a8a380cfbdf5db91dcb6f85707aaab213b1674"}, + {file = "kiwisolver-1.4.7-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:801fa7802e5cfabe3ab0c81a34c323a319b097dfb5004be950482d882f3d7225"}, + {file = "kiwisolver-1.4.7-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:0c6c43471bc764fad4bc99c5c2d6d16a676b1abf844ca7c8702bdae92df01ee0"}, + {file = "kiwisolver-1.4.7.tar.gz", hash = "sha256:9893ff81bd7107f7b685d3017cc6583daadb4fc26e4a888350df530e41980a60"}, +] + [[package]] name = "lxml" version = "5.3.0" @@ -486,6 +1167,69 @@ profiling = ["gprof2dot"] rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] +[[package]] +name = "matplotlib" +version = "3.9.2" +description = "Python plotting package" +optional = false +python-versions = ">=3.9" +files = [ + {file = "matplotlib-3.9.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:9d78bbc0cbc891ad55b4f39a48c22182e9bdaea7fc0e5dbd364f49f729ca1bbb"}, + {file = "matplotlib-3.9.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c375cc72229614632c87355366bdf2570c2dac01ac66b8ad048d2dabadf2d0d4"}, + {file = "matplotlib-3.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d94ff717eb2bd0b58fe66380bd8b14ac35f48a98e7c6765117fe67fb7684e64"}, + {file = "matplotlib-3.9.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab68d50c06938ef28681073327795c5db99bb4666214d2d5f880ed11aeaded66"}, + {file = "matplotlib-3.9.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:65aacf95b62272d568044531e41de26285d54aec8cb859031f511f84bd8b495a"}, + {file = "matplotlib-3.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:3fd595f34aa8a55b7fc8bf9ebea8aa665a84c82d275190a61118d33fbc82ccae"}, + {file = "matplotlib-3.9.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d8dd059447824eec055e829258ab092b56bb0579fc3164fa09c64f3acd478772"}, + {file = "matplotlib-3.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c797dac8bb9c7a3fd3382b16fe8f215b4cf0f22adccea36f1545a6d7be310b41"}, + {file = "matplotlib-3.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d719465db13267bcef19ea8954a971db03b9f48b4647e3860e4bc8e6ed86610f"}, + {file = "matplotlib-3.9.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8912ef7c2362f7193b5819d17dae8629b34a95c58603d781329712ada83f9447"}, + {file = "matplotlib-3.9.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7741f26a58a240f43bee74965c4882b6c93df3e7eb3de160126d8c8f53a6ae6e"}, + {file = "matplotlib-3.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:ae82a14dab96fbfad7965403c643cafe6515e386de723e498cf3eeb1e0b70cc7"}, + {file = "matplotlib-3.9.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:ac43031375a65c3196bee99f6001e7fa5bdfb00ddf43379d3c0609bdca042df9"}, + {file = "matplotlib-3.9.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:be0fc24a5e4531ae4d8e858a1a548c1fe33b176bb13eff7f9d0d38ce5112a27d"}, + {file = "matplotlib-3.9.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf81de2926c2db243c9b2cbc3917619a0fc85796c6ba4e58f541df814bbf83c7"}, + {file = "matplotlib-3.9.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6ee45bc4245533111ced13f1f2cace1e7f89d1c793390392a80c139d6cf0e6c"}, + {file = "matplotlib-3.9.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:306c8dfc73239f0e72ac50e5a9cf19cc4e8e331dd0c54f5e69ca8758550f1e1e"}, + {file = "matplotlib-3.9.2-cp312-cp312-win_amd64.whl", hash = "sha256:5413401594cfaff0052f9d8b1aafc6d305b4bd7c4331dccd18f561ff7e1d3bd3"}, + {file = "matplotlib-3.9.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:18128cc08f0d3cfff10b76baa2f296fc28c4607368a8402de61bb3f2eb33c7d9"}, + {file = "matplotlib-3.9.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4876d7d40219e8ae8bb70f9263bcbe5714415acfdf781086601211335e24f8aa"}, + {file = "matplotlib-3.9.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d9f07a80deab4bb0b82858a9e9ad53d1382fd122be8cde11080f4e7dfedb38b"}, + {file = "matplotlib-3.9.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7c0410f181a531ec4e93bbc27692f2c71a15c2da16766f5ba9761e7ae518413"}, + {file = "matplotlib-3.9.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:909645cce2dc28b735674ce0931a4ac94e12f5b13f6bb0b5a5e65e7cea2c192b"}, + {file = "matplotlib-3.9.2-cp313-cp313-win_amd64.whl", hash = "sha256:f32c7410c7f246838a77d6d1eff0c0f87f3cb0e7c4247aebea71a6d5a68cab49"}, + {file = "matplotlib-3.9.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:37e51dd1c2db16ede9cfd7b5cabdfc818b2c6397c83f8b10e0e797501c963a03"}, + {file = "matplotlib-3.9.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b82c5045cebcecd8496a4d694d43f9cc84aeeb49fe2133e036b207abe73f4d30"}, + {file = "matplotlib-3.9.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f053c40f94bc51bc03832a41b4f153d83f2062d88c72b5e79997072594e97e51"}, + {file = "matplotlib-3.9.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dbe196377a8248972f5cede786d4c5508ed5f5ca4a1e09b44bda889958b33f8c"}, + {file = "matplotlib-3.9.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5816b1e1fe8c192cbc013f8f3e3368ac56fbecf02fb41b8f8559303f24c5015e"}, + {file = "matplotlib-3.9.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:cef2a73d06601437be399908cf13aee74e86932a5ccc6ccdf173408ebc5f6bb2"}, + {file = "matplotlib-3.9.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e0830e188029c14e891fadd99702fd90d317df294c3298aad682739c5533721a"}, + {file = "matplotlib-3.9.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:03ba9c1299c920964e8d3857ba27173b4dbb51ca4bab47ffc2c2ba0eb5e2cbc5"}, + {file = "matplotlib-3.9.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1cd93b91ab47a3616b4d3c42b52f8363b88ca021e340804c6ab2536344fad9ca"}, + {file = "matplotlib-3.9.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6d1ce5ed2aefcdce11904fc5bbea7d9c21fff3d5f543841edf3dea84451a09ea"}, + {file = "matplotlib-3.9.2-cp39-cp39-win_amd64.whl", hash = "sha256:b2696efdc08648536efd4e1601b5fd491fd47f4db97a5fbfd175549a7365c1b2"}, + {file = "matplotlib-3.9.2-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:d52a3b618cb1cbb769ce2ee1dcdb333c3ab6e823944e9a2d36e37253815f9556"}, + {file = "matplotlib-3.9.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:039082812cacd6c6bec8e17a9c1e6baca230d4116d522e81e1f63a74d01d2e21"}, + {file = "matplotlib-3.9.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6758baae2ed64f2331d4fd19be38b7b4eae3ecec210049a26b6a4f3ae1c85dcc"}, + {file = "matplotlib-3.9.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:050598c2b29e0b9832cde72bcf97627bf00262adbc4a54e2b856426bb2ef0697"}, + {file = "matplotlib-3.9.2.tar.gz", hash = "sha256:96ab43906269ca64a6366934106fa01534454a69e471b7bf3d79083981aaab92"}, +] + +[package.dependencies] +contourpy = ">=1.0.1" +cycler = ">=0.10" +fonttools = ">=4.22.0" +kiwisolver = ">=1.3.1" +numpy = ">=1.23" +packaging = ">=20.0" +pillow = ">=8" +pyparsing = ">=2.3.1" +python-dateutil = ">=2.7" + +[package.extras] +dev = ["meson-python (>=0.13.1)", "numpy (>=1.25)", "pybind11 (>=2.6)", "setuptools (>=64)", "setuptools_scm (>=7)"] + [[package]] name = "mdurl" version = "0.1.2" @@ -497,6 +1241,107 @@ files = [ {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, ] +[[package]] +name = "multidict" +version = "6.1.0" +description = "multidict implementation" +optional = false +python-versions = ">=3.8" +files = [ + {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3380252550e372e8511d49481bd836264c009adb826b23fefcc5dd3c69692f60"}, + {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:99f826cbf970077383d7de805c0681799491cb939c25450b9b5b3ced03ca99f1"}, + {file = "multidict-6.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a114d03b938376557927ab23f1e950827c3b893ccb94b62fd95d430fd0e5cf53"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1c416351ee6271b2f49b56ad7f308072f6f44b37118d69c2cad94f3fa8a40d5"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6b5d83030255983181005e6cfbac1617ce9746b219bc2aad52201ad121226581"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3e97b5e938051226dc025ec80980c285b053ffb1e25a3db2a3aa3bc046bf7f56"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d618649d4e70ac6efcbba75be98b26ef5078faad23592f9b51ca492953012429"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10524ebd769727ac77ef2278390fb0068d83f3acb7773792a5080f2b0abf7748"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ff3827aef427c89a25cc96ded1759271a93603aba9fb977a6d264648ebf989db"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:06809f4f0f7ab7ea2cabf9caca7d79c22c0758b58a71f9d32943ae13c7ace056"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f179dee3b863ab1c59580ff60f9d99f632f34ccb38bf67a33ec6b3ecadd0fd76"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:aaed8b0562be4a0876ee3b6946f6869b7bcdb571a5d1496683505944e268b160"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3c8b88a2ccf5493b6c8da9076fb151ba106960a2df90c2633f342f120751a9e7"}, + {file = "multidict-6.1.0-cp310-cp310-win32.whl", hash = "sha256:4a9cb68166a34117d6646c0023c7b759bf197bee5ad4272f420a0141d7eb03a0"}, + {file = "multidict-6.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:20b9b5fbe0b88d0bdef2012ef7dee867f874b72528cf1d08f1d59b0e3850129d"}, + {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3efe2c2cb5763f2f1b275ad2bf7a287d3f7ebbef35648a9726e3b69284a4f3d6"}, + {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7053d3b0353a8b9de430a4f4b4268ac9a4fb3481af37dfe49825bf45ca24156"}, + {file = "multidict-6.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:27e5fc84ccef8dfaabb09d82b7d179c7cf1a3fbc8a966f8274fcb4ab2eb4cadb"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e2b90b43e696f25c62656389d32236e049568b39320e2735d51f08fd362761b"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d83a047959d38a7ff552ff94be767b7fd79b831ad1cd9920662db05fec24fe72"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1a9dd711d0877a1ece3d2e4fea11a8e75741ca21954c919406b44e7cf971304"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec2abea24d98246b94913b76a125e855eb5c434f7c46546046372fe60f666351"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4867cafcbc6585e4b678876c489b9273b13e9fff9f6d6d66add5e15d11d926cb"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5b48204e8d955c47c55b72779802b219a39acc3ee3d0116d5080c388970b76e3"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:d8fff389528cad1618fb4b26b95550327495462cd745d879a8c7c2115248e399"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a7a9541cd308eed5e30318430a9c74d2132e9a8cb46b901326272d780bf2d423"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:da1758c76f50c39a2efd5e9859ce7d776317eb1dd34317c8152ac9251fc574a3"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c943a53e9186688b45b323602298ab727d8865d8c9ee0b17f8d62d14b56f0753"}, + {file = "multidict-6.1.0-cp311-cp311-win32.whl", hash = "sha256:90f8717cb649eea3504091e640a1b8568faad18bd4b9fcd692853a04475a4b80"}, + {file = "multidict-6.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:82176036e65644a6cc5bd619f65f6f19781e8ec2e5330f51aa9ada7504cc1926"}, + {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b04772ed465fa3cc947db808fa306d79b43e896beb677a56fb2347ca1a49c1fa"}, + {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6180c0ae073bddeb5a97a38c03f30c233e0a4d39cd86166251617d1bbd0af436"}, + {file = "multidict-6.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:071120490b47aa997cca00666923a83f02c7fbb44f71cf7f136df753f7fa8761"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50b3a2710631848991d0bf7de077502e8994c804bb805aeb2925a981de58ec2e"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b58c621844d55e71c1b7f7c498ce5aa6985d743a1a59034c57a905b3f153c1ef"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55b6d90641869892caa9ca42ff913f7ff1c5ece06474fbd32fb2cf6834726c95"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b820514bfc0b98a30e3d85462084779900347e4d49267f747ff54060cc33925"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10a9b09aba0c5b48c53761b7c720aaaf7cf236d5fe394cd399c7ba662d5f9966"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e16bf3e5fc9f44632affb159d30a437bfe286ce9e02754759be5536b169b305"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76f364861c3bfc98cbbcbd402d83454ed9e01a5224bb3a28bf70002a230f73e2"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:820c661588bd01a0aa62a1283f20d2be4281b086f80dad9e955e690c75fb54a2"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:0e5f362e895bc5b9e67fe6e4ded2492d8124bdf817827f33c5b46c2fe3ffaca6"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3ec660d19bbc671e3a6443325f07263be452c453ac9e512f5eb935e7d4ac28b3"}, + {file = "multidict-6.1.0-cp312-cp312-win32.whl", hash = "sha256:58130ecf8f7b8112cdb841486404f1282b9c86ccb30d3519faf301b2e5659133"}, + {file = "multidict-6.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:188215fc0aafb8e03341995e7c4797860181562380f81ed0a87ff455b70bf1f1"}, + {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d569388c381b24671589335a3be6e1d45546c2988c2ebe30fdcada8457a31008"}, + {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:052e10d2d37810b99cc170b785945421141bf7bb7d2f8799d431e7db229c385f"}, + {file = "multidict-6.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f90c822a402cb865e396a504f9fc8173ef34212a342d92e362ca498cad308e28"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b225d95519a5bf73860323e633a664b0d85ad3d5bede6d30d95b35d4dfe8805b"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:23bfd518810af7de1116313ebd9092cb9aa629beb12f6ed631ad53356ed6b86c"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c09fcfdccdd0b57867577b719c69e347a436b86cd83747f179dbf0cc0d4c1f3"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf6bea52ec97e95560af5ae576bdac3aa3aae0b6758c6efa115236d9e07dae44"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57feec87371dbb3520da6192213c7d6fc892d5589a93db548331954de8248fd2"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0c3f390dc53279cbc8ba976e5f8035eab997829066756d811616b652b00a23a3"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:59bfeae4b25ec05b34f1956eaa1cb38032282cd4dfabc5056d0a1ec4d696d3aa"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b2f59caeaf7632cc633b5cf6fc449372b83bbdf0da4ae04d5be36118e46cc0aa"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:37bb93b2178e02b7b618893990941900fd25b6b9ac0fa49931a40aecdf083fe4"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4e9f48f58c2c523d5a06faea47866cd35b32655c46b443f163d08c6d0ddb17d6"}, + {file = "multidict-6.1.0-cp313-cp313-win32.whl", hash = "sha256:3a37ffb35399029b45c6cc33640a92bef403c9fd388acce75cdc88f58bd19a81"}, + {file = "multidict-6.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:e9aa71e15d9d9beaad2c6b9319edcdc0a49a43ef5c0a4c8265ca9ee7d6c67774"}, + {file = "multidict-6.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:db7457bac39421addd0c8449933ac32d8042aae84a14911a757ae6ca3eef1392"}, + {file = "multidict-6.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d094ddec350a2fb899fec68d8353c78233debde9b7d8b4beeafa70825f1c281a"}, + {file = "multidict-6.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5845c1fd4866bb5dd3125d89b90e57ed3138241540897de748cdf19de8a2fca2"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9079dfc6a70abe341f521f78405b8949f96db48da98aeb43f9907f342f627cdc"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3914f5aaa0f36d5d60e8ece6a308ee1c9784cd75ec8151062614657a114c4478"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c08be4f460903e5a9d0f76818db3250f12e9c344e79314d1d570fc69d7f4eae4"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d093be959277cb7dee84b801eb1af388b6ad3ca6a6b6bf1ed7585895789d027d"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3702ea6872c5a2a4eeefa6ffd36b042e9773f05b1f37ae3ef7264b1163c2dcf6"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:2090f6a85cafc5b2db085124d752757c9d251548cedabe9bd31afe6363e0aff2"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:f67f217af4b1ff66c68a87318012de788dd95fcfeb24cc889011f4e1c7454dfd"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:189f652a87e876098bbc67b4da1049afb5f5dfbaa310dd67c594b01c10388db6"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:6bb5992037f7a9eff7991ebe4273ea7f51f1c1c511e6a2ce511d0e7bdb754492"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:ac10f4c2b9e770c4e393876e35a7046879d195cd123b4f116d299d442b335bcd"}, + {file = "multidict-6.1.0-cp38-cp38-win32.whl", hash = "sha256:e27bbb6d14416713a8bd7aaa1313c0fc8d44ee48d74497a0ff4c3a1b6ccb5167"}, + {file = "multidict-6.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:22f3105d4fb15c8f57ff3959a58fcab6ce36814486500cd7485651230ad4d4ef"}, + {file = "multidict-6.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4e18b656c5e844539d506a0a06432274d7bd52a7487e6828c63a63d69185626c"}, + {file = "multidict-6.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a185f876e69897a6f3325c3f19f26a297fa058c5e456bfcff8015e9a27e83ae1"}, + {file = "multidict-6.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ab7c4ceb38d91570a650dba194e1ca87c2b543488fe9309b4212694174fd539c"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e617fb6b0b6953fffd762669610c1c4ffd05632c138d61ac7e14ad187870669c"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:16e5f4bf4e603eb1fdd5d8180f1a25f30056f22e55ce51fb3d6ad4ab29f7d96f"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4c035da3f544b1882bac24115f3e2e8760f10a0107614fc9839fd232200b875"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:957cf8e4b6e123a9eea554fa7ebc85674674b713551de587eb318a2df3e00255"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:483a6aea59cb89904e1ceabd2b47368b5600fb7de78a6e4a2c2987b2d256cf30"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:87701f25a2352e5bf7454caa64757642734da9f6b11384c1f9d1a8e699758057"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:682b987361e5fd7a139ed565e30d81fd81e9629acc7d925a205366877d8c8657"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce2186a7df133a9c895dea3331ddc5ddad42cdd0d1ea2f0a51e5d161e4762f28"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9f636b730f7e8cb19feb87094949ba54ee5357440b9658b2a32a5ce4bce53972"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:73eae06aa53af2ea5270cc066dcaf02cc60d2994bbb2c4ef5764949257d10f43"}, + {file = "multidict-6.1.0-cp39-cp39-win32.whl", hash = "sha256:1ca0083e80e791cffc6efce7660ad24af66c8d4079d2a750b29001b53ff59ada"}, + {file = "multidict-6.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:aa466da5b15ccea564bdab9c89175c762bc12825f4659c11227f515cee76fa4a"}, + {file = "multidict-6.1.0-py3-none-any.whl", hash = "sha256:48e171e52d1c4d33888e529b999e5900356b9ae588c2f09a52dcefb158b27506"}, + {file = "multidict-6.1.0.tar.gz", hash = "sha256:22ae2ebf9b0c69d206c003e2f6a914ea33f0a932d4aa16f236afc049d9958f4a"}, +] + [[package]] name = "multimethod" version = "1.10" @@ -521,47 +1366,64 @@ files = [ [[package]] name = "numpy" -version = "1.26.4" +version = "2.1.1" description = "Fundamental package for array computing in Python" optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" files = [ - {file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"}, - {file = "numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a"}, - {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4"}, - {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f"}, - {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a"}, - {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2"}, - {file = "numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07"}, - {file = "numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5"}, - {file = "numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71"}, - {file = "numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef"}, - {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e"}, - {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5"}, - {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a"}, - {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a"}, - {file = "numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20"}, - {file = "numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2"}, - {file = "numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218"}, - {file = "numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b"}, - {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b"}, - {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed"}, - {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a"}, - {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0"}, - {file = "numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110"}, - {file = "numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818"}, - {file = "numpy-1.26.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7349ab0fa0c429c82442a27a9673fc802ffdb7c7775fad780226cb234965e53c"}, - {file = "numpy-1.26.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:52b8b60467cd7dd1e9ed082188b4e6bb35aa5cdd01777621a1658910745b90be"}, - {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5241e0a80d808d70546c697135da2c613f30e28251ff8307eb72ba696945764"}, - {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f870204a840a60da0b12273ef34f7051e98c3b5961b61b0c2c1be6dfd64fbcd3"}, - {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:679b0076f67ecc0138fd2ede3a8fd196dddc2ad3254069bcb9faf9a79b1cebcd"}, - {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:47711010ad8555514b434df65f7d7b076bb8261df1ca9bb78f53d3b2db02e95c"}, - {file = "numpy-1.26.4-cp39-cp39-win32.whl", hash = "sha256:a354325ee03388678242a4d7ebcd08b5c727033fcff3b2f536aea978e15ee9e6"}, - {file = "numpy-1.26.4-cp39-cp39-win_amd64.whl", hash = "sha256:3373d5d70a5fe74a2c1bb6d2cfd9609ecf686d47a2d7b1d37a8f3b6bf6003aea"}, - {file = "numpy-1.26.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:afedb719a9dcfc7eaf2287b839d8198e06dcd4cb5d276a3df279231138e83d30"}, - {file = "numpy-1.26.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95a7476c59002f2f6c590b9b7b998306fba6a5aa646b1e22ddfeaf8f78c3a29c"}, - {file = "numpy-1.26.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7e50d0a0cc3189f9cb0aeb3a6a6af18c16f59f004b866cd2be1c14b36134a4a0"}, - {file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"}, + {file = "numpy-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c8a0e34993b510fc19b9a2ce7f31cb8e94ecf6e924a40c0c9dd4f62d0aac47d9"}, + {file = "numpy-2.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7dd86dfaf7c900c0bbdcb8b16e2f6ddf1eb1fe39c6c8cca6e94844ed3152a8fd"}, + {file = "numpy-2.1.1-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:5889dd24f03ca5a5b1e8a90a33b5a0846d8977565e4ae003a63d22ecddf6782f"}, + {file = "numpy-2.1.1-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:59ca673ad11d4b84ceb385290ed0ebe60266e356641428c845b39cd9df6713ab"}, + {file = "numpy-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13ce49a34c44b6de5241f0b38b07e44c1b2dcacd9e36c30f9c2fcb1bb5135db7"}, + {file = "numpy-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:913cc1d311060b1d409e609947fa1b9753701dac96e6581b58afc36b7ee35af6"}, + {file = "numpy-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:caf5d284ddea7462c32b8d4a6b8af030b6c9fd5332afb70e7414d7fdded4bfd0"}, + {file = "numpy-2.1.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:57eb525e7c2a8fdee02d731f647146ff54ea8c973364f3b850069ffb42799647"}, + {file = "numpy-2.1.1-cp310-cp310-win32.whl", hash = "sha256:9a8e06c7a980869ea67bbf551283bbed2856915f0a792dc32dd0f9dd2fb56728"}, + {file = "numpy-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:d10c39947a2d351d6d466b4ae83dad4c37cd6c3cdd6d5d0fa797da56f710a6ae"}, + {file = "numpy-2.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0d07841fd284718feffe7dd17a63a2e6c78679b2d386d3e82f44f0108c905550"}, + {file = "numpy-2.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b5613cfeb1adfe791e8e681128f5f49f22f3fcaa942255a6124d58ca59d9528f"}, + {file = "numpy-2.1.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:0b8cc2715a84b7c3b161f9ebbd942740aaed913584cae9cdc7f8ad5ad41943d0"}, + {file = "numpy-2.1.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:b49742cdb85f1f81e4dc1b39dcf328244f4d8d1ded95dea725b316bd2cf18c95"}, + {file = "numpy-2.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8d5f8a8e3bc87334f025194c6193e408903d21ebaeb10952264943a985066ca"}, + {file = "numpy-2.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d51fc141ddbe3f919e91a096ec739f49d686df8af254b2053ba21a910ae518bf"}, + {file = "numpy-2.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:98ce7fb5b8063cfdd86596b9c762bf2b5e35a2cdd7e967494ab78a1fa7f8b86e"}, + {file = "numpy-2.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:24c2ad697bd8593887b019817ddd9974a7f429c14a5469d7fad413f28340a6d2"}, + {file = "numpy-2.1.1-cp311-cp311-win32.whl", hash = "sha256:397bc5ce62d3fb73f304bec332171535c187e0643e176a6e9421a6e3eacef06d"}, + {file = "numpy-2.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:ae8ce252404cdd4de56dcfce8b11eac3c594a9c16c231d081fb705cf23bd4d9e"}, + {file = "numpy-2.1.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7c803b7934a7f59563db459292e6aa078bb38b7ab1446ca38dd138646a38203e"}, + {file = "numpy-2.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6435c48250c12f001920f0751fe50c0348f5f240852cfddc5e2f97e007544cbe"}, + {file = "numpy-2.1.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:3269c9eb8745e8d975980b3a7411a98976824e1fdef11f0aacf76147f662b15f"}, + {file = "numpy-2.1.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:fac6e277a41163d27dfab5f4ec1f7a83fac94e170665a4a50191b545721c6521"}, + {file = "numpy-2.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fcd8f556cdc8cfe35e70efb92463082b7f43dd7e547eb071ffc36abc0ca4699b"}, + {file = "numpy-2.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b9cd92c8f8e7b313b80e93cedc12c0112088541dcedd9197b5dee3738c1201"}, + {file = "numpy-2.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:afd9c680df4de71cd58582b51e88a61feed4abcc7530bcd3d48483f20fc76f2a"}, + {file = "numpy-2.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8661c94e3aad18e1ea17a11f60f843a4933ccaf1a25a7c6a9182af70610b2313"}, + {file = "numpy-2.1.1-cp312-cp312-win32.whl", hash = "sha256:950802d17a33c07cba7fd7c3dcfa7d64705509206be1606f196d179e539111ed"}, + {file = "numpy-2.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:3fc5eabfc720db95d68e6646e88f8b399bfedd235994016351b1d9e062c4b270"}, + {file = "numpy-2.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:046356b19d7ad1890c751b99acad5e82dc4a02232013bd9a9a712fddf8eb60f5"}, + {file = "numpy-2.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6e5a9cb2be39350ae6c8f79410744e80154df658d5bea06e06e0ac5bb75480d5"}, + {file = "numpy-2.1.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:d4c57b68c8ef5e1ebf47238e99bf27657511ec3f071c465f6b1bccbef12d4136"}, + {file = "numpy-2.1.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:8ae0fd135e0b157365ac7cc31fff27f07a5572bdfc38f9c2d43b2aff416cc8b0"}, + {file = "numpy-2.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:981707f6b31b59c0c24bcda52e5605f9701cb46da4b86c2e8023656ad3e833cb"}, + {file = "numpy-2.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ca4b53e1e0b279142113b8c5eb7d7a877e967c306edc34f3b58e9be12fda8df"}, + {file = "numpy-2.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e097507396c0be4e547ff15b13dc3866f45f3680f789c1a1301b07dadd3fbc78"}, + {file = "numpy-2.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7506387e191fe8cdb267f912469a3cccc538ab108471291636a96a54e599556"}, + {file = "numpy-2.1.1-cp313-cp313-win32.whl", hash = "sha256:251105b7c42abe40e3a689881e1793370cc9724ad50d64b30b358bbb3a97553b"}, + {file = "numpy-2.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:f212d4f46b67ff604d11fff7cc62d36b3e8714edf68e44e9760e19be38c03eb0"}, + {file = "numpy-2.1.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:920b0911bb2e4414c50e55bd658baeb78281a47feeb064ab40c2b66ecba85553"}, + {file = "numpy-2.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:bab7c09454460a487e631ffc0c42057e3d8f2a9ddccd1e60c7bb8ed774992480"}, + {file = "numpy-2.1.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:cea427d1350f3fd0d2818ce7350095c1a2ee33e30961d2f0fef48576ddbbe90f"}, + {file = "numpy-2.1.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:e30356d530528a42eeba51420ae8bf6c6c09559051887196599d96ee5f536468"}, + {file = "numpy-2.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8dfa9e94fc127c40979c3eacbae1e61fda4fe71d84869cc129e2721973231ef"}, + {file = "numpy-2.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:910b47a6d0635ec1bd53b88f86120a52bf56dcc27b51f18c7b4a2e2224c29f0f"}, + {file = "numpy-2.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:13cc11c00000848702322af4de0147ced365c81d66053a67c2e962a485b3717c"}, + {file = "numpy-2.1.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:53e27293b3a2b661c03f79aa51c3987492bd4641ef933e366e0f9f6c9bf257ec"}, + {file = "numpy-2.1.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7be6a07520b88214ea85d8ac8b7d6d8a1839b0b5cb87412ac9f49fa934eb15d5"}, + {file = "numpy-2.1.1-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:52ac2e48f5ad847cd43c4755520a2317f3380213493b9d8a4c5e37f3b87df504"}, + {file = "numpy-2.1.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50a95ca3560a6058d6ea91d4629a83a897ee27c00630aed9d933dff191f170cd"}, + {file = "numpy-2.1.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:99f4a9ee60eed1385a86e82288971a51e71df052ed0b2900ed30bc840c0f2e39"}, + {file = "numpy-2.1.1.tar.gz", hash = "sha256:d0cf7d55b1051387807405b3898efafa862997b4cba8aa5dbe657be794afeafd"}, ] [[package]] @@ -591,40 +1453,53 @@ files = [ [[package]] name = "pandas" -version = "2.2.2" +version = "2.2.3" description = "Powerful data structures for data analysis, time series, and statistics" optional = false python-versions = ">=3.9" files = [ - {file = "pandas-2.2.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:90c6fca2acf139569e74e8781709dccb6fe25940488755716d1d354d6bc58bce"}, - {file = "pandas-2.2.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c7adfc142dac335d8c1e0dcbd37eb8617eac386596eb9e1a1b77791cf2498238"}, - {file = "pandas-2.2.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4abfe0be0d7221be4f12552995e58723c7422c80a659da13ca382697de830c08"}, - {file = "pandas-2.2.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8635c16bf3d99040fdf3ca3db669a7250ddf49c55dc4aa8fe0ae0fa8d6dcc1f0"}, - {file = "pandas-2.2.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:40ae1dffb3967a52203105a077415a86044a2bea011b5f321c6aa64b379a3f51"}, - {file = "pandas-2.2.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8e5a0b00e1e56a842f922e7fae8ae4077aee4af0acb5ae3622bd4b4c30aedf99"}, - {file = "pandas-2.2.2-cp310-cp310-win_amd64.whl", hash = "sha256:ddf818e4e6c7c6f4f7c8a12709696d193976b591cc7dc50588d3d1a6b5dc8772"}, - {file = "pandas-2.2.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:696039430f7a562b74fa45f540aca068ea85fa34c244d0deee539cb6d70aa288"}, - {file = "pandas-2.2.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8e90497254aacacbc4ea6ae5e7a8cd75629d6ad2b30025a4a8b09aa4faf55151"}, - {file = "pandas-2.2.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58b84b91b0b9f4bafac2a0ac55002280c094dfc6402402332c0913a59654ab2b"}, - {file = "pandas-2.2.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2123dc9ad6a814bcdea0f099885276b31b24f7edf40f6cdbc0912672e22eee"}, - {file = "pandas-2.2.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2925720037f06e89af896c70bca73459d7e6a4be96f9de79e2d440bd499fe0db"}, - {file = "pandas-2.2.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0cace394b6ea70c01ca1595f839cf193df35d1575986e484ad35c4aeae7266c1"}, - {file = "pandas-2.2.2-cp311-cp311-win_amd64.whl", hash = "sha256:873d13d177501a28b2756375d59816c365e42ed8417b41665f346289adc68d24"}, - {file = "pandas-2.2.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9dfde2a0ddef507a631dc9dc4af6a9489d5e2e740e226ad426a05cabfbd7c8ef"}, - {file = "pandas-2.2.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e9b79011ff7a0f4b1d6da6a61aa1aa604fb312d6647de5bad20013682d1429ce"}, - {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cb51fe389360f3b5a4d57dbd2848a5f033350336ca3b340d1c53a1fad33bcad"}, - {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eee3a87076c0756de40b05c5e9a6069c035ba43e8dd71c379e68cab2c20f16ad"}, - {file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3e374f59e440d4ab45ca2fffde54b81ac3834cf5ae2cdfa69c90bc03bde04d76"}, - {file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:43498c0bdb43d55cb162cdc8c06fac328ccb5d2eabe3cadeb3529ae6f0517c32"}, - {file = "pandas-2.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:d187d355ecec3629624fccb01d104da7d7f391db0311145817525281e2804d23"}, - {file = "pandas-2.2.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0ca6377b8fca51815f382bd0b697a0814c8bda55115678cbc94c30aacbb6eff2"}, - {file = "pandas-2.2.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9057e6aa78a584bc93a13f0a9bf7e753a5e9770a30b4d758b8d5f2a62a9433cd"}, - {file = "pandas-2.2.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:001910ad31abc7bf06f49dcc903755d2f7f3a9186c0c040b827e522e9cef0863"}, - {file = "pandas-2.2.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66b479b0bd07204e37583c191535505410daa8df638fd8e75ae1b383851fe921"}, - {file = "pandas-2.2.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a77e9d1c386196879aa5eb712e77461aaee433e54c68cf253053a73b7e49c33a"}, - {file = "pandas-2.2.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:92fd6b027924a7e178ac202cfbe25e53368db90d56872d20ffae94b96c7acc57"}, - {file = "pandas-2.2.2-cp39-cp39-win_amd64.whl", hash = "sha256:640cef9aa381b60e296db324337a554aeeb883ead99dc8f6c18e81a93942f5f4"}, - {file = "pandas-2.2.2.tar.gz", hash = "sha256:9e79019aba43cb4fda9e4d983f8e88ca0373adbb697ae9c6c43093218de28b54"}, + {file = "pandas-2.2.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1948ddde24197a0f7add2bdc4ca83bf2b1ef84a1bc8ccffd95eda17fd836ecb5"}, + {file = "pandas-2.2.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:381175499d3802cde0eabbaf6324cce0c4f5d52ca6f8c377c29ad442f50f6348"}, + {file = "pandas-2.2.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d9c45366def9a3dd85a6454c0e7908f2b3b8e9c138f5dc38fed7ce720d8453ed"}, + {file = "pandas-2.2.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86976a1c5b25ae3f8ccae3a5306e443569ee3c3faf444dfd0f41cda24667ad57"}, + {file = "pandas-2.2.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b8661b0238a69d7aafe156b7fa86c44b881387509653fdf857bebc5e4008ad42"}, + {file = "pandas-2.2.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:37e0aced3e8f539eccf2e099f65cdb9c8aa85109b0be6e93e2baff94264bdc6f"}, + {file = "pandas-2.2.3-cp310-cp310-win_amd64.whl", hash = "sha256:56534ce0746a58afaf7942ba4863e0ef81c9c50d3f0ae93e9497d6a41a057645"}, + {file = "pandas-2.2.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:66108071e1b935240e74525006034333f98bcdb87ea116de573a6a0dccb6c039"}, + {file = "pandas-2.2.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7c2875855b0ff77b2a64a0365e24455d9990730d6431b9e0ee18ad8acee13dbd"}, + {file = "pandas-2.2.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd8d0c3be0515c12fed0bdbae072551c8b54b7192c7b1fda0ba56059a0179698"}, + {file = "pandas-2.2.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c124333816c3a9b03fbeef3a9f230ba9a737e9e5bb4060aa2107a86cc0a497fc"}, + {file = "pandas-2.2.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:63cc132e40a2e084cf01adf0775b15ac515ba905d7dcca47e9a251819c575ef3"}, + {file = "pandas-2.2.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:29401dbfa9ad77319367d36940cd8a0b3a11aba16063e39632d98b0e931ddf32"}, + {file = "pandas-2.2.3-cp311-cp311-win_amd64.whl", hash = "sha256:3fc6873a41186404dad67245896a6e440baacc92f5b716ccd1bc9ed2995ab2c5"}, + {file = "pandas-2.2.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b1d432e8d08679a40e2a6d8b2f9770a5c21793a6f9f47fdd52c5ce1948a5a8a9"}, + {file = "pandas-2.2.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a5a1595fe639f5988ba6a8e5bc9649af3baf26df3998a0abe56c02609392e0a4"}, + {file = "pandas-2.2.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5de54125a92bb4d1c051c0659e6fcb75256bf799a732a87184e5ea503965bce3"}, + {file = "pandas-2.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fffb8ae78d8af97f849404f21411c95062db1496aeb3e56f146f0355c9989319"}, + {file = "pandas-2.2.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dfcb5ee8d4d50c06a51c2fffa6cff6272098ad6540aed1a76d15fb9318194d8"}, + {file = "pandas-2.2.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:062309c1b9ea12a50e8ce661145c6aab431b1e99530d3cd60640e255778bd43a"}, + {file = "pandas-2.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:59ef3764d0fe818125a5097d2ae867ca3fa64df032331b7e0917cf5d7bf66b13"}, + {file = "pandas-2.2.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f00d1345d84d8c86a63e476bb4955e46458b304b9575dcf71102b5c705320015"}, + {file = "pandas-2.2.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3508d914817e153ad359d7e069d752cdd736a247c322d932eb89e6bc84217f28"}, + {file = "pandas-2.2.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22a9d949bfc9a502d320aa04e5d02feab689d61da4e7764b62c30b991c42c5f0"}, + {file = "pandas-2.2.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3a255b2c19987fbbe62a9dfd6cff7ff2aa9ccab3fc75218fd4b7530f01efa24"}, + {file = "pandas-2.2.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:800250ecdadb6d9c78eae4990da62743b857b470883fa27f652db8bdde7f6659"}, + {file = "pandas-2.2.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6374c452ff3ec675a8f46fd9ab25c4ad0ba590b71cf0656f8b6daa5202bca3fb"}, + {file = "pandas-2.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:61c5ad4043f791b61dd4752191d9f07f0ae412515d59ba8f005832a532f8736d"}, + {file = "pandas-2.2.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3b71f27954685ee685317063bf13c7709a7ba74fc996b84fc6821c59b0f06468"}, + {file = "pandas-2.2.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:38cf8125c40dae9d5acc10fa66af8ea6fdf760b2714ee482ca691fc66e6fcb18"}, + {file = "pandas-2.2.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ba96630bc17c875161df3818780af30e43be9b166ce51c9a18c1feae342906c2"}, + {file = "pandas-2.2.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db71525a1538b30142094edb9adc10be3f3e176748cd7acc2240c2f2e5aa3a4"}, + {file = "pandas-2.2.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:15c0e1e02e93116177d29ff83e8b1619c93ddc9c49083f237d4312337a61165d"}, + {file = "pandas-2.2.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ad5b65698ab28ed8d7f18790a0dc58005c7629f227be9ecc1072aa74c0c1d43a"}, + {file = "pandas-2.2.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc6b93f9b966093cb0fd62ff1a7e4c09e6d546ad7c1de191767baffc57628f39"}, + {file = "pandas-2.2.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5dbca4c1acd72e8eeef4753eeca07de9b1db4f398669d5994086f788a5d7cc30"}, + {file = "pandas-2.2.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8cd6d7cc958a3910f934ea8dbdf17b2364827bb4dafc38ce6eef6bb3d65ff09c"}, + {file = "pandas-2.2.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99df71520d25fade9db7c1076ac94eb994f4d2673ef2aa2e86ee039b6746d20c"}, + {file = "pandas-2.2.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:31d0ced62d4ea3e231a9f228366919a5ea0b07440d9d4dac345376fd8e1477ea"}, + {file = "pandas-2.2.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7eee9e7cea6adf3e3d24e304ac6b8300646e2a5d1cd3a3c2abed9101b0846761"}, + {file = "pandas-2.2.3-cp39-cp39-win_amd64.whl", hash = "sha256:4850ba03528b6dd51d6c5d273c46f183f39a9baf3f0143e566b89450965b105e"}, + {file = "pandas-2.2.3.tar.gz", hash = "sha256:4f18ba62b61d7e192368b84517265a99b4d7ee8912f8708660fb4a366cc82667"}, ] [package.dependencies] @@ -660,13 +1535,13 @@ xml = ["lxml (>=4.9.2)"] [[package]] name = "pandera" -version = "0.20.3" +version = "0.20.4" description = "A light-weight and flexible data validation and testing tool for statistical data objects." optional = false python-versions = ">=3.7" files = [ - {file = "pandera-0.20.3-py3-none-any.whl", hash = "sha256:0c251a3947d8c6d7a0e26602e78fa425268bc9f448f3a0fded85249b158a65c2"}, - {file = "pandera-0.20.3.tar.gz", hash = "sha256:d8542a68493d2dfe3226c186cc145be8d27535d7edf396178e93811515fe3470"}, + {file = "pandera-0.20.4-py3-none-any.whl", hash = "sha256:40368d9162938f304ce4ce6ad41ce4b57991b88f7de1bb4574aeb5a1ecf2dc8c"}, + {file = "pandera-0.20.4.tar.gz", hash = "sha256:ccf6178293ef9d4393dc4776e47477e3d2dd51c800ecdfedec67fff50f4ad3c2"}, ] [package.dependencies] @@ -680,7 +1555,7 @@ typing-inspect = ">=0.6.0" wrapt = "*" [package.extras] -all = ["black", "dask[dataframe]", "fastapi", "frictionless (<=4.40.8)", "geopandas", "hypothesis (>=6.92.7)", "modin", "pandas-stubs", "polars (>=0.20.0)", "pyspark (>=3.2.0)", "pyyaml (>=5.1)", "ray", "scipy", "shapely"] +all = ["black", "dask[dataframe]", "fastapi", "frictionless (<=4.40.8)", "geopandas", "hypothesis (>=6.92.7)", "modin", "pandas-stubs", "polars (>=0.20.0)", "pyspark[connect] (>=3.2.0)", "pyyaml (>=5.1)", "ray", "scipy", "shapely"] dask = ["dask[dataframe]"] fastapi = ["fastapi"] geopandas = ["geopandas", "shapely"] @@ -691,7 +1566,7 @@ modin-dask = ["dask[dataframe]", "modin"] modin-ray = ["modin", "ray"] mypy = ["pandas-stubs"] polars = ["polars (>=0.20.0)"] -pyspark = ["pyspark (>=3.2.0)"] +pyspark = ["pyspark[connect] (>=3.2.0)"] strategies = ["hypothesis (>=6.92.7)"] [[package]] @@ -705,21 +1580,118 @@ files = [ {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, ] +[[package]] +name = "pillow" +version = "10.4.0" +description = "Python Imaging Library (Fork)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pillow-10.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e"}, + {file = "pillow-10.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d"}, + {file = "pillow-10.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7928ecbf1ece13956b95d9cbcfc77137652b02763ba384d9ab508099a2eca856"}, + {file = "pillow-10.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4d49b85c4348ea0b31ea63bc75a9f3857869174e2bf17e7aba02945cd218e6f"}, + {file = "pillow-10.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:6c762a5b0997f5659a5ef2266abc1d8851ad7749ad9a6a5506eb23d314e4f46b"}, + {file = "pillow-10.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a985e028fc183bf12a77a8bbf36318db4238a3ded7fa9df1b9a133f1cb79f8fc"}, + {file = "pillow-10.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:812f7342b0eee081eaec84d91423d1b4650bb9828eb53d8511bcef8ce5aecf1e"}, + {file = "pillow-10.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ac1452d2fbe4978c2eec89fb5a23b8387aba707ac72810d9490118817d9c0b46"}, + {file = "pillow-10.4.0-cp310-cp310-win32.whl", hash = "sha256:bcd5e41a859bf2e84fdc42f4edb7d9aba0a13d29a2abadccafad99de3feff984"}, + {file = "pillow-10.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:ecd85a8d3e79cd7158dec1c9e5808e821feea088e2f69a974db5edf84dc53141"}, + {file = "pillow-10.4.0-cp310-cp310-win_arm64.whl", hash = "sha256:ff337c552345e95702c5fde3158acb0625111017d0e5f24bf3acdb9cc16b90d1"}, + {file = "pillow-10.4.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:0a9ec697746f268507404647e531e92889890a087e03681a3606d9b920fbee3c"}, + {file = "pillow-10.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe91cb65544a1321e631e696759491ae04a2ea11d36715eca01ce07284738be"}, + {file = "pillow-10.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dc6761a6efc781e6a1544206f22c80c3af4c8cf461206d46a1e6006e4429ff3"}, + {file = "pillow-10.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e84b6cc6a4a3d76c153a6b19270b3526a5a8ed6b09501d3af891daa2a9de7d6"}, + {file = "pillow-10.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:bbc527b519bd3aa9d7f429d152fea69f9ad37c95f0b02aebddff592688998abe"}, + {file = "pillow-10.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:76a911dfe51a36041f2e756b00f96ed84677cdeb75d25c767f296c1c1eda1319"}, + {file = "pillow-10.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:59291fb29317122398786c2d44427bbd1a6d7ff54017075b22be9d21aa59bd8d"}, + {file = "pillow-10.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:416d3a5d0e8cfe4f27f574362435bc9bae57f679a7158e0096ad2beb427b8696"}, + {file = "pillow-10.4.0-cp311-cp311-win32.whl", hash = "sha256:7086cc1d5eebb91ad24ded9f58bec6c688e9f0ed7eb3dbbf1e4800280a896496"}, + {file = "pillow-10.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cbed61494057c0f83b83eb3a310f0bf774b09513307c434d4366ed64f4128a91"}, + {file = "pillow-10.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:f5f0c3e969c8f12dd2bb7e0b15d5c468b51e5017e01e2e867335c81903046a22"}, + {file = "pillow-10.4.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:673655af3eadf4df6b5457033f086e90299fdd7a47983a13827acf7459c15d94"}, + {file = "pillow-10.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:866b6942a92f56300012f5fbac71f2d610312ee65e22f1aa2609e491284e5597"}, + {file = "pillow-10.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29dbdc4207642ea6aad70fbde1a9338753d33fb23ed6956e706936706f52dd80"}, + {file = "pillow-10.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf2342ac639c4cf38799a44950bbc2dfcb685f052b9e262f446482afaf4bffca"}, + {file = "pillow-10.4.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f5b92f4d70791b4a67157321c4e8225d60b119c5cc9aee8ecf153aace4aad4ef"}, + {file = "pillow-10.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:86dcb5a1eb778d8b25659d5e4341269e8590ad6b4e8b44d9f4b07f8d136c414a"}, + {file = "pillow-10.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:780c072c2e11c9b2c7ca37f9a2ee8ba66f44367ac3e5c7832afcfe5104fd6d1b"}, + {file = "pillow-10.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:37fb69d905be665f68f28a8bba3c6d3223c8efe1edf14cc4cfa06c241f8c81d9"}, + {file = "pillow-10.4.0-cp312-cp312-win32.whl", hash = "sha256:7dfecdbad5c301d7b5bde160150b4db4c659cee2b69589705b6f8a0c509d9f42"}, + {file = "pillow-10.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:1d846aea995ad352d4bdcc847535bd56e0fd88d36829d2c90be880ef1ee4668a"}, + {file = "pillow-10.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:e553cad5179a66ba15bb18b353a19020e73a7921296a7979c4a2b7f6a5cd57f9"}, + {file = "pillow-10.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8bc1a764ed8c957a2e9cacf97c8b2b053b70307cf2996aafd70e91a082e70df3"}, + {file = "pillow-10.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6209bb41dc692ddfee4942517c19ee81b86c864b626dbfca272ec0f7cff5d9fb"}, + {file = "pillow-10.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bee197b30783295d2eb680b311af15a20a8b24024a19c3a26431ff83eb8d1f70"}, + {file = "pillow-10.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ef61f5dd14c300786318482456481463b9d6b91ebe5ef12f405afbba77ed0be"}, + {file = "pillow-10.4.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:297e388da6e248c98bc4a02e018966af0c5f92dfacf5a5ca22fa01cb3179bca0"}, + {file = "pillow-10.4.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:e4db64794ccdf6cb83a59d73405f63adbe2a1887012e308828596100a0b2f6cc"}, + {file = "pillow-10.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd2880a07482090a3bcb01f4265f1936a903d70bc740bfcb1fd4e8a2ffe5cf5a"}, + {file = "pillow-10.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b35b21b819ac1dbd1233317adeecd63495f6babf21b7b2512d244ff6c6ce309"}, + {file = "pillow-10.4.0-cp313-cp313-win32.whl", hash = "sha256:551d3fd6e9dc15e4c1eb6fc4ba2b39c0c7933fa113b220057a34f4bb3268a060"}, + {file = "pillow-10.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:030abdbe43ee02e0de642aee345efa443740aa4d828bfe8e2eb11922ea6a21ea"}, + {file = "pillow-10.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:5b001114dd152cfd6b23befeb28d7aee43553e2402c9f159807bf55f33af8a8d"}, + {file = "pillow-10.4.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:8d4d5063501b6dd4024b8ac2f04962d661222d120381272deea52e3fc52d3736"}, + {file = "pillow-10.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7c1ee6f42250df403c5f103cbd2768a28fe1a0ea1f0f03fe151c8741e1469c8b"}, + {file = "pillow-10.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b15e02e9bb4c21e39876698abf233c8c579127986f8207200bc8a8f6bb27acf2"}, + {file = "pillow-10.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a8d4bade9952ea9a77d0c3e49cbd8b2890a399422258a77f357b9cc9be8d680"}, + {file = "pillow-10.4.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:43efea75eb06b95d1631cb784aa40156177bf9dd5b4b03ff38979e048258bc6b"}, + {file = "pillow-10.4.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:950be4d8ba92aca4b2bb0741285a46bfae3ca699ef913ec8416c1b78eadd64cd"}, + {file = "pillow-10.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d7480af14364494365e89d6fddc510a13e5a2c3584cb19ef65415ca57252fb84"}, + {file = "pillow-10.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:73664fe514b34c8f02452ffb73b7a92c6774e39a647087f83d67f010eb9a0cf0"}, + {file = "pillow-10.4.0-cp38-cp38-win32.whl", hash = "sha256:e88d5e6ad0d026fba7bdab8c3f225a69f063f116462c49892b0149e21b6c0a0e"}, + {file = "pillow-10.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:5161eef006d335e46895297f642341111945e2c1c899eb406882a6c61a4357ab"}, + {file = "pillow-10.4.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:0ae24a547e8b711ccaaf99c9ae3cd975470e1a30caa80a6aaee9a2f19c05701d"}, + {file = "pillow-10.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:298478fe4f77a4408895605f3482b6cc6222c018b2ce565c2b6b9c354ac3229b"}, + {file = "pillow-10.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:134ace6dc392116566980ee7436477d844520a26a4b1bd4053f6f47d096997fd"}, + {file = "pillow-10.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:930044bb7679ab003b14023138b50181899da3f25de50e9dbee23b61b4de2126"}, + {file = "pillow-10.4.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:c76e5786951e72ed3686e122d14c5d7012f16c8303a674d18cdcd6d89557fc5b"}, + {file = "pillow-10.4.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b2724fdb354a868ddf9a880cb84d102da914e99119211ef7ecbdc613b8c96b3c"}, + {file = "pillow-10.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dbc6ae66518ab3c5847659e9988c3b60dc94ffb48ef9168656e0019a93dbf8a1"}, + {file = "pillow-10.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:06b2f7898047ae93fad74467ec3d28fe84f7831370e3c258afa533f81ef7f3df"}, + {file = "pillow-10.4.0-cp39-cp39-win32.whl", hash = "sha256:7970285ab628a3779aecc35823296a7869f889b8329c16ad5a71e4901a3dc4ef"}, + {file = "pillow-10.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:961a7293b2457b405967af9c77dcaa43cc1a8cd50d23c532e62d48ab6cdd56f5"}, + {file = "pillow-10.4.0-cp39-cp39-win_arm64.whl", hash = "sha256:32cda9e3d601a52baccb2856b8ea1fc213c90b340c542dcef77140dfa3278a9e"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5b4815f2e65b30f5fbae9dfffa8636d992d49705723fe86a3661806e069352d4"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8f0aef4ef59694b12cadee839e2ba6afeab89c0f39a3adc02ed51d109117b8da"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f4727572e2918acaa9077c919cbbeb73bd2b3ebcfe033b72f858fc9fbef0026"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff25afb18123cea58a591ea0244b92eb1e61a1fd497bf6d6384f09bc3262ec3e"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dc3e2db6ba09ffd7d02ae9141cfa0ae23393ee7687248d46a7507b75d610f4f5"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:02a2be69f9c9b8c1e97cf2713e789d4e398c751ecfd9967c18d0ce304efbf885"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0755ffd4a0c6f267cccbae2e9903d95477ca2f77c4fcf3a3a09570001856c8a5"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:a02364621fe369e06200d4a16558e056fe2805d3468350df3aef21e00d26214b"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:1b5dea9831a90e9d0721ec417a80d4cbd7022093ac38a568db2dd78363b00908"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b885f89040bb8c4a1573566bbb2f44f5c505ef6e74cec7ab9068c900047f04b"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87dd88ded2e6d74d31e1e0a99a726a6765cda32d00ba72dc37f0651f306daaa8"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:2db98790afc70118bd0255c2eeb465e9767ecf1f3c25f9a1abb8ffc8cfd1fe0a"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f7baece4ce06bade126fb84b8af1c33439a76d8a6fd818970215e0560ca28c27"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:cfdd747216947628af7b259d274771d84db2268ca062dd5faf373639d00113a3"}, + {file = "pillow-10.4.0.tar.gz", hash = "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06"}, +] + +[package.extras] +docs = ["furo", "olefile", "sphinx (>=7.3)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinxext-opengraph"] +fpx = ["olefile"] +mic = ["olefile"] +tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] +typing = ["typing-extensions"] +xmp = ["defusedxml"] + [[package]] name = "platformdirs" -version = "4.2.2" +version = "4.3.6" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" files = [ - {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, - {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, + {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, + {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, ] [package.extras] -docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] -type = ["mypy (>=1.8)"] +docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"] +type = ["mypy (>=1.11.2)"] [[package]] name = "pluggy" @@ -736,111 +1708,217 @@ files = [ dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] +[[package]] +name = "polars" +version = "1.8.2" +description = "Blazingly fast DataFrame library" +optional = false +python-versions = ">=3.8" +files = [ + {file = "polars-1.8.2-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:114be1ebfb051b794fb9e1f15999430c79cc0824595e237d3f45632be3e56d73"}, + {file = "polars-1.8.2-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:e4fc36cfe48972d4c5be21a7cb119d6378fb7af0bb3eeb61456b66a1f43228e3"}, + {file = "polars-1.8.2-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67c1e448d6e38697650b22dd359f13c40b567c0b66686c8602e4367400e87801"}, + {file = "polars-1.8.2-cp38-abi3-manylinux_2_24_aarch64.whl", hash = "sha256:570ee86b033dc5a6dbe2cb0df48522301642f304dda3da48f53d7488899a2206"}, + {file = "polars-1.8.2-cp38-abi3-win_amd64.whl", hash = "sha256:ce1a1c1e2150ffcc44a5f1c461d738e1dcd95abbd0f210af0271c7ac0c9f7ef9"}, + {file = "polars-1.8.2.tar.gz", hash = "sha256:42f69277d5be2833b0b826af5e75dcf430222d65c9633872856e176a0bed27a0"}, +] + +[package.extras] +adbc = ["adbc-driver-manager[dbapi]", "adbc-driver-sqlite[dbapi]"] +all = ["polars[async,cloudpickle,database,deltalake,excel,fsspec,graph,iceberg,numpy,pandas,plot,pyarrow,pydantic,style,timezone]"] +async = ["gevent"] +calamine = ["fastexcel (>=0.9)"] +cloudpickle = ["cloudpickle"] +connectorx = ["connectorx (>=0.3.2)"] +database = ["nest-asyncio", "polars[adbc,connectorx,sqlalchemy]"] +deltalake = ["deltalake (>=0.15.0)"] +excel = ["polars[calamine,openpyxl,xlsx2csv,xlsxwriter]"] +fsspec = ["fsspec"] +gpu = ["cudf-polars-cu12"] +graph = ["matplotlib"] +iceberg = ["pyiceberg (>=0.5.0)"] +numpy = ["numpy (>=1.16.0)"] +openpyxl = ["openpyxl (>=3.0.0)"] +pandas = ["pandas", "polars[pyarrow]"] +plot = ["altair (>=5.4.0)"] +pyarrow = ["pyarrow (>=7.0.0)"] +pydantic = ["pydantic"] +sqlalchemy = ["polars[pandas]", "sqlalchemy"] +style = ["great-tables (>=0.8.0)"] +timezone = ["backports-zoneinfo", "tzdata"] +xlsx2csv = ["xlsx2csv (>=0.8.0)"] +xlsxwriter = ["xlsxwriter"] + +[[package]] +name = "pyarrow" +version = "17.0.0" +description = "Python library for Apache Arrow" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyarrow-17.0.0-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:a5c8b238d47e48812ee577ee20c9a2779e6a5904f1708ae240f53ecbee7c9f07"}, + {file = "pyarrow-17.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:db023dc4c6cae1015de9e198d41250688383c3f9af8f565370ab2b4cb5f62655"}, + {file = "pyarrow-17.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da1e060b3876faa11cee287839f9cc7cdc00649f475714b8680a05fd9071d545"}, + {file = "pyarrow-17.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75c06d4624c0ad6674364bb46ef38c3132768139ddec1c56582dbac54f2663e2"}, + {file = "pyarrow-17.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:fa3c246cc58cb5a4a5cb407a18f193354ea47dd0648194e6265bd24177982fe8"}, + {file = "pyarrow-17.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:f7ae2de664e0b158d1607699a16a488de3d008ba99b3a7aa5de1cbc13574d047"}, + {file = "pyarrow-17.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:5984f416552eea15fd9cee03da53542bf4cddaef5afecefb9aa8d1010c335087"}, + {file = "pyarrow-17.0.0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:1c8856e2ef09eb87ecf937104aacfa0708f22dfeb039c363ec99735190ffb977"}, + {file = "pyarrow-17.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2e19f569567efcbbd42084e87f948778eb371d308e137a0f97afe19bb860ccb3"}, + {file = "pyarrow-17.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b244dc8e08a23b3e352899a006a26ae7b4d0da7bb636872fa8f5884e70acf15"}, + {file = "pyarrow-17.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b72e87fe3e1db343995562f7fff8aee354b55ee83d13afba65400c178ab2597"}, + {file = "pyarrow-17.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:dc5c31c37409dfbc5d014047817cb4ccd8c1ea25d19576acf1a001fe07f5b420"}, + {file = "pyarrow-17.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:e3343cb1e88bc2ea605986d4b94948716edc7a8d14afd4e2c097232f729758b4"}, + {file = "pyarrow-17.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:a27532c38f3de9eb3e90ecab63dfda948a8ca859a66e3a47f5f42d1e403c4d03"}, + {file = "pyarrow-17.0.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:9b8a823cea605221e61f34859dcc03207e52e409ccf6354634143e23af7c8d22"}, + {file = "pyarrow-17.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f1e70de6cb5790a50b01d2b686d54aaf73da01266850b05e3af2a1bc89e16053"}, + {file = "pyarrow-17.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0071ce35788c6f9077ff9ecba4858108eebe2ea5a3f7cf2cf55ebc1dbc6ee24a"}, + {file = "pyarrow-17.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:757074882f844411fcca735e39aae74248a1531367a7c80799b4266390ae51cc"}, + {file = "pyarrow-17.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:9ba11c4f16976e89146781a83833df7f82077cdab7dc6232c897789343f7891a"}, + {file = "pyarrow-17.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b0c6ac301093b42d34410b187bba560b17c0330f64907bfa4f7f7f2444b0cf9b"}, + {file = "pyarrow-17.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:392bc9feabc647338e6c89267635e111d71edad5fcffba204425a7c8d13610d7"}, + {file = "pyarrow-17.0.0-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:af5ff82a04b2171415f1410cff7ebb79861afc5dae50be73ce06d6e870615204"}, + {file = "pyarrow-17.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:edca18eaca89cd6382dfbcff3dd2d87633433043650c07375d095cd3517561d8"}, + {file = "pyarrow-17.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c7916bff914ac5d4a8fe25b7a25e432ff921e72f6f2b7547d1e325c1ad9d155"}, + {file = "pyarrow-17.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f553ca691b9e94b202ff741bdd40f6ccb70cdd5fbf65c187af132f1317de6145"}, + {file = "pyarrow-17.0.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:0cdb0e627c86c373205a2f94a510ac4376fdc523f8bb36beab2e7f204416163c"}, + {file = "pyarrow-17.0.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:d7d192305d9d8bc9082d10f361fc70a73590a4c65cf31c3e6926cd72b76bc35c"}, + {file = "pyarrow-17.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:02dae06ce212d8b3244dd3e7d12d9c4d3046945a5933d28026598e9dbbda1fca"}, + {file = "pyarrow-17.0.0-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:13d7a460b412f31e4c0efa1148e1d29bdf18ad1411eb6757d38f8fbdcc8645fb"}, + {file = "pyarrow-17.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9b564a51fbccfab5a04a80453e5ac6c9954a9c5ef2890d1bcf63741909c3f8df"}, + {file = "pyarrow-17.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32503827abbc5aadedfa235f5ece8c4f8f8b0a3cf01066bc8d29de7539532687"}, + {file = "pyarrow-17.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a155acc7f154b9ffcc85497509bcd0d43efb80d6f733b0dc3bb14e281f131c8b"}, + {file = "pyarrow-17.0.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:dec8d129254d0188a49f8a1fc99e0560dc1b85f60af729f47de4046015f9b0a5"}, + {file = "pyarrow-17.0.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:a48ddf5c3c6a6c505904545c25a4ae13646ae1f8ba703c4df4a1bfe4f4006bda"}, + {file = "pyarrow-17.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:42bf93249a083aca230ba7e2786c5f673507fa97bbd9725a1e2754715151a204"}, + {file = "pyarrow-17.0.0.tar.gz", hash = "sha256:4beca9521ed2c0921c1023e68d097d0299b62c362639ea315572a58f3f50fd28"}, +] + +[package.dependencies] +numpy = ">=1.16.6" + +[package.extras] +test = ["cffi", "hypothesis", "pandas", "pytest", "pytz"] + [[package]] name = "pydantic" -version = "2.7.4" +version = "2.9.2" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic-2.7.4-py3-none-any.whl", hash = "sha256:ee8538d41ccb9c0a9ad3e0e5f07bf15ed8015b481ced539a1759d8cc89ae90d0"}, - {file = "pydantic-2.7.4.tar.gz", hash = "sha256:0c84efd9548d545f63ac0060c1e4d39bb9b14db8b3c0652338aecc07b5adec52"}, + {file = "pydantic-2.9.2-py3-none-any.whl", hash = "sha256:f048cec7b26778210e28a0459867920654d48e5e62db0958433636cde4254f12"}, + {file = "pydantic-2.9.2.tar.gz", hash = "sha256:d155cef71265d1e9807ed1c32b4c8deec042a44a50a4188b25ac67ecd81a9c0f"}, ] [package.dependencies] -annotated-types = ">=0.4.0" -pydantic-core = "2.18.4" -typing-extensions = ">=4.6.1" +annotated-types = ">=0.6.0" +pydantic-core = "2.23.4" +typing-extensions = [ + {version = ">=4.12.2", markers = "python_version >= \"3.13\""}, + {version = ">=4.6.1", markers = "python_version < \"3.13\""}, +] [package.extras] email = ["email-validator (>=2.0.0)"] +timezone = ["tzdata"] [[package]] name = "pydantic-core" -version = "2.18.4" +version = "2.23.4" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic_core-2.18.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:f76d0ad001edd426b92233d45c746fd08f467d56100fd8f30e9ace4b005266e4"}, - {file = "pydantic_core-2.18.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:59ff3e89f4eaf14050c8022011862df275b552caef8082e37b542b066ce1ff26"}, - {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a55b5b16c839df1070bc113c1f7f94a0af4433fcfa1b41799ce7606e5c79ce0a"}, - {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4d0dcc59664fcb8974b356fe0a18a672d6d7cf9f54746c05f43275fc48636851"}, - {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8951eee36c57cd128f779e641e21eb40bc5073eb28b2d23f33eb0ef14ffb3f5d"}, - {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4701b19f7e3a06ea655513f7938de6f108123bf7c86bbebb1196eb9bd35cf724"}, - {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e00a3f196329e08e43d99b79b286d60ce46bed10f2280d25a1718399457e06be"}, - {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:97736815b9cc893b2b7f663628e63f436018b75f44854c8027040e05230eeddb"}, - {file = "pydantic_core-2.18.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6891a2ae0e8692679c07728819b6e2b822fb30ca7445f67bbf6509b25a96332c"}, - {file = "pydantic_core-2.18.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bc4ff9805858bd54d1a20efff925ccd89c9d2e7cf4986144b30802bf78091c3e"}, - {file = "pydantic_core-2.18.4-cp310-none-win32.whl", hash = "sha256:1b4de2e51bbcb61fdebd0ab86ef28062704f62c82bbf4addc4e37fa4b00b7cbc"}, - {file = "pydantic_core-2.18.4-cp310-none-win_amd64.whl", hash = "sha256:6a750aec7bf431517a9fd78cb93c97b9b0c496090fee84a47a0d23668976b4b0"}, - {file = "pydantic_core-2.18.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:942ba11e7dfb66dc70f9ae66b33452f51ac7bb90676da39a7345e99ffb55402d"}, - {file = "pydantic_core-2.18.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b2ebef0e0b4454320274f5e83a41844c63438fdc874ea40a8b5b4ecb7693f1c4"}, - {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a642295cd0c8df1b86fc3dced1d067874c353a188dc8e0f744626d49e9aa51c4"}, - {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f09baa656c904807e832cf9cce799c6460c450c4ad80803517032da0cd062e2"}, - {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:98906207f29bc2c459ff64fa007afd10a8c8ac080f7e4d5beff4c97086a3dabd"}, - {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:19894b95aacfa98e7cb093cd7881a0c76f55731efad31073db4521e2b6ff5b7d"}, - {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0fbbdc827fe5e42e4d196c746b890b3d72876bdbf160b0eafe9f0334525119c8"}, - {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f85d05aa0918283cf29a30b547b4df2fbb56b45b135f9e35b6807cb28bc47951"}, - {file = "pydantic_core-2.18.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e85637bc8fe81ddb73fda9e56bab24560bdddfa98aa64f87aaa4e4b6730c23d2"}, - {file = "pydantic_core-2.18.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2f5966897e5461f818e136b8451d0551a2e77259eb0f73a837027b47dc95dab9"}, - {file = "pydantic_core-2.18.4-cp311-none-win32.whl", hash = "sha256:44c7486a4228413c317952e9d89598bcdfb06399735e49e0f8df643e1ccd0558"}, - {file = "pydantic_core-2.18.4-cp311-none-win_amd64.whl", hash = "sha256:8a7164fe2005d03c64fd3b85649891cd4953a8de53107940bf272500ba8a788b"}, - {file = "pydantic_core-2.18.4-cp311-none-win_arm64.whl", hash = "sha256:4e99bc050fe65c450344421017f98298a97cefc18c53bb2f7b3531eb39bc7805"}, - {file = "pydantic_core-2.18.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:6f5c4d41b2771c730ea1c34e458e781b18cc668d194958e0112455fff4e402b2"}, - {file = "pydantic_core-2.18.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2fdf2156aa3d017fddf8aea5adfba9f777db1d6022d392b682d2a8329e087cef"}, - {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4748321b5078216070b151d5271ef3e7cc905ab170bbfd27d5c83ee3ec436695"}, - {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:847a35c4d58721c5dc3dba599878ebbdfd96784f3fb8bb2c356e123bdcd73f34"}, - {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3c40d4eaad41f78e3bbda31b89edc46a3f3dc6e171bf0ecf097ff7a0ffff7cb1"}, - {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:21a5e440dbe315ab9825fcd459b8814bb92b27c974cbc23c3e8baa2b76890077"}, - {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01dd777215e2aa86dfd664daed5957704b769e726626393438f9c87690ce78c3"}, - {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4b06beb3b3f1479d32befd1f3079cc47b34fa2da62457cdf6c963393340b56e9"}, - {file = "pydantic_core-2.18.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:564d7922e4b13a16b98772441879fcdcbe82ff50daa622d681dd682175ea918c"}, - {file = "pydantic_core-2.18.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0eb2a4f660fcd8e2b1c90ad566db2b98d7f3f4717c64fe0a83e0adb39766d5b8"}, - {file = "pydantic_core-2.18.4-cp312-none-win32.whl", hash = "sha256:8b8bab4c97248095ae0c4455b5a1cd1cdd96e4e4769306ab19dda135ea4cdb07"}, - {file = "pydantic_core-2.18.4-cp312-none-win_amd64.whl", hash = "sha256:14601cdb733d741b8958224030e2bfe21a4a881fb3dd6fbb21f071cabd48fa0a"}, - {file = "pydantic_core-2.18.4-cp312-none-win_arm64.whl", hash = "sha256:c1322d7dd74713dcc157a2b7898a564ab091ca6c58302d5c7b4c07296e3fd00f"}, - {file = "pydantic_core-2.18.4-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:823be1deb01793da05ecb0484d6c9e20baebb39bd42b5d72636ae9cf8350dbd2"}, - {file = "pydantic_core-2.18.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ebef0dd9bf9b812bf75bda96743f2a6c5734a02092ae7f721c048d156d5fabae"}, - {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae1d6df168efb88d7d522664693607b80b4080be6750c913eefb77e34c12c71a"}, - {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f9899c94762343f2cc2fc64c13e7cae4c3cc65cdfc87dd810a31654c9b7358cc"}, - {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99457f184ad90235cfe8461c4d70ab7dd2680e28821c29eca00252ba90308c78"}, - {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18f469a3d2a2fdafe99296a87e8a4c37748b5080a26b806a707f25a902c040a8"}, - {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7cdf28938ac6b8b49ae5e92f2735056a7ba99c9b110a474473fd71185c1af5d"}, - {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:938cb21650855054dc54dfd9120a851c974f95450f00683399006aa6e8abb057"}, - {file = "pydantic_core-2.18.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:44cd83ab6a51da80fb5adbd9560e26018e2ac7826f9626bc06ca3dc074cd198b"}, - {file = "pydantic_core-2.18.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:972658f4a72d02b8abfa2581d92d59f59897d2e9f7e708fdabe922f9087773af"}, - {file = "pydantic_core-2.18.4-cp38-none-win32.whl", hash = "sha256:1d886dc848e60cb7666f771e406acae54ab279b9f1e4143babc9c2258213daa2"}, - {file = "pydantic_core-2.18.4-cp38-none-win_amd64.whl", hash = "sha256:bb4462bd43c2460774914b8525f79b00f8f407c945d50881568f294c1d9b4443"}, - {file = "pydantic_core-2.18.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:44a688331d4a4e2129140a8118479443bd6f1905231138971372fcde37e43528"}, - {file = "pydantic_core-2.18.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a2fdd81edd64342c85ac7cf2753ccae0b79bf2dfa063785503cb85a7d3593223"}, - {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:86110d7e1907ab36691f80b33eb2da87d780f4739ae773e5fc83fb272f88825f"}, - {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:46387e38bd641b3ee5ce247563b60c5ca098da9c56c75c157a05eaa0933ed154"}, - {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:123c3cec203e3f5ac7b000bd82235f1a3eced8665b63d18be751f115588fea30"}, - {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dc1803ac5c32ec324c5261c7209e8f8ce88e83254c4e1aebdc8b0a39f9ddb443"}, - {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53db086f9f6ab2b4061958d9c276d1dbe3690e8dd727d6abf2321d6cce37fa94"}, - {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:abc267fa9837245cc28ea6929f19fa335f3dc330a35d2e45509b6566dc18be23"}, - {file = "pydantic_core-2.18.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a0d829524aaefdebccb869eed855e2d04c21d2d7479b6cada7ace5448416597b"}, - {file = "pydantic_core-2.18.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:509daade3b8649f80d4e5ff21aa5673e4ebe58590b25fe42fac5f0f52c6f034a"}, - {file = "pydantic_core-2.18.4-cp39-none-win32.whl", hash = "sha256:ca26a1e73c48cfc54c4a76ff78df3727b9d9f4ccc8dbee4ae3f73306a591676d"}, - {file = "pydantic_core-2.18.4-cp39-none-win_amd64.whl", hash = "sha256:c67598100338d5d985db1b3d21f3619ef392e185e71b8d52bceacc4a7771ea7e"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:574d92eac874f7f4db0ca653514d823a0d22e2354359d0759e3f6a406db5d55d"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1f4d26ceb5eb9eed4af91bebeae4b06c3fb28966ca3a8fb765208cf6b51102ab"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77450e6d20016ec41f43ca4a6c63e9fdde03f0ae3fe90e7c27bdbeaece8b1ed4"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d323a01da91851a4f17bf592faf46149c9169d68430b3146dcba2bb5e5719abc"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43d447dd2ae072a0065389092a231283f62d960030ecd27565672bd40746c507"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:578e24f761f3b425834f297b9935e1ce2e30f51400964ce4801002435a1b41ef"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:81b5efb2f126454586d0f40c4d834010979cb80785173d1586df845a632e4e6d"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ab86ce7c8f9bea87b9d12c7f0af71102acbf5ecbc66c17796cff45dae54ef9a5"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:90afc12421df2b1b4dcc975f814e21bc1754640d502a2fbcc6d41e77af5ec312"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:51991a89639a912c17bef4b45c87bd83593aee0437d8102556af4885811d59f5"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:293afe532740370aba8c060882f7d26cfd00c94cae32fd2e212a3a6e3b7bc15e"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b48ece5bde2e768197a2d0f6e925f9d7e3e826f0ad2271120f8144a9db18d5c8"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:eae237477a873ab46e8dd748e515c72c0c804fb380fbe6c85533c7de51f23a8f"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:834b5230b5dfc0c1ec37b2fda433b271cbbc0e507560b5d1588e2cc1148cf1ce"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e858ac0a25074ba4bce653f9b5d0a85b7456eaddadc0ce82d3878c22489fa4ee"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2fd41f6eff4c20778d717af1cc50eca52f5afe7805ee530a4fbd0bae284f16e9"}, - {file = "pydantic_core-2.18.4.tar.gz", hash = "sha256:ec3beeada09ff865c344ff3bc2f427f5e6c26401cc6113d77e372c3fdac73864"}, + {file = "pydantic_core-2.23.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b10bd51f823d891193d4717448fab065733958bdb6a6b351967bd349d48d5c9b"}, + {file = "pydantic_core-2.23.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4fc714bdbfb534f94034efaa6eadd74e5b93c8fa6315565a222f7b6f42ca1166"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63e46b3169866bd62849936de036f901a9356e36376079b05efa83caeaa02ceb"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed1a53de42fbe34853ba90513cea21673481cd81ed1be739f7f2efb931b24916"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cfdd16ab5e59fc31b5e906d1a3f666571abc367598e3e02c83403acabc092e07"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255a8ef062cbf6674450e668482456abac99a5583bbafb73f9ad469540a3a232"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a7cd62e831afe623fbb7aabbb4fe583212115b3ef38a9f6b71869ba644624a2"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f09e2ff1f17c2b51f2bc76d1cc33da96298f0a036a137f5440ab3ec5360b624f"}, + {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e38e63e6f3d1cec5a27e0afe90a085af8b6806ee208b33030e65b6516353f1a3"}, + {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0dbd8dbed2085ed23b5c04afa29d8fd2771674223135dc9bc937f3c09284d071"}, + {file = "pydantic_core-2.23.4-cp310-none-win32.whl", hash = "sha256:6531b7ca5f951d663c339002e91aaebda765ec7d61b7d1e3991051906ddde119"}, + {file = "pydantic_core-2.23.4-cp310-none-win_amd64.whl", hash = "sha256:7c9129eb40958b3d4500fa2467e6a83356b3b61bfff1b414c7361d9220f9ae8f"}, + {file = "pydantic_core-2.23.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:77733e3892bb0a7fa797826361ce8a9184d25c8dffaec60b7ffe928153680ba8"}, + {file = "pydantic_core-2.23.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b84d168f6c48fabd1f2027a3d1bdfe62f92cade1fb273a5d68e621da0e44e6d"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df49e7a0861a8c36d089c1ed57d308623d60416dab2647a4a17fe050ba85de0e"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff02b6d461a6de369f07ec15e465a88895f3223eb75073ffea56b84d9331f607"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:996a38a83508c54c78a5f41456b0103c30508fed9abcad0a59b876d7398f25fd"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d97683ddee4723ae8c95d1eddac7c192e8c552da0c73a925a89fa8649bf13eea"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:216f9b2d7713eb98cb83c80b9c794de1f6b7e3145eef40400c62e86cee5f4e1e"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6f783e0ec4803c787bcea93e13e9932edab72068f68ecffdf86a99fd5918878b"}, + {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d0776dea117cf5272382634bd2a5c1b6eb16767c223c6a5317cd3e2a757c61a0"}, + {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d5f7a395a8cf1621939692dba2a6b6a830efa6b3cee787d82c7de1ad2930de64"}, + {file = "pydantic_core-2.23.4-cp311-none-win32.whl", hash = "sha256:74b9127ffea03643e998e0c5ad9bd3811d3dac8c676e47db17b0ee7c3c3bf35f"}, + {file = "pydantic_core-2.23.4-cp311-none-win_amd64.whl", hash = "sha256:98d134c954828488b153d88ba1f34e14259284f256180ce659e8d83e9c05eaa3"}, + {file = "pydantic_core-2.23.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f3e0da4ebaef65158d4dfd7d3678aad692f7666877df0002b8a522cdf088f231"}, + {file = "pydantic_core-2.23.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f69a8e0b033b747bb3e36a44e7732f0c99f7edd5cea723d45bc0d6e95377ffee"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:723314c1d51722ab28bfcd5240d858512ffd3116449c557a1336cbe3919beb87"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb2802e667b7051a1bebbfe93684841cc9351004e2badbd6411bf357ab8d5ac8"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d18ca8148bebe1b0a382a27a8ee60350091a6ddaf475fa05ef50dc35b5df6327"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33e3d65a85a2a4a0dc3b092b938a4062b1a05f3a9abde65ea93b233bca0e03f2"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:128585782e5bfa515c590ccee4b727fb76925dd04a98864182b22e89a4e6ed36"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:68665f4c17edcceecc112dfed5dbe6f92261fb9d6054b47d01bf6371a6196126"}, + {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:20152074317d9bed6b7a95ade3b7d6054845d70584216160860425f4fbd5ee9e"}, + {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9261d3ce84fa1d38ed649c3638feefeae23d32ba9182963e465d58d62203bd24"}, + {file = "pydantic_core-2.23.4-cp312-none-win32.whl", hash = "sha256:4ba762ed58e8d68657fc1281e9bb72e1c3e79cc5d464be146e260c541ec12d84"}, + {file = "pydantic_core-2.23.4-cp312-none-win_amd64.whl", hash = "sha256:97df63000f4fea395b2824da80e169731088656d1818a11b95f3b173747b6cd9"}, + {file = "pydantic_core-2.23.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7530e201d10d7d14abce4fb54cfe5b94a0aefc87da539d0346a484ead376c3cc"}, + {file = "pydantic_core-2.23.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:df933278128ea1cd77772673c73954e53a1c95a4fdf41eef97c2b779271bd0bd"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cb3da3fd1b6a5d0279a01877713dbda118a2a4fc6f0d821a57da2e464793f05"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c6dcb030aefb668a2b7009c85b27f90e51e6a3b4d5c9bc4c57631292015b0d"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:696dd8d674d6ce621ab9d45b205df149399e4bb9aa34102c970b721554828510"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2971bb5ffe72cc0f555c13e19b23c85b654dd2a8f7ab493c262071377bfce9f6"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8394d940e5d400d04cad4f75c0598665cbb81aecefaca82ca85bd28264af7f9b"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0dff76e0602ca7d4cdaacc1ac4c005e0ce0dcfe095d5b5259163a80d3a10d327"}, + {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7d32706badfe136888bdea71c0def994644e09fff0bfe47441deaed8e96fdbc6"}, + {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ed541d70698978a20eb63d8c5d72f2cc6d7079d9d90f6b50bad07826f1320f5f"}, + {file = "pydantic_core-2.23.4-cp313-none-win32.whl", hash = "sha256:3d5639516376dce1940ea36edf408c554475369f5da2abd45d44621cb616f769"}, + {file = "pydantic_core-2.23.4-cp313-none-win_amd64.whl", hash = "sha256:5a1504ad17ba4210df3a045132a7baeeba5a200e930f57512ee02909fc5c4cb5"}, + {file = "pydantic_core-2.23.4-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d4488a93b071c04dc20f5cecc3631fc78b9789dd72483ba15d423b5b3689b555"}, + {file = "pydantic_core-2.23.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:81965a16b675b35e1d09dd14df53f190f9129c0202356ed44ab2728b1c905658"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ffa2ebd4c8530079140dd2d7f794a9d9a73cbb8e9d59ffe24c63436efa8f271"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:61817945f2fe7d166e75fbfb28004034b48e44878177fc54d81688e7b85a3665"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:29d2c342c4bc01b88402d60189f3df065fb0dda3654744d5a165a5288a657368"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5e11661ce0fd30a6790e8bcdf263b9ec5988e95e63cf901972107efc49218b13"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d18368b137c6295db49ce7218b1a9ba15c5bc254c96d7c9f9e924a9bc7825ad"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ec4e55f79b1c4ffb2eecd8a0cfba9955a2588497d96851f4c8f99aa4a1d39b12"}, + {file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:374a5e5049eda9e0a44c696c7ade3ff355f06b1fe0bb945ea3cac2bc336478a2"}, + {file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5c364564d17da23db1106787675fc7af45f2f7b58b4173bfdd105564e132e6fb"}, + {file = "pydantic_core-2.23.4-cp38-none-win32.whl", hash = "sha256:d7a80d21d613eec45e3d41eb22f8f94ddc758a6c4720842dc74c0581f54993d6"}, + {file = "pydantic_core-2.23.4-cp38-none-win_amd64.whl", hash = "sha256:5f5ff8d839f4566a474a969508fe1c5e59c31c80d9e140566f9a37bba7b8d556"}, + {file = "pydantic_core-2.23.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a4fa4fc04dff799089689f4fd502ce7d59de529fc2f40a2c8836886c03e0175a"}, + {file = "pydantic_core-2.23.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0a7df63886be5e270da67e0966cf4afbae86069501d35c8c1b3b6c168f42cb36"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcedcd19a557e182628afa1d553c3895a9f825b936415d0dbd3cd0bbcfd29b4b"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f54b118ce5de9ac21c363d9b3caa6c800341e8c47a508787e5868c6b79c9323"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86d2f57d3e1379a9525c5ab067b27dbb8a0642fb5d454e17a9ac434f9ce523e3"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de6d1d1b9e5101508cb37ab0d972357cac5235f5c6533d1071964c47139257df"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1278e0d324f6908e872730c9102b0112477a7f7cf88b308e4fc36ce1bdb6d58c"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9a6b5099eeec78827553827f4c6b8615978bb4b6a88e5d9b93eddf8bb6790f55"}, + {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e55541f756f9b3ee346b840103f32779c695a19826a4c442b7954550a0972040"}, + {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a5c7ba8ffb6d6f8f2ab08743be203654bb1aaa8c9dcb09f82ddd34eadb695605"}, + {file = "pydantic_core-2.23.4-cp39-none-win32.whl", hash = "sha256:37b0fe330e4a58d3c58b24d91d1eb102aeec675a3db4c292ec3928ecd892a9a6"}, + {file = "pydantic_core-2.23.4-cp39-none-win_amd64.whl", hash = "sha256:1498bec4c05c9c787bde9125cfdcc63a41004ff167f495063191b863399b1a29"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f455ee30a9d61d3e1a15abd5068827773d6e4dc513e795f380cdd59932c782d5"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1e90d2e3bd2c3863d48525d297cd143fe541be8bbf6f579504b9712cb6b643ec"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e203fdf807ac7e12ab59ca2bfcabb38c7cf0b33c41efeb00f8e5da1d86af480"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e08277a400de01bc72436a0ccd02bdf596631411f592ad985dcee21445bd0068"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f220b0eea5965dec25480b6333c788fb72ce5f9129e8759ef876a1d805d00801"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d06b0c8da4f16d1d1e352134427cb194a0a6e19ad5db9161bf32b2113409e728"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ba1a0996f6c2773bd83e63f18914c1de3c9dd26d55f4ac302a7efe93fb8e7433"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:9a5bce9d23aac8f0cf0836ecfc033896aa8443b501c58d0602dbfd5bd5b37753"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:78ddaaa81421a29574a682b3179d4cf9e6d405a09b99d93ddcf7e5239c742e21"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:883a91b5dd7d26492ff2f04f40fbb652de40fcc0afe07e8129e8ae779c2110eb"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88ad334a15b32a791ea935af224b9de1bf99bcd62fabf745d5f3442199d86d59"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:233710f069d251feb12a56da21e14cca67994eab08362207785cf8c598e74577"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:19442362866a753485ba5e4be408964644dd6a09123d9416c54cd49171f50744"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:624e278a7d29b6445e4e813af92af37820fafb6dcc55c012c834f9e26f9aaaef"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f5ef8f42bec47f21d07668a043f077d507e5bf4e668d5c6dfe6aaba89de1a5b8"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:aea443fffa9fbe3af1a9ba721a87f926fe548d32cab71d188a6ede77d0ff244e"}, + {file = "pydantic_core-2.23.4.tar.gz", hash = "sha256:2584f7cf844ac4d970fba483a717dbe10c1c1c96a969bf65d61ffe94df1b2863"}, ] [package.dependencies] @@ -860,6 +1938,20 @@ files = [ [package.extras] windows-terminal = ["colorama (>=0.4.6)"] +[[package]] +name = "pyparsing" +version = "3.1.4" +description = "pyparsing module - Classes and methods to define and execute parsing grammars" +optional = false +python-versions = ">=3.6.8" +files = [ + {file = "pyparsing-3.1.4-py3-none-any.whl", hash = "sha256:a6a7ee4235a3f944aa1fa2249307708f893fe5717dc603503c6c7969c070fb7c"}, + {file = "pyparsing-3.1.4.tar.gz", hash = "sha256:f86ec8d1a83f11977c9a6ea7598e8c27fc5cddfa5b07ea2241edbbde1d7bc032"}, +] + +[package.extras] +diagrams = ["jinja2", "railroad-diagrams"] + [[package]] name = "pytest" version = "8.3.2" @@ -914,13 +2006,13 @@ six = ">=1.5" [[package]] name = "pytz" -version = "2024.1" +version = "2024.2" description = "World timezone definitions, modern and historical" optional = false python-versions = "*" files = [ - {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, - {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, + {file = "pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725"}, + {file = "pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a"}, ] [[package]] @@ -946,13 +2038,13 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "rich" -version = "13.7.1" +version = "13.8.1" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.7.0" files = [ - {file = "rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"}, - {file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"}, + {file = "rich-13.8.1-py3-none-any.whl", hash = "sha256:1760a3c0848469b97b558fc61c85233e3dafb69c7a071b4d60c38099d3cd4c06"}, + {file = "rich-13.8.1.tar.gz", hash = "sha256:8260cda28e3db6bf04d2d1ef4dbc03ba80a824c88b0e7668a0f23126a424844a"}, ] [package.dependencies] @@ -989,6 +2081,43 @@ files = [ {file = "ruff-0.6.3.tar.gz", hash = "sha256:183b99e9edd1ef63be34a3b51fee0a9f4ab95add123dbf89a71f7b1f0c991983"}, ] +[[package]] +name = "s3fs" +version = "2024.9.0" +description = "Convenient Filesystem interface over S3" +optional = false +python-versions = ">=3.8" +files = [ + {file = "s3fs-2024.9.0-py3-none-any.whl", hash = "sha256:3a7dc7acae4358af8e8dfb693e82a8477f9f2c847de5d44cf65fee75752eaca3"}, + {file = "s3fs-2024.9.0.tar.gz", hash = "sha256:6493705abb50374d6b7994f9616d27adbdd8a219c8635100bdc286382efd91f5"}, +] + +[package.dependencies] +aiobotocore = ">=2.5.4,<3.0.0" +aiohttp = "<4.0.0a0 || >4.0.0a0,<4.0.0a1 || >4.0.0a1" +fsspec = "==2024.9.0.*" + +[package.extras] +awscli = ["aiobotocore[awscli] (>=2.5.4,<3.0.0)"] +boto3 = ["aiobotocore[boto3] (>=2.5.4,<3.0.0)"] + +[[package]] +name = "s3transfer" +version = "0.10.2" +description = "An Amazon S3 Transfer Manager" +optional = false +python-versions = ">=3.8" +files = [ + {file = "s3transfer-0.10.2-py3-none-any.whl", hash = "sha256:eca1c20de70a39daee580aef4986996620f365c4e0fda6a86100231d62f1bf69"}, + {file = "s3transfer-0.10.2.tar.gz", hash = "sha256:0711534e9356d3cc692fdde846b4a1e4b0cb6519971860796e6bc4c7aea00ef6"}, +] + +[package.dependencies] +botocore = ">=1.33.2,<2.0a.0" + +[package.extras] +crt = ["botocore[crt] (>=1.33.2,<2.0a.0)"] + [[package]] name = "shellingham" version = "1.5.4" @@ -1013,13 +2142,13 @@ files = [ [[package]] name = "soupsieve" -version = "2.5" +version = "2.6" description = "A modern CSS selector implementation for Beautiful Soup." optional = false python-versions = ">=3.8" files = [ - {file = "soupsieve-2.5-py3-none-any.whl", hash = "sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7"}, - {file = "soupsieve-2.5.tar.gz", hash = "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690"}, + {file = "soupsieve-2.6-py3-none-any.whl", hash = "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9"}, + {file = "soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb"}, ] [[package]] @@ -1099,13 +2228,13 @@ typing-extensions = ">=3.7.4" [[package]] name = "tzdata" -version = "2024.1" +version = "2024.2" description = "Provider of IANA time zone data" optional = false python-versions = ">=2" files = [ - {file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"}, - {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, + {file = "tzdata-2024.2-py2.py3-none-any.whl", hash = "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd"}, + {file = "tzdata-2024.2.tar.gz", hash = "sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc"}, ] [[package]] @@ -1197,13 +2326,13 @@ files = [ [[package]] name = "urllib3" -version = "2.2.2" +version = "2.2.3" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.8" files = [ - {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"}, - {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"}, + {file = "urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac"}, + {file = "urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"}, ] [package.extras] @@ -1291,7 +2420,112 @@ files = [ {file = "wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d"}, ] +[[package]] +name = "yarl" +version = "1.13.1" +description = "Yet another URL library" +optional = false +python-versions = ">=3.8" +files = [ + {file = "yarl-1.13.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:82e692fb325013a18a5b73a4fed5a1edaa7c58144dc67ad9ef3d604eccd451ad"}, + {file = "yarl-1.13.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df4e82e68f43a07735ae70a2d84c0353e58e20add20ec0af611f32cd5ba43fb4"}, + {file = "yarl-1.13.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ec9dd328016d8d25702a24ee274932aebf6be9787ed1c28d021945d264235b3c"}, + {file = "yarl-1.13.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5820bd4178e6a639b3ef1db8b18500a82ceab6d8b89309e121a6859f56585b05"}, + {file = "yarl-1.13.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86c438ce920e089c8c2388c7dcc8ab30dfe13c09b8af3d306bcabb46a053d6f7"}, + {file = "yarl-1.13.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3de86547c820e4f4da4606d1c8ab5765dd633189791f15247706a2eeabc783ae"}, + {file = "yarl-1.13.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ca53632007c69ddcdefe1e8cbc3920dd88825e618153795b57e6ebcc92e752a"}, + {file = "yarl-1.13.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d4ee1d240b84e2f213565f0ec08caef27a0e657d4c42859809155cf3a29d1735"}, + {file = "yarl-1.13.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c49f3e379177f4477f929097f7ed4b0622a586b0aa40c07ac8c0f8e40659a1ac"}, + {file = "yarl-1.13.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5c5e32fef09ce101fe14acd0f498232b5710effe13abac14cd95de9c274e689e"}, + {file = "yarl-1.13.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:ab9524e45ee809a083338a749af3b53cc7efec458c3ad084361c1dbf7aaf82a2"}, + {file = "yarl-1.13.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:b1481c048fe787f65e34cb06f7d6824376d5d99f1231eae4778bbe5c3831076d"}, + {file = "yarl-1.13.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:31497aefd68036d8e31bfbacef915826ca2e741dbb97a8d6c7eac66deda3b606"}, + {file = "yarl-1.13.1-cp310-cp310-win32.whl", hash = "sha256:1fa56f34b2236f5192cb5fceba7bbb09620e5337e0b6dfe2ea0ddbd19dd5b154"}, + {file = "yarl-1.13.1-cp310-cp310-win_amd64.whl", hash = "sha256:1bbb418f46c7f7355084833051701b2301092e4611d9e392360c3ba2e3e69f88"}, + {file = "yarl-1.13.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:216a6785f296169ed52cd7dcdc2612f82c20f8c9634bf7446327f50398732a51"}, + {file = "yarl-1.13.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:40c6e73c03a6befb85b72da213638b8aaa80fe4136ec8691560cf98b11b8ae6e"}, + {file = "yarl-1.13.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2430cf996113abe5aee387d39ee19529327205cda975d2b82c0e7e96e5fdabdc"}, + {file = "yarl-1.13.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fb4134cc6e005b99fa29dbc86f1ea0a298440ab6b07c6b3ee09232a3b48f495"}, + {file = "yarl-1.13.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:309c104ecf67626c033845b860d31594a41343766a46fa58c3309c538a1e22b2"}, + {file = "yarl-1.13.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f90575e9fe3aae2c1e686393a9689c724cd00045275407f71771ae5d690ccf38"}, + {file = "yarl-1.13.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d2e1626be8712333a9f71270366f4a132f476ffbe83b689dd6dc0d114796c74"}, + {file = "yarl-1.13.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b66c87da3c6da8f8e8b648878903ca54589038a0b1e08dde2c86d9cd92d4ac9"}, + {file = "yarl-1.13.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cf1ad338620249f8dd6d4b6a91a69d1f265387df3697ad5dc996305cf6c26fb2"}, + {file = "yarl-1.13.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9915300fe5a0aa663c01363db37e4ae8e7c15996ebe2c6cce995e7033ff6457f"}, + {file = "yarl-1.13.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:703b0f584fcf157ef87816a3c0ff868e8c9f3c370009a8b23b56255885528f10"}, + {file = "yarl-1.13.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:1d8e3ca29f643dd121f264a7c89f329f0fcb2e4461833f02de6e39fef80f89da"}, + {file = "yarl-1.13.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7055bbade838d68af73aea13f8c86588e4bcc00c2235b4b6d6edb0dbd174e246"}, + {file = "yarl-1.13.1-cp311-cp311-win32.whl", hash = "sha256:a3442c31c11088e462d44a644a454d48110f0588de830921fd201060ff19612a"}, + {file = "yarl-1.13.1-cp311-cp311-win_amd64.whl", hash = "sha256:81bad32c8f8b5897c909bf3468bf601f1b855d12f53b6af0271963ee67fff0d2"}, + {file = "yarl-1.13.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f452cc1436151387d3d50533523291d5f77c6bc7913c116eb985304abdbd9ec9"}, + {file = "yarl-1.13.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9cec42a20eae8bebf81e9ce23fb0d0c729fc54cf00643eb251ce7c0215ad49fe"}, + {file = "yarl-1.13.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d959fe96e5c2712c1876d69af0507d98f0b0e8d81bee14cfb3f6737470205419"}, + {file = "yarl-1.13.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b8c837ab90c455f3ea8e68bee143472ee87828bff19ba19776e16ff961425b57"}, + {file = "yarl-1.13.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:94a993f976cdcb2dc1b855d8b89b792893220db8862d1a619efa7451817c836b"}, + {file = "yarl-1.13.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b2442a415a5f4c55ced0fade7b72123210d579f7d950e0b5527fc598866e62c"}, + {file = "yarl-1.13.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3fdbf0418489525231723cdb6c79e7738b3cbacbaed2b750cb033e4ea208f220"}, + {file = "yarl-1.13.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6b7f6e699304717fdc265a7e1922561b02a93ceffdaefdc877acaf9b9f3080b8"}, + {file = "yarl-1.13.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bcd5bf4132e6a8d3eb54b8d56885f3d3a38ecd7ecae8426ecf7d9673b270de43"}, + {file = "yarl-1.13.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:2a93a4557f7fc74a38ca5a404abb443a242217b91cd0c4840b1ebedaad8919d4"}, + {file = "yarl-1.13.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:22b739f99c7e4787922903f27a892744189482125cc7b95b747f04dd5c83aa9f"}, + {file = "yarl-1.13.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:2db874dd1d22d4c2c657807562411ffdfabec38ce4c5ce48b4c654be552759dc"}, + {file = "yarl-1.13.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4feaaa4742517eaceafcbe74595ed335a494c84634d33961214b278126ec1485"}, + {file = "yarl-1.13.1-cp312-cp312-win32.whl", hash = "sha256:bbf9c2a589be7414ac4a534d54e4517d03f1cbb142c0041191b729c2fa23f320"}, + {file = "yarl-1.13.1-cp312-cp312-win_amd64.whl", hash = "sha256:d07b52c8c450f9366c34aa205754355e933922c79135125541daae6cbf31c799"}, + {file = "yarl-1.13.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:95c6737f28069153c399d875317f226bbdea939fd48a6349a3b03da6829fb550"}, + {file = "yarl-1.13.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:cd66152561632ed4b2a9192e7f8e5a1d41e28f58120b4761622e0355f0fe034c"}, + {file = "yarl-1.13.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6a2acde25be0cf9be23a8f6cbd31734536a264723fca860af3ae5e89d771cd71"}, + {file = "yarl-1.13.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9a18595e6a2ee0826bf7dfdee823b6ab55c9b70e8f80f8b77c37e694288f5de1"}, + {file = "yarl-1.13.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a31d21089894942f7d9a8df166b495101b7258ff11ae0abec58e32daf8088813"}, + {file = "yarl-1.13.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:45f209fb4bbfe8630e3d2e2052535ca5b53d4ce2d2026bed4d0637b0416830da"}, + {file = "yarl-1.13.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f722f30366474a99745533cc4015b1781ee54b08de73260b2bbe13316079851"}, + {file = "yarl-1.13.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3bf60444269345d712838bb11cc4eadaf51ff1a364ae39ce87a5ca8ad3bb2c8"}, + {file = "yarl-1.13.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:942c80a832a79c3707cca46bd12ab8aa58fddb34b1626d42b05aa8f0bcefc206"}, + {file = "yarl-1.13.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:44b07e1690f010c3c01d353b5790ec73b2f59b4eae5b0000593199766b3f7a5c"}, + {file = "yarl-1.13.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:396e59b8de7e4d59ff5507fb4322d2329865b909f29a7ed7ca37e63ade7f835c"}, + {file = "yarl-1.13.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3bb83a0f12701c0b91112a11148b5217617982e1e466069d0555be9b372f2734"}, + {file = "yarl-1.13.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c92b89bffc660f1274779cb6fbb290ec1f90d6dfe14492523a0667f10170de26"}, + {file = "yarl-1.13.1-cp313-cp313-win32.whl", hash = "sha256:269c201bbc01d2cbba5b86997a1e0f73ba5e2f471cfa6e226bcaa7fd664b598d"}, + {file = "yarl-1.13.1-cp313-cp313-win_amd64.whl", hash = "sha256:1d0828e17fa701b557c6eaed5edbd9098eb62d8838344486248489ff233998b8"}, + {file = "yarl-1.13.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8be8cdfe20787e6a5fcbd010f8066227e2bb9058331a4eccddec6c0db2bb85b2"}, + {file = "yarl-1.13.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:08d7148ff11cb8e886d86dadbfd2e466a76d5dd38c7ea8ebd9b0e07946e76e4b"}, + {file = "yarl-1.13.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4afdf84610ca44dcffe8b6c22c68f309aff96be55f5ea2fa31c0c225d6b83e23"}, + {file = "yarl-1.13.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0d12fe78dcf60efa205e9a63f395b5d343e801cf31e5e1dda0d2c1fb618073d"}, + {file = "yarl-1.13.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:298c1eecfd3257aa16c0cb0bdffb54411e3e831351cd69e6b0739be16b1bdaa8"}, + {file = "yarl-1.13.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c14c16831b565707149c742d87a6203eb5597f4329278446d5c0ae7a1a43928e"}, + {file = "yarl-1.13.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a9bacedbb99685a75ad033fd4de37129449e69808e50e08034034c0bf063f99"}, + {file = "yarl-1.13.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:658e8449b84b92a4373f99305de042b6bd0d19bf2080c093881e0516557474a5"}, + {file = "yarl-1.13.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:373f16f38721c680316a6a00ae21cc178e3a8ef43c0227f88356a24c5193abd6"}, + {file = "yarl-1.13.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:45d23c4668d4925688e2ea251b53f36a498e9ea860913ce43b52d9605d3d8177"}, + {file = "yarl-1.13.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:f7917697bcaa3bc3e83db91aa3a0e448bf5cde43c84b7fc1ae2427d2417c0224"}, + {file = "yarl-1.13.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:5989a38ba1281e43e4663931a53fbf356f78a0325251fd6af09dd03b1d676a09"}, + {file = "yarl-1.13.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:11b3ca8b42a024513adce810385fcabdd682772411d95bbbda3b9ed1a4257644"}, + {file = "yarl-1.13.1-cp38-cp38-win32.whl", hash = "sha256:dcaef817e13eafa547cdfdc5284fe77970b891f731266545aae08d6cce52161e"}, + {file = "yarl-1.13.1-cp38-cp38-win_amd64.whl", hash = "sha256:7addd26594e588503bdef03908fc207206adac5bd90b6d4bc3e3cf33a829f57d"}, + {file = "yarl-1.13.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a0ae6637b173d0c40b9c1462e12a7a2000a71a3258fa88756a34c7d38926911c"}, + {file = "yarl-1.13.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:576365c9f7469e1f6124d67b001639b77113cfd05e85ce0310f5f318fd02fe85"}, + {file = "yarl-1.13.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:78f271722423b2d4851cf1f4fa1a1c4833a128d020062721ba35e1a87154a049"}, + {file = "yarl-1.13.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d74f3c335cfe9c21ea78988e67f18eb9822f5d31f88b41aec3a1ec5ecd32da5"}, + {file = "yarl-1.13.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1891d69a6ba16e89473909665cd355d783a8a31bc84720902c5911dbb6373465"}, + {file = "yarl-1.13.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fb382fd7b4377363cc9f13ba7c819c3c78ed97c36a82f16f3f92f108c787cbbf"}, + {file = "yarl-1.13.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c8854b9f80693d20cec797d8e48a848c2fb273eb6f2587b57763ccba3f3bd4b"}, + {file = "yarl-1.13.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bbf2c3f04ff50f16404ce70f822cdc59760e5e2d7965905f0e700270feb2bbfc"}, + {file = "yarl-1.13.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fb9f59f3848edf186a76446eb8bcf4c900fe147cb756fbbd730ef43b2e67c6a7"}, + {file = "yarl-1.13.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ef9b85fa1bc91c4db24407e7c4da93a5822a73dd4513d67b454ca7064e8dc6a3"}, + {file = "yarl-1.13.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:098b870c18f1341786f290b4d699504e18f1cd050ed179af8123fd8232513424"}, + {file = "yarl-1.13.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:8c723c91c94a3bc8033dd2696a0f53e5d5f8496186013167bddc3fb5d9df46a3"}, + {file = "yarl-1.13.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:44a4c40a6f84e4d5955b63462a0e2a988f8982fba245cf885ce3be7618f6aa7d"}, + {file = "yarl-1.13.1-cp39-cp39-win32.whl", hash = "sha256:84bbcdcf393139f0abc9f642bf03f00cac31010f3034faa03224a9ef0bb74323"}, + {file = "yarl-1.13.1-cp39-cp39-win_amd64.whl", hash = "sha256:fc2931ac9ce9c61c9968989ec831d3a5e6fcaaff9474e7cfa8de80b7aff5a093"}, + {file = "yarl-1.13.1-py3-none-any.whl", hash = "sha256:6a5185ad722ab4dd52d5fb1f30dcc73282eb1ed494906a92d1a228d3f89607b0"}, + {file = "yarl-1.13.1.tar.gz", hash = "sha256:ec8cfe2295f3e5e44c51f57272afbd69414ae629ec7c6b27f5a410efc78b70a0"}, +] + +[package.dependencies] +idna = ">=2.0" +multidict = ">=4.0" + [metadata] lock-version = "2.0" python-versions = ">=3.12,<4" -content-hash = "7e85a50b9b22e511b3fa4628e8f3f2b376ef6674e4ac0dd011a1cb7693c5c07b" +content-hash = "4425a4f78f96815ed053e6d932202106b12a99c4455390643dfa5a4d422b1beb" diff --git a/pyproject.toml b/pyproject.toml index 51e99184..6e067ee2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,6 +17,12 @@ pandera = "^0.20.3" requests = "^2.32.3" tabulate = "^0.9.0" ujson = "^5.9.0" +matplotlib = "^3.9.0" +fsspec = "^2024.6.1" +polars = "^1.6.0" +pyarrow = "^17.0.0" +boto3 = "~1.34.0" +s3fs = "^2024.9.0" [tool.poetry.group.dev.dependencies] pytest = "8.3.2" diff --git a/src/regtech_data_validator/check_functions.py b/src/regtech_data_validator/check_functions.py index 4a74c5eb..2ffeca1f 100644 --- a/src/regtech_data_validator/check_functions.py +++ b/src/regtech_data_validator/check_functions.py @@ -9,13 +9,24 @@ The names of the check functions should clearly indicate the purpose of the function. This may or may not align with the name of the validation -in the fig.""" +in the fig. -import re -from datetime import datetime, timedelta -from typing import Dict +Majority of these checks utilize Pandera Polars PolarsData objects. These +objects contain a key, which is the field the check is associated with, and +a lazyframe which is then used to do the check. This significantly improves +processing time over Pandas, as well as resource utilization. Pandera Polars +does NOT support group_by functions, therefore a "related_fields" arg was added +to the checks that require validating data across multiple fields. -import pandas as pd +Each Pandera Polars check returns either a scalar boolean, or a lazyframe with a +'check_results' boolean column which shows if each row passed or failed the check + +""" + +from datetime import datetime + +import pandera.polars as pa +import polars as pl import operator @@ -48,11 +59,11 @@ def comparison_helper(value: str, limit: str, accept_blank: bool, operand) -> bo return operand(float(value), float(limit)) -def begins_with_same_lei(ulis: pd.Series) -> bool: +def begins_with_same_lei(ulis: pl.Series) -> bool: """Verifies that only a single LEI prefixes the list of ULIs. Args: - ulis (pd.Series): ULIs supplied in the submission. + ulis (pl.Series): ULIs supplied in the submission. Returns: bool: True indicates that only a single LEI is present. False @@ -60,14 +71,14 @@ def begins_with_same_lei(ulis: pd.Series) -> bool: """ # the lei is the first 20 characters of the supplied uli - leis = ulis.apply(lambda s: s[:20]) + leis = ulis.apply(lambda s: s[:20], pl.String) # there should only be a single lei present in the submission across # all records. return leis.nunique() == 1 -def is_date(date: str) -> bool: +def is_date(field_data: pa.PolarsData) -> bool: """Attempt datetime conversion. This checks whether the date string has the format %Y%m%d and @@ -83,155 +94,100 @@ def is_date(date: str) -> bool: successfully to a datetime.datetime object without error. """ - try: - # check for number type and length must be 8 to match YYYYMMDD - if date.isdigit() and len(date) == 8: - # if format and type is correct, verify datetime content - datetime.strptime(date, "%Y%m%d") - return True - else: - return False - except ValueError: - return False - - -def ct_credit_product_ff_blank_validity(grouped_data: Dict[int, pd.Series]) -> pd.Series: - """Checks the validity of the field `ct_credit_product_ff` based on - the selection of `ct_credit_product`. - - The free form field should be blank unless 977 is selected. - - Args: - grouped_data (Dict[int, pd.Series]): This is a mapping of values - from ct_credit_product to a pd.Series containing all free - form text entries supplied for the given value of the credit - product field. - - Example: {977: pd.Series(["some text", "Some more text."]), - 1: pd.Series(["", "", "I should be blank!"])} - - Returns: - pd.Series: A boolean series specifying whether the value of - ct_credit_product_ff is valid at the specified index. - """ - - # will hold individual boolean series to be concatenated at return - validation_holder = [] - - for ct_credit_product_value, ct_credit_product_ff_series in grouped_data.items(): - if ct_credit_product_value != 977: - # free form text field should be blank for non 977 entries - validation_holder.append(ct_credit_product_ff_series == "") - else: - # if 977 selected an explanation must be provided - validation_holder.append(ct_credit_product_ff_series != "") - - return pd.concat(validation_holder) - - -# helper function to get non blank values -def _get_non_blank_values(values: list[str]): - return filter(lambda v: v.strip() != "", values) - - -# helper function for has_valid_multi_field_value_count: -# process series and return validations -def _get_related_series_validations(value_count: int, series: pd.Series, max_length: int, separator: str = ";") -> dict: - series_validations = {} - for index, value in series.items(): - series_count = len(set(_get_non_blank_values(value.split(separator)))) - series_validations[index] = (series_count + value_count) <= max_length - return series_validations + # check for number type and length must be 8 to match YYYYMMDD + lf = field_data.lazyframe + # polars striptime uses chrono format which allows for non-padded %d, so check + # that a full 8 digits are present in the date. + date_check = ( + pl.col(field_data.key).str.contains(r'^\d{8}$') + & pl.col(field_data.key).str.strptime(pl.Date, "%Y%m%d", strict=False).is_not_null() + ) + rf = lf.with_columns(date_check.alias("check_results")) + return rf.select("check_results") def has_valid_multi_field_value_count( - grouped_data: Dict[str, pd.Series], + field_data: pa.PolarsData, max_length: int, ignored_values: set[str] = set(), + related_fields: str = "", separator: str = ";", -) -> pd.Series: - validation_holder = [] - items = grouped_data.items() - - for value, other_series in items: - processed_value = set(_get_non_blank_values(value.split(separator))) - validation_holder.append( - pd.Series( - index=other_series.index, - name=other_series.name, - data=_get_related_series_validations( - len(processed_value - ignored_values), - other_series, - max_length, - ), - ) - ) - - return pd.concat(validation_holder) - - -def _get_conditional_field_series_validations(series: pd.Series, conditional_func) -> dict: - series_validations = {} - for index, value in series.items(): - series_validations[index] = conditional_func(value) - return series_validations +) -> pl.LazyFrame: + lf = field_data.lazyframe + + # split the related field and check field values, strip off empty spaces and only collect non-empty values + # this uses the polars str and list column type functions + groupby_list = ( + pl.col(related_fields) + .str.split(separator) + .list.eval(pl.element().str.strip_chars()) + .list.eval(pl.element().filter(pl.element() != "")) + ) + check_field_list = ( + pl.col(field_data.key) + .str.split(separator) + .list.eval(pl.element().str.strip_chars()) + .list.eval(pl.element().filter(pl.element() != "")) + ) + + # expressions to get the list count of values not in ignored_values. Polars allows you + # to chain together expressions into a single expression reference + groupby_lengths = groupby_list.list.set_difference(list(ignored_values)).list.len() + field_lengths = check_field_list.list.set_difference(list(ignored_values)).list.len() + + # evaluate the expressions on the lazyframe, putting the results into the alias columnns, and + # collect just the results of the expressions + rf = ( + lf.with_columns([groupby_lengths.alias("groupby_length"), field_lengths.alias("field_length")]) + .select(["groupby_length", "field_length"]) + .collect() + ) + # sum up the total lengths of the related and field values and compare to the max_length + check_results = (rf["groupby_length"] + rf["field_length"]) <= max_length + return pl.DataFrame({"check_results": check_results}).lazy() def has_no_conditional_field_conflict( - grouped_data: Dict[str, pd.Series], + field_data: pa.PolarsData, condition_values: set[str] = {"977"}, + related_fields: str = "", separator: str = ";", -) -> pd.Series: +) -> pl.LazyFrame: """ Validates a column's content based on another column's values. - - if (at least one) other column values is in condition_values list then create + - if (at least one) related_fields column values is in condition_values list then create validation that validate current column value is not empty. - - if other column values are NOT part of condition_values list then current column - values should be empty. + - if related_fields column values are NOT part of condition_values list then current column + should be empty. + """ - Args: - grouped_data (Dict[str, pd.Series]): parsed data/series from source file - condition_values (list[str], optional): list of acceptable values for other - column. Defaults to ["977"]. - separator (str, optional): character used to separate multiple values. - Defaults to ";". + lf = field_data.lazyframe + # expression to split the related field value and alias a boolean if the intersection with the conditional values is empty + check_col = ( + pl.col(related_fields).str.split(separator).list.set_intersection(list(condition_values)).list.len() == 0 + ).alias("check_col") + # expression to check if the check field value is empty + val_col = (pl.col(field_data.key).str.strip_chars().str.len_chars() == 0).alias("val_col") - Returns: - pd.Series: series of current column validations - """ - # will hold individual boolean series to be concatenated at return - validation_holder = [] - for value, other_series in grouped_data.items(): - received_values = set(value.split(separator)) - if received_values.isdisjoint(condition_values): - # disjoint will return TRUE if received values do not contain - # condition values - # free form should be blank if acceptable values NOT existed - # in received list - validation_holder.append( - pd.Series( - index=other_series.index, - name=other_series.name, - data=_get_conditional_field_series_validations(other_series, lambda v: not v.strip()), - ) - ) - else: - # free form text should NOT be blank if acceptable values - # existed in received list - validation_holder.append( - pd.Series( - index=other_series.index, - name=other_series.name, - data=_get_conditional_field_series_validations(other_series, lambda v: v.strip() != ""), - ) - ) + rf = lf.with_columns([check_col, val_col]).select(["check_col", "val_col"]).collect() + rf = rf["check_col"] ^ rf["val_col"] - return pd.concat(validation_holder) + # flip the results of ^ so that the check fails if one expression was True but the other False + return pl.DataFrame({"check_results": ~rf}).lazy() -def is_unique_in_field(ct_value: str, separator: str = ";") -> bool: - values = ct_value.split(separator) - return len(set(values)) == len(values) +def is_unique_in_field( + field_data: pa.PolarsData, + separator: str = ";", +) -> pl.LazyFrame: + lf = field_data.lazyframe + # simply check that the length of a unique list of values equals the original list length. + # Otherwise, there are duplicates + val_list = pl.col(field_data.key).str.split(separator).list + unique_list = val_list.unique().list + unique_check = val_list.len() == unique_list.len() + rf = lf.with_columns(unique_check.alias("check_results")) + return rf.select("check_results") def meets_multi_value_field_restriction(ct_value: str, single_values: set[str], separator: str = ";") -> bool: @@ -243,68 +199,66 @@ def meets_multi_value_field_restriction(ct_value: str, single_values: set[str], def is_valid_enum( - ct_value: str, + field_data: pa.PolarsData, accepted_values: list[str], accept_blank: bool = False, separator: str = ";", ) -> bool: - ct_values_set = set(ct_value.split(separator)) - enum_check = ct_values_set.issubset(accepted_values) - if accept_blank: - return enum_check or not ct_value.strip() - else: - return enum_check + lf = field_data.lazyframe + split_col = pl.col(field_data.key).str.split(separator) + # format_check is two expressions. Either the field data is empty and blanks are allowed, + # or the split field values are all in the accepted values (difference is 0). + format_check = ((pl.col(field_data.key).str.strip_chars() == "") & accept_blank) | ( + split_col.list.set_difference(accepted_values).list.len() == 0 + ) + rf = lf.with_columns(format_check.alias("check_results")) + return rf.select("check_results") -def has_valid_value_count(ct_value: str, min_length: int, max_length: int = None, separator: str = ";") -> bool: - values_count = len(ct_value.split(separator)) - if max_length is None: - return min_length <= values_count - else: - return min_length <= values_count and values_count <= max_length +def has_valid_value_count( + field_data: pa.PolarsData, min_length: int, max_length: int = None, separator: str = ";" +) -> pl.LazyFrame: -def is_date_in_range(date_value: str, start_date_value: str, end_date_value: str) -> bool: - """Checks that the date_value is within the range of the start_date_value - and the end_date_value + # checks that the split list length of the check field is between the min/max length. + lf = field_data.lazyframe + val_list = pl.col(field_data.key).str.split(separator).list.len() + length_check = val_list.is_between(min_length, max_length) + rf = lf.with_columns(length_check.alias("check_results")) + return rf.select("check_results") - Args: - date_value: Date input ideally within the range of the current reporting period - start_date_value: Starting date of reporting period - end_date_value: End date of the reporting period - Returns: Returns True if date_value occurs within the current reporting period - """ - try: - date = datetime.strptime(date_value, "%Y%m%d") - start_date = datetime.strptime(start_date_value, "%Y%m%d") - end_date = datetime.strptime(end_date_value, "%Y%m%d") - return start_date <= date <= end_date - except ValueError: - return False +def is_date_in_range(field_data: pa.PolarsData, start_date_value: str, end_date_value: str) -> bool: + + # checks the date field value is between the start and end date. + lf = field_data.lazyframe + + start_date = datetime.strptime(start_date_value, "%Y%m%d") + end_date = datetime.strptime(end_date_value, "%Y%m%d") + + value_dates = pl.col(field_data.key).str.strptime(pl.Date, "%Y%m%d") + range_check = value_dates.is_between(start_date, end_date) + + return lf.with_columns(range_check.alias("check_results")).select("check_results") def is_date_after( - grouped_data: Dict[str, pd.Series], -) -> pd.Series: - """Checks if date in column is after the date value of another column + field_data: pa.PolarsData, + related_fields: str = "", +) -> pl.Series: - Args: - grouped_data: Data grouped on before_date column + lf = field_data.lazyframe - Returns: Series with corresponding True/False validation values for the column - """ - # will hold individual boolean series to be concatenated at return - validation_holder = [] - for value, other_series in grouped_data.items(): - try: - before_date = datetime.strptime(value, "%Y%m%d") - other_series = pd.to_datetime(other_series) # Convert other series to Date time object + related_dates = pl.col(related_fields).str.strptime(pl.Date, "%Y%m%d") + check_col_dates = pl.col(field_data.key).str.strptime(pl.Date, "%Y%m%d") - validation_holder.append(other_series.apply(lambda date: date >= before_date)) - except ValueError: - validation_holder.append(other_series.apply(lambda v: False)) - return pd.concat(validation_holder) + # evaluates the above strings to dates expressions and puts them into new columns, and then + # collects just those columns for comparison, verifying the related field date is less than + # or equal to the check field date (happens on or before) + rf = lf.with_columns([related_dates.alias("check_date_tmp"), check_col_dates.alias("v_date_tmp")]) + rf = rf.select(["check_date_tmp", "v_date_tmp"]).collect() + check_results = rf['check_date_tmp'] <= rf['v_date_tmp'] + return pl.DataFrame({"check_results": check_results}).lazy() def is_number(ct_value: str, accept_blank: bool = False, is_whole: bool = False) -> bool: @@ -318,7 +272,6 @@ def is_number(ct_value: str, accept_blank: bool = False, is_whole: bool = False) Returns: bool: True if value is number , False if value is not number """ - value_check = True num_parser = int if is_whole else float try: @@ -329,57 +282,15 @@ def is_number(ct_value: str, accept_blank: bool = False, is_whole: bool = False) return _check_blank_(ct_value, value_check, accept_blank) -def _has_valid_enum_pair_validation_helper( - condition=True, - series: pd.Series = None, - condition_value=None, -) -> pd.Series: - result = pd.Series(index=series.index, name=series.name, data=True) - if condition: - result = series.str.split(';').apply(lambda x: condition_value in x) - else: - result = series.str.split(';').apply(lambda x: condition_value not in x) - return result - - -def _has_valid_enum_pair_helper( - conditions: list[list] = None, - received_values: set[str] = None, - other_series: pd.Series = None, -) -> pd.Series: - for condition in conditions: - if ( - condition["condition_values"] is not None - and condition["is_equal_condition"] - and received_values.issubset(condition["condition_values"]) - ): - return _has_valid_enum_pair_validation_helper( - condition["should_equal_target"], - other_series, - condition["target_value"], - ) - elif ( - condition["condition_values"] is not None - and not condition["is_equal_condition"] - and received_values.isdisjoint(condition["condition_values"]) - ): - return _has_valid_enum_pair_validation_helper( - condition["should_equal_target"], - other_series, - condition["target_value"], - ) - - return pd.Series(index=other_series.index, name=other_series.name, data=True) - - def has_valid_enum_pair( - grouped_data: Dict[str, pd.Series], + field_data: pa.PolarsData, conditions: list[list] = None, + related_fields: str = "", separator: str = ";", -) -> pd.Series: +) -> pl.Series: """Validates a column's enum value based on another column's enum values. Args: - grouped_data (Dict[str, pd.Series]): parsed data/series from source file + field_data (Dict[str, pl.Series]): parsed data/series from source file conditions: list of list of key-value pairs conditions should be passed in the following format: Example: @@ -403,36 +314,73 @@ def has_valid_enum_pair( Returns: Series with corresponding True/False validation values for the column """ + lf = field_data.lazyframe - # will hold individual boolean series to be concatenated at return - validation_holder = [] - for value, other_series in grouped_data.items(): - received_values = set(value.split(separator)) - validation_holder.append(_has_valid_enum_pair_helper(conditions, received_values, other_series)) - return pd.concat(validation_holder) + # expressions to strip off white space of the related value to check, and + # split the check field value + related_field_value = pl.col(related_fields).str.strip_chars() + field_values = pl.col(field_data.key).str.split(separator) + # start with a True series, which will then be anded to for each condition + check_results = pl.lit(True) + for condition in conditions: + check = check_condition(condition, field_values, related_field_value) + # and all conditions, any failure fails the check + check_results = check_results & check + rf = lf.with_columns(check_results.alias("check_results")) + return rf.select("check_results") -def is_date_before_in_days(grouped_data: Dict[str, pd.Series], days_value: int = 730) -> pd.Series: - """Checks if the provided date is not beyond - the grouped column date plus the days_value parameter - Args: - grouped_data: Data grouped on the initial date column - days_value: This value is added to our grouped data to find our - unreasonable_date value - Returns: Series with corresponding True/False validation values for the column - """ - # will hold individual boolean series to be concatenated at return - validation_holder = [] - for value, other_series in grouped_data.items(): - try: - initial_date = datetime.strptime(value, "%Y%m%d") - unreasonable_date = initial_date + timedelta(days=days_value) - other_series = pd.to_datetime(other_series) # Convert other series to Date time object - validation_holder.append(other_series.apply(lambda date: date < unreasonable_date)) - except ValueError: - validation_holder.append(other_series.apply(lambda v: False)) - return pd.concat(validation_holder) +def check_condition(condition, field_values, related_field_value): + + # returns an expression to check if the field value list does or + # does not contain the target value + def field_check(field_values, target_value, should_equal_target): + if should_equal_target: + return field_values.list.contains(target_value) + else: + return ~field_values.list.contains(target_value) + + con_values = condition["condition_values"] + target_value = condition["target_value"] + should_equal_target = condition["should_equal_target"] + + # first build an expression based on if we're looking for + # the related field being in a list of conditional values + if condition["is_equal_condition"]: + check = related_field_value.is_in(list(con_values)) + else: + check = ~related_field_value.is_in(list(con_values)) + # next build an expression to check if the field values do or do not contain the target value + field_check_exp = field_check(field_values, target_value, should_equal_target) + + # uses polars when/then/otherwise notation, which is basically an if/else + # here the expression is doing + # if the is_equal_condition check is met: + # return target value field check result + # else: + # return True (the conditional check wasn't meant, so we're good) + return pl.when(check).then(field_check_exp).otherwise(True) + + +def is_date_before_in_days(field_data: pa.PolarsData, days_value: int = 730, related_fields: str = "") -> pl.Series: + + lf = field_data.lazyframe + # builds a dataframe by converting the check and related fields from strings to dates and putting them + # into columns + rf = lf.with_columns( + [ + pl.col(related_fields).str.strptime(pl.Date, "%Y%m%d").alias("check_date_tmp"), + pl.col(field_data.key).str.strptime(pl.Date, "%Y%m%d").alias("v_date_tmp"), + ] + ) + rf = rf.select(["v_date_tmp", "check_date_tmp"]).collect() + + # diff the two columns as date objects and verify the diff in days is less than the max days + diff_values = (rf['v_date_tmp'] - rf['check_date_tmp']).dt.total_days() + check_results = diff_values < days_value + rf = pl.DataFrame({"check_results": check_results}).lazy() + return rf def has_correct_length(ct_value: str, accepted_length: int, accept_blank: bool = False) -> bool: @@ -477,104 +425,32 @@ def is_less_than(value: str, max_value: str, accept_blank: bool = False) -> bool return comparison_helper(value, max_value, accept_blank, operator.lt) -def has_valid_format(value: str, regex: str, accept_blank: bool = False) -> bool: - return _check_blank_(value, bool(re.match(regex, value)), accept_blank) - - -def _is_unique_column_helper(series: pd.Series, count_limit: int): - """ - helper function for is_unique_column - - Args: - series (pd.Series): series related to a row - - Returns: - all rows validations - """ - series_validations = {} - check_result = series.count() <= count_limit - for current_index, _ in series.items(): - series_validations[current_index] = check_result - return series_validations - - -def is_unique_column(grouped_data: Dict[any, pd.Series], count_limit: int = 1) -> pd.Series: - """ - verify if the content of a column is unique. - - To be used with element_wise set to false - - To be used with group_by set to itself column - - Return validations for each row +def has_valid_format(field_data: pa.PolarsData, regex: str, accept_blank: bool = False) -> bool: + lf = field_data.lazyframe - Args: - grouped_data (Dict[any, pd.Series]): rows data + # here format_check is two expressions, one that checks if the field value is empty and blanks are allowed, + # or the field value meets the passed in regex + format_check = ((pl.col(field_data.key).str.strip_chars() == "") & accept_blank) | pl.col( + field_data.key + ).str.contains(regex) + rf = lf.with_columns(format_check.alias("check_results")) + return rf.select("check_results") - Returns: - pd.Series: all rows validations - """ - validation_holder = [] - for _, main_series in grouped_data.items(): - validation_holder.append( - pd.Series( - index=main_series.index, - name=main_series.name, - data=_is_unique_column_helper(main_series, count_limit), - ) - ) - return pd.concat(validation_holder) - - -def _get_has_valid_fieldset_pair_eq_neq_validation_value( - current_values: list[str], - should_fieldset_key_equal_to: dict({str: (int, bool, str)}) = None, -) -> bool: - # for field_name, (index, equal_to, target_value) in should_fieldset_key_equal_to: - for index, should_equal_to, target_value in should_fieldset_key_equal_to.values(): - if should_equal_to: - # if received value != target value, then returns False (Warning) - if current_values[index] != target_value: - return False - else: - # if received value equal target value, then returns False (Warning) - if current_values[index] == target_value: - return False - # By default returns True (No Warning and fieldset pair is VALID) - return True +def is_unique_column(field_data: pa.PolarsData, related_fields: str = "", count_limit: int = 1) -> pl.Series: -def _has_valid_fieldset_pair_helper( - current_values: list[str], - series: pd.Series, - condition_values: list[str], - should_fieldset_key_equal_to: dict({str: (int, bool, str)}) = None, -): - series_validations = {} - for current_index, current_value in series.items(): - """Getting the validation result for comparing current_values to the - should_fieldset_key_equal_to (target values)""" - has_valid_fieldset_pair_eq_neq_validation_value = _get_has_valid_fieldset_pair_eq_neq_validation_value( - current_values, should_fieldset_key_equal_to - ) - """ - If current_value is in condition_values AND - has_valid_fieldset_pair_eq_neq_validation_value is True, - then fieldset pair is valid (True). - - If current_value is in condition_values AND - has_valid_fieldset_pair_eq_neq_validation_value is False, - then fieldset pair is NOT valid (False). - """ - validation = ( - current_value in condition_values and has_valid_fieldset_pair_eq_neq_validation_value - ) or current_value not in condition_values - series_validations[current_index] = validation - return series_validations + # uses polars column is_unique() function to check there are no duplicate values + rf = field_data.lazyframe.select(field_data.key).collect().is_unique() + rf = pl.DataFrame({"check_results": rf}).lazy() + return rf def has_valid_fieldset_pair( - grouped_data: Dict[any, pd.Series], + field_data: pa.PolarsData, condition_values: list[str], + related_fields: list[str], should_fieldset_key_equal_to: dict({str: (int, bool, str)}) = None, -) -> pd.Series: +) -> pl.Series: """conditional check to verify if groups of fields equal to specific values (equal_to_values) when another field is set/equal to condition_values. @@ -584,7 +460,7 @@ def has_valid_fieldset_pair( and the column data in the series Args: - grouped_data (Dict[list[str], pd.Series]): parsed data provided by pandera + field_data (Dict[list[str], pl.Series]): parsed data provided by pandera condition_values (list[str]): list of value to be compared to main series should_fieldset_key_equal_to Dict{str, (int, bool, str)}: dict of field name and tuple, where the first value is the index of field in the groupby and it @@ -632,26 +508,41 @@ def has_valid_fieldset_pair( "po_4_gender_flag": (11, True, ""), }, Returns: - pd.Series: list of series with update validations + pl.Series: list of series with update validations """ - validation_holder = [] - for values, main_series in grouped_data.items(): - validation_holder.append( - pd.Series( - index=main_series.index, - name=main_series.name, - data=( - _has_valid_fieldset_pair_helper( - values, - main_series, - condition_values, - should_fieldset_key_equal_to, - ) - ), - dtype=bool, - ) - ) - return pd.concat(validation_holder) + lf = field_data.lazyframe + + def check_fieldset_expression(field, condition): + idx, must_equal, target_value = condition + operator_check = pl.col(field) == target_value if must_equal else pl.col(field) != target_value + return operator_check + + # this builds a list of def calls to be anded together by the below expression, for each key/tuple defined + # in the check should_fieldset_key_equal_to + conditions = [ + check_fieldset_expression(field, condition) for field, condition in should_fieldset_key_equal_to.items() + ] + combinded_conditions = conditions[0] + for cond in conditions[1:]: + combinded_conditions &= cond + + # polars has an expression chain called when/then/otherwise which equates to the concept of if/else. + # Here, this chain of expressions is doing: + # if field data is not in condition_values: (don't bother checking the conditionals) + # return True + # elif evaluate the anded check_fieldset_expressions: (all conditional checks have to result in True) + # return True + # else: + # return False (field data was a condition value and one or more of the conditional checks failed) + conditions_check = ( + pl.when(~pl.col(field_data.key).is_in(condition_values)) + .then(pl.lit(True)) + .when(combinded_conditions) + .then(pl.lit(True)) + .otherwise(pl.lit(False)) + ) + rf = lf.with_columns(conditions_check.alias("check_results")) + return rf.select("check_results") def string_contains( @@ -665,7 +556,7 @@ def string_contains( Args: value (str): parsed value - containing_value (str): tcontaining value to which value is compared to + containing_value (str): ßcontaining value to which value is compared to start_idx (int): the start index if the value needs to sliced end_idx (int): the end index if the value needs to sliced Returns: diff --git a/src/regtech_data_validator/checks.py b/src/regtech_data_validator/checks.py index d2e80c3f..3518f12a 100644 --- a/src/regtech_data_validator/checks.py +++ b/src/regtech_data_validator/checks.py @@ -7,7 +7,7 @@ from pandera import Check from pandera.backends.base import BaseCheckBackend -from pandera.backends.pandas.checks import PandasCheckBackend +from pandera.backends.polars.checks import PolarsCheckBackend class Severity(StrEnum): @@ -56,4 +56,4 @@ def __init__( @classmethod def get_backend(cls, check_obj: Any) -> Type[BaseCheckBackend]: """Assume Pandas DataFrame and return PandasCheckBackend""" - return PandasCheckBackend + return PolarsCheckBackend diff --git a/src/regtech_data_validator/cli.py b/src/regtech_data_validator/cli.py index e1445ade..1ec6513c 100644 --- a/src/regtech_data_validator/cli.py +++ b/src/regtech_data_validator/cli.py @@ -4,11 +4,12 @@ from regtech_data_validator.data_formatters import df_to_csv, df_to_str, df_to_json, df_to_table, df_to_download from typing import Annotated, Optional -import pandas as pd +import polars as pl import typer import typer.core -from regtech_data_validator.create_schemas import validate_phases +from regtech_data_validator.validator import validate_batch_csv +from regtech_data_validator.validation_results import ValidationPhase # Need to do this because the latest version of typer, if the rich package exists # will create a Panel with borders in the error output. This causes stderr during @@ -37,11 +38,11 @@ def parse_key_value(kv_str: str) -> KeyValueOpt: class OutputFormat(StrEnum): - CSV = 'csv' JSON = 'json' - PANDAS = 'pandas' + POLARS = 'polars' TABLE = 'table' DOWNLOAD = 'download' + CSV = 'csv' @app.command() @@ -76,50 +77,47 @@ def validate( ), ] = None, output: Annotated[Optional[OutputFormat], typer.Option()] = OutputFormat.TABLE, -) -> tuple[bool, pd.DataFrame]: +) -> tuple[bool, pl.DataFrame]: """ Validate CFPB data submission """ context_dict = {x.key: x.value for x in context} if context else {} - input_df = None - try: - input_df = pd.read_csv(path, dtype=str, na_filter=False) - except Exception as e: - raise RuntimeError(e) - validation_results = validate_phases(input_df, context_dict) - - status = 'SUCCESS' - no_of_findings = 0 - total_errors = 0 - findings_df = pd.DataFrame() - if not validation_results.is_valid: - status = 'FAILURE' - findings_df = validation_results.findings - no_of_findings = len(findings_df.index.unique()) - warning_count = validation_results.warning_counts.total_count - error_count = validation_results.error_counts.total_count - - match output: - case OutputFormat.PANDAS: - print(df_to_str(findings_df)) - case OutputFormat.CSV: - print(df_to_csv(findings_df)) - case OutputFormat.JSON: - print(df_to_json(findings_df)) - case OutputFormat.TABLE: - print(df_to_table(findings_df)) - case OutputFormat.DOWNLOAD: - print(df_to_download(findings_df, warning_count, error_count)) - case _: - raise ValueError(f'output format "{output}" not supported') + + total_findings = 0 + final_phase = ValidationPhase.LOGICAL + all_findings = [] + final_df = pl.DataFrame() + # path = "s3://cfpb-devpub-regtech-sbl-filing-main/upload/2024/1234364890REGTECH006/156.csv" + for validation_results in validate_batch_csv(path, context_dict, batch_size=50000, batch_count=1): + total_findings += validation_results.error_counts.total_count + validation_results.warning_counts.total_count + final_phase = validation_results.phase + all_findings.append(validation_results) + + if all_findings: + final_df = pl.concat([v.findings for v in all_findings], how="diagonal") + + status = "SUCCESS" if total_findings == 0 else "FAILURE" + + match output: + case OutputFormat.CSV: + print(df_to_csv(final_df)) + case OutputFormat.POLARS: + print(df_to_str(final_df)) + case OutputFormat.JSON: + print(df_to_json(final_df, max_group_size=200)) + case OutputFormat.TABLE: + print(df_to_table(final_df)) + case OutputFormat.DOWNLOAD: + df_to_download(final_df) + case _: + raise ValueError(f'output format "{output}" not supported') typer.echo( - f"status: {status}, total errors: {total_errors}, findings: {no_of_findings}, validation phase: {validation_results.phase}", + f"Status: {status}, Total Errors: {total_findings}, Validation Phase: {final_phase}", err=True, ) - # returned values are only used in unit tests - return validation_results.is_valid, findings_df + return (status, final_df) if __name__ == '__main__': diff --git a/src/regtech_data_validator/create_schemas.py b/src/regtech_data_validator/create_schemas.py deleted file mode 100644 index 44425d10..00000000 --- a/src/regtech_data_validator/create_schemas.py +++ /dev/null @@ -1,255 +0,0 @@ -"""Creates two DataFrameSchema objects by rendering the schema template -with validations listed in phase 1 and phase 2.""" - -import math -import pandas as pd -from pandera import Check, DataFrameSchema -from pandera.errors import SchemaErrors, SchemaError, SchemaErrorReason - -from regtech_data_validator.checks import SBLCheck, Severity -from regtech_data_validator.phase_validations import get_phase_1_and_2_validations_for_lei -from regtech_data_validator.schema_template import get_template -from regtech_data_validator.validation_results import ValidationPhase, ValidationResults, Counts - -# Get separate schema templates for phase 1 and 2 -phase_1_template = get_template() -phase_2_template = get_template() - - -def get_schema_by_phase_for_lei(template: dict, phase: str, context: dict[str, str] | None = None): - for column in get_phase_1_and_2_validations_for_lei(context): - validations = get_phase_1_and_2_validations_for_lei(context)[column] - template[column].checks = validations[phase] - - return DataFrameSchema(template, name=phase) - - -def get_phase_1_schema_for_lei(context: dict[str, str] | None = None): - return get_schema_by_phase_for_lei(phase_1_template, ValidationPhase.SYNTACTICAL, context) - - -def get_phase_2_schema_for_lei(context: dict[str, str] | None = None): - return get_schema_by_phase_for_lei(phase_2_template, ValidationPhase.LOGICAL, context) - - -def _get_check_fields(check: Check, primary_column: str) -> list[str]: - """ - Don't sort field list to maintain original ordering. Use List as - python Set does not guarantee order. - """ - - field_list = [primary_column] - - if check.groupby: - field_list += check.groupby - # remove possible dupes but maintain order - field_list = list(dict.fromkeys(field_list)) - return field_list - - -def _filter_valid_records(df: pd.DataFrame, check_output: pd.Series, fields: list[str]) -> pd.DataFrame: - """ - Return only records and fields associated with a given `Check`'s - """ - - # `check_output` must be sorted so its index lines up with `df`'s index - sorted_check_output: pd.Series = check_output.sort_index() - - # Filter records using Pandas's boolean indexing, where all False values get filtered out. - # The `~` does the inverse since it's actually the False values we want to keep. - # http://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#boolean-indexing - # We then up the index by 1 so that record_no is indexed starting with 1 instead of 0 - sorted_fields = df.loc[sorted_check_output[~sorted_check_output].index][fields] - index = [i + 1 for i in sorted_fields.index] - sorted_fields.index = index - failed_records_df = sorted_fields.reset_index(names='record_no') - - failed_records_df.index.rename('finding_no', inplace=True) - - return failed_records_df - - -def _records_to_fields(failed_records_df: pd.DataFrame) -> pd.DataFrame: - """ - Transforms a DataFrame with columns per Check field to DataFrame with a row per field - """ - - # Melts a DataFrame with the line number as the index columns for the validations's fields' values - # into one with the validation_id, record_no, and field_name as a multiindex, and all of the validation - # metadata merged in as well. - failed_record_fields_df = failed_records_df.melt( - var_name='field_name', value_name='field_value', id_vars='record_no', ignore_index=False - ) - - return failed_record_fields_df - - -def _add_validation_metadata(failed_check_fields_df: pd.DataFrame, check: SBLCheck): - validation_fields_df = failed_check_fields_df.assign(validation_id=check.title) - return validation_fields_df - - -def validate(schema: DataFrameSchema, submission_df: pd.DataFrame, max_errors: int = 1000000) -> ValidationResults: - """ - validate received dataframe with schema and return list of - schema errors - Args: - schema (DataFrameSchema): schema to be used for validation - submission_df (pd.DataFrame): data to be validated against the schema - Returns: - bool whether the given submission was valid or not - pd.DataFrame containing validation results data - """ - is_valid = True - findings_df: pd.DataFrame = pd.DataFrame() - next_finding_no: int = 1 - error_counts = warning_counts = Counts() - - try: - schema(submission_df, lazy=True) - except SchemaErrors as err: - is_valid = False - check_findings = [] - # NOTE: `type: ignore` because SchemaErrors.schema_errors is supposed to be - # `list[dict[str,Any]]`, but it's actually of type `SchemaError` - schema_error: SchemaError - error_counts, warning_counts = get_scope_counts(err.schema_errors) - total_error_count = sum([error_counts.total_count, warning_counts.total_count]) - if total_error_count > max_errors: - err.schema_errors = trim_down_errors(err.schema_errors, max_errors) - for schema_error in err.schema_errors: # type: ignore - check = schema_error.check - column_name = schema_error.schema.name - - if schema_error.reason_code == SchemaErrorReason.COLUMN_NOT_IN_DATAFRAME: - raise RuntimeError(schema_error) from schema_error - if not check: - raise RuntimeError( - f'SchemaError occurred with no associated Check for {column_name} column' - ) from schema_error - - if not isinstance(check, SBLCheck): - raise RuntimeError( - f'Check {check} type on {column_name} column not supported. Must be of type {SBLCheck}' - ) from schema_error - fields = _get_check_fields(check, column_name) - check_output: pd.Series | None = schema_error.check_output - - if check_output is not None: - # Filter data not associated with failed Check, and update index for merging with findings_df - failed_records_df = _filter_valid_records(submission_df, check_output, fields) - failed_records_df.index += next_finding_no - next_finding_no = failed_records_df.tail(1).index + 1 # type: ignore - failed_record_fields_df = _records_to_fields(failed_records_df) - check_findings.append(_add_validation_metadata(failed_record_fields_df, check)) - else: - # The above exception handling _should_ prevent this from ever happenin, but...just in case. - raise RuntimeError(f'No check output for "{check.name}" check. Pandera SchemaError: {schema_error}') - findings_df = pd.concat(check_findings) - - updated_df = add_uid(findings_df, submission_df) - results = ValidationResults( - error_counts=error_counts, - warning_counts=warning_counts, - is_valid=is_valid, - findings=updated_df, - phase=schema.name, - ) - return results - - -def add_uid(results_df: pd.DataFrame, submission_df: pd.DataFrame) -> pd.DataFrame: - if results_df.empty: - return results_df - - # uses pandas column operation to get list of record_no - 1 values, which would be indexes in the submission, since - # record_no is index offset by 1, and the uid column values for that into a new series that is then - # assigned to the results uid column. Much simpler and faster than looping over and assigning row by row. - - results_df['uid'] = submission_df.loc[results_df['record_no'] - 1, 'uid'].values - return results_df - - -def validate_phases( - df: pd.DataFrame, context: dict[str, str] | None = None, max_errors: int = 1000000 -) -> ValidationResults: - results = validate(get_phase_1_schema_for_lei(context), df, max_errors) - - if not results.is_valid: - return results - - return validate(get_phase_2_schema_for_lei(context), df, max_errors) - - -def get_scope_counts(schema_errors: list[SchemaError]): - singles = [ - error for error in schema_errors if isinstance(error.check, SBLCheck) and error.check.scope == 'single-field' - ] - single_errors = int( - sum([(~error.check_output).sum() for error in singles if error.check.severity == Severity.ERROR]) - ) - single_warnings = int( - sum([(~error.check_output).sum() for error in singles if error.check.severity == Severity.WARNING]) - ) - multi = [ - error for error in schema_errors if isinstance(error.check, SBLCheck) and error.check.scope == 'multi-field' - ] - multi_errors = int(sum([(~error.check_output).sum() for error in multi if error.check.severity == Severity.ERROR])) - multi_warnings = int( - sum([(~error.check_output).sum() for error in multi if error.check.severity == Severity.WARNING]) - ) - register_errors = int( - sum( - [ - (~error.check_output).sum() - for error in schema_errors - if isinstance(error.check, SBLCheck) and error.check.scope == 'register' - ] - ) - ) - - return Counts( - single_field_count=single_errors, - multi_field_count=multi_errors, - register_count=register_errors, - total_count=sum([single_errors, multi_errors, register_errors]), - ), Counts( - single_field_count=single_warnings, - multi_field_count=multi_warnings, - total_count=sum([single_warnings, multi_warnings]), # There are no register-level warnings at this time - ) - - -def trim_down_errors(schema_errors: list[SchemaError], max_errors: int): - error_counts = [sum(~error.check_output) for error in schema_errors] - total_error_count = sum(error_counts) - - # Take the list of counts per error to determine a ratio for each, - # relative to the total number of errors, and use that ratio in - # relation to the max error count to determine a total count for that error - # use a max so that we always have at least 1 (for really small ratios) - error_ratios = [(count / total_error_count) for count in error_counts] - new_counts = [math.ceil(max_errors * prop) for prop in error_ratios] - - # Adjust the counts in case we went over max. This is very likely since we're using - # ceil, unless we have an exact equality of the new counts. Because of the use - # of ceil, we will never have the sum of the new counts be less than max. - if sum(new_counts) > max_errors: - while sum(new_counts) > max_errors: - # arbitrary reversal to contain errors in FIG order, if we need to remove - # errors to fit max - for i in reversed(range(len(new_counts))): - if new_counts[i] > 1: - new_counts[i] -= 1 - # check if all the counts are equal to 1, then - # start removing those until we hit max - elif new_counts[i] == 1 and sum(new_counts) <= len(new_counts): - new_counts[i] -= 1 - if sum(new_counts) == max_errors: - break - - for error, new_count in zip(schema_errors, new_counts): - error_indices = error.check_output[~error.check_output].index - keep_indices = error_indices[:new_count] - error.check_output = error.check_output.loc[keep_indices] - return schema_errors diff --git a/src/regtech_data_validator/data_formatters.py b/src/regtech_data_validator/data_formatters.py index 4a4417e7..62bd7904 100644 --- a/src/regtech_data_validator/data_formatters.py +++ b/src/regtech_data_validator/data_formatters.py @@ -1,21 +1,21 @@ -import csv -import math +import boto3 import ujson -import pandas as pd +import polars as pl +import fsspec from tabulate import tabulate -from regtech_data_validator.phase_validations import get_phase_1_and_2_validations_for_lei -from regtech_data_validator.checks import SBLCheck +from functools import partial +from io import BytesIO -def get_all_checks(): - return [ - check - for phases in get_phase_1_and_2_validations_for_lei().values() - for checks in phases.values() - for check in checks - ] +from regtech_data_validator.checks import SBLCheck +from regtech_data_validator.validation_results import ValidationPhase +from regtech_data_validator.phase_validations import ( + get_phase_1_schema_for_lei, + get_phase_2_schema_for_lei, + get_register_schema, +) def find_check(group_name, checks): @@ -23,240 +23,291 @@ def find_check(group_name, checks): return next(gen) -def df_to_download(df: pd.DataFrame, warning_count: int = 0, error_count: int = 0, max_errors: int = 1000000) -> str: - header = "validation_type,validation_id,validation_name,row,unique_identifier,fig_link,validation_description," - if df.empty: - # return headers of csv for 'emtpy' report - return header +def get_checks(phase): + if phase == ValidationPhase.SYNTACTICAL: + syntax_schema = get_phase_1_schema_for_lei() + checks = [check for col_schema in syntax_schema.columns.values() for check in col_schema.checks] else: - total_csv = "" - largest_field_count = 1 - checks = get_all_checks() - df.reset_index(drop=True, inplace=True) - - for validation_id, group in df.groupby("validation_id"): - group['field_number'] = ( - group.groupby( - [ - "record_no", - "uid", - ] - ).cumcount() - + 1 + logic_schema = get_phase_2_schema_for_lei() + checks = [check for col_schema in logic_schema.columns.values() for check in col_schema.checks] + register_schema = get_register_schema() + checks.extend([check for col_schema in register_schema.columns.values() for check in col_schema.checks]) + return checks + + +# Takes the error dataframe, which is a bit obscure, and translates it to a format of: +# validation_type, validation_id, validation_name, row, unique_identifier, fig_link, validation_description, scope, field_#, value_# +# which corresponds to severity, error/warning code, name of error/warning, row number in sblar, UID, fig link, +# error/warning description (markdown formatted), single/multi/register, and the fields and values associated with the error/warning. +# Each row in the final dataframe represents all data for that one finding. +def format_findings(df: pl.DataFrame, phase, checks): + final_df = pl.DataFrame() + + sorted_df = df.with_columns(pl.col('validation_id').cast(pl.Categorical(ordering='lexical'))).sort('validation_id') + + # validation_id is a tuple returned from the group_by, so we'll be getting [0] for the actual error/warning code + for validation_id, group in sorted_df.group_by(["validation_id"], maintain_order=True): + validation_id = validation_id[0] + # in the error dataframe, each field is its own row, so count those and put them into field_name_field_number_# + # and field_value_field_number_# columns to break out eventually to related field_# and value_# + group = group.with_columns(pl.col('record_no').cum_count().over(['record_no', 'uid']).alias('field_number')) + df_pivot = group.pivot( + index=[ + "record_no", + "uid", + ], + columns="field_number", + values=["field_name", "field_value"], + aggregate_function="first", + ) + df_pivot.columns = [ + ( + col.replace('field_name_', 'field_').replace('field_value_', 'value_') + if ('field_name_' in col or 'field_value_' in col) + else col ) - df_pivot = group.pivot_table( - index=[ - "record_no", - "uid", - ], - columns="field_number", - values=["field_name", "field_value"], - aggfunc="first", - ).reset_index() - - df_pivot.columns = [f"{col[0]}_{col[1]}" if col[1] else col[0] for col in df_pivot.columns] - - check = find_check(validation_id, checks) - df_pivot['validation_type'] = check.severity - df_pivot['validation_description'] = check.description - df_pivot['validation_name'] = check.name - df_pivot['fig_link'] = check.fig_link - - df_pivot.rename( - columns={ - "record_no": "row", - "uid": "unique_identifier", - }, - inplace=True, + for col in df_pivot.columns + ] + + check = find_check(validation_id, checks) + # convert the SBLCheck data into column data + df_pivot = df_pivot.with_columns( + validation_type=pl.lit(check.severity.value), + validation_id=pl.lit(validation_id), + scope=pl.lit(check.scope), + # validation_description=pl.lit(check.description), + # validation_name=pl.lit(check.name), + # fig_link=pl.lit(check.fig_link), + ).rename( + { + "record_no": "row", + "uid": "unique_identifier", + } + ) + # match field_1 with value_1, field_2 with value_2, etc + field_columns = [col for col in df_pivot.columns if col.startswith('field_')] + value_columns = [col for col in df_pivot.columns if col.startswith('value_')] + sorted_columns = [col for pair in zip(field_columns, value_columns) for col in pair] + + # swap two-field errors/warnings to keep order of FIG + if len(field_columns) == 2: + df_pivot = df_pivot.with_columns( + field_1=pl.col('field_2'), + value_1=pl.col('value_2'), + field_2=pl.col('field_1'), + value_2=pl.col('value_1'), ) - field_columns = [col for col in df_pivot.columns if col.startswith('field_name_')] - value_columns = [col for col in df_pivot.columns if col.startswith('field_value_')] - sorted_columns = [col for pair in zip(field_columns, value_columns) for col in pair] - group_field_count = len(field_columns) - largest_field_count = largest_field_count if group_field_count <= largest_field_count else group_field_count - # swap two-field errors/warnings to keep order of FIG - if len(field_columns) == 2: - f1_data = df_pivot['field_name_1'] - v1_data = df_pivot['field_value_1'] - df_pivot['field_name_1'] = df_pivot['field_name_2'] - df_pivot['field_value_1'] = df_pivot['field_value_2'] - df_pivot['field_name_2'] = f1_data - df_pivot['field_value_2'] = v1_data - df_pivot['validation_id'] = validation_id - - df_pivot = df_pivot[ - [ - "validation_type", - "validation_id", - "validation_name", - "row", - "unique_identifier", - "fig_link", - "validation_description", - ] - + sorted_columns + + # adjust row by 1 and concat group with final dataframe + df_pivot = df_pivot.with_columns(row=pl.col('row') + 1).select( + [ + "validation_type", + "validation_id", + "row", + "unique_identifier", + "scope", ] + + sorted_columns + ) + final_df = pl.concat([final_df, df_pivot], how="diagonal") + final_df = final_df.with_columns(phase=pl.lit(phase)) + return final_df + + +def df_to_download( + df: pl.DataFrame, + path: str = "download_report.csv", + warning_count: int = 0, + error_count: int = 0, + max_errors: int = 1000000, +): + if df.is_empty(): + # return headers of csv for 'emtpy' report + empty_df = pl.DataFrame( + { + "validation_type": [], + "validation_id": [], + "validation_name": [], + "row": [], + "unique_identifier": [], + "fig_link": [], + "validation_description": [], + } + ) + with fsspec.open(path, mode='wb') as f: + empty_df.write_csv(f, quote_style='non_numeric') + return - df_pivot['row'] += 1 - total_csv += df_pivot.to_csv(index=False, quoting=csv.QUOTE_NONNUMERIC, header=False) + # get the check for the phase the results were in, so we can pull out static data from each + # found check + checks = get_checks(df.select(pl.first("phase")).item()) + + # place the static data into a dataframe, and then join the results frame with it where the validation ids are the same. + # This is much faster than applying the fields + check_values = [ + { + "validation_id": check.title, + "validation_description": check.description, + "validation_name": check.name, + "fig_link": check.fig_link, + } + for check in checks + ] + checks_df = pl.DataFrame(check_values) + joined_df = df.join(checks_df, on="validation_id") + + # Sort by validation id, order the field and value columns so they end up like field_1, value_1, field_2, value_2,... + # and organize the columns as desired for the csv + joined_df = joined_df.with_columns(pl.col('validation_id').cast(pl.Categorical(ordering='lexical'))).sort( + 'validation_id' + ) + + field_columns = [col for col in joined_df.columns if col.startswith('field_')] + value_columns = [col for col in joined_df.columns if col.startswith('value_')] + sorted_columns = [col for pair in zip(field_columns, value_columns) for col in pair] + + sorted_df = joined_df[ + [ + "validation_type", + "validation_id", + "validation_name", + "row", + "unique_identifier", + "fig_link", + "validation_description", + ] + + sorted_columns + ] - field_headers = [] - for i in range(largest_field_count): - field_headers.append(f"field_{i+1}") - field_headers.append(f"value_{i+1}") - header += ",".join(field_headers) + "\n" + buffer = BytesIO() + headers = ','.join(sorted_df.columns) + '\n' + buffer.write(headers.encode()) - total_errors = warning_count + error_count - error_type = "errors" - if warning_count > 0: - if error_count > 0: - error_type = "errors and warnings" - else: - error_type = "warnings" + total_errors = warning_count + error_count + error_type = "errors" + if warning_count > 0: + if error_count > 0: + error_type = "errors and warnings" + else: + error_type = "warnings" - if total_errors and total_errors > max_errors: - header += f'"Your register contains {total_errors} {error_type}, however, only {max_errors} records are displayed in this report. To see additional {error_type}, correct the listed records, and upload a new file."\n' + if total_errors and total_errors > max_errors: + buffer.write( + f'"Your register contains {total_errors} {error_type}, however, only {max_errors} records are displayed in this report. To see additional {error_type}, correct the listed records, and upload a new file."\n'.encode() + ) - csv_data = header + total_csv - return csv_data + if path.startswith("s3"): + sorted_df.write_csv(buffer, quote_style='non_numeric', include_header=False) + buffer.seek(0) + upload(path, buffer.getvalue()) + else: + with fsspec.open(path, mode='wb') as f: + sorted_df.write_csv(buffer, quote_style='non_numeric', include_header=False) + buffer.seek(0) + f.write(buffer.getvalue()) -def df_to_str(df: pd.DataFrame) -> str: - with pd.option_context('display.width', None, 'display.max_rows', None): - return str(df) +def upload(path: str, content: bytes) -> None: + bucket = path.split("s3://")[1].split("/")[0] + opath = path.split("s3://")[1].replace(bucket + "/", "") + s3 = boto3.client("s3") + s3.put_object( + Bucket=bucket, + Key=opath, + Body=content, + ) -def df_to_csv(df: pd.DataFrame) -> str: - return df.to_csv() +def df_to_csv(df: pl.DataFrame) -> str: + sorted_df = df.with_columns(pl.col('validation_id').cast(pl.Categorical(ordering='lexical'))).sort('validation_id') + return sorted_df.write_csv(quote_style='non_numeric') -def df_to_table(df: pd.DataFrame) -> str: - # trim field_value field to just 50 chars, similar to DataFrame default - table_df = df.sort_index() - table_df['field_value'] = table_df['field_value'].str[0:50] +def df_to_str(df: pl.DataFrame) -> str: + with pl.Config(tbl_width_chars=0, tbl_rows=-1, tbl_cols=-1): + return str(df) - # NOTE: `type: ignore` because tabulate package typing does not include Pandas - # DataFrame as input, but the library itself does support it. ¯\_(ツ)_/¯ - return tabulate(table_df, headers='keys', showindex=True, tablefmt='rounded_outline') # type: ignore +def df_to_table(df: pl.DataFrame) -> str: + df = df.select([df[col].str.slice(0, 50) for col in df.columns if df[col].dtype == pl.Utf8]) -def df_to_json(df: pd.DataFrame, max_records: int = 10000, max_group_size: int = None) -> str: - return ujson.dumps(df_to_dicts(df, max_records, max_group_size), indent=4, escape_forward_slashes=False) + return tabulate(df, headers='keys', showindex=True, tablefmt='rounded_outline') # type: ignore -def df_to_dicts(df: pd.DataFrame, max_records: int = 10000, max_group_size: int = None) -> list[dict]: - # grouping and processing keeps the process from crashing on really large error - # dataframes (millions of errors). We can't chunk because could cause splitting - # related validation data across chunks, without having to add extra processing - # for tying those objects back together. Grouping adds a little more processing - # time for smaller datasets but keeps really larger ones from crashing. +def df_to_json(df: pl.DataFrame, max_records: int = 10000, max_group_size: int = 200) -> str: + results = df_to_dicts(df, max_records, max_group_size) + return ujson.dumps(results, indent=4, escape_forward_slashes=False) - checks = get_all_checks() +def df_to_dicts(df: pl.DataFrame, max_records: int = 10000, max_group_size: int = 200) -> list[dict]: json_results = [] - if not df.empty: - grouped_df = df.groupby('validation_id', group_keys=False) - if not max_group_size: - total_errors_per_group = calculate_group_chunk_sizes(grouped_df, max_records) - else: - total_errors_per_group = {} - for group_name, group_data in grouped_df: - total_errors_per_group[group_name] = max_group_size - for validation_id, group in df.groupby("validation_id"): - check = find_check(validation_id, checks) - truncated_group, need_to_truncate = truncate_validation_group_records( - group, total_errors_per_group[validation_id] - ) - group_json = process_chunk(truncated_group, validation_id, check) - if group_json: - group_json["validation"]["is_truncated"] = need_to_truncate - json_results.append(group_json) - json_results = sorted(json_results, key=lambda x: x['validation']['id']) - return json_results + if not df.is_empty(): + # polars str columns sort by entry, not lexigraphical sorting like we'd expect, so cast the column to use + # standard python str column sorting. Polars throws a warning at this. + sorted_df = df.with_columns(pl.col('validation_id').cast(pl.Categorical(ordering='lexical'))).sort( + 'validation_id' + ) + checks = get_checks(df.select(pl.first("phase")).item()) -def calculate_group_chunk_sizes(grouped_df, max_records): - # This function is similar to create_schemas.trim_down_errors but focuses on number of - # records per validation id. It uses a ratio relative to total errors to determine - # each group's adjusted errors relative to max_records, and then adjusts to hit max. - error_counts = {} - for group_name, group_data in grouped_df: - error_counts[group_name] = len(group_data["record_no"].unique()) - error_count_list = list(error_counts.values()) - total_error_count = sum(error_count_list) - - if total_error_count > max_records: - error_ratios = [(count / total_error_count) for count in error_count_list] - new_counts = [math.ceil(max_records * prop) for prop in error_ratios] - - # Adjust the counts in case we went over max. This is very likely since we're using - # ceil, unless we have an exact equality of the new counts. Because of the use - # of ceil, we will never have the sum of the new counts be less than max. - if sum(new_counts) > max_records: - while sum(new_counts) > max_records: - # arbitrary reversal to contain errors in FIG order, if we need to remove - # records from errors to fit max - for i in reversed(range(len(new_counts))): - if new_counts[i] > 1: - new_counts[i] -= 1 - # check if all the counts are equal to 1, then - # start removing those until we hit max - elif new_counts[i] == 1 and sum(new_counts) <= len(new_counts): - new_counts[i] -= 1 - if sum(new_counts) == max_records: - break - - error_counts = dict(zip(error_counts.keys(), new_counts)) - return error_counts + partial_process_group = partial( + process_group_data, json_results=json_results, group_size=max_group_size, checks=checks + ) + + # collecting just the currently processed group from a lazyframe is faster and more efficient than using "apply" + sorted_df.lazy().group_by('validation_id').map_groups(partial_process_group, schema=None).collect() + json_results = sorted(json_results, key=lambda x: x['validation']['id']) + return json_results # Cuts off the number of records. Can't just 'head' on the group due to the dataframe structure. # So this function uses the group error counts to truncate on record numbers def truncate_validation_group_records(group, group_size): - need_to_truncate = len(group['record_no'].unique()) > group_size - unique_record_nos = group['record_no'].unique()[:group_size] - truncated_group = group[group['record_no'].isin(unique_record_nos)] + need_to_truncate = group.select(pl.col('row').n_unique()).item() > group_size + unique_record_nos = group.select('row').unique(maintain_order=True).limit(group_size) + truncated_group = group.filter(pl.col('row').is_in(unique_record_nos['row'])) return truncated_group, need_to_truncate -def process_chunk(df: pd.DataFrame, validation_id: str, check: SBLCheck) -> [dict]: - df.reset_index(drop=True, inplace=True) - findings_json = ujson.loads(df.to_json(orient='columns')) - grouped_data = [] - if not findings_json['record_no']: +def process_group_data(group_df, json_results, group_size, checks): + validation_id = group_df['validation_id'].item(0) + check = find_check(validation_id, checks) + trunc_group, need_to_truncate = truncate_validation_group_records(group_df, group_size) + group_json = process_chunk(trunc_group, validation_id, check) + if group_json: + group_json["validation"]["is_truncated"] = need_to_truncate + json_results.append(group_json) + return group_df + + +def process_chunk(df: pl.DataFrame, validation_id: str, check: SBLCheck) -> [dict]: + # once we have a grouped dataframe, working with the data as a + # python dict is much faster + findings_json = ujson.loads(df.write_json()) + records = [] + if not findings_json: return - for i in range(len(findings_json['record_no'])): - grouped_data.append( - { - 'record_no': findings_json['record_no'][str(i)], - 'uid': findings_json['uid'][str(i)], - 'field_name': findings_json['field_name'][str(i)], - 'field_value': findings_json['field_value'][str(i)], - } - ) + for finding in findings_json: + fields = [] + for key, value in finding.items(): + if 'field_' in key and value: + num = key.split("_")[1] + fields.append({"name": value, "value": finding[f"value_{num}"]}) + records.append({'record_no': finding['row'] - 1, 'uid': finding['unique_identifier'], 'fields': fields}) + + first_finding = findings_json[0] validation_info = { 'validation': { 'id': validation_id, 'name': check.name, 'description': check.description, - 'severity': check.severity, + 'severity': first_finding['validation_type'], 'scope': check.scope, 'fig_link': check.fig_link, }, - 'records': [], + 'records': records, } - records_dict = {} - for record in grouped_data: - record_no = record['record_no'] - if record_no not in records_dict: - records_dict[record_no] = {'record_no': record['record_no'], 'uid': record['uid'], 'fields': []} - records_dict[record_no]['fields'].append({'name': record['field_name'], 'value': record['field_value']}) - validation_info['records'] = list(records_dict.values()) - - for record in validation_info['records']: - if len(record['fields']) == 2: - record['fields'][0], record['fields'][1] = record['fields'][1], record['fields'][0] return validation_info diff --git a/src/regtech_data_validator/phase_validations.py b/src/regtech_data_validator/phase_validations.py index 04bb0fcf..1f884a87 100644 --- a/src/regtech_data_validator/phase_validations.py +++ b/src/regtech_data_validator/phase_validations.py @@ -4,6 +4,8 @@ This mapping is used to populate the schema template object and create an instance of a PanderaSchema object for SYNTACTICAL and LOGICAL phases""" +import pandera.polars as pa + from textwrap import dedent from regtech_data_validator import global_data @@ -31,8 +33,66 @@ string_contains, ) from regtech_data_validator.checks import SBLCheck, Severity +from regtech_data_validator.schema_template import get_template, get_register_template from regtech_data_validator.validation_results import ValidationPhase +# Get separate schema templates for phase 1 and 2 +phase_1_template = get_template() +phase_2_template = get_template() +register_template = get_register_template() + + +def get_schema_by_phase_for_lei(template: dict, phase: str, context: dict[str, str] | None = None): + for column in get_phase_1_and_2_validations_for_lei(context): + validations = get_phase_1_and_2_validations_for_lei(context)[column] + template[column].checks = validations[phase] + + return pa.DataFrameSchema(template, name=phase) + + +def get_phase_1_schema_for_lei(context: dict[str, str] | None = None): + return get_schema_by_phase_for_lei(phase_1_template, ValidationPhase.SYNTACTICAL, context) + + +def get_phase_2_schema_for_lei(context: dict[str, str] | None = None): + return get_schema_by_phase_for_lei(phase_2_template, ValidationPhase.LOGICAL, context) + + +# since we process the data in chunks/batch, we need to handle all file/register +# checks separately, as a separate set of schema and checks. +def get_register_schema(context: dict[str, str] | None = None): + for column in get_phase_2_register_validations(context): + validations = get_phase_2_register_validations(context)[column] + register_template[column].checks = validations[ValidationPhase.LOGICAL] + + return pa.DataFrameSchema(register_template, name=ValidationPhase.LOGICAL) + + +# since we process the data in chunks/batch, we need to handle all file/register +# checks separately, as a separate set of schema and checks. +def get_phase_2_register_validations(context: dict[str, str] | None = None): + return { + "uid": { + ValidationPhase.LOGICAL: [ + SBLCheck( + is_unique_column, + id="E3000", + fig_link=global_data.fig_base_url + "#4.3.1", + name="uid.duplicates_in_dataset", + description=dedent( + """\ + * Any 'unique identifier' may **not** be used in more than one + record within a small business lending application register. + """ + ), + severity=Severity.ERROR, + scope="register", + related_fields="uid", + ), + ] + } + } + def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None): lei: str | None = context.get("lei", None) if context else None @@ -69,26 +129,10 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="single-field", - element_wise=True, regex="^[A-Z0-9]+$", ), ], ValidationPhase.LOGICAL: [ - SBLCheck( - is_unique_column, - id="E3000", - fig_link=global_data.fig_base_url + "#4.3.1", - name="uid.duplicates_in_dataset", - description=dedent( - """\ - * Any 'unique identifier' may **not** be used in more than one - record within a small business lending application register. - """ - ), - severity=Severity.ERROR, - scope="register", - groupby="uid", - ), SBLCheck( string_contains, id="W0003", @@ -118,7 +162,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) description="* 'Application date' must be a real calendar date using YYYYMMDD format.", severity=Severity.ERROR, scope="single-field", - element_wise=True, ), ], ValidationPhase.LOGICAL: [], @@ -133,7 +176,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) description="* 'Application method' must equal 1, 2, 3, or 4.", severity=Severity.ERROR, scope="single-field", - element_wise=True, accepted_values=[ "1", "2", @@ -154,7 +196,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) description="* 'Application recipient' must equal 1 or 2.", severity=Severity.ERROR, scope="single-field", - element_wise=True, accepted_values=[ "1", "2", @@ -173,7 +214,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) description="* 'Credit product' must equal 1, 2, 3, 4, 5, 6, 7, 8, 977, or 988.", severity=Severity.ERROR, scope="single-field", - element_wise=True, accepted_values=[ "1", "2", @@ -225,7 +265,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="multi-field", - groupby="ct_credit_product", + related_fields="ct_credit_product", condition_values={"977"}, ), ], @@ -245,7 +285,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="single-field", - element_wise=True, accepted_values=[ "1", "2", @@ -277,7 +316,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="single-field", - element_wise=True, min_length=1, max_length=5, ), @@ -289,7 +327,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) description="* 'Type of guarantee' should **not** contain duplicated values.", severity=Severity.WARNING, scope="single-field", - element_wise=True, ), SBLCheck( meets_multi_value_field_restriction, @@ -336,7 +373,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="multi-field", - groupby="ct_guarantee", + related_fields="ct_guarantee", condition_values={"977"}, ), SBLCheck( @@ -354,7 +391,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.WARNING, scope="multi-field", - groupby="ct_guarantee", + related_fields="ct_guarantee", ignored_values={"977"}, max_length=5, ), @@ -370,7 +407,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) description="* 'Loan term: NA/NP flag' must equal 900, 988, or 999.", severity=Severity.ERROR, scope="single-field", - element_wise=True, accepted_values=[ "900", "988", @@ -394,7 +430,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="multi-field", - groupby="ct_credit_product", + related_fields="ct_credit_product", conditions=[ { "condition_values": {"1", "2"}, @@ -441,7 +477,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="multi-field", - groupby="ct_loan_term_flag", + related_fields="ct_loan_term_flag", condition_values={"900"}, ), SBLCheck( @@ -485,7 +521,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="single-field", - element_wise=True, accepted_values=[ "1", "2", @@ -518,7 +553,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="single-field", - element_wise=True, min_length=1, max_length=3, ), @@ -550,7 +584,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) description="* 'Credit purpose' should **not** contain duplicated values.", severity=Severity.WARNING, scope="single-field", - element_wise=True, ), ], }, @@ -588,7 +621,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="multi-field", - groupby="credit_purpose", + related_fields="credit_purpose", condition_values={"977"}, ), SBLCheck( @@ -607,7 +640,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.WARNING, scope="multi-field", - groupby="credit_purpose", + related_fields="credit_purpose", ignored_values={"977"}, max_length=3, ), @@ -623,7 +656,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) description="* 'Amount applied For: NA/NP flag' must equal 900, 988, or 999.", severity=Severity.ERROR, scope="single-field", - element_wise=True, accepted_values=[ "900", "988", @@ -663,7 +695,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="multi-field", - groupby="amount_applied_for_flag", + related_fields="amount_applied_for_flag", condition_values={"900"}, ), SBLCheck( @@ -721,7 +753,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="multi-field", - groupby="action_taken", + related_fields="action_taken", condition_values={"1", "2"}, ), ], @@ -736,7 +768,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) description="* 'Action taken' must equal 1, 2, 3, 4, or 5.", severity=Severity.ERROR, scope="single-field", - element_wise=True, accepted_values=[ "1", "2", @@ -770,7 +801,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="multi-field", - groupby=[ + related_fields=[ "pricing_interest_rate_type", "pricing_mca_addcost_flag", "pricing_prepenalty_allowed", @@ -811,7 +842,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="multi-field", - groupby=[ + related_fields=[ "pricing_origination_charges", "pricing_broker_fees", "pricing_initial_charges", @@ -839,7 +870,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) description="* 'Action taken date' must be a real calendar date using YYYYMMDD format.", severity=Severity.ERROR, scope="single-field", - element_wise=True, ), ], ValidationPhase.LOGICAL: [ @@ -856,7 +886,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="single-field", - element_wise=True, start_date_value="20241001", end_date_value="20241231", ), @@ -868,7 +897,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) description="* The date indicated by 'action taken date' must occur on or after 'application date'.", severity=Severity.ERROR, scope="multi-field", - groupby="app_date", + related_fields="app_date", ), SBLCheck( is_date_before_in_days, @@ -884,7 +913,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.WARNING, scope="multi-field", - groupby="app_date", + related_fields="app_date", days_value=730, ), ], @@ -904,7 +933,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="single-field", - element_wise=True, accepted_values=[ "1", "2", @@ -934,7 +962,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="single-field", - element_wise=True, min_length=1, max_length=4, ), @@ -951,7 +978,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="multi-field", - groupby="action_taken", + related_fields="action_taken", conditions=[ { "condition_values": {"3"}, @@ -991,7 +1018,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) description="* 'Denial reason(s)' should **not** contain duplicated values.", severity=Severity.WARNING, scope="single-field", - element_wise=True, ), ], }, @@ -1029,7 +1055,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="multi-field", - groupby="denial_reasons", + related_fields="denial_reasons", condition_values={"977"}, ), SBLCheck( @@ -1048,7 +1074,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.WARNING, scope="multi-field", - groupby="denial_reasons", + related_fields="denial_reasons", ignored_values={"977"}, max_length=4, ), @@ -1064,7 +1090,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) description="* 'Interest rate type' must equal 1, 2, 3, 4, 5, 6, or 999.", severity=Severity.ERROR, scope="single-field", - element_wise=True, accepted_values=[ "1", "2", @@ -1113,7 +1138,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="multi-field", - groupby="pricing_interest_rate_type", + related_fields="pricing_interest_rate_type", condition_values={"3", "4", "5", "6"}, ), SBLCheck( @@ -1164,7 +1189,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="multi-field", - groupby="pricing_interest_rate_type", + related_fields="pricing_interest_rate_type", condition_values={"2", "4", "6"}, ), SBLCheck( @@ -1212,7 +1237,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="multi-field", - groupby="pricing_interest_rate_type", + related_fields="pricing_interest_rate_type", condition_values={"1", "3", "5"}, ), SBLCheck( @@ -1248,7 +1273,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="single-field", - element_wise=True, accepted_values=[ "1", "2", @@ -1282,7 +1306,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="multi-field", - groupby="pricing_interest_rate_type", + related_fields="pricing_interest_rate_type", conditions=[ { "condition_values": {"1", "3", "5"}, @@ -1333,7 +1357,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="multi-field", - groupby="pricing_adj_index_name", + related_fields="pricing_adj_index_name", condition_values={"977"}, ), ], @@ -1368,7 +1392,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="multi-field", - groupby="pricing_interest_rate_type", + related_fields="pricing_interest_rate_type", condition_values={"1", "3"}, ), ], @@ -1436,7 +1460,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="single-field", - element_wise=True, accepted_values=[ "900", "999", @@ -1460,7 +1483,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="multi-field", - groupby="ct_credit_product", + related_fields="ct_credit_product", conditions=[ { "condition_values": {"7", "8", "977"}, @@ -1510,7 +1533,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="multi-field", - groupby="pricing_mca_addcost_flag", + related_fields="pricing_mca_addcost_flag", condition_values={"900"}, ), ], @@ -1525,7 +1548,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) description="* Prepayment penalty could be imposed' must equal 1, 2, or 999.", severity=Severity.ERROR, scope="single-field", - element_wise=True, accepted_values=[ "1", "2", @@ -1545,7 +1567,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) description="* 'Prepayment penalty exists' must equal 1, 2, or 999.", severity=Severity.ERROR, scope="single-field", - element_wise=True, accepted_values=[ "1", "2", @@ -1565,7 +1586,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) description="* 'Census tract: type of address' must equal 1, 2, 3, or 988.", severity=Severity.ERROR, scope="single-field", - element_wise=True, accepted_values=[ "1", "2", @@ -1609,7 +1629,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="multi-field", - groupby="census_tract_adr_type", + related_fields="census_tract_adr_type", conditions=[ { "condition_values": {"1", "2", "3"}, @@ -1654,7 +1674,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) description="* 'Gross annual revenue: NP flag' must equal 900 or 988.", severity=Severity.ERROR, scope="single-field", - element_wise=True, accepted_values=[ "900", "988", @@ -1693,7 +1712,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="multi-field", - groupby="gross_annual_revenue_flag", + related_fields="gross_annual_revenue_flag", condition_values={"900"}, ), ], @@ -1713,7 +1732,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="single-field", - element_wise=True, accepted_values=[ "900", "988", @@ -1793,7 +1811,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="multi-field", - groupby="naics_code_flag", + related_fields="naics_code_flag", condition_values={"900"}, ), ], @@ -1808,7 +1826,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) description="* 'Number of workers' must equal 1, 2, 3, 4, 5, 6, 7, 8, 9, or 988.", severity=Severity.ERROR, scope="single-field", - element_wise=True, accepted_values=[ "1", "2", @@ -1835,7 +1852,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) description="* 'Time in business: type of response' must equal 1, 2, 3, or 988.", severity=Severity.ERROR, scope="single-field", - element_wise=True, accepted_values=[ "1", "2", @@ -1888,7 +1904,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="multi-field", - groupby="time_in_business_type", + related_fields="time_in_business_type", condition_values={"1"}, ), ], @@ -1908,7 +1924,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="single-field", - element_wise=True, accepted_values=[ "1", "2", @@ -1928,7 +1943,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) description="* 'Business ownership status' must contain at least one value.", severity=Severity.ERROR, scope="single-field", - element_wise=True, min_length=1, ), SBLCheck( @@ -1939,7 +1953,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) description="* 'Business ownership status' should **not** contain duplicated values.", severity=Severity.WARNING, scope="single-field", - element_wise=True, ), SBLCheck( meets_multi_value_field_restriction, @@ -1971,7 +1984,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) description="* 'Number of principal owners: NP flag' must equal 900 or 988.", severity=Severity.ERROR, scope="single-field", - element_wise=True, accepted_values=[ "900", "988", @@ -1990,7 +2002,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) description="* When present, 'number of principal owners' must equal 0, 1, 2, 3, or 4.", severity=Severity.ERROR, scope="single-field", - element_wise=True, accepted_values=["0", "1", "2", "3", "4"], accept_blank=True, ), @@ -2011,7 +2022,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="multi-field", - groupby="num_principal_owners_flag", + related_fields="num_principal_owners_flag", condition_values={"900"}, ), SBLCheck( @@ -2028,7 +2039,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.WARNING, scope="multi-field", - groupby=[ + related_fields=[ "po_1_ethnicity", "po_1_race", "po_1_gender_flag", @@ -2073,7 +2084,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.WARNING, scope="multi-field", - groupby=[ + related_fields=[ "po_1_ethnicity", "po_1_race", "po_1_gender_flag", @@ -2118,7 +2129,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.WARNING, scope="multi-field", - groupby=[ + related_fields=[ "po_1_ethnicity", "po_1_race", "po_1_gender_flag", @@ -2163,7 +2174,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.WARNING, scope="multi-field", - groupby=[ + related_fields=[ "po_1_ethnicity", "po_1_race", "po_1_gender_flag", @@ -2207,7 +2218,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.WARNING, scope="multi-field", - groupby=[ + related_fields=[ "po_1_ethnicity", "po_1_race", "po_1_gender_flag", @@ -2254,7 +2265,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="single-field", - element_wise=True, accepted_values=[ "1", "11", @@ -2278,7 +2288,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) description="* 'Ethnicity of principal owner 1' should **not** contain duplicated values.", severity=Severity.WARNING, scope="single-field", - element_wise=True, ), SBLCheck( meets_multi_value_field_restriction, @@ -2335,7 +2344,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="multi-field", - groupby="po_1_ethnicity", + related_fields="po_1_ethnicity", condition_values={"977"}, ), ], @@ -2357,7 +2366,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="single-field", - element_wise=True, accepted_values=[ "1", "2", @@ -2401,7 +2409,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) description="* 'Race of principal owner 1' should **not** contain duplicated values.", severity=Severity.WARNING, scope="single-field", - element_wise=True, ), SBLCheck( meets_multi_value_field_restriction, @@ -2459,7 +2466,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="multi-field", - groupby="po_1_race", + related_fields="po_1_race", condition_values={"971"}, ), ], @@ -2498,7 +2505,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="multi-field", - groupby="po_1_race", + related_fields="po_1_race", condition_values={"972"}, ), ], @@ -2539,7 +2546,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="multi-field", - groupby="po_1_race", + related_fields="po_1_race", condition_values={"973"}, ), ], @@ -2580,7 +2587,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="multi-field", - groupby="po_1_race", + related_fields="po_1_race", condition_values={"974"}, ), ], @@ -2595,7 +2602,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) description="* When present, 'sex/gender of principal owner 1: NP flag' must equal 1, 966, or 988.", severity=Severity.ERROR, scope="single-field", - element_wise=True, accepted_values=[ "1", "966", @@ -2641,7 +2647,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="multi-field", - groupby="po_1_gender_flag", + related_fields="po_1_gender_flag", condition_values={"1"}, ), ], @@ -2661,7 +2667,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="single-field", - element_wise=True, accepted_values=[ "1", "11", @@ -2685,7 +2690,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) description="* 'Ethnicity of principal owner 2' should **not** contain duplicated values.", severity=Severity.WARNING, scope="single-field", - element_wise=True, ), SBLCheck( meets_multi_value_field_restriction, @@ -2742,7 +2746,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="multi-field", - groupby="po_2_ethnicity", + related_fields="po_2_ethnicity", condition_values={"977"}, ), ], @@ -2764,7 +2768,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="single-field", - element_wise=True, accepted_values=[ "1", "2", @@ -2808,7 +2811,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) description="* 'Race of principal owner 2' should **not** contain duplicated values.", severity=Severity.WARNING, scope="single-field", - element_wise=True, ), SBLCheck( meets_multi_value_field_restriction, @@ -2868,7 +2870,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="multi-field", - groupby="po_2_race", + related_fields="po_2_race", condition_values={"971"}, ), ], @@ -2907,7 +2909,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="multi-field", - groupby="po_2_race", + related_fields="po_2_race", condition_values={"972"}, ), ], @@ -2947,7 +2949,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="multi-field", - groupby="po_2_race", + related_fields="po_2_race", condition_values={"973"}, ), ], @@ -2987,7 +2989,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="multi-field", - groupby="po_2_race", + related_fields="po_2_race", condition_values={"974"}, ), ], @@ -3002,7 +3004,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) description="* When present, 'sex/gender of principal owner 2: NP flag' must equal 1, 966, or 988.", severity=Severity.ERROR, scope="single-field", - element_wise=True, accepted_values=[ "1", "966", @@ -3050,7 +3051,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="multi-field", - groupby="po_2_gender_flag", + related_fields="po_2_gender_flag", condition_values={"1"}, ), ], @@ -3071,7 +3072,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="single-field", - element_wise=True, accepted_values=[ "1", "11", @@ -3095,7 +3095,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) description="* 'Ethnicity of principal owner 3' should **not** contain duplicated values.", severity=Severity.WARNING, scope="single-field", - element_wise=True, ), SBLCheck( meets_multi_value_field_restriction, @@ -3152,7 +3151,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="multi-field", - groupby="po_3_ethnicity", + related_fields="po_3_ethnicity", condition_values={"977"}, ), ], @@ -3174,7 +3173,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="single-field", - element_wise=True, accepted_values=[ "1", "2", @@ -3218,7 +3216,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) description="* 'Race of principal owner 3' should **not** contain duplicated values.", severity=Severity.WARNING, scope="single-field", - element_wise=True, ), SBLCheck( meets_multi_value_field_restriction, @@ -3277,7 +3274,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="multi-field", - groupby="po_3_race", + related_fields="po_3_race", condition_values={"971"}, ), ], @@ -3317,7 +3314,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="multi-field", - groupby="po_3_race", + related_fields="po_3_race", condition_values={"972"}, ), ], @@ -3357,7 +3354,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="multi-field", - groupby="po_3_race", + related_fields="po_3_race", condition_values={"973"}, ), ], @@ -3397,7 +3394,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="multi-field", - groupby="po_3_race", + related_fields="po_3_race", condition_values={"974"}, ), ], @@ -3412,7 +3409,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) description="* When present, 'sex/gender of principal owner 3: NP flag' must equal 1, 966, or 988.", severity=Severity.ERROR, scope="single-field", - element_wise=True, accepted_values=[ "1", "966", @@ -3458,7 +3454,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="multi-field", - groupby="po_3_gender_flag", + related_fields="po_3_gender_flag", condition_values={"1"}, ), ], @@ -3478,7 +3474,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="single-field", - element_wise=True, accepted_values=[ "1", "11", @@ -3502,7 +3497,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) description="* 'Ethnicity of principal owner 4' should **not** contain duplicated values.", severity=Severity.WARNING, scope="single-field", - element_wise=True, ), SBLCheck( meets_multi_value_field_restriction, @@ -3559,7 +3553,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="multi-field", - groupby="po_4_ethnicity", + related_fields="po_4_ethnicity", condition_values={"977"}, ), ], @@ -3581,7 +3575,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="single-field", - element_wise=True, accepted_values=[ "1", "2", @@ -3625,7 +3618,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) description="* 'Race of principal owner 4' should **not** contain duplicated values.", severity=Severity.WARNING, scope="single-field", - element_wise=True, ), SBLCheck( meets_multi_value_field_restriction, @@ -3684,7 +3676,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="multi-field", - groupby="po_4_race", + related_fields="po_4_race", condition_values={"971"}, ), ], @@ -3724,7 +3716,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="multi-field", - groupby="po_4_race", + related_fields="po_4_race", condition_values={"972"}, ), ], @@ -3765,7 +3757,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="multi-field", - groupby="po_4_race", + related_fields="po_4_race", condition_values={"973"}, ), ], @@ -3806,7 +3798,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="multi-field", - groupby="po_4_race", + related_fields="po_4_race", condition_values={"974"}, ), ], @@ -3821,7 +3813,6 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) description="* When present, 'sex/gender of principal owner 4: NP flag' must equal 1, 966, or 988.", severity=Severity.ERROR, scope="single-field", - element_wise=True, accepted_values=[ "1", "966", @@ -3869,7 +3860,7 @@ def get_phase_1_and_2_validations_for_lei(context: dict[str, str] | None = None) ), severity=Severity.ERROR, scope="multi-field", - groupby="po_4_gender_flag", + related_fields="po_4_gender_flag", condition_values={"1"}, ), ], diff --git a/src/regtech_data_validator/schema_template.py b/src/regtech_data_validator/schema_template.py index a2229cdd..b2d58015 100644 --- a/src/regtech_data_validator/schema_template.py +++ b/src/regtech_data_validator/schema_template.py @@ -12,7 +12,7 @@ from copy import deepcopy from typing import Dict -from pandera import Column +from pandera.polars import Column _schema_template = { "uid": Column( @@ -447,3 +447,18 @@ def get_template() -> Dict: to debug.""" return deepcopy(_schema_template) + + +# since we process the data in chunks/batch, we need to handle all file/register +# checks separately, as a separate set of schema and checks. +_register_template = { + "uid": Column( + str, + title="Field 1: Unique identifier", + checks=[], + ) +} + + +def get_register_template() -> Dict: + return deepcopy(_register_template) diff --git a/src/regtech_data_validator/validation_results.py b/src/regtech_data_validator/validation_results.py index 72d065cb..40a63526 100644 --- a/src/regtech_data_validator/validation_results.py +++ b/src/regtech_data_validator/validation_results.py @@ -1,4 +1,4 @@ -import pandas as pd +import polars as pl from dataclasses import dataclass from enum import StrEnum @@ -9,7 +9,8 @@ class ValidationPhase(StrEnum): LOGICAL = "Logical" -@dataclass(frozen=True) +# @dataclass(frozen=True) +@dataclass class Counts(object): single_field_count: int = 0 multi_field_count: int = 0 @@ -17,10 +18,11 @@ class Counts(object): total_count: int = 0 -@dataclass(frozen=True) +# @dataclass(frozen=True) +@dataclass class ValidationResults(object): error_counts: Counts warning_counts: Counts is_valid: bool - findings: pd.DataFrame + findings: pl.DataFrame phase: ValidationPhase diff --git a/src/regtech_data_validator/validator.py b/src/regtech_data_validator/validator.py new file mode 100644 index 00000000..2bd1851d --- /dev/null +++ b/src/regtech_data_validator/validator.py @@ -0,0 +1,323 @@ +"""Creates two DataFrameSchema objects by rendering the schema template +with validations listed in phase 1 and phase 2.""" + +from pathlib import Path +import polars as pl +import pandera.polars as pa +from pandera import Check +from pandera.errors import SchemaErrors, SchemaError, SchemaErrorReason + +from regtech_data_validator.checks import SBLCheck, Severity + +from regtech_data_validator.validation_results import ValidationPhase, Counts, ValidationResults +from regtech_data_validator.data_formatters import format_findings + +from fsspec import AbstractFileSystem, filesystem + +import shutil +import os + +from regtech_data_validator.phase_validations import ( + get_phase_1_schema_for_lei, + get_phase_2_schema_for_lei, + get_register_schema, +) + + +# Gets all associated field names from the check +def _get_check_fields(check: Check, primary_column: str) -> list[str]: + + field_list = [primary_column] + if "related_fields" in check._check_kwargs: + related_fields = check._check_kwargs["related_fields"] + if related_fields: + # related_fields can be a single str or list of str + if isinstance(related_fields, str): + field_list.append(related_fields) + else: + field_list.extend(related_fields) + # remove possible dupes but maintain order + field_list = list(dict.fromkeys(field_list)) + return field_list + + +# Retrieves the row data from the original dataframe that threw errors/warnings, and pulls out the fields/values +# from the original row data that caused the error/warning +def _filter_valid_records(df: pl.DataFrame, check_output: pl.Series, fields: list[str]) -> pl.DataFrame: + sorted_check_output = check_output["index"] + fields = ["index"] + fields + filtered_df = df.filter(pl.col('index').is_in(sorted_check_output)) + failed_records_df = filtered_df[fields] + # record_no is indexed by 1 instead of 0, so offset and drop index since it's no longer needed + failed_records_df = failed_records_df.with_columns((pl.col("index") + 1).alias("record_no")) + failed_records_df = failed_records_df.drop("index") + return failed_records_df + + +def _records_to_fields(failed_records_df: pl.DataFrame) -> pl.DataFrame: + # Melts the DataFrame with columns per Check field to DataFrame with a row per field + failed_record_fields_df = failed_records_df.melt( + variable_name='field_name', value_name='field_value', id_vars=['record_no'] + ) + return failed_record_fields_df + + +def _add_validation_metadata(failed_check_fields_df: pl.DataFrame, check: SBLCheck): + # add the error/warning code from the check to the error dataframe + validation_fields_df = failed_check_fields_df.with_columns(pl.lit(check.title).alias("validation_id")) + return validation_fields_df + + +def validate( + schema: pa.DataFrameSchema, submission_df: pl.LazyFrame, row_start: int, process_errors: bool +) -> pl.DataFrame: + """ + validate received dataframe with schema and return list of + schema errors + Args: + schema (DataFrameSchema): schema to be used for validation + submission_df (pl.DataFrame): data to be validated against the schema + Returns: + pd.DataFrame containing validation results data + """ + findings_df: pl.DataFrame = pl.DataFrame() + error_counts = warning_counts = Counts() + + try: + # since polars dataframes don't normally have an index column, add it, so that we can match + # up original submission rows with rows found with errors/warnings + submission_df = submission_df.with_row_index(offset=row_start) + schema(submission_df, lazy=True) + except SchemaErrors as err: + check_findings = [] + # NOTE: `type: ignore` because SchemaErrors.schema_errors is supposed to be + # `list[dict[str,Any]]`, but it's actually of type `SchemaError` + schema_error: SchemaError + + error_counts, warning_counts = get_scope_counts(err.schema_errors) + if process_errors: + for schema_error in err.schema_errors: + check = schema_error.check + column_name = schema_error.schema.name + + # CHECK_ERROR is thrown by pandera polars if the check itself has a coding error, NOT if the check data results in an error + if ( + schema_error.reason_code is SchemaErrorReason.CHECK_ERROR + or schema_error.reason_code is SchemaErrorReason.COLUMN_NOT_IN_DATAFRAME + ): + raise RuntimeError(schema_error) from schema_error + if not check: + raise RuntimeError( + f'SchemaError occurred with no associated Check for {column_name} column' + ) from schema_error + + if not isinstance(check, SBLCheck): + raise RuntimeError( + f'Check {check} type on {column_name} column not supported. Must be of type {SBLCheck}' + ) from schema_error + + schema_error = gather_errors(schema_error) + + fields = _get_check_fields(check, column_name) + check_output: pl.Series | None = schema_error.check_output + + if check_output is not None: + # Filter data not associated with failed Check, and update index for merging with findings_df + failed_records_df = _filter_valid_records(submission_df, check_output, fields) + failed_record_fields_df = _records_to_fields(failed_records_df) + findings = _add_validation_metadata(failed_record_fields_df, check) + check_findings.append(findings) + else: + # The above exception handling _should_ prevent this from ever happenin, but...just in case. + raise RuntimeError( + f'No check output for "{check.name}" check. Pandera SchemaError: {schema_error}' + ) + if check_findings: + findings_df = pl.concat(check_findings) + + updated_df = add_uid(findings_df, submission_df) + results = ValidationResults( + error_counts=error_counts, + warning_counts=warning_counts, + is_valid=((error_counts.total_count + warning_counts.total_count) == 0), + findings=updated_df, + phase=schema.name, + ) + return results + + +# Add the uid for the record throwing the error/warning to the error dataframe +def add_uid(results_df: pl.DataFrame, submission_df: pl.DataFrame) -> pl.DataFrame: + if results_df.is_empty(): + return results_df + + uid_records = results_df['record_no'] - 1 + results_df = results_df.with_columns(submission_df['uid'].gather(uid_records).alias('uid')) + return results_df + + +# This function is a Generator, and will yield the results of each batch of processing, along with the +# phase (SYNTACTICAL/LOGICAL) that the findings were found. Callers of this function will want to +# store or concat each iteration of findings +def validate_batch_csv( + path: Path | str, + context: dict[str, str] | None = None, + batch_size: int = 50000, + batch_count: int = 1, + max_errors=1000000, +): + has_syntax_errors = False + real_path = get_real_file_path(path) + # process the data first looking for syntax (phase 1) errors, then looking for logical (phase 2) errors/warnings + syntax_schema = get_phase_1_schema_for_lei(context) + syntax_checks = [check for col_schema in syntax_schema.columns.values() for check in col_schema.checks] + + logic_schema = get_phase_2_schema_for_lei(context) + logic_checks = [check for col_schema in logic_schema.columns.values() for check in col_schema.checks] + + all_uids = [] + + for validation_results, uids in validate_chunks( + syntax_schema, real_path, batch_size, batch_count, max_errors, syntax_checks + ): + all_uids.extend(uids) + # validate, and therefore validate_chunks, can return an empty dataframe for findings + if not validation_results.findings.is_empty(): + has_syntax_errors = True + yield validation_results + + if not has_syntax_errors: + register_schema = get_register_schema(context) + validation_results = validate(register_schema, pl.DataFrame({"uid": all_uids}), 0, True) + if not validation_results.findings.is_empty(): + validation_results.findings = format_findings( + validation_results.findings, + ValidationPhase.LOGICAL.value, + [check for col_schema in register_schema.columns.values() for check in col_schema.checks], + ) + yield validation_results + + for validation_results, _ in validate_chunks( + logic_schema, real_path, batch_size, batch_count, max_errors, logic_checks + ): + yield validation_results + + if os.path.isdir("/tmp/s3"): + shutil.rmtree("/tmp/s3") + + +# Reads in a path to a csv in batches, using batch_size to determine number of rows to read into the buffer, +# and batch_count to determine how many batches to process in parallel. Performance testing for large files +# shows 50K batch_size with 1 batch_count to be a nice balance of speed and resource utilization. Increasing +# these increases resource utilization but increases speed (especially batch_count). Reducing these, espectially +# batch_count adds processing cylces (time) but can significantly reduce resources. +def validate_chunks(schema, path, batch_size, batch_count, max_errors, checks): + reader = pl.read_csv_batched(path, infer_schema_length=0, missing_utf8_is_empty_string=True, batch_size=batch_size) + batches = reader.next_batches(batch_count) + process_errors = True + total_count = 0 + row_start = 0 + while batches: + df = pl.concat(batches) + validation_results = validate(schema, df, row_start, process_errors) + if not validation_results.findings.is_empty(): + validation_results.findings = format_findings( + validation_results.findings, validation_results.phase.value, checks + ) + + total_count += validation_results.findings.height + + if total_count > max_errors and process_errors: + process_errors = False + head_count = validation_results.findings.height - (total_count - max_errors) + validation_results.findings = validation_results.findings.head(head_count) + + row_start += df.height + batches = reader.next_batches(batch_count) + yield validation_results, df["uid"].to_list() + + +def get_real_file_path(path): + path = str(path) + if path.startswith("s3://"): + fs: AbstractFileSystem = filesystem(protocol="filecache", target_protocol="s3", cache_storage="/tmp/s3") + path = fs.unstrip_protocol(path) + with fs.open(path, "r") as f: + return f.name + return path + + +# This function adds an index column (polars dataframes do not normally have one), and filters out +# any row that did not fail a check. +def gather_errors(schema_error: SchemaError): + schema_error.check_output = schema_error.check_output.with_row_index() + error_indices = schema_error.check_output.filter(~pl.col("check_output"))["index"].to_list() + schema_error.check_output = schema_error.check_output.filter(pl.col("index").is_in(error_indices)) + return schema_error + + +def get_scope_counts(schema_errors: list[SchemaError]): + singles = [ + error for error in schema_errors if isinstance(error.check, SBLCheck) and error.check.scope == 'single-field' + ] + + single_errors = int( + sum( + [ + (error.check_output.filter(~pl.col("check_output"))).height + for error in singles + if error.check.severity == Severity.ERROR + ] + ) + ) + single_warnings = int( + sum( + [ + (error.check_output.filter(~pl.col("check_output"))).height + for error in singles + if error.check.severity == Severity.WARNING + ] + ) + ) + multi = [ + error for error in schema_errors if isinstance(error.check, SBLCheck) and error.check.scope == 'multi-field' + ] + multi_errors = int( + sum( + [ + (error.check_output.filter(~pl.col("check_output"))).height + for error in multi + if error.check.severity == Severity.ERROR + ] + ) + ) + multi_warnings = int( + sum( + [ + (error.check_output.filter(~pl.col("check_output"))).height + for error in multi + if error.check.severity == Severity.WARNING + ] + ) + ) + + register_errors = int( + sum( + [ + (error.check_output.filter(~pl.col("check_output"))).height + for error in schema_errors + if isinstance(error.check, SBLCheck) and error.check.scope == 'register' + ] + ) + ) + + return Counts( + single_field_count=single_errors, + multi_field_count=multi_errors, + register_count=register_errors, + total_count=sum([single_errors, multi_errors, register_errors]), + ), Counts( + single_field_count=single_warnings, + multi_field_count=multi_warnings, + total_count=sum([single_warnings, multi_warnings]), # There are no register-level warnings at this time + ) diff --git a/tests/data/all_logic_errors.csv b/tests/data/all_logic_errors.csv new file mode 100644 index 00000000..a091da79 --- /dev/null +++ b/tests/data/all_logic_errors.csv @@ -0,0 +1,9 @@ +uid,app_date,app_method,app_recipient,ct_credit_product,ct_credit_product_ff,ct_guarantee,ct_guarantee_ff,ct_loan_term_flag,ct_loan_term,credit_purpose,credit_purpose_ff,amount_applied_for_flag,amount_applied_for,amount_approved,action_taken,action_taken_date,denial_reasons,denial_reasons_ff,pricing_interest_rate_type,pricing_init_rate_period,pricing_fixed_rate,pricing_adj_margin,pricing_adj_index_name,pricing_adj_index_name_ff,pricing_adj_index_value,pricing_origination_charges,pricing_broker_fees,pricing_initial_charges,pricing_mca_addcost_flag,pricing_mca_addcost,pricing_prepenalty_allowed,pricing_prepenalty_exists,census_tract_adr_type,census_tract_number,gross_annual_revenue_flag,gross_annual_revenue,naics_code_flag,naics_code,number_of_workers,time_in_business_type,time_in_business,business_ownership_status,num_principal_owners_flag,num_principal_owners,po_1_ethnicity,po_1_ethnicity_ff,po_1_race,po_1_race_anai_ff,po_1_race_asian_ff,po_1_race_baa_ff,po_1_race_pi_ff,po_1_gender_flag,po_1_gender_ff,po_2_ethnicity,po_2_ethnicity_ff,po_2_race,po_2_race_anai_ff,po_2_race_asian_ff,po_2_race_baa_ff,po_2_race_pi_ff,po_2_gender_flag,po_2_gender_ff,po_3_ethnicity,po_3_ethnicity_ff,po_3_race,po_3_race_anai_ff,po_3_race_asian_ff,po_3_race_baa_ff,po_3_race_pi_ff,po_3_gender_flag,po_3_gender_ff,po_4_ethnicity,po_4_ethnicity_ff,po_4_race,po_4_race_anai_ff,po_4_race_asian_ff,po_4_race_baa_ff,po_4_race_pi_ff,po_4_gender_flag,po_4_gender_ff +123456789TESTBANK12300001,20201201,1,1,988,test,999;999;999;999;999;999,test,988,0,999;999;999;999,test,999,0,0,3,20241031,999;999;999;999;999,test,5,,0,,999,test,0,,,,900,,999,999,988,01001020100,988,0,988,1234,988,988,0,988;988,900,0,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +123456789TESTBANK12300001,20241201,1,1,988,test,999;999;999;999;999;999,test,988,0,999;999;999;999,test,999,0,0,1,20201231,977,,5,,0,,999,test,0,,,,900,,999,999,988,01001020100,988,0,988,1234,988,988,0,988;988,900,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +123456789TESTBANK12300001,20241201,1,1,988,test,999;999;999;999;999;999,test,988,0,999;999;999;999,test,999,0,0,1,20201231,977,,5,0,0,,999,test,0,,,,900,,999,999,988,01001020100,988,0,988,1234,988,988,0,988;988,900,4,966;966,test,966;966,test,test,test,test,,test,966;966,test,966;966,test,test,test,test,,test,966;966,test,966;966,test,test,test,test,,test,966;966,test,966;966,test,test,test,test,,test +123456789TESTBANK12300001,20241201,1,1,988,1;2;3;4;5;test,999;999;977,1;2;3;4;5;test,988,1300,999;999;988;999;977,1;2;3;4;5;test,999,0,0,3,20201231,999;999;977;999;999,1;2;3;4;5;test,5,,0,0.01,999,test,0,,,,900,,999,999,988,01001020101,988,0,988,1234,988,988,0,988;988;966,988,4,988;966,test,988;966,test,test,test,test,,test,988;966,test,988;966,test,test,test,test,,test,988;966,test,988;966,test,test,test,test,,test,988;966,test,988;966,test,test,test,test,,test +123456789TESTBANK12300001,20241201,1,1,988,1;2;3;4;5;test,999;999;977,1;2;3;4;5;test,988,1300,999;999;988;999;977,1;2;3;4;5;test,999,0,0,3,20201231,999;999;977;999;999,1;2;3;4;5;test,5,,0,0.01,999,test,0,,,,900,,999,999,988,01001020101,988,0,988,1234,988,988,0,988;988;966,988,1,988;966,test,988;966,test,test,test,test,,test,988;966,test,988;966,test,test,test,test,,test,988;966,test,988;966,test,test,test,test,,test,988;966,test,988;966,test,test,test,test,,test +123456789TESTBANK12300001,20241201,1,1,988,1;2;3;4;5;test,999;999;977,1;2;3;4;5;test,988,1300,999;999;988;999;977,1;2;3;4;5;test,999,0,0,3,20201231,999;999;977;999;999,1;2;3;4;5;test,5,,0,0.01,999,test,0,,,,900,,999,999,988,01001020101,988,0,988,1234,988,988,0,988;988;966,988,2,988;966,test,988;966,test,test,test,test,,test,988;966,test,988;966,test,test,test,test,,test,988;966,test,988;966,test,test,test,test,,test,988;966,test,988;966,test,test,test,test,,test +123456789TESTBANK12300001,20241201,1,1,988,1;2;3;4;5;test,999;999;977,1;2;3;4;5;test,988,1300,999;999;988;999;977,1;2;3;4;5;test,999,0,0,3,20201231,999;999;977;999;999,1;2;3;4;5;test,5,,0,0.01,999,test,0,,,,900,,999,999,988,01001020101,988,0,988,1234,988,988,-1,988;988;966,988,3,988;966,test,988;966,test,test,test,test,,test,988;966,test,988;966,test,test,test,test,,test,988;966,test,988;966,test,test,test,test,,test,988;966,test,988;966,test,test,test,test,,test +123456789TESTBANK12300001,20241201,1,1,988,1;2;3;4;5;test,999;999;977,1;2;3;4;5;test,988,1300,999;999;988;999;977,1;2;3;4;5;test,999,0,0,3,20201231,999;999;977;999;999,1;2;3;4;5;test,5,,0,0.01,999,test,0,,,,900,,999,999,988,01001020101,988,0,988,1234,988,988,-1,988;988;966,988,3,988;966,test,988;966,test,test,test,test,,test,988;966,test,988;966,test,test,test,test,,test,988;966,test,988;966,test,test,test,test,,test,988;966,test,988;966,test,test,test,test,,test \ No newline at end of file diff --git a/tests/data/all_logic_warnings.csv b/tests/data/all_logic_warnings.csv new file mode 100644 index 00000000..ab6732c7 --- /dev/null +++ b/tests/data/all_logic_warnings.csv @@ -0,0 +1,9 @@ +uid,app_date,app_method,app_recipient,ct_credit_product,ct_credit_product_ff,ct_guarantee,ct_guarantee_ff,ct_loan_term_flag,ct_loan_term,credit_purpose,credit_purpose_ff,amount_applied_for_flag,amount_applied_for,amount_approved,action_taken,action_taken_date,denial_reasons,denial_reasons_ff,pricing_interest_rate_type,pricing_init_rate_period,pricing_fixed_rate,pricing_adj_margin,pricing_adj_index_name,pricing_adj_index_name_ff,pricing_adj_index_value,pricing_origination_charges,pricing_broker_fees,pricing_initial_charges,pricing_mca_addcost_flag,pricing_mca_addcost,pricing_prepenalty_allowed,pricing_prepenalty_exists,census_tract_adr_type,census_tract_number,gross_annual_revenue_flag,gross_annual_revenue,naics_code_flag,naics_code,number_of_workers,time_in_business_type,time_in_business,business_ownership_status,num_principal_owners_flag,num_principal_owners,po_1_ethnicity,po_1_ethnicity_ff,po_1_race,po_1_race_anai_ff,po_1_race_asian_ff,po_1_race_baa_ff,po_1_race_pi_ff,po_1_gender_flag,po_1_gender_ff,po_2_ethnicity,po_2_ethnicity_ff,po_2_race,po_2_race_anai_ff,po_2_race_asian_ff,po_2_race_baa_ff,po_2_race_pi_ff,po_2_gender_flag,po_2_gender_ff,po_3_ethnicity,po_3_ethnicity_ff,po_3_race,po_3_race_anai_ff,po_3_race_asian_ff,po_3_race_baa_ff,po_3_race_pi_ff,po_3_gender_flag,po_3_gender_ff,po_4_ethnicity,po_4_ethnicity_ff,po_4_race,po_4_race_anai_ff,po_4_race_asian_ff,po_4_race_baa_ff,po_4_race_pi_ff,po_4_gender_flag,po_4_gender_ff +123456789TESTBANK12300001,20201201,1,1,1,,999;999;977,1;2;3;4;5,900,1203,999;999;977,1;2;3,900,1,,4,20241031,999,,999,,,,999,,,,,,999,,999,999,1,01001020101,988,,900,123,988,1,1,988;988;966,900,0,1,,1,,,,,,,,,1,,,,,,,,,1,,,,,,,,,1,,,,,, +123456789TESTBANK12300002,20201201,1,1,1,,999;999;977,1;2;3;4;5,900,1203,999;999;977,1;2;3,900,1,,4,20241031,999,,999,,,,999,,,,,,999,,999,999,1,01001020101,988,,900,123,988,1,1,988;988;966,900,4,1;1;988,,1;1;988,,,,,,,1;1;988,,1;1;988,,,,,,,1;1;988,,1;1;988,,,,,,,1;1;988,,1;1;988,,,,,, +123456789TESTBANK12300003,20201201,1,1,1,,999;999;977,1;2;3;4;5,900,1203,999;999;977,1;2;3,900,1,,4,20241031,999,,999,,,,999,,,,,,999,,999,999,1,01001020101,988,,900,123,988,1,1,988;988;966,900,3,1;1;988,,1;1;988,,,,,,,1;1;988,,1;1;988,,,,,,,1;1;988,,1;1;988,,,,,,,1;1;988,,1;1;988,,,,,, +123456789TESTBANK12300004,20201201,1,1,1,,999;999;977,1;2;3;4;5,900,1203,999;999;977,1;2;3,900,1,,4,20241031,999,,999,,,,999,,,,,,999,,999,999,1,01001020101,988,,900,123,988,1,1,988;988;966,900,2,1;1;988,,1;1;988,,,,,,,1;1;988,,1;1;988,,,,,,,1;1;988,,1;1;988,,,,,,,1;1;988,,1;1;988,,,,,, +999139389TESTBANK12300005,20201201,1,1,1,,999;999;977,1;2;3;4;5,900,1203,999;999;977,1;2;3,900,1,,4,20241031,999,,999,,,,999,,,,,,999,,999,999,1,01001020101,988,,900,123,988,1,1,988;988;966,900,1,1;1;988,,1;1;988,,,,,,,1;1;988,,1;1;988,,,,,,,1;1;988,,1;1;988,,,,,,,1;1;988,,1;1;988,,,,,, +999139389TESTBANK12300006,20201201,1,1,1,,999;999;977,1;2;3;4;5,900,1203,999;999;977,1;2;3,900,1,,3,20241031,1;1;999;977,1;2;3,999,,,,999,,,,,,999,,999,999,1,01001020101,988,,900,123,988,1,1,988;988;966,900,1,1;1;988,,1;1;988,,,,,,,1;1;988,,1;1;988,,,,,,,1;1;988,,1;1;988,,,,,,,1;1;988,,1;1;988,,,,,, +999139389TESTBANK12300008,20201201,1,1,1,,999;999;977,1;2;3;4;5,900,1203,999;999;977,1;2;3,900,1,10,1,20241031,999,,6,1,0.01,,999,,,1,1,1,999,,1,1,1,01001020101,988,,900,123,988,1,1,988;988;966,900,1,1;1;988,,1;1;988,,,,,,,1;1;988,,1;1;988,,,,,,,1;1;988,,1;1;988,,,,,,,1;1;988,,1;1;988,,,,,, +999139389TESTBANK12300009,20201201,1,1,1,,999;999;977,1;2;3;4;5,900,1203,999;999;977,1;2;3,900,1,10,1,20241031,999,,5,1,,0.01,977,test,,1,1,1,999,,1,1,1,01001020101,988,,900,123,988,1,1,988;988;966,900,1,1;1;988,,1;1;988,,,,,,,1;1;988,,1;1;988,,,,,,,1;1;988,,1;1;988,,,,,,,1;1;988,,1;1;988,,,,,, \ No newline at end of file diff --git a/tests/data/all_syntax_errors.csv b/tests/data/all_syntax_errors.csv new file mode 100644 index 00000000..18d1694a --- /dev/null +++ b/tests/data/all_syntax_errors.csv @@ -0,0 +1,140 @@ +uid,app_date,app_method,app_recipient,ct_credit_product,ct_credit_product_ff,ct_guarantee,ct_guarantee_ff,ct_loan_term_flag,ct_loan_term,credit_purpose,credit_purpose_ff,amount_applied_for_flag,amount_applied_for,amount_approved,action_taken,action_taken_date,denial_reasons,denial_reasons_ff,pricing_interest_rate_type,pricing_init_rate_period,pricing_fixed_rate,pricing_adj_margin,pricing_adj_index_name,pricing_adj_index_name_ff,pricing_adj_index_value,pricing_origination_charges,pricing_broker_fees,pricing_initial_charges,pricing_mca_addcost_flag,pricing_mca_addcost,pricing_prepenalty_allowed,pricing_prepenalty_exists,census_tract_adr_type,census_tract_number,gross_annual_revenue_flag,gross_annual_revenue,naics_code_flag,naics_code,number_of_workers,time_in_business_type,time_in_business,business_ownership_status,num_principal_owners_flag,num_principal_owners,po_1_ethnicity,po_1_ethnicity_ff,po_1_race,po_1_race_anai_ff,po_1_race_asian_ff,po_1_race_baa_ff,po_1_race_pi_ff,po_1_gender_flag,po_1_gender_ff,po_2_ethnicity,po_2_ethnicity_ff,po_2_race,po_2_race_anai_ff,po_2_race_asian_ff,po_2_race_baa_ff,po_2_race_pi_ff,po_2_gender_flag,po_2_gender_ff,po_3_ethnicity,po_3_ethnicity_ff,po_3_race,po_3_race_anai_ff,po_3_race_asian_ff,po_3_race_baa_ff,po_3_race_pi_ff,po_3_gender_flag,po_3_gender_ff,po_4_ethnicity,po_4_ethnicity_ff,po_4_race,po_4_race_anai_ff,po_4_race_asian_ff,po_4_race_baa_ff,po_4_race_pi_ff,po_4_gender_flag,po_4_gender_ff +,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +4234000O91BZ220CHARS,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +4234000O91BZ2SUPERCALIFRAGILISTICEXPIALI46CHARS,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +4234000O91BZ2K36OTHERCHAR!!V67280Y,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +4234000lowercase67280letters,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +4234000O91BZ2K36V67280Y,,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +4234000O91BZ2K36V67280Z,12012024,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +4234000O91BZ2K36V67280Z1,20230229,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +956600PI6JSOV7ZZ3106TIW,20241201,,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +956600PI6JSOV7ZZ3106TIW1,20241201,5,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +2386009EECAG20ZLMV23XI5HSNMQCEYP,20241201,1,,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +2386009EECAG20ZLMV23XI5HSNMQCEYP1,20241201,1,3,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +6669004Q2DXWMELKL032HCW6EU9K,20241201,1,1,,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +6669004Q2DXWMELKL032HCW6EU9K1,20241201,1,1,999,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +121400XX2UX92GN0KC71AW3GMD8SG9AJV73O75,20241201,1,1,977,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +55190073SEH3UYA1XB917W7WTS6,20241201,1,1,988,,9001,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +669400C4GD7Q2LN72M85PCGMDQ5AF1GI6FR5EPZYSUB1,20241201,1,1,988,,977,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +022500XI3ZBZIE5DCB49BP1HNCTVRIUMK7BZ,20241201,1,1,988,,999,,,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +022500XI3ZBZIE5DCB49BP1HNCTVRIUMK7BZ1,20241201,1,1,988,,999,,1000,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +833600MLEY29NPXZ6Y503ST2LTBI68MUMNL17EBB4HBB,20241201,1,1,988,,999,,900,1.78,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +833600MLEY29NPXZ6Y503ST2LTBI68MUMNL17EBB4HBB1,20241201,1,1,988,,999,,900,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +739000LYJIZOKLRVXF584Z86FH,20241201,1,1,988,,999,,999,,1;2;9001,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +6987006ZF7XLRTK2XB767WTGH5QX4,20241201,1,1,988,,999,,999,,977,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +8131003OTZ6C2PDKHJ53KBIQAJAE1QC7W6C1BOJDS1,20241201,1,1,988,,999,,999,,999,,,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +8131003OTZ6C2PDKHJ53KBIQAJAE1QC7W6C1BOJDS2,20241201,1,1,988,,999,,999,,999,,1000,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +010100ICK2NVPQKI6841B639N3R,20241201,1,1,988,,999,,999,,999,,900,nonNumericValue,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +010100ICK2NVPQKI6841B639N3R1,20241201,1,1,988,,999,,999,,999,,900,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +760900JJCAT3MCVKV06438QG,20241201,1,1,988,,999,,999,,999,,999,,nonNumericValue,1,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +760900JJCAT3MCVKV06438QG1,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +923500LZ57G0IK4ETD64HDFTU1U,20241201,1,1,988,,999,,999,,999,,999,,,,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +923500LZ57G0IK4ETD64HDFTU1U1,20241201,1,1,988,,999,,999,,999,,999,,,6,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +438600FLG46UDVULW406H7TGK,20241201,1,1,988,,999,,999,,999,,999,,,5,12312024,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +438600FLG46UDVULW406H7TGK1,20241201,1,1,988,,999,,999,,999,,999,,,5,,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +3587006IEWU1OXMYCO17VXEXYG1HVXTRYBCX6A6YXD01R,20241201,1,1,988,,999,,999,,999,,999,,,3,20241231,9001,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +0279000815BBNQHM7N23JA4KGL,20241201,1,1,988,,999,,999,,999,,999,,,3,20241231,977,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +5666002FKB3UAMTDTM62F6,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +5666002FKB3UAMTDTM62F7,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,977,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +5666002FKB3UAMTDTM62F8,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,nonNumericValue,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +947800HCW5V55ZENLP42GL86,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,3,nonNumericValue,,,999,,2,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +947800HCW5V55ZENLP42GL87,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,3,3.5,,,999,,2,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +519200EYK7H2VZ93DO21126B1WPFKI348QJZM4ZO8Z,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,2,,nonNumericValue,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +208800QOK8SUWHEU5C755CP8X0OQ8MAUWJHDQC4SC5UZ,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,999,,,,,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +208800QOK8SUWHEU5C755CP8X0OQ8MAUWJHDQC4SC5UA,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,999,,,,13,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +208800QOK8SUWHEU5C755CP8X0OQ8MAUWJHDQC4SC5UB,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,999,,,,nonNumericValue,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +144300EH823V0RA87A48KQ2565H26X5K6WSGGPUG,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,999,,,,977,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +564600TPCC0WFV22CY34VATRK5OXYIZ63GCN0QB,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,999,,,,1,,nonNumericValue,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +481900WFUKMHDSY6OI11EOX9OTYIIU9,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,999,,,,999,,,nonNumericValue,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +411600W24ZABFWYVR029T1JQTJW5S,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,999,,,,999,,,,nonNumericValue,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +9228000EBRPNTUQ3SG57FPB,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,999,,,,999,,,,,nonNumericValue,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +4548000TKXGRV839KU65F58PXAWP9E,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +4548000TKXGRV839KU65F58PXAWP9F,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,0,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +4548000TKXGRV839KU65F58PXAWP9G,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,nonNumericValue,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +4617007WISXGJOFB1Y03MWVAR51OL,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,900,nonNumericValue,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +995500RE1N72ZJMSAH7866I98J1W2D76J,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,999,,,,999,,,,,,999,,,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +995500RE1N72ZJMSAH7866I98J1W2D76K,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,999,,,,999,,,,,,999,,3,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +995500RE1N72ZJMSAH7866I98J1W2D76L,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,999,,,,999,,,,,,999,,nonNumericValue,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +524900222U3R5SQMYW237GS42YO489SDRTVMQ6,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,999,,,,999,,,,,,999,,999,,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +524900222U3R5SQMYW237GS42YO489SDRTVMQ8,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,999,,,,999,,,,,,999,,999,nonNumericValue,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +524900222U3R5SQMYW237GS42YO489SDRTVMQ7,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,999,,,,999,,,,,,999,,999,3,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +1619008FAF6ESQ96KD22DHAWGTCO7YODP1A4X,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +1619008FAF6ESQ96KD22DHAWGTCO7YODP1A4Y,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,4,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +1619008FAF6ESQ96KD22DHAWGTCO7YODP1A4Z,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,nonNumericValue,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +025200MYCASCDDLSR022RQGPYJG75Z8RQFU,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,1,1234567890,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +025200MYCASCDDLSR022RQGPYJG75Z8RQFV,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,1,nonNumericValue,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +756000U6MPCSWDC39C3607M2KLGM53,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +756000U6MPCSWDC39C3607M2KLGM54,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,0,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +756000U6MPCSWDC39C3607M2KLGM55,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,nonNumericValue,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +0257001EM8QF36D1NJ89R8,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,900,nonNumericValue,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +983600MVQMOOWTZ69R0223670KOWF40NTV8VN,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +983600MVQMOOWTZ69R0223670KOWF40NTV8VM,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,0,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +983600MVQMOOWTZ69R0223670KOWF40NTV8VO,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,nonNumericValue,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +484200VUF1I2NJNY9D943S9V2533N3J4NSL6BSRZ5342,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,900,notDigits,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +1614006AUE2H31FJCV329IPSH,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +1614006AUE2H31FJCV329IPSI,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,10,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +1614006AUE2H31FJCV329IPSJ,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,nonNumericValue,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +784900N2TYCSGSHFRM17MT7EJ4503Z,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +784900N2TYCSGSHFRM17MT7EJ4503A,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,4,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +784900N2TYCSGSHFRM17MT7EJ4503B,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,nonNumericValue,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +966700OSTIM259Q6CP94X1VKYM2ZWSJ,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,must be blank,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +966700OSTIM259Q6CP94X1VKYM2ZWSJ,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,1.5,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +750400GOSOP76WU55334EJ2QRQ921TIQVKKF2D4HXQQ,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,1;2; 9001,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +750400GOSOP76WU55334EJ2QRQ921TIQVKKF2D4HXQR,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,nonNumericValue,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +856800BWNYZD262GLO73QEQTDL9FEHGRGE189DQ,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +856800BWNYZD262GLO73QEQTDL9FEHGRGE189DR,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +856800BWNYZD262GLO73QEQTDL9FEHGRGE189DS,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,nonNumericValue,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +458700OOQCHMD0HUVA16PMC2ZLA6N5NAT58YOL66AQ2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,9001,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +458700OOQCHMD0HUVA16PMC2ZLA6N5NAT58YOL66AQ3,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,nonNumericValue,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +953200W2IZVPSVLRGN24GARG,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,9001;1,,2,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, +953200W2IZVPSVLRGN24GARH,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,nonNumericValue,,2,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, +7078000SDH2MQNT0V717R4H7E7B7DHM04Z,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,977;1,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,2,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, +00700040O75RW3NKLR04T13PT853UH3WV0JZTNTX8ZJ9X,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,9001;1,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, +00700040O75RW3NKLR04T13PT853UH3WV0JZTNTX8ZJ9Y,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,nonNumericValue,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, +368400H1B7FE9H82JJ003SHINJFYB1M,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,971;1,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, +296400UYONUQI7CMF5222BXL5SKIOTA,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,972;1,,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, +36440045G09V5RRH5H513,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,973;1,,,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, +025100XQUUZSD2ONZH69E8HEGBOPD8WOQ3L6N6,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,974;1,,,,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, +629800L4RJQQG75YSW97IFRTSDX6VBVNTBZMZMNFLR9NT,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,2,,,,,9001,,,,,,,,,,,,,,,,,,,,,,,,,,,, +629800L4RJQQG75YSW97IFRTSDX6VBVNTBZMZMNFLR9NU,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,2,,,,,nonNumericValue,,,,,,,,,,,,,,,,,,,,,,,,,,,, +420700T5JZSX56SFOU40S9ZOPJIW834OE18MBP8U,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,2,,,,,1,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,,,,,,,,,,,,,,,,,,,,,,,,,,, +774200J5EAXZO2IWU109S5MV2VEZ472HY1TKQHB,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,9001;1,,2,,,,,988,,,,,,,,,,,,,,,,,,, +774200J5EAXZO2IWU109S5MV2VEZ472HY1TKQHC,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,nonNumericValue,,2,,,,,988,,,,,,,,,,,,,,,,,,, +303800A197B9MNHQHT62EGGOXXXNQ8D6X04QKXD,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,977;1,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,2,,,,,988,,,,,,,,,,,,,,,,,,, +8322000N96DIPAMONE94DZKW3KB0E1HY,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,9001;1,,,,,988,,,,,,,,,,,,,,,,,,, +8322000N96DIPAMONE94DZKW3KB0E1HZ,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,nonNumericValue,,,,,988,,,,,,,,,,,,,,,,,,, +953400ZC66FNY57FVD98JD56PBGGWW8OGHP,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,971;1,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,,,,988,,,,,,,,,,,,,,,,,,, +0349008MKL3X8WPMYS93OKKM,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,972;1,,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,,,988,,,,,,,,,,,,,,,,,,, +414500Y8SBL2RVDDOY94Z,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,973;1,,,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,,988,,,,,,,,,,,,,,,,,,, +122800M4K2CH3FL25Y97KTK4H9UAJSFLE7NVO,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,974;1,,,,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,988,,,,,,,,,,,,,,,,,,, +9842007R4HCY0XOQ5L00TU0JH7EF1H2AQUYYVXXR,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,2,,,,,9001,,,,,,,,,,,,,,,,,,, +9842007R4HCY0XOQ5L00TU0JH7EF1H2AQUYYVXXS,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,2,,,,,nonNumericValue,,,,,,,,,,,,,,,,,,, +85200017SN17F1A5UL14SF57XEHW1N0IOIYM2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,2,,,,,1,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,,,,,,,,,,,,,,,,,, +362500YIIQ8UVRB27812IS,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,9001;1,,2,,,,,988,,,,,,,,,, +362500YIIQ8UVRB27812IT,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,nonNumericValue,,2,,,,,988,,,,,,,,,, +420100W1AXMLNU16B456G4Q2AEXZSNHV,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,977;1,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,2,,,,,988,,,,,,,,,, +105100TLHSNS7JJ18418PHX6ANI,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,9001;1,,,,,988,,,,,,,,,, +105100TLHSNS7JJ18418PHX6ANJ,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,nonNumericValue,,,,,988,,,,,,,,,, +354600GJUWVBT7YOLF75YIWERLW2JG569JM,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,971;1,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,,,,988,,,,,,,,,, +413800VHGM6XJMYTRA38H5KB4YEKXZO,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,972;1,,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,,,988,,,,,,,,,, +333000WW4NNIB7XTHI72MSHK1AJ97WODC4N,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,973;1,,,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,,988,,,,,,,,,, +624400TM8ZN8K73YMT88IAVE1I3S37,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,974;1,,,,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,988,,,,,,,,,, +71090039E5LSNPB3H174W1YEUOC52R6KF792O6,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,9001,,,,,,,,,, +71090039E5LSNPB3H174W1YEUOC52R6KF792O7,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,nonNumericValue,,,,,,,,,, +185800T9RBUG5XEIQE275C212JFWUY44WH7,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,1,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,,,,,,,,, +279200NW36SQ744VS396D,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,9001;1,,2,,,,,988, +279200NW36SQ744VS396E,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,nonNumericValue,,2,,,,,988, +100300CBL92FBZU4FA80U,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,977;1,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,2,,,,,988, +248800WFV15GUR5YQB72TZ3IO1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,9001;1,,,,,988, +248800WFV15GUR5YQB72TZ3IO2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,nonNumericValue,,,,,988, +884200OCJ8635YVBT146RI7WQCSMFR5ICPTOJA3BOLI,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,971;1,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,,,,988, +210600RWMYD2WHOXJ330UOWYW0ZTIY,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,972;1,,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,,,988, +624400V8RSEQNAR5W804KQPXCQ9MMGXD8,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,973;1,,,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,,988, +644600XBVWLW5SRSW113BT,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,974;1,,,,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,988, +1381007XS24PDR3BW6547JM8,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,9001, +1381007XS24PDR3BW6547JM9,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,nonNumericValue, +504500FRY0L3IER8X3901SSIZFKI1OAVF1S45LSR1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,1,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX +000TESTFIUIDDONOTUSEXBXVID13XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +000TESTFIUIDDONOTUSEXBXVID13XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +000TESTFIUIDDONOTUSEXBXVID13XTC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,abc,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, \ No newline at end of file diff --git a/tests/data/sbl-validations-fail.csv b/tests/data/sbl-validations-fail.csv deleted file mode 100644 index 0e08ca91..00000000 --- a/tests/data/sbl-validations-fail.csv +++ /dev/null @@ -1,370 +0,0 @@ -uid,app_date,app_method,app_recipient,ct_credit_product,ct_credit_product_ff,ct_guarantee,ct_guarantee_ff,ct_loan_term_flag,ct_loan_term,credit_purpose,credit_purpose_ff,amount_applied_for_flag,amount_applied_for,amount_approved,action_taken,action_taken_date,denial_reasons,denial_reasons_ff,pricing_interest_rate_type,pricing_init_rate_period,pricing_fixed_rate,pricing_adj_margin,pricing_adj_index_name,pricing_adj_index_name_ff,pricing_adj_index_value,pricing_origination_charges,pricing_broker_fees,pricing_initial_charges,pricing_mca_addcost_flag,pricing_mca_addcost,pricing_prepenalty_allowed,pricing_prepenalty_exists,census_tract_adr_type,census_tract_number,gross_annual_revenue_flag,gross_annual_revenue,naics_code_flag,naics_code,number_of_workers,time_in_business_type,time_in_business,business_ownership_status,num_principal_owners_flag,num_principal_owners,po_1_ethnicity,po_1_ethnicity_ff,po_1_race,po_1_race_anai_ff,po_1_race_asian_ff,po_1_race_baa_ff,po_1_race_pi_ff,po_1_gender_flag,po_1_gender_ff,po_2_ethnicity,po_2_ethnicity_ff,po_2_race,po_2_race_anai_ff,po_2_race_asian_ff,po_2_race_baa_ff,po_2_race_pi_ff,po_2_gender_flag,po_2_gender_ff,po_3_ethnicity,po_3_ethnicity_ff,po_3_race,po_3_race_anai_ff,po_3_race_asian_ff,po_3_race_baa_ff,po_3_race_pi_ff,po_3_gender_flag,po_3_gender_ff,po_4_ethnicity,po_4_ethnicity_ff,po_4_race,po_4_race_anai_ff,po_4_race_asian_ff,po_4_race_baa_ff,po_4_race_pi_ff,po_4_gender_flag,po_4_gender_ff -,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -BXUIDXVID11XTC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -BXUIDXVID11XTC31234567890123456789012345678901,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -BXUIDXVID12XTC1abcdef,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID13XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID13XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID14XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID21XTC1,,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID21XTC2,12012024,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID31XTC1,20241201,,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID31XTC2,20241201,9001,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID41XTC1,20241201,1,,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID41XTC2,20241201,1,9001,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID51XTC1,20241201,1,1,,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID51XTC2,20241201,1,1,9001,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID61XTC1,20241201,1,1,977,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID62XTC1,20241201,1,1,988,must be blank,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID62XTC2,20241201,1,1,977,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID71XTC1,20241201,1,1,988,,9001,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID72XTC1,20241201,1,1,988,,,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID72XTC2,20241201,1,1,988,,1;2;3;4;5;6,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID73XTC1,20241201,1,1,988,,999;1,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID74XTC1,20241201,1,1,988,,1;2;2;3,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID81XTC1,20241201,1,1,988,,977,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID82XTC1,20241201,1,1,988,,999,"should, be, blank",999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID82XTC2,20241201,1,1,988,,977,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID83XTC1,20241201,1,1,988,,1;2;3;977,entry1;entry2;entry3,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID83XTC2,20241201,1,1,988,,1;2;3;4;5;977,entry1,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID91XTC1,20241201,1,1,988,,999,,,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID91XTC2,20241201,1,1,988,,999,,9001,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID92XTC1,20241201,1,1,1,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID92XTC2,20241201,1,1,2,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID92XTC3,20241201,1,1,988,,999,,1,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID101XTC1,20241201,1,1,988,,999,,900,1.78,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID102XTC1,20241201,1,1,988,,999,,900,0,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID103XTC1,20241201,1,1,988,,999,,999,must be blank,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID103XTC2,20241201,1,1,988,,999,,900,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID104XTC1,20241201,1,1,988,,999,,900,1201,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID111XTC1,20241201,1,1,988,,999,,999,,1;2;9001,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID112XTC1,20241201,1,1,988,,999,,999,,,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID112XTC2,20241201,1,1,988,,999,,999,,1;2;3;4,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID113XTC1,20241201,1,1,988,,999,,999,,2;3;988,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID113XTC2,20241201,1,1,988,,999,,999,,2;3;999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID114XTC1,20241201,1,1,988,,999,,999,,1;2;2;4,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID121XTC1,20241201,1,1,988,,999,,999,,977,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID122XTC1,20241201,1,1,988,,999,,999,,999,must be blank,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID122XTC2,20241201,1,1,988,,999,,999,,977,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID123XTC1,20241201,1,1,988,,999,,999,,1;977,entry1;entry2;entry3,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID123XTC2,20241201,1,1,988,,999,,999,,1;2;3;977,entry1,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID131XTC1,20241201,1,1,988,,999,,999,,999,,,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID131XTC2,20241201,1,1,988,,999,,999,,999,,9001,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID141XTC1,20241201,1,1,988,,999,,999,,999,,900,nonNumericValue,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID142XTC1,20241201,1,1,988,,999,,999,,999,,900,0,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID143XTC1,20241201,1,1,988,,999,,999,,999,,900,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID143XTC2,20241201,1,1,988,,999,,999,,999,,988,must be blank,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID151XTC1,20241201,1,1,988,,999,,999,,999,,999,,nonNumericValue,1,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID152XTC1,20241201,1,1,988,,999,,999,,999,,999,,0,1,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID153XTC1,20241201,1,1,988,,999,,999,,999,,999,,9559555,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID153XTC2,20241201,1,1,988,,999,,999,,999,,999,,44444,4,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID153XTC3,20241201,1,1,988,,999,,999,,999,,999,,33333,3,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID153XTC4,20241201,1,1,988,,999,,999,,999,,999,,,2,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID153XTC5,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID161XTC1,20241201,1,1,988,,999,,999,,999,,999,,,,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID161XTC2,20241201,1,1,988,,999,,999,,999,,999,,,9001,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID171XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,12312024,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID172XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20240630,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID172XTC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20250101,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID173XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241130,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID174XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20261202,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID181XTC1,20241201,1,1,988,,999,,999,,999,,999,,,3,20241231,9001,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID182XTC1,20241201,1,1,988,,999,,999,,999,,999,,,3,20241231,,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID182XTC2,20241201,1,1,988,,999,,999,,999,,999,,,3,20241231,1;2;3;4;5,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID183XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,1,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID183XTC2,20241201,1,1,988,,999,,999,,999,,999,,,4,20241231,1,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID183XTC3,20241201,1,1,988,,999,,999,,999,,999,,,2,20241231,1,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID183XTC4,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,1,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID183XTC5,20241201,1,1,988,,999,,999,,999,,999,,,3,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID184XTC1,20241201,1,1,988,,999,,999,,999,,999,,,3,20241231,999;1; 2,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID185XTC1,20241201,1,1,988,,999,,999,,999,,999,,,3,20241231,1;2;2;3,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID191XTC1,20241201,1,1,988,,999,,999,,999,,999,,,3,20241231,977,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID192XTC1,20241201,1,1,988,,999,,999,,999,,999,,,3,20241231,1,"should, be, blank",999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID192XTC2,20241201,1,1,988,,999,,999,,999,,999,,,3,20241231,977,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID193XTC1,20241201,1,1,988,,999,,999,,999,,999,,,3,20241231,1;2;977,entry1;entry2;entry3,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID193XTC2,20241201,1,1,988,,999,,999,,999,,999,,,3,20241231,1;2;3;4;977,entry1,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID201XTC1,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID201XTC2,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,9001,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID202XTC1,20241201,1,1,988,,999,,999,,999,,999,,,3,20241231,999,,900,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID202XTC2,20241201,1,1,988,,999,,999,,999,,999,,,3,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID202XTC3,20241201,1,1,988,,999,,999,,999,,999,,,3,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID202XTC4,20241201,1,1,988,,999,,999,,999,,999,,,3,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID202XTC5,20241201,1,1,988,,999,,999,,999,,999,,,3,20241231,999,,999,,,,999,,,0,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID202XTC6,20241201,1,1,988,,999,,999,,999,,999,,,3,20241231,999,,999,,,,999,,,,0,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID202XTC7,20241201,1,1,988,,999,,999,,999,,999,,,3,20241231,999,,999,,,,999,,,,,0,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID202XTC8,20241201,1,1,988,,999,,999,,999,,999,,,4,20241231,999,,900,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID202XTC9,20241201,1,1,988,,999,,999,,999,,999,,,4,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID202XTC10,20241201,1,1,988,,999,,999,,999,,999,,,4,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID202XTC11,20241201,1,1,988,,999,,999,,999,,999,,,4,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID202XTC12,20241201,1,1,988,,999,,999,,999,,999,,,4,20241231,999,,999,,,,999,,,0,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID202XTC13,20241201,1,1,988,,999,,999,,999,,999,,,4,20241231,999,,999,,,,999,,,,0,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID202XTC14,20241201,1,1,988,,999,,999,,999,,999,,,4,20241231,999,,999,,,,999,,,,,0,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID202XTC15,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,900,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID202XTC16,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID202XTC17,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID202XTC18,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID202XTC19,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,0,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID202XTC20,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,0,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID202XTC21,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,0,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID203XTC1,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,999,,,,999,,,,,,999,,999,1,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID203XTC2,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,999,,,,999,,,,,,999,,1,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID203XTC3,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,999,,,,999,,,,0,0,999,,1,1,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID203XTC4,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,999,,,,999,,,0,,0,999,,1,1,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID203XTC5,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,999,,,,999,,,0,0,,999,,1,1,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID203XTC6,20241201,1,1,988,,999,,999,,999,,999,,,2,20241231,999,,999,,,,999,,,,,,999,,999,1,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID203XTC7,20241201,1,1,988,,999,,999,,999,,999,,,2,20241231,999,,999,,,,999,,,,,,999,,1,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID203XTC8,20241201,1,1,988,,999,,999,,999,,999,,,2,20241231,999,,999,,,,999,,,,0,0,999,,1,1,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID203XTC9,20241201,1,1,988,,999,,999,,999,,999,,,2,20241231,999,,999,,,,999,,,0,,0,999,,1,1,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID203XTC10,20241201,1,1,988,,999,,999,,999,,999,,,2,20241231,999,,999,,,,999,,,0,0,,999,,1,1,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID211XTC1,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,3,nonNumericValue,,,999,,2,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID212XTC1,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,3,0,,,999,,2,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID213XTC1,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,1,1,,,999,,2,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID213XTC2,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,2,1,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID213XTC3,20241201,1,1,988,,999,,999,,999,,999,,,3,20241231,999,,999,1,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID213XTC4,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,3,,,,999,,2,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID213XTC5,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,4,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID213XTC6,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,5,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID213XTC7,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,6,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID221XTC1,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,2,,nonNumericValue,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID222XTC1,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,1,,1,,999,,2,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID222XTC2,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,3,,1,,999,,2,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID222XTC3,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,5,,1,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID222XTC4,20241201,1,1,988,,999,,999,,999,,999,,,3,20241231,999,,999,,1,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID222XTC5,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,2,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID222XTC6,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,4,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID222XTC7,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,6,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID223XTC1,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,2,,0.1,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID231XTC1,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,1,,,,999,,2,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID232XTC1,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,2,,,1,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID232XTC2,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,4,,,1,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID232XTC3,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,6,,,1,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID232XTC4,20241201,1,1,988,,999,,999,,999,,999,,,3,20241231,999,,999,,,1,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID232XTC5,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,1,,,,999,,2,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID232XTC6,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,3,,,,999,,2,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID232XTC7,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,5,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID233XTC1,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID241XTC1,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,999,,,,,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID241XTC2,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,999,,,,9001,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID242XTC1,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,2,,,,1,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID242XTC2,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,4,,,,1,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID242XTC3,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,6,,,,1,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID242XTC4,20241201,1,1,988,,999,,999,,999,,999,,,3,20241231,999,,999,,,,1,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID242XTC5,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,1,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID242XTC6,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,3,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID242XTC7,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,5,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID251XTC1,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,999,,,,977,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID252XTC1,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,999,,,,1,must be blank,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID252XTC2,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,999,,,,977,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID261XTC1,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,999,,,,1,,nonNumericValue,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID262XTC1,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,2,,,,1,,2,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID262XTC2,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,4,,,,1,,2,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID262XTC3,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,5,,,,1,,2,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID262XTC4,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,6,,,,1,,2,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID262XTC5,20241201,1,1,988,,999,,999,,999,,999,,,3,20241231,999,,999,,,,1,,2,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID262XTC6,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,1,,,,1,,2,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID262XTC7,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,3,,,,1,,2,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID271XTC1,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,999,,,,999,,,nonNumericValue,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID281XTC1,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,999,,,,999,,,,nonNumericValue,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID291XTC1,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,999,,,,999,,,,,nonNumericValue,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID301XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID301XTC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,99009001,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID302XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,900,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID311XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,900,nonNumericValue,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID312XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,must be blank,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID312XTC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,900,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID321XTC1,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,999,,,,999,,,,,,999,,,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID321XTC2,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,999,,,,999,,,,,,999,,9001,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID331XTC1,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,999,,,,999,,,,,,999,,999,,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID331XTC2,20241201,1,1,988,,999,,999,,999,,999,,,1,20241231,999,,999,,,,999,,,,,,999,,999,9001,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID341XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID341XTC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,9001,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID351XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,1,1.23457E+11,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID351XTC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,1,1234567890,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID352XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,must be blank,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID352XTC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,1,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID352XTC3,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,2,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID352XTC4,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,3,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID353XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,1,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID361XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID361XTC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,99009001,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID371XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,900,nonNumericValue,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID372XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,must be blank,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID372XTC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,900,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID381XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID381XTC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,9001,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID391XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,900,9001,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID391XTC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,900,90,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID392XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,900,notDigits,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID393XTC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,900,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID394XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,900,666,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID401XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID401XTC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,9001,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID411XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID411XTC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,9001,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID421XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,1,1.1,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID422XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,1,-1.1,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID423XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,must be blank,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID423XTC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,1,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID431XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,1;2; 9001,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID432XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID433XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,1;2;2,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID434XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,2;3;988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID441XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID441XTC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,9001,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID451XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,9001,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID452XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,must be blank,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID452XTC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID461XPO1TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,9001;1,,2,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID461XPO2TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,9001;1,,2,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID461XPO3TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,9001;1,,2,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID461XPO4TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,9001;1,,2,,,,,988, -000TESTFIUIDDONOTUSEXBXVID462XPO1TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,1;11;11,,2,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID462XPO2TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,1;11;11,,2,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID462XPO3TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,1;11;11,,2,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID462XPO4TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,1;11;11,,2,,,,,988, -000TESTFIUIDDONOTUSEXBXVID463XPO1TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988;1,,2,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID463XPO2TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988;1,,2,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID463XPO3TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988;1,,2,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID463XPO4TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988;1,,2,,,,,988, -000TESTFIUIDDONOTUSEXBXVID471XPO1TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,977;1,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,2,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID471XPO2TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,977;1,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,2,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID471XPO3TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,977;1,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,2,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID471XPO4TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,977;1,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,2,,,,,988, -000TESTFIUIDDONOTUSEXBXVID472XPO1TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,1;11,must be blank,2,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID472XPO1TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,977;1,,2,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID472XPO2TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,1;11,must be blank,2,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID472XPO2TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,977;1,,2,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID472XPO3TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,1;11,must be blank,2,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID472XPO3TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,977;1,,2,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID472XPO4TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,1;11,must be blank,2,,,,,988, -000TESTFIUIDDONOTUSEXBXVID472XPO4TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,977;1,,2,,,,,988, -000TESTFIUIDDONOTUSEXBXVID481XPO1TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,9001;1,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID481XPO2TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,9001;1,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID481XPO3TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,9001;1,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID481XPO4TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,9001;1,,,,,988, -000TESTFIUIDDONOTUSEXBXVID482XPO1TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,1;2;2,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID482XPO2TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,1;2;2,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID482XPO3TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,1;2;2,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID482XPO4TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,1;2;2,,,,,988, -000TESTFIUIDDONOTUSEXBXVID483XPO1TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,988;1,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID483XPO2TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,988;1,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID483XPO3TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,988;1,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID483XPO4TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,988;1,,,,,988, -000TESTFIUIDDONOTUSEXBXVID491XPO1TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,971;1,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID491XPO2TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,971;1,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID491XPO3TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,971;1,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID491XPO4TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,971;1,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,,,,988, -000TESTFIUIDDONOTUSEXBXVID492XPO1TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,1;2,must be blank,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID492XPO1TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,971;1,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID492XPO2TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,1;2,must be blank,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID492XPO2TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,971;1,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID492XPO3TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,1;2,must be blank,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID492XPO3TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,971;1,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID492XPO4TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,1;2,must be blank,,,,988, -000TESTFIUIDDONOTUSEXBXVID492XPO4TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,971;1,,,,,988, -000TESTFIUIDDONOTUSEXBXVID501XPO1TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,972;1,,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID501XPO2TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,972;1,,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID501XPO3TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,972;1,,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID501XPO4TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,972;1,,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,,,988, -000TESTFIUIDDONOTUSEXBXVID502XPO1TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,1;2,,must be blank,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID502XPO1TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,972;1,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID502XPO2TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,1;2,,must be blank,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID502XPO2TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,972;1,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID502XPO3TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,1;2,,must be blank,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID502XPO3TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,972;1,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID502XPO4TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,1;2,,must be blank,,,988, -000TESTFIUIDDONOTUSEXBXVID502XPO4TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,972;1,,,,,988, -000TESTFIUIDDONOTUSEXBXVID511XPO1TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,973;1,,,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID511XPO2TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,973;1,,,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID511XPO3TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,973;1,,,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID511XPO4TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,973;1,,,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,,988, -000TESTFIUIDDONOTUSEXBXVID512XPO1TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,1;2,,,must be blank,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID512XPO1TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,973;1,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID512XPO2TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,1;2,,,must be blank,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID512XPO2TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,973;1,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID512XPO3TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,1;2,,,must be blank,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID512XPO3TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,973;1,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID512XPO4TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,1;2,,,must be blank,,988, -000TESTFIUIDDONOTUSEXBXVID512XPO4TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,973;1,,,,,988, -000TESTFIUIDDONOTUSEXBXVID521XPO1TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,974;1,,,,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID521XPO2TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,974;1,,,,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID521XPO3TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,974;1,,,,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID521XPO4TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,974;1,,,,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,988, -000TESTFIUIDDONOTUSEXBXVID522XPO1TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,1;2,,,,must be blank,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID522XPO1TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,974;1,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID522XPO2TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,1;2,,,,must be blank,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID522XPO2TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,974;1,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID522XPO3TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,1;2,,,,must be blank,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID522XPO3TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,974;1,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID522XPO4TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,1;2,,,,must be blank,988, -000TESTFIUIDDONOTUSEXBXVID522XPO4TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,974;1,,,,,988, -000TESTFIUIDDONOTUSEXBXVID531XPO1TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,2,,,,,9001,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID531XPO2TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,2,,,,,9001,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID531XPO3TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,9001,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID531XPO4TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,9001, -000TESTFIUIDDONOTUSEXBXVID541XPO1TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,2,,,,,1,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID541XPO2TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,2,,,,,1,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID541XPO3TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,1,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID541XPO4TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,1,123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890XXX -000TESTFIUIDDONOTUSEXBXVID542XPO1TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,2,,,,,988,must be blank,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID542XPO1TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,2,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID542XPO2TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,2,,,,,988,must be blank,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID542XPO2TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,2,,,,,1,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID542XPO3TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,must be blank,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID542XPO3TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,1,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID542XPO4TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,must be blank -000TESTFIUIDDONOTUSEXBXVID542XPO4TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,1, -000TESTFIUIDDONOTUSEXBXVIDPODEMO0XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,0,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVIDPODEMO0XTC10,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,988,,,,,,,, -000TESTFIUIDDONOTUSEXBXVIDPODEMO0XTC11,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,, -000TESTFIUIDDONOTUSEXBXVIDPODEMO0XTC12,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,988, -000TESTFIUIDDONOTUSEXBXVIDPODEMO0XTC13,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVIDPODEMO0XTC14,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVIDPODEMO0XTC15,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVIDPODEMO0XTC16,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVIDPODEMO0XTC17,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,2,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVIDPODEMO0XTC18,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVIDPODEMO0XTC19,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,988,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVIDPODEMO0XTC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,0,,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVIDPODEMO0XTC20,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVIDPODEMO0XTC21,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVIDPODEMO0XTC22,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,988,,,,,,,, -000TESTFIUIDDONOTUSEXBXVIDPODEMO0XTC23,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,, -000TESTFIUIDDONOTUSEXBXVIDPODEMO0XTC24,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,988, -000TESTFIUIDDONOTUSEXBXVIDPODEMO0XTC3,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,0,,,,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVIDPODEMO0XTC4,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,0,,,,,,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVIDPODEMO0XTC5,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,0,,,,,,,,,,,,2,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVIDPODEMO0XTC6,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,0,,,,,,,,,,,,,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVIDPODEMO0XTC7,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,0,,,,,,,,,,,,,,,,,,,988,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVIDPODEMO0XTC8,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,0,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVIDPODEMO0XTC9,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,0,,,,,,,,,,,,,,,,,,,,,,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVIDPODEMO1XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,2,,,,,988,,988,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVIDPODEMO1XTC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,2,,,,,988,,,,2,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVIDPODEMO1XTC3,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,2,,,,,988,,,,,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVIDPODEMO1XTC4,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,2,,,,,988,,,,,,,,,,,988,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVIDPODEMO1XTC5,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,2,,,,,988,,,,,,,,,,,,,2,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVIDPODEMO1XTC6,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,2,,,,,988,,,,,,,,,,,,,,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVIDPODEMO1XTC7,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,2,,,,,988,,,,,,,,,,,,,,,,,,,,988,,,,,,,, -000TESTFIUIDDONOTUSEXBXVIDPODEMO1XTC8,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,2,,,,,988,,,,,,,,,,,,,,,,,,,,,,2,,,,,, -000TESTFIUIDDONOTUSEXBXVIDPODEMO1XTC9,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,2,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,988, -000TESTFIUIDDONOTUSEXBXVIDPODEMO2XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,2,,,,,988,,988,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVIDPODEMO2XTC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,2,,,,,988,,,,2,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVIDPODEMO2XTC3,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,2,,,,,988,,,,,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVIDPODEMO2XTC4,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,2,,,,,988,,,,,,,,,,,988,,,,,,,, -000TESTFIUIDDONOTUSEXBXVIDPODEMO2XTC5,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,2,,,,,988,,,,,,,,,,,,,2,,,,,, -000TESTFIUIDDONOTUSEXBXVIDPODEMO2XTC6,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,2,,,,,988,,,,,,,,,,,,,,,,,,988, -000TESTFIUIDDONOTUSEXBXVIDPODEMO3XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,,,,,,, -000TESTFIUIDDONOTUSEXBXVIDPODEMO3XTC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,,,2,,,,,, -000TESTFIUIDDONOTUSEXBXVIDPODEMO3XTC3,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,,,,,,,,988, -000TESTFIUIDDONOTUSEXBXVIDPODEMO4XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988, -000TESTFIUIDDONOTUSEXBXVIDPODEMO4XTC10,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,,,2,,,,,988, -000TESTFIUIDDONOTUSEXBXVIDPODEMO4XTC11,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,,,,,,988, -000TESTFIUIDDONOTUSEXBXVIDPODEMO4XTC12,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,, -000TESTFIUIDDONOTUSEXBXVIDPODEMO4XTC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988, -000TESTFIUIDDONOTUSEXBXVIDPODEMO4XTC3,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,,,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988, -000TESTFIUIDDONOTUSEXBXVIDPODEMO4XTC4,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988, -000TESTFIUIDDONOTUSEXBXVIDPODEMO4XTC5,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988, -000TESTFIUIDDONOTUSEXBXVIDPODEMO4XTC6,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,,,988,,2,,,,,988,,988,,2,,,,,988, -000TESTFIUIDDONOTUSEXBXVIDPODEMO4XTC7,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,,,2,,,,,988,,988,,2,,,,,988, -000TESTFIUIDDONOTUSEXBXVIDPODEMO4XTC8,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,,,,,,988,,988,,2,,,,,988, -000TESTFIUIDDONOTUSEXBXVIDPODEMO4XTC9,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,,,988,,2,,,,,988, \ No newline at end of file diff --git a/tests/data/sbl-validations-pass.csv b/tests/data/sbl-validations-pass.csv deleted file mode 100644 index 38c4edb9..00000000 --- a/tests/data/sbl-validations-pass.csv +++ /dev/null @@ -1,528 +0,0 @@ -uid,app_date,app_method,app_recipient,ct_credit_product,ct_credit_product_ff,ct_guarantee,ct_guarantee_ff,ct_loan_term_flag,ct_loan_term,credit_purpose,credit_purpose_ff,amount_applied_for_flag,amount_applied_for,amount_approved,action_taken,action_taken_date,denial_reasons,denial_reasons_ff,pricing_interest_rate_type,pricing_init_rate_period,pricing_fixed_rate,pricing_adj_margin,pricing_adj_index_name,pricing_adj_index_name_ff,pricing_adj_index_value,pricing_origination_charges,pricing_broker_fees,pricing_initial_charges,pricing_mca_addcost_flag,pricing_mca_addcost,pricing_prepenalty_allowed,pricing_prepenalty_exists,census_tract_adr_type,census_tract_number,gross_annual_revenue_flag,gross_annual_revenue,naics_code_flag,naics_code,number_of_workers,time_in_business_type,time_in_business,business_ownership_status,num_principal_owners_flag,num_principal_owners,po_1_ethnicity,po_1_ethnicity_ff,po_1_race,po_1_race_anai_ff,po_1_race_asian_ff,po_1_race_baa_ff,po_1_race_pi_ff,po_1_gender_flag,po_1_gender_ff,po_2_ethnicity,po_2_ethnicity_ff,po_2_race,po_2_race_anai_ff,po_2_race_asian_ff,po_2_race_baa_ff,po_2_race_pi_ff,po_2_gender_flag,po_2_gender_ff,po_3_ethnicity,po_3_ethnicity_ff,po_3_race,po_3_race_anai_ff,po_3_race_asian_ff,po_3_race_baa_ff,po_3_race_pi_ff,po_3_gender_flag,po_3_gender_ff,po_4_ethnicity,po_4_ethnicity_ff,po_4_race,po_4_race_anai_ff,po_4_race_asian_ff,po_4_race_baa_ff,po_4_race_pi_ff,po_4_gender_flag,po_4_gender_ff -000TESTFIUIDDONOTUSEXGXVID11XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID12XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID13XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID14XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID21XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID31XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID31XTC2,20241201,2,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID31XTC3,20241201,3,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID31XTC4,20241201,4,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID41XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID41XTC2,20241201,1,2,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID51XTC1,20241201,1,1,1,,999,,988,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID51XTC2,20241201,1,1,2,,999,,988,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID51XTC3,20241201,1,1,3,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID51XTC4,20241201,1,1,4,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID51XTC5,20241201,1,1,5,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID51XTC6,20241201,1,1,6,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID51XTC7,20241201,1,1,7,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID51XTC8,20241201,1,1,8,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID51XTC9,20241201,1,1,977,is not blank,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID51XTC10,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID61XTC1,20241201,1,1,977,less than 300 characters,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID62XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID62XTC2,20241201,1,1,977,is not blank,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID71XTC1,20241201,1,1,988,,1,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID71XTC2,20241201,1,1,988,,2,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID71XTC3,20241201,1,1,988,,3,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID71XTC4,20241201,1,1,988,,4,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID71XTC5,20241201,1,1,988,,5,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID71XTC6,20241201,1,1,988,,6,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID71XTC7,20241201,1,1,988,,7,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID71XTC8,20241201,1,1,988,,8,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID71XTC9,20241201,1,1,988,,9,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID71XTC10,20241201,1,1,988,,10,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID71XTC11,20241201,1,1,988,,11,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID71XTC12,20241201,1,1,988,,977,not blank,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID71XTC13,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID72XTC1,20241201,1,1,988,,1,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID73XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID74XTC1,20241201,1,1,988,,1;2;3;4;5,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID81XTC1,20241201,1,1,988,,977,less than 300 characters,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID82XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID82XTC2,20241201,1,1,988,,977,is not blank,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID83XTC2,20241201,1,1,988,,1;2;3;4;977,value1,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID91XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID91XTC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID91XTC3,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID92XTC1,20241201,1,1,1,,999,,988,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID92XTC2,20241201,1,1,2,,999,,988,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID92XTC3,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID101XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID102XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID103XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID103XTC2,20241201,1,1,1,,999,,900,1,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID104XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID111XTC1,20241201,1,1,988,,999,,999,,1,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID111XTC2,20241201,1,1,988,,999,,999,,2,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID111XTC3,20241201,1,1,988,,999,,999,,3,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID111XTC4,20241201,1,1,988,,999,,999,,4,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID111XTC5,20241201,1,1,988,,999,,999,,5,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID111XTC6,20241201,1,1,988,,999,,999,,6,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID111XTC7,20241201,1,1,988,,999,,999,,7,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID111XTC8,20241201,1,1,988,,999,,999,,8,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID111XTC9,20241201,1,1,988,,999,,999,,9,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID111XTC10,20241201,1,1,988,,999,,999,,10,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID111XTC11,20241201,1,1,988,,999,,999,,11,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID111XTC12,20241201,1,1,988,,999,,999,,977,not blank,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID111XTC13,20241201,1,1,988,,999,,999,,988,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID111XTC14,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID112XTC1,20241201,1,1,988,,999,,999,,1,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID112XTC2,20241201,1,1,988,,999,,999,,1;2;3,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID113XTC1,20241201,1,1,988,,999,,999,,988,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID113XTC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID114XTC1,20241201,1,1,988,,999,,999,,1;2;3,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID121XTC1,20241201,1,1,988,,999,,999,,977,less than 300 characters,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID122XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID122XTC2,20241201,1,1,988,,999,,999,,977,is not blank,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID123XTC1,20241201,1,1,988,,999,,999,,1;2;977,value1,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID131XTC1,20241201,1,1,988,,999,,999,,999,,900,7777,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID131XTC2,20241201,1,1,988,,999,,999,,999,,988,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID131XTC3,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID141XTC1,20241201,1,1,988,,999,,999,,999,,900,7777,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID142XTC1,20241201,1,1,988,,999,,999,,999,,900,7777,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID143XTC1,20241201,1,1,988,,999,,999,,999,,988,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID143XTC2,20241201,1,1,988,,999,,999,,999,,900,7777,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID151XTC1,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,999,,,,999,,,0,0,0,999,,1,1,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID152XTC1,20241201,1,1,988,,999,,999,,999,,999,,1,1,20241231,999,,999,,,,999,,,0,0,0,999,,2,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID153XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID153XTC2,20241201,1,1,988,,999,,999,,999,,999,,,4,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID153XTC3,20241201,1,1,988,,999,,999,,999,,999,,,3,20241231,1,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID153XTC4,20241201,1,1,988,,999,,999,,999,,999,,7777,2,20241231,999,,999,,,,999,,,0,0,0,999,,1,1,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID153XTC5,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,999,,,,999,,,0,0,0,999,,2,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID161XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID161XTC2,20241201,1,1,988,,999,,999,,999,,999,,,4,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID161XTC3,20241201,1,1,988,,999,,999,,999,,999,,,3,20241231,1,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID161XTC4,20241201,1,1,988,,999,,999,,999,,999,,7777,2,20241231,999,,999,,,,999,,,0,0,0,999,,1,1,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID161XTC5,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,999,,,,999,,,0,0,0,999,,2,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID171XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID172XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID173XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID174XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241202,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID181XTC1,20241201,1,1,988,,999,,999,,999,,999,,,3,20241231,1,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID181XTC2,20241201,1,1,988,,999,,999,,999,,999,,,3,20241231,2,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID181XTC3,20241201,1,1,988,,999,,999,,999,,999,,,3,20241231,3,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID181XTC4,20241201,1,1,988,,999,,999,,999,,999,,,3,20241231,4,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID181XTC5,20241201,1,1,988,,999,,999,,999,,999,,,3,20241231,5,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID181XTC6,20241201,1,1,988,,999,,999,,999,,999,,,3,20241231,6,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID181XTC7,20241201,1,1,988,,999,,999,,999,,999,,,3,20241231,7,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID181XTC8,20241201,1,1,988,,999,,999,,999,,999,,,3,20241231,8,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID181XTC9,20241201,1,1,988,,999,,999,,999,,999,,,3,20241231,9,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID181XTC10,20241201,1,1,988,,999,,999,,999,,999,,,3,20241231,977,not blank,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID181XTC11,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,999,,,,999,,,0,0,0,999,,1,1,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID182XTC1,20241201,1,1,988,,999,,999,,999,,999,,,3,20241231,1,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID182XTC2,20241201,1,1,988,,999,,999,,999,,999,,,3,20241231,1;2,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID182XTC3,20241201,1,1,988,,999,,999,,999,,999,,,3,20241231,1;2;3,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID182XTC4,20241201,1,1,988,,999,,999,,999,,999,,,3,20241231,1;2;3;4,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID183XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID183XTC2,20241201,1,1,988,,999,,999,,999,,999,,,4,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID183XTC3,20241201,1,1,988,,999,,999,,999,,999,,7777,2,20241231,999,,999,,,,999,,,0,0,0,999,,2,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID183XTC4,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,999,,,,999,,,0,0,0,999,,1,1,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID184XTC1,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,999,,,,999,,,0,0,0,999,,1,1,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID185XTC1,20241201,1,1,988,,999,,999,,999,,999,,,3,20241231,1;2;3;4,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID191XTC1,20241201,1,1,988,,999,,999,,999,,999,,,3,20241231,977,less than 300 characters,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID192XTC1,20241201,1,1,988,,999,,999,,999,,999,,,3,20241231,1,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID192XTC2,20241201,1,1,988,,999,,999,,999,,999,,,3,20241231,977,"is, not, blank",999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID193XTC1,20241201,1,1,988,,999,,999,,999,,999,,,3,20241231,1;2;3;977,value1,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID201XTC1,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,1,,,1,1,,1,0,0,0,999,,1,1,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID201XTC2,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,2,,1,,999,,,0,0,0,999,,2,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID201XTC3,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,3,1,,1,1,,1,0,0,0,999,,1,1,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID201XTC4,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,4,1,1,,999,,,0,0,0,999,,2,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID201XTC5,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,5,1,,1,1,,,0,0,0,999,,1,1,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID201XTC6,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,6,1,1,,999,,,0,0,0,999,,2,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID201XTC7,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,999,,,,999,,,0,0,0,999,,1,1,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID202XTC1,20241201,1,1,988,,999,,999,,999,,999,,,3,20241231,1,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID202XTC2,20241201,1,1,988,,999,,999,,999,,999,,,4,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID202XTC3,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID203XTC1,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,999,,,,999,,,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID203XTC2,20241201,1,1,988,,999,,999,,999,,999,,7777,2,20241231,999,,999,,,,999,,,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID211XTC1,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,3,7777,,1,1,,1,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID212XTC1,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,3,1,,1,1,,1,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID213XTC1,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,1,,,1,1,,1,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID213XTC2,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,2,,1,,999,,,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID213XTC3,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,3,1,,1,1,,1,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID213XTC4,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,4,1,1,,999,,,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID213XTC5,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,5,1,,1,1,,,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID213XTC6,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,6,1,1,,999,,,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID221XTC1,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,2,,7777,,999,,,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID222XTC1,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,1,,,1,1,,1,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID222XTC2,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,3,1,,1,1,,1,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID222XTC3,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,5,1,,1,1,,,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID222XTC4,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,2,,7777,,999,,,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID222XTC5,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,4,1,7777,,999,,,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID222XTC6,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,6,1,7777,,999,,,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID223XTC1,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,2,,0.2,,999,,,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID231XTC1,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,1,,,7777,1,,1,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID232XTC1,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,2,,1,,999,,,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID232XTC2,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,4,1,1,,999,,,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID232XTC3,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,6,1,1,,999,,,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID232XTC4,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,1,,,7777,1,,1,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID232XTC5,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,3,1,,7777,1,,1,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID232XTC6,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,5,1,,7777,1,,,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID233XTC1,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,999,,,,999,,,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID241XTC1,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,1,,,1,1,,1,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID241XTC2,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,1,,,1,2,,1,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID241XTC3,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,1,,,1,3,,1,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID241XTC4,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,1,,,1,4,,1,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID241XTC5,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,1,,,1,5,,1,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID241XTC6,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,1,,,1,6,,1,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID241XTC7,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,1,,,1,7,,1,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID241XTC8,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,1,,,1,8,,1,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID241XTC9,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,1,,,1,9,,1,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID241XTC10,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,1,,,1,10,,1,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID241XTC11,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,1,,,1,977,not blank,1,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID241XTC12,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,2,,1,,999,,,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID242XTC1,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,2,,1,,999,,,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID242XTC2,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,4,1,1,,999,,,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID242XTC3,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,6,1,1,,999,,,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID242XTC4,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,1,,,1,1,,1,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID242XTC5,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,3,1,,1,1,,1,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID242XTC6,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,5,1,,1,1,,,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID251XTC1,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,1,,,1,1,,1,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID252XTC1,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,1,,,1,1,,1,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID252XTC2,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,1,,,1,977,not blank,1,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID261XTC1,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,1,,,1,1,,1,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID262XTC1,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,2,,1,,999,,,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID262XTC2,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,4,1,1,,999,,,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID262XTC3,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,5,1,,1,1,,,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID262XTC4,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,6,1,1,,999,,,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID262XTC5,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,999,,,,999,,,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID262XTC6,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,1,,,1,1,,7777,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID262XTC7,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,3,1,,1,1,,7777,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID271XTC1,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,999,,,,999,,,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID281XTC1,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,999,,,,999,,,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID291XTC1,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,999,,,,999,,,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID301XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID301XTC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID302XTC1,20241201,1,1,1,,999,,900,1,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID302XTC2,20241201,1,1,2,,999,,900,1,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID302XTC3,20241201,1,1,3,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID302XTC4,20241201,1,1,4,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID302XTC5,20241201,1,1,5,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID302XTC6,20241201,1,1,6,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID302XTC7,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID311XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID312XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID312XTC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID321XTC1,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,999,,,,999,,,0,0,0,999,,1,1,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID321XTC2,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,999,,,,999,,,0,0,0,999,,2,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID321XTC3,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,999,,,,999,,,0,0,0,999,,1,1,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID331XTC1,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,999,,,,999,,,0,0,0,999,,2,1,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID331XTC2,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,999,,,,999,,,0,0,0,999,,1,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID331XTC3,20241201,1,1,988,,999,,999,,999,,999,,7777,1,20241231,999,,999,,,,999,,,0,0,0,999,,2,2,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID341XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,1,72109999999,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID341XTC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,2,72109999999,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID341XTC3,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,3,72109999999,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID341XTC4,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID351XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,1,53041971900,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID352XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID352XTC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,1,48439106502,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID352XTC3,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,2,48439106502,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID352XTC4,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,3,48439106502,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID353XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,1,48439106502,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID361XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,900,1,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID361XTC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID371XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,900,1,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID372XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID372XTC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,900,1,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID381XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,900,111,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID381XTC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID391XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,900,111,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID392XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,900,111,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID393XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXBXVID393XTC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,900,111,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID394XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,900,311,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID401XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,1,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID401XTC10,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,2,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID401XTC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,3,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID401XTC3,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,4,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID401XTC4,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,5,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID401XTC5,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,6,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID401XTC6,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,7,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID401XTC7,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,8,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID401XTC8,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,9,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID401XTC9,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID411XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,1,1,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID411XTC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,2,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID411XTC3,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,3,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID411XTC4,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID421XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,1,1,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID422XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,1,1,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID423XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID423XTC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,1,1,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID431XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,1,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID431XTC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,2,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID431XTC3,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,3,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID431XTC4,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,955,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID431XTC5,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,966,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID431XTC6,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID432XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,1,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID433XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,1;2;3,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID434XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID441XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,11,,2,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID441XTC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID451XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID451XTC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,11,,2,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID451XTC3,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,11,,2,,,,,988,,1,,2,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID451XTC4,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,11,,2,,,,,988,,1,,2,,,,,988,,1,,2,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID451XTC5,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,11,,2,,,,,988,,1,,2,,,,,988,,1,,2,,,,,988,,1,,2,,,,,988, -000TESTFIUIDDONOTUSEXGXVID452XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,988,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID452XTC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,11,,2,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID461XPO1TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,1,,2,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID461XPO1TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,11,,2,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID461XPO1TC3,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,12,,2,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID461XPO1TC4,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,13,,2,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID461XPO1TC5,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,14,,2,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID461XPO1TC6,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,2,,2,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID461XPO1TC7,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,966,,2,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID461XPO1TC8,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,977,not blank,2,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID461XPO1TC9,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,2,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID461XPO2TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,1,,2,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID461XPO2TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,11,,2,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID461XPO2TC3,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,12,,2,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID461XPO2TC4,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,13,,2,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID461XPO2TC5,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,14,,2,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID461XPO2TC6,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,2,,2,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID461XPO2TC7,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,966,,2,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID461XPO2TC8,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,977,not blank,2,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID461XPO2TC9,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,2,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID461XPO3TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,1,,2,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID461XPO3TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,11,,2,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID461XPO3TC3,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,12,,2,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID461XPO3TC4,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,13,,2,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID461XPO3TC5,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,14,,2,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID461XPO3TC6,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,2,,2,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID461XPO3TC7,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,966,,2,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID461XPO3TC8,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,977,not blank,2,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID461XPO3TC9,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID461XPO4TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,1,,2,,,,,988, -000TESTFIUIDDONOTUSEXGXVID461XPO4TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,11,,2,,,,,988, -000TESTFIUIDDONOTUSEXGXVID461XPO4TC3,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,12,,2,,,,,988, -000TESTFIUIDDONOTUSEXGXVID461XPO4TC4,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,13,,2,,,,,988, -000TESTFIUIDDONOTUSEXGXVID461XPO4TC5,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,14,,2,,,,,988, -000TESTFIUIDDONOTUSEXGXVID461XPO4TC6,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,2,,2,,,,,988, -000TESTFIUIDDONOTUSEXGXVID461XPO4TC7,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,966,,2,,,,,988, -000TESTFIUIDDONOTUSEXGXVID461XPO4TC8,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,977,not blank,2,,,,,988, -000TESTFIUIDDONOTUSEXGXVID461XPO4TC9,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988, -000TESTFIUIDDONOTUSEXGXVID462XPO1TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,1;11,,2,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID462XPO2TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,1;11,,2,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID462XPO3TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,1;11,,2,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID462XPO4TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,1;11,,2,,,,,988, -000TESTFIUIDDONOTUSEXGXVID463XPO1TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,2,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID463XPO2TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,2,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID463XPO3TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID463XPO4TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988, -000TESTFIUIDDONOTUSEXGXVID471XPO1TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,977;1,less than 300 characters,2,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID471XPO2TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,977;1,less than 300 characters,2,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID471XPO3TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,977;1,less than 300 characters,2,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID471XPO4TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,977;1,less than 300 characters,2,,,,,988, -000TESTFIUIDDONOTUSEXGXVID472XPO1TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,1;11,,2,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID472XPO1TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,977;1,is not blank,2,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID472XPO2TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,1;11,,2,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID472XPO2TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,977;1,is not blank,2,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID472XPO3TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,1;11,,2,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID472XPO3TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,977;1,is not blank,2,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID472XPO4TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,1;11,,2,,,,,988, -000TESTFIUIDDONOTUSEXGXVID472XPO4TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,977;1,is not blank,2,,,,,988, -000TESTFIUIDDONOTUSEXGXVID481XPO1TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,1,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO1TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,2,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO1TC3,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,21,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO1TC4,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,22,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO1TC5,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,23,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO1TC6,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,24,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO1TC7,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,25,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO1TC8,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,26,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO1TC9,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,27,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO1TC10,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,3,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO1TC11,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,31,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO1TC12,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,32,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO1TC13,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,33,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO1TC14,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,34,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO1TC15,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,35,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO1TC16,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,36,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO1TC17,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,37,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO1TC18,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,4,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO1TC19,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,41,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO1TC20,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,42,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO1TC21,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,43,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO1TC22,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,44,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO1TC23,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,5,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO1TC24,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,966,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO1TC25,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,971,not blank,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO1TC26,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,972,,not blank,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO1TC27,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,973,,,not blank,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO1TC28,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,974,,,,not blank,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO1TC29,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,988,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO2TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,1,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO2TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,2,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO2TC3,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,21,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO2TC4,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,22,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO2TC5,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,23,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO2TC6,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,24,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO2TC7,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,25,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO2TC8,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,26,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO2TC9,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,27,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO2TC10,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,3,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO2TC11,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,31,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO2TC12,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,32,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO2TC13,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,33,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO2TC14,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,34,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO2TC15,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,35,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO2TC16,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,36,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO2TC17,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,37,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO2TC18,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,4,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO2TC19,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,41,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO2TC20,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,42,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO2TC21,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,43,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO2TC22,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,44,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO2TC23,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,5,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO2TC24,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,966,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO2TC25,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,971,not blank,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO2TC26,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,972,,not blank,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO2TC27,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,973,,,not blank,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO2TC28,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,974,,,,not blank,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO2TC29,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,988,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO3TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,1,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO3TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO3TC3,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,21,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO3TC4,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,22,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO3TC5,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,23,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO3TC6,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,24,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO3TC7,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,25,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO3TC8,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,26,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO3TC9,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,27,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO3TC10,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,3,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO3TC11,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,31,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO3TC12,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,32,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO3TC13,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,33,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO3TC14,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,34,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO3TC15,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,35,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO3TC16,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,36,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO3TC17,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,37,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO3TC18,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,4,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO3TC19,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,41,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO3TC20,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,42,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO3TC21,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,43,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO3TC22,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,44,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO3TC23,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,5,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO3TC24,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,966,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO3TC25,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,971,not blank,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO3TC26,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,972,,not blank,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO3TC27,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,973,,,not blank,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO3TC28,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,974,,,,not blank,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO3TC29,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,988,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID481XPO4TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,1,,,,,988, -000TESTFIUIDDONOTUSEXGXVID481XPO4TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988, -000TESTFIUIDDONOTUSEXGXVID481XPO4TC3,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,21,,,,,988, -000TESTFIUIDDONOTUSEXGXVID481XPO4TC4,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,22,,,,,988, -000TESTFIUIDDONOTUSEXGXVID481XPO4TC5,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,23,,,,,988, -000TESTFIUIDDONOTUSEXGXVID481XPO4TC6,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,24,,,,,988, -000TESTFIUIDDONOTUSEXGXVID481XPO4TC7,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,25,,,,,988, -000TESTFIUIDDONOTUSEXGXVID481XPO4TC8,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,26,,,,,988, -000TESTFIUIDDONOTUSEXGXVID481XPO4TC9,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,27,,,,,988, -000TESTFIUIDDONOTUSEXGXVID481XPO4TC10,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,3,,,,,988, -000TESTFIUIDDONOTUSEXGXVID481XPO4TC11,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,31,,,,,988, -000TESTFIUIDDONOTUSEXGXVID481XPO4TC12,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,32,,,,,988, -000TESTFIUIDDONOTUSEXGXVID481XPO4TC13,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,33,,,,,988, -000TESTFIUIDDONOTUSEXGXVID481XPO4TC14,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,34,,,,,988, -000TESTFIUIDDONOTUSEXGXVID481XPO4TC15,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,35,,,,,988, -000TESTFIUIDDONOTUSEXGXVID481XPO4TC16,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,36,,,,,988, -000TESTFIUIDDONOTUSEXGXVID481XPO4TC17,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,37,,,,,988, -000TESTFIUIDDONOTUSEXGXVID481XPO4TC18,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,4,,,,,988, -000TESTFIUIDDONOTUSEXGXVID481XPO4TC19,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,41,,,,,988, -000TESTFIUIDDONOTUSEXGXVID481XPO4TC20,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,42,,,,,988, -000TESTFIUIDDONOTUSEXGXVID481XPO4TC21,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,43,,,,,988, -000TESTFIUIDDONOTUSEXGXVID481XPO4TC22,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,44,,,,,988, -000TESTFIUIDDONOTUSEXGXVID481XPO4TC23,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,5,,,,,988, -000TESTFIUIDDONOTUSEXGXVID481XPO4TC24,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,966,,,,,988, -000TESTFIUIDDONOTUSEXGXVID481XPO4TC25,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,971,not blank,,,,988, -000TESTFIUIDDONOTUSEXGXVID481XPO4TC26,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,972,,not blank,,,988, -000TESTFIUIDDONOTUSEXGXVID481XPO4TC27,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,973,,,not blank,,988, -000TESTFIUIDDONOTUSEXGXVID481XPO4TC28,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,974,,,,not blank,988, -000TESTFIUIDDONOTUSEXGXVID481XPO4TC29,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,988,,,,,988, -000TESTFIUIDDONOTUSEXGXVID482XPO1TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,1;2,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID482XPO2TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,1;2,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID482XPO3TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,1;2,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID482XPO4TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,1;2,,,,,988, -000TESTFIUIDDONOTUSEXGXVID483XPO1TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,988,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID483XPO2TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,988,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID483XPO3TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,988,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID483XPO4TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,988,,,,,988, -000TESTFIUIDDONOTUSEXGXVID491XPO1TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,971;1,less than 300 characters,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID491XPO2TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,971;1,less than 300 characters,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID491XPO3TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,971;1,less than 300 characters,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID491XPO4TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,971;1,less than 300 characters,,,,988, -000TESTFIUIDDONOTUSEXGXVID492XPO1TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,1;2,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID492XPO1TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,971;1,is not blank,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID492XPO2TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,1;2,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID492XPO2TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,971;1,is not blank,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID492XPO3TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,1;2,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID492XPO3TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,971;1,is not blank,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID492XPO4TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,1;2,,,,,988, -000TESTFIUIDDONOTUSEXGXVID492XPO4TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,971;1,is not blank,,,,988, -000TESTFIUIDDONOTUSEXGXVID501XPO1TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,972;1,,less than 300 characters,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID501XPO2TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,972;1,,less than 300 characters,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID501XPO3TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,972;1,,less than 300 characters,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID501XPO4TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,972;1,,less than 300 characters,,,988, -000TESTFIUIDDONOTUSEXGXVID502XPO1TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,1;2,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID502XPO1TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,972;1,,is not blank,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID502XPO2TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,1;2,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID502XPO2TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,972;1,,is not blank,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID502XPO3TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,1;2,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID502XPO3TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,972;1,,is not blank,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID502XPO4TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,1;2,,,,,988, -000TESTFIUIDDONOTUSEXGXVID502XPO4TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,972;1,,is not blank,,,988, -000TESTFIUIDDONOTUSEXGXVID511XPO1TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,973;1,,,less than 300 characters,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID511XPO2TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,973;1,,,less than 300 characters,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID511XPO3TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,973;1,,,less than 300 characters,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID511XPO4TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,973;1,,,less than 300 characters,,988, -000TESTFIUIDDONOTUSEXGXVID512XPO1TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,1;2,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID512XPO1TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,973;1,,,is not blank,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID512XPO2TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,1;2,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID512XPO2TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,973;1,,,is not blank,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID512XPO3TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,1;2,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID512XPO3TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,973;1,,,is not blank,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID512XPO4TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,1;2,,,,,988, -000TESTFIUIDDONOTUSEXGXVID512XPO4TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,973;1,,,is not blank,,988, -000TESTFIUIDDONOTUSEXGXVID521XPO1TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,974;1,,,,less than 300 characters,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID521XPO2TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,974;1,,,,less than 300 characters,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID521XPO3TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,974;1,,,,less than 300 characters,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID521XPO4TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,974;1,,,,less than 300 characters,988, -000TESTFIUIDDONOTUSEXGXVID522XPO1TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,1;2,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID522XPO1TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,974;1,,,,is not blank,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID522XPO2TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,1;2,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID522XPO2TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,974;1,,,,is not blank,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID522XPO3TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,1;2,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID522XPO3TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,974;1,,,,is not blank,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID522XPO4TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,1;2,,,,,988, -000TESTFIUIDDONOTUSEXGXVID522XPO4TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,974;1,,,,is not blank,988, -000TESTFIUIDDONOTUSEXGXVID531XPO1TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,2,,,,,1,gender,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID531XPO1TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,2,,,,,966,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID531XPO1TC3,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,2,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID531XPO2TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,2,,,,,1,gender,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID531XPO2TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,2,,,,,966,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID531XPO2TC3,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,2,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID531XPO3TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,1,gender,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID531XPO3TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,966,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID531XPO3TC3,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID531XPO4TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,1,gender -000TESTFIUIDDONOTUSEXGXVID531XPO4TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,966, -000TESTFIUIDDONOTUSEXGXVID531XPO4TC3,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988, -000TESTFIUIDDONOTUSEXGXVID541XPO1TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,2,,,,,1,less than 300 characters,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID541XPO2TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,2,,,,,1,less than 300 characters,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID541XPO3TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,1,less than 300 characters,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID541XPO4TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,1,less than 300 characters -000TESTFIUIDDONOTUSEXGXVID542XPO1TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,2,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID542XPO1TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,2,,,,,1,is not blank,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID542XPO2TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,2,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID542XPO2TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,2,,,,,1,is not blank,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID542XPO3TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID542XPO3TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,1,is not blank,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVID542XPO4TC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988, -000TESTFIUIDDONOTUSEXGXVID542XPO4TC2,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,1,is not blank -000TESTFIUIDDONOTUSEXGXVIDPODEMO0XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVIDPODEMO1XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,1,988,,2,,,,,988,,,,,,,,,,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVIDPODEMO2XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,2,988,,2,,,,,988,,988,,2,,,,,988,,,,,,,,,,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVIDPODEMO3XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,3,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,,,,,,,,, -000TESTFIUIDDONOTUSEXGXVIDPODEMO4XTC1,20241201,1,1,988,,999,,999,,999,,999,,,5,20241231,999,,999,,,,999,,,,,,999,,999,999,988,,988,,988,,988,988,,988,900,4,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988,,988,,2,,,,,988, \ No newline at end of file diff --git a/tests/data/sblar_no_findings.csv b/tests/data/sblar_no_findings.csv new file mode 100644 index 00000000..f41fa728 --- /dev/null +++ b/tests/data/sblar_no_findings.csv @@ -0,0 +1,11 @@ +uid,app_date,app_method,app_recipient,ct_credit_product,ct_credit_product_ff,ct_guarantee,ct_guarantee_ff,ct_loan_term_flag,ct_loan_term,credit_purpose,credit_purpose_ff,amount_applied_for_flag,amount_applied_for,amount_approved,action_taken,action_taken_date,denial_reasons,denial_reasons_ff,pricing_interest_rate_type,pricing_init_rate_period,pricing_fixed_rate,pricing_adj_margin,pricing_adj_index_name,pricing_adj_index_name_ff,pricing_adj_index_value,pricing_origination_charges,pricing_broker_fees,pricing_initial_charges,pricing_mca_addcost_flag,pricing_mca_addcost,pricing_prepenalty_allowed,pricing_prepenalty_exists,census_tract_adr_type,census_tract_number,gross_annual_revenue_flag,gross_annual_revenue,naics_code_flag,naics_code,number_of_workers,time_in_business_type,time_in_business,business_ownership_status,num_principal_owners_flag,num_principal_owners,po_1_ethnicity,po_1_ethnicity_ff,po_1_race,po_1_race_anai_ff,po_1_race_asian_ff,po_1_race_baa_ff,po_1_race_pi_ff,po_1_gender_flag,po_1_gender_ff,po_2_ethnicity,po_2_ethnicity_ff,po_2_race,po_2_race_anai_ff,po_2_race_asian_ff,po_2_race_baa_ff,po_2_race_pi_ff,po_2_gender_flag,po_2_gender_ff,po_3_ethnicity,po_3_ethnicity_ff,po_3_race,po_3_race_anai_ff,po_3_race_asian_ff,po_3_race_baa_ff,po_3_race_pi_ff,po_3_gender_flag,po_3_gender_ff,po_4_ethnicity,po_4_ethnicity_ff,po_4_race,po_4_race_anai_ff,po_4_race_asian_ff,po_4_race_baa_ff,po_4_race_pi_ff,po_4_gender_flag,po_4_gender_ff +123456789TESTBANK12300001,20241201,1,1,977,test,977,test,900,1,977,test,900,1,10,1,20241231,999,,3,1,,1,977,test,1,1,1,1,999,,1,1,1,01001020100,900,1,900,111,988,1,1,1,900,1,1,,1,,,,,966,,,,,,,,,,,,,,,,,,,,,,,,,,,, +123456789TESTBANK12300002,20241201,1,1,977,test,977,test,900,1,977,test,900,1,10,1,20241231,999,,3,1,,1,977,test,1,1,1,1,999,,1,1,1,01001020100,900,1,900,111,988,1,1,1,900,1,1,,1,,,,,966,,,,,,,,,,,,,,,,,,,,,,,,,,,, +123456789TESTBANK12300003,20241201,1,1,977,test,977,test,900,1,977,test,900,1,10,1,20241231,999,,3,1,,1,977,test,1,1,1,1,999,,1,1,1,01001020100,900,1,900,111,988,1,1,1,900,1,1,,1,,,,,966,,,,,,,,,,,,,,,,,,,,,,,,,,,, +123456789TESTBANK12300004,20241201,1,1,977,test,977,test,900,1,977,test,900,1,10,1,20241231,999,,3,1,,1,977,test,1,1,1,1,999,,1,1,1,01001020100,900,1,900,111,988,1,1,1,900,1,1,,1,,,,,966,,,,,,,,,,,,,,,,,,,,,,,,,,,, +123456789TESTBANK12300005,20241201,1,1,977,test,977,test,900,1,977,test,900,1,10,1,20241231,999,,3,1,,1,977,test,1,1,1,1,999,,1,1,1,01001020100,900,1,900,111,988,1,1,1,900,1,1,,1,,,,,966,,,,,,,,,,,,,,,,,,,,,,,,,,,, +123456789TESTBANK12300006,20241201,1,1,977,test,977,test,900,1,977,test,900,1,10,1,20241231,999,,3,1,,1,977,test,1,1,1,1,999,,1,1,1,01001020100,900,1,900,111,988,1,1,1,900,1,1,,1,,,,,966,,,,,,,,,,,,,,,,,,,,,,,,,,,, +123456789TESTBANK12300007,20241201,1,1,977,test,977,test,900,1,977,test,900,1,10,1,20241231,999,,3,1,,1,977,test,1,1,1,1,999,,1,1,1,01001020100,900,1,900,111,988,1,1,1,900,1,1,,1,,,,,966,,,,,,,,,,,,,,,,,,,,,,,,,,,, +123456789TESTBANK12300008,20241201,1,1,977,test,977,test,900,1,977,test,900,1,10,1,20241231,999,,3,1,,1,977,test,1,1,1,1,999,,1,1,1,01001020100,900,1,900,111,988,1,1,1,900,1,1,,1,,,,,966,,,,,,,,,,,,,,,,,,,,,,,,,,,, +123456789TESTBANK12300009,20241201,1,1,977,test,977,test,900,1,977,test,900,1,10,1,20241231,999,,3,1,,1,977,test,1,1,1,1,999,,1,1,1,01001020100,900,1,900,111,988,1,1,1,900,1,1,,1,,,,,966,,,,,,,,,,,,,,,,,,,,,,,,,,,, +123456789TESTBANK123000010,20241201,1,1,977,test,977,test,900,1,977,test,900,1,10,1,20241231,999,,3,1,,1,977,test,1,1,1,1,999,,1,1,1,01001020100,900,1,900,111,988,1,1,1,900,1,1,,1,,,,,966,,,,,,,,,,,,,,,,,,,,,,,,,,,, diff --git a/tests/test_check_functions.py b/tests/test_check_functions.py index 4198efa0..bd6cf550 100644 --- a/tests/test_check_functions.py +++ b/tests/test_check_functions.py @@ -1,4 +1,5 @@ -import pandas as pd +import polars as pl +import pandera.polars as pa from regtech_data_validator import global_data from regtech_data_validator.check_functions import ( @@ -28,65 +29,103 @@ class TestInvalidDateFormat: invalid_date_format_1 = "202310101" invalid_date_format_2 = "20231032" invalid_date_format_3 = "20231301" - invalid_date_format_4 = "00001201" + invalid_date_format_4 = "A0001201" invalid_date_format_5 = "2020121" invalid_date_format_6 = "2020120A" def test_with_valid_date(self): - assert is_date(self.valid_date_format) is True + date_data = pa.PolarsData(pl.DataFrame({"date": self.valid_date_format}).lazy(), "date") + results = is_date(date_data).collect() + assert results["check_results"].eq(True).all() def test_with_invalid_date(self): - assert is_date(self.invalid_date_format_1) is False + date_data = pa.PolarsData(pl.DataFrame({"date": self.invalid_date_format_1}).lazy(), "date") + results = is_date(date_data).collect() + assert results["check_results"].eq(False).all() def test_with_invalid_day(self): - assert is_date(self.invalid_date_format_2) is False + date_data = pa.PolarsData(pl.DataFrame({"date": self.invalid_date_format_2}).lazy(), "date") + results = is_date(date_data).collect() + assert results["check_results"].eq(False).all() def test_with_invalid_month(self): - assert is_date(self.invalid_date_format_3) is False + date_data = pa.PolarsData(pl.DataFrame({"date": self.invalid_date_format_3}).lazy(), "date") + results = is_date(date_data).collect() + assert results["check_results"].eq(False).all() def test_with_invalid_year(self): - assert is_date(self.invalid_date_format_4) is False + date_data = pa.PolarsData(pl.DataFrame({"date": self.invalid_date_format_4}).lazy(), "date") + results = is_date(date_data).collect() + assert results["check_results"].eq(False).all() def test_with_invalid_format(self): - assert is_date(self.invalid_date_format_5) is False + date_data = pa.PolarsData(pl.DataFrame({"date": self.invalid_date_format_5}).lazy(), "date") + results = is_date(date_data).collect() + print(f"5 results: {results}") + assert results["check_results"].eq(False).all() def test_with_invalid_type(self): - assert is_date(self.invalid_date_format_6) is False + date_data = pa.PolarsData(pl.DataFrame({"date": self.invalid_date_format_6}).lazy(), "date") + results = is_date(date_data).collect() + assert results["check_results"].eq(False).all() class TestDuplicatesInField: def test_with_blank(self): - assert is_unique_in_field("") is True + unique_data = pa.PolarsData(pl.DataFrame({"unique": ""}).lazy(), "unique") + results = is_unique_in_field(unique_data).collect() + assert results["check_results"].eq(True).all() def test_with_no_duplicates(self): - assert is_unique_in_field("1") is True - assert is_unique_in_field("1;2;3;4") is True + unique_data = pa.PolarsData(pl.DataFrame({"unique": "1"}).lazy(), "unique") + results = is_unique_in_field(unique_data).collect() + assert results["check_results"].eq(True).all() + + unique_data = pa.PolarsData(pl.DataFrame({"unique": "1;2;3;4"}).lazy(), "unique") + results = is_unique_in_field(unique_data).collect() + assert results["check_results"].eq(True).all() def test_with_duplicates(self): - assert is_unique_in_field("1;2;3;3;4") is False + unique_data = pa.PolarsData(pl.DataFrame({"unique": "1;2;3;3;4"}).lazy(), "unique") + results = is_unique_in_field(unique_data).collect() + assert results["check_results"].eq(False).all() class TestInvalidNumberOfValues: def test_with_in_range(self): - assert has_valid_value_count("1;2;", 1, 4) is True + count_data = pa.PolarsData(pl.DataFrame({"count": "1;2;"}).lazy(), "count") + results = has_valid_value_count(count_data, 1, 4).collect() + assert results["check_results"].eq(True).all() def test_with_lower_range_value(self): - assert has_valid_value_count("1", 1, 4) is True + count_data = pa.PolarsData(pl.DataFrame({"count": "1"}).lazy(), "count") + results = has_valid_value_count(count_data, 1, 4).collect() + assert results["check_results"].eq(True).all() def test_with_invalid_lower_range_value(self): - assert has_valid_value_count("1", 2, 4) is False + count_data = pa.PolarsData(pl.DataFrame({"count": "1"}).lazy(), "count") + results = has_valid_value_count(count_data, 2, 4).collect() + assert results["check_results"].eq(False).all() def test_with_upper_range_value(self): - assert has_valid_value_count("1;2", 1, 2) is True + count_data = pa.PolarsData(pl.DataFrame({"count": "1;2"}).lazy(), "count") + results = has_valid_value_count(count_data, 1, 2).collect() + assert results["check_results"].eq(True).all() def test_with_invalid_upper_range_value(self): - assert has_valid_value_count("1;2;3;4", 2, 3) is False + count_data = pa.PolarsData(pl.DataFrame({"count": "1;2;3;4"}).lazy(), "count") + results = has_valid_value_count(count_data, 2, 3).collect() + assert results["check_results"].eq(False).all() def test_valid_with_no_upper_bound(self): - assert has_valid_value_count("1;2;3;4", 1, None) is True + count_data = pa.PolarsData(pl.DataFrame({"count": "1;2;3;4"}).lazy(), "count") + results = has_valid_value_count(count_data, 1, None).collect() + assert results["check_results"].eq(True).all() def test_invalid_with_no_upper_bound(self): - assert has_valid_value_count("1", 2, None) is False + count_data = pa.PolarsData(pl.DataFrame({"count": "1"}).lazy(), "count") + results = has_valid_value_count(count_data, 2, None).collect() + assert results["check_results"].eq(False).all() class TestMultiValueFieldRestriction: @@ -102,117 +141,138 @@ def test_with_valid_values(self): class TestMultiInvalidNumberOfValues: - series = pd.Series(["999"], name="test_name", index=[2]) + good_df = pa.PolarsData(pl.DataFrame({"value": "4", "other_value": "999"}).lazy(), "value") - blank_series = pd.Series([""], name="test_name", index=[2]) + blank_df = pa.PolarsData(pl.DataFrame({"value": "4;1", "other_value": ""}).lazy(), "value") - multiple_values_series = pd.Series(["1;2;3"], name="test_name", index=[2]) + multiple_values_series = pl.Series(values=["1;2;3"], name="test_name") - multiple_values_series_with_977 = pd.Series(["1;2;3;977"], name="test_name", index=[2]) + multiple_values_series_with_977 = pl.Series(values=["1;2;3;977"], name="test_name") - multiple_values_series_with_blanks = pd.Series(["1;2;; ;3"], name="test_name", index=[2]) + multiple_values_series_with_blanks = pl.Series(values=["1;2;; ;3"], name="test_name") def test_inside_maxlength(self): - result = has_valid_multi_field_value_count({"4": self.series}, 5) - assert result.values == [True] + good_df = pa.PolarsData(pl.DataFrame({"value": "4", "other_value": "999"}).lazy(), "value") + results = has_valid_multi_field_value_count(good_df, max_length=5, related_fields="other_value").collect() + assert results["check_results"].eq(True).all() def test_on_maxlength(self): - result = has_valid_multi_field_value_count({"4": self.series}, 2) - assert result.values == [True] + good_df = pa.PolarsData(pl.DataFrame({"value": "4", "other_value": "999"}).lazy(), "value") + results = has_valid_multi_field_value_count(good_df, max_length=2, related_fields="other_value").collect() + assert results["check_results"].eq(True).all() def test_with_blank(self): - result = has_valid_multi_field_value_count({"4;1": self.blank_series}, 2) - assert result.values == [True] + blank_df = pa.PolarsData(pl.DataFrame({"value": "4;1", "other_value": ""}).lazy(), "value") + results = has_valid_multi_field_value_count(blank_df, max_length=2, related_fields="other_value").collect() + assert results["check_results"].eq(True).all() def test_invalid_length_with_blank(self): - result = has_valid_multi_field_value_count({"4;1": self.blank_series}, 1) - assert result.values == [False] + blank_df = pa.PolarsData(pl.DataFrame({"value": "4;1", "other_value": ""}).lazy(), "value") + results = has_valid_multi_field_value_count(blank_df, max_length=1, related_fields="other_value").collect() + assert results["check_results"].eq(False).all() def test_invalid_length_with_blank_and_ignored_values(self): - result = has_valid_multi_field_value_count({"4;1;977": self.blank_series}, 1, ignored_values={"977"}) - assert result.values == [False] + blank_df = pa.PolarsData(pl.DataFrame({"value": "4;1;977", "other_value": ""}).lazy(), "value") + results = has_valid_multi_field_value_count( + blank_df, max_length=1, ignored_values={"977"}, related_fields="other_value" + ).collect() + assert results["check_results"].eq(False).all() def test_valid_length_with_blank_and_ignored_values(self): - result = has_valid_multi_field_value_count({"4;1;977": self.blank_series}, 2, ignored_values={"977"}) - assert result.values == [True] + blank_df = pa.PolarsData(pl.DataFrame({"value": "4;1;977", "other_value": ""}).lazy(), "value") + results = has_valid_multi_field_value_count( + blank_df, max_length=2, ignored_values={"977"}, related_fields="other_value" + ).collect() + assert results["check_results"].eq(True).all() def test_outside_maxlength(self): - result = has_valid_multi_field_value_count({"4": self.series}, 1) - assert result.values == [False] + max_df = pa.PolarsData(pl.DataFrame({"value": "4", "other_value": "999"}).lazy(), "value") + results = has_valid_multi_field_value_count(max_df, max_length=1, related_fields="other_value").collect() + assert results["check_results"].eq(False).all() - def test_valid_length_with_non_blank(self): - result = has_valid_multi_field_value_count({"4;1": self.multiple_values_series}, 5) - assert result.values == [True] + def test_valid_length_with_multi(self): + multi_df = pa.PolarsData(pl.DataFrame({"value": "4;1", "other_value": "1;2;3"}).lazy(), "value") + results = has_valid_multi_field_value_count(multi_df, max_length=5, related_fields="other_value").collect() + assert results["check_results"].eq(True).all() - def test_invalid_length_with_non_blank(self): - result = has_valid_multi_field_value_count({"4;1": self.multiple_values_series}, 4) - assert result.values == [False] + def test_invalid_length_with_multi(self): + multi_df = pa.PolarsData(pl.DataFrame({"value": "4;1", "other_value": "1;2;3"}).lazy(), "value") + results = has_valid_multi_field_value_count(multi_df, max_length=4, related_fields="other_value").collect() + assert results["check_results"].eq(False).all() def test_valid_length_with_ignored_values(self): - result = has_valid_multi_field_value_count( - {"4;1": self.multiple_values_series_with_977}, 6, ignored_values={"977"} - ) - assert result.values == [True] - - result = has_valid_multi_field_value_count( - {"4;1;977": self.multiple_values_series_with_977}, 6, ignored_values={"977"} - ) - assert result.values == [True] + multi_df = pa.PolarsData(pl.DataFrame({"value": "4;1", "other_value": "1;2;3;977"}).lazy(), "value") + results = has_valid_multi_field_value_count( + multi_df, max_length=5, ignored_values={"977"}, related_fields="other_value" + ).collect() + assert results["check_results"].eq(True).all() + + multi_df = pa.PolarsData(pl.DataFrame({"value": "4;1;977", "other_value": "1;2;3;977"}).lazy(), "value") + results = has_valid_multi_field_value_count( + multi_df, max_length=5, ignored_values={"977"}, related_fields="other_value" + ).collect() + assert results["check_results"].eq(True).all() def test_invalid_length_with_ignored_values(self): - result = has_valid_multi_field_value_count( - {"4;1": self.multiple_values_series_with_977}, 5, ignored_values={"977"} - ) - assert result.values == [False] - - result = has_valid_multi_field_value_count( - {"4;1;977": self.multiple_values_series_with_977}, 5, ignored_values={"977"} - ) - assert result.values == [False] + multi_df = pa.PolarsData(pl.DataFrame({"value": "4;1", "other_value": "1;2;3;977"}).lazy(), "value") + results = has_valid_multi_field_value_count( + multi_df, max_length=4, ignored_values={"977"}, related_fields="other_value" + ).collect() + assert results["check_results"].eq(False).all() + + multi_df = pa.PolarsData(pl.DataFrame({"value": "4;1;977", "other_value": "1;2;3;977"}).lazy(), "value") + results = has_valid_multi_field_value_count( + multi_df, max_length=4, ignored_values={"977"}, related_fields="other_value" + ).collect() + assert results["check_results"].eq(False).all() def test_valid_length_with_blank_values(self): - result = has_valid_multi_field_value_count( - {"4;1;977": self.multiple_values_series_with_blanks}, - 5, - ignored_values={"977"}, - ) - assert result.values == [True] + has_blank_df = pa.PolarsData(pl.DataFrame({"value": "4;1", "other_value": "1;2;; ;3"}).lazy(), "value") + results = has_valid_multi_field_value_count(has_blank_df, max_length=5, related_fields="other_value").collect() + assert results["check_results"].eq(True).all() - result = has_valid_multi_field_value_count({"4;1;977": self.multiple_values_series_with_blanks}, 6) - assert result.values == [True] + has_blank_df = pa.PolarsData(pl.DataFrame({"value": "4;1;977", "other_value": "1;2;; ;3;977"}).lazy(), "value") + results = has_valid_multi_field_value_count( + has_blank_df, max_length=5, ignored_values={"977"}, related_fields="other_value" + ).collect() + assert results["check_results"].eq(True).all() def test_invalid_length_with_blank_values(self): - result = has_valid_multi_field_value_count( - {"4;1;977": self.multiple_values_series_with_blanks}, - 4, - ignored_values={"977"}, - ) - assert result.values == [False] + has_blank_df = pa.PolarsData(pl.DataFrame({"value": "4;1", "other_value": "1;2;; ;3"}).lazy(), "value") + results = has_valid_multi_field_value_count(has_blank_df, max_length=4, related_fields="other_value").collect() + assert results["check_results"].eq(False).all() - result = has_valid_multi_field_value_count({"4;1;977": self.multiple_values_series_with_blanks}, 5) - assert result.values == [False] + has_blank_df = pa.PolarsData(pl.DataFrame({"value": "4;1;977", "other_value": "1;2;; ;3;977"}).lazy(), "value") + results = has_valid_multi_field_value_count( + has_blank_df, max_length=4, ignored_values={"977"}, related_fields="other_value" + ).collect() + assert results["check_results"].eq(False).all() class TestInvalidEnumValue: def test_with_valid_enum_values(self): accepted_values = ["1", "2"] - result = is_valid_enum("1;2", accepted_values) - assert result is True + check_df = pa.PolarsData(pl.DataFrame({"value": "1;2"}).lazy(), "value") + results = is_valid_enum(check_df, accepted_values).collect() + assert results["check_results"].eq(True).all() def test_with_is_valid_enums(self): accepted_values = ["1", "2"] - result = is_valid_enum("0;3", accepted_values) - assert result is False + check_df = pa.PolarsData(pl.DataFrame({"value": "0;3"}).lazy(), "value") + results = is_valid_enum(check_df, accepted_values).collect() + assert results["check_results"].eq(False).all() def test_with_valid_blank(self): accepted_values = ["1", "2"] - result = is_valid_enum("", accepted_values, True) - assert result is True + check_df = pa.PolarsData(pl.DataFrame({"value": ""}).lazy(), "value") + results = is_valid_enum(check_df, accepted_values, accept_blank=True).collect() + assert results["check_results"].eq(True).all() def test_with_invalid_blank(self): accepted_values = ["1", "2"] - result = is_valid_enum("", accepted_values) - assert result is False + check_df = pa.PolarsData(pl.DataFrame({"value": ""}).lazy(), "value") + results = is_valid_enum(check_df, accepted_values).collect() + assert results["check_results"].eq(False).all() class TestIsNumber: @@ -268,38 +328,48 @@ def test_invalid_blank(self): class TestConditionalFieldConflict: def test_conditional_field_conflict_correct(self): # if ct_loan_term_flag != 900 then ct_loan_term must be blank - series = pd.Series([""], name="ct_loan_term", index=[2]) condition_values: set[str] = {"900"} - - result1 = has_no_conditional_field_conflict({"988": series}, condition_values) - assert result1.values == [True] + check_df = pa.PolarsData(pl.DataFrame({"ct_loan_term_flag": "988", "ct_loan_term": ""}).lazy(), "ct_loan_term") + results = has_no_conditional_field_conflict( + check_df, condition_values=condition_values, related_fields="ct_loan_term_flag" + ).collect() + assert results["check_results"].eq(True).all() # if ct_loan_term_flag == 900 then ct_loan_term must not be blank - series2 = pd.Series(["36"], name="ct_loan_term", index=[2]) - condition_values2: set[str] = {"900"} - result2 = has_no_conditional_field_conflict({"900": series2}, condition_values2) - assert result2.values == [True] + check_df = pa.PolarsData( + pl.DataFrame({"ct_loan_term_flag": "900", "ct_loan_term": "36"}).lazy(), "ct_loan_term" + ) + results = has_no_conditional_field_conflict( + check_df, condition_values=condition_values, related_fields="ct_loan_term_flag" + ).collect() + assert results["check_results"].eq(True).all() def test_conditional_field_conflict_incorrect(self): # if ct_loan_term_flag != 900 then ct_loan_term must be blank # in this test, ct_loan_term_flag is not 900 and ct_loan_term is # NOT blank, so must return False - series = pd.Series(["36"], name="ct_loan_term", index=[2]) condition_values: set[str] = {"900"} - - result1 = has_no_conditional_field_conflict({"988": series}, condition_values) - assert result1.values == [False] + check_df = pa.PolarsData( + pl.DataFrame({"ct_loan_term_flag": "988", "ct_loan_term": "36"}).lazy(), "ct_loan_term" + ) + results = has_no_conditional_field_conflict( + check_df, condition_values=condition_values, related_fields="ct_loan_term_flag" + ).collect() + assert results["check_results"].eq(False).all() # if ct_loan_term_flag == 900 then ct_loan_term must not be blank - # in this testm ct_loan_term is blank, so must return False - series2 = pd.Series([""], name="ct_loan_term", index=[2]) - condition_values2: set[str] = {"900"} - result2 = has_no_conditional_field_conflict({"900": series2}, condition_values2) - assert result2.values == [False] + # in this test ct_loan_term is blank, so must return False + check_df = pa.PolarsData(pl.DataFrame({"ct_loan_term_flag": "900", "ct_loan_term": ""}).lazy(), "ct_loan_term") + results = has_no_conditional_field_conflict( + check_df, condition_values=condition_values, related_fields="ct_loan_term_flag" + ).collect() + assert results["check_results"].eq(False).all() - series3 = pd.Series([" "], name="ct_loan_term", index=[2]) - result3 = has_no_conditional_field_conflict({"900": series3}, condition_values2) - assert result3.values == [False] + check_df = pa.PolarsData(pl.DataFrame({"ct_loan_term_flag": "900", "ct_loan_term": " "}).lazy(), "ct_loan_term") + results = has_no_conditional_field_conflict( + check_df, condition_values=condition_values, related_fields="ct_loan_term_flag" + ).collect() + assert results["check_results"].eq(False).all() class TestEnumValueConflict: @@ -320,25 +390,24 @@ def test_enum_value_confict_correct(self): ] # If ct_credit_product == 1, 2 then pricing_mca_addcost_flag must not equal 999 - pricing_mca_addcost_flag_series_1 = pd.Series(["900"], name="pricing_mca_addcost_flag", index=[2]) - - ct_credit_product_1 = "1" - - pricing_mca_addcost_flag_result_1 = has_valid_enum_pair( - {ct_credit_product_1: pricing_mca_addcost_flag_series_1}, - pricing_mca_addcost_flag_conditions, + check_df = pa.PolarsData( + pl.DataFrame({"ct_credit_product": "1", "pricing_mca_addcost_flag": "900"}).lazy(), + "pricing_mca_addcost_flag", ) - assert pricing_mca_addcost_flag_result_1.values == [True] + results = has_valid_enum_pair( + check_df, conditions=pricing_mca_addcost_flag_conditions, related_fields="ct_credit_product" + ).collect() + assert results["check_results"].eq(True).all() # If ct_credit_product == 988 then pricing_mca_addcost_flag must equal 999 - ct_credit_product_2 = "988" - # Case when pricing_mca_addcost_flag = 999 - pricing_mca_addcost_flag_series_2 = pd.Series(["999"], name="pricing_mca_addcost_flag", index=[2]) - pricing_mca_addcost_flag_result_2 = has_valid_enum_pair( - {ct_credit_product_2: pricing_mca_addcost_flag_series_2}, - pricing_mca_addcost_flag_conditions, + check_df = pa.PolarsData( + pl.DataFrame({"ct_credit_product": "988", "pricing_mca_addcost_flag": "999"}).lazy(), + "pricing_mca_addcost_flag", ) - assert pricing_mca_addcost_flag_result_2.values == [True] + results = has_valid_enum_pair( + check_df, conditions=pricing_mca_addcost_flag_conditions, related_fields="ct_credit_product" + ).collect() + assert results["check_results"].eq(True).all() # If there is more than one condition: """ If action_taken is equal to 3 THEN @@ -366,17 +435,18 @@ def test_enum_value_confict_correct(self): }, ] # If action_taken is 3, and denial_reasons must not equal 999, - denial_reasons_1 = pd.Series(["988"], name="denial_reasons", index=[2]) - action_taken_1 = "3" - - denial_reason_result_1 = has_valid_enum_pair({action_taken_1: denial_reasons_1}, denial_reasons_conditions) - assert denial_reason_result_1.values == [True] + check_df = pa.PolarsData(pl.DataFrame({"action_taken": "3", "denial_reasons": "988"}).lazy(), "denial_reasons") + results = has_valid_enum_pair( + check_df, conditions=denial_reasons_conditions, related_fields="action_taken" + ).collect() + assert results["check_results"].eq(True).all() # If action_taken is NOT 3, and denial_reasons must equal 999 - denial_reasons_2 = pd.Series(["999"], name="denial_reasons", index=[2]) - action_taken_2 = "1" - denial_reason_result_2 = has_valid_enum_pair({action_taken_2: denial_reasons_2}, denial_reasons_conditions) - assert denial_reason_result_2.values == [True] + check_df = pa.PolarsData(pl.DataFrame({"action_taken": "1", "denial_reasons": "999"}).lazy(), "denial_reasons") + results = has_valid_enum_pair( + check_df, conditions=denial_reasons_conditions, related_fields="action_taken" + ).collect() + assert results["check_results"].eq(True).all() def test_enum_value_confict_incorrect(self): pricing_mca_addcost_flag_conditions = [ @@ -395,25 +465,24 @@ def test_enum_value_confict_incorrect(self): ] # If ct_credit_product == 1, 2 then pricing_mca_addcost_flag must not equal 999 - pricing_mca_addcost_flag_series_1 = pd.Series(["999"], name="pricing_mca_addcost_flag", index=[2]) - - ct_credit_product_1 = "1" - - pricing_mca_addcost_flag_result_1 = has_valid_enum_pair( - {ct_credit_product_1: pricing_mca_addcost_flag_series_1}, - pricing_mca_addcost_flag_conditions, + check_df = pa.PolarsData( + pl.DataFrame({"ct_credit_product": "1", "pricing_mca_addcost_flag": "999"}).lazy(), + "pricing_mca_addcost_flag", ) - assert pricing_mca_addcost_flag_result_1.values == [False] + results = has_valid_enum_pair( + check_df, conditions=pricing_mca_addcost_flag_conditions, related_fields="ct_credit_product" + ).collect() + assert results["check_results"].eq(False).all() # If ct_credit_product == 988 then pricing_mca_addcost_flag must equal 999 - ct_credit_product_2 = "988" - # Case when pricing_mca_addcost_flag = 999 - pricing_mca_addcost_flag_series_2 = pd.Series(["900"], name="pricing_mca_addcost_flag", index=[2]) - pricing_mca_addcost_flag_result_2 = has_valid_enum_pair( - {ct_credit_product_2: pricing_mca_addcost_flag_series_2}, - pricing_mca_addcost_flag_conditions, + check_df = pa.PolarsData( + pl.DataFrame({"ct_credit_product": "988", "pricing_mca_addcost_flag": "900"}).lazy(), + "pricing_mca_addcost_flag", ) - assert pricing_mca_addcost_flag_result_2.values == [False] + results = has_valid_enum_pair( + check_df, conditions=pricing_mca_addcost_flag_conditions, related_fields="ct_credit_product" + ).collect() + assert results["check_results"].eq(False).all() # If there is more than one condition: """ If action_taken is equal to 3 THEN @@ -441,17 +510,18 @@ def test_enum_value_confict_incorrect(self): }, ] # If action_taken is 3, and denial_reasons must not equal 999, - denial_reasons_1 = pd.Series(["999"], name="denial_reasons", index=[2]) - action_taken_1 = "3" - - denial_reason_result_1 = has_valid_enum_pair({action_taken_1: denial_reasons_1}, denial_reasons_conditions) - assert denial_reason_result_1.values == [False] + check_df = pa.PolarsData(pl.DataFrame({"action_taken": "3", "denial_reasons": "999"}).lazy(), "denial_reasons") + results = has_valid_enum_pair( + check_df, conditions=denial_reasons_conditions, related_fields="action_taken" + ).collect() + assert results["check_results"].eq(False).all() # If action_taken is NOT 3, and denial_reasons must equal 999 - denial_reasons_2 = pd.Series(["988"], name="denial_reasons", index=[2]) - action_taken_2 = "1" - denial_reason_result_2 = has_valid_enum_pair({action_taken_2: denial_reasons_2}, denial_reasons_conditions) - assert denial_reason_result_2.values == [False] + check_df = pa.PolarsData(pl.DataFrame({"action_taken": "1", "denial_reasons": "988"}).lazy(), "denial_reasons") + results = has_valid_enum_pair( + check_df, conditions=denial_reasons_conditions, related_fields="action_taken" + ).collect() + assert results["check_results"].eq(False).all() class TestHasCorrectLength: @@ -573,73 +643,97 @@ def test_with_larger_numbers(self): class TestHasValidFormat: def test_with_valid_data_alphanumeric(self): - assert has_valid_format("1", "^[0-9A-Z]$") is True - assert has_valid_format("A", "^[0-9A-Z]$") is True - assert has_valid_format("1ABC", "^[0-9A-Z]+$") is True + check_df = pa.PolarsData(pl.DataFrame({"value": "1"}).lazy(), "value") + results = has_valid_format(check_df, regex="^[0-9A-Z]$").collect() + assert results["check_results"].eq(True).all() + + check_df = pa.PolarsData(pl.DataFrame({"value": "A"}).lazy(), "value") + results = has_valid_format(check_df, regex="^[0-9A-Z]$").collect() + assert results["check_results"].eq(True).all() + + check_df = pa.PolarsData(pl.DataFrame({"value": "1ABC"}).lazy(), "value") + results = has_valid_format(check_df, regex="^[0-9A-Z]+$").collect() + assert results["check_results"].eq(True).all() def test_with_invalid_data_alphanumeric(self): - assert has_valid_format("a", "^[0-9A-Z]$") is False - assert has_valid_format("aaaa", "^[0-9A-Z]+$") is False - assert has_valid_format("!", "^[0-9A-Z]$") is False + check_df = pa.PolarsData(pl.DataFrame({"value": "a"}).lazy(), "value") + results = has_valid_format(check_df, regex="^[0-9A-Z]$").collect() + assert results["check_results"].eq(False).all() + + check_df = pa.PolarsData(pl.DataFrame({"value": "aaaa"}).lazy(), "value") + results = has_valid_format(check_df, regex="^[0-9A-Z]+$").collect() + assert results["check_results"].eq(False).all() + + check_df = pa.PolarsData(pl.DataFrame({"value": "!"}).lazy(), "value") + results = has_valid_format(check_df, regex="^[0-9A-Z]$").collect() + assert results["check_results"].eq(False).all() def test_with_accepting_blank(self): - assert has_valid_format("", "^[0-9A-Z]+$", True) is True + check_df = pa.PolarsData(pl.DataFrame({"value": ""}).lazy(), "value") + results = has_valid_format(check_df, regex="^[0-9A-Z]+$", accept_blank=True).collect() + assert results["check_results"].eq(True).all() def test_with_not_accepting_blank(self): - assert has_valid_format("", "^[0-9A-Z]+$") is False + check_df = pa.PolarsData(pl.DataFrame({"value": ""}).lazy(), "value") + results = has_valid_format(check_df, regex="^[0-9A-Z]+$").collect() + assert results["check_results"].eq(False).all() # tests with different regex def test_with_valid_data_ip(self): - assert has_valid_format("192.168.0.1", "^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$") is True - assert has_valid_format("192.168.120.100", "^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$") is True + check_df = pa.PolarsData(pl.DataFrame({"value": "192.168.0.1"}).lazy(), "value") + results = has_valid_format(check_df, regex=r"^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$").collect() + assert results["check_results"].eq(True).all() + + check_df = pa.PolarsData(pl.DataFrame({"value": "192.168.120.100"}).lazy(), "value") + results = has_valid_format(check_df, regex=r"^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$").collect() + assert results["check_results"].eq(True).all() def test_with_invalid_data_ip(self): - assert has_valid_format("192.168.0.1000", "^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$") is False - assert has_valid_format("192.168.0", "^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$") is False + check_df = pa.PolarsData(pl.DataFrame({"value": "192.168.0.1000"}).lazy(), "value") + results = has_valid_format(check_df, regex=r"^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$").collect() + assert results["check_results"].eq(False).all() + + check_df = pa.PolarsData(pl.DataFrame({"value": "192.168.0"}).lazy(), "value") + results = has_valid_format(check_df, regex=r"^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$").collect() + assert results["check_results"].eq(False).all() class TestIsUniqueColumn: - series = pd.Series(["ABC123"], name="id", index=[1]) - other_series = pd.Series(["DEF456"], name="id", index=[3]) - invalid_series = pd.Series(["ABC123", "ABC123"], name="id", index=[1, 2]) - multi_invalid_series = pd.Series(["GHI123", "GHI123", "GHI123"], name="id", index=[3, 4, 5]) - blank_value_series = pd.Series([""], name="id", index=[1]) + series = pl.Series(values=["ABC123"], name="id") + other_series = pl.Series(values=["DEF456"], name="id") + invalid_series = pl.Series(values=["ABC123", "ABC123"], name="id") + multi_invalid_series = pl.Series(values=["GHI123", "GHI123", "GHI123"], name="id") + blank_value_series = pl.Series(values=[""], name="id") def test_with_valid_series(self): - result = is_unique_column({"ABC123": self.series}) - assert result.values == [True] + check_df = pa.PolarsData(pl.DataFrame({"uid": "ABC123"}).lazy(), "uid") + results = is_unique_column(check_df).collect() + assert results["check_results"].eq(True).all() def test_with_multiple_valid_series(self): - result = is_unique_column({"ABC123": self.series, "DEF456": self.other_series}) - assert result.values[0] and result.values[1] + check_df = pa.PolarsData(pl.DataFrame({"uid": ["ABC123", "ABC456"]}).lazy(), "uid") + results = is_unique_column(check_df).collect() + assert results["check_results"].eq(True).all() def test_with_invalid_series(self): - result = is_unique_column({"ABC123": self.invalid_series}) - assert not result.values.all() + check_df = pa.PolarsData(pl.DataFrame({"uid": ["ABC123", "ABC123"]}).lazy(), "uid") + results = is_unique_column(check_df).collect() + assert results["check_results"].eq(False).all() def test_with_multiple_items_series(self): - result = is_unique_column({"GHI123": self.multi_invalid_series}) - assert not result.values.all() - - def test_with_multiple_invalid_series(self): - result = is_unique_column({"ABC123": self.invalid_series, "GHI123": self.multi_invalid_series}) - # ALL rows should be FALSE - assert ( - not result.values[0] - and not result.values[1] - and not result.values[2] - and not result.values[3] - and not result.values[4] - ) + check_df = pa.PolarsData(pl.DataFrame({"uid": ["ABC123", "ABC456", "ABC123", "ABC456"]}).lazy(), "uid") + results = is_unique_column(check_df).collect() + assert results["check_results"].eq(False).all() def test_with_multiple_mix_series(self): - result = is_unique_column({"ABC123": self.invalid_series, "DEF456": self.other_series}) - # first two rows should be FALSE and last Row should be TRUE - assert not result.values[0] and not result.values[1] and result.values[2] + check_df = pa.PolarsData(pl.DataFrame({"uid": ["ABC123", "ABC456", "ABC123"]}).lazy(), "uid") + results = is_unique_column(check_df).collect() + assert not results["check_results"][0] and results["check_results"][1] and not results["check_results"][2] def test_with_blank_value_series(self): - result = is_unique_column({"": self.blank_value_series}) - assert result.values == [True] + check_df = pa.PolarsData(pl.DataFrame({"uid": [""]}).lazy(), "uid") + results = is_unique_column(check_df).collect() + assert results["check_results"].eq(True).all() class TestHasValidFieldsetPair: @@ -650,10 +744,16 @@ def test_with_correct_is_not_equal_condition(self): "field2": (1, True, ""), "field3": (2, True, ""), } - series = pd.Series(["0"], name="num_principal_owners", index=[1]) - groupby_values = tuple(["", "", ""]) - result1 = has_valid_fieldset_pair({groupby_values: series}, condition_values, should_fieldset_key_equal_to) - assert result1.values == [True] + check_data = {"num_principal_owners": "0", "field1": "", "field2": "", "field3": ""} + + check_df = pa.PolarsData(pl.DataFrame(check_data).lazy(), "num_principal_owners") + results = has_valid_fieldset_pair( + check_df, + condition_values=condition_values, + related_fields=["field1", "field2", "field3"], + should_fieldset_key_equal_to=should_fieldset_key_equal_to, + ).collect() + assert results["check_results"].eq(True).all() def test_with_correct_is_equal_condition(self): condition_values = ["0", ""] @@ -662,10 +762,16 @@ def test_with_correct_is_equal_condition(self): "field2": (1, False, ""), "field3": (2, False, ""), } - series = pd.Series(["0"], name="num_principal_owners", index=[1]) - groupby_values = tuple(["999", "999", "0"]) - result1 = has_valid_fieldset_pair({groupby_values: series}, condition_values, should_fieldset_key_equal_to) - assert result1.values == [True] + check_data = {"num_principal_owners": "0", "field1": "999", "field2": "999", "field3": "0"} + + check_df = pa.PolarsData(pl.DataFrame(check_data).lazy(), "num_principal_owners") + results = has_valid_fieldset_pair( + check_df, + condition_values=condition_values, + related_fields=["field1", "field2", "field3"], + should_fieldset_key_equal_to=should_fieldset_key_equal_to, + ).collect() + assert results["check_results"].eq(True).all() def test_with_correct_is_equal_and_not_equal_conditions(self): condition_values = ["0", ""] @@ -674,13 +780,25 @@ def test_with_correct_is_equal_and_not_equal_conditions(self): "field2": (1, True, "999"), "field3": (2, True, "0"), "field4": (3, False, ""), - "fiedl5": (4, False, ""), + "field5": (4, False, ""), + } + check_data = { + "num_principal_owners": "0", + "field1": "999", + "field2": "999", + "field3": "0", + "field4": "1", + "field5": "2", } - series = pd.Series(["0"], name="num_principal_owners", index=[1]) - groupby_values = tuple(["999", "999", "0", "1", "2"]) - result1 = has_valid_fieldset_pair({groupby_values: series}, condition_values, should_fieldset_key_equal_to) - assert result1.values == [True] + check_df = pa.PolarsData(pl.DataFrame(check_data).lazy(), "num_principal_owners") + results = has_valid_fieldset_pair( + check_df, + condition_values=condition_values, + related_fields=["field1", "field2", "field3", "field4", "field5"], + should_fieldset_key_equal_to=should_fieldset_key_equal_to, + ).collect() + assert results["check_results"].eq(True).all() def test_with_value_not_in_condition_values(self): condition_values = ["0", ""] @@ -688,14 +806,26 @@ def test_with_value_not_in_condition_values(self): "field1": (0, True, "999"), "field2": (1, True, "999"), "field3": (2, True, "0"), - "fiedl4": (3, False, "1"), + "field4": (3, False, "1"), "field5": (4, False, "2"), } + check_data = { + "num_principal_owners": "2", + "field1": "999", + "field2": "999", + "field3": "0", + "field4": "1", + "field5": "2", + } - series = pd.Series(["2"], name="num_principal_owners", index=[1]) - groupby_values = tuple(["999", "999", "0", "1", "2"]) - result1 = has_valid_fieldset_pair({groupby_values: series}, condition_values, should_fieldset_key_equal_to) - assert result1.values == [True] + check_df = pa.PolarsData(pl.DataFrame(check_data).lazy(), "num_principal_owners") + results = has_valid_fieldset_pair( + check_df, + condition_values=condition_values, + related_fields=["field1", "field2", "field3", "field4", "field5"], + should_fieldset_key_equal_to=should_fieldset_key_equal_to, + ).collect() + assert results["check_results"].eq(True).all() def test_with_incorrect_is_not_equal_condition(self): condition_values = ["0", ""] @@ -704,11 +834,16 @@ def test_with_incorrect_is_not_equal_condition(self): "field2": (1, True, ""), "field3": (2, True, ""), } + check_data = {"num_principal_owners": "0", "field1": "999", "field2": "999", "field3": "999"} - series = pd.Series(["0"], name="num_principal_owners", index=[1]) - groupby_values = tuple(["999", "999", "999"]) - result1 = has_valid_fieldset_pair({groupby_values: series}, condition_values, should_fieldset_key_equal_to) - assert result1.values == [False] + check_df = pa.PolarsData(pl.DataFrame(check_data).lazy(), "num_principal_owners") + results = has_valid_fieldset_pair( + check_df, + condition_values=condition_values, + related_fields=["field1", "field2", "field3"], + should_fieldset_key_equal_to=should_fieldset_key_equal_to, + ).collect() + assert results["check_results"].eq(False).all() def test_with_incorrect_is_equal_condition(self): condition_values = ["0", ""] @@ -717,11 +852,16 @@ def test_with_incorrect_is_equal_condition(self): "field2": (1, False, ""), "field3": (2, False, ""), } + check_data = {"num_principal_owners": "0", "field1": "", "field2": "", "field3": ""} - series = pd.Series(["0"], name="num_principal_owners", index=[1]) - groupby_values = tuple(["", "", ""]) - result1 = has_valid_fieldset_pair({groupby_values: series}, condition_values, should_fieldset_key_equal_to) - assert result1.values == [False] + check_df = pa.PolarsData(pl.DataFrame(check_data).lazy(), "num_principal_owners") + results = has_valid_fieldset_pair( + check_df, + condition_values=condition_values, + related_fields=["field1", "field2", "field3"], + should_fieldset_key_equal_to=should_fieldset_key_equal_to, + ).collect() + assert results["check_results"].eq(False).all() def test_with_incorrect_is_equal_and_not_equal_conditions(self): condition_values = ["0", ""] @@ -732,11 +872,23 @@ def test_with_incorrect_is_equal_and_not_equal_conditions(self): "field4": (3, False, ""), "field5": (4, False, ""), } + check_data = { + "num_principal_owners": "0", + "field1": "", + "field2": "", + "field3": "3", + "field4": "4", + "field5": "5", + } - series = pd.Series(["0"], name="num_principal_owners", index=[1]) - groupby_values = tuple(["", "", "3", "4", "5"]) - result1 = has_valid_fieldset_pair({groupby_values: series}, condition_values, should_fieldset_key_equal_to) - assert result1.values == [False] + check_df = pa.PolarsData(pl.DataFrame(check_data).lazy(), "num_principal_owners") + results = has_valid_fieldset_pair( + check_df, + condition_values=condition_values, + related_fields=["field1", "field2", "field3", "field4", "field5"], + should_fieldset_key_equal_to=should_fieldset_key_equal_to, + ).collect() + assert results["check_results"].eq(False).all() class TestIsValidId: diff --git a/tests/test_cli.py b/tests/test_cli.py index 9280c4f1..3c723aa6 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -1,5 +1,4 @@ from pathlib import Path -import os import pytest from typer.testing import CliRunner @@ -7,9 +6,8 @@ from regtech_data_validator import cli cli_runner = CliRunner(mix_stderr=False) -data_dir = f'{os.path.dirname(os.path.realpath(__file__))}/data' -pass_file = f'{data_dir}/sbl-validations-pass.csv' -fail_file = f'{data_dir}/sbl-validations-fail.csv' +pass_file = './tests/data/sblar_no_findings.csv' +fail_file = './tests/data/all_syntax_errors.csv' class TestParseKeyValue: @@ -45,51 +43,51 @@ def test_defaults(self): class TestValidateCommand: - valid_lei_context = cli.KeyValueOpt('lei', '000TESTFIUIDDONOTUSE') + valid_lei_context = cli.KeyValueOpt('lei', '123456789TESTBANK123') invalid_lei_context = cli.KeyValueOpt('lei', 'XXXXXXXXXXXXXXXXXXXX') pass_path = Path(pass_file) fail_path = Path(fail_file) def test_pass_file_defaults(self): - is_valid, findings_df = cli.validate(path=self.pass_path) + status, findings_df = cli.validate(path=self.pass_path) - assert is_valid + assert status == 'SUCCESS' def test_pass_file_with_valid_context(self): - is_valid, findings_df = cli.validate(path=self.pass_path, context=[self.valid_lei_context]) + status, findings_df = cli.validate(path=self.pass_path, context=[self.valid_lei_context]) - assert is_valid + assert status == 'SUCCESS' def test_pass_file_with_invalid_context(self): - is_valid, findings_df = cli.validate(path=self.pass_path, context=[self.invalid_lei_context]) + status, findings_df = cli.validate(path=self.pass_path, context=[self.invalid_lei_context]) - assert not is_valid + assert status == 'FAILURE' def test_fail_file_csv_output(self): - is_valid, findings_df = cli.validate(path=self.fail_path, output=cli.OutputFormat.CSV) + status, findings_df = cli.validate(path=self.fail_path, output=cli.OutputFormat.CSV) - assert not is_valid + assert status == 'FAILURE' def test_fail_file_json_output(self): - is_valid, findings_df = cli.validate(path=self.fail_path, output=cli.OutputFormat.JSON) + status, findings_df = cli.validate(path=self.fail_path, output=cli.OutputFormat.JSON) - assert not is_valid + assert status == 'FAILURE' def test_fail_file_pandas_output(self): - is_valid, findings_df = cli.validate(path=self.fail_path, output=cli.OutputFormat.PANDAS) + status, findings_df = cli.validate(path=self.fail_path, output=cli.OutputFormat.POLARS) - assert not is_valid + assert status == 'FAILURE' def test_fail_file_table_output(self): - is_valid, findings_df = cli.validate(path=self.fail_path, output=cli.OutputFormat.TABLE) + status, findings_df = cli.validate(path=self.fail_path, output=cli.OutputFormat.TABLE) - assert not is_valid + assert status == 'FAILURE' def test_fail_download_output(self): - is_valid, findings_df = cli.validate(path=self.fail_path, output=cli.OutputFormat.DOWNLOAD) + status, findings_df = cli.validate(path=self.fail_path, output=cli.OutputFormat.DOWNLOAD) - assert not is_valid + assert status == 'FAILURE' class TestDescribeCli: @@ -113,8 +111,8 @@ def test_pass_file_defaults(self): result = cli_runner.invoke(cli.app, ['validate', pass_file]) assert result.exit_code == 0 - assert result.stdout == '' - assert result.stderr == 'status: SUCCESS, total errors: 0, findings: 0, validation phase: Logical\n' + assert result.stdout == '\n' + assert result.stderr == 'Status: SUCCESS, Total Errors: 0, Validation Phase: Logical\n' def test_pass_file_invalid_output_arg_value(self): result = cli_runner.invoke(cli.app, ['validate', pass_file, '--output', 'pdf']) @@ -126,7 +124,6 @@ def test_fail_file_defaults(self): assert result.exit_code == 0 assert result.stdout != '' - assert 'status: FAILURE' in result.stderr - assert 'total errors:' in result.stderr - assert 'findings:' in result.stderr - assert 'validation phase: Syntactical' in result.stderr + assert 'Status: FAILURE' in result.stderr + assert 'Total Errors:' in result.stderr + assert 'Validation Phase: Syntactical' in result.stderr diff --git a/tests/test_csv_to_code_differences.py b/tests/test_csv_to_code_differences.py index 1cff0c1c..c5b1de18 100644 --- a/tests/test_csv_to_code_differences.py +++ b/tests/test_csv_to_code_differences.py @@ -1,4 +1,7 @@ -from regtech_data_validator.phase_validations import get_phase_1_and_2_validations_for_lei +from regtech_data_validator.phase_validations import ( + get_phase_1_and_2_validations_for_lei, + get_phase_2_register_validations, +) from regtech_data_validator.validation_results import ValidationPhase import pandas as pd @@ -7,11 +10,13 @@ class TestCSVDifferences: def test_csv_differences(self): py_codes = get_phase_1_and_2_validations_for_lei() + reg_code = get_phase_2_register_validations() py_validations = [ {"validation_id": s.title, "type": s.severity, "description": s.description} for s in ( [v[ValidationPhase.SYNTACTICAL] for v in py_codes.values()] + [v[ValidationPhase.LOGICAL] for v in py_codes.values()] + + [v[ValidationPhase.LOGICAL] for v in reg_code.values()] ) for s in s ] diff --git a/tests/test_fig_anchors.py b/tests/test_fig_anchors.py deleted file mode 100644 index 16d06ff5..00000000 --- a/tests/test_fig_anchors.py +++ /dev/null @@ -1,39 +0,0 @@ -import requests - -from regtech_data_validator.phase_validations import get_phase_1_and_2_validations_for_lei -from regtech_data_validator.global_data import fig_base_url -from bs4 import BeautifulSoup - - -class TestFigAnchors: - - def test_fig_links(self): - - html_text = requests.get( - "https://www.consumerfinance.gov/data-research/small-business-lending/filing-instructions-guide/2024-guide/#4" - ).text - source_links = BeautifulSoup(html_text, 'html.parser') - - validators = get_phase_1_and_2_validations_for_lei() - checks = [] - validator_anchors = [] - fig_links = [] - - for k in validators.keys(): - v = validators[k] - for p in v.keys(): - checks.extend(v[p]) - - for check in checks: - validator_anchors.append({"id": check.title, "anchor": check.fig_link}) - - elements = source_links.find_all(lambda tag: tag.name == "a" and "Validation ID:" in tag.text) - for e in elements: - anchor = e.get('href') - id = e.text.split("Validation ID:")[1].strip() - fig_links.append({"id": id, "anchor": fig_base_url + anchor}) - - validator_anchors = sorted(validator_anchors, key=lambda d: d['id']) - fig_links = sorted(fig_links, key=lambda d: d['id']) - anchors = zip(validator_anchors, fig_links) - assert not any(x != y for x, y in anchors) diff --git a/tests/test_fig_links.py b/tests/test_fig_links.py index 16d06ff5..de3cc3d9 100644 --- a/tests/test_fig_links.py +++ b/tests/test_fig_links.py @@ -1,11 +1,14 @@ import requests -from regtech_data_validator.phase_validations import get_phase_1_and_2_validations_for_lei +from regtech_data_validator.phase_validations import ( + get_phase_1_and_2_validations_for_lei, + get_phase_2_register_validations, +) from regtech_data_validator.global_data import fig_base_url from bs4 import BeautifulSoup -class TestFigAnchors: +class TestFigLinks: def test_fig_links(self): @@ -15,6 +18,7 @@ def test_fig_links(self): source_links = BeautifulSoup(html_text, 'html.parser') validators = get_phase_1_and_2_validations_for_lei() + registers = get_phase_2_register_validations() checks = [] validator_anchors = [] fig_links = [] @@ -24,6 +28,11 @@ def test_fig_links(self): for p in v.keys(): checks.extend(v[p]) + for k in registers.keys(): + v = registers[k] + for p in v.keys(): + checks.extend(v[p]) + for check in checks: validator_anchors.append({"id": check.title, "anchor": check.fig_link}) diff --git a/tests/test_output_formats.py b/tests/test_output_formats.py index 87e71ec7..c45fd2a2 100644 --- a/tests/test_output_formats.py +++ b/tests/test_output_formats.py @@ -1,102 +1,190 @@ -import pandas as pd +import polars as pl import ujson +import tempfile +from pathlib import Path + +from regtech_data_validator import global_data from regtech_data_validator.data_formatters import df_to_csv, df_to_str, df_to_json, df_to_table, df_to_download +from regtech_data_validator.validation_results import ValidationPhase from textwrap import dedent class TestOutputFormat: # TODO: Figure out why uid.duplicates_in_dataset returns different findings for matched records - input_df = pd.DataFrame( + findings_df = pl.DataFrame( data=[ { - 'record_no': 1, - 'uid': '12345678901234567890', - 'field_name': 'uid', - 'field_value': '12345678901234567890', + 'validation_type': 'Error', 'validation_id': 'E3000', + 'validation_name': 'uid.duplicates_in_dataset', + 'row': 2, + 'unique_identifier': '12345678901234567890', + 'fig_link': global_data.fig_base_url + "#4.3.1", + 'scope': 'register', + 'phase': ValidationPhase.LOGICAL.value, + 'validation_description': dedent( + """\ + * Any 'unique identifier' may **not** be used in more than one + record within a small business lending application register. + """ + ), + 'field_1': 'uid', + 'value_1': '12345678901234567890', }, { - 'record_no': 2, - 'uid': '12345678901234567890', - 'field_name': 'uid', - 'field_value': '12345678901234567890', + 'validation_type': 'Error', 'validation_id': 'E3000', + 'validation_name': 'uid.duplicates_in_dataset', + 'row': 3, + 'unique_identifier': '12345678901234567890', + 'fig_link': global_data.fig_base_url + "#4.3.1", + 'scope': 'register', + 'phase': ValidationPhase.LOGICAL.value, + 'validation_description': dedent( + """\ + * Any 'unique identifier' may **not** be used in more than one + record within a small business lending application register. + """ + ), + 'field_1': 'uid', + 'value_1': '12345678901234567890', }, { - 'record_no': 3, - 'uid': '12345678901234567891', - 'field_name': 'action_taken', - 'field_value': '1', - 'validation_id': 'E2008', - }, - { - 'record_no': 3, - 'uid': '12345678901234567891', - 'field_name': 'amount_approved', - 'field_value': '', + 'validation_type': 'Error', 'validation_id': 'E2008', + 'validation_name': 'amount_approved.conditional_field_conflict', + 'row': 4, + 'unique_identifier': '12345678901234567891', + 'fig_link': global_data.fig_base_url + "#4.2.7", + 'scope': 'multi-field', + 'phase': ValidationPhase.LOGICAL.value, + 'validation_description': dedent( + """\ + * When 'action taken' does **not** equal 1 (originated) or + 2 (approved but not accepted), 'amount approved or originated' must be blank. + * When 'action taken' equals 1 or 2, 'amount approved or originated' must **not** be blank. + """ + ), + 'field_1': 'action_taken', + 'value_1': '1', + 'field_2': 'amount_approved', + 'value_2': '', }, ], ) - input_df.index.name = 'finding_no' - input_df.index += 1 - def test_output_pandas(self): + def test_output_polars(self): expected_output = dedent( """ - record_no uid field_name field_value validation_id - finding_no - 1 1 12345678901234567890 uid 12345678901234567890 E3000 - 2 2 12345678901234567890 uid 12345678901234567890 E3000 - 3 3 12345678901234567891 action_taken 1 E2008 - 4 3 12345678901234567891 amount_approved E2008 + shape: (3, 13) + ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐ + │ val ┆ val ┆ val ┆ row ┆ uni ┆ fig ┆ sco ┆ pha ┆ val ┆ fie ┆ val ┆ fie ┆ val │ + │ ida ┆ ida ┆ ida ┆ --- ┆ que ┆ _li ┆ pe ┆ se ┆ ida ┆ ld_ ┆ ue_ ┆ ld_ ┆ ue_ │ + │ tio ┆ tio ┆ tio ┆ i64 ┆ _id ┆ nk ┆ --- ┆ --- ┆ tio ┆ 1 ┆ 1 ┆ 2 ┆ 2 │ + │ n_t ┆ n_i ┆ n_n ┆ ┆ ent ┆ --- ┆ str ┆ str ┆ n_d ┆ --- ┆ --- ┆ --- ┆ --- │ + │ ype ┆ d ┆ ame ┆ ┆ ifi ┆ str ┆ ┆ ┆ esc ┆ str ┆ str ┆ str ┆ str │ + │ --- ┆ --- ┆ --- ┆ ┆ er ┆ ┆ ┆ ┆ rip ┆ ┆ ┆ ┆ │ + │ str ┆ str ┆ str ┆ ┆ --- ┆ ┆ ┆ ┆ tio ┆ ┆ ┆ ┆ │ + │ ┆ ┆ ┆ ┆ str ┆ ┆ ┆ ┆ n ┆ ┆ ┆ ┆ │ + │ ┆ ┆ ┆ ┆ ┆ ┆ ┆ ┆ --- ┆ ┆ ┆ ┆ │ + │ ┆ ┆ ┆ ┆ ┆ ┆ ┆ ┆ str ┆ ┆ ┆ ┆ │ + ╞═════╪═════╪═════╪═════╪═════╪═════╪═════╪═════╪═════╪═════╪═════╪═════╪═════╡ + │ Err ┆ E30 ┆ uid ┆ 2 ┆ 123 ┆ htt ┆ reg ┆ Log ┆ * ┆ uid ┆ 123 ┆ nul ┆ nul │ + │ or ┆ 00 ┆ .du ┆ ┆ 456 ┆ ps: ┆ ist ┆ ica ┆ Any ┆ ┆ 456 ┆ l ┆ l │ + │ ┆ ┆ pli ┆ ┆ 789 ┆ //w ┆ er ┆ l ┆ 'un ┆ ┆ 789 ┆ ┆ │ + │ ┆ ┆ cat ┆ ┆ 012 ┆ ww. ┆ ┆ ┆ iqu ┆ ┆ 012 ┆ ┆ │ + │ ┆ ┆ es_ ┆ ┆ 345 ┆ con ┆ ┆ ┆ e ┆ ┆ 345 ┆ ┆ │ + │ ┆ ┆ in_ ┆ ┆ 678 ┆ sum ┆ ┆ ┆ ide ┆ ┆ 678 ┆ ┆ │ + │ ┆ ┆ dat ┆ ┆ 90 ┆ erf ┆ ┆ ┆ nti ┆ ┆ 90 ┆ ┆ │ + │ ┆ ┆ ase ┆ ┆ ┆ ina ┆ ┆ ┆ fie ┆ ┆ ┆ ┆ │ + │ ┆ ┆ t ┆ ┆ ┆ nce ┆ ┆ ┆ r' ┆ ┆ ┆ ┆ │ + │ ┆ ┆ ┆ ┆ ┆ .go ┆ ┆ ┆ may ┆ ┆ ┆ ┆ │ + │ ┆ ┆ ┆ ┆ ┆ … ┆ ┆ ┆ … ┆ ┆ ┆ ┆ │ + │ Err ┆ E30 ┆ uid ┆ 3 ┆ 123 ┆ htt ┆ reg ┆ Log ┆ * ┆ uid ┆ 123 ┆ nul ┆ nul │ + │ or ┆ 00 ┆ .du ┆ ┆ 456 ┆ ps: ┆ ist ┆ ica ┆ Any ┆ ┆ 456 ┆ l ┆ l │ + │ ┆ ┆ pli ┆ ┆ 789 ┆ //w ┆ er ┆ l ┆ 'un ┆ ┆ 789 ┆ ┆ │ + │ ┆ ┆ cat ┆ ┆ 012 ┆ ww. ┆ ┆ ┆ iqu ┆ ┆ 012 ┆ ┆ │ + │ ┆ ┆ es_ ┆ ┆ 345 ┆ con ┆ ┆ ┆ e ┆ ┆ 345 ┆ ┆ │ + │ ┆ ┆ in_ ┆ ┆ 678 ┆ sum ┆ ┆ ┆ ide ┆ ┆ 678 ┆ ┆ │ + │ ┆ ┆ dat ┆ ┆ 90 ┆ erf ┆ ┆ ┆ nti ┆ ┆ 90 ┆ ┆ │ + │ ┆ ┆ ase ┆ ┆ ┆ ina ┆ ┆ ┆ fie ┆ ┆ ┆ ┆ │ + │ ┆ ┆ t ┆ ┆ ┆ nce ┆ ┆ ┆ r' ┆ ┆ ┆ ┆ │ + │ ┆ ┆ ┆ ┆ ┆ .go ┆ ┆ ┆ may ┆ ┆ ┆ ┆ │ + │ ┆ ┆ ┆ ┆ ┆ … ┆ ┆ ┆ … ┆ ┆ ┆ ┆ │ + │ Err ┆ E20 ┆ amo ┆ 4 ┆ 123 ┆ htt ┆ mul ┆ Log ┆ * ┆ act ┆ 1 ┆ amo ┆ │ + │ or ┆ 08 ┆ unt ┆ ┆ 456 ┆ ps: ┆ ti- ┆ ica ┆ Whe ┆ ion ┆ ┆ unt ┆ │ + │ ┆ ┆ _ap ┆ ┆ 789 ┆ //w ┆ fie ┆ l ┆ n ┆ _ta ┆ ┆ _ap ┆ │ + │ ┆ ┆ pro ┆ ┆ 012 ┆ ww. ┆ ld ┆ ┆ 'ac ┆ ken ┆ ┆ pro ┆ │ + │ ┆ ┆ ved ┆ ┆ 345 ┆ con ┆ ┆ ┆ tio ┆ ┆ ┆ ved ┆ │ + │ ┆ ┆ .co ┆ ┆ 678 ┆ sum ┆ ┆ ┆ n ┆ ┆ ┆ ┆ │ + │ ┆ ┆ ndi ┆ ┆ 91 ┆ erf ┆ ┆ ┆ tak ┆ ┆ ┆ ┆ │ + │ ┆ ┆ tio ┆ ┆ ┆ ina ┆ ┆ ┆ en' ┆ ┆ ┆ ┆ │ + │ ┆ ┆ nal ┆ ┆ ┆ nce ┆ ┆ ┆ doe ┆ ┆ ┆ ┆ │ + │ ┆ ┆ _fi ┆ ┆ ┆ .go ┆ ┆ ┆ s ┆ ┆ ┆ ┆ │ + │ ┆ ┆ … ┆ ┆ ┆ … ┆ ┆ ┆ **n ┆ ┆ ┆ ┆ │ + │ ┆ ┆ ┆ ┆ ┆ ┆ ┆ ┆ … ┆ ┆ ┆ ┆ │ + └─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘ """ ).strip( '\n' ) # noqa: E501 - actual_output = df_to_str(self.input_df) + actual_output = df_to_str(self.findings_df) assert actual_output == expected_output def test_output_table(self): expected_output = dedent( """ - ╭──────────────┬─────────────┬──────────────────────┬─────────────────┬──────────────────────┬─────────────────╮ - │ finding_no │ record_no │ uid │ field_name │ field_value │ validation_id │ - ├──────────────┼─────────────┼──────────────────────┼─────────────────┼──────────────────────┼─────────────────┤ - │ 1 │ 1 │ 12345678901234567890 │ uid │ 12345678901234567890 │ E3000 │ - │ 2 │ 2 │ 12345678901234567890 │ uid │ 12345678901234567890 │ E3000 │ - │ 3 │ 3 │ 12345678901234567891 │ action_taken │ 1 │ E2008 │ - │ 4 │ 3 │ 12345678901234567891 │ amount_approved │ │ E2008 │ - ╰──────────────┴─────────────┴──────────────────────┴─────────────────┴──────────────────────┴─────────────────╯ + ╭────┬────────────────────────────────────────────────────┬────────────────────────────────────────────────────┬────────────────────────────────────────────────────╮ + │ │ 0 │ 1 │ 2 │ + ├────┼────────────────────────────────────────────────────┼────────────────────────────────────────────────────┼────────────────────────────────────────────────────┤ + │ 0 │ Error │ Error │ Error │ + │ 1 │ E3000 │ E3000 │ E2008 │ + │ 2 │ uid.duplicates_in_dataset │ uid.duplicates_in_dataset │ amount_approved.conditional_field_conflict │ + │ 3 │ 12345678901234567890 │ 12345678901234567890 │ 12345678901234567891 │ + │ 4 │ https://www.consumerfinance.gov/data-research/smal │ https://www.consumerfinance.gov/data-research/smal │ https://www.consumerfinance.gov/data-research/smal │ + │ 5 │ register │ register │ multi-field │ + │ 6 │ Logical │ Logical │ Logical │ + │ 7 │ * Any 'unique identifier' may **not** be used in m │ * Any 'unique identifier' may **not** be used in m │ * When 'action taken' does **not** equal 1 (origin │ + │ 8 │ uid │ uid │ action_taken │ + │ 9 │ 12345678901234567890 │ 12345678901234567890 │ 1 │ + │ 10 │ │ │ amount_approved │ + │ 11 │ │ │ │ + ╰────┴────────────────────────────────────────────────────┴────────────────────────────────────────────────────┴────────────────────────────────────────────────────╯ """ ).strip( '\n' ) # noqa: E501 - actual_output = df_to_table(self.input_df) + actual_output = df_to_table(self.findings_df) assert actual_output == expected_output def test_output_csv(self): expected_output = dedent( """ - finding_no,record_no,uid,field_name,field_value,validation_id - 1,1,12345678901234567890,uid,12345678901234567890,E3000 - 2,2,12345678901234567890,uid,12345678901234567890,E3000 - 3,3,12345678901234567891,action_taken,1,E2008 - 4,3,12345678901234567891,amount_approved,,E2008 + "validation_type","validation_id","validation_name","row","unique_identifier","fig_link","scope","phase","validation_description","field_1","value_1","field_2","value_2" + "Error","E2008","amount_approved.conditional_field_conflict",4,"12345678901234567891","https://www.consumerfinance.gov/data-research/small-business-lending/filing-instructions-guide/2024-guide/#4.2.7","multi-field","Logical","* When 'action taken' does **not** equal 1 (originated) or + 2 (approved but not accepted), 'amount approved or originated' must be blank. + * When 'action taken' equals 1 or 2, 'amount approved or originated' must **not** be blank. + ","action_taken","1","amount_approved","" + "Error","E3000","uid.duplicates_in_dataset",2,"12345678901234567890","https://www.consumerfinance.gov/data-research/small-business-lending/filing-instructions-guide/2024-guide/#4.3.1","register","Logical","* Any 'unique identifier' may **not** be used in more than one + record within a small business lending application register. + ","uid","12345678901234567890",, + "Error","E3000","uid.duplicates_in_dataset",3,"12345678901234567890","https://www.consumerfinance.gov/data-research/small-business-lending/filing-instructions-guide/2024-guide/#4.3.1","register","Logical","* Any 'unique identifier' may **not** be used in more than one + record within a small business lending application register. + ","uid","12345678901234567890",, """ ).strip( '\n' ) # noqa: E501 - actual_output = df_to_csv(self.input_df) + actual_output = df_to_csv(self.findings_df) assert actual_output.strip('\n') == expected_output def test_empty_results_json(self): expected_output = ujson.dumps([], indent=4, escape_forward_slashes=False) - actual_output = df_to_json(pd.DataFrame()) + actual_output = df_to_json(pl.DataFrame()) assert actual_output == expected_output @@ -116,7 +204,7 @@ def test_output_json(self): { "record_no": 3, "uid": "12345678901234567891", - "fields": [{"name": "amount_approved", "value": ""}, {"name": "action_taken", "value": "1"}], + "fields": [{"name": "action_taken", "value": "1"}, {"name": "amount_approved", "value": ""}], } ], }, @@ -146,143 +234,46 @@ def test_output_json(self): ] expected_output = ujson.dumps(results_object, indent=4, escape_forward_slashes=False) - actual_output = df_to_json(self.input_df) - assert actual_output == expected_output - - def test_output_json_with_max_group_size(self): - results_object = [ - { - "validation": { - "id": "E2008", - "name": "amount_approved.conditional_field_conflict", - "description": "* When 'action taken' does **not** equal 1 (originated) or \n2 (approved but not accepted), 'amount approved or originated' must be blank.\n* When 'action taken' equals 1 or 2, 'amount approved or originated' must **not** be blank.\n", - "severity": "Error", - "scope": "multi-field", - "fig_link": "https://www.consumerfinance.gov/data-research/small-business-lending/filing-instructions-guide/2024-guide/#4.2.7", - "is_truncated": False, - }, - "records": [ - { - "record_no": 3, - "uid": "12345678901234567891", - "fields": [{"name": "amount_approved", "value": ""}, {"name": "action_taken", "value": "1"}], - } - ], - }, - { - "validation": { - "id": "E3000", - "name": "uid.duplicates_in_dataset", - "description": "* Any 'unique identifier' may **not** be used in more than one \nrecord within a small business lending application register.\n", - "severity": "Error", - "scope": "register", - "fig_link": "https://www.consumerfinance.gov/data-research/small-business-lending/filing-instructions-guide/2024-guide/#4.3.1", - "is_truncated": True, - }, - "records": [ - { - "record_no": 1, - "uid": "12345678901234567890", - "fields": [{"name": "uid", "value": "12345678901234567890"}], - }, - ], - }, - ] - expected_output = ujson.dumps(results_object, indent=4, escape_forward_slashes=False) - - actual_output = df_to_json(self.input_df, max_group_size=1) - assert actual_output == expected_output - - def test_output_json_with_max_records(self): - results_object = [ - { - "validation": { - "id": "E0040", - "name": "app_method.invalid_enum_value", - "description": "* 'Application method' must equal 1, 2, 3, or 4.", - "severity": "Error", - "scope": "single-field", - "fig_link": "https://www.consumerfinance.gov/data-research/small-business-lending/filing-instructions-guide/2024-guide/#4.1.4", - "is_truncated": True, - }, - "records": [ - {"record_no": 4, "uid": "12345678901234567890", "fields": [{"name": "app_method", "value": "5"}]} - ], - }, - { - "validation": { - "id": "E2008", - "name": "amount_approved.conditional_field_conflict", - "description": "* When 'action taken' does **not** equal 1 (originated) or \n2 (approved but not accepted), 'amount approved or originated' must be blank.\n* When 'action taken' equals 1 or 2, 'amount approved or originated' must **not** be blank.\n", - "severity": "Error", - "scope": "multi-field", - "fig_link": "https://www.consumerfinance.gov/data-research/small-business-lending/filing-instructions-guide/2024-guide/#4.2.7", - "is_truncated": False, - }, - "records": [ - { - "record_no": 3, - "uid": "12345678901234567891", - "fields": [{"name": "amount_approved", "value": ""}, {"name": "action_taken", "value": "1"}], - } - ], - }, - ] - expected_output = ujson.dumps(results_object, indent=4, escape_forward_slashes=False) - - error_df = pd.DataFrame(self.input_df) - error_df.loc[-1] = [5, '12345678901234567890', 'app_method', '5', 'E0040'] - error_df.loc[-2] = [4, '12345678901234567890', 'app_method', '5', 'E0040'] - error_df.index = error_df.index + 2 - error_df.sort_index(inplace=True) - - actual_output = df_to_json(error_df, max_records=2) + actual_output = df_to_json(self.findings_df) assert actual_output == expected_output def test_download_csv(self): expected_output = dedent( """ - validation_type,validation_id,validation_name,row,unique_identifier,fig_link,validation_description,field_1,value_1,field_2,value_2 - "Error","E2008","amount_approved.conditional_field_conflict",4,"12345678901234567891","https://www.consumerfinance.gov/data-research/small-business-lending/filing-instructions-guide/2024-guide/#4.2.7","* When 'action taken' does **not** equal 1 (originated) or - 2 (approved but not accepted), 'amount approved or originated' must be blank. - * When 'action taken' equals 1 or 2, 'amount approved or originated' must **not** be blank. - ","amount_approved","","action_taken","1" - "Error","E3000","uid.duplicates_in_dataset",2,"12345678901234567890","https://www.consumerfinance.gov/data-research/small-business-lending/filing-instructions-guide/2024-guide/#4.3.1","* Any 'unique identifier' may **not** be used in more than one - record within a small business lending application register. - ","uid","12345678901234567890" - "Error","E3000","uid.duplicates_in_dataset",3,"12345678901234567890","https://www.consumerfinance.gov/data-research/small-business-lending/filing-instructions-guide/2024-guide/#4.3.1","* Any 'unique identifier' may **not** be used in more than one - record within a small business lending application register. - ","uid","12345678901234567890" + validation_type,validation_id,validation_name,row,unique_identifier,fig_link,validation_description,field_1,value_1,field_2,value_2 + "Error","E2008","amount_approved.conditional_field_conflict",4,"12345678901234567891","https://www.consumerfinance.gov/data-research/small-business-lending/filing-instructions-guide/2024-guide/#4.2.7","* When 'action taken' does **not** equal 1 (originated) or + 2 (approved but not accepted), 'amount approved or originated' must be blank. + * When 'action taken' equals 1 or 2, 'amount approved or originated' must **not** be blank. + ","action_taken","1","amount_approved","" + "Error","E3000","uid.duplicates_in_dataset",2,"12345678901234567890","https://www.consumerfinance.gov/data-research/small-business-lending/filing-instructions-guide/2024-guide/#4.3.1","* Any 'unique identifier' may **not** be used in more than one + record within a small business lending application register. + ","uid","12345678901234567890",, + "Error","E3000","uid.duplicates_in_dataset",3,"12345678901234567890","https://www.consumerfinance.gov/data-research/small-business-lending/filing-instructions-guide/2024-guide/#4.3.1","* Any 'unique identifier' may **not** be used in more than one + record within a small business lending application register. + ","uid","12345678901234567890",, """ ).strip('\n') - actual_output = df_to_download(self.input_df, error_count=2) - assert actual_output.strip() == expected_output - def test_download_max_message_csv(self): - expected_output = dedent( - """ - validation_type,validation_id,validation_name,row,unique_identifier,fig_link,validation_description,field_1,value_1,field_2,value_2 - "Your register contains 6 errors and warnings, however, only 3 records are displayed in this report. To see additional errors and warnings, correct the listed records, and upload a new file." - "Error","E2008","amount_approved.conditional_field_conflict",4,"12345678901234567891","https://www.consumerfinance.gov/data-research/small-business-lending/filing-instructions-guide/2024-guide/#4.2.7","* When 'action taken' does **not** equal 1 (originated) or - 2 (approved but not accepted), 'amount approved or originated' must be blank. - * When 'action taken' equals 1 or 2, 'amount approved or originated' must **not** be blank. - ","amount_approved","","action_taken","1" - "Error","E3000","uid.duplicates_in_dataset",2,"12345678901234567890","https://www.consumerfinance.gov/data-research/small-business-lending/filing-instructions-guide/2024-guide/#4.3.1","* Any 'unique identifier' may **not** be used in more than one - record within a small business lending application register. - ","uid","12345678901234567890" - "Error","E3000","uid.duplicates_in_dataset",3,"12345678901234567890","https://www.consumerfinance.gov/data-research/small-business-lending/filing-instructions-guide/2024-guide/#4.3.1","* Any 'unique identifier' may **not** be used in more than one - record within a small business lending application register. - ","uid","12345678901234567890" - """ - ).strip('\n') - actual_output = df_to_download(self.input_df, warning_count=1, error_count=5, max_errors=3) + gf = tempfile.NamedTemporaryFile(delete=False, mode='w', suffix='.csv') + temp_path = Path(gf.name) + df_to_download(self.findings_df, str(temp_path.resolve())) + with open(temp_path, 'r') as output: + actual_output = output.read() + print(f"{actual_output}") assert actual_output.strip() == expected_output + temp_path.unlink() def test_empty_download_csv(self): expected_output = dedent( """ - validation_type,validation_id,validation_name,row,unique_identifier,fig_link,validation_description, + "validation_type","validation_id","validation_name","row","unique_identifier","fig_link","validation_description" """ ).strip('\n') - actual_output = df_to_download(pd.DataFrame(), warning_count=0, error_count=0) - assert actual_output == expected_output + + gf = tempfile.NamedTemporaryFile(delete=False, mode='w', suffix='.csv') + temp_path = Path(gf.name) + df_to_download(pl.DataFrame(), str(temp_path.resolve())) + with open(temp_path, 'r') as output: + actual_output = output.read() + assert actual_output.strip() == expected_output + temp_path.unlink() diff --git a/tests/test_sample_data.py b/tests/test_sample_data.py index 279fc578..3033a503 100644 --- a/tests/test_sample_data.py +++ b/tests/test_sample_data.py @@ -1,59 +1,87 @@ -import pandas as pd -import pytest - -from regtech_data_validator.create_schemas import validate_phases +import polars as pl + +from regtech_data_validator.checks import Severity +from regtech_data_validator.validator import ( + validate_batch_csv, + get_phase_1_schema_for_lei, + get_phase_2_schema_for_lei, + get_register_schema, +) from regtech_data_validator.validation_results import ValidationPhase -GOOD_FILE_PATH = "./tests/data/sbl-validations-pass.csv" -BAD_FILE_PATH = "./tests/data/sbl-validations-fail.csv" +GOOD_FILE_PATH = "./tests/data/sblar_no_findings.csv" +ALL_SYNTAX_ERRORS = "./tests/data/all_syntax_errors.csv" +ALL_LOGIC_ERRORS = "./tests/data/all_logic_errors.csv" +ALL_LOGIC_WARNINGS = "./tests/data/all_logic_warnings.csv" class TestValidatingSampleData: - good_file_df = pd.read_csv(GOOD_FILE_PATH, dtype=str, na_filter=False) - bad_file_df = pd.read_csv(BAD_FILE_PATH, dtype=str, na_filter=False) - - def test_invalid_data_file(self): - failed_fpath = "./file-does-not-exist.csv" - with pytest.raises(Exception) as exc: - pd.read_csv(failed_fpath, dtype=str, na_filter=False) - assert exc.type is FileNotFoundError def test_run_validation_on_good_data_invalid_lei(self): lei = "000TESTFIUIDDONOTUS1" - results = validate_phases(self.good_file_df, {'lei': lei}) - - assert not results.is_valid + results = list(validate_batch_csv(GOOD_FILE_PATH, {'lei': lei})) - # Only 'uid.invalid_uid_lei' validation returned - assert len(results.findings['validation_id'].unique()) == 1 - assert len(results.findings['validation_id'] == 'W0003') > 0 - assert results.phase == ValidationPhase.LOGICAL.value + assert results[2].findings.select(pl.col('validation_id').eq('W0003').all()).item() + assert results[2].findings.select(pl.col('phase').eq(ValidationPhase.LOGICAL.value).all()).item() def test_run_validation_on_good_data_valid_lei(self): - lei = "000TESTFIUIDDONOTUSE" - results = validate_phases(self.good_file_df, {'lei': lei}) - - assert results.is_valid - assert results.findings.empty - assert results.phase == ValidationPhase.LOGICAL.value - - def test_run_validation_on_bad_data_invalid_lei(self): - lei = "000TESTFIUIDDONOTUS1" - results = validate_phases(self.bad_file_df, {'lei': lei}) - - assert not results.is_valid - - # 'uid.invalid_uid_lei' and other validations returned - assert len(results.findings['validation_id'].unique()) > 1 - assert len(results.findings['validation_id'] == 'W0003') > 0 - assert results.phase == ValidationPhase.SYNTACTICAL.value - - def test_run_validation_on_bad_data_valid_lei(self): - lei = "000TESTFIUIDDONOTUSE" - results = validate_phases(self.bad_file_df, {'lei': lei}) - - assert not results.is_valid - - # 'uid.invalid_uid_lei' and other validations returned - assert len(results.findings['validation_id'].unique()) > 1 - assert results.phase == ValidationPhase.SYNTACTICAL.value + lei = "123456789TESTBANK123" + results = list(validate_batch_csv(GOOD_FILE_PATH, {'lei': lei})) + + assert results[2].findings.is_empty() + + def test_all_syntax_errors(self): + results = list(validate_batch_csv(ALL_SYNTAX_ERRORS)) + + syntax_schema = get_phase_1_schema_for_lei() + syntax_checks = [check.title for col_schema in syntax_schema.columns.values() for check in col_schema.checks] + + # check that the findings validation_id Series contains at least 1 of every syntax check id + assert len(set(results[0].findings['validation_id'].to_list()).difference(set(syntax_checks))) == 0 + assert results[0].findings.select(pl.col('phase').eq(ValidationPhase.SYNTACTICAL.value).all()).item() + + def test_all_logic_errors(self): + vresults = [] + for vresult in validate_batch_csv(ALL_LOGIC_ERRORS): + vresults.append(vresult) + + results = pl.concat([vr.findings for vr in vresults], how="diagonal") + + logic_schema = get_phase_2_schema_for_lei() + register_schema = get_register_schema() + logic_checks = [ + check.title + for col_schema in logic_schema.columns.values() + for check in col_schema.checks + if check.severity == Severity.ERROR + ] + logic_checks.extend( + [check.title for col_schema in register_schema.columns.values() for check in col_schema.checks] + ) + + results = results.filter(pl.col('validation_type') == 'Error') + + # check that the findings validation_id Series contains at least 1 of every logic error check id + assert len(set(results['validation_id'].to_list()).difference(set(logic_checks))) == 0 + assert results.select(pl.col('phase').eq(ValidationPhase.LOGICAL.value).all()).item() + + def test_all_logic_warnings(self): + vresults = [] + for vresult in validate_batch_csv(ALL_LOGIC_WARNINGS, context={"lei": "000TESTFIUIDDONOTUSE"}): + vresults.append(vresult) + + results = pl.concat([vr.findings for vr in vresults], how="diagonal") + + logic_schema = get_phase_2_schema_for_lei() + logic_checks = [ + check.title + for col_schema in logic_schema.columns.values() + for check in col_schema.checks + if check.severity == Severity.WARNING + ] + + results = results.filter(pl.col('validation_type') == 'Warning') + + # check that the findings validation_id Series contains at least 1 of every logic warning check id + assert len(set(results['validation_id'].to_list()).difference(set(logic_checks))) == 0 + assert results.select(pl.col('phase').eq(ValidationPhase.LOGICAL.value).all()).item() diff --git a/tests/test_schema_functions.py b/tests/test_schema_functions.py index 92b02289..d0b10b0b 100644 --- a/tests/test_schema_functions.py +++ b/tests/test_schema_functions.py @@ -1,111 +1,140 @@ -import pandas as pd +import polars as pl import pytest +import tempfile +from pathlib import Path -from regtech_data_validator.create_schemas import ( - get_phase_1_schema_for_lei, - get_phase_2_schema_for_lei, - validate, - validate_phases, -) +from regtech_data_validator.validator import validate_batch_csv from regtech_data_validator.validation_results import ValidationPhase -class TestUtil: - def get_data(self, update_data: dict[str, list[str]] = {}) -> dict[str, list[str]]: - default = { - "uid": ["000TESTFIUIDDONOTUSEXGXVID11XTC1"], - "app_date": ["20241201"], - "app_method": ["1"], - "app_recipient": ["1"], - "ct_credit_product": ["988"], - "ct_credit_product_ff": [""], - "ct_guarantee": ["999"], - "ct_guarantee_ff": [""], - "ct_loan_term_flag": ["999"], - "ct_loan_term": [""], - "credit_purpose": ["999"], - "credit_purpose_ff": [""], - "amount_applied_for_flag": ["999"], - "amount_applied_for": [""], - "amount_approved": [""], - "action_taken": ["5"], - "action_taken_date": ["20241231"], - "denial_reasons": ["999"], - "denial_reasons_ff": [""], - "pricing_interest_rate_type": ["999"], - "pricing_init_rate_period": [""], - "pricing_fixed_rate": [""], - "pricing_adj_margin": [""], - "pricing_adj_index_name": ["999"], - "pricing_adj_index_name_ff": [""], - "pricing_adj_index_value": [""], - "pricing_origination_charges": [""], - "pricing_broker_fees": [""], - "pricing_initial_charges": [""], - "pricing_mca_addcost_flag": ["999"], - "pricing_mca_addcost": [""], - "pricing_prepenalty_allowed": ["999"], - "pricing_prepenalty_exists": ["999"], - "census_tract_adr_type": ["988"], - "census_tract_number": [""], - "gross_annual_revenue_flag": ["988"], - "gross_annual_revenue": [""], - "naics_code_flag": ["988"], - "naics_code": [""], - "number_of_workers": ["988"], - "time_in_business_type": ["988"], - "time_in_business": [""], - "business_ownership_status": ["988"], - "num_principal_owners_flag": ["988"], - "num_principal_owners": [""], - "po_1_ethnicity": [""], - "po_1_ethnicity_ff": [""], - "po_1_race": [""], - "po_1_race_anai_ff": [""], - "po_1_race_asian_ff": [""], - "po_1_race_baa_ff": [""], - "po_1_race_pi_ff": [""], - "po_1_gender_flag": [""], - "po_1_gender_ff": [""], - "po_2_ethnicity": [""], - "po_2_ethnicity_ff": [""], - "po_2_race": [""], - "po_2_race_anai_ff": [""], - "po_2_race_asian_ff": [""], - "po_2_race_baa_ff": [""], - "po_2_race_pi_ff": [""], - "po_2_gender_flag": [""], - "po_2_gender_ff": [""], - "po_3_ethnicity": [""], - "po_3_ethnicity_ff": [""], - "po_3_race": [""], - "po_3_race_anai_ff": [""], - "po_3_race_asian_ff": [""], - "po_3_race_baa_ff": [""], - "po_3_race_pi_ff": [""], - "po_3_gender_flag": [""], - "po_3_gender_ff": [""], - "po_4_ethnicity": [""], - "po_4_ethnicity_ff": [""], - "po_4_race": [""], - "po_4_race_anai_ff": [""], - "po_4_race_asian_ff": [""], - "po_4_race_baa_ff": [""], - "po_4_race_pi_ff": [""], - "po_4_gender_flag": [""], - "po_4_gender_ff": [""], - } - default.update(update_data) - return default - - +@pytest.fixture +def csv_df_file(request): + df_data = get_data(request.param) if hasattr(request, 'param') else get_data() + with tempfile.NamedTemporaryFile(delete=False, mode='w', suffix='.csv') as gf: + gf.write(pl.DataFrame(df_data).write_csv()) + temp_path = Path(gf.name) + yield temp_path + temp_path.unlink() + + +@pytest.fixture +def csv_df_mission_column_file(request): + df_data = get_data(request.param) if hasattr(request, 'param') else get_data() + df_data = df_data.pop('uid') + with tempfile.NamedTemporaryFile(delete=False, mode='w', suffix='.csv') as gf: + gf.write(pl.DataFrame(df_data).write_csv()) + temp_path = Path(gf.name) + yield temp_path + temp_path.unlink() + + +@pytest.fixture +def csv_df_same_uids_file(request): + df_data = get_data(request.param) if hasattr(request, 'param') else get_data() + df = pl.concat([pl.DataFrame(df_data), pl.DataFrame(df_data)]) + with tempfile.NamedTemporaryFile(delete=False, mode='w', suffix='.csv') as gf: + gf.write(df.write_csv()) + temp_path = Path(gf.name) + yield temp_path + temp_path.unlink() + + +def get_data(update_data: dict[str, list[str]] = {}) -> dict[str, list[str]]: + default = { + "uid": ["000TESTFIUIDDONOTUSEXGXVID11XTC1"], + "app_date": ["20241201"], + "app_method": ["1"], + "app_recipient": ["1"], + "ct_credit_product": ["988"], + "ct_credit_product_ff": [""], + "ct_guarantee": ["999"], + "ct_guarantee_ff": [""], + "ct_loan_term_flag": ["999"], + "ct_loan_term": [""], + "credit_purpose": ["999"], + "credit_purpose_ff": [""], + "amount_applied_for_flag": ["999"], + "amount_applied_for": [""], + "amount_approved": [""], + "action_taken": ["5"], + "action_taken_date": ["20241231"], + "denial_reasons": ["999"], + "denial_reasons_ff": [""], + "pricing_interest_rate_type": ["999"], + "pricing_init_rate_period": [""], + "pricing_fixed_rate": [""], + "pricing_adj_margin": [""], + "pricing_adj_index_name": ["999"], + "pricing_adj_index_name_ff": [""], + "pricing_adj_index_value": [""], + "pricing_origination_charges": [""], + "pricing_broker_fees": [""], + "pricing_initial_charges": [""], + "pricing_mca_addcost_flag": ["999"], + "pricing_mca_addcost": [""], + "pricing_prepenalty_allowed": ["999"], + "pricing_prepenalty_exists": ["999"], + "census_tract_adr_type": ["988"], + "census_tract_number": [""], + "gross_annual_revenue_flag": ["988"], + "gross_annual_revenue": [""], + "naics_code_flag": ["988"], + "naics_code": [""], + "number_of_workers": ["988"], + "time_in_business_type": ["988"], + "time_in_business": [""], + "business_ownership_status": ["988"], + "num_principal_owners_flag": ["988"], + "num_principal_owners": [""], + "po_1_ethnicity": [""], + "po_1_ethnicity_ff": [""], + "po_1_race": [""], + "po_1_race_anai_ff": [""], + "po_1_race_asian_ff": [""], + "po_1_race_baa_ff": [""], + "po_1_race_pi_ff": [""], + "po_1_gender_flag": [""], + "po_1_gender_ff": [""], + "po_2_ethnicity": [""], + "po_2_ethnicity_ff": [""], + "po_2_race": [""], + "po_2_race_anai_ff": [""], + "po_2_race_asian_ff": [""], + "po_2_race_baa_ff": [""], + "po_2_race_pi_ff": [""], + "po_2_gender_flag": [""], + "po_2_gender_ff": [""], + "po_3_ethnicity": [""], + "po_3_ethnicity_ff": [""], + "po_3_race": [""], + "po_3_race_anai_ff": [""], + "po_3_race_asian_ff": [""], + "po_3_race_baa_ff": [""], + "po_3_race_pi_ff": [""], + "po_3_gender_flag": [""], + "po_3_gender_ff": [""], + "po_4_ethnicity": [""], + "po_4_ethnicity_ff": [""], + "po_4_race": [""], + "po_4_race_anai_ff": [""], + "po_4_race_asian_ff": [""], + "po_4_race_baa_ff": [""], + "po_4_race_pi_ff": [""], + "po_4_gender_flag": [""], + "po_4_gender_ff": [""], + } + default.update(update_data) + return default + + +''' class TestValidate: util = TestUtil() phase1_schema = get_phase_1_schema_for_lei() phase2_schema = get_phase_2_schema_for_lei() def test_with_valid_dataframe(self): - df = pd.DataFrame(data=self.util.get_data()) + df = pl.DataFrame(data=self.util.get_data()) p1_results = validate(self.phase1_schema, df) p2_results = validate(self.phase2_schema, df) @@ -117,7 +146,7 @@ def test_with_valid_lei(self): phase1_schema_by_lei = get_phase_1_schema_for_lei({'lei': lei}) phase2_schema_by_lei = get_phase_2_schema_for_lei({'lei': lei}) - df = pd.DataFrame(data=self.util.get_data()) + df = pl.DataFrame(data=self.util.get_data()) p1_results = validate(phase1_schema_by_lei, df) p2_results = validate(phase2_schema_by_lei, df) @@ -126,7 +155,7 @@ def test_with_valid_lei(self): assert p2_results.is_valid def test_with_invalid_dataframe(self): - df = pd.DataFrame(data=self.util.get_data({"ct_credit_product": ["989"]})) + df = pl.DataFrame(data=self.util.get_data({"ct_credit_product": ["989"]})) p1_results = validate(self.phase1_schema, df) p2_results = validate(self.phase2_schema, df) @@ -136,7 +165,7 @@ def test_with_invalid_dataframe(self): assert p2_results.is_valid def test_with_multi_invalid_dataframe(self): - df = pd.DataFrame( + df = pl.DataFrame( data=self.util.get_data( { "ct_credit_product": ["989"], @@ -150,8 +179,8 @@ def test_with_multi_invalid_dataframe(self): assert len(results.findings) == 1 results = validate(self.phase2_schema, df) - # 3 unique findings raised - assert len(results.findings.index.unique()) == 4 + # 4 unique findings raised + assert results.findings['finding_no'].n_unique() == 4 def test_with_invalid_lei(self): lei = "000TESTFIUIDDONOTUS1" @@ -159,126 +188,96 @@ def test_with_invalid_lei(self): phase1_schema_by_lei = get_phase_1_schema_for_lei({'lei': lei}) phase2_schema_by_lei = get_phase_2_schema_for_lei({'lei': lei}) - df = pd.DataFrame(data=self.util.get_data({"ct_credit_product": ["989"]})) + df = pl.DataFrame(data=self.util.get_data({"ct_credit_product": ["989"]})) p1_results = validate(phase1_schema_by_lei, df) p2_results = validate(phase2_schema_by_lei, df) # 1 unique findings raised in phase 1 assert not p1_results.is_valid - assert len(p1_results.findings.index.unique()) == 1 + assert p1_results.findings['finding_no'].n_unique() == 1 # 1 unique finding raised in phase 2 assert not p2_results.is_valid - assert len(p2_results.findings.index.unique()) == 1 + assert p2_results.findings['finding_no'].n_unique() == 1 +''' class TestValidatePhases: - util = TestUtil() - def test_with_valid_data(self): - results = validate_phases(pd.DataFrame(data=self.util.get_data())) - - assert results.is_valid - assert results.phase == ValidationPhase.LOGICAL.value - - def test_with_valid_lei(self): - lei = "000TESTFIUIDDONOTUSE" - df = pd.DataFrame(data=self.util.get_data()) - results = validate_phases(df, {'lei': lei}) - - assert results.is_valid - assert results.phase == ValidationPhase.LOGICAL.value - - def test_with_invalid_data(self): - results = validate_phases(pd.DataFrame(data=self.util.get_data({"ct_credit_product": ["989"]}))) - - assert not results.is_valid - assert len(results.findings) == 1 - assert results.phase == ValidationPhase.SYNTACTICAL.value - - def test_max_errors(self): - errors = [pd.DataFrame(data=self.util.get_data({"app_recipient": ["3"]})) for i in range(0, 6)] - errors.append( - pd.DataFrame( - data=self.util.get_data( - {"ct_credit_product": ["989"], "ct_loan_term_flag": ["989"], "ct_guarantee": ["989"]} - ) + def test_with_valid_data(self, csv_df_file): + results = validate_batch_csv(csv_df_file) + assert all([r.findings.is_empty() for r in results]) + + def test_with_valid_lei(self, csv_df_file): + results = validate_batch_csv(csv_df_file, context={'lei': "000TESTFIUIDDONOTUSE"}) + assert all([r.findings.is_empty() for r in results]) + + @pytest.mark.parametrize( + "csv_df_file", + [({"ct_credit_product": ["989"]})], + indirect=True, + ) + def test_with_invalid_data(self, csv_df_file): + results = list(validate_batch_csv(csv_df_file)) + assert len(results) == 1 + assert results[0].findings.height == 1 + assert results[0].phase == ValidationPhase.SYNTACTICAL + + @pytest.mark.parametrize( + "csv_df_file", + [ + ( + { + "ct_credit_product": ["989"], + "num_principal_owners": ["10"], + "action_taken": ["20"], + } ) - ) - errors = pd.concat(errors) - errors = errors.reset_index() - results = validate_phases(errors, max_errors=2) - - assert not results.is_valid - assert len(results.findings) == 2 - assert results.phase == ValidationPhase.SYNTACTICAL.value - - errors = [pd.DataFrame(data=self.util.get_data({"app_recipient": ["3"]})) for i in range(0, 200)] - errors.extend([pd.DataFrame(data=self.util.get_data({"ct_credit_product": ["989"]})) for i in range(0, 200)]) - errors.extend([pd.DataFrame(data=self.util.get_data({"app_method": ["5"]})) for i in range(0, 300)]) - errors = pd.concat(errors) - errors = errors.reset_index() - - results = validate_phases(errors, max_errors=100) - - assert not results.is_valid - assert len(results.findings) == 100 - assert results.phase == ValidationPhase.SYNTACTICAL.value - - def test_with_multi_invalid_data_with_phase1(self): - results = validate_phases( - pd.DataFrame( - data=self.util.get_data( - { - "ct_credit_product": ["989"], - "num_principal_owners": ["1"], - "action_taken": ["2"], - } - ) + ], + indirect=True, + ) + def test_with_multi_invalid_data_with_phase1(self, csv_df_file): + results = list(validate_batch_csv(csv_df_file)) + assert len(results) == 1 + assert results[0].findings.height == 3 + assert results[0].phase == ValidationPhase.SYNTACTICAL + + @pytest.mark.parametrize( + "csv_df_file", + [ + ( + { + "num_principal_owners_flag": ["988"], + "num_principal_owners": ["1"], + "action_taken": ["3"], + "pricing_interest_rate_type": ["1"], + } ) - ) - - # should only return phase 1 validation result since phase1 failed - assert not results.is_valid - - assert results.phase == ValidationPhase.SYNTACTICAL.value - assert len(results.findings) == 1 - - def test_with_multi_invalid_data_with_phase2(self): - results = validate_phases( - pd.DataFrame( - data=self.util.get_data( - { - "num_principal_owners": ["1"], - "action_taken": ["2"], - } - ) - ), - ) - # since the data passed phase 1 validations - # this should return phase 2 validations - assert not results.is_valid - assert results.phase == ValidationPhase.LOGICAL.value - assert len(results.findings.index.unique()) == 4 - - def test_with_invalid_lei(self): - lei = "000TESTFIUIDDONOTUS1" - df = pd.DataFrame(data=self.util.get_data()) - results = validate_phases(df, {'lei': lei}) - - assert not results.is_valid - assert len(results.findings['validation_id'] == 'W0003') > 0 - assert results.phase == ValidationPhase.LOGICAL.value - - def test_column_not_found_in_df(self): + ], + indirect=True, + ) + def test_with_multi_invalid_data_with_phase2(self, csv_df_file): + results = list(validate_batch_csv(csv_df_file)) + assert len(results) == 3 + assert results[2].findings.height == 7 + assert results[2].phase == ValidationPhase.LOGICAL + + def test_with_non_matching_lei(self, csv_df_file): + results = list(validate_batch_csv(csv_df_file, context={'lei': "000TESTFIUIDDONOTUS1"})) + assert len(results) == 3 + assert results[2].findings.height == 1 + assert results[2].findings['validation_id'].item() == 'W0003' + assert results[2].phase == ValidationPhase.LOGICAL + + def test_column_not_found_in_df(self, csv_df_mission_column_file): with pytest.raises(RuntimeError) as re: - validate_phases( - pd.DataFrame( - data={ - "Test Column": ["1"], - } - ), - ) - - assert str(re.value == "RuntimeError: column 'uid' not in dataframe. Columns in dataframe: ['Test Column']") + list(validate_batch_csv(csv_df_mission_column_file)) + assert "column 'uid' not in dataframe" in str(re.value) + + def test_same_uids(self, csv_df_same_uids_file): + results = list(validate_batch_csv(csv_df_same_uids_file)) + assert len(results) == 3 + assert results[1].findings.height == 2 + assert results[1].findings.select(pl.col('validation_id').eq('E3000').all()).item() + assert results[1].phase == ValidationPhase.LOGICAL