Skip to content

Commit

Permalink
added background process to import area; improved test cases
Browse files Browse the repository at this point in the history
  • Loading branch information
reichie020212 committed Jan 24, 2024
1 parent fbcec83 commit fc01a3f
Show file tree
Hide file tree
Showing 6 changed files with 189 additions and 18 deletions.
6 changes: 6 additions & 0 deletions spp_area/data/queue_job_channel.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<odoo noupdate="1">
<record model="queue.job.channel" id="channel_area_import">
<field name="name">area_import</field>
<field name="parent_id" ref="queue_job.channel_root" />
</record>
</odoo>
114 changes: 103 additions & 11 deletions spp_area/models/area_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,17 @@
from odoo import _, api, fields, models
from odoo.exceptions import ValidationError

from odoo.addons.queue_job.delay import group

_logger = logging.getLogger(__name__)


class OpenSPPAreaImport(models.Model):
_name = "spp.area.import"
_description = "Areas Import Table"

MIN_ROW_JOB_QUEUE = 400

NEW = "New"
UPLOADED = "Uploaded"
IMPORTED = "Imported"
Expand Down Expand Up @@ -59,6 +63,9 @@ class OpenSPPAreaImport(models.Model):
default=NEW,
)

locked = fields.Boolean(default=False)
locked_reason = fields.Char(readonly=True)

