Загрузка данных


"""
Датаклассы для конфигурации тестовых наборов.

Архитектура:
- TestSuiteConfig - главный конфиг набора, содержит всё для запуска тестов
- LeakTestConfig - конфиг утечки с параметрами и тест-кейсами
- TestCaseMarkers - маркеры для allure и pytest

Принцип: один файл конфига select_xx.py в папке datasets = один набор данных.
"""

from dataclasses import asdict, dataclass, field
from typing import Any, Dict, Optional

from constants.enums import (
    TU,
    ConfirmationStatus,
    LdsStatus,
    RejectionCriteria,
    RejectionSensorTag,
    ReservedType,
    StationaryStatus,
)
from constants.test_constants import BaseTN3Constants
from models.subscribe_main_page_signals_info_model import SignalsInfo


@dataclass
class BaseSuiteConfig:
    """
    Структура:
    1. Метаданные набора (имя, id, архив)
    2. Технологический участок (из enum TU)
    """

    # ===== Метаданные набора =====
    suite_name: str
    suite_data_id: int
    archive_name: str = ""  # Автоматически вычисляется из suite_name

    # ===== Технологический участок =====
    technological_unit: TU = TU.TIKHORETSK_NOVOROSSIYSK_3

    # ===== Общие константы (можно переопределить) =====
    allowed_distance_diff_meters: int = BaseTN3Constants.ALLOWED_DISTANCE_DIFF_METERS
    precision: int = BaseTN3Constants.PRECISION
    basic_message_timeout: float = BaseTN3Constants.BASIC_MESSAGE_TIMEOUT
    mask_message_timeout: float = BaseTN3Constants.MASK_MESSAGE_TIMEOUT
    mask_du_name: Optional[str] = None
    main_pipe_line: Optional[str] = None
    mask_du_event: Optional[str] = None
    unmask_du_event: Optional[str] = None

    # ===== Свойства для удобства =====
    @property
    def tu_id(self) -> int:
        """ID технологического участка"""
        return self.technological_unit.id

    @property
    def tu_name(self) -> str:
        """Название технологического участка"""
        return self.technological_unit.description

    @property
    def has_multiple_leaks(self) -> bool:
        return False


@dataclass
class CaseData:
    """
    Данные тест-кейса.
    """

    name: str = ""
    params: Optional[Dict[str, Any]] = None
    expected_result: Optional[Any] = None
    description: str = ""


@dataclass
class CaseMarkers:
    """
    Маркеры тест-кейса для pytest и allure.
    """

    test_case_id: str
    offset: float


@dataclass
class DiagnosticAreaStatusConfig:
    """
    Конфигурация ожидаемых статусов СОУ для диагностического участка.
    Используется в тесте lds_status_during_leak.
    """

    leak_diagnostic_area_id: int
    leak_du_expected_lds_status: int
    leak_diagnostic_area_pipe_id: Optional[int] = None
    # Соседние ДУ и их статусы: словари {diagnostic_area_pipe_id: leak_du_expected_lds_status}
    # Позволяет указывать 0..N соседей независимо от in/out.
    #
    # Пример:
    #   in_neighbors={1: LdsStatus.DEGRADATION.value}
    #   out_neighbors={3: LdsStatus.DEGRADATION.value, 4: LdsStatus.DEGRADATION.value}
    in_neighbors: dict[int, int] = field(default_factory=dict)
    out_neighbors: dict[int, int] = field(default_factory=dict)


