merge
This commit is contained in:
@@ -7,3 +7,4 @@ syntax: glob
|
|||||||
samples/cmdline/*.dat
|
samples/cmdline/*.dat
|
||||||
htmlcov/*
|
htmlcov/*
|
||||||
.coverage
|
.coverage
|
||||||
|
database.sqlite3
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"data": {
|
"data": {
|
||||||
"moderator": {
|
"moderator": {
|
||||||
"v1": {
|
"v1": {
|
||||||
"baseUrl": "https://www.googleapis.com/",
|
"baseUrl": "https://www.googleapis.com/",
|
||||||
"auth": {
|
"auth": {
|
||||||
"request": {
|
"request": {
|
||||||
"url": "https://www.google.com/accounts/OAuthGetRequestToken",
|
"url": "https://www.google.com/accounts/OAuthGetRequestToken",
|
||||||
|
|||||||
@@ -45,13 +45,21 @@ except ImportError: # pragma: no cover
|
|||||||
import json as simplejson
|
import json as simplejson
|
||||||
|
|
||||||
|
|
||||||
class HttpError(Exception):
|
class Error(Exception):
|
||||||
|
"""Base error for this module."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class UnknownLinkType(Exception):
|
class HttpError(Error):
|
||||||
|
"""HTTP data was invalid or unexpected."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class UnknownLinkType(Error):
|
||||||
|
"""Link type unknown or unexpected."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
DISCOVERY_URI = ('http://www.googleapis.com/discovery/0.1/describe'
|
DISCOVERY_URI = ('http://www.googleapis.com/discovery/0.1/describe'
|
||||||
'{?api,apiVersion}')
|
'{?api,apiVersion}')
|
||||||
|
|
||||||
@@ -86,12 +94,15 @@ class JsonModel(object):
|
|||||||
if body_value is None:
|
if body_value is None:
|
||||||
return (headers, path_params, query, None)
|
return (headers, path_params, query, None)
|
||||||
else:
|
else:
|
||||||
model = {'data': body_value}
|
if len(body_value) == 1 and 'data' in body_value:
|
||||||
|
model = body_value
|
||||||
|
else:
|
||||||
|
model = {'data': body_value}
|
||||||
headers['content-type'] = 'application/json'
|
headers['content-type'] = 'application/json'
|
||||||
return (headers, path_params, query, simplejson.dumps(model))
|
return (headers, path_params, query, simplejson.dumps(model))
|
||||||
|
|
||||||
def build_query(self, params):
|
def build_query(self, params):
|
||||||
params.update({'alt': 'json', 'prettyprint': 'true'})
|
params.update({'alt': 'json'})
|
||||||
astuples = []
|
astuples = []
|
||||||
for key, value in params.iteritems():
|
for key, value in params.iteritems():
|
||||||
if getattr(value, 'encode', False) and callable(value.encode):
|
if getattr(value, 'encode', False) and callable(value.encode):
|
||||||
@@ -103,6 +114,9 @@ class JsonModel(object):
|
|||||||
# Error handling is TBD, for example, do we retry
|
# Error handling is TBD, for example, do we retry
|
||||||
# for some operation/error combinations?
|
# for some operation/error combinations?
|
||||||
if resp.status < 300:
|
if resp.status < 300:
|
||||||
|
if resp.status == 204:
|
||||||
|
# A 204: No Content response should be treated differently to all the other success states
|
||||||
|
return simplejson.loads('{}')
|
||||||
return simplejson.loads(content)['data']
|
return simplejson.loads(content)['data']
|
||||||
else:
|
else:
|
||||||
logging.debug('Content from bad request was: %s' % content)
|
logging.debug('Content from bad request was: %s' % content)
|
||||||
|
|||||||
@@ -21,7 +21,17 @@ except ImportError:
|
|||||||
from cgi import parse_qs, parse_qsl
|
from cgi import parse_qs, parse_qsl
|
||||||
|
|
||||||
|
|
||||||
class MissingParameter(Exception):
|
class Error(Exception):
|
||||||
|
"""Base error for this module."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class RequestError(Error):
|
||||||
|
"""Error occurred during request."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class MissingParameter(Error):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@@ -120,8 +130,10 @@ class OAuthCredentials(Credentials):
|
|||||||
if headers == None:
|
if headers == None:
|
||||||
headers = {}
|
headers = {}
|
||||||
headers.update(req.to_header())
|
headers.update(req.to_header())
|
||||||
if 'user-agent' not in headers:
|
if 'user-agent' in headers:
|
||||||
headers['user-agent'] = self.user_agent
|
headers['user-agent'] = self.user_agent + ' ' + headers['user-agent']
|
||||||
|
else:
|
||||||
|
headers['user-agent'] = self.user_agent
|
||||||
return request_orig(uri, method, body, headers,
|
return request_orig(uri, method, body, headers,
|
||||||
redirections, connection_type)
|
redirections, connection_type)
|
||||||
|
|
||||||
@@ -185,7 +197,7 @@ class FlowThreeLegged(object):
|
|||||||
body=body)
|
body=body)
|
||||||
if resp['status'] != '200':
|
if resp['status'] != '200':
|
||||||
logging.error('Failed to retrieve temporary authorization: %s' % content)
|
logging.error('Failed to retrieve temporary authorization: %s' % content)
|
||||||
raise Exception('Invalid response %s.' % resp['status'])
|
raise RequestError('Invalid response %s.' % resp['status'])
|
||||||
|
|
||||||
self.request_token = dict(parse_qsl(content))
|
self.request_token = dict(parse_qsl(content))
|
||||||
|
|
||||||
@@ -222,7 +234,7 @@ class FlowThreeLegged(object):
|
|||||||
resp, content = client.request(uri, 'POST', headers=headers)
|
resp, content = client.request(uri, 'POST', headers=headers)
|
||||||
if resp['status'] != '200':
|
if resp['status'] != '200':
|
||||||
logging.error('Failed to retrieve access token: %s' % content)
|
logging.error('Failed to retrieve access token: %s' % content)
|
||||||
raise Exception('Invalid response %s.' % resp['status'])
|
raise RequestError('Invalid response %s.' % resp['status'])
|
||||||
|
|
||||||
oauth_params = dict(parse_qsl(content))
|
oauth_params = dict(parse_qsl(content))
|
||||||
token = oauth.Token(
|
token = oauth.Token(
|
||||||
|
|||||||
@@ -36,6 +36,17 @@ REQUEST_TOKEN_URL = 'https://www.google.com/accounts/OAuthGetRequestToken?domain
|
|||||||
AUTHORIZE_URL = 'https://www.google.com/buzz/api/auth/OAuthAuthorizeToken?domain=anonymous&scope=https://www.googleapis.com/auth/buzz'
|
AUTHORIZE_URL = 'https://www.google.com/buzz/api/auth/OAuthAuthorizeToken?domain=anonymous&scope=https://www.googleapis.com/auth/buzz'
|
||||||
ACCESS_TOKEN_URL = 'https://www.google.com/accounts/OAuthGetAccessToken'
|
ACCESS_TOKEN_URL = 'https://www.google.com/accounts/OAuthGetAccessToken'
|
||||||
|
|
||||||
|
|
||||||
|
class Error(Exception):
|
||||||
|
"""Base error for this module."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class RequestError(Error):
|
||||||
|
"""Request returned failure or unexpected data."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
# TODO(ade) This class is really a BuzzGaeBuilder. Rename it.
|
# TODO(ade) This class is really a BuzzGaeBuilder. Rename it.
|
||||||
class BuzzGaeClient(object):
|
class BuzzGaeClient(object):
|
||||||
def __init__(self, consumer_key='anonymous', consumer_secret='anonymous'):
|
def __init__(self, consumer_key='anonymous', consumer_secret='anonymous'):
|
||||||
@@ -49,7 +60,7 @@ class BuzzGaeClient(object):
|
|||||||
|
|
||||||
if resp['status'] != '200':
|
if resp['status'] != '200':
|
||||||
logging.warn('Request: %s failed with status: %s. Content was: %s' % (url, resp['status'], content))
|
logging.warn('Request: %s failed with status: %s. Content was: %s' % (url, resp['status'], content))
|
||||||
raise Exception('Invalid response %s.' % resp['status'])
|
raise RequestError('Invalid response %s.' % resp['status'])
|
||||||
return resp, content
|
return resp, content
|
||||||
|
|
||||||
def get_request_token(self, callback_url, display_name = None):
|
def get_request_token(self, callback_url, display_name = None):
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ These tests are read-only in order to ensure they're repeatable. They also
|
|||||||
only work with publicly visible data in order to avoid dealing with OAuth.
|
only work with publicly visible data in order to avoid dealing with OAuth.
|
||||||
"""
|
"""
|
||||||
import httplib2
|
import httplib2
|
||||||
|
import pprint
|
||||||
|
|
||||||
__author__ = 'ade@google.com (Ade Oshineye)'
|
__author__ = 'ade@google.com (Ade Oshineye)'
|
||||||
|
|
||||||
@@ -18,6 +19,7 @@ import httplib2
|
|||||||
import logging
|
import logging
|
||||||
import pickle
|
import pickle
|
||||||
import os
|
import os
|
||||||
|
import time
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
# TODO(ade) Remove this mock once the bug in the discovery document is fixed
|
# TODO(ade) Remove this mock once the bug in the discovery document is fixed
|
||||||
@@ -34,29 +36,43 @@ class HttpMock(object):
|
|||||||
return httplib2.Response(self.headers), self.data
|
return httplib2.Response(self.headers), self.data
|
||||||
|
|
||||||
class BuzzFunctionalTest(unittest.TestCase):
|
class BuzzFunctionalTest(unittest.TestCase):
|
||||||
|
def test_can_get_specific_activity(self):
|
||||||
|
buzz = build('buzz', 'v1')
|
||||||
|
activity = buzz.activities().get(userId='105037104815911535953',
|
||||||
|
postId='B:z12sspviqyakfvye123wehng0muwz5jzq04').execute()
|
||||||
|
|
||||||
|
self.assertTrue(activity is not None)
|
||||||
|
|
||||||
|
def test_can_get_specific_activity_with_tag_id(self):
|
||||||
|
buzz = build('buzz', 'v1')
|
||||||
|
activity = buzz.activities().get(userId='105037104815911535953',
|
||||||
|
postId='tag:google.com,2010:buzz:z13ptnw5usmnv15ey22fzlswnuqoebasu').execute()
|
||||||
|
|
||||||
|
self.assertTrue(activity is not None)
|
||||||
|
|
||||||
def test_can_get_buzz_activities_with_many_params(self):
|
def test_can_get_buzz_activities_with_many_params(self):
|
||||||
buzz = build('buzz', 'v1')
|
buzz = build('buzz', 'v1')
|
||||||
max_results = 2
|
max_results = 2
|
||||||
actcol = buzz.activities()
|
activities_command = buzz.activities()
|
||||||
activities = actcol.list(userId='googlebuzz', scope='@self',
|
activities = activities_command.list(userId='googlebuzz', scope='@self',
|
||||||
max_comments=max_results*2 ,max_liked=max_results*3,
|
max_comments=max_results*2 ,max_liked=max_results*3,
|
||||||
max_results=max_results).execute()
|
max_results=max_results).execute()
|
||||||
activity_count = len(activities['items'])
|
activity_count = len(activities['items'])
|
||||||
self.assertEquals(max_results, activity_count)
|
self.assertEquals(max_results, activity_count)
|
||||||
|
|
||||||
activities = actcol.list_next(activities).execute()
|
activities = activities_command.list_next(activities).execute()
|
||||||
activity_count = len(activities['items'])
|
activity_count = len(activities['items'])
|
||||||
self.assertEquals(max_results, activity_count)
|
self.assertEquals(max_results, activity_count)
|
||||||
|
|
||||||
def test_can_get_multiple_pages_of_buzz_activities(self):
|
def test_can_get_multiple_pages_of_buzz_activities(self):
|
||||||
buzz = build('buzz', 'v1')
|
buzz = build('buzz', 'v1')
|
||||||
max_results = 2
|
max_results = 2
|
||||||
actcol = buzz.activities()
|
activities_command = buzz.activities()
|
||||||
|
|
||||||
activities = actcol.list(userId='adewale', scope='@self',
|
activities = activities_command.list(userId='adewale', scope='@self',
|
||||||
max_results=max_results).execute()
|
max_results=max_results).execute()
|
||||||
for count in range(10):
|
for count in range(10):
|
||||||
activities = actcol.list_next(activities).execute()
|
activities = activities_command.list_next(activities).execute()
|
||||||
activity_count = len(activities['items'])
|
activity_count = len(activities['items'])
|
||||||
self.assertEquals(max_results, activity_count, 'Failed after %s pages' % str(count))
|
self.assertEquals(max_results, activity_count, 'Failed after %s pages' % str(count))
|
||||||
|
|
||||||
@@ -86,6 +102,31 @@ class BuzzFunctionalTest(unittest.TestCase):
|
|||||||
self.assertEquals('111062888259659218284', person['id'])
|
self.assertEquals('111062888259659218284', person['id'])
|
||||||
self.assertEquals('http://www.google.com/profiles/googlebuzz', person['profileUrl'])
|
self.assertEquals('http://www.google.com/profiles/googlebuzz', person['profileUrl'])
|
||||||
|
|
||||||
|
def test_can_get_followees_of_user(self):
|
||||||
|
buzz = build('buzz', 'v1')
|
||||||
|
expected_followees = 30
|
||||||
|
following = buzz.people().list(userId='googlebuzz', groupId='@following', max_results=expected_followees).execute()
|
||||||
|
|
||||||
|
self.assertEquals(expected_followees, following['totalResults'])
|
||||||
|
self.assertEquals(expected_followees, len(following['entry']))
|
||||||
|
|
||||||
|
def test_can_efficiently_get_follower_count_of_user(self):
|
||||||
|
buzz = build('buzz', 'v1')
|
||||||
|
|
||||||
|
# Restricting max_results to 1 means only a tiny amount of data comes back but the totalResults still has the total.
|
||||||
|
following = buzz.people().list(userId='googlebuzz', groupId='@followers', max_results=1).execute()
|
||||||
|
|
||||||
|
# @googlebuzz has a large but fluctuating number of followers
|
||||||
|
# It is sufficient if the result is bigger than 10, 000
|
||||||
|
follower_count = following['totalResults']
|
||||||
|
self.assertTrue(follower_count > 10000, follower_count)
|
||||||
|
|
||||||
|
def test_follower_count_is_zero_for_user_with_hidden_follower_count(self):
|
||||||
|
buzz = build('buzz', 'v1')
|
||||||
|
following = buzz.people().list(userId='adewale', groupId='@followers').execute()
|
||||||
|
|
||||||
|
self.assertEquals(0, following['totalResults'])
|
||||||
|
|
||||||
|
|
||||||
class BuzzAuthenticatedFunctionalTest(unittest.TestCase):
|
class BuzzAuthenticatedFunctionalTest(unittest.TestCase):
|
||||||
def __init__(self, method_name):
|
def __init__(self, method_name):
|
||||||
@@ -97,8 +138,36 @@ class BuzzAuthenticatedFunctionalTest(unittest.TestCase):
|
|||||||
|
|
||||||
self.http = credentials.authorize(httplib2.Http())
|
self.http = credentials.authorize(httplib2.Http())
|
||||||
|
|
||||||
def test_can_list_groups_belonging_to_user(self):
|
def test_can_create_activity(self):
|
||||||
# TODO(ade) This should not require authentication. It does because we're adding a spurious @self to the URL
|
buzz = build('buzz', 'v1', http=self.http)
|
||||||
|
|
||||||
|
activity = buzz.activities().insert(userId='@me', body={
|
||||||
|
'title': 'Testing insert',
|
||||||
|
'object': {
|
||||||
|
'content': u'Just a short note to show that insert is working. ?',
|
||||||
|
'type': 'note'}
|
||||||
|
}
|
||||||
|
).execute()
|
||||||
|
self.assertTrue(activity is not None)
|
||||||
|
|
||||||
|
def test_can_create_private_activity(self):
|
||||||
|
buzz = build('buzz', 'v1', http=self.http)
|
||||||
|
|
||||||
|
activity = buzz.activities().insert(userId='@me', body={
|
||||||
|
'title': 'Testing insert',
|
||||||
|
'object': {
|
||||||
|
'content': 'This is a private post.'
|
||||||
|
},
|
||||||
|
'visibility': {
|
||||||
|
'entries': [
|
||||||
|
{ 'id': 'tag:google.com,2010:buzz-group:108242092577082601423:13' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
).execute()
|
||||||
|
self.assertTrue(activity is not None)
|
||||||
|
|
||||||
|
def test_can_identify_number_of_groups_belonging_to_user(self):
|
||||||
buzz = build('buzz', 'v1', http=self.http)
|
buzz = build('buzz', 'v1', http=self.http)
|
||||||
groups = buzz.groups().list(userId='108242092577082601423').execute()
|
groups = buzz.groups().list(userId='108242092577082601423').execute()
|
||||||
|
|
||||||
@@ -106,14 +175,75 @@ class BuzzAuthenticatedFunctionalTest(unittest.TestCase):
|
|||||||
expected_default_number_of_groups = 4
|
expected_default_number_of_groups = 4
|
||||||
self.assertEquals(expected_default_number_of_groups, len(groups['items']))
|
self.assertEquals(expected_default_number_of_groups, len(groups['items']))
|
||||||
|
|
||||||
def IGNORE__test_can_get_followees_of_user(self):
|
def IGNORE__test_can_like_activity(self):
|
||||||
# This currently fails with:
|
|
||||||
# Attempting to access self view of a different user.
|
|
||||||
# and URL:
|
|
||||||
buzz = build('buzz', 'v1', http=self.http)
|
buzz = build('buzz', 'v1', http=self.http)
|
||||||
following = buzz.groups().get(userId='googlebuzz', groupId='@following').execute()
|
activity = buzz.activities().insert(userId='@me', body={
|
||||||
|
'title': 'Testing insert',
|
||||||
|
'object': {
|
||||||
|
'content': u'Just a short note to show that insert is working. ?',
|
||||||
|
'type': 'note'}
|
||||||
|
}
|
||||||
|
).execute()
|
||||||
|
pprint.pprint(activity)
|
||||||
|
id = activity['id']
|
||||||
|
likers = buzz.people().liked(userId='105037104815911535953', postId=id, groupId='@liked', scope='@self').execute()
|
||||||
|
# Todo(ade) Insert the new liker once the Buzz back-end bug is fixed
|
||||||
|
|
||||||
self.assertEquals(17, len(following))
|
def test_can_comment_on_activity(self):
|
||||||
|
buzz = build('buzz', 'v1', http=self.http)
|
||||||
|
|
||||||
|
activity = buzz.activities().insert(userId='@me', body={
|
||||||
|
'title': 'A new activity',
|
||||||
|
'object': {
|
||||||
|
'content': u'The body of the new activity',
|
||||||
|
'type': 'note'}
|
||||||
|
}
|
||||||
|
).execute()
|
||||||
|
|
||||||
|
id = activity['id']
|
||||||
|
comment = buzz.comments().insert(userId='@me', postId=id, body={
|
||||||
|
"content": "A comment on the new activity"
|
||||||
|
}).execute()
|
||||||
|
|
||||||
|
def IGNORE__test_can_list_groups_belonging_to_user(self):
|
||||||
|
# TODO(ade) Uncomment this test once the related Buzz back-end bug is fixed
|
||||||
|
buzz = build('buzz', 'v1', http=self.http)
|
||||||
|
groups = buzz.groups().list(userId='108242092577082601423').execute()
|
||||||
|
pprint.pprint(groups)
|
||||||
|
|
||||||
|
group = buzz.groups().get(userId='108242092577082601423', groupId='G:108242092577082601423:15').execute()
|
||||||
|
self.assertEquals('G:108242092577082601423:15', group['id'], group)
|
||||||
|
|
||||||
|
group = buzz.groups().get(userId='108242092577082601423', groupId='G:108242092577082601423:14').execute()
|
||||||
|
self.assertEquals('G:108242092577082601423:14', group['id'], group)
|
||||||
|
|
||||||
|
group = buzz.groups().get(userId='108242092577082601423', groupId='G:108242092577082601423:13').execute()
|
||||||
|
self.assertEquals('G:108242092577082601423:13', group['id'], group)
|
||||||
|
|
||||||
|
group = buzz.groups().get(userId='108242092577082601423', groupId='G:108242092577082601423:6').execute()
|
||||||
|
self.assertEquals('G:108242092577082601423:6', group['id'], group)
|
||||||
|
|
||||||
|
group = buzz.groups().get(userId='108242092577082601423', groupId='G:108242092577082601423:9999999').execute()
|
||||||
|
self.assertEquals(None, group, group)
|
||||||
|
|
||||||
|
def test_can_delete_activity(self):
|
||||||
|
buzz = build('buzz', 'v1', http=self.http)
|
||||||
|
|
||||||
|
activity = buzz.activities().insert(userId='@me', body={
|
||||||
|
'title': 'Activity to be deleted',
|
||||||
|
'object': {
|
||||||
|
'content': u'Created this activity so that it can be deleted.',
|
||||||
|
'type': 'note'}
|
||||||
|
}
|
||||||
|
).execute()
|
||||||
|
id = activity['id']
|
||||||
|
|
||||||
|
buzz.activities().delete(scope='@self', userId='@me', postId=id).execute()
|
||||||
|
time.sleep(2)
|
||||||
|
|
||||||
|
activity_url = activity['links']['self'][0]['href']
|
||||||
|
resp, content = self.http.request(activity_url, 'GET')
|
||||||
|
self.assertEquals(404, resp.status)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
@@ -55,9 +55,9 @@ from gettext import gettext as _
|
|||||||
import socket
|
import socket
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from httplib2 import socks
|
from httplib2 import socks
|
||||||
except ImportError:
|
except ImportError:
|
||||||
socks = None
|
socks = None
|
||||||
|
|
||||||
# Build the appropriate socket wrapper for ssl
|
# Build the appropriate socket wrapper for ssl
|
||||||
try:
|
try:
|
||||||
@@ -83,7 +83,7 @@ def has_timeout(timeout): # python 2.6
|
|||||||
__all__ = ['Http', 'Response', 'ProxyInfo', 'HttpLib2Error',
|
__all__ = ['Http', 'Response', 'ProxyInfo', 'HttpLib2Error',
|
||||||
'RedirectMissingLocation', 'RedirectLimit', 'FailedToDecompressContent',
|
'RedirectMissingLocation', 'RedirectLimit', 'FailedToDecompressContent',
|
||||||
'UnimplementedDigestAuthOptionError', 'UnimplementedHmacDigestAuthOptionError',
|
'UnimplementedDigestAuthOptionError', 'UnimplementedHmacDigestAuthOptionError',
|
||||||
'debuglevel']
|
'debuglevel', 'ProxiesUnavailableError']
|
||||||
|
|
||||||
|
|
||||||
# The httplib debug level, set to a non-zero value to get debug output
|
# The httplib debug level, set to a non-zero value to get debug output
|
||||||
@@ -125,6 +125,7 @@ class UnimplementedHmacDigestAuthOptionError(HttpLib2ErrorWithResponse): pass
|
|||||||
|
|
||||||
class RelativeURIError(HttpLib2Error): pass
|
class RelativeURIError(HttpLib2Error): pass
|
||||||
class ServerNotFoundError(HttpLib2Error): pass
|
class ServerNotFoundError(HttpLib2Error): pass
|
||||||
|
class ProxiesUnavailableError(HttpLib2Error): pass
|
||||||
|
|
||||||
# Open Items:
|
# Open Items:
|
||||||
# -----------
|
# -----------
|
||||||
@@ -721,6 +722,9 @@ class HTTPConnectionWithTimeout(httplib.HTTPConnection):
|
|||||||
def connect(self):
|
def connect(self):
|
||||||
"""Connect to the host and port specified in __init__."""
|
"""Connect to the host and port specified in __init__."""
|
||||||
# Mostly verbatim from httplib.py.
|
# Mostly verbatim from httplib.py.
|
||||||
|
if self.proxy_info and socks is None:
|
||||||
|
raise ProxiesUnavailableError(
|
||||||
|
'Proxy support missing but proxy use was requested!')
|
||||||
msg = "getaddrinfo returns an empty list"
|
msg = "getaddrinfo returns an empty list"
|
||||||
for res in socket.getaddrinfo(self.host, self.port, 0,
|
for res in socket.getaddrinfo(self.host, self.port, 0,
|
||||||
socket.SOCK_STREAM):
|
socket.SOCK_STREAM):
|
||||||
|
|||||||
@@ -41,12 +41,13 @@ mainly to merge bug fixes found in Sourceforge
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import socket
|
import socket
|
||||||
|
|
||||||
|
if getattr(socket, 'socket', None) is None:
|
||||||
|
raise ImportError('socket.socket missing, proxy support unusable')
|
||||||
|
|
||||||
import struct
|
import struct
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
if not hasattr(socket, 'socket'):
|
|
||||||
raise ImportError("Running on App Engine?")
|
|
||||||
|
|
||||||
PROXY_TYPE_SOCKS4 = 1
|
PROXY_TYPE_SOCKS4 = 1
|
||||||
PROXY_TYPE_SOCKS5 = 2
|
PROXY_TYPE_SOCKS5 = 2
|
||||||
PROXY_TYPE_HTTP = 3
|
PROXY_TYPE_HTTP = 3
|
||||||
|
|||||||
6
samples/api-python-client-doc/README
Normal file
6
samples/api-python-client-doc/README
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
This sample is the code that drives
|
||||||
|
|
||||||
|
http://api-python-client-doc.appspot.com/
|
||||||
|
|
||||||
|
It is an application that serves up the Python help documentation
|
||||||
|
for each API.
|
||||||
66
samples/buzz/buzz.py
Normal file
66
samples/buzz/buzz.py
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
#!/usr/bin/python2.4
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# Copyright 2010 Google Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
"""Simple command-line example for Buzz.
|
||||||
|
|
||||||
|
Command-line application that retrieves the users
|
||||||
|
latest content and then adds a new entry.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__author__ = 'jcgregorio@google.com (Joe Gregorio)'
|
||||||
|
|
||||||
|
from apiclient.discovery import build
|
||||||
|
|
||||||
|
import httplib2
|
||||||
|
import pickle
|
||||||
|
import pprint
|
||||||
|
|
||||||
|
# Uncomment the next line to get very detailed logging
|
||||||
|
# httplib2.debuglevel = 4
|
||||||
|
|
||||||
|
def main():
|
||||||
|
f = open("buzz.dat", "r")
|
||||||
|
credentials = pickle.loads(f.read())
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
http = httplib2.Http()
|
||||||
|
http = credentials.authorize(http)
|
||||||
|
|
||||||
|
p = build("buzz", "v1", http=http)
|
||||||
|
activities = p.activities()
|
||||||
|
|
||||||
|
# Retrieve the first two activities
|
||||||
|
activitylist = activities.list(max_results='2', scope='@self', userId='@me').execute()
|
||||||
|
print "Retrieved the first two activities"
|
||||||
|
|
||||||
|
# Retrieve the next two activities
|
||||||
|
activitylist = activities.list_next(activitylist).execute()
|
||||||
|
print "Retrieved the next two activities"
|
||||||
|
|
||||||
|
# Add a new activity
|
||||||
|
new_activity_body = {
|
||||||
|
'title': 'Testing insert',
|
||||||
|
'object': {
|
||||||
|
'content': u'Just a short note to show that insert is working. ☄',
|
||||||
|
'type': 'note'}
|
||||||
|
}
|
||||||
|
activity = activities.insert(userId='@me', body=new_activity_body).execute()
|
||||||
|
print "Added a new activity"
|
||||||
|
|
||||||
|
activitylist = activities.list(max_results='2', scope='@self', userId='@me').execute()
|
||||||
|
|
||||||
|
# Add a comment to that activity
|
||||||
|
comment_body = {
|
||||||
|
"content": "This is a comment"
|
||||||
|
}
|
||||||
|
item = activitylist['items'][0]
|
||||||
|
comment = p.comments().insert(
|
||||||
|
userId=item['actor']['id'], postId=item['id'], body=comment_body
|
||||||
|
).execute()
|
||||||
|
print 'Added a comment to the new activity'
|
||||||
|
pprint.pprint(comment)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
@@ -22,11 +22,6 @@ other example apps in the same directory.
|
|||||||
|
|
||||||
__author__ = 'jcgregorio@google.com (Joe Gregorio)'
|
__author__ = 'jcgregorio@google.com (Joe Gregorio)'
|
||||||
|
|
||||||
# Enable this sample to be run from the top-level directory
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
sys.path.insert(0, os.getcwd())
|
|
||||||
|
|
||||||
from apiclient.discovery import build
|
from apiclient.discovery import build
|
||||||
from apiclient.oauth import FlowThreeLegged
|
from apiclient.oauth import FlowThreeLegged
|
||||||
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
#!/usr/bin/python2.4
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
# Copyright 2010 Google Inc. All Rights Reserved.
|
|
||||||
|
|
||||||
"""Simple command-line example for Buzz.
|
|
||||||
|
|
||||||
Command-line application that retrieves the users
|
|
||||||
latest content and then adds a new entry.
|
|
||||||
"""
|
|
||||||
|
|
||||||
__author__ = 'jcgregorio@google.com (Joe Gregorio)'
|
|
||||||
|
|
||||||
# Enable this sample to be run from the top-level directory
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
sys.path.insert(0, os.getcwd())
|
|
||||||
|
|
||||||
from apiclient.discovery import build
|
|
||||||
|
|
||||||
import httplib2
|
|
||||||
# httplib2.debuglevel = 4
|
|
||||||
import pickle
|
|
||||||
import pprint
|
|
||||||
|
|
||||||
def main():
|
|
||||||
f = open("buzz.dat", "r")
|
|
||||||
credentials = pickle.loads(f.read())
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
http = httplib2.Http()
|
|
||||||
http = credentials.authorize(http)
|
|
||||||
|
|
||||||
p = build("buzz", "v1", http=http)
|
|
||||||
activities = p.activities()
|
|
||||||
activitylist = activities.list(max_results='2', scope='@self', userId='@me').execute()
|
|
||||||
print activitylist['items'][0]['title']
|
|
||||||
activitylist = activities.list_next(activitylist).execute()
|
|
||||||
print activitylist['items'][0]['title']
|
|
||||||
|
|
||||||
activity = activities.insert(userId='@me', body={
|
|
||||||
'title': 'Testing insert',
|
|
||||||
'object': {
|
|
||||||
'content': u'Just a short note to show that insert is working. ☄',
|
|
||||||
'type': 'note'}
|
|
||||||
}
|
|
||||||
).execute()
|
|
||||||
pprint.pprint(activity)
|
|
||||||
print
|
|
||||||
print 'Just created: ', activity['links']['alternate'][0]['href']
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
#!/usr/bin/python2.4
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
# Copyright 2010 Google Inc. All Rights Reserved.
|
|
||||||
|
|
||||||
"""Simple command-line example for Buzz.
|
|
||||||
|
|
||||||
Command-line application that retrieves the users
|
|
||||||
latest content and then adds a new entry.
|
|
||||||
"""
|
|
||||||
|
|
||||||
__author__ = 'jcgregorio@google.com (Joe Gregorio)'
|
|
||||||
|
|
||||||
|
|
||||||
from apiclient.discovery import build
|
|
||||||
|
|
||||||
import httplib2
|
|
||||||
import pickle
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
f = open("moderator.dat", "r")
|
|
||||||
credentials = pickle.loads(f.read())
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
http = httplib2.Http()
|
|
||||||
http = credentials.authorize(http)
|
|
||||||
|
|
||||||
p = build("moderator", "v1", http=http)
|
|
||||||
print p.submissions().list(seriesId="7035", topicId="64").execute()
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
||||||
0
samples/django_sample/__init__.py
Normal file
0
samples/django_sample/__init__.py
Normal file
0
samples/django_sample/buzz/__init__.py
Normal file
0
samples/django_sample/buzz/__init__.py
Normal file
62
samples/django_sample/buzz/models.py
Normal file
62
samples/django_sample/buzz/models.py
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import pickle
|
||||||
|
import base64
|
||||||
|
|
||||||
|
import apiclient.oauth
|
||||||
|
from django.contrib import admin
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
# Create your models here.
|
||||||
|
|
||||||
|
class OAuthCredentialsField(models.Field):
|
||||||
|
|
||||||
|
__metaclass__ = models.SubfieldBase
|
||||||
|
|
||||||
|
def db_type(self):
|
||||||
|
return 'VARCHAR'
|
||||||
|
|
||||||
|
def to_python(self, value):
|
||||||
|
if value is None:
|
||||||
|
return None
|
||||||
|
if isinstance(value, apiclient.oauth.Credentials):
|
||||||
|
return value
|
||||||
|
return pickle.loads(base64.b64decode(value))
|
||||||
|
|
||||||
|
def get_db_prep_value(self, value):
|
||||||
|
return base64.b64encode(pickle.dumps(value))
|
||||||
|
|
||||||
|
class FlowThreeLeggedField(models.Field):
|
||||||
|
|
||||||
|
__metaclass__ = models.SubfieldBase
|
||||||
|
|
||||||
|
def db_type(self):
|
||||||
|
return 'VARCHAR'
|
||||||
|
|
||||||
|
def to_python(self, value):
|
||||||
|
print "In to_python", value
|
||||||
|
if value is None:
|
||||||
|
return None
|
||||||
|
if isinstance(value, apiclient.oauth.FlowThreeLegged):
|
||||||
|
return value
|
||||||
|
return pickle.loads(base64.b64decode(value))
|
||||||
|
|
||||||
|
def get_db_prep_value(self, value):
|
||||||
|
return base64.b64encode(pickle.dumps(value))
|
||||||
|
|
||||||
|
# The Flow could also be stored in memcache since it is short lived.
|
||||||
|
class Flow(models.Model):
|
||||||
|
id = models.ForeignKey(User, primary_key=True)
|
||||||
|
flow = FlowThreeLeggedField()
|
||||||
|
|
||||||
|
class Credential(models.Model):
|
||||||
|
id = models.ForeignKey(User, primary_key=True)
|
||||||
|
credential = OAuthCredentialsField()
|
||||||
|
|
||||||
|
class CredentialAdmin(admin.ModelAdmin):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class FlowAdmin(admin.ModelAdmin):
|
||||||
|
pass
|
||||||
|
|
||||||
|
admin.site.register(Credential, CredentialAdmin)
|
||||||
|
admin.site.register(Flow, FlowAdmin)
|
||||||
23
samples/django_sample/buzz/tests.py
Normal file
23
samples/django_sample/buzz/tests.py
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
"""
|
||||||
|
This file demonstrates two different styles of tests (one doctest and one
|
||||||
|
unittest). These will both pass when you run "manage.py test".
|
||||||
|
|
||||||
|
Replace these with more appropriate tests for your application.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
class SimpleTest(TestCase):
|
||||||
|
def test_basic_addition(self):
|
||||||
|
"""
|
||||||
|
Tests that 1 + 1 always equals 2.
|
||||||
|
"""
|
||||||
|
self.failUnlessEqual(1 + 1, 2)
|
||||||
|
|
||||||
|
__test__ = {"doctest": """
|
||||||
|
Another way to test that 1 + 1 is equal to 2.
|
||||||
|
|
||||||
|
>>> 1 + 1 == 2
|
||||||
|
True
|
||||||
|
"""}
|
||||||
|
|
||||||
62
samples/django_sample/buzz/views.py
Normal file
62
samples/django_sample/buzz/views.py
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import os
|
||||||
|
import logging
|
||||||
|
import httplib2
|
||||||
|
|
||||||
|
from django.http import HttpResponse
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django_sample.buzz.models import Credential, Flow
|
||||||
|
from apiclient.discovery import build
|
||||||
|
from apiclient.oauth import FlowThreeLegged
|
||||||
|
from django.http import HttpResponseRedirect
|
||||||
|
from django.shortcuts import render_to_response
|
||||||
|
|
||||||
|
print os.environ
|
||||||
|
STEP2_URI = 'http://localhost:8000/auth_return'
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def index(request):
|
||||||
|
try:
|
||||||
|
c = Credential.objects.get(id=request.user)
|
||||||
|
http = httplib2.Http()
|
||||||
|
http = c.credential.authorize(http)
|
||||||
|
p = build("buzz", "v1", http=http)
|
||||||
|
activities = p.activities()
|
||||||
|
activitylist = activities.list(scope='@consumption',
|
||||||
|
userId='@me').execute()
|
||||||
|
logging.info(activitylist)
|
||||||
|
|
||||||
|
return render_to_response('buzz/welcome.html', {
|
||||||
|
'activitylist': activitylist,
|
||||||
|
})
|
||||||
|
|
||||||
|
except Credential.DoesNotExist:
|
||||||
|
p = build("buzz", "v1")
|
||||||
|
flow = FlowThreeLegged(p.auth_discovery(),
|
||||||
|
consumer_key='anonymous',
|
||||||
|
consumer_secret='anonymous',
|
||||||
|
user_agent='google-api-client-python-buzz-django/1.0',
|
||||||
|
domain='anonymous',
|
||||||
|
scope='https://www.googleapis.com/auth/buzz',
|
||||||
|
xoauth_displayname='Django Example Web App')
|
||||||
|
|
||||||
|
authorize_url = flow.step1_get_authorize_url(STEP2_URI)
|
||||||
|
f = Flow(id=request.user, flow=flow)
|
||||||
|
f.save()
|
||||||
|
return HttpResponseRedirect(authorize_url)
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def auth_return(request):
|
||||||
|
try:
|
||||||
|
f = Flow.objects.get(id=request.user)
|
||||||
|
print f
|
||||||
|
print f.flow
|
||||||
|
print dir(f.flow)
|
||||||
|
print type(f.flow)
|
||||||
|
credential = f.flow.step2_exchange(request.REQUEST)
|
||||||
|
c = Credential(id=request.user, credential=credential)
|
||||||
|
c.save()
|
||||||
|
f.delete()
|
||||||
|
return HttpResponseRedirect("/")
|
||||||
|
except Flow.DoesNotExist:
|
||||||
|
pass
|
||||||
11
samples/django_sample/manage.py
Executable file
11
samples/django_sample/manage.py
Executable file
@@ -0,0 +1,11 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
from django.core.management import execute_manager
|
||||||
|
try:
|
||||||
|
import settings # Assumed to be in the same directory.
|
||||||
|
except ImportError:
|
||||||
|
import sys
|
||||||
|
sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
execute_manager(settings)
|
||||||
83
samples/django_sample/settings.py
Normal file
83
samples/django_sample/settings.py
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
# Django settings for django_sample project.
|
||||||
|
import os
|
||||||
|
|
||||||
|
DEBUG = True
|
||||||
|
TEMPLATE_DEBUG = DEBUG
|
||||||
|
|
||||||
|
ADMINS = (
|
||||||
|
# ('Your Name', 'your_email@domain.com'),
|
||||||
|
)
|
||||||
|
|
||||||
|
MANAGERS = ADMINS
|
||||||
|
|
||||||
|
DATABASE_ENGINE = 'sqlite3' # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
|
||||||
|
DATABASE_NAME = 'database.sqlite3' # Or path to database file if using sqlite3.
|
||||||
|
DATABASE_USER = '' # Not used with sqlite3.
|
||||||
|
DATABASE_PASSWORD = '' # Not used with sqlite3.
|
||||||
|
DATABASE_HOST = '' # Set to empty string for localhost. Not used with sqlite3.
|
||||||
|
DATABASE_PORT = '' # Set to empty string for default. Not used with sqlite3.
|
||||||
|
|
||||||
|
# Local time zone for this installation. Choices can be found here:
|
||||||
|
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
|
||||||
|
# although not all choices may be available on all operating systems.
|
||||||
|
# If running in a Windows environment this must be set to the same as your
|
||||||
|
# system time zone.
|
||||||
|
TIME_ZONE = 'America/New_York'
|
||||||
|
|
||||||
|
# Language code for this installation. All choices can be found here:
|
||||||
|
# http://www.i18nguy.com/unicode/language-identifiers.html
|
||||||
|
LANGUAGE_CODE = 'en-us'
|
||||||
|
|
||||||
|
SITE_ID = 1
|
||||||
|
|
||||||
|
# If you set this to False, Django will make some optimizations so as not
|
||||||
|
# to load the internationalization machinery.
|
||||||
|
USE_I18N = True
|
||||||
|
|
||||||
|
# Absolute path to the directory that holds media.
|
||||||
|
# Example: "/home/media/media.lawrence.com/"
|
||||||
|
MEDIA_ROOT = ''
|
||||||
|
|
||||||
|
# URL that handles the media served from MEDIA_ROOT. Make sure to use a
|
||||||
|
# trailing slash if there is a path component (optional in other cases).
|
||||||
|
# Examples: "http://media.lawrence.com", "http://example.com/media/"
|
||||||
|
MEDIA_URL = ''
|
||||||
|
|
||||||
|
# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a
|
||||||
|
# trailing slash.
|
||||||
|
# Examples: "http://foo.com/media/", "/media/".
|
||||||
|
ADMIN_MEDIA_PREFIX = '/media/'
|
||||||
|
|
||||||
|
# Make this unique, and don't share it with anybody.
|
||||||
|
SECRET_KEY = '_=9hq-$t_uv1ckf&s!y2$9g$1dm*6p1cl%*!^mg=7gr)!zj32d'
|
||||||
|
|
||||||
|
# List of callables that know how to import templates from various sources.
|
||||||
|
TEMPLATE_LOADERS = (
|
||||||
|
'django.template.loaders.filesystem.load_template_source',
|
||||||
|
'django.template.loaders.app_directories.load_template_source',
|
||||||
|
# 'django.template.loaders.eggs.load_template_source',
|
||||||
|
)
|
||||||
|
|
||||||
|
MIDDLEWARE_CLASSES = (
|
||||||
|
'django.middleware.common.CommonMiddleware',
|
||||||
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||||
|
)
|
||||||
|
|
||||||
|
ROOT_URLCONF = 'django_sample.urls'
|
||||||
|
|
||||||
|
TEMPLATE_DIRS = (
|
||||||
|
# Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
|
||||||
|
# Always use forward slashes, even on Windows.
|
||||||
|
# Don't forget to use absolute paths, not relative paths.
|
||||||
|
os.path.join(os.path.dirname(__file__), 'templates')
|
||||||
|
)
|
||||||
|
|
||||||
|
INSTALLED_APPS = (
|
||||||
|
'django.contrib.admin',
|
||||||
|
'django.contrib.auth',
|
||||||
|
'django.contrib.contenttypes',
|
||||||
|
'django.contrib.sessions',
|
||||||
|
'django.contrib.sites',
|
||||||
|
'django_sample.buzz'
|
||||||
|
)
|
||||||
BIN
samples/django_sample/static/go.png
Normal file
BIN
samples/django_sample/static/go.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
23
samples/django_sample/templates/buzz/login.html
Normal file
23
samples/django_sample/templates/buzz/login.html
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{% block content %}
|
||||||
|
|
||||||
|
{% if form.errors %}
|
||||||
|
<p>Your username and password didn't match. Please try again.</p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<form method="post" action="{% url django.contrib.auth.views.login %}">
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td>{{ form.username.label_tag }}</td>
|
||||||
|
<td>{{ form.username }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ form.password.label_tag }}</td>
|
||||||
|
<td>{{ form.password }}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<input type="submit" value="login" />
|
||||||
|
<input type="hidden" name="next" value="{{ next }}" />
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
33
samples/django_sample/templates/buzz/welcome.html
Normal file
33
samples/django_sample/templates/buzz/welcome.html
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Buzz Stuff</title>
|
||||||
|
<style type=text/css>
|
||||||
|
td { vertical-align: top; padding: 0.5em }
|
||||||
|
img { border:0 }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<table border=0>
|
||||||
|
{% for item in activitylist.items %}
|
||||||
|
<tr valign=top>
|
||||||
|
<td>
|
||||||
|
{% if item.actor.thumbnailUrl %}
|
||||||
|
<a href="{{ item.actor.profileUrl }}">
|
||||||
|
<img src="{{ item.actor.thumbnailUrl }}">
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
<br>
|
||||||
|
<a href="{{ item.actor.profileUrl }}">{{ item.actor.name }}</a></td>
|
||||||
|
<td>
|
||||||
|
{{ item.object.content|safe }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="{{ item.object.links.alternate.0.href }}"><img
|
||||||
|
src="/static/go.png"></a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
25
samples/django_sample/urls.py
Normal file
25
samples/django_sample/urls.py
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import os
|
||||||
|
from django.conf.urls.defaults import *
|
||||||
|
|
||||||
|
# Uncomment the next two lines to enable the admin:
|
||||||
|
from django.contrib import admin
|
||||||
|
admin.autodiscover()
|
||||||
|
|
||||||
|
urlpatterns = patterns('',
|
||||||
|
# Example:
|
||||||
|
(r'^$', 'django_sample.buzz.views.index'),
|
||||||
|
(r'^auth_return', 'django_sample.buzz.views.auth_return'),
|
||||||
|
|
||||||
|
# Uncomment the admin/doc line below and add 'django.contrib.admindocs'
|
||||||
|
# to INSTALLED_APPS to enable admin documentation:
|
||||||
|
# (r'^admin/doc/', include('django.contrib.admindocs.urls')),
|
||||||
|
|
||||||
|
# Uncomment the next line to enable the admin:
|
||||||
|
(r'^admin/', include(admin.site.urls)),
|
||||||
|
(r'^accounts/login/$', 'django.contrib.auth.views.login',
|
||||||
|
{'template_name': 'buzz/login.html'}),
|
||||||
|
|
||||||
|
(r'^static/(?P<path>.*)$', 'django.views.static.serve',
|
||||||
|
{'document_root': os.path.join(os.path.dirname(__file__), 'static')
|
||||||
|
}),
|
||||||
|
)
|
||||||
75
samples/moderator/moderator.py
Normal file
75
samples/moderator/moderator.py
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
#!/usr/bin/python2.4
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# Copyright 2010 Google Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
"""Simple command-line example for Buzz.
|
||||||
|
|
||||||
|
Command-line application that retrieves the users
|
||||||
|
latest content and then adds a new entry.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__author__ = 'jcgregorio@google.com (Joe Gregorio)'
|
||||||
|
|
||||||
|
|
||||||
|
from apiclient.discovery import build
|
||||||
|
|
||||||
|
import httplib2
|
||||||
|
import pickle
|
||||||
|
|
||||||
|
# Uncomment to get detailed logging
|
||||||
|
# httplib2.debuglevel = 4
|
||||||
|
|
||||||
|
def main():
|
||||||
|
f = open("moderator.dat", "r")
|
||||||
|
credentials = pickle.loads(f.read())
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
http = httplib2.Http()
|
||||||
|
http = credentials.authorize(http)
|
||||||
|
|
||||||
|
p = build("moderator", "v1", http=http)
|
||||||
|
|
||||||
|
series_body = {
|
||||||
|
"description": "Share and rank tips for eating healthily on the cheaps!",
|
||||||
|
"name": "Eating Healthy & Cheap",
|
||||||
|
"videoSubmissionAllowed": False
|
||||||
|
}
|
||||||
|
series = p.series().insert(body=series_body).execute()
|
||||||
|
print "Created a new series"
|
||||||
|
|
||||||
|
topic_body = {
|
||||||
|
"data": {
|
||||||
|
"description": "Share your ideas on eating healthy!",
|
||||||
|
"name": "Ideas",
|
||||||
|
"presenter": "liz"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
topic = p.topics().insert(seriesId=series['id']['seriesId'], body=topic_body).execute()
|
||||||
|
print "Created a new topic"
|
||||||
|
|
||||||
|
submission_body = {
|
||||||
|
"data": {
|
||||||
|
"attachmentUrl": "http://www.youtube.com/watch?v=1a1wyc5Xxpg",
|
||||||
|
"attribution": {
|
||||||
|
"displayName": "Bashan",
|
||||||
|
"location": "Bainbridge Island, WA"
|
||||||
|
},
|
||||||
|
"text": "Charlie Ayers @ Google"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
submission = p.submissions().insert(seriesId=topic['id']['seriesId'],
|
||||||
|
topicId=topic['id']['topicId'], body=submission_body).execute()
|
||||||
|
print "Inserted a new submisson on the topic"
|
||||||
|
|
||||||
|
vote_body = {
|
||||||
|
"data": {
|
||||||
|
"vote": "PLUS"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.votes().insert(seriesId=topic['id']['seriesId'], submissionId=submission['id']['submissionId'], body=vote_body)
|
||||||
|
print "Voted on the submission"
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
@@ -35,6 +35,7 @@ flow = FlowThreeLegged(moderator_discovery,
|
|||||||
user_agent='google-api-client-python-mdrtr-cmdline/1.0',
|
user_agent='google-api-client-python-mdrtr-cmdline/1.0',
|
||||||
domain='anonymous',
|
domain='anonymous',
|
||||||
scope='https://www.googleapis.com/auth/moderator',
|
scope='https://www.googleapis.com/auth/moderator',
|
||||||
|
#scope='tag:google.com,2010:auth/moderator',
|
||||||
xoauth_displayname='Google API Client Example App')
|
xoauth_displayname='Google API Client Example App')
|
||||||
|
|
||||||
authorize_url = flow.step1_get_authorize_url()
|
authorize_url = flow.step1_get_authorize_url()
|
||||||
@@ -65,6 +65,22 @@ class Model(unittest.TestCase):
|
|||||||
self.assertNotEqual(query, '')
|
self.assertNotEqual(query, '')
|
||||||
self.assertEqual(body, '{"data": {}}')
|
self.assertEqual(body, '{"data": {}}')
|
||||||
|
|
||||||
|
def test_json_body_default_data(self):
|
||||||
|
"""Test that a 'data' wrapper doesn't get added if one is already present."""
|
||||||
|
model = JsonModel()
|
||||||
|
|
||||||
|
headers = {}
|
||||||
|
path_params = {}
|
||||||
|
query_params = {}
|
||||||
|
body = {'data': 'foo'}
|
||||||
|
|
||||||
|
headers, params, query, body = model.request(headers, path_params, query_params, body)
|
||||||
|
|
||||||
|
self.assertEqual(headers['accept'], 'application/json')
|
||||||
|
self.assertEqual(headers['content-type'], 'application/json')
|
||||||
|
self.assertNotEqual(query, '')
|
||||||
|
self.assertEqual(body, '{"data": "foo"}')
|
||||||
|
|
||||||
def test_json_build_query(self):
|
def test_json_build_query(self):
|
||||||
model = JsonModel()
|
model = JsonModel()
|
||||||
|
|
||||||
|
|||||||
@@ -487,7 +487,7 @@ group.add_option("--rev", action="store", dest="revision",
|
|||||||
help="Base revision/branch/tree to diff against. Use "
|
help="Base revision/branch/tree to diff against. Use "
|
||||||
"rev1:rev2 range to review already committed changeset.")
|
"rev1:rev2 range to review already committed changeset.")
|
||||||
group.add_option("--send_mail", action="store_true",
|
group.add_option("--send_mail", action="store_true",
|
||||||
dest="send_mail", default=False,
|
dest="send_mail", default=True,
|
||||||
help="Send notification email to reviewers.")
|
help="Send notification email to reviewers.")
|
||||||
group.add_option("--vcs", action="store", dest="vcs",
|
group.add_option("--vcs", action="store", dest="vcs",
|
||||||
metavar="VCS", default=None,
|
metavar="VCS", default=None,
|
||||||
|
|||||||
Reference in New Issue
Block a user