📚 Pré-requisitos Teóricos: este projeto aplica conceitos ensinados em Especialização em Sistemas com Python. Recomendado revisar antes de começar.
Trilha de Especialização Pedagógica — Nível Intermediário (Curso 25: Sistemas Desktop com Python & POO)
- Visão Geral da Trilha: 📚 Módulo Sistemas com Python
- Trilha Evolutiva Completa: 🚀 Especificação das 4 Fases
- Catálogo de Projetos: 🏗️ Catálogo de Projetos
- ➡️ Projeto 1 de 4: Processador de Arquivos em Lote Desktop (
sistemas_python_01_desktop_poo)
Este projeto atua como a ponte pedagógica fundamental entre os scripts introdutórios e a arquitetura de software profissional. Nele, o estudante aprende a construir um Processador e Sanitizador de Arquivos em Lote para automação de rotinas de escritórios de contabilidade e despachantes, aplicando:
list[T], Optional[str], Tuple[int, int], Callable).@dataclass(slots=True)): modelagem de entidades de domínio com baixo consumo de memória e sem código boilerplate.enum.Enum): eliminação de “strings mágicas” através dos enums StatusProcessamento e TipoOperacao.@property: cálculo dinâmico de extensões e formatação amigável de tamanhos (B, KB, MB).models.py), Regras de Negócio/Serviços (services.py), Interface Desktop (app.py) e CLI (cli.py).UTF-8, Latin-1, CP1252) para evitar travamentos em arquivos corrompidos.flowchart TD
subgraph View ["🖥️ Camada de Apresentação (View / Controller)"]
GUI["app.py (AppProcessadorLote)\nTkinter / ttk.Treeview / Progressbar"]
CLI["cli.py (Interface de Terminal)\nExecução em lote sem GUI"]
end
subgraph Service ["⚙️ Camada de Serviços / Negócio (Service)"]
SVC["services.py (ProcessadorArquivosService)\nTransformação, Encodings e Callbacks"]
end
subgraph Model ["📦 Camada de Domínio / Dados (Model)"]
M1["ArquivoLote (@dataclass)\nMetadados, @property tamanho_formatado"]
M2["ConfiguracaoLote (@dataclass)\nOperação, prefixo, sufixo e destino"]
E1["StatusProcessamento (Enum)"]
E2["TipoOperacao (Enum)"]
end
GUI -->|Invoca| SVC
CLI -->|Invoca| SVC
SVC -->|Manipula| M1
SVC -->|Utiliza| M2
M1 --> E1
M2 --> E2
style View fill:#e3f2fd,stroke:#1976d2,stroke-width:2px
style Service fill:#e8f5e9,stroke:#388e3c,stroke-width:2px
style Model fill:#fffde7,stroke:#fbc02d,stroke-width:2px
sistemas_python_01_desktop_poo/
├── models.py # Entidades Dataclass, Enums e Type Hints PEP 484/585
├── services.py # Regras de transformação, tratamento de encodings e callbacks
├── app.py # Interface Gráfica Desktop com Tkinter / ttk.Treeview
├── cli.py # Interface de Linha de Comando interativa
├── main.py # Hub Launcher central (GUI, CLI ou Testes)
├── tests/
│ └── test_processor.py # Suíte de testes unitários automatizados com unittest
├── docs/
│ └── adr/ # Architectural Decision Records (ADRs)
└── index.md # Guia técnico e documentação pedagógica
No VS Code / PyCharm, abra a pasta deste projeto (File > Open Folder) ou navegue via terminal:
cd sistemas_python_01_desktop_poo
# Executar o Hub Launcher (Menu interativo):
python main.py
# Ou executar diretamente a Interface Gráfica Desktop:
python app.py
# Executar a Interface de Linha de Comando (CLI):
python cli.py
# Executar a Suíte de Testes Unitários:
python -m unittest discover -s tests
[!TIP] Dica para execução a partir da raiz do repositório: Se você abriu o repositório completo no VS Code, basta executar:
python proj_aplicacoes_full_stack/projetos/sistemas_python_01_desktop_poo/main.py
models.py)from dataclasses import dataclass, field
from enum import Enum
import os
from typing import Optional
class StatusProcessamento(str, Enum):
PENDENTE = "Pendente"
PROCESSANDO = "Processando"
SUCESSO = "Sucesso"
ERRO = "Erro"
class TipoOperacao(str, Enum):
RENOMEAR_PREFIXO = "Adicionar Prefixo/Sufixo"
SANITIZAR_ENCODING = "Sanitizar Encoding (UTF-8)"
MAIUSCULAS = "Converter Conteúdo para MAIÚSCULAS"
MINUSCULAS = "Converter Conteúdo para minúsculas"
@dataclass(slots=True)
class ArquivoLote:
caminho_origem: str
nome_original: str = field(init=False)
tamanho_bytes: int = field(init=False)
status: StatusProcessamento = StatusProcessamento.PENDENTE
mensagem_erro: Optional[str] = None
caminho_destino: Optional[str] = None
def __post_init__(self) -> None:
self.nome_original = os.path.basename(self.caminho_origem)
self.tamanho_bytes = os.path.getsize(self.caminho_origem) if os.path.exists(self.caminho_origem) else 0
@property
def tamanho_formatado(self) -> str:
if self.tamanho_bytes < 1024:
return f"{self.tamanho_bytes} B"
elif self.tamanho_bytes < 1024 * 1024:
return f"{self.tamanho_bytes / 1024:.2f} KB"
return f"{self.tamanho_bytes / (1024 * 1024):.2f} MB"
services.py)class ProcessadorArquivosService:
def processar_lote(self, arquivos: list[ArquivoLote], config: ConfiguracaoLote) -> tuple[int, int]:
sucessos, erros = 0, 0
total = len(arquivos)
for indice, arq in enumerate(arquivos, start=1):
try:
self._processar_arquivo_individual(arq, config)
sucessos += 1
except Exception as e:
arq.marcar_erro(str(e))
erros += 1
if self.callback_progresso:
self.callback_progresso(indice, total, arq)
return sucessos, erros
Ran 4 tests in 0.032s — OK
Os testes automatizados cobrem:
| ⬅️ Ver Todos os Projetos no Super-Hub | 📚 Trilha Sistemas com Python | 🏠 Página Inicial |