Browse files

Flask app initial commit

  • Loading branch information...
jonthornton committed Mar 24, 2014
0 parents commit f9a7e7a7aa1073cd024a887edd0226cd05d335d2
@@ -0,0 +1,4 @@

Large diffs are not rendered by default.

Oops, something went wrong.
BIN +23.3 KB pytumblr/__init__.pyc
Binary file not shown.
@@ -0,0 +1,45 @@
def validate_params(valid_options, params):
Helps us validate the parameters for the request
:param valid_options: a list of strings of valid options for the
api request
:param params: a dict, the key-value store which we really only care about
the key which has tells us what the user is using for the
API request
:returns: None or throws an exception if the validation fails
#crazy little if statement hanging by himself :(
if not params:
#We only allow one version of the data parameter to be passed
data_filter = ['data', 'source', 'external_url', 'embed']
multiple_data = filter(lambda x: x in data_filter, params.keys())
if len(multiple_data) > 1:
raise Exception("You can't mix and match data parameters")
#No bad fields which are not in valid options can pass
disallowed_fields = filter(lambda x: x not in valid_options, params.keys())
if disallowed_fields:
field_strings = ",".join(disallowed_fields)
raise Exception("%s are not allowed fields" % field_strings)
def validate_blogname(fn):
Decorator to validate the blogname and let you pass in a blogname like:
and query all the same blog.
def add_dot_tumblr(*args, **kwargs):
if (len(args) > 1 and ("." not in args[1])):
args = list(args)
args[1] += ""
return fn(*args, **kwargs)
return add_dot_tumblr
BIN +2.13 KB pytumblr/helpers.pyc
Binary file not shown.
@@ -0,0 +1,155 @@
import urllib
import urllib2
import time
import json
from urlparse import parse_qsl
import oauth2 as oauth
from httplib2 import RedirectLimit
class TumblrRequest(object):
A simple request object that lets us query the Tumblr API
def __init__(self, consumer_key, consumer_secret="", oauth_token="", oauth_secret="", host=""): = host
self.consumer = oauth.Consumer(key=consumer_key, secret=consumer_secret)
self.token = oauth.Token(key=oauth_token, secret=oauth_secret)
def get(self, url, params):
Issues a GET request against the API, properly formatting the params
:param url: a string, the url you are requesting
:param params: a dict, the key-value of all the paramaters needed
in the request
:returns: a dict parsed of the JSON response
url = + url
if params:
url = url + "?" + urllib.urlencode(params)
client = oauth.Client(self.consumer, self.token)
client.follow_redirects = False
resp, content = client.request(url, method="GET", redirections=False)
except RedirectLimit, e:
resp, content = e.args
return self.json_parse(content)
def post(self, url, params={}, files=[]):
Issues a POST request against the API, allows for multipart data uploads
:param url: a string, the url you are requesting
:param params: a dict, the key-value of all the parameters needed
in the request
:param files: a list, the list of tuples of files
:returns: a dict parsed of the JSON response
url = + url
if files:
return self.post_multipart(url, params, files)
client = oauth.Client(self.consumer, self.token)
resp, content = client.request(url, method="POST", body=urllib.urlencode(params))
return self.json_parse(content)
except urllib2.HTTPError, e:
return self.json_parse(
def json_parse(self, content):
Wraps and abstracts content validation and JSON parsing
to make sure the user gets the correct response.
:param content: The content returned from the web request to be parsed as json
:returns: a dict of the json response
data = json.loads(content)
except ValueError, e:
data = {'meta': { 'status': 500, 'msg': 'Server Error'}, 'response': {"error": "Malformed JSON or HTML was returned."}}
#We only really care about the response if we succeed
#and the error if we fail
if data['meta']['status'] in [200, 201, 301]:
return data['response']
return data
def post_multipart(self, url, params, files):
Generates and issues a multipart request for data files
:param url: a string, the url you are requesting
:param params: a dict, a key-value of all the parameters
:param files: a list, the list of tuples for your data
:returns: a dict parsed from the JSON response
#combine the parameters with the generated oauth params
params = dict(params.items() + self.generate_oauth_params().items())
faux_req = oauth.Request(method="POST", url=url, parameters=params)
faux_req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), self.consumer, self.token)
params = dict(parse_qsl(faux_req.to_postdata()))
content_type, body = self.encode_multipart_formdata(params, files)
headers = {'Content-Type': content_type, 'Content-Length': str(len(body))}
#Do a bytearray of the body and everything seems ok
r = urllib2.Request(url, bytearray(body), headers)
content = urllib2.urlopen(r).read()
return self.json_parse(content)
def encode_multipart_formdata(self, fields, files):
Properly encodes the multipart body of the request
:param fields: a dict, the parameters used in the request
:param files: a list of tuples containing information about the files
:returns: the content for the body and the content-type value
import mimetools
import mimetypes
BOUNDARY = mimetools.choose_boundary()
CRLF = '\r\n'
L = []
for (key, value) in fields.items():
L.append('--' + BOUNDARY)
L.append('Content-Disposition: form-data; name="%s"' % key)
for (key, filename, value) in files:
L.append('--' + BOUNDARY)
L.append('Content-Disposition: form-data; name="%s"; filename="%s"' % (key, filename))
L.append('Content-Type: %s' % mimetypes.guess_type(filename)[0] or 'application/octet-stream')
L.append('Content-Transfer-Encoding: binary')
L.append('--' + BOUNDARY + '--')
body = CRLF.join(L)
content_type = 'multipart/form-data; boundary=%s' % BOUNDARY
return content_type, body
def generate_oauth_params(self):
Generates the oauth parameters needed for multipart/form requests
:returns: a dictionary of the proper headers that can be used
in the request
params = {
'oauth_version': "1.0",
'oauth_nonce': oauth.generate_nonce(),
'oauth_timestamp': int(time.time()),
'oauth_token': self.token.key,
'oauth_consumer_key': self.consumer.key
return params
BIN +6.28 KB pytumblr/request.pyc
Binary file not shown.
@@ -0,0 +1,3 @@
@@ -0,0 +1,6 @@
# get your consumer keys from
TUMBLR_CONSUMER_KEY = 'key goes here'
TUMBLR_CONSUMER_SECRET = 'key goes here'
DEBUG = True
SECRET_KEY = 'development'
@@ -0,0 +1,4 @@
html {
margin: 0;
padding: 0;
@@ -0,0 +1,31 @@
# Import a Wordpress Blog into Tumblr: wp2tumblr
wp2tumblr is a simple [Flask]( app that will import a Wordpress XML export into Tumblr with publishing dates intact.
# Alpha Version
This script is under active development and has minimal testing. Use at your own risk.
## Requirements
* [Python](
* [pip](
## Usage
1. Clone the wp2tumblr repo.
2. Visit and register a new application. Set the default callback url to ``````. The rest of the fields can be filled in however you want.
3. Copy to and fill in your client keys.
4. Export the path to the settings file: ```$ export WP2TUMBLR_SETTINGS=/path/to/```
5. Start the server ```$ python```
6. Open your browser to and follow the instructions
## Help
Submit a [GitHub Issues request](
This software is made available under the open source MIT License. © 2014 [Jon Thornton](
@@ -0,0 +1,12 @@
{% extends "layout.html" %}
{% block body %}
<h1>wp2tumblr &ndash; Wordpress to Tumblr</h1>
{% if userinfo %}
{% for blog in userinfo['user']['blogs'] %}
<div><a href="/upload?tumblog_name={{ blog['name'] }}">{{ blog['name'] }}</a></div>
{% endfor %}
{% else %}
<div><a href="/login">Sign in to Tumblr</a></div>
{% endif %}
{% endblock %}
@@ -0,0 +1,17 @@
<!doctype html>
<title>wp2tumblr &ndash; Wordpress to Tumblr</title>
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='style.css') }}">
<div id="wrapper">
{% for message in get_flashed_messages() %}
<div class=flash>{{ message }}</div>
{% endfor %}
{% block body %}{% endblock %}
@@ -0,0 +1,19 @@
{% extends "layout.html" %}
{% block body %}
<h1>wp2tumblr &ndash; Wordpress to Tumblr</h1>
<div>Blog: {{ bloginfo['blog']['title'] }}</div>
<div>URL: {{ bloginfo['blog']['url'] }}</div>
<form method="post" action="/upload" enctype="multipart/form-data">
<label for="tumblog_name">Wordpress export XML file</label>
<input type="file" name="wordpress_xml" />
<input type="hidden" name="tumblog_name" value="{{ tumblog_name }}" />
<button type="submit">Submit</button>
{% endblock %}
Oops, something went wrong.

0 comments on commit f9a7e7a

Please sign in to comment.