This commit is contained in:
Tom Miller
2010-10-11 11:52:05 -07:00
36 changed files with 691 additions and 123 deletions

View File

@@ -7,3 +7,4 @@ syntax: glob
samples/cmdline/*.dat
htmlcov/*
.coverage
database.sqlite3

View File

@@ -2,7 +2,7 @@
"data": {
"moderator": {
"v1": {
"baseUrl": "https://www.googleapis.com/",
"baseUrl": "https://www.googleapis.com/",
"auth": {
"request": {
"url": "https://www.google.com/accounts/OAuthGetRequestToken",

View File

@@ -45,13 +45,21 @@ except ImportError: # pragma: no cover
import json as simplejson
class HttpError(Exception):
class Error(Exception):
"""Base error for this module."""
pass
class UnknownLinkType(Exception):
class HttpError(Error):
"""HTTP data was invalid or unexpected."""
pass
class UnknownLinkType(Error):
"""Link type unknown or unexpected."""
pass
DISCOVERY_URI = ('http://www.googleapis.com/discovery/0.1/describe'
'{?api,apiVersion}')
@@ -86,12 +94,15 @@ class JsonModel(object):
if body_value is None:
return (headers, path_params, query, None)
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'
return (headers, path_params, query, simplejson.dumps(model))
def build_query(self, params):
params.update({'alt': 'json', 'prettyprint': 'true'})
params.update({'alt': 'json'})
astuples = []
for key, value in params.iteritems():
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
# for some operation/error combinations?
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']
else:
logging.debug('Content from bad request was: %s' % content)

View File

@@ -21,7 +21,17 @@ except ImportError:
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
@@ -120,8 +130,10 @@ class OAuthCredentials(Credentials):
if headers == None:
headers = {}
headers.update(req.to_header())
if 'user-agent' not in headers:
headers['user-agent'] = self.user_agent
if 'user-agent' in headers:
headers['user-agent'] = self.user_agent + ' ' + headers['user-agent']
else:
headers['user-agent'] = self.user_agent
return request_orig(uri, method, body, headers,
redirections, connection_type)
@@ -185,7 +197,7 @@ class FlowThreeLegged(object):
body=body)
if resp['status'] != '200':
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))
@@ -222,7 +234,7 @@ class FlowThreeLegged(object):
resp, content = client.request(uri, 'POST', headers=headers)
if resp['status'] != '200':
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))
token = oauth.Token(

View File

@@ -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'
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.
class BuzzGaeClient(object):
def __init__(self, consumer_key='anonymous', consumer_secret='anonymous'):
@@ -49,7 +60,7 @@ class BuzzGaeClient(object):
if resp['status'] != '200':
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
def get_request_token(self, callback_url, display_name = None):

View File

@@ -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.
"""
import httplib2
import pprint
__author__ = 'ade@google.com (Ade Oshineye)'
@@ -18,6 +19,7 @@ import httplib2
import logging
import pickle
import os
import time
import unittest
# 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
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):
buzz = build('buzz', 'v1')
max_results = 2
actcol = buzz.activities()
activities = actcol.list(userId='googlebuzz', scope='@self',
activities_command = buzz.activities()
activities = activities_command.list(userId='googlebuzz', scope='@self',
max_comments=max_results*2 ,max_liked=max_results*3,
max_results=max_results).execute()
activity_count = len(activities['items'])
self.assertEquals(max_results, activity_count)
activities = actcol.list_next(activities).execute()
activities = activities_command.list_next(activities).execute()
activity_count = len(activities['items'])
self.assertEquals(max_results, activity_count)
def test_can_get_multiple_pages_of_buzz_activities(self):
buzz = build('buzz', 'v1')
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()
for count in range(10):
activities = actcol.list_next(activities).execute()
activities = activities_command.list_next(activities).execute()
activity_count = len(activities['items'])
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('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):
def __init__(self, method_name):
@@ -97,8 +138,36 @@ class BuzzAuthenticatedFunctionalTest(unittest.TestCase):
self.http = credentials.authorize(httplib2.Http())
def test_can_list_groups_belonging_to_user(self):
# TODO(ade) This should not require authentication. It does because we're adding a spurious @self to the URL
def test_can_create_activity(self):
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)
groups = buzz.groups().list(userId='108242092577082601423').execute()
@@ -106,14 +175,75 @@ class BuzzAuthenticatedFunctionalTest(unittest.TestCase):
expected_default_number_of_groups = 4
self.assertEquals(expected_default_number_of_groups, len(groups['items']))
def IGNORE__test_can_get_followees_of_user(self):
# This currently fails with:
# Attempting to access self view of a different user.
# and URL:
def IGNORE__test_can_like_activity(self):
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__':
unittest.main()

View File

@@ -55,9 +55,9 @@ from gettext import gettext as _
import socket
try:
from httplib2 import socks
from httplib2 import socks
except ImportError:
socks = None
socks = None
# Build the appropriate socket wrapper for ssl
try:
@@ -83,7 +83,7 @@ def has_timeout(timeout): # python 2.6
__all__ = ['Http', 'Response', 'ProxyInfo', 'HttpLib2Error',
'RedirectMissingLocation', 'RedirectLimit', 'FailedToDecompressContent',
'UnimplementedDigestAuthOptionError', 'UnimplementedHmacDigestAuthOptionError',
'debuglevel']
'debuglevel', 'ProxiesUnavailableError']
# 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 ServerNotFoundError(HttpLib2Error): pass
class ProxiesUnavailableError(HttpLib2Error): pass
# Open Items:
# -----------
@@ -721,6 +722,9 @@ class HTTPConnectionWithTimeout(httplib.HTTPConnection):
def connect(self):
"""Connect to the host and port specified in __init__."""
# 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"
for res in socket.getaddrinfo(self.host, self.port, 0,
socket.SOCK_STREAM):

View File

@@ -41,12 +41,13 @@ mainly to merge bug fixes found in Sourceforge
"""
import socket
if getattr(socket, 'socket', None) is None:
raise ImportError('socket.socket missing, proxy support unusable')
import struct
import sys
if not hasattr(socket, 'socket'):
raise ImportError("Running on App Engine?")
PROXY_TYPE_SOCKS4 = 1
PROXY_TYPE_SOCKS5 = 2
PROXY_TYPE_HTTP = 3

View 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
View 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()

View File

@@ -22,11 +22,6 @@ other example apps in the same directory.
__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.oauth import FlowThreeLegged

View File

@@ -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()

View File

@@ -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()

View File

View File

View 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)

View 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
"""}

View 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
View 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)

View 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'
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View 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 %}

View 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>

View 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')
}),
)

View 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()

View File

@@ -35,6 +35,7 @@ flow = FlowThreeLegged(moderator_discovery,
user_agent='google-api-client-python-mdrtr-cmdline/1.0',
domain='anonymous',
scope='https://www.googleapis.com/auth/moderator',
#scope='tag:google.com,2010:auth/moderator',
xoauth_displayname='Google API Client Example App')
authorize_url = flow.step1_get_authorize_url()

View File

@@ -65,6 +65,22 @@ class Model(unittest.TestCase):
self.assertNotEqual(query, '')
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):
model = JsonModel()

View File

@@ -487,7 +487,7 @@ group.add_option("--rev", action="store", dest="revision",
help="Base revision/branch/tree to diff against. Use "
"rev1:rev2 range to review already committed changeset.")
group.add_option("--send_mail", action="store_true",
dest="send_mail", default=False,
dest="send_mail", default=True,
help="Send notification email to reviewers.")
group.add_option("--vcs", action="store", dest="vcs",
metavar="VCS", default=None,