# app/services/platform_service.py """ Platform Service Business logic for platform management in the Multi-Platform CMS. Platforms represent different business offerings (OMS, Loyalty, Site Builder, Main Marketing). Each platform has its own: - Marketing pages (homepage, pricing, features) - Vendor defaults (about, terms, privacy) - Configuration and branding """ import logging from dataclasses import dataclass from sqlalchemy import func from sqlalchemy.orm import Session from app.exceptions.platform import ( PlatformNotFoundException, ) from app.modules.cms.models import ContentPage from models.database.platform import Platform from models.database.vendor_platform import VendorPlatform logger = logging.getLogger(__name__) @dataclass class PlatformStats: """Platform statistics.""" platform_id: int platform_code: str platform_name: str vendor_count: int platform_pages_count: int vendor_defaults_count: int vendor_overrides_count: int = 0 published_pages_count: int = 0 draft_pages_count: int = 0 class PlatformService: """Service for platform operations.""" @staticmethod def get_platform_by_code(db: Session, code: str) -> Platform: """ Get platform by code. Args: db: Database session code: Platform code (oms, loyalty, main, etc.) Returns: Platform object Raises: PlatformNotFoundException: If platform not found """ platform = db.query(Platform).filter(Platform.code == code).first() if not platform: raise PlatformNotFoundException(code) return platform @staticmethod def get_platform_by_code_optional(db: Session, code: str) -> Platform | None: """ Get platform by code, returns None if not found. Args: db: Database session code: Platform code Returns: Platform object or None """ return db.query(Platform).filter(Platform.code == code).first() @staticmethod def get_platform_by_id(db: Session, platform_id: int) -> Platform: """ Get platform by ID. Args: db: Database session platform_id: Platform ID Returns: Platform object Raises: PlatformNotFoundException: If platform not found """ platform = db.query(Platform).filter(Platform.id == platform_id).first() if not platform: raise PlatformNotFoundException(str(platform_id)) return platform @staticmethod def list_platforms( db: Session, include_inactive: bool = False ) -> list[Platform]: """ List all platforms. Args: db: Database session include_inactive: Include inactive platforms Returns: List of Platform objects """ query = db.query(Platform) if not include_inactive: query = query.filter(Platform.is_active == True) return query.order_by(Platform.id).all() @staticmethod def get_vendor_count(db: Session, platform_id: int) -> int: """ Get count of vendors on a platform. Args: db: Database session platform_id: Platform ID Returns: Vendor count """ return ( db.query(func.count(VendorPlatform.vendor_id)) .filter(VendorPlatform.platform_id == platform_id) .scalar() or 0 ) @staticmethod def get_platform_pages_count(db: Session, platform_id: int) -> int: """ Get count of platform marketing pages. Args: db: Database session platform_id: Platform ID Returns: Platform pages count """ return ( db.query(func.count(ContentPage.id)) .filter( ContentPage.platform_id == platform_id, ContentPage.vendor_id == None, ContentPage.is_platform_page == True, ) .scalar() or 0 ) @staticmethod def get_vendor_defaults_count(db: Session, platform_id: int) -> int: """ Get count of vendor default pages. Args: db: Database session platform_id: Platform ID Returns: Vendor defaults count """ return ( db.query(func.count(ContentPage.id)) .filter( ContentPage.platform_id == platform_id, ContentPage.vendor_id == None, ContentPage.is_platform_page == False, ) .scalar() or 0 ) @staticmethod def get_vendor_overrides_count(db: Session, platform_id: int) -> int: """ Get count of vendor override pages. Args: db: Database session platform_id: Platform ID Returns: Vendor overrides count """ return ( db.query(func.count(ContentPage.id)) .filter( ContentPage.platform_id == platform_id, ContentPage.vendor_id != None, ) .scalar() or 0 ) @staticmethod def get_published_pages_count(db: Session, platform_id: int) -> int: """ Get count of published pages on a platform. Args: db: Database session platform_id: Platform ID Returns: Published pages count """ return ( db.query(func.count(ContentPage.id)) .filter( ContentPage.platform_id == platform_id, ContentPage.is_published == True, ) .scalar() or 0 ) @staticmethod def get_draft_pages_count(db: Session, platform_id: int) -> int: """ Get count of draft pages on a platform. Args: db: Database session platform_id: Platform ID Returns: Draft pages count """ return ( db.query(func.count(ContentPage.id)) .filter( ContentPage.platform_id == platform_id, ContentPage.is_published == False, ) .scalar() or 0 ) @classmethod def get_platform_stats(cls, db: Session, platform: Platform) -> PlatformStats: """ Get comprehensive statistics for a platform. Args: db: Database session platform: Platform object Returns: PlatformStats dataclass """ return PlatformStats( platform_id=platform.id, platform_code=platform.code, platform_name=platform.name, vendor_count=cls.get_vendor_count(db, platform.id), platform_pages_count=cls.get_platform_pages_count(db, platform.id), vendor_defaults_count=cls.get_vendor_defaults_count(db, platform.id), vendor_overrides_count=cls.get_vendor_overrides_count(db, platform.id), published_pages_count=cls.get_published_pages_count(db, platform.id), draft_pages_count=cls.get_draft_pages_count(db, platform.id), ) @staticmethod def update_platform( db: Session, platform: Platform, update_data: dict ) -> Platform: """ Update platform fields. Note: This method does NOT commit the transaction. The caller (API endpoint) is responsible for committing. Args: db: Database session platform: Platform to update update_data: Dictionary of fields to update Returns: Updated Platform object (with pending changes) """ for field, value in update_data.items(): if hasattr(platform, field): setattr(platform, field, value) logger.info(f"[PLATFORMS] Updated platform: {platform.code}") return platform # Singleton instance for convenience platform_service = PlatformService()