Switch to py-trello on pypi, pep8, unicode decode error

This commit is contained in:
nikolark committed 2014-09-21 20:42:36 +02:00
1 parent 822f425a9c
commit df42dea0f7
7 files changed
+39 -565

No files matched your search

+2
View File
@@ -1,4 +1,6 @@
venv
.idea
*.pyc
*.swp
bestillingsskjema.db
bestilling/bestilling/settings.py
+31 -31
View File
@@ -1,3 +1,4 @@
from __future__ import unicode_literals
from datetime import datetime, timedelta
from django.core.context_processors import csrf
from django.core import mail
@@ -6,11 +7,14 @@ from django.http import HttpResponse, Http404
from django.template import Context
from django.template.loader import get_template
from django.views.generic import TemplateView
from settings import TRELLO_API_KEY, TRELLO_TOKEN
from lib import trello
from django.conf import settings
import trello
import utils
class BaseFormView(TemplateView):
mail_template_name = "confirmation_mail.txt"
mail_from = 'no-reply@studentersamfundet.no'
@@ -46,7 +50,7 @@ class BaseFormView(TemplateView):
template = get_template('form_success.html')
context_data = {
'url' : self._get_order_url(request, order),
'url': self._get_order_url(request, order),
}
response = template.render(Context(context_data))
return HttpResponse(response)
@@ -56,11 +60,11 @@ class BaseFormView(TemplateView):
def _get_errors(self, params):
return {
'client_error' : params['client'] == '' or len(params['client']) > 50,
'deadline_error' : params['deadline'] == '',
'contact_name_error' : params['contact_name'] == '' or len(params['contact_name']) > 50,
'contact_email_error' : not utils.is_email_valid(params['contact_email']),
'contact_number_error' : not utils.is_phone_valid(params['contact_number']),
'client_error': params['client'] == '' or len(params['client']) > 50,
'deadline_error': params['deadline'] == '',
'contact_name_error': params['contact_name'] == '' or len(params['contact_name']) > 50,
'contact_email_error': not utils.is_email_valid(params['contact_email']),
'contact_number_error': not utils.is_phone_valid(params['contact_number']),
}
def _get_order_url(self, request, order):
@@ -70,7 +74,7 @@ class BaseFormView(TemplateView):
raise NotImplementedError
def _save_to_trello(self, card_name, card_description, card_due, card_colour="green", list_name='Bestillinger'):
client = trello.TrelloClient(api_key=TRELLO_API_KEY, token=TRELLO_TOKEN)
client = trello.TrelloClient(api_key=settings.TRELLO_API_KEY, token=settings.TRELLO_TOKEN)
board = client.get_board(self.trello_board_id)
@@ -86,15 +90,16 @@ class BaseFormView(TemplateView):
# Set card deadline
card._set_remote_attribute('due', card_due)
# Set colour of the card. We have to do this like this, because there is no shortcut and _set_remote_attribute() uses PUT
card.client.fetch_json('/cards/'+card.id+'/labels', http_method="POST", post_args = { 'value' : card_colour })
# Set colour of the card.
# Note: We have to do it like this, because there is no shortcut and _set_remote_attribute() uses PUT
card.client.fetch_json('/cards/'+card.id+'/labels', http_method="POST", post_args={'value': card_colour})
return card
def _send_confirmation_mail(self, request, order):
template = get_template(self.mail_template_name)
context_data = {
'order' : order,
'url' : self._get_order_url(request, order),
'order': order,
'url': self._get_order_url(request, order),
}
mail_from = self.mail_from
@@ -102,7 +107,7 @@ class BaseFormView(TemplateView):
mail_subject = "{id}: Bestillingen din er mottatt.".format(id=order.client)
mail_content = template.render(Context(context_data))
mail.send_mail(mail_subject, mail_content, mail_from, [ mail_to ], fail_silently=True)
mail.send_mail(mail_subject, mail_content, mail_from, [mail_to], fail_silently=True)
class BaseOrderView(TemplateView):
@@ -113,12 +118,12 @@ class BaseOrderView(TemplateView):
template = get_template(self.template_name)
order = self._get_order(order_id)
data = self._get_additional_data(order)
except (trello.ResourceUnavailable, ObjectDoesNotExist) as exception:
except (trello.ResourceUnavailable, ObjectDoesNotExist):
raise Http404
context_data = {
'order' : order,
'data' : data,
'order': order,
'data': data,
}
response = template.render(Context(context_data))
@@ -134,7 +139,7 @@ class BaseOrderView(TemplateView):
def _get_from_trello(self, card_id):
client = trello.TrelloClient(api_key=TRELLO_API_KEY, token=TRELLO_TOKEN)
client = trello.TrelloClient(api_key=settings.TRELLO_API_KEY, token=settings.TRELLO_TOKEN)
obj = client.fetch_json('/cards/' + card_id)
@@ -143,11 +148,7 @@ class BaseOrderView(TemplateView):
tmp_list = MockObject()
tmp_list.client = client
card = trello.Card(
card_id = obj['id'],
trello_list = tmp_list,
name = obj['name']
)
card = trello.Card.from_json(tmp_list, obj)
return card
def _get_data_from_card(self, card):
@@ -155,22 +156,21 @@ class BaseOrderView(TemplateView):
days_left = None
if card.badges['due'] is not None:
now = datetime.now()
due = datetime.strptime(card.badges['due'][:-5], "%Y-%m-%dT%H:%M:%S")
due = datetime.strptime(card.badges['due'][:-5], "%Y-%m-%dT%H:%M:%S")
days_left = (due - now).days if due > now else timedelta(0).days
return {
"card" : card,
"created" : card.create_date,
"due" : due,
"days_left" : days_left,
"members" : self._list_members_from_card(card),
"card": card,
"created": card.create_date,
"due": due,
"days_left": days_left,
"members": self._list_members_from_card(card),
}
def _list_members_from_card(self, card):
client = trello.TrelloClient(api_key=TRELLO_API_KEY, token=TRELLO_TOKEN)
client = trello.TrelloClient(api_key=settings.TRELLO_API_KEY, token=settings.TRELLO_TOKEN)
if not hasattr(card, "member_ids"):
card.fetch()
return map(lambda member_id: client.get_member(member_id).fetch(), card.member_ids)
View File
Whitespace-only changes.
-2
View File
@@ -1,2 +0,0 @@
This is a "fork" of py-trello 0.1.3 with few custom modifications. Basically, you probably don't want to replace it with an updated version from original creator.
Project url: https://github.com/sarumont/py-trello
-461
View File
@@ -1,461 +0,0 @@
from httplib2 import Http
from urllib import urlencode
from datetime import datetime
import exceptions
import json
import oauth2 as oauth
import os
import random
import time
import urlparse
class ResourceUnavailable(Exception):
"""Exception representing a failed request to a resource"""
def __init__(self, msg, http_response):
Exception.__init__(self)
self._msg = msg
self._status = http_response.status
def __str__(self):
return "Resource unavailable: %s (HTTP status: %s)" % (self._msg, self._status)
class Unauthorized(ResourceUnavailable):
pass
class TrelloClient(object):
""" Base class for Trello API access """
def __init__(self, api_key, token, api_secret = None, token_secret = None):
"""
Constructor
:api_key: API key generated at https://trello.com/1/appKey/generate
:oauth_token: OAuth token generated by the user
"""
if api_key and api_secret and token and token_secret:
# oauth
self.oauth_consumer = oauth.Consumer(key = api_key, secret = api_secret)
self.oauth_token = oauth.Token(key = token, secret = token_secret)
self.client = oauth.Client(self.oauth_consumer, self.oauth_token)
elif api_key and token:
self.client = Http()
self.api_key = api_key
self.auth_token = token
def logout(self):
"""Log out of Trello. This method is idempotent."""
# TODO: refactor
pass
#if not self._cookie:
#return
#headers = {'Cookie': self._cookie, 'Accept': 'application/json'}
#response, content = self.client.request(
#'https://trello.com/logout',
#'GET',
#headers = headers,
#)
## TODO: error checking
#self._cookie = None
def build_url(self, path, query = {}):
"""
Builds a Trello URL.
:path: URL path
:params: dict of key-value pairs for the query string
"""
url = 'https://api.trello.com/1'
if path[0:1] != '/':
url += '/'
url += path
if hasattr(self, 'oauth_token'):
url += '?'
url += "key="+self.oauth_token.key
url += "&token="+self.oauth_consumer.key
else:
url += '?'
url += "key="+self.api_key
url += "&token="+self.auth_token
if len(query) > 0:
url += '&'+urlencode(query)
return url
def list_boards(self):
"""
Returns all boards for your Trello user
:return: a list of Python objects representing the Trello boards. Each board has the
following noteworthy attributes:
- id: the board's identifier
- name: Name of the board
- desc: Description of the board (optional - may be missing from the returned JSON)
- closed: Boolean representing whether this board is closed or not
- url: URL to the board
"""
json_obj = self.fetch_json('/members/me/boards/all')
boards = list()
for obj in json_obj:
boards.append(self._board_from_json(obj))
return boards
def get_board(self, board_id):
obj = self.fetch_json('/boards/' + board_id)
return self._board_from_json(obj)
def get_list(self, list_id):
obj = self.fetch_json('/lists/' + list_id)
list = List(self.get_board(obj['idBoard']), obj['id'], name=obj['name'].encode('utf-8'))
list.closed = obj['closed']
return list
def get_member(self, member_id):
return Member(self, member_id).fetch()
def fetch_json(
self,
uri_path,
http_method = 'GET',
headers = {},
query_params = {},
post_args = {}):
""" Fetch some JSON from Trello """
if http_method in ("POST", "PUT", "DELETE"):
headers['Content-type'] = 'application/json'
headers['Accept'] = 'application/json'
url = self.build_url(uri_path, query_params)
response, content = self.client.request(
url,
http_method,
headers = headers,
body = json.dumps(post_args))
# error checking
if response.status == 401:
raise Unauthorized(url)
if response.status != 200:
raise ResourceUnavailable(url, response)
return json.loads(content)
def _board_from_json(self, json):
board = Board(self, json['id'], name = json['name'].encode('utf-8'))
board.description = json.get('desc','').encode('utf-8')
board.closed = json['closed']
board.url = json['url']
return board
class Board(object):
"""Class representing a Trello board. Board attributes are stored as normal Python attributes;
access to all sub-objects, however, is always an API call (Lists, Cards).
"""
def __init__(self, client, board_id, name=''):
"""Constructor.
:trello: Reference to a Trello object
:board_id: ID for the board
"""
self.client = client
self.id = board_id
self.name = name
def __repr__(self):
return '<Board %s>' % self.name
def fetch(self):
"""Fetch all attributes for this board"""
json_obj = self.client.fetch_json('/boards/'+self.id)
self.name = json_obj['name'].encode('utf-8')
self.description = json_obj.get('desc','')
self.closed = json_obj['closed']
self.url = json_obj['url']
def save(self):
pass
def all_lists(self):
"""Returns all lists on this board"""
return self.get_lists('all')
def open_lists(self):
"""Returns all open lists on this board"""
return self.get_lists('open')
def closed_lists(self):
"""Returns all closed lists on this board"""
return self.get_lists('closed')
def get_lists(self, list_filter):
# error checking
json_obj = self.client.fetch_json(
'/boards/'+self.id+'/lists',
query_params = {'cards': 'none', 'filter': list_filter})
lists = list()
for obj in json_obj:
l = List(self, obj['id'], name=obj['name'].encode('utf-8'))
l.closed = obj['closed']
lists.append(l)
return lists
def add_list(self, name):
"""Add a card to this list
:name: name for the card
:return: the card
"""
obj = self.client.fetch_json(
'/lists',
http_method = 'POST',
post_args = {'name': name, 'idBoard': self.id},)
list = List(self, obj['id'], name=obj['name'].encode('utf-8'))
list.closed = obj['closed']
return list
def all_cards(self):
"""Returns all cards on this board"""
return self.get_cards('all')
def open_cards(self):
"""Returns all open cards on this board"""
return self.get_cards('open')
def closed_cards(self):
"""Returns all closed cards on this board"""
return self.get_cards('closed')
def get_cards(self, card_filter):
# error checking
json_obj = self.client.fetch_json(
'/boards/'+self.id+'/cards',
query_params = {'filter': card_filter})
cards = list()
for obj in json_obj:
card = Card(self, obj['id'], name=obj['name'].encode('utf-8'))
card.closed = obj['closed']
card.member_ids = obj['idMembers']
cards.append(card)
return cards
class List(object):
"""Class representing a Trello list. List attributes are stored on the object, but access to
sub-objects (Cards) require an API call"""
def __init__(self, board, list_id, name=''):
"""Constructor
:board: reference to the parent board
:list_id: ID for this list
"""
self.board = board
self.client = board.client
self.id = list_id
self.name = name
def __repr__(self):
return '<List %s>' % self.name
def fetch(self):
"""Fetch all attributes for this list"""
json_obj = self.client.fetch_json('/lists/'+self.id)
self.name = json_obj['name'].encode('utf-8')
self.closed = json_obj['closed']
def list_cards(self):
"""Lists all cards in this list"""
json_obj = self.client.fetch_json('/lists/'+self.id+'/cards')
cards = list()
for c in json_obj:
card = Card(self, c['id'], name = c['name'].encode('utf-8'))
card.description = c.get('desc','').encode('utf-8')
card.closed = c['closed']
card.url = c['url']
card.member_ids = c['idMembers']
cards.append(card)
return cards
def add_card(self, name, desc = None):
"""Add a card to this list
:name: name for the card
:return: the card
"""
json_obj = self.client.fetch_json(
'/lists/'+self.id+'/cards',
http_method = 'POST',
post_args = {'name': name, 'idList': self.id, 'desc': desc},)
card = Card(self, json_obj['id'])
card.name = json_obj['name']
card.description = json_obj.get('desc','')
card.closed = json_obj['closed']
card.url = json_obj['url']
card.member_ids = json_obj['idMembers']
return card
def fetch_actions(self, action_filter):
"""
Fetch actions for this list can give more argv to action_filter,
split for ',' json_obj is list
"""
json_obj = self.client.fetch_json(
'/lists/'+self.id+'/actions',
query_params = {'filter': action_filter})
self.actions = json_obj
class Card(object):
"""
Class representing a Trello card. Card attributes are stored on
the object
"""
def __init__(self, trello_list, card_id, name=''):
"""Constructor
:trello_list: reference to the parent list
:card_id: ID for this card
"""
self.trello_list = trello_list
self.client = trello_list.client
self.id = card_id
self.name = name
def __repr__(self):
return '<Card %s>' % self.name
def fetch(self):
"""Fetch all attributes for this card"""
json_obj = self.client.fetch_json(
'/cards/'+self.id,
query_params = {'badges': False})
self.name = json_obj['name'].encode('utf-8')
self.description = json_obj.get('desc','')
self.closed = json_obj['closed']
self.url = json_obj['url']
self.member_ids = json_obj['idMembers']
self.short_id = json_obj['idShort']
self.list_id = json_obj['idList']
self.board_id = json_obj['idBoard']
self.labels = json_obj['labels']
self.badges = json_obj['badges']
self.checked = json_obj['checkItemStates']
self.checklists = []
if self.badges['checkItems'] > 0:
json_obj = self.client.fetch_json(
'/cards/'+self.id+'/checklists',)
for cl in json_obj:
self.checklists.append(Checklist(self.client, self.checked, cl))
self.comments = []
if self.badges['comments'] > 0:
self.comments = self.client.fetch_json(
'/cards/'+self.id+'/actions',
query_params = {'filter': 'commentCard'})
def fetch_actions(self, action_filter='createCard'):
"""
Fetch actions for this card can give more argv to action_filter,
split for ',' json_obj is list
"""
json_obj = self.client.fetch_json(
'/cards/'+self.id+'/actions',
query_params = {'filter': action_filter})
self.actions = json_obj
@property
def create_date(self):
self.fetch_actions()
date_str = self.actions[0]['date'][:-5]
return datetime.strptime(date_str, '%Y-%m-%dT%H:%M:%S')
def set_description(self, description):
self._set_remote_attribute('desc', description)
self.description = description
def set_closed(self, closed):
self._set_remote_attribute('closed', closed)
self.closed = closed
def delete(self):
# Delete this card permanently
self.client.fetch_json(
'/cards/'+self.id,
http_method = 'DELETE',)
def assign(self, member_id):
self.client.fetch_json(
'/cards/'+self.id+'/members',
http_method = 'POST',
post_args = {'value' : member_id, })
def change_list(self, list_id):
self.client.fetch_json(
'/cards/'+self.id+'/idList',
http_method = 'PUT',
post_args = {'value' : list_id, })
def _set_remote_attribute(self, attribute, value):
self.client.fetch_json(
'/cards/'+self.id+'/'+attribute,
http_method = 'PUT',
post_args = {'value': value,},)
class Member(object):
"""
Class representing a Trello member.
"""
def __init__(self, client, member_id):
self.client = client
self.id = member_id
def __repr__(self):
return '<Member %s>' % self.id
def fetch(self):
"""Fetch all attributes for this card"""
json_obj = self.client.fetch_json(
'/members/'+self.id,
query_params = {'badges': False})
self.status = json_obj['status'].encode('utf-8')
self.id = json_obj.get('id','')
self.bio = json_obj.get('bio','')
self.url = json_obj.get('url','')
self.username = json_obj['username'].encode('utf-8')
self.full_name = json_obj['fullName'].encode('utf-8')
self.initials = json_obj['initials'].encode('utf-8')
return self
class Checklist(object):
"""
Class representing a Trello checklist.
"""
def __init__(self, client, checked, obj):
self.client = client
self.id = obj['id']
self.items = obj['checkItems']
for i in self.items:
i['checked'] = False
for cis in checked:
if cis['idCheckItem'] == i['id'] and cis['state'] == 'complete':
i['checked'] = True
def __repr__(self):
return '<Checklist %s>' % self.id
# vim:noexpandtab
-61
View File
@@ -1,61 +0,0 @@
import oauth2 as oauth
def test_oauth():
request_token_url = 'https://trello.com/1/OAuthGetRequestToken'
authorize_url = 'https://trello.com/1/OAuthAuthorizeToken'
access_token_url = 'https://trello.com/1/OAuthGetAccessToken'
consumer = oauth.Consumer(consumer_key, consumer_secret)
client = oauth.Client(consumer)
# Step 1: Get a request token. This is a temporary token that is used for
# having the user authorize an access token and to sign the request to obtain
# said access token.
resp, content = client.request(request_token_url, "GET")
if resp['status'] != '200':
raise Exception("Invalid response %s." % resp['status'])
request_token = dict(urlparse.parse_qsl(content))
print "Request Token:"
print " - oauth_token = %s" % request_token['oauth_token']
print " - oauth_token_secret = %s" % request_token['oauth_token_secret']
print
# Step 2: Redirect to the provider. Since this is a CLI script we do not
# redirect. In a web application you would redirect the user to the URL
# below.
print "Go to the following link in your browser:"
print "%s?oauth_token=%s&scope=read,write,comment" % (authorize_url, request_token['oauth_token'])
print
# After the user has granted access to you, the consumer, the provider will
# redirect you to whatever URL you have told them to redirect to. You can
# usually define this in the oauth_callback argument as well.
accepted = 'n'
while accepted.lower() == 'n':
accepted = raw_input('Have you authorized me? (y/n) ')
oauth_verifier = raw_input('What is the PIN? ')
# Step 3: Once the consumer has redirected the user back to the oauth_callback
# URL you can request the access token the user has approved. You use the
# request token to sign this request. After this is done you throw away the
# request token and use the access token returned. You should store this
# access token somewhere safe, like a database, for future use.
token = oauth.Token(request_token['oauth_token'],
request_token['oauth_token_secret'])
token.set_verifier(oauth_verifier)
client = oauth.Client(consumer, token)
resp, content = client.request(access_token_url, "POST")
access_token = dict(urlparse.parse_qsl(content))
print "Access Token:"
print " - oauth_token = %s" % access_token['oauth_token']
print " - oauth_token_secret = %s" % access_token['oauth_token_secret']
print
print "You may now access protected resources using the access tokens above."
print
+6 -10
View File
@@ -1,11 +1,7 @@
Django==1.6.4
MySQL-python==1.2.4
South==0.7.6
argparse==1.2.1
distribute==0.6.34
django-extensions==1.1.1
httplib2==0.8
oauth2==1.5.211
python-dateutil==1.5
six==1.6.1
wsgiref==0.1.2
Django>=1.6,<1.7
django-extensions==1.4.0
MySQL-python==1.2.5
python-dateutil==2.2
py-trello==0.1.6
South==1.0