Some checks failed
Resolves the billing (core) → monitoring (optional) architecture violation by moving CapacityForecastService to the monitoring module where it belongs. - Create BillingMetricsProvider to expose subscription counts via stats_aggregator - Move CapacitySnapshot model from billing to monitoring - Replace direct MerchantSubscription queries with stats_aggregator calls - Fix middleware test mocks to cover StoreDomain/MerchantDomain fallback chains Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
72 lines
2.3 KiB
Python
72 lines
2.3 KiB
Python
# app/modules/monitoring/models/capacity_snapshot.py
|
|
"""
|
|
Capacity snapshot model for platform capacity monitoring.
|
|
|
|
Stores daily snapshots of platform metrics for growth trending and capacity forecasting.
|
|
"""
|
|
|
|
from sqlalchemy import (
|
|
Column,
|
|
DateTime,
|
|
Index,
|
|
Integer,
|
|
Numeric,
|
|
)
|
|
from sqlalchemy.dialects.sqlite import JSON
|
|
|
|
from app.core.database import Base
|
|
from models.database.base import TimestampMixin
|
|
|
|
|
|
class CapacitySnapshot(Base, TimestampMixin):
|
|
"""
|
|
Daily snapshot of platform capacity metrics.
|
|
|
|
Used for growth trending and capacity forecasting.
|
|
Captured daily by background job.
|
|
"""
|
|
|
|
__tablename__ = "capacity_snapshots"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
snapshot_date = Column(DateTime(timezone=True), nullable=False, unique=True, index=True)
|
|
|
|
# Store metrics
|
|
total_stores = Column(Integer, default=0, nullable=False)
|
|
active_stores = Column(Integer, default=0, nullable=False)
|
|
trial_stores = Column(Integer, default=0, nullable=False)
|
|
|
|
# Subscription metrics
|
|
total_subscriptions = Column(Integer, default=0, nullable=False)
|
|
active_subscriptions = Column(Integer, default=0, nullable=False)
|
|
|
|
# Resource metrics
|
|
total_products = Column(Integer, default=0, nullable=False)
|
|
total_orders_month = Column(Integer, default=0, nullable=False)
|
|
total_team_members = Column(Integer, default=0, nullable=False)
|
|
|
|
# Storage metrics
|
|
storage_used_gb = Column(Numeric(10, 2), default=0, nullable=False)
|
|
db_size_mb = Column(Numeric(10, 2), default=0, nullable=False)
|
|
|
|
# Capacity metrics (theoretical limits from subscriptions)
|
|
theoretical_products_limit = Column(Integer, nullable=True)
|
|
theoretical_orders_limit = Column(Integer, nullable=True)
|
|
theoretical_team_limit = Column(Integer, nullable=True)
|
|
|
|
# Tier distribution (JSON: {"essential": 10, "professional": 5, ...})
|
|
tier_distribution = Column(JSON, nullable=True)
|
|
|
|
# Performance metrics
|
|
avg_response_ms = Column(Integer, nullable=True)
|
|
peak_cpu_percent = Column(Numeric(5, 2), nullable=True)
|
|
peak_memory_percent = Column(Numeric(5, 2), nullable=True)
|
|
|
|
# Indexes
|
|
__table_args__ = (
|
|
Index("ix_capacity_snapshots_date", "snapshot_date"),
|
|
)
|
|
|
|
def __repr__(self) -> str:
|
|
return f"<CapacitySnapshot(date={self.snapshot_date}, stores={self.total_stores})>"
|