205 lines
6.6 KiB
Python
205 lines
6.6 KiB
Python
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,
|
|
)
|