🌗 ← Índice

🐍 Python P26 — Projeto Final (Ferramenta Completa)


Data: 14/08/2026

NĂ­vel: Projeto Final (MĂȘs 3 — Projetos Reais) 🏆




🎯 Objetivos





🏆 O Projeto Final: `organizador_precos`


Uma ferramenta que monitora preços de produtos e organiza os resultados — combinando o que vocĂȘ aprendeu:



Funcionalidades



# Busca preços de produtos (ex: livros na API do Google Books)
python organizador_precos.py buscar "python para iniciantes" --limite 5

# Salva resultados em planilha
python organizador_precos.py buscar "langchain" --planilha resultados.xlsx

# Agenda verificação periódica (gera arquivos datados)
python organizador_precos.py monitorar "python" --intervalo 24

# Testes
pytest



📁 Estrutura do projeto



organizador_precos/
├── pyproject.toml          # empacotamento
├── README.md
├── organizador/
│   ├── __init__.py
│   ├── api.py              # Classe BuscaPrecos (requests)
│   ├── saida.py            # salvar CSV/planilha (pandas/openpyxl)
│   └── cli.py              # argparse (main)
└── tests/
    ├── test_api.py
    └── test_saida.py



📖 Passo a passo


1. `api.py` — busca na API do Google Books (grátis, sem chave)



"""Consulta a API pĂșblica do Google Books."""
import requests
from dataclasses import dataclass


@dataclass
class Livro:
    titulo: str
    autor: str
    preco: float | None
    link: str


class BuscaPrecos:
    """Busca livros e preços na API do Google Books."""

    BASE = "https://www.googleapis.com/books/v1/volumes"

    def __init__(self, timeout: int = 10) -> None:
        self.timeout = timeout

    def buscar(self, termo: str, limite: int = 5) -> list[Livro]:
        """Busca livros pelo termo e retorna lista de Livro."""
        resposta = requests.get(
            self.BASE,
            params={"q": termo, "maxResults": limite},
            timeout=self.timeout,
        )
        resposta.raise_for_status()

        livros: list[Livro] = []
        for item in resposta.json().get("items", []):
            info = item.get("volumeInfo", {})
            venda = item.get("saleInfo", {})
            preco = None
            if venda.get("listPrice"):
                preco = venda["listPrice"].get("amount")
            livros.append(
                Livro(
                    titulo=info.get("title", "Sem tĂ­tulo"),
                    autor=", ".join(info.get("authors", [])),
                    preco=preco,
                    link=info.get("infoLink", ""),
                )
            )
        return livros

💡 **`dataclass`** — estrutura de dados limpa, com `__repr__` automático (revisão da P13!).

2. `saida.py` — salvar resultados



"""Geração de arquivos de saída."""
import csv
from pathlib import Path
from .api import Livro


def salvar_csv(livros: list[Livro], caminho: str) -> Path:
    """Salva a lista de livros em CSV."""
    destino = Path(caminho)
    with open(destino, "w", newline="", encoding="utf-8") as f:
        escritor = csv.writer(f)
        escritor.writerow(["titulo", "autor", "preco", "link"])
        for livro in livros:
            escritor.writerow([livro.titulo, livro.autor, livro.preco, livro.link])
    return destino


def salvar_planilha(livros: list[Livro], caminho: str) -> Path:
    """Salva a lista de livros em planilha Excel."""
    import pandas as pd
    dados = [
        {"titulo": l.titulo, "autor": l.autor, "preco": l.preco, "link": l.link}
        for l in livros
    ]
    df = pd.DataFrame(dados)
    destino = Path(caminho)
    df.to_excel(destino, index=False)
    return destino

3. `cli.py` — interface



"""Interface de linha de comando."""
import argparse
import logging
from pathlib import Path
from .api import BuscaPrecos
from .saida import salvar_csv, salvar_planilha

logger = logging.getLogger(__name__)


