# app/modules/base.py """ Base module definition class. A Module is a self-contained unit of functionality that can be enabled/disabled per platform. Each module contains: - Features: Granular capabilities for tier-based access control - Menu items: Sidebar entries per frontend type - Routes: API and page routes (future: dynamically registered) """ from dataclasses import dataclass, field from typing import TYPE_CHECKING if TYPE_CHECKING: from fastapi import APIRouter from models.database.admin_menu_config import FrontendType @dataclass class ModuleDefinition: """ Definition of a platform module. A module groups related functionality that can be enabled/disabled per platform. Core modules cannot be disabled and are always available. Attributes: code: Unique identifier (e.g., "billing", "marketplace") name: Display name (e.g., "Billing & Subscriptions") description: Description of what this module provides requires: List of module codes this module depends on features: List of feature codes this module provides menu_items: Dict mapping FrontendType to list of menu item IDs is_core: Core modules cannot be disabled admin_router: FastAPI router for admin routes (future) vendor_router: FastAPI router for vendor routes (future) Example: billing_module = ModuleDefinition( code="billing", name="Billing & Subscriptions", description="Subscription tiers, billing history, and payment processing", features=["subscription_management", "billing_history", "stripe_integration"], menu_items={ FrontendType.ADMIN: ["subscription-tiers", "subscriptions", "billing-history"], FrontendType.VENDOR: ["billing"], }, ) """ # Identity code: str name: str description: str = "" # Dependencies requires: list[str] = field(default_factory=list) # Components features: list[str] = field(default_factory=list) menu_items: dict[FrontendType, list[str]] = field(default_factory=dict) # Status is_core: bool = False # Routes (registered dynamically) - Future implementation admin_router: "APIRouter | None" = None vendor_router: "APIRouter | None" = None def get_menu_items(self, frontend_type: FrontendType) -> list[str]: """Get menu item IDs for a specific frontend type.""" return self.menu_items.get(frontend_type, []) def get_all_menu_items(self) -> set[str]: """Get all menu item IDs across all frontend types.""" all_items = set() for items in self.menu_items.values(): all_items.update(items) return all_items def has_feature(self, feature_code: str) -> bool: """Check if this module provides a specific feature.""" return feature_code in self.features def has_menu_item(self, menu_item_id: str) -> bool: """Check if this module provides a specific menu item.""" return menu_item_id in self.get_all_menu_items() def check_dependencies(self, enabled_modules: set[str]) -> list[str]: """ Check if all required modules are enabled. Args: enabled_modules: Set of enabled module codes Returns: List of missing required module codes """ return [req for req in self.requires if req not in enabled_modules] def __hash__(self) -> int: return hash(self.code) def __eq__(self, other: object) -> bool: if isinstance(other, ModuleDefinition): return self.code == other.code return False def __repr__(self) -> str: return f""