Merge pull request #417 from openHPI/add_more_insights_to_exercises

Add more insights to exercises
This commit is contained in:
MrSerth
2019-11-29 16:00:28 +01:00
committed by GitHub
10 changed files with 56 additions and 23 deletions

View File

@ -7,7 +7,7 @@ class ExercisesController < ApplicationController
before_action :handle_file_uploads, only: [:create, :update] before_action :handle_file_uploads, only: [:create, :update]
before_action :set_execution_environments, only: [:create, :edit, :new, :update] before_action :set_execution_environments, only: [:create, :edit, :new, :update]
before_action :set_exercise_and_authorize, only: MEMBER_ACTIONS + [:clone, :implement, :working_times, :intervention, :search, :run, :statistics, :submit, :reload, :feedback, :study_group_dashboard] before_action :set_exercise_and_authorize, only: MEMBER_ACTIONS + [:clone, :implement, :working_times, :intervention, :search, :run, :statistics, :submit, :reload, :feedback, :requests_for_comments, :study_group_dashboard]
before_action :set_external_user_and_authorize, only: [:statistics] before_action :set_external_user_and_authorize, only: [:statistics]
before_action :set_file_types, only: [:create, :edit, :new, :update] before_action :set_file_types, only: [:create, :edit, :new, :update]
before_action :set_course_token, only: [:implement] before_action :set_course_token, only: [:implement]
@ -105,6 +105,21 @@ class ExercisesController < ApplicationController
def feedback def feedback
authorize! authorize!
@feedbacks = @exercise.user_exercise_feedbacks.paginate(page: params[:page]) @feedbacks = @exercise.user_exercise_feedbacks.paginate(page: params[:page])
@submissions = @feedbacks.map do |feedback|
feedback.exercise.final_submission(feedback.user)
end
end
def requests_for_comments
authorize!
@search = RequestForComment
.with_last_activity
.where(exercise: @exercise)
.ransack(params[:q])
@request_for_comments = @search.result
.order('last_comment DESC')
.paginate(page: params[:page])
render 'request_for_comments/index'
end end
def import_proforma_xml def import_proforma_xml

View File

@ -493,12 +493,17 @@ class Exercise < ApplicationRecord
def maximum_score(user = nil) def maximum_score(user = nil)
if user if user
# FIXME: where(user: user) will not work here!
submissions.where(user: user).where("cause IN ('submit','assess')").where("score IS NOT NULL").order("score DESC").first.score || 0 rescue 0 submissions.where(user: user).where("cause IN ('submit','assess')").where("score IS NOT NULL").order("score DESC").first.score || 0 rescue 0
else else
files.teacher_defined_tests.sum(:weight) files.teacher_defined_tests.sum(:weight)
end end
end end
def final_submission(user)
submissions.final.where(user_id: user.id, user_type: user.class.name).order(created_at: :desc).first
end
def has_user_solved(user) def has_user_solved(user)
maximum_score(user).to_i == maximum_score.to_i maximum_score(user).to_i == maximum_score.to_i
end end

View File

@ -3,11 +3,11 @@ class ExercisePolicy < AdminOrAuthorPolicy
admin? admin?
end end
[:show?, :study_group_dashboard?].each do |action| [:show?, :study_group_dashboard?, :feedback?, :requests_for_comments?, :statistics?].each do |action|
define_method(action) { admin? || teacher? } define_method(action) { admin? || teacher? }
end end
[:clone?, :destroy?, :edit?, :statistics?, :update?, :feedback?].each do |action| [:clone?, :destroy?, :edit?, :update?].each do |action|
define_method(action) { admin? || author? } define_method(action) { admin? || author? }
end end

View File