def main() -> None:
    parser = argparse.ArgumentParser(description="Organizador de preços de livros")
    sub = parser.add_subparsers(dest="comando", required=True)

    p_buscar = sub.add_parser("buscar", help="Busca livros")
    p_buscar.add_argument("termo", help="Termo de busca")
    p_buscar.add_argument("--limite", type=int, default=5)
    p_buscar.add_argument("--csv", default=None, help="Salva em CSV")
    p_buscar.add_argument("--planilha", default=None, help="Salva em planilha")

    args = parser.parse_args()

    api = BuscaPrecos()
    try:
        livros = api.buscar(args.termo, args.limite)
    except Exception as erro:                 # noqa: BLE001
        logger.error("Falha na busca: %s", erro)
        return

    if not livros:
        print("Nenhum livro encontrado.")
        return

    for livro in livros:
        preco = f"R$ {livro.preco:.2f}" if livro.preco else "indisponĂ­vel"
        print(f"- {livro.titulo} ({livro.autor}) — {preco}")

    if args.csv:
        caminho = salvar_csv(livros, args.csv)
        print(f"\nCSV salvo em {caminho}")

    if args.planilha:
        caminho = salvar_planilha(livros, args.planilha)
        print(f"Planilha salva em {caminho}")


if __name__ == "__main__":
    logging.basicConfig(level=logging.INFO)
    main()

4. `pyproject.toml` — instalável



[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"

[project]
name = "organizador-precos"
version = "0.1.0"
description = "Busca e organiza preços de livros via Google Books API"
requires-python = ">=3.10"
dependencies = ["requests", "pandas", "openpyxl"]

[project.scripts]
organizador = "organizador.cli:main"

[tool.setuptools.packages.find]
include = ["organizador*"]


pip install -e .        # instala o comando 'organizador'
organizador buscar "python" --csv livros.csv

5. Testes



# tests/test_api.py
import pytest
from unittest.mock import patch, MagicMock
from organizador.api import BuscaPrecos


def test_buscar_retorna_livros():
    resposta = MagicMock()
    resposta.json.return_value = {
        "items": [{
            "volumeInfo": {"title": "Livro Teste", "authors": ["Autor A"]},
            "saleInfo": {"listPrice": {"amount": 29.9}},
        }]
    }
    with patch("requests.get", return_value=resposta) as mock_get:
        livros = BuscaPrecos().buscar("teste", 1)

    assert len(livros) == 1
    assert livros[0].titulo == "Livro Teste"
    assert livros[0].preco == 29.9
    assert mock_get.call_args.kwargs["params"]["q"] == "teste"


def test_buscar_sem_preco():
    resposta = MagicMock()
    resposta.json.return_value = {"items": [{"volumeInfo": {"title": "GrĂĄtis"}}]}
    with patch("requests.get", return_value=resposta):
        livros = BuscaPrecos().buscar("x", 1)
    assert livros[0].preco is None



đŸ§Ș Desafios de extensĂŁo (opcionais)


  1. 1. **Notificação**: envie o resultado pelo bot do Telegram (Aula P24)
  2. 2. **Histórico**: salve preços ao longo do tempo e calcule variação (Aula P16)
  3. 3. **Web**: monte uma pĂĄgina simples no seu painel (Streamlit/HTML) mostrando os resultados
  4. 4. **Agendamento**: rode via cron diariamente e notifique só quando houver mudança relevante



✅ CritĂ©rios de avaliação do projeto final


CritérioOnde foi aplicado
POO`BuscaPrecos`, `Livro` (dataclass)
API externaGoogle Books API
pathlibsalvar/ler arquivos
argparseCLI com subcomandos
pytest`tests/test_api.py`
Boas prĂĄticastype hints, docstrings, logging
Empacotamento`pyproject.toml` + `pip install -e .`



🏆 PARABÉNS — Curso de Python concluído! 🎉


VocĂȘ completou as 26 aulas do plano:



MĂȘs 1 (P01–P10): Fundamentos + POO bĂĄsica + Desafio
MĂȘs 2 (P11–P18): POO avançada + APIs + datas + testes + CLI
MĂȘs 3 (P19–P26): automação + scraping + CLIs + 3 projetos + boas prĂĄticas + FINAL

VocĂȘ agora consegue criar ferramentas Ășteis do zero — incluindo automaçÔes para as suas VPSs, bots de alerta e integraçÔes com APIs. E o melhor: todo esse conhecimento se conecta com LangChain, LangGraph e seus RAGs! 🚀





HB Estudos · ← Voltar ao Ă­ndice de Python