Initial project import

This commit is contained in:
Lucas
2026-06-26 18:07:04 -03:00
commit 6443c0e2dc
48 changed files with 3948 additions and 0 deletions

View File

@@ -0,0 +1,136 @@
"""Importa a aba VOOS do Quadro de Voo 2025 para a base de validacao."""
from __future__ import annotations
import argparse
import re
import sys
from datetime import time, timedelta
from pathlib import Path
import pandas as pd
BASE_DIR = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(BASE_DIR))
from src.planejador_missao.utils import normalizar_texto
COLUNAS_VALIDACAO = ["slot_id", "data", "aeronave", "tipo_escala", "tripulante", "funcao", "oi", "horas_voadas", "sbv"]
def _horas(valor: object) -> float:
if pd.isna(valor):
return 0.0
if isinstance(valor, timedelta):
return round(valor.total_seconds() / 3600, 2)
if isinstance(valor, time):
return round(valor.hour + valor.minute / 60 + valor.second / 3600, 2)
if isinstance(valor, str):
texto = valor.strip()
if not texto or texto == "-":
return 0.0
partes = texto.split(":")
if len(partes) >= 2:
return round(float(partes[0]) + float(partes[1]) / 60, 2)
return float(texto.replace(",", "."))
if isinstance(valor, (int, float)):
return round(float(valor) * 24, 2) if 0 < float(valor) < 1 else float(valor)
return 0.0
def _aeronave(valor: object) -> str:
texto = normalizar_texto(valor)
match = re.search(r"(\d{4})", texto)
if not match:
return ""
numero = match.group(1)
if numero.startswith("20"):
return "C97"
if numero.startswith("23"):
return "C95"
if numero.startswith("27"):
return "C98"
return ""
def _tripulantes(valor: object) -> list[str]:
partes = re.split(r"\s*/\s*", str(valor))
trips = []
for parte in partes:
texto = normalizar_texto(parte)
texto = re.sub(r"\([^)]*\)", " ", texto)
texto = re.sub(r"[^A-Z0-9]+", " ", texto).strip()
if texto and texto != "NAN":
trips.append(texto)
return trips[:2]
def importar(origem: Path, destino: Path) -> pd.DataFrame:
raw = pd.read_excel(origem, sheet_name="VOOS", header=None)
linhas = []
slot_id = 0
for _, row in raw.iterrows():
data = pd.to_datetime(row[2], errors="coerce")
if pd.isna(data) or data.year != 2025:
continue
if normalizar_texto(row[41]) != "REALIZADO":
continue
aeronave = _aeronave(row[37])
if not aeronave:
continue
slot_id += 1
tipo_escala = normalizar_texto(row[1])
horas_voadas = _horas(row[30])
oi = normalizar_texto(row[35])
if not oi or oi == "-":
oi = normalizar_texto(row[32])
for idx, tripulante in enumerate(_tripulantes(row[8]), start=1):
linhas.append(
{
"slot_id": slot_id,
"data": data.date().isoformat(),
"aeronave": aeronave,
"tipo_escala": tipo_escala,
"tripulante": tripulante,
"funcao": "PILOTO" if idx == 1 else "COPILOTO",
"oi": oi,
"horas_voadas": horas_voadas,
"sbv": 0,
}
)
df = pd.DataFrame(linhas, columns=COLUNAS_VALIDACAO)
destino.parent.mkdir(parents=True, exist_ok=True)
df.to_csv(destino, index=False)
return df
def main() -> None:
parser = argparse.ArgumentParser(description="Importa voos realizados de 2025 para a validacao.")
parser.add_argument(
"--origem",
type=Path,
default=BASE_DIR / "dados" / "Quadro de Voo 2025 (2).xlsx",
help="Planilha com aba VOOS.",
)
parser.add_argument(
"--destino",
type=Path,
default=BASE_DIR / "dados" / "validacao" / "voos_2025.csv",
help="CSV de validacao gerado.",
)
args = parser.parse_args()
df = importar(args.origem, args.destino)
print(f"Arquivo gerado: {args.destino}")
print(f"Registros exportados: {len(df)}")
print(f"Voos/slots exportados: {df['slot_id'].nunique() if not df.empty else 0}")
print(f"Tripulantes exportados: {df['tripulante'].nunique() if not df.empty else 0}")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,57 @@
"""Executa a validacao retrospectiva com voos de 2025."""
from __future__ import annotations
import argparse
import sys
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(BASE_DIR))
from src.planejador_missao.validacao import (
carregar_voos_2025,
criar_modelo_voos_2025,
otimizar_equalizacao_50,
salvar_relatorio_validacao,
)
def main() -> None:
parser = argparse.ArgumentParser(description="Valida o planejador com voos historicos de 2025.")
parser.add_argument("--meta", type=float, default=50.0, help="Meta de horas usada na equalizacao.")
parser.add_argument("--tempo-limite", type=float, default=180.0, help="Tempo limite do resolvedor MILP, em segundos.")
parser.add_argument("--gap", type=float, default=0.05, help="Gap relativo aceitavel para encerrar o MILP.")
args = parser.parse_args()
entrada = BASE_DIR / "dados" / "validacao" / "voos_2025.csv"
criar_modelo_voos_2025(entrada)
voos = carregar_voos_2025(entrada)
if voos.empty:
print("Base de validacao ainda vazia.")
print(f"Preencha o arquivo: {entrada}")
print("Colunas esperadas: data,aeronave,tipo_escala,tripulante,funcao,oi,horas_voadas,sbv")
return
resultado = otimizar_equalizacao_50(
BASE_DIR,
voos,
meta_horas=args.meta,
tempo_limite_segundos=args.tempo_limite,
gap_relativo=args.gap,
)
arquivo = salvar_relatorio_validacao(BASE_DIR, resultado, meta_horas=args.meta)
print("\n=== Validacao 2025 concluida ===")
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"Solver: {resultado['status_solver']['mensagem']}")
print("\nMetricas:")
print(resultado["metricas"].to_string(index=False))
if __name__ == "__main__":
main()