@dataclass
class LeakTestConfig:
    """
    Полная конфигурация утечки со всеми параметрами и тест-кейсами.

    Все данные для тестов утечки:
    - Параметры утечки (координата, объём)
    - Временные интервалы
    - Ожидаемые значения
    - Маркеры тестов (AllLeaksInfo, TuLeaksInfo, и т.д.)
    """

    # ===== Идентификаторы =====
    control_site_id: Optional[int] = None
    diagnostic_area_id: Optional[int] = None
    diagnostic_area_name: Optional[str] = None
    linear_part_id: Optional[int] = None
    technological_object: Optional[str] = None
    message_event_leak_completion: Optional[str] = None

    # ===== Параметры утечки =====
    coordinate_meters: float = None
    volume_m3: float = None
    max_pumping_m3: int = 2500  # Производительность(максимальная перекачка)
    flow_rate_settings_threshold: Optional[float] = None  # Порог объема дебаланса для текущего ДУ в текущем режиме

    # ===== Временные интервалы (секунды) =====
    leak_start_interval_seconds: int = BaseTN3Constants.LEAK_START_INTERVAL
    allowed_time_diff_seconds: int = 0  # Допустимое время обнаружения
    output_test_delay_seconds: int = BaseTN3Constants.OUTPUT_TEST_DELAY

    # ===== Ожидаемые статусы =====
    expected_lds_status: int = LdsStatus.SERVICEABLE.value
    expected_stationary_status: int = StationaryStatus.UNSTATIONARY.value
    expected_algorithm_type: int = ReservedType.UNSTATIONARY_FLOW.value
    expected_leak_status: int = ConfirmationStatus.CONFIRMED.value
    expected_complete_leak_status: int = ConfirmationStatus.CONFIRMED_AND_LEAK_CLOSED.value

    # ===== Конфигурация статусов СОУ во время утечки =====
    lds_status_during_leak_config: Optional[DiagnosticAreaStatusConfig] = None

    # ===== Тест-кейсы для этой утечки =====
    balance_algorithm_leak_waiting_test: Optional[CaseMarkers] = None
    balance_algorithm_leak_detected_test: Optional[CaseMarkers] = None
    leaks_content_test: Optional[CaseMarkers] = None
    all_leaks_info_test: Optional[CaseMarkers] = None
    all_leaks_is_empty_test: Optional[CaseMarkers] = None
    tu_leaks_info_test: Optional[CaseMarkers] = None
    leak_info_in_journal: Optional[CaseMarkers] = None
    possible_leak_in_journal_test: Optional[CaseMarkers] = None
    acknowledge_leak_test: Optional[CaseMarkers] = None
    acknowledge_leak_in_journal_test: Optional[CaseMarkers] = None
    output_signals_test: Optional[CaseMarkers] = None
    lds_status_during_leak_test: Optional[CaseMarkers] = None
    the_leak_is_complete_on_kg_test: Optional[CaseMarkers] = None
    leak_is_complete_in_output_signals_test: Optional[CaseMarkers] = None
    leak_is_complete_on_main_page_test: Optional[CaseMarkers] = None
    leak_is_confirm_on_main_page_test: Optional[CaseMarkers] = None
    complete_tu_leaks_info_content_test: Optional[CaseMarkers] = None
    completed_leak_info_in_journal_test: Optional[CaseMarkers] = None
    balance_algorithm_leak_completed_test: Optional[CaseMarkers] = None

    @property
    def leak_diagnostic_area_id(self) -> Optional[int]:
        """ID диагностического участка с утечкой из lds_status_during_leak_config"""
        if self.lds_status_during_leak_config is not None:
            return self.lds_status_during_leak_config.leak_diagnostic_area_id
        return None

    @property
    def allowed_volume_m3(self) -> float:
        """Допустимая погрешность объёма"""
        return self.volume_m3 * BaseTN3Constants.ALLOWED_VOLUME_DIFF

    @property
    def leak_rate_percentages(self) -> float:
        """Интенсивность утечки в процентах"""
        return round((self.volume_m3 / self.max_pumping_m3) * 100, 2)

    @property
    def allowed_time_diff_minutes(self) -> float:
        """Допустимое время обнаружения утечки в минутах"""
        return round(self.allowed_time_diff_seconds / 60, 2)

    @property
    def output_allowed_time_diff_seconds(self) -> int:
        """Допустимое время для теста выходных сигналов"""
        return self.allowed_time_diff_seconds + self.output_test_delay_seconds


