import os from typing import List from db.db import get_db from db.models import InvestorTable from fastapi import APIRouter, Depends, HTTPException from pydantic import BaseModel from services.crm import FolkAPI from sqlalchemy.orm import Session, selectinload router = APIRouter(prefix="/folk", tags=["Folk CRM"]) def get_folk_client(): """Get Folk API client with loaded environment variables""" return FolkAPI(api_key=os.environ.get("FOLK_API_KEY", "")) class GroupResponse(BaseModel): id: str name: str class SyncInvestorsRequest(BaseModel): investor_ids: List[int] group_id: str class SyncResult(BaseModel): investor_id: int investor_name: str company_id: str company_name: str team_members_synced: int person_ids: List[str] class SyncInvestorsResponse(BaseModel): success: bool synced_count: int results: List[SyncResult] errors: List[dict] @router.get("/groups", response_model=List[GroupResponse]) def get_folk_groups(): """Get all groups from Folk CRM. Returns a list of groups with their id and name that can be used to sync investors to Folk. """ try: folk = get_folk_client() groups_data = folk.get_groups() items = groups_data.get("data", {}).get("items", []) return [GroupResponse(id=item["id"], name=item["name"]) for item in items] except Exception as e: raise HTTPException( status_code=500, detail=f"Failed to fetch groups from Folk: {str(e)}" ) @router.post("/sync-investors", response_model=SyncInvestorsResponse) def sync_investors_to_folk( request: SyncInvestorsRequest, db: Session = Depends(get_db) ): """Sync investors to Folk CRM as companies with their team members as people. Takes a list of investor IDs and a Folk group ID, then: 1. Creates each investor as a company in the specified Folk group 2. Creates each team member as a person linked to that company Args: investor_ids: List of investor IDs from the database group_id: Folk group ID where investors should be added Returns: Summary of sync operation including successes and errors """ folk = get_folk_client() # Fetch investors with their team members investors = ( db.query(InvestorTable) .options( selectinload(InvestorTable.team_members), selectinload(InvestorTable.sectors), ) .filter(InvestorTable.id.in_(request.investor_ids)) .all() ) if not investors: raise HTTPException( status_code=404, detail="No investors found with the provided IDs" ) results = [] errors = [] for investor in investors: try: # Create company in Folk company_data = folk.create_company( name=investor.name, group_id=request.group_id, website=investor.website, description=investor.description, addresses=[investor.headquarters] if investor.headquarters else None, ) company_id = company_data.get("data", {}).get("id") if not company_id: errors.append( { "investor_id": investor.id, "investor_name": investor.name, "error": "No company ID returned from Folk API", } ) continue # Create team members as people person_ids = [] team_members_synced = 0 for member in investor.team_members: try: # Extract first name and last name from full name name_parts = member.name.split(maxsplit=1) first_name = name_parts[0] if name_parts else member.name last_name = name_parts[1] if len(name_parts) > 1 else "" # Build URLs list from source_url if available urls_list = None if hasattr(member, "source_url") and member.source_url: urls_list = [member.source_url] # Get LinkedIn URL if available linkedin_url = None if hasattr(member, "linkedin") and member.linkedin: linkedin_url = member.linkedin # Build job title from title or role job_title = None if hasattr(member, "title") and member.title: job_title = member.title elif hasattr(member, "role") and member.role: job_title = member.role person_data = folk.create_person( first_name=first_name, last_name=last_name, email=member.email, company_id=company_id, group_id=request.group_id, linkedin_url=linkedin_url, urls=urls_list, jobTitle=job_title, ) person_id = person_data.get("data", {}).get("id") if person_id: person_ids.append(person_id) team_members_synced += 1 except Exception as person_error: # Log person creation error but continue with other members errors.append( { "investor_id": investor.id, "investor_name": investor.name, "team_member_name": member.name, "error": f"Failed to create person: {str(person_error)}", } ) results.append( SyncResult( investor_id=investor.id, investor_name=investor.name, company_id=company_id, company_name=company_data.get("data", {}).get( "name", investor.name ), team_members_synced=team_members_synced, person_ids=person_ids, ) ) except Exception as e: errors.append( { "investor_id": investor.id, "investor_name": investor.name, "error": str(e), } ) return SyncInvestorsResponse( success=len(results) > 0, synced_count=len(results), results=results, errors=errors, )