From 0676cf1e7fb0f3bec6526b666124187ff01c5ca6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20V=C3=A9zina?= Date: Wed, 19 Feb 2025 14:19:30 -0500 Subject: [PATCH] new ai 2 --- openai_connector/__init__.py | 1 - openai_connector/__manifest__.py | 22 - openai_connector/models/__init__.py | 2 - openai_connector/models/res_company.py | 9 - .../models/res_config_settings.py | 96 - openai_connector/security/ir.model.access.csv | 2 - .../views/res_config_settings_views.xml | 46 - openai_partner_purchase_analysis/__init__.py | 2 - .../__manifest__.py | 31 - .../data/queue_job_group.xml | 7 - .../models/__init__.py | 4 - .../partner_purchase_analysis_wizard.py | 93 - .../models/res_company.py | 11 - .../models/res_config_settings.py | 36 - .../models/res_partner.py | 20 - .../openai_connector | 1 - .../security/ir.model.access.csv | 2 - .../views/res_config_settings_view.xml | 19 - .../views/res_partner_view.xml | 17 - .../wizard/__init__.py | 1 - .../partner_purchase_analysis_wizard.py | 141 -- .../partner_purchase_analysis_wizard_view.xml | 45 - openwebui_integration/Docs/OllamaAPI.md | 1627 ----------------- openwebui_integration/Spécifications.md | 110 -- openwebui_integration/__init__.py | 2 - openwebui_integration/__manifest__.py | 34 - openwebui_integration/controllers/__init__.py | 1 - openwebui_integration/controllers/main.py | 22 - openwebui_integration/hooks.py | 9 - openwebui_integration/models/__init__.py | 6 - .../models/openwebui_bot_mixin.py | 223 --- .../models/openwebui_model.py | 535 ------ openwebui_integration/models/res_company.py | 300 --- .../security/ir.model.access.csv | 3 - .../security/openwebui_security.xml | 7 - openwebui_integration/security/security.xml | 36 - .../static/description/icon.png | Bin 10741 -> 0 bytes .../static/src/css/openwebui.css | 12 - .../static/src/js/openwebui.js | 30 - .../static/src/scss/openwebui.scss | 26 - .../static/src/xml/templates.xml | 23 - .../views/res_company_views.xml | 98 - openwebui_integration_chat/Spécifications.md | 0 openwebui_integration_chat/__init__.py | 5 - openwebui_integration_chat/__manifest__.py | 35 - openwebui_integration_chat/models/__init__.py | 6 - .../models/discuss_channel.py | 46 - .../models/openwebui_bot.py | 545 ------ .../models/res_users.py | 17 - .../security/ir.model.access.csv | 3 - .../security/security.xml | 12 - .../views/openwebui_bot_views.xml | 61 - .../Spécifications.md | 123 -- openwebui_integration_product/__init__.py | 2 - openwebui_integration_product/__manifest__.py | 25 - .../migrations/18.0.1.0.1/__init__.py | 1 - .../migrations/18.0.1.0.1/pre-migration.py | 17 - .../models/__init__.py | 2 - .../models/category_suggestion_history.py | 56 - .../models/product_template.py | 281 --- .../openwebui_integration | 1 - .../openwebui_integration_chat | 1 - .../security/ir.model.access.csv | 4 - .../views/product_template_action.xml | 14 - .../views/product_template_view.xml | 63 - .../wizard/__init__.py | 1 - .../wizard/category_suggestion_wizard.py | 74 - .../category_suggestion_wizard_view.xml | 36 - 68 files changed, 5143 deletions(-) delete mode 100644 openai_connector/__init__.py delete mode 100644 openai_connector/__manifest__.py delete mode 100644 openai_connector/models/__init__.py delete mode 100644 openai_connector/models/res_company.py delete mode 100644 openai_connector/models/res_config_settings.py delete mode 100644 openai_connector/security/ir.model.access.csv delete mode 100644 openai_connector/views/res_config_settings_views.xml delete mode 100644 openai_partner_purchase_analysis/__init__.py delete mode 100644 openai_partner_purchase_analysis/__manifest__.py delete mode 100644 openai_partner_purchase_analysis/data/queue_job_group.xml delete mode 100644 openai_partner_purchase_analysis/models/__init__.py delete mode 100644 openai_partner_purchase_analysis/models/partner_purchase_analysis_wizard.py delete mode 100644 openai_partner_purchase_analysis/models/res_company.py delete mode 100644 openai_partner_purchase_analysis/models/res_config_settings.py delete mode 100644 openai_partner_purchase_analysis/models/res_partner.py delete mode 120000 openai_partner_purchase_analysis/openai_connector delete mode 100644 openai_partner_purchase_analysis/security/ir.model.access.csv delete mode 100644 openai_partner_purchase_analysis/views/res_config_settings_view.xml delete mode 100644 openai_partner_purchase_analysis/views/res_partner_view.xml delete mode 100644 openai_partner_purchase_analysis/wizard/__init__.py delete mode 100644 openai_partner_purchase_analysis/wizard/partner_purchase_analysis_wizard.py delete mode 100644 openai_partner_purchase_analysis/wizard/partner_purchase_analysis_wizard_view.xml delete mode 100644 openwebui_integration/Docs/OllamaAPI.md delete mode 100644 openwebui_integration/Spécifications.md delete mode 100644 openwebui_integration/__init__.py delete mode 100644 openwebui_integration/__manifest__.py delete mode 100644 openwebui_integration/controllers/__init__.py delete mode 100644 openwebui_integration/controllers/main.py delete mode 100644 openwebui_integration/hooks.py delete mode 100644 openwebui_integration/models/__init__.py delete mode 100644 openwebui_integration/models/openwebui_bot_mixin.py delete mode 100644 openwebui_integration/models/openwebui_model.py delete mode 100644 openwebui_integration/models/res_company.py delete mode 100644 openwebui_integration/security/ir.model.access.csv delete mode 100644 openwebui_integration/security/openwebui_security.xml delete mode 100644 openwebui_integration/security/security.xml delete mode 100644 openwebui_integration/static/description/icon.png delete mode 100644 openwebui_integration/static/src/css/openwebui.css delete mode 100644 openwebui_integration/static/src/js/openwebui.js delete mode 100644 openwebui_integration/static/src/scss/openwebui.scss delete mode 100644 openwebui_integration/static/src/xml/templates.xml delete mode 100644 openwebui_integration/views/res_company_views.xml delete mode 100644 openwebui_integration_chat/Spécifications.md delete mode 100644 openwebui_integration_chat/__init__.py delete mode 100644 openwebui_integration_chat/__manifest__.py delete mode 100644 openwebui_integration_chat/models/__init__.py delete mode 100644 openwebui_integration_chat/models/discuss_channel.py delete mode 100644 openwebui_integration_chat/models/openwebui_bot.py delete mode 100644 openwebui_integration_chat/models/res_users.py delete mode 100644 openwebui_integration_chat/security/ir.model.access.csv delete mode 100644 openwebui_integration_chat/security/security.xml delete mode 100644 openwebui_integration_chat/views/openwebui_bot_views.xml delete mode 100644 openwebui_integration_product/Spécifications.md delete mode 100644 openwebui_integration_product/__init__.py delete mode 100644 openwebui_integration_product/__manifest__.py delete mode 100644 openwebui_integration_product/migrations/18.0.1.0.1/__init__.py delete mode 100644 openwebui_integration_product/migrations/18.0.1.0.1/pre-migration.py delete mode 100644 openwebui_integration_product/models/__init__.py delete mode 100644 openwebui_integration_product/models/category_suggestion_history.py delete mode 100644 openwebui_integration_product/models/product_template.py delete mode 120000 openwebui_integration_product/openwebui_integration delete mode 120000 openwebui_integration_product/openwebui_integration_chat delete mode 100644 openwebui_integration_product/security/ir.model.access.csv delete mode 100644 openwebui_integration_product/views/product_template_action.xml delete mode 100644 openwebui_integration_product/views/product_template_view.xml delete mode 100644 openwebui_integration_product/wizard/__init__.py delete mode 100644 openwebui_integration_product/wizard/category_suggestion_wizard.py delete mode 100644 openwebui_integration_product/wizard/category_suggestion_wizard_view.xml diff --git a/openai_connector/__init__.py b/openai_connector/__init__.py deleted file mode 100644 index 9a7e03e..0000000 --- a/openai_connector/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from . import models \ No newline at end of file diff --git a/openai_connector/__manifest__.py b/openai_connector/__manifest__.py deleted file mode 100644 index 3832c8f..0000000 --- a/openai_connector/__manifest__.py +++ /dev/null @@ -1,22 +0,0 @@ -{ - 'name': 'OpenAI Connector', - 'version': '1.0', - 'category': 'Tools', - 'summary': 'Manage OpenAI connection settings', - 'license': 'AGPL-3', - 'description': ''' - This module allows the configuration of OpenAI API connection - settings, including API key and Organization ID. - ''', - 'author': 'Bemade Inc.', - 'depends': ['base_setup'], - 'data': [ - 'views/res_config_settings_views.xml', - 'security/ir.model.access.csv', - ], - 'external_dependencies': { - 'python': ['openai'], - }, - 'installable': True, - 'application': False, -} \ No newline at end of file diff --git a/openai_connector/models/__init__.py b/openai_connector/models/__init__.py deleted file mode 100644 index 3468e42..0000000 --- a/openai_connector/models/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from . import res_config_settings -from . import res_company \ No newline at end of file diff --git a/openai_connector/models/res_company.py b/openai_connector/models/res_company.py deleted file mode 100644 index 0945476..0000000 --- a/openai_connector/models/res_company.py +++ /dev/null @@ -1,9 +0,0 @@ - -from odoo import models, fields - -class ResCompany(models.Model): - _inherit = 'res.company' - - api_key = fields.Char(string="API Key", help="API Key for OpenAI specific to this company. It should start with 'sk-'") - organization = fields.Char(string="Organization ID", help="Organization ID for OpenAI specific to this company. It should start with 'org-'") - \ No newline at end of file diff --git a/openai_connector/models/res_config_settings.py b/openai_connector/models/res_config_settings.py deleted file mode 100644 index e8438f0..0000000 --- a/openai_connector/models/res_config_settings.py +++ /dev/null @@ -1,96 +0,0 @@ -from odoo import models, fields, api, _ -from odoo.exceptions import UserError -import openai -import logging - -_logger = logging.getLogger(__name__) - -class ResConfigSettings(models.TransientModel): - _inherit = 'res.config.settings' - - api_key = fields.Char( - string="API Key", - config_parameter='openai_connector.api_key', - help="API Key for OpenAI, used as a default if the company-specific key is not set. Format should start with 'sk-'" - ) - organization = fields.Char( - string="Organization ID", - config_parameter='openai_connector.organization', - help="Organization ID for OpenAI, used as a default if the company-specific ID is not set. Format should start with 'org-'" - ) - - connection_status = fields.Char( - string="Connection Status", - compute='_compute_connection_status', - help="Displays the current connection status with OpenAI." - ) - - @api.depends('api_key', 'organization') - def _compute_connection_status(self): - for record in self: - try: - if record.api_key and record.organization: # Vérifie que les champs ne sont pas vides - record._test_openai_connection() - record.connection_status = "Connected" - else: - record.connection_status = "Disconnected" - except Exception as e: - record.connection_status = "Disconnected" - _logger.error(f"OpenAI connection test failed: {str(e)}") - - def set_values(self): - super(ResConfigSettings, self).set_values() - self.env['ir.config_parameter'].sudo().set_param( - 'openai_connector.api_key', self.api_key) - self.env['ir.config_parameter'].sudo().set_param( - 'openai_connector.organization', self.organization) - - # Test de connexion automatique lors de l'enregistrement si les champs sont remplis - if self.api_key and self.organization: - self._test_openai_connection() - - @api.model - def get_values(self): - res = super(ResConfigSettings, self).get_values() - res.update( - api_key=self.env['ir.config_parameter'].sudo().get_param( - 'openai_connector.api_key', default=''), - organization=self.env['ir.config_parameter'].sudo().get_param( - 'openai_connector.organization', default='') - ) - return res - - def _test_openai_connection(self): - """Method to test connection to OpenAI.""" - if not self.api_key or not self.organization: - return # Ne fait rien si l'un des champs est vide - - try: - client = openai.OpenAI(organization=self.organization, api_key=self.api_key) - client.models.list() # Test basique de la connexion - except Exception as e: - raise UserError(_("Failed to connect to OpenAI API. Please check the API Key and Organization ID.\nError: %s") % str(e)) - - def action_test_openai_connection(self): - """Action to test connection manually and display the result.""" - try: - self._test_openai_connection() - return { - 'type': 'ir.actions.client', - 'tag': 'display_notification', - 'params': { - 'title': _("Connection Test Successful"), - 'message': _("The connection to OpenAI was successful."), - 'sticky': False, - }, - } - except UserError as e: - return { - 'type': 'ir.actions.client', - 'tag': 'display_notification', - 'params': { - 'title': _("Connection Test Failed"), - 'message': str(e), - 'sticky': True, - }, - } \ No newline at end of file diff --git a/openai_connector/security/ir.model.access.csv b/openai_connector/security/ir.model.access.csv deleted file mode 100644 index 1d9caaa..0000000 --- a/openai_connector/security/ir.model.access.csv +++ /dev/null @@ -1,2 +0,0 @@ -id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink -access_res_config_settings_openai,res.config.settings.openai,model_res_config_settings,base.group_system,1,1,1,1 diff --git a/openai_connector/views/res_config_settings_views.xml b/openai_connector/views/res_config_settings_views.xml deleted file mode 100644 index 3d4131e..0000000 --- a/openai_connector/views/res_config_settings_views.xml +++ /dev/null @@ -1,46 +0,0 @@ - - - - openai.connector.config.settings.form - res.config.settings - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
-
-
-
\ No newline at end of file diff --git a/openai_partner_purchase_analysis/__init__.py b/openai_partner_purchase_analysis/__init__.py deleted file mode 100644 index c536983..0000000 --- a/openai_partner_purchase_analysis/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from . import models -from . import wizard \ No newline at end of file diff --git a/openai_partner_purchase_analysis/__manifest__.py b/openai_partner_purchase_analysis/__manifest__.py deleted file mode 100644 index 684df61..0000000 --- a/openai_partner_purchase_analysis/__manifest__.py +++ /dev/null @@ -1,31 +0,0 @@ -{ - 'name': 'Partner Purchase Analysis with Optional OpenAI and Queue Job', - 'version': '1.0', - 'category': 'Tools', - 'summary': 'Manage OpenAI connection settings', - 'license': 'AGPL-3', - 'description': ''' - This module allows the configuration of OpenAI API connection - settings, including API key and Organization ID. - ''', - 'author': 'Bemade Inc.', - 'depends': [ - 'base', - 'sale', - 'product', - 'openai_connector', - 'sale_management', - ], - 'data': [ - 'security/ir.model.access.csv', # Fichier de sécurité mis à jour - 'data/queue_job_group.xml', - 'views/res_config_settings_view.xml', - 'views/res_partner_view.xml', - 'wizard/partner_purchase_analysis_wizard_view.xml', - ], - 'external_dependencies': { - 'python': ['openai'], - }, - 'installable': True, - 'application': False, -} \ No newline at end of file diff --git a/openai_partner_purchase_analysis/data/queue_job_group.xml b/openai_partner_purchase_analysis/data/queue_job_group.xml deleted file mode 100644 index a2ac0e3..0000000 --- a/openai_partner_purchase_analysis/data/queue_job_group.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - Use Queue Job for Asynchronous Processing - - - diff --git a/openai_partner_purchase_analysis/models/__init__.py b/openai_partner_purchase_analysis/models/__init__.py deleted file mode 100644 index b861208..0000000 --- a/openai_partner_purchase_analysis/models/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from . import res_partner -# from . import partner_purchase_analysis_wizard -from . import res_config_settings -from . import res_company \ No newline at end of file diff --git a/openai_partner_purchase_analysis/models/partner_purchase_analysis_wizard.py b/openai_partner_purchase_analysis/models/partner_purchase_analysis_wizard.py deleted file mode 100644 index b1a6faa..0000000 --- a/openai_partner_purchase_analysis/models/partner_purchase_analysis_wizard.py +++ /dev/null @@ -1,93 +0,0 @@ -from odoo import models, fields, api, _ -from odoo.exceptions import UserError -import openai - -try: - from odoo.addons.queue_job.job import job -except ImportError: - job = None # Si queue_job n'est pas disponible, job reste None - - -class PartnerPurchaseAnalysisWizard(models.TransientModel): - _name = 'partner.purchase.analysis.wizard' - _description = 'Wizard for Partner Purchase Analysis with OpenAI' - - partner_id = fields.Many2one('res.partner', string="Customer", required=True, readonly=True) - date_start = fields.Date(string="Start Date") - date_end = fields.Date(string="End Date") - analysis_result = fields.Text(string="Analysis Result", readonly=True) - - def start_analysis(self): - use_queue = self.env['ir.config_parameter'].sudo().get_param('my_module.use_queue_job') - if use_queue and job: - return self.with_delay().perform_analysis() - else: - return self.perform_analysis() - - def perform_analysis(self): - """Effectue l'analyse des ventes pour le partenaire sélectionné.""" - selected_categories = self.env.company.product_categories_analyzed - if selected_categories: - category_ids = selected_categories.ids - domain = [ - ('order_id.partner_id', '=', self.partner_id.id), - ('product_id.categ_id', 'child_of', category_ids), - ] - else: - domain = [('order_id.partner_id', '=', self.partner_id.id)] - - if self.date_start: - domain.append(('order_id.date_order', '>=', self.date_start)) - if self.date_end: - domain.append(('order_id.date_order', '<=', self.date_end)) - - sale_order_lines = self.env['sale.order.line'].search(domain) - - if not sale_order_lines: - raise UserError(_("No relevant purchase history found for this customer based on the selected filters.")) - - # Préparation des données pour l'API d'OpenAI - purchase_data = {} - for line in sale_order_lines: - category_name = line.product_id.categ_id.name or "Uncategorized" - if category_name not in purchase_data: - purchase_data[category_name] = {} - product_name = line.product_id.display_name - if product_name not in purchase_data[category_name]: - purchase_data[category_name][product_name] = [] - purchase_data[category_name][product_name].append({ - 'date': line.order_id.date_order, - 'quantity': line.product_uom_qty, - 'unit_price': line.price_unit, - }) - - # Construire le prompt pour OpenAI - user_lang = self.env.user.lang or 'en_US' - user_lang_name = self.env['res.lang'].search([('code', '=', user_lang)], limit=1).name or "English" - - purchase_details = f"Customer Purchase Analysis Grouped by Product Category and Product (Response in {user_lang_name}):\n\n" - for category, products in purchase_data.items(): - purchase_details += f"Category: {category}\n" - for product, entries in products.items(): - purchase_details += f" Product: {product}\n" - for entry in entries: - purchase_details += ( - f" - Date: {entry['date']}, " - f"Qty: {entry['quantity']}, " - f"U.Price: {entry['unit_price']}\n" - ) - purchase_details += "\n" - purchase_details += "\n" - - try: - response = openai.ChatCompletion.create( - model="gpt-4", - messages=[ - {"role": "system", - "content": f"Analyze the following customer purchase history. Identify trends, product category preferences, and any significant deviations. Respond in {user_lang_name}."}, - {"role": "user", "content": purchase_details} - ] - ) - self.analysis_result = response.choices[0].message['content'] - except Exception as e: - raise UserError(_("Failed to get response from OpenAI. Error: %s") % str(e)) \ No newline at end of file diff --git a/openai_partner_purchase_analysis/models/res_company.py b/openai_partner_purchase_analysis/models/res_company.py deleted file mode 100644 index 2582c6c..0000000 --- a/openai_partner_purchase_analysis/models/res_company.py +++ /dev/null @@ -1,11 +0,0 @@ -from odoo import models, fields - -class ResCompany(models.Model): - _inherit = 'res.company' - - product_categories_analyzed = fields.Many2many( - 'product.category', - string="Product Categories Analyzed", - help="Select product categories to include in the purchase analysis. Only products in these categories and their subcategories will be analyzed." - ) - diff --git a/openai_partner_purchase_analysis/models/res_config_settings.py b/openai_partner_purchase_analysis/models/res_config_settings.py deleted file mode 100644 index b7b1aec..0000000 --- a/openai_partner_purchase_analysis/models/res_config_settings.py +++ /dev/null @@ -1,36 +0,0 @@ -from odoo import models, fields, api -from odoo.modules.module import get_module_resource - -class ResConfigSettings(models.TransientModel): - _inherit = 'res.config.settings' - - use_queue_job = fields.Boolean( - string="Use Queue Job for Asynchronous Processing", - help="If enabled, the system will use queue jobs for background tasks. Requires the queue_job module.", - default=False - ) - product_categories_analyzed = fields.Many2many( - related='company_id.product_categories_analyzed', - comodel_name='product.category', - string="Product Categories Analyzed", - help="Select product categories to include in the purchase analysis." - ) - - @api.model - def get_values(self): - res = super().get_values() - res.update( - use_queue_job=self.env['ir.config_parameter'].sudo().get_param('my_module.use_queue_job', default=False) - ) - return res - - def set_values(self): - super().set_values() - self.env['ir.config_parameter'].sudo().set_param('my_module.use_queue_job', self.use_queue_job) - - @api.model - def enable_queue_job_group(self): - """Enable the group if the queue_job module is installed""" - group = self.env.ref('partner_purchase_analysis_with_openai_filtered.group_use_queue_job', raise_if_not_found=False) - if group and get_module_resource('queue_job'): - group.sudo().write({'users': [(4, self.env.user.id)]}) \ No newline at end of file diff --git a/openai_partner_purchase_analysis/models/res_partner.py b/openai_partner_purchase_analysis/models/res_partner.py deleted file mode 100644 index a37f8cf..0000000 --- a/openai_partner_purchase_analysis/models/res_partner.py +++ /dev/null @@ -1,20 +0,0 @@ -from odoo import models, fields - -class ResPartner(models.Model): - _inherit = 'res.partner' - - sale_analysis = fields.Html("Sale Analysis", help="Analysis of the partner's sales.") - sale_analysis_date = fields.Date("Sale Analysis Date", help="Date of the latest sale analysis.") - - def action_open_purchase_analysis(self): - """Ouvre le wizard d'analyse des achats pour le client actuel.""" - return { - 'name': 'Purchase Analysis', - 'type': 'ir.actions.act_window', - 'res_model': 'partner.purchase.analysis.wizard', - 'view_mode': 'form', - 'target': 'new', - 'context': { - 'default_partner_id': self.id, - }, - } \ No newline at end of file diff --git a/openai_partner_purchase_analysis/openai_connector b/openai_partner_purchase_analysis/openai_connector deleted file mode 120000 index a280bd5..0000000 --- a/openai_partner_purchase_analysis/openai_connector +++ /dev/null @@ -1 +0,0 @@ -../.repos/bemade-addons/openai_connector \ No newline at end of file diff --git a/openai_partner_purchase_analysis/security/ir.model.access.csv b/openai_partner_purchase_analysis/security/ir.model.access.csv deleted file mode 100644 index fe746dd..0000000 --- a/openai_partner_purchase_analysis/security/ir.model.access.csv +++ /dev/null @@ -1,2 +0,0 @@ -id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink -access_partner_purchase_analysis_wizard_sales,partner.purchase.analysis.wizard access,model_partner_purchase_analysis_wizard,sales_team.group_sale_salesman,1,1,1,1 \ No newline at end of file diff --git a/openai_partner_purchase_analysis/views/res_config_settings_view.xml b/openai_partner_purchase_analysis/views/res_config_settings_view.xml deleted file mode 100644 index d2b2269..0000000 --- a/openai_partner_purchase_analysis/views/res_config_settings_view.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - res.config.settings.view.form.inherit.partner.analysis - res.config.settings - - - -
-
-
-
-
-
-
-
\ No newline at end of file diff --git a/openai_partner_purchase_analysis/views/res_partner_view.xml b/openai_partner_purchase_analysis/views/res_partner_view.xml deleted file mode 100644 index 9fb4018..0000000 --- a/openai_partner_purchase_analysis/views/res_partner_view.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - res.partner.form.inherit.purchase.analysis - res.partner - - - - - - - - - - - - - \ No newline at end of file diff --git a/openai_partner_purchase_analysis/wizard/__init__.py b/openai_partner_purchase_analysis/wizard/__init__.py deleted file mode 100644 index 7e6aa79..0000000 --- a/openai_partner_purchase_analysis/wizard/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from . import partner_purchase_analysis_wizard \ No newline at end of file diff --git a/openai_partner_purchase_analysis/wizard/partner_purchase_analysis_wizard.py b/openai_partner_purchase_analysis/wizard/partner_purchase_analysis_wizard.py deleted file mode 100644 index d4d8fcb..0000000 --- a/openai_partner_purchase_analysis/wizard/partner_purchase_analysis_wizard.py +++ /dev/null @@ -1,141 +0,0 @@ -from odoo import models, fields, api, _ -from odoo.exceptions import UserError -from openai import OpenAI -from datetime import datetime -from bs4 import BeautifulSoup -from markupsafe import escape - -# Import conditionnel de queue_job pour gérer les files d'attente si elles sont disponibles -try: - from odoo.addons.queue_job.job import job -except ImportError: - job = None # Si queue_job n'est pas disponible, job reste None - -class PartnerPurchaseAnalysisWizard(models.TransientModel): - _name = 'partner.purchase.analysis.wizard' - _description = 'Wizard for Partner Purchase Analysis with OpenAI' - - partner_id = fields.Many2one('res.partner', string="Customer", required=True, readonly=True) - date_start = fields.Date(string="Start Date", required=True) - date_end = fields.Date(string="End Date", default=fields.Date.today, required=True) - - def start_analysis(self): - """Lance l'analyse en arrière-plan si `queue_job` est disponible, sinon exécute immédiatement.""" - use_queue = self.env['ir.config_parameter'].sudo().get_param('my_module.use_queue_job') - if use_queue and job: - return self.with_delay().perform_analysis() - else: - return self.perform_analysis() - - def perform_analysis(self): - """Effectue l'analyse des ventes pour le partenaire sélectionné.""" - - # Récupération de l'organisation et de la clé API OpenAI dans les paramètres - organization = self.env['ir.config_parameter'].sudo().get_param('openai_connector.organization') - api_key = self.env['ir.config_parameter'].sudo().get_param('openai_connector.api_key') - - if not api_key or not organization: - raise UserError(_("API Key or Organization ID for OpenAI is missing in settings.")) - - client = OpenAI( - api_key=api_key, - organization=organization - ) - - selected_categories = self.env.company.product_categories_analyzed - if selected_categories: - category_ids = selected_categories.ids - domain = [ - ('order_id.partner_id', '=', self.partner_id.id), - ('product_id.categ_id', 'child_of', category_ids), - ] - else: - domain = [('order_id.partner_id', '=', self.partner_id.id)] - - if self.date_start: - domain.append(('order_id.date_order', '>=', self.date_start)) - if self.date_end: - domain.append(('order_id.date_order', '<=', self.date_end)) - - sale_order_lines = self.env['sale.order.line'].search(domain) - - if not sale_order_lines: - raise UserError(_("No relevant purchase history found for this customer based on the selected filters.")) - - # Préparation des données pour le prompt OpenAI - purchase_data = {} - for line in sale_order_lines: - category_name = line.product_id.categ_id.name or "Uncategorized" - if category_name not in purchase_data: - purchase_data[category_name] = {} - product_name = line.product_id.display_name - if product_name not in purchase_data[category_name]: - purchase_data[category_name][product_name] = [] - purchase_data[category_name][product_name].append({ - 'date': line.order_id.date_order, - 'quantity': line.product_uom_qty, - 'unit_price': line.price_unit, - }) - - # Prompt défini en plusieurs lignes pour lisibilité - user_lang = self.env.user.lang or 'en_US' - user_lang_name = self.env['res.lang'].search([('code', '=', user_lang)], limit=1).name or "English" - - prompt = ( - "Analyze the following customer purchase history. Identify trends, product " - "category preferences, and any significant deviations. Respond in " - f"{user_lang_name}. Produce graph and table of the analysis. You output all " - "in html format." - ) - - # Construction de `purchase_details` pour le contenu du prompt - purchase_details = f"Customer Purchase Analysis Grouped by Product Category and Product:\n\n" - for category, products in purchase_data.items(): - purchase_details += f"Category: {category}\n" - for product, entries in products.items(): - purchase_details += f" Product: {product}\n" - for entry in entries: - purchase_details += ( - f" - Date: {entry['date']}, " - f"Qty: {entry['quantity']}, " - f"U.Price: {entry['unit_price']}\n" - ) - purchase_details += "\n" - purchase_details += "\n" - - # Appel à l'API OpenAI - try: - response = client.chat.completions.create( - messages=[ - { - "role": "system", - "content": prompt - }, - { - "role": "user", - "content": purchase_details - } - ], - model="gpt-4o", - ) - - # Supposons que `response` contient la réponse complète au format HTML - response_text = response.choices[0].message.content - - # Parse avec BeautifulSoup pour extraire le contenu du - soup = BeautifulSoup(response_text, "html.parser") - body_content = soup.body - - # Si body est présent, on utilise son contenu ; sinon, on utilise tout le texte - cleaned_text = escape(body_content.get_text()) if body_content else escape(response_text) - - # Ajouter des balises de base pour structurer le texte en HTML simple - html_content = f"
{cleaned_text.replace('\n', '
')}
" - - self.partner_id.sale_analysis = body_content - self.partner_id.sale_analysis_date = fields.Datetime.today() -# self.sale_analysis = response.choices[0].message.content -# self.sale_analysis_date = fields.Datetime.today() - print(response.choices[0].message.content) - except Exception as e: - raise UserError(_("Failed to get response from OpenAI. Error: %s") % str(e)) \ No newline at end of file diff --git a/openai_partner_purchase_analysis/wizard/partner_purchase_analysis_wizard_view.xml b/openai_partner_purchase_analysis/wizard/partner_purchase_analysis_wizard_view.xml deleted file mode 100644 index 75f0238..0000000 --- a/openai_partner_purchase_analysis/wizard/partner_purchase_analysis_wizard_view.xml +++ /dev/null @@ -1,45 +0,0 @@ - - - partner.purchase.analysis.wizard.form - partner.purchase.analysis.wizard - -
- - - - - - - - - - - - - - - - - - -
- - - product.template.tree.inherit.openwebui - product.template - - - - - - - - - -
diff --git a/openwebui_integration_product/wizard/__init__.py b/openwebui_integration_product/wizard/__init__.py deleted file mode 100644 index 91f745b..0000000 --- a/openwebui_integration_product/wizard/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from . import category_suggestion_wizard diff --git a/openwebui_integration_product/wizard/category_suggestion_wizard.py b/openwebui_integration_product/wizard/category_suggestion_wizard.py deleted file mode 100644 index 020e413..0000000 --- a/openwebui_integration_product/wizard/category_suggestion_wizard.py +++ /dev/null @@ -1,74 +0,0 @@ -from odoo import models, fields, api, _ -from odoo.exceptions import UserError -import json - -class CategorySuggestionWizard(models.TransientModel): - _name = 'product.category.suggestion.wizard' - _description = 'Assistant de suggestions de catégories' - - product_suggestion_ids = fields.One2many('product.category.suggestion.wizard.line', 'wizard_id', - string='Suggestions de produits') - - @api.model - def default_get(self, fields): - res = super().default_get(fields) - context = self.env.context - if context.get('active_model') == 'product.template' and context.get('active_ids'): - products = self.env['product.template'].browse(context['active_ids']) - suggestion_lines = [] - for product in products: - if product.suggested_category_id: - suggestion_lines.append((0, 0, { - 'product_id': product.id, - 'current_category_id': product.categ_id.id, - 'suggested_category_id': product.suggested_category_id.id, - 'confidence': product.suggestion_confidence, - 'apply_suggestion': False - })) - res['product_suggestion_ids'] = suggestion_lines - return res - - def action_apply_selected_suggestions(self): - """Applique les suggestions sélectionnées aux produits.""" - selected_lines = self.product_suggestion_ids.filtered(lambda l: l.apply_suggestion) - if not selected_lines: - raise UserError(_('Veuillez sélectionner au moins une suggestion à appliquer.')) - - for line in selected_lines: - # Mise à jour du produit - line.product_id.write({ - 'categ_id': line.suggested_category_id.id - }) - - # Mise à jour de l'historique - history = self.env['product.category.suggestion.history'].search([ - ('product_id', '=', line.product_id.id), - ('suggested_category_id', '=', line.suggested_category_id.id) - ], limit=1) - if history: - history.write({'applied': True}) - - # Message de confirmation - return { - 'type': 'ir.actions.client', - 'tag': 'display_notification', - 'params': { - 'title': _('Succès'), - 'message': _('%d catégories ont été mises à jour.') % len(selected_lines), - 'type': 'success', - 'sticky': False, - } - } - - -class CategorySuggestionWizardLine(models.TransientModel): - _name = 'product.category.suggestion.wizard.line' - _description = 'Ligne de suggestion de catégorie' - _order = 'confidence desc, product_id' - - wizard_id = fields.Many2one('product.category.suggestion.wizard', string='Assistant') - product_id = fields.Many2one('product.template', string='Produit', required=True, readonly=True) - current_category_id = fields.Many2one('product.category', string='Catégorie actuelle', readonly=True) - suggested_category_id = fields.Many2one('product.category', string='Catégorie suggérée', readonly=True) - confidence = fields.Float(string='Confiance (%)', readonly=True) - apply_suggestion = fields.Boolean(string='Appliquer', default=False) diff --git a/openwebui_integration_product/wizard/category_suggestion_wizard_view.xml b/openwebui_integration_product/wizard/category_suggestion_wizard_view.xml deleted file mode 100644 index b39c9d8..0000000 --- a/openwebui_integration_product/wizard/category_suggestion_wizard_view.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - product.category.suggestion.wizard.form - product.category.suggestion.wizard - -
- - - - - - - - - - - - - -
-
-
-