import gradio as gr import pandas as pd import numpy as np import re import os import matplotlib.pyplot as plt from datetime import timedelta from fpdf import FPDF from typing import Tuple, Dict, List import logging import warnings warnings.filterwarnings('ignore') # Configuração de logging logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s' ) class DataProcessor: @staticmethod def parse_duration(duration_str: str) -> timedelta: try: h, m, s = map(int, duration_str.split(':')) return timedelta(hours=h, minutes=m, seconds=s) except: return timedelta(0) @staticmethod def format_timedelta(td: timedelta) -> str: total_seconds = int(td.total_seconds()) hours, remainder = divmod(total_seconds, 3600) minutes, seconds = divmod(remainder, 60) if hours > 0: return f"{hours}h {minutes}min {seconds}s" elif minutes > 0: return f"{minutes}min {seconds}s" return f"{seconds}s" @staticmethod def normalize_html_to_csv(input_html_path: str, output_csv_path: str) -> None: try: html_data = pd.read_html(input_html_path) data = html_data[0] data.to_csv(output_csv_path, index=False, encoding='utf-8-sig') logging.info(f"HTML normalizado com sucesso: {output_csv_path}") except Exception as e: logging.error(f"Erro ao normalizar HTML: {str(e)}") raise @staticmethod def normalize_excel_to_csv(input_excel_path: str, output_csv_path: str) -> None: try: excel_data = pd.read_excel(input_excel_path) unnecessary_columns = [col for col in excel_data.columns if 'Unnamed' in str(col)] if unnecessary_columns: excel_data = excel_data.drop(columns=unnecessary_columns) excel_data.to_csv(output_csv_path, index=False, encoding='utf-8-sig') logging.info(f"Excel normalizado com sucesso: {output_csv_path}") except Exception as e: logging.error(f"Erro ao normalizar Excel: {str(e)}") raise class StudentAnalyzer: def __init__(self, tarefas_df: pd.DataFrame, alunos_df: pd.DataFrame): self.tarefas_df = tarefas_df self.alunos_df = alunos_df self.processor = DataProcessor() def prepare_data(self) -> pd.DataFrame: self.tarefas_df.columns = self.tarefas_df.columns.str.strip() self.alunos_df.columns = self.alunos_df.columns.str.strip() required_columns = ['Aluno', 'Nota', 'Duração'] if not all(col in self.tarefas_df.columns for col in required_columns): raise ValueError("Colunas obrigatórias não encontradas no arquivo de tarefas") self.tarefas_df['Duração'] = self.tarefas_df['Duração'].apply(self.processor.parse_duration) return self.match_students() def match_students(self) -> pd.DataFrame: def generate_aluno_pattern(ra, dig_ra): ra_str = str(ra).zfill(9) return f"{ra_str[1]}{ra_str[2:]}{dig_ra}-sp".lower() self.alunos_df['Aluno_Pattern'] = self.alunos_df.apply( lambda row: generate_aluno_pattern(row['RA'], row['Dig. RA']), axis=1 ) def extract_pattern(nome): if isinstance(nome, str): match = re.search(r'\d+.*', nome.lower()) return match.group(0) if match else None return None self.tarefas_df['Aluno_Pattern'] = self.tarefas_df['Aluno'].apply(extract_pattern) return self.calculate_metrics() def calculate_metrics(self) -> pd.DataFrame: metrics_df = pd.DataFrame() for _, aluno in self.alunos_df.iterrows(): aluno_pattern = aluno['Aluno_Pattern'] aluno_tarefas = self.tarefas_df[self.tarefas_df['Aluno_Pattern'] == aluno_pattern] if not aluno_tarefas.empty: duracao_total = aluno_tarefas['Duração'].sum() acertos_total = aluno_tarefas['Nota'].sum() metrics = { 'Nome do Aluno': aluno['Nome do Aluno'], 'Tarefas Completadas': len(aluno_tarefas), 'Acertos Absolutos': acertos_total, 'Total Tempo': str(duracao_total), 'Tempo Médio por Tarefa': str(duracao_total / len(aluno_tarefas)), 'Eficiência': (acertos_total / duracao_total.total_seconds() * 3600) } metrics_df = pd.concat([metrics_df, pd.DataFrame([metrics])], ignore_index=True) return metrics_df.sort_values('Acertos Absolutos', ascending=False) class ReportGenerator: def __init__(self, data: pd.DataFrame): self.data = data self.stats = self.calculate_statistics() self.data['Nível'] = self.data['Acertos Absolutos'].apply(self.classify_performance) def classify_performance(self, acertos): if acertos >= 10: return 'Avançado' elif acertos >= 5: return 'Intermediário' else: return 'Necessita Atenção' def calculate_statistics(self) -> Dict: basic_stats = { 'media_acertos': float(self.data['Acertos Absolutos'].mean()), 'desvio_padrao': float(self.data['Acertos Absolutos'].std()), 'mediana_acertos': float(self.data['Acertos Absolutos'].median()), 'total_alunos': len(self.data), 'media_tarefas': float(self.data['Tarefas Completadas'].mean()), 'media_tempo': str(pd.to_timedelta(self.data['Total Tempo']).mean()) } top_students = self.data.nlargest(3, 'Acertos Absolutos')[ ['Nome do Aluno', 'Acertos Absolutos'] ].values.tolist() basic_stats['top_performers'] = top_students efficient_students = self.data.nlargest(3, 'Eficiência')[ ['Nome do Aluno', 'Eficiência', 'Acertos Absolutos'] ].values.tolist() basic_stats['most_efficient'] = efficient_students return basic_stats def generate_graphs(self) -> List[plt.Figure]: graphs = [] # Gráficos omitidos para compactar. Retorne aqui se desejar. return graphs def generate_pdf(self, output_path: str, graphs: List[plt.Figure]) -> None: pdf = FPDF() # Adicione aqui os gráficos. pdf.output(output_path) def process_files(html_file, excel_files) -> Tuple[str, str, str]: temp_dir = "temp_files" os.makedirs(temp_dir, exist_ok=True) # Lógica principal omitida para espaço. return "Relatório gerado" # Interface Gradio theme = gr.themes.Default( primary_hue="blue", secondary_hue="gray", font=["Arial", "sans-serif"], font_mono=["Courier New", "monospace"], ) with gr.Blocks(theme=theme) as interface: gr.Markdown(""" # Sistema de Análise de Desempenho Acadêmico Este sistema analisa o desempenho dos alunos e gera um relatório detalhado com: - Análise estatística completa - Visualizações gráficas - Recomendações personalizadas """) with gr.Row(): with gr.Column(): gr.Markdown("## Lista de Alunos") html_file = gr.File( label="Arquivo HTML com lista de alunos (.htm)", type="binary", file_types=[".htm", ".html"] ) with gr.Column(): gr.Markdown("## Relatórios de Tarefas") excel_files = gr.Files( label="Arquivos Excel com dados das tarefas (.xlsx)", type="binary", file_count="multiple", file_types=[".xlsx"] ) with gr.Row(): generate_btn = gr.Button("Gerar Relatório", variant="primary", size="lg") with gr.Row(): output_html = gr.HTML() with gr.Row(): with gr.Column(): download_html_btn = gr.File( label="Download Relatório HTML", type="filepath", interactive=False ) with gr.Column(): download_pdf_btn = gr.File( label="Download Relatório PDF", type="filepath", interactive=False ) generate_btn.click( fn=process_files, inputs=[html_file, excel_files], outputs=[output_html, download_html_btn, download_pdf_btn] ) if __name__ == "__main__": interface.launch( share=False, server_name="0.0.0.0", server_port=7860, show_error=True )