Merge branch 'master' into client-routesv2
This commit is contained in:
@ -5,7 +5,7 @@ class ApplicationController < ActionController::Base
|
||||
MEMBER_ACTIONS = [:destroy, :edit, :show, :update]
|
||||
|
||||
after_action :verify_authorized, except: [:help, :welcome]
|
||||
before_action :set_locale
|
||||
before_action :set_locale, :allow_iframe_requests
|
||||
protect_from_forgery(with: :exception)
|
||||
rescue_from Pundit::NotAuthorizedError, with: :render_not_authorized
|
||||
|
||||
@ -29,4 +29,8 @@ class ApplicationController < ActionController::Base
|
||||
|
||||
def welcome
|
||||
end
|
||||
|
||||
def allow_iframe_requests
|
||||
response.headers.delete('X-Frame-Options')
|
||||
end
|
||||
end
|
||||
|
@ -49,7 +49,7 @@ class CommentsController < ApplicationController
|
||||
@comment = Comment.new(comment_params_without_request_id)
|
||||
|
||||
if comment_params[:request_id]
|
||||
UserMailer.got_new_comment(@comment, RequestForComment.find(comment_params[:request_id]), current_user)
|
||||
UserMailer.got_new_comment(@comment, RequestForComment.find(comment_params[:request_id]), current_user).deliver_now
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
|
@ -74,7 +74,12 @@ module Lti
|
||||
private :require_valid_consumer_key
|
||||
|
||||
def require_valid_exercise_token
|
||||
@exercise = Exercise.find_by(token: params[:custom_token])
|
||||
proxy_exercise = ProxyExercise.find_by(token: params[:custom_token])
|
||||
unless proxy_exercise.nil?
|
||||
@exercise = proxy_exercise.get_matching_exercise(@current_user)
|
||||
else
|
||||
@exercise = Exercise.find_by(token: params[:custom_token])
|
||||
end
|
||||
refuse_lti_launch(message: t('sessions.oauth.invalid_exercise_token')) unless @exercise
|
||||
end
|
||||
private :require_valid_exercise_token
|
||||
@ -129,19 +134,16 @@ module Lti
|
||||
private :set_current_user
|
||||
|
||||
def store_lti_session_data(options = {})
|
||||
exercise = Exercise.where(token: options[:parameters][:custom_token]).first
|
||||
exercise_id = exercise.id unless exercise.nil?
|
||||
|
||||
current_user = ExternalUser.find_or_create_by(consumer_id: options[:consumer].id, external_id: options[:parameters][:user_id].to_s)
|
||||
lti_parameters = LtiParameter.find_or_create_by(consumers_id: options[:consumer].id,
|
||||
external_users_id: current_user.id,
|
||||
exercises_id: exercise_id)
|
||||
external_users_id: @current_user.id,
|
||||
exercises_id: @exercise.id)
|
||||
|
||||
lti_parameters.lti_parameters = options[:parameters].slice(*SESSION_PARAMETERS).to_json
|
||||
lti_parameters.save!
|
||||
@lti_parameters = lti_parameters
|
||||
|
||||
session[:consumer_id] = options[:consumer].id
|
||||
session[:external_user_id] = current_user.id
|
||||
session[:external_user_id] = @current_user.id
|
||||
end
|
||||
private :store_lti_session_data
|
||||
|
||||
|
@ -25,7 +25,7 @@ module SubmissionScoring
|
||||
|
||||
def feedback_message(file, score)
|
||||
set_locale
|
||||
score == Assessor::MAXIMUM_SCORE ? I18n.t('exercises.implement.default_feedback') : file.feedback_message
|
||||
score == Assessor::MAXIMUM_SCORE ? I18n.t('exercises.implement.default_feedback') : render_markdown(file.feedback_message)
|
||||
end
|
||||
|
||||
def score_submission(submission)
|
||||
|
@ -40,7 +40,7 @@ class ExecutionEnvironmentsController < ApplicationController
|
||||
FROM
|
||||
(SELECT user_id,
|
||||
exercise_id,
|
||||
CASE WHEN working_time >= '0:30:00' THEN '0' ELSE working_time END AS working_time_new
|
||||
CASE WHEN working_time >= '0:05:00' THEN '0' ELSE working_time END AS working_time_new
|
||||
FROM
|
||||
(SELECT user_id,
|
||||
exercise_id,
|
||||
|
@ -6,9 +6,10 @@ class ExercisesController < ApplicationController
|
||||
|
||||
before_action :handle_file_uploads, only: [:create, :update]
|
||||
before_action :set_execution_environments, only: [:create, :edit, :new, :update]
|
||||
before_action :set_exercise, only: MEMBER_ACTIONS + [:clone, :implement, :run, :statistics, :submit, :reload]
|
||||
before_action :set_exercise, only: MEMBER_ACTIONS + [:clone, :implement, :working_times, :intervention, :search, :run, :statistics, :submit, :reload]
|
||||
before_action :set_external_user, only: [:statistics]
|
||||
before_action :set_file_types, only: [:create, :edit, :new, :update]
|
||||
before_action :set_course_token, only: [:implement]
|
||||
|
||||
skip_before_filter :verify_authenticity_token, only: [:import_proforma_xml]
|
||||
skip_after_action :verify_authorized, only: [:import_proforma_xml]
|
||||
@ -19,6 +20,15 @@ class ExercisesController < ApplicationController
|
||||
end
|
||||
private :authorize!
|
||||
|
||||
def max_intervention_count
|
||||
3
|
||||
end
|
||||
|
||||
|
||||
def java_course_token
|
||||
"702cbd2a-c84c-4b37-923a-692d7d1532d0"
|
||||
end
|
||||
|
||||
def batch_update
|
||||
@exercises = Exercise.all
|
||||
authorize!
|
||||
@ -54,6 +64,20 @@ class ExercisesController < ApplicationController
|
||||
|
||||
def create
|
||||
@exercise = Exercise.new(exercise_params)
|
||||
collect_set_and_unset_exercise_tags
|
||||
myparam = exercise_params
|
||||
checked_exercise_tags = @exercise_tags.select { | et | myparam[:tag_ids].include? et.tag.id.to_s }
|
||||
removed_exercise_tags = @exercise_tags.reject { | et | myparam[:tag_ids].include? et.tag.id.to_s }
|
||||
|
||||
for et in checked_exercise_tags
|
||||
et.factor = params[:tag_factors][et.tag_id.to_s][:factor]
|
||||
et.exercise = @exercise
|
||||
end
|
||||
|
||||
myparam[:exercise_tags] = checked_exercise_tags
|
||||
myparam.delete :tag_ids
|
||||
removed_exercise_tags.map {|et| et.destroy}
|
||||
|
||||
authorize!
|
||||
create_and_respond(object: @exercise)
|
||||
end
|
||||
@ -63,6 +87,7 @@ class ExercisesController < ApplicationController
|
||||
end
|
||||
|
||||
def edit
|
||||
collect_set_and_unset_exercise_tags
|
||||
end
|
||||
|
||||
def import_proforma_xml
|
||||
@ -118,7 +143,8 @@ class ExercisesController < ApplicationController
|
||||
private :user_by_code_harbor_token
|
||||
|
||||
def exercise_params
|
||||
params[:exercise].permit(:description, :execution_environment_id, :file_id, :instructions, :public, :hide_file_tree, :allow_file_creation, :allow_auto_completion, :title, files_attributes: file_attributes).merge(user_id: current_user.id, user_type: current_user.class.name)
|
||||
params[:exercise][:expected_worktime_seconds] = params[:exercise][:expected_worktime_minutes].to_i * 60
|
||||
params[:exercise].permit(:description, :execution_environment_id, :file_id, :instructions, :public, :hide_file_tree, :allow_file_creation, :allow_auto_completion, :title, :expected_difficulty, :expected_worktime_seconds, files_attributes: file_attributes, :tag_ids => []).merge(user_id: current_user.id, user_type: current_user.class.name)
|
||||
end
|
||||
private :exercise_params
|
||||
|
||||
@ -139,6 +165,22 @@ class ExercisesController < ApplicationController
|
||||
|
||||
def implement
|
||||
redirect_to(@exercise, alert: t('exercises.implement.no_files')) unless @exercise.files.visible.exists?
|
||||
user_solved_exercise = @exercise.has_user_solved(current_user)
|
||||
user_got_enough_interventions = UserExerciseIntervention.where(user: current_user).where("created_at >= ?", Time.zone.now.beginning_of_day).count >= max_intervention_count
|
||||
is_java_course = @course_token && @course_token.eql?(java_course_token)
|
||||
|
||||
user_intervention_group = UserGroupSeparator.getInterventionGroup(current_user)
|
||||
|
||||
case user_intervention_group
|
||||
when :no_intervention
|
||||
when :break_intervention
|
||||
@show_break_interventions = (user_solved_exercise || !is_java_course || user_got_enough_interventions) ? "false" : "true"
|
||||
when :rfc_intervention
|
||||
@show_rfc_interventions = (user_solved_exercise || !is_java_course || user_got_enough_interventions) ? "false" : "true"
|
||||
end
|
||||
|
||||
@search = Search.new
|
||||
@search.exercise = @exercise
|
||||
@submission = current_user.submissions.where(exercise_id: @exercise.id).order('created_at DESC').first
|
||||
@files = (@submission ? @submission.collect_files : @exercise.files).select(&:visible).sort_by(&:name_with_extension)
|
||||
@paths = collect_paths(@files)
|
||||
@ -150,6 +192,59 @@ class ExercisesController < ApplicationController
|
||||
end
|
||||
end
|
||||
|
||||
def set_course_token
|
||||
lti_parameters = LtiParameter.find_by(external_users_id: current_user.id,
|
||||
exercises_id: @exercise.id)
|
||||
if lti_parameters
|
||||
lti_json = lti_parameters.lti_parameters["launch_presentation_return_url"]
|
||||
|
||||
@course_token =
|
||||
unless lti_json.nil?
|
||||
if match = lti_json.match(/^.*courses\/([a-z0-9\-]+)\/sections/)
|
||||
match.captures.first
|
||||
else
|
||||
java_course_token
|
||||
end
|
||||
else
|
||||
""
|
||||
end
|
||||
else
|
||||
# no consumer, therefore implementation with internal user
|
||||
@course_token = java_course_token
|
||||
end
|
||||
end
|
||||
private :set_course_token
|
||||
|
||||
def working_times
|
||||
working_time_accumulated = @exercise.accumulated_working_time_for_only(current_user)
|
||||
working_time_75_percentile = @exercise.get_quantiles([0.75]).first
|
||||
render(json: {working_time_75_percentile: working_time_75_percentile, working_time_accumulated: working_time_accumulated})
|
||||
end
|
||||
|
||||
def intervention
|
||||
intervention = Intervention.find_by_name(params[:intervention_type])
|
||||
unless intervention.nil?
|
||||
uei = UserExerciseIntervention.new(
|
||||
user: current_user, exercise: @exercise, intervention: intervention,
|
||||
accumulated_worktime_s: @exercise.accumulated_working_time_for_only(current_user))
|
||||
uei.save
|
||||
render(json: {success: 'true'})
|
||||
else
|
||||
render(json: {success: 'false', error: "undefined intervention #{params[:intervention_type]}"})
|
||||
end
|
||||
end
|
||||
|
||||
def search
|
||||
search_text = params[:search_text]
|
||||
search = Search.new(user: current_user, exercise: @exercise, search: search_text)
|
||||
|
||||
begin search.save
|
||||
render(json: {success: 'true'})
|
||||
rescue
|
||||
render(json: {success: 'false', error: "could not save search: #{$!}"})
|
||||
end
|
||||
end
|
||||
|
||||
def index
|
||||
@search = policy_scope(Exercise).search(params[:q])
|
||||
@exercises = @search.result.includes(:execution_environment, :user).order(:title).paginate(page: params[:page])
|
||||
@ -174,6 +269,8 @@ class ExercisesController < ApplicationController
|
||||
|
||||
def new
|
||||
@exercise = Exercise.new
|
||||
collect_set_and_unset_exercise_tags
|
||||
|
||||
authorize!
|
||||
end
|
||||
|
||||
@ -201,6 +298,16 @@ class ExercisesController < ApplicationController
|
||||
end
|
||||
private :set_file_types
|
||||
|
||||
def collect_set_and_unset_exercise_tags
|
||||
@search = policy_scope(Tag).search(params[:q])
|
||||
@tags = @search.result.order(:name)
|
||||
checked_exercise_tags = @exercise.exercise_tags
|
||||
checked_tags = checked_exercise_tags.collect{|e| e.tag}.to_set
|
||||
unchecked_tags = Tag.all.to_set.subtract checked_tags
|
||||
@exercise_tags = checked_exercise_tags + unchecked_tags.collect { |tag| ExerciseTag.new(exercise: @exercise, tag: tag)}
|
||||
end
|
||||
private :collect_set_and_unset_exercise_tags
|
||||
|
||||
def show
|
||||
end
|
||||
|
||||
@ -252,7 +359,20 @@ class ExercisesController < ApplicationController
|
||||
private :transmit_lti_score
|
||||
|
||||
def update
|
||||
update_and_respond(object: @exercise, params: exercise_params)
|
||||
collect_set_and_unset_exercise_tags
|
||||
myparam = exercise_params
|
||||
checked_exercise_tags = @exercise_tags.select { | et | myparam[:tag_ids].include? et.tag.id.to_s }
|
||||
removed_exercise_tags = @exercise_tags.reject { | et | myparam[:tag_ids].include? et.tag.id.to_s }
|
||||
|
||||
for et in checked_exercise_tags
|
||||
et.factor = params[:tag_factors][et.tag_id.to_s][:factor]
|
||||
et.exercise = @exercise
|
||||
end
|
||||
|
||||
myparam[:exercise_tags] = checked_exercise_tags
|
||||
myparam.delete :tag_ids
|
||||
removed_exercise_tags.map {|et| et.destroy}
|
||||
update_and_respond(object: @exercise, params: myparam)
|
||||
end
|
||||
|
||||
def redirect_after_submit
|
||||
@ -260,8 +380,12 @@ class ExercisesController < ApplicationController
|
||||
if @submission.normalized_score == 1.0
|
||||
# if user is external and has an own rfc, redirect to it and message him to clean up and accept the answer. (we need to check that the user is external,
|
||||
# otherwise an internal user could be shown a false rfc here, since current_user.id is polymorphic, but only makes sense for external users when used with rfcs.)
|
||||
# redirect 10 percent pseudorandomly to the feedback page
|
||||
if current_user.respond_to? :external_id
|
||||
if rfc = RequestForComment.unsolved.where(exercise_id: @submission.exercise, user_id: current_user.id).first
|
||||
if ((current_user.id + @submission.exercise.created_at.to_i) % 10 == 1)
|
||||
redirect_to_user_feedback
|
||||
return
|
||||
elsif rfc = RequestForComment.unsolved.where(exercise_id: @submission.exercise, user_id: current_user.id).first
|
||||
# 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.keep(:notice)
|
||||
@ -273,7 +397,7 @@ class ExercisesController < ApplicationController
|
||||
return
|
||||
|
||||
# else: show open rfc for same exercise if available
|
||||
elsif rfc = RequestForComment.unsolved.where(exercise_id: @submission.exercise).where.not(question: nil).order("RANDOM()").first
|
||||
elsif rfc = RequestForComment.unsolved.where(exercise_id: @submission.exercise).where.not(question: nil).order("RANDOM()").find { | rfc_element |(rfc_element.comments_count < 5) }
|
||||
# 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.keep(:notice)
|
||||
@ -285,8 +409,25 @@ class ExercisesController < ApplicationController
|
||||
return
|
||||
end
|
||||
end
|
||||
else
|
||||
# redirect to feedback page if score is less than 100 percent
|
||||
redirect_to_user_feedback
|
||||
return
|
||||
end
|
||||
redirect_to_lti_return_path
|
||||
end
|
||||
|
||||
def redirect_to_user_feedback
|
||||
url = if UserExerciseFeedback.find_by(exercise: @exercise, user: current_user)
|
||||
edit_user_exercise_feedback_path(user_exercise_feedback: {exercise_id: @exercise.id})
|
||||
else
|
||||
new_user_exercise_feedback_path(user_exercise_feedback: {exercise_id: @exercise.id})
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to(url) }
|
||||
format.json { render(json: {redirect: url}) }
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -27,7 +27,7 @@ class ExternalUsersController < ApplicationController
|
||||
score,
|
||||
id,
|
||||
CASE
|
||||
WHEN working_time >= '0:30:00' THEN '0'
|
||||
WHEN working_time >= '0:05:00' THEN '0'
|
||||
ELSE working_time
|
||||
END AS working_time_new
|
||||
FROM
|
||||
|
55
app/controllers/interventions_controller.rb
Normal file
55
app/controllers/interventions_controller.rb
Normal file
@ -0,0 +1,55 @@
|
||||
class InterventionsController < ApplicationController
|
||||
include CommonBehavior
|
||||
|
||||
before_action :set_intervention, only: MEMBER_ACTIONS
|
||||
|
||||
def authorize!
|
||||
authorize(@intervention || @interventions)
|
||||
end
|
||||
private :authorize!
|
||||
|
||||
def create
|
||||
#@intervention = Intervention.new(intervention_params)
|
||||
#authorize!
|
||||
#create_and_respond(object: @intervention)
|
||||
end
|
||||
|
||||
def destroy
|
||||
destroy_and_respond(object: @intervention)
|
||||
end
|
||||
|
||||
def edit
|
||||
end
|
||||
|
||||
def intervention_params
|
||||
params[:intervention].permit(:name)
|
||||
end
|
||||
private :intervention_params
|
||||
|
||||
def index
|
||||
@interventions = Intervention.all.paginate(page: params[:page])
|
||||
authorize!
|
||||
end
|
||||
|
||||
def new
|
||||
#@intervention = Intervention.new
|
||||
#authorize!
|
||||
end
|
||||
|
||||
def set_intervention
|
||||
@intervention = Intervention.find(params[:id])
|
||||
authorize!
|
||||
end
|
||||
private :set_intervention
|
||||
|
||||
def show
|
||||
end
|
||||
|
||||
def update
|
||||
update_and_respond(object: @intervention, params: intervention_params)
|
||||
end
|
||||
|
||||
def to_s
|
||||
name
|
||||
end
|
||||
end
|
80
app/controllers/proxy_exercises_controller.rb
Normal file
80
app/controllers/proxy_exercises_controller.rb
Normal file
@ -0,0 +1,80 @@
|
||||
class ProxyExercisesController < ApplicationController
|
||||
include CommonBehavior
|
||||
|
||||
before_action :set_exercise, only: MEMBER_ACTIONS + [:clone, :reload]
|
||||
|
||||
def authorize!
|
||||
authorize(@proxy_exercise || @proxy_exercises)
|
||||
end
|
||||
private :authorize!
|
||||
|
||||
def clone
|
||||
proxy_exercise = @proxy_exercise.duplicate(token: nil, exercises: @proxy_exercise.exercises)
|
||||
proxy_exercise.send(:generate_token)
|
||||
if proxy_exercise.save
|
||||
redirect_to(proxy_exercise, notice: t('shared.object_cloned', model: ProxyExercise.model_name.human))
|
||||
else
|
||||
flash[:danger] = t('shared.message_failure')
|
||||
redirect_to(@proxy_exercise)
|
||||
end
|
||||
end
|
||||
|
||||
def create
|
||||
myparams = proxy_exercise_params
|
||||
myparams[:exercises] = Exercise.find(myparams[:exercise_ids].reject { |c| c.empty? })
|
||||
@proxy_exercise = ProxyExercise.new(myparams)
|
||||
authorize!
|
||||
|
||||
create_and_respond(object: @proxy_exercise)
|
||||
end
|
||||
|
||||
def destroy
|
||||
destroy_and_respond(object: @proxy_exercise)
|
||||
end
|
||||
|
||||
def edit
|
||||
@search = policy_scope(Exercise).search(params[:q])
|
||||
@exercises = @search.result.order(:title)
|
||||
authorize!
|
||||
end
|
||||
|
||||
def proxy_exercise_params
|
||||
params[:proxy_exercise].permit(:description, :title, :exercise_ids => [])
|
||||
end
|
||||
private :proxy_exercise_params
|
||||
|
||||
def index
|
||||
@search = policy_scope(ProxyExercise).search(params[:q])
|
||||
@proxy_exercises = @search.result.order(:title).paginate(page: params[:page])
|
||||
authorize!
|
||||
end
|
||||
|
||||
def new
|
||||
@proxy_exercise = ProxyExercise.new
|
||||
@search = policy_scope(Exercise).search(params[:q])
|
||||
@exercises = @search.result.order(:title)
|
||||
authorize!
|
||||
end
|
||||
|
||||
def set_exercise
|
||||
@proxy_exercise = ProxyExercise.find(params[:id])
|
||||
authorize!
|
||||
end
|
||||
private :set_exercise
|
||||
|
||||
def show
|
||||
@search = @proxy_exercise.exercises.search
|
||||
@exercises = @proxy_exercise.exercises.search.result.order(:title) #@search.result.order(:title)
|
||||
end
|
||||
|
||||
#we might want to think about auth here
|
||||
def reload
|
||||
end
|
||||
|
||||
def update
|
||||
myparams = proxy_exercise_params
|
||||
myparams[:exercises] = Exercise.find(myparams[:exercise_ids].reject { |c| c.blank? })
|
||||
update_and_respond(object: @proxy_exercise, params: myparams)
|
||||
end
|
||||
|
||||
end
|
@ -11,12 +11,14 @@ class RequestForCommentsController < ApplicationController
|
||||
# GET /request_for_comments
|
||||
# GET /request_for_comments.json
|
||||
def index
|
||||
@request_for_comments = RequestForComment.last_per_user(2).order('created_at DESC').paginate(page: params[:page])
|
||||
@search = RequestForComment.last_per_user(2).search(params[:q])
|
||||
@request_for_comments = @search.result.order('created_at DESC').paginate(page: params[:page])
|
||||
authorize!
|
||||
end
|
||||
|
||||
def get_my_comment_requests
|
||||
@request_for_comments = RequestForComment.where(user_id: current_user.id).order('created_at DESC').paginate(page: params[:page])
|
||||
@search = RequestForComment.where(user_id: current_user.id).order('created_at DESC').search(params[:q])
|
||||
@request_for_comments = @search.result.paginate(page: params[:page])
|
||||
render 'index'
|
||||
end
|
||||
|
||||
@ -32,6 +34,10 @@ class RequestForCommentsController < ApplicationController
|
||||
end
|
||||
end
|
||||
|
||||
def submit
|
||||
|
||||
end
|
||||
|
||||
# GET /request_for_comments/1
|
||||
# GET /request_for_comments/1.json
|
||||
def show
|
||||
@ -63,6 +69,20 @@ class RequestForCommentsController < ApplicationController
|
||||
authorize!
|
||||
end
|
||||
|
||||
def create_comment_exercise
|
||||
old = UserExerciseFeedback.find_by(exercise_id: params[:exercise_id], user_id: current_user.id, user_type: current_user.class.name)
|
||||
if old
|
||||
old.delete
|
||||
end
|
||||
uef = UserExerciseFeedback.new(comment_params)
|
||||
|
||||
if uef.save
|
||||
render(json: {success: "true"})
|
||||
else
|
||||
render(json: {success: "false"})
|
||||
end
|
||||
end
|
||||
|
||||
# DELETE /request_for_comments/1
|
||||
# DELETE /request_for_comments/1.json
|
||||
def destroy
|
||||
@ -74,6 +94,10 @@ class RequestForCommentsController < ApplicationController
|
||||
authorize!
|
||||
end
|
||||
|
||||
def comment_params
|
||||
params.permit(:exercise_id, :feedback_text).merge(user_id: current_user.id, user_type: current_user.class.name)
|
||||
end
|
||||
|
||||
private
|
||||
# Use callbacks to share common setup or constraints between actions.
|
||||
def set_request_for_comment
|
||||
@ -85,4 +109,5 @@ class RequestForCommentsController < ApplicationController
|
||||
# we are using the current_user.id here, since internal users are not able to create comments. The external_user.id is a primary key and does not require the consumer_id to be unique.
|
||||
params.require(:request_for_comment).permit(:exercise_id, :file_id, :question, :requested_at, :solved, :submission_id).merge(user_id: current_user.id, user_type: current_user.class.name)
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -1,7 +1,7 @@
|
||||
class SessionsController < ApplicationController
|
||||
include Lti
|
||||
|
||||
[:require_oauth_parameters, :require_valid_consumer_key, :require_valid_oauth_signature, :require_unique_oauth_nonce, :require_valid_exercise_token].each do |method_name|
|
||||
[:require_oauth_parameters, :require_valid_consumer_key, :require_valid_oauth_signature, :require_unique_oauth_nonce, :set_current_user, :require_valid_exercise_token].each do |method_name|
|
||||
before_action(method_name, only: :create_through_lti)
|
||||
end
|
||||
|
||||
@ -18,7 +18,6 @@ class SessionsController < ApplicationController
|
||||
end
|
||||
|
||||
def create_through_lti
|
||||
set_current_user
|
||||
store_lti_session_data(consumer: @consumer, parameters: params)
|
||||
store_nonce(params[:oauth_nonce])
|
||||
redirect_to(implement_exercise_path(@exercise),
|
||||
|
@ -13,6 +13,10 @@ class SubmissionsController < ApplicationController
|
||||
before_action :set_mime_type, only: [:download_file, :render_file]
|
||||
skip_before_action :verify_authenticity_token, only: [:download_file, :render_file]
|
||||
|
||||
def max_message_buffer_size
|
||||
500
|
||||
end
|
||||
|
||||
def authorize!
|
||||
authorize(@submission || @submissions)
|
||||
end
|
||||
@ -156,7 +160,7 @@ class SubmissionsController < ApplicationController
|
||||
tubesock.onmessage do |data|
|
||||
Rails.logger.info(Time.now.getutc.to_s + ": Client sending: " + data)
|
||||
# Check whether the client send a JSON command and kill container
|
||||
# if the command is 'client_exit', send it to docker otherwise.
|
||||
# if the command is 'client_kill', send it to docker otherwise.
|
||||
begin
|
||||
parsed = JSON.parse(data)
|
||||
if parsed['cmd'] == 'client_kill'
|
||||
@ -183,21 +187,31 @@ class SubmissionsController < ApplicationController
|
||||
end
|
||||
|
||||
def kill_socket(tubesock)
|
||||
# save the output of this "run" as a "testrun" (scoring runs are saved in submission_scoring.rb)
|
||||
save_run_output
|
||||
|
||||
# Hijacked connection needs to be notified correctly
|
||||
tubesock.send_data JSON.dump({'cmd' => 'exit'})
|
||||
tubesock.close
|
||||
end
|
||||
|
||||
def handle_message(message, tubesock, container)
|
||||
@message_buffer ||= ""
|
||||
# Handle special commands first
|
||||
if (/^exit/.match(message))
|
||||
kill_socket(tubesock)
|
||||
if (/^#exit/.match(message))
|
||||
# Just call exit_container on the docker_client.
|
||||
# Do not call kill_socket for the websocket to the client here.
|
||||
# @docker_client.exit_container closes the socket to the container,
|
||||
# kill_socket is called in the "on close handler" of the websocket to the container
|
||||
@docker_client.exit_container(container)
|
||||
elsif /^#timeout/.match(message)
|
||||
@message_buffer = 'timeout: ' + @message_buffer # add information that this run timed out to the buffer
|
||||
else
|
||||
# Filter out information about run_command, test_command, user or working directory
|
||||
run_command = @submission.execution_environment.run_command % command_substitutions(params[:filename])
|
||||
test_command = @submission.execution_environment.test_command % command_substitutions(params[:filename])
|
||||
if !(/root|workspace|#{run_command}|#{test_command}/.match(message))
|
||||
@message_buffer += message if @message_buffer.size <= max_message_buffer_size
|
||||
parse_message(message, 'stdout', tubesock)
|
||||
end
|
||||
end
|
||||
@ -245,6 +259,13 @@ class SubmissionsController < ApplicationController
|
||||
end
|
||||
end
|
||||
|
||||
def save_run_output
|
||||
if !@message_buffer.blank?
|
||||
@message_buffer = @message_buffer[(0..max_message_buffer_size-1)] # trim the string to max_message_buffer_size chars
|
||||
Testrun.create(file: @file, submission: @submission, output: @message_buffer)
|
||||
end
|
||||
end
|
||||
|
||||
def score
|
||||
hijack do |tubesock|
|
||||
Thread.new { EventMachine.run } unless EventMachine.reactor_running? && EventMachine.reactor_thread.alive?
|
||||
|
55
app/controllers/tags_controller.rb
Normal file
55
app/controllers/tags_controller.rb
Normal file
@ -0,0 +1,55 @@
|
||||
class TagsController < ApplicationController
|
||||
include CommonBehavior
|
||||
|
||||
before_action :set_tag, only: MEMBER_ACTIONS
|
||||
|
||||
def authorize!
|
||||
authorize(@tag || @tags)
|
||||
end
|
||||
private :authorize!
|
||||
|
||||
def create
|
||||
@tag = Tag.new(tag_params)
|
||||
authorize!
|
||||
create_and_respond(object: @tag)
|
||||
end
|
||||
|
||||
def destroy
|
||||
destroy_and_respond(object: @tag)
|
||||
end
|
||||
|
||||
def edit
|
||||
end
|
||||
|
||||
def tag_params
|
||||
params[:tag].permit(:name)
|
||||
end
|
||||
private :tag_params
|
||||
|
||||
def index
|
||||
@tags = Tag.all.paginate(page: params[:page])
|
||||
authorize!
|
||||
end
|
||||
|
||||
def new
|
||||
@tag = Tag.new
|
||||
authorize!
|
||||
end
|
||||
|
||||
def set_tag
|
||||
@tag = Tag.find(params[:id])
|
||||
authorize!
|
||||
end
|
||||
private :set_tag
|
||||
|
||||
def show
|
||||
end
|
||||
|
||||
def update
|
||||
update_and_respond(object: @tag, params: tag_params)
|
||||
end
|
||||
|
||||
def to_s
|
||||
name
|
||||
end
|
||||
end
|
115
app/controllers/user_exercise_feedbacks_controller.rb
Normal file
115
app/controllers/user_exercise_feedbacks_controller.rb
Normal file
@ -0,0 +1,115 @@
|
||||
class UserExerciseFeedbacksController < ApplicationController
|
||||
include CommonBehavior
|
||||
|
||||
before_action :set_user_exercise_feedback, only: [:edit, :update]
|
||||
|
||||
def comment_presets
|
||||
[[0,t('user_exercise_feedback.difficulty_easy')],
|
||||
[1,t('user_exercise_feedback.difficulty_some_what_easy')],
|
||||
[2,t('user_exercise_feedback.difficulty_ok')],
|
||||
[3,t('user_exercise_feedback.difficulty_some_what_difficult')],
|
||||
[4,t('user_exercise_feedback.difficult_too_difficult')]]
|
||||
end
|
||||
|
||||
def time_presets
|
||||
[[0,t('user_exercise_feedback.estimated_time_less_5')],
|
||||
[1,t('user_exercise_feedback.estimated_time_5_to_10')],
|
||||
[2,t('user_exercise_feedback.estimated_time_10_to_20')],
|
||||
[3,t('user_exercise_feedback.estimated_time_20_to_30')],
|
||||
[4,t('user_exercise_feedback.estimated_time_more_30')]]
|
||||
end
|
||||
|
||||
def authorize!
|
||||
authorize(@uef)
|
||||
end
|
||||
private :authorize!
|
||||
|
||||
def create
|
||||
@exercise = Exercise.find(uef_params[:exercise_id])
|
||||
rfc = RequestForComment.unsolved.where(exercise_id: @exercise.id, user_id: current_user.id).first
|
||||
submission = current_user.submissions.where(exercise_id: @exercise.id).order('created_at DESC').first rescue nil
|
||||
|
||||
if @exercise
|
||||
@uef = UserExerciseFeedback.new(uef_params)
|
||||
if validate_inputs(uef_params)
|
||||
authorize!
|
||||
path =
|
||||
if rfc && submission && submission.normalized_score == 1.0
|
||||
request_for_comment_path(rfc)
|
||||
else
|
||||
implement_exercise_path(@exercise)
|
||||
end
|
||||
create_and_respond(object: @uef, path: proc{path})
|
||||
else
|
||||
flash[:danger] = t('shared.message_failure')
|
||||
redirect_to(:back, id: uef_params[:exercise_id])
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def destroy
|
||||
destroy_and_respond(object: @tag)
|
||||
end
|
||||
|
||||
def edit
|
||||
@texts = comment_presets.to_a
|
||||
@times = time_presets.to_a
|
||||
authorize!
|
||||
end
|
||||
|
||||
def uef_params
|
||||
params[:user_exercise_feedback].permit(:feedback_text, :difficulty, :exercise_id, :user_estimated_worktime).merge(user_id: current_user.id, user_type: current_user.class.name)
|
||||
end
|
||||
private :uef_params
|
||||
|
||||
def new
|
||||
@texts = comment_presets.to_a
|
||||
@times = time_presets.to_a
|
||||
@uef = UserExerciseFeedback.new
|
||||
@exercise = Exercise.find(params[:user_exercise_feedback][:exercise_id])
|
||||
authorize!
|
||||
end
|
||||
|
||||
def update
|
||||
submission = current_user.submissions.where(exercise_id: @exercise.id).order('created_at DESC').first rescue nil
|
||||
rfc = RequestForComment.unsolved.where(exercise_id: @exercise.id, user_id: current_user.id).first
|
||||
authorize!
|
||||
if @exercise && validate_inputs(uef_params)
|
||||
path =
|
||||
if rfc && submission && submission.normalized_score == 1.0
|
||||
request_for_comment_path(rfc)
|
||||
else
|
||||
implement_exercise_path(@exercise)
|
||||
end
|
||||
update_and_respond(object: @uef, params: uef_params, path: path)
|
||||
else
|
||||
flash[:danger] = t('shared.message_failure')
|
||||
redirect_to(:back, id: uef_params[:exercise_id])
|
||||
end
|
||||
end
|
||||
|
||||
def to_s
|
||||
name
|
||||
end
|
||||
|
||||
def set_user_exercise_feedback
|
||||
@exercise = Exercise.find(params[:user_exercise_feedback][:exercise_id])
|
||||
@uef = UserExerciseFeedback.find_by(exercise_id: params[:user_exercise_feedback][:exercise_id], user: current_user)
|
||||
end
|
||||
|
||||
def validate_inputs(uef_params)
|
||||
begin
|
||||
if uef_params[:difficulty].to_i < 0 || uef_params[:difficulty].to_i >= comment_presets.size
|
||||
return false
|
||||
elsif uef_params[:user_estimated_worktime].to_i < 0 || uef_params[:user_estimated_worktime].to_i >= time_presets.size
|
||||
return false
|
||||
else
|
||||
return true
|
||||
end
|
||||
rescue
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
end
|
Reference in New Issue
Block a user