@ -4,12 +4,17 @@ h1 = link_to_if(policy(@exercise).show?, @exercise, exercise_path(@exercise))
.header = t('activerecord.attributes.exercise.description') .header = t('activerecord.attributes.exercise.description')
.value = render_markdown(@exercise.description) .value = render_markdown(@exercise.description)
.header = t('activerecord.models.user_exercise_feedback.other') span.header.col-sm-3.pl-0 = "#{t('activerecord.attributes.exercise.maximum_score')}"
span.col-sm-9 = @exercise.maximum_score
.header.mt-3 = t('activerecord.models.user_exercise_feedback.other')
- if @feedbacks.nil? or @feedbacks.size == 0 - if @feedbacks.nil? or @feedbacks.size == 0
.no-feedback = t('user_exercise_feedback.no_feedback') .no-feedback = t('user_exercise_feedback.no_feedback')
ul.list-unstyled ul.list-unstyled
- @feedbacks.each do |feedback| - comment_presets = UserExerciseFeedbacksController.new.comment_presets
- time_presets = UserExerciseFeedbacksController.new.time_presets
- @feedbacks.each_with_index do |feedback, index|
li.card.mt-2 li.card.mt-2
.card-header role="tab" id="heading" .card-header role="tab" id="heading"
div.clearfix.feedback-header div.clearfix.feedback-header
@ -20,8 +25,11 @@ h1 = link_to_if(policy(@exercise).show?, @exercise, exercise_path(@exercise))
.card-collapse role="tabpanel" .card-collapse role="tabpanel"
.card-body.feedback .card-body.feedback
.text = feedback.feedback_text .text = feedback.feedback_text
.difficulty = "#{t('user_exercise_feedback.difficulty')} #{feedback.difficulty}" if feedback.difficulty .difficulty = "#{t('user_exercise_feedback.difficulty')} #{comment_presets[feedback.difficulty].join(' - ')}" if feedback.difficulty
.worktime = "#{t('user_exercise_feedback.working_time')} #{feedback.user_estimated_worktime}" if feedback.user_estimated_worktime .worktime = "#{t('user_exercise_feedback.working_time')} #{time_presets[feedback.user_estimated_worktime].join(' - ')}" if feedback.user_estimated_worktime
.card-footer
span.points = "#{t('exercises.statistics.score')}: #{@submissions[index].score}"
span.working_time.pull-right = "#{t('exercises.statistics.worktime')}: #{@exercise.average_working_time_for(feedback.user.id) or 0}"
= render('shared/pagination', collection: @feedbacks) = render('shared/pagination', collection: @feedbacks)

View File

@ -44,6 +44,7 @@ h1 = Exercise.model_name.human(count: 2)
ul.dropdown-menu.float-right role="menu" ul.dropdown-menu.float-right role="menu"
li = link_to(t('shared.show'), exercise, 'data-turbolinks' => "false", class: 'dropdown-item') if policy(exercise).show? li = link_to(t('shared.show'), exercise, 'data-turbolinks' => "false", class: 'dropdown-item') if policy(exercise).show?
li = link_to(t('activerecord.models.user_exercise_feedback.other'), feedback_exercise_path(exercise), class: 'dropdown-item') if policy(exercise).feedback? li = link_to(t('activerecord.models.user_exercise_feedback.other'), feedback_exercise_path(exercise), class: 'dropdown-item') if policy(exercise).feedback?
li = link_to(t('activerecord.models.request_for_comment.other'), requests_for_comments_exercise_path(exercise), class: 'dropdown-item') if policy(exercise).requests_for_comments?
li = link_to(t('shared.destroy'), exercise, data: {confirm: t('shared.confirm_destroy')}, method: :delete, class: 'dropdown-item') if policy(exercise).destroy? li = link_to(t('shared.destroy'), exercise, data: {confirm: t('shared.confirm_destroy')}, method: :delete, class: 'dropdown-item') if policy(exercise).destroy?
li = link_to(t('.clone'), clone_exercise_path(exercise), data: {confirm: t('shared.confirm_destroy')}, method: :post, class: 'dropdown-item') if policy(exercise).clone? li = link_to(t('.clone'), clone_exercise_path(exercise), data: {confirm: t('shared.confirm_destroy')}, method: :post, class: 'dropdown-item') if policy(exercise).clone?

