Guilherme Favaron
Fix bullet point formatting for training opportunities and information capture assessment fields
4c67ffd
import gradio as gr
import json
import os
from openai import OpenAI
# Dicionário para tradução dos campos de avaliação para português
traducoes = {
"call_strengths": "Pontos Fortes da Chamada",
"general_assessment": "Avaliação Geral",
"areas_of_improvement": "Áreas de Melhoria",
"training_opportunities": "Oportunidades de Treinamento",
"process_adherence_score": "Pontuação de Aderência ao Processo",
"development_recommendations": "Recomendações de Desenvolvimento",
"process_adherence_scorecard": "Scorecard de Aderência ao Processo",
"information_capture_assessment": "Avaliação de Captura de Informações",
"next_steps_and_meeting_confirmation": "Próximos Passos e Confirmação de Reunião",
# Campos do scorecard
"name": "Nome",
"score": "Pontuação",
"evaluation": "Avaliação"
}
# Configurar cliente OpenAI
client = OpenAI(api_key=os.getenv('OPENAI_API_KEY'))
def traduzir_json_para_portugues(json_content):
"""Traduz o conteúdo JSON para português brasileiro usando OpenAI"""
try:
prompt = f"""Traduza o seguinte JSON de avaliação de chamada para português brasileiro.
IMPORTANTE:
1. Mantenha a estrutura JSON EXATA e válida
2. Traduza APENAS os valores de texto (strings), não as chaves
3. Preserve quebras de linha (\n) nos valores
4. Retorne um JSON válido e bem formatado
5. Não adicione explicações, apenas o JSON
JSON para traduzir:
{json_content}"""
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{
"role": "user",
"content": prompt
}
],
temperature=0.1,
max_tokens=4000
)
# Limpar a resposta e garantir que é um JSON válido
translated_content = response.choices[0].message.content.strip()
# Remover possíveis marcadores de código se existirem
if translated_content.startswith('```json'):
translated_content = translated_content[7:]
if translated_content.endswith('```'):
translated_content = translated_content[:-3]
translated_content = translated_content.strip()
# Validar se é um JSON válido
import json
json.loads(translated_content) # Isso vai gerar erro se não for JSON válido
return translated_content
except Exception as e:
return f"Erro na tradução: {str(e)}"
def processar_transcricao(json_data):
"""Processa o JSON de transcrição para exibição estilizada - apenas segmentos formatados"""
try:
data = json.loads(json_data)
# Informações gerais
html = "<div class='container'>"
html += f"<h2>Transcrição da Chamada</h2>"
html += f"<p><strong>Idioma:</strong> {data.get('language', 'Não especificado')}</p>"
# Segmentos da conversa
html += "<div class='segments'>"
html += "<h3>Segmentos da Conversa</h3>"
html += "<table>"
html += "<tr><th>Início</th><th>Fim</th><th>Falante</th><th>Texto</th></tr>"
for segment in data.get('segments', []):
speaker_class = 'speaker-0' if segment.get('speaker') == 'speaker_0' else 'speaker-1'
html += f"<tr class='{speaker_class}'>"
html += f"<td>{segment.get('start')}</td>"
html += f"<td>{segment.get('end')}</td>"
html += f"<td>{segment.get('speaker')}</td>"
html += f"<td>{segment.get('text')}</td>"
html += "</tr>"
html += "</table>"
html += "</div>"
html += "</div>"
return html
except Exception as e:
return f"<div class='error'>Erro ao processar a transcrição: {str(e)}</div>"
def processar_avaliacao(json_data):
"""Processa o JSON de avaliação para exibição estilizada em português"""
try:
data = json.loads(json_data)
html = "<div class='container'>"
html += "<h2>Avaliação da Chamada</h2>"
# Pontuação geral
score = data.get('process_adherence_score', 'N/A')
html += f"<div class='score-box'><h3>{traducoes['process_adherence_score']}</h3><div class='score'>{score}/5</div></div>"
# Pontos fortes
call_strengths = data.get('call_strengths', '').replace('\n', '<br>')
html += f"<div class='section'>"
html += f"<h3>{traducoes['call_strengths']}</h3>"
html += f"<div class='content'>{call_strengths}</div>"
html += "</div>"
# Avaliação geral
html += f"<div class='section'>"
html += f"<h3>{traducoes['general_assessment']}</h3>"
html += f"<div class='content'>{data.get('general_assessment', '')}</div>"
html += "</div>"
# Áreas de melhoria
areas_improvement = data.get('areas_of_improvement', '').replace('\n', '<br>')
html += f"<div class='section'>"
html += f"<h3>{traducoes['areas_of_improvement']}</h3>"
html += f"<div class='content'>{areas_improvement}</div>"
html += "</div>"
# Oportunidades de treinamento
training_opportunities = data.get('training_opportunities', '').replace('\n', '<br>')
html += f"<div class='section'>"
html += f"<h3>{traducoes['training_opportunities']}</h3>"
html += f"<div class='content'>{training_opportunities}</div>"
html += "</div>"
# Recomendações de desenvolvimento
dev_recommendations = data.get('development_recommendations', '').replace('\n', '<br>')
html += f"<div class='section'>"
html += f"<h3>{traducoes['development_recommendations']}</h3>"
html += f"<div class='content'>{dev_recommendations}</div>"
html += "</div>"
# Scorecard
html += f"<div class='section'>"
html += f"<h3>{traducoes['process_adherence_scorecard']}</h3>"
html += "<table class='scorecard'>"
html += f"<tr><th>{traducoes['name']}</th><th>{traducoes['score']}</th><th>{traducoes['evaluation']}</th></tr>"
for item in data.get('process_adherence_scorecard', []):
score_class = f"score-{item.get('score')}"
html += f"<tr class='{score_class}'>"
html += f"<td>{item.get('name')}</td>"
html += f"<td class='score-cell'>{item.get('score')}</td>"
html += f"<td>{item.get('evaluation')}</td>"
html += "</tr>"
html += "</table>"
html += "</div>"
# Avaliação de captura de informações
information_capture = data.get('information_capture_assessment', '').replace('\n', '<br>')
html += f"<div class='section'>"
html += f"<h3>{traducoes['information_capture_assessment']}</h3>"
html += f"<div class='content'>{information_capture}</div>"
html += "</div>"
# Próximos passos
html += f"<div class='section'>"
html += f"<h3>{traducoes['next_steps_and_meeting_confirmation']}</h3>"
html += f"<div class='content'>{data.get('next_steps_and_meeting_confirmation', '')}</div>"
html += "</div>"
html += "</div>"
return html
except Exception as e:
return f"<div class='error'>Erro ao processar a avaliação: {str(e)}</div>"
def processar_arquivos(arquivo_transcricao=None, arquivo_avaliacao=None, texto_transcricao="", texto_avaliacao="", traduzir=False):
"""Processa os arquivos de transcrição e avaliação ou texto colado diretamente"""
resultado_html = ""
css = """
<style>
.container {
font-family: Arial, sans-serif;
margin: 20px;
padding: 20px;
border-radius: 10px;
background-color: #f9f9f9;
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
h2 {
color: #2c3e50;
border-bottom: 2px solid #3498db;
padding-bottom: 10px;
}
h3 {
color: #2980b9;
margin-top: 20px;
}
.section {
margin: 20px 0;
padding: 15px;
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
}
.content {
line-height: 1.6;
}
table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
th {
background-color: #3498db;
color: white;
padding: 10px;
text-align: left;
}
td {
padding: 8px 10px;
border-bottom: 1px solid #ddd;
}
.speaker-0 {
background-color: #e8f4f8;
}
.speaker-1 {
background-color: #f0f0f0;
}
.score-box {
display: flex;
align-items: center;
margin: 20px 0;
}
.score {
margin-left: 20px;
font-size: 24px;
font-weight: bold;
color: white;
background-color: #3498db;
padding: 10px 15px;
border-radius: 50%;
}
.score-cell {
font-weight: bold;
text-align: center;
}
.score-1 { background-color: #ffcccc; }
.score-2 { background-color: #ffe0b3; }
.score-3 { background-color: #ffffcc; }
.score-4 { background-color: #ccffcc; }
.score-5 { background-color: #b3ffb3; }
.raw-text {
background-color: #f5f5f5;
padding: 15px;
border-radius: 8px;
margin: 15px 0;
white-space: pre-wrap;
}
.error {
color: red;
padding: 10px;
background-color: #ffeeee;
border-radius: 5px;
}
</style>
"""
# Processar transcrição (arquivo ou texto)
conteudo_transcricao = None
if arquivo_transcricao is not None:
try:
conteudo_transcricao = arquivo_transcricao.decode('utf-8')
except Exception as e:
resultado_html += f"<div class='error'>Erro ao ler o arquivo de transcrição: {str(e)}</div>"
elif texto_transcricao.strip():
conteudo_transcricao = texto_transcricao.strip()
if conteudo_transcricao:
resultado_html += processar_transcricao(conteudo_transcricao)
# Processar avaliação (arquivo ou texto)
conteudo_avaliacao = None
if arquivo_avaliacao is not None:
try:
conteudo_avaliacao = arquivo_avaliacao.decode('utf-8')
except Exception as e:
resultado_html += f"<div class='error'>Erro ao ler o arquivo de avaliação: {str(e)}</div>"
elif texto_avaliacao.strip():
conteudo_avaliacao = texto_avaliacao.strip()
if conteudo_avaliacao:
# Se tradução foi solicitada, traduzir primeiro
if traduzir:
try:
conteudo_traduzido = traduzir_json_para_portugues(conteudo_avaliacao)
# Processar apenas o JSON traduzido formatado (sem mostrar o JSON bruto)
resultado_html += processar_avaliacao(conteudo_traduzido)
except Exception as e:
resultado_html += f"<div class='error'>Erro na tradução: {str(e)}</div>"
resultado_html += processar_avaliacao(conteudo_avaliacao)
else:
resultado_html += processar_avaliacao(conteudo_avaliacao)
# Se nenhum conteúdo foi fornecido
if not resultado_html:
resultado_html = "<div class='container'><h2>Por favor, faça upload dos arquivos JSON ou cole o conteúdo nos campos de texto</h2></div>"
# Retornar resultado HTML e campos limpos
return css + resultado_html, "", ""
# Interface Gradio
with gr.Blocks(title="Beautiful Jayson - Estilizador de JSONs de Chamadas") as demo:
gr.Markdown("# Beautiful Jayson 🎨")
gr.Markdown("### Estilizador de JSONs de Chamadas e Avaliações")
gr.Markdown("**Você pode fazer upload de arquivos JSON ou colar o conteúdo diretamente nos campos de texto abaixo.**")
with gr.Row():
with gr.Column():
gr.Markdown("#### 📁 Upload de Arquivos")
arquivo_transcricao = gr.File(label="Upload do JSON de Transcrição")
arquivo_avaliacao = gr.File(label="Upload do JSON de Avaliação")
gr.Markdown("#### 📝 Ou Cole o JSON Diretamente")
texto_transcricao = gr.Textbox(
label="JSON de Transcrição",
placeholder="Cole aqui o conteúdo JSON da transcrição...",
lines=8,
max_lines=15
)
texto_avaliacao = gr.Textbox(
label="JSON de Avaliação",
placeholder="Cole aqui o conteúdo JSON da avaliação...",
lines=8,
max_lines=15
)
gr.Markdown("#### 🌐 Opções de Tradução")
traduzir_checkbox = gr.Checkbox(
label="Traduzir JSON de Avaliação para Português Brasileiro",
value=False,
info="Usa OpenAI GPT-4o-mini para traduzir o conteúdo da avaliação"
)
btn = gr.Button("🎨 Processar e Estilizar", variant="primary")
with gr.Column():
saida = gr.HTML(label="Visualização Estilizada")
btn.click(fn=processar_arquivos,
inputs=[arquivo_transcricao, arquivo_avaliacao, texto_transcricao, texto_avaliacao, traduzir_checkbox],
outputs=[saida, texto_transcricao, texto_avaliacao])
# Exemplos removidos para proteger dados confidenciais
# Iniciar a aplicação
if __name__ == "__main__":
demo.launch(share=True, server_name="0.0.0.0", server_port=None)