From 38839c5346898671c1dc7e9b3879e41e02a74a06 Mon Sep 17 00:00:00 2001 From: Matthieu Bourgain Date: Tue, 20 Dec 2022 23:04:24 +0100 Subject: [PATCH] improve code quality --- CHANGELOG.md | 4 ++ custom_components/polar/__init__.py | 14 ++++-- custom_components/polar/config_flow.py | 13 +++-- custom_components/polar/const.py | 4 -- .../polar/lib/endpoints/__init__.py | 5 -- .../polar/lib/endpoints/users.py | 47 ------------------- custom_components/polar/manifest.json | 5 +- .../{lib => polaraccesslink}/__init__.py | 0 .../{lib => polaraccesslink}/accesslink.py | 30 ++++++++---- .../polaraccesslink/endpoints/__init__.py | 1 + .../endpoints/daily_activity.py | 0 .../endpoints/daily_activity_transaction.py | 0 .../endpoints/physical_info.py | 0 .../endpoints/physical_info_transaction.py | 0 .../endpoints/pull_notifications.py | 0 .../endpoints/resource.py | 0 .../endpoints/training_data.py | 14 ++---- .../endpoints/training_data_transaction.py | 32 ++++--------- .../endpoints/transaction.py | 9 ++-- .../polar/polaraccesslink/endpoints/users.py | 28 +++++++++++ .../polar/{lib => polaraccesslink}/oauth2.py | 37 +++++++-------- 21 files changed, 107 insertions(+), 136 deletions(-) delete mode 100644 custom_components/polar/lib/endpoints/__init__.py delete mode 100644 custom_components/polar/lib/endpoints/users.py rename custom_components/polar/{lib => polaraccesslink}/__init__.py (100%) rename custom_components/polar/{lib => polaraccesslink}/accesslink.py (80%) create mode 100644 custom_components/polar/polaraccesslink/endpoints/__init__.py rename custom_components/polar/{lib => polaraccesslink}/endpoints/daily_activity.py (100%) rename custom_components/polar/{lib => polaraccesslink}/endpoints/daily_activity_transaction.py (100%) rename custom_components/polar/{lib => polaraccesslink}/endpoints/physical_info.py (100%) rename custom_components/polar/{lib => polaraccesslink}/endpoints/physical_info_transaction.py (100%) rename custom_components/polar/{lib => polaraccesslink}/endpoints/pull_notifications.py (100%) rename custom_components/polar/{lib => polaraccesslink}/endpoints/resource.py (100%) rename custom_components/polar/{lib => polaraccesslink}/endpoints/training_data.py (67%) rename custom_components/polar/{lib => polaraccesslink}/endpoints/training_data_transaction.py (67%) rename custom_components/polar/{lib => polaraccesslink}/endpoints/transaction.py (75%) create mode 100644 custom_components/polar/polaraccesslink/endpoints/users.py rename custom_components/polar/{lib => polaraccesslink}/oauth2.py (84%) diff --git a/CHANGELOG.md b/CHANGELOG.md index e14cd97..c915f0a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 0.1.2 + +- Improve code quality + ## 0.1.1 - Fix HACS manifest diff --git a/custom_components/polar/__init__.py b/custom_components/polar/__init__.py index 41e1852..e1b7278 100644 --- a/custom_components/polar/__init__.py +++ b/custom_components/polar/__init__.py @@ -5,7 +5,14 @@ import logging from homeassistant.config_entries import ConfigEntry -from homeassistant.const import CONF_NAME, CONF_SCAN_INTERVAL, Platform +from homeassistant.const import ( + CONF_ACCESS_TOKEN, + CONF_CLIENT_ID, + CONF_CLIENT_SECRET, + CONF_NAME, + CONF_SCAN_INTERVAL, + Platform, +) from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.helpers.update_coordinator import DataUpdateCoordinator @@ -20,13 +27,10 @@ ATTR_RECHARGE_DATA, ATTR_SLEEP_DATA, ATTR_USER_DATA, - CONF_ACCESS_TOKEN, - CONF_CLIENT_ID, - CONF_CLIENT_SECRET, CONF_USER_ID, DOMAIN, ) -from .lib.accesslink import AccessLink +from .polaraccesslink.accesslink import AccessLink _LOGGER = logging.getLogger(__name__) PLATFORMS: list[Platform] = [Platform.SENSOR] diff --git a/custom_components/polar/config_flow.py b/custom_components/polar/config_flow.py index c002f6f..64c3872 100644 --- a/custom_components/polar/config_flow.py +++ b/custom_components/polar/config_flow.py @@ -10,7 +10,13 @@ from homeassistant import config_entries from homeassistant.components.http import HomeAssistantView -from homeassistant.const import CONF_NAME, CONF_SCAN_INTERVAL +from homeassistant.const import ( + CONF_ACCESS_TOKEN, + CONF_CLIENT_ID, + CONF_CLIENT_SECRET, + CONF_NAME, + CONF_SCAN_INTERVAL, +) from homeassistant.core import callback from homeassistant.data_entry_flow import FlowResult from homeassistant.helpers.config_entry_oauth2_flow import _decode_jwt, _encode_jwt @@ -18,14 +24,11 @@ from .const import ( AUTH_CALLBACK_NAME, AUTH_CALLBACK_PATH, - CONF_ACCESS_TOKEN, - CONF_CLIENT_ID, - CONF_CLIENT_SECRET, CONF_USER_ID, DEFAULT_SCAN_INTERVAL, DOMAIN, ) -from .lib.accesslink import AccessLink +from .polaraccesslink.accesslink import AccessLink _LOGGER = logging.getLogger(__name__) diff --git a/custom_components/polar/const.py b/custom_components/polar/const.py index cb01e21..34fc016 100644 --- a/custom_components/polar/const.py +++ b/custom_components/polar/const.py @@ -2,13 +2,9 @@ DOMAIN = "polar" -CONF_CLIENT_ID = "client_id" -CONF_CLIENT_SECRET = "client_secret" CONF_USER_ID = "user_id" -CONF_ACCESS_TOKEN = "access_token" DEFAULT_SCAN_INTERVAL = 30 - ATTR_EXERCISE_DATA = "exercisedata" ATTR_SLEEP_DATA = "sleepdata" ATTR_RECHARGE_DATA = "rechargedata" diff --git a/custom_components/polar/lib/endpoints/__init__.py b/custom_components/polar/lib/endpoints/__init__.py deleted file mode 100644 index cebcdc7..0000000 --- a/custom_components/polar/lib/endpoints/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from .users import Users -from .pull_notifications import PullNotifications -from .daily_activity import DailyActivity -from .physical_info import PhysicalInfo -from .training_data import TrainingData diff --git a/custom_components/polar/lib/endpoints/users.py b/custom_components/polar/lib/endpoints/users.py deleted file mode 100644 index 8ad95fc..0000000 --- a/custom_components/polar/lib/endpoints/users.py +++ /dev/null @@ -1,47 +0,0 @@ -import uuid - -from .resource import Resource - - -class Users(Resource): - """This resource provides all the necessary functions to manage users. - - https://www.polar.com/accesslink-api/?http#users - """ - - def register(self, access_token, member_id=uuid.uuid4().hex): - """Registration - - Once partner has been authorized by user, partner must register user before being able to access her data. - - :param access_token: access token of the user - :param member_id: unique client-specific identifier for the user - """ - return self._post( - endpoint="/users", access_token=access_token, json={"member-id": member_id} - ) - - def delete(self, user_id, access_token): - """De-registration - - When partner wishes no longer to receive user data, user can be de-registered. - This will revoke the access token authorized by user. - - :param user_id: id of the user - :param access_token: access token of the user - """ - return self._delete( - endpoint=f"/users/{user_id}", - access_token=access_token, - ) - - def get_information(self, user_id, access_token): - """List user's basic information. - - :param user_id: id of the user - :param access_token: access token of the user - """ - return self._get( - endpoint=f"/users/{user_id}", - access_token=access_token, - ) diff --git a/custom_components/polar/manifest.json b/custom_components/polar/manifest.json index c84afae..b80e0cc 100644 --- a/custom_components/polar/manifest.json +++ b/custom_components/polar/manifest.json @@ -6,7 +6,8 @@ "codeowners": ["@Aohzan"], "config_flow": true, "requirements": ["isodate==0.6.1"], - "dependencies": ["application_credentials", "http"], + "dependencies": ["http"], "iot_class": "cloud_polling", - "version": "0.1.0" + "integration_type": "service", + "version": "0.1.2" } diff --git a/custom_components/polar/lib/__init__.py b/custom_components/polar/polaraccesslink/__init__.py similarity index 100% rename from custom_components/polar/lib/__init__.py rename to custom_components/polar/polaraccesslink/__init__.py diff --git a/custom_components/polar/lib/accesslink.py b/custom_components/polar/polaraccesslink/accesslink.py similarity index 80% rename from custom_components/polar/lib/accesslink.py rename to custom_components/polar/polaraccesslink/accesslink.py index 63fc38d..e1189c8 100644 --- a/custom_components/polar/lib/accesslink.py +++ b/custom_components/polar/polaraccesslink/accesslink.py @@ -2,10 +2,15 @@ from datetime import datetime import json import logging +from os import path import isodate -from . import endpoints +from .endpoints.daily_activity import DailyActivity +from .endpoints.physical_info import PhysicalInfo +from .endpoints.pull_notifications import PullNotifications +from .endpoints.training_data import TrainingData +from .endpoints.users import Users from .oauth2 import OAuth2Client AUTHORIZATION_URL = "https://flow.polar.com/oauth2/authorization" @@ -37,11 +42,11 @@ def __init__(self, client_id, client_secret, redirect_url=None): client_secret=client_secret, ) - self.users = endpoints.Users(oauth=self.oauth) - self.pull_notifications = endpoints.PullNotifications(oauth=self.oauth) - self.training_data = endpoints.TrainingData(oauth=self.oauth) - self.physical_info = endpoints.PhysicalInfo(oauth=self.oauth) - self.daily_activity = endpoints.DailyActivity(oauth=self.oauth) + self.users = Users(oauth=self.oauth) + self.pull_notifications = PullNotifications(oauth=self.oauth) + self.training_data = TrainingData(oauth=self.oauth) + self.physical_info = PhysicalInfo(oauth=self.oauth) + self.daily_activity = DailyActivity(oauth=self.oauth) def get_authorization_url(self, state=None): """Get the authorization url for the client.""" @@ -99,10 +104,17 @@ def get_daily_activities(self, user_id, access_token, state_file_path): ) if not transaction: - _LOGGER.debug("No new daily activity available, get from backup file") try: - state_file = open(state_file_path, encoding="utf-8") - activities = json.loads(state_file.read()) + if path.isfile(state_file_path): + _LOGGER.debug( + "No new daily activity available, get from backup file" + ) + with open(state_file_path, encoding="utf-8") as state_file: + activities = json.loads(state_file.read()) + else: + _LOGGER.debug( + "No daily activity available, will try for the next sync" + ) except OSError as exc: _LOGGER.error( "Unable to get daily activities from backup file %s: %s", diff --git a/custom_components/polar/polaraccesslink/endpoints/__init__.py b/custom_components/polar/polaraccesslink/endpoints/__init__.py new file mode 100644 index 0000000..f3c578b --- /dev/null +++ b/custom_components/polar/polaraccesslink/endpoints/__init__.py @@ -0,0 +1 @@ +"""Endpoints for Polar Access Link.""" diff --git a/custom_components/polar/lib/endpoints/daily_activity.py b/custom_components/polar/polaraccesslink/endpoints/daily_activity.py similarity index 100% rename from custom_components/polar/lib/endpoints/daily_activity.py rename to custom_components/polar/polaraccesslink/endpoints/daily_activity.py diff --git a/custom_components/polar/lib/endpoints/daily_activity_transaction.py b/custom_components/polar/polaraccesslink/endpoints/daily_activity_transaction.py similarity index 100% rename from custom_components/polar/lib/endpoints/daily_activity_transaction.py rename to custom_components/polar/polaraccesslink/endpoints/daily_activity_transaction.py diff --git a/custom_components/polar/lib/endpoints/physical_info.py b/custom_components/polar/polaraccesslink/endpoints/physical_info.py similarity index 100% rename from custom_components/polar/lib/endpoints/physical_info.py rename to custom_components/polar/polaraccesslink/endpoints/physical_info.py diff --git a/custom_components/polar/lib/endpoints/physical_info_transaction.py b/custom_components/polar/polaraccesslink/endpoints/physical_info_transaction.py similarity index 100% rename from custom_components/polar/lib/endpoints/physical_info_transaction.py rename to custom_components/polar/polaraccesslink/endpoints/physical_info_transaction.py diff --git a/custom_components/polar/lib/endpoints/pull_notifications.py b/custom_components/polar/polaraccesslink/endpoints/pull_notifications.py similarity index 100% rename from custom_components/polar/lib/endpoints/pull_notifications.py rename to custom_components/polar/polaraccesslink/endpoints/pull_notifications.py diff --git a/custom_components/polar/lib/endpoints/resource.py b/custom_components/polar/polaraccesslink/endpoints/resource.py similarity index 100% rename from custom_components/polar/lib/endpoints/resource.py rename to custom_components/polar/polaraccesslink/endpoints/resource.py diff --git a/custom_components/polar/lib/endpoints/training_data.py b/custom_components/polar/polaraccesslink/endpoints/training_data.py similarity index 67% rename from custom_components/polar/lib/endpoints/training_data.py rename to custom_components/polar/polaraccesslink/endpoints/training_data.py index 6fdff1e..ace8353 100644 --- a/custom_components/polar/lib/endpoints/training_data.py +++ b/custom_components/polar/polaraccesslink/endpoints/training_data.py @@ -1,21 +1,13 @@ +"""Training data.""" from .resource import Resource from .training_data_transaction import TrainingDataTransaction class TrainingData(Resource): - """This resource allows partners to access their users' training data. - - https://www.polar.com/accesslink-api/?http#training-data - """ + """This resource allows partners to access their users' training data.""" def create_transaction(self, user_id, access_token): - """Initiate exercise transaction - - Check for new training data and create a new transaction if data is available. - - :param user_id: id of the user - :param access_token: access token of the user - """ + """Initiate exercise transaction.""" response = self._post( endpoint=f"/users/{user_id}/exercise-transactions", access_token=access_token, diff --git a/custom_components/polar/lib/endpoints/training_data_transaction.py b/custom_components/polar/polaraccesslink/endpoints/training_data_transaction.py similarity index 67% rename from custom_components/polar/lib/endpoints/training_data_transaction.py rename to custom_components/polar/polaraccesslink/endpoints/training_data_transaction.py index a2db067..c23aebf 100644 --- a/custom_components/polar/lib/endpoints/training_data_transaction.py +++ b/custom_components/polar/polaraccesslink/endpoints/training_data_transaction.py @@ -3,6 +3,8 @@ class TrainingDataTransaction(Transaction): + """Training data transaction.""" + def list_exercises(self): """Retrieve list of urls to available exercises.""" return self._get( @@ -10,17 +12,11 @@ def list_exercises(self): ) def get_exercise_summary(self, url): - """Retrieve training session summary data - - :param url: url of the exercise entity - """ + """Retrieve training session summary data.""" return self._get(endpoint=None, url=url, access_token=self.access_token) def get_gpx(self, url): - """Retrieve training session summary data in GPX format - - :param url: url of the exercise entity - """ + """Retrieve training session summary data in GPX format.""" return self._get( endpoint=None, url=url + "/gpx", @@ -29,10 +25,7 @@ def get_gpx(self, url): ) def get_tcx(self, url): - """Retrieve training session summary data in TCX format - - :param url: url of the exercise entity - """ + """Retrieve training session summary data in TCX format.""" return self._get( endpoint=None, url=url + "/tcx", @@ -41,26 +34,17 @@ def get_tcx(self, url): ) def get_heart_rate_zones(self, url): - """Retrieve heart rate zones in training session - - :param url: url of the exercise entity - """ + """Retrieve heart rate zones in training session.""" return self._get( endpoint=None, url=url + "/heart-rate-zones", access_token=self.access_token ) def get_available_samples(self, url): - """Retrieve list of urls to available samples in training session - - :param url: url of the exercise entity - """ + """Retrieve list of urls to available samples in training session.""" return self._get( endpoint=None, url=url + "/samples", access_token=self.access_token ) def get_samples(self, url): - """Retrieve sample data of given type - - :param url: url pointing to single sample type data - """ + """Retrieve sample data of given type.""" return self._get(endpoint=None, url=url, access_token=self.access_token) diff --git a/custom_components/polar/lib/endpoints/transaction.py b/custom_components/polar/polaraccesslink/endpoints/transaction.py similarity index 75% rename from custom_components/polar/lib/endpoints/transaction.py rename to custom_components/polar/polaraccesslink/endpoints/transaction.py index 911a8b4..240819e 100644 --- a/custom_components/polar/lib/endpoints/transaction.py +++ b/custom_components/polar/polaraccesslink/endpoints/transaction.py @@ -1,18 +1,19 @@ +"""Generic transaction.""" from .resource import Resource class Transaction(Resource): + """Generic transaction.""" + def __init__(self, oauth, transaction_url, user_id, access_token): + """Init the transaction object.""" super().__init__(oauth) self.transaction_url = transaction_url self.user_id = user_id self.access_token = access_token def commit(self): - """Commit the transaction - - This should be done after retrieving data from the transaction. - """ + """Commit the transaction.""" return self._put( endpoint=None, url=self.transaction_url, access_token=self.access_token ) diff --git a/custom_components/polar/polaraccesslink/endpoints/users.py b/custom_components/polar/polaraccesslink/endpoints/users.py new file mode 100644 index 0000000..6ce86b3 --- /dev/null +++ b/custom_components/polar/polaraccesslink/endpoints/users.py @@ -0,0 +1,28 @@ +"""Users.""" +import uuid + +from .resource import Resource + + +class Users(Resource): + """This resource provides all the necessary functions to manage users.""" + + def register(self, access_token, member_id=uuid.uuid4().hex): + """Registration.""" + return self._post( + endpoint="/users", access_token=access_token, json={"member-id": member_id} + ) + + def delete(self, user_id, access_token): + """De-registration.""" + return self._delete( + endpoint=f"/users/{user_id}", + access_token=access_token, + ) + + def get_information(self, user_id, access_token): + """List user's basic information.""" + return self._get( + endpoint=f"/users/{user_id}", + access_token=access_token, + ) diff --git a/custom_components/polar/lib/oauth2.py b/custom_components/polar/polaraccesslink/oauth2.py similarity index 84% rename from custom_components/polar/lib/oauth2.py rename to custom_components/polar/polaraccesslink/oauth2.py index bb9016b..26a426e 100644 --- a/custom_components/polar/lib/oauth2.py +++ b/custom_components/polar/polaraccesslink/oauth2.py @@ -1,19 +1,16 @@ +"""OAuth access for Polar Access Link.""" import logging +from urllib.parse import urlencode import requests from requests.auth import HTTPBasicAuth from requests.exceptions import HTTPError -try: - from urllib.parse import urlencode -except ImportError: - from urllib import urlencode - _LOGGER = logging.getLogger(__name__) class OAuth2Client: - """Wrapper class for OAuth2 requests""" + """Wrapper class for OAuth2 requests.""" def __init__( self, @@ -24,6 +21,7 @@ def __init__( client_id, client_secret, ): + """Init the client object.""" self.url = url self.authorization_url = authorization_url self.access_token_url = access_token_url @@ -32,7 +30,7 @@ def __init__( self.client_secret = client_secret def get_auth_headers(self, access_token): - """Get authorization headers for user level api resources""" + """Get authorization headers for user level api resources.""" return { "Authorization": f"Bearer {access_token}", @@ -41,7 +39,7 @@ def get_auth_headers(self, access_token): } def get_authorization_url(self, response_type="code", state=None): - """Build authorization url for the client""" + """Build authorization url for the client.""" params = { "client_id": self.client_id, @@ -59,7 +57,7 @@ def get_authorization_url(self, response_type="code", state=None): ) def get_access_token(self, authorization_code): - """Exchange authorization code for an access token""" + """Exchange authorization code for an access token.""" headers = { "Content-Type": "application/x-www-form-urlencoded", @@ -78,11 +76,7 @@ def get_access_token(self, authorization_code): ) def __build_endpoint_kwargs(self, **kwargs): - """Create endpoint url for requests - - If `endpoint` argument is given, it is appended to the api url - and used as the request url. Otherwise `url` argument is used. - """ + """Create endpoint url for requests.""" if "endpoint" in kwargs: if kwargs["endpoint"] is not None: @@ -92,11 +86,7 @@ def __build_endpoint_kwargs(self, **kwargs): return kwargs def __build_auth_kwargs(self, **kwargs): - """Setup authentication for requests - - If `access_token` is given, it is used in Authentication header. - Otherwise basic auth is used with the client credentials. - """ + """Build the authentication to make requests.""" if "access_token" in kwargs: headers = self.get_auth_headers(kwargs["access_token"]) @@ -112,11 +102,13 @@ def __build_auth_kwargs(self, **kwargs): return kwargs def __build_request_kwargs(self, **kwargs): + """Build requests.""" kwargs = self.__build_endpoint_kwargs(**kwargs) kwargs = self.__build_auth_kwargs(**kwargs) return kwargs def __parse_response(self, response): + """Parse response.""" if response.status_code >= 400: message = "{code} {reason}: {body}".format( code=response.status_code, reason=response.reason, body=response.text @@ -132,21 +124,26 @@ def __parse_response(self, response): return response.text def __request(self, method, **kwargs): + """Make a request.""" kwargs = self.__build_request_kwargs(**kwargs) _LOGGER.debug("%s request to URL: %s", method.upper(), kwargs["url"]) - response = requests.request(method, **kwargs) + response = requests.request(method=method, timeout=60, **kwargs) return self.__parse_response(response) def get(self, endpoint, **kwargs): + """Make a GET request.""" return self.__request("get", endpoint=endpoint, **kwargs) def post(self, endpoint, **kwargs): + """Make a POST request.""" return self.__request("post", endpoint=endpoint, **kwargs) def put(self, endpoint, **kwargs): + """Make a PUT request.""" return self.__request("put", endpoint=endpoint, **kwargs) def delete(self, endpoint, **kwargs): + """Make a DELETE request.""" return self.__request("delete", endpoint=endpoint, **kwargs)