Skip to content
Snippets Groups Projects

Implement caching and optimization for Redner template handling

Merged oury.balde requested to merge topic/17.0/fsb into branch/17.0
4 files
+ 72
60
Compare changes
  • Side-by-side
  • Inline
Files
4
  • - Added `ormcache` to improve performance by caching template retrieval
    in `_get_cached_template`.
    - Refactored template processing logic:
      - Introduced `_to_odoo_template` to centralize transformation
        of external templates into Odoo-compatible format.
      - Simplified template computation in `_compute_template` and
        `_compute_template_data` using cached templates.
    - Removed redundant methods (`to_cache`) and consolidated logic for clarity.
+ 65
59
@@ -23,6 +23,7 @@
from odoo import _, api, fields, models
from odoo.exceptions import ValidationError
from odoo.tools.cache import ormcache
from ..redner import REDNER_API_PATH, Redner
from ..utils.mimetype import b64_to_extension
@@ -201,4 +202,51 @@
logger.error("Failed to get preview of redner template :%s", e)
return
def _to_odoo_template(self, template):
"""
Convert the external template to the Odoo format.
"""
language = "{}|{}".format(template.get("produces"), template.get("language"))
odoo_template = {
"name": template.get("name"),
"description": template.get("description", ""),
"redner_id": template.get("name"),
"locale_id": self.env["res.lang"].search(
[("code", "=", template.get("locale", "fr_FR"))], limit=1
),
"language": language,
"slug": template.get("slug"),
"is_mjml": language == LANGUAGE_MJML_MUSTACHE,
"body": "",
"template_data": False,
}
match template.get("body-format"):
case "base64":
body = base64.b64decode(template.get("body", ""))
case _:
body = template.get("body", "")
if template.get("language") == "od+mustache":
odoo_template["template_data"] = body
else:
odoo_template["body"] = body
return odoo_template
@ormcache("record_id")
def _get_cached_template(self, record_id):
"""
Retrieves and caches the template from Redner for a given record.
"""
record = self.browse(record_id)
if not record.redner_id:
return {}
try:
# Fetch the template from the external system
template = self.redner.templates.account_template_read(record.redner_id)
# Convert the template to Odoo's format
return self._to_odoo_template(template)
except Exception as e:
logger.error("Failed to read Redner template: %s", e)
return {}
def _compute_template(self):
@@ -204,4 +252,7 @@
def _compute_template(self):
"""
Computes the template values for the records and applies cached or fetched data.
"""
for record in self:
if not record.id or not record.redner_id:
continue
@@ -205,4 +256,8 @@
for record in self:
if not record.id or not record.redner_id:
continue
# Fetch the cached template
cached_template = self._get_cached_template(record.id)
if not any([getattr(record, f) for f in COMPUTED_FIELDS]):
@@ -208,16 +263,7 @@
if not any([getattr(record, f) for f in COMPUTED_FIELDS]):
# if all the fields are undefined, get from redner
try:
template = self.redner.templates.account_template_read(
record.redner_id
)
cached_template = self.to_cache(record.id, template)
for f in COMPUTED_FIELDS + EDITABLE_FIELDS:
if f in cached_template:
new_val = cached_template[f]
setattr(record, f, new_val)
except Exception as e:
logger.error("Failed to read redner template :%s", e)
return
# If all computed fields are undefined, populate them
# from the cached template.
for f in COMPUTED_FIELDS + EDITABLE_FIELDS:
if f in cached_template:
setattr(record, f, cached_template[f])
else:
@@ -223,5 +269,3 @@
else:
# if at least one field is defined, we get from the cache
# if the field is undefined
cached_template = self.cache[record.id]
# If at least one field is defined, populate only undefined fields
for f in COMPUTED_FIELDS:
@@ -227,8 +271,6 @@
for f in COMPUTED_FIELDS:
attr = getattr(record, f)
if not attr:
new_val = cached_template[f]
setattr(record, f, new_val)
if not getattr(record, f):
setattr(record, f, cached_template.get(f, None))
@api.depends("template_data")
def _compute_template_data(self):
@@ -237,10 +279,7 @@
continue
if not record.template_data:
try:
template = self.redner.templates.account_template_read(
record.redner_id
)
cached_template = self.to_cache(record.id, template)
cached_template = self._get_cached_template(record.id)
if "template_data" in cached_template:
new_val = cached_template["template_data"]
encoded = base64.b64encode(new_val).decode("utf-8")
@@ -270,6 +309,6 @@
for template in template_list:
# Filter out templates that already exist in Odoo
if template["name"] not in existing_template_ids:
new_templates.append(self.to_odoo_template(template))
new_templates.append(self._to_odoo_template(template))
return new_templates
@@ -274,38 +313,5 @@
return new_templates
def to_cache(self, record_id, template):
cached_template = self.to_odoo_template(template)
self.cache[record_id] = cached_template
return cached_template
def to_odoo_template(self, template):
# define a odoo_like template to put into the cache
language = "{}|{}".format(template.get("produces"), template.get("language"))
odoo_template = {
"name": template.get("name"),
"description": template.get("description", ""),
"redner_id": template.get("name"),
"locale_id": self.env["res.lang"].search(
[("code", "=", template.get("locale", "fr_FR"))], limit=1
),
"language": language,
"slug": template.get("slug"),
"is_mjml": language == LANGUAGE_MJML_MUSTACHE,
"body": "",
"template_data": False,
}
match template.get("body-format"):
case "base64":
body = base64.b64decode(template.get("body", ""))
case _:
body = template.get("body", "")
if template.get("language") == "od+mustache":
odoo_template["template_data"] = body
else:
odoo_template["body"] = body
return odoo_template
@property
def redner(self):
"""Try to avoid Redner instance to be over created"""
Loading