diff --git a/README.md b/README.md index b28488c..bf20713 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,24 @@ -# Planejador Diario de Missoes e Sobreaviso +# Planejador Diário de Missões e Sobreaviso -Projeto em Python para gerar a escala diaria de missao acionada, missao local e sobreaviso usando Programacao Linear Inteira Mista (MILP). +Projeto em Python para gerar a escala diária de missão acionada, missão local e sobreaviso usando Programação Linear Inteira Mista (MILP). ## 1. Estrutura do projeto ```text dados/ Arquivos de entrada do planejamento resultados/ Planilhas geradas pelo planejador -src/planejador_missao/ Codigo-fonte Python do modelo -scripts/ Scripts numerados de execucao, validacao e teste +src/planejador_missao/ Código-fonte Python do modelo +scripts/ Scripts numerados de execução, validação e teste .vscode/ Arquivos de workspace do VS Code web_app.py Interface web HTML para abrir no navegador run_planner.py Ponto de entrada para executar o planejamento -requirements.txt Dependencias Python +requirements.txt Dependências Python Abrir Planejador.vbs Atalho para abrir a interface sem prompt ``` -A execucao oficial do projeto e feita somente em Python. +A execução oficial do projeto é feita somente em Python. -## 2. Preparacao do ambiente +## 2. Preparação do ambiente No PowerShell, a partir da raiz do projeto: @@ -29,13 +29,13 @@ python -m venv .venv pip install -r requirements.txt ``` -A pasta `.venv` nao acompanha a entrega oficial. Ela deve ser recriada pelo usuario com os comandos acima. +A pasta `.venv` não acompanha a entrega oficial. Ela deve ser recriada pelo usuário com os comandos acima. ## 3. Como executar pelo app no navegador Forma recomendada: -1. De dois cliques em `Abrir Planejador.vbs`. +1. Dê dois cliques em `Abrir Planejador.vbs`. 2. Aguarde o navegador abrir em `http://127.0.0.1:8050`. Se preferir executar manualmente pelo PowerShell, use: @@ -46,19 +46,19 @@ Na raiz do projeto: python web_app.py ``` -O navegador abre automaticamente em `http://127.0.0.1:8050`. Se nao abrir, copie esse endereco no Google Chrome. +O navegador abre automaticamente em `http://127.0.0.1:8050`. Se não abrir, copie esse endereço no Google Chrome. O app permite: -- editar data e criterios de otimizacao; -- informar a missao de rota acionada; -- marcar condicao das aeronaves; -- selecionar tripulantes disponiveis; -- consultar a aba `Quadrinhos`, com proxima OI, horas do ano, SBV e falta para 50 horas; +- editar data e critérios de otimização; +- informar a missão de rota acionada; +- marcar condição das aeronaves; +- selecionar tripulantes disponíveis; +- consultar a aba `Quadrinhos`, com próxima OI, horas do ano, SBV e falta para 50 horas; - salvar os arquivos de entrada em `dados/`; -- gerar a escala e abrir a ultima planilha. +- gerar a escala e abrir a última planilha. -A versao final usa Python e interface web local. +A versão final usa Python e interface web local. ## 4. Como executar pelo terminal @@ -81,17 +81,17 @@ Os arquivos de entrada ficam em `dados/`. | Arquivo | Finalidade | | --- | --- | -| `Modelagem_C98_ETA2_local.xlsx` ou `Modelagem C98 ETA2.xlsx` | Cadastro base de tripulantes, qualificacoes, projetos, soldo e metas de horas. | -| `catalogo_ois.xlsx` | Catalogo de OIs por aeronave, subprograma, ordem e tipo de missao. | -| `indisponibilidades_2026.xlsx` | Periodos em que cada tripulante nao pode ser escalado. | -| `parametros_missao.csv` | Data de planejamento e criterios de otimizacao. | -| `aeronaves_disponiveis.csv` | Condicao diaria de cada aeronave. | -| `rotas_acionadas.csv` | Missao de rota acionada para atendimento obrigatorio. | -| `tripulantes_disponiveis.csv` | Selecao manual dos tripulantes disponiveis no dia. | -| `progresso_ois_2026.xlsx` | OIs ja concluidas manualmente. | -| `historico_horas_voadas.csv` | Historico acumulado gerado automaticamente pelo planejador. | +| `Modelagem_C98_ETA2_local.xlsx` ou `Modelagem C98 ETA2.xlsx` | Cadastro base de tripulantes, qualificações, projetos, soldo e metas de horas. | +| `catalogo_ois.xlsx` | Catálogo de OIs por aeronave, subprograma, ordem e tipo de missão. | +| `indisponibilidades_2026.xlsx` | Períodos em que cada tripulante não pode ser escalado. | +| `parametros_missao.csv` | Data de planejamento e critérios de otimização. | +| `aeronaves_disponiveis.csv` | Condição diária de cada aeronave. | +| `rotas_acionadas.csv` | Missão de rota acionada para atendimento obrigatório. | +| `tripulantes_disponiveis.csv` | Seleção manual dos tripulantes disponíveis no dia. | +| `progresso_ois_2026.xlsx` | OIs já concluídas manualmente. | +| `historico_horas_voadas.csv` | Histórico acumulado gerado automaticamente pelo planejador. | -## 6. Saida gerada +## 6. Saída gerada O planejador cria uma planilha em: @@ -99,17 +99,17 @@ O planejador cria uma planilha em: resultados/planejamento_diario_YYYY-MM-DD.xlsx ``` -Quando ja existe arquivo para a mesma data, o sistema acrescenta o horario ao nome para preservar a saida anterior. +Quando já existe arquivo para a mesma data, o sistema acrescenta o horário ao nome para preservar a saída anterior. -A aba `ESCALA DIARIA` contem tres blocos: +A aba `ESCALA DIARIA` contém três blocos: - `MISSAO ACIONADA`; - `VOOS LOCAIS`; - `SOBREAVISO`. -Apos a solucao, o arquivo `dados/historico_horas_voadas.csv` tambem e atualizado com horas voadas e sobreavisos registrados. +Após a solução, o arquivo `dados/historico_horas_voadas.csv` também é atualizado com horas voadas e sobreavisos registrados. -## 7. Scripts e validacao com voos de 2025 +## 7. Scripts e validação com voos de 2025 O fluxo pelo VS Code pode ser executado com: @@ -123,7 +123,7 @@ Para conferir rapidamente o ambiente: python scripts\09_testar_instalacao.py ``` -Para validar o modelo com dados historicos, coloque os voos reais de 2025 em: +Para validar o modelo com dados históricos, coloque os voos reais de 2025 em: ```text dados/validacao/voos_2025.csv @@ -135,19 +135,19 @@ O arquivo deve ter as colunas: data,aeronave,tipo_escala,tripulante,funcao,oi,horas_voadas,sbv ``` -Se a planilha `Quadro de Voo 2025 (2).xlsx` estiver em `dados/`, a validacao importa a aba `VOOS` automaticamente: +Se a planilha `Quadro de Voo 2025 (2).xlsx` estiver em `dados/`, a validação importa a aba `VOOS` automaticamente: ```powershell python scripts\00_main.py --validacao 2025 ``` -Tambem e possivel chamar diretamente: +Também é possível chamar diretamente: ```powershell python scripts\07_validacao_2025.py ``` -Os relatorios sao salvos em: +Os relatórios são salvos em: ```text resultados/validacao/validacao_2025_resumo.xlsx @@ -156,61 +156,61 @@ resultados/validacao/validacao_2025_metricas.csv resultados/validacao/validacao_2025_barras.png ``` -Ele contem: +Ele contém: -- `metricas`: comparacao entre escala real 2025 e escala otimizada; +- `metricas`: comparação entre escala real 2025 e escala otimizada; - `comparativo_trips`: horas reais, horas otimizadas e delta por tripulante; -- `voos_2025_slots`: voos historicos agregados em slots; -- `escala_otimizada`: redistribuicao proposta pelo MILP. +- `voos_2025_slots`: voos históricos agregados em slots; +- `escala_otimizada`: redistribuição proposta pelo MILP. -Os scripts antigos de importacao/validacao foram preservados em `scripts/_arquivados/`. +Os scripts antigos de importação/validação foram preservados em `scripts/_arquivados/`. -## 8. Metodo de otimizacao +## 8. Método de otimização -O modelo monta previamente todas as colunas candidatas viaveis. Cada coluna representa uma possivel escala de dupla para: +O modelo monta previamente todas as colunas candidatas viáveis. Cada coluna representa uma possível escala de dupla para: - rota acionada; -- missao local; +- missão local; - sobreaviso. -Cada coluna recebe uma variavel binaria no MILP: +Cada coluna recebe uma variável binária no MILP: ```text x[i] = 1 se a coluna candidata i for escolhida -x[i] = 0 caso contrario +x[i] = 0 caso contrário ``` -A funcao objetivo maximiza o score operacional, com prioridade para: +A função objetivo maximiza o score operacional, com prioridade para: 1. atender rotas acionadas; -2. aproveitar missoes locais para progressao operacional; -3. escolher sobreavisos conforme o criterio definido. +2. aproveitar missões locais para progressão operacional; +3. escolher sobreavisos conforme o critério definido. -Restricoes principais: +Restrições principais: -- cada tripulante aparece no maximo uma vez na escala; +- cada tripulante aparece no máximo uma vez na escala; - cada aeronave livre recebe exatamente uma cobertura principal, por rota acionada ou sobreaviso; -- cada aeronave executa no maximo uma rota acionada; -- cada aeronave executa no maximo uma missao local; -- rota acionada e missao local nao podem usar a mesma aeronave no mesmo dia; +- cada aeronave executa no máximo uma rota acionada; +- cada aeronave executa no máximo uma missão local; +- rota acionada e missão local não podem usar a mesma aeronave no mesmo dia; - toda rota acionada informada deve ser atendida exatamente uma vez. -O resolvedor usado e `scipy.optimize.milp`, um resolvedor exato de programacao inteira disponivel diretamente em Python. +O resolvedor usado é `scipy.optimize.milp`, um resolvedor exato de programação inteira disponível diretamente em Python. -## 9. Organizacao do codigo +## 9. Organização do código -O codigo em `src/planejador_missao/` esta dividido por blocos funcionais: +O código em `src/planejador_missao/` está dividido por blocos funcionais: | Arquivo | Responsabilidade | | --- | --- | -| `data_io.py` | Leitura das planilhas/CSVs e escrita do historico. | -| `rules.py` | Regras de disponibilidade, qualificacao e progresso operacional. | -| `candidates.py` | Geracao das colunas candidatas para o MILP. | -| `optimizer.py` | Formulacao e solucao do modelo MILP. | -| `report.py` | Registro de horas e criacao da planilha Excel. | +| `data_io.py` | Leitura das planilhas/CSVs e escrita do histórico. | +| `rules.py` | Regras de disponibilidade, qualificação e progresso operacional. | +| `candidates.py` | Geração das colunas candidatas para o MILP. | +| `optimizer.py` | Formulação e solução do modelo MILP. | +| `report.py` | Registro de horas e criação da planilha Excel. | | `quadrinhos.py` | Resumo operacional da aba Quadrinhos. | -| `validacao.py` | Validacao retrospectiva com voos historicos. | -| `main.py` | Orquestracao do fluxo completo. | -| `utils.py` | Normalizacao de textos, datas, booleanos e horas. | +| `validacao.py` | Validação retrospectiva com voos históricos. | +| `main.py` | Orquestração do fluxo completo. | +| `utils.py` | Normalização de textos, datas, booleanos e horas. | -Os comentarios no codigo indicam os blocos do fluxo e as restricoes relevantes do modelo, sem repetir conceitos elementares da linguagem. +Os comentários no código indicam os blocos do fluxo e as restrições relevantes do modelo, sem repetir conceitos elementares da linguagem. diff --git a/run_planner.py b/run_planner.py index 4cb535b..cb87c61 100644 --- a/run_planner.py +++ b/run_planner.py @@ -1,4 +1,4 @@ -"""Ponto de entrada do planejador diario de missoes.""" +"""Ponto de entrada do planejador diário de missões.""" from pathlib import Path @@ -9,6 +9,6 @@ if __name__ == "__main__": resultado = executar_planejamento(Path(__file__).resolve().parent) print("\n=== Planejamento concluido ===") print(f"Data: {resultado['data_planejamento']}") - print(f"Arquivo de saida: {resultado['arquivo_saida']}") + print(f"Arquivo de saída: {resultado['arquivo_saida']}") print(f"Colunas candidatas: {resultado['total_candidatas']}") print(f"Escalas selecionadas: {resultado['total_selecionadas']}") diff --git a/scripts/00_main.py b/scripts/00_main.py index f874d0e..99567d8 100644 --- a/scripts/00_main.py +++ b/scripts/00_main.py @@ -1,16 +1,16 @@ -"""Orquestrador principal do Planejador Missao. +"""Orquestrador principal do Planejador Missão. Entradas: Arquivos em dados/, argumentos opcionais de linha de comando e configuracoes centralizadas em scripts/01_config.py. -Saidas: - Planejamento diario em resultados/, historico operacional atualizado, - relatorios de validacao quando solicitado e logs em logs/execucao.log. +Saídas: + Planejamento diário em resultados/, histórico operacional atualizado, + relatórios de validação quando solicitado e logs em logs/execucao.log. Papel no pipeline: Executa o fluxo completo: leitura de dados, preparacao, geracao de colunas - candidatas, montagem/solucao do MILP, exportacao de resultados e validacao. + candidatas, montagem/solução do MILP, exportação de resultados e validação. Exemplos: python scripts/00_main.py --data 2026-01-02 --modo diario @@ -43,7 +43,7 @@ def atualizar_data_planejamento(data: str) -> None: def executar_diario(args: argparse.Namespace, logger) -> dict: - """Executa o planejamento diario oficial em Python.""" + """Executa o planejamento diário oficial em Python.""" io_utils.configurar_ambiente() if args.data: atualizar_data_planejamento(args.data) @@ -60,24 +60,24 @@ def executar_diario(args: argparse.Namespace, logger) -> dict: logger.info("Colunas candidatas: %s", resultado["total_candidatas"]) logger.info("Escalas selecionadas: %s", resultado["total_selecionadas"]) logger.info("Arquivo exportado: %s", resultado["arquivo_saida"]) - logger.info("Tempo de execucao: %.1f s", duracao) + logger.info("Tempo de execução: %.1f s", duracao) return resultado def executar_validacao(ano: str, logger) -> dict: - """Executa validacao retrospectiva suportada pelo projeto.""" + """Executa validação retrospectiva suportada pelo projeto.""" if str(ano) != "2025": - raise ValueError("Erro: no momento a validacao disponivel e --validacao 2025.") + raise ValueError("Erro: no momento a validação disponível é --validacao 2025.") validacao = importlib.import_module("07_validacao_2025") return validacao.executar_validacao_2025(importar=True, logger=logger) def parse_args() -> argparse.Namespace: - parser = argparse.ArgumentParser(description="Executa o Planejador Missao em Python.") - parser.add_argument("--data", help="Data do planejamento diario no formato AAAA-MM-DD.") - parser.add_argument("--aeronave", choices=config.AERONAVES, help="Aeronave de referencia para diagnostico.") - parser.add_argument("--modo", default="diario", choices=["diario"], help="Modo de execucao do planejamento.") - parser.add_argument("--validacao", help="Ano da validacao retrospectiva. Exemplo: --validacao 2025.") + parser = argparse.ArgumentParser(description="Executa o Planejador Missão em Python.") + parser.add_argument("--data", help="Data do planejamento diário no formato AAAA-MM-DD.") + parser.add_argument("--aeronave", choices=config.AERONAVES, help="Aeronave de referência para diagnóstico.") + parser.add_argument("--modo", default="diario", choices=["diario"], help="Modo de execução do planejamento.") + parser.add_argument("--validacao", help="Ano da validação retrospectiva. Exemplo: --validacao 2025.") return parser.parse_args() @@ -86,13 +86,13 @@ def main() -> None: logger = io_utils.configurar_logger("main") if args.validacao: saida = executar_validacao(args.validacao, logger) - print("\n=== Validacao concluida ===") + print("\n=== Validação concluída ===") for path in saida["arquivos"]: print(f"- {path}") return resultado = executar_diario(args, logger) - print("\n=== Planejamento diario concluido ===") + print("\n=== Planejamento diário concluído ===") print(f"Data: {resultado['data_planejamento']}") print(f"Arquivo: {resultado['arquivo_saida']}") print(f"Colunas candidatas: {resultado['total_candidatas']}") diff --git a/scripts/01_config.py b/scripts/01_config.py index 9316026..893a7b9 100644 --- a/scripts/01_config.py +++ b/scripts/01_config.py @@ -1,11 +1,11 @@ -"""Configuracao central dos scripts do Planejador Missao. +"""Configuração central dos scripts do Planejador Missão. Entradas: - Estrutura padrao do projeto, com pastas dados/, src/ e resultados/. + Estrutura padrão do projeto, com pastas dados/, src/ e resultados/. -Saidas: - Constantes de caminho, parametros operacionais, nomes de arquivos e pesos - usados pelos scripts de execucao, validacao e teste. +Saídas: + Constantes de caminho, parâmetros operacionais, nomes de arquivos e pesos + usados pelos scripts de execução, validação e teste. Papel no pipeline: Evita caminhos absolutos e concentra convencoes para que o projeto rode em @@ -67,6 +67,6 @@ ARQUIVOS_ENTRADA_OBRIGATORIOS = [ def garantir_diretorios() -> None: - """Cria diretorios de saida e apoio usados pelos scripts.""" + """Cria diretórios de saída e apoio usados pelos scripts.""" for path in [RESULTADOS_DIR, VALIDACAO_DIR, LOGS_DIR, TEMP_DIR, ARQUIVO_VOOS_2025.parent]: path.mkdir(parents=True, exist_ok=True) diff --git a/scripts/02_io_utils.py b/scripts/02_io_utils.py index 42d575e..8a9745a 100644 --- a/scripts/02_io_utils.py +++ b/scripts/02_io_utils.py @@ -1,14 +1,14 @@ -"""Utilitarios de entrada, saida e log dos scripts. +"""Utilitários de entrada, saída e log dos scripts. Entradas: Caminhos definidos em 01_config.py. -Saidas: - Validacoes de arquivos, diretorios criados, logs em logs/execucao.log e +Saídas: + Validações de arquivos, diretórios criados, logs em logs/execucao.log e funcoes pequenas para exportacao. Papel no pipeline: - Padroniza mensagens de erro e evita repeticao de codigo de filesystem. + Padroniza mensagens de erro e evita repetição de código de filesystem. """ from __future__ import annotations @@ -52,7 +52,7 @@ def configurar_logger(nome: str = "planejador_missao") -> logging.Logger: def exigir_arquivos(paths: Iterable[Path]) -> None: - """Interrompe a execucao com mensagem clara se algum arquivo faltar.""" + """Interrompe a execução com mensagem clara se algum arquivo faltar.""" ausentes = [path for path in paths if not path.exists()] if ausentes: lista = "\n".join(f"- {path}" for path in ausentes) @@ -64,7 +64,7 @@ def exigir_colunas(df: pd.DataFrame, colunas: Iterable[str], origem: str) -> Non faltantes = [col for col in colunas if col not in df.columns] if faltantes: raise ValueError( - f"Erro: coluna obrigatoria '{faltantes[0]}' nao encontrada em {origem}. " + f"Erro: coluna obrigatória '{faltantes[0]}' não encontrada em {origem}. " "Verifique o arquivo de entrada." ) diff --git a/scripts/03_preparar_dados.py b/scripts/03_preparar_dados.py index 17b1798..235f603 100644 --- a/scripts/03_preparar_dados.py +++ b/scripts/03_preparar_dados.py @@ -1,15 +1,15 @@ -"""Preparacao das bases operacionais do Planejador Missao. +"""Preparação das bases operacionais do Planejador Missão. Entradas: Planilhas e CSVs em dados/: cadastro, catalogo de OIs, indisponibilidades, - progresso, historico, aeronaves, tripulantes disponiveis e rotas acionadas. + progresso, histórico, aeronaves, tripulantes disponíveis e rotas acionadas. Saidas: Dicionario com DataFrames normalizados para geracao de colunas candidatas. Papel no pipeline: Concentra a leitura e a preparacao antes do MILP, mantendo rastreavel quais - arquivos alimentam cada execucao. + arquivos alimentam cada execução. """ from __future__ import annotations @@ -64,7 +64,7 @@ def main() -> None: logger = io_utils.configurar_logger("preparar_dados") bases = carregar_bases() logger.info("Tripulantes no cadastro: %s", len(bases["cadastro"])) - logger.info("Tripulantes disponiveis: %s", len(bases["disponiveis"])) + logger.info("Tripulantes disponíveis: %s", len(bases["disponiveis"])) logger.info("Aeronaves informadas: %s", len(bases["aeronaves"])) logger.info("Rotas acionadas: %s", len(bases["rotas"])) diff --git a/scripts/04_gerar_colunas_candidatas.py b/scripts/04_gerar_colunas_candidatas.py index e480e95..e3e2d57 100644 --- a/scripts/04_gerar_colunas_candidatas.py +++ b/scripts/04_gerar_colunas_candidatas.py @@ -1,11 +1,11 @@ -"""Geracao das colunas candidatas viaveis. +"""Geração das colunas candidatas viáveis. Entradas: Bases preparadas por 03_preparar_dados.py. Saidas: - DataFrame em que cada linha representa uma escala possivel: rota acionada, - missao local ou sobreaviso, com dupla, aeronave, score, custo e metadados. + DataFrame em que cada linha representa uma escala possível: rota acionada, + missão local ou sobreaviso, com dupla, aeronave, score, custo e metadados. Papel no pipeline: Materializa o espaco de decisoes do MILP. A compatibilidade de aeronave, @@ -21,13 +21,13 @@ io_utils = importlib.import_module("02_io_utils") def gerar_colunas_do_dia(bases: dict): - """Gera colunas candidatas para a data e parametros informados nas bases.""" + """Gera colunas candidatas para a data e parâmetros informados nas bases.""" from src.planejador_missao.candidates import gerar_colunas parametros = bases["parametros"] - # Cada coluna candidata equivale a uma variavel x_j do MILP. + # Cada coluna candidata equivale a uma variável x_j do MILP. # As regras de dupla, qualificacao, instrucao e indisponibilidade sao - # aplicadas antes do solver para reduzir o modelo a alternativas viaveis. + # aplicadas antes do solver para reduzir o modelo a alternativas viáveis. return gerar_colunas( parametros["data_planejamento"], bases["rotas"], diff --git a/scripts/05_modelo_milp.py b/scripts/05_modelo_milp.py index ad6d80c..d6bd6b0 100644 --- a/scripts/05_modelo_milp.py +++ b/scripts/05_modelo_milp.py @@ -1,7 +1,7 @@ -"""Modelo MILP do Planejador Missao usando scipy.optimize.milp/HiGHS. +"""Modelo MILP do Planejador Missão usando scipy.optimize.milp/HiGHS. Entradas: - Colunas candidatas viaveis geradas por 04_gerar_colunas_candidatas.py. + Colunas candidatas viáveis geradas por 04_gerar_colunas_candidatas.py. Saidas: Subconjunto de colunas selecionadas pelo solver, representando a escala @@ -14,12 +14,12 @@ Metodologia: Restricoes implementadas no motor src.planejador_missao.optimizer: a) rota acionada obrigatoria coberta exatamente uma vez; - b) cada tripulante aparece no maximo uma vez no dia; + b) cada tripulante aparece no máximo uma vez no dia; c) cada aeronave recebe cobertura principal por rota acionada ou SBV; - d) aeronave nao executa escalas conflitantes de rota/local no mesmo dia; + d) aeronave não executa escalas conflitantes de rota/local no mesmo dia; e) qualificacao, indisponibilidade e regras de instrucao entram no filtro de - colunas candidatas, logo colunas inviaveis nao chegam ao MILP; - f) regras especificas de rota, missao local e SBV sao preservadas por tipo. + colunas candidatas, logo colunas inviáveis não chegam ao MILP; + f) regras específicas de rota, missão local e SBV são preservadas por tipo. """ from __future__ import annotations @@ -36,10 +36,10 @@ def resolver_escala(colunas, rotas, aeronaves): from src.planejador_missao.optimizer import resolver_milp if colunas.empty: - raise RuntimeError("Erro: nao ha colunas candidatas viaveis para montar o MILP.") + raise RuntimeError("Erro: não há colunas candidatas viáveis para montar o MILP.") # O resolvedor cria x_j binario para cada linha de colunas. - # A funcao objetivo usa -score_milp porque o HiGHS via SciPy resolve + # A função objetivo usa -score_milp porque o HiGHS via SciPy resolve # minimizacao; isso preserva a interpretacao de maximizar score. return resolver_milp(colunas, rotas, aeronaves) diff --git a/scripts/06_exportar_resultados.py b/scripts/06_exportar_resultados.py index 02ba2f1..810f900 100644 --- a/scripts/06_exportar_resultados.py +++ b/scripts/06_exportar_resultados.py @@ -5,10 +5,10 @@ Entradas: Saidas: Planilha em resultados/planejamento_diario_YYYY-MM-DD.xlsx e registros de - historico operacional para atualizacao do acumulado. + histórico operacional para atualização do acumulado. Papel no pipeline: - Converte a solucao matematica em artefatos utilizaveis pelo esquadrao. + Converte a solução matemática em artefatos utilizáveis pelo esquadrão. """ from __future__ import annotations diff --git a/scripts/07_validacao_2025.py b/scripts/07_validacao_2025.py index 7d3a6ea..9d2ce60 100644 --- a/scripts/07_validacao_2025.py +++ b/scripts/07_validacao_2025.py @@ -11,8 +11,8 @@ Saidas: resultados/validacao/validacao_2025_barras.png Papel no pipeline: - Mantem a demanda historica de 2025, gera candidatos compativeis para cada - slot historico e usa MILP para redistribuir tripulantes preservando horas. + Mantém a demanda histórica de 2025, gera candidatos compatíveis para cada + slot histórico e usa MILP para redistribuir tripulantes preservando horas. """ from __future__ import annotations @@ -89,7 +89,7 @@ def _tripulantes(valor: object) -> list[str]: def importar_voos_2025(origem: Path = config.ARQUIVO_QUADRO_VOO_2025, destino: Path = config.ARQUIVO_VOOS_2025) -> pd.DataFrame: - """Importa voos realizados de 2025 da aba VOOS para o CSV de validacao.""" + """Importa voos realizados de 2025 da aba VOOS para o CSV de validação.""" io_utils.exigir_arquivos([origem]) raw = pd.read_excel(origem, sheet_name=config.ABAS["voos_2025"], header=None) linhas = [] @@ -176,7 +176,7 @@ def _png_barras(path: Path, metricas: pd.DataFrame) -> Path: def executar_validacao_2025(importar: bool = True, logger=None) -> dict: - """Executa a validacao completa e exporta os artefatos finais.""" + """Executa a validação completa e exporta os artefatos finais.""" io_utils.configurar_ambiente() logger = logger or io_utils.configurar_logger("validacao_2025") inicio = pd.Timestamp.now() @@ -189,9 +189,9 @@ def executar_validacao_2025(importar: bool = True, logger=None) -> dict: voos = carregar_voos_2025(config.ARQUIVO_VOOS_2025) if voos.empty: - raise RuntimeError("Erro: base de validacao 2025 vazia. Verifique dados/validacao/voos_2025.csv.") + raise RuntimeError("Erro: base de validação 2025 vazia. Verifique dados/validacao/voos_2025.csv.") if (voos["horas_voadas"] < 0).any(): - raise RuntimeError("Erro: foram encontradas horas voadas negativas na validacao 2025.") + raise RuntimeError("Erro: foram encontradas horas voadas negativas na validação 2025.") resultado = otimizar_equalizacao_50( config.PROJECT_ROOT, @@ -204,7 +204,7 @@ def executar_validacao_2025(importar: bool = True, logger=None) -> dict: total_real = float(metricas.loc[metricas["cenario"] == "real_2025", "horas_total"].iloc[0]) total_otim = float(metricas.loc[metricas["cenario"] == "otimizado_meta_50", "horas_total"].iloc[0]) if abs(total_real - total_otim) > 0.01: - logger.warning("Horas nao preservadas: real=%s otimizado=%s", total_real, total_otim) + logger.warning("Horas não preservadas: real=%s otimizado=%s", total_real, total_otim) resumo_path = config.VALIDACAO_DIR / "validacao_2025_resumo.xlsx" detalhada_path = config.VALIDACAO_DIR / "validacao_2025_detalhada.xlsx" @@ -231,7 +231,7 @@ def executar_validacao_2025(importar: bool = True, logger=None) -> dict: logger.info("Alocacoes otimizadas: %s", len(resultado["otimizados"])) logger.info("Tripulantes comparados: %s", len(resultado["comparativo"])) logger.info("Status solver: %s", resultado["status_solver"]["mensagem"]) - logger.info("Tempo de validacao: %.1f s", duracao) + logger.info("Tempo de validação: %.1f s", duracao) logger.info("Arquivos exportados: %s | %s | %s | %s", resumo_path, detalhada_path, metricas_path, grafico_path) return { @@ -242,11 +242,11 @@ def executar_validacao_2025(importar: bool = True, logger=None) -> dict: def main() -> None: - """Ponto de entrada da validacao 2025.""" + """Ponto de entrada da validação 2025.""" logger = io_utils.configurar_logger("validacao_2025") saida = executar_validacao_2025(importar=True, logger=logger) metricas = saida["resultado"]["metricas"] - print("\n=== Validacao 2025 concluida ===") + print("\n=== Validação 2025 concluída ===") print(metricas.to_string(index=False)) print("\nArquivos gerados:") for path in saida["arquivos"]: diff --git a/scripts/08_app_utils.py b/scripts/08_app_utils.py index c117b18..4cf03b4 100644 --- a/scripts/08_app_utils.py +++ b/scripts/08_app_utils.py @@ -30,7 +30,7 @@ def comando_python_app() -> list[str]: def abrir_app() -> None: - """Inicia o app em segundo plano sem janela de console quando possivel.""" + """Inicia o app em segundo plano sem janela de console quando possível.""" io_utils.configurar_ambiente() subprocess.Popen(comando_python_app(), cwd=config.PROJECT_ROOT) diff --git a/scripts/09_testar_instalacao.py b/scripts/09_testar_instalacao.py index 4dc2aed..8f6a858 100644 --- a/scripts/09_testar_instalacao.py +++ b/scripts/09_testar_instalacao.py @@ -1,13 +1,13 @@ -"""Teste rapido de instalacao do Planejador Missao. +"""Teste rápido de instalação do Planejador Missão. Entradas: Ambiente Python atual, dependencias instaladas e arquivos em dados/. -Saidas: - Diagnostico amigavel no terminal e log em logs/execucao.log. +Saídas: + Diagnóstico amigável no terminal e log em logs/execucao.log. Papel no pipeline: - Permite verificar rapidamente se o computador esta pronto para executar o + Permite verificar rapidamente se o computador está pronto para executar o planejador e o solver HiGHS/scipy.optimize.milp. """ @@ -56,7 +56,7 @@ def main() -> None: testar_solver_minimo() print("Solver scipy.optimize.milp/HiGHS OK") logger.info("Teste de instalacao concluido com sucesso.") - print("Ambiente OK para executar o Planejador Missao.") + print("Ambiente OK para executar o Planejador Missão.") if __name__ == "__main__": diff --git a/scripts/_arquivados/importar_quadro_voo_2025.py b/scripts/_arquivados/importar_quadro_voo_2025.py index 0dddc99..0135248 100644 --- a/scripts/_arquivados/importar_quadro_voo_2025.py +++ b/scripts/_arquivados/importar_quadro_voo_2025.py @@ -1,4 +1,4 @@ -"""Importa a aba VOOS do Quadro de Voo 2025 para a base de validacao.""" +"""Importa a aba VOOS do Quadro de Voo 2025 para a base de validação.""" from __future__ import annotations @@ -110,7 +110,7 @@ def importar(origem: Path, destino: Path) -> pd.DataFrame: def main() -> None: - parser = argparse.ArgumentParser(description="Importa voos realizados de 2025 para a validacao.") + parser = argparse.ArgumentParser(description="Importa voos realizados de 2025 para a validação.") parser.add_argument( "--origem", type=Path, @@ -121,7 +121,7 @@ def main() -> None: "--destino", type=Path, default=BASE_DIR / "dados" / "validacao" / "voos_2025.csv", - help="CSV de validacao gerado.", + help="CSV de validação gerado.", ) args = parser.parse_args() diff --git a/scripts/_arquivados/validar_2025.py b/scripts/_arquivados/validar_2025.py index 466ff7f..964cc23 100644 --- a/scripts/_arquivados/validar_2025.py +++ b/scripts/_arquivados/validar_2025.py @@ -1,4 +1,4 @@ -"""Executa a validacao retrospectiva com voos de 2025.""" +"""Executa a validação retrospectiva com voos de 2025.""" from __future__ import annotations @@ -29,7 +29,7 @@ def main() -> None: voos = carregar_voos_2025(entrada) if voos.empty: - print("Base de validacao ainda vazia.") + print("Base de validação ainda vazia.") print(f"Preencha o arquivo: {entrada}") print("Colunas esperadas: data,aeronave,tipo_escala,tripulante,funcao,oi,horas_voadas,sbv") return @@ -43,13 +43,13 @@ def main() -> None: ) arquivo = salvar_relatorio_validacao(BASE_DIR, resultado, meta_horas=args.meta) - print("\n=== Validacao 2025 concluida ===") + print("\n=== Validação 2025 concluída ===") print(f"Meta de equalizacao: {args.meta:g} horas") print(f"Voos/slots avaliados: {len(resultado['slots'])}") print(f"Tripulantes comparados: {len(resultado['comparativo'])}") - print(f"Relatorio: {arquivo}") + print(f"Relatório: {arquivo}") print(f"Solver: {resultado['status_solver']['mensagem']}") - print("\nMetricas:") + print("\nMétricas:") print(resultado["metricas"].to_string(index=False)) diff --git a/src/planejador_missao/__init__.py b/src/planejador_missao/__init__.py index ef8516f..fac32dd 100644 --- a/src/planejador_missao/__init__.py +++ b/src/planejador_missao/__init__.py @@ -1,2 +1 @@ -"""Planejador de missao e sobreaviso por MILP.""" - +"""Planejador de missão e sobreaviso por MILP.""" diff --git a/src/planejador_missao/candidates.py b/src/planejador_missao/candidates.py index 9c5bbb5..451adc2 100644 --- a/src/planejador_missao/candidates.py +++ b/src/planejador_missao/candidates.py @@ -172,13 +172,13 @@ def gerar_colunas(data_planejamento, rotas, aeronaves, cadastro, indisponibilida candidatos = filtrar_por_horas_aeronave(candidatos, horas_disp) if candidatos.empty: continue - colunas.append(gerar_duplas(aplicar_prioridades(candidatos), criterio_missao, "ROTA_ACIONADA", av, data_planejamento, cadastro, indisponibilidades, progresso, catalogo, disponiveis, rota_id=rota_id + 1, rota=rota, observacao="Missao de rota acionada")) + colunas.append(gerar_duplas(aplicar_prioridades(candidatos), criterio_missao, "ROTA_ACIONADA", av, data_planejamento, cadastro, indisponibilidades, progresso, catalogo, disponiveis, rota_id=rota_id + 1, rota=rota, observacao="Missão de rota acionada")) for av in livres: horas_disp = horas_disponiveis(aeronaves, av) candidatos_local = candidatos_missao(data_planejamento, av, "LOCAL", cadastro, indisponibilidades, progresso, catalogo, disponiveis) candidatos_local = filtrar_por_horas_aeronave(candidatos_local, horas_disp) if not candidatos_local.empty: - colunas.append(gerar_duplas(aplicar_prioridades(candidatos_local), criterio_missao, "MISSAO_LOCAL", av, data_planejamento, cadastro, indisponibilidades, progresso, catalogo, disponiveis, observacao="Missao local planejada para progressao operacional")) + colunas.append(gerar_duplas(aplicar_prioridades(candidatos_local), criterio_missao, "MISSAO_LOCAL", av, data_planejamento, cadastro, indisponibilidades, progresso, catalogo, disponiveis, observacao="Missão local planejada para progressão operacional")) candidatos_sbv = [] for _, row in cadastro[cadastro[f"qt_{av.lower()}"]].iterrows(): if row["tripulante"] in set(disponiveis["tripulante"]) and piloto_disponivel(row["tripulante"], data_planejamento, indisponibilidades): @@ -191,7 +191,7 @@ def gerar_colunas(data_planejamento, rotas, aeronaves, cadastro, indisponibilida r["bloqueado"] = False candidatos_sbv.append(r) if len(candidatos_sbv) >= 2: - colunas.append(gerar_duplas(pd.DataFrame(candidatos_sbv), criterio_sbv, "SBV", av, data_planejamento, cadastro, indisponibilidades, progresso, catalogo, disponiveis, observacao="Sobreaviso para missao de rota")) + colunas.append(gerar_duplas(pd.DataFrame(candidatos_sbv), criterio_sbv, "SBV", av, data_planejamento, cadastro, indisponibilidades, progresso, catalogo, disponiveis, observacao="Sobreaviso para missão de rota")) if not colunas: return pd.DataFrame() df = pd.concat(colunas, ignore_index=True) diff --git a/src/planejador_missao/data_io.py b/src/planejador_missao/data_io.py index 2e1e68c..31303a0 100644 --- a/src/planejador_missao/data_io.py +++ b/src/planejador_missao/data_io.py @@ -1,4 +1,4 @@ -"""Leitura das bases de entrada e escrita do historico operacional.""" +"""Leitura das bases de entrada e escrita do histórico operacional.""" from __future__ import annotations diff --git a/src/planejador_missao/main.py b/src/planejador_missao/main.py index 6a9dc83..b42c6e0 100644 --- a/src/planejador_missao/main.py +++ b/src/planejador_missao/main.py @@ -51,7 +51,7 @@ def executar_planejamento(base_dir: Path) -> dict: # Bloco 03 - Modelo MILP e selecao final. solucao = resolver_milp(colunas, rotas, aeronaves) - # Bloco 04 - Persistencia operacional e relatorio. + # Bloco 04 - Persistência operacional e relatório. registros = gerar_registros(solucao, catalogo) salvar_historico(base_dir, registros) arquivo_saida = gerar_excel(base_dir, parametros["data_planejamento"], solucao) diff --git a/src/planejador_missao/optimizer.py b/src/planejador_missao/optimizer.py index fae8d56..f5a629d 100644 --- a/src/planejador_missao/optimizer.py +++ b/src/planejador_missao/optimizer.py @@ -16,7 +16,7 @@ def resolver_milp(colunas: pd.DataFrame, rotas: pd.DataFrame, aeronaves: pd.Data bounds = Bounds(np.zeros(n), np.ones(n)) restricoes = [] - # R1 - Cada tripulante aparece no maximo uma vez. + # R1 - Cada tripulante aparece no máximo uma vez. for trip in sorted(set(colunas["trip1"]).union(set(colunas["trip2"]))): linha = np.zeros(n) linha[(colunas["trip1"] == trip) | (colunas["trip2"] == trip)] = 1 @@ -28,7 +28,7 @@ def resolver_milp(colunas: pd.DataFrame, rotas: pd.DataFrame, aeronaves: pd.Data idx_local = (colunas["aeronave"] == av) & (colunas["tipo_escala"] == "MISSAO_LOCAL") idx_sbv = (colunas["aeronave"] == av) & (colunas["tipo_escala"] == "SBV") if not (idx_rota | idx_sbv).any(): - raise RuntimeError(f"Modelo inviavel: nao ha rota nem SBV candidato para a aeronave {av}.") + raise RuntimeError(f"Modelo inviável: não há rota nem SBV candidato para a aeronave {av}.") linha = np.zeros(n) linha[idx_rota | idx_sbv] = 1 restricoes.append(LinearConstraint(linha, 1, 1)) @@ -48,13 +48,13 @@ def resolver_milp(colunas: pd.DataFrame, rotas: pd.DataFrame, aeronaves: pd.Data for rota_id in range(1, len(rotas) + 1): idx = (colunas["tipo_escala"] == "ROTA_ACIONADA") & (colunas["rota_id"] == rota_id) if not idx.any(): - raise RuntimeError(f"Modelo inviavel: nao ha coluna candidata para a rota acionada {rota_id}.") + raise RuntimeError(f"Modelo inviável: não há coluna candidata para a rota acionada {rota_id}.") linha = np.zeros(n) linha[idx] = 1 restricoes.append(LinearConstraint(linha, 1, 1)) resultado = milp(c=-colunas["score_milp"].to_numpy(dtype=float), integrality=integrality, bounds=bounds, constraints=restricoes) if not resultado.success: - raise RuntimeError(f"MILP nao encontrou solucao viavel: {resultado.message}") + raise RuntimeError(f"MILP não encontrou solução viável: {resultado.message}") selecionadas = resultado.x > 0.5 return colunas.loc[selecionadas].reset_index(drop=True) diff --git a/src/planejador_missao/report.py b/src/planejador_missao/report.py index 7e53e9f..6187c1c 100644 --- a/src/planejador_missao/report.py +++ b/src/planejador_missao/report.py @@ -1,4 +1,4 @@ -"""Registro de horas e geracao do relatorio Excel final.""" +"""Registro de horas e geração do relatório Excel final.""" from __future__ import annotations @@ -107,7 +107,7 @@ def gerar_excel(base_dir: Path, data_planejamento, solucao: pd.DataFrame) -> Pat ws["A2"].font = Font(color="1F4E78", bold=True, size=18) ws["A2"].alignment = Alignment(horizontal="center") - escrever_tabela(ws, "MISSAO ACIONADA", bloco(solucao, "ROTA_ACIONADA"), 5, 1, "C00000") + escrever_tabela(ws, "MISSÃO ACIONADA", bloco(solucao, "ROTA_ACIONADA"), 5, 1, "C00000") escrever_tabela(ws, "VOOS LOCAIS", bloco(solucao, "MISSAO_LOCAL"), 5, 7, "548235") escrever_tabela(ws, "SOBREAVISO", bloco(solucao, "SBV"), 5, 13, "8064A2") for col in range(1, 18): diff --git a/src/planejador_missao/rules.py b/src/planejador_missao/rules.py index 639b3b0..b0ec434 100644 --- a/src/planejador_missao/rules.py +++ b/src/planejador_missao/rules.py @@ -1,4 +1,4 @@ -"""Regras operacionais de disponibilidade, qualificacao e progressao.""" +"""Regras operacionais de disponibilidade, qualificação e progressão.""" from __future__ import annotations diff --git a/src/planejador_missao/utils.py b/src/planejador_missao/utils.py index 94458fb..6b71f78 100644 --- a/src/planejador_missao/utils.py +++ b/src/planejador_missao/utils.py @@ -1,4 +1,4 @@ -"""Utilitarios pequenos de normalizacao usados em todo o planejador.""" +"""Utilitários pequenos de normalização usados em todo o planejador.""" from __future__ import annotations diff --git a/src/planejador_missao/validacao.py b/src/planejador_missao/validacao.py index efb46e4..61892ae 100644 --- a/src/planejador_missao/validacao.py +++ b/src/planejador_missao/validacao.py @@ -1,4 +1,4 @@ -"""Validacao retrospectiva usando voos historicos.""" +"""Validação retrospectiva usando voos históricos.""" from __future__ import annotations @@ -110,11 +110,11 @@ def otimizar_equalizacao_50( slots = _montar_slots(voos) if slots.empty: - raise RuntimeError("A base de 2025 nao possui linhas com horas_voadas maiores que zero.") + raise RuntimeError("A base de 2025 não possui linhas com horas_voadas maiores que zero.") trips = sorted(set(voos["tripulante"]) & trips_cadastro) if not trips: - raise RuntimeError("Nao ha tripulantes da base de 2025 presentes no cadastro atual.") + raise RuntimeError("Não há tripulantes da base de 2025 presentes no cadastro atual.") pares = [] for s_idx, slot in slots.iterrows(): @@ -124,7 +124,7 @@ def otimizar_equalizacao_50( if not linha.empty and bool(linha.iloc[0].get(qt_col, False)): pares.append((s_idx, t_idx, float(slot["horas_voadas"]))) if not pares: - raise RuntimeError("Nao ha candidatos compativeis entre aeronaves dos voos 2025 e cadastro.") + raise RuntimeError("Não há candidatos compatíveis entre aeronaves dos voos 2025 e cadastro.") n_x = len(pares) n_t = len(trips) @@ -188,7 +188,7 @@ def otimizar_equalizacao_50( options={"time_limit": tempo_limite_segundos, "mip_rel_gap": gap_relativo}, ) if result.x is None: - raise RuntimeError(f"Validacao MILP inviavel: {result.message}") + raise RuntimeError(f"Validação MILP inviável: {result.message}") escolhidos = result.x[:n_x] > 0.5 linhas = [] diff --git a/web_app.py b/web_app.py index e360f5b..30cf25c 100644 --- a/web_app.py +++ b/web_app.py @@ -1,4 +1,4 @@ -"""Interface web local em HTML para o planejador diario.""" +"""Interface web local em HTML para o planejador diário.""" from __future__ import annotations @@ -222,7 +222,7 @@ INDEX_HTML = r""" - Planejador Diario de Missoes + Planejador Diário de Missões