Automatiza componente B e ajuste frontend do ranking

This commit is contained in:
Frederico Castro
2025-12-10 13:49:43 -03:00
parent d1379b4f5c
commit 6f11b7c166
11 changed files with 431 additions and 186 deletions

237
backend/scripts/popular_componente_b.py Executable file → Normal file
View File

@@ -1,18 +1,58 @@
#!/usr/bin/env python3
import sys
"""
Preenche o Componente B (PPG) na TB_RANKING_CONSULTOR usando Oracle remoto.
Usa .env para credenciais e busca em lotes (IN) para reduzir round-trips.
"""
import os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
import sys
from datetime import datetime
from itertools import islice
from pathlib import Path
import cx_Oracle
from datetime import datetime
from dotenv import load_dotenv
ORACLE_LOCAL_USER = "local123"
ORACLE_LOCAL_PASSWORD = "local123"
ORACLE_LOCAL_DSN = "127.0.0.1:1521/XEPDB1"
ROOT_DIR = Path(__file__).resolve().parents[1]
load_dotenv(ROOT_DIR / ".env")
ORACLE_LOCAL_USER = os.getenv("ORACLE_LOCAL_USER", "local123")
ORACLE_LOCAL_PASSWORD = os.getenv("ORACLE_LOCAL_PASSWORD", "local123")
ORACLE_LOCAL_DSN = os.getenv("ORACLE_LOCAL_DSN", "oracle18c:1521/XEPDB1")
ORACLE_REMOTE_USER = os.getenv("ORACLE_REMOTE_USER")
ORACLE_REMOTE_PASSWORD = os.getenv("ORACLE_REMOTE_PASSWORD")
ORACLE_REMOTE_DSN = os.getenv("ORACLE_REMOTE_DSN")
BATCH_IDS = 500
BATCH_UPDATES = 1000
QUERY_BASE = """
SELECT
c.ID_PESSOA,
c.ID_PROGRAMA_SNPG,
p.NM_PROGRAMA,
p.CD_PROGRAMA_PPG,
p.DS_CONCEITO,
p.NM_PROGRAMA_MODALIDADE,
aa.NM_AREA_AVALIACAO,
c.DT_INICIO_VIGENCIA,
c.DT_FIM_VIGENCIA
FROM SUCUPIRA_PAINEL.VM_COORDENADOR c
INNER JOIN SUCUPIRA_PAINEL.VM_PROGRAMA_SUCUPIRA p ON c.ID_PROGRAMA_SNPG = p.ID_PROGRAMA
LEFT JOIN SUCUPIRA_PAINEL.VM_AREA_CONHECIMENTO ac ON p.ID_AREA_CONHECIMENTO_ATUAL = ac.ID_AREA_CONHECIMENTO
LEFT JOIN SUCUPIRA_PAINEL.VM_AREA_AVALIACAO aa ON ac.ID_AREA_AVALIACAO = aa.ID_AREA_AVALIACAO
WHERE c.ID_PESSOA IN ({placeholders})
"""
def chunked(iterable, size):
it = iter(iterable)
while True:
bloco = list(islice(it, size))
if not bloco:
break
yield bloco
ORACLE_REMOTE_USER = "FREDERICOAC"
ORACLE_REMOTE_PASSWORD = "FREDEricoac"
ORACLE_REMOTE_DSN = "oracledhtsrv02.hom.capes.gov.br:1521/hom_dr"
def calcular_componente_b(coordenacoes):
if not coordenacoes:
@@ -21,115 +61,124 @@ def calcular_componente_b(coordenacoes):
base = 70
anos_totais = 0
for c in coordenacoes:
inicio = c[7]
fim = c[8]
if fim:
anos = (fim - inicio).days // 365
else:
anos = (datetime.now() - inicio).days // 365
anos_totais += anos
inicio = c.get("DT_INICIO_VIGENCIA")
fim = c.get("DT_FIM_VIGENCIA")
if not inicio:
continue
if fim and fim < inicio:
fim = None
fim_ref = fim or datetime.now()
anos_totais += (fim_ref - inicio).days // 365
tempo = min(int(anos_totais * 5), 50)
programas_distintos = len(set(c[1] for c in coordenacoes))
programas_distintos = len({c.get("ID_PROGRAMA_SNPG") for c in coordenacoes})
extras = min((programas_distintos - 1) * 20, 40) if programas_distintos > 1 else 0
maior_nota = 0
for c in coordenacoes:
nota_str = str(c[4]).strip() if c[4] else ""
if nota_str == '7':
maior_nota = max(maior_nota, 7)
elif nota_str == '6':
maior_nota = max(maior_nota, 6)
elif nota_str == '5':
maior_nota = max(maior_nota, 5)
elif nota_str == '4':
maior_nota = max(maior_nota, 4)
elif nota_str == '3':
maior_nota = max(maior_nota, 3)
nota_str = str(c.get("DS_CONCEITO") or "").strip()
if nota_str in ("7", "6", "5", "4", "3"):
maior_nota = max(maior_nota, int(nota_str))
mapa_nota = {7: 20, 6: 15, 5: 10, 4: 5, 3: 0}
bonus = mapa_nota.get(maior_nota, 0)
bonus_nota = {7: 20, 6: 15, 5: 10, 4: 5, 3: 0}.get(maior_nota, 0)
return base + tempo + extras + bonus
return base + tempo + extras + bonus_nota
print("Conectando Oracle LOCAL...")
conn_local = cx_Oracle.connect(ORACLE_LOCAL_USER, ORACLE_LOCAL_PASSWORD, ORACLE_LOCAL_DSN)
print("Conectando Oracle REMOTO...")
conn_remote = cx_Oracle.connect(ORACLE_REMOTE_USER, ORACLE_REMOTE_PASSWORD, ORACLE_REMOTE_DSN)
def fetch_ppg_por_lote(cursor_remote, ids):
placeholders = ", ".join(f":id{i}" for i in range(len(ids)))
params = {f"id{i}": val for i, val in enumerate(ids)}
cursor_remote.execute(QUERY_BASE.format(placeholders=placeholders), params)
cols = [col[0] for col in cursor_remote.description]
return [dict(zip(cols, row)) for row in cursor_remote.fetchall()]
cursor_local = conn_local.cursor()
cursor_remote = conn_remote.cursor()
print("Buscando consultores...")
cursor_local.execute("SELECT ID_PESSOA FROM TB_RANKING_CONSULTOR")
ids_pessoas = [row[0] for row in cursor_local.fetchall()]
print(f"Total: {len(ids_pessoas)} consultores")
def main():
if not ORACLE_REMOTE_USER or not ORACLE_REMOTE_PASSWORD or not ORACLE_REMOTE_DSN:
print("ERRO: credenciais do Oracle REMOTO não encontradas (.env).")
sys.exit(1)
processados = 0
com_ppg = 0
batch_updates = []
print("Conectando Oracle LOCAL (ranking)...")
conn_local = cx_Oracle.connect(ORACLE_LOCAL_USER, ORACLE_LOCAL_PASSWORD, ORACLE_LOCAL_DSN)
cursor_local = conn_local.cursor()
for id_pessoa in ids_pessoas:
try:
cursor_remote.execute("""
SELECT
c.ID_PESSOA,
c.ID_PROGRAMA_SNPG,
p.NM_PROGRAMA,
p.CD_PROGRAMA_PPG,
p.DS_CONCEITO,
p.NM_PROGRAMA_MODALIDADE,
aa.NM_AREA_AVALIACAO,
c.DT_INICIO_VIGENCIA,
c.DT_FIM_VIGENCIA
FROM SUCUPIRA_PAINEL.VM_COORDENADOR c
INNER JOIN SUCUPIRA_PAINEL.VM_PROGRAMA_SUCUPIRA p ON c.ID_PROGRAMA_SNPG = p.ID_PROGRAMA
LEFT JOIN SUCUPIRA_PAINEL.VM_AREA_CONHECIMENTO ac ON p.ID_AREA_CONHECIMENTO_ATUAL = ac.ID_AREA_CONHECIMENTO
LEFT JOIN SUCUPIRA_PAINEL.VM_AREA_AVALIACAO aa ON ac.ID_AREA_AVALIACAO = aa.ID_AREA_AVALIACAO
WHERE c.ID_PESSOA = :id_pessoa
""", {"id_pessoa": id_pessoa})
print("Conectando Oracle REMOTO (SUCUPIRA_PAINEL)...")
conn_remote = cx_Oracle.connect(ORACLE_REMOTE_USER, ORACLE_REMOTE_PASSWORD, ORACLE_REMOTE_DSN)
cursor_remote = conn_remote.cursor()
cursor_remote.arraysize = 2000
coordenacoes = cursor_remote.fetchall()
print("Buscando IDs a atualizar (COMPONENTE_B = 0)...")
cursor_local.execute("SELECT ID_PESSOA FROM TB_RANKING_CONSULTOR WHERE NVL(COMPONENTE_B,0) = 0")
ids_pessoas = [row[0] for row in cursor_local.fetchall()]
total_ids = len(ids_pessoas)
print(f"Total: {total_ids} consultores pendentes")
processados = 0
com_ppg = 0
batch_updates = []
for lote_ids in chunked(ids_pessoas, BATCH_IDS):
try:
registros = fetch_ppg_por_lote(cursor_remote, lote_ids)
except Exception as e:
print(f"Erro ao buscar PPG para lote (IDs {lote_ids[:3]}...): {e}")
continue
coord_por_pessoa = {}
for r in registros:
coord_por_pessoa.setdefault(r["ID_PESSOA"], []).append(r)
for id_pessoa in lote_ids:
coordenacoes = coord_por_pessoa.get(id_pessoa, [])
if not coordenacoes:
processados += 1
continue
if coordenacoes:
comp_b = calcular_componente_b(coordenacoes)
batch_updates.append((comp_b, id_pessoa))
batch_updates.append({"comp_b": comp_b, "id_pessoa": id_pessoa})
com_ppg += 1
processados += 1
processados += 1
if len(batch_updates) >= BATCH_UPDATES:
cursor_local.executemany(
"""
UPDATE TB_RANKING_CONSULTOR
SET COMPONENTE_B = :comp_b,
PONTUACAO_TOTAL = COMPONENTE_A + :comp_b + COMPONENTE_C + COMPONENTE_D
WHERE ID_PESSOA = :id_pessoa
""",
batch_updates,
)
conn_local.commit()
print(f"Processados: {processados}/{total_ids} | Com PPG: {com_ppg}")
batch_updates = []
if len(batch_updates) >= 1000:
cursor_local.executemany(
"UPDATE TB_RANKING_CONSULTOR SET COMPONENTE_B = :comp_b, PONTUACAO_TOTAL = COMPONENTE_A + :comp_b + COMPONENTE_C + COMPONENTE_D WHERE ID_PESSOA = :id_pessoa",
batch_updates
)
conn_local.commit()
print(f"Processados: {processados}/{len(ids_pessoas)} | Com PPG: {com_ppg}")
batch_updates = []
if batch_updates:
cursor_local.executemany(
"""
UPDATE TB_RANKING_CONSULTOR
SET COMPONENTE_B = :comp_b,
PONTUACAO_TOTAL = COMPONENTE_A + :comp_b + COMPONENTE_C + COMPONENTE_D
WHERE ID_PESSOA = :id_pessoa
""",
batch_updates,
)
conn_local.commit()
except Exception as e:
print(f"Erro no consultor {id_pessoa}: {e}")
if batch_updates:
cursor_local.executemany(
"UPDATE TB_RANKING_CONSULTOR SET COMPONENTE_B = :comp_b, PONTUACAO_TOTAL = COMPONENTE_A + :comp_b + COMPONENTE_C + COMPONENTE_D WHERE ID_PESSOA = :id_pessoa",
batch_updates
)
print("\nAtualizando posições...")
cursor_local.callproc("SP_ATUALIZAR_POSICOES")
conn_local.commit()
print(f"\nFinalizado!")
print(f"Total processados: {processados}")
print(f"Com PPG: {com_ppg}")
print("\nFinalizado!")
print(f"Total processados: {processados}")
print(f"Com PPG: {com_ppg}")
print("\nAtualizando posições...")
cursor_local.callproc("SP_ATUALIZAR_POSICOES")
conn_local.commit()
cursor_local.close()
cursor_remote.close()
conn_local.close()
conn_remote.close()
cursor_local.close()
cursor_remote.close()
conn_local.close()
conn_remote.close()
print("Concluído!")
if __name__ == "__main__":
main()