@dataclass
class SmokeSuiteConfig(BaseSuiteConfig):
    """
    Полная конфигурация тестового набора.

    Один конфиг = один набор данных = один файл в test_config/datasets/

    Структура:
    1. Базовые тесты с маркерами
    2. Конфигурации утечек (LeakTestConfig)
    """

    # ===== Ожидаемый статусы для main_page_info =====
    expected_stationary_status: int = StationaryStatus.STATIONARY.value
    expected_main_page_signals: dict = field(default_factory=lambda: asdict(SignalsInfo()))

    # ===== Название Магистрального Нефтепровода =====
    main_pipeline: Optional[str] = None

    # ===== Ожидаемые переменные при маскировании ДУ =====
    mask_reason: Optional[str] = None
    unmask_reason: Optional[str] = None
    mask_one_du: Optional[int] = None
    not_mask_du: Optional[int] = None
    linear_part_identifier_for_mask: Optional[int] = None
    technological_section: Optional[str] = None
    imitate_flowmeter_signal_test_data: Optional[CaseData] = None
    imitate_pressure_senor_signal_test_data: Optional[CaseData] = None
    lds_status_after_confirming_leak_data: Optional[CaseData] = None
    lds_status_after_completed_leak_data: Optional[CaseData] = None

    # ===== Базовые тесты =====
    basic_info_test: Optional[CaseMarkers] = None
    imitate_flowmeter_signal_test: Optional[CaseMarkers] = None
    imitate_pressure_sensor_signal_test: Optional[CaseMarkers] = None
    journal_info_test: Optional[CaseMarkers] = None
    lds_status_initialization_test: Optional[CaseMarkers] = None
    lds_status_init_in_journal_test: Optional[CaseMarkers] = None
    main_page_info_test: Optional[CaseMarkers] = None
    main_page_info_signals_test: Optional[CaseMarkers] = None
    mask_signal_test: Optional[CaseMarkers] = None
    mask_info_in_journal_test: Optional[CaseMarkers] = None
    lds_status_initialization_out_test: Optional[CaseMarkers] = None
    lds_status_init_out_in_journal_test: Optional[CaseMarkers] = None
    mask_du_on_mini_scheme_test: Optional[CaseMarkers] = None
    unmask_du_on_mini_scheme_test: Optional[CaseMarkers] = None
    lds_status_after_confirming_leak_test: Optional[CaseMarkers] = None
    lds_status_completed_leak_test: Optional[CaseMarkers] = None

    # ===== Конфигурации утечек =====
    # Для наборов с одной утечкой
    leak: Optional[LeakTestConfig] = None

    # Для наборов с несколькими утечками (select_19_20)
    leaks: list[LeakTestConfig] = field(default_factory=list)

    # ===== Дополнительные тесты для двух утечек =====
    main_page_info_unstationary_test: Optional[CaseMarkers] = None

    def get_leak(self, index: int = 0) -> Optional[LeakTestConfig]:
        """Получить конфигурацию утечки по индексу"""
        if self.leak and index == 0:
            return self.leak
        if self.leaks and index < len(self.leaks):
            return self.leaks[index]
        return None

    @property
    def has_multiple_leaks(self) -> bool:
        """Проверить, есть ли несколько утечек"""
        return len(self.leaks) > 1

    @property
    def allowed_volume_diff(self) -> float:
        """Относительная погрешность по объёму"""
        return BaseTN3Constants.ALLOWED_VOLUME_DIFF


