From 05293402432b3acdc45b36f7d8e2cef0d43a2fa9 Mon Sep 17 00:00:00 2001 From: Anna Janiszewska Date: Thu, 26 Jan 2023 12:55:11 +0100 Subject: [PATCH] [IMP] pingen: pre-commit stuff --- pingen/__init__.py | 3 +- pingen/__manifest__.py | 47 ++-- pingen/data/pingen_data.xml | 14 +- pingen/models/__init__.py | 1 - pingen/models/base_config_settings.py | 7 +- pingen/models/ir_attachment.py | 73 +++--- pingen/models/pingen.py | 128 +++++----- pingen/models/pingen_document.py | 333 ++++++++++++++------------ pingen/models/res_company.py | 12 +- pingen/views/base_config_settings.xml | 8 +- pingen/views/ir_attachment_view.xml | 29 ++- pingen/views/pingen_document_view.xml | 191 +++++++++------ requirements.txt | 1 + setup/pingen/odoo/__init__.py | 1 + setup/pingen/odoo/addons/__init__.py | 1 + setup/pingen/odoo/addons/pingen | 1 + setup/pingen/setup.py | 6 + 17 files changed, 471 insertions(+), 385 deletions(-) create mode 100644 setup/pingen/odoo/__init__.py create mode 100644 setup/pingen/odoo/addons/__init__.py create mode 120000 setup/pingen/odoo/addons/pingen create mode 100644 setup/pingen/setup.py diff --git a/pingen/__init__.py b/pingen/__init__.py index d93cc0f..b90e099 100644 --- a/pingen/__init__.py +++ b/pingen/__init__.py @@ -1,6 +1,5 @@ -# -*- coding: utf-8 -*- # Author: Guewen Baconnier # Copyright 2012-2017 Camptocamp SA # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -from . import models \ No newline at end of file +from . import models diff --git a/pingen/__manifest__.py b/pingen/__manifest__.py index d292266..1cb10e3 100644 --- a/pingen/__manifest__.py +++ b/pingen/__manifest__.py @@ -1,30 +1,29 @@ -# -*- coding: utf-8 -*- # Author: Guewen Baconnier # Copyright 2012-2017 Camptocamp SA # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). { - 'name': 'pingen.com integration', - 'version': '10.0.1.0.0', - 'author': "Camptocamp,Odoo Community Association (OCA)", - 'maintainer': 'Camptocamp', - 'license': 'AGPL-3', - 'category': 'Reporting', - 'complexity': 'easy', - 'depends': ['base_setup'], - 'external_dependencies': { - 'python': ['requests'], - }, - 'website': 'http://www.camptocamp.com', - 'data': [ - 'views/ir_attachment_view.xml', - 'views/pingen_document_view.xml', - 'data/pingen_data.xml', - 'views/base_config_settings.xml', - 'security/ir.model.access.csv', - ], - 'tests': [], - 'installable': True, - 'auto_install': False, - 'application': True, + "name": "pingen.com integration", + "version": "10.0.1.0.0", + "author": "Camptocamp,Odoo Community Association (OCA)", + "maintainer": "Camptocamp", + "license": "AGPL-3", + "category": "Reporting", + "complexity": "easy", + "depends": ["base_setup"], + "external_dependencies": { + "python": ["requests"], + }, + "website": "https://github.com/OCA/report-print-send", + "data": [ + "views/ir_attachment_view.xml", + "views/pingen_document_view.xml", + "data/pingen_data.xml", + "views/base_config_settings.xml", + "security/ir.model.access.csv", + ], + "tests": [], + "installable": True, + "auto_install": False, + "application": True, } diff --git a/pingen/data/pingen_data.xml b/pingen/data/pingen_data.xml index 5c0572d..d9f98d0 100644 --- a/pingen/data/pingen_data.xml +++ b/pingen/data/pingen_data.xml @@ -1,15 +1,15 @@ - + Run Pingen Document Push - - + + 1 hours -1 - + pingen.document _push_and_send_to_pingen_cron () @@ -17,12 +17,12 @@ Run Pingen Document Update - - + + 1 days -1 - + pingen.document _update_post_infos_cron () diff --git a/pingen/models/__init__.py b/pingen/models/__init__.py index 5cce2da..abb9a90 100644 --- a/pingen/models/__init__.py +++ b/pingen/models/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2018 Camptocamp SA # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from . import ir_attachment diff --git a/pingen/models/base_config_settings.py b/pingen/models/base_config_settings.py index c1d43c1..2b1b151 100644 --- a/pingen/models/base_config_settings.py +++ b/pingen/models/base_config_settings.py @@ -1,11 +1,10 @@ -# -*- coding: utf-8 -*- # Copyright 2018 Camptocamp SA # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from odoo import fields, models class BaseConfigSettings(models.TransientModel): - _inherit = 'base.config.settings' + _inherit = "base.config.settings" - pingen_token = fields.Char(related='company_id.pingen_token') - pingen_staging = fields.Boolean(related='company_id.pingen_staging') + pingen_token = fields.Char(related="company_id.pingen_token") + pingen_staging = fields.Boolean(related="company_id.pingen_staging") diff --git a/pingen/models/ir_attachment.py b/pingen/models/ir_attachment.py index c8791ee..f88169a 100644 --- a/pingen/models/ir_attachment.py +++ b/pingen/models/ir_attachment.py @@ -1,40 +1,43 @@ -# -*- coding: utf-8 -*- # Author: Guewen Baconnier # Copyright 2012-2017 Camptocamp SA # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -import requests import base64 -from odoo import models, fields, _, api +import requests + +from odoo import _, api, fields, models from odoo.exceptions import UserError class IrAttachment(models.Model): - _inherit = 'ir.attachment' + _inherit = "ir.attachment" send_to_pingen = fields.Boolean("Send to Pingen.com") pingen_document_ids = fields.One2many( - 'pingen.document', 'attachment_id', - string='Pingen Document', readonly=True) + "pingen.document", "attachment_id", string="Pingen Document", readonly=True + ) pingen_send = fields.Boolean( - 'Send', help="Defines if a document is merely uploaded or also sent", - default=True) + "Send", + help="Defines if a document is merely uploaded or also sent", + default=True, + ) pingen_speed = fields.Selection( - [('1', 'Priority'), ('2', 'Economy')], - 'Speed', default='2', - help='Defines the sending speed if the document is automatically sent') - pingen_color = fields.Selection([('0', 'B/W'), ('1', 'Color')], - 'Type of print', - default='0') + [("1", "Priority"), ("2", "Economy")], + "Speed", + default="2", + help="Defines the sending speed if the document is automatically sent", + ) + pingen_color = fields.Selection( + [("0", "B/W"), ("1", "Color")], "Type of print", default="0" + ) def _prepare_pingen_document_vals(self): - return {'attachment_id': self.id, - 'config': 'created from attachment'} + return {"attachment_id": self.id, "config": "created from attachment"} def _handle_pingen_document(self): - """ Reponsible of the related ``pingen.document`` + """Reponsible of the related ``pingen.document`` when the ``send_to_pingen`` field is modified. Only one pingen document can be created per attachment. @@ -48,52 +51,52 @@ class IrAttachment(models.Model): * If it has already been pushed to pingen.com, raises an `osv.except_osv` exception """ - pingen_document_obj = self.env['pingen.document'] - document = self.pingen_document_ids[0] if \ - self.pingen_document_ids else None + pingen_document_obj = self.env["pingen.document"] + document = self.pingen_document_ids[0] if self.pingen_document_ids else None if self.send_to_pingen: if document: - document.write({'state': 'pending'}) + document.write({"state": "pending"}) else: - pingen_document_obj.create( - self._prepare_pingen_document_vals()) + pingen_document_obj.create(self._prepare_pingen_document_vals()) else: if document: - if document.state == 'pushed': + if document.state == "pushed": raise UserError( - _('Error. The attachment %s is ' - 'already pushed to pingen.com.') % self.name) - document.write({'state': 'canceled'}) + _( + "Error. The attachment %s is " + "already pushed to pingen.com." + ) + % self.name + ) + document.write({"state": "canceled"}) return @api.model def create(self, vals): attachment = super(IrAttachment, self).create(vals) - if 'send_to_pingen' in vals: + if "send_to_pingen" in vals: attachment._handle_pingen_document() return attachment @api.multi def write(self, vals): res = super(IrAttachment, self).write(vals) - if 'send_to_pingen' in vals: + if "send_to_pingen" in vals: for attachment in self: attachment._handle_pingen_document() return res def _decoded_content(self): - """ Returns the decoded content of an attachment (stored or url) + """Returns the decoded content of an attachment (stored or url) Returns None if the type is 'url' and the url is not reachable. """ decoded_document = None - if self.type == 'binary': + if self.type == "binary": decoded_document = base64.b64decode(self.datas) - elif self.type == 'url': + elif self.type == "url": response = requests.get(self.url) if response.ok: decoded_document = requests.content else: - raise UserError( - _('The type of attachment %s is not handled') - % self.type) + raise UserError(_("The type of attachment %s is not handled") % self.type) return decoded_document diff --git a/pingen/models/pingen.py b/pingen/models/pingen.py index 0cb5964..2ce8dfa 100644 --- a/pingen/models/pingen.py +++ b/pingen/models/pingen.py @@ -1,35 +1,34 @@ -# -*- coding: utf-8 -*- # Author: Guewen Baconnier # Copyright 2012-2017 Camptocamp SA # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -import requests -import logging -import urlparse import json -import pytz - +import logging from datetime import datetime + +import pytz +import requests +import urlparse 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', - 1: 'Sent', - 300: 'Some error occured and object wasn\'t sent', - 400: 'Sending cancelled', + 100: "Ready/Pending", + 101: "Processing", + 102: "Waiting for confirmation", + 1: "Sent", + 300: "Some error occured and object wasn't sent", + 400: "Sending cancelled", } -DATETIME_FORMAT = '%Y-%m-%d %H:%M:%S' # this is the format used by pingen API +DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S" # this is the format used by pingen API -TZ = pytz.timezone('Europe/Zurich') # this is the timezone of the pingen API +TZ = pytz.timezone("Europe/Zurich") # this is the timezone of the pingen API def pingen_datetime_to_utc(dt): - """ Convert a date/time used by pingen.com to UTC timezone + """Convert a date/time used by pingen.com to UTC timezone :param dt: pingen date/time as string (as received from the API) to convert to UTC @@ -51,7 +50,7 @@ class APIError(PingenException): class Pingen(object): - """ Interface to the pingen.com API """ + """Interface to the pingen.com API""" def __init__(self, token, staging=True): self._token = token @@ -62,16 +61,16 @@ class Pingen(object): @property def url(self): if self.staging: - return 'https://stage-api.pingen.com' - return 'https://api.pingen.com' + return "https://stage-api.pingen.com" + return "https://api.pingen.com" @property def session(self): - """ Build a requests session """ + """Build a requests session""" if self._session is not None: return self._session self._session = requests.Session() - self._session.params = {'token': self._token} + self._session.params = {"token": self._token} self._session.verify = not self.staging return self._session @@ -82,12 +81,12 @@ class Pingen(object): self.close() def close(self): - """Dispose of any internal state. """ + """Dispose of any internal state.""" if self._session: self._session.close() def _send(self, method, endpoint, **kwargs): - """ Send a request to the pingen API using requests + """Send a request to the pingen API using requests Add necessary boilerplate to call pingen.com API (authentication, configuration, ...) @@ -99,29 +98,25 @@ class Pingen(object): p_url = urlparse.urljoin(self.url, endpoint) - if endpoint == 'document/get': - complete_url = '{}{}{}{}{}'.format(p_url, - '/id/', - kwargs['params']['id'], - '/token/', - self._token) + if endpoint == "document/get": + complete_url = "{}{}{}{}{}".format( + p_url, "/id/", kwargs["params"]["id"], "/token/", self._token + ) else: - complete_url = '{}{}{}'.format(p_url, - '/token/', - self._token) + complete_url = "{}{}{}".format(p_url, "/token/", self._token) response = method(complete_url, **kwargs) - if response.json()['error']: + if response.json()["error"]: raise APIError( - "%s: %s" % (response.json()['errorcode'], - response.json()['errormessage'])) + "%s: %s" + % (response.json()["errorcode"], response.json()["errormessage"]) + ) return response - def push_document(self, filename, filestream, - send=None, speed=None, color=None): - """ Upload a document to pingen.com and eventually ask to send it + 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 str filename: name of the file to push :param StringIO filestream: file to push @@ -135,10 +130,10 @@ class Pingen(object): 3. dict of the created item on pingen (details) """ data = { - 'send': send, - 'speed': speed, - 'color': color, - } + "send": send, + "speed": speed, + "color": color, + } # we cannot use the `files` param alongside # with the `datas`param when data is a @@ -146,30 +141,31 @@ class Pingen(object): # the entire body and send it to `data` # https://github.com/kennethreitz/requests/issues/950 formdata = { - 'file': (filename, filestream.read()), - 'data': json.dumps(data), - } + "file": (filename, filestream.read()), + "data": json.dumps(data), + } multipart, content_type = encode_multipart_formdata(formdata) response = self._send( self.session.post, - 'document/upload', - headers={'Content-Type': content_type}, - data=multipart) + "document/upload", + headers={"Content-Type": content_type}, + data=multipart, + ) rjson = response.json() - document_id = rjson['id'] - if rjson.get('send'): + 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'] + posted_id = rjson["send"][0]["send_id"] + item = rjson["item"] return document_id, posted_id, item def send_document(self, document_id, speed=None, color=None): - """ Send a uploaded document to pingen.com + """Send a uploaded document to pingen.com :param int document_id: id of the document to send :param int/str speed: sending speed of the document if it is send @@ -178,34 +174,32 @@ class Pingen(object): :return: id of the post on pingen.com """ data = { - 'speed': speed, - 'color': color, - } + "speed": speed, + "color": color, + } response = self._send( self.session.post, - 'document/send', - params={'id': document_id}, - data={'data': json.dumps(data)}) + "document/send", + params={"id": document_id}, + data={"data": json.dumps(data)}, + ) - return response.json()['id'] + return response.json()["id"] def post_infos(self, post_id): - """ Return the information of a post + """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( - self.session.get, - 'document/get', - params={'id': post_id}) + response = self._send(self.session.get, "document/get", params={"id": post_id}) - return response.json()['item'] + return response.json()["item"] @staticmethod def is_posted(post_infos): - """ return True if the post has been sent + """return True if the post has been sent :param dict post_infos: post infos returned by `post_infos` """ - return post_infos['status'] == 1 + return post_infos["status"] == 1 diff --git a/pingen/models/pingen_document.py b/pingen/models/pingen_document.py index 48cef4f..e54c6a0 100644 --- a/pingen/models/pingen_document.py +++ b/pingen/models/pingen_document.py @@ -1,77 +1,85 @@ -# -*- coding: utf-8 -*- # Author: Guewen Baconnier # Copyright 2012-2017 Camptocamp SA # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). import logging -from requests.exceptions import ConnectionError + from cStringIO import StringIO +from requests.exceptions import ConnectionError import odoo -from odoo import models, fields, _ +from odoo import _, fields, models from odoo.exceptions import UserError -from .pingen import APIError, pingen_datetime_to_utc, POST_SENDING_STATUS +from .pingen import POST_SENDING_STATUS, APIError, pingen_datetime_to_utc _logger = logging.getLogger(__name__) class PingenDocument(models.Model): - """ A pingen document is the state of the synchronization of + """A pingen document is the state of the synchronization of an attachment with pingen.com It stores the configuration and the current state of the synchronization. It also serves as a queue of documents to push to pingen.com """ - _name = 'pingen.document' - _inherits = {'ir.attachment': 'attachment_id'} + _name = "pingen.document" + _inherits = {"ir.attachment": "attachment_id"} attachment_id = fields.Many2one( - 'ir.attachment', 'Document', - required=True, readonly=True, - ondelete='cascade') + "ir.attachment", "Document", required=True, readonly=True, ondelete="cascade" + ) state = fields.Selection( - [('pending', 'Pending'), - ('pushed', 'Pushed'), - ('sendcenter', 'In Sendcenter'), - ('sent', 'Sent'), - ('error', 'Connection Error'), - ('pingen_error', 'Pingen Error'), - ('canceled', 'Canceled')], - string='State', readonly=True, - required=True, default='pending') - push_date = fields.Datetime('Push Date', readonly=True) + [ + ("pending", "Pending"), + ("pushed", "Pushed"), + ("sendcenter", "In Sendcenter"), + ("sent", "Sent"), + ("error", "Connection Error"), + ("pingen_error", "Pingen Error"), + ("canceled", "Canceled"), + ], + string="State", + readonly=True, + required=True, + default="pending", + ) + 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) + last_error_message = fields.Text("Error Message", readonly=True) # pingen IDs pingen_id = fields.Integer( - 'Pingen ID', readonly=True, - help="ID of the document in the Pingen Documents") + "Pingen ID", readonly=True, help="ID of the document in the Pingen Documents" + ) post_id = fields.Integer( - 'Pingen Post ID', readonly=True, - help="ID of the document in the Pingen Sendcenter") + "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) + 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) _sql_constraints = [ - ('pingen_document_attachment_uniq', - 'unique (attachment_id)', - 'Only one Pingen document is allowed per attachment.'), + ( + "pingen_document_attachment_uniq", + "unique (attachment_id)", + "Only one Pingen document is allowed per attachment.", + ), ] def _get_pingen_session(self): - """ Returns a pingen session for a user """ + """Returns a pingen session for a user""" return self.company_id._pingen() def _push_to_pingen(self, pingen=None): - """ Push a document to pingen.com + """Push a document to pingen.com :param Pingen pingen: optional pingen object to reuse session """ decoded_document = self.attachment_id._decoded_content() @@ -83,36 +91,41 @@ class PingenDocument(models.Model): StringIO(decoded_document), self.pingen_send, self.pingen_speed, - self.pingen_color) + self.pingen_color, + ) except ConnectionError: _logger.exception( - 'Connection Error when pushing Pingen Document %s to %s.' % - (self.id, pingen.url)) + "Connection Error when pushing Pingen Document %s to %s." + % (self.id, pingen.url) + ) raise except APIError: _logger.error( - 'API Error when pushing Pingen Document %s to %s.' % - (self.id, pingen.url)) + "API Error when pushing Pingen Document %s to %s." + % (self.id, pingen.url) + ) raise error = False - state = 'pushed' + state = "pushed" if post_id: - state = 'sendcenter' - elif infos['requirement_failure']: - state = 'pingen_error' - error = _('The document does not meet the Pingen requirements.') - push_date = pingen_datetime_to_utc(infos['date']) + state = "sendcenter" + elif infos["requirement_failure"]: + state = "pingen_error" + error = _("The document does not meet the Pingen requirements.") + push_date = pingen_datetime_to_utc(infos["date"]) self.write( - {'last_error_message': error, - 'state': state, - 'push_date': fields.Datetime.to_string(push_date), - 'pingen_id': doc_id, - 'post_id': post_id},) - _logger.info( - 'Pingen Document %s: pushed to %s' % (self.id, pingen.url)) + { + "last_error_message": error, + "state": state, + "push_date": fields.Datetime.to_string(push_date), + "pingen_id": doc_id, + "post_id": post_id, + }, + ) + _logger.info("Pingen Document %s: pushed to %s" % (self.id, pingen.url)) def push_to_pingen(self): - """ Push a document to pingen.com + """Push a document to pingen.com Convert errors to osv.except_osv to be handled by the client. Wrapper method for multiple ids (when triggered from button for instance) for public interface. @@ -123,117 +136,126 @@ class PingenDocument(models.Model): try: session = self._get_pingen_session() self._push_to_pingen(pingen=session) - except ConnectionError as e: - state = 'error' - error_msg = _('Connection Error when asking for ' - 'sending the document %s to Pingen') % self.name + except ConnectionError: + state = "error" + error_msg = ( + _( + "Connection Error when asking for " + "sending the document %s to Pingen" + ) + % self.name + ) except APIError as e: - state = 'pingen_error' - error_msg = _('Error when asking Pingen to send the document %s: ' - '\n%s') % (self.name, e) - except Exception as e: + state = "pingen_error" + error_msg = _( + "Error when asking Pingen to send the document %s: " "\n%s" + ) % (self.name, e) + except Exception: _logger.exception( - 'Unexpected Error when updating the status of pingen.document ' - '%s: ' % self.id) - error_msg = _('Unexpected Error when updating the ' - 'status of Document %s') % self.name + "Unexpected Error when updating the status of pingen.document " + "%s: " % self.id + ) + error_msg = ( + _("Unexpected Error when updating the " "status of Document %s") + % self.name + ) finally: if error_msg: - vals = {'last_error_message': error_msg} + vals = {"last_error_message": error_msg} if state: - vals.update({'state': state}) + vals.update({"state": state}) with odoo.registry(self.env.cr.dbname).cursor() as new_cr: new_env = odoo.api.Environment( - new_cr, self.env.uid, self.env.context) + new_cr, self.env.uid, self.env.context + ) self.with_env(new_env).write(vals) raise UserError(error_msg) return True def _push_and_send_to_pingen_cron(self): - """ Push a document to pingen.com + """Push a document to pingen.com Intended to be used in a cron. Commit after each record Instead of raising, store the error in the pingen.document """ with odoo.api.Environment.manage(): with odoo.registry(self.env.cr.dbname).cursor() as new_cr: - new_env = odoo.api.Environment( - new_cr, self.env.uid, self.env.context) + new_env = odoo.api.Environment(new_cr, self.env.uid, self.env.context) # Instead of raising, store the error in the pingen.document self = self.with_env(new_env) - not_sent_docs = self.search([('state', '!=', 'sent')]) + not_sent_docs = self.search([("state", "!=", "sent")]) for document in not_sent_docs: session = document._get_pingen_session() - if document.state == 'error': + if document.state == "error": document._resolve_error() document.refresh() try: - if document.state == 'pending': + if document.state == "pending": document._push_to_pingen(pingen=session) - elif document.state == 'pushed': + elif document.state == "pushed": document._ask_pingen_send(pingen=session) except ConnectionError as e: - document.write({'last_error_message': e, - 'state': 'error'}) + document.write({"last_error_message": e, "state": "error"}) except APIError as e: - document.write({'last_error_message': e, - 'state': 'pingen_error'}) - except BaseException as e: - _logger.error('Unexpected error in pingen cron') + document.write( + {"last_error_message": e, "state": "pingen_error"} + ) + except BaseException: + _logger.error("Unexpected error in pingen cron") return True def _resolve_error(self): - """ A document as resolved, put in the correct state """ + """A document as resolved, put in the correct state""" if self.post_id: - state = 'sendcenter' + state = "sendcenter" elif self.pingen_id: - state = 'pushed' + state = "pushed" else: - state = 'pending' - self.write({'state': state}) + state = "pending" + self.write({"state": state}) def resolve_error(self): - """ A document as resolved, put in the correct state """ + """A document as resolved, put in the correct state""" for document in self: document._resolve_error() return True def _ask_pingen_send(self, pingen): - """ For a document already pushed to pingen, ask to send it. + """For a document already pushed to pingen, ask to send it. :param Pingen pingen: pingen object to reuse """ # sending has been explicitely asked so we change the option # for consistency if not self.pingen_send: - self.write({'pingen_send': True}) + self.write({"pingen_send": True}) try: post_id = pingen.send_document( - self.pingen_id, - self.pingen_speed, - self.pingen_color) + self.pingen_id, self.pingen_speed, self.pingen_color + ) except ConnectionError: _logger.exception( - 'Connection Error when asking for sending Pingen Document %s ' - 'to %s.' % (self.id, pingen.url)) + "Connection Error when asking for sending Pingen Document %s " + "to %s." % (self.id, pingen.url) + ) raise except APIError: _logger.exception( - 'API Error when asking for sending Pingen Document %s to %s.' % - (self.id, pingen.url)) + "API Error when asking for sending Pingen Document %s to %s." + % (self.id, pingen.url) + ) raise self.write( - {'last_error_message': False, - 'state': 'sendcenter', - 'post_id': post_id}) + {"last_error_message": False, "state": "sendcenter", "post_id": post_id} + ) _logger.info( - 'Pingen Document %s: asked for sending to %s' % ( - self.id, pingen.url)) + "Pingen Document %s: asked for sending to %s" % (self.id, pingen.url) + ) return True def ask_pingen_send(self): - """ For a document already pushed to pingen, ask to send it. + """For a document already pushed to pingen, ask to send it. Wrapper method for multiple ids (when triggered from button for instance) for public interface. """ @@ -241,27 +263,34 @@ class PingenDocument(models.Model): try: session = self._get_pingen_session() self._ask_pingen_send(pingen=session) - except ConnectionError as e: + except ConnectionError: raise UserError( - _('Connection Error when asking for ' - 'sending the document %s to Pingen') % self.name) + _( + "Connection Error when asking for " + "sending the document %s to Pingen" + ) + % self.name + ) except APIError as e: raise UserError( - _('Error when asking Pingen to send the document %s: ' - '\n%s') % (self.name, e)) + _("Error when asking Pingen to send the document %s: " "\n%s") + % (self.name, e) + ) - except BaseException as e: + except BaseException: _logger.exception( - 'Unexpected Error when updating the status ' - 'of pingen.document %s: ' % self.id) + "Unexpected Error when updating the status " + "of pingen.document %s: " % self.id + ) raise UserError( - _('Unexpected Error when updating the status ' - 'of Document %s') % self.name) + _("Unexpected Error when updating the status " "of Document %s") + % self.name + ) return True def _update_post_infos(self, pingen): - """ Update the informations from + """Update the informations from pingen of a document in the Sendcenter :param Pingen pingen: pingen object to reuse """ @@ -271,44 +300,43 @@ class PingenDocument(models.Model): post_infos = pingen.post_infos(self.pingen_id) except ConnectionError: _logger.exception( - 'Connection Error when asking for ' - 'sending Pingen Document %s to %s.' % - (self.id, pingen.url)) + "Connection Error when asking for " + "sending Pingen Document %s to %s." % (self.id, pingen.url) + ) raise except APIError: _logger.exception( - 'API Error when asking for sending Pingen Document %s to %s.' % - (self.id, pingen.url)) + "API Error when asking for sending Pingen Document %s to %s." + % (self.id, pingen.url) + ) raise - country = self.env['res.country'].search( - [('code', '=', post_infos['country'])]) - send_date = pingen_datetime_to_utc(post_infos['date']) + country = self.env["res.country"].search([("code", "=", post_infos["country"])]) + send_date = pingen_datetime_to_utc(post_infos["date"]) vals = { - 'post_status': POST_SENDING_STATUS[post_infos['status']], - 'parsed_address': post_infos['address'], - 'country_id': country.id, - 'send_date': fields.Datetime.to_string(send_date), - 'pages': post_infos['pages'], - 'last_error_message': False, - } + "post_status": POST_SENDING_STATUS[post_infos["status"]], + "parsed_address": post_infos["address"], + "country_id": country.id, + "send_date": fields.Datetime.to_string(send_date), + "pages": post_infos["pages"], + "last_error_message": False, + } if pingen.is_posted(post_infos): - vals['state'] = 'sent' + vals["state"] = "sent" self.write(vals) - _logger.info('Pingen Document %s: status updated' % self.id) + _logger.info("Pingen Document %s: status updated" % self.id) def _update_post_infos_cron(self): - """ Update the informations from pingen of a + """Update the informations from pingen of a document in the Sendcenter Intended to be used in a cron. Commit after each record Do not raise errors, only skip the update of the record.""" with odoo.api.Environment.manage(): with odoo.registry(self.env.cr.dbname).cursor() as new_cr: - new_env = odoo.api.Environment( - new_cr, self.env.uid, self.env.context) + new_env = odoo.api.Environment(new_cr, self.env.uid, self.env.context) # Instead of raising, store the error in the pingen.document self = self.with_env(new_env) - pushed_docs = self.search([('state', '!=', 'sent')]) + pushed_docs = self.search([("state", "!=", "sent")]) for document in pushed_docs: session = document._get_pingen_session() try: @@ -319,12 +347,12 @@ class PingenDocument(models.Model): # logged by _update_post_infos pass except BaseException as e: - _logger.error('Unexcepted error in pingen cron: %', e) + _logger.error("Unexcepted error in pingen cron: %", e) raise return True def update_post_infos(self): - """ Update the informations from pingen of a document in the Sendcenter + """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. """ @@ -332,19 +360,26 @@ class PingenDocument(models.Model): try: session = self._get_pingen_session() self._update_post_infos(pingen=session) - except ConnectionError as e: + except ConnectionError: raise UserError( - _('Connection Error when updating the status ' - 'of Document %s from Pingen') % self.name) + _( + "Connection Error when updating the status " + "of Document %s from Pingen" + ) + % self.name + ) except APIError as e: raise UserError( - _('Error when updating the status of Document %s from ' - 'Pingen: \n%s') % (self.name, e)) - except BaseException as e: + _("Error when updating the status of Document %s from " "Pingen: \n%s") + % (self.name, e) + ) + except BaseException: _logger.exception( - 'Unexpected Error when updating the status ' - 'of pingen.document %s: ' % self.id) + "Unexpected Error when updating the status " + "of pingen.document %s: " % self.id + ) raise UserError( - _('Unexpected Error when updating the status ' - 'of Document %s') % self.name) + _("Unexpected Error when updating the status " "of Document %s") + % self.name + ) return True diff --git a/pingen/models/res_company.py b/pingen/models/res_company.py index 1da79a1..e236c96 100644 --- a/pingen/models/res_company.py +++ b/pingen/models/res_company.py @@ -1,20 +1,20 @@ -# -*- coding: utf-8 -*- # Author: Guewen Baconnier # Copyright 2012-2017 Camptocamp SA # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -from odoo import models, fields +from odoo import fields, models + from .pingen import Pingen class ResCompany(models.Model): - _inherit = 'res.company' + _inherit = "res.company" - pingen_token = fields.Char('Pingen Token', size=32) - pingen_staging = fields.Boolean('Pingen Staging') + pingen_token = fields.Char("Pingen Token", size=32) + pingen_staging = fields.Boolean("Pingen Staging") def _pingen(self): - """ Return a Pingen instance to work on """ + """Return a Pingen instance to work on""" self.ensure_one() return Pingen(self.pingen_token, staging=self.pingen_staging) diff --git a/pingen/views/base_config_settings.xml b/pingen/views/base_config_settings.xml index 24287f4..ea1a0dc 100644 --- a/pingen/views/base_config_settings.xml +++ b/pingen/views/base_config_settings.xml @@ -1,14 +1,14 @@ - + base.config.settings.inherit - + base.config.settings - - + + diff --git a/pingen/views/ir_attachment_view.xml b/pingen/views/ir_attachment_view.xml index 19cb8ed..3bd5aeb 100644 --- a/pingen/views/ir_attachment_view.xml +++ b/pingen/views/ir_attachment_view.xml @@ -1,16 +1,22 @@ - + ir.attachment.pingen.view ir.attachment form - + - - - + + + @@ -18,10 +24,11 @@ + context="{'search_default_attachment_id': [active_id], 'default_attachment_id': active_id}" + id="act_attachment_to_pingen_document" + name="Pingen Document" + groups="" + res_model="pingen.document" + src_model="ir.attachment" + /> diff --git a/pingen/views/pingen_document_view.xml b/pingen/views/pingen_document_view.xml index 69e9bc7..c70c93a 100644 --- a/pingen/views/pingen_document_view.xml +++ b/pingen/views/pingen_document_view.xml @@ -1,4 +1,4 @@ - + @@ -7,12 +7,12 @@ tree - - - - - - + + + + + + @@ -24,88 +24,127 @@
- +
- - - + + + - + - - - + + + - + - + - - + + - + - - + + - + - - + + - - - - - + + + + + - + -