Skip to content

Commit 6f173ee

Browse files
Merge branch 'main' into feature/docker-deploy
2 parents ed0ba4a + c6df437 commit 6f173ee

File tree

12 files changed

+578
-122
lines changed

12 files changed

+578
-122
lines changed

.github/workflows/pipeline_lint.yml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
name: Pipeline de Qualidade de Código
2+
on:
3+
push:
4+
branches:
5+
- main
6+
pull_request:
7+
branches:
8+
- main
9+
10+
jobs:
11+
build:
12+
runs-on: ubuntu-latest
13+
steps:
14+
- name: Verificar código
15+
uses: actions/checkout@v4
16+
17+
- name: Configurar Python
18+
uses: actions/setup-python@v5
19+
with:
20+
python-version: '3.10'
21+
22+
- name: Instalar Poetry
23+
run: pip install poetry
24+
25+
- name: Instalar dependências
26+
run: poetry install
27+
28+
- name: Executar ruff check
29+
run: poetry run ruff check .
30+
31+
- name: Formatando código com ruff
32+
run: poetry run ruff format .
33+
34+
- name: Verificar formatação com black
35+
run: poetry run black --check .
36+
37+
- name: Executar bandit
38+
run: poetry run bandit -r . --skip B101

.github/workflows/pipeline_test.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: Pipeline de Testes
2+
3+
on:
4+
pull_request:
5+
branches:
6+
- main
7+
8+
jobs:
9+
test:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- name: Verificar código
13+
uses: actions/checkout@v4
14+
15+
- name: Configurar Python
16+
uses: actions/setup-python@v5
17+
with:
18+
python-version: '3.10'
19+
20+
- name: Instalar Poetry
21+
run: pip install poetry
22+
23+
- name: Instalar dependências
24+
run: poetry install
25+
26+
- name: Executar testes unitários
27+
run: poetry run pytest --cov=.
28+

__init__.py

Whitespace-only changes.

artifacts/models/svc_model.joblib

5.1 KB
Binary file not shown.

config/settings.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
INTERIM_DATA_DIR = DATA_DIR / "interim"
1212

1313
MODELS_DIR = BASE_DIR / "artifacts" / "models"
14-
1514
NOTEBOOKS_DIR = BASE_DIR / "notebooks"
1615
REPORTS_DIR = BASE_DIR / "reports"
1716
FIGURES_DIR = REPORTS_DIR / "figures"
@@ -24,3 +23,4 @@
2423
DOCS_DIR = BASE_DIR / "docs"
2524
TESTS_DIR = BASE_DIR / "tests"
2625
UI_DIR = BASE_DIR / "ui"
26+
TEST_LOAD_DATA_CSV = TESTS_DIR / "test_load_data.csv"

poetry.lock

Lines changed: 414 additions & 118 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,17 @@ requests = "^2.25.1"
1111
pandas = "^2.0.0"
1212
typer = "0.12.5"
1313
scikit-learn = "1.5.1"
14-
fastapi = "^0.115.0"
14+
fastapi = "^0.114.1"
1515
uvicorn = "^0.30.6"
16+
joblib = "^1.4.2"
1617

1718
[tool.poetry.dev-dependencies]
1819
pytest = "^6.2.4"
20+
pytest-cov = "^5.0.0"
21+
pytest-mock = "^3.14.0"
1922
pre-commit = "^3.7.0"
2023
ruff = "0.6.3"
24+
black = "^23.1.0"
2125
bandit = {version = "1.7.7", extras = ["toml"]}
2226
dvc = {version = "3.55.2", extras = ["s3"]}
2327

@@ -32,3 +36,6 @@ exclude_dirs = [
3236
"__pycache__",
3337
"venv/",
3438
]
39+
40+
[tool.bandit.assert_used]
41+
skips = ['*/test_*.py']

src/pipelines/features.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
from pathlib import Path
55
import typer
66
from config.settings import PROCESSED_DATA_DIR, RAW_DATA_DIR
7+
from typing import Optional
8+
79

810
app = typer.Typer()
911

@@ -70,7 +72,9 @@ def preprocess_data(df: pd.DataFrame):
7072

