diff --git a/doc/model_equation_implementation.md b/doc/model_equation_implementation.md deleted file mode 100644 index 7904c66..0000000 --- a/doc/model_equation_implementation.md +++ /dev/null @@ -1,78 +0,0 @@ -# Implementação da Equação do Modelo como Artefato JSON - -## Visão Geral - -Esta implementação adiciona a capacidade de extrair e salvar a equação do modelo de regressão linear como um artefato JSON, seguindo a arquitetura existente do projeto. - -## Mudanças Implementadas - -### 1. TrainModelResult -- **Arquivo**: `model_manager/utils/models/train_model_result.py` -- **Mudanças**: - - Adicionado campo `equation: dict | None = None` para armazenar os metadados da equação - - Adicionado campo `equation_path: str | None = None` para armazenar o caminho do arquivo JSON - -### 2. TrainingRepository -- **Arquivo**: `model_manager/utils/repository/training_repository.py` -- **Mudanças**: - - Adicionado método `_extract_model_equation()` para extrair coeficientes e intercept do modelo - - Integrado a extração da equação no método `after_train_calculation()` - -### 3. ModelRepository -- **Arquivo**: `model_manager/utils/repository/model_repository.py` -- **Mudanças**: - - Adicionado import do módulo `json` - - Modificado `_generate_report()` para salvar a equação como arquivo JSON - - Modificado `_save_run()` para fazer log do artefato da equação no MLflow - -## Estrutura do JSON da Equação - -O arquivo `model_equation.json` terá a seguinte estrutura: - -```json -{ - "target_variable": "target_column_name", - "coefficients": { - "feature1": 0.123456, - "feature2": -0.789012, - "feature3": 0.345678 - }, - "intercept": 1.234567, - "equation_string": "target_column_name = 1.234567 + 0.123456 * feature1 + -0.789012 * feature2 + 0.345678 * feature3", - "latex_equation": "target_column_name = 1.234567 + 0.123456 \\cdot feature1 + -0.789012 \\cdot feature2 + 0.345678 \\cdot feature3", - "model_type": "Linear Regression" -} -``` - -## Fluxo de Execução - -1. **Treinamento**: O modelo é treinado no `TrainingRepository.train()` -2. **Pós-treinamento**: O método `after_train_calculation()` é chamado, que: - - Calcula as métricas (MSE, MAE, R²) - - Extrai a equação usando `_extract_model_equation()` -3. **Salvamento**: O `ModelRepository.save_model()` é chamado, que: - - Gera os artefatos (relatórios, dados CSV) - - Salva a equação como `model_equation.json` - - Faz log de todos os artefatos no MLflow - -## Benefícios - -- **Rastreabilidade**: A equação fica disponível como artefato versionado no MLflow -- **Transparência**: Fácil acesso aos coeficientes e estrutura do modelo -- **Compatibilidade**: Formato JSON facilita integração com outras ferramentas -- **Flexibilidade**: Inclui tanto formato legível quanto LaTeX para diferentes usos - -## Compatibilidade - -Esta implementação é totalmente compatível com: -- A arquitetura existente do projeto -- O fluxo de treinamento atual -- O sistema de logging do MLflow -- Os testes existentes (não quebra funcionalidades) - -## Exemplo de Uso - -Após o treinamento, a equação estará disponível em: -- **Memória**: `train_result.equation` (dicionário Python) -- **Arquivo**: `train_result.equation_path` (caminho para o JSON) -- **MLflow**: Como artefato `model_equation.json` no run do experimento diff --git a/tests/utils/repository/test_model_repository.py b/tests/utils/repository/test_model_repository.py index 72b18d9..cbc25bc 100644 --- a/tests/utils/repository/test_model_repository.py +++ b/tests/utils/repository/test_model_repository.py @@ -802,3 +802,48 @@ def test_generate_artifacts_header_not_found( with pytest.raises(FileNotFoundError, match='Header file does not exist'): repo._generate_artifacts(mock_train_result) + + +@patch('model_manager.utils.repository.model_repository.ModelServing') +@patch('model_manager.utils.repository.model_repository.path.exists') +@patch('model_manager.utils.repository.model_repository.path.join') +def test_generate_artifacts_success( + mock_join, mock_exists, mock_model_serving_class, mock_logger, mock_train_result +): + """Test _generate_artifacts success case covering lines 147-148.""" + from model_manager.utils.repository.model_repository import ModelRepository + + repo = ModelRepository( + url='http://mlflow.test', username='user', password='pass', logger=mock_logger + ) + + # Mock path.join to return predictable paths + def join_side_effect(*args): + return '/'.join(args) + + mock_join.side_effect = join_side_effect + mock_exists.return_value = True # Both reports dir and header.html exist + + # Mock the internal methods to avoid actual file operations + with ( + patch.object(repo, '_setup_run_directory') as mock_setup, + patch.object(repo, '_generate_report') as mock_generate_report, + patch.object(repo, '_init_artifacts_data') as mock_init_data, + patch.object(repo, '_get_reports_directory') as mock_get_reports_dir, + patch.object(repo, '_create_run_directory') as mock_create_run_dir, + ): + # Setup mocks + mock_init_data.return_value = (pd.DataFrame(), pd.DataFrame()) + mock_get_reports_dir.return_value = '/reports' + mock_create_run_dir.return_value = '/reports/run_1' + mock_generate_report.return_value = mock_train_result + + # Call the method + result = repo._generate_artifacts(mock_train_result) + + # Verify the methods on lines 147-148 were called + mock_setup.assert_called_once_with('/reports/run_1', '/reports/header.html') + mock_generate_report.assert_called_once() + + # Verify result + assert result == mock_train_result