# inventario_dashboard_final_v22_corrigido.py
import pandas as pd
import re
from pathlib import Path
import plotly.graph_objects as go
import json
# --- CONFIGURAÇÃO DE COLUNAS ---
COLUNAS = {
"nome": "NOME", "email_pessoal": "Email Pessoal Sendo Usado", "contato_colaborador": "Contato Colaborador",
"contratacao": "Contratação", "status_contrato": "Status do Contrato", "cliente": "CLIENTE",
"base": "BASE", "cargo": "CARGO", "responsavel_direto": "RESPONSÁVEL DIRETO",
"contato_responsavel": "Contato do RESPONSÁVEL",
"contato_1": "Momento do Contato Para Busca de Dados Contato 1",
"contato_2": "Momento do Contato Para Busca de Dados Contato 2",
"contato_3": "Momento do Contato Para Busca de Dados Contato 3",
"subordinado": "Realmente e um Subordinado?", "usa_notebook": "Usa Notebook da Empresa",
"email_corporativo_status": "Tem Email Corporativo?", "email_corporativo_endereco": "Email Corporativo",
"tem_ad": "Tem conta no AD?", "maquina_registrada_status": "Maquina Registrada para o Cliente?",
"maquina_registrada_nome": "Nome da Maquina Registrada", "tag_ativo": "Tag do Ativo",
"numero_serie": "Rótulo de Serviço / Número de Série"
}
# --- Funções Auxiliares ---
def normalizar_texto(texto):
if isinstance(texto, str): return texto.strip().title()
return texto
def normalizar_telefone(numero):
if not isinstance(numero, (str, int, float)): return None
numeros_apenas = re.sub(r'\D', '', str(numero))
if len(numeros_apenas) == 11 and numeros_apenas.startswith('0'):
numeros_apenas = numeros_apenas[1:]
if len(numeros_apenas) in [10, 11]: return f"+55{numeros_apenas}"
if len(numeros_apenas) == 12 and numeros_apenas.startswith('55'): return f"+{numeros_apenas}"
if len(numeros_apenas) == 13 and numeros_apenas.startswith('55'): return f"+{numeros_apenas}"
return None
def extrair_sim_nao(texto):
if not isinstance(texto, str): return "não informado"
texto_limpo = texto.strip().lower()
if texto_limpo.startswith('sim'): return 'sim'
if texto_limpo.startswith('não') or texto_limpo.startswith('nao'): return 'não'
return 'não informado'
def formatar_contratacao(texto):
if not isinstance(texto, str) or texto == "Não informado": return texto
texto_lower = texto.lower()
classe_css = "generico"
if "pessoa juridica" in texto_lower or "pj" in texto_lower: classe_css = "pj"
elif "clt" in texto_lower: classe_css = "clt"
elif "estagio" in texto_lower or "estagiario" in texto_lower: classe_css = "estagio"
return f"{texto}"
def criar_grafico_pizza(valores, legendas, titulo, cores_personalizadas=None):
valores_filtrados, legendas_filtradas, cores_filtradas = [], [], []
cores_originais = cores_personalizadas if cores_personalizadas else ['#28a745', '#dc3545', '#ffc107', '#6c757d']
for i, valor in enumerate(valores):
if valor > 0:
valores_filtrados.append(valor)
legendas_filtradas.append(legendas[i])
cores_filtradas.append(cores_originais[i % len(cores_originais)])
if not valores_filtrados:
return f"
"
for index, linha in df_grupo.iterrows():
filtros_da_linha = [key for key, value in linha.items() if key.startswith('filtro_') and value == True]
data_filters_attr = ' '.join(filtros_da_linha)
contato_colab = linha['Telefone Colaborador Normalizado']
contato_html = f'{contato_colab} ' if contato_colab else "Sem contato"
contratacao_raw = linha.get(COLUNAS['contratacao'])
contratacao = formatar_contratacao(contratacao_raw if pd.notna(contratacao_raw) else "Não informado")
status_contrato = linha.get(COLUNAS['status_contrato'], "Não informado")
status_contrato = status_contrato if pd.notna(status_contrato) else "Não informado"
indicadores_html = ""
info_notebook_original = linha.get(COLUNAS['usa_notebook'])
classe_linha = ""
if (isinstance(info_notebook_original, str) and 'saiu da empresa' in info_notebook_original.lower()) or \
(isinstance(status_contrato, str) and 'saiu da empresa' in status_contrato.lower()):
classe_linha = " class='terminated'"
if linha['filtro_em_conformidade']:
indicadores_html = " Em conformidade"
else:
if linha.get('filtro_ativo_sem_ad') == True: indicadores_html += " Ativo sem Conta AD"
if linha.get('filtro_ativo_nao_registrado') == True: indicadores_html += " Ativo Não Registrado"
if linha['Usa Notebook (Valor)'] in ['não', 'não informado']: indicadores_html += " Ativo Próprio / Não Declarado"
if pd.notna(linha['Email Pessoal']): indicadores_html += " Drive Externo"
if linha['Tem Email Corp (Valor)'] == 'não': indicadores_html += " Sem E-mail Corp."
if str(linha.get(COLUNAS["subordinado"])).lower() == 'não': indicadores_html += " Não Subordinado"
is_cadastro_incompleto = linha['filtro_sem_gestor'] or linha['filtro_sem_contrato'] or linha['filtro_sem_status_contrato'] or linha['filtro_sem_cargo']
if is_cadastro_incompleto: indicadores_html += " Cadastro Incompleto"
if not indicadores_html: indicadores_html = " Fora de Conformidade"
detalhes_html = ""
if 'terminated' in classe_linha:
detalhes_html += "
Colaborador desligado da empresa.
"
if pd.notna(linha.get(COLUNAS['cargo'])):
detalhes_html += f"
{linha[COLUNAS['cargo']]}
"
if linha['Usa Notebook (Valor)'] == 'sim':
detalhes_html += "
Utiliza equipamento fornecido pela empresa.
"
info_maquina = linha.get(COLUNAS['maquina_registrada_nome'])
if pd.notna(info_maquina):
detalhes_html += f"
Máquina: {info_maquina}
"
elif isinstance(info_notebook_original, str) and 'usa pessoal' in info_notebook_original.lower():
detalhes_html += "
Colaborador utiliza equipamento pessoal.
"
elif isinstance(info_notebook_original, str) and 'aguardando equipamento' in info_notebook_original.lower():
detalhes_html += "
"
# FIX: Seção para colaboradores sem gestor
df_sem_gestor = df[df[COLUNAS["responsavel_direto"]].isnull()].copy()
if not df_sem_gestor.empty:
html_secoes_gestores += "
"""
with open('dashboard_interativo_final.html', 'w', encoding='utf-8') as f:
f.write(html_content)
print("Dashboard 'dashboard_interativo_final.html' com KPIs interativos e contagens corrigidas foi gerado com sucesso!")
except (FileNotFoundError, ValueError) as e:
print(f"ERRO: {e}")
except Exception as e:
print(f"Ocorreu um erro inesperado: {e}")