First take on Goodreads integration.
Can get userid, username, and display shelves info Using sessions to store user info right now.pull/1/head
parent
cc7b9907c8
commit
299d3d1be3
|
@ -0,0 +1,146 @@
|
|||
import json
|
||||
import logging
|
||||
from xml.etree import ElementTree as ET
|
||||
from requests import request
|
||||
import oauth2 as oauth
|
||||
from urlparse import urlparse, urlunparse, urljoin
|
||||
from urllib import urlencode
|
||||
import httplib
|
||||
# import parse_qsl from cgi if it doesn't exist in urlparse
|
||||
try:
|
||||
from urlparse import parse_qsl
|
||||
except:
|
||||
from cgi import parse_qsl
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# QUESTION: should the request_token, access_token be part of the state of the client?
|
||||
# for simplicity for now, I will make them part of the state of GoodReadsClient
|
||||
|
||||
class GoodreadsException(Exception):
|
||||
pass
|
||||
|
||||
class GoodreadsAuthorizationRequired(GoodreadsException):
|
||||
pass
|
||||
|
||||
class GoodreadsClient(object):
|
||||
|
||||
url = 'http://www.goodreads.com'
|
||||
request_token_url = urljoin(url,'oauth/request_token/')
|
||||
authorize_url = urljoin(url, '/oauth/authorize/')
|
||||
access_token_url = urljoin(url,'/oauth/access_token/')
|
||||
|
||||
def __init__(self,key,secret,access_token=None):
|
||||
self.key = key
|
||||
self.secret = secret
|
||||
self.consumer = oauth.Consumer(key=self.key,
|
||||
secret=self.secret)
|
||||
|
||||
self.client = oauth.Client(self.consumer)
|
||||
self.unauth_client = None
|
||||
|
||||
if access_token is not None:
|
||||
self.__load_access_token(access_token)
|
||||
else:
|
||||
self.access_token = None
|
||||
|
||||
@property
|
||||
def is_authorized(self):
|
||||
return (self.access_token is not None)
|
||||
def begin_authorization (self, callback_url=None):
|
||||
# get request token
|
||||
response, content = self.client.request(GoodreadsClient.request_token_url, 'GET')
|
||||
|
||||
if int(response['status']) != httplib.OK:
|
||||
raise Exception('Invalid response: %s' % response['status'])
|
||||
|
||||
request_token = dict(parse_qsl(content))
|
||||
|
||||
q = {'oauth_token':request_token['oauth_token']}
|
||||
if callback_url is not None:
|
||||
q['oauth_callback'] = callback_url
|
||||
|
||||
authorize_link = GoodreadsClient.authorize_url + '?' + urlencode(q)
|
||||
return (authorize_link, request_token)
|
||||
|
||||
def complete_authorization(self, request_token):
|
||||
token = oauth.Token(request_token['oauth_token'],
|
||||
request_token['oauth_token_secret'])
|
||||
|
||||
self.client = oauth.Client(self.consumer, token)
|
||||
response, content = self.client.request(GoodreadsClient.access_token_url, 'POST')
|
||||
if int(response['status']) != httplib.OK:
|
||||
raise Exception('Invalid response: %s' % response['status'])
|
||||
|
||||
access_token_raw = dict(parse_qsl(content))
|
||||
self.__load_access_token(access_token_raw)
|
||||
return access_token_raw
|
||||
|
||||
def __load_access_token(self, access_token):
|
||||
token = oauth.Token(access_token['oauth_token'],
|
||||
access_token['oauth_token_secret'])
|
||||
self.access_token = token
|
||||
self.client = oauth.Client(self.consumer, self.access_token)
|
||||
|
||||
def __clear_access_token(self):
|
||||
self.access_token = None
|
||||
self.consumer = oauth.Consumer(key=self.key,
|
||||
secret=self.secret)
|
||||
|
||||
def auth_user(self):
|
||||
if self.is_authorized:
|
||||
response, content = self.client.request('%s/api/auth_user' % GoodreadsClient.url,
|
||||
'GET')
|
||||
if int(response['status']) != httplib.OK:
|
||||
raise GoodreadsException('Error authenticating Goodreads user: %s ' % response)
|
||||
else:
|
||||
doc = ET.fromstring(content)
|
||||
user = doc.find('user')
|
||||
userid = user.get('id')
|
||||
name = user.find('name').text
|
||||
link = user.find('link').text
|
||||
return({'userid':userid, 'name':name, 'link':link})
|
||||
else:
|
||||
raise GoodreadsAuthorizationRequired('Attempt to access auth_user without authorization.')
|
||||
|
||||
def add_book(self, book_id=871441, shelf_name='to-read'):
|
||||
# the book is: "Moby-Dick: A Pop-Up Book" 871441
|
||||
body = urlencode({'name': 'to-read', 'book_id': book_id})
|
||||
headers = {'content-type': 'application/x-www-form-urlencoded'}
|
||||
response, content = self.client.request('%s/shelf/add_to_shelf.xml' % GoodreadsClient.url,
|
||||
'POST', body, headers)
|
||||
# check that the new resource has been created
|
||||
if int(response['status']) != httplib.CREATED:
|
||||
raise GoodreadsException('Cannot create resource: %s' % response['status'])
|
||||
logger.info('response,content: %s | %s ' % (response,content))
|
||||
else:
|
||||
return True
|
||||
|
||||
def shelves_list(self,user_id,page=1):
|
||||
path = "/shelf/list.xml"
|
||||
params = {'user_id':user_id, 'page':page}
|
||||
params["key"] = self.key
|
||||
method = "GET"
|
||||
request_url = urljoin(GoodreadsClient.url, path)
|
||||
|
||||
r = request(method,request_url,params=params)
|
||||
|
||||
if r.status_code != httplib.OK:
|
||||
logger.info('headers, content: %s | %s ' % (r.headers,r.content))
|
||||
raise GoodreadsException('Error in shelves_list: %s %s ' % (r.headers, r.content))
|
||||
else:
|
||||
logger.info('headers, content: %s | %s ' % (r.headers,r.content))
|
||||
doc = ET.fromstring(r.content)
|
||||
shelves = doc.find('shelves')
|
||||
# do a simple parsing to a dictionary
|
||||
d = {k:int(shelves.attrib[k]) for k in shelves.attrib }
|
||||
d["user_shelves"] = [{'name':shelf.find('name').text,
|
||||
'book_count':int(shelf.find('book_count').text),
|
||||
'description':shelf.find('description').text if shelf.find('description').attrib['nil'] != 'true' else None } \
|
||||
for shelf in shelves.findall('user_shelf')]
|
||||
return d
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
{% extends "base.html" %}
|
||||
{% block extra_head %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block title %}Goodreads{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div id="main-container">
|
||||
<div class="js-main">
|
||||
<div id="js-maincol-fr">
|
||||
<div class="js-maincol-inner">
|
||||
<div class="content-block">
|
||||
|
||||
<h1>Goodreads</h1>
|
||||
{% if goodreads_userid %}
|
||||
<p>Welcome, Goodreads user id# {{goodreads_userid}}. Your GR username is {{goodreads_username}}. Want to see <a href="{{goodreads_userlink}}">your Goodreads profile</a>?</p>
|
||||
{% else %}
|
||||
<p>We don't currently know your Goodreads user information.</p>
|
||||
<p><a href="{{goodreads_auth_url}}">Enable us to link to your Goodreads account.</a></p>
|
||||
{% endif %}
|
||||
{% if shelves_info %}
|
||||
<p>You have {{shelves_info.total}} shelves. </p>
|
||||
{% for shelf in shelves_info.user_shelves %}
|
||||
<p>{{shelf.name}}: {{shelf.book_count}} book(s)</p>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
<p>Clear the association with your Goodreads account by <a href="{% url goodreads_flush_session %}">flushing your session.</a></p>
|
||||
<p>Revoke access for Unglue.it at <a href="http://www.goodreads.com/user/edit?format=html">Goodreads</a>: Go to the API panel.</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
|
Loading…
Reference in New Issue