import gradio as gr import pandas as pd import re import numpy as np from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.linear_model import LogisticRegression import joblib import pickle import os from datetime import datetime # ====================== CONFIGURAÇÕES ====================== AREAS = { "IP": "Instituto de Pesquisa", "FD": "Faculdade de Direito", "FE": "Faculdade de Educação", "Reitoria": "Reitoria" } TIPO_MANIFESTACAO = ["Reclamação", "Sugestão"] # ====================== SISTEMA DE VALIDAÇÃO ====================== class ValidadorManifestacoes: def __init__(self): self.carregar_datasets() self.preparar_modelos() def carregar_datasets(self): """Carrega os datasets de validação""" # Palavras ofensivas padrão self.palavras_ofensivas = [ "idiota", "burro", "imbecil", "estupido", "cretino", "analfabeto", "incompetente", "ignorante", "nojento", "asco", "vergonha", "lixo" ] # Frases sem sentido padrão self.frases_sem_sentido = [ "asdfghjkl", "qwertyuiop", "123456789", "aaa bbb ccc", "teste teste", "xyz abc", "mmmm nnnn oooo" ] # Padrões de frases incompletas self.padroes_incompletos = [ "e depois não sei", "mas não", "porque sim", "sem motivo", "só isso", "nada mais", "e pronto" ] def preparar_modelos(self): """Prepara modelos ML para validação de sentido""" self.criar_modelo_sentido_padrao() def criar_modelo_sentido_padrao(self): """Cria modelo padrão se não existir""" # Dados de treino para frases com/sem sentido frases_com_sentido = [ "gostaria de reclamar do atendimento", "sugiro melhorar a limpeza", "o serviço está muito lento", "precisamos de mais computadores", "a internet está caindo muito", "as salas de aula precisam de ar condicionado", "a biblioteca deveria ficar aberta até mais tarde", "os banheiros precisam de manutenção urgente" ] frases_sem_sentido = [ "asdf ghjk qwert", "123 456 789", "teste teste teste", "aaa bbb ccc ddd", "xyz abc def ghi", "mmmm nnnn oooo pppp", "111 222 333 444", "qqqq wwww eeee rrrr" ] textos = frases_com_sentido + frases_sem_sentido labels = [1] * len(frases_com_sentido) + [0] * len(frases_sem_sentido) self.vectorizer_sentido = TfidfVectorizer(max_features=50) X = self.vectorizer_sentido.fit_transform(textos) self.modelo_sentido = LogisticRegression() self.modelo_sentido.fit(X, labels) print("✅ Modelo de validação de sentido criado com sucesso!") def validar_ofensas(self, texto): """Verifica palavras ofensivas""" texto_lower = texto.lower() ofensas_encontradas = [] for palavra in self.palavras_ofensivas: if re.search(r'\b' + re.escape(palavra) + r'\b', texto_lower): ofensas_encontradas.append(palavra) return { "tem_ofensa": len(ofensas_encontradas) > 0, "palavras": ofensas_encontradas, "nivel": "ALTO" if len(ofensas_encontradas) > 2 else "MÉDIO" if len(ofensas_encontradas) > 0 else "BAIXO" } def validar_sentido(self, texto): """Verifica se a frase faz sentido usando ML""" if len(texto.split()) < 3: return {"tem_problema": True, "tipo": "FRASE_MUITO_CURTA"} # Verificar padrões de spam texto_lower = texto.lower() for padrao in self.frases_sem_sentido: if padrao in texto_lower: return {"tem_problema": True, "tipo": "PADRAO_SPAM"} # Usar modelo ML try: X = self.vectorizer_sentido.transform([texto]) predicao = self.modelo_sentido.predict(X)[0] proba = self.modelo_sentido.predict_proba(X)[0][1] if predicao == 0 or proba < 0.3: return {"tem_problema": True, "tipo": "SEM_SENTIDO", "confianca": proba} else: return {"tem_problema": False, "tipo": "OK", "confianca": proba} except Exception as e: print(f"⚠️ Erro no modelo de sentido: {e}") return {"tem_problema": False, "tipo": "OK", "confianca": 0.5} def validar_completude(self, texto): """Verifica se a frase está completa""" texto_limpo = texto.strip() # Verifica se termina com pontuação termina_pontuacao = texto_limpo.endswith(('.', '!', '?', ';', ':')) # Verifica tamanho mínimo palavras = texto_limpo.split() tem_tamanho_minimo = len(palavras) >= 5 # Verifica padrões incompletos texto_lower = texto_limpo.lower() tem_padrao_incompleto = any( padrao in texto_lower for padrao in self.padroes_incompletos ) # Verifica se começa com conectores e não continua conectores = ["e", "mas", "porque", "então", "porém", "contudo"] primeira_palavra = palavras[0].lower() if palavras else "" comeca_conector = primeira_palavra in conectores problemas = [] if not termina_pontuacao: problemas.append("FALTA_PONTUACAO") if not tem_tamanho_minimo: problemas.append("FRASE_MUITO_CURTA") if tem_padrao_incompleto: problemas.append("PADRAO_INCOMPLETO") if comeca_conector and len(palavras) < 4: problemas.append("INICIO_INCOMPLETO") return { "tem_problema": len(problemas) > 0, "problemas": problemas, "pontuacao_ok": termina_pontuacao, "tamanho_ok": tem_tamanho_minimo } def validar_tudo(self, texto): """Executa todas as validações""" resultado_ofensas = self.validar_ofensas(texto) resultado_sentido = self.validar_sentido(texto) resultado_completude = self.validar_completude(texto) alertas = [] if resultado_ofensas["tem_ofensa"]: alertas.append({ "tipo": "🚫 OFENSA", "mensagem": f"Palavras ofensivas detectadas: {', '.join(resultado_ofensas['palavras'])}", "nivel": "ALTO", "cor": "#FF0000" }) if resultado_sentido["tem_problema"]: alertas.append({ "tipo": "🤔 SEM SENTIDO", "mensagem": f"Frase pode não fazer sentido ({resultado_sentido['tipo']})", "nivel": "MÉDIO", "cor": "#FF9900" }) if resultado_completude["tem_problema"]: problemas_texto = ", ".join(resultado_completude["problemas"]) alertas.append({ "tipo": "📝 INCOMPLETO", "mensagem": f"Frase incompleta ou mal estruturada: {problemas_texto}", "nivel": "BAIXO", "cor": "#FFCC00" }) return { "alertas": alertas, "ofensas": resultado_ofensas, "sentido": resultado_sentido, "completude": resultado_completude, "aprovado": len(alertas) == 0 } # ====================== SISTEMA DE CLASSIFICAÇÃO ====================== class ClassificadorAreas: def __init__(self): self.modelo = None self.vectorizer = None self.criar_modelo_padrao() def criar_modelo_padrao(self): """Cria modelo padrão baseado em palavras-chave""" # Dados de treino para cada área dados_treino = { "IP": [ "pesquisa científica", "laboratório", "experimento", "artigo científico", "publicação", "projeto de pesquisa", "cientista", "dados de pesquisa", "metodologia", "análise estatística" ], "FD": [ "direito constitucional", "processo judicial", "advogado", "tribunal", "lei", "jurisprudência", "contrato", "ação judicial", "penal", "civil" ], "FE": [ "educação infantil", "professor", "pedagogia", "ensino fundamental", "currículo escolar", "didática", "aprendizagem", "avaliação", "alfabetização" ], "Reitoria": [ "administração universitária", "reitor", "decisão institucional", "gestão acadêmica", "políticas da universidade", "coordenação geral", "orçamento universitário", "planejamento institucional" ] } textos = [] labels = [] for area, frases in dados_treino.items(): for frase in frases: textos.append(frase) labels.append(area) self.vectorizer = TfidfVectorizer(max_features=100) X = self.vectorizer.fit_transform(textos) self.modelo = LogisticRegression() self.modelo.fit(X, labels) print("✅ Modelo de classificação de áreas criado com sucesso!") def classificar_area(self, texto, area_selecionada): """Classifica a área baseada no texto""" try: X = self.vectorizer.transform([texto]) predicao = self.modelo.predict(X)[0] probabilidade = np.max(self.modelo.predict_proba(X)[0]) # Se a área predita for diferente da selecionada e tiver boa confiança, alertar if predicao != area_selecionada and probabilidade > 0.6: return predicao, probabilidade else: return area_selecionada, probabilidade except Exception as e: print(f"⚠️ Erro na classificação de área: {e}") return area_selecionada, 1.0 # ====================== INICIALIZAR SISTEMAS ====================== print("🔄 Inicializando sistemas...") validador = ValidadorManifestacoes() classificador = ClassificadorAreas() print("✅ Sistemas inicializados com sucesso!") # ====================== FUNÇÕES DA INTERFACE ====================== def processar_manifestacao(tipo, area, mensagem): """Processa a manifestação com todas as validações""" if not mensagem.strip(): return ( "
⚠️ Digite uma mensagem!
", "", {}, "AGUARDANDO" ) # Validar mensagem validacao = validador.validar_tudo(mensagem) # Classificar área sugerida area_sugerida, confianca_area = classificador.classificar_area(mensagem, area) # Resultado base resultado = { "tipo": tipo, "area_selecionada": area, "area_sugerida": area_sugerida, "confianca_area": f"{confianca_area:.1%}", "mensagem": mensagem, "data_hora": datetime.now().strftime("%d/%m/%Y %H:%M:%S"), "validacao": validacao, "status": "APROVADA" if validacao["aprovado"] else "REQUER REVISÃO" } # Formatar alertas para exibição alertas_html = "
" if validacao["aprovado"]: alertas_html += """
✅ MENSAGEM APROVADA - PRONTA PARA ENVIO
""" else: for alerta in validacao["alertas"]: alertas_html += f"""
{alerta["tipo"]} ({alerta["nivel"]}):
{alerta["mensagem"]}
""" alertas_html += "
" # Detalhes da validação detalhes_html = f"""

