Files
ss-tools/backend/src/models/maintenance.py
busya f49b7d909e feat: GRACE-Poly protocol optimization — ~3200→10 warnings (↓99.7%)
Full optimization cycle:

Protocol (15 files):
- 4-layer SSOT architecture for agent prompts & skills
- Anti-Corruption Protocol consolidated from 5 duplicates
- Tag-to-tier permissiveness matrix (all @tags allowed at all tiers)

Axiom config:
- complexity_rules: all 22+ tags available on C1-C5
- contract_type_overrides: removed (was narrowing per-type)
- 18 new tags added, LAYER enum expanded (Infra, Frontend, Atom, etc.)
- RELATION predicates expanded (USES, CONTAINS, BELONGS_TO, etc.)

Code fixes:
- 2216 @TAG: normalized to @TAG (colon→space)
- 518 [DEF] blocks migrated to #region/#endregion (37 files)
- VERIFIES→BINDS_TO, :Class/:Function suffixes removed, paths→IDs
- 1173-line _external_stubs.py deleted (EXT: handled natively)
- Batch EXT: reference audit (240 targets: 132 external, 99 internal, 9 fix)
- QA regression check: 0 regressions across all checks

Infrastructure:
- DuckDB rebuild stabilized (appender API, INSERT OR IGNORE)
- Anchor regex fix (parent-child BINDS_TO now resolves)
- EXT:*/DTO:/NEED_CONTEXT: regex fixed in validator
- 34MB Doxygen API portal (3194 contract pages)
2026-05-26 11:14:25 +03:00

216 lines
7.4 KiB
Python

