Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -2,7 +2,9 @@ import gradio as gr
|
|
| 2 |
import pandas as pd
|
| 3 |
import re
|
| 4 |
import os
|
|
|
|
| 5 |
from datetime import timedelta
|
|
|
|
| 6 |
|
| 7 |
def parse_duration(duration_str):
|
| 8 |
try:
|
|
@@ -46,9 +48,6 @@ def match_alunos(tarefas_csv_path, alunos_csv_path, contador_csv_path):
|
|
| 46 |
print(f"Arquivo {tarefas_csv_path} ou {alunos_csv_path} está vazio. Pulando...")
|
| 47 |
return
|
| 48 |
|
| 49 |
-
print(f"Tarefas DataFrame (antes da normalização):\n{tarefas_df.head()}")
|
| 50 |
-
print(f"Alunos DataFrame (antes da normalização):\n{alunos_df.head()}")
|
| 51 |
-
|
| 52 |
tarefas_df.columns = tarefas_df.columns.str.strip()
|
| 53 |
alunos_df.columns = alunos_df.columns.str.strip()
|
| 54 |
|
|
@@ -75,8 +74,6 @@ def match_alunos(tarefas_csv_path, alunos_csv_path, contador_csv_path):
|
|
| 75 |
|
| 76 |
alunos_df['Aluno_Pattern'] = alunos_df.apply(lambda row: generate_aluno_pattern(row['RA'], row['Dig. RA']), axis=1)
|
| 77 |
|
| 78 |
-
print(f"Alunos DataFrame (com padrão):\n{alunos_df.head()}")
|
| 79 |
-
|
| 80 |
def extract_aluno_pattern(nome):
|
| 81 |
if isinstance(nome, str):
|
| 82 |
match = re.search(r'\d+.*', nome.lower())
|
|
@@ -86,12 +83,8 @@ def match_alunos(tarefas_csv_path, alunos_csv_path, contador_csv_path):
|
|
| 86 |
tarefas_df['Aluno_Pattern'] = tarefas_df['Aluno'].apply(extract_aluno_pattern)
|
| 87 |
tarefas_df['Duração'] = tarefas_df['Duração'].apply(parse_duration)
|
| 88 |
|
| 89 |
-
print(f"Tarefas DataFrame (com padrão):\n{tarefas_df.head()}")
|
| 90 |
-
|
| 91 |
matched_alunos = alunos_df[alunos_df['Aluno_Pattern'].isin(tarefas_df['Aluno_Pattern'])]
|
| 92 |
|
| 93 |
-
print(f"Matched Alunos DataFrame:\n{matched_alunos.head()}")
|
| 94 |
-
|
| 95 |
result_df = matched_alunos[['Nome do Aluno']].drop_duplicates()
|
| 96 |
|
| 97 |
for aluno in result_df['Nome do Aluno']:
|
|
@@ -108,8 +101,6 @@ def match_alunos(tarefas_csv_path, alunos_csv_path, contador_csv_path):
|
|
| 108 |
else:
|
| 109 |
contador_df = pd.concat([contador_df, pd.DataFrame({'Nome do Aluno': [aluno], 'Tarefas Completadas': [1], 'Acertos Absolutos': [nota_total], 'Total Tempo': [str(tempo_total)]})], ignore_index=True)
|
| 110 |
|
| 111 |
-
print(f"Contador DataFrame (atualizado):\n{contador_df.head()}")
|
| 112 |
-
|
| 113 |
contador_df.to_csv(contador_csv_path, index=False)
|
| 114 |
|
| 115 |
return result_df
|
|
@@ -118,9 +109,7 @@ def process_all_tarefas_in_directory(directory, alunos_csv_path, contador_csv_pa
|
|
| 118 |
tarefas_files = [os.path.join(directory, f) for f in os.listdir(directory) if f.endswith('.csv') and f not in ['alunos_fim.csv', 'contador_tarefas.csv']]
|
| 119 |
|
| 120 |
for i, tarefas_file in enumerate(tarefas_files):
|
| 121 |
-
print(f"Processando arquivo {i+1}/{len(tarefas_files)}: {tarefas_file}")
|
| 122 |
match_alunos(tarefas_file, alunos_csv_path, contador_csv_path)
|
| 123 |
-
print(f"Arquivo {tarefas_file} processado.")
|
| 124 |
|
| 125 |
process_relatorios(contador_csv_path, relatorio_csv_path)
|
| 126 |
|
|
@@ -134,6 +123,78 @@ def process_relatorios(contador_csv_path, relatorio_csv_path):
|
|
| 134 |
contador_df.to_csv(relatorio_csv_path, index=False)
|
| 135 |
return contador_df
|
| 136 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 137 |
|
| 138 |
def processar_relatorio(html_file, tarefa_files):
|
| 139 |
input_directory = "temp_files" # Diretório temporário para os arquivos
|
|
@@ -169,11 +230,14 @@ def processar_relatorio(html_file, tarefa_files):
|
|
| 169 |
process_all_tarefas_in_directory(output_directory, alunos_csv_path, contador_csv_path, relatorio_csv_path)
|
| 170 |
df = process_relatorios(contador_csv_path, relatorio_csv_path)
|
| 171 |
|
| 172 |
-
# Salva o relatório em HTML
|
| 173 |
html_output_path = os.path.join(output_directory, "relatorio_final.html")
|
| 174 |
df.to_html(html_output_path, index=False)
|
|
|
|
|
|
|
|
|
|
| 175 |
|
| 176 |
-
return df.to_html(index=False), html_output_path
|
| 177 |
|
| 178 |
# --- Interface Gradio ---
|
| 179 |
with gr.Blocks() as interface:
|
|
@@ -182,8 +246,13 @@ with gr.Blocks() as interface:
|
|
| 182 |
excel_files = gr.Files(label="Upload Excel Files (Relatórios de Tarefas)", type="binary", file_count="multiple")
|
| 183 |
generate_btn = gr.Button("Generate Report")
|
| 184 |
output_html = gr.HTML()
|
| 185 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 186 |
|
| 187 |
-
generate_btn.click(fn=
|
| 188 |
|
| 189 |
interface.launch()
|
|
|
|
| 2 |
import pandas as pd
|
| 3 |
import re
|
| 4 |
import os
|
| 5 |
+
import matplotlib.pyplot as plt
|
| 6 |
from datetime import timedelta
|
| 7 |
+
from fpdf import FPDF
|
| 8 |
|
| 9 |
def parse_duration(duration_str):
|
| 10 |
try:
|
|
|
|
| 48 |
print(f"Arquivo {tarefas_csv_path} ou {alunos_csv_path} está vazio. Pulando...")
|
| 49 |
return
|
| 50 |
|
|
|
|
|
|
|
|
|
|
| 51 |
tarefas_df.columns = tarefas_df.columns.str.strip()
|
| 52 |
alunos_df.columns = alunos_df.columns.str.strip()
|
| 53 |
|
|
|
|
| 74 |
|
| 75 |
alunos_df['Aluno_Pattern'] = alunos_df.apply(lambda row: generate_aluno_pattern(row['RA'], row['Dig. RA']), axis=1)
|
| 76 |
|
|
|
|
|
|
|
| 77 |
def extract_aluno_pattern(nome):
|
| 78 |
if isinstance(nome, str):
|
| 79 |
match = re.search(r'\d+.*', nome.lower())
|
|
|
|
| 83 |
tarefas_df['Aluno_Pattern'] = tarefas_df['Aluno'].apply(extract_aluno_pattern)
|
| 84 |
tarefas_df['Duração'] = tarefas_df['Duração'].apply(parse_duration)
|
| 85 |
|
|
|
|
|
|
|
| 86 |
matched_alunos = alunos_df[alunos_df['Aluno_Pattern'].isin(tarefas_df['Aluno_Pattern'])]
|
| 87 |
|
|
|
|
|
|
|
| 88 |
result_df = matched_alunos[['Nome do Aluno']].drop_duplicates()
|
| 89 |
|
| 90 |
for aluno in result_df['Nome do Aluno']:
|
|
|
|
| 101 |
else:
|
| 102 |
contador_df = pd.concat([contador_df, pd.DataFrame({'Nome do Aluno': [aluno], 'Tarefas Completadas': [1], 'Acertos Absolutos': [nota_total], 'Total Tempo': [str(tempo_total)]})], ignore_index=True)
|
| 103 |
|
|
|
|
|
|
|
| 104 |
contador_df.to_csv(contador_csv_path, index=False)
|
| 105 |
|
| 106 |
return result_df
|
|
|
|
| 109 |
tarefas_files = [os.path.join(directory, f) for f in os.listdir(directory) if f.endswith('.csv') and f not in ['alunos_fim.csv', 'contador_tarefas.csv']]
|
| 110 |
|
| 111 |
for i, tarefas_file in enumerate(tarefas_files):
|
|
|
|
| 112 |
match_alunos(tarefas_file, alunos_csv_path, contador_csv_path)
|
|
|
|
| 113 |
|
| 114 |
process_relatorios(contador_csv_path, relatorio_csv_path)
|
| 115 |
|
|
|
|
| 123 |
contador_df.to_csv(relatorio_csv_path, index=False)
|
| 124 |
return contador_df
|
| 125 |
|
| 126 |
+
def generate_pdf_report(dataframe, output_pdf_path):
|
| 127 |
+
class PDF(FPDF):
|
| 128 |
+
def header(self):
|
| 129 |
+
self.set_font('Arial', 'B', 12)
|
| 130 |
+
self.cell(0, 10, 'Relatório de Tarefas', 0, 1, 'C')
|
| 131 |
+
|
| 132 |
+
def footer(self):
|
| 133 |
+
self.set_y(-15)
|
| 134 |
+
self.set_font('Arial', 'I', 8)
|
| 135 |
+
self.cell(0, 10, f'Page {self.page_no()}', 0, 0, 'C')
|
| 136 |
+
|
| 137 |
+
def add_table(self, dataframe):
|
| 138 |
+
self.set_font('Arial', 'B', 10)
|
| 139 |
+
col_width = self.w / len(dataframe.columns)
|
| 140 |
+
row_height = self.font_size
|
| 141 |
+
|
| 142 |
+
# Adiciona os cabeçalhos
|
| 143 |
+
for col in dataframe.columns:
|
| 144 |
+
self.cell(col_width, row_height * 2, col, border=1)
|
| 145 |
+
self.ln(row_height * 2)
|
| 146 |
+
|
| 147 |
+
# Adiciona os dados
|
| 148 |
+
self.set_font('Arial', '', 10)
|
| 149 |
+
for row in dataframe.itertuples(index=False):
|
| 150 |
+
for item in row:
|
| 151 |
+
self.cell(col_width, row_height * 2, str(item), border=1)
|
| 152 |
+
self.ln(row_height * 2)
|
| 153 |
+
|
| 154 |
+
def add_image(self, image_path):
|
| 155 |
+
self.add_page()
|
| 156 |
+
self.image(image_path, x=10, y=10, w=270)
|
| 157 |
+
|
| 158 |
+
pdf = PDF(orientation='L', unit='mm', format='A4')
|
| 159 |
+
pdf.add_page()
|
| 160 |
+
pdf.add_table(dataframe)
|
| 161 |
+
|
| 162 |
+
# Gerar gráficos e adicionar ao PDF
|
| 163 |
+
top_students = dataframe.nlargest(5, 'Acertos Absolutos')
|
| 164 |
+
plt.figure(figsize=(10, 6))
|
| 165 |
+
plt.bar(top_students['Nome do Aluno'], top_students['Acertos Absolutos'], color='blue')
|
| 166 |
+
plt.xlabel('Nome do Aluno')
|
| 167 |
+
plt.ylabel('Acertos Absolutos')
|
| 168 |
+
plt.title('Top 5 Alunos - Acertos Absolutos')
|
| 169 |
+
plt.xticks(rotation=45, ha='right')
|
| 170 |
+
plt.tight_layout()
|
| 171 |
+
graph_path = 'top_5_acertos_absolutos.png'
|
| 172 |
+
plt.savefig(graph_path)
|
| 173 |
+
pdf.add_image(graph_path)
|
| 174 |
+
|
| 175 |
+
plt.figure(figsize=(10, 6))
|
| 176 |
+
plt.bar(top_students['Nome do Aluno'], top_students['Média de Acertos'].str.rstrip('%').astype('float'), color='green')
|
| 177 |
+
plt.xlabel('Nome do Aluno')
|
| 178 |
+
plt.ylabel('Percentual de Acertos (%)')
|
| 179 |
+
plt.title('Top 5 Alunos - Percentual de Acertos')
|
| 180 |
+
plt.xticks(rotation=45, ha='right')
|
| 181 |
+
plt.tight_layout()
|
| 182 |
+
graph_path = 'top_5_percentual_acertos.png'
|
| 183 |
+
plt.savefig(graph_path)
|
| 184 |
+
pdf.add_image(graph_path)
|
| 185 |
+
|
| 186 |
+
plt.figure(figsize=(10, 6))
|
| 187 |
+
plt.bar(top_students['Nome do Aluno'], top_students['Tarefas Completadas'], color='red')
|
| 188 |
+
plt.xlabel('Nome do Aluno')
|
| 189 |
+
plt.ylabel('Tarefas Completadas')
|
| 190 |
+
plt.title('Top 5 Alunos - Tarefas Completadas')
|
| 191 |
+
plt.xticks(rotation=45, ha='right')
|
| 192 |
+
plt.tight_layout()
|
| 193 |
+
graph_path = 'top_5_tarefas_completadas.png'
|
| 194 |
+
plt.savefig(graph_path)
|
| 195 |
+
pdf.add_image(graph_path)
|
| 196 |
+
|
| 197 |
+
pdf.output(output_pdf_path)
|
| 198 |
|
| 199 |
def processar_relatorio(html_file, tarefa_files):
|
| 200 |
input_directory = "temp_files" # Diretório temporário para os arquivos
|
|
|
|
| 230 |
process_all_tarefas_in_directory(output_directory, alunos_csv_path, contador_csv_path, relatorio_csv_path)
|
| 231 |
df = process_relatorios(contador_csv_path, relatorio_csv_path)
|
| 232 |
|
| 233 |
+
# Salva o relatório em HTML e PDF
|
| 234 |
html_output_path = os.path.join(output_directory, "relatorio_final.html")
|
| 235 |
df.to_html(html_output_path, index=False)
|
| 236 |
+
|
| 237 |
+
pdf_output_path = os.path.join(output_directory, "relatorio_final.pdf")
|
| 238 |
+
generate_pdf_report(df, pdf_output_path)
|
| 239 |
|
| 240 |
+
return df.to_html(index=False), html_output_path, pdf_output_path
|
| 241 |
|
| 242 |
# --- Interface Gradio ---
|
| 243 |
with gr.Blocks() as interface:
|
|
|
|
| 246 |
excel_files = gr.Files(label="Upload Excel Files (Relatórios de Tarefas)", type="binary", file_count="multiple")
|
| 247 |
generate_btn = gr.Button("Generate Report")
|
| 248 |
output_html = gr.HTML()
|
| 249 |
+
download_html_btn = gr.File(label="Download HTML Report")
|
| 250 |
+
download_pdf_btn = gr.File(label="Download PDF Report")
|
| 251 |
+
|
| 252 |
+
def wrapper(html_file, excel_files):
|
| 253 |
+
html_content, html_path, pdf_path = processar_relatorio(html_file, excel_files)
|
| 254 |
+
return html_content, html_path, pdf_path
|
| 255 |
|
| 256 |
+
generate_btn.click(fn=wrapper, inputs=[html_file, excel_files], outputs=[output_html, download_html_btn, download_pdf_btn])
|
| 257 |
|
| 258 |
interface.launch()
|