diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
new file mode 100644
index 0000000..61c0fac
--- /dev/null
+++ b/.github/workflows/tests.yml
@@ -0,0 +1,33 @@
+name: Tests
+
+on:
+ push:
+ branches: [master, develop]
+ pull_request:
+ branches: [master, develop]
+
+jobs:
+ unittest:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ python-version: ['3.7', '3.8', '3.9', '3.10']
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v3
+
+ - name: Set up Python ${{ matrix.python-version }}
+ uses: actions/setup-python@v4
+ with:
+ python-version: ${{ matrix.python-version }}
+
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip
+ python -m pip install pipenv
+ pipenv install
+
+ - name: Run Unittest
+ run: |
+ pipenv run python -m unittest discover ./tests/ --verbose
diff --git a/.gitignore b/.gitignore
index 6bf4e1e..4989d93 100644
--- a/.gitignore
+++ b/.gitignore
@@ -128,3 +128,6 @@ dmypy.json
# Pyre type checker
.pyre/
+
+# VSCode
+.vscode
diff --git a/Pipfile b/Pipfile
index bd65ac8..79d0c86 100644
--- a/Pipfile
+++ b/Pipfile
@@ -5,9 +5,9 @@ name = "pypi"
[packages]
requests = ">=2.27.1"
-pandas = ">=1.4.1"
[dev-packages]
+pylint = "*"
[requires]
python_version = "3.8"
diff --git a/Pipfile.lock b/Pipfile.lock
index 6599585..5d46c4d 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
- "sha256": "c2bc3ad49aa1ec0cea5f148538c9965f7f5f5c5be86f2936e9d0807e7dba360d"
+ "sha256": "8d286595dd20848e21afca75948139b315fde9d4b313d3e58d574b33c020b1d7"
},
"pipfile-spec": 6,
"requires": {
@@ -18,79 +18,27 @@
"default": {
"certifi": {
"hashes": [
- "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872",
- "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"
+ "sha256:0d9c601124e5a6ba9712dbc60d9c53c21e34f5f641fe83002317394311bdce14",
+ "sha256:90c1a32f1d68f940488354e36370f6cca89f0f106db09518524c88d6ed83f382"
],
- "version": "==2021.10.8"
+ "markers": "python_version >= '3.6'",
+ "version": "==2022.9.24"
},
"charset-normalizer": {
"hashes": [
- "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597",
- "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"
+ "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845",
+ "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"
],
- "markers": "python_version >= '3'",
- "version": "==2.0.12"
+ "markers": "python_full_version >= '3.6.0'",
+ "version": "==2.1.1"
},
"idna": {
"hashes": [
- "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff",
- "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"
- ],
- "markers": "python_version >= '3'",
- "version": "==3.3"
- },
- "numpy": {
- "hashes": [
- "sha256:07a8c89a04997625236c5ecb7afe35a02af3896c8aa01890a849913a2309c676",
- "sha256:08d9b008d0156c70dc392bb3ab3abb6e7a711383c3247b410b39962263576cd4",
- "sha256:201b4d0552831f7250a08d3b38de0d989d6f6e4658b709a02a73c524ccc6ffce",
- "sha256:2c10a93606e0b4b95c9b04b77dc349b398fdfbda382d2a39ba5a822f669a0123",
- "sha256:3ca688e1b9b95d80250bca34b11a05e389b1420d00e87a0d12dc45f131f704a1",
- "sha256:48a3aecd3b997bf452a2dedb11f4e79bc5bfd21a1d4cc760e703c31d57c84b3e",
- "sha256:568dfd16224abddafb1cbcce2ff14f522abe037268514dd7e42c6776a1c3f8e5",
- "sha256:5bfb1bb598e8229c2d5d48db1860bcf4311337864ea3efdbe1171fb0c5da515d",
- "sha256:639b54cdf6aa4f82fe37ebf70401bbb74b8508fddcf4797f9fe59615b8c5813a",
- "sha256:8251ed96f38b47b4295b1ae51631de7ffa8260b5b087808ef09a39a9d66c97ab",
- "sha256:92bfa69cfbdf7dfc3040978ad09a48091143cffb778ec3b03fa170c494118d75",
- "sha256:97098b95aa4e418529099c26558eeb8486e66bd1e53a6b606d684d0c3616b168",
- "sha256:a3bae1a2ed00e90b3ba5f7bd0a7c7999b55d609e0c54ceb2b076a25e345fa9f4",
- "sha256:c34ea7e9d13a70bf2ab64a2532fe149a9aced424cd05a2c4ba662fd989e3e45f",
- "sha256:dbc7601a3b7472d559dc7b933b18b4b66f9aa7452c120e87dfb33d02008c8a18",
- "sha256:e7927a589df200c5e23c57970bafbd0cd322459aa7b1ff73b7c2e84d6e3eae62",
- "sha256:f8c1f39caad2c896bc0018f699882b345b2a63708008be29b1f355ebf6f933fe",
- "sha256:f950f8845b480cffe522913d35567e29dd381b0dc7e4ce6a4a9f9156417d2430",
- "sha256:fade0d4f4d292b6f39951b6836d7a3c7ef5b2347f3c420cd9820a1d90d794802",
- "sha256:fdf3c08bce27132395d3c3ba1503cac12e17282358cb4bddc25cc46b0aca07aa"
- ],
- "markers": "python_version < '3.10' and platform_machine != 'aarch64' and platform_machine != 'arm64'",
- "version": "==1.22.3"
- },
- "pandas": {
- "hashes": [
- "sha256:0259cd11e7e6125aaea3af823b80444f3adad6149ff4c97fef760093598b3e34",
- "sha256:04dd15d9db538470900c851498e532ef28d4e56bfe72c9523acb32042de43dfb",
- "sha256:0b1a13f647e4209ed7dbb5da3497891d0045da9785327530ab696417ef478f84",
- "sha256:19f7c632436b1b4f84615c3b127bbd7bc603db95e3d4332ed259dc815c9aaa26",
- "sha256:1b384516dbb4e6aae30e3464c2e77c563da5980440fbdfbd0968e3942f8f9d70",
- "sha256:1d85d5f6be66dfd6d1d8d13b9535e342a2214260f1852654b19fa4d7b8d1218b",
- "sha256:2e5a7a1e0ecaac652326af627a3eca84886da9e667d68286866d4e33f6547caf",
- "sha256:3129a35d9dad1d80c234dd78f8f03141b914395d23f97cf92a366dcd19f8f8bf",
- "sha256:358b0bc98a5ff067132d23bf7a2242ee95db9ea5b7bbc401cf79205f11502fd3",
- "sha256:3dfb32ed50122fe8c5e7f2b8d97387edd742cc78f9ec36f007ee126cd3720907",
- "sha256:4e1176f45981c8ccc8161bc036916c004ca51037a7ed73f2d2a9857e6dbe654f",
- "sha256:508c99debccd15790d526ce6b1624b97a5e1e4ca5b871319fb0ebfd46b8f4dad",
- "sha256:6105af6533f8b63a43ea9f08a2ede04e8f43e49daef0209ab0d30352bcf08bee",
- "sha256:6d6ad1da00c7cc7d8dd1559a6ba59ba3973be6b15722d49738b2be0977eb8a0c",
- "sha256:7ea47ba1d6f359680130bd29af497333be6110de8f4c35b9211eec5a5a9630fa",
- "sha256:8db93ec98ac7cb5f8ac1420c10f5e3c43533153f253fe7fb6d891cf5aa2b80d2",
- "sha256:96e9ece5759f9b47ae43794b6359bbc54805d76e573b161ae770c1ea59393106",
- "sha256:bbb15ad79050e8b8d39ec40dd96a30cd09b886a2ae8848d0df1abba4d5502a67",
- "sha256:c614001129b2a5add5e3677c3a213a9e6fd376204cb8d17c04e84ff7dfc02a73",
- "sha256:e6a7bbbb7950063bfc942f8794bc3e31697c020a14f1cd8905fc1d28ec674a01",
- "sha256:f02e85e6d832be37d7f16cf6ac8bb26b519ace3e5f3235564a91c7f658ab2a43"
+ "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4",
+ "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"
],
- "index": "pypi",
- "version": "==1.4.1"
+ "markers": "python_version >= '3.5'",
+ "version": "==3.4"
},
"python-dateutil": {
"hashes": [
@@ -102,18 +50,18 @@
},
"pytz": {
"hashes": [
- "sha256:1e760e2fe6a8163bc0b3d9a19c4f84342afa0a2affebfaa84b01b978a02ecaa7",
- "sha256:e68985985296d9a66a881eb3193b0906246245294a881e7c8afe623866ac6a5c"
+ "sha256:2c0784747071402c6e99f0bafdb7da0fa22645f06554c7ae06bf6358897e9c91",
+ "sha256:48ce799d83b6f8aab2020e369b627446696619e79645419610b9facd909b3174"
],
- "version": "==2022.1"
+ "version": "==2022.4"
},
"requests": {
"hashes": [
- "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61",
- "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"
+ "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983",
+ "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"
],
"index": "pypi",
- "version": "==2.27.1"
+ "version": "==2.28.1"
},
"six": {
"hashes": [
@@ -125,12 +73,206 @@
},
"urllib3": {
"hashes": [
- "sha256:44ece4d53fb1706f667c9bd1c648f5469a2ec925fcf3a776667042d645472c14",
- "sha256:aabaf16477806a5e1dd19aa41f8c2b7950dd3c746362d7e3223dbe6de6ac448e"
+ "sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e",
+ "sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997"
],
- "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
- "version": "==1.26.9"
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5' and python_version < '4'",
+ "version": "==1.26.12"
}
},
- "develop": {}
+ "develop": {
+ "astroid": {
+ "hashes": [
+ "sha256:2df4f9980c4511474687895cbfdb8558293c1a826d9118bb09233d7c2bff1c83",
+ "sha256:867a756bbf35b7bc07b35bfa6522acd01f91ad9919df675e8428072869792dce"
+ ],
+ "markers": "python_full_version >= '3.7.2'",
+ "version": "==2.12.11"
+ },
+ "dill": {
+ "hashes": [
+ "sha256:33501d03270bbe410c72639b350e941882a8b0fd55357580fbc873fba0c59302",
+ "sha256:d75e41f3eff1eee599d738e76ba8f4ad98ea229db8b085318aa2b3333a208c86"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6'",
+ "version": "==0.3.5.1"
+ },
+ "isort": {
+ "hashes": [
+ "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7",
+ "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"
+ ],
+ "markers": "python_version < '4.0' and python_full_version >= '3.6.1'",
+ "version": "==5.10.1"
+ },
+ "lazy-object-proxy": {
+ "hashes": [
+ "sha256:043651b6cb706eee4f91854da4a089816a6606c1428fd391573ef8cb642ae4f7",
+ "sha256:07fa44286cda977bd4803b656ffc1c9b7e3bc7dff7d34263446aec8f8c96f88a",
+ "sha256:12f3bb77efe1367b2515f8cb4790a11cffae889148ad33adad07b9b55e0ab22c",
+ "sha256:2052837718516a94940867e16b1bb10edb069ab475c3ad84fd1e1a6dd2c0fcfc",
+ "sha256:2130db8ed69a48a3440103d4a520b89d8a9405f1b06e2cc81640509e8bf6548f",
+ "sha256:39b0e26725c5023757fc1ab2a89ef9d7ab23b84f9251e28f9cc114d5b59c1b09",
+ "sha256:46ff647e76f106bb444b4533bb4153c7370cdf52efc62ccfc1a28bdb3cc95442",
+ "sha256:4dca6244e4121c74cc20542c2ca39e5c4a5027c81d112bfb893cf0790f96f57e",
+ "sha256:553b0f0d8dbf21890dd66edd771f9b1b5f51bd912fa5f26de4449bfc5af5e029",
+ "sha256:677ea950bef409b47e51e733283544ac3d660b709cfce7b187f5ace137960d61",
+ "sha256:6a24357267aa976abab660b1d47a34aaf07259a0c3859a34e536f1ee6e76b5bb",
+ "sha256:6a6e94c7b02641d1311228a102607ecd576f70734dc3d5e22610111aeacba8a0",
+ "sha256:6aff3fe5de0831867092e017cf67e2750c6a1c7d88d84d2481bd84a2e019ec35",
+ "sha256:6ecbb350991d6434e1388bee761ece3260e5228952b1f0c46ffc800eb313ff42",
+ "sha256:7096a5e0c1115ec82641afbdd70451a144558ea5cf564a896294e346eb611be1",
+ "sha256:70ed0c2b380eb6248abdef3cd425fc52f0abd92d2b07ce26359fcbc399f636ad",
+ "sha256:8561da8b3dd22d696244d6d0d5330618c993a215070f473b699e00cf1f3f6443",
+ "sha256:85b232e791f2229a4f55840ed54706110c80c0a210d076eee093f2b2e33e1bfd",
+ "sha256:898322f8d078f2654d275124a8dd19b079080ae977033b713f677afcfc88e2b9",
+ "sha256:8f3953eb575b45480db6568306893f0bd9d8dfeeebd46812aa09ca9579595148",
+ "sha256:91ba172fc5b03978764d1df5144b4ba4ab13290d7bab7a50f12d8117f8630c38",
+ "sha256:9d166602b525bf54ac994cf833c385bfcc341b364e3ee71e3bf5a1336e677b55",
+ "sha256:a57d51ed2997e97f3b8e3500c984db50a554bb5db56c50b5dab1b41339b37e36",
+ "sha256:b9e89b87c707dd769c4ea91f7a31538888aad05c116a59820f28d59b3ebfe25a",
+ "sha256:bb8c5fd1684d60a9902c60ebe276da1f2281a318ca16c1d0a96db28f62e9166b",
+ "sha256:c19814163728941bb871240d45c4c30d33b8a2e85972c44d4e63dd7107faba44",
+ "sha256:c4ce15276a1a14549d7e81c243b887293904ad2d94ad767f42df91e75fd7b5b6",
+ "sha256:c7a683c37a8a24f6428c28c561c80d5f4fd316ddcf0c7cab999b15ab3f5c5c69",
+ "sha256:d609c75b986def706743cdebe5e47553f4a5a1da9c5ff66d76013ef396b5a8a4",
+ "sha256:d66906d5785da8e0be7360912e99c9188b70f52c422f9fc18223347235691a84",
+ "sha256:dd7ed7429dbb6c494aa9bc4e09d94b778a3579be699f9d67da7e6804c422d3de",
+ "sha256:df2631f9d67259dc9620d831384ed7732a198eb434eadf69aea95ad18c587a28",
+ "sha256:e368b7f7eac182a59ff1f81d5f3802161932a41dc1b1cc45c1f757dc876b5d2c",
+ "sha256:e40f2013d96d30217a51eeb1db28c9ac41e9d0ee915ef9d00da639c5b63f01a1",
+ "sha256:f769457a639403073968d118bc70110e7dce294688009f5c24ab78800ae56dc8",
+ "sha256:fccdf7c2c5821a8cbd0a9440a456f5050492f2270bd54e94360cac663398739b",
+ "sha256:fd45683c3caddf83abbb1249b653a266e7069a09f486daa8863fb0e7496a9fdb"
+ ],
+ "markers": "python_version >= '3.6'",
+ "version": "==1.7.1"
+ },
+ "mccabe": {
+ "hashes": [
+ "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325",
+ "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"
+ ],
+ "markers": "python_version >= '3.6'",
+ "version": "==0.7.0"
+ },
+ "platformdirs": {
+ "hashes": [
+ "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788",
+ "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"
+ ],
+ "markers": "python_version >= '3.7'",
+ "version": "==2.5.2"
+ },
+ "pylint": {
+ "hashes": [
+ "sha256:5441e9294335d354b7bad57c1044e5bd7cce25c433475d76b440e53452fa5cb8",
+ "sha256:629cf1dbdfb6609d7e7a45815a8bb59300e34aa35783b5ac563acaca2c4022e9"
+ ],
+ "index": "pypi",
+ "version": "==2.15.4"
+ },
+ "setuptools": {
+ "hashes": [
+ "sha256:7999cbd87f1b6e1f33bf47efa368b224bed5e27b5ef2c4d46580186cbcb1a86a",
+ "sha256:a65e3802053e99fc64c6b3b29c11132943d5b8c8facbcc461157511546510967"
+ ],
+ "markers": "python_version >= '3.7'",
+ "version": "==62.0.0"
+ },
+ "tomli": {
+ "hashes": [
+ "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc",
+ "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"
+ ],
+ "markers": "python_version < '3.11'",
+ "version": "==2.0.1"
+ },
+ "tomlkit": {
+ "hashes": [
+ "sha256:571854ebbb5eac89abcb4a2e47d7ea27b89bf29e09c35395da6f03dd4ae23d1c",
+ "sha256:f2ef9da9cef846ee027947dc99a45d6b68a63b0ebc21944649505bf2e8bc5fe7"
+ ],
+ "markers": "python_version >= '3.6' and python_version < '4.0'",
+ "version": "==0.11.5"
+ },
+ "typing-extensions": {
+ "hashes": [
+ "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa",
+ "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"
+ ],
+ "markers": "python_version < '3.10'",
+ "version": "==4.4.0"
+ },
+ "wrapt": {
+ "hashes": [
+ "sha256:00b6d4ea20a906c0ca56d84f93065b398ab74b927a7a3dbd470f6fc503f95dc3",
+ "sha256:01c205616a89d09827986bc4e859bcabd64f5a0662a7fe95e0d359424e0e071b",
+ "sha256:02b41b633c6261feff8ddd8d11c711df6842aba629fdd3da10249a53211a72c4",
+ "sha256:07f7a7d0f388028b2df1d916e94bbb40624c59b48ecc6cbc232546706fac74c2",
+ "sha256:11871514607b15cfeb87c547a49bca19fde402f32e2b1c24a632506c0a756656",
+ "sha256:1b376b3f4896e7930f1f772ac4b064ac12598d1c38d04907e696cc4d794b43d3",
+ "sha256:21ac0156c4b089b330b7666db40feee30a5d52634cc4560e1905d6529a3897ff",
+ "sha256:257fd78c513e0fb5cdbe058c27a0624c9884e735bbd131935fd49e9fe719d310",
+ "sha256:2b39d38039a1fdad98c87279b48bc5dce2c0ca0d73483b12cb72aa9609278e8a",
+ "sha256:2cf71233a0ed05ccdabe209c606fe0bac7379fdcf687f39b944420d2a09fdb57",
+ "sha256:2fe803deacd09a233e4762a1adcea5db5d31e6be577a43352936179d14d90069",
+ "sha256:3232822c7d98d23895ccc443bbdf57c7412c5a65996c30442ebe6ed3df335383",
+ "sha256:34aa51c45f28ba7f12accd624225e2b1e5a3a45206aa191f6f9aac931d9d56fe",
+ "sha256:36f582d0c6bc99d5f39cd3ac2a9062e57f3cf606ade29a0a0d6b323462f4dd87",
+ "sha256:380a85cf89e0e69b7cfbe2ea9f765f004ff419f34194018a6827ac0e3edfed4d",
+ "sha256:40e7bc81c9e2b2734ea4bc1aceb8a8f0ceaac7c5299bc5d69e37c44d9081d43b",
+ "sha256:43ca3bbbe97af00f49efb06e352eae40434ca9d915906f77def219b88e85d907",
+ "sha256:4fcc4649dc762cddacd193e6b55bc02edca674067f5f98166d7713b193932b7f",
+ "sha256:5a0f54ce2c092aaf439813735584b9537cad479575a09892b8352fea5e988dc0",
+ "sha256:5a9a0d155deafd9448baff28c08e150d9b24ff010e899311ddd63c45c2445e28",
+ "sha256:5b02d65b9ccf0ef6c34cba6cf5bf2aab1bb2f49c6090bafeecc9cd81ad4ea1c1",
+ "sha256:60db23fa423575eeb65ea430cee741acb7c26a1365d103f7b0f6ec412b893853",
+ "sha256:642c2e7a804fcf18c222e1060df25fc210b9c58db7c91416fb055897fc27e8cc",
+ "sha256:6a9a25751acb379b466ff6be78a315e2b439d4c94c1e99cb7266d40a537995d3",
+ "sha256:6b1a564e6cb69922c7fe3a678b9f9a3c54e72b469875aa8018f18b4d1dd1adf3",
+ "sha256:6d323e1554b3d22cfc03cd3243b5bb815a51f5249fdcbb86fda4bf62bab9e164",
+ "sha256:6e743de5e9c3d1b7185870f480587b75b1cb604832e380d64f9504a0535912d1",
+ "sha256:709fe01086a55cf79d20f741f39325018f4df051ef39fe921b1ebe780a66184c",
+ "sha256:7b7c050ae976e286906dd3f26009e117eb000fb2cf3533398c5ad9ccc86867b1",
+ "sha256:7d2872609603cb35ca513d7404a94d6d608fc13211563571117046c9d2bcc3d7",
+ "sha256:7ef58fb89674095bfc57c4069e95d7a31cfdc0939e2a579882ac7d55aadfd2a1",
+ "sha256:80bb5c256f1415f747011dc3604b59bc1f91c6e7150bd7db03b19170ee06b320",
+ "sha256:81b19725065dcb43df02b37e03278c011a09e49757287dca60c5aecdd5a0b8ed",
+ "sha256:833b58d5d0b7e5b9832869f039203389ac7cbf01765639c7309fd50ef619e0b1",
+ "sha256:88bd7b6bd70a5b6803c1abf6bca012f7ed963e58c68d76ee20b9d751c74a3248",
+ "sha256:8ad85f7f4e20964db4daadcab70b47ab05c7c1cf2a7c1e51087bfaa83831854c",
+ "sha256:8c0ce1e99116d5ab21355d8ebe53d9460366704ea38ae4d9f6933188f327b456",
+ "sha256:8d649d616e5c6a678b26d15ece345354f7c2286acd6db868e65fcc5ff7c24a77",
+ "sha256:903500616422a40a98a5a3c4ff4ed9d0066f3b4c951fa286018ecdf0750194ef",
+ "sha256:9736af4641846491aedb3c3f56b9bc5568d92b0692303b5a305301a95dfd38b1",
+ "sha256:988635d122aaf2bdcef9e795435662bcd65b02f4f4c1ae37fbee7401c440b3a7",
+ "sha256:9cca3c2cdadb362116235fdbd411735de4328c61425b0aa9f872fd76d02c4e86",
+ "sha256:9e0fd32e0148dd5dea6af5fee42beb949098564cc23211a88d799e434255a1f4",
+ "sha256:9f3e6f9e05148ff90002b884fbc2a86bd303ae847e472f44ecc06c2cd2fcdb2d",
+ "sha256:a85d2b46be66a71bedde836d9e41859879cc54a2a04fad1191eb50c2066f6e9d",
+ "sha256:a9a52172be0b5aae932bef82a79ec0a0ce87288c7d132946d645eba03f0ad8a8",
+ "sha256:aa31fdcc33fef9eb2552cbcbfee7773d5a6792c137b359e82879c101e98584c5",
+ "sha256:b014c23646a467558be7da3d6b9fa409b2c567d2110599b7cf9a0c5992b3b471",
+ "sha256:b21bb4c09ffabfa0e85e3a6b623e19b80e7acd709b9f91452b8297ace2a8ab00",
+ "sha256:b5901a312f4d14c59918c221323068fad0540e34324925c8475263841dbdfe68",
+ "sha256:b9b7a708dd92306328117d8c4b62e2194d00c365f18eff11a9b53c6f923b01e3",
+ "sha256:d1967f46ea8f2db647c786e78d8cc7e4313dbd1b0aca360592d8027b8508e24d",
+ "sha256:d52a25136894c63de15a35bc0bdc5adb4b0e173b9c0d07a2be9d3ca64a332735",
+ "sha256:d77c85fedff92cf788face9bfa3ebaa364448ebb1d765302e9af11bf449ca36d",
+ "sha256:d79d7d5dc8a32b7093e81e97dad755127ff77bcc899e845f41bf71747af0c569",
+ "sha256:dbcda74c67263139358f4d188ae5faae95c30929281bc6866d00573783c422b7",
+ "sha256:ddaea91abf8b0d13443f6dac52e89051a5063c7d014710dcb4d4abb2ff811a59",
+ "sha256:dee0ce50c6a2dd9056c20db781e9c1cfd33e77d2d569f5d1d9321c641bb903d5",
+ "sha256:dee60e1de1898bde3b238f18340eec6148986da0455d8ba7848d50470a7a32fb",
+ "sha256:e2f83e18fe2f4c9e7db597e988f72712c0c3676d337d8b101f6758107c42425b",
+ "sha256:e3fb1677c720409d5f671e39bac6c9e0e422584e5f518bfd50aa4cbbea02433f",
+ "sha256:ee2b1b1769f6707a8a445162ea16dddf74285c3964f605877a20e38545c3c462",
+ "sha256:ee6acae74a2b91865910eef5e7de37dc6895ad96fa23603d1d27ea69df545015",
+ "sha256:ef3f72c9666bba2bab70d2a8b79f2c6d2c1a42a7f7e2b0ec83bb2f9e383950af"
+ ],
+ "markers": "python_version < '3.11'",
+ "version": "==1.14.1"
+ }
+ }
}
diff --git a/README.md b/README.md
index 9891742..11b4fc6 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
-
-
Filômetro
+
+
Filometro
@@ -9,9 +9,6 @@
-
-
-
@@ -26,6 +23,9 @@
+
+
+
@@ -36,28 +36,30 @@
- [Demo](#demo)
- [Documentação](#documentação)
- [Como utilizar?](#como-utilizar)
+ - [Objeto Posto](#objeto-posto)
- [Métodos da classe Filometro](#métodos-da-classe-filometro)
- - [Identificadores para filtragem (Enums)](#identificadores-para-filtragem-enumsidentificadores-para-filtragem-enums)
+ - [Identificadores para filtragem](#identificadores-para-filtragem)
- [Contribuições](#contribuições)
- [Licença](#licença)
## O que é?
-Filômetro é um pacote que faz o papel de um wrapper (embrulho) do site [De Olho Na Fila](https://deolhonafila.prefeitura.sp.gov.br/), de modo a disponibilizar acesso a diversos dados sobre postos de vacinação em todo o Estado de São Paulo.
+Filometro é um wrapper (embrulho) do site [De Olho Na Fila](https://deolhonafila.prefeitura.sp.gov.br/), de modo a disponibilizar acesso a diversos dados sobre os postos de vacinação em todo o Estado de São Paulo.
Com esse pacote você tem acesso aos seguintes dados dos postos:
- Equipamento
- Endereço
+- Contatos
- Distrito
- Zona
-- Os imunizantes
+- Os imunizantes disponíveis
- Situação da fila
- Modalidade
- Data e hora da última atualização
-As informações são exatamente as mesmas disponíveis no site oficial (De Olho na Fila), porém disponibilizados por meio de uma interface Python simples para facilitar o uso, manipulação e filtragem dos dados. Consulte a [documentação](#documentação) para saber como utiliza-lo.
+As informações são exatamente as mesmas disponíveis no site oficial (De Olho na Fila), porém disponibilizados por meio de um pacote Python simples para facilitar a coleta, manipulação e filtragem dos dados. Consulte a [documentação](#documentação) para saber como utiliza-lo.
## Instalação
@@ -71,7 +73,7 @@ $ pip install filometro
## Demo
-Obtenha uma lista de postos que tem disponível em seu estoque o imunizante da PFizer e mostre a situação da fila, o endereço e a zona em que o posto está localizado:
+Obtenha uma lista de postos que tem disponível em seu estoque o imunizante da PFizer:
```python
>>> from filometro import Filometro
@@ -86,24 +88,9 @@ Obtenha uma lista de postos que tem disponível em seu estoque o imunizante da P
... # comprimido
Posto(equipment='UBS SANTA CRUZ', last_update='2021-12-22 12:46:35.190')
]
-
->>> for posto in postos:
-... print(f'Fila: {posto.situation}')
-... print(f'Endereço: {posto.address}')
-... print(f'Zona: {posto.zone}', end='\n\n')
-
-Fila: FILA PEQUENA
-Endereço: R. HUMAITÁ, 520 - BELA VISTA - CEP: 01321-010 - Tel: 3241- 1632/ 3241-1163
-Zona: CENTRO
-
-... # comprimido
-
-Fila: FILA PEQUENA
-Endereço: Rua Santa Cruz, 1.191 - Vila Mariana
-Zona: SUL
```
-Verifique a documentação para obter informações sobre os métodos disponíveis no pacote.
+Verifique a documentação para obter informações sobre os atributos e métodos disponíveis.
## Documentação
@@ -147,64 +134,109 @@ Para atualizar os dados dos postos armazenados em memória é indicado utilizar
Esse método recarrega todos os dados com as informações mais recentes disponíveis no site oficial.
+Sempre que precisar, utilize a função `help()` em alguma classe, objeto ou método para obter ajuda:
+
+```python
+>>> help(filometro)
+```
+
+### Objeto Posto
+
+Todos os métodos tem como retorno uma lista de dados, esses dados são representados no objeto Posto. Veja quais são seus atributos:
+
+```python
+>>> posto.equipment
+>>> posto.address
+>>> posto.district
+>>> posto.zone
+>>> posto.maps
+>>> posto.contacts
+>>> posto.astrazeneca
+>>> posto.coronavac
+>>> posto.coronavac_pediatrica
+>>> posto.pfizer
+>>> posto.pfizer_pediatrica
+>>> posto.janssen
+>>> posto.influenza
+>>> posto.intercambialidade
+>>> posto.situation
+>>> posto.modality
+>>> posto.last_update
+```
+
### Métodos da classe Filometro
-- `Filometro.reload(...)` - Recarregar os dados com as informações mais recentes.
-- `Filometro.all_postos(...)` - Retorna os dados de todos os postos.
-- `Filometro.all_postos_open(...)` - Retorna os dados de todos os postos abertos no momento da busca.
-- `Filometro.all_postos_closed(...)` - Retorna os dados de todos os postos fechados no momento da busca.
-- `Filometro.by_zone(...)` - Retorna os dados dos postos por zona selecionada.
-- `Filometro.by_modality(...)` - Retorna os dados dos postos por modalidade selecionada.
-- `Filometro.by_district(...)` - Retorna os dados dos postos por distrito selecionado.
-- `Filometro.by_situation(...)` - Retorna os dados dos postos por situação selecionada.
-- `Filometro.by_immunizing(...)` - Retorna os dados dos postos por imunizante selecionado.
-- `Filometro.to_dict(...)` - Retorna uma lista de objetos do tipo `dict` contendo todos os dados de postos.
-- `Filometro.to_json(...)` - Retorna uma string `json` contendo todos os dados de postos. Também há suporte para a manipulação do retorno utilizando os mesmos argumentos do [método json integrado ao Python](https://docs.python.org/3/library/json.html).
-- `Filometro.to_dataframe(...)` - Retorna um [DataFrame](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.html) contendo os dados de todos os postos.
+```python
+>>> filometro.reload() # Atualizar dados em memória.
+>>> filometro.all_postos() # Obter todos os postos.
+>>> filometro.all_postos_open() # Obter todos os postos abertos.
+>>> filometro.all_postos_closed() # Obter todos os postos fechados.
+>>> filometro.by_zone(...) # Obter todos postos de uma zona.
+>>> filometro.by_modality(...) # Obter todos postos de uma modalidade.
+>>> filometro.by_district(...) # Obter todos postos de um distrito.
+>>> filometro.by_situation(...) # Obter todos postos por situação.
+>>> filometro.by_immunizing(...) # Obter todos postos que possuem um imunizante.
+>>> filometro.to_dict() # Obter todos postos convertidos para `dict`.
+```
-### Identificadores para filtragem (Enums)
+### Identificadores para filtragem
-Todos os Enums estão disponíveis para uso atráves da interface príncipal do pacote:
+Todos os identificadores estão disponíveis para uso atráves da interface príncipal do pacote:
```python
>>> from filometro import Zone, Modality, District, Situation, Immunizing
```
-> Para obter mais informações sobre cada um dos Enums, use as funções `dir()` ou `help()` passando um dos Enums como argumento.
+`Zone` - Representa as zonas do Estado de São Paulo:
+
+```python
+>>> Zone.SUL
+>>> Zone.OESTE
+>>> Zone.NORTE
+>>> Zone.LESTE
+>>> Zone.CENTRO
+>>> Zone.MEGA_DRIVES
+```
+
+`Modality` - Representa as modalidades dos postos:
+
+```python
+>>> Modality.PARQUES
+>>> Modality.POSTO_FIXO
+>>> Modality.POSTO_VOLANTE
+>>> Modality.DRIVE_THRU
+>>> Modality.MEGAPOSTO
+```
+
+`District` - Representa todos os distritos do Estado de São Paulo que disponíbilizam um imunizante em seus postos. Como há muitos distritos, use a função `dir` ou `help` para mais infomações:
-- `Zone` - Representa as zonas do Estado de São Paulo.
- - `Zone.SUL`
- - `Zone.OESTE`
- - `Zone.NORTE`
- - `Zone.LESTE`
- - `Zone.CENTRO`
- - `Zone.MEGA_DRIVES`
+```python
+>>> help(District)
+>>> dir(District)
+```
-- `Modality` - Representa as modalidades dos postos de saúde.
- - `Modality.PARQUES`
- - `Modality.POSTO_FIXO`
- - `Modality.POSTO_VOLANTE`
- - `Modality.DRIVE_THRU`
- - `Modality.MEGAPOSTO`
+`Situation` - Representa as situações das filas nos postos:
-- `District` - Representa todos os distritos do Estado de São Paulo que disponíbilizam um imunizante em seus postos de saúde. Use a função `dir(District)` ou `help(District)` para mais infomações.
+```python
+>>> Situation.NAO_FUNCIONANDO
+>>> Situation.SEM_FILA
+>>> Situation.FILA_PEQUENA
+>>> Situation.FILA_MEDIA
+>>> Situation.FILA_GRANDE
+```
-- `Situation` - Representa as possíveis situações das filas nos postos de saúde.
- - `Situation.NAO_FUNCIONANDO`
- - `Situation.SEM_FILA`
- - `Situation.FILA_PEQUENA`
- - `Situation.FILA_MEDIA`
- - `Situation.FILA_GRANDE`
+`Immunizing` - Representa os imunizantes disponíveis nos postos no Estado de São Paulo:
-- `Immunizing` - Representa os imunizantes disponíveis nos postos de saúde do Estado de São Paulo.
- - `Immunizing.ASTRAZENECA`
- - `Immunizing.INTERCAMBIALIDADE`
- - `Immunizing.PFIZER`
- - `Immunizing.PFIZER_PEDIATRICA`
- - `Immunizing.CORONAVAC`
- - `Immunizing.CORONAVAC_PEDIATRICA`
- - `Immunizing.JANSSEN`
- - `Immunizing.INFLUENZA`
+```python
+>>> Immunizing.ASTRAZENECA
+>>> Immunizing.INTERCAMBIALIDADE
+>>> Immunizing.PFIZER
+>>> Immunizing.PFIZER_PEDIATRICA
+>>> Immunizing.CORONAVAC
+>>> Immunizing.CORONAVAC_PEDIATRICA
+>>> Immunizing.JANSSEN
+>>> Immunizing.INFLUENZA
+```
## Contribuições
@@ -217,11 +249,11 @@ Abaixo mostro com o que você pode contribuir:
- Existe uma issue aberta e você quer resolve-la, quer implementar uma nova funcionalidade ou melhorar a documentação? Faça suas adições e me envie um *Pull Request*
-- Gostou do projeto, mas não quer ou ainda não consegue contribuir com ele? Considere deixar uma estrela ⭐ para o **Filômetro**
+- Gostou do projeto, mas não quer ou ainda não consegue contribuir com ele? Considere deixar uma estrela ⭐ para o **Filometro**
-Obrigado pelo interesse em colaborar de alguma forma com o projeto 😄
+Obrigado pelo interesse em colaborar de alguma forma com o projeto ❤
## Licença
-**Filômetro** utiliza a *licença MIT* em todo seu código, confira suas condições em [MIT License](https://github.com/matheusfelipeog/filometro/blob/master/LICENSE).
+**Filometro** utiliza a *licença MIT* em todo seu código, confira suas condições em [MIT License](https://github.com/matheusfelipeog/filometro/blob/master/LICENSE).
diff --git a/filometro/__init__.py b/filometro/__init__.py
index aee0e9f..edfbc6f 100644
--- a/filometro/__init__.py
+++ b/filometro/__init__.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
"""
filometro
---------
@@ -7,7 +6,6 @@
de todos os postos de saúde que são mostrados no site 'De Olho na Fila'.
"""
-
__all__ = [
'Zone',
'Modality',
@@ -16,7 +14,7 @@
'District',
'Filometro'
]
-__version__ = '0.3.0'
+__version__ = '1.0.0'
__author__ = 'Matheus Felipe'
from filometro.enums import Zone
diff --git a/filometro/dataclasses.py b/filometro/dataclasses.py
index 559956f..38bf478 100644
--- a/filometro/dataclasses.py
+++ b/filometro/dataclasses.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
"""
filometro.dataclasses
---------------------
@@ -8,19 +7,24 @@
__all__ = ['Posto']
-from filometro import __version__
-from filometro import __author__
-
from dataclasses import dataclass
from dataclasses import field
+from typing import List
+
+import re
+
@dataclass
class Posto():
+ """Modelo de dados do Posto."""
+
equipment: str # equipamento
address: str = field(repr=False) # endereço
district: str = field(repr=False) # distrito
zone: str = field(repr=False) # crs (Coordinate Reference Systems)
+ maps: str = field(init=False, repr=False)
+ contacts: List[str] = field(init=False, repr=False, default_factory=list)
astrazeneca: str = field(repr=False)
coronavac: str = field(repr=False)
coronavac_pediatrica: str = field(repr=False)
@@ -37,3 +41,45 @@ class Posto():
_id_zone: str = field(repr=False) # id_crs
_id_tb_unidades: str = field(repr=False)
_id_modality: str = field(repr=False) # id_tipo_posto
+
+ def __post_init__(self) -> None:
+ self.contacts.extend(self._extract_contacts())
+ self.address = self._extract_address()
+ self.maps = self._build_maps_link()
+
+ @staticmethod
+ def _remove_substring_until_the_end(string: str, substring: str) -> str:
+ new_string = string
+ if substring in string:
+ idx_substring = string.find(substring)
+ new_string = string[:idx_substring]
+ return new_string
+
+ def _extract_address(self) -> str:
+ address = self.address.upper()
+ address = address.replace('ENDEREÇO:', '')
+
+ address = self._remove_substring_until_the_end(address, 'F:')
+ address = self._remove_substring_until_the_end(address, 'FONE:')
+ address = self._remove_substring_until_the_end(address, 'TEL:')
+
+ address = address.strip().strip('-').strip(',')
+
+ return address.strip()
+
+ def _build_maps_link(self) -> str:
+ address = self.address.replace(' ', '+')
+
+ return f'https://www.google.com.br/maps/place/{address}'
+
+ def _extract_contacts(self) -> List[str]:
+ address = self.address.replace(' ', '').upper()
+
+ concatenated_contacts = re.split(r'F:|TEL:|FONE:', address)[-1]
+ contacts = re.findall(r'\d{4}-?\d{4}', concatenated_contacts)
+
+ for idx, contact in enumerate(contacts):
+ if '-' not in contact:
+ contacts[idx] = f'{contact[:4]}-{contact[4:]}'
+
+ return contacts
diff --git a/filometro/deolhonafila.py b/filometro/deolhonafila.py
index 02f2c7a..24868fd 100644
--- a/filometro/deolhonafila.py
+++ b/filometro/deolhonafila.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
"""
filometro.deolhonafila
----------------------
@@ -8,13 +7,10 @@
__all__ = ['APIDeOlhoNaFila']
-from filometro import __version__
-from filometro import __author__
+from typing import List
import requests
-from typing import List
-
class APIDeOlhoNaFila():
"""Um wrapper da API do site 'De Olho na Fila'."""
@@ -23,7 +19,10 @@ def __init__(self) -> None:
self.endpoint = 'https://deolhonafila.prefeitura.sp.gov.br/processadores/dados.php'
self.payload = {'dados': 'dados'}
self.headers = {
- 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36',
+ 'User-Agent': (
+ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)'
+ 'Chrome/95.0.4638.69 Safari/537.36'
+ ),
'Host': 'deolhonafila.prefeitura.sp.gov.br',
'Connection': 'keep-alive',
'Content-Length': '11',
diff --git a/filometro/enums.py b/filometro/enums.py
index 86fd68f..3db6d63 100644
--- a/filometro/enums.py
+++ b/filometro/enums.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
"""
filometro.enums
---------------
@@ -14,13 +13,12 @@
'District'
]
-from filometro import __version__
-from filometro import __author__
-
from enum import Enum
class Zone(Enum):
+ """Fornece todas as zonas onde há um ou mais postos disponíveis."""
+
SUL = 'SUL'
OESTE = 'OESTE'
NORTE = 'NORTE'
@@ -30,6 +28,8 @@ class Zone(Enum):
class Modality(Enum):
+ """Fornece todas as modalidades dos postos."""
+
PARQUES = 'PARQUES'
POSTO_FIXO = 'POSTO FIXO'
POSTO_VOLANTE = 'POSTO VOLANTE'
@@ -38,6 +38,8 @@ class Modality(Enum):
class Situation(Enum):
+ """Fornece todas as situações que um posto pode estar."""
+
NAO_FUNCIONANDO = 'NÃO FUNCIONANDO'
SEM_FILA = 'SEM FILA'
FILA_PEQUENA = 'FILA PEQUENA'
@@ -46,6 +48,8 @@ class Situation(Enum):
class Immunizing(Enum):
+ """Fornece todos os imunizantes disponíveis nos postos."""
+
ASTRAZENECA = 'astrazeneca'
INTERCAMBIALIDADE = 'intercambialidade'
PFIZER = 'pfizer'
@@ -57,14 +61,16 @@ class Immunizing(Enum):
class District(Enum):
+ """Fornece todos os distritos onde há um ou mais postos."""
+
AGUA_RASA = 'Água Rasa'
ALTO_DE_PINHEIROS = 'Alto de Pinheiros'
ANHANGUERA = 'Anhanguera'
ARICANDUVA = 'Aricanduva'
ARTUR_ALVIM = 'Artur Alvim'
- BELA_VISTA = 'Bela Vista'
+ BELA_VISTA = 'Bela Vista'
BELEM = 'Belém'
- BOM_RETIRO = 'Bom Retiro'
+ BOM_RETIRO = 'Bom Retiro'
BRASILANDIA = 'Brasilândia'
BRAS = 'Brás'
BUTANTA = 'Butantã'
diff --git a/filometro/filometro.py b/filometro/filometro.py
index e2841df..d876053 100644
--- a/filometro/filometro.py
+++ b/filometro/filometro.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
"""
filometro.filometro
-------------------
@@ -8,17 +7,10 @@
__all__ = ['Filometro']
-from filometro import __version__
-from filometro import __author__
-
from typing import List
-import json
-
from dataclasses import asdict
-from pandas import DataFrame
-
from filometro.deolhonafila import APIDeOlhoNaFila
from filometro.dataclasses import Posto
@@ -30,60 +22,59 @@
from filometro.enums import District
-def _posto_dict_to_posto_object(posto_dict: dict) -> Posto:
- """Converte um dict com informações de um posto em um objeto Posto."""
+def _posto_factory(data: dict) -> Posto:
+ """Cria um objeto Posto com base no dicionário de dados fornecido."""
return Posto(
- equipment=posto_dict['equipamento'],
- address=posto_dict['endereco'],
- district=posto_dict['distrito'],
- zone=posto_dict['crs'],
- astrazeneca=posto_dict['astrazeneca'],
- coronavac=posto_dict['coronavac'],
- coronavac_pediatrica=posto_dict['corona_ped'],
- pfizer=posto_dict['pfizer'],
- pfizer_pediatrica=posto_dict['pfizer_ped'],
- janssen=posto_dict['janssen'],
- influenza=posto_dict['influenza'],
- intercambialidade=posto_dict['intercambialidade'],
- situation=posto_dict['status_fila'],
- modality=posto_dict['tipo_posto'],
- last_update=posto_dict['data_hora'],
- _index_situation=posto_dict['indice_fila'],
- _id_district=posto_dict['id_distrito'],
- _id_zone=posto_dict['id_crs'],
- _id_tb_unidades=posto_dict['id_tb_unidades'],
- _id_modality=posto_dict['id_tipo_posto']
+ equipment=data['equipamento'],
+ address=data['endereco'],
+ district=data['distrito'],
+ zone=data['crs'],
+ astrazeneca=data['astrazeneca'],
+ coronavac=data['coronavac'],
+ coronavac_pediatrica=data['corona_ped'],
+ pfizer=data['pfizer'],
+ pfizer_pediatrica=data['pfizer_ped'],
+ janssen=data['janssen'],
+ influenza=data['influenza'],
+ intercambialidade=data['intercambialidade'],
+ situation=data['status_fila'],
+ modality=data['tipo_posto'],
+ last_update=data['data_hora'],
+ _index_situation=data['indice_fila'],
+ _id_district=data['id_distrito'],
+ _id_zone=data['id_crs'],
+ _id_tb_unidades=data['id_tb_unidades'],
+ _id_modality=data['id_tipo_posto']
)
-def _postos_dicts_to_postos_objects(postos_dicts: List[dict]) -> List[Posto]:
- """Converte uma lista de dict com informações de vários postos em
- uma lista de objetos Posto."""
-
- postos_objects = []
- for posto_dict in postos_dicts:
- posto_object = _posto_dict_to_posto_object(posto_dict)
- postos_objects.append(posto_object)
+def _postos_factory(data_list: List[dict]) -> List[Posto]:
+ """Cria uma lista de objetos Posto com base na lista de dados fornecido."""
- return postos_objects
+ return [_posto_factory(d) for d in data_list]
class Filometro():
- """Filometro é a API príncipal do pacote.
+ """Filometro é a API príncipal do pacote.
Fornence os métodos para coletar e filtrar os dados dos postos.
"""
- def __init__(self) -> None:
- self._api = APIDeOlhoNaFila()
+ def __init__(self, _api: APIDeOlhoNaFila = None) -> None:
+ self._api = _api or APIDeOlhoNaFila()
self._postos = self._load_postos()
+ def __repr__(self) -> str:
+ return f'{self.__class__.__name__}(postos={len(self._postos)})'
+
+ def __str__(self) -> str:
+ return repr(self)
+
def _load_postos(self) -> List[Posto]:
- postos_dicts = self._api.get_data()
- postos_objects = _postos_dicts_to_postos_objects(postos_dicts)
- return postos_objects
+ data_list = self._api.get_data()
+ return _postos_factory(data_list)
def reload(self) -> None:
"""Recarregar os dados com as informações mais recentes."""
@@ -94,7 +85,7 @@ def reload(self) -> None:
def all_postos(self) -> List[Posto]:
"""Retorna os dados de todos os postos."""
- return self._postos
+ return self._postos.copy()
def all_postos_open(self) -> List[Posto]:
"""Retorna uma lista com todos os postos abertos no momento da busca."""
@@ -132,29 +123,9 @@ def by_situation(self, situation: Situation) -> List[Posto]:
def by_immunizing(self, immunizing: Immunizing) -> List[Posto]:
"""Retorna os dados dos postos por imunizante selecionado."""
- return [posto for posto in self._postos if posto.__dict__[immunizing.value] == '1']
+ return [posto for posto in self._postos if asdict(posto).get(immunizing.value) == '1']
def to_dict(self) -> List[dict]:
"""Retorna uma lista de dict contendo os dados de todos os postos."""
return [asdict(posto) for posto in self.all_postos()]
-
- def to_json(self, *args, **kwargs) -> str:
- """Retorna uma string json contendo os dados de todos os postos.
-
- Argumentos para configurar o retorno em json são aceitos:
-
- >>> filometro.to_json(indent=4)
-
- Consulte a documentação do pacote json para saber quais argumentos
- são suportados.
- """
-
- data = self.to_dict()
-
- return json.dumps(data, *args, **kwargs)
-
- def to_dataframe(self) -> DataFrame:
- """Retorna um DataFrame contendo os dados de todos os postos."""
-
- return DataFrame(self.all_postos())
diff --git a/setup.py b/setup.py
index d403a75..4bec4cb 100644
--- a/setup.py
+++ b/setup.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
import os
import sys
from shutil import rmtree
@@ -21,7 +19,7 @@ class PublishCommand(Command):
@staticmethod
def print_status(msg):
"""Prints message in bold and yellow."""
- print('\033[1;33m{m}\033[0m'.format(m=msg))
+ print(f'\033[1;33m{msg}\033[0m')
def initialize_options(self):
pass
@@ -38,9 +36,10 @@ def run(self):
except OSError:
pass
-
+
self.print_status('Build Source and Wheel distribution…')
- os.system('{python} setup.py sdist bdist_wheel'.format(python=sys.executable))
+ python = sys.executable
+ os.system(f'{python} setup.py sdist bdist_wheel')
self.print_status('Uploading the package to PyPi via Twine…')
os.system('twine upload --config-file .pypirc --repository pypi dist/*')
@@ -55,7 +54,7 @@ def run(self):
setup(
name='filometro',
version=__version__,
- description='Um wrapper Python do site "De Olho na Fila": obtenha os dados dos postos de vacinação da covid-19 em São Paulo',
+ description='Obtenha os dados dos postos de vacinação da covid-19 em São Paulo',
long_description=long_description,
long_description_content_type='text/markdown',
license='MIT License',
@@ -66,8 +65,7 @@ def run(self):
exclude=('tests',)
),
install_requires=[
- 'requests>=2.27.1',
- 'pandas>=1.4.1'
+ 'requests>=2.27.1'
],
zip_safe=False,
python_requires='>=3.7',
@@ -78,10 +76,11 @@ def run(self):
},
keywords=[
'filometro', 'de-olho-na-fila', 'data', 'sao-paulo',
- 'covid-19', 'vacina', 'vacinasampa', 'python', 'wrapper'
+ 'covid-19', 'vacina', 'vacinasampa', 'python', 'wrapper',
+ 'coronavirus'
],
classifiers=[
- 'Development Status :: 4 - Beta',
+ 'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
'Intended Audience :: Information Technology',
'License :: OSI Approved :: MIT License',
@@ -100,4 +99,4 @@ def run(self):
cmdclass={
'publish': PublishCommand
}
-)
\ No newline at end of file
+)
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/test_deolhonafila.py b/tests/test_deolhonafila.py
new file mode 100644
index 0000000..24e7728
--- /dev/null
+++ b/tests/test_deolhonafila.py
@@ -0,0 +1,53 @@
+"""Testes do módulo filometro.deolhonafila."""
+
+import unittest
+
+from filometro.deolhonafila import APIDeOlhoNaFila
+
+
+class TestAPIDeOlhoNaFila(unittest.TestCase):
+ """Testes da classe APIDeOlhoNaFila."""
+
+ def setUp(self):
+ self.api = APIDeOlhoNaFila()
+
+ def test_if_has_the_endpoint_attribute_with_the_valid_value(self):
+ valid_endpoint = 'https://deolhonafila.prefeitura.sp.gov.br/processadores/dados.php'
+
+ self.assertTrue(hasattr(self.api, 'endpoint'))
+ self.assertEqual(self.api.endpoint, valid_endpoint)
+
+ def test_if_has_the_payload_attribute_with_the_valid_value(self):
+ valid_payload = {'dados': 'dados'}
+
+ self.assertTrue(hasattr(self.api, 'payload'))
+ self.assertEqual(self.api.payload, valid_payload)
+
+ def test_if_has_the_headers_attribute_with_the_valid_value(self):
+ valid_headers = {
+ 'User-Agent': ( # implicit concatenation
+ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)'
+ 'Chrome/95.0.4638.69 Safari/537.36'
+ ),
+ 'Host': 'deolhonafila.prefeitura.sp.gov.br',
+ 'Connection': 'keep-alive',
+ 'Content-Length': '11',
+ 'sec-ch-ua': '"Google Chrome";v="95", "Chromium";v="95", ";Not A Brand";v="99"',
+ 'Accept': 'application/json, text/javascript, */*; q=0.01',
+ 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
+ 'X-Requested-With': 'XMLHttpRequest',
+ 'sec-ch-ua-mobile': '?0',
+ 'sec-ch-ua-platform': '"Windows"',
+ 'Origin': 'https://deolhonafila.prefeitura.sp.gov.br',
+ 'Sec-Fetch-Site': 'same-origin',
+ 'Sec-Fetch-Mode': 'cors',
+ 'Sec-Fetch-Dest': 'empty',
+ 'Referer': 'https://deolhonafila.prefeitura.sp.gov.br/',
+ 'Accept-Encoding': 'gzip, deflate, br',
+ 'Accept-Language': 'pt,en-US;q=0.9,en;q=0.8,de;q=0.7',
+ 'dnt': '1',
+ 'sec-gpc': '1'
+ }
+
+ self.assertTrue(hasattr(self.api, 'headers'))
+ self.assertEqual(self.api.headers, valid_headers)
diff --git a/tests/test_filometro.py b/tests/test_filometro.py
new file mode 100644
index 0000000..3ed6a0d
--- /dev/null
+++ b/tests/test_filometro.py
@@ -0,0 +1,213 @@
+"""Testes do módulo filometro.filometro."""
+
+import unittest
+
+from typing import List
+
+from filometro.dataclasses import Posto
+
+from filometro.enums import Zone
+from filometro.enums import Modality
+from filometro.enums import District
+from filometro.enums import Situation
+from filometro.enums import Immunizing
+
+from filometro.filometro import _posto_factory
+from filometro.filometro import _postos_factory
+from filometro.filometro import Filometro
+
+
+class FakeAPIDeOlhoNaFila:
+ def __init__(self) -> None:
+ self._data = [
+ {
+ 'equipamento': 'test',
+ 'endereco': 'test',
+ 'distrito': 'Campo Limpo',
+ 'crs': 'SUL',
+ 'astrazeneca': '0',
+ 'coronavac': '0',
+ 'corona_ped': '0',
+ 'pfizer': '0',
+ 'pfizer_ped': '0',
+ 'janssen': '0',
+ 'influenza': '0',
+ 'intercambialidade': '0',
+ 'status_fila': 'NÃO FUNCIONANDO',
+ 'tipo_posto': 'POSTO FIXO',
+ 'data_hora': 'test',
+ 'indice_fila': 'test',
+ 'id_distrito': 'test',
+ 'id_crs': 'test',
+ 'id_tb_unidades': 'test',
+ 'id_tipo_posto': 'test'
+ },
+ {
+ 'equipamento': 'test',
+ 'endereco': 'test',
+ 'distrito': 'Brás',
+ 'crs': 'LESTE',
+ 'astrazeneca': '1',
+ 'coronavac': '1',
+ 'corona_ped': '1',
+ 'pfizer': '1',
+ 'pfizer_ped': '1',
+ 'janssen': '1',
+ 'influenza': '1',
+ 'intercambialidade': '1',
+ 'status_fila': 'FILA GRANDE',
+ 'tipo_posto': 'POSTO FIXO',
+ 'data_hora': 'test',
+ 'indice_fila': 'test',
+ 'id_distrito': 'test',
+ 'id_crs': 'test',
+ 'id_tb_unidades': 'test',
+ 'id_tipo_posto': 'test'
+ }
+ ]
+
+ def get_data(self) -> List[dict]:
+ return self._data.copy()
+
+
+class TestPostoFactory(unittest.TestCase):
+ """Testes para criação de objetos Posto."""
+
+ def setUp(self):
+ self.data = {
+ 'equipamento': 'test',
+ 'endereco': 'test',
+ 'distrito': 'test',
+ 'crs': 'test',
+ 'astrazeneca': 'test',
+ 'coronavac': 'test',
+ 'corona_ped': 'test',
+ 'pfizer': 'test',
+ 'pfizer_ped': 'test',
+ 'janssen': 'test',
+ 'influenza': 'test',
+ 'intercambialidade': 'test',
+ 'status_fila': 'test',
+ 'tipo_posto': 'test',
+ 'data_hora': 'test',
+ 'indice_fila': 'test',
+ 'id_distrito': 'test',
+ 'id_crs': 'test',
+ 'id_tb_unidades': 'test',
+ 'id_tipo_posto': 'test'
+ }
+
+ def test_create_posto_object(self):
+ posto = _posto_factory(self.data)
+
+ self.assertIsInstance(posto, Posto)
+
+ def test_raise_exception_with_missing_mandatory_key(self):
+ self.data.pop('equipamento')
+
+ with self.assertRaises(KeyError):
+ _posto_factory(self.data)
+
+ def test_create_a_list_of_postos_objects(self):
+ number_of_postos = 5
+ data_list = [self.data.copy() for _ in range(number_of_postos)]
+
+ postos = _postos_factory(data_list)
+
+ self.assertIsInstance(postos, list)
+
+ postos_length = len(postos)
+ self.assertEqual(postos_length, number_of_postos)
+
+ for posto in postos:
+ with self.subTest():
+ self.assertIsInstance(posto, Posto)
+
+
+class TestFilometro(unittest.TestCase):
+ """Testes relacionados a classe Filometro."""
+
+ def setUp(self):
+ self.filometro = Filometro(_api=FakeAPIDeOlhoNaFila())
+ self.total_of_postos = len(self.filometro._postos)
+
+ def test_get_all_postos(self):
+ postos = self.filometro.all_postos()
+
+ self.assertIsInstance(postos, list)
+
+ num_of_postos = len(postos)
+ self.assertEqual(num_of_postos, self.total_of_postos)
+
+ def test_return_from_all_postos_cannot_reference_internal_data_list(self):
+ postos = self.filometro.all_postos()
+
+ self.assertIsNot(postos, self.filometro._postos)
+
+ def test_get_all_postos_open(self):
+ postos = self.filometro.all_postos_open()
+
+ num_of_postos = len(postos)
+ expected_number_of_postos_open = 1
+
+ self.assertEqual(num_of_postos, expected_number_of_postos_open)
+
+ def test_get_all_postos_closed(self):
+ postos = self.filometro.all_postos_closed()
+
+ num_of_postos = len(postos)
+ expected_number_of_postos_closed = 1
+
+ self.assertEqual(num_of_postos, expected_number_of_postos_closed)
+
+ def test_get_all_postos_from_a_specific_zone(self):
+ postos = self.filometro.by_zone(Zone.SUL)
+
+ num_of_postos = len(postos)
+ expected_number_of_postos_from_sul_zone = 1
+
+ self.assertEqual(num_of_postos, expected_number_of_postos_from_sul_zone)
+
+ def test_get_all_postos_from_a_specific_modality(self):
+ postos = self.filometro.by_modality(Modality.POSTO_FIXO)
+
+ num_of_postos = len(postos)
+ expected_number_of_postos_fixos = 2
+
+ self.assertEqual(num_of_postos, expected_number_of_postos_fixos)
+
+ def test_get_all_postos_from_a_specific_district(self):
+ postos = self.filometro.by_district(District.CAMPO_LIMPO)
+
+ num_of_postos = len(postos)
+ expected_number_of_postos_from_campo_limpo_district = 1
+
+ self.assertEqual(num_of_postos, expected_number_of_postos_from_campo_limpo_district)
+
+ def test_get_all_postos_from_a_specific_situation(self):
+ postos = self.filometro.by_situation(Situation.FILA_GRANDE)
+
+ num_of_postos = len(postos)
+ expected_number_of_postos_from_situation = 1
+
+ self.assertEqual(num_of_postos, expected_number_of_postos_from_situation)
+
+ def test_get_all_postos_from_a_specific_immunizing(self):
+ postos = self.filometro.by_immunizing(Immunizing.CORONAVAC)
+
+ num_of_postos = len(postos)
+ expected_number_of_postos_from_coronavac_immunizing = 1
+
+ self.assertEqual(num_of_postos, expected_number_of_postos_from_coronavac_immunizing)
+
+ def test_convert_postos_to_dict(self):
+ data = self.filometro.to_dict()
+
+ self.assertIsInstance(data, list)
+
+ num_of_data = len(data)
+ self.assertEqual(num_of_data, self.total_of_postos)
+
+ for d in data:
+ with self.subTest():
+ self.assertIsInstance(d, dict)