bemade-addons/openwebui_integration_product/models/product_template.py
2025-02-18 08:55:21 -05:00

203 lines
No EOL
8.5 KiB
Python

# -*- coding: utf-8 -*-
"""OpenWebUI Product Integration Module
This module extends product.template functionality to integrate
OpenWebUI artificial intelligence into product management.
It enables automatic product category suggestions
based on description and characteristics analysis.
Main features:
- AI-powered category suggestions
- Suggestion history tracking
- Integrated user interface
"""
import json
import logging
from datetime import datetime
from odoo import models, fields, _
from odoo.exceptions import UserError
from odoo.tools import float_round
from odoo.addons.openwebui_integration.models.openwebui_bot_mixin import OpenWebUIBotMixin
_logger = logging.getLogger(__name__)
class ProductTemplate(models.Model, OpenWebUIBotMixin):
_inherit = "product.template"
suggested_category_id = fields.Many2one(
comodel_name='product.category',
string="Suggested Category",
readonly=True,
help="Category suggested by AI based on product information analysis"
)
suggestion_confidence = fields.Float(
string="Confidence",
readonly=True,
help="Confidence score (0-100) indicating how sure the AI is about the suggested category"
)
suggestion_date = fields.Datetime(
string="Suggestion Date",
readonly=True,
help="Date and time when the category was suggested by the AI"
)
suggestion_history_ids = fields.One2many(
comodel_name='product.category.suggestion.history',
inverse_name='product_id',
string="Suggestion History",
help="History of all category suggestions made by AI for this product"
)
def _generate_bot_message(self, records, values, command=None):
"""Generates the message to send to the bot to get category suggestions."""
# Préparer les données des produits
products_data = [{
'odoo_id': record.id, # ID interne Odoo
'name': record.name,
'description': record.description or '',
'description_sale': record.description_sale or '',
'default_code': record.default_code or '',
'current_category': record.categ_id.display_name,
'sellers': [
{
'name': seller.partner_id.display_name,
'product_code': seller.product_code or '',
'product_name': seller.product_name or ''
} for seller in record.seller_ids
]
} for record in records]
# Récupérer toutes les catégories disponibles
Category = self.env['product.category']
categories = Category.search([('parent_id', '!=', False)], order='complete_name')
available_categories = [{
'id': cat.id,
'name': cat.name,
'complete_name': cat.complete_name or cat.name,
'level': len(cat.parent_path.split('/')) - 1 if cat.parent_path else 0
} for cat in categories]
# Construire le message pour l'IA
message = {
'task': 'product_categorization',
'products': products_data,
'available_categories': available_categories,
'instructions': """For each product in the products list, analyze the product information and suggest the most appropriate product category from the available list.
Consider each product's name, description, and supplier information to make the best match.
Return a list of JSON objects, one for each product, with:
- 'odoo_id' (integer): The internal Odoo ID of the product
- 'category_id' (integer): The ID of the most appropriate category
- 'confidence' (float between 0 and 100): How confident you are about this suggestion
- 'explanation' (string): A detailed explanation of why this category was chosen, including analysis of the product name, description, and other relevant information.""",
'format': 'json'
}
return json.dumps(message)
def _process_bot_response(self, values, response):
"""Process the bot response to extract the suggested category."""
try:
results = json.loads(response)
if not isinstance(results, list):
raise ValueError("La réponse n'est pas une liste JSON valide")
for result in results:
category_id = result.get('category_id')
confidence = result.get('confidence', 0.0)
product_id = result.get('odoo_id')
if not category_id:
raise ValueError("No category ID in response")
if not product_id:
raise ValueError("No product ID in response")
# Vérifier que la catégorie existe
category = self.env['product.category'].browse(category_id).exists()
if not category:
raise ValueError(f"Category {category_id} not found")
# Trouver le produit concerné
product = self.filtered(lambda p: p.id == product_id)
if not product:
raise ValueError(f"Product {product_id} not found in selection")
# Mettre à jour les valeurs pour ce produit
product.write({
'suggested_category_id': category_id,
'suggestion_confidence': confidence,
'suggestion_date': fields.Datetime.now(),
})
# Créer l'historique
self.env['product.category.suggestion.history'].create({
'product_id': product_id,
'suggested_category_id': category_id,
'suggestion_confidence': confidence,
'input_data': json.dumps(result),
'explanation': result.get('explanation', ''),
'applied': False
})
except Exception as e:
raise UserError(_("Erreur lors du traitement de la réponse de l'IA: %s") % str(e))
return values
def action_suggest_category(self):
"""Request category suggestions from AI."""
if len(self) > 800:
raise UserError(_("For performance reasons, you cannot analyze more than 800 products at once."))
# Get company settings
company = self.env.company
if not company.openwebui_enabled:
raise UserError(_("OpenWebUI is not enabled for your company. Please enable it in company settings."))
model = company.openwebui_default_model_id
if not model:
raise UserError(_("No default OpenWebUI model configured. Please configure it in company settings."))
# Process products in batches
batch_size = company.openwebui_products_per_request
successful_products = self.env['product.template']
for i in range(0, len(self), batch_size):
batch = self[i:i + batch_size]
# Créer une nouvelle transaction pour ce batch
with self.env.cr.savepoint():
try:
values = {'bot': model}
self._apply_logic(batch, values)
# Si on arrive ici, le batch a réussi
successful_products |= batch
_logger.info('Successfully processed batch of %d products: %s',
len(batch), batch.mapped('default_code'))
except Exception as e:
_logger.error('Batch processing failed for products %s: %s',
batch.mapped('default_code'), str(e))
# Le savepoint sera rollback automatiquement
continue
# Si aucun produit n'a été traité avec succès
if not successful_products:
raise UserError(_('No suggestions could be generated by AI.'))
# Ouvrir l'assistant si des suggestions ont été générées
if any(product.suggested_category_id for product in successful_products):
wizard = self.env['product.category.suggestion.wizard'].create({})
return {
'name': _('Category Suggestions'),
'type': 'ir.actions.act_window',
'res_model': 'product.category.suggestion.wizard',
'res_id': wizard.id,
'view_mode': 'form',
'target': 'new',
'context': self.env.context,
}
else:
raise UserError(_('No valid suggestions could be generated by AI.'))