168 lines
6.4 KiB
Python
168 lines
6.4 KiB
Python
"""Generador del reporte de texto completo."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime
|
|
|
|
|
|
def build_report(
|
|
geo: dict,
|
|
sub_results: dict,
|
|
scores: dict,
|
|
overall_score: int,
|
|
narratives: dict,
|
|
exec_summary: dict,
|
|
) -> dict:
|
|
"""Construye el dict completo del reporte con todas las secciones."""
|
|
now = datetime.now()
|
|
|
|
report = {
|
|
"address": geo.get("address", ""),
|
|
"lat": geo.get("lat"),
|
|
"lon": geo.get("lon"),
|
|
"analysis_date": now.strftime("%Y-%m-%d %H:%M"),
|
|
"overall_score": overall_score,
|
|
"scores": scores,
|
|
"sections": {},
|
|
"exec_summary": exec_summary,
|
|
}
|
|
|
|
# Sección 1: Resumen ejecutivo (construido arriba)
|
|
report["sections"]["executive"] = {
|
|
"title": "Resumen Ejecutivo",
|
|
"score": overall_score,
|
|
"summary": exec_summary.get("summary", ""),
|
|
"strengths": exec_summary.get("strengths", []),
|
|
"weaknesses": exec_summary.get("weaknesses", []),
|
|
}
|
|
|
|
# Sección 2: Criminalidad
|
|
crime_data = sub_results.get("crime", {})
|
|
report["sections"]["crime"] = {
|
|
"title": "Criminalidad",
|
|
"score": scores.get("crime", 0),
|
|
"total_crimes_30d": crime_data.get("score_input", {}).get("total_crimes_30d", "N/A"),
|
|
"crime_types": crime_data.get("crime_types", {}),
|
|
"has_violent": crime_data.get("score_input", {}).get("has_violent", False),
|
|
"sources": crime_data.get("sources", []),
|
|
"errors": crime_data.get("errors", []),
|
|
"narrative": narratives.get("crime", ""),
|
|
}
|
|
|
|
# Sección 3: Valoración inmobiliaria
|
|
prop_data = sub_results.get("property", {})
|
|
report["sections"]["property"] = {
|
|
"title": "Valoración y Mercado Inmobiliario",
|
|
"score": scores.get("property", 0),
|
|
"estimated_value": prop_data.get("estimated_value"),
|
|
"price_per_sqft": prop_data.get("price_per_sqft"),
|
|
"appreciation_1y": prop_data.get("appreciation_1y"),
|
|
"appreciation_3y": prop_data.get("appreciation_3y"),
|
|
"days_on_market": prop_data.get("days_on_market"),
|
|
"median_list_price": prop_data.get("median_list_price"),
|
|
"inventory": prop_data.get("inventory"),
|
|
"county_assessed_value": prop_data.get("county_assessed_value"),
|
|
"sources": prop_data.get("sources", []),
|
|
"errors": prop_data.get("errors", []),
|
|
"narrative": narratives.get("property", ""),
|
|
}
|
|
|
|
# Sección 4: Escuelas
|
|
school_data = sub_results.get("schools", {})
|
|
report["sections"]["schools"] = {
|
|
"title": "Escuelas",
|
|
"score": scores.get("schools", 0),
|
|
"avg_rating": school_data.get("avg_rating"),
|
|
"schools": school_data.get("schools", []),
|
|
"best_elementary": school_data.get("best_elementary"),
|
|
"best_middle": school_data.get("best_middle"),
|
|
"best_high": school_data.get("best_high"),
|
|
"sources": school_data.get("sources", []),
|
|
"errors": school_data.get("errors", []),
|
|
"narrative": narratives.get("schools", ""),
|
|
}
|
|
|
|
# Sección 5: Amenities
|
|
amenity_data = sub_results.get("amenities", {})
|
|
report["sections"]["amenities"] = {
|
|
"title": "Amenities y Walkability",
|
|
"score": scores.get("amenities", 0),
|
|
"walk_score": amenity_data.get("walk_score_estimate"),
|
|
"total_amenities": amenity_data.get("total_amenities", 0),
|
|
"categories": amenity_data.get("categories", {}),
|
|
"nearest": amenity_data.get("nearest", {}),
|
|
"sources": amenity_data.get("sources", []),
|
|
"errors": amenity_data.get("errors", []),
|
|
"narrative": narratives.get("amenities", ""),
|
|
}
|
|
|
|
# Sección 6: Demografía
|
|
demo_data = sub_results.get("demographics", {})
|
|
report["sections"]["demographics"] = {
|
|
"title": "Demografía",
|
|
"score": scores.get("demographics", 0),
|
|
"median_household_income": demo_data.get("median_household_income"),
|
|
"median_age": demo_data.get("median_age"),
|
|
"unemployment_rate": demo_data.get("unemployment_rate"),
|
|
"education_bachelors_pct": demo_data.get("education_bachelors_pct"),
|
|
"ethnicity": demo_data.get("ethnicity", {}),
|
|
"total_population": demo_data.get("total_population"),
|
|
"sources": demo_data.get("sources", []),
|
|
"errors": demo_data.get("errors", []),
|
|
"narrative": narratives.get("demographics", ""),
|
|
}
|
|
|
|
# Sección 7: Mercado marítimo
|
|
maritime_data = sub_results.get("maritime", {})
|
|
report["sections"]["maritime"] = {
|
|
"title": "Mercado Laboral Marítimo",
|
|
"score": scores.get("maritime", 0),
|
|
"shipyards": maritime_data.get("shipyards", []),
|
|
"marinas_with_jobs": maritime_data.get("marinas_with_jobs", []),
|
|
"maritime_employers": maritime_data.get("maritime_employers", []),
|
|
"bls_employment": maritime_data.get("bls_employment", {}),
|
|
"sources": maritime_data.get("sources", []),
|
|
"errors": maritime_data.get("errors", []),
|
|
"narrative": narratives.get("maritime", ""),
|
|
}
|
|
|
|
# Sección 8: Lifestyle náutico
|
|
life_data = sub_results.get("lifestyle", {})
|
|
report["sections"]["lifestyle"] = {
|
|
"title": "Estilo de Vida Náutico",
|
|
"score": scores.get("lifestyle", 0),
|
|
"marinas": life_data.get("marinas", []),
|
|
"boat_ramps": life_data.get("boat_ramps", []),
|
|
"beaches": life_data.get("beaches", []),
|
|
"nearest_marina": life_data.get("nearest_marina"),
|
|
"nearest_beach": life_data.get("nearest_beach"),
|
|
"ocean_access": life_data.get("ocean_access", False),
|
|
"waterway_nearby": life_data.get("waterway_nearby", False),
|
|
"sources": life_data.get("sources", []),
|
|
"errors": life_data.get("errors", []),
|
|
"narrative": narratives.get("lifestyle", ""),
|
|
}
|
|
|
|
# Texto plano del reporte para guardar en BD
|
|
report["report_text"] = _build_text(report)
|
|
|
|
return report
|
|
|
|
|
|
def _build_text(report: dict) -> str:
|
|
lines = [
|
|
f"AR-HOUSE LOCATION INTELLIGENCE REPORT",
|
|
f"Dirección: {report['address']}",
|
|
f"Fecha: {report['analysis_date']}",
|
|
f"Score General: {report['overall_score']}/100",
|
|
"",
|
|
]
|
|
for key, sec in report["sections"].items():
|
|
lines.append(f"=== {sec['title'].upper()} ===")
|
|
if sec.get("score") is not None:
|
|
lines.append(f"Score: {sec['score']}/100")
|
|
if sec.get("narrative"):
|
|
lines.append(sec["narrative"])
|
|
lines.append("")
|
|
return "\n".join(lines)
|