Refactor LTI parameters and add study group

* This change also requires that submissions in our test need to have a valid study group.
This commit is contained in:
Sebastian Serth
2023-08-19 11:28:57 +02:00
committed by Sebastian Serth
parent e2baa2ee55
commit e3603758ef
18 changed files with 114 additions and 54 deletions

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true
require 'oauth/request_proxy/rack_request'
require 'oauth/request_proxy/action_controller_request'
module Lti
extend ActiveSupport::Concern
@ -21,7 +21,7 @@ module Lti
# exercise_id.nil? ==> the user has logged out. All session data is to be destroyed
# exercise_id.exists? ==> the user has submitted the results of an exercise to the consumer.
# Only the lti_parameters are deleted.
# Only the assignment of the programming group is deleted.
def clear_lti_session_data(exercise_id = nil)
if exercise_id.nil?
session.delete(:external_user_id)
@ -148,10 +148,7 @@ module Lti
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
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
@ -237,14 +234,13 @@ module Lti
private :set_embedding_options
def store_lti_session_data(options = {})
lti_parameters = LtiParameter.find_or_create_by(consumers_id: options[:consumer].id,
external_users_id: current_user.id,
exercises_id: @exercise.id)
def store_lti_session_data(parameters)
@lti_parameters = LtiParameter.find_or_initialize_by(external_user: current_user,
study_group_id: session[:study_group_id],
exercise: @exercise)
lti_parameters.lti_parameters = options[:parameters].slice(*SESSION_PARAMETERS).permit!.to_h
lti_parameters.save!
@lti_parameters = lti_parameters
@lti_parameters.lti_parameters = parameters.slice(*SESSION_PARAMETERS).permit!.to_h
@lti_parameters.save!
session[:external_user_id] = current_user.id
end

View File

@ -527,7 +527,7 @@ class ExercisesController < ApplicationController
@submission = Submission.create(submission_params)
@submission.calculate_score
if @submission.users.map {|user| user.external_user? && lti_outcome_service?(@submission.exercise_id, user.id) }.any?
if @submission.users.map {|user| lti_outcome_service?(@submission.exercise, user, @submission.study_group_id) }.any?
transmit_lti_score
else
redirect_after_submit

View File

@ -13,7 +13,7 @@ class ProgrammingGroupsController < ApplicationController
if existing_programming_group
session[:pg_id] = existing_programming_group.id
redirect_to(implement_exercise_path(@exercise),
notice: t("sessions.create_through_lti.session_#{lti_outcome_service?(@exercise.id, current_user.id) ? 'with' : 'without'}_outcome", consumer: @consumer))
notice: t("sessions.create_through_lti.session_#{lti_outcome_service?(@exercise, current_user) ? 'with' : 'without'}_outcome", consumer: @consumer))
end
end

View File

@ -35,7 +35,7 @@ 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)
if !@submission.user.nil? && lti_outcome_service?(@submission.exercise, @submission.user, @submission.study_group_id)
lti_responses = send_scores(@submission)
process_lti_response(lti_responses.first)
else

View File