# #region MaintenanceModels [C:2] [TYPE Module] [SEMANTICS sqlalchemy, maintenance, banner, event, settings, model]
# @BRIEF SQLAlchemy models for the Maintenance Banner feature: event, banner, dashboard state, and settings.
# @LAYER Domain
# @RELATION DEPENDS_ON -> [EXT:Library:sqlalchemy]
# @RELATION DEPENDS_ON -> [MappingModels]
# @INVARIANT MaintenanceDashboardBanner has unique partial index on (environment_id, dashboard_id) WHERE status='active'
# @INVARIANT MaintenanceSettings is a singleton row (id='default') enforced by CheckConstraint
import enum
import uuid
from sqlalchemy import (
CheckConstraint,
Column,
DateTime,
Enum as SQLEnum,
ForeignKey,
Index,
Integer,
JSON,
String,
Text,
)
from sqlalchemy.orm import relationship
from sqlalchemy.sql import func
from .mapping import Base
# #region MaintenanceEventStatus [C:1] [TYPE Class]
class MaintenanceEventStatus(str, enum.Enum):
PENDING = "pending"
APPLYING = "applying"
ACTIVE = "active"
PARTIAL = "partial"
ENDING = "ending"
COMPLETED = "completed"
FAILED = "failed"
# #endregion MaintenanceEventStatus
# #region MaintenanceDashboardBannerStatus [C:1] [TYPE Class]
class MaintenanceDashboardBannerStatus(str, enum.Enum):
ACTIVE = "active"
REMOVED = "removed"
# #endregion MaintenanceDashboardBannerStatus
# #region MaintenanceDashboardStateStatus [C:1] [TYPE Class]
class MaintenanceDashboardStateStatus(str, enum.Enum):
PENDING_APPLY = "pending_apply"
ACTIVE = "active"
REMOVING = "removing"
REMOVED = "removed"
APPLY_FAILED = "apply_failed"
REMOVAL_FAILED = "removal_failed"
# #endregion MaintenanceDashboardStateStatus
# #region DashboardScope [C:1] [TYPE Class]
class DashboardScope(str, enum.Enum):
PUBLISHED_ONLY = "published_only"
DRAFT_ONLY = "draft_only"
ALL = "all"
# #endregion DashboardScope
# #region MaintenanceEvent [C:1] [TYPE Class]
# @BRIEF A record of a maintenance event with table list, time window, status, and linked dashboard states.
# @RELATION DEPENDS_ON -> [MaintenanceDashboardState]
class MaintenanceEvent(Base):
__tablename__ = "maintenance_events"
id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4()), index=True)
task_id = Column(String, nullable=True, index=True)
environment_id = Column(String, nullable=False)
tables = Column(JSON, nullable=False)
start_time = Column(DateTime(timezone=True), nullable=False)
end_time = Column(DateTime(timezone=True), nullable=True)
message = Column(Text, nullable=True)
status = Column(
SQLEnum(MaintenanceEventStatus),
nullable=False,
default=MaintenanceEventStatus.PENDING,
index=True,
)
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(
DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
)
dashboard_states = relationship(
"MaintenanceDashboardState",
back_populates="maintenance_event",
cascade="all, delete-orphan",
)
# #endregion MaintenanceEvent
# #region MaintenanceDashboardBanner [C:1] [TYPE Class]
# @BRIEF The canonical "one chart per dashboard" entity. Unique partial index enforces single active banner per dashboard.
# @INVARIANT Unique partial index (environment_id, dashboard_id) WHERE status='active'
class MaintenanceDashboardBanner(Base):
__tablename__ = "maintenance_dashboard_banners"
id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4()), index=True)
environment_id = Column(String, nullable=False)
dashboard_id = Column(Integer, nullable=False, index=True)
chart_id = Column(Integer, nullable=True)
banner_text = Column(Text, nullable=True)
status = Column(
SQLEnum(MaintenanceDashboardBannerStatus),
nullable=False,
default=MaintenanceDashboardBannerStatus.ACTIVE,
)
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(
DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
)
dashboard_states = relationship(
"MaintenanceDashboardState", back_populates="banner"
)
__table_args__ = (
Index(
"ix_banner_unique_active",
"environment_id",
"dashboard_id",
unique=True,
postgresql_where=(status == "active"),
),
)
# #endregion MaintenanceDashboardBanner
# #region MaintenanceDashboardState [C:1] [TYPE Class]
# @BRIEF Links a maintenance event to a dashboard via a banner. Tracks per-dashboard-per-event lifecycle.
# @RELATION DEPENDS_ON -> [MaintenanceEvent]
# @RELATION DEPENDS_ON -> [MaintenanceDashboardBanner]
class MaintenanceDashboardState(Base):
__tablename__ = "maintenance_dashboard_states"
id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4()), index=True)
event_id = Column(
String, ForeignKey("maintenance_events.id"), nullable=False, index=True
)
dashboard_id = Column(Integer, nullable=False, index=True)
banner_id = Column(
String,
ForeignKey("maintenance_dashboard_banners.id"),
nullable=True,
)
status = Column(
SQLEnum(MaintenanceDashboardStateStatus),
nullable=False,
default=MaintenanceDashboardStateStatus.PENDING_APPLY,
)
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(
DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
)
maintenance_event = relationship(
"MaintenanceEvent", back_populates="dashboard_states"
)
banner = relationship(
"MaintenanceDashboardBanner", back_populates="dashboard_states"
)
__table_args__ = (
Index("ix_mds_dashboard_status", "dashboard_id", "status"),
)
# #endregion MaintenanceDashboardState
# #region MaintenanceSettings [C:1] [TYPE Class]
# @BRIEF Single-row maintenance mode configuration. Enforces singleton via CheckConstraint(id='default').
# @INVARIANT id must always be 'default' — enforced by CheckConstraint
class MaintenanceSettings(Base):
__tablename__ = "maintenance_settings"
id = Column(String, primary_key=True, default="default")
target_environment_id = Column(String, nullable=False)
display_timezone = Column(String, nullable=False, default="UTC")
banner_template = Column(
Text,
nullable=False,
default=(
'<div style="background:#FFF3E0;padding:16px;border-left:4px solid #FF9800;border-radius:4px">\n\n'
"## ⚠️ Технические работы\n\n"
"{message}\n\n"
"**Начало:** {start_time}\n"
"**Конец:** {end_time}\n\n"
"*Данные могут быть неполными или временно недоступны.*\n\n"
"</div>"
),
)
dashboard_scope = Column(
SQLEnum(DashboardScope),
nullable=False,
default=DashboardScope.PUBLISHED_ONLY,
)
excluded_dashboard_ids = Column(JSON, nullable=False, default=list)
forced_dashboard_ids = Column(JSON, nullable=False, default=list)
updated_at = Column(
DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
)
__table_args__ = (
CheckConstraint("id = 'default'", name="ck_settings_singleton"),
)
# #endregion MaintenanceSettings
# #endregion MaintenanceModels