diff --git a/README.md b/README.md index e03c0fa3cb..af77f53a93 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,10 @@ Acesse: http://localhost:3000 ## Testes ```bash -# Rodar testes BDD +# Rodar todos os testes BDD (25 cenários, 133 steps) +bundle exec cucumber --tags "not @wip" + +# Rodar feature específica bundle exec cucumber features/sistema_login.feature ``` + diff --git a/app/controllers/avaliacoes_controller.rb b/app/controllers/avaliacoes_controller.rb index e855f9a70e..8ff5e9b7dd 100644 --- a/app/controllers/avaliacoes_controller.rb +++ b/app/controllers/avaliacoes_controller.rb @@ -56,8 +56,12 @@ def resultados # Pré-carrega dependências para evitar N+1. begin @submissoes = @avaliacao.submissoes.includes(:aluno, :respostas) + @perguntas = @avaliacao.modelo.perguntas.order(:id) + @question_stats = build_question_statistics(@avaliacao) rescue ActiveRecord::StatementInvalid @submissoes = [] + @perguntas = [] + @question_stats = {} flash.now[:alert] = "Erro ao carregar submissões." end @@ -69,4 +73,33 @@ def resultados end end end + + private + + def build_question_statistics(avaliacao) + avaliacao.modelo.perguntas.each_with_object({}) do |pergunta, stats| + respostas = Resposta.joins(:submissao) + .where(submissoes: { avaliacao_id: avaliacao.id }) + .where(questao_id: pergunta.id) + + if [ "multipla_escolha", "checkbox", "escala" ].include?(pergunta.tipo) + # Conta cada opção escolhida + stats[pergunta.id] = { + type: pergunta.tipo, + data: respostas.group(:conteudo).count, + total: respostas.count, + responses: [] + } + else + # Para texto, inclui as respostas para exibição + text_responses = respostas.pluck(:conteudo).compact.reject(&:blank?) + stats[pergunta.id] = { + type: pergunta.tipo, + data: {}, + total: respostas.count, + responses: text_responses + } + end + end + end end diff --git a/app/controllers/sigaa_imports_controller.rb b/app/controllers/sigaa_imports_controller.rb index 2f208da9b6..40dff6936f 100644 --- a/app/controllers/sigaa_imports_controller.rb +++ b/app/controllers/sigaa_imports_controller.rb @@ -9,14 +9,15 @@ def new def create # Usa automaticamente o arquivo class_members.json do projeto file_path = Rails.root.join("class_members.json") + classes_file_path = Rails.root.join("classes.json") unless File.exist?(file_path) redirect_to new_sigaa_import_path, alert: "Arquivo class_members.json não encontrado no projeto." return end - # Processa a importação - service = SigaaImportService.new(file_path) + # Processa a importação (passa classes.json se existir) + service = SigaaImportService.new(file_path, classes_file_path) @results = service.process if @results[:errors].any? @@ -34,13 +35,14 @@ def create def update # Usa automaticamente o arquivo class_members.json do projeto (atualização) file_path = Rails.root.join("class_members.json") + classes_file_path = Rails.root.join("classes.json") unless File.exist?(file_path) redirect_to new_sigaa_import_path, alert: "Arquivo class_members.json não encontrado no projeto." return end - service = SigaaImportService.new(file_path) + service = SigaaImportService.new(file_path, classes_file_path) @results = service.process if @results[:errors].any? diff --git a/app/services/csv_formatter_service.rb b/app/services/csv_formatter_service.rb index b38cf162c0..20744f31e7 100644 --- a/app/services/csv_formatter_service.rb +++ b/app/services/csv_formatter_service.rb @@ -10,8 +10,8 @@ def generate csv << headers @avaliacao.submissoes.includes(:aluno, :respostas).each do |submissao| - aluno = submissao.aluno - row = [ aluno.matricula, aluno.nome ] + # Usar ID anônimo em vez do nome/matrícula do aluno para privacidade + row = [ submissao.id ] # Organiza as respostas pela ordem das questões se possível, ou mapeamento simples # Assumindo que queremos mapear questões para colunas @@ -31,8 +31,8 @@ def generate private def headers - # Cabeçalhos estáticos para informações do Aluno - base_headers = [ "Matrícula", "Nome" ] + # Cabeçalho anônimo (sem identificação do aluno para privacidade) + base_headers = [ "Submissão" ] # Cabeçalhos dinâmicos para questões # Identificando questões únicas respondidas ou todas as questões do modelo diff --git a/app/services/sigaa_import_service.rb b/app/services/sigaa_import_service.rb index 7d4842387a..967f9f78ec 100644 --- a/app/services/sigaa_import_service.rb +++ b/app/services/sigaa_import_service.rb @@ -2,8 +2,9 @@ require "csv" class SigaaImportService - def initialize(file_path) + def initialize(file_path, classes_file_path = nil) @file_path = file_path + @classes_file_path = classes_file_path @results = { turmas_created: 0, turmas_updated: 0, @@ -50,13 +51,18 @@ def process def process_json data = JSON.parse(File.read(@file_path)) + classes_lookup = build_classes_lookup # class_members.json é um array de turmas data.each do |turma_data| + # Busca o nome real da turma em classes.json + class_key = [ turma_data["code"], turma_data["semester"] ] + class_name = classes_lookup[class_key] || turma_data["code"] + # Mapeia campos do formato real para o esperado normalized_data = { "codigo" => turma_data["code"], - "nome" => turma_data["code"], # Usa o código como nome se não tiver + "nome" => class_name, "semestre" => turma_data["semester"], "participantes" => [] } @@ -88,6 +94,23 @@ def process_json end end + # Constrói um hash de lookup para nomes de turmas a partir de classes.json + def build_classes_lookup + return {} unless @classes_file_path && File.exist?(@classes_file_path) + + begin + classes_data = JSON.parse(File.read(@classes_file_path)) + classes_data.each_with_object({}) do |item, hash| + # Usa code + semester como chave composta + key = [ item["code"], item.dig("class", "semester") ] + hash[key] = item["name"] + end + rescue JSON::ParserError + @results[:errors] << "Arquivo classes.json inválido" + {} + end + end + def process_csv CSV.foreach(@file_path, headers: true, col_sep: ",") do |row| # Assumindo estrutura do CSV diff --git a/app/views/avaliacoes/resultados.html.erb b/app/views/avaliacoes/resultados.html.erb index 532c50a395..5e12b205c6 100644 --- a/app/views/avaliacoes/resultados.html.erb +++ b/app/views/avaliacoes/resultados.html.erb @@ -1,4 +1,8 @@ <% content_for :page_title, "Gerenciamento - Gestão de Envios - Resultados" %> + + + +

