diff options
author | Aaron Borden <adborden@live.com> | 2013-04-17 15:46:25 (GMT) |
---|---|---|
committer | Aaron Borden <adborden@live.com> | 2013-04-17 15:46:25 (GMT) |
commit | 3d160c87403993347cfa67161c860972c6e5ba66 (patch) | |
tree | ee7f6bedd8dec02c0c02a53fbeaf65c8a8b626bb | |
parent | 367f24b01dc5e9eccddbc2b8b255b66110ebcc50 (diff) |
Beginning implementation of GDrivegoogle-drive
-rw-r--r-- | extensions/web/google/google.py | 191 | ||||
-rw-r--r-- | extensions/web/google/google_online_account.py | 17 |
2 files changed, 107 insertions, 101 deletions
diff --git a/extensions/web/google/google.py b/extensions/web/google/google.py index 9ee669d..2d0373e 100644 --- a/extensions/web/google/google.py +++ b/extensions/web/google/google.py @@ -20,18 +20,46 @@ #OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN #THE SOFTWARE. +import httplib import json import logging -import pycurl +import mimetypes import time import urllib +import urllib2 +import os from gi.repository import GObject -class GDriveAccount(): +class GoogleAccount(): + APP_ID = "634890811982.apps.googleusercontent.com" + SCOPE = [ + 'https://www.googleapis.com/auth/drive.file', + 'https://www.googleapis.com/auth/userinfo.email' + ] + REDIRECT_URI = "http://localhost" + + _refresh_token = "" _access_token = "" @classmethod + def auth_url(cls): + url = 'https://accounts.google.com/o/oauth2/auth' + params = [ + ('client_id', cls.APP_ID), + ('redirect_uri', cls.REDIRECT_URI), + ('response_type', 'code'), + ('scope', ' '.join(cls.SCOPE)), + ('state', 'auth_cb') + ] + + return "%s?%s" % (url, urllib.urlencode(params)) + + @classmethod + def set_refresh_token(cls, refresh_token): + cls._refresh_token = refresh_token + + @classmethod def set_access_token(cls, access_token): cls._access_token = access_token @@ -39,6 +67,17 @@ class GDriveAccount(): def access_token(cls): return cls._access_token + @classmethod + def refresh_access_token(cls, token): + token_url = 'https://accounts.google.com/o/oauth2/token' + params = [ + ('client_id', cls.APP_ID), + ('refresh_token', token), + ('grant_type', 'refresh_token') + ] + + return json.load(urllib2.urlopen(token_url, urllib.urlencode(params))) + class GDriveObjectNotCreatedException(Exception): pass @@ -46,132 +85,86 @@ class GDriveBadCall(Exception): pass class GDriveFile(GObject.GObject): - API_URL = 'https://www.googleapis.com/drive/v2' - FILES_URL = "/files" __gsignals__ = { 'file-created': (GObject.SignalFlags.RUN_FIRST, None, ([str])), 'file-create-failed': (GObject.SignalFlags.RUN_FIRST, None, ([str])), } - def __init__(self, fb_object_id=None): + def __init__(self, gdrive_object_id=None): GObject.GObject.__init__(self) - self.fb_object_id = fb_object_id + self.gdrive_object_id = gdrive_object_id def create(self, file_path): GObject.idle_add(self._create, file_path) def check_created(self, method_name): - if self.fb_object_id is None: + if self.gdrive_object_id is None: errmsg = "Need to call create before calling %s" % (method_name) raise GDriveObjectNotCreatedException(errmsg) def _create(self, file_path): - url = self.PHOTOS_URL % (GDriveAccount.access_token()) - c = pycurl.Curl() - params = [('source', (c.FORM_FILE, file_path))] - - response = [] - def write_cb(buf): - response.append(buf) - - result = self._http_call(url, params, write_cb, post=True) - if result == 200: - photo_id = self._id_from_response("".join(response)) - self.fb_object_id = photo_id - self.emit('file-created', photo_id) - else: - logging.debug("_create failed, HTTP resp code: %d" % result) - - if result == 400: - failed_reason = "Expired access token." - elif result == 6: - failed_reason = "Network is down." - failed_reason += \ - "Please connect to the network and try again." - else: - failed_reason = "Failed reason unknown: %s" % (str(result)) - - self.emit('photo-create-failed', failed_reason) - - def _id_from_response(self, response_str): - response_object = json.loads(response_str) - - if not "id" in response_object: - raise FbBadCall(response_str) - - fb_object_id = response_object['id'].encode('ascii', 'replace') - return fb_object_id - - def _refresh_comments(self): - """ this blocks """ - url = self.COMMENTS_URL % (self.fb_object_id) + # Upload the file + response = self._http_call('POST', '/upload/drive/v2/files?uploadType=media', file_path) #TODO use the resumable interface + if not self._check_success('_upload', response): + return - logging.debug("_refresh_comments fetching %s" % (url)) + data = json.loads(response.read()) + if not 'id' in data: + raise GDriveBadCall(data) - response_comments = [] - def write_cb(buf): - response_comments.append(buf) + self.gdrive_object_id = data['id'] - ret = self._http_call(url, [], write_cb, post=False) - if ret != 200: - logging.debug("_refresh_comments failed, HTTP resp code: %d" % ret) - self.emit('comments-download-failed', - "Comments download failed: %d" % (ret)) - return + # Update metadata + url = '/drive/v2/files' + params = { + 'title': os.path.basename(file_path), + 'description': 'A Sugar Journal file' + } - logging.debug("_refresh_comments: %s" % ("".join(response_comments))) - - try: - response_data = json.loads("".join(response_comments)) - if 'data' not in response_data: - logging.debug("No data inside the FB response") - self.emit('comments-download-failed', - "Comments download failed with no data") - return - except Exception as ex: - logging.debug("Couldn't parse FB response: %s" % str(ex)) - self.emit('comments-download-failed', - "Comments download failed: %s" % (str(ex))) + response = self._http_call('PUT', '%s/%s' % (url, self.gdrive_object_id), params) + if not self._check_success('_create', response): return - comments = [] - for c in response_data['data']: - comment = {} # this should be an Object - comment['from'] = c['from']['name'] - comment['message'] = c['message'] - comment['created_time'] = c['created_time'] - comment['like_count'] = c['like_count'] - comments.append(comment) - - if len(comments) > 0: - self.emit('comments-downloaded', comments) - else: - self.emit('comments-download-failed', 'No comments found') + self.emit('file-created', self.gdrive_object_id) - def _http_call(self, url, params, write_cb, post=False): - app_auth_params = [('access_token', FbAccount.access_token())] + def _http_call(self, method, url, params = None): + if not params: + params = {} - c = pycurl.Curl() - c.setopt(c.WRITEFUNCTION, write_cb) + headers = { + 'Authorization': "Bearer %s" % (self.access_token()) + } - if post: - c.setopt(c.POST, 1) - c.setopt(c.HTTPPOST, app_auth_params + params) + if isinstance(params, basestring): + mimetype, encoding = mimetypes.guess_type(params, strict=False) + if mimetype: + headers['Content-Type'] = mimetype + if encoding: + headers['Content-Encoding'] = encoding + params = open(params, 'rb') else: - c.setopt(c.HTTPGET, 1) - params_str = urllib.urlencode(app_auth_params + params) - url = "%s?%s" % (url, params_str) + params = json.dumps(params) + headers['Content-Type'] = 'application/json' - logging.debug("_http_call: %s" % (url)) + conn = httplib.HTTPSConnection('www.googleapis.com') + conn.request(method, url, params, headers) - c.setopt(c.URL, url) - c.perform() - result = c.getinfo(c.HTTP_CODE) - c.close() + return conn.getresponse() - return result + def _check_success(self, operation, response): + if response.status and response.status == 200: + return True + + logging.debug("%s failed, HTTP resp code: %d" % (operation, response.status)) + if response.status == 401: + failed_reason = "Expired access token." + else: + failed_reason = "Failed reason unknown: %s" % (str(response)) + print failed_reason + self.emit('file-create-failed', failed_reason) + return False if __name__ == '__main__': import sys @@ -180,7 +173,7 @@ if __name__ == '__main__': exit(1) access_token, file_path = sys.argv[1:3] - GDriveAccount.set_access_token(access_token) + GoogleAccount.set_access_token(access_token) def test_create_file(loop): diff --git a/extensions/web/google/google_online_account.py b/extensions/web/google/google_online_account.py index c0f556b..973510b 100644 --- a/extensions/web/google/google_online_account.py +++ b/extensions/web/google/google_online_account.py @@ -54,10 +54,20 @@ class GoogleOnlineAccount(online_account.OnlineAccount): def __init__(self): online_account.OnlineAccount.__init__(self) self._client = GConf.Client.get_default() - google.GDriveAccount.set_access_token(self._access_token()) + google.GoogleAccount.set_refresh_token(self._refresh_token()) + google.GoogleAccount.set_access_token(self._access_token()) + + if not self.is_active(): + data = google.GoogleAccount.refresh_access_token(self._refresh_token()) + client = self._client + client.set_string(self.ACCESS_TOKEN_KEY, data.access_token) + expiry_time = int(time.time()) + data.expires_in + client.set_int(self.ACCESS_TOKEN_KEY_EXPIRATION_DATE, + expiry_time) + google.GoogleAccount.set_access_token(self._access_token()) def is_configured(self): - return self._access_token() is not None + return self._refresh_token() is not None def is_active(self): expiration_date = \ @@ -78,6 +88,9 @@ class GoogleOnlineAccount(online_account.OnlineAccount): def get_refresh_button(self): return FacebookRefreshButton(self.is_active()) + def _refresh_token(self): + return self._client.get_string(self.REFRESH_TOKEN_KEY) + def _access_token(self): return self._client.get_string(self.ACCESS_TOKEN_KEY) |