annator-command-center / saas /usage_service.py
techprotrade's picture
Deploy ATOM FastAPI command center runtime (part 6)
383cb38 verified
Raw
History Blame Contribute Delete
2.17 kB
import datetime
from datetime import timezone
import logging
from typing import Dict, Optional
from ecommerce.models import Subscription
from saas.models import UsageEvent
from sqlalchemy import func
from sqlalchemy.orm import Session
logger = logging.getLogger(__name__)
class UsageMeteringService:
def __init__(self, db: Session):
self.db = db
def ingest_event(self, subscription_id: str, event_type: str, quantity: float = 1.0, metadata: dict = None) -> UsageEvent:
"""
Record a usage event and update the subscription cache.
"""
sub = self.db.query(Subscription).filter(Subscription.id == subscription_id).first()
if not sub:
logger.error(f"Subscription {subscription_id} not found")
return None
# 1. Store Raw Event
event = UsageEvent(
workspace_id=sub.workspace_id,
subscription_id=subscription_id,
event_type=event_type,
quantity=quantity,
metadata_json=metadata,
timestamp=datetime.datetime.now(timezone.utc)
)
self.db.add(event)
# 2. Update Cache (Atomic Increment approach is better, but JSON update is MVP)
current_usage = sub.current_period_usage or {}
current_val = current_usage.get(event_type, 0.0)
current_usage[event_type] = current_val + quantity
# Re-assign to trigger SQL update for JSON
sub.current_period_usage = dict(current_usage)
self.db.commit()
return event
def get_aggregated_usage(self, subscription_id: str, start_date: datetime.datetime, end_date: datetime.datetime) -> Dict[str, float]:
"""
Sum usage by type for a given period.
"""
results = self.db.query(
UsageEvent.event_type,
func.sum(UsageEvent.quantity)
).filter(
UsageEvent.subscription_id == subscription_id,
UsageEvent.timestamp >= start_date,
UsageEvent.timestamp <= end_date
).group_by(UsageEvent.event_type).all()
return {r[0]: r[1] for r in results}