@dataclass
class LDSStatusConfig(BaseSuiteConfig):
    """
    Полная конфигурация тестового набора.

    Один конфиг = один набор данных = один файл в test_config/datasets/

    Структура:
    1. Данные для тестов(параметры и ожидаемый результат)
    2. Тесты с маркерами
    """

    # ===== Данные для тестов =====
    lds_status_init_accumulation_data_test_data: Optional[CaseData] = None
    lds_status_init_cold_start_test_data: Optional[CaseData] = None
    lds_status_init_exiting_faulty_test_data: Optional[CaseData] = None
    lds_status_init_switching_shut_off_test_data: Optional[CaseData] = None
    lds_status_serviceable_all_test_data: Optional[CaseData] = None
    lds_status_serviceable_after_switching_shut_off_test_data: Optional[CaseData] = None
    lds_status_serviceable_after_deg_faulty_pressure_sensors_at_pump_test_data: Optional[CaseData] = None
    lds_status_deg_faulty_pressure_sensors_at_pump_station_test_data: Optional[CaseData] = None
    lds_status_deg_additive_injectors_operation_test_data: Optional[CaseData] = None
    lds_status_deg_absence_min_pressure_sensors_test_data: Optional[CaseData] = None
    lds_status_deg_exceeding_distance_between_pressure_sensors_test_data: Optional[CaseData] = None
    lds_status_deg_gravity_section_pumping_test_data: Optional[CaseData] = None
    lds_status_deg_gravity_section_pumping_in_stopping_test_data: Optional[CaseData] = None
    lds_status_deg_pig_sensor_passage_test_data: Optional[CaseData] = None
    lds_status_deg_starting_pumping_out_pumps_test_data: Optional[CaseData] = None
    lds_status_deg_exceeding_distance_between_flow_meters_test_data: Optional[CaseData] = None
    lds_status_deg_rejection_temperature_sensor_on_du_2_test_data: Optional[CaseData] = None
    lds_status_deg_rejection_temperature_sensor_on_du_3_test_data: Optional[CaseData] = None
    lds_status_deg_rejection_temperature_sensor_on_du_5_test_data: Optional[CaseData] = None
    lds_status_deg_rejection_density_and_viscosity_on_du_2_test_data: Optional[CaseData] = None
    lds_status_deg_rejection_density_and_viscosity_on_du_3_test_data: Optional[CaseData] = None
    lds_status_deg_rejection_density_and_viscosity_on_du_5_test_data: Optional[CaseData] = None
    lds_status_faulty_absence_min_flow_meters_test_data: Optional[CaseData] = None
    lds_status_faulty_absence_min_pressure_sensors_test_data: Optional[CaseData] = None
    # ===== Тесты =====
    lds_status_basic_info_test: Optional[CaseMarkers] = None
    lds_status_init_accumulation_data_test: Optional[CaseMarkers] = None
    lds_status_init_cold_start_test: Optional[CaseMarkers] = None
    lds_status_init_exiting_faulty_test: Optional[CaseMarkers] = None
    lds_status_init_switching_shut_off_test: Optional[CaseMarkers] = None
    lds_status_serviceable_after_cold_start_test: Optional[CaseMarkers] = None
    lds_status_serviceable_after_switching_shut_off_test: Optional[CaseMarkers] = None
    lds_status_serviceable_after_deg_absence_min_pressure_sensors_test: Optional[CaseMarkers] = None
    lds_status_serviceable_after_deg_starting_pumping_out_pumps_test: Optional[CaseMarkers] = None
    lds_status_serviceable_after_deg_faulty_pressure_sensors_at_pump_test: Optional[CaseMarkers] = None
    lds_status_serviceable_after_faulty_test: Optional[CaseMarkers] = None
    lds_status_deg_additive_injectors_operation_test: Optional[CaseMarkers] = None
    lds_status_deg_exceeding_distance_between_pressure_sensors_test: Optional[CaseMarkers] = None
    lds_status_deg_absence_min_pressure_sensors_test: Optional[CaseMarkers] = None
    lds_status_deg_faulty_pressure_sensors_at_pump_station_test: Optional[CaseMarkers] = None
    lds_status_deg_gravity_section_pumping_test: Optional[CaseMarkers] = None
    lds_status_deg_gravity_section_pumping_in_stopping_test: Optional[CaseMarkers] = None
    lds_status_deg_pig_sensor_passage_test: Optional[CaseMarkers] = None
    lds_status_deg_starting_pumping_out_pumps_test: Optional[CaseMarkers] = None
    lds_status_deg_exceeding_distance_between_flow_meters_test: Optional[CaseMarkers] = None
    lds_status_deg_rejection_temperature_sensor_on_du_2_test: Optional[CaseMarkers] = None
    lds_status_deg_rejection_temperature_sensor_on_du_3_test: Optional[CaseMarkers] = None
    lds_status_deg_rejection_temperature_sensor_on_du_5_test: Optional[CaseMarkers] = None
    lds_status_deg_rejection_density_and_viscosity_on_du_2_test: Optional[CaseMarkers] = None
    lds_status_deg_rejection_density_and_viscosity_on_du_3_test: Optional[CaseMarkers] = None
    lds_status_deg_rejection_density_and_viscosity_on_du_5_test: Optional[CaseMarkers] = None
    lds_status_faulty_absence_min_flow_meters_test: Optional[CaseMarkers] = None
    lds_status_faulty_absence_min_pressure_sensors_test: Optional[CaseMarkers] = None


@dataclass
class RejectionTestCase:
    """
    Описание одного события отбраковки для тестирования.

    Содержит:
    - Тег и id датчика (из RejectionSensorTag)
    - Ожидаемые значения для проверок журнала и схемы
    - Маркеры (offset и test_case_id)
    """

    name: str = ""
    sensor: RejectionSensorTag = ""
    expected_event: str = ""
    expected_signal_name: str = ""
    expected_criteria_names: RejectionCriteria = RejectionCriteria(0)
    time_range_start_s: float = 0
    time_range_end_s: float = 0
    rejection_input_signals_test: Optional[CaseMarkers] = None
    rejection_journal_test: Optional[CaseMarkers] = None
    rejection_main_page_test: Optional[CaseMarkers] = None
    rejection_scheme_signals_state_test: Optional[CaseMarkers] = None


@dataclass
class IsRejectedConfig(BaseSuiteConfig):
    """
    Конфигурация тестового набора отбраковки сигналов.

    Структура:
    1. Название МН
    2. Список случаев отбраковки (RejectionTestCase) - по 4 теста на каждый
    """

    main_pipeline: str = ""
    rejection_cases: list[RejectionTestCase] = field(default_factory=list)