Trans_for_doctors / INTEGRATION_GUIDE.md
Mintik24's picture
🎉 Полный рефакторинг проекта Medical Transcriber
e275025

Гайд по интеграции рефакторинга

🚀 Быстрый старт

После рефакторинга были созданы новые модули в папке common/. Ниже показано, как их использовать.

📋 Содержание

  1. Константы вместо магических чисел
  2. Логирование
  3. Валидация данных
  4. Типизированные структуры
  5. Обработка ошибок

Константы

Прежде всего обновите импорты

# app/gui_app.py
from common import (
    UIColors,
    UIDimensions,
    Messages,
    FontConfig,
    AudioFormats,
    get_logger
)

logger = get_logger(__name__)

UI размеры

# ДО
self.setGeometry(100, 100, 1200, 800)

# ПОСЛЕ
self.setGeometry(
    100, 100,
    UIDimensions.MAIN_WINDOW_WIDTH,
    UIDimensions.MAIN_WINDOW_HEIGHT
)

UI цвета

# ДО
self.start_btn.setStyleSheet("""
    QPushButton {
        background-color: #4CAF50;
        color: white;
    }
    QPushButton:hover {
        background-color: #45a049;
    }
""")

# ПОСЛЕ
self.start_btn.setStyleSheet(f"""
    QPushButton {{
        background-color: {UIColors.PRIMARY_GREEN};
        color: white;
    }}
    QPushButton:hover {{
        background-color: {UIColors.HOVER_GREEN};
    }}
""")

Текстовые сообщения

# ДО
if not self.audio_path:
    QMessageBox.warning(
        self,
        "Ошибка",
        "Пожалуйста, выберите аудиофайл!"
    )
    return

# ПОСЛЕ
if not self.audio_path:
    QMessageBox.warning(
        self,
        Messages.WARNING_TITLE,
        Messages.ERROR_NO_AUDIO_FILE
    )
    return

Диалоги выбора файлов

# ДО
file_path, _ = QFileDialog.getOpenFileName(
    self,
    "Выберите аудиофайл",
    "",
    "Audio Files (*.wav *.mp3 *.m4a);;All Files (*)"
)

# ПОСЛЕ
file_path, _ = QFileDialog.getOpenFileName(
    self,
    "Выберите аудиофайл",
    "",
    AudioFormats.FILE_DIALOG_FILTER
)

Логирование

Инициализация (в main.py или run_gui.py)

from common import configure_logging, get_logger

if __name__ == "__main__":
    # Один раз в начале программы
    configure_logging()  # Создаст папку logs/ и файл логов
    
    app = QApplication(sys.argv)
    window = MedicalTranscriptionApp()
    window.show()
    sys.exit(app.exec())

Использование в модулях

# В каждом файле
from common import get_logger

logger = get_logger(__name__)

def my_function():
    logger.info("Starting operation")
    try:
        # ...
        logger.debug("Processing step 1")
    except Exception as e:
        logger.error(f"Error occurred: {e}", exc_info=True)

Удалите старый код логирования

# ДО (удалить)
import logging
logger = logging.getLogger(__name__)
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s'
)

# ПОСЛЕ (достаточно)
from common import get_logger
logger = get_logger(__name__)

Валидация

Валидация аудиофайлов

from common import Validator, AudioFileException
from pathlib import Path

def start_transcription(self):
    try:
        # Валидирует файл, проверяет существование, формат и размер
        audio_file = Validator.validate_audio_file(self.audio_path)
        # audio_file является объектом Path
        
    except AudioFileException as e:
        QMessageBox.critical(self, "Ошибка аудиофайла", e.message)

Валидация пациента

from common import Validator, ValidationException

def open_patient_dialog(self):
    dialog = PatientDataDialog(self)
    if dialog.exec() == QDialog.DialogCode.Accepted:
        try:
            data = dialog.get_data()
            
            # Валидация каждого поля
            patient_name = Validator.validate_patient_name(data["patient_name"])
            patient_dob = Validator.validate_date(data["patient_dob"])
            
            self.patient_data = data
            
        except ValidationException as e:
            QMessageBox.warning(
                self,
                f"Ошибка в поле {e.field}",
                e.message
            )

Валидация текста

from common import Validator, ValidationException

def correct_text(text):
    try:
        validated_text = Validator.validate_text(text, "transcription")
        # Дальше работаем с проверенным текстом
        
    except ValidationException as e:
        logger.error(f"Validation error: {e.message}")

Структуры данных

Использование типизированных результатов

from common import PipelineResult, TranscriptionResult, PatientMetadata
from datetime import datetime
from pathlib import Path

def process_pipeline():
    # Создание структурированного результата
    result = PipelineResult(
        timestamp=datetime.now(),
        audio_file=Path("audio.wav"),
        patient_data=PatientMetadata(
            name="Иванов Иван Иванович",
            date_of_birth="01.01.1980",
            study_area="МРТ головы"
        ),
        transcription=TranscriptionResult(
            timestamp=datetime.now(),
            audio_file=Path("audio.wav"),
            original_text="исходный текст",
            corrected_text="исправленный текст",
            corrections_count=5
        ),
        status="success"
    )
    
    # IDE будет подсказывать все доступные поля!
    print(result.status)
    print(result.transcription.corrections_count)
    print(result.is_successful())  # Вспомогательный метод
    
    # Сериализация в JSON
    result_dict = result.to_dict()
    json.dump(result_dict, f)

