feat: Add insight generation functionality with compatibility scoring and web search integration
This commit is contained in:
@@ -507,3 +507,140 @@ def get_compatibility_score_breakdown(
|
||||
),
|
||||
"note": "Using investor-level data (no specific fund selected)",
|
||||
}
|
||||
|
||||
|
||||
def generate_compatibility_explanation(
|
||||
project: ProjectTable, investor: InvestorTable, score: float, use_funds: bool = True
|
||||
) -> str:
|
||||
"""
|
||||
Generate a detailed, natural language explanation of the compatibility score.
|
||||
|
||||
Args:
|
||||
project: The project being evaluated
|
||||
investor: The investor being compared against
|
||||
score: The calculated compatibility score (0-1)
|
||||
use_funds: Whether fund-level data was used
|
||||
|
||||
Returns:
|
||||
A formatted string with the compatibility score and detailed explanation
|
||||
"""
|
||||
score_percentage = int(score * 100)
|
||||
|
||||
# Determine match quality
|
||||
if score_percentage >= 80:
|
||||
match_level = "Excellent match"
|
||||
elif score_percentage >= 65:
|
||||
match_level = "Strong match"
|
||||
elif score_percentage >= 50:
|
||||
match_level = "Good match"
|
||||
elif score_percentage >= 35:
|
||||
match_level = "Moderate match"
|
||||
else:
|
||||
match_level = "Limited match"
|
||||
|
||||
# Collect alignment factors
|
||||
alignment_factors = []
|
||||
recommendations = []
|
||||
|
||||
# Get the best matching fund if using funds
|
||||
best_fund = None
|
||||
if use_funds and investor.funds:
|
||||
best_score = 0
|
||||
for fund in investor.funds:
|
||||
fund_score = _calculate_project_fund_compatibility(project, fund)
|
||||
if fund_score > best_score:
|
||||
best_score = fund_score
|
||||
best_fund = fund
|
||||
|
||||
# Analyze sector alignment
|
||||
if project.sector:
|
||||
project_sectors = [s.name for s in project.sector if hasattr(s, "name")]
|
||||
|
||||
if best_fund and best_fund.sectors:
|
||||
fund_sectors = {s.name for s in best_fund.sectors if hasattr(s, "name")}
|
||||
common_sectors = set(project_sectors) & fund_sectors
|
||||
|
||||
if common_sectors:
|
||||
sectors_str = ", ".join(list(common_sectors)[:2])
|
||||
alignment_factors.append(f"{sectors_str} sector focus")
|
||||
elif project_sectors:
|
||||
recommendations.append(
|
||||
f"Consider emphasizing any {project_sectors[0]} industry connections"
|
||||
)
|
||||
elif investor.sectors:
|
||||
investor_sectors = {s.name for s in investor.sectors if hasattr(s, "name")}
|
||||
common_sectors = set(project_sectors) & investor_sectors
|
||||
|
||||
if common_sectors:
|
||||
sectors_str = ", ".join(list(common_sectors)[:2])
|
||||
alignment_factors.append(f"{sectors_str} sector focus")
|
||||
|
||||
# Analyze stage alignment
|
||||
if project.stage:
|
||||
stage_name = (
|
||||
project.stage.value
|
||||
if hasattr(project.stage, "value")
|
||||
else str(project.stage)
|
||||
)
|
||||
stage_display = stage_name.replace("_", " ").title()
|
||||
|
||||
if best_fund and best_fund.investment_stages:
|
||||
fund_stage_names = {
|
||||
s.name for s in best_fund.investment_stages if hasattr(s, "name")
|
||||
}
|
||||
if stage_name in fund_stage_names:
|
||||
alignment_factors.append(f"{stage_display} stage")
|
||||
else:
|
||||
recommendations.append(
|
||||
"Investor typically focuses on different stages; highlight your traction and growth metrics"
|
||||
)
|
||||
|
||||
if not best_fund:
|
||||
alignment_factors.append(f"{stage_display} stage")
|
||||
|
||||
# Analyze geographic alignment
|
||||
if project.location:
|
||||
if best_fund and best_fund.geographic_focus:
|
||||
if (
|
||||
project.location.lower() in best_fund.geographic_focus.lower()
|
||||
or best_fund.geographic_focus.lower() in project.location.lower()
|
||||
):
|
||||
alignment_factors.append(f"{project.location} presence")
|
||||
elif investor.headquarters:
|
||||
if (
|
||||
project.location.lower() in investor.headquarters.lower()
|
||||
or investor.headquarters.lower() in project.location.lower()
|
||||
):
|
||||
alignment_factors.append(f"{project.location} market presence")
|
||||
|
||||
# Analyze valuation/check size fit
|
||||
if project.valuation:
|
||||
if best_fund and best_fund.check_size_lower and best_fund.check_size_upper:
|
||||
reasonable_min = best_fund.check_size_lower * 3
|
||||
reasonable_max = best_fund.check_size_upper * 10
|
||||
|
||||
if reasonable_min <= project.valuation <= reasonable_max:
|
||||
alignment_factors.append("appropriate funding stage")
|
||||
elif project.valuation < reasonable_min:
|
||||
recommendations.append(
|
||||
"You may be early for this investor; consider approaching at a later stage"
|
||||
)
|
||||
else:
|
||||
recommendations.append(
|
||||
"Consider highlighting your growth trajectory and market opportunity"
|
||||
)
|
||||
|
||||
# Build the explanation
|
||||
explanation_parts = [f"Based on your startup profile: {score_percentage}% match"]
|
||||
|
||||
if alignment_factors:
|
||||
alignment_text = ", ".join(alignment_factors)
|
||||
explanation_parts.append(f"{match_level}: {alignment_text}.")
|
||||
else:
|
||||
explanation_parts.append(f"{match_level}.")
|
||||
|
||||
if recommendations:
|
||||
rec_text = recommendations[0] # Show the most important recommendation
|
||||
explanation_parts.append(rec_text + ".")
|
||||
|
||||
return " ".join(explanation_parts)
|
||||
|
||||
Reference in New Issue
Block a user