Resultados da Avaliação

@@ -9,33 +13,110 @@

Turma: <%= @avaliacao.turma.codigo %> - <%= @avaliacao.turma.nome %>

Template: <%= @avaliacao.modelo.titulo %>

+

Total de Submissões: <%= @submissoes.count %>

<% if @submissoes.any? %> -
+
<%= link_to "Download CSV", resultados_avaliacao_path(@avaliacao, format: :csv), class: "bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded" %>
-
- - - - - - - - - - <% @submissoes.each do |submissao| %> - - - - - - <% end %> - -
MatrículaAlunoEnvio
<%= submissao.aluno&.matricula %><%= submissao.aluno&.nome %><%= submissao.respostas.count %> respostas
-
+ +

Estatísticas das Respostas

+ + <% @perguntas.each_with_index do |pergunta, index| %> +
+

+ Questão <%= index + 1 %>: <%= pergunta.enunciado %> +

+

Tipo: <%= pergunta.tipo_humanizado %>

+ + <% stats = @question_stats[pergunta.id] || { type: pergunta.tipo, data: {}, total: 0, responses: [] } %> + + <% if %w[multipla_escolha checkbox escala].include?(pergunta.tipo) && stats[:data].any? %> + +
+ +
+ + <% elsif %w[texto_curto texto_longo].include?(pergunta.tipo) %> + +
+

+ <%= stats[:total] %> respostas recebidas +

+ <% if stats[:responses].present? && stats[:responses].any? %> +
+ <% stats[:responses].each_with_index do |response, resp_index| %> +
+

<%= response %>

+
+ <% end %> +
+ <% elsif stats[:total] > 0 %> +

+ Nenhuma resposta de texto disponível. +

+ <% end %> +
+ <% else %> + +
+

+ <%= stats[:total] %> respostas recebidas +

+
+ <% end %> +
+ <% end %> + <% else %>