📋 Detalhes da Validação:

🔍 Palavras ofensivas: {len(validacao['ofensas']['palavras'])} encontradas

🧠 Faz sentido: {'✅ Sim' if not validacao['sentido']['tem_problema'] else '❌ Não'} ({validacao['sentido'].get('confianca', 0.5):.1%})

📝 Frase completa: {'✅ Sim' if not validacao['completude']['tem_problema'] else '❌ Não'}

🏛️ Área sugerida pelo sistema: {area_sugerida} ({confianca_area:.1%} de confiança)

📊 Status final: {resultado['status']}

""" # JSON para download/armazenamento json_resultado = { "manifestacao": { "tipo": tipo, "area_original": area, "area_sugerida": area_sugerida, "mensagem": mensagem, "data_hora": resultado["data_hora"], "status": resultado["status"] }, "validacoes": { "ofensas": validacao["ofensas"], "sentido": validacao["sentido"], "completude": validacao["completude"] } } return alertas_html, detalhes_html, json_resultado, resultado["status"] def atualizar_descricao(area): """Atualiza descrição da área selecionada""" return f"**Descrição:** {AREAS.get(area, 'Área não encontrada')}" def atualizar_contador(texto): """Atualiza contador de caracteres""" palavras = len(texto.split()) return f"
📝 Caracteres: {len(texto)} | 📊 Palavras: {palavras}
" # ====================== INTERFACE GRADIO ====================== with gr.Blocks(title="📋 Sistema de Manifestações Universitárias") as app: # CSS customizado app.css = """ .gradio-container { max-width: 1200px !important; margin: 0 auto !important; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } .header-container { padding: 25px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 10px; margin-bottom: 25px; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); } .header-title { color: white !important; text-align: center; margin-bottom: 10px !important; font-size: 2.2em !important; font-weight: 700 !important; } .header-subtitle { color: rgba(255,255,255,0.9); text-align: center; margin-bottom: 0; font-size: 1.1em; } .section-title { background: linear-gradient(90deg, #f0f4ff, #e6f7ff); padding: 12px 20px; border-radius: 8px; margin: 20px 0 15px 0; border-left: 5px solid #667eea; } .example-btn { margin: 5px !important; } .status-aprovado { color: #10B981 !important; font-weight: bold !important; } .status-revisao { color: #F59E0B !important; font-weight: bold !important; } """ # Cabeçalho gr.HTML("""

🏛️ Sistema de Manifestações Universitárias

Registre reclamações ou sugestões para as áreas da universidade

""") with gr.Row(): with gr.Column(scale=1): gr.Markdown("### 1️⃣ Tipo de Manifestação") tipo_input = gr.Dropdown( choices=TIPO_MANIFESTACAO, label="Selecione o tipo", value="Reclamação", interactive=True ) gr.Markdown("### 2️⃣ Área Destino") area_input = gr.Dropdown( choices=list(AREAS.keys()), label="Selecione a área", value="Reitoria", interactive=True ) # Mostrar descrição da área area_desc = gr.Markdown(f"**Descrição:** {AREAS['Reitoria']}") area_input.change(atualizar_descricao, inputs=area_input, outputs=area_desc) gr.Markdown("---") gr.Markdown("### 📊 Estatísticas") contador_total = gr.Textbox("Total de manifestações: 0", label="", interactive=False) with gr.Column(scale=2): gr.Markdown("### 3️⃣ Escreva sua Manifestação") mensagem_input = gr.Textbox( label="Descreva detalhadamente sua reclamação ou sugestão:", placeholder="Ex: Gostaria de sugerir a instalação de mais bebedouros no prédio da Faculdade de Educação, pois os existentes são insuficientes para a quantidade de alunos, especialmente nos horários de pico.", lines=8, interactive=True ) # Contador de caracteres contador_chars = gr.HTML("
📝 Caracteres: 0 | 📊 Palavras: 0
") mensagem_input.change(atualizar_contador, inputs=mensagem_input, outputs=contador_chars) with gr.Row(): btn_enviar = gr.Button("📤 Enviar Manifestação", variant="primary", size="lg") btn_limpar = gr.Button("🗑️ Limpar Tudo", variant="secondary") # Resultados with gr.Row(): with gr.Column(): gr.Markdown("### ⚠️ Alertas de Validação") alertas_output = gr.HTML(label="", elem_classes=["alertas-container"]) with gr.Column(): gr.Markdown("### 📊 Resultado da Análise") detalhes_output = gr.HTML(label="", elem_classes=["detalhes-container"]) status_output = gr.Textbox(label="📈 Status Final", interactive=False) # JSON para download json_output = gr.JSON(label="📁 Dados Completos (para download)") # Histórico - CORRIGIDO: removido parâmetro 'height' with gr.Accordion("📜 Histórico de Manifestações (últimas 10)", open=False): historico_df = gr.Dataframe( headers=["Data", "Tipo", "Área", "Status", "Preview"], value=[], interactive=False ) # Estado para histórico historico_state = gr.State([]) # Processamento completo def processar_e_armazenar(tipo, area, mensagem, historico): """Processa e armazena a manifestação""" alertas, detalhes, json_data, status = processar_manifestacao(tipo, area, mensagem) # Adicionar ao histórico nova_entrada = [ datetime.now().strftime("%d/%m %H:%M"), tipo, area, status, mensagem[:50] + ("..." if len(mensagem) > 50 else "") ] historico.append(nova_entrada) # Atualizar contador contador_atualizado = f"Total de manifestações: {len(historico)}" return ( alertas, detalhes, json_data, status, historico, pd.DataFrame(historico[-10:]), contador_atualizado ) # Limpar tudo def limpar_tudo(): return ["", "", {}, "", [], pd.DataFrame([]), "Total de manifestações: 0"] # Conectar eventos btn_enviar.click( fn=processar_e_armazenar, inputs=[tipo_input, area_input, mensagem_input, historico_state], outputs=[alertas_output, detalhes_output, json_output, status_output, historico_state, historico_df, contador_total] ) btn_limpar.click( fn=limpar_tudo, inputs=None, outputs=[mensagem_input, alertas_output, detalhes_output, json_output, status_output, historico_state, historico_df, contador_total] ) # Exemplos pré-definidos gr.Markdown("### 💡 Exemplos Rápidos (clique para carregar)") with gr.Row(): exemplo1 = gr.Button("📡 Internet lenta (IP)", size="sm", elem_classes=["example-btn"]) exemplo2 = gr.Button("🚰 Mais bebedouros (FE)", size="sm", elem_classes=["example-btn"]) exemplo3 = gr.Button("🪑 Cadeiras quebradas (FD)", size="sm", elem_classes=["example-btn"]) exemplo4 = gr.Button("⚠️ Exemplo com problema", size="sm", elem_classes=["example-btn"]) def carregar_exemplo1(): return "Reclamação", "IP", "A internet do laboratório de pesquisas está extremamente lenta, impossibilitando o acesso aos artigos científicos. Isso está atrasando nossas pesquisas significativamente. Precisamos de uma solução urgente!" def carregar_exemplo2(): return "Sugestão", "FE", "Sugiro a instalação de mais bebedouros nos corredores da Faculdade de Educação, pois os existentes são insuficientes para a quantidade de alunos, especialmente nos horários de pico entre as aulas." def carregar_exemplo3(): return "Reclamação", "FD", "As cadeiras da sala 205 estão quebradas há mais de um mês e ninguém fez a manutenção. Isso compromete o conforto e a segurança dos alunos durante as aulas." def carregar_exemplo4(): return "Reclamação", "Reitoria", "Isso é uma vergonha! Incompetentes! asdfgh nada funciona direito nessa universidade." exemplo1.click(carregar_exemplo1, outputs=[tipo_input, area_input, mensagem_input]) exemplo2.click(carregar_exemplo2, outputs=[tipo_input, area_input, mensagem_input]) exemplo3.click(carregar_exemplo3, outputs=[tipo_input, area_input, mensagem_input]) exemplo4.click(carregar_exemplo4, outputs=[tipo_input, area_input, mensagem_input]) # Rodapé informativo gr.Markdown("---") gr.Markdown("""

ℹ️ Como funciona o sistema:

✅ Validações automáticas

🚫 Detecção de ofensas: Verifica palavras inadequadas no texto

🤔 Análise de sentido: Identifica frases sem sentido ou spam

📝 Verificação de completude: Checa se a mensagem está bem estruturada

🏛️ Classificação inteligente

Sugere a área mais apropriada baseada no conteúdo

Compara com a área selecionada pelo usuário

Mostra nível de confiança da sugestão

📊 Resultados

Status claro (APROVADA ou REQUER REVISÃO)

Alertas detalhados com cores por gravidade

Histórico das últimas manifestações

Dados exportáveis em JSON

""") # ====================== INICIAR APLICAÇÃO ====================== if __name__ == "__main__": print("🚀 Iniciando aplicação Gradio...") app.launch( debug=True, share=False, server_name="0.0.0.0", server_port=7860 )