Skip to content

Commit eba1ed2

Browse files
committed
[19.0][MIG] base_import_async: migrate
- Move BaseImportImport wizard code to wizard/\n- Update tests and async import flow\n- Update pre-commit exclusions
1 parent d2a8e90 commit eba1ed2

File tree

10 files changed

+200
-49
lines changed

10 files changed

+200
-49
lines changed

.pre-commit-config.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
exclude: |
22
(?x)
33
# NOT INSTALLABLE ADDONS
4-
^base_import_async/|
54
^queue_job_batch/|
65
^queue_job_cron/|
76
^queue_job_cron_jobrunner/|

base_import_async/README.rst

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
.. image:: https://odoo-community.org/readme-banner-image
2+
:target: https://odoo-community.org/get-involved?utm_source=readme
3+
:alt: Odoo Community Association
4+
15
===================
26
Asynchronous Import
37
===================
@@ -13,17 +17,17 @@ Asynchronous Import
1317
.. |badge1| image:: https://img.shields.io/badge/maturity-Production%2FStable-green.png
1418
:target: https://odoo-community.org/page/development-status
1519
:alt: Production/Stable
16-
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
20+
.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png
1721
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
1822
:alt: License: AGPL-3
1923
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fqueue-lightgray.png?logo=github
20-
:target: https://github.com/OCA/queue/tree/18.0/base_import_async
24+
:target: https://github.com/OCA/queue/tree/19.0/base_import_async
2125
:alt: OCA/queue
2226
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
23-
:target: https://translation.odoo-community.org/projects/queue-18-0/queue-18-0-base_import_async
27+
:target: https://translation.odoo-community.org/projects/queue-19-0/queue-19-0-base_import_async
2428
:alt: Translate me on Weblate
2529
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
26-
:target: https://runboat.odoo-community.org/builds?repo=OCA/queue&target_branch=18.0
30+
:target: https://runboat.odoo-community.org/builds?repo=OCA/queue&target_branch=19.0
2731
:alt: Try me on Runboat
2832

2933
|badge1| |badge2| |badge3| |badge4| |badge5|
@@ -87,7 +91,7 @@ Bug Tracker
8791
Bugs are tracked on `GitHub Issues <https://github.com/OCA/queue/issues>`_.
8892
In case of trouble, please check there if your issue has already been reported.
8993
If you spotted it first, help us to smash it by providing a detailed and welcomed
90-
`feedback <https://github.com/OCA/queue/issues/new?body=module:%20base_import_async%0Aversion:%2018.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
94+
`feedback <https://github.com/OCA/queue/issues/new?body=module:%20base_import_async%0Aversion:%2019.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
9195

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

@@ -149,6 +153,6 @@ OCA, or the Odoo Community Association, is a nonprofit organization whose
149153
mission is to support the collaborative development of Odoo features and
150154
promote its widespread use.
151155

152-
This module is part of the `OCA/queue <https://github.com/OCA/queue/tree/18.0/base_import_async>`_ project on GitHub.
156+
This module is part of the `OCA/queue <https://github.com/OCA/queue/tree/19.0/base_import_async>`_ project on GitHub.
153157

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

base_import_async/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
22

33
from . import models
4+
from . import wizard

base_import_async/__manifest__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
{
66
"name": "Asynchronous Import",
77
"summary": "Import CSV files in the background",
8-
"version": "18.0.1.0.0",
8+
"version": "19.0.1.0.0",
99
"author": "Akretion, ACSONE SA/NV, Odoo Community Association (OCA)",
1010
"license": "AGPL-3",
1111
"website": "https://github.com/OCA/queue",
@@ -20,6 +20,6 @@
2020
"base_import_async/static/src/xml/import_data_sidepanel.xml",
2121
],
2222
},
23-
"installable": False,
23+
"installable": True,
2424
"development_status": "Production/Stable",
2525
}
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
11
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
22

3-
from . import base_import_import
43
from . import queue_job

base_import_async/models/queue_job.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Copyright 2017 ACSONE SA/NV
22
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
33

4-
from odoo import _, models
4+
from odoo import models
55

66

77
class QueueJob(models.Model):
@@ -11,7 +11,7 @@ class QueueJob(models.Model):
1111

