This commit introduces the foundational elements for the new "Company Explorer" web application, marking a significant step away from the legacy Google Sheets / CLI system. Key changes include: - Project Structure: A new directory with separate (FastAPI) and (React/Vite) components. - Data Persistence: Migration from Google Sheets to a local SQLite database () using SQLAlchemy. - Core Utilities: Extraction and cleanup of essential helper functions (LLM wrappers, text utilities) into . - Backend Services: , , for AI-powered analysis, and logic. - Frontend UI: Basic React application with company table, import wizard, and dynamic inspector sidebar. - Docker Integration: Updated and for multi-stage builds and sideloading. - Deployment & Access: Integrated into central Nginx proxy and dashboard, accessible via . Lessons Learned & Fixed during development: - Frontend Asset Loading: Addressed issues with Vite's path and FastAPI's . - TypeScript Configuration: Added and . - Database Schema Evolution: Solved errors by forcing a new database file and correcting override. - Logging: Implemented robust file-based logging (). This new foundation provides a powerful and maintainable platform for future B2B robotics lead generation.
104 lines
3.5 KiB
Python
104 lines
3.5 KiB
Python
import os
|
|
import logging
|
|
from sqlalchemy.orm import Session
|
|
from ..database import Company
|
|
from ..interfaces import LeadData, TaskData, CRMRepository
|
|
from ..repositories.mock import MockRepository
|
|
from ..repositories.superoffice import SuperOfficeRepository
|
|
from ..config import settings
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
class CRMFactory:
|
|
_instance: CRMRepository = None
|
|
|
|
@classmethod
|
|
def get_repository(cls) -> CRMRepository:
|
|
if cls._instance:
|
|
return cls._instance
|
|
|
|
crm_type = os.getenv("CRM_TYPE", "MOCK").upper()
|
|
|
|
if crm_type == "SUPEROFFICE":
|
|
# Load credentials securely from settings/env
|
|
tenant = os.getenv("SO_TENANT_ID", "")
|
|
token = os.getenv("SO_API_TOKEN", "")
|
|
logger.info("Initializing SuperOffice Repository...")
|
|
cls._instance = SuperOfficeRepository(tenant, token)
|
|
else:
|
|
logger.info("Initializing Mock Repository (Default)...")
|
|
cls._instance = MockRepository()
|
|
|
|
return cls._instance
|
|
|
|
class SyncService:
|
|
def __init__(self, db: Session):
|
|
self.db = db
|
|
self.repo = CRMFactory.get_repository()
|
|
|
|
def sync_company(self, company_id: int) -> dict:
|
|
"""
|
|
Pushes a local company to the external CRM.
|
|
"""
|
|
local_company = self.db.query(Company).filter(Company.id == company_id).first()
|
|
if not local_company:
|
|
return {"error": "Company not found"}
|
|
|
|
# 1. Map Data
|
|
# Extract highest robotics potential score
|
|
max_score = 0
|
|
reason = ""
|
|
for sig in local_company.signals:
|
|
if sig.confidence > max_score:
|
|
max_score = int(sig.confidence)
|
|
reason = f"{sig.signal_type} ({sig.value})"
|
|
|
|
lead_data = LeadData(
|
|
name=local_company.name,
|
|
website=local_company.website,
|
|
city=local_company.city,
|
|
country=local_company.country,
|
|
industry=local_company.industry_ai, # We suggest our AI industry
|
|
robotics_potential_score=max_score,
|
|
robotics_potential_reason=reason
|
|
)
|
|
|
|
# 2. Check if already linked
|
|
external_id = local_company.crm_id
|
|
|
|
# 3. Check if exists in CRM (by name) if not linked yet
|
|
if not external_id:
|
|
external_id = self.repo.find_company(local_company.name)
|
|
|
|
action = "none"
|
|
if external_id:
|
|
# Update
|
|
success = self.repo.update_lead(external_id, lead_data)
|
|
if success:
|
|
action = "updated"
|
|
# If we found it by search, link it locally
|
|
if not local_company.crm_id:
|
|
local_company.crm_id = external_id
|
|
self.db.commit()
|
|
else:
|
|
# Create
|
|
new_id = self.repo.create_lead(lead_data)
|
|
if new_id:
|
|
action = "created"
|
|
local_company.crm_id = new_id
|
|
self.db.commit()
|
|
|
|
# Create a task for the sales rep if high potential
|
|
if max_score > 70:
|
|
self.repo.create_task(new_id, TaskData(
|
|
subject="🔥 Hot Robotics Lead",
|
|
description=f"AI detected high potential ({max_score}%). Reason: {reason}. Please check website."
|
|
))
|
|
|
|
return {
|
|
"status": "success",
|
|
"action": action,
|
|
"crm": self.repo.get_name(),
|
|
"external_id": local_company.crm_id
|
|
}
|