# app/modules/prospecting/services/stats_service.py """ Statistics service for the prospecting dashboard. """ import logging from sqlalchemy import func from sqlalchemy.orm import Session from app.modules.prospecting.models import ( Prospect, ProspectChannel, ProspectScanJob, ProspectScore, ) logger = logging.getLogger(__name__) class StatsService: """Service for dashboard statistics and reporting.""" def get_overview(self, db: Session) -> dict: """Get overview statistics for the dashboard.""" total = db.query(func.count(Prospect.id)).scalar() or 0 digital = db.query(func.count(Prospect.id)).filter(Prospect.channel == ProspectChannel.DIGITAL).scalar() or 0 offline = db.query(func.count(Prospect.id)).filter(Prospect.channel == ProspectChannel.OFFLINE).scalar() or 0 with_website = db.query(func.count(Prospect.id)).filter(Prospect.has_website.is_(True)).scalar() or 0 with_https = db.query(func.count(Prospect.id)).filter(Prospect.uses_https.is_(True)).scalar() or 0 scored = db.query(func.count(ProspectScore.id)).scalar() or 0 avg_score = db.query(func.avg(ProspectScore.score)).scalar() # Leads by tier tier_results = ( db.query(ProspectScore.lead_tier, func.count(ProspectScore.id)) .group_by(ProspectScore.lead_tier) .all() ) leads_by_tier = {tier: count for tier, count in tier_results if tier} # Common issues (from reason_flags JSON) # Simplified: count scored prospects per tier top_priority = leads_by_tier.get("top_priority", 0) return { "total_prospects": total, "digital_count": digital, "offline_count": offline, "with_website": with_website, "with_https": with_https, "scored": scored, "avg_score": round(avg_score, 1) if avg_score else None, "top_priority": top_priority, "leads_by_tier": leads_by_tier, "common_issues": self._get_common_issues(db), } def get_scan_jobs( self, db: Session, *, page: int = 1, per_page: int = 20, status: str | None = None, ) -> tuple[list[ProspectScanJob], int]: """Get paginated scan jobs.""" query = db.query(ProspectScanJob) if status: query = query.filter(ProspectScanJob.status == status) total = query.count() jobs = ( query.order_by(ProspectScanJob.created_at.desc()) .offset((page - 1) * per_page) .limit(per_page) .all() ) return jobs, total def _get_common_issues(self, db: Session) -> list[dict]: """Extract common issue flags from scored prospects.""" scores = db.query(ProspectScore.reason_flags).filter(ProspectScore.reason_flags.isnot(None)).all() import json flag_counts: dict[str, int] = {} for (flags_json,) in scores: try: flags = json.loads(flags_json) for flag in flags: flag_counts[flag] = flag_counts.get(flag, 0) + 1 except (json.JSONDecodeError, TypeError): continue sorted_flags = sorted(flag_counts.items(), key=lambda x: x[1], reverse=True) return [{"flag": flag, "count": count} for flag, count in sorted_flags[:10]] stats_service = StatsService()