feat: Implement report generation service and add report route for investor profiles

This commit is contained in:
bolade
2025-10-14 12:02:23 +01:00
parent e386ebbdef
commit 9e1ec258f1
10 changed files with 697 additions and 1 deletions
Binary file not shown.
Binary file not shown.
+121
View File
@@ -0,0 +1,121 @@
from typing import Optional
from db.db import get_db
from db.models import FundTable, InvestorTable, ProjectTable
from fastapi import APIRouter, Depends, HTTPException, Query
from fastapi.responses import Response
from services.report_gen import ReportGenerator
from sqlalchemy.orm import Session, selectinload
router = APIRouter(tags=["Report Generation"])
@router.get("/report/investor/{investor_id}")
async def generate_investor_report(
investor_id: int,
project_id: Optional[int] = Query(
None, description="Optional project ID for compatibility analysis"
),
db: Session = Depends(get_db),
):
"""
Generate a PDF report for an investor profile.
Args:
investor_id: The ID of the investor to generate a report for
project_id: Optional project ID to include mandate match analysis
Returns:
PDF file as a downloadable response
"""
# Fetch investor data with all relationships
investor = (
db.query(InvestorTable)
.options(
selectinload(InvestorTable.portfolio_companies),
selectinload(InvestorTable.team_members),
selectinload(InvestorTable.sectors),
selectinload(InvestorTable.funds).selectinload(FundTable.investment_stages),
selectinload(InvestorTable.funds).selectinload(FundTable.sectors),
)
.filter(InvestorTable.id == investor_id)
.first()
)
if not investor:
raise HTTPException(status_code=404, detail="Investor not found")
# Prepare investor data dictionary
investor_data = {
"name": investor.name,
"description": investor.description,
"website": investor.website,
"headquarters": investor.headquarters,
"aum": investor.aum,
"geographic_focus": investor.geographic_focus,
"portfolio_highlights": investor.portfolio_highlights or [],
"investment_thesis": investor.investment_thesis or [],
"sectors": [sector.name for sector in investor.sectors],
"team_members": [
{
"name": member.name,
"role": member.role,
"title": member.title,
"email": member.email,
}
for member in investor.team_members
],
"check_size_lower": None,
"check_size_upper": None,
"investment_stages": [],
}
# Get check sizes and stages from funds
if investor.funds:
# Use the first fund's data or aggregate
fund = investor.funds[0]
investor_data["check_size_lower"] = fund.check_size_lower
investor_data["check_size_upper"] = fund.check_size_upper
# Aggregate all investment stages from all funds
stages = set()
for fund in investor.funds:
for stage in fund.investment_stages:
stages.add(stage.name)
investor_data["investment_stages"] = list(stages)
# Fetch project data if project_id is provided
project_data = None
if project_id:
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")
project_data = {
"name": project.name,
"description": project.description,
"location": project.location,
"valuation": project.valuation,
"stage": project.stage.name if project.stage else None,
"sectors": [sector.name for sector in project.sector],
}
# Generate PDF report
report_generator = ReportGenerator()
pdf_bytes = await report_generator.generate_investor_report(
investor_data, project_data
)
# Return PDF as downloadable file
filename = f"{investor.name.replace(' ', '_')}_Report.pdf"
return Response(
content=pdf_bytes,
media_type="application/pdf",
headers={"Content-Disposition": f'attachment; filename="{filename}"'},
)