1212
def _related_action_attachment(self):
1313
return {
14-
"name": _("Attachment"),
14+
"name": self.env._("Attachment"),
1515
"type": "ir.actions.act_window",
1616
"res_model": "ir.attachment",
1717
"view_mode": "form",

base_import_async/static/description/index.html

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<head>
44
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
55
<meta name="generator" content="Docutils: https://docutils.sourceforge.io/" />
6-
<title>Asynchronous Import</title>
6+
<title>README.rst</title>
77
<style type="text/css">
88

99
/*
@@ -360,16 +360,21 @@
360360
</style>
361361
</head>
362362
<body>
363-
<div class="document" id="asynchronous-import">
364-
<h1 class="title">Asynchronous Import</h1>
363+
<div class="document">
365364

365+
366+
<a class="reference external image-reference" href="https://odoo-community.org/get-involved?utm_source=readme">
367+
<img alt="Odoo Community Association" src="https://odoo-community.org/readme-banner-image" />
368+
</a>
369+
<div class="section" id="asynchronous-import">
370+
<h1>Asynchronous Import</h1>
366371
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
367372
!! This file is generated by oca-gen-addon-readme !!
368373
!! changes will be overwritten. !!
369374
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
370375
!! source digest: sha256:299c64b165f6348dfa062b8925a95235390daa6063964f21a5a1d0757cdf1614
371376
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
372-
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Production/Stable" src="https://img.shields.io/badge/maturity-Production%2FStable-green.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/queue/tree/18.0/base_import_async"><img alt="OCA/queue" src="https://img.shields.io/badge/github-OCA%2Fqueue-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/queue-18-0/queue-18-0-base_import_async"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/queue&amp;target_branch=18.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
377+
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Production/Stable" src="https://img.shields.io/badge/maturity-Production%2FStable-green.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/license-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/queue/tree/19.0/base_import_async"><img alt="OCA/queue" src="https://img.shields.io/badge/github-OCA%2Fqueue-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/queue-19-0/queue-19-0-base_import_async"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/queue&amp;target_branch=19.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
373378
<p>This module extends the standard CSV import functionality to import
374379
files in the background using the OCA/queue framework.</p>
375380
<p><strong>Table of contents</strong></p>
@@ -392,7 +397,7 @@ <h1 class="title">Asynchronous Import</h1>
392397
</ul>
393398
</div>
394399
<div class="section" id="usage">
395-
<h1><a class="toc-backref" href="#toc-entry-1">Usage</a></h1>
400+
<h2><a class="toc-backref" href="#toc-entry-1">Usage</a></h2>
396401
<p>The user is presented with a new checkbox in the import screen. When
397402
selected, the import is delayed in a background job.</p>
398403
<p>This job in turn splits the CSV file in chunks of minimum 100 lines (or
@@ -416,7 +421,7 @@ <h1><a class="toc-backref" href="#toc-entry-1">Usage</a></h1>
416421
</ol>
417422
</div>
418423
<div class="section" id="known-issues-roadmap">
419-
<h1><a class="toc-backref" href="#toc-entry-2">Known issues / Roadmap</a></h1>
424+
<h2><a class="toc-backref" href="#toc-entry-2">Known issues / Roadmap</a></h2>
420425
<ul class="simple">
421426
<li>There is currently no user interface to control the chunk size, which
422427
is currently 100 by default. Should this proves to be an issue, it is
@@ -425,33 +430,33 @@ <h1><a class="toc-backref" href="#toc-entry-2">Known issues / Roadmap</a></h1>
425430
</ul>
426431
</div>
427432
<div class="section" id="changelog">
428-
<h1><a class="toc-backref" href="#toc-entry-3">Changelog</a></h1>
433+
<h2><a class="toc-backref" href="#toc-entry-3">Changelog</a></h2>
429434
<div class="section" id="section-1">
430-
<h2><a class="toc-backref" href="#toc-entry-4">13.0.1.0.0 (2019-12-20)</a></h2>
435+
<h3><a class="toc-backref" href="#toc-entry-4">13.0.1.0.0 (2019-12-20)</a></h3>
431436
<ul class="simple">
432437
<li>[MIGRATION] from 12.0 branched at rev. a7f8031</li>
433438
</ul>
434439
</div>
435440
</div>
436441
<div class="section" id="bug-tracker">
437-
<h1><a class="toc-backref" href="#toc-entry-5">Bug Tracker</a></h1>
442+
<h2><a class="toc-backref" href="#toc-entry-5">Bug Tracker</a></h2>
438443
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/queue/issues">GitHub Issues</a>.
439444
In case of trouble, please check there if your issue has already been reported.
440445
If you spotted it first, help us to smash it by providing a detailed and welcomed
441-
<a class="reference external" href="https://github.com/OCA/queue/issues/new?body=module:%20base_import_async%0Aversion:%2018.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
446+
<a class="reference external" href="https://github.com/OCA/queue/issues/new?body=module:%20base_import_async%0Aversion:%2019.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
442447
<p>Do not contact contributors directly about support or help with technical issues.</p>
443448
</div>
444449
<div class="section" id="credits">
445-
<h1><a class="toc-backref" href="#toc-entry-6">Credits</a></h1>
450+
<h2><a class="toc-backref" href="#toc-entry-6">Credits</a></h2>
446451
<div class="section" id="authors">
447-
<h2><a class="toc-backref" href="#toc-entry-7">Authors</a></h2>
452+
<h3><a class="toc-backref" href="#toc-entry-7">Authors</a></h3>
448453
<ul class="simple">
449454
<li>Akretion</li>
450455
<li>ACSONE SA/NV</li>
451456
</ul>
452457
</div>
453458
<div class="section" id="contributors">
454-
<h2><a class="toc-backref" href="#toc-entry-8">Contributors</a></h2>
459+
<h3><a class="toc-backref" href="#toc-entry-8">Contributors</a></h3>
455460
<p>Sébastien Beau (Akretion) authored the initial prototype.</p>
456461
<p>Stéphane Bidoul (ACSONE) extended it to version 1.0 to support
457462
multi-line records, store data to import as attachments and let the user
@@ -473,23 +478,24 @@ <h2><a class="toc-backref" href="#toc-entry-8">Contributors</a></h2>
473478
</ul>
474479
</div>
475480
<div class="section" id="other-credits">
476-
<h2><a class="toc-backref" href="#toc-entry-9">Other credits</a></h2>
481+
<h3><a class="toc-backref" href="#toc-entry-9">Other credits</a></h3>
477482
<p>The migration of this module from 17.0 to 18.0 was financially supported
478483
by Camptocamp</p>
479484
</div>
480485
<div class="section" id="maintainers">
481-
<h2><a class="toc-backref" href="#toc-entry-10">Maintainers</a></h2>
486+
<h3><a class="toc-backref" href="#toc-entry-10">Maintainers</a></h3>
482487
<p>This module is maintained by the OCA.</p>
483488
<a class="reference external image-reference" href="https://odoo-community.org">
484489
<img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" />
485490
</a>
486491
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose
487492
mission is to support the collaborative development of Odoo features and
488493
promote its widespread use.</p>
489-
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/queue/tree/18.0/base_import_async">OCA/queue</a> project on GitHub.</p>
494+
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/queue/tree/19.0/base_import_async">OCA/queue</a> project on GitHub.</p>
490495
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
491496
</div>
492497
</div>
493498
</div>
499+
</div>
494500
</body>
495501
</html>

base_import_async/tests/test_base_import_import.py

Lines changed: 142 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,16 @@
33

44
from odoo.tests.common import RecordCapturer, TransactionCase
55

6-
from ..models.base_import_import import OPT_USE_QUEUE
6+
from odoo.addons.queue_job.exception import FailedJobError
7+
8+
from ..wizard.base_import_import import (
9+
INIT_PRIORITY,
10+
OPT_CHUNK_SIZE,
11+
OPT_HAS_HEADER,
12+
OPT_QUOTING,
13+
OPT_SEPARATOR,
14+
OPT_USE_QUEUE,
15+
)
716

817

918
class TestBaseImportImport(TransactionCase):
@@ -59,6 +68,54 @@ def test_normal_import_res_partners(self):
5968
self.assertEqual(len(records_created), 2)
6069
self.assertIn("partner1", records_created[0].email)
6170

71+
def test_execute_import_res_partners_async(self):
72+
values = [
73+
[
74+
"name",
75+
"email",
76+
"is_company",
77+
],
78+
[
79+
"partner 1",
80+
81+
"1",
82+
],
83+
[
84+
"partner 2",
85+
86+
"0",
87+
],
88+
]
89+
import_vals = {
90+
"res_model": self.res_partners._name,
91+
"file": "\n".join([";".join(values) for values in values]),
92+
"file_type": "text/csv",
93+
"file_name": "partners.csv",
94+
}
95+
import_wizard = self.import_wizard.create(import_vals)
96+
opts = {
97+
OPT_QUOTING: '"',
98+
OPT_SEPARATOR: ";",
99+
OPT_HAS_HEADER: True,
100+
OPT_USE_QUEUE: True,
101+
}
102+
preview = import_wizard.parse_preview(opts)
103+
with RecordCapturer(self.env["queue.job"], []) as capture:
104+
result = import_wizard.execute_import(
105+
[field[0] for field in preview["matches"].values()],
106+
[],
107+
opts,
108+
)
109+
self.assertEqual(result, [])
110+
self.assertEqual(len(capture.records), 1)
111+
job = capture.records[0]
112+
self.assertEqual(job.method_name, "_split_file")
113+
self.assertEqual(job.model_name, "base_import.import")
114+
attachment = self.env["ir.attachment"].search(
115+
[("res_model", "=", "queue.job"), ("res_id", "=", job.id)], limit=1
116+
)
117+
self.assertTrue(attachment)
118+
62119
def test_wrong_import_res_partners(self):
63120
values = [
64121
[
@@ -97,3 +154,87 @@ def test_wrong_import_res_partners(self):
97154
opts,
98155
)
99156
self.assertTrue(any(msg["type"] == "error" for msg in results["messages"]))
157+
158+
def test_split_file_creates_chunk_jobs_and_links_attachments(self):
159+
import_wizard = self.import_wizard.create(
160+
{
161+
"res_model": self.res_partners._name,
162+
"file": "dummy",
163+
"file_type": "text/csv",
164+
}
165+
)
166+
fields = ["name", "email", "is_company"]
167+
data = [
168+
["partner 1", "[email protected]", "1"],
169+
["partner 2", "[email protected]", "0"],
170+
["partner 3", "[email protected]", "0"],
171+
]
172+
opts = {
173+
OPT_QUOTING: '"',
174+
OPT_SEPARATOR: ";",
175+
OPT_HAS_HEADER: True,
176+
OPT_CHUNK_SIZE: 2,
177+
}
178+
attachment = import_wizard._create_csv_attachment(
179+
fields, data, opts, "partners.csv"
180+
)
181+
with RecordCapturer(self.env["queue.job"], []) as capture:
182+
import_wizard._split_file(
183+
model_name=self.res_partners._name,
184+
translated_model_name="Contacts",
185+
attachment=attachment,
186+
options=opts,
187+
file_name="partners.csv",
188+
)
189+
jobs = capture.records.sorted("priority")
190+
self.assertEqual(len(jobs), 2)
191+
self.assertEqual(jobs.mapped("method_name"), ["_import_one_chunk"] * 2)
192+
self.assertEqual(jobs.mapped("priority"), [INIT_PRIORITY, INIT_PRIORITY + 1])
193+
self.assertEqual(
194+
jobs.mapped("name"),
195+
[
196+
"Import Contacts from file partners.csv - #0 - lines 2 to 3",
197+
"Import Contacts from file partners.csv - #1 - lines 4 to 4",
198+
],
199+
)
200+
chunk_attachments = self.env["ir.attachment"].search(
201+
[("name", "in", ["partners-0.csv", "partners-1.csv"])]
202+
)
203+
self.assertEqual(len(chunk_attachments), 2)
204+
self.assertTrue(all(att.res_model == "queue.job" for att in chunk_attachments))
205+
self.assertTrue(all(att.res_id in jobs.ids for att in chunk_attachments))
206+
207+
def test_import_one_chunk_success_and_error(self):
208+
import_wizard = self.import_wizard.create(
209+
{
210+
"res_model": self.res_partners._name,
211+
"file": "dummy",
212+
"file_type": "text/csv",
213+
}
214+
)
215+
opts = {OPT_QUOTING: '"', OPT_SEPARATOR: ";"}
216+
217+
fields = ["name", "email", "is_company"]
218+
data = [
219+
["partner 1", "[email protected]", "1"],
220+
["partner 2", "[email protected]", "0"],
221+
]
222+
attachment = import_wizard._create_csv_attachment(
223+
fields, data, opts, "partners.csv"
224+
)
225+
with RecordCapturer(self.res_partners, []) as capture:
226+
result = import_wizard._import_one_chunk(
227+
self.res_partners._name, attachment, opts
228+
)
229+
self.assertEqual(result["messages"], [])
230+
self.assertEqual(len(capture.records), 2)
231+
232+
invalid_fields = ["name", "company_type"]
233+
invalid_data = [["partner 3", "invalid"]]
234+
invalid_attachment = import_wizard._create_csv_attachment(
235+
invalid_fields, invalid_data, opts, "invalid.csv"
236+
)
237+
with self.assertRaises(FailedJobError):
238+
import_wizard._import_one_chunk(
239+
self.res_partners._name, invalid_attachment, opts
240+
)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
2+
3+
from . import base_import_import

0 commit comments

Comments
 (0)