techprotrade's picture
Deploy ATOM FastAPI command center runtime (part 6)
383cb38 verified
Raw
History Blame Contribute Delete
3.28 kB
import datetime
import logging
from typing import Dict, Optional
from accounting.models import Entity, Invoice, InvoiceStatus
from ecommerce.models import EcommerceCustomer, Subscription
from intelligence.models import ClientHealthScore
from saas.models import UsageEvent
from sqlalchemy import func
from sqlalchemy.orm import Session
logger = logging.getLogger(__name__)
class HealthScoringEngine:
def __init__(self, db: Session):
self.db = db
def calculate_health_score(self, client_entity_id: str) -> ClientHealthScore:
"""
Computes a 0-100 score based on 3 pillars:
1. Financial (40%): Are invoices paid on time?
2. Usage (40%): Is SaaS usage stable/growing?
3. Sentiment (20%): CRM sentiment (Placeholder for now)
"""
entity = self.db.query(Entity).filter(Entity.id == client_entity_id).first()
if not entity:
return None
# 1. Financial Score (0-100)
# Logic: If overdue > 0, score drops significantly.
overdue = self.db.query(Invoice).filter(
Invoice.customer_id == client_entity_id,
Invoice.status == InvoiceStatus.OVERDUE
).count()
financial_score = 100.0
if overdue > 0:
financial_score = max(0, 100 - (overdue * 20)) # -20 per overdue invoice
# 2. Usage Score (0-100)
# Logic: Find linked ecommerce customer -> subscription -> check usage trend
# For MVP, we'll check if they have ANY usage in last 30 days
usage_score = 50.0 # Neutral default
# Link Accounting Entity -> Ecommerce Customer (via metadata or resolver)
# We will assume linkage exists. If not, finding by name partial match for MVP.
ecom_customer = self.db.query(EcommerceCustomer).filter(
EcommerceCustomer.email == entity.email # Assuming simplistic match
).first()
if ecom_customer:
# Check active subs
sub = self.db.query(Subscription).filter(
Subscription.customer_id == ecom_customer.id,
Subscription.status == 'active'
).first()
if sub:
# Check usage events
recent_events = self.db.query(UsageEvent).filter(
UsageEvent.subscription_id == sub.id
).count()
if recent_events > 0:
usage_score = 100.0
else:
usage_score = 20.0 # Ghost (Zombie) account
# 3. Sentiment Score
# Placeholder: 80
sentiment_score = 80.0
# Weighted Average
overall = (financial_score * 0.4) + (usage_score * 0.4) + (sentiment_score * 0.2)
# Create Record
score_record = ClientHealthScore(
workspace_id=entity.workspace_id,
client_entity_id=client_entity_id,
overall_score=overall,
financial_score=financial_score,
usage_score=usage_score,
sentiment_score=sentiment_score,
metadata_json={"overdue_count": overdue}
)
self.db.add(score_record)
self.db.commit()
return score_record