From b88f5c556ac3fcd1a8593d16b7b91b100fce9bfb Mon Sep 17 00:00:00 2001 From: "Guewen Baconnier @ Camptocamp" Date: Fri, 23 Nov 2012 12:15:00 +0100 Subject: [PATCH] [FIX] the data must be JSON-encoded when we call the document/upload endpoint. Need to build the body instead of data and files params --- pingen/pingen.py | 37 +++++++++++++++++++++++++++---------- pingen/pingen_task.py | 8 ++++++-- 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/pingen/pingen.py b/pingen/pingen.py index ab7ce56..77f46af 100644 --- a/pingen/pingen.py +++ b/pingen/pingen.py @@ -22,6 +22,9 @@ import requests import logging import urlparse +import json + +from requests.packages.urllib3.filepost import encode_multipart_formdata _logger = logging.getLogger(__name__) @@ -95,10 +98,11 @@ class Pingen(object): return response - def push_document(self, document, send=None, speed=None, color=None): + def push_document(self, filename, filestream, send=None, speed=None, color=None): """ Upload a document to pingen.com and eventually ask to send it - :param tuple document: (filename, file stream) to push + :param str filename: name of the file to push + :param StringIO filestream: file to push :param boolean send: if True, the document will be sent by pingen.com :param int/str speed: sending speed of the document if it is send 1 = Priority, 2 = Economy @@ -108,24 +112,37 @@ class Pingen(object): 2. post_id on pingen.com if it has been sent or None 3. dict of the created item on pingen (details) """ - document = {'file': document} data = { 'send': send, 'speed': speed, 'color': color, } + + # we cannot use the `files` param alongside + # with the `datas`param when data is a + # JSON-encoded data. We have to construct + # the entire body and send it to `data` + # https://github.com/kennethreitz/requests/issues/950 + formdata = { + 'file': (filename, filestream.read()), + 'data': json.dumps(data), + } + + multipart, content_type = encode_multipart_formdata(formdata) + response = self._send( requests.post, 'document/upload', - data=data, - files=document) + headers={'Content-Type': content_type}, + data=multipart) - json = response.json + rjson = response.json - document_id = json['id'] - # confusing name but send_id is the posted id - posted_id = json.get('send', {}).get('send_id') - item = json['item'] + document_id = rjson['id'] + if rjson.get('send'): + # confusing name but send_id is the posted id + posted_id = rjson['send'][0]['send_id'] + item = rjson['item'] return document_id, posted_id, item diff --git a/pingen/pingen_task.py b/pingen/pingen_task.py index 3e4b60d..ffe607e 100644 --- a/pingen/pingen_task.py +++ b/pingen/pingen_task.py @@ -91,10 +91,14 @@ class pingen_task(orm.Model): success = False # parameterize pingen = Pingen(TOKEN, staging=True) - doc = (task.datas_fname, StringIO(decoded_document)) + doc = (task.datas_fname, ) try: doc_id, post_id, __ = pingen.push_document( - doc, task.pingen_send, task.pingen_speed, task.pingen_color) + task.datas_fname, + StringIO(decoded_document), + task.pingen_send, + task.pingen_speed, + task.pingen_color) except ConnectionError as e: # we can continue and it will be retried the next time _logger.exception('Connection Error when pushing Pingen Task %s to %s.' %