@api.onchange("excel_file")
def excel_file_change(self):
"""
Expand Down Expand Up @@ -215,25 +222,110 @@ def import_data(self):

def validate_raw_data(self):
"""
The function iterates through a collection of records and validates the raw data associated with
each record, updating the state of the record if there are no validation errors.
The function iterates through a collection of records and checks if the count of raw data is
less than a minimum threshold, and if so, it calls a validation function, otherwise it calls an
asynchronous function.
"""
for rec in self:
has_error = rec.raw_data_ids.validate_raw_data()
if not has_error:
rec.update(
{
"state": self.VALIDATED,
}
raw_data_count = len(rec.raw_data_ids)
if raw_data_count < self.MIN_ROW_JOB_QUEUE:
rec._validate_raw_data()
else:
rec._async_function(
raw_data_count, _("Validating data."), "_validate_raw_data"
)

def _validate_raw_data(self):
"""
The function validates raw data and updates the state if there are no errors.
"""
self.ensure_one()

has_error = self.raw_data_ids.validate_raw_data()
if not has_error:
self.update(
{
"state": self.VALIDATED,
}
)

def _async_function(self, raw_data_count, reason_message, function_name):
"""
The above function is an asynchronous function that locks the current record, executes a delayed
job, and marks the job as done when it completes.
:param raw_data_count: The `raw_data_count` parameter represents the number of raw data items
that will be processed in the async function
:param reason_message: The `reason_message` parameter is a string that represents the reason for
locking the object
:param function_name: The `function_name` parameter is the name of the function that will be
called asynchronously
"""
self.ensure_one()

self.write(
{
"locked": True,
"locked_reason": reason_message,
}
)

jobs = []

func = getattr(self.delayable(channel="root.area_import"), function_name)

jobs.append(func())

main_job = group(*jobs)

main_job.on_done(self.delayable(channel="root.area_import")._async_mark_done())
main_job.delay()

def _async_mark_done(self):
"""
The function `_async_mark_done` unlocks a resource by setting the `locked` attribute to `False`
and clearing the `locked_reason` attribute.
"""
self.ensure_one()

Check warning on line 289 in spp_area/models/area_import.py

View check run for this annotation

Codecov / codecov/patch

spp_area/models/area_import.py#L289

Added line #L289 was not covered by tests

self.locked = False
self.locked_reason = None

Check warning on line 292 in spp_area/models/area_import.py

View check run for this annotation

Codecov / codecov/patch

spp_area/models/area_import.py#L291-L292

Added lines #L291 - L292 were not covered by tests

def save_to_area(self):
"""
The function saves the raw data IDs to an area and updates the state to "Done".
The function saves data to an area, either synchronously or asynchronously depending on the
number of raw data records.
"""
for rec in self:
rec.raw_data_ids.save_to_area()
rec.state = self.DONE
raw_data_count = len(rec.raw_data_ids)

if raw_data_count < self.MIN_ROW_JOB_QUEUE:
rec._save_to_area()
else:
rec._async_function(
raw_data_count, _("Saving to Area."), "_save_to_area"
)

def _save_to_area(self):
"""
The function saves raw data to an area and updates the state to "DONE".
"""
self.ensure_one()

self.raw_data_ids.save_to_area()
self.state = self.DONE

def refresh_page(self):
"""
The function `refresh_page` returns a dictionary with the type and tag values to reload the
page.
:return: The code is returning a dictionary with two key-value pairs. The "type" key has the
value "ir.actions.client" and the "tag" key has the value "reload".
"""
return {
"type": "ir.actions.client",
"tag": "reload",
}


# Assets Import Raw Data
Expand Down
18 changes: 18 additions & 0 deletions spp_area/tests/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ class AreaImportTestMixin(TransactionCase):
@classmethod
def setUpClass(cls):
super(AreaImportTestMixin, cls).setUpClass()
# Greater than or equal to 400 rows
xls_file = None
xls_file_name = None

Expand All @@ -23,3 +24,20 @@ def setUpClass(cls):
"state": "Uploaded",
}
)

# Less than 400 rows
xls_file_2 = None
xls_file_name_2 = None

file_path_2 = f"{os.path.dirname(os.path.abspath(__file__))}/pse_adminboundaries_tabulardata.xlsx"
with open(file_path_2, "rb") as f:
xls_file_name_2 = f.name
xls_file_2 = base64.b64encode(f.read())

cls.area_import_id_2 = cls.env["spp.area.import"].create(
{
"excel_file": xls_file_2,
"name": xls_file_name_2,
"state": "Uploaded",
}
)
Binary file not shown.
51 changes: 45 additions & 6 deletions spp_area/tests/test_area_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,33 +32,61 @@ def test_03_import_data(self):
)

def test_04_validate_raw_data(self):
# Greater than or equal to 400 rows
self.area_import_id.import_data()
has_error = self.area_import_id.validate_raw_data()
self.area_import_id.validate_raw_data()

raw_data_ids = self.area_import_id.raw_data_ids

self.assertFalse(has_error)
self.assertEqual(len(raw_data_ids.ids), self.area_import_id.tot_rows_imported)
self.assertEqual(0, self.area_import_id.tot_rows_error)
self.assertEqual(self.area_import_id.state, "Validated")
self.assertEqual(self.area_import_id.state, "Imported")
self.assertTrue(self.area_import_id.locked)
self.assertEqual(self.area_import_id.locked_reason, "Validating data.")

# Less than 400 rows
self.area_import_id_2.import_data()
self.area_import_id_2.validate_raw_data()

raw_data_ids = self.area_import_id_2.raw_data_ids
self.assertEqual(len(raw_data_ids.ids), self.area_import_id_2.tot_rows_imported)
self.assertEqual(0, self.area_import_id_2.tot_rows_error)
self.assertEqual(self.area_import_id_2.state, "Validated")
self.assertFalse(self.area_import_id_2.locked)
self.assertFalse(self.area_import_id_2.locked_reason)
self.assertEqual(
self.env["spp.area.import.raw"].search(
[("id", "in", raw_data_ids.ids), ("state", "=", "Validated")],
count=True,
),
self.area_import_id.tot_rows_imported,
self.area_import_id_2.tot_rows_imported,
)

def test_05_save_to_area(self):
# Greater than or equal to 400 rows
self.area_import_id.import_data()
self.area_import_id.validate_raw_data()
self.area_import_id.save_to_area()

raw_data_ids = self.area_import_id.raw_data_ids

self.assertEqual(len(raw_data_ids.ids), self.area_import_id.tot_rows_imported)
self.assertEqual(0, self.area_import_id.tot_rows_error)
self.assertEqual(self.area_import_id.state, "Done")
self.assertEqual(self.area_import_id.state, "Imported")
self.assertTrue(self.area_import_id.locked)
self.assertEqual(self.area_import_id.locked_reason, "Saving to Area.")

# Less than 400 rows
self.area_import_id_2.import_data()
self.area_import_id_2.validate_raw_data()
self.area_import_id_2.save_to_area()

raw_data_ids = self.area_import_id_2.raw_data_ids

self.assertEqual(len(raw_data_ids.ids), self.area_import_id_2.tot_rows_imported)
self.assertEqual(0, self.area_import_id_2.tot_rows_error)
self.assertEqual(self.area_import_id_2.state, "Done")
self.assertFalse(self.area_import_id_2.locked)
self.assertFalse(self.area_import_id_2.locked_reason)

for raw_data_id in raw_data_ids:
self.assertTrue(
Expand All @@ -72,3 +100,14 @@ def test_05_save_to_area(self):
)
)
)

def test_06_refresh_page(self):
action = self.area_import_id.refresh_page()

self.assertEqual(
action,
{
"type": "ir.actions.client",
"tag": "reload",
},
)
18 changes: 17 additions & 1 deletion spp_area/views/area_import_views.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
<field name="priority">1</field>
<field name="arch" type="xml">
<form string="Area Import">
<header>
<field name="locked" invisible="True" />
<header attrs="{'invisible': [('locked', '=', True)]}">
<button
name="import_data"
type="object"
Expand Down Expand Up @@ -49,6 +50,21 @@
statusbar_visible="New,Uploaded,Imported,Done,Cancelled"
/>
</header>
<div
class="alert alert-warning text-center o_form_header"
role="status"
attrs="{'invisible': [('locked', '!=', True)]}"
>
<span>Warning: Operation in progress: </span>
<field name="locked_reason" class="o_stat_value" />
<button
name="refresh_page"
type="object"
class="btn_warning"
icon="fa-refresh"
string="Refresh"
/>
</div>
<sheet>
<div class="oe_button_box" name="button_box">
</div>
Expand Down

0 comments on commit fc01a3f

Please sign in to comment.