Skip to content

Commit

Permalink
Refactor score, transmit_lti_score, send_score & send_scores_for
Browse files Browse the repository at this point in the history
  • Loading branch information
kiragrammel committed Oct 31, 2023
1 parent 92aeb59 commit 6b168cd
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 103 deletions.
20 changes: 5 additions & 15 deletions app/assets/javascripts/editor/editor.js.erb
Original file line number Diff line number Diff line change
Expand Up @@ -785,14 +785,11 @@ var CodeOceanEditor = {
case 'not_for_all_users_submitted':
this.showNotForAllUsersSubmittedMessage(output.failed_users);
break;
case 'too_late_and_not_for_all_users_submitted':
this.showTooLateAndNotForAllUsersSubmittedMessage(output.score_sent, output.failed_users);
break;
case 'scoring_too_late':
this.showScoringTooLateMessage(output.score_sent);
break;
case 'full_score_reached':
this.showScoringFullScoreMessage(output.url);
case 'exercise_finished':
this.showExerciseFinishedMessage(output.url);
break;
}
},
Expand Down Expand Up @@ -874,25 +871,18 @@ var CodeOceanEditor = {
});
},

showTooLateAndNotForAllUsersSubmittedMessage: function (score_sent, failed_users) {
$.flash.warning({
icon: ['fa-solid', 'fa-triangle-exclamation'],
text: I18n.t('exercises.submit.too_late_and_not_for_all_users_submitted', {score_sent: score_sent, user: failed_users})
});
},

