Files
qt6-migration-pipeline/app/migration_pipeline/models.py
T

152 lines
4.5 KiB
Python

"""Data models for the migration pipeline."""
from __future__ import annotations
from dataclasses import dataclass, field
from enum import Enum
from typing import Optional
# ---------------------------------------------------------------------------
# Enumerations
# ---------------------------------------------------------------------------
class Track(str, Enum):
"""Migration track."""
A = "track_a_conservative" # Qt5→Qt6 compatibility only
B = "track_b_modern" # Qt6 + C++20 modernisation
class FileStatus(str, Enum):
PENDING = "pending"
IN_PROGRESS = "in_progress"
SUCCESS = "success"
QUARANTINED = "quarantined"
class FailureClass(str, Enum):
COMPILE_ERROR = "compile_error"
LINK_ERROR = "link_error"
TEST_FAILURE = "test_failure"
TEST_BUILD_FAILURE = "test_build_failure"
BUDGET_EXCEEDED = "budget_exceeded"
API_ERROR = "api_error"
CHUNK_MERGE_ERROR = "chunk_merge_error"
UNKNOWN = "unknown"
# ---------------------------------------------------------------------------
# Chunk
# ---------------------------------------------------------------------------
@dataclass
class CodeChunk:
"""A contiguous slice of a C++ source file."""
index: int
start_line: int # 0-based, inclusive
end_line: int # 0-based, exclusive
content: str
boundary_type: str # "complete" | "semantic" | "fallback"
@property
def line_count(self) -> int:
return self.end_line - self.start_line
# ---------------------------------------------------------------------------
# Quality gates
# ---------------------------------------------------------------------------
@dataclass
class QualityGateResult:
"""Result of all quality gates for one migration attempt."""
build_ok: bool = False
build_errors: str = ""
build_warnings: list[str] = field(default_factory=list)
existing_tests_ok: bool = False
new_tests_ok: bool = False
test_output: str = ""
test_warnings: list[str] = field(default_factory=list)
@property
def all_gates_passed(self) -> bool:
"""Hard gates: build green, all tests green.
Warnings are advisory and do NOT block (config.WARNINGS_ARE_BLOCKING=False)."""
from migration_pipeline.config import WARNINGS_ARE_BLOCKING
if WARNINGS_ARE_BLOCKING:
return (
self.build_ok
and self.existing_tests_ok
and self.new_tests_ok
and not self.build_warnings
and not self.test_warnings
)
return self.build_ok and self.existing_tests_ok and self.new_tests_ok
@property
def total_warnings(self) -> int:
return len(self.build_warnings) + len(self.test_warnings)
# ---------------------------------------------------------------------------
# Quarantine
# ---------------------------------------------------------------------------
@dataclass
class QuarantineEntry:
"""Rich metadata for a file that could not be migrated automatically."""
file_path: str
track: str
failure_class: str
last_error: str # truncated at 2000 chars
affected_apis: list[str]
chunk_info: str
manual_recommendation: str
timestamp: str # ISO-8601 UTC
retry_count: int
elapsed_seconds: float
# ---------------------------------------------------------------------------
# Per-file result
# ---------------------------------------------------------------------------
@dataclass
class FileResult:
file_path: str
track: str
status: str # FileStatus value
iterations: int
elapsed_seconds: float
gate: QualityGateResult
quarantine_entry: Optional[QuarantineEntry] = None
migrated_path: str = ""
test_name: str = ""
# ---------------------------------------------------------------------------
# Batch result
# ---------------------------------------------------------------------------
@dataclass
class BatchResult:
"""Aggregated result for one batch + one track."""
batch_id: str
track: str
branch: str
total_files: int
success_count: int = 0
quarantine_count: int = 0
total_warnings: int = 0
mean_elapsed_seconds: float = 0.0
results: list[FileResult] = field(default_factory=list)
@property
def success_rate(self) -> float:
return self.success_count / self.total_files if self.total_files else 0.0
@property
def quarantine_rate(self) -> float:
return self.quarantine_count / self.total_files if self.total_files else 0.0