feat: Integrate Folk CRM API for investor synchronization and compatibility scoring
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
from typing import Optional
|
||||
|
||||
from db.db import get_db
|
||||
from db.models import FundTable, InvestorTable, SectorTable
|
||||
from db.models import FundTable, InvestorTable, ProjectTable, SectorTable
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from pydantic import BaseModel
|
||||
from schemas.router_schemas import (
|
||||
@@ -12,6 +12,7 @@ from schemas.router_schemas import (
|
||||
PaginatedResponse,
|
||||
SectorMinimal,
|
||||
)
|
||||
from services.compatibility_score import calculate_project_investor_compatibility
|
||||
from sqlalchemy.orm import Session, selectinload
|
||||
|
||||
router = APIRouter(tags=["Investor Routes"])
|
||||
@@ -46,12 +47,17 @@ class InvestorUpdate(BaseModel):
|
||||
def read_investors(
|
||||
page: int = Query(1, ge=1, description="Page number (starts at 1)"),
|
||||
page_size: int = Query(10, ge=1, le=100, description="Items per page (max 100)"),
|
||||
project_id: Optional[int] = Query(
|
||||
None, description="Optional project ID for compatibility scoring"
|
||||
),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""Get all investors with their funds as separate entries (paginated)
|
||||
|
||||
Each investor-fund combination is returned as a separate row.
|
||||
An investor with 3 funds will appear as 3 entries.
|
||||
|
||||
If project_id is provided, calculates compatibility scores for each investor.
|
||||
"""
|
||||
# Calculate offset
|
||||
offset = (page - 1) * page_size
|
||||
@@ -59,6 +65,18 @@ def read_investors(
|
||||
# Get total count
|
||||
total_count = db.query(InvestorTable).count()
|
||||
|
||||
# Load project if project_id provided
|
||||
project = None
|
||||
if project_id is not None:
|
||||
project = (
|
||||
db.query(ProjectTable)
|
||||
.options(selectinload(ProjectTable.sector))
|
||||
.filter(ProjectTable.id == project_id)
|
||||
.first()
|
||||
)
|
||||
if not project:
|
||||
raise HTTPException(status_code=404, detail="Project not found")
|
||||
|
||||
# Get paginated results
|
||||
investors = (
|
||||
db.query(InvestorTable)
|
||||
@@ -66,7 +84,8 @@ def read_investors(
|
||||
selectinload(InvestorTable.portfolio_companies),
|
||||
selectinload(InvestorTable.team_members),
|
||||
selectinload(InvestorTable.sectors),
|
||||
selectinload(InvestorTable.funds),
|
||||
selectinload(InvestorTable.funds).selectinload(FundTable.investment_stages),
|
||||
selectinload(InvestorTable.funds).selectinload(FundTable.sectors),
|
||||
)
|
||||
.offset(offset)
|
||||
.limit(page_size)
|
||||
@@ -76,6 +95,13 @@ def read_investors(
|
||||
# Transform to InvestmentResponse format (one row per investor-fund combination)
|
||||
investment_responses = []
|
||||
for investor in investors:
|
||||
# Calculate compatibility score if project provided
|
||||
compatibility_score = 1.0
|
||||
if project is not None:
|
||||
compatibility_score = calculate_project_investor_compatibility(
|
||||
project=project, investor=investor, use_funds=True
|
||||
)
|
||||
|
||||
# Get top 3 portfolio companies (id and name only)
|
||||
portfolio_companies = [
|
||||
CompanyMinimal(id=company.id, name=company.name)
|
||||
@@ -110,7 +136,7 @@ def read_investors(
|
||||
stage_focus=stage_focus,
|
||||
portfolio_companies=portfolio_companies,
|
||||
sectors=fund_sectors,
|
||||
compatibility_score=1.0,
|
||||
compatibility_score=compatibility_score,
|
||||
)
|
||||
investment_responses.append(investment_response)
|
||||
else:
|
||||
@@ -125,7 +151,7 @@ def read_investors(
|
||||
stage_focus=None,
|
||||
portfolio_companies=portfolio_companies,
|
||||
sectors=[],
|
||||
compatibility_score=1.0,
|
||||
compatibility_score=compatibility_score,
|
||||
)
|
||||
investment_responses.append(investment_response)
|
||||
|
||||
@@ -156,14 +182,31 @@ def filter_investors(
|
||||
max_aum: Optional[int] = Query(None, description="Maximum AUM"),
|
||||
page: int = Query(1, ge=1, description="Page number (starts at 1)"),
|
||||
page_size: int = Query(10, ge=1, le=100, description="Items per page (max 100)"),
|
||||
project_id: Optional[int] = Query(
|
||||
None, description="Optional project ID for compatibility scoring"
|
||||
),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""Filter investors based on various criteria (paginated)
|
||||
|
||||
Returns investor-fund combinations as separate rows.
|
||||
Queries the funds table to find matching funds.
|
||||
|
||||
If project_id is provided, calculates compatibility scores for each investor.
|
||||
"""
|
||||
|
||||
# Load project if project_id provided
|
||||
project = None
|
||||
if project_id is not None:
|
||||
project = (
|
||||
db.query(ProjectTable)
|
||||
.options(selectinload(ProjectTable.sector))
|
||||
.filter(ProjectTable.id == project_id)
|
||||
.first()
|
||||
)
|
||||
if not project:
|
||||
raise HTTPException(status_code=404, detail="Project not found")
|
||||
|
||||
# Start with base query on funds table
|
||||
query = db.query(FundTable).options(
|
||||
selectinload(FundTable.investor).selectinload(
|
||||
@@ -212,6 +255,13 @@ def filter_investors(
|
||||
for fund in funds:
|
||||
investor = fund.investor
|
||||
|
||||
# Calculate compatibility score if project provided
|
||||
compatibility_score = 1.0
|
||||
if project is not None:
|
||||
compatibility_score = calculate_project_investor_compatibility(
|
||||
project=project, investor=investor, use_funds=True
|
||||
)
|
||||
|
||||
# Get top 3 portfolio companies (id and name only)
|
||||
portfolio_companies = [
|
||||
CompanyMinimal(id=company.id, name=company.name)
|
||||
@@ -243,7 +293,7 @@ def filter_investors(
|
||||
stage_focus=stage_focus,
|
||||
portfolio_companies=portfolio_companies,
|
||||
sectors=fund_sectors,
|
||||
compatibility_score=1.0,
|
||||
compatibility_score=compatibility_score,
|
||||
)
|
||||
investment_responses.append(investment_response)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user