From 26c795216efd1ec0eacf249f4d1be70c36461fe6 Mon Sep 17 00:00:00 2001 From: git_admin Date: Mon, 27 Apr 2026 08:46:08 +0000 Subject: [PATCH] Tower: upload queue_job 16.0.2.12.0 (via marketplace) --- addons/queue_job/fields.py | 123 +++++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 addons/queue_job/fields.py diff --git a/addons/queue_job/fields.py b/addons/queue_job/fields.py new file mode 100644 index 0000000..2c78c18 --- /dev/null +++ b/addons/queue_job/fields.py @@ -0,0 +1,123 @@ +# copyright 2016 Camptocamp +# license lgpl-3.0 or later (http://www.gnu.org/licenses/lgpl.html) + +import json +from datetime import date, datetime + +import dateutil +import lxml + +from odoo import fields, models +from odoo.tools.func import lazy + + +class JobSerialized(fields.Field): + """Provide the storage for job fields stored as json + + A base_type must be set, it must be dict, list or tuple. + When the field is not set, the json will be the corresponding + json string ("{}" or "[]"). + + Support for some custom types has been added to the json decoder/encoder + (see JobEncoder and JobDecoder). + """ + + type = "job_serialized" + column_type = ("text", "text") + + _base_type = None + + # these are the default values when we convert an empty value + _default_json_mapping = { + dict: "{}", + list: "[]", + tuple: "[]", + models.BaseModel: lambda env: json.dumps( + {"_type": "odoo_recordset", "model": "base", "ids": [], "uid": env.uid} + ), + } + + def __init__(self, string=fields.Default, base_type=fields.Default, **kwargs): + super().__init__(string=string, _base_type=base_type, **kwargs) + + def _setup_attrs(self, model, name): # pylint: disable=missing-return + super()._setup_attrs(model, name) + if self._base_type not in self._default_json_mapping: + raise ValueError("%s is not a supported base type" % (self._base_type)) + + def _base_type_default_json(self, env): + default_json = self._default_json_mapping.get(self._base_type) + if not isinstance(default_json, str): + default_json = default_json(env) + return default_json + + def convert_to_column(self, value, record, values=None, validate=True): + return self.convert_to_cache(value, record, validate=validate) + + def convert_to_cache(self, value, record, validate=True): + # cache format: json.dumps(value) or None + if isinstance(value, self._base_type): + return json.dumps(value, cls=JobEncoder) + else: + return value or None + + def convert_to_record(self, value, record): + default = self._base_type_default_json(record.env) + return json.loads(value or default, cls=JobDecoder, env=record.env) + + +class JobEncoder(json.JSONEncoder): + """Encode Odoo recordsets so that we can later recompose them""" + + def _get_record_context(self, obj): + return obj._job_prepare_context_before_enqueue() + + def default(self, obj): + if isinstance(obj, models.BaseModel): + return { + "_type": "odoo_recordset", + "model": obj._name, + "ids": obj.ids, + "uid": obj.env.uid, + "su": obj.env.su, + "context": self._get_record_context(obj), + } + elif isinstance(obj, datetime): + return {"_type": "datetime_isoformat", "value": obj.isoformat()} + elif isinstance(obj, date): + return {"_type": "date_isoformat", "value": obj.isoformat()} + elif isinstance(obj, lxml.etree._Element): + return { + "_type": "etree_element", + "value": lxml.etree.tostring(obj, encoding=str), + } + elif isinstance(obj, lazy): + return obj._value + return json.JSONEncoder.default(self, obj) + + +class JobDecoder(json.JSONDecoder): + """Decode json, recomposing recordsets""" + + def __init__(self, *args, **kwargs): + env = kwargs.pop("env") + super().__init__(object_hook=self.object_hook, *args, **kwargs) + assert env + self.env = env + + def object_hook(self, obj): + if "_type" not in obj: + return obj + type_ = obj["_type"] + if type_ == "odoo_recordset": + model = self.env(user=obj.get("uid"), su=obj.get("su"))[obj["model"]] + if obj.get("context"): + model = model.with_context(**obj.get("context")) + return model.browse(obj["ids"]) + elif type_ == "datetime_isoformat": + return dateutil.parser.parse(obj["value"]) + elif type_ == "date_isoformat": + return dateutil.parser.parse(obj["value"]).date() + elif type_ == "etree_element": + return lxml.etree.fromstring(obj["value"]) + return obj