showScoringTooLateMessage: function (score_sent) {
$.flash.warning({
$.flash.info({
icon: ['fa-solid', 'fa-triangle-exclamation'],
text: I18n.t('exercises.submit.too_late', {score_sent: score_sent})
});
},

showScoringFullScoreMessage: function (url) {
showExerciseFinishedMessage: function (url) {
$.flash.success({
showPermanent: true,
icon: ['fa-solid', 'fa-graduation-cap'],
text: I18n.t('exercises.submit.full_score', {url: url})
text: I18n.t('exercises.submit.exercise_finished', {url: url})
});
},

Expand Down
93 changes: 56 additions & 37 deletions app/controllers/concerns/lti.rb
Original file line number Diff line number Diff line change
Expand Up @@ -123,47 +123,66 @@ def send_scores(submission)
raise Error.new("Score #{submission.normalized_score} must be between 0 and #{MAXIMUM_SCORE}!")
end

submission.users.map {|user| send_score_for submission, user }
# Prepare score to be sent
score = submission.normalized_score
deadline = :none
if submission.before_deadline?
# Keep the full score
deadline = :before_deadline

Check warning on line 131 in app/controllers/concerns/lti.rb

View check run for this annotation

Codecov / codecov/patch

app/controllers/concerns/lti.rb#L131

Added line #L131 was not covered by tests
elsif submission.within_grace_period?
# Reduce score by 20%
score *= 0.8
deadline = :within_grace_period

Check warning on line 135 in app/controllers/concerns/lti.rb

View check run for this annotation

Codecov / codecov/patch

app/controllers/concerns/lti.rb#L134-L135

Added lines #L134 - L135 were not covered by tests
elsif submission.after_late_deadline?
# Reduce score by 100%
score *= 0.0
deadline = :after_late_deadline

Check warning on line 139 in app/controllers/concerns/lti.rb

View check run for this annotation

Codecov / codecov/patch

app/controllers/concerns/lti.rb#L138-L139

Added lines #L138 - L139 were not covered by tests
end

# Actually send the score for all users
detailed_results = submission.users.map {|user| send_score_for submission, user, score }

# Prepare return value
erroneous_results = detailed_results.filter {|result| ERROR_STATUS.include?(result[:status]) }
statistics = {
all: detailed_results,
success: detailed_results - erroneous_results,
error: erroneous_results,
}

{
users: statistics.transform_values {|value| value.pluck(:user) },
score: {original: submission.normalized_score, sent: score},
deadline:,
detailed_results:,
}
end

private :send_scores

def send_score_for(submission, user)
if user.external_user? && user.consumer
lti_parameter = user.lti_parameters.find_by(exercise: submission.exercise, study_group: submission.study_group)
provider = build_tool_provider(consumer: user.consumer, parameters: lti_parameter&.lti_parameters)
end

if provider.nil?
{status: 'error', user: user.displayname}
elsif provider.outcome_service?
Sentry.set_extras({
provider: provider.inspect,
score: submission.normalized_score,
lti_parameter: lti_parameter.inspect,
session: session.to_hash,
exercise_id: submission.exercise_id,
})
normalized_lti_score = submission.normalized_score
if submission.before_deadline?
# Keep the full score
elsif submission.within_grace_period?
# Reduce score by 20%
normalized_lti_score *= 0.8
elsif submission.after_late_deadline?
# Reduce score by 100%
normalized_lti_score *= 0.0
end

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, user: user.displayname}
rescue IMS::LTI::XMLParseError, Net::OpenTimeout, Net::ReadTimeout, Errno::ECONNRESET, SocketError, EOFError
# A parsing error might happen if the LTI provider is down and doesn't return a valid XML response
{status: 'error', user: user.displayname}
end
else
{status: 'unsupported', user: user.displayname}
def send_score_for(submission, user, score)
return {status: 'error', user:} unless user.external_user? && user.consumer

lti_parameter = user.lti_parameters.find_by(exercise: submission.exercise, study_group: submission.study_group)
provider = build_tool_provider(consumer: user.consumer, parameters: lti_parameter&.lti_parameters)
return {status: 'error', user:} if provider.nil?
return {status: 'unsupported', user:} unless provider.outcome_service?

Sentry.set_extras({
provider: provider.inspect,
normalized_score: submission.normalized_score,
score:,
lti_parameter: lti_parameter.inspect,
session: defined?(session) ? session.to_hash : nil,
exercise_id: submission.exercise_id,
})

begin
response = provider.post_replace_result!(score)
{code: response.response_code, message: response.post_response.body, status: response.code_major, user:}
rescue IMS::LTI::XMLParseError, Net::OpenTimeout, Net::ReadTimeout, Errno::ECONNRESET, SocketError, EOFError

Check warning on line 183 in app/controllers/concerns/lti.rb

View check run for this annotation

Codecov / codecov/patch

app/controllers/concerns/lti.rb#L183

Added line #L183 was not covered by tests
# A parsing error might happen if the LTI provider is down and doesn't return a valid XML response
{status: 'error', user:}

Check warning on line 185 in app/controllers/concerns/lti.rb

View check run for this annotation

Codecov / codecov/patch

app/controllers/concerns/lti.rb#L185

Added line #L185 was not covered by tests
end
end

Expand Down
4 changes: 2 additions & 2 deletions app/controllers/concerns/redirect_behavior.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def redirect_after_submit
rfc = @submission.own_unsolved_rfc(current_user)
if rfc
# set a message that informs the user that his own RFC should be closed.
flash[:notice] = I18n.t('exercises.submit.full_score_redirect_to_own_rfc')
flash[:notice] = I18n.t('exercises.submit.exercise_finished_redirect_to_own_rfc')

Check warning on line 27 in app/controllers/concerns/redirect_behavior.rb

View check run for this annotation

Codecov / codecov/patch

app/controllers/concerns/redirect_behavior.rb#L27

Added line #L27 was not covered by tests
flash.keep(:notice)

respond_to do |format|
Expand All @@ -38,7 +38,7 @@ def redirect_after_submit
rfc = @submission.unsolved_rfc(current_user)
unless rfc.nil? || @embed_options[:disable_redirect_to_rfcs] || @embed_options[:disable_rfc]
# set a message that informs the user that his score was perfect and help in RFC is greatly appreciated.
flash[:notice] = I18n.t('exercises.submit.full_score_redirect_to_rfc')
flash[:notice] = I18n.t('exercises.submit.exercise_finished_redirect_to_rfc')

Check warning on line 41 in app/controllers/concerns/redirect_behavior.rb

View check run for this annotation

Codecov / codecov/patch

app/controllers/concerns/redirect_behavior.rb#L41

Added line #L41 was not covered by tests
flash.keep(:notice)

# increase counter 'times_featured' in rfc
Expand Down
83 changes: 42 additions & 41 deletions app/controllers/submissions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -279,20 +279,18 @@ def score
# To enable hints when scoring a submission, uncomment the next line:
# send_hints(client_socket, StructuredError.where(submission: @submission))

# Submit the score
if @submission.users.map {|user| lti_outcome_service?(@submission.exercise, user, @submission.study_group_id) }.any?
url = finalize_submission_path(@submission)
submit_info = transmit_lti_score
submit_info&.fetch(:status, [])&.each do |s|
# The own score can only be transmitted when the lti_outcome_service is available
# if it is not available the user also does not send the score for the programming group members
if lti_outcome_service?(@submission.exercise, @current_user, @submission.study_group_id)
transmit_lti_score(client_socket)

Check warning on line 285 in app/controllers/submissions_controller.rb

View check run for this annotation

Codecov / codecov/patch

app/controllers/submissions_controller.rb#L285

Added line #L285 was not covered by tests
else
if @submission.score == @submission.exercise.maximum_score
client_socket&.send_data({

Check warning on line 288 in app/controllers/submissions_controller.rb

View check run for this annotation

Codecov / codecov/patch

app/controllers/submissions_controller.rb#L287-L288

Added lines #L287 - L288 were not covered by tests
cmd: :status,
status: s,
url:,
score_sent: submit_info[:score_sent],
failed_users: submit_info[:failed_users],
status: :exercise_finished,
url: finalize_submission_path(@submission),
}.to_json)
end
else
finalize

Check warning on line 294 in app/controllers/submissions_controller.rb

View check run for this annotation

Codecov / codecov/patch

app/controllers/submissions_controller.rb#L294

Added line #L294 was not covered by tests
end
rescue Runner::Error::RunnerInUse => e
Expand Down Expand Up @@ -509,39 +507,42 @@ def set_testrun
}
end

def transmit_lti_score
responses = send_scores(@submission)
failed_users = []
too_late = false
status = []
score_sent = nil

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
too_late = true
score_sent = response[:score_sent]
end
def check_scoring_too_late(submit_info, client_socket)
if submit_info[:deadline] == :within_grace_period || submit_info[:deadline] == :after_late_deadline
client_socket&.send_data({

Check warning on line 512 in app/controllers/submissions_controller.rb

View check run for this annotation

Codecov / codecov/patch

app/controllers/submissions_controller.rb#L511-L512

Added lines #L511 - L512 were not covered by tests
cmd: :status,
status: :scoring_too_late,
score_sent: submit_info[:score][:sent],
}.to_json)
end
end

if failed_users.size == responses.size # all submissions failed
status.push(:scoring_failure)
{status:}
elsif too_late & failed_users.size.positive? # submissions are too late and at least one submission failed
status.push(:too_late_and_not_for_all_users_submitted)
failed_users = failed_users.join(', ')
{status:, score_sent:, failed_users:}
elsif failed_users.size.positive? # at least one submission failed
status.push(:not_for_all_users_submitted)
failed_users = failed_users.join(', ')
{status:, failed_users:}
elsif too_late # submissions are too late
status.push(:scoring_too_late)
{status:, score_sent:}
elsif @submission.score == @submission.exercise.maximum_score
status.push(:full_score_reached)
{status:}
def transmit_lti_score(client_socket)
submit_info = send_scores(@submission)

Check warning on line 521 in app/controllers/submissions_controller.rb

View check run for this annotation

Codecov / codecov/patch

app/controllers/submissions_controller.rb#L521

Added line #L521 was not covered by tests

if (submit_info[:users][:all].count == submit_info[:users][:success].count) & (submit_info[:score][:original] == BigDecimal('1.0'))
client_socket&.send_data({

Check warning on line 524 in app/controllers/submissions_controller.rb

View check run for this annotation

Codecov / codecov/patch

app/controllers/submissions_controller.rb#L523-L524

Added lines #L523 - L524 were not covered by tests
cmd: :status,
status: :exercise_finished,
url: finalize_submission_path(@submission),
}.to_json)

check_scoring_too_late(submit_info, client_socket)
elsif submit_info[:users][:all].count == submit_info[:users][:error].count
client_socket&.send_data({

Check warning on line 532 in app/controllers/submissions_controller.rb

View check run for this annotation

Codecov / codecov/patch

app/controllers/submissions_controller.rb#L530-L532

Added lines #L530 - L532 were not covered by tests
cmd: :status,
status: :scoring_failure,
}.to_json)
elsif submit_info[:users][:all].count != submit_info[:users][:success].count
failed_users = submit_info[:users][:error].map(&:displayname).join(', ')

Check warning on line 537 in app/controllers/submissions_controller.rb

View check run for this annotation

Codecov / codecov/patch

app/controllers/submissions_controller.rb#L536-L537

Added lines #L536 - L537 were not covered by tests

client_socket&.send_data({

Check warning on line 539 in app/controllers/submissions_controller.rb

View check run for this annotation

Codecov / codecov/patch

app/controllers/submissions_controller.rb#L539

Added line #L539 was not covered by tests
cmd: :status,
status: :not_for_all_users_submitted,
failed_users:,
}.to_json)

check_scoring_too_late(submit_info, client_socket)

Check warning on line 545 in app/controllers/submissions_controller.rb

View check run for this annotation

Codecov / codecov/patch

app/controllers/submissions_controller.rb#L545

Added line #L545 was not covered by tests
end
end

Expand Down
7 changes: 3 additions & 4 deletions config/locales/de.yml
Original file line number Diff line number Diff line change
Expand Up @@ -558,10 +558,9 @@ de:
submit:
failure: Die Bewertung wurde erfolgreich durchgeführt, aber beim Übermitteln Ihrer Punktzahl ist ein Fehler aufgetreten. Bitte versuchen Sie es später erneut.
too_late: Ihre Abgabe wurde erfolgreich gespeichert, ging jedoch nach der Abgabefrist ein, sodass nur %{score_sent} Punkte übertragen wurden.
too_late_and_not_for_all_users_submitted: "Die Punkteübertragung war nur teilweise erfolgreich. Ihre Abgabe wurde erfolgreich gespeichert, ging jedoch nach der Abgabefrist ein, sodass nur %{score_sent} Punkte übertragen wurden. Für %{user} konnten die Punkte nicht übertragen werden. Diese Person(en) sollte die Aufgabe über die e-Learning Platform erneut öffnen und anschließend die Punkte selbst übermitteln."
full_score: Herzlichen Glückwunsch! Sie haben die maximale Punktzahl für diese Aufgabe erreicht. <a href="%{url}" class="alert-link">Klicken Sie hier, um die Aufgabe jetzt abzuschließen.</a>
full_score_redirect_to_rfc: Herzlichen Glückwunsch! Sie haben die maximale Punktzahl für diese Aufgabe an den Kurs übertragen. Ein anderer Teilnehmer hat eine Frage zu der von Ihnen gelösten Aufgabe. Er würde sich sicherlich sehr über ihre Hilfe und Kommentare freuen.
full_score_redirect_to_own_rfc: Herzlichen Glückwunsch! Sie haben die maximale Punktzahl für diese Aufgabe an den Kurs übertragen. Ihre Frage ist damit wahrscheinlich gelöst? Falls ja, fügen Sie doch den entscheidenden Kniff als Antwort hinzu und markieren die Frage als gelöst, bevor sie das Fenster schließen.
exercise_finished: Herzlichen Glückwunsch! Sie haben die Aufgabe vollständig gelöst. <a href="%{url}" class="alert-link">Klicken Sie hier, um die Aufgabe jetzt abzuschließen.</a>
exercise_finished_redirect_to_rfc: Herzlichen Glückwunsch! Sie haben die Aufgabe vollständig gelöst und die Punkte übertragen. Ein anderer Teilnehmer hat eine Frage zu der von Ihnen gelösten Aufgabe. Er würde sich sicherlich sehr über Ihre Hilfe und Kommentare freuen.
exercise_finished_redirect_to_own_rfc: Herzlichen Glückwunsch! Sie haben die Aufgabe vollständig gelöst und die Punkte übertragen. Ihre Frage ist damit wahrscheinlich gelöst? Falls ja, fügen Sie doch den entscheidenden Kniff als Antwort hinzu und markieren die Frage als gelöst, bevor Sie das Fenster schließen.
warning_not_for_all_users_submitted: "Die Punkteübertragung war nur teilweise erfolgreich. Die Punkte konnten nicht für %{user} übertragen werden. Diese Person(en) sollte die Aufgabe über die e-Learning Platform erneut öffnen und anschließend die Punkte selbst übermitteln."
study_group_dashboard:
live_dashboard: Live Dashboard
Expand Down
7 changes: 3 additions & 4 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -558,10 +558,9 @@ en:
submit:
failure: The scoring was performed successfully but an error occurred while transmitting your score. Please try again later.
too_late: Your submission was saved successfully but was received after the deadline, so that only %{score_sent} points were transmitted.
too_late_and_not_for_all_users_submitted: "The transmission of points was only partially successful. Your submission was saved successfully but was received after the deadline, so that only %{score_sent} points were transmitted. For %{user} the score was not transmitted. The user(s) should reopen the exercise via the e-learning platform and then try to submit the points themselves."
full_score: Congratulations! You have achieved the highest possible score for this exercise. <a href="%{url}" class="alert-link">Please click here to finish the exercise now.</a>
full_score_redirect_to_rfc: Congratulations! You achieved and submitted the highest possible score for this exercise. Another participant has a question concerning the exercise you just solved. Your help and comments will be greatly appreciated!
full_score_redirect_to_own_rfc: Congratulations! You achieved and submitted the highest possible score for this exercise. Your question concerning the exercise is solved? If so, please share the essential insight with your fellows and mark the question as solved, before you close this window!
exercise_finished: Congratulations! You have completely solved this exercise. <a href="%{url}" class="alert-link">Please click here to finish the exercise now.</a>
exercise_finished_redirect_to_rfc: Congratulations! You have completely solved this exercise and submitted the points. Another participant has a question concerning the exercise you just solved. Your help and comments will be greatly appreciated!
exercise_finished_redirect_to_own_rfc: Congratulations! You have completely solved this exercise and submitted the points. Your question concerning the exercise is solved? If so, please share the essential insight with your fellows and mark the question as solved, before you close this window!
warning_not_for_all_users_submitted: "The transmission of points was only partially successful. The score was not transmitted for %{user}. The user(s) should reopen the exercise via the e-learning platform and then try to submit the points themselves."
study_group_dashboard:
live_dashboard: Live Dashboard
Expand Down

0 comments on commit 6b168cd

Please sign in to comment.