From 1e3569173356f151407ed30154e611e9e17baaeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20BEAU?= Date: Mon, 11 May 2020 14:41:08 +0200 Subject: [PATCH 01/56] [REF] Rename to attachement_queue --- attachment_queue/__init__.py | 1 + attachment_queue/__manifest__.py | 28 +++++ attachment_queue/data/cron.xml | 16 +++ attachment_queue/data/ir_config_parameter.xml | 7 ++ attachment_queue/data/mail_template.xml | 16 +++ .../demo/attachment_queue_demo.xml | 10 ++ attachment_queue/models/__init__.py | 1 + attachment_queue/models/attachment_queue.py | 106 ++++++++++++++++++ attachment_queue/readme/CONTRIBUTORS.rst | 4 + attachment_queue/readme/DESCRIPTION.rst | 5 + attachment_queue/readme/USAGE.rst | 8 ++ attachment_queue/security/ir.model.access.csv | 3 + attachment_queue/static/description/icon.png | Bin 0 -> 9455 bytes attachment_queue/tests/__init__.py | 3 + .../tests/test_attachment_queue.py | 50 +++++++++ .../views/attachment_queue_view.xml | 106 ++++++++++++++++++ 16 files changed, 364 insertions(+) create mode 100644 attachment_queue/__init__.py create mode 100644 attachment_queue/__manifest__.py create mode 100644 attachment_queue/data/cron.xml create mode 100644 attachment_queue/data/ir_config_parameter.xml create mode 100644 attachment_queue/data/mail_template.xml create mode 100644 attachment_queue/demo/attachment_queue_demo.xml create mode 100644 attachment_queue/models/__init__.py create mode 100644 attachment_queue/models/attachment_queue.py create mode 100644 attachment_queue/readme/CONTRIBUTORS.rst create mode 100644 attachment_queue/readme/DESCRIPTION.rst create mode 100644 attachment_queue/readme/USAGE.rst create mode 100644 attachment_queue/security/ir.model.access.csv create mode 100644 attachment_queue/static/description/icon.png create mode 100644 attachment_queue/tests/__init__.py create mode 100644 attachment_queue/tests/test_attachment_queue.py create mode 100644 attachment_queue/views/attachment_queue_view.xml diff --git a/attachment_queue/__init__.py b/attachment_queue/__init__.py new file mode 100644 index 00000000000..0650744f6bc --- /dev/null +++ b/attachment_queue/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/attachment_queue/__manifest__.py b/attachment_queue/__manifest__.py new file mode 100644 index 00000000000..ece97f5a2c9 --- /dev/null +++ b/attachment_queue/__manifest__.py @@ -0,0 +1,28 @@ +# Copyright 2015 Florian DA COSTA @ Akretion +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +{ + 'name': 'Attachment Queue', + 'version': '12.0.1.0.0', + 'author': 'Akretion,Odoo Community Association (OCA)', + 'summary': "Base module that add the concept of queue for processing file", + 'website': 'https://github.com/OCA/server-tools', + 'maintainers': ['florian-dacosta', 'sebastienbeau'], + 'license': 'AGPL-3', + 'category': 'Generic Modules', + 'depends': [ + 'base', + 'mail', + ], + 'data': [ + 'views/attachment_queue_view.xml', + 'security/ir.model.access.csv', + 'data/cron.xml', + 'data/ir_config_parameter.xml', + 'data/mail_template.xml', + ], + 'demo': [ + 'demo/attachment_queue_demo.xml' + ], + 'installable': True, +} diff --git a/attachment_queue/data/cron.xml b/attachment_queue/data/cron.xml new file mode 100644 index 00000000000..ad4e6540a7d --- /dev/null +++ b/attachment_queue/data/cron.xml @@ -0,0 +1,16 @@ + + + + + Run Attachments Queue + 30 + minutes + -1 + False + + + code + model.run_attachment_queue_scheduler() + + + diff --git a/attachment_queue/data/ir_config_parameter.xml b/attachment_queue/data/ir_config_parameter.xml new file mode 100644 index 00000000000..5b723255a6a --- /dev/null +++ b/attachment_queue/data/ir_config_parameter.xml @@ -0,0 +1,7 @@ + + + + attachment_queue_cron_batch_limit + 200 + + diff --git a/attachment_queue/data/mail_template.xml b/attachment_queue/data/mail_template.xml new file mode 100644 index 00000000000..4eec884927d --- /dev/null +++ b/attachment_queue/data/mail_template.xml @@ -0,0 +1,16 @@ + + + + + ${object.failure_emails} + Attachment Failure notification + The attachment ${object.name} has failed + + Hello,

+

The attachment ${object.name} has failed with the following error message :
${object.state_message}

+

Regards,

