From 690a55f410955c382252f726ec123c7cbca388e5 Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 15 Mar 2022 22:17:58 +0200 Subject: [PATCH 01/15] chore: add mypy linter, black and flake8 as development dependencies --- requirements.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index c3dccde..01ceeeb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,7 @@ pandas==1.4.1 numpy==1.22.2 requests==2.27.1 openpyxl -ratelimit==2.2.1 \ No newline at end of file +ratelimit==2.2.1 +mypy +flake8 +black \ No newline at end of file From 3b3482cda7096c083311f04e836b8b0d195fbc05 Mon Sep 17 00:00:00 2001 From: Nuclear03020704 <52926983+lahdjirayhan@users.noreply.github.com> Date: Wed, 16 Mar 2022 21:55:12 +0700 Subject: [PATCH 02/15] fix: Raise error for problematic station/city --- src/ozone/ozone.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/ozone/ozone.py b/src/ozone/ozone.py index 582a5ff..09d7407 100644 --- a/src/ozone/ozone.py +++ b/src/ozone/ozone.py @@ -314,7 +314,14 @@ def get_city_air( r = self._make_api_request(f"{self._search_aqi_url}/{city}/?token={self.token}") if self._check_status_code(r): # Get all the data. - data_obj = json.loads(r.content)["data"] + response_content = json.loads(r.content) + status, data_obj = response_content['status'], response_content['data'] + if status != 'ok': + if data_obj == 'Unknown station': + raise Exception(f'There is no known AQI station for the city "{city}"') + + raise Exception(f'There is a problem with city "{city}", the returned data: {data_obj}') + row = self._parse_data(data_obj, city, params) df = pandas.concat([df, pandas.DataFrame(row)], ignore_index=True) From 4804aea333ef2c60999560a2adfa721e3bdd8f55 Mon Sep 17 00:00:00 2001 From: Nuclear03020704 <52926983+lahdjirayhan@users.noreply.github.com> Date: Wed, 16 Mar 2022 22:23:21 +0700 Subject: [PATCH 03/15] fix: Raise error when given AQI beyond range --- src/ozone/ozone.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/ozone/ozone.py b/src/ozone/ozone.py index 09d7407..e9d98c7 100644 --- a/src/ozone/ozone.py +++ b/src/ozone/ozone.py @@ -196,7 +196,7 @@ def _AQI_meaning(self, aqi: float) -> Tuple[str, str]: str: The meaning and health implication of the AQI data. """ - if aqi <= 50: + if 0 <= aqi <= 50: AQI_meaning = "Good" AQI_health_implications = "Air quality is considered satisfactory, and air pollution poses little or no risk" elif 51 <= aqi <= 100: @@ -211,11 +211,13 @@ def _AQI_meaning(self, aqi: float) -> Tuple[str, str]: elif 201 <= aqi <= 300: AQI_meaning = "Very Unhealthy" AQI_health_implications = "Health warnings of emergency conditions. The entire population is more likely to be affected." - else: + elif 301 <= aqi <= 500: AQI_meaning = "Hazardous" AQI_health_implications = ( "Health alert: everyone may experience more serious health effects." ) + else: + raise Exception(f'{aqi} is not valid air quality index value. Should be between 0 to 500.') return AQI_meaning, AQI_health_implications From 01412496cfe9e9e4fd5b03dc9f49400a8c5bc71c Mon Sep 17 00:00:00 2001 From: Nuclear03020704 <52926983+lahdjirayhan@users.noreply.github.com> Date: Wed, 16 Mar 2022 22:29:13 +0700 Subject: [PATCH 04/15] fix: Raise error when invalid data format is specified --- src/ozone/ozone.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ozone/ozone.py b/src/ozone/ozone.py index e9d98c7..9d4de6a 100644 --- a/src/ozone/ozone.py +++ b/src/ozone/ozone.py @@ -139,7 +139,7 @@ def _format_output( ) print("File saved to disk as air_quality_data.xlsx") else: - print("Invalid file format. Use any of: csv, json, xlsx, df") + raise Exception(f'Invalid file format {data_format}. Use any of: csv, json, xlsx, df') return pandas.DataFrame() def _parse_data( From 9a2cb71f0f0e650d804d62024c09dae817a331ab Mon Sep 17 00:00:00 2001 From: Nuclear03020704 <52926983+lahdjirayhan@users.noreply.github.com> Date: Wed, 16 Mar 2022 22:45:11 +0700 Subject: [PATCH 05/15] fix: Use warnings library to emit warning instead of printing --- src/ozone/ozone.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ozone/ozone.py b/src/ozone/ozone.py index 9d4de6a..34848c2 100644 --- a/src/ozone/ozone.py +++ b/src/ozone/ozone.py @@ -16,6 +16,7 @@ import requests import json import itertools +import warnings from ratelimit import limits, sleep_and_retry from .urls import URLs @@ -73,7 +74,7 @@ def _check_token_validity(self) -> None: if self._check_status_code(r): if json.loads(r.content)["status"] != "ok": - print("Warning: Token may be invalid!") + warnings.warn("Token may be invalid!") @sleep_and_retry @limits(calls=CALLS, period=RATE_LIMIT) From 6ad2d97fc4e1d49bbd97a1844354f5075b825b23 Mon Sep 17 00:00:00 2001 From: Nuclear03020704 <52926983+lahdjirayhan@users.noreply.github.com> Date: Wed, 16 Mar 2022 22:53:38 +0700 Subject: [PATCH 06/15] fix: Move irrelevant code out of try block This will make KeyError caught only when it's about missing air quality parameter, and not something else. --- src/ozone/ozone.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/ozone/ozone.py b/src/ozone/ozone.py index 34848c2..d2933fe 100644 --- a/src/ozone/ozone.py +++ b/src/ozone/ozone.py @@ -431,15 +431,15 @@ def get_specific_parameter( float: Value of the specified parameter for the given city. """ result: float = 0.0 - try: - r = self._make_api_request( - f"{self._search_aqi_url}/{city}/?token={self.token}" - ) - if self._check_status_code(r): - data_obj = json.loads(r.content)["data"] - row = self._parse_data(data_obj, city, [air_param])[0] - result = float(row[air_param]) + r = self._make_api_request( + f"{self._search_aqi_url}/{city}/?token={self.token}" + ) + if self._check_status_code(r): + data_obj = json.loads(r.content)["data"] + row = self._parse_data(data_obj, city, [air_param])[0] + try: + result = float(row[air_param]) except KeyError: print( "Missing air quality parameter!\n" From 1584182e5f18f508e0814df2c174de02f62330fb Mon Sep 17 00:00:00 2001 From: Nuclear03020704 <52926983+lahdjirayhan@users.noreply.github.com> Date: Wed, 16 Mar 2022 22:58:05 +0700 Subject: [PATCH 07/15] fix: Reraise exception after printing information for user --- src/ozone/ozone.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ozone/ozone.py b/src/ozone/ozone.py index d2933fe..f9afe19 100644 --- a/src/ozone/ozone.py +++ b/src/ozone/ozone.py @@ -445,6 +445,7 @@ def get_specific_parameter( "Missing air quality parameter!\n" + "Try: get_specific_parameter(`city name`, `aqi` or `no2` or `co`)" ) + raise return result From 09bc1a1f4def4a8f17f66e2247ebf2d88daf367a Mon Sep 17 00:00:00 2001 From: Nuclear03020704 <52926983+lahdjirayhan@users.noreply.github.com> Date: Wed, 16 Mar 2022 23:21:15 +0700 Subject: [PATCH 08/15] docs: Fix typo in docstring --- src/ozone/ozone.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ozone/ozone.py b/src/ozone/ozone.py index f9afe19..493333b 100644 --- a/src/ozone/ozone.py +++ b/src/ozone/ozone.py @@ -188,7 +188,7 @@ def _parse_data( return [row] def _AQI_meaning(self, aqi: float) -> Tuple[str, str]: - """Retrieve API Meaning and health implications + """Retrieve AQI meaning and health implications Args: row["aqi"] (float): parsed AQI data. From 999326bf235340ab1c8ff6b8898ac3d093afeea3 Mon Sep 17 00:00:00 2001 From: Nuclear03020704 <52926983+lahdjirayhan@users.noreply.github.com> Date: Wed, 16 Mar 2022 23:33:00 +0700 Subject: [PATCH 09/15] refactor: Reuse get_city_air code in get_specific_parameter instead of rewriting most of the mostly the same logic again. --- src/ozone/ozone.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/ozone/ozone.py b/src/ozone/ozone.py index 493333b..3d4bc6f 100644 --- a/src/ozone/ozone.py +++ b/src/ozone/ozone.py @@ -430,13 +430,7 @@ def get_specific_parameter( Returns: float: Value of the specified parameter for the given city. """ - result: float = 0.0 - r = self._make_api_request( - f"{self._search_aqi_url}/{city}/?token={self.token}" - ) - if self._check_status_code(r): - data_obj = json.loads(r.content)["data"] - row = self._parse_data(data_obj, city, [air_param])[0] + row = self.get_city_air(city, params=[air_param]) try: result = float(row[air_param]) From 0120df4d13169f565fb2d68359b9cd580ecfff1c Mon Sep 17 00:00:00 2001 From: AliShahpurwala Date: Wed, 16 Mar 2022 12:52:37 -0400 Subject: [PATCH 10/15] feat: Create a simple shell script to update the version number --- updateVersion.sh | 61 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 updateVersion.sh diff --git a/updateVersion.sh b/updateVersion.sh new file mode 100644 index 0000000..472ba50 --- /dev/null +++ b/updateVersion.sh @@ -0,0 +1,61 @@ +#!/usr/bin/bash + +# This script is used for automating the process of updating patch numbers. +# Specifically, it updates the version number in setup.cfg (Line 3) and setup.py (Line 12 & 13) +# You can provide the script with either major, minor or patch (Case sensitive) +# Example usage: +# If the current version is 1.4.0, running './updateVersion.sh major' will change +# the current version to 2.0.0 + +if [ "$1" = "" ]; then +echo "You must provide an argument for this script" +echo "Your argument must be either major, minor or patch" +echo "See version semantics for more" +exit +fi + +if [ "$1" != "major" ] && [ "$1" != "minor" ] && [ "$1" != "patch" ]; then +echo "Incorrect argument provided" +echo "Your argument must be either major, minor or patch (Case sensivite)" +exit +fi + +VERSION_STRING=$(cat setup.cfg | grep "version") + +IFS=' ' +read -ra VERSION_STRING_ARR <<< $VERSION_STRING + +VERSION="${VERSION_STRING_ARR[@]:2:2}" + +echo "Current Version: $VERSION" + +IFS='.' + +read -ra VERSION_NUMBERS <<< $VERSION + +MAJOR=${VERSION_NUMBERS[@]::1} +MINOR=${VERSION_NUMBERS[@]:1:1} +PATCH=${VERSION_NUMBERS[@]:2:2} + +if [ "$1" = "major" ]; then +PATCH="0" +MINOR="0" +MAJOR=$(( $MAJOR + 1)) +fi + +if [ "$1" = "minor" ]; then +PATCH="0" +MINOR=$(( $MINOR + 1)) +fi + +if [ "$1" = "patch" ]; then +PATCH=$(( $PATCH + 1)) +fi + +UPDATED_VERSION="${MAJOR}.${MINOR}.${PATCH}" + +echo "Updated Version: $UPDATED_VERSION" + +sed -i "s/version = ${VERSION}/version = ${UPDATED_VERSION}/" setup.cfg +sed -i "s/^ version=\"${VERSION}\",/ version=\"${UPDATED_VERSION}\",/" setup.py +sed -i "s/^ download_url=\"https:\/\/github.com\/Milind220\/Ozone\/archive\/refs\/tags\/v${VERSION}.tar.gz\",/ download_url=\"https:\/\/github.com\/Milind220\/Ozone\/archive\/refs\/tags\/v${UPDATED_VERSION}.tar.gz\",/" setup.py From 53bbf36a7cc8f74df9eaa0150ee976977dc34797 Mon Sep 17 00:00:00 2001 From: Milind Sharma <68847270+Milind220@users.noreply.github.com> Date: Thu, 17 Mar 2022 14:01:53 +0530 Subject: [PATCH 11/15] fix: raise new exception instead of throwing old one Co-authored-by: Sameh --- src/ozone/ozone.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/ozone/ozone.py b/src/ozone/ozone.py index 3d4bc6f..6feb99a 100644 --- a/src/ozone/ozone.py +++ b/src/ozone/ozone.py @@ -435,11 +435,10 @@ def get_specific_parameter( try: result = float(row[air_param]) except KeyError: - print( - "Missing air quality parameter!\n" - + "Try: get_specific_parameter(`city name`, `aqi` or `no2` or `co`)" - ) - raise + raise Exception("Missing air quality parameter!\n" + + "Try: get_specific_parameter(`city name`, `aqi` or `no2` or `co`)") + + return result From 43fbdc1d937fadc3bd86fb0864a1339bbb7f2501 Mon Sep 17 00:00:00 2001 From: Nuclear03020704 <52926983+lahdjirayhan@users.noreply.github.com> Date: Thu, 17 Mar 2022 15:59:05 +0700 Subject: [PATCH 12/15] fix: Improve exception message --- src/ozone/ozone.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ozone/ozone.py b/src/ozone/ozone.py index 6feb99a..1ac8412 100644 --- a/src/ozone/ozone.py +++ b/src/ozone/ozone.py @@ -435,10 +435,10 @@ def get_specific_parameter( try: result = float(row[air_param]) except KeyError: - raise Exception("Missing air quality parameter!\n" + - "Try: get_specific_parameter(`city name`, `aqi` or `no2` or `co`)") + raise Exception(f'Missing air quality parameter "{air_param}"\n' + + 'Try another air quality parameters: "aqi", "no2", or "co"') + - return result From 0fe7c46b14f51c28222ec3635f01ee85b3f86edf Mon Sep 17 00:00:00 2001 From: Milind Sharma Date: Thu, 17 Mar 2022 15:11:53 +0530 Subject: [PATCH 13/15] fix [updateVersion.sh]: spelling error fix --- updateVersion.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/updateVersion.sh b/updateVersion.sh index 472ba50..359d276 100644 --- a/updateVersion.sh +++ b/updateVersion.sh @@ -16,7 +16,7 @@ fi if [ "$1" != "major" ] && [ "$1" != "minor" ] && [ "$1" != "patch" ]; then echo "Incorrect argument provided" -echo "Your argument must be either major, minor or patch (Case sensivite)" +echo "Your argument must be either major, minor or patch (Case sensitive)" exit fi From b92450d6b42822b31abe8d840f9133e4d9747efc Mon Sep 17 00:00:00 2001 From: Milind Sharma Date: Thu, 17 Mar 2022 15:16:55 +0530 Subject: [PATCH 14/15] chore: Bump release version to 1.4.3 --- setup.cfg | 2 +- setup.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/setup.cfg b/setup.cfg index 6a8c289..58e447e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = ozon3 -version = 1.4.2 +version = 1.4.3 author = Milind Sharma author_email = milindsharma8@gmail.com description = A package to get air quality data using the WAQI API diff --git a/setup.py b/setup.py index d5fe52e..ab027eb 100644 --- a/setup.py +++ b/setup.py @@ -9,8 +9,8 @@ description="A package to get air quality data using the WAQI API", license="GPLv3+", url="https://github.com/Milind220/Ozone", - version="1.4.2", - download_url="https://github.com/Milind220/Ozone/archive/refs/tags/v1.4.2.tar.gz", + version="1.4.3", + download_url="https://github.com/Milind220/Ozone/archive/refs/tags/v1.4.3.tar.gz", packages=setuptools.find_packages(), install_requires=[ "numpy; python_version>='3'", From 03f6020c05d7c35162c5c3a01674b4253b2aca0c Mon Sep 17 00:00:00 2001 From: Milind Sharma Date: Thu, 17 Mar 2022 15:17:51 +0530 Subject: [PATCH 15/15] style: Reformat ozone with black --- src/ozone/ozone.py | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/ozone/ozone.py b/src/ozone/ozone.py index 1ac8412..ad62ca0 100644 --- a/src/ozone/ozone.py +++ b/src/ozone/ozone.py @@ -112,9 +112,7 @@ def reset_token(self, token: str) -> None: self._check_token_validity() def _format_output( - self, - data_format: str = "df", - df: pandas.DataFrame = pandas.DataFrame(), + self, data_format: str = "df", df: pandas.DataFrame = pandas.DataFrame(), ) -> pandas.DataFrame: """Format output data @@ -135,12 +133,12 @@ def _format_output( df.to_json("air_quality_data.json") print("File saved to disk as air_quality_data.json") elif data_format == "xlsx": - df.to_excel( - "air_quality_data.xlsx", - ) + df.to_excel("air_quality_data.xlsx",) print("File saved to disk as air_quality_data.xlsx") else: - raise Exception(f'Invalid file format {data_format}. Use any of: csv, json, xlsx, df') + raise Exception( + f"Invalid file format {data_format}. Use any of: csv, json, xlsx, df" + ) return pandas.DataFrame() def _parse_data( @@ -218,7 +216,9 @@ def _AQI_meaning(self, aqi: float) -> Tuple[str, str]: "Health alert: everyone may experience more serious health effects." ) else: - raise Exception(f'{aqi} is not valid air quality index value. Should be between 0 to 500.') + raise Exception( + f"{aqi} is not valid air quality index value. Should be between 0 to 500." + ) return AQI_meaning, AQI_health_implications @@ -318,12 +318,16 @@ def get_city_air( if self._check_status_code(r): # Get all the data. response_content = json.loads(r.content) - status, data_obj = response_content['status'], response_content['data'] - if status != 'ok': - if data_obj == 'Unknown station': - raise Exception(f'There is no known AQI station for the city "{city}"') + status, data_obj = response_content["status"], response_content["data"] + if status != "ok": + if data_obj == "Unknown station": + raise Exception( + f'There is no known AQI station for the city "{city}"' + ) - raise Exception(f'There is a problem with city "{city}", the returned data: {data_obj}') + raise Exception( + f'There is a problem with city "{city}", the returned data: {data_obj}' + ) row = self._parse_data(data_obj, city, params) @@ -415,11 +419,7 @@ def get_multiple_city_air( df.reset_index(inplace=True, drop=True) return self._format_output(data_format, df) - def get_specific_parameter( - self, - city: str, - air_param: str = "", - ) -> float: + def get_specific_parameter(self, city: str, air_param: str = "",) -> float: """Get specific parameter as a float Args: @@ -435,10 +435,10 @@ def get_specific_parameter( try: result = float(row[air_param]) except KeyError: - raise Exception(f'Missing air quality parameter "{air_param}"\n' + - 'Try another air quality parameters: "aqi", "no2", or "co"') - - + raise Exception( + f'Missing air quality parameter "{air_param}"\n' + + 'Try another air quality parameters: "aqi", "no2", or "co"' + ) return result