Send score for all members of a programming group
This commit is contained in:

committed by
Sebastian Serth

parent
2fb8def1d0
commit
e2baa2ee55
@ -211,10 +211,13 @@ CodeOceanEditorSubmissions = {
|
||||
Turbolinks.visit(response.redirect);
|
||||
} else if (response.status === 'container_depleted') {
|
||||
this.showContainerDepletedMessage();
|
||||
} else if (response.message) {
|
||||
$.flash.danger({
|
||||
text: response.message
|
||||
});
|
||||
} else {
|
||||
for (let [type, text] of Object.entries(response)) {
|
||||
$.flash[type]({
|
||||
text: text,
|
||||
showPermanent: true // We might display a very long text message!
|
||||
})
|
||||
}
|
||||
}
|
||||
this.initializeEventHandlers();
|
||||
})
|
||||
|
@ -18,6 +18,7 @@ class ApplicationController < ActionController::Base
|
||||
rescue_from Pundit::NotAuthorizedError, with: :render_not_authorized
|
||||
rescue_from ActiveRecord::RecordNotFound, with: :render_not_found
|
||||
rescue_from ActionController::InvalidAuthenticityToken, with: :render_csrf_error
|
||||
add_flash_types :danger, :warning, :info, :success
|
||||
|
||||
def current_user
|
||||
@current_user ||= find_or_login_current_user&.store_current_study_group_id(session[:study_group_id])
|
||||
|
@ -9,6 +9,7 @@ module Lti
|
||||
MAXIMUM_SCORE = 1
|
||||
MAXIMUM_SESSION_AGE = 60.minutes
|
||||
SESSION_PARAMETERS = %w[launch_presentation_return_url lis_outcome_service_url lis_result_sourcedid].freeze
|
||||
ERROR_STATUS = %w[error unsupported].freeze
|
||||
|
||||
def build_tool_provider(options = {})
|
||||
if options[:consumer] && options[:parameters]
|
||||
@ -135,21 +136,27 @@ module Lti
|
||||
|
||||
private :return_to_consumer
|
||||
|
||||
def send_score(submission)
|
||||
def send_scores(submission)
|
||||
unless (0..MAXIMUM_SCORE).cover?(submission.normalized_score)
|
||||
raise Error.new("Score #{submission.normalized_score} must be between 0 and #{MAXIMUM_SCORE}!")
|
||||
end
|
||||
|
||||
if submission.contributor.consumer
|
||||
lti_parameter = LtiParameter.where(consumers_id: submission.contributor.consumer.id,
|
||||
external_users_id: submission.contributor_id,
|
||||
submission.users.map {|user| send_score_for submission, user }
|
||||
end
|
||||
|
||||
private :send_scores
|
||||
|
||||
def send_score_for(submission, user)
|
||||
if user.consumer
|
||||
lti_parameter = LtiParameter.where(consumers_id: user.consumer.id,
|
||||
external_users_id: user.id,
|
||||
exercises_id: submission.exercise_id).last
|
||||
|
||||
provider = build_tool_provider(consumer: submission.contributor.consumer, parameters: lti_parameter.lti_parameters)
|
||||
provider = build_tool_provider(consumer: user.consumer, parameters: lti_parameter&.lti_parameters)
|
||||
end
|
||||
|
||||
if provider.nil?
|
||||
{status: 'error'}
|
||||
{status: 'error', user: user.displayname}
|
||||
elsif provider.outcome_service?
|
||||
Sentry.set_extras({
|
||||
provider: provider.inspect,
|
||||
@ -171,17 +178,17 @@ module Lti
|
||||
|
||||
begin
|
||||
response = provider.post_replace_result!(normalized_lti_score)
|
||||
{code: response.response_code, message: response.post_response.body, status: response.code_major, score_sent: normalized_lti_score}
|
||||
{code: response.response_code, message: response.post_response.body, status: response.code_major, score_sent: normalized_lti_score, user: user.displayname}
|
||||
rescue IMS::LTI::XMLParseError, Net::OpenTimeout, Net::ReadTimeout, Errno::ECONNRESET, SocketError
|
||||
# A parsing error might happen if the LTI provider is down and doesn't return a valid XML response
|
||||
{status: 'error'}
|
||||
{status: 'error', user: user.displayname}
|
||||
end
|
||||
else
|
||||
{status: 'unsupported'}
|
||||
{status: 'unsupported', user: user.displayname}
|
||||
end
|
||||
end
|
||||
|
||||
private :send_score
|
||||
private :send_score_for
|
||||
|
||||
def set_current_user
|
||||
@current_user = ExternalUser.find_or_create_by(consumer_id: @consumer.id, external_id: @provider.user_id)
|
||||
|
@ -536,25 +536,39 @@ class ExercisesController < ApplicationController
|
||||
Rails.logger.debug { "Runner error while submitting submission #{@submission.id}: #{e.message}" }
|
||||
respond_to do |format|
|
||||
format.html { redirect_to(implement_exercise_path(@submission.exercise)) }
|
||||
format.json { render(json: {message: I18n.t('exercises.editor.depleted'), status: :container_depleted}) }
|
||||
format.json { render(json: {danger: I18n.t('exercises.editor.depleted'), status: :container_depleted}) }
|
||||
end
|
||||
end
|
||||
|
||||
def transmit_lti_score
|
||||
response = send_score(@submission)
|
||||
responses = send_scores(@submission)
|
||||
messages = {}
|
||||
failed_users = []
|
||||
|
||||
if response[:status] == 'success'
|
||||
if response[:score_sent] != @submission.normalized_score
|
||||
# Score has been reduced due to the passed deadline
|
||||
flash.now[:warning] = I18n.t('exercises.submit.too_late')
|
||||
flash.keep(:warning)
|
||||
responses.each do |response|
|
||||
if Lti::ERROR_STATUS.include? response[:status]
|
||||
failed_users << response[:user]
|
||||
elsif response[:score_sent] != @submission.normalized_score # the score was sent successfully, but received too late
|
||||
messages[:warning] = I18n.t('exercises.submit.too_late')
|
||||
end
|
||||
redirect_after_submit
|
||||
end
|
||||
|
||||
if failed_users.size == responses.size # all submissions failed
|
||||
messages[:danger] = I18n.t('exercises.submit.failure')
|
||||
elsif failed_users.size.positive? # at least one submission failed
|
||||
messages[:warning] = [[messages[:warning]], I18n.t('exercises.submit.warning_not_for_all_users_submitted', user: failed_users.join(', '))].join('<br><br>')
|
||||
messages[:warning] = "#{messages[:warning]}\n\n#{I18n.t('exercises.submit.warning_not_for_all_users_submitted', user: failed_users.join(', '))}".strip
|
||||
else
|
||||
respond_to do |format|
|
||||
format.html { redirect_to(implement_exercise_path(@submission.exercise, alert: I18n.t('exercises.submit.failure'))) }
|
||||
format.json { render(json: {message: I18n.t('exercises.submit.failure')}, status: :service_unavailable) }
|
||||
messages.each do |type, message_text|
|
||||
flash.now[type] = message_text
|
||||
flash.keep(type)
|
||||
end
|
||||
return redirect_after_submit
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to(implement_exercise_path(@submission.exercise), **messages) }
|
||||
format.json { render(json: messages) } # We must not change the HTTP status code here, since otherwise the custom message is not displayed.
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -36,8 +36,8 @@ class RemoteEvaluationController < ApplicationController
|
||||
def try_lti
|
||||
# TODO: Need to consider and support programming groups
|
||||
if !@submission.user.nil? && lti_outcome_service?(@submission.exercise_id, @submission.user.id)
|
||||
lti_response = send_score(@submission)
|
||||
process_lti_response(lti_response)
|
||||
lti_responses = send_scores(@submission)
|
||||
process_lti_response(lti_responses.first)
|
||||
else
|
||||
{
|
||||
message: "Your submission was successfully scored with #{@submission.normalized_score * 100}%. " \
|
||||
|
@ -72,11 +72,11 @@ class Submission < ApplicationRecord
|
||||
end
|
||||
|
||||
def normalized_score
|
||||
if !score.nil? && !exercise.maximum_score.nil? && exercise.maximum_score.positive?
|
||||
score / exercise.maximum_score
|
||||
else
|
||||
0
|
||||
end
|
||||
@normalized_score ||= if !score.nil? && !exercise.maximum_score.nil? && exercise.maximum_score.positive?
|
||||
score / exercise.maximum_score
|
||||
else
|
||||
0
|
||||
end
|
||||
end
|
||||
|
||||
def percentage
|
||||
|
@ -1,6 +1,6 @@
|
||||
h1 = t('.headline')
|
||||
|
||||
- consumer = @submission.user.consumer
|
||||
- consumer = current_user.consumer
|
||||
|
||||
p
|
||||
= t(".success_#{consumer ? 'with' : 'without'}_outcome", consumer: consumer)
|
||||
|
Reference in New Issue
Block a user