#!/usr/bin/env python3 """ 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 import sys from datetime import datetime from itertools import islice from pathlib import Path import cx_Oracle from dotenv import load_dotenv 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 def calcular_componente_b(coordenacoes): if not coordenacoes: return 0 base = 70 anos_totais = 0 for c in coordenacoes: 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({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.get("DS_CONCEITO") or "").strip() if nota_str in ("7", "6", "5", "4", "3"): maior_nota = max(maior_nota, int(nota_str)) bonus_nota = {7: 20, 6: 15, 5: 10, 4: 5, 3: 0}.get(maior_nota, 0) return base + tempo + extras + bonus_nota 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()] 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) print("Conectando Oracle LOCAL (ranking)...") conn_local = cx_Oracle.connect(ORACLE_LOCAL_USER, ORACLE_LOCAL_PASSWORD, ORACLE_LOCAL_DSN) cursor_local = conn_local.cursor() 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 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 comp_b = calcular_componente_b(coordenacoes) batch_updates.append({"comp_b": comp_b, "id_pessoa": id_pessoa}) com_ppg += 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 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("\nAtualizando posições...") cursor_local.callproc("SP_ATUALIZAR_POSICOES") conn_local.commit() print("\nFinalizado!") print(f"Total processados: {processados}") print(f"Com PPG: {com_ppg}") cursor_local.close() cursor_remote.close() conn_local.close() conn_remote.close() if __name__ == "__main__": main()