@ -17,7 +17,7 @@ class SessionsController < ApplicationController
end
def create_through_lti
store_lti_session_data(consumer: @consumer, parameters: params)
store_lti_session_data(params)
store_nonce(params[:oauth_nonce])
if params[:custom_redirect_target]
redirect_to(URI.parse(params[:custom_redirect_target].to_s).path)
@ -25,7 +25,7 @@ class SessionsController < ApplicationController
redirect_to(new_exercise_programming_group_path(@exercise))
else
redirect_to(implement_exercise_path(@exercise),
notice: t("sessions.create_through_lti.session_#{lti_outcome_service?(@exercise.id, current_user.id) ? 'with' : 'without'}_outcome",
notice: t("sessions.create_through_lti.session_#{lti_outcome_service?(@exercise, current_user) ? 'with' : 'without'}_outcome",
consumer: @consumer))
end
end
@ -44,7 +44,7 @@ class SessionsController < ApplicationController
def destroy_through_lti
@submission = Submission.find(params[:submission_id])
authorize(@submission, :show?)
lti_parameter = LtiParameter.where(external_users_id: current_user.id, exercises_id: @submission.exercise_id).last
lti_parameter = current_user.lti_parameters.find_by(exercise: @submission.exercise, study_group_id: current_user.current_study_group_id)
@url = consumer_return_url(build_tool_provider(consumer: current_user.consumer, parameters: lti_parameter&.lti_parameters))
clear_lti_session_data(@submission.exercise_id)

View File

@ -1,13 +1,17 @@
# frozen_string_literal: true
require 'oauth/request_proxy/action_controller_request' # Rails 5 changed `Rack::Request` to `ActionDispatch::Request`
module LtiHelper
def lti_outcome_service?(exercise_id, external_user_id)
return false if external_user_id == ''
# Checks for support of the LTI outcome service for the given exercise and user.
# If the user passed is not the `current_user`, a study group id **must** be passed as well.
def lti_outcome_service?(exercise, user, study_group_id = user.current_study_group_id)
return false unless user.external_user?
lti_parameters = LtiParameter.where(external_users_id: external_user_id,
exercises_id: exercise_id).lis_outcome_service_url?.last
!lti_parameters.nil? && lti_parameters.present?
lis_outcome_service_parameters(exercise, user, study_group_id).present?
end
private
def lis_outcome_service_parameters(exercise, external_user, study_group_id)
external_user.lti_parameters.lis_outcome_service_url?.find_by(exercise:, study_group_id:)
end
end

View File

@ -2,6 +2,7 @@
class ExternalUser < User
validates :external_id, presence: true
has_many :lti_parameters, dependent: :destroy
def displayname
name.presence || "User #{id}"

View File

@ -1,9 +1,12 @@
# frozen_string_literal: true
class LtiParameter < ApplicationRecord
belongs_to :consumer, foreign_key: 'consumers_id'
belongs_to :exercise, foreign_key: 'exercises_id'
belongs_to :external_user, foreign_key: 'external_users_id'
belongs_to :exercise
belongs_to :external_user
belongs_to :study_group, optional: true
delegate :consumer, to: :external_user
validates :external_user_id, uniqueness: {scope: %i[study_group_id exercise_id]}
scope :lis_outcome_service_url?, lambda {
where("lti_parameters.lti_parameters ? 'lis_outcome_service_url'")

View File

@ -8,6 +8,7 @@ class StudyGroup < ApplicationRecord
has_many :remote_evaluation_mappings, dependent: :nullify
has_many :subscriptions, dependent: :nullify
has_many :authentication_tokens, dependent: :nullify
has_many :lti_parameters, dependent: :delete_all
belongs_to :consumer
def users

View File

@ -1,6 +1,4 @@
- external_user_external_id = current_user.respond_to?(:external_id) ? current_user.external_id : ''
- external_user_id = current_user.respond_to?(:external_id) ? current_user.id : ''
- consumer_id = current_user.respond_to?(:external_id) ? current_user.consumer_id : ''
- show_break_interventions = @show_break_interventions || "false"
- show_rfc_interventions = @show_rfc_interventions || "false"
- show_tips_interventions = @show_tips_interventions || "false"
@ -53,7 +51,7 @@
= t('exercises.editor.start_over_active_file')
- unless @embed_options[:disable_run] && @embed_options[:disable_score]
div id='output_sidebar' class='output-col-collapsed' = render('exercises/editor_output', external_user_id: external_user_id, consumer_id: consumer_id )
div id='output_sidebar' class='output-col-collapsed' = render('exercises/editor_output')
= render('shared/modal', id: 'comment-modal', title: t('exercises.implement.comment.request'), template: 'exercises/_request_comment_dialogcontent') unless @embed_options[:disable_rfc]

View File

@ -60,7 +60,7 @@ div.d-grid id='output_sidebar_uncollapsed' class='d-none col-sm-12 enforce-botto
.progress-bar role='progressbar'
br
- if lti_outcome_service?(@exercise.id, external_user_id)
- if lti_outcome_service?(@exercise, current_user)
p.text-center = render('editor_button', classes: 'btn-lg btn-success d-none', data: {:'data-url' => submit_exercise_path(@exercise)}, icon: 'fa-solid fa-paper-plane', id: 'submit', label: t('exercises.editor.submit'))
- if @exercise.submission_deadline.present? || @exercise.late_submission_deadline.present?
#deadline data-submission-deadline=@exercise.submission_deadline&.rfc2822 data-late-submission-deadline=@exercise.late_submission_deadline&.rfc2822