diff --git a/Dockerfile b/Dockerfile index fb04f1a..03d6d60 100644 --- a/Dockerfile +++ b/Dockerfile @@ -27,4 +27,5 @@ FROM base as web-server RUN pip install --find-links=dist aki-prj23-transparenzregister[web-server] --no-cache-dir && \ rm dist/ -R -CMD python -m aki_prj23_transparenzregister.ui.company_finance_dash +ENTRYPOINT ["webserver", "ENV"] +CMD ["--level", "DEBUG"] diff --git a/README.md b/README.md index 03414c2..7005910 100644 --- a/README.md +++ b/README.md @@ -16,8 +16,10 @@ See the [CONTRIBUTING.md](CONTRIBUTING.md) about how code should be formatted an The project has currently the following entrypoint available: -- **data-transfer** > Transfers all the data from the mongodb into the sql db to make it available as production data. +- **data-processing** > Transfers all the data from the mongodb into the sql db to make it available as production data. - **reset-sql** > Resets all sql tables in the connected db. +- **copy-sql** > Copys the content of a db to another db. +- **webserver** > Starts the webserver showing the analysis results. ## DB Connection settings @@ -26,13 +28,15 @@ To connect to the Mongo db see [connect] Create a `secrets.json` in the root of this repo with the following structure (values to be replaces by desired config): +The sqlite db is alternative to the postgres section. ```json { - "postgres": { - "username": "postgres", - "password": "postgres", + "sqlite": "path-to-sqlite.db", + "postgres": { + "username": "username", + "password": "password", "host": "localhost", - "database": "postgres", + "database": "db-name", "port": 5432 }, "mongo": { @@ -61,6 +65,8 @@ PYTHON_MONGO_PASSWORD=password PYTHON_MONGO_PORT=27017 PYTHON_MONGO_DATABASE=transparenzregister +PYTHON_SQLITE_PATH=PathToSQLite3.db # An overwrite path to an sqllite db + PYTHON_DASH_LOGIN_USERNAME=some-login-to-webgui PYTHON_DASH_LOGIN_PW=some-pw-to-login-to-webgui diff --git a/poetry.lock b/poetry.lock index f3e703f..2ca0079 100644 --- a/poetry.lock +++ b/poetry.lock @@ -719,63 +719,63 @@ test-no-images = ["pytest", "pytest-cov", "wurlitzer"] [[package]] name = "coverage" -version = "7.3.1" +version = "7.3.2" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cd0f7429ecfd1ff597389907045ff209c8fdb5b013d38cfa7c60728cb484b6e3"}, - {file = "coverage-7.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:966f10df9b2b2115da87f50f6a248e313c72a668248be1b9060ce935c871f276"}, - {file = "coverage-7.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0575c37e207bb9b98b6cf72fdaaa18ac909fb3d153083400c2d48e2e6d28bd8e"}, - {file = "coverage-7.3.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:245c5a99254e83875c7fed8b8b2536f040997a9b76ac4c1da5bff398c06e860f"}, - {file = "coverage-7.3.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c96dd7798d83b960afc6c1feb9e5af537fc4908852ef025600374ff1a017392"}, - {file = "coverage-7.3.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:de30c1aa80f30af0f6b2058a91505ea6e36d6535d437520067f525f7df123887"}, - {file = "coverage-7.3.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:50dd1e2dd13dbbd856ffef69196781edff26c800a74f070d3b3e3389cab2600d"}, - {file = "coverage-7.3.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b9c0c19f70d30219113b18fe07e372b244fb2a773d4afde29d5a2f7930765136"}, - {file = "coverage-7.3.1-cp310-cp310-win32.whl", hash = "sha256:770f143980cc16eb601ccfd571846e89a5fe4c03b4193f2e485268f224ab602f"}, - {file = "coverage-7.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:cdd088c00c39a27cfa5329349cc763a48761fdc785879220d54eb785c8a38520"}, - {file = "coverage-7.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:74bb470399dc1989b535cb41f5ca7ab2af561e40def22d7e188e0a445e7639e3"}, - {file = "coverage-7.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:025ded371f1ca280c035d91b43252adbb04d2aea4c7105252d3cbc227f03b375"}, - {file = "coverage-7.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6191b3a6ad3e09b6cfd75b45c6aeeffe7e3b0ad46b268345d159b8df8d835f9"}, - {file = "coverage-7.3.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7eb0b188f30e41ddd659a529e385470aa6782f3b412f860ce22b2491c89b8593"}, - {file = "coverage-7.3.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75c8f0df9dfd8ff745bccff75867d63ef336e57cc22b2908ee725cc552689ec8"}, - {file = "coverage-7.3.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7eb3cd48d54b9bd0e73026dedce44773214064be93611deab0b6a43158c3d5a0"}, - {file = "coverage-7.3.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ac3c5b7e75acac31e490b7851595212ed951889918d398b7afa12736c85e13ce"}, - {file = "coverage-7.3.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5b4ee7080878077af0afa7238df1b967f00dc10763f6e1b66f5cced4abebb0a3"}, - {file = "coverage-7.3.1-cp311-cp311-win32.whl", hash = "sha256:229c0dd2ccf956bf5aeede7e3131ca48b65beacde2029f0361b54bf93d36f45a"}, - {file = "coverage-7.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:c6f55d38818ca9596dc9019eae19a47410d5322408140d9a0076001a3dcb938c"}, - {file = "coverage-7.3.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5289490dd1c3bb86de4730a92261ae66ea8d44b79ed3cc26464f4c2cde581fbc"}, - {file = "coverage-7.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ca833941ec701fda15414be400c3259479bfde7ae6d806b69e63b3dc423b1832"}, - {file = "coverage-7.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd694e19c031733e446c8024dedd12a00cda87e1c10bd7b8539a87963685e969"}, - {file = "coverage-7.3.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aab8e9464c00da5cb9c536150b7fbcd8850d376d1151741dd0d16dfe1ba4fd26"}, - {file = "coverage-7.3.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87d38444efffd5b056fcc026c1e8d862191881143c3aa80bb11fcf9dca9ae204"}, - {file = "coverage-7.3.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:8a07b692129b8a14ad7a37941a3029c291254feb7a4237f245cfae2de78de037"}, - {file = "coverage-7.3.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:2829c65c8faaf55b868ed7af3c7477b76b1c6ebeee99a28f59a2cb5907a45760"}, - {file = "coverage-7.3.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1f111a7d85658ea52ffad7084088277135ec5f368457275fc57f11cebb15607f"}, - {file = "coverage-7.3.1-cp312-cp312-win32.whl", hash = "sha256:c397c70cd20f6df7d2a52283857af622d5f23300c4ca8e5bd8c7a543825baa5a"}, - {file = "coverage-7.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:5ae4c6da8b3d123500f9525b50bf0168023313963e0e2e814badf9000dd6ef92"}, - {file = "coverage-7.3.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ca70466ca3a17460e8fc9cea7123c8cbef5ada4be3140a1ef8f7b63f2f37108f"}, - {file = "coverage-7.3.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f2781fd3cabc28278dc982a352f50c81c09a1a500cc2086dc4249853ea96b981"}, - {file = "coverage-7.3.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6407424621f40205bbe6325686417e5e552f6b2dba3535dd1f90afc88a61d465"}, - {file = "coverage-7.3.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:04312b036580ec505f2b77cbbdfb15137d5efdfade09156961f5277149f5e344"}, - {file = "coverage-7.3.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac9ad38204887349853d7c313f53a7b1c210ce138c73859e925bc4e5d8fc18e7"}, - {file = "coverage-7.3.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:53669b79f3d599da95a0afbef039ac0fadbb236532feb042c534fbb81b1a4e40"}, - {file = "coverage-7.3.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:614f1f98b84eb256e4f35e726bfe5ca82349f8dfa576faabf8a49ca09e630086"}, - {file = "coverage-7.3.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f1a317fdf5c122ad642db8a97964733ab7c3cf6009e1a8ae8821089993f175ff"}, - {file = "coverage-7.3.1-cp38-cp38-win32.whl", hash = "sha256:defbbb51121189722420a208957e26e49809feafca6afeef325df66c39c4fdb3"}, - {file = "coverage-7.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:f4f456590eefb6e1b3c9ea6328c1e9fa0f1006e7481179d749b3376fc793478e"}, - {file = "coverage-7.3.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f12d8b11a54f32688b165fd1a788c408f927b0960984b899be7e4c190ae758f1"}, - {file = "coverage-7.3.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f09195dda68d94a53123883de75bb97b0e35f5f6f9f3aa5bf6e496da718f0cb6"}, - {file = "coverage-7.3.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6601a60318f9c3945be6ea0f2a80571f4299b6801716f8a6e4846892737ebe4"}, - {file = "coverage-7.3.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07d156269718670d00a3b06db2288b48527fc5f36859425ff7cec07c6b367745"}, - {file = "coverage-7.3.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:636a8ac0b044cfeccae76a36f3b18264edcc810a76a49884b96dd744613ec0b7"}, - {file = "coverage-7.3.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5d991e13ad2ed3aced177f524e4d670f304c8233edad3210e02c465351f785a0"}, - {file = "coverage-7.3.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:586649ada7cf139445da386ab6f8ef00e6172f11a939fc3b2b7e7c9082052fa0"}, - {file = "coverage-7.3.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4aba512a15a3e1e4fdbfed2f5392ec221434a614cc68100ca99dcad7af29f3f8"}, - {file = "coverage-7.3.1-cp39-cp39-win32.whl", hash = "sha256:6bc6f3f4692d806831c136c5acad5ccedd0262aa44c087c46b7101c77e139140"}, - {file = "coverage-7.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:553d7094cb27db58ea91332e8b5681bac107e7242c23f7629ab1316ee73c4981"}, - {file = "coverage-7.3.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:220eb51f5fb38dfdb7e5d54284ca4d0cd70ddac047d750111a68ab1798945194"}, - {file = "coverage-7.3.1.tar.gz", hash = "sha256:6cb7fe1581deb67b782c153136541e20901aa312ceedaf1467dcb35255787952"}, + {file = "coverage-7.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d872145f3a3231a5f20fd48500274d7df222e291d90baa2026cc5152b7ce86bf"}, + {file = "coverage-7.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:310b3bb9c91ea66d59c53fa4989f57d2436e08f18fb2f421a1b0b6b8cc7fffda"}, + {file = "coverage-7.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f47d39359e2c3779c5331fc740cf4bce6d9d680a7b4b4ead97056a0ae07cb49a"}, + {file = "coverage-7.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa72dbaf2c2068404b9870d93436e6d23addd8bbe9295f49cbca83f6e278179c"}, + {file = "coverage-7.3.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:beaa5c1b4777f03fc63dfd2a6bd820f73f036bfb10e925fce067b00a340d0f3f"}, + {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:dbc1b46b92186cc8074fee9d9fbb97a9dd06c6cbbef391c2f59d80eabdf0faa6"}, + {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:315a989e861031334d7bee1f9113c8770472db2ac484e5b8c3173428360a9148"}, + {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d1bc430677773397f64a5c88cb522ea43175ff16f8bfcc89d467d974cb2274f9"}, + {file = "coverage-7.3.2-cp310-cp310-win32.whl", hash = "sha256:a889ae02f43aa45032afe364c8ae84ad3c54828c2faa44f3bfcafecb5c96b02f"}, + {file = "coverage-7.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:c0ba320de3fb8c6ec16e0be17ee1d3d69adcda99406c43c0409cb5c41788a611"}, + {file = "coverage-7.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ac8c802fa29843a72d32ec56d0ca792ad15a302b28ca6203389afe21f8fa062c"}, + {file = "coverage-7.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:89a937174104339e3a3ffcf9f446c00e3a806c28b1841c63edb2b369310fd074"}, + {file = "coverage-7.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e267e9e2b574a176ddb983399dec325a80dbe161f1a32715c780b5d14b5f583a"}, + {file = "coverage-7.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2443cbda35df0d35dcfb9bf8f3c02c57c1d6111169e3c85fc1fcc05e0c9f39a3"}, + {file = "coverage-7.3.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4175e10cc8dda0265653e8714b3174430b07c1dca8957f4966cbd6c2b1b8065a"}, + {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0cbf38419fb1a347aaf63481c00f0bdc86889d9fbf3f25109cf96c26b403fda1"}, + {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5c913b556a116b8d5f6ef834038ba983834d887d82187c8f73dec21049abd65c"}, + {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1981f785239e4e39e6444c63a98da3a1db8e971cb9ceb50a945ba6296b43f312"}, + {file = "coverage-7.3.2-cp311-cp311-win32.whl", hash = "sha256:43668cabd5ca8258f5954f27a3aaf78757e6acf13c17604d89648ecc0cc66640"}, + {file = "coverage-7.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10c39c0452bf6e694511c901426d6b5ac005acc0f78ff265dbe36bf81f808a2"}, + {file = "coverage-7.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4cbae1051ab791debecc4a5dcc4a1ff45fc27b91b9aee165c8a27514dd160836"}, + {file = "coverage-7.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12d15ab5833a997716d76f2ac1e4b4d536814fc213c85ca72756c19e5a6b3d63"}, + {file = "coverage-7.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c7bba973ebee5e56fe9251300c00f1579652587a9f4a5ed8404b15a0471f216"}, + {file = "coverage-7.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe494faa90ce6381770746077243231e0b83ff3f17069d748f645617cefe19d4"}, + {file = "coverage-7.3.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6e9589bd04d0461a417562649522575d8752904d35c12907d8c9dfeba588faf"}, + {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d51ac2a26f71da1b57f2dc81d0e108b6ab177e7d30e774db90675467c847bbdf"}, + {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:99b89d9f76070237975b315b3d5f4d6956ae354a4c92ac2388a5695516e47c84"}, + {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fa28e909776dc69efb6ed975a63691bc8172b64ff357e663a1bb06ff3c9b589a"}, + {file = "coverage-7.3.2-cp312-cp312-win32.whl", hash = "sha256:289fe43bf45a575e3ab10b26d7b6f2ddb9ee2dba447499f5401cfb5ecb8196bb"}, + {file = "coverage-7.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:7dbc3ed60e8659bc59b6b304b43ff9c3ed858da2839c78b804973f613d3e92ed"}, + {file = "coverage-7.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f94b734214ea6a36fe16e96a70d941af80ff3bfd716c141300d95ebc85339738"}, + {file = "coverage-7.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:af3d828d2c1cbae52d34bdbb22fcd94d1ce715d95f1a012354a75e5913f1bda2"}, + {file = "coverage-7.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:630b13e3036e13c7adc480ca42fa7afc2a5d938081d28e20903cf7fd687872e2"}, + {file = "coverage-7.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9eacf273e885b02a0273bb3a2170f30e2d53a6d53b72dbe02d6701b5296101c"}, + {file = "coverage-7.3.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8f17966e861ff97305e0801134e69db33b143bbfb36436efb9cfff6ec7b2fd9"}, + {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b4275802d16882cf9c8b3d057a0839acb07ee9379fa2749eca54efbce1535b82"}, + {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:72c0cfa5250f483181e677ebc97133ea1ab3eb68645e494775deb6a7f6f83901"}, + {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:cb536f0dcd14149425996821a168f6e269d7dcd2c273a8bff8201e79f5104e76"}, + {file = "coverage-7.3.2-cp38-cp38-win32.whl", hash = "sha256:307adb8bd3abe389a471e649038a71b4eb13bfd6b7dd9a129fa856f5c695cf92"}, + {file = "coverage-7.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:88ed2c30a49ea81ea3b7f172e0269c182a44c236eb394718f976239892c0a27a"}, + {file = "coverage-7.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b631c92dfe601adf8f5ebc7fc13ced6bb6e9609b19d9a8cd59fa47c4186ad1ce"}, + {file = "coverage-7.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d3d9df4051c4a7d13036524b66ecf7a7537d14c18a384043f30a303b146164e9"}, + {file = "coverage-7.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f7363d3b6a1119ef05015959ca24a9afc0ea8a02c687fe7e2d557705375c01f"}, + {file = "coverage-7.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f11cc3c967a09d3695d2a6f03fb3e6236622b93be7a4b5dc09166a861be6d25"}, + {file = "coverage-7.3.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:149de1d2401ae4655c436a3dced6dd153f4c3309f599c3d4bd97ab172eaf02d9"}, + {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:3a4006916aa6fee7cd38db3bfc95aa9c54ebb4ffbfc47c677c8bba949ceba0a6"}, + {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9028a3871280110d6e1aa2df1afd5ef003bab5fb1ef421d6dc748ae1c8ef2ebc"}, + {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9f805d62aec8eb92bab5b61c0f07329275b6f41c97d80e847b03eb894f38d083"}, + {file = "coverage-7.3.2-cp39-cp39-win32.whl", hash = "sha256:d1c88ec1a7ff4ebca0219f5b1ef863451d828cccf889c173e1253aa84b1e07ce"}, + {file = "coverage-7.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b4767da59464bb593c07afceaddea61b154136300881844768037fd5e859353f"}, + {file = "coverage-7.3.2-pp38.pp39.pp310-none-any.whl", hash = "sha256:ae97af89f0fbf373400970c0a21eef5aa941ffeed90aee43650b81f7d7f47637"}, + {file = "coverage-7.3.2.tar.gz", hash = "sha256:be32ad29341b0170e795ca590e1c07e81fc061cb5b10c74ce7203491484404ef"}, ] [package.extras] @@ -5828,13 +5828,13 @@ dev = ["flake8", "flake8-annotations", "flake8-bandit", "flake8-bugbear", "flake [[package]] name = "urllib3" -version = "2.0.5" +version = "2.0.6" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.7" files = [ - {file = "urllib3-2.0.5-py3-none-any.whl", hash = "sha256:ef16afa8ba34a1f989db38e1dbbe0c302e4289a47856990d0682e374563ce35e"}, - {file = "urllib3-2.0.5.tar.gz", hash = "sha256:13abf37382ea2ce6fb744d4dad67838eec857c9f4f57009891805e0b5e123594"}, + {file = "urllib3-2.0.6-py3-none-any.whl", hash = "sha256:7a7c7003b000adf9e7ca2a377c9688bbc54ed41b985789ed576570342a375cd2"}, + {file = "urllib3-2.0.6.tar.gz", hash = "sha256:b19e1a85d206b56d7df1d5e683df4a7725252a964e3993648dd0fb5a1c157564"}, ] [package.dependencies] @@ -5987,11 +5987,11 @@ files = [ ] [extras] -ingest = ["deutschland", "selenium"] +ingest = ["deutschland", "selenium", "xmltodict"] processing = [] web-server = ["dash", "dash-auth", "dash-bootstrap-components", "matplotlib", "seaborn"] [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "54b48197ec857377c4cbb8a122f7dc76cc714bb437c31631984caa44f3adb943" +content-hash = "9be00fc417a83776eef560907a9c6b853dbe8e1a75909b65f3db2f613f10db65" diff --git a/pyproject.toml b/pyproject.toml index f207b42..97d38ee 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ requires = ["poetry-core"] target-version = ["py311"] [tool.coverage.report] -exclude_also = ["if __name__ == .__main__.:", "if not isinstance(engine, Engine):"] +exclude_also = ["if __name__ == .__main__.:", "if not isinstance(engine, Engine):", "@overload"] [tool.coverage.run] branch = true @@ -69,7 +69,7 @@ tqdm = "^4.66.1" xmltodict = "^0.13.0" [tool.poetry.extras] -ingest = ["selenium", "deutschland"] +ingest = ["selenium", "deutschland", "xmltodict"] processing = [] web-server = ["dash", "dash-auth", "dash-bootstrap-components", "matplotlib", "seaborn"] @@ -119,8 +119,8 @@ pytest-repeat = "^0.9.1" [tool.poetry.scripts] copy-sql = "aki_prj23_transparenzregister.utils.sql.copy_sql:copy_db_cli" -data-transfer = {reference = "aki_prj23_transparenzregister.utils.data_transfer:transfer_data", extras = ["processing"], type = "console"} -reset-sql = {reference = "aki_prj23_transparenzregister.utils.sql.connector:reset_all_tables", extras = ["processing"], type = "console"} +data-processing = "aki_prj23_transparenzregister.utils.data_transfer:transfer_data_cli" +reset-sql = "aki_prj23_transparenzregister.utils.sql.connector:reset_all_tables_cli" webserver = "aki_prj23_transparenzregister.ui.app:main" [tool.ruff] diff --git a/src/aki_prj23_transparenzregister/config/config_providers.py b/src/aki_prj23_transparenzregister/config/config_providers.py index 1e539a6..f3ae252 100644 --- a/src/aki_prj23_transparenzregister/config/config_providers.py +++ b/src/aki_prj23_transparenzregister/config/config_providers.py @@ -4,20 +4,31 @@ import abc import errno import json import os +import re from pathlib import Path -from typing import Any +from typing import Any, Final from dotenv import load_dotenv -from aki_prj23_transparenzregister.config.config_template import PostgreConnectionString -from aki_prj23_transparenzregister.utils.mongo.connector import MongoConnection +from aki_prj23_transparenzregister.config.config_template import ( + MongoConnection, + PostgreConnectionString, + SQLConnectionString, + SQLiteConnectionString, +) + +HELP_TEXT_CONFIG: Final[str] = ( + "Database configuration. " + "Either give the paths to a *.json containing the secrets. " + "Alternativly specify the use of enviromental vairables by entering the ENV or the einviromental prefix ending with a '_'." +) class ConfigProvider(metaclass=abc.ABCMeta): """Generic abstract class for a wrapper providing the config options for PostgreSQL and MongoDB.""" @abc.abstractmethod - def get_postgre_connection_string(self) -> PostgreConnectionString: + def get_sql_connection_string(self) -> SQLConnectionString: """Get PostgreSQL connection string. Raises: @@ -41,7 +52,7 @@ class ConfigProvider(metaclass=abc.ABCMeta): def __str__(self) -> str: connections = [] try: - pg_str = str(self.get_postgre_connection_string()) + pg_str = str(self.get_sql_connection_string()) except KeyError: pass else: @@ -80,12 +91,15 @@ class JsonFileConfigProvider(ConfigProvider): except Exception as error: raise TypeError("File content is not a valid JSON object") from error - def get_postgre_connection_string(self) -> PostgreConnectionString: + def get_sql_connection_string(self) -> SQLConnectionString: """Read PostgreSQL connection string from .json file added in constructor. Returns: PostgreConnectionString: Connection details """ + if "sqlite" in self.__data__: + return SQLiteConnectionString(self.__data__["sqlite"]) # type: ignore + details = self.__data__["postgres"] return PostgreConnectionString( details["username"], @@ -123,16 +137,18 @@ class EnvironmentConfigProvider(ConfigProvider): prefix (str, optional): Variable prefix. Defaults to "PYTHON_". """ load_dotenv() - relevant_keys = [key for key in os.environ if key.startswith(prefix)] - for key in relevant_keys: - self.__data__[key.replace(prefix, "")] = os.environ.get(key) + for key in os.environ: + if key.startswith(prefix): + self.__data__[key.replace(prefix, "")] = os.environ.get(key) - def get_postgre_connection_string(self) -> PostgreConnectionString: + def get_sql_connection_string(self) -> SQLConnectionString: """Read PostgreSQL connection string from environment variables. Returns: PostgreConnectionString: Connection details """ + if "SQLITE_PATH" in self.__data__: + return SQLiteConnectionString(self.__data__["SQLITE_PATH"]) return PostgreConnectionString( self.__data__["POSTGRES_USERNAME"], self.__data__["POSTGRES_PASSWORD"], @@ -154,3 +170,23 @@ class EnvironmentConfigProvider(ConfigProvider): self.__data__["MONGO_USERNAME"], self.__data__["MONGO_PASSWORD"], ) + + +def get_config_provider(config_place: str | Path | None) -> ConfigProvider: + """Collects the configuration and passes them to a parser. + + Args: + config_place: The place where a configuration is placed. + + Returns: + An config provider. + """ + if not config_place or ( + isinstance(config_place, str) and config_place.upper() == "ENV" + ): + return EnvironmentConfigProvider() + if isinstance(config_place, str) and re.fullmatch(r"[A-Z]+_", config_place): + return EnvironmentConfigProvider(config_place) + if isinstance(config_place, Path) or re.fullmatch(r".*\.json", config_place): + return JsonFileConfigProvider(config_place) + raise ValueError("No configuration found.") diff --git a/src/aki_prj23_transparenzregister/config/config_template.py b/src/aki_prj23_transparenzregister/config/config_template.py index a8d8548..874911d 100644 --- a/src/aki_prj23_transparenzregister/config/config_template.py +++ b/src/aki_prj23_transparenzregister/config/config_template.py @@ -3,7 +3,12 @@ from dataclasses import dataclass @dataclass -class PostgreConnectionString: +class SQLConnectionString: + """An SQL Connection args wrapper.""" + + +@dataclass +class PostgreConnectionString(SQLConnectionString): """PostgreSQL Connection String args wrapper.""" username: str @@ -14,3 +19,44 @@ class PostgreConnectionString: def __str__(self) -> str: return f"Postgre configuration: username: {self.username}, password {self.password}, host {self.host}:{self.port}, database {self.database}." + + +@dataclass +class SQLiteConnectionString(SQLConnectionString): + """SQLite Connection String wrapper.""" + + connect_to: str + + def __str__(self) -> str: + return f"sqlite:///{self.connect_to}" + + +@dataclass +class MongoConnection: + """Wrapper for MongoDB connection string.""" + + hostname: str + database: str + port: int | None + username: str | None + password: str | None + + def __str__(self) -> str: + return f"Mongo configuration: username: {self.username}, password {self.password}, host {self.hostname}:{self.port}, database {self.database}." + + def get_conn_string(self) -> str: + """Transforms the information of the object to a MongoDB connection string. + + Returns: + str: Connection string + """ + if self.username is not None and self.password is not None: + connection_string = ( + f"mongodb+srv://{self.username}:{self.password}@{self.hostname}" + ) + else: + connection_string = f"mongodb+srv://{self.hostname}" + if self.port is not None: + connection_string += f":{self.port}" + connection_string = connection_string.replace("mongodb+srv", "mongodb") + return connection_string diff --git a/src/aki_prj23_transparenzregister/ui/app.py b/src/aki_prj23_transparenzregister/ui/app.py index c7e5822..e49a975 100644 --- a/src/aki_prj23_transparenzregister/ui/app.py +++ b/src/aki_prj23_transparenzregister/ui/app.py @@ -1,13 +1,22 @@ """Main Dash app.""" +import argparse +import sys import dash import dash_bootstrap_components as dbc from cachetools import TTLCache, cached from dash import Dash, Input, Output, dcc, html -from aki_prj23_transparenzregister.config.config_providers import JsonFileConfigProvider +from aki_prj23_transparenzregister.config.config_providers import ( + HELP_TEXT_CONFIG, + get_config_provider, +) from aki_prj23_transparenzregister.ui import ui_elements from aki_prj23_transparenzregister.ui.session_handler import SessionHandler +from aki_prj23_transparenzregister.utils.logger_config import ( + add_logger_options_to_argparse, + configer_logger, +) from aki_prj23_transparenzregister.utils.sql import connector app = Dash( @@ -85,9 +94,23 @@ def update_options(search_value: str) -> list: def main() -> None: """The main application starting the Dashboard.""" - SessionHandler.session = connector.get_session( - JsonFileConfigProvider("./secrets.json") + parser = argparse.ArgumentParser( + prog="Transparenzregister Webserver", + description="Starts an Dash Webserver that shows our Analysis.", + epilog="Example: webserver --log-level ERROR --log-path print.log", ) + parser.add_argument( + "config", + metavar="config", + default="ENV", + help=HELP_TEXT_CONFIG, + ) + add_logger_options_to_argparse(parser) + + parsed = parser.parse_args(sys.argv[1:]) + configer_logger(namespace=parsed) + config = parsed.config + SessionHandler.session = connector.get_session(get_config_provider(config)) app.run(debug=False) diff --git a/src/aki_prj23_transparenzregister/ui/pages/__init__.py b/src/aki_prj23_transparenzregister/ui/pages/__init__.py new file mode 100644 index 0000000..284498f --- /dev/null +++ b/src/aki_prj23_transparenzregister/ui/pages/__init__.py @@ -0,0 +1 @@ +"""A package containing the pages of the Dashboard.""" diff --git a/src/aki_prj23_transparenzregister/utils/data_transfer.py b/src/aki_prj23_transparenzregister/utils/data_transfer.py index 7892815..21ebba5 100644 --- a/src/aki_prj23_transparenzregister/utils/data_transfer.py +++ b/src/aki_prj23_transparenzregister/utils/data_transfer.py @@ -1,4 +1,6 @@ """This module contains the data transfer and refinement functionalities between staging and production DB.""" +import argparse +import sys from datetime import date from functools import lru_cache from typing import Any, Final, Literal @@ -11,10 +13,17 @@ from loguru import logger from sqlalchemy.orm import Session from tqdm import tqdm -from aki_prj23_transparenzregister.config.config_providers import JsonFileConfigProvider +from aki_prj23_transparenzregister.config.config_providers import ( + HELP_TEXT_CONFIG, + ConfigProvider, + get_config_provider, +) from aki_prj23_transparenzregister.models.company import CapitalTypeEnum, CurrencyEnum from aki_prj23_transparenzregister.utils.enum_types import RelationTypeEnum -from aki_prj23_transparenzregister.utils.logger_config import configer_logger +from aki_prj23_transparenzregister.utils.logger_config import ( + add_logger_options_to_argparse, + configer_logger, +) from aki_prj23_transparenzregister.utils.mongo.company_mongo_service import ( CompanyMongoService, ) @@ -427,10 +436,7 @@ def add_annual_report(company_id: int, year: int, report: dict, db: Session) -> ), ) for auditor in report.get("auditors", ""): - pass _ = auditor - # person_id = get_person_id(person.get("name")) # how to create a person relation? - # company relation? def add_annual_financial_reports(companies: list[dict], db: Session) -> None: @@ -478,20 +484,17 @@ def add_annual_financial_reports(companies: list[dict], db: Session) -> None: logger.info("Company connections added.") -def transfer_data(db: Session | None = None) -> None: - """This functions transfers all the data from a production environment to a staging environment.""" - configer_logger("info", "data-transfer.log") +def transfer_data(config_provider: ConfigProvider) -> None: + """This functions transfers all the data from a production environment to a staging environment. - mongo_connector = MongoConnector( - JsonFileConfigProvider("./secrets.json").get_mongo_connection_string() - ) + Args: + config_provider: The configuration that defines the connection to sql and mongodb. + """ + mongo_connector = MongoConnector(config_provider.get_mongo_connection_string()) mongo_company = CompanyMongoService(mongo_connector) companies: list[dict[str, Any]] = mongo_company.get_all() # type: ignore del mongo_company - - if db is None: - db = get_session(JsonFileConfigProvider("./secrets.json")) - + db = get_session(config_provider) reset_all_tables(db) add_companies(companies, db) @@ -500,5 +503,20 @@ def transfer_data(db: Session | None = None) -> None: db.close() -if __name__ == "__main__": - transfer_data(get_session("sqlite:///local-test-data.db")) +def transfer_data_cli() -> None: + """A cli interface for the data transfer.""" + parser = argparse.ArgumentParser( + prog="Process data", + description="Copy data from one SQL database to another.", + epilog="Example: 'data-processing secrets.json' or 'data-processing ENV_VARS_'", + ) + parser.add_argument( + "config", + metavar="config", + default="ENV", + help=HELP_TEXT_CONFIG, + ) + add_logger_options_to_argparse(parser) + parsed = parser.parse_args(sys.argv[1:]) + configer_logger(namespace=parsed) + transfer_data(get_config_provider(parsed.config)) diff --git a/src/aki_prj23_transparenzregister/utils/logger_config.py b/src/aki_prj23_transparenzregister/utils/logger_config.py index c639e1e..d3f0313 100644 --- a/src/aki_prj23_transparenzregister/utils/logger_config.py +++ b/src/aki_prj23_transparenzregister/utils/logger_config.py @@ -1,22 +1,68 @@ """Configures the logger.""" import sys +from argparse import ArgumentParser, Namespace from pathlib import Path -from typing import Literal +from typing import Literal, overload from loguru import logger +@overload def configer_logger( + *, level: Literal["info", "debug", "warning", "error"], path: str | Path, +) -> None: + ... + + +@overload +def configer_logger(*, namespace: Namespace | None) -> None: + ... + + +def configer_logger( + *, + level: Literal["info", "debug", "warning", "error"] | None = None, + path: str | Path | None = None, + namespace: Namespace | None = None, ) -> None: """Configures the logger. Args: level: Defines the logging level that should be used. path: The path where the logs should be saved. + namespace: A ArgParsed namespace containing the log arguments. """ logger.remove() - logger.add(sys.stdout, level=level.upper(), catch=True) - if path: - logger.add(path, level=level.upper(), retention=5) + level_combined: str = str( + level if level else (namespace.level if namespace else "info") + ).upper() + logger.add(sys.stdout, level=level_combined, catch=True) + if path or (namespace and namespace.log_path): + logger.add( + path if path else namespace.log_path if namespace else "", + level=level_combined, + retention=5, + ) + + +def add_logger_options_to_argparse(parser: ArgumentParser) -> None: + """Adds logging options to the ArgumentParser. + + Args: + parser: The parser to whom the arguments for logging should be added. + """ + parser.add_argument( + "--level", + choices=["info", "debug", "error", "warning"], + default="info", + metavar="level", + help="The log level for the output.", + type=str.lower, + ) + parser.add_argument( + "--log-path", + metavar="log_path", + help="A path to write the log to.", + ) diff --git a/src/aki_prj23_transparenzregister/utils/mongo/connector.py b/src/aki_prj23_transparenzregister/utils/mongo/connector.py index 11d8b90..e7b855d 100644 --- a/src/aki_prj23_transparenzregister/utils/mongo/connector.py +++ b/src/aki_prj23_transparenzregister/utils/mongo/connector.py @@ -1,38 +1,8 @@ """Mongo Wrapper.""" -from dataclasses import dataclass import pymongo - -@dataclass -class MongoConnection: - """Wrapper for MongoDB connection string.""" - - hostname: str - database: str - port: int | None - username: str | None - password: str | None - - def __str__(self) -> str: - return f"Mongo configuration: username: {self.username}, password {self.password}, host {self.hostname}:{self.port}, database {self.database}." - - def get_conn_string(self) -> str: - """Transforms the information of the object to a MongoDB connection string. - - Returns: - str: Connection string - """ - if self.username is not None and self.password is not None: - connection_string = ( - f"mongodb+srv://{self.username}:{self.password}@{self.hostname}" - ) - else: - connection_string = f"mongodb+srv://{self.hostname}" - if self.port is not None: - connection_string += f":{self.port}" - connection_string = connection_string.replace("mongodb+srv", "mongodb") - return connection_string +from aki_prj23_transparenzregister.config.config_template import MongoConnection class MongoConnector: diff --git a/src/aki_prj23_transparenzregister/utils/sql/connector.py b/src/aki_prj23_transparenzregister/utils/sql/connector.py index 441ea9e..7b95985 100644 --- a/src/aki_prj23_transparenzregister/utils/sql/connector.py +++ b/src/aki_prj23_transparenzregister/utils/sql/connector.py @@ -1,5 +1,6 @@ """Module containing connection utils for PostgreSQL DB.""" -import re +import argparse +import sys import sqlalchemy as sa from loguru import logger @@ -8,45 +9,50 @@ from sqlalchemy.orm import Session, declarative_base, sessionmaker from sqlalchemy.pool import SingletonThreadPool from aki_prj23_transparenzregister.config.config_providers import ( + HELP_TEXT_CONFIG, ConfigProvider, JsonFileConfigProvider, + get_config_provider, +) +from aki_prj23_transparenzregister.config.config_template import ( + PostgreConnectionString, + SQLConnectionString, + SQLiteConnectionString, +) +from aki_prj23_transparenzregister.utils.logger_config import ( + add_logger_options_to_argparse, + configer_logger, ) -from aki_prj23_transparenzregister.config.config_template import PostgreConnectionString -def get_pg_engine(conn_args: PostgreConnectionString) -> Engine: +def get_engine(conn_args: SQLConnectionString) -> Engine: """Creates an engine connected to a Postgres instance. Returns: sqlalchemy.engine: connection engine """ - url = URL.create( - drivername="postgresql", - username=conn_args.username, - password=conn_args.password, - host=conn_args.host, - database=conn_args.database, - port=conn_args.port, - ) - return sa.create_engine(url) - - -def get_sqlite_engine(connect_to: str) -> Engine: - """Creates an engine connected to a sqlite instance. - - Returns: - sqlalchemy.engine: connection engine - """ - return sa.create_engine( - connect_to, - connect_args={"check_same_thread": True}, - poolclass=SingletonThreadPool, - ) + if isinstance(conn_args, PostgreConnectionString): + url = URL.create( + drivername="postgresql", + username=conn_args.username, + password=conn_args.password, + host=conn_args.host, + database=conn_args.database, + port=conn_args.port, + ) + return sa.create_engine(url) + if isinstance(conn_args, SQLiteConnectionString): + return sa.create_engine( + str(conn_args), + connect_args={"check_same_thread": True}, + poolclass=SingletonThreadPool, + ) + raise TypeError("The type of the configuration is invalid.") def get_session( - connect_to: ConfigProvider | str, -) -> Session: # pragma: no cover + connect_to: ConfigProvider, +) -> Session: """Creates a sql session. Args: @@ -55,23 +61,10 @@ def get_session( Returns: A session to connect to an SQL db via SQLAlchemy. """ - engine: Engine - if isinstance(connect_to, str) and re.fullmatch(r".*\.json$", connect_to): - logger.debug(connect_to) - connect_to = JsonFileConfigProvider(connect_to) - if isinstance(connect_to, ConfigProvider): - pg_string = connect_to.get_postgre_connection_string() - logger.debug(str(connect_to)) - engine = get_pg_engine(pg_string) - - elif isinstance(connect_to, str) and re.fullmatch( - r"sqlite:\/{3}[A-Za-z].*", connect_to - ): - engine = get_sqlite_engine(connect_to) - logger.debug(f"Connection to sqlite3 {connect_to}") - else: - raise TypeError("No valid connection is defined!") - return sessionmaker(autocommit=False, autoflush=False, bind=engine)() + sql_connector_config = connect_to.get_sql_connection_string() + return sessionmaker( + autocommit=False, autoflush=False, bind=get_engine(sql_connector_config) + )() Base = declarative_base() @@ -93,6 +86,25 @@ def reset_all_tables(db: Session) -> None: init_db(db) +def reset_all_tables_cli() -> None: + """Resets all tables via a cli.""" + parser = argparse.ArgumentParser( + prog="Reset SQL", + description="Copy data from one SQL database to another.", + epilog="Example: 'reset-sql secrets.json' or 'reset-sql ENV_VARS_'", + ) + parser.add_argument( + "config", + metavar="config", + default="ENV", + help=HELP_TEXT_CONFIG, + ) + add_logger_options_to_argparse(parser) + parsed = parser.parse_args(sys.argv[1:]) + configer_logger(namespace=parsed) + reset_all_tables(get_session(get_config_provider(parsed.config))) + + if __name__ == "__main__": """Main flow creating tables""" init_db(get_session(JsonFileConfigProvider("./secrets.json"))) diff --git a/src/aki_prj23_transparenzregister/utils/sql/copy_sql.py b/src/aki_prj23_transparenzregister/utils/sql/copy_sql.py index 37a7ab8..85499fa 100644 --- a/src/aki_prj23_transparenzregister/utils/sql/copy_sql.py +++ b/src/aki_prj23_transparenzregister/utils/sql/copy_sql.py @@ -7,7 +7,11 @@ from loguru import logger from sqlalchemy.engine import Engine from sqlalchemy.orm import Session -from aki_prj23_transparenzregister.utils.logger_config import configer_logger +from aki_prj23_transparenzregister.config.config_providers import get_config_provider +from aki_prj23_transparenzregister.utils.logger_config import ( + add_logger_options_to_argparse, + configer_logger, +) from aki_prj23_transparenzregister.utils.sql.connector import ( Base, get_session, @@ -36,48 +40,30 @@ def transfer_db_function(*, source: Session, destination: Session) -> None: ) -def copy_db_cli(args: list[str] | None = None) -> None: - """CLI interfaces to copy a db from source to destination. - - Args: - args: The args ar automaticlly collected from the cli if none are given. They should only be given for testing. - """ +def copy_db_cli() -> None: + """CLI interfaces to copy a db from source to destination.""" parser = argparse.ArgumentParser( prog="copy-sql", description="Copy data from one SQL database to another.", epilog="Example: copy-sql source.db destination.json", ) parser.add_argument( - "source", + "source-configuration", metavar="source", - help="Source database configuration.", + help="Source database configuration. Define the path to an sql lite db or a secrets.json", ) parser.add_argument( - "destination", + "destination-configuration", metavar="destination", - help="Destination database configuration.", - ) - parser.add_argument( - "--log-level", - choices=["info", "debug", "error", "warning"], - default="info", - metavar="log-level", - help="The log level for the output.", - ) - parser.add_argument( - "--log-path", - metavar="log_path", - help="A path to write the log to.", + help="Destination database configuration. Define the path to an sql lite db or a secrets.json", ) + add_logger_options_to_argparse(parser) - if not args: - args = sys.argv[1:] - - parsed = parser.parse_args(args) - configer_logger(level=parsed.log_level, path=parsed.log_path) - source = get_session(parsed.source) + parsed = parser.parse_args(sys.argv[1:]) + configer_logger(namespace=parsed) + source = get_session(get_config_provider(parsed.source)) logger.info(f"Connecting to {source.bind} as a source to copy from.") - destination = get_session(parsed.destination) + destination = get_session(get_config_provider(parsed.destination)) logger.info(f"Connecting to {destination.bind} as a destination to copy to.") transfer_db_function( source=source, diff --git a/tests/config/config_providers_test.py b/tests/config/config_providers_test.py index 3a06f51..5679298 100644 --- a/tests/config/config_providers_test.py +++ b/tests/config/config_providers_test.py @@ -1,12 +1,19 @@ """Tests the config provers.""" import json +from pathlib import Path from unittest.mock import mock_open, patch import pytest +from _pytest.monkeypatch import MonkeyPatch from aki_prj23_transparenzregister.config.config_providers import ( EnvironmentConfigProvider, JsonFileConfigProvider, + get_config_provider, +) +from aki_prj23_transparenzregister.config.config_template import ( + PostgreConnectionString, + SQLiteConnectionString, ) @@ -19,7 +26,7 @@ def test_json_provider_init_fail() -> None: def test_json_provider_init_no_json() -> None: """Tests if a non json file throws the correct error.""" with patch("os.path.isfile") as mock_isfile, patch( - "builtins.open", mock_open(read_data="fhdaofhdoas") + "builtins.open", mock_open(read_data="fh1da23of4!hdo4s") ): mock_isfile.return_value = True with pytest.raises(TypeError): @@ -30,20 +37,47 @@ def test_json_provider_init() -> None: """Tests the JsonFileConfigProvider creation.""" data = {"hello": "world"} input_data = json.dumps(data) + with patch("os.path.isfile") as mock_isfile, patch( + "builtins.open", mock_open(read_data=input_data) + ): + mock_isfile.return_value = True + provider = JsonFileConfigProvider("someWhere") + assert provider.__data__ == data + + +@pytest.mark.parametrize("as_path", [True, False]) +def test_get_config_provider_json(as_path: bool) -> None: + """Tests the JsonFileConfigProvider creation by the factory method.""" + data = {"hello": "world"} + input_data = json.dumps(data) with patch("os.path.isfile") as mock_isfile: mock_isfile.return_value = True with patch("builtins.open", mock_open(read_data=input_data)): - provider = JsonFileConfigProvider("someWhere") - assert provider.__data__ == data + config_provider = get_config_provider( + Path("some_where.json") if as_path else "some_where.json" + ) + assert isinstance(config_provider, JsonFileConfigProvider) + assert config_provider.__data__ == data -def test_json_provider_get_postgres() -> None: +@pytest.mark.parametrize("args", ["ENV", "env", "", None, "PYTHON_", "ELSE_"]) +def test_get_config_provider_env(args: str | None) -> None: + env_provider = get_config_provider(args) + assert isinstance(env_provider, EnvironmentConfigProvider) + + +def test_get_config_provider_fail() -> None: + with pytest.raises(ValueError, match="No configuration found"): + get_config_provider("something-else") + + +def test_json_provider_get_sql() -> None: """Tests if the config provider can return the postgre config string.""" data = { "postgres": { "username": "user", "password": "pass", - "host": "locahost", + "host": "localhost", "database": "postgres", "port": 420, } @@ -52,7 +86,8 @@ def test_json_provider_get_postgres() -> None: with patch("os.path.isfile") as mock_isfile: mock_isfile.return_value = True with patch("builtins.open", mock_open(read_data=input_data)): - config = JsonFileConfigProvider("someWhere").get_postgre_connection_string() + config = JsonFileConfigProvider("someWhere").get_sql_connection_string() + assert isinstance(config, PostgreConnectionString) assert config.username == data["postgres"]["username"] assert config.password == data["postgres"]["password"] assert config.host == data["postgres"]["host"] @@ -67,14 +102,39 @@ def test_json_provider_get_postgres() -> None: assert "Mongo" not in str(JsonFileConfigProvider("someWhere")) +@pytest.mark.parametrize("additional_data", [True, False]) +def test_json_provider_get_sqlit3(additional_data: bool) -> None: + """Tests if the config provider can return the sqlite config string.""" + data = { + "sqlite": "some.db", + } + if additional_data: + data |= { + "postgres": { # type: ignore + "username": "user", + "password": "pass", + "host": "localhost", + "database": "postgres", + "port": 420, + } + } + input_data = json.dumps(data) + with patch("os.path.isfile") as mock_isfile: + mock_isfile.return_value = True + with patch("builtins.open", mock_open(read_data=input_data)): + assert JsonFileConfigProvider( + "someWhere" + ).get_sql_connection_string() == SQLiteConnectionString("some.db") + + def test_json_provider_get_mongo() -> None: """Tests the JsonConfigProvider for the mongo db.""" data = { "mongo": { "username": "user", "password": "pass", - "host": "locahost", - "database": "postgres", + "host": "localhost", + "database": "mongo", "port": 420, } } @@ -98,35 +158,59 @@ def test_json_provider_get_mongo() -> None: assert "Postgre" not in str(JsonFileConfigProvider("someWhere")) -def test_env_provider_constructor() -> None: - with patch("aki_prj23_transparenzregister.config.config_providers.os") as mock_os: - keys = {"PYTHON_TEST": "test", "NOT_PYTHON_TEST": ""} - mock_os.environ = keys - provider = EnvironmentConfigProvider() - assert provider.__data__ == {"TEST": "test"} - - -def test_env_provider_postgres() -> None: +def test_env_provider_constructor(monkeypatch: MonkeyPatch) -> None: + env_configs = {"PYTHON_TEST": "test", "NOT_PYTHON_TEST": ""} + for key, value in env_configs.items(): + monkeypatch.setenv(key, value) provider = EnvironmentConfigProvider() - env_data = { - "POSTGRES_USERNAME": "postgres", - "POSTGRES_PASSWORD": "postgres", - "POSTGRES_HOST": "localhost", - "POSTGRES_DATABASE": "postgres", - "POSTGRES_PORT": "5432", + assert "TEST" in provider.__data__ + assert provider.__data__["TEST"] == "test" + assert "NOT_PYTHON_TEST" not in provider.__data__ + + +def test_env_provider_postgres(monkeypatch: MonkeyPatch) -> None: + env_configs = { + "PYTHON_POSTGRES_USERNAME": "postgres-user", + "PYTHON_POSTGRES_PASSWORD": "postgres-pw", + "PYTHON_POSTGRES_HOST": "localhost", + "PYTHON_POSTGRES_DATABASE": "postgres", + "PYTHON_POSTGRES_PORT": "5432", } - provider.__data__ = env_data - conn_string = provider.get_postgre_connection_string() + for env_config in env_configs.items(): + monkeypatch.setenv(*env_config) - assert conn_string.database == env_data["POSTGRES_DATABASE"] - assert conn_string.host == env_data["POSTGRES_HOST"] - assert conn_string.password == env_data["POSTGRES_PASSWORD"] - assert conn_string.port == env_data["POSTGRES_PORT"] - assert conn_string.username == env_data["POSTGRES_USERNAME"] - - -def test_env_provider_mongodb() -> None: provider = EnvironmentConfigProvider() + + conn_string = provider.get_sql_connection_string() + assert isinstance(conn_string, PostgreConnectionString) + + assert conn_string.database == env_configs["PYTHON_POSTGRES_DATABASE"] + assert conn_string.host == env_configs["PYTHON_POSTGRES_HOST"] + assert conn_string.password == env_configs["PYTHON_POSTGRES_PASSWORD"] + assert conn_string.port == env_configs["PYTHON_POSTGRES_PORT"] + assert conn_string.username == env_configs["PYTHON_POSTGRES_USERNAME"] + + +@pytest.mark.parametrize("additional_args", [True, False]) +def test_env_provider_sqlite(additional_args: bool, monkeypatch: MonkeyPatch) -> None: + env_configs = {"PYTHON_SQLITE_PATH": "some.db"} + if additional_args: + env_configs |= { + "PYTHON_POSTGRES_USERNAME": "postgres-user", + "PYTHON_POSTGRES_PASSWORD": "postgres-pw", + "PYTHON_POSTGRES_HOST": "localhost", + "PYTHON_POSTGRES_DATABASE": "postgres", + "PYTHON_POSTGRES_PORT": "5432", + } + for env_config in env_configs.items(): + monkeypatch.setenv(*env_config) + + provider = EnvironmentConfigProvider() + assert provider.get_sql_connection_string() == SQLiteConnectionString("some.db") + + +@pytest.mark.parametrize("prefix", ["", "NON_PYTHON", "ELSE"]) +def test_env_provider_mongodb(prefix: str, monkeypatch: MonkeyPatch) -> None: env_data = { "MONGO_USERNAME": "username", "MONGO_HOST": "localhost", @@ -134,6 +218,13 @@ def test_env_provider_mongodb() -> None: "MONGO_PORT": 27017, "MONGO_DATABASE": "transparenzregister", } + for key, value in env_data.items(): + monkeypatch.setenv(f"{prefix if prefix else 'PYTHON_'}{key}", str(value)) + if prefix: + provider = EnvironmentConfigProvider(prefix) + else: + provider = EnvironmentConfigProvider() + provider.__data__ = env_data conn_string = provider.get_mongo_connection_string() diff --git a/tests/config/config_template_test.py b/tests/config/config_template_test.py new file mode 100644 index 0000000..f1e0d44 --- /dev/null +++ b/tests/config/config_template_test.py @@ -0,0 +1,34 @@ +"""Test for config templates.""" +from aki_prj23_transparenzregister.config.config_template import ( + MongoConnection, + SQLiteConnectionString, +) + + +def test_sqlite_connection_string() -> None: + """Tests if the sqlite protocol is correctly added to a path.""" + assert str(SQLiteConnectionString("some-path-to.db")) == "sqlite:///some-path-to.db" + assert ( + str(SQLiteConnectionString("other-path-to.db")) == "sqlite:///other-path-to.db" + ) + assert SQLiteConnectionString("some-path-to.db") == SQLiteConnectionString( + "some-path-to.db" + ) + + +def test_get_conn_string_no_credentials() -> None: + """Tests the mongo connection string generation.""" + conn = MongoConnection("localhost", "", 27017, None, None) + assert conn.get_conn_string() == "mongodb://localhost:27017" + + +def test_get_conn_string_no_port_but_credentials() -> None: + """Tests the mongo connection string generation.""" + conn = MongoConnection("localhost", "", None, "admin", "password") + assert conn.get_conn_string() == "mongodb+srv://admin:password@localhost" + + +def test_get_conn_simple() -> None: + """Tests the mongo connection string generation.""" + conn = MongoConnection("localhost", "", None, None, None) + assert conn.get_conn_string() == "mongodb+srv://localhost" diff --git a/tests/conftest.py b/tests/conftest.py index 6fafcdc..ef44986 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -7,11 +7,15 @@ from typing import Any import pytest from sqlalchemy.engine import Engine -from sqlalchemy.orm import Session +from sqlalchemy.orm import Session, sessionmaker +from aki_prj23_transparenzregister.config.config_template import SQLiteConnectionString from aki_prj23_transparenzregister.utils import data_transfer from aki_prj23_transparenzregister.utils.sql import entities -from aki_prj23_transparenzregister.utils.sql.connector import get_session, init_db +from aki_prj23_transparenzregister.utils.sql.connector import ( + get_engine, + init_db, +) @pytest.fixture(autouse=True) @@ -39,7 +43,11 @@ def empty_db() -> Generator[Session, None, None]: """Generates a db Session to a sql_lite db.""" if os.path.exists("test-db.db"): os.remove("test-db.db") - db = get_session("sqlite:///test-db.db") + db = sessionmaker( + autocommit=False, + autoflush=False, + bind=get_engine(SQLiteConnectionString("test-db.db")), + )() init_db(db) yield db db.close() diff --git a/tests/ui/app_test.py b/tests/ui/app_test.py index e4cde6a..36c57c7 100644 --- a/tests/ui/app_test.py +++ b/tests/ui/app_test.py @@ -1,7 +1,11 @@ """Test for the main app dashboard.""" +import sys from collections.abc import Generator +from unittest.mock import MagicMock import pytest +from _pytest.monkeypatch import MonkeyPatch +from pytest_mock import MockerFixture from sqlalchemy.orm import Session from aki_prj23_transparenzregister.ui import app @@ -31,3 +35,27 @@ def test_go_to_company_page() -> None: """Checks if the go_to_company_page callback yields a result.""" output = app.go_to_company_page(1) assert output == "/Unternehmensdetails/1" + + +def test_main_of_app(monkeypatch: MonkeyPatch) -> None: + monkeypatch.setattr(sys, "argv", [sys.argv[0]]) + with pytest.raises(SystemExit): + app.main() + + +def test_main_of_app_help(monkeypatch: MonkeyPatch) -> None: + monkeypatch.setattr(sys, "argv", [sys.argv[0], "-h"]) + with pytest.raises(SystemExit): + app.main() + + +@pytest.mark.parametrize("upper", [True, False]) +def test_main_of_app_env( + monkeypatch: MonkeyPatch, upper: bool, mocker: MockerFixture +) -> None: + MagicMock() + monkeypatch.setattr(sys, "argv", [sys.argv[0], "ENV" if upper else "env"]) + mocked = mocker.patch("aki_prj23_transparenzregister.ui.app.Dash.run") + mocked.return_value = None + app.main() + mocked.assert_called_once() diff --git a/tests/utils/data_transfer_test.py b/tests/utils/data_transfer_test.py index a67525e..b26c404 100644 --- a/tests/utils/data_transfer_test.py +++ b/tests/utils/data_transfer_test.py @@ -1,6 +1,7 @@ """Test the transfer functions from mongodb to sql.""" import random import string +import sys from datetime import date from typing import Any @@ -8,13 +9,13 @@ import numpy as np import pandas as pd import pytest import sqlalchemy as sa +from _pytest.monkeypatch import MonkeyPatch from pytest_mock import MockerFixture from sqlalchemy.engine import Engine from sqlalchemy.orm import Session from aki_prj23_transparenzregister.models.company import CapitalTypeEnum, CurrencyEnum from aki_prj23_transparenzregister.utils import data_transfer -from aki_prj23_transparenzregister.utils.data_transfer import norm_capital from aki_prj23_transparenzregister.utils.sql import entities @@ -1025,7 +1026,9 @@ def test_add_annual_report_financial_key_error(full_db: Session) -> None: @pytest.mark.parametrize("currency", ["€", "EUR"]) def test_norm_capital_eur(currency: str, capital_type: str) -> None: """Tests if eur entries can be converted / normed correctly.""" - assert norm_capital({"value": 5, "currency": currency, "type": capital_type}) == { + assert data_transfer.norm_capital( + {"value": 5, "currency": currency, "type": capital_type} + ) == { "capital_value": 5.0, "capital_currency": CurrencyEnum("EUR"), "capital_type": CapitalTypeEnum(capital_type), @@ -1036,7 +1039,7 @@ def test_norm_capital_eur(currency: str, capital_type: str) -> None: @pytest.mark.parametrize("currency", ["DM", "DEM"]) def test_norm_capital_dm(currency: str, capital_type: CapitalTypeEnum) -> None: """Tests if dm entries can be converted / normed correctly.""" - assert norm_capital( + assert data_transfer.norm_capital( capital={"value": 5, "currency": currency, "type": capital_type} ) == { "capital_value": 2.56, @@ -1047,7 +1050,7 @@ def test_norm_capital_dm(currency: str, capital_type: CapitalTypeEnum) -> None: def test_norm_capital_fail() -> None: """Tests if the entry is dropped if it isn't complete.""" - assert norm_capital({"something": "something"}) == {} # type: ignore + assert data_transfer.norm_capital({"something": "something"}) == {} # type: ignore @pytest.mark.parametrize( @@ -1062,3 +1065,30 @@ def test_norm_capital_fail() -> None: ) def test_get_geocodes(zip_code: str | None, results: dict) -> None: assert data_transfer.get_geocodes(zip_code) == results + + +def test_transfer_data_cli(monkeypatch: MonkeyPatch) -> None: + monkeypatch.setattr(sys, "argv", [sys.argv[0]]) + with pytest.raises(SystemExit): + data_transfer.transfer_data_cli() + + +def test_transfer_data_cli_help(monkeypatch: MonkeyPatch) -> None: + monkeypatch.setattr(sys, "argv", [sys.argv[0], "-h"]) + with pytest.raises(SystemExit): + data_transfer.transfer_data_cli() + + +@pytest.mark.parametrize("upper", [True, False]) +def test_transfer_data_cli_env( + monkeypatch: MonkeyPatch, upper: bool, mocker: MockerFixture +) -> None: + monkeypatch.setattr(sys, "argv", [sys.argv[0], "ENV" if upper else "env"]) + mocker.patch( + "aki_prj23_transparenzregister.utils.data_transfer.transfer_data", lambda _: _ + ) + spy = mocker.spy(data_transfer, "transfer_data") + + # with pytest.raises(KeyError): + data_transfer.transfer_data_cli() + spy.assert_called_once() diff --git a/tests/utils/logger_config_test.py b/tests/utils/logger_config_test.py index 03263f9..12ca765 100644 --- a/tests/utils/logger_config_test.py +++ b/tests/utils/logger_config_test.py @@ -1,15 +1,21 @@ """Smoke-test over the logger config.""" +from argparse import ArgumentParser from pathlib import Path import pytest -from aki_prj23_transparenzregister.utils.logger_config import configer_logger +from aki_prj23_transparenzregister.utils.logger_config import ( + add_logger_options_to_argparse, + configer_logger, +) +@pytest.mark.parametrize("parser", [True, False]) @pytest.mark.parametrize("path", [None, "test-log.log", ""]) @pytest.mark.parametrize("upper", [True, False]) -@pytest.mark.parametrize("level", ["info", "debug", "error", "warning"]) +@pytest.mark.parametrize("level", ["info", "debug", "error"]) def test_configer_logger( + parser: bool, level: str, upper: bool, path: Path | str | None, @@ -17,10 +23,32 @@ def test_configer_logger( """Tests the configuration of the logger. Args: + parser: If the arguments should be given via the parser or not. level: The log-level to configure. upper: If the upper variant of the level should be used. path: The path where to save the log. """ if level.upper(): level = level.upper() - configer_logger(level, path) # type: ignore + if parser: + args_parser = ArgumentParser() + add_logger_options_to_argparse(args_parser) + configer_logger( + namespace=args_parser.parse_args( + [ + "--level", + level if level else "", + "--log-path", + str(path) if path else "", + ] + ) + ) + else: + configer_logger(level=level, path=path) # type: ignore + + +def test_add_logger_options_to_argparse() -> None: + """A test checking if the ArgumentParser is modified.""" + args_parser = ArgumentParser() + + add_logger_options_to_argparse(args_parser) diff --git a/tests/utils/mongo/mongo_test.py b/tests/utils/mongo/mongo_test.py index eb63a4b..4195af5 100644 --- a/tests/utils/mongo/mongo_test.py +++ b/tests/utils/mongo/mongo_test.py @@ -1,30 +1,12 @@ """Tests for connecting to the mongodb.""" from unittest.mock import patch +from aki_prj23_transparenzregister.config.config_template import MongoConnection from aki_prj23_transparenzregister.utils.mongo.connector import ( - MongoConnection, MongoConnector, ) -def test_get_conn_string_no_credentials() -> None: - """Tests the mongo connection string generation.""" - conn = MongoConnection("localhost", "", 27017, None, None) - assert conn.get_conn_string() == "mongodb://localhost:27017" - - -def test_get_conn_string_no_port_but_credentials() -> None: - """Tests the mongo connection string generation.""" - conn = MongoConnection("localhost", "", None, "admin", "password") - assert conn.get_conn_string() == "mongodb+srv://admin:password@localhost" - - -def test_get_conn_simple() -> None: - """Tests the mongo connection string generation.""" - conn = MongoConnection("localhost", "", None, None, None) - assert conn.get_conn_string() == "mongodb+srv://localhost" - - def test_mongo_connector() -> None: """Tests the MongoConnector.""" with patch("pymongo.MongoClient") as mock_mongo_client: diff --git a/tests/utils/sql/connector_test.py b/tests/utils/sql/connector_test.py index a671883..61d9bf1 100644 --- a/tests/utils/sql/connector_test.py +++ b/tests/utils/sql/connector_test.py @@ -1,16 +1,14 @@ """Tests the sql connector.""" import os.path from collections.abc import Generator -from typing import Any from unittest.mock import Mock, patch import pytest -from sqlalchemy.engine import Engine from aki_prj23_transparenzregister.config.config_providers import JsonFileConfigProvider from aki_prj23_transparenzregister.config.config_template import PostgreConnectionString from aki_prj23_transparenzregister.utils.sql.connector import ( - get_pg_engine, + get_engine, get_session, init_db, ) @@ -24,7 +22,13 @@ def test_get_engine_pg() -> None: ) as mock_create_engine: result = "someThing" mock_create_engine.return_value = result - assert get_pg_engine(conn_args) == result + assert get_engine(conn_args) == result + + +def test_get_engine_fail() -> None: + """Tests what happens if the wrong type is given to the engine factory.""" + with pytest.raises(TypeError, match="The type of the configuration is invalid."): + get_engine(None) # type: ignore @pytest.fixture() @@ -44,29 +48,10 @@ def delete_sqlite_table() -> Generator[str, None, None]: os.remove(sqlite_test_path) -def test_get_sqlite_init(delete_sqlite_table: str) -> None: - """Tests if a sql table file can be initiated.""" - assert not os.path.exists(delete_sqlite_table) - session = get_session(f"sqlite:///{delete_sqlite_table}") - init_db(session) - session.close() - engine = session.bind - assert isinstance(engine, Engine) - engine.dispose() - assert os.path.exists(delete_sqlite_table) - - -@pytest.mark.parametrize("connection", ["faulty-name", 0, 9.2, True]) -def test_get_invalid_connection(connection: Any) -> None: - """Tests if an error is thrown on a faulty connections.""" - with pytest.raises(TypeError): - get_session(connection) - - def test_init_pd_db() -> None: """Tests if a pg sql database can be connected and initiated to.""" with patch( - "aki_prj23_transparenzregister.utils.sql.connector.get_pg_engine" + "aki_prj23_transparenzregister.utils.sql.connector.get_engine" ) as mock_get_engine, patch( "aki_prj23_transparenzregister.utils.sql.connector.declarative_base" ) as mock_declarative_base: @@ -77,6 +62,6 @@ def test_init_pd_db() -> None: mock_declarative_base.return_value = mock_value mock_value = Mock(spec=JsonFileConfigProvider) - mock_value.get_postgre_connection_string.return_value = "" + mock_value.get_sql_connection_string.return_value = "" init_db(get_session(mock_value)) diff --git a/tests/utils/sql/copy_sql_test.py b/tests/utils/sql/copy_sql_test.py index ebe3cea..28fad91 100644 --- a/tests/utils/sql/copy_sql_test.py +++ b/tests/utils/sql/copy_sql_test.py @@ -1,13 +1,20 @@ """Test if the sql db can be copied.""" import os +import sys from collections.abc import Generator import pandas as pd import pytest +from _pytest.monkeypatch import MonkeyPatch from sqlalchemy.engine import Engine -from sqlalchemy.orm import Session +from sqlalchemy.orm import Session, sessionmaker -from aki_prj23_transparenzregister.utils.sql.connector import Base, get_session, init_db +from aki_prj23_transparenzregister.config.config_template import SQLiteConnectionString +from aki_prj23_transparenzregister.utils.sql.connector import ( + Base, + get_engine, + init_db, +) from aki_prj23_transparenzregister.utils.sql.copy_sql import ( copy_db_cli, transfer_db_function, @@ -19,7 +26,13 @@ def destination_db() -> Generator[Session, None, None]: """Generates a db Session to a sqlite db to copy data to.""" if os.path.exists("secondary.db"): os.remove("secondary.db") - db = get_session("sqlite:///secondary.db") + + db = sessionmaker( + autocommit=False, + autoflush=False, + bind=get_engine(SQLiteConnectionString("secondary.db")), + )() + init_db(db) yield db db.close() @@ -44,13 +57,15 @@ def test_transfer_db(full_db: Session, destination_db: Session) -> None: ) -def test_copy_db_cli_help1() -> None: +def test_copy_db_cli_help1(monkeypatch: MonkeyPatch) -> None: """Tests if the help argument exits the software gracefully.""" - with pytest.raises(SystemExit): - copy_db_cli(["-h"]) + with monkeypatch.context() as m, pytest.raises(SystemExit): # noqa: PT012 + m.setattr(sys, "argv", [sys.argv[0], "-h"]) + copy_db_cli() -def test_copy_db_cli_help2() -> None: +def test_copy_db_cli_help2(monkeypatch: MonkeyPatch) -> None: """Tests if the help argument exits the software gracefully.""" - with pytest.raises(SystemExit): - copy_db_cli(["eskse", "-h", "asdf"]) + with monkeypatch.context() as m, pytest.raises(SystemExit): # noqa: PT012 + m.setattr(sys, "argv", [sys.argv[0], "eskse", "-h", "asdf"]) + copy_db_cli()