class EndomondoService(): def __init__(self): self.parameter_manager = ParameterManager() self.config = self._get_config() if not all(k in self.config for k in ("email", "password")): raise ConfigError("""Please put the following paths and their values in the aws parameter store /endomondo/email /endomondo/password """) self._authenticate() def _get_config(self): return self.parameter_manager.get_multiple('/endomondo/') def _authenticate(self): auth_key = self.config.get("auth_key") if not auth_key: auth_key = self._get_key() self.parameter_manager.store('/endomondo/auth_key', auth_key) def _get_key(self) -> str: url = 'https://api.mobile.endomondo.com/mobile/auth' # taken from https://github.com/isoteemu/sports-tracker-liberator/blob/master/endomondo/endomondo.py params = { 'os': platform.system(), 'model': platform.python_implementation(), 'osVersion': platform.release(), 'vendor': 'github/kreusen', 'appVariant': 'endomondo-api', 'country': 'GB', 'v': '2.4', 'appVersion': '0.1', 'deviceId': str(uuid.uuid5(uuid.NAMESPACE_DNS, socket.gethostname())), "action": "pair", "email": self.config.get("email"), "password": self.config.get("password") } r = requests.get(url, params=params) lines = r.text.split("\n") if lines[0] != "OK": raise AuthenticationError( f"Could not authenticate with Endomondo, Expected 'OK', got '{lines[0]}'" ) for line in lines[1:]: key, value = line.split("=") if key == "authToken": return value def get_runs(self, maxresults: int = 25) -> List[Run]: url = f"https://api.mobile.endomondo.com/mobile/api/workouts?authToken={self.config.get('auth_key')}&maxResults={maxresults}&fields=basic" r = requests.get(url) response = r.json() runs = [] for workout in response["data"]: # only get runs if workout["sport"] == 0 and workout["live"] == False: run = create_model_from_dict(Run, workout) runs.append(run) return runs
class GoogleFitManager(): def __init__(self): self.parameter_manager = ParameterManager() self.access_token = None def authenticate(self): if self.access_token is None: credentials = self._get_credentials() self.access_token = credentials.get("access_token") def _get_credentials(self) -> dict: """ Authenticates with google fit and returns a dictionary with the most recent valid credentials """ online_credentials = self.parameter_manager.get_multiple('/google_fit/') credentials = GoogleCredentials(**online_credentials, token_expiry=None, user_agent=None) http = credentials.authorize(httplib2.Http()) credentials.refresh(http) credentials_dict = json.loads(credentials.to_json()) self.access_token = credentials_dict.get("access_token") self._store_credentials_online(credentials_dict) return credentials_dict def _store_credentials_online(self, credentials_dict: dict): keys_to_store = ["access_token", "client_secret", "client_id", "refresh_token", "token_uri"] for key in keys_to_store: if key in credentials_dict: path = f'/google_fit/{key}' value = credentials_dict.get(key) self.parameter_manager.store(path, value) def get_weights(self, min_nanos: int, max_nanos: Optional[int] = None) -> Optional[List[WeightEntry]]: if max_nanos is None: max_nanos = get_current_nanos() base_url = 'https://www.googleapis.com/fitness/v1/users/me/dataSources/derived:com.google.weight:com.google.android.gms:merge_weight/datasets' # batch requests pe def batch_requests(self, base_url: str, min_nanos: int, max_nanos: int, batch_per_n_nanos: int = 8640000000): pass def build_url(self, base_url: str, min_nanos: int, max_nanos: int) -> str: return os.path.join(base_url, f'{min_nanos}-{max_nanos}') def make_request(self, url: str): headers = { "Authorization": f"Bearer {self.access_token}" } r = requests.get(url, headers=headers) response = r.json() if "error" in response: raise GoogleFitError(response) return response def extract_values(self, response: dict) -> List[dict]: point = response.get("point") extracted_values = [] if point: for p in point: extracted_value = { "startTimeNanos": p.get("startTimeNanos"), "value": p["value"][0]["fpVal"] } extracted_values.append(extracted_value) return extracted_values