crowdata / app /reports /service.py
YOSOYYONOSOYOTRO's picture
Upload folder using huggingface_hub (part 2)
4223796 verified
Raw
History Blame Contribute Delete
18 kB
"""
Report Service — Orquestador principal de reportes.
Usa orchestrator y registry para ejecutar scrapers y construir reportes.
"""
import asyncio
import logging
import re
import uuid
from datetime import datetime, timezone
from typing import Any
from sqlalchemy import select, insert
from app.database import get_db
from app.config import get_settings
from app.reports.models import SearchHistory, MonitorTask, ReportCache
from app.reports.schemas import (
ScoreHistorial, ScoreCrediticio,
PersonReport, CompanyReport, Identificacion, Contacto, Domicilio,
DatosFiscales, DatosFinancieros, DatosSocietarios, DatosPatrimoniales,
DatosJudiciales, PublicacionBO, BcraHistorial, ChequeRechazado,
CausaJudicial, Vehiculo, InfraccionTransito, MarcaINPI, Vinculo,
DatosPrevisionales, DatosAcademicos, TituloAcademico, IndicadorRiesgo,
Inmueble, Inhibicion, DatosRegistroCivil, GroupReport, VehicleReport,
PropertyReport, ReportMeta, ActividadFiscal, CompanyIdentificacion, RedSocial,
DatosIGJ, ViasSalud, ContratoEstado, MatriculaProfesional,
DatosBilleterasVirtuales, BilleteraVirtual, TimelineEvent, DeudorAlimentario, Monotributo
)
from app.cache.redis_client import cache_get, cache_set
from app.scrapers.base import AntiBotBlockedError
from app.utils.scoring import compute_score, compute_company_score, compute_group_score
from app.reports.orchestrator import ScraperOrchestrator
from app.reports.registry import registry, ScraperCategory, ScraperDef
from app.reports.fallback import apply_fallbacks
from app.reports.builders import (
build_person_report, build_company_report, build_vehicle_report,
build_property_report, build_group_report
)
SITUACIONES_BCRA = {
1: "Normal",
2: "Con seguimiento especial / Riesgo bajo",
3: "Con problemas / Riesgo medio",
4: "Con alto riesgo de insolvencia / Riesgo alto",
5: "Irrecuperable",
6: "Irrecuperable por disposición técnica",
}
logger = logging.getLogger(__name__)
# Register all scrapers in the registry
def _register_scrapers():
"""Registra todos los scrapers disponibles."""
from app.scrapers.arca_afip import ArcaAfipScraper
from app.scrapers.bcra import BcraScraper
from app.scrapers.boletin_oficial import BoletinOficialScraper
from app.scrapers.igj import IgjScraper
from app.scrapers.poder_judicial import PoderJudicialScraper
from app.scrapers.dnrpa import DnrpaScraper
from app.scrapers.sinai import SinaiScraper
from app.scrapers.inpi import InpiScraper
from app.scrapers.anses import AnsesScraper
from app.scrapers.redes_sociales import RedesSocialesScraper
from app.scrapers.telefonia import TelefoniaScraper
from app.scrapers.renaper import RenaperScraper
from app.scrapers.boletines_provinciales import BoletinesProvincialesScraper
from app.scrapers.colegios_profesionales import ColegiosProfesionalesScraper
from app.scrapers.ruido import RuidoScraper
from app.scrapers.compras_estatales import ComprasEstatalesScraper
from app.scrapers.padron_electoral import PadronElectoralScraper
from app.scrapers.sgarhu import SgarhuScraper
from app.scrapers.siscop import SiscopScraper
from app.scrapers.arba_automotores import ArbaAutomotoresScraper
from app.scrapers.arba_catastro import ArbaCatastroScraper
from app.scrapers.cnv import CnvScraper
from app.scrapers.uif import UifScraper
from app.scrapers.juba import JubaScraper
from app.scrapers.renaper_facial import RenaperFacialScraper
from app.scrapers.name_search import NameSearchScraper
from app.scrapers.google_images import GoogleImagesScraper
from app.scrapers.infracciones import InfraccionesScraper
from app.scrapers.inhibiciones import InhibicionesScraper
from app.scrapers.poder_judicial_provincial import PoderJudicialProvincialScraper
from app.scrapers.contratar import ContratarScraper
from app.scrapers.deudores_alimentarios import DeudoresAlimentariosScraper
from app.scrapers.carto_arba import CartoArbaScraper
from app.scrapers.archive_org import ArchiveOrgScraper
from app.scrapers.monotributo_historial import MonotributoHistorialScraper
from app.scrapers.registro_conductores import RegistroConductoresScraper
from app.scrapers.timeline_boa import TimelineBoletinScraper
from app.scrapers.billeteras_virtuales import BilleterasVirtualesScraper
from app.scrapers.compras_estatales import ComprasEstatalesScraper
# CORE - Críticos
registry.register(ScraperDef(
name="arca_afip", display_name="ARCA/AFIP",
category=ScraperCategory.CORE, instance=ArcaAfipScraper(),
timeout=60, critical=True
))
registry.register(ScraperDef(
name="bcra", display_name="BCRA",
category=ScraperCategory.CORE, instance=BcraScraper(),
timeout=30, critical=True
))
registry.register(ScraperDef(
name="dnrpa", display_name="DNRPA",
category=ScraperCategory.CORE, instance=DnrpaScraper(),
timeout=90, critical=True
))
registry.register(ScraperDef(
name="poder_judicial", display_name="Poder Judicial",
category=ScraperCategory.CORE, instance=PoderJudicialScraper(),
timeout=120, critical=True
))
registry.register(ScraperDef(
name="renaper", display_name="RENAPER",
category=ScraperCategory.CORE, instance=RenaperScraper(),
timeout=60, critical=True
))
registry.register(ScraperDef(
name="igj", display_name="IGJ",
category=ScraperCategory.CORE, instance=IgjScraper(),
timeout=60, critical=True
))
registry.register(ScraperDef(
name="inpi", display_name="INPI",
category=ScraperCategory.CORE, instance=InpiScraper(),
timeout=60, critical=True
))
registry.register(ScraperDef(
name="uif", display_name="UIF/PEPs",
category=ScraperCategory.CORE, instance=UifScraper(),
timeout=60, critical=True
))
# FINANCIAL
registry.register(ScraperDef(
name="arba_automotores", display_name="ARBA Automotores",
category=ScraperCategory.FINANCIAL, instance=ArbaAutomotoresScraper(),
timeout=60, critical=False
))
registry.register(ScraperDef(
name="arba_catastro", display_name="ARBA Catastro",
category=ScraperCategory.FINANCIAL, instance=ArbaCatastroScraper(),
timeout=60, critical=False
))
registry.register(ScraperDef(
name="boletin_oficial", display_name="Boletín Oficial",
category=ScraperCategory.FINANCIAL, instance=BoletinOficialScraper(),
timeout=30, critical=False
))
registry.register(ScraperDef(
name="boletines_provinciales", display_name="Boletines Provinciales",
category=ScraperCategory.FINANCIAL, instance=BoletinesProvincialesScraper(),
timeout=60, critical=False
))
registry.register(ScraperDef(
name="anses", display_name="ANSES",
category=ScraperCategory.FINANCIAL, instance=AnsesScraper(),
timeout=120, critical=False
))
registry.register(ScraperDef(
name="cnv", display_name="CNV",
category=ScraperCategory.FINANCIAL, instance=CnvScraper(),
timeout=60, critical=False
))
registry.register(ScraperDef(
name="compras_estatales", display_name="COMPR.AR",
category=ScraperCategory.FINANCIAL, instance=ComprasEstatalesScraper(),
timeout=60, critical=False
))
registry.register(ScraperDef(
name="monotributo_historial", display_name="Monotributo Historial",
category=ScraperCategory.FINANCIAL, instance=MonotributoHistorialScraper(),
timeout=60, critical=False
))
registry.register(ScraperDef(
name="registro_conductores", display_name="Registro Conductores",
category=ScraperCategory.FINANCIAL, instance=RegistroConductoresScraper(),
timeout=60, critical=False
))
# JUDICIAL
registry.register(ScraperDef(
name="infracciones", display_name="Infracciones",
category=ScraperCategory.JUDICIAL, instance=InfraccionesScraper(),
timeout=60, critical=False
))
registry.register(ScraperDef(
name="deudores_alimentarios", display_name="Deudores Alimentarios",
category=ScraperCategory.JUDICIAL, instance=DeudoresAlimentariosScraper(),
timeout=120, critical=False
))
registry.register(ScraperDef(
name="juba", display_name="JUBA",
category=ScraperCategory.JUDICIAL, instance=JubaScraper(),
timeout=120, critical=False
))
registry.register(ScraperDef(
name="poder_judicial_provincial", display_name="Poder Judicial Provincial",
category=ScraperCategory.JUDICIAL, instance=PoderJudicialProvincialScraper(),
timeout=120, critical=False
))
registry.register(ScraperDef(
name="inhibiciones", display_name="Inhibiciones",
category=ScraperCategory.JUDICIAL, instance=InhibicionesScraper(),
timeout=60, critical=False
))
# PATRIMONIAL
registry.register(ScraperDef(
name="arba_catastro", display_name="ARBA Catastro",
category=ScraperCategory.PATRIMONIAL, instance=ArbaCatastroScraper(),
timeout=60, critical=False
))
registry.register(ScraperDef(
name="carto_arba", display_name="CARTO ARBA",
category=ScraperCategory.PATRIMONIAL, instance=CartoArbaScraper(),
timeout=60, critical=False
))
registry.register(ScraperDef(
name="sinai", display_name="SINAI",
category=ScraperCategory.PATRIMONIAL, instance=SinaiScraper(),
timeout=60, critical=False
))
registry.register(ScraperDef(
name="siscop", display_name="SISCOP",
category=ScraperCategory.PATRIMONIAL, instance=SiscopScraper(),
timeout=60, critical=False
))
# OSINT
registry.register(ScraperDef(
name="google_images", display_name="Google Images",
category=ScraperCategory.OSINT, instance=GoogleImagesScraper(),
timeout=30, critical=False
))
registry.register(ScraperDef(
name="redes_sociales", display_name="Redes Sociales",
category=ScraperCategory.OSINT, instance=RedesSocialesScraper(),
timeout=30, critical=False
))
registry.register(ScraperDef(
name="telefonia", display_name="Teléfono",
category=ScraperCategory.OSINT, instance=TelefoniaScraper(),
timeout=30, critical=False
))
registry.register(ScraperDef(
name="name_search", display_name="Name Search",
category=ScraperCategory.OSINT, instance=NameSearchScraper(),
timeout=60, critical=False
))
# OTHER
registry.register(ScraperDef(
name="billeteras_virtuales", display_name="Billeteras Virtuales",
category=ScraperCategory.OTHER, instance=BilleterasVirtualesScraper(),
timeout=30, critical=False
))
registry.register(ScraperDef(
name="ruido", display_name="RUIDO",
category=ScraperCategory.OTHER, instance=RuidoScraper(),
timeout=120, critical=False
))
registry.register(ScraperDef(
name="sgarhu", display_name="SGARHU",
category=ScraperCategory.OTHER, instance=SgarhuScraper(),
timeout=60, critical=False
))
registry.register(ScraperDef(
name="contratar", display_name="CONTRATAR",
category=ScraperCategory.OTHER, instance=ContratarScraper(),
timeout=60, critical=False
))
registry.register(ScraperDef(
name="colegios_profesionales", display_name="Colegios Profesionales",
category=ScraperCategory.OTHER, instance=ColegiosProfesionalesScraper(),
timeout=60, critical=False
))
registry.register(ScraperDef(
name="ruido_ssalud", display_name="RUIDO Salud",
category=ScraperCategory.OTHER, instance=SgarhuScraper(), # alias
timeout=120, critical=False
))
registry.register(ScraperDef(
name="renaper_facial", display_name="RENAPER Facial",
category=ScraperCategory.OTHER, instance=RenaperFacialScraper(),
timeout=60, critical=False
))
registry.register(ScraperDef(
name="timeline_boa", display_name="Timeline BO",
category=ScraperCategory.OTHER, instance=TimelineBoletinScraper(),
timeout=30, critical=False
))
registry.register(ScraperDef(
name="archive_org", display_name="Archive.org",
category=ScraperCategory.OTHER, instance=ArchiveOrgScraper(),
timeout=60, critical=False
))
registry.register(ScraperDef(
name="padron_electoral", display_name="Padrón Electoral",
category=ScraperCategory.OTHER, instance=PadronElectoralScraper(),
timeout=60, critical=False
))
registry.register(ScraperDef(
name="compras_estatales", display_name="COMPR.AR/Contrataciones",
category=ScraperCategory.OTHER, instance=ComprasEstatalesScraper(),
timeout=60, critical=False
))
registry.register(ScraperDef(
name="cnv", display_name="CNV",
category=ScraperCategory.OTHER, instance=CnvScraper(),
timeout=60, critical=False
))
# Initialize registry on import
_register_scrapers()
async def get_person_report(
cuit: str,
skip_redes: bool = False,
force_refresh: bool = False,
) -> PersonReport:
"""Genera reporte completo de persona física."""
# Orchestrate scrapers
orchestrator = ScraperOrchestrator("persona", cuit, force_refresh)
results = await orchestrator.run()
# Extract data from results
data = {r.name: r.data for r in results.values() if r.data}
# Build report
report = await build_person_report(cuit, data, results)
# Save to cache
await _save_report_cache("persona", cuit, report)
return report
async def get_company_report(
cuit: str,
force_refresh: bool = False,
) -> CompanyReport:
"""Genera reporte completo de empresa."""
orchestrator = ScraperOrchestrator("empresa", cuit, force_refresh)
results = await orchestrator.run()
data = {r.name: r.data for r in results.values() if r.data}
report = await build_company_report(cuit, data, results)
await _save_report_cache("empresa", cuit, report)
return report
async def get_vehicle_report(
dominio: str,
force_refresh: bool = False,
) -> VehicleReport:
"""Genera reporte de vehículo."""
orchestrator = ScraperOrchestrator("vehiculo", dominio, force_refresh)
results = await orchestrator.run()
data = {r.name: r.data for r in results.values() if r.data}
report = await build_vehicle_report(dominio, data, results)
await _save_report_cache("vehiculo", dominio, report)
return report
async def get_property_report(
calle: str, numero: str, localidad: str, provincia: str,
force_refresh: bool = False,
) -> PropertyReport:
"""Genera reporte de inmueble."""
identifier = f"{calle} {numero} {localidad} {provincia}"
orchestrator = ScraperOrchestrator("propiedad", identifier, force_refresh)
results = await orchestrator.run()
data = {r.name: r.data for r in results.values() if r.data}
report = await build_property_report(calle, numero, localidad, provincia, data, results)
await _save_report_cache("propiedad", identifier, report)
return report
async def get_group_report(
cuit: str,
force_refresh: bool = False,
) -> GroupReport:
"""Genera reporte de grupo económico."""
# Primero persona
person_report = await get_person_report(cuit, force_refresh=force_refresh)
# Luego empresas asociadas (desde IGJ)
igj_data = person_report.patrimonial.datos_igj
empresas_vinculadas = []
if igj_data and igj_data.socios_directivos:
for socio in igj_data.socios_directivos:
cuil = socio.get("cuil")
if cuil and cuil != cuit:
try:
emp_report = await get_company_report(cuil, force_refresh=force_refresh)
empresas_vinculadas.append(emp_report)
except Exception as e:
logger.warning(f"Failed to get company report for {cuil}: {e}")
# Vehículos
# ... lógica similar
return GroupReport(
persona=person_report,
empresas_vinculadas=empresas_vinculadas,
vehiculos=[],
inmuebles=[],
score_grupo=compute_group_score(person_report, empresas_vinculadas, [], []),
)
async def _save_report_cache(report_type: str, identifier: str, report: Any) -> None:
"""Guarda reporte en caché PostgreSQL."""
from app.reports.models import ReportCache
import json
async for db in get_db():
try:
await db.execute(
insert(ReportCache).values(
cache_key=f"{report_type}:{identifier}",
report_type=report_type,
identifier=identifier,
data=json.dumps(report.model_dump() if hasattr(report, 'model_dump') else report),
sources_used=list(report.model_fields.keys()) if hasattr(report, 'model_fields') else [],
expires_at=datetime.now(timezone.utc).replace(hour=23, minute=59, second=59),
hit_count=0,
)
)
await db.commit()
except Exception as e:
logger.warning(f"Error saving report cache: {e}")
finally:
break