diff --git a/pingen/pingen.py b/pingen/pingen.py
index 66f4af4..0d84856 100644
--- a/pingen/pingen.py
+++ b/pingen/pingen.py
@@ -28,6 +28,14 @@ from requests.packages.urllib3.filepost import encode_multipart_formdata
_logger = logging.getLogger(__name__)
+POST_SENDING_STATUS = {
+ 100: 'Ready/Pending',
+ 101: 'Processing',
+ 102: 'Waiting for confirmation',
+ 200: 'Sent',
+ 300: 'Some error occured and object wasn\'t sent',
+ 400: 'Sending cancelled',
+}
class PingenException(RuntimeError):
"""There was an ambiguous exception that occurred while handling your
@@ -42,7 +50,6 @@ class APIError(PingenException):
"""An Error occured with the pingen API"""
-
class Pingen(object):
""" Interface to pingen.com API
"""
@@ -90,7 +97,9 @@ class Pingen(object):
response = method(complete_url, **kwargs)
if not response.ok:
- raise ConnectionError(response.error)
+ raise ConnectionError(
+ "%s: %s" % (response.json['errorcode'],
+ response.json['errormessage']))
if response.json['error']:
raise APIError(
@@ -167,3 +176,24 @@ class Pingen(object):
return response.json['id']
+ def post_infos(self, post_id):
+ """ Return the information of a post
+
+ :param int post_id: id of the document to send
+ :return: dict of infos of the post
+ """
+ response = self._send(
+ requests.get,
+ 'post/get',
+ params={'id': post_id})
+
+ return response.json['item']
+
+ @staticmethod
+ def is_posted(post_infos):
+ """ return True if the post has been sent
+
+ :param dict post_infos: post infos returned by `post_infos`
+ """
+ return post_infos['status'] == 200
+
diff --git a/pingen/pingen_task.py b/pingen/pingen_task.py
index 02afde7..3166fac 100644
--- a/pingen/pingen_task.py
+++ b/pingen/pingen_task.py
@@ -20,17 +20,17 @@
##############################################################################
import logging
-import datetime
from cStringIO import StringIO
from openerp.osv import osv, orm, fields
from openerp import tools
from openerp.tools.translate import _
-from .pingen import Pingen, APIError, ConnectionError
+from .pingen import Pingen, APIError, ConnectionError, POST_SENDING_STATUS
# TODO should be configurable
TOKEN = '6bc041af6f02854461ef31c2121ef853'
+STAGING = True
_logger = logging.getLogger(__name__)
@@ -58,15 +58,28 @@ class pingen_task(orm.Model):
('pingen_error', 'Pingen Error'),
('canceled', 'Canceled')],
string='State', readonly=True, required=True),
- 'date': fields.datetime('Creation Date'),
- 'push_date': fields.datetime('Push Date'),
+ 'date': fields.datetime('Creation Date', readonly=True),
+ 'push_date': fields.datetime('Push Date', readonly=True),
+
+ # for `error` and `pingen_error` states when we push
'last_error_message': fields.text('Error Message', readonly=True),
+
+ # pingen IDs
'pingen_id': fields.integer(
- 'Pingen ID',
+ 'Pingen ID', readonly=True,
help="ID of the document in the Pingen Documents"),
'post_id': fields.integer(
- 'Pingen Post ID',
+ 'Pingen Post ID', readonly=True,
help="ID of the document in the Pingen Sendcenter"),
+
+ # sendcenter infos
+ 'post_status': fields.char('Post Status', size=128, readonly=True),
+ 'parsed_address': fields.text('Parsed Address', readonly=True),
+ 'cost': fields.float('Cost', readonly=True),
+ 'currency_id': fields.many2one('res.currency', 'Currency', readonly=True),
+ 'country_id': fields.many2one('res.country', 'Country', readonly=True),
+ 'send_date': fields.datetime('Date of sending', readonly=True),
+ 'pages': fields.integer('Pages', readonly=True),
}
_defaults = {
@@ -79,6 +92,12 @@ class pingen_task(orm.Model):
'Only one Pingen task is allowed per attachment.'),
]
+ def _pingen(self, cr, uid, ids, context=None):
+ """ Return a Pingen instance to work on """
+ assert not ids, "ids is there by convention, should not be used"
+ # TODO parameterize
+ return Pingen(TOKEN, staging=STAGING)
+
def _push_to_pingen(self, cr, uid, task, context=None):
""" Push a document to pingen.com """
attachment_obj = self.pool.get('ir.attachment')
@@ -88,31 +107,32 @@ class pingen_task(orm.Model):
pingen = self._pingen(cr, uid, [], context=context)
try:
- doc_id, post_id, __ = pingen.push_document(
+ doc_id, post_id, infos = pingen.push_document(
task.datas_fname,
StringIO(decoded_document),
task.pingen_send,
task.pingen_speed,
task.pingen_color)
except ConnectionError as e:
- _logger.exception('Connection Error when pushing Pingen Task %s to %s.' %
+ _logger.exception(
+ 'Connection Error when pushing Pingen Task %s to %s.' %
(task.id, pingen.url))
raise
except APIError as e:
- _logger.error('API Error when pushing Pingen Task %s to %s.' %
+ _logger.error(
+ 'API Error when pushing Pingen Task %s to %s.' %
(task.id, pingen.url))
raise
- now = datetime.datetime.now().strftime(tools.DEFAULT_SERVER_DATETIME_FORMAT)
task.write(
{'last_error_message': False,
'state': 'sendcenter' if post_id else 'pushed',
- 'push_date': now,
+ 'push_date': infos['date'],
'pingen_id': doc_id,
'post_id': post_id},
context=context)
- _logger.info('Pingen Task %s pushed to %s' % (task.id, pingen.url))
+ _logger.info('Pingen Task %s: pushed to %s' % (task.id, pingen.url))
def push_to_pingen(self, cr, uid, ids, context=None):
""" Push a document to pingen.com
@@ -137,26 +157,29 @@ class pingen_task(orm.Model):
'\n%s') % (task.name, e))
return True
- def _push_to_pingen_with_logs(self, cr, uid, ids, context=None):
+ def _push_and_send_to_pingen_silent(self, cr, uid, ids, context=None):
""" Push a document to pingen.com
Instead of raising, store the error in the pingen.document
"""
+ if not ids:
+ ids = self.search(
+ cr, uid,
+ # do not retry pingen_error, they should be treated manually
+ [('state', 'in', ['pending', 'pushed', 'error'])],
+ context=context)
for task in self.browse(cr, uid, ids, context=context):
try:
- self._push_to_pingen(cr, uid, task, context=context)
+ if not task.pingen_id:
+ self._push_to_pingen(cr, uid, task, context=context)
+ if not task.post_id and task.pingen_send:
+ self._ask_pingen_send(cr, uid, task, context=context)
except ConnectionError as e:
task.write({'last_error_message': e, 'state': 'error'}, context=context)
except APIError as e:
task.write({'last_error_message': e, 'state': 'pingen_error'}, context=context)
- return True
- def _pingen(self, cr, uid, ids, context=None):
- """
- """
- assert not ids, "ids is there by convention, should not be used"
- # TODO parameterize
- return Pingen(TOKEN, staging=True)
+ return True
def _ask_pingen_send(self, cr, uid, task, context=None):
""" For a document already pushed to pingen, ask to send it.
@@ -186,7 +209,7 @@ class pingen_task(orm.Model):
'state': 'sendcenter',
'post_id': post_id},
context=context)
- _logger.info('Pingen Task %s asked for sending to %s' % (task.id, pingen.url))
+ _logger.info('Pingen Task %s: asked for sending to %s' % (task.id, pingen.url))
return True
@@ -211,3 +234,84 @@ class pingen_task(orm.Model):
'\n%s') % (task.name, e))
return True
+ def _update_post_infos(self, cr, uid, task, context=None):
+ """ Update the informations from pingen of a document in the Sendcenter
+ """
+ if not task.post_id:
+ return
+
+ pingen = self._pingen(cr, uid, [], context=context)
+ try:
+ post_infos = pingen.post_infos(task.post_id)
+ except ConnectionError as e:
+ _logger.exception('Connection Error when asking for sending Pingen Task %s to %s.' %
+ (task.id, pingen.url))
+ raise
+ except APIError as e:
+ _logger.exception('API Error when asking for sending Pingen Task %s to %s.' %
+ (task.id, pingen.url))
+ raise
+
+ currency_ids = self.pool.get('res.currency').search(
+ cr, uid, [('name', '=', post_infos['currency'])], context=context)
+ country_ids = self.pool.get('res.country').search(
+ cr, uid, [('code', '=', post_infos['country'])], context=context)
+ vals = {
+ 'post_status': POST_SENDING_STATUS[post_infos['status']],
+ 'cost': post_infos['cost'],
+ 'currency_id': currency_ids[0] if currency_ids else False,
+ 'parsed_address': post_infos['address'],
+ 'country_id': country_ids[0] if country_ids else False,
+ 'send_date': post_infos['date'],
+ 'pages': post_infos['pages'],
+ }
+ if pingen.is_posted(post_infos):
+ vals['state'] = 'sent'
+
+ task.write(vals, context=context)
+ _logger.info('Pingen Task %s: status updated' % task.id)
+
+ def _update_post_infos_silent(self, cr, uid, ids, context=None):
+ """ Update the informations from pingen of a document in the Sendcenter
+
+ Do not raise errors, only skip the update of the record.
+ """
+ if not ids:
+ ids = self.search(
+ cr, uid,
+ [('state', '=', 'sendcenter')],
+ context=context)
+
+ for task in self.browse(cr, uid, ids, context=context):
+ try:
+ self._update_post_infos(cr, uid, task, context=context)
+ except (ConnectionError, APIError):
+ # Intended silented exception, we can consider that it's not
+ # important if the update not worked, that's
+ # only informative, and it will be retried the next time
+ # In any case, the error has been by _update_post_infos
+ pass
+ return True
+
+ def update_post_infos(self, cr, uid, ids, context=None):
+ """ Update the informations from pingen of a document in the Sendcenter
+
+ Wrapper method for multiple ids (when triggered from button for
+ instance) for public interface.
+ """
+ for task in self.browse(cr, uid, ids, context=context):
+ try:
+ self._update_post_infos(cr, uid, task, context=context)
+ except ConnectionError as e:
+ raise osv.except_osv(
+ _('Pingen Connection Error'),
+ _('Connection Error when updating the status of Document %s'
+ ' from Pingen') % task.name)
+
+ except APIError as e:
+ raise osv.except_osv(
+ _('Pingen Error'),
+ _('Error when updating the status of Document %s from Pingen: '
+ '\n%s') % (task.name, e))
+ return True
+
diff --git a/pingen/pingen_task_view.xml b/pingen/pingen_task_view.xml
index 5f59421..f036d4e 100644
--- a/pingen/pingen_task_view.xml
+++ b/pingen/pingen_task_view.xml
@@ -46,15 +46,33 @@
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+