From 55c0896b31ed6ecba625d9e4d300af92475b949a Mon Sep 17 00:00:00 2001 From: Fabio Manganiello Date: Thu, 14 Mar 2019 01:12:39 +0100 Subject: [PATCH] Refactored Google plugins with support multiple scopes and a simpler API and added Google Fit plugin --- platypush/plugins/google/__init__.py | 24 ++++++++--- platypush/plugins/google/calendar.py | 14 +------ platypush/plugins/google/credentials.py | 14 +++---- platypush/plugins/google/fit.py | 55 +++++++++++++++++++++++++ platypush/plugins/google/mail.py | 12 +----- platypush/plugins/google/youtube.py | 15 +------ 6 files changed, 84 insertions(+), 50 deletions(-) create mode 100644 platypush/plugins/google/fit.py diff --git a/platypush/plugins/google/__init__.py b/platypush/plugins/google/__init__.py index 95707b5549..110ffad36d 100644 --- a/platypush/plugins/google/__init__.py +++ b/platypush/plugins/google/__init__.py @@ -33,7 +33,7 @@ class GooglePlugin(Plugin): * **google-api-python-client** (``pip install google-api-python-client``) """ - def __init__(self, scopes, *args, **kwargs): + def __init__(self, scopes=None, *args, **kwargs): """ Initialized the Google plugin with the required scopes. @@ -42,11 +42,25 @@ class GooglePlugin(Plugin): """ super().__init__(*args, **kwargs) - self.credentials = {} + self._scopes = scopes or [] - for scope in scopes: - self.credentials[scope] = get_credentials(scope) + scopes = ' '.join(sorted(self._scopes)) + self.credentials = { + scopes: get_credentials(scopes) + } + + + def get_service(self, service, version, scopes=None): + import httplib2 + from apiclient import discovery + + if scopes is None: + scopes = getattr(self, 'scopes') if hasattr(self, 'scopes') else [] + + scopes = ' '.join(sorted(scopes)) + credentials = self.credentials[scopes] + http = credentials.authorize(httplib2.Http()) + return discovery.build(service, version, http=http, cache_discovery=False) # vim:sw=4:ts=4:et: - diff --git a/platypush/plugins/google/calendar.py b/platypush/plugins/google/calendar.py index cc103c9d4b..50a6dcc193 100644 --- a/platypush/plugins/google/calendar.py +++ b/platypush/plugins/google/calendar.py @@ -4,11 +4,8 @@ import base64 import datetime -import httplib2 import os -from apiclient import discovery - from platypush.plugins import action from platypush.plugins.google import GooglePlugin from platypush.plugins.calendar import CalendarInterface @@ -33,7 +30,7 @@ class GoogleCalendarPlugin(GooglePlugin, CalendarInterface): """ now = datetime.datetime.utcnow().isoformat() + 'Z' - service = self._get_service() + service = self.get_service('calendar', 'v3') result = service.events().list(calendarId='primary', timeMin=now, maxResults=max_results, singleEvents=True, orderBy='startTime').execute() @@ -42,14 +39,5 @@ class GoogleCalendarPlugin(GooglePlugin, CalendarInterface): return events - def _get_service(self, scope=None): - if scope is None: - scope = self.scopes[0] - - credentials = self.credentials[scope] - http = credentials.authorize(httplib2.Http()) - return discovery.build('calendar', 'v3', http=http, cache_discovery=False) - - # vim:sw=4:ts=4:et: diff --git a/platypush/plugins/google/credentials.py b/platypush/plugins/google/credentials.py index 1c1097c5ae..3ce4091fff 100644 --- a/platypush/plugins/google/credentials.py +++ b/platypush/plugins/google/credentials.py @@ -8,21 +8,19 @@ from oauth2client import tools from oauth2client.file import Storage -def get_credentials_filename(scope): +def get_credentials_filename(*scopes): from platypush.config import Config - scope_name = scope.split('/')[-1] + scope_name = '-'.join([scope.split('/')[-1] for scope in scopes]) credentials_dir = os.path.join( Config.get('workdir'), 'credentials', 'google') - if not os.path.exists(credentials_dir): - os.makedirs(credentials_dir) - + os.makedirs(credentials_dir, exist_ok=True) return os.path.join(credentials_dir, scope_name + '.json') def get_credentials(scope): - credentials_file = get_credentials_filename(scope) + credentials_file = get_credentials_filename(*sorted(scope.split(' '))) if not os.path.exists(credentials_file): raise RuntimeError(('Credentials file {} not found. Generate it through:\n' + '\tpython -m platypush.plugins.google.credentials "{}" ' + @@ -43,7 +41,7 @@ def get_credentials(scope): def generate_credentials(client_secret_path, scope): - credentials_file = get_credentials_filename(scope) + credentials_file = get_credentials_filename(*sorted(scope.split(' '))) store = Storage(credentials_file) flow = client.flow_from_clientsecrets(client_secret_path, scope) @@ -62,7 +60,7 @@ def main(): """ scope = sys.argv.pop(1) if len(sys.argv) > 1 \ - else input('Comma separated list of OAuth scopes: ') + else input('Space separated list of OAuth scopes: ') client_secret_path = os.path.expanduser( sys.argv.pop(1) if len(sys.argv) > 1 diff --git a/platypush/plugins/google/fit.py b/platypush/plugins/google/fit.py new file mode 100644 index 0000000000..7d541c8cdf --- /dev/null +++ b/platypush/plugins/google/fit.py @@ -0,0 +1,55 @@ +from apiclient import discovery + +from platypush.plugins import action +from platypush.plugins.google import GooglePlugin + + +class GoogleFitPlugin(GooglePlugin): + """ + Google Fit plugin + """ + + scopes = ['https://www.googleapis.com/auth/fitness.activity.read', + 'https://www.googleapis.com/auth/fitness.body.read', + 'https://www.googleapis.com/auth/fitness.body_temperature.read', + 'https://www.googleapis.com/auth/fitness.location.read'] + + def __init__(self, user_id='me', *args, **kwargs): + """ + :param user_id: Default Google user_id (default: 'me', default + configured account user) + :type user_id: str or int + """ + + super().__init__(scopes=self.scopes, *args, **kwargs) + self.user_id = user_id + + + @action + def get_data_sources(self, user_id=None): + """ + Get the available data sources for the specified user_id + """ + + service = self.get_service(service='fitness', version='v1') + sources = service.users().dataSources(). \ + list(userId=user_id or self.user_id).execute() + + return sources['dataSource'] + + @action + def get_data(self, data_source_id, user_id=None): + """ + Get raw data for the specified data_source_id + + :param data_source_id: Data source ID, see `get_data_sources` + :type data_source_id: str + """ + + service = self.get_service(service='fitness', version='v1') + return service.users().dataSources().dataPointChanges() \ + .list(dataSourceId=data_source_id, userId=user_id or self.user_id) \ + .execute().get('insertedDataPoint', []) + + +# vim:sw=4:ts=4:et: diff --git a/platypush/plugins/google/mail.py b/platypush/plugins/google/mail.py index ec3d696a36..78d612b728 100644 --- a/platypush/plugins/google/mail.py +++ b/platypush/plugins/google/mail.py @@ -87,7 +87,7 @@ class GoogleMailPlugin(GooglePlugin): msg.add_header('Content-Disposition', 'attachment', filename=filename) message.attach(msg) - service = self._get_service() + service = self.get_service('gmail', 'v1') body = { 'raw': base64.urlsafe_b64encode(message.as_bytes()).decode() } message = (service.users().messages().send( userId='me', body=body).execute()) @@ -100,18 +100,10 @@ class GoogleMailPlugin(GooglePlugin): """ Returns the available labels on the GMail account """ - service = self._get_service() + service = self.get_service('gmail', 'v1') results = service.users().labels().list(userId='me').execute() labels = results.get('labels', []) return labels - def _get_service(self): - scope = self.scopes[0] - credentials = self.credentials[scope] - http = credentials.authorize(httplib2.Http()) - return discovery.build('gmail', 'v1', http=http, cache_discovery=False) - - # vim:sw=4:ts=4:et: - diff --git a/platypush/plugins/google/youtube.py b/platypush/plugins/google/youtube.py index 1755eab126..4912969e16 100644 --- a/platypush/plugins/google/youtube.py +++ b/platypush/plugins/google/youtube.py @@ -4,11 +4,8 @@ import base64 import datetime -import httplib2 import os -from apiclient import discovery - from platypush.plugins import action from platypush.plugins.google import GooglePlugin from platypush.plugins.calendar import CalendarInterface @@ -62,7 +59,7 @@ class GoogleYoutubePlugin(GooglePlugin, CalendarInterface): if isinstance(types, list): types = ','.join(types) - service = self._get_service() + service = self.get_service('youtube', 'v3') result = service.search().list(part=parts, q=query, type=types, maxResults=max_results, **kwargs).execute() @@ -71,14 +68,4 @@ class GoogleYoutubePlugin(GooglePlugin, CalendarInterface): return events - def _get_service(self, scope=None): - if scope is None: - scope = self.scopes[0] - - credentials = self.credentials[scope] - http = credentials.authorize(httplib2.Http()) - return discovery.build('youtube', 'v3', http=http, cache_discovery=False) - - # vim:sw=4:ts=4:et: -