View File

@ -38,18 +38,19 @@ h1 = @exercise
hr hr
div#chart_2 div#chart_2
hr hr
.table-responsive - if current_user.admin?
table.table.table-striped.sortable .table-responsive
thead table.table.table-striped.sortable
tr thead
- ['.user', '.score', '.runs', '.worktime'].each do |title|
th.header = t(title)
tbody
- @exercise.send(symbol).distinct().each do |user|
- if user_statistics[user.id] then us = user_statistics[user.id] else us = {"maximum_score" => nil, "runs" => nil}
- label = "#{user.displayname}"
tr tr
td = link_to_if symbol==:external_users && policy(user).statistics?, label, {controller: "exercises", action: "statistics", external_user_id: user.id, id: @exercise.id} - ['.user', '.score', '.runs', '.worktime'].each do |title|
td = us['maximum_score'] or 0 th.header = t(title)
td = us['runs'] tbody
td = @exercise.average_working_time_for(user.id) or 0 - @exercise.send(symbol).distinct().each do |user|
- if user_statistics[user.id] then us = user_statistics[user.id] else us = {"maximum_score" => nil, "runs" => nil}
- label = "#{user.displayname}"
tr
td = link_to_if symbol==:external_users && policy(user).statistics?, label, {controller: "exercises", action: "statistics", external_user_id: user.id, id: @exercise.id}
td = us['maximum_score'] or 0
td = us['runs']
td = @exercise.average_working_time_for(user.id) or 0

View File

@ -353,6 +353,7 @@ de:
implement: Implementieren implement: Implementieren
test_files: Test-Dateien test_files: Test-Dateien
feedback: Feedback feedback: Feedback
requests_for_comments: Kommentaranfragen
study_group_dashboard: Live Dashboard study_group_dashboard: Live Dashboard
statistics: statistics:
average_score: Durchschnittliche Punktzahl average_score: Durchschnittliche Punktzahl

View File

@ -353,6 +353,7 @@ en:
implement: Implement implement: Implement
test_files: Test Files test_files: Test Files
feedback: Feedback feedback: Feedback
requests_for_comments: Requests for Comments
study_group_dashboard: Live Dashboard study_group_dashboard: Live Dashboard
statistics: statistics:
average_score: Average Score average_score: Average Score

View File

@ -81,6 +81,7 @@ Rails.application.routes.draw do
post :search post :search
get :statistics get :statistics
get :feedback get :feedback
get :requests_for_comments
get :reload get :reload
post :submit post :submit
get 'study_group_dashboard/:study_group_id', to: 'exercises#study_group_dashboard' get 'study_group_dashboard/:study_group_id', to: 'exercises#study_group_dashboard'

View File

@ -14,7 +14,7 @@ let(:exercise) { FactoryBot.build(:dummy) }
end end
end end
[:create?, :index?, :new?].each do |action| [:create?, :index?, :new?, :statistics?, :feedback?, :requests_for_comments?].each do |action|
permissions(action) do permissions(action) do
it 'grants access to admins' do it 'grants access to admins' do
expect(subject).to permit(FactoryBot.build(:admin), exercise) expect(subject).to permit(FactoryBot.build(:admin), exercise)
@ -30,7 +30,7 @@ let(:exercise) { FactoryBot.build(:dummy) }
end end
end end
[:clone?, :destroy?, :edit?, :statistics?, :update?].each do |action| [:clone?, :destroy?, :edit?, :update?].each do |action|
permissions(action) do permissions(action) do
it 'grants access to admins' do it 'grants access to admins' do
expect(subject).to permit(FactoryBot.build(:admin), exercise) expect(subject).to permit(FactoryBot.build(:admin), exercise)