Создание метаданных пациента

from common import PatientMetadata

patient_data = PatientMetadata(
    name="Петров Петр Петрович",
    date_of_birth="15.03.1975",
    study_area="КТ грудной клетки",
    study_number="12345",
    study_date="16.01.2026",
    doctor_name="Сидоров С.С."
)

# Проверка полноты данных
if patient_data.is_complete():
    print("Все необходимые данные заполнены")

# Преобразование в словарь
patient_dict = patient_data.to_dict()

Обработка ошибок

Специфичные исключения

from common import (
    AudioFileException,
    TranscriptionException,
    APIException,
    ValidationException,
    ConfigurationException
)

def pipeline_process():
    try:
        # ...
        pass
        
    except AudioFileException as e:
        # Обработка ошибок с аудио файлом
        logger.error(f"Audio file error: {e.message}")
        show_error_dialog(f"Ошибка аудиофайла: {e.file_path}")
        
    except APIException as e:
        # Обработка ошибок API
        logger.error(f"API error: {e.message} (code: {e.status_code})")
        show_error_dialog(f"Ошибка API: {e.status_code}")
        
    except ValidationException as e:
        # Обработка ошибок валидации
        logger.warning(f"Validation error in {e.field}: {e.message}")
        show_warning_dialog(f"Проверьте поле '{e.field}'")
        
    except ConfigurationException as e:
        # Обработка ошибок конфигурации
        logger.error(f"Config error: {e}")
        show_error_dialog("Неверная конфигурация")

Информативные ошибки с контекстом

# ДО
except Exception as e:
    logger.error(f"Error: {e}")
    # Непонятно, что произошло

# ПОСЛЕ
except APIException as e:
    logger.error(
        f"API request failed for {e.endpoint} "
        f"with status {e.status_code}: {e.message}"
    )
    # Точно известно, что произошло, где и почему

Шаблон для новых модулей

При создании нового модуля используйте этот шаблон:

"""
Описание модуля.

Example:
    >>> from my_module import MyClass
    >>> obj = MyClass()
    >>> result = obj.my_method()
"""

from pathlib import Path
from typing import Optional, Dict, Any
from common import get_logger, Validator, ValidationException

logger = get_logger(__name__)


class MyClass:
    """Описание класса."""
    
    def __init__(self, param: str) -> None:
        """
        Initialize.
        
        Args:
            param: Parameter description
            
        Raises:
            ValueError: If param is invalid
        """
        self.param = param
        logger.info(f"Initialized MyClass with param: {param}")
    
    def my_method(self, data: str) -> Dict[str, Any]:
        """
        Do something.
        
        Args:
            data: Input data
            
        Returns:
            Result dictionary
            
        Raises:
            ValidationException: If data is invalid
        """
        try:
            validated_data = Validator.validate_text(data)
            logger.debug(f"Processing {len(validated_data)} characters")
            
            result = {"status": "success", "data": validated_data}
            logger.info("Processing completed successfully")
            return result
            
        except ValidationException as e:
            logger.error(f"Validation failed: {e.message}")
            raise

Чек-лист для интеграции

Phase 1: Основные импорты

  • common/exceptions.py создан ✅
  • common/constants.py создан ✅
  • common/logger.py создан ✅
  • common/validators.py создан ✅
  • common/models.py создан ✅
  • common/__init__.py создан ✅

Phase 2: Обновление импортов в app/

  • app/gui_app.py - добавить импорты common
  • app/main.py - вызвать configure_logging()
  • Заменить все магические числа на константы
  • Заменить все Exception на специфичные типы

Phase 3: Обновление импортов в pipeline/

  • pipeline/medical_pipeline.py - использовать новые структуры
  • pipeline/pipeline_config.py - использовать константы

Phase 4: Обновление импортов в corrector/

  • corrector/llm_corrector.py - улучшить типизацию ✅
  • corrector/openrouter_client.py - использовать APISettings ✅
  • corrector/report_generator.py - использовать ReportDefaults

Phase 5: Обновление импортов в stt/

  • stt/whisper_transcriber.py - использовать ModelDefaults
  • stt/audio_processor.py - использовать AudioFormats

Phase 6: Обновление импортов в knowledge_base/

  • knowledge_base/term_manager.py - использовать новые структуры

Полезные ссылки в коде

# Все константы
from common import UIColors, UIDimensions, Messages, etc.

# Логирование
from common import get_logger, configure_logging

# Валидация
from common import Validator

# Структуры данных
from common import PatientMetadata, PipelineResult, etc.

# Исключения
from common import (
    AudioFileException,
    ValidationException,
    APIException,
    etc.
)

Итого

  1. Константы - используйте вместо магических чисел
  2. Логирование - вызовите configure_logging() в main, затем get_logger()
  3. Валидация - используйте Validator.validate_*()
  4. Структуры - создавайте с типизацией вместо dict
  5. Ошибки - ловите специфичные исключения

Это сделает код более читаемым, надёжным и поддерживаемым! 🎉