7173
@app.command()
7274
def preprocess(
73-
input_file: str = None, output_train_file: str = None, output_test_file: str = None
75+
input_file: Optional[str] = None,
76+
output_train_file: Optional[str] = None,
77+
output_test_file: Optional[str] = None,
7478
):
7579
"""
7680
Função principal para o pré-processamento dos dados. Carrega os dados brutos, realiza
@@ -95,7 +99,11 @@ def preprocess(
9599
output_train_file.parent.mkdir(parents=True, exist_ok=True)
96100

97101
# Carregar dados
98-
df = load_data(input_file)
102+
try:
103+
df = load_data(input_file)
104+
except FileNotFoundError:
105+
typer.echo("Falha ao carregar os dados. Arquivo não encontrado.", err=True)
106+
raise SystemExit(1)
99107

100108
# Pré-processar dados (dividir em treino/teste e escalonar)
101109
train_data, test_data = preprocess_data(df)

tests/__init__.py

Whitespace-only changes.

tests/test_features.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import pytest
2+
import pandas as pd
3+
from src.pipelines.features import load_data, preprocess, preprocess_data
4+
from config.settings import TEST_LOAD_DATA_CSV
5+
6+
7+
def test_load_data_success():
8+
# Carrega os dados usando a função load_data
9+
df = load_data(TEST_LOAD_DATA_CSV)
10+
11+
# DataFrame esperado
12+
expected_df = pd.DataFrame(
13+
{
14+
"sepal_length": [5.1, 4.9],
15+
"sepal_width": [3.5, 3.0],
16+
"petal_length": [1.4, 1.4],
17+
"petal_width": [0.2, 0.2],
18+
"species": ["setosa", "setosa"],
19+
}
20+
)
21+
# Verifica se o DataFrame carregado corresponde ao esperado
22+
pd.testing.assert_frame_equal(df, expected_df)
23+
24+
25+
def test_preprocess_data_no_species_column():
26+
# Dados fictícios sem a coluna 'species' para verificar o comportamento
27+
df = pd.DataFrame(
28+
{
29+
"sepal_length": [5.1, 4.9, 4.7, 4.6],
30+
"sepal_width": [3.5, 3.0, 3.2, 3.1],
31+
"petal_length": [1.4, 1.4, 1.3, 1.5],
32+
"petal_width": [0.2, 0.2, 0.2, 0.2],
33+
}
34+
)
35+
36+
with pytest.raises(KeyError):
37+
preprocess_data(df)
38+
39+
40+
def test_preprocess_failure(capfd):
41+
# Chamar a função preprocess e garantir que uma exceção seja levantada
42+
with pytest.raises(SystemExit):
43+
preprocess(
44+
input_file="fake_input.csv",
45+
output_train_file="fake_train.csv",
46+
output_test_file="fake_test.csv",
47+
)
48+
49+
# Capturar a saída de erro (stderr)
50+
captured = capfd.readouterr()
51+
print(captured)
52+
53+
# Verificar se a mensagem de erro está no stderr
54+
assert "Falha ao carregar os dados. Arquivo não encontrado." in captured.err

tests/test_load_data.csv

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
5.1,3.5,1.4,0.2,setosa
2+
4.9,3.0,1.4,0.2,setosa

tests/test_train.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
from src.pipelines.train import save_model
2+
from unittest.mock import MagicMock
3+
4+
5+
def test_save_model(mocker):
6+
# Mock para joblib.dump
7+
mock_dump = mocker.patch("joblib.dump")
8+
9+
# Mock para typer.echo
10+
mock_echo = mocker.patch("typer.echo")
11+
12+
# Modelo fictício
13+
mock_model = MagicMock()
14+
15+
# Caminho fictício para o arquivo
16+
output_file = "fake_model.joblib"
17+
18+
# Chamar a função save_model
19+
save_model(mock_model, output_file)
20+
21+
# Verificar se joblib.dump foi chamado corretamente
22+
mock_dump.assert_called_once_with(mock_model, output_file)
23+
mock_echo.assert_called_once_with(f"Modelo salvo em: {output_file}")

0 commit comments

Comments
 (0)