Files
Odoo-Tutorial/quickboard/wizard/quickboard_generator.py
T
2025-08-19 15:05:55 +07:00

133 lines
5.6 KiB
Python

# -*- coding: utf-8 -*-
import logging
import json
from jsonschema import validate
from odoo import _, fields, models
from odoo.exceptions import ValidationError
from .ai import QuickboardAiGenerator
from .ai.consts import QUICKBOARD_DATA_UI_JSON_SCHEMA, QUICKBOARD_BG_COLORS, QUICKBOARD_FG_COLORS
_logger = logging.getLogger(__name__)
class QuickboardGenerator(models.TransientModel):
_name = "quickboard.generator"
_description = "Generate quickbaord items with AI."
model_ids = fields.Many2many('ir.model', string='Model')
layout_by_ai = fields.Boolean("Layout by AI", default=False)
def action_generate_quickboard(self):
if len(self.model_ids.ids) < 1:
return {
'name': _('Generate Quickboard'),
'type': 'ir.actions.act_window',
'res_model': 'quickboard.generator',
'view_type': 'form',
'view_mode': 'form',
'res_id': self.id,
'target': 'new',
}
screen_width = self.env.context.get("screen_width")
screen_height = self.env.context.get("screen_height")
cell_width = self.env.context.get("cell_width")
cell_height = self.env.context.get("cell_height")
try:
gen = QuickboardAiGenerator(self.env)
quickboard = gen.generate_quickboard(
self.model_ids,
self.layout_by_ai,
screen_width,
screen_height,
cell_width,
cell_height
)
quickboard_json = json.loads(quickboard)
validate(quickboard_json, QUICKBOARD_DATA_UI_JSON_SCHEMA)
quickboard_item = self.env['quickboard.item']
items = quickboard_item.search([])
for o in items:
o.unlink()
for o in quickboard_json:
_logger.info(f"Creating quickboard item: {o}")
model = self.env["ir.model"].search([("model", "=", o["model"])])
value_field = self.env["ir.model.fields"].search([("model_id", "=", model.id), ("name", "=", o["value_field"])])
if not value_field.id:
raise Exception("AI generated invalid field: %s." % {o["value_field"]})
# Sometime AI choose the wrong aggregate function, we could return the result to AI with json schema validation.
# But, it would mean failing the result and making another attempt, the alternative is we just fix it here.
if value_field.ttype not in ['float', 'integer', 'monetary'] and o["aggregate_function"] != "count":
o["aggregate_function"] = "count"
vals = {
"name": o["name"],
"model_id": model.id,
"icon": o["icon"],
"type": o["type"],
"value_field_id": [value_field.id],
"aggregate_function": o["aggregate_function"],
"x_pos": o["x_pos"],
"y_pos": o["y_pos"],
"height": o["height"],
"width": o["width"]
}
if o["type"] == "basic":
text_color = 0
if o["text_color"] and o["text_color"] in QUICKBOARD_FG_COLORS:
text_color = QUICKBOARD_FG_COLORS.index(o["text_color"])
back_color = 0
if o["text_color"] and o["text_color"] in QUICKBOARD_BG_COLORS:
back_color = QUICKBOARD_BG_COLORS.index(o["text_color"])
vals.update({
"text_color": text_color,
"background_color": back_color,
})
elif o["type"] == "chart":
dimension_field = self.env["ir.model.fields"].search([("model_id", "=", model.id), ("name", "=", o["dimension_field"])])
if not dimension_field.id:
raise Exception("AI generated invalid field: %s." % {o["dimension_field"]})
vals.update({
"dimension_field_id": dimension_field.id,
"chart_type": o["chart_type"],
})
if "datetime_granularity" in o:
vals.update({
"datetime_granularity": o["datetime_granularity"]
})
elif o["type"] == "list":
dimension_field = self.env["ir.model.fields"].search([("model_id", "=", model.id), ("name", "=", o["dimension_field"])])
if not dimension_field.id:
raise Exception("AI generated invalid field: %s." % {o["dimension_field"]})
vals.update({
"dimension_field_id": dimension_field.id,
"list_row_limit": o["list_row_limit"]
})
if "datetime_granularity" in o:
vals.update({
"datetime_granularity": o["datetime_granularity"]
})
quickboard_item.with_context(ai_generation=True).create(vals)
except Exception as e:
_logger.error("Error generating quickboard", e)
raise ValidationError(_("Unfortunately the AI didn't generate valid quickboard. Please try again."))
for rec in self:
self.env["bus.bus"]._sendone("quickboard", "quickboard_updated", {})
return True