+ ]]>
+
+ +
diff --git a/attachment_queue/demo/attachment_queue_demo.xml b/attachment_queue/demo/attachment_queue_demo.xml new file mode 100644 index 00000000000..ef69be60a39 --- /dev/null +++ b/attachment_queue/demo/attachment_queue_demo.xml @@ -0,0 +1,10 @@ + + + + + bWlncmF0aW9uIHRlc3Q= + attachment_queue_demo.doc + attachment_queue_demo.doc + + + diff --git a/attachment_queue/models/__init__.py b/attachment_queue/models/__init__.py new file mode 100644 index 00000000000..89333838a78 --- /dev/null +++ b/attachment_queue/models/__init__.py @@ -0,0 +1 @@ +from . import attachment_queue diff --git a/attachment_queue/models/attachment_queue.py b/attachment_queue/models/attachment_queue.py new file mode 100644 index 00000000000..72e057ced99 --- /dev/null +++ b/attachment_queue/models/attachment_queue.py @@ -0,0 +1,106 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +import logging +from odoo import api, fields, models, registry + + +_logger = logging.getLogger(__name__) + + +class AttachmentQueue(models.Model): + _name = 'attachment.queue' + _inherits = {'ir.attachment': 'attachment_id'} + _inherit = ['mail.thread'] + + attachment_id = fields.Many2one( + 'ir.attachment', required=True, ondelete='cascade', + help="Link to ir.attachment model ") + file_type = fields.Selection( + selection=[], + help="The file type determines an import method to be used " + "to parse and transform data before their import in ERP or an export") + date_done = fields.Datetime() + state = fields.Selection([ + ('pending', 'Pending'), + ('failed', 'Failed'), + ('done', 'Done'), + ], readonly=False, required=True, default='pending') + state_message = fields.Text() + failure_emails = fields.Char( + compute='_compute_failure_emails', + string="Failure Emails", + help="list of email (separated by comma) which should be notified in " + "case of failure") + + def _compute_failure_emails(self): + for attach in self: + attach.failure_emails = attach._get_failure_emails() + + def _get_failure_emails(self): + # to be overriden in submodules implementing the file_type + self.ensure_one() + return "" + + @api.model + def run_attachment_queue_scheduler(self, domain=None): + if domain is None: + domain = [('state', '=', 'pending')] + batch_limit = self.env.ref( + 'attachment_queue.attachment_queue_cron_batch_limit') \ + .value + if batch_limit and batch_limit.isdigit(): + limit = int(batch_limit) + else: + limit = 200 + attachments = self.search(domain, limit=limit) + if attachments: + return attachments.run() + return True + + def run(self): + """ + Run the process for each attachment queue + """ + failure_tmpl = self.env.ref( + 'attachment_queue.attachment_failure_notification') + for attachment in self: + with api.Environment.manage(): + with registry(self.env.cr.dbname).cursor() as new_cr: + new_env = api.Environment( + new_cr, self.env.uid, self.env.context) + attach = attachment.with_env(new_env) + try: + attach._run() + # pylint: disable=broad-except + except Exception as e: + attach.env.cr.rollback() + _logger.exception(str(e)) + attach.write({ + 'state': 'failed', + 'state_message': str(e), + }) + emails = attach.failure_emails + if emails: + failure_tmpl.send_mail(attach.id) + attach.env.cr.commit() + else: + vals = { + 'state': 'done', + 'date_done': fields.Datetime.now(), + } + attach.write(vals) + attach.env.cr.commit() + return True + + @api.multi + def _run(self): + self.ensure_one() + _logger.info('Start to process attachment queue id %d', self.id) + + @api.multi + def set_done(self): + """ + Manually set to done + """ + message = "Manually set to done by %s" % self.env.user.name + self.write({'state_message': message, 'state': 'done'}) diff --git a/attachment_queue/readme/CONTRIBUTORS.rst b/attachment_queue/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000000..1394f35aa61 --- /dev/null +++ b/attachment_queue/readme/CONTRIBUTORS.rst @@ -0,0 +1,4 @@ +* Valentin CHEMIERE +* Florian da Costa +* Angel Moya +* Dan Kiplangat diff --git a/attachment_queue/readme/DESCRIPTION.rst b/attachment_queue/readme/DESCRIPTION.rst new file mode 100644 index 00000000000..a5e20daeebe --- /dev/null +++ b/attachment_queue/readme/DESCRIPTION.rst @@ -0,0 +1,5 @@ +This module implement a queue for processing file. +File are stored in Odoo standard ir.attachment. +The attachments will be processed depending on their type. + +An example of the use of this module, can be found in the module `attachment_synchronize`. diff --git a/attachment_queue/readme/USAGE.rst b/attachment_queue/readme/USAGE.rst new file mode 100644 index 00000000000..ec276d247b0 --- /dev/null +++ b/attachment_queue/readme/USAGE.rst @@ -0,0 +1,8 @@ +Go the menu Settings > Technical > Database Structure > Attachments Queue + +You can create / see standard attachments with additional fields + +Configure the batch limit for attachments that can be sync by the cron task at a go: + +Settings > Technical > System parameters > attachment_queue_cron_batch_limit + diff --git a/attachment_queue/security/ir.model.access.csv b/attachment_queue/security/ir.model.access.csv new file mode 100644 index 00000000000..e1e0880a392 --- /dev/null +++ b/attachment_queue/security/ir.model.access.csv @@ -0,0 +1,3 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_attachment_queue_user,attachment.queue.user,model_attachment_queue,,1,0,0,0 +access_attachment_queue_manager,attachment.queue.manager,model_attachment_queue,base.group_no_one,1,1,1,1 diff --git a/attachment_queue/static/description/icon.png b/attachment_queue/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3a0328b516c4980e8e44cdb63fd945757ddd132d GIT binary patch literal 9455 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 z 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 Xviia!U7SGha1wx#SCgwmn*{w2TRX*I literal 0 HcmV?d00001 diff --git a/attachment_queue/tests/__init__.py b/attachment_queue/tests/__init__.py new file mode 100644 index 00000000000..7f25150f672 --- /dev/null +++ b/attachment_queue/tests/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import test_attachment_queue diff --git a/attachment_queue/tests/test_attachment_queue.py b/attachment_queue/tests/test_attachment_queue.py new file mode 100644 index 00000000000..fc29f902f92 --- /dev/null +++ b/attachment_queue/tests/test_attachment_queue.py @@ -0,0 +1,50 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo.tests.common import TransactionCase +import odoo +from odoo import api + + +class TestAttachmentBaseQueue(TransactionCase): + + def setUp(self): + super().setUp() + self.registry.enter_test_mode(self.env.cr) + self.env = api.Environment(self.registry.test_cr, self.env.uid, + self.env.context) + self.attachment = self.env.ref('attachment_queue.attachment_queue_demo') + + def tearDown(self): + self.registry.leave_test_mode() + super().tearDown() + + def test_attachment_queue(self): + """Test run_attachment_queue_scheduler to ensure set state to done + """ + self.assertEqual( + self.attachment.state, + 'pending' + ) + self.env['attachment.queue'].run_attachment_queue_scheduler() + self.env.cache.invalidate() + with odoo.registry(self.env.cr.dbname).cursor() as new_cr: + new_env = api.Environment( + new_cr, self.env.uid, self.env.context) + attach = self.attachment.with_env(new_env) + self.assertEqual( + attach.state, + 'done' + ) + + def test_set_done(self): + """Test set_done manually + """ + self.assertEqual( + self.attachment.state, + 'pending' + ) + self.attachment.set_done() + self.assertEqual( + self.attachment.state, + 'done' + ) diff --git a/attachment_queue/views/attachment_queue_view.xml b/attachment_queue/views/attachment_queue_view.xml new file mode 100644 index 00000000000..f8bc7c9d329 --- /dev/null +++ b/attachment_queue/views/attachment_queue_view.xml @@ -0,0 +1,106 @@ + + + + + attachment.queue + + + +
+
+
+ + + + + + + + + + + +
+ + + attachment.queue + + + + + + + + + + + + + + attachment.queue + + + + + + + + + + + + + + + + + + + + + + + + + + + Attachments Queue + ir.actions.act_window + attachment.queue + form + tree,form + + + + + + + tree + + + + + + + form + + + + + + +
From f25e35be508026b83f1a66010509e6444f1f432b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20BEAU?= Date: Mon, 11 May 2020 14:45:56 +0200 Subject: [PATCH 02/56] [REF] apply black on the code --- attachment_queue/__manifest__.py | 39 +++++------ attachment_queue/models/attachment_queue.py | 65 ++++++++++-------- attachment_queue/readme/DESCRIPTION.rst | 6 +- attachment_queue/readme/USAGE.rst | 9 +++ attachment_queue/static/description/form.png | Bin 0 -> 76955 bytes attachment_queue/static/description/tree.png | Bin 0 -> 42403 bytes .../tests/test_attachment_queue.py | 37 ++++------ .../views/attachment_queue_view.xml | 8 +-- 8 files changed, 80 insertions(+), 84 deletions(-) create mode 100644 attachment_queue/static/description/form.png create mode 100644 attachment_queue/static/description/tree.png diff --git a/attachment_queue/__manifest__.py b/attachment_queue/__manifest__.py index ece97f5a2c9..54b8225a599 100644 --- a/attachment_queue/__manifest__.py +++ b/attachment_queue/__manifest__.py @@ -2,27 +2,22 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). { - 'name': 'Attachment Queue', - 'version': '12.0.1.0.0', - 'author': 'Akretion,Odoo Community Association (OCA)', - 'summary': "Base module that add the concept of queue for processing file", - 'website': 'https://github.com/OCA/server-tools', - 'maintainers': ['florian-dacosta', 'sebastienbeau'], - 'license': 'AGPL-3', - 'category': 'Generic Modules', - 'depends': [ - 'base', - 'mail', + "name": "Attachment Queue", + "version": "12.0.1.0.0", + "author": "Akretion,Odoo Community Association (OCA)", + "summary": "Base module adding the concept of queue for processing file", + "website": "https://github.com/OCA/server-tools", + "maintainers": ["florian-dacosta", "sebastienbeau"], + "license": "AGPL-3", + "category": "Generic Modules", + "depends": ["base", "mail"], + "data": [ + "views/attachment_queue_view.xml", + "security/ir.model.access.csv", + "data/cron.xml", + "data/ir_config_parameter.xml", + "data/mail_template.xml", ], - 'data': [ - 'views/attachment_queue_view.xml', - 'security/ir.model.access.csv', - 'data/cron.xml', - 'data/ir_config_parameter.xml', - 'data/mail_template.xml', - ], - 'demo': [ - 'demo/attachment_queue_demo.xml' - ], - 'installable': True, + "demo": ["demo/attachment_queue_demo.xml"], + "installable": True, } diff --git a/attachment_queue/models/attachment_queue.py b/attachment_queue/models/attachment_queue.py index 72e057ced99..4c058dac348 100644 --- a/attachment_queue/models/attachment_queue.py +++ b/attachment_queue/models/attachment_queue.py @@ -1,36 +1,42 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import logging -from odoo import api, fields, models, registry +from odoo import api, fields, models, registry _logger = logging.getLogger(__name__) class AttachmentQueue(models.Model): - _name = 'attachment.queue' - _inherits = {'ir.attachment': 'attachment_id'} - _inherit = ['mail.thread'] + _name = "attachment.queue" + _inherits = {"ir.attachment": "attachment_id"} + _inherit = ["mail.thread"] attachment_id = fields.Many2one( - 'ir.attachment', required=True, ondelete='cascade', - help="Link to ir.attachment model ") + "ir.attachment", + required=True, + ondelete="cascade", + help="Link to ir.attachment model ", + ) file_type = fields.Selection( selection=[], help="The file type determines an import method to be used " - "to parse and transform data before their import in ERP or an export") + "to parse and transform data before their import in ERP or an export", + ) date_done = fields.Datetime() - state = fields.Selection([ - ('pending', 'Pending'), - ('failed', 'Failed'), - ('done', 'Done'), - ], readonly=False, required=True, default='pending') + state = fields.Selection( + [("pending", "Pending"), ("failed", "Failed"), ("done", "Done")], + readonly=False, + required=True, + default="pending", + ) state_message = fields.Text() failure_emails = fields.Char( - compute='_compute_failure_emails', + compute="_compute_failure_emails", string="Failure Emails", - help="list of email (separated by comma) which should be notified in " - "case of failure") + help="Comma-separated list of email addresses to be notified in case of" + "failure", + ) def _compute_failure_emails(self): for attach in self: @@ -44,10 +50,10 @@ def _get_failure_emails(self): @api.model def run_attachment_queue_scheduler(self, domain=None): if domain is None: - domain = [('state', '=', 'pending')] + domain = [("state", "=", "pending")] batch_limit = self.env.ref( - 'attachment_queue.attachment_queue_cron_batch_limit') \ - .value + "attachment_queue.attachment_queue_cron_batch_limit" + ).value if batch_limit and batch_limit.isdigit(): limit = int(batch_limit) else: @@ -62,12 +68,14 @@ def run(self): Run the process for each attachment queue """ failure_tmpl = self.env.ref( - 'attachment_queue.attachment_failure_notification') + "attachment_queue.attachment_failure_notification" + ) for attachment in self: with api.Environment.manage(): with registry(self.env.cr.dbname).cursor() as new_cr: new_env = api.Environment( - new_cr, self.env.uid, self.env.context) + new_cr, self.env.uid, self.env.context + ) attach = attachment.with_env(new_env) try: attach._run() @@ -75,32 +83,29 @@ def run(self): except Exception as e: attach.env.cr.rollback() _logger.exception(str(e)) - attach.write({ - 'state': 'failed', - 'state_message': str(e), - }) + attach.write( + {"state": "failed", "state_message": str(e)} + ) emails = attach.failure_emails if emails: failure_tmpl.send_mail(attach.id) attach.env.cr.commit() else: vals = { - 'state': 'done', - 'date_done': fields.Datetime.now(), + "state": "done", + "date_done": fields.Datetime.now(), } attach.write(vals) attach.env.cr.commit() return True - @api.multi def _run(self): self.ensure_one() - _logger.info('Start to process attachment queue id %d', self.id) + _logger.info("Starting processing of attachment queue id %d", self.id) - @api.multi def set_done(self): """ Manually set to done """ message = "Manually set to done by %s" % self.env.user.name - self.write({'state_message': message, 'state': 'done'}) + self.write({"state_message": message, "state": "done"}) diff --git a/attachment_queue/readme/DESCRIPTION.rst b/attachment_queue/readme/DESCRIPTION.rst index a5e20daeebe..57bac75fdf3 100644 --- a/attachment_queue/readme/DESCRIPTION.rst +++ b/attachment_queue/readme/DESCRIPTION.rst @@ -1,5 +1,3 @@ -This module implement a queue for processing file. -File are stored in Odoo standard ir.attachment. -The attachments will be processed depending on their type. +This module adds async processing capabilities to attachments by implementing a new model attachment.queue that wraps attachments and stores additional information so that it can be processed in an asynchronous way. -An example of the use of this module, can be found in the module `attachment_synchronize`. +A use case of this module can be found in the attachment_synchronize module. diff --git a/attachment_queue/readme/USAGE.rst b/attachment_queue/readme/USAGE.rst index ec276d247b0..a97a271e528 100644 --- a/attachment_queue/readme/USAGE.rst +++ b/attachment_queue/readme/USAGE.rst @@ -6,3 +6,12 @@ Configure the batch limit for attachments that can be sync by the cron task at a Settings > Technical > System parameters > attachment_queue_cron_batch_limit + +image:: ../static/description/tree.png + + +This module can be used in combination with attachment_synchronize to control file processing workflow + + +image:: ../static/description/form.png + diff --git a/attachment_queue/static/description/form.png b/attachment_queue/static/description/form.png new file mode 100644 index 0000000000000000000000000000000000000000..fbc9f6aa644ad673c73401af6778367da0ac754b GIT binary patch literal 76955 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 F{{g=F2~hw5 literal 0 HcmV?d00001 diff --git a/attachment_queue/tests/test_attachment_queue.py b/attachment_queue/tests/test_attachment_queue.py index fc29f902f92..74d368f2bfb 100644 --- a/attachment_queue/tests/test_attachment_queue.py +++ b/attachment_queue/tests/test_attachment_queue.py @@ -1,18 +1,20 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo.tests.common import TransactionCase import odoo from odoo import api +from odoo.tests.common import TransactionCase class TestAttachmentBaseQueue(TransactionCase): - def setUp(self): super().setUp() self.registry.enter_test_mode(self.env.cr) - self.env = api.Environment(self.registry.test_cr, self.env.uid, - self.env.context) - self.attachment = self.env.ref('attachment_queue.attachment_queue_demo') + self.env = api.Environment( + self.registry.test_cr, self.env.uid, self.env.context + ) + self.attachment = self.env.ref( + "attachment_queue.attachment_queue_demo" + ) def tearDown(self): self.registry.leave_test_mode() @@ -21,30 +23,17 @@ def tearDown(self): def test_attachment_queue(self): """Test run_attachment_queue_scheduler to ensure set state to done """ - self.assertEqual( - self.attachment.state, - 'pending' - ) - self.env['attachment.queue'].run_attachment_queue_scheduler() + self.assertEqual(self.attachment.state, "pending") + self.env["attachment.queue"].run_attachment_queue_scheduler() self.env.cache.invalidate() with odoo.registry(self.env.cr.dbname).cursor() as new_cr: - new_env = api.Environment( - new_cr, self.env.uid, self.env.context) + new_env = api.Environment(new_cr, self.env.uid, self.env.context) attach = self.attachment.with_env(new_env) - self.assertEqual( - attach.state, - 'done' - ) + self.assertEqual(attach.state, "done") def test_set_done(self): """Test set_done manually """ - self.assertEqual( - self.attachment.state, - 'pending' - ) + self.assertEqual(self.attachment.state, "pending") self.attachment.set_done() - self.assertEqual( - self.attachment.state, - 'done' - ) + self.assertEqual(self.attachment.state, "done") diff --git a/attachment_queue/views/attachment_queue_view.xml b/attachment_queue/views/attachment_queue_view.xml index f8bc7c9d329..f83f60a7289 100644 --- a/attachment_queue/views/attachment_queue_view.xml +++ b/attachment_queue/views/attachment_queue_view.xml @@ -63,10 +63,10 @@ - - - - + + + + From 276b667d858d98cb51af5e88028dd38caec3212c Mon Sep 17 00:00:00 2001 From: beau sebastien Date: Tue, 12 May 2020 18:56:49 +0200 Subject: [PATCH 03/56] Update attachment_queue/__manifest__.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Raphaël Valyi --- attachment_queue/__manifest__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/attachment_queue/__manifest__.py b/attachment_queue/__manifest__.py index 54b8225a599..7581c49f575 100644 --- a/attachment_queue/__manifest__.py +++ b/attachment_queue/__manifest__.py @@ -5,7 +5,7 @@ "name": "Attachment Queue", "version": "12.0.1.0.0", "author": "Akretion,Odoo Community Association (OCA)", - "summary": "Base module adding the concept of queue for processing file", + "summary": "Base module adding the concept of queue for processing files", "website": "https://github.com/OCA/server-tools", "maintainers": ["florian-dacosta", "sebastienbeau"], "license": "AGPL-3", From c170673fca53eb7899fac830c2b5dd02ae0be203 Mon Sep 17 00:00:00 2001 From: oca-travis Date: Thu, 14 May 2020 14:40:55 +0000 Subject: [PATCH 04/56] [UPD] Update attachment_queue.pot --- attachment_queue/i18n/attachment_queue.pot | 444 +++++++++++++++++++++ 1 file changed, 444 insertions(+) create mode 100644 attachment_queue/i18n/attachment_queue.pot diff --git a/attachment_queue/i18n/attachment_queue.pot b/attachment_queue/i18n/attachment_queue.pot new file mode 100644 index 00000000000..e9e07908199 --- /dev/null +++ b/attachment_queue/i18n/attachment_queue.pot @@ -0,0 +1,444 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * attachment_queue +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 12.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: attachment_queue +#: model:mail.template,body_html:attachment_queue.attachment_failure_notification +msgid "\n" +"

Hello,

\n" +"

The attachment ${object.name} has failed with the following error message :
${object.state_message}

\n" +"

Regards,

\n" +" " +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__access_token +msgid "Access Token" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__message_needaction +msgid "Action Needed" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__active +msgid "Active" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__attachment_id +#: model_terms:ir.ui.view,arch_db:attachment_queue.view_attachment_queue_search +msgid "Attachment" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__message_attachment_count +msgid "Attachment Count" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__local_url +msgid "Attachment URL" +msgstr "" + +#. module: attachment_queue +#: model_terms:ir.ui.view,arch_db:attachment_queue.view_attachment_queue_search +msgid "Attachments" +msgstr "" + +#. module: attachment_queue +#: model:ir.actions.act_window,name:attachment_queue.action_attachment_queue +#: model:ir.ui.menu,name:attachment_queue.menu_attachment_queue +msgid "Attachments Queue" +msgstr "" + +#. module: attachment_queue +#: model_terms:ir.ui.view,arch_db:attachment_queue.view_attachment_queue_search +msgid "Binary" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__checksum +msgid "Checksum/SHA1" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,help:attachment_queue.field_attachment_queue__failure_emails +msgid "Comma-separated list of email addresses to be notified in case offailure" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__company_id +#: model_terms:ir.ui.view,arch_db:attachment_queue.view_attachment_queue_search +msgid "Company" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__create_uid +msgid "Created by" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__create_date +msgid "Created on" +msgstr "" + +#. module: attachment_queue +#: model_terms:ir.ui.view,arch_db:attachment_queue.view_attachment_queue_search +msgid "Creation Month" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__db_datas +msgid "Database Data" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__date_done +msgid "Date Done" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__description +msgid "Description" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__display_name +msgid "Display Name" +msgstr "" + +#. module: attachment_queue +#: selection:attachment.queue,state:0 +#: model_terms:ir.ui.view,arch_db:attachment_queue.view_attachment_queue_search +msgid "Done" +msgstr "" + +#. module: attachment_queue +#: model_terms:ir.ui.view,arch_db:attachment_queue.view_attachment_queue_form +msgid "Error" +msgstr "" + +#. module: attachment_queue +#: selection:attachment.queue,state:0 +#: model_terms:ir.ui.view,arch_db:attachment_queue.view_attachment_queue_search +msgid "Failed" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__failure_emails +msgid "Failure Emails" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__datas +msgid "File Content" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__file_size +msgid "File Size" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__file_type +msgid "File Type" +msgstr "" + +#. module: attachment_queue +#: model_terms:ir.ui.view,arch_db:attachment_queue.view_attachment_queue_search +msgid "File type" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__datas_fname +msgid "Filename" +msgstr "" + +#. module: attachment_queue +#: model_terms:ir.ui.view,arch_db:attachment_queue.view_attachment_queue_search +msgid "Filter on my documents" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__message_follower_ids +msgid "Followers" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__message_channel_ids +msgid "Followers (Channels)" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__message_partner_ids +msgid "Followers (Partners)" +msgstr "" + +#. module: attachment_queue +#: model_terms:ir.ui.view,arch_db:attachment_queue.view_attachment_queue_search +msgid "Group By" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__id +msgid "ID" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,help:attachment_queue.field_attachment_queue__message_unread +msgid "If checked new messages require your attention." +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,help:attachment_queue.field_attachment_queue__message_needaction +msgid "If checked, new messages require your attention." +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,help:attachment_queue.field_attachment_queue__message_has_error +msgid "If checked, some messages have a delivery error." +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__index_content +msgid "Indexed Content" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__message_is_follower +msgid "Is Follower" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__public +msgid "Is public document" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue____last_update +msgid "Last Modified on" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__write_date +msgid "Last Updated on" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,help:attachment_queue.field_attachment_queue__attachment_id +msgid "Link to ir.attachment model " +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__message_main_attachment_id +msgid "Main Attachment" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__message_has_error +msgid "Message Delivery error" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__message_ids +msgid "Messages" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__mimetype +msgid "Mime Type" +msgstr "" + +#. module: attachment_queue +#: model_terms:ir.ui.view,arch_db:attachment_queue.view_attachment_queue_search +msgid "My Document(s)" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__name +msgid "Name" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__message_needaction_counter +msgid "Number of Actions" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__message_has_error_counter +msgid "Number of error" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,help:attachment_queue.field_attachment_queue__message_needaction_counter +msgid "Number of messages which requires an action" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,help:attachment_queue.field_attachment_queue__message_has_error_counter +msgid "Number of messages with delivery error" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,help:attachment_queue.field_attachment_queue__message_unread_counter +msgid "Number of unread messages" +msgstr "" + +#. module: attachment_queue +#: model_terms:ir.ui.view,arch_db:attachment_queue.view_attachment_queue_search +msgid "Owner" +msgstr "" + +#. module: attachment_queue +#: selection:attachment.queue,state:0 +#: model_terms:ir.ui.view,arch_db:attachment_queue.view_attachment_queue_search +msgid "Pending" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__res_model_name +msgid "Res Model Name" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__res_field +msgid "Resource Field" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__res_id +msgid "Resource ID" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__res_model +msgid "Resource Model" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__res_name +msgid "Resource Name" +msgstr "" + +#. module: attachment_queue +#: model_terms:ir.ui.view,arch_db:attachment_queue.view_attachment_queue_form +msgid "Run" +msgstr "" + +#. module: attachment_queue +#: model:ir.actions.server,name:attachment_queue.cronjob_run_attachment_queue_ir_actions_server +#: model:ir.cron,cron_name:attachment_queue.cronjob_run_attachment_queue +#: model:ir.cron,name:attachment_queue.cronjob_run_attachment_queue +msgid "Run Attachments Queue" +msgstr "" + +#. module: attachment_queue +#: model_terms:ir.ui.view,arch_db:attachment_queue.view_attachment_queue_form +msgid "Set to Done" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__state +#: model_terms:ir.ui.view,arch_db:attachment_queue.view_attachment_queue_search +msgid "State" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__state_message +msgid "State Message" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__store_fname +msgid "Stored Filename" +msgstr "" + +#. module: attachment_queue +#: model:mail.template,subject:attachment_queue.attachment_failure_notification +msgid "The attachment ${object.name} has failed" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,help:attachment_queue.field_attachment_queue__res_model +msgid "The database object this attachment will be attached to." +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,help:attachment_queue.field_attachment_queue__file_type +msgid "The file type determines an import method to be used to parse and transform data before their import in ERP or an export" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,help:attachment_queue.field_attachment_queue__res_id +msgid "The record id this is attached to." +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__thumbnail +msgid "Thumbnail" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__type +#: model_terms:ir.ui.view,arch_db:attachment_queue.view_attachment_queue_search +msgid "Type" +msgstr "" + +#. module: attachment_queue +#: model_terms:ir.ui.view,arch_db:attachment_queue.view_attachment_queue_search +msgid "URL" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__message_unread +msgid "Unread Messages" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__message_unread_counter +msgid "Unread Messages Counter" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__url +msgid "Url" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__website_message_ids +msgid "Website Messages" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,help:attachment_queue.field_attachment_queue__website_message_ids +msgid "Website communication history" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,help:attachment_queue.field_attachment_queue__type +msgid "You can either upload a file from your computer or copy/paste an internet link to your file." +msgstr "" + +#. module: attachment_queue +#: model:ir.model,name:attachment_queue.model_attachment_queue +msgid "attachment.queue" +msgstr "" + From 1496d69bdd1c97ed7e46a37b8ac177cd1b0a6a1d Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Thu, 14 May 2020 14:55:08 +0000 Subject: [PATCH 05/56] [UPD] README.rst --- attachment_queue/README.rst | 110 +++++ .../static/description/index.html | 436 ++++++++++++++++++ 2 files changed, 546 insertions(+) create mode 100644 attachment_queue/README.rst create mode 100644 attachment_queue/static/description/index.html diff --git a/attachment_queue/README.rst b/attachment_queue/README.rst new file mode 100644 index 00000000000..ef2d8928ace --- /dev/null +++ b/attachment_queue/README.rst @@ -0,0 +1,110 @@ +================ +Attachment Queue +================ + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fserver--tools-lightgray.png?logo=github + :target: https://github.com/OCA/server-tools/tree/12.0/attachment_queue + :alt: OCA/server-tools +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/server-tools-12-0/server-tools-12-0-attachment_queue + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/149/12.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module adds async processing capabilities to attachments by implementing a new model attachment.queue that wraps attachments and stores additional information so that it can be processed in an asynchronous way. + +A use case of this module can be found in the attachment_synchronize module. + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +Go the menu Settings > Technical > Database Structure > Attachments Queue + +You can create / see standard attachments with additional fields + +Configure the batch limit for attachments that can be sync by the cron task at a go: + +Settings > Technical > System parameters > attachment_queue_cron_batch_limit + + +image:: ../static/description/tree.png + + +This module can be used in combination with attachment_synchronize to control file processing workflow + + +image:: ../static/description/form.png + + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Akretion + +Contributors +~~~~~~~~~~~~ + +* Valentin CHEMIERE +* Florian da Costa +* Angel Moya +* Dan Kiplangat + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +.. |maintainer-florian-dacosta| image:: https://github.com/florian-dacosta.png?size=40px + :target: https://github.com/florian-dacosta + :alt: florian-dacosta +.. |maintainer-sebastienbeau| image:: https://github.com/sebastienbeau.png?size=40px + :target: https://github.com/sebastienbeau + :alt: sebastienbeau + +Current `maintainers `__: + +|maintainer-florian-dacosta| |maintainer-sebastienbeau| + +This module is part of the `OCA/server-tools `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/attachment_queue/static/description/index.html b/attachment_queue/static/description/index.html new file mode 100644 index 00000000000..ee751f31062 --- /dev/null +++ b/attachment_queue/static/description/index.html @@ -0,0 +1,436 @@ + + + + + + +Attachment Queue + + + + + + From 62293a827ffe54c782fa58cc7e425a14bc032a5b Mon Sep 17 00:00:00 2001 From: oca-travis Date: Thu, 27 Aug 2020 15:02:55 +0000 Subject: [PATCH 06/56] [UPD] Update attachment_queue.pot --- attachment_queue/i18n/attachment_queue.pot | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/attachment_queue/i18n/attachment_queue.pot b/attachment_queue/i18n/attachment_queue.pot index e9e07908199..f20c0243cbb 100644 --- a/attachment_queue/i18n/attachment_queue.pot +++ b/attachment_queue/i18n/attachment_queue.pot @@ -131,6 +131,11 @@ msgstr "" msgid "Error" msgstr "" +#. module: attachment_queue +#: selection:attachment.queue,file_type:0 +msgid "Export File (External location)" +msgstr "" + #. module: attachment_queue #: selection:attachment.queue,state:0 #: model_terms:ir.ui.view,arch_db:attachment_queue.view_attachment_queue_search From d61df5c17f7717a2b719004ac29579f37ce0a0cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20BEAU?= Date: Fri, 23 Oct 2020 15:16:54 +0200 Subject: [PATCH 07/56] [FIX] fix incorrect view mode --- attachment_queue/views/attachment_queue_view.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/attachment_queue/views/attachment_queue_view.xml b/attachment_queue/views/attachment_queue_view.xml index f83f60a7289..1f3b1964fdd 100644 --- a/attachment_queue/views/attachment_queue_view.xml +++ b/attachment_queue/views/attachment_queue_view.xml @@ -4,6 +4,7 @@ attachment.queue + primary
From 2582a9f6432c558d06b78b46e8e2b73a1ec6389e Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Fri, 23 Oct 2020 13:54:57 +0000 Subject: [PATCH 08/56] attachment_queue 12.0.1.0.1 --- attachment_queue/__manifest__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/attachment_queue/__manifest__.py b/attachment_queue/__manifest__.py index 7581c49f575..9fbe2edd57b 100644 --- a/attachment_queue/__manifest__.py +++ b/attachment_queue/__manifest__.py @@ -3,7 +3,7 @@ { "name": "Attachment Queue", - "version": "12.0.1.0.0", + "version": "12.0.1.0.1", "author": "Akretion,Odoo Community Association (OCA)", "summary": "Base module adding the concept of queue for processing files", "website": "https://github.com/OCA/server-tools", From 4a7911e703ede70ef4d928728873fd71b1fceae5 Mon Sep 17 00:00:00 2001 From: Kevin Khao Date: Thu, 4 Mar 2021 12:12:04 +0100 Subject: [PATCH 09/56] [IMP] attachment_queue: pre-commit --- attachment_queue/data/cron.xml | 4 +- attachment_queue/data/ir_config_parameter.xml | 2 +- attachment_queue/data/mail_template.xml | 8 +- .../demo/attachment_queue_demo.xml | 2 +- attachment_queue/models/attachment_queue.py | 14 +- attachment_queue/readme/USAGE.rst | 1 - .../tests/test_attachment_queue.py | 10 +- .../views/attachment_queue_view.xml | 153 ++++++++++++------ 8 files changed, 117 insertions(+), 77 deletions(-) diff --git a/attachment_queue/data/cron.xml b/attachment_queue/data/cron.xml index ad4e6540a7d..fa8130f4b65 100644 --- a/attachment_queue/data/cron.xml +++ b/attachment_queue/data/cron.xml @@ -1,4 +1,4 @@ - + @@ -8,7 +8,7 @@ -1 False - + code model.run_attachment_queue_scheduler() diff --git a/attachment_queue/data/ir_config_parameter.xml b/attachment_queue/data/ir_config_parameter.xml index 5b723255a6a..ecf34bf8c75 100644 --- a/attachment_queue/data/ir_config_parameter.xml +++ b/attachment_queue/data/ir_config_parameter.xml @@ -1,4 +1,4 @@ - + attachment_queue_cron_batch_limit diff --git a/attachment_queue/data/mail_template.xml b/attachment_queue/data/mail_template.xml index 4eec884927d..ce8d48146bc 100644 --- a/attachment_queue/data/mail_template.xml +++ b/attachment_queue/data/mail_template.xml @@ -1,12 +1,14 @@ - + ${object.failure_emails} Attachment Failure notification The attachment ${object.name} has failed - - + Hello,

The attachment ${object.name} has failed with the following error message :
${object.state_message}

Regards,

diff --git a/attachment_queue/demo/attachment_queue_demo.xml b/attachment_queue/demo/attachment_queue_demo.xml index ef69be60a39..b906a760ed9 100644 --- a/attachment_queue/demo/attachment_queue_demo.xml +++ b/attachment_queue/demo/attachment_queue_demo.xml @@ -1,4 +1,4 @@ - + diff --git a/attachment_queue/models/attachment_queue.py b/attachment_queue/models/attachment_queue.py index 4c058dac348..e3cf5e58a18 100644 --- a/attachment_queue/models/attachment_queue.py +++ b/attachment_queue/models/attachment_queue.py @@ -35,7 +35,7 @@ class AttachmentQueue(models.Model): compute="_compute_failure_emails", string="Failure Emails", help="Comma-separated list of email addresses to be notified in case of" - "failure", + "failure", ) def _compute_failure_emails(self): @@ -67,15 +67,11 @@ def run(self): """ Run the process for each attachment queue """ - failure_tmpl = self.env.ref( - "attachment_queue.attachment_failure_notification" - ) + failure_tmpl = self.env.ref("attachment_queue.attachment_failure_notification") for attachment in self: with api.Environment.manage(): with registry(self.env.cr.dbname).cursor() as new_cr: - new_env = api.Environment( - new_cr, self.env.uid, self.env.context - ) + new_env = api.Environment(new_cr, self.env.uid, self.env.context) attach = attachment.with_env(new_env) try: attach._run() @@ -83,9 +79,7 @@ def run(self): except Exception as e: attach.env.cr.rollback() _logger.exception(str(e)) - attach.write( - {"state": "failed", "state_message": str(e)} - ) + attach.write({"state": "failed", "state_message": str(e)}) emails = attach.failure_emails if emails: failure_tmpl.send_mail(attach.id) diff --git a/attachment_queue/readme/USAGE.rst b/attachment_queue/readme/USAGE.rst index a97a271e528..bb13e8c9e82 100644 --- a/attachment_queue/readme/USAGE.rst +++ b/attachment_queue/readme/USAGE.rst @@ -14,4 +14,3 @@ This module can be used in combination with attachment_synchronize to control fi image:: ../static/description/form.png - diff --git a/attachment_queue/tests/test_attachment_queue.py b/attachment_queue/tests/test_attachment_queue.py index 74d368f2bfb..3d8910be33d 100644 --- a/attachment_queue/tests/test_attachment_queue.py +++ b/attachment_queue/tests/test_attachment_queue.py @@ -12,17 +12,14 @@ def setUp(self): self.env = api.Environment( self.registry.test_cr, self.env.uid, self.env.context ) - self.attachment = self.env.ref( - "attachment_queue.attachment_queue_demo" - ) + self.attachment = self.env.ref("attachment_queue.attachment_queue_demo") def tearDown(self): self.registry.leave_test_mode() super().tearDown() def test_attachment_queue(self): - """Test run_attachment_queue_scheduler to ensure set state to done - """ + """Test run_attachment_queue_scheduler to ensure set state to done""" self.assertEqual(self.attachment.state, "pending") self.env["attachment.queue"].run_attachment_queue_scheduler() self.env.cache.invalidate() @@ -32,8 +29,7 @@ def test_attachment_queue(self): self.assertEqual(attach.state, "done") def test_set_done(self): - """Test set_done manually - """ + """Test set_done manually""" self.assertEqual(self.attachment.state, "pending") self.attachment.set_done() self.assertEqual(self.attachment.state, "done") diff --git a/attachment_queue/views/attachment_queue_view.xml b/attachment_queue/views/attachment_queue_view.xml index 1f3b1964fdd..081d420c965 100644 --- a/attachment_queue/views/attachment_queue_view.xml +++ b/attachment_queue/views/attachment_queue_view.xml @@ -1,27 +1,36 @@ - + attachment.queue - + primary
-
- - - + + + - + @@ -31,12 +40,12 @@ attachment.queue - - - - - - + + + + + +
@@ -45,31 +54,69 @@ attachment.queue - - - - - - - - - - - + + + + + + + + + + + - - - - - - + + + + + + @@ -81,27 +128,29 @@ attachment.queue form tree,form - - + +
- + tree - - + + - + form - - + + - +
From aeeb218b8df945c1020430687ac16646a67b0fda Mon Sep 17 00:00:00 2001 From: Kevin Khao Date: Thu, 4 Mar 2021 12:12:54 +0100 Subject: [PATCH 10/56] [14.0][MIG] attachment_queue: migration to 14.0 --- attachment_queue/__manifest__.py | 2 +- attachment_queue/demo/attachment_queue_demo.xml | 1 - attachment_queue/readme/CONTRIBUTORS.rst | 1 + attachment_queue/views/attachment_queue_view.xml | 5 +---- 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/attachment_queue/__manifest__.py b/attachment_queue/__manifest__.py index 9fbe2edd57b..8e30aff9527 100644 --- a/attachment_queue/__manifest__.py +++ b/attachment_queue/__manifest__.py @@ -3,7 +3,7 @@ { "name": "Attachment Queue", - "version": "12.0.1.0.1", + "version": "14.0.1.0.0", "author": "Akretion,Odoo Community Association (OCA)", "summary": "Base module adding the concept of queue for processing files", "website": "https://github.com/OCA/server-tools", diff --git a/attachment_queue/demo/attachment_queue_demo.xml b/attachment_queue/demo/attachment_queue_demo.xml index b906a760ed9..c1d8460e8fb 100644 --- a/attachment_queue/demo/attachment_queue_demo.xml +++ b/attachment_queue/demo/attachment_queue_demo.xml @@ -3,7 +3,6 @@ bWlncmF0aW9uIHRlc3Q= - attachment_queue_demo.doc attachment_queue_demo.doc diff --git a/attachment_queue/readme/CONTRIBUTORS.rst b/attachment_queue/readme/CONTRIBUTORS.rst index 1394f35aa61..0ab92468b59 100644 --- a/attachment_queue/readme/CONTRIBUTORS.rst +++ b/attachment_queue/readme/CONTRIBUTORS.rst @@ -2,3 +2,4 @@ * Florian da Costa * Angel Moya * Dan Kiplangat +* Kevin Khao diff --git a/attachment_queue/views/attachment_queue_view.xml b/attachment_queue/views/attachment_queue_view.xml index 081d420c965..38109902654 100644 --- a/attachment_queue/views/attachment_queue_view.xml +++ b/attachment_queue/views/attachment_queue_view.xml @@ -41,10 +41,8 @@ - - @@ -56,7 +54,7 @@ @@ -126,7 +124,6 @@ Attachments Queue ir.actions.act_window attachment.queue - form tree,form From 23473f3bec68222b12e5aa341f3a29acf4c41624 Mon Sep 17 00:00:00 2001 From: Kevin Khao Date: Thu, 4 Mar 2021 12:21:00 +0100 Subject: [PATCH 11/56] [IMP] Avoid using deprecated function _list, _delete and replace appropriately --- attachment_queue/models/attachment_queue.py | 1 + 1 file changed, 1 insertion(+) diff --git a/attachment_queue/models/attachment_queue.py b/attachment_queue/models/attachment_queue.py index e3cf5e58a18..d379f6b63bb 100644 --- a/attachment_queue/models/attachment_queue.py +++ b/attachment_queue/models/attachment_queue.py @@ -9,6 +9,7 @@ class AttachmentQueue(models.Model): _name = "attachment.queue" + _description = "Attachment Queue" _inherits = {"ir.attachment": "attachment_id"} _inherit = ["mail.thread"] From 9b40d60084d238ff68cbe22341020c630a45a478 Mon Sep 17 00:00:00 2001 From: KevinKhao Date: Fri, 23 Jul 2021 00:52:35 +0200 Subject: [PATCH 12/56] [IMP] Add superuser to running attachment queue --- attachment_queue/models/attachment_queue.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/attachment_queue/models/attachment_queue.py b/attachment_queue/models/attachment_queue.py index d379f6b63bb..b7d279c8ce4 100644 --- a/attachment_queue/models/attachment_queue.py +++ b/attachment_queue/models/attachment_queue.py @@ -2,7 +2,7 @@ import logging -from odoo import api, fields, models, registry +from odoo import SUPERUSER_ID, api, fields, models, registry _logger = logging.getLogger(__name__) @@ -72,7 +72,7 @@ def run(self): for attachment in self: with api.Environment.manage(): with registry(self.env.cr.dbname).cursor() as new_cr: - new_env = api.Environment(new_cr, self.env.uid, self.env.context) + new_env = api.Environment(new_cr, SUPERUSER_ID, self.env.context) attach = attachment.with_env(new_env) try: attach._run() From 0270b1dade58f5dac3c3c378acfda36b1e228a5d Mon Sep 17 00:00:00 2001 From: oca-ci Date: Fri, 15 Apr 2022 12:50:50 +0000 Subject: [PATCH 13/56] [UPD] Update attachment_queue.pot --- attachment_queue/i18n/attachment_queue.pot | 133 +++++++++++++-------- 1 file changed, 85 insertions(+), 48 deletions(-) diff --git a/attachment_queue/i18n/attachment_queue.pot b/attachment_queue/i18n/attachment_queue.pot index f20c0243cbb..4218f0a6062 100644 --- a/attachment_queue/i18n/attachment_queue.pot +++ b/attachment_queue/i18n/attachment_queue.pot @@ -1,12 +1,12 @@ # Translation of Odoo Server. # This file contains the translation of the following modules: -# * attachment_queue +# * attachment_queue # msgid "" msgstr "" -"Project-Id-Version: Odoo Server 12.0\n" +"Project-Id-Version: Odoo Server 14.0\n" "Report-Msgid-Bugs-To: \n" -"Last-Translator: <>\n" +"Last-Translator: \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -15,7 +15,8 @@ msgstr "" #. module: attachment_queue #: model:mail.template,body_html:attachment_queue.attachment_failure_notification -msgid "\n" +msgid "" +"\n" "

Hello,

\n" "

The attachment ${object.name} has failed with the following error message :
${object.state_message}

\n" "

Regards,

\n" @@ -32,11 +33,6 @@ msgstr "" msgid "Action Needed" msgstr "" -#. module: attachment_queue -#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__active -msgid "Active" -msgstr "" - #. module: attachment_queue #: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__attachment_id #: model_terms:ir.ui.view,arch_db:attachment_queue.view_attachment_queue_search @@ -48,6 +44,11 @@ msgstr "" msgid "Attachment Count" msgstr "" +#. module: attachment_queue +#: model:ir.model,name:attachment_queue.model_attachment_queue +msgid "Attachment Queue" +msgstr "" + #. module: attachment_queue #: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__local_url msgid "Attachment URL" @@ -69,6 +70,16 @@ msgstr "" msgid "Binary" msgstr "" +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__changeset_change_ids +msgid "Changeset Changes" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__changeset_ids +msgid "Changesets" +msgstr "" + #. module: attachment_queue #: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__checksum msgid "Checksum/SHA1" @@ -76,7 +87,8 @@ msgstr "" #. module: attachment_queue #: model:ir.model.fields,help:attachment_queue.field_attachment_queue__failure_emails -msgid "Comma-separated list of email addresses to be notified in case offailure" +msgid "" +"Comma-separated list of email addresses to be notified in case offailure" msgstr "" #. module: attachment_queue @@ -85,6 +97,16 @@ msgstr "" msgid "Company" msgstr "" +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__count_pending_changeset_changes +msgid "Count Pending Changeset Changes" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__count_pending_changesets +msgid "Count Pending Changesets" +msgstr "" + #. module: attachment_queue #: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__create_uid msgid "Created by" @@ -121,7 +143,7 @@ msgid "Display Name" msgstr "" #. module: attachment_queue -#: selection:attachment.queue,state:0 +#: model:ir.model.fields.selection,name:attachment_queue.selection__attachment_queue__state__done #: model_terms:ir.ui.view,arch_db:attachment_queue.view_attachment_queue_search msgid "Done" msgstr "" @@ -132,12 +154,7 @@ msgid "Error" msgstr "" #. module: attachment_queue -#: selection:attachment.queue,file_type:0 -msgid "Export File (External location)" -msgstr "" - -#. module: attachment_queue -#: selection:attachment.queue,state:0 +#: model:ir.model.fields.selection,name:attachment_queue.selection__attachment_queue__state__failed #: model_terms:ir.ui.view,arch_db:attachment_queue.view_attachment_queue_search msgid "Failed" msgstr "" @@ -149,7 +166,12 @@ msgstr "" #. module: attachment_queue #: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__datas -msgid "File Content" +msgid "File Content (base64)" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__raw +msgid "File Content (raw)" msgstr "" #. module: attachment_queue @@ -167,11 +189,6 @@ msgstr "" msgid "File type" msgstr "" -#. module: attachment_queue -#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__datas_fname -msgid "Filename" -msgstr "" - #. module: attachment_queue #: model_terms:ir.ui.view,arch_db:attachment_queue.view_attachment_queue_search msgid "Filter on my documents" @@ -202,21 +219,33 @@ msgstr "" msgid "ID" msgstr "" -#. module: attachment_queue -#: model:ir.model.fields,help:attachment_queue.field_attachment_queue__message_unread -msgid "If checked new messages require your attention." -msgstr "" - #. module: attachment_queue #: model:ir.model.fields,help:attachment_queue.field_attachment_queue__message_needaction +#: model:ir.model.fields,help:attachment_queue.field_attachment_queue__message_unread msgid "If checked, new messages require your attention." msgstr "" #. module: attachment_queue #: model:ir.model.fields,help:attachment_queue.field_attachment_queue__message_has_error +#: model:ir.model.fields,help:attachment_queue.field_attachment_queue__message_has_sms_error msgid "If checked, some messages have a delivery error." msgstr "" +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__image_height +msgid "Image Height" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__image_src +msgid "Image Src" +msgstr "" + +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__image_width +msgid "Image Width" +msgstr "" + #. module: attachment_queue #: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__index_content msgid "Indexed Content" @@ -289,7 +318,7 @@ msgstr "" #. module: attachment_queue #: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__message_has_error_counter -msgid "Number of error" +msgid "Number of errors" msgstr "" #. module: attachment_queue @@ -308,19 +337,19 @@ msgid "Number of unread messages" msgstr "" #. module: attachment_queue -#: model_terms:ir.ui.view,arch_db:attachment_queue.view_attachment_queue_search -msgid "Owner" +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__original_id +msgid "Original (unoptimized, unresized) attachment" msgstr "" #. module: attachment_queue -#: selection:attachment.queue,state:0 #: model_terms:ir.ui.view,arch_db:attachment_queue.view_attachment_queue_search -msgid "Pending" +msgid "Owner" msgstr "" #. module: attachment_queue -#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__res_model_name -msgid "Res Model Name" +#: model:ir.model.fields.selection,name:attachment_queue.selection__attachment_queue__state__pending +#: model_terms:ir.ui.view,arch_db:attachment_queue.view_attachment_queue_search +msgid "Pending" msgstr "" #. module: attachment_queue @@ -355,11 +384,21 @@ msgstr "" msgid "Run Attachments Queue" msgstr "" +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__message_has_sms_error +msgid "SMS Delivery error" +msgstr "" + #. module: attachment_queue #: model_terms:ir.ui.view,arch_db:attachment_queue.view_attachment_queue_form msgid "Set to Done" msgstr "" +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__smart_search +msgid "Smart Search" +msgstr "" + #. module: attachment_queue #: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__state #: model_terms:ir.ui.view,arch_db:attachment_queue.view_attachment_queue_search @@ -388,7 +427,9 @@ msgstr "" #. module: attachment_queue #: model:ir.model.fields,help:attachment_queue.field_attachment_queue__file_type -msgid "The file type determines an import method to be used to parse and transform data before their import in ERP or an export" +msgid "" +"The file type determines an import method to be used to parse and transform " +"data before their import in ERP or an export" msgstr "" #. module: attachment_queue @@ -396,11 +437,6 @@ msgstr "" msgid "The record id this is attached to." msgstr "" -#. module: attachment_queue -#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__thumbnail -msgid "Thumbnail" -msgstr "" - #. module: attachment_queue #: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__type #: model_terms:ir.ui.view,arch_db:attachment_queue.view_attachment_queue_search @@ -427,6 +463,11 @@ msgstr "" msgid "Url" msgstr "" +#. module: attachment_queue +#: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__user_can_see_changeset +msgid "User Can See Changeset" +msgstr "" + #. module: attachment_queue #: model:ir.model.fields,field_description:attachment_queue.field_attachment_queue__website_message_ids msgid "Website Messages" @@ -439,11 +480,7 @@ msgstr "" #. module: attachment_queue #: model:ir.model.fields,help:attachment_queue.field_attachment_queue__type -msgid "You can either upload a file from your computer or copy/paste an internet link to your file." -msgstr "" - -#. module: attachment_queue -#: model:ir.model,name:attachment_queue.model_attachment_queue -msgid "attachment.queue" +msgid "" +"You can either upload a file from your computer or copy/paste an internet " +"link to your file." msgstr "" - From 1b3cff073278bcdfd674b2892fa9b3158516b5ab Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Fri, 15 Apr 2022 12:59:36 +0000 Subject: [PATCH 14/56] [UPD] README.rst --- attachment_queue/README.rst | 12 ++++++------ attachment_queue/static/description/index.html | 7 ++++--- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/attachment_queue/README.rst b/attachment_queue/README.rst index ef2d8928ace..8fe65b54753 100644 --- a/attachment_queue/README.rst +++ b/attachment_queue/README.rst @@ -14,13 +14,13 @@ Attachment Queue :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fserver--tools-lightgray.png?logo=github - :target: https://github.com/OCA/server-tools/tree/12.0/attachment_queue + :target: https://github.com/OCA/server-tools/tree/14.0/attachment_queue :alt: OCA/server-tools .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/server-tools-12-0/server-tools-12-0-attachment_queue + :target: https://translation.odoo-community.org/projects/server-tools-14-0/server-tools-14-0-attachment_queue :alt: Translate me on Weblate .. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png - :target: https://runbot.odoo-community.org/runbot/149/12.0 + :target: https://runbot.odoo-community.org/runbot/149/14.0 :alt: Try me on Runbot |badge1| |badge2| |badge3| |badge4| |badge5| @@ -54,14 +54,13 @@ This module can be used in combination with attachment_synchronize to control fi image:: ../static/description/form.png - Bug Tracker =========== Bugs are tracked on `GitHub Issues `_. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us smashing it by providing a detailed and welcomed -`feedback `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -80,6 +79,7 @@ Contributors * Florian da Costa * Angel Moya * Dan Kiplangat +* Kevin Khao Maintainers ~~~~~~~~~~~ @@ -105,6 +105,6 @@ Current `maintainers `__: |maintainer-florian-dacosta| |maintainer-sebastienbeau| -This module is part of the `OCA/server-tools `_ project on GitHub. +This module is part of the `OCA/server-tools `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/attachment_queue/static/description/index.html b/attachment_queue/static/description/index.html index ee751f31062..1195ce97d31 100644 --- a/attachment_queue/static/description/index.html +++ b/attachment_queue/static/description/index.html @@ -367,7 +367,7 @@

Attachment Queue

!! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> -

Beta License: AGPL-3 OCA/server-tools Translate me on Weblate Try me on Runbot

+

Beta License: AGPL-3 OCA/server-tools Translate me on Weblate Try me on Runbot

This module adds async processing capabilities to attachments by implementing a new model attachment.queue that wraps attachments and stores additional information so that it can be processed in an asynchronous way.

A use case of this module can be found in the attachment_synchronize module.

Table of contents

@@ -398,7 +398,7 @@

Bug Tracker

Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us smashing it by providing a detailed and welcomed -feedback.

+feedback.

Do not contact contributors directly about support or help with technical issues.

@@ -416,6 +416,7 @@

Contributors

  • Florian da Costa <florian.dacosta@akretion.com>
  • Angel Moya <http://angelmoya.es>
  • Dan Kiplangat <dan@sunflowerweb.nl>
  • +
  • Kevin Khao <kevin.khao@akretion.com>
  • @@ -427,7 +428,7 @@

    Maintainers

    promote its widespread use.

    Current maintainers:

    florian-dacosta sebastienbeau

    -

    This module is part of the OCA/server-tools project on GitHub.

    +

    This module is part of the OCA/server-tools project on GitHub.

    You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

    From 092349fcf855bb5a05133a35f036bd5f46861c25 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Fri, 15 Apr 2022 12:59:38 +0000 Subject: [PATCH 15/56] attachment_queue 14.0.1.0.1 --- attachment_queue/__manifest__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/attachment_queue/__manifest__.py b/attachment_queue/__manifest__.py index 8e30aff9527..7522a464d31 100644 --- a/attachment_queue/__manifest__.py +++ b/attachment_queue/__manifest__.py @@ -3,7 +3,7 @@ { "name": "Attachment Queue", - "version": "14.0.1.0.0", + "version": "14.0.1.0.1", "author": "Akretion,Odoo Community Association (OCA)", "summary": "Base module adding the concept of queue for processing files", "website": "https://github.com/OCA/server-tools", From 20a7fa88a19f361c15592c943e83a381e7b4026f Mon Sep 17 00:00:00 2001 From: Kevin Khao Date: Wed, 22 Feb 2023 13:12:28 +0200 Subject: [PATCH 16/56] [16.0][MIG][REF] attachment_queue: Migration to 16.0 --- attachment_queue/__manifest__.py | 8 +- attachment_queue/data/cron.xml | 16 --- attachment_queue/data/ir_config_parameter.xml | 7 - attachment_queue/data/queue_job_channel.xml | 7 + .../demo/attachment_queue_demo.xml | 9 -- attachment_queue/models/attachment_queue.py | 115 +++++++++------- .../tests/test_attachment_queue.py | 124 ++++++++++++++---- attachment_queue/tests/test_models.py | 20 +++ .../views/attachment_queue_view.xml | 11 +- 9 files changed, 210 insertions(+), 107 deletions(-) delete mode 100644 attachment_queue/data/cron.xml delete mode 100644 attachment_queue/data/ir_config_parameter.xml create mode 100644 attachment_queue/data/queue_job_channel.xml delete mode 100644 attachment_queue/demo/attachment_queue_demo.xml create mode 100644 attachment_queue/tests/test_models.py diff --git a/attachment_queue/__manifest__.py b/attachment_queue/__manifest__.py index 7522a464d31..45cd06bac4a 100644 --- a/attachment_queue/__manifest__.py +++ b/attachment_queue/__manifest__.py @@ -3,21 +3,19 @@ { "name": "Attachment Queue", - "version": "14.0.1.0.1", + "version": "16.0.1.0.1", "author": "Akretion,Odoo Community Association (OCA)", "summary": "Base module adding the concept of queue for processing files", "website": "https://github.com/OCA/server-tools", "maintainers": ["florian-dacosta", "sebastienbeau"], "license": "AGPL-3", "category": "Generic Modules", - "depends": ["base", "mail"], + "depends": ["base", "mail", "queue_job"], "data": [ "views/attachment_queue_view.xml", "security/ir.model.access.csv", - "data/cron.xml", - "data/ir_config_parameter.xml", "data/mail_template.xml", + "data/queue_job_channel.xml", ], - "demo": ["demo/attachment_queue_demo.xml"], "installable": True, } diff --git a/attachment_queue/data/cron.xml b/attachment_queue/data/cron.xml deleted file mode 100644 index fa8130f4b65..00000000000 --- a/attachment_queue/data/cron.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - Run Attachments Queue - 30 - minutes - -1 - False - - - code - model.run_attachment_queue_scheduler() - - - diff --git a/attachment_queue/data/ir_config_parameter.xml b/attachment_queue/data/ir_config_parameter.xml deleted file mode 100644 index ecf34bf8c75..00000000000 --- a/attachment_queue/data/ir_config_parameter.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - attachment_queue_cron_batch_limit - 200 - - diff --git a/attachment_queue/data/queue_job_channel.xml b/attachment_queue/data/queue_job_channel.xml new file mode 100644 index 00000000000..5e635cb03e7 --- /dev/null +++ b/attachment_queue/data/queue_job_channel.xml @@ -0,0 +1,7 @@ + + + + Attachment queues + + + diff --git a/attachment_queue/demo/attachment_queue_demo.xml b/attachment_queue/demo/attachment_queue_demo.xml deleted file mode 100644 index c1d8460e8fb..00000000000 --- a/attachment_queue/demo/attachment_queue_demo.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - bWlncmF0aW9uIHRlc3Q= - attachment_queue_demo.doc - - - diff --git a/attachment_queue/models/attachment_queue.py b/attachment_queue/models/attachment_queue.py index b7d279c8ce4..64f39f1f375 100644 --- a/attachment_queue/models/attachment_queue.py +++ b/attachment_queue/models/attachment_queue.py @@ -2,10 +2,19 @@ import logging -from odoo import SUPERUSER_ID, api, fields, models, registry +from odoo import api, fields, models +from odoo.exceptions import UserError + +from odoo.addons.queue_job.exception import RetryableJobError _logger = logging.getLogger(__name__) +DEFAULT_ETA_FOR_RETRY = 60 * 60 +STR_ERR_ATTACHMENT_RUNNING = ( + "The attachment is currently flagged as being in processing" +) +STR_ERROR_DURING_PROCESSING = "Error during processing of attachment_queue id {}: \n" + class AttachmentQueue(models.Model): _name = "attachment.queue" @@ -34,10 +43,38 @@ class AttachmentQueue(models.Model): state_message = fields.Text() failure_emails = fields.Char( compute="_compute_failure_emails", - string="Failure Emails", help="Comma-separated list of email addresses to be notified in case of" "failure", ) + running_lock = fields.Boolean() + + @property + def _eta_for_retry(self): + return DEFAULT_ETA_FOR_RETRY + + @property + def _job_attrs(self): + return {"channel": "Attachment queues"} + + def _schedule_jobs(self): + for el in self: + el.with_delay(**self._job_attrs).run() + + @api.model_create_multi + def create(self, vals_list): + res = super().create(vals_list) + res._schedule_jobs() + return res + + def button_reschedule(self): + self.state = "pending" + self.state_message = "" + self._schedule_jobs() + + def button_manual_run(self): + if self.running_lock: + raise UserError(STR_ERR_ATTACHMENT_RUNNING) + self.run() def _compute_failure_emails(self): for attach in self: @@ -48,51 +85,41 @@ def _get_failure_emails(self): self.ensure_one() return "" - @api.model - def run_attachment_queue_scheduler(self, domain=None): - if domain is None: - domain = [("state", "=", "pending")] - batch_limit = self.env.ref( - "attachment_queue.attachment_queue_cron_batch_limit" - ).value - if batch_limit and batch_limit.isdigit(): - limit = int(batch_limit) - else: - limit = 200 - attachments = self.search(domain, limit=limit) - if attachments: - return attachments.run() - return True - def run(self): """ - Run the process for each attachment queue + Run the process for an individual attachment queue """ - failure_tmpl = self.env.ref("attachment_queue.attachment_failure_notification") - for attachment in self: - with api.Environment.manage(): - with registry(self.env.cr.dbname).cursor() as new_cr: - new_env = api.Environment(new_cr, SUPERUSER_ID, self.env.context) - attach = attachment.with_env(new_env) - try: - attach._run() - # pylint: disable=broad-except - except Exception as e: - attach.env.cr.rollback() - _logger.exception(str(e)) - attach.write({"state": "failed", "state_message": str(e)}) - emails = attach.failure_emails - if emails: - failure_tmpl.send_mail(attach.id) - attach.env.cr.commit() - else: - vals = { - "state": "done", - "date_done": fields.Datetime.now(), - } - attach.write(vals) - attach.env.cr.commit() - return True + if self.state != "pending": + return + if self.running_lock is True: + raise RetryableJobError( + STR_ERR_ATTACHMENT_RUNNING, seconds=self._eta_for_retry + ) + self.running_lock = True + self.flush_recordset() + try: + with self.env.cr.savepoint(): + self._run() + except Exception as e: + _logger.warning(STR_ERROR_DURING_PROCESSING.format(self.id) + str(e)) + self.write( + {"state": "failed", "state_message": str(e), "running_lock": False} + ) + emails = self.failure_emails + if emails: + self.env.ref( + "attachment_queue.attachment_failure_notification" + ).send_mail(self.id) + return False + else: + self.write( + { + "state": "done", + "date_done": fields.Datetime.now(), + "running_lock": False, + } + ) + return True def _run(self): self.ensure_one() diff --git a/attachment_queue/tests/test_attachment_queue.py b/attachment_queue/tests/test_attachment_queue.py index 3d8910be33d..346841b42d9 100644 --- a/attachment_queue/tests/test_attachment_queue.py +++ b/attachment_queue/tests/test_attachment_queue.py @@ -1,35 +1,111 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -import odoo -from odoo import api +from unittest import mock + +from odoo_test_helper import FakeModelLoader + +from odoo.exceptions import UserError from odoo.tests.common import TransactionCase +from odoo.addons.queue_job.exception import RetryableJobError +from odoo.addons.queue_job.tests.common import trap_jobs + +DUMMY_AQ_VALS = { + "datas": "", + "name": "dummy_aq.doc", +} +MOCK_PATH_RUN = ( + "odoo.addons.attachment_queue.models.attachment_queue.AttachmentQueue._run" +) + class TestAttachmentBaseQueue(TransactionCase): + def _create_dummy_attachment(self, override=False, no_job=False): + override = override or {} + vals = DUMMY_AQ_VALS.copy() + vals.update(override) + if no_job: + return ( + self.env["attachment.queue"].with_context(test_queue_job_no_delay=True) + ).create(vals) + return self.env["attachment.queue"].create(vals) + + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.loader = FakeModelLoader(cls.env, cls.__module__) + cls.loader.backup_registry() + from .test_models import AttachmentQueue + + cls.loader.update_registry((AttachmentQueue,)) + + @classmethod + def tearDownClass(cls): + super().tearDownClass() + cls.loader.restore_registry() + return super().tearDownClass() + def setUp(self): super().setUp() - self.registry.enter_test_mode(self.env.cr) - self.env = api.Environment( - self.registry.test_cr, self.env.uid, self.env.context - ) - self.attachment = self.env.ref("attachment_queue.attachment_queue_demo") - - def tearDown(self): - self.registry.leave_test_mode() - super().tearDown() - - def test_attachment_queue(self): - """Test run_attachment_queue_scheduler to ensure set state to done""" - self.assertEqual(self.attachment.state, "pending") - self.env["attachment.queue"].run_attachment_queue_scheduler() - self.env.cache.invalidate() - with odoo.registry(self.env.cr.dbname).cursor() as new_cr: - new_env = api.Environment(new_cr, self.env.uid, self.env.context) - attach = self.attachment.with_env(new_env) - self.assertEqual(attach.state, "done") + self.aq_model = self.env["attachment.queue"] + + def test_job_created(self): + with trap_jobs() as trap: + self._create_dummy_attachment() + trap.assert_enqueued_job( + self.env["attachment.queue"].run, + ) + + def test_aq_locked_job(self): + """If an attachment is already running, and a job tries to run it, retry later""" + with self.assertRaises(RetryableJobError): + self._create_dummy_attachment({"running_lock": True}, no_job=True) + + def test_aq_locked_button(self): + """If an attachment is already running, and a user tries to run it manually, + raise error window""" + attachment = self._create_dummy_attachment(no_job=True) + attachment.running_lock = True + with self.assertRaises(UserError): + attachment.button_manual_run() + + def test_run_ok(self): + """Attachment queue should have correct state and result""" + partners_initial = len(self.env["res.partner"].search([])) + with mock.patch.object( + type(self.aq_model), + "_run", + self.env["attachment.queue"].mock_run_create_partners, + ): + attachment = self._create_dummy_attachment(no_job=True) + partners_after = len(self.env["res.partner"].search([])) + self.assertEqual(partners_after, partners_initial + 10) + self.assertEqual(attachment.state, "done") + + def test_run_fails(self): + """Attachment queue should have correct state/error message""" + with mock.patch.object( + type(self.aq_model), "_run", self.env["attachment.queue"].mock_run_fail + ): + attachment = self._create_dummy_attachment(no_job=True) + self.assertEqual(attachment.state, "failed") + self.assertEqual(attachment.state_message, "boom") + + def test_run_fails_rollback(self): + """In case of failure, no side effects should occur""" + partners_initial = len(self.env["res.partner"].search([])) + with mock.patch.object( + type(self.aq_model), + "_run", + self.env["attachment.queue"].mock_run_create_partners_and_fail, + ): + self._create_dummy_attachment(no_job=True) + partners_after = len(self.env["res.partner"].search([])) + self.assertEqual(partners_after, partners_initial) def test_set_done(self): """Test set_done manually""" - self.assertEqual(self.attachment.state, "pending") - self.attachment.set_done() - self.assertEqual(self.attachment.state, "done") + attachment = self._create_dummy_attachment() + self.assertEqual(attachment.state, "pending") + attachment.set_done() + self.assertEqual(attachment.state, "done") diff --git a/attachment_queue/tests/test_models.py b/attachment_queue/tests/test_models.py new file mode 100644 index 00000000000..d84c8eaadce --- /dev/null +++ b/attachment_queue/tests/test_models.py @@ -0,0 +1,20 @@ +# Copyright 2023 Akretion +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from odoo import _, models +from odoo.exceptions import UserError + + +class AttachmentQueue(models.Model): + _inherit = "attachment.queue" + _name = "attachment.queue" + + def mock_run_fail(self): + raise UserError(_("boom")) + + def mock_run_create_partners(self): + for x in range(10): + self.env["res.partner"].create({"name": str(x)}) + + def mock_run_create_partners_and_fail(self): + self.mock_run_create_partners() + raise UserError(_("boom")) diff --git a/attachment_queue/views/attachment_queue_view.xml b/attachment_queue/views/attachment_queue_view.xml index 38109902654..89eddcc6650 100644 --- a/attachment_queue/views/attachment_queue_view.xml +++ b/attachment_queue/views/attachment_queue_view.xml @@ -9,9 +9,16 @@
    +

    Attachment Queue

    + + +

    Beta License: AGPL-3 OCA/server-tools Translate me on Weblate Try me on Runbot

    +

    This module adds async processing capabilities to attachments by implementing a new model attachment.queue that wraps attachments and stores additional information so that it can be processed in an asynchronous way.

    +

    A use case of this module can be found in the attachment_synchronize module.

    +

    Table of contents

    + +
    +

    Usage

    +

    Go the menu Settings > Technical > Database Structure > Attachments Queue

    +

    You can create / see standard attachments with additional fields

    +

    Configure the batch limit for attachments that can be sync by the cron task at a go:

    +

    Settings > Technical > System parameters > attachment_queue_cron_batch_limit

    +

    image:: ../static/description/tree.png

    +

    This module can be used in combination with attachment_synchronize to control file processing workflow

    +

    image:: ../static/description/form.png

    +
    +
    +

    Bug Tracker

    +

    Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +feedback.

    +

    Do not contact contributors directly about support or help with technical issues.

    +
    +
    +

    Credits

    +
    +

    Authors

    +
      +
    • Akretion
    • +
    +
    +
    +

    Contributors

    + +
    +
    +

    Maintainers

    +

    This module is maintained by the OCA.

    +Odoo Community Association +

    OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

    +

    Current maintainers:

    +

    florian-dacosta sebastienbeau

    +

    This module is part of the OCA/server-tools project on GitHub.

    +

    You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

    +
    +
    +