-
Notifications
You must be signed in to change notification settings - Fork 2
Feature: CRM Leads #141
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: lingo/main
Are you sure you want to change the base?
Feature: CRM Leads #141
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| import xmlrpc.client | ||
| from typing import Optional | ||
|
|
||
|
|
||
| class OdooCRMClient: | ||
| def __init__(self, url: str, db: str, username: str, password: str): | ||
| self.url = url | ||
| self.db = db | ||
| self.username = username | ||
| self.password = password | ||
|
|
||
|
|
||
| self.common = xmlrpc.client.ServerProxy(f"{url}/xmlrpc/2/common", allow_none=True) | ||
| self.uid = self.common.authenticate(db, username, password, {}) | ||
| if not self.uid: | ||
| raise Exception("Authentication failed. Check credentials or DB name.") | ||
| self.models = xmlrpc.client.ServerProxy(f"{url}/xmlrpc/2/object", allow_none=True) | ||
|
||
|
|
||
|
|
||
| def create_lead(self, name: str, email: Optional[str], phone: Optional[str], lead_type: str = "opportunity"): | ||
| lead_id = self.models.execute_kw( | ||
| self.db, self.uid, self.password, | ||
| "crm.lead", "create", | ||
| [{ | ||
| "name": name, | ||
| "contact_name": name, | ||
| "email_from": email, | ||
| "phone": phone, | ||
| "type": lead_type, | ||
| }] | ||
| ) | ||
| return lead_id | ||
|
|
||
| def update_lead(self, lead_id: int, vals: dict): | ||
| return self.models.execute_kw( | ||
| self.db, self.uid, self.password, | ||
| "crm.lead", "write", | ||
| [[lead_id], vals] | ||
| ) | ||
|
|
||
| def add_internal_note(self, lead_id: int, note_text: str): | ||
| return self.update_lead(lead_id, {"description": note_text}) | ||
|
|
||
| def add_chatter_note(self, lead_id: int, note_text: str): | ||
| return self.models.execute_kw( | ||
| self.db, self.uid, self.password, | ||
| "mail.message", "create", | ||
| [{ | ||
| "model": "crm.lead", | ||
| "res_id": lead_id, | ||
| "body": note_text, | ||
| "message_type": "comment", | ||
| "subtype_id": 2, | ||
| }] | ||
| ) | ||
|
|
||
| def add_contact_details(self, lead_id: int, contact_name: Optional[str] = None, email: Optional[str] = None, phone: Optional[str] = None): | ||
| vals = {} | ||
| if contact_name: | ||
| vals["name"] = contact_name | ||
| if email: | ||
| vals["email"] = email | ||
| if phone: | ||
| vals["phone"] = phone | ||
|
|
||
| partner_id = self.models.execute_kw( | ||
| self.db, self.uid, self.password, | ||
| "res.partner", "create", | ||
| [vals] | ||
| ) | ||
|
|
||
| self.update_lead(lead_id, {"partner_id": partner_id}) | ||
| return partner_id | ||
|
|
||
| def update_contact_address(self, partner_id: int, street: Optional[str] = None, street2: Optional[str] = None, city: Optional[str] = None, state_id: Optional[int] = None, zip_code: Optional[str] = None, country_id: Optional[int] = None): | ||
| vals = {} | ||
| if street: | ||
| vals["street"] = street | ||
| if street2: | ||
| vals["street2"] = street2 | ||
| if city: | ||
| vals["city"] = city | ||
| if state_id: | ||
| vals["state_id"] = state_id | ||
| if zip_code: | ||
| vals["zip"] = zip_code | ||
| if country_id: | ||
| vals["country_id"] = country_id | ||
|
|
||
| return self.models.execute_kw( | ||
| self.db, self.uid, self.password, | ||
| "res.partner", "write", | ||
| [[partner_id], vals] | ||
| ) | ||
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -7,13 +7,17 @@ | |||||||
| from audio_service import translate_with_whisper_timestamped, translate_with_whisper_from_upload | ||||||||
| from detect_intent import detect_intent_with_llama, format_intent_response | ||||||||
| from summarizer import summarize_using_openai | ||||||||
| from summarizer import summarize_using_ollama | ||||||||
| from summarizer import summarize_using_ollama, extract_contact_detailed_using_ollama | ||||||||
| from pydantic import BaseModel | ||||||||
| import traceback | ||||||||
| from util import generate_timestamp_json | ||||||||
| from fastapi_versionizer.versionizer import Versionizer, api_version | ||||||||
| import json | ||||||||
| from core_banking_mock import router as core_banking_mock_router | ||||||||
| import os | ||||||||
| import requests | ||||||||
| from config import odoo_url, odoo_db, odoo_username, odoo_password | ||||||||
| from crm_client import OdooCRMClient | ||||||||
|
|
||||||||
| app = FastAPI() | ||||||||
|
|
||||||||
|
|
@@ -139,3 +143,199 @@ async def transcribe_intent(audio: UploadFile = File(...), session_id: str = For | |||||||
| except Exception as e: | ||||||||
| logger.info(traceback.format_exc()) | ||||||||
| return JSONResponse(content={"message": str(e)}, status_code=500) | ||||||||
| async def save_crm_lead_data(lead_id, file_path, translation, extracted_data, summary, user_id=None, transcription_id=None): | ||||||||
|
|
||||||||
| """ | ||||||||
| Save CRM lead data to the database through the Next.js API. | ||||||||
| """ | ||||||||
| try: | ||||||||
| # Get base URL from environment or use default | ||||||||
| api_base_url = os.environ.get("NEXT_API_BASE_URL", "http://localhost:3000") | ||||||||
|
|
||||||||
| # Extract just the filename from the file path | ||||||||
| file_name = os.path.basename(file_path) | ||||||||
|
|
||||||||
| # Prepare the data to send | ||||||||
| crm_lead_data = { | ||||||||
| "leadId": str(lead_id), | ||||||||
| "crmUrl": odoo_url, # Using the odoo_url from config | ||||||||
| "fileName": file_name, | ||||||||
| "transcriptionId": transcription_id, # This might be None if not provided | ||||||||
| "extractedData": extracted_data, | ||||||||
| "translation": translation, | ||||||||
| "userId": user_id, # This might be None if not provided | ||||||||
| "isDefault": False # Adding the default field set to false | ||||||||
| } | ||||||||
|
|
||||||||
| # Make the API call | ||||||||
| response = requests.post( | ||||||||
| f"{api_base_url}/api/crm-leads", | ||||||||
| json=crm_lead_data, | ||||||||
| headers={"Content-Type": "application/json"} | ||||||||
|
||||||||
| headers={"Content-Type": "application/json"} | |
| headers={"Content-Type": "application/json"}, | |
| timeout=30 |
Copilot
AI
Sep 9, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing timeout parameter for the HTTP request. Add a timeout to prevent the request from hanging indefinitely: requests.get(..., timeout=30).
Outdated
Copilot
AI
Sep 9, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing timeout parameter for the HTTP request. Add a timeout to prevent the request from hanging indefinitely: requests.get(..., timeout=30).
| headers={"Content-Type": "application/json"} | |
| headers={"Content-Type": "application/json"}, | |
| timeout=30 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generic Exception should be replaced with a more specific exception type, such as
AuthenticationErrororConnectionError, to provide better error handling context.