Apply automatic rubocop fixes
This commit is contained in:
@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Admin
|
||||
class DashboardController < ApplicationController
|
||||
include DashboardHelper
|
||||
|
@ -14,7 +14,8 @@ class ApplicationController < ActionController::Base
|
||||
rescue_from ActionController::InvalidAuthenticityToken, with: :render_csrf_error
|
||||
|
||||
def current_user
|
||||
::NewRelic::Agent.add_custom_attributes(external_user_id: session[:external_user_id], session_user_id: session[:user_id])
|
||||
::NewRelic::Agent.add_custom_attributes(external_user_id: session[:external_user_id],
|
||||
session_user_id: session[:user_id])
|
||||
@current_user ||= ExternalUser.find_by(id: session[:external_user_id]) || login_from_session || login_from_other_sources || nil
|
||||
end
|
||||
|
||||
@ -59,7 +60,7 @@ class ApplicationController < ActionController::Base
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
# Prevent redirect loop
|
||||
if request.url == request.referrer
|
||||
if request.url == request.referer
|
||||
redirect_to :root, alert: message
|
||||
else
|
||||
redirect_back fallback_location: :root, allow_other_host: false, alert: message
|
||||
|
@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module CodeOcean
|
||||
class FilesController < ApplicationController
|
||||
include CommonBehavior
|
||||
@ -25,9 +27,10 @@ module CodeOcean
|
||||
if @object.save
|
||||
yield if block_given?
|
||||
path = options[:path].try(:call) || @object
|
||||
respond_with_valid_object(format, notice: t('shared.object_created', model: @object.class.model_name.human), path: path, status: :created)
|
||||
respond_with_valid_object(format, notice: t('shared.object_created', model: @object.class.model_name.human),
|
||||
path: path, status: :created)
|
||||
else
|
||||
filename = (@object.path || '') + '/' + (@object.name || '') + (@object.file_type.try(:file_extension) || '')
|
||||
filename = "#{@object.path || ''}/#{@object.name || ''}#{@object.file_type.try(:file_extension) || ''}"
|
||||
format.html { redirect_to(options[:path]); flash[:danger] = t('files.error.filename', name: filename) }
|
||||
format.json { render(json: @object.errors, status: :unprocessable_entity) }
|
||||
end
|
||||
@ -41,7 +44,10 @@ module CodeOcean
|
||||
end
|
||||
|
||||
def file_params
|
||||
params[:code_ocean_file].permit(file_attributes).merge(context_type: 'Submission', role: 'user_defined_file') if params[:code_ocean_file].present?
|
||||
if params[:code_ocean_file].present?
|
||||
params[:code_ocean_file].permit(file_attributes).merge(context_type: 'Submission',
|
||||
role: 'user_defined_file')
|
||||
end
|
||||
end
|
||||
private :file_params
|
||||
end
|
||||
|
@ -7,7 +7,8 @@ class CodeharborLinksController < ApplicationController
|
||||
|
||||
def new
|
||||
base_url = CodeOcean::Config.new(:code_ocean).read[:codeharbor][:url] || ''
|
||||
@codeharbor_link = CodeharborLink.new(push_url: base_url + '/import_exercise', check_uuid_url: base_url + '/import_uuid_check')
|
||||
@codeharbor_link = CodeharborLink.new(push_url: "#{base_url}/import_exercise",
|
||||
check_uuid_url: "#{base_url}/import_uuid_check")
|
||||
authorize!
|
||||
end
|
||||
|
||||
|
@ -1,8 +1,10 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class CommentsController < ApplicationController
|
||||
before_action :set_comment, only: [:show, :edit, :update, :destroy]
|
||||
before_action :set_comment, only: %i[show edit update destroy]
|
||||
|
||||
# to disable authorization check: comment the line below back in
|
||||
# skip_after_action :verify_authorized
|
||||
# skip_after_action :verify_authorized
|
||||
|
||||
def authorize!
|
||||
authorize(@comment || @comments)
|
||||
@ -12,16 +14,16 @@ class CommentsController < ApplicationController
|
||||
# GET /comments.json
|
||||
def index
|
||||
file = CodeOcean::File.find(params[:file_id])
|
||||
#there might be no submission yet, so dont use find
|
||||
# there might be no submission yet, so dont use find
|
||||
submission = Submission.find_by(id: file.context_id)
|
||||
if submission
|
||||
@comments = Comment.where(file_id: params[:file_id])
|
||||
@comments.map{|comment|
|
||||
@comments.map do |comment|
|
||||
comment.username = comment.user.displayname
|
||||
comment.date = comment.created_at.strftime('%d.%m.%Y %k:%M')
|
||||
comment.updated = (comment.created_at != comment.updated_at)
|
||||
comment.editable = comment.user == current_user
|
||||
}
|
||||
end
|
||||
else
|
||||
@comments = []
|
||||
end
|
||||
@ -81,9 +83,10 @@ class CommentsController < ApplicationController
|
||||
|
||||
# Never trust parameters from the scary internet, only allow the white list through.
|
||||
def comment_params
|
||||
#params.require(:comment).permit(:user_id, :file_id, :row, :column, :text)
|
||||
# params.require(:comment).permit(:user_id, :file_id, :row, :column, :text)
|
||||
# fuer production mode, damit böse menschen keine falsche user_id uebergeben:
|
||||
params.require(:comment).permit(:file_id, :row, :column, :text, :request_id).merge(user_id: current_user.id, user_type: current_user.class.name)
|
||||
params.require(:comment).permit(:file_id, :row, :column, :text, :request_id).merge(user_id: current_user.id,
|
||||
user_type: current_user.class.name)
|
||||
end
|
||||
|
||||
def send_mail_to_author(comment, request_for_comment)
|
||||
@ -96,15 +99,14 @@ class CommentsController < ApplicationController
|
||||
request_for_comment.commenters.each do |commenter|
|
||||
already_sent_mail = false
|
||||
subscriptions = Subscription.where(
|
||||
:request_for_comment_id => request_for_comment.id,
|
||||
:user_id => commenter.id, :user_type => commenter.class.name,
|
||||
:deleted => false)
|
||||
request_for_comment_id: request_for_comment.id,
|
||||
user_id: commenter.id, user_type: commenter.class.name,
|
||||
deleted: false
|
||||
)
|
||||
subscriptions.each do |subscription|
|
||||
if (subscription.subscription_type == 'author' and current_user == request_for_comment.user) or subscription.subscription_type == 'all'
|
||||
unless subscription.user == current_user or already_sent_mail
|
||||
UserMailer.got_new_comment_for_subscription(comment, subscription, current_user).deliver_now
|
||||
already_sent_mail = true
|
||||
end
|
||||
if (((subscription.subscription_type == 'author') && (current_user == request_for_comment.user)) || (subscription.subscription_type == 'all')) && !((subscription.user == current_user) || already_sent_mail)
|
||||
UserMailer.got_new_comment_for_subscription(comment, subscription, current_user).deliver_now
|
||||
already_sent_mail = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module CommonBehavior
|
||||
def create_and_respond(options = {})
|
||||
@object = options[:object]
|
||||
@ -5,7 +7,8 @@ module CommonBehavior
|
||||
if @object.save
|
||||
yield if block_given?
|
||||
path = options[:path].try(:call) || @object
|
||||
respond_with_valid_object(format, notice: t('shared.object_created', model: @object.class.model_name.human), path: path, status: :created)
|
||||
respond_with_valid_object(format, notice: t('shared.object_created', model: @object.class.model_name.human),
|
||||
path: path, status: :created)
|
||||
else
|
||||
respond_with_invalid_object(format, template: :new)
|
||||
end
|
||||
@ -40,7 +43,8 @@ module CommonBehavior
|
||||
respond_to do |format|
|
||||
if @object.update(options[:params])
|
||||
path = options[:path] || @object
|
||||
respond_with_valid_object(format, notice: t('shared.object_updated', model: @object.class.model_name.human), path: path, status: :ok)
|
||||
respond_with_valid_object(format, notice: t('shared.object_updated', model: @object.class.model_name.human),
|
||||
path: path, status: :ok)
|
||||
else
|
||||
respond_with_invalid_object(format, template: :edit)
|
||||
end
|
||||
|
@ -15,7 +15,8 @@ module FileParameters
|
||||
private :reject_illegal_file_attributes
|
||||
|
||||
def file_attributes
|
||||
%w[content context_id feedback_message file_id file_type_id hidden id name native_file path read_only role weight file_template_id]
|
||||
%w[content context_id feedback_message file_id file_type_id hidden id name native_file path read_only role weight
|
||||
file_template_id]
|
||||
end
|
||||
private :file_attributes
|
||||
end
|
||||
|
@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'oauth/request_proxy/rack_request'
|
||||
|
||||
module Lti
|
||||
@ -6,7 +8,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)
|
||||
SESSION_PARAMETERS = %w[launch_presentation_return_url lis_outcome_service_url lis_result_sourcedid].freeze
|
||||
|
||||
def build_tool_provider(options = {})
|
||||
if options[:consumer] && options[:parameters]
|
||||
@ -20,7 +22,7 @@ module Lti
|
||||
# exercise_id.exists? ==> the user has submitted the results of an exercise to the consumer.
|
||||
# Only the lti_parameters are deleted.
|
||||
def clear_lti_session_data(exercise_id = nil, user_id = nil)
|
||||
if (exercise_id.nil?)
|
||||
if exercise_id.nil?
|
||||
session.delete(:external_user_id)
|
||||
session.delete(:study_group_id)
|
||||
session.delete(:embed_options)
|
||||
@ -48,27 +50,23 @@ module Lti
|
||||
def external_user_name(provider)
|
||||
# save person_name_full if supplied. this is the display_name, if it is set.
|
||||
# else only save the firstname, we don't want lastnames (family names)
|
||||
if provider.lis_person_name_full
|
||||
provider.lis_person_name_full
|
||||
else
|
||||
provider.lis_person_name_given
|
||||
end
|
||||
provider.lis_person_name_full || provider.lis_person_name_given
|
||||
end
|
||||
|
||||
private :external_user_name
|
||||
|
||||
def external_user_role(provider)
|
||||
result = 'learner'
|
||||
unless provider.roles.blank?
|
||||
if provider.roles.present?
|
||||
provider.roles.each do |role|
|
||||
case role.downcase
|
||||
when 'administrator'
|
||||
# We don't want anyone to get admin privileges through LTI
|
||||
result = 'teacher' if result == 'learner'
|
||||
when 'instructor'
|
||||
result = 'teacher' if result == 'learner'
|
||||
else # 'learner'
|
||||
next
|
||||
when 'administrator'
|
||||
# We don't want anyone to get admin privileges through LTI
|
||||
result = 'teacher' if result == 'learner'
|
||||
when 'instructor'
|
||||
result = 'teacher' if result == 'learner'
|
||||
else # 'learner'
|
||||
next
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -141,7 +139,9 @@ module Lti
|
||||
|
||||
def send_score(submission)
|
||||
::NewRelic::Agent.add_custom_attributes({score: submission.normalized_score, session: session})
|
||||
fail(Error, "Score #{submission.normalized_score} must be between 0 and #{MAXIMUM_SCORE}!") unless (0..MAXIMUM_SCORE).include?(submission.normalized_score)
|
||||
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.user.consumer
|
||||
lti_parameter = LtiParameter.where(consumers_id: submission.user.consumer.id,
|
||||
@ -155,12 +155,12 @@ module Lti
|
||||
{status: 'error'}
|
||||
elsif provider.outcome_service?
|
||||
Sentry.set_extras({
|
||||
provider: provider.inspect,
|
||||
provider: provider.inspect,
|
||||
score: submission.normalized_score,
|
||||
lti_parameter: lti_parameter.inspect,
|
||||
session: session.to_hash,
|
||||
exercise_id: submission.exercise_id
|
||||
})
|
||||
exercise_id: submission.exercise_id,
|
||||
})
|
||||
normalized_lit_score = submission.normalized_score
|
||||
if submission.before_deadline?
|
||||
# Keep the full score
|
||||
@ -170,11 +170,10 @@ module Lti
|
||||
elsif submission.after_late_deadline?
|
||||
# Reduce score by 100%
|
||||
normalized_lit_score *= 0.0
|
||||
else # no deadline
|
||||
# Keep the full score
|
||||
end
|
||||
response = provider.post_replace_result!(normalized_lit_score)
|
||||
{code: response.response_code, message: response.post_response.body, status: response.code_major, score_sent: normalized_lit_score}
|
||||
{code: response.response_code, message: response.post_response.body, status: response.code_major,
|
||||
score_sent: normalized_lit_score}
|
||||
else
|
||||
{status: 'unsupported'}
|
||||
end
|
||||
@ -186,22 +185,21 @@ module Lti
|
||||
@current_user = ExternalUser.find_or_create_by(consumer_id: @consumer.id, external_id: @provider.user_id)
|
||||
external_role = external_user_role(@provider)
|
||||
internal_role = @current_user.role
|
||||
desired_role = internal_role != 'admin' ? external_role : internal_role
|
||||
desired_role = internal_role == 'admin' ? internal_role : external_role
|
||||
# Update user with new information but change the role only if he is no admin user
|
||||
@current_user.update(email: external_user_email(@provider), name: external_user_name(@provider), role: desired_role)
|
||||
end
|
||||
|
||||
private :set_current_user
|
||||
|
||||
|
||||
def set_study_group_membership
|
||||
group = if not context_id?
|
||||
StudyGroup.find_or_create_by(external_id: @provider.resource_link_id, consumer: @consumer)
|
||||
else
|
||||
group = if context_id?
|
||||
# Ensure to find the group independent of the name and set it only once.
|
||||
StudyGroup.find_or_create_by(external_id: @provider.context_id, consumer: @consumer) do |new_group|
|
||||
new_group.name = @provider.context_title
|
||||
end
|
||||
else
|
||||
StudyGroup.find_or_create_by(external_id: @provider.resource_link_id, consumer: @consumer)
|
||||
end
|
||||
group.external_users << @current_user unless group.external_users.include? @current_user
|
||||
group.save
|
||||
|
@ -1,8 +1,13 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module RemoteEvaluationParameters
|
||||
include FileParameters
|
||||
|
||||
def remote_evaluation_params
|
||||
remote_evaluation_params = params[:remote_evaluation].permit(:validation_token, files_attributes: file_attributes) if params[:remote_evaluation].present?
|
||||
if params[:remote_evaluation].present?
|
||||
remote_evaluation_params = params[:remote_evaluation].permit(:validation_token,
|
||||
files_attributes: file_attributes)
|
||||
end
|
||||
end
|
||||
private :remote_evaluation_params
|
||||
end
|
||||
end
|
||||
|
@ -4,7 +4,12 @@ module SubmissionParameters
|
||||
include FileParameters
|
||||
|
||||
def submission_params
|
||||
submission_params = params[:submission].present? ? params[:submission].permit(:cause, :exercise_id, files_attributes: file_attributes) : {}
|
||||
submission_params = if params[:submission].present?
|
||||
params[:submission].permit(:cause, :exercise_id,
|
||||
files_attributes: file_attributes)
|
||||
else
|
||||
{}
|
||||
end
|
||||
submission_params = merge_user(submission_params)
|
||||
files_attributes = submission_params[:files_attributes]
|
||||
exercise = Exercise.find_by(id: submission_params[:exercise_id])
|
||||
|
@ -12,8 +12,8 @@ module SubmissionScoring
|
||||
output = execute_test_file(file, submission)
|
||||
assessment = assessor.assess(output)
|
||||
passed = ((assessment[:passed] == assessment[:count]) and (assessment[:score]).positive?)
|
||||
testrun_output = passed ? nil : 'status: ' + output[:status].to_s + "\n stdout: " + output[:stdout].to_s + "\n stderr: " + output[:stderr].to_s
|
||||
unless testrun_output.blank?
|
||||
testrun_output = passed ? nil : "status: #{output[:status]}\n stdout: #{output[:stdout]}\n stderr: #{output[:stderr]}"
|
||||
if testrun_output.present?
|
||||
submission.exercise.execution_environment.error_templates.each do |template|
|
||||
pattern = Regexp.new(template.signature).freeze
|
||||
StructuredError.create_from_template(template, testrun_output, submission) if pattern.match(testrun_output)
|
||||
@ -50,7 +50,8 @@ module SubmissionScoring
|
||||
private :collect_test_results
|
||||
|
||||
def execute_test_file(file, submission)
|
||||
DockerClient.new(execution_environment: file.context.execution_environment).execute_test_command(submission, file.name_with_extension)
|
||||
DockerClient.new(execution_environment: file.context.execution_environment).execute_test_command(submission,
|
||||
file.name_with_extension)
|
||||
end
|
||||
|
||||
private :execute_test_file
|
||||
@ -69,17 +70,21 @@ module SubmissionScoring
|
||||
def score_submission(submission)
|
||||
outputs = collect_test_results(submission)
|
||||
score = 0.0
|
||||
unless outputs.nil? || outputs.empty?
|
||||
if outputs.present?
|
||||
outputs.each do |output|
|
||||
score += output[:score] * output[:weight] unless output.nil?
|
||||
|
||||
output[:stderr] += "\n\n#{t('exercises.editor.timeout', permitted_execution_time: submission.exercise.execution_environment.permitted_execution_time.to_s)}" if output.present? && output[:status] == :timeout
|
||||
if output.present? && output[:status] == :timeout
|
||||
output[:stderr] += "\n\n#{t('exercises.editor.timeout',
|
||||
permitted_execution_time: submission.exercise.execution_environment.permitted_execution_time.to_s)}"
|
||||
end
|
||||
end
|
||||
end
|
||||
submission.update(score: score)
|
||||
if submission.normalized_score == 1.0
|
||||
Thread.new do
|
||||
RequestForComment.where(exercise_id: submission.exercise_id, user_id: submission.user_id, user_type: submission.user_type).each do |rfc|
|
||||
RequestForComment.where(exercise_id: submission.exercise_id, user_id: submission.user_id,
|
||||
user_type: submission.user_type).each do |rfc|
|
||||
rfc.full_score_reached = true
|
||||
rfc.save
|
||||
end
|
||||
|
@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ConsumersController < ApplicationController
|
||||
include CommonBehavior
|
||||
|
||||
@ -18,8 +20,7 @@ class ConsumersController < ApplicationController
|
||||
destroy_and_respond(object: @consumer)
|
||||
end
|
||||
|
||||
def edit
|
||||
end
|
||||
def edit; end
|
||||
|
||||
def consumer_params
|
||||
params[:consumer].permit(:name, :oauth_key, :oauth_secret) if params[:consumer].present?
|
||||
@ -42,8 +43,7 @@ class ConsumersController < ApplicationController
|
||||
end
|
||||
private :set_consumer
|
||||
|
||||
def show
|
||||
end
|
||||
def show; end
|
||||
|
||||
def update
|
||||
update_and_respond(object: @consumer, params: consumer_params)
|
||||
|
@ -1,5 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ErrorTemplateAttributesController < ApplicationController
|
||||
before_action :set_error_template_attribute, only: [:show, :edit, :update, :destroy]
|
||||
before_action :set_error_template_attribute, only: %i[show edit update destroy]
|
||||
|
||||
def authorize!
|
||||
authorize(@error_template_attributes || @error_template_attribute)
|
||||
@ -9,7 +11,8 @@ class ErrorTemplateAttributesController < ApplicationController
|
||||
# GET /error_template_attributes
|
||||
# GET /error_template_attributes.json
|
||||
def index
|
||||
@error_template_attributes = ErrorTemplateAttribute.all.order('important DESC', :key, :id).paginate(page: params[:page])
|
||||
@error_template_attributes = ErrorTemplateAttribute.all.order('important DESC', :key,
|
||||
:id).paginate(page: params[:page])
|
||||
authorize!
|
||||
end
|
||||
|
||||
@ -38,7 +41,9 @@ class ErrorTemplateAttributesController < ApplicationController
|
||||
|
||||
respond_to do |format|
|
||||
if @error_template_attribute.save
|
||||
format.html { redirect_to @error_template_attribute, notice: 'Error template attribute was successfully created.' }
|
||||
format.html do
|
||||
redirect_to @error_template_attribute, notice: 'Error template attribute was successfully created.'
|
||||
end
|
||||
format.json { render :show, status: :created, location: @error_template_attribute }
|
||||
else
|
||||
format.html { render :new }
|
||||
@ -53,7 +58,9 @@ class ErrorTemplateAttributesController < ApplicationController
|
||||
authorize!
|
||||
respond_to do |format|
|
||||
if @error_template_attribute.update(error_template_attribute_params)
|
||||
format.html { redirect_to @error_template_attribute, notice: 'Error template attribute was successfully updated.' }
|
||||
format.html do
|
||||
redirect_to @error_template_attribute, notice: 'Error template attribute was successfully updated.'
|
||||
end
|
||||
format.json { render :show, status: :ok, location: @error_template_attribute }
|
||||
else
|
||||
format.html { render :edit }
|
||||
@ -68,19 +75,25 @@ class ErrorTemplateAttributesController < ApplicationController
|
||||
authorize!
|
||||
@error_template_attribute.destroy
|
||||
respond_to do |format|
|
||||
format.html { redirect_to error_template_attributes_url, notice: 'Error template attribute was successfully destroyed.' }
|
||||
format.html do
|
||||
redirect_to error_template_attributes_url, notice: 'Error template attribute was successfully destroyed.'
|
||||
end
|
||||
format.json { head :no_content }
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
# Use callbacks to share common setup or constraints between actions.
|
||||
def set_error_template_attribute
|
||||
@error_template_attribute = ErrorTemplateAttribute.find(params[:id])
|
||||
end
|
||||
|
||||
# Never trust parameters from the scary internet, only allow the white list through.
|
||||
def error_template_attribute_params
|
||||
params[:error_template_attribute].permit(:key, :description, :regex, :important) if params[:error_template_attribute].present?
|
||||
# Use callbacks to share common setup or constraints between actions.
|
||||
def set_error_template_attribute
|
||||
@error_template_attribute = ErrorTemplateAttribute.find(params[:id])
|
||||
end
|
||||
|
||||
# Never trust parameters from the scary internet, only allow the white list through.
|
||||
def error_template_attribute_params
|
||||
if params[:error_template_attribute].present?
|
||||
params[:error_template_attribute].permit(:key, :description, :regex,
|
||||
:important)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1,5 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ErrorTemplatesController < ApplicationController
|
||||
before_action :set_error_template, only: [:show, :edit, :update, :destroy, :add_attribute, :remove_attribute]
|
||||
before_action :set_error_template, only: %i[show edit update destroy add_attribute remove_attribute]
|
||||
|
||||
def authorize!
|
||||
authorize(@error_templates || @error_template)
|
||||
@ -92,13 +94,17 @@ class ErrorTemplatesController < ApplicationController
|
||||
end
|
||||
|
||||
private
|
||||
# Use callbacks to share common setup or constraints between actions.
|
||||
def set_error_template
|
||||
@error_template = ErrorTemplate.find(params[:id])
|
||||
end
|
||||
|
||||
# Never trust parameters from the scary internet, only allow the white list through.
|
||||
def error_template_params
|
||||
params[:error_template].permit(:name, :execution_environment_id, :signature, :description, :hint) if params[:error_template].present?
|
||||
# Use callbacks to share common setup or constraints between actions.
|
||||
def set_error_template
|
||||
@error_template = ErrorTemplate.find(params[:id])
|
||||
end
|
||||
|
||||
# Never trust parameters from the scary internet, only allow the white list through.
|
||||
def error_template_params
|
||||
if params[:error_template].present?
|
||||
params[:error_template].permit(:name, :execution_environment_id, :signature, :description,
|
||||
:hint)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1,9 +1,11 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ExecutionEnvironmentsController < ApplicationController
|
||||
include CommonBehavior
|
||||
|
||||
before_action :set_docker_images, only: [:create, :edit, :new, :update]
|
||||
before_action :set_execution_environment, only: MEMBER_ACTIONS + [:execute_command, :shell, :statistics]
|
||||
before_action :set_testing_framework_adapters, only: [:create, :edit, :new, :update]
|
||||
before_action :set_docker_images, only: %i[create edit new update]
|
||||
before_action :set_execution_environment, only: MEMBER_ACTIONS + %i[execute_command shell statistics]
|
||||
before_action :set_testing_framework_adapters, only: %i[create edit new update]
|
||||
|
||||
def authorize!
|
||||
authorize(@execution_environment || @execution_environments)
|
||||
@ -20,17 +22,15 @@ class ExecutionEnvironmentsController < ApplicationController
|
||||
destroy_and_respond(object: @execution_environment)
|
||||
end
|
||||
|
||||
def edit
|
||||
end
|
||||
def edit; end
|
||||
|
||||
def execute_command
|
||||
@docker_client = DockerClient.new(execution_environment: @execution_environment)
|
||||
render(json: @docker_client.execute_arbitrary_command(params[:command]))
|
||||
end
|
||||
|
||||
|
||||
def working_time_query
|
||||
"""
|
||||
"
|
||||
SELECT exercise_id, avg(working_time) as average_time, stddev_samp(extract('epoch' from working_time)) * interval '1 second' as stddev_time
|
||||
FROM
|
||||
(
|
||||
@ -52,11 +52,11 @@ class ExecutionEnvironmentsController < ApplicationController
|
||||
GROUP BY exercise_id, user_id, id) AS foo) AS bar
|
||||
GROUP BY user_id, exercise_id
|
||||
) AS baz GROUP BY exercise_id;
|
||||
"""
|
||||
"
|
||||
end
|
||||
|
||||
def user_query
|
||||
"""
|
||||
"
|
||||
SELECT
|
||||
id AS exercise_id,
|
||||
COUNT(DISTINCT user_id) AS users,
|
||||
@ -79,7 +79,7 @@ class ExecutionEnvironmentsController < ApplicationController
|
||||
GROUP BY e.id,
|
||||
s.user_id) AS inner_query
|
||||
GROUP BY id;
|
||||
"""
|
||||
"
|
||||
end
|
||||
|
||||
def statistics
|
||||
@ -87,21 +87,25 @@ class ExecutionEnvironmentsController < ApplicationController
|
||||
user_statistics = {}
|
||||
|
||||
ApplicationRecord.connection.execute(working_time_query).each do |tuple|
|
||||
working_time_statistics[tuple["exercise_id"].to_i] = tuple
|
||||
working_time_statistics[tuple['exercise_id'].to_i] = tuple
|
||||
end
|
||||
|
||||
ApplicationRecord.connection.execute(user_query).each do |tuple|
|
||||
user_statistics[tuple["exercise_id"].to_i] = tuple
|
||||
user_statistics[tuple['exercise_id'].to_i] = tuple
|
||||
end
|
||||
|
||||
render locals: {
|
||||
working_time_statistics: working_time_statistics,
|
||||
user_statistics: user_statistics
|
||||
user_statistics: user_statistics,
|
||||
}
|
||||
end
|
||||
|
||||
def execution_environment_params
|
||||
params[:execution_environment].permit(:docker_image, :exposed_ports, :editor_mode, :file_extension, :file_type_id, :help, :indent_size, :memory_limit, :name, :network_enabled, :permitted_execution_time, :pool_size, :run_command, :test_command, :testing_framework).merge(user_id: current_user.id, user_type: current_user.class.name) if params[:execution_environment].present?
|
||||
if params[:execution_environment].present?
|
||||
params[:execution_environment].permit(:docker_image, :exposed_ports, :editor_mode, :file_extension, :file_type_id, :help, :indent_size, :memory_limit, :name, :network_enabled, :permitted_execution_time, :pool_size, :run_command, :test_command, :testing_framework).merge(
|
||||
user_id: current_user.id, user_type: current_user.class.name
|
||||
)
|
||||
end
|
||||
end
|
||||
private :execution_environment_params
|
||||
|
||||
@ -118,10 +122,10 @@ class ExecutionEnvironmentsController < ApplicationController
|
||||
def set_docker_images
|
||||
DockerClient.check_availability!
|
||||
@docker_images = DockerClient.image_tags.sort
|
||||
rescue DockerClient::Error => error
|
||||
rescue DockerClient::Error => e
|
||||
@docker_images = []
|
||||
flash[:warning] = error.message
|
||||
Sentry.capture_exception(error)
|
||||
flash[:warning] = e.message
|
||||
Sentry.capture_exception(e)
|
||||
end
|
||||
private :set_docker_images
|
||||
|
||||
@ -139,8 +143,7 @@ class ExecutionEnvironmentsController < ApplicationController
|
||||
end
|
||||
private :set_testing_framework_adapters
|
||||
|
||||
def shell
|
||||
end
|
||||
def shell; end
|
||||
|
||||
def show
|
||||
if @execution_environment.testing_framework?
|
||||
|
@ -1,15 +1,16 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ExerciseCollectionsController < ApplicationController
|
||||
include CommonBehavior
|
||||
|
||||
before_action :set_exercise_collection, only: [:show, :edit, :update, :destroy, :statistics]
|
||||
before_action :set_exercise_collection, only: %i[show edit update destroy statistics]
|
||||
|
||||
def index
|
||||
@exercise_collections = ExerciseCollection.all.paginate(:page => params[:page])
|
||||
@exercise_collections = ExerciseCollection.all.paginate(page: params[:page])
|
||||
authorize!
|
||||
end
|
||||
|
||||
def show
|
||||
end
|
||||
def show; end
|
||||
|
||||
def new
|
||||
@exercise_collection = ExerciseCollection.new
|
||||
@ -28,16 +29,14 @@ class ExerciseCollectionsController < ApplicationController
|
||||
destroy_and_respond(object: @exercise_collection)
|
||||
end
|
||||
|
||||
def edit
|
||||
end
|
||||
def edit; end
|
||||
|
||||
def update
|
||||
authorize!
|
||||
update_and_respond(object: @exercise_collection, params: exercise_collection_params)
|
||||
end
|
||||
|
||||
def statistics
|
||||
end
|
||||
def statistics; end
|
||||
|
||||
private
|
||||
|
||||
@ -51,8 +50,18 @@ class ExerciseCollectionsController < ApplicationController
|
||||
end
|
||||
|
||||
def exercise_collection_params
|
||||
sanitized_params = params[:exercise_collection].present? ? params[:exercise_collection].permit(:name, :use_anomaly_detection, :user_id, :user_type, :exercise_ids => []).merge(user_type: InternalUser.name) : {}
|
||||
sanitized_params[:exercise_ids] = sanitized_params[:exercise_ids].reject {|v| v.nil? or v == ''}
|
||||
sanitized_params.tap {|p| p[:exercise_collection_items] = p[:exercise_ids].map.with_index {|_id, index| ExerciseCollectionItem.find_or_create_by(exercise_id: _id, exercise_collection_id: @exercise_collection.id, position: index)}; p.delete(:exercise_ids)}
|
||||
sanitized_params = if params[:exercise_collection].present?
|
||||
params[:exercise_collection].permit(:name,
|
||||
:use_anomaly_detection, :user_id, :user_type, exercise_ids: []).merge(user_type: InternalUser.name)
|
||||
else
|
||||
{}
|
||||
end
|
||||
sanitized_params[:exercise_ids] = sanitized_params[:exercise_ids].reject {|v| v.nil? or v == '' }
|
||||
sanitized_params.tap do |p|
|
||||
p[:exercise_collection_items] = p[:exercise_ids].map.with_index do |_id, index|
|
||||
ExerciseCollectionItem.find_or_create_by(exercise_id: _id, exercise_collection_id: @exercise_collection.id, position: index)
|
||||
end
|
||||
p.delete(:exercise_ids)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -9,15 +9,19 @@ class ExercisesController < ApplicationController
|
||||
|
||||
before_action :handle_file_uploads, only: %i[create update]
|
||||
before_action :set_execution_environments, only: %i[create edit new update]
|
||||
before_action :set_exercise_and_authorize, only: MEMBER_ACTIONS + %i[clone implement working_times intervention search run statistics submit reload feedback requests_for_comments study_group_dashboard export_external_check export_external_confirm]
|
||||
before_action :set_exercise_and_authorize,
|
||||
only: MEMBER_ACTIONS + %i[clone implement working_times intervention search run statistics submit reload feedback
|
||||
requests_for_comments study_group_dashboard export_external_check export_external_confirm]
|
||||
before_action :set_external_user_and_authorize, only: [:statistics]
|
||||
before_action :set_file_types, only: %i[create edit new update]
|
||||
before_action :set_course_token, only: [:implement]
|
||||
before_action :set_available_tips, only: %i[implement show new edit]
|
||||
|
||||
skip_before_action :verify_authenticity_token, only: %i[import_exercise import_uuid_check export_external_confirm export_external_check]
|
||||
skip_before_action :verify_authenticity_token,
|
||||
only: %i[import_exercise import_uuid_check export_external_confirm export_external_check]
|
||||
skip_after_action :verify_authorized, only: %i[import_exercise import_uuid_check export_external_confirm]
|
||||
skip_after_action :verify_policy_scoped, only: %i[import_exercise import_uuid_check export_external_confirm], raise: false
|
||||
skip_after_action :verify_policy_scoped, only: %i[import_exercise import_uuid_check export_external_confirm],
|
||||
raise: false
|
||||
|
||||
def authorize!
|
||||
authorize(@exercise || @exercises)
|
||||
@ -72,8 +76,8 @@ class ExercisesController < ApplicationController
|
||||
return if performed?
|
||||
|
||||
myparam = exercise_params.presence || {}
|
||||
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 }
|
||||
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 }
|
||||
|
||||
checked_exercise_tags.each do |et|
|
||||
et.factor = params[:tag_factors][et.tag_id.to_s][:factor]
|
||||
@ -106,7 +110,8 @@ class ExercisesController < ApplicationController
|
||||
end
|
||||
|
||||
def export_external_check
|
||||
codeharbor_check = ExerciseService::CheckExternal.call(uuid: @exercise.uuid, codeharbor_link: current_user.codeharbor_link)
|
||||
codeharbor_check = ExerciseService::CheckExternal.call(uuid: @exercise.uuid,
|
||||
codeharbor_link: current_user.codeharbor_link)
|
||||
render json: {
|
||||
message: codeharbor_check[:message],
|
||||
actions: render_to_string(
|
||||
@ -116,9 +121,9 @@ class ExercisesController < ApplicationController
|
||||
exercise_found: codeharbor_check[:exercise_found],
|
||||
update_right: codeharbor_check[:update_right],
|
||||
error: codeharbor_check[:error],
|
||||
exported: false
|
||||
exported: false,
|
||||
}
|
||||
)
|
||||
),
|
||||
}, status: :ok
|
||||
end
|
||||
|
||||
@ -133,14 +138,16 @@ class ExercisesController < ApplicationController
|
||||
render json: {
|
||||
status: 'success',
|
||||
message: t('exercises.export_codeharbor.successfully_exported', id: @exercise.id, title: @exercise.title),
|
||||
actions: render_to_string(partial: 'export_actions', locals: {exercise: @exercise, exported: true, error: error})
|
||||
actions: render_to_string(partial: 'export_actions',
|
||||
locals: {exercise: @exercise, exported: true, error: error}),
|
||||
}
|
||||
@exercise.save
|
||||
else
|
||||
render json: {
|
||||
status: 'fail',
|
||||
message: t('exercises.export_codeharbor.export_failed', id: @exercise.id, title: @exercise.title, error: error),
|
||||
actions: render_to_string(partial: 'export_actions', locals: {exercise: @exercise, exported: true, error: error})
|
||||
actions: render_to_string(partial: 'export_actions',
|
||||
locals: {exercise: @exercise, exported: true, error: error}),
|
||||
}
|
||||
end
|
||||
end
|
||||
@ -177,7 +184,7 @@ class ExercisesController < ApplicationController
|
||||
render json: t('exercises.import_codeharbor.import_errors.invalid'), status: :bad_request
|
||||
rescue StandardError => e
|
||||
Sentry.capture_exception(e)
|
||||
render json: t('exercises.import_codeharbor.import_errors.internal_error'), status: 500
|
||||
render json: t('exercises.import_codeharbor.import_errors.internal_error'), status: :internal_server_error
|
||||
end
|
||||
|
||||
def user_from_api_key
|
||||
@ -194,7 +201,11 @@ class ExercisesController < ApplicationController
|
||||
private :user_by_codeharbor_token
|
||||
|
||||
def exercise_params
|
||||
params[:exercise].permit(:description, :execution_environment_id, :file_id, :instructions, :submission_deadline, :late_submission_deadline, :public, :unpublished, :hide_file_tree, :allow_file_creation, :allow_auto_completion, :title, :expected_difficulty, :tips, files_attributes: file_attributes, tag_ids: []).merge(user_id: current_user.id, user_type: current_user.class.name) if params[:exercise].present?
|
||||
if params[:exercise].present?
|
||||
params[:exercise].permit(:description, :execution_environment_id, :file_id, :instructions, :submission_deadline, :late_submission_deadline, :public, :unpublished, :hide_file_tree, :allow_file_creation, :allow_auto_completion, :title, :expected_difficulty, :tips, files_attributes: file_attributes, tag_ids: []).merge(
|
||||
user_id: current_user.id, user_type: current_user.class.name
|
||||
)
|
||||
end
|
||||
end
|
||||
private :exercise_params
|
||||
|
||||
@ -261,8 +272,10 @@ class ExercisesController < ApplicationController
|
||||
redirect_to(@exercise, alert: t('exercises.implement.unpublished')) if @exercise.unpublished? && current_user.role != 'admin' && current_user.role != 'teacher' # TODO: TESTESTEST
|
||||
redirect_to(@exercise, alert: t('exercises.implement.no_files')) unless @exercise.files.visible.exists?
|
||||
user_solved_exercise = @exercise.has_user_solved(current_user)
|
||||
count_interventions_today = UserExerciseIntervention.where(user: current_user).where('created_at >= ?', Time.zone.now.beginning_of_day).count
|
||||
user_got_intervention_in_exercise = UserExerciseIntervention.where(user: current_user, exercise: @exercise).size >= max_intervention_count_per_exercise
|
||||
count_interventions_today = UserExerciseIntervention.where(user: current_user).where('created_at >= ?',
|
||||
Time.zone.now.beginning_of_day).count
|
||||
user_got_intervention_in_exercise = UserExerciseIntervention.where(user: current_user,
|
||||
exercise: @exercise).size >= max_intervention_count_per_exercise
|
||||
(user_got_enough_interventions = count_interventions_today >= max_intervention_count_per_day) || user_got_intervention_in_exercise
|
||||
|
||||
if @embed_options[:disable_interventions]
|
||||
@ -331,14 +344,15 @@ class ExercisesController < ApplicationController
|
||||
end
|
||||
|
||||
# Return an array with top-level tips
|
||||
@tips = nested_tips.values.select { |tip| tip.parent_exercise_tip_id.nil? }
|
||||
@tips = nested_tips.values.select {|tip| tip.parent_exercise_tip_id.nil? }
|
||||
end
|
||||
private :set_available_tips
|
||||
|
||||
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})
|
||||
render(json: {working_time_75_percentile: working_time_75_percentile,
|
||||
working_time_accumulated: working_time_accumulated})
|
||||
end
|
||||
|
||||
def intervention
|
||||
@ -436,7 +450,9 @@ class ExercisesController < ApplicationController
|
||||
checked_exercise_tags = @exercise.exercise_tags
|
||||
checked_tags = checked_exercise_tags.collect(&: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) }
|
||||
@exercise_tags = checked_exercise_tags + unchecked_tags.collect do |tag|
|
||||
ExerciseTag.new(exercise: @exercise, tag: tag)
|
||||
end
|
||||
end
|
||||
private :collect_set_and_unset_exercise_tags
|
||||
|
||||
@ -453,8 +469,10 @@ class ExercisesController < ApplicationController
|
||||
# Render statistics page for one specific external user
|
||||
authorize(@external_user, :statistics?)
|
||||
if policy(@exercise).detailed_statistics?
|
||||
@submissions = Submission.where(user: @external_user, exercise_id: @exercise.id).in_study_group_of(current_user).order('created_at')
|
||||
interventions = UserExerciseIntervention.where('user_id = ? AND exercise_id = ?', @external_user.id, @exercise.id)
|
||||
@submissions = Submission.where(user: @external_user,
|
||||
exercise_id: @exercise.id).in_study_group_of(current_user).order('created_at')
|
||||
interventions = UserExerciseIntervention.where('user_id = ? AND exercise_id = ?', @external_user.id,
|
||||
@exercise.id)
|
||||
@all_events = (@submissions + interventions).sort_by(&:created_at)
|
||||
@deltas = @all_events.map.with_index do |item, index|
|
||||
delta = item.created_at - @all_events[index - 1].created_at if index.positive?
|
||||
@ -465,7 +483,8 @@ class ExercisesController < ApplicationController
|
||||
@working_times_until.push((format_time_difference(@deltas[0..index].inject(:+)) if index.positive?))
|
||||
end
|
||||
else
|
||||
final_submissions = Submission.where(user: @external_user, exercise_id: @exercise.id).in_study_group_of(current_user).final
|
||||
final_submissions = Submission.where(user: @external_user,
|
||||
exercise_id: @exercise.id).in_study_group_of(current_user).final
|
||||
@submissions = []
|
||||
%i[before_deadline within_grace_period after_late_deadline].each do |filter|
|
||||
relevant_submission = final_submissions.send(filter).latest
|
||||
@ -492,7 +511,7 @@ class ExercisesController < ApplicationController
|
||||
user_statistics[tuple['user_id'].to_i] = tuple
|
||||
end
|
||||
render locals: {
|
||||
user_statistics: user_statistics
|
||||
user_statistics: user_statistics,
|
||||
}
|
||||
end
|
||||
end
|
||||
@ -508,7 +527,8 @@ class ExercisesController < ApplicationController
|
||||
end
|
||||
|
||||
def transmit_lti_score
|
||||
::NewRelic::Agent.add_custom_attributes({submission: @submission.id, normalized_score: @submission.normalized_score})
|
||||
::NewRelic::Agent.add_custom_attributes({submission: @submission.id,
|
||||
normalized_score: @submission.normalized_score})
|
||||
response = send_score(@submission)
|
||||
|
||||
if response[:status] == 'success'
|
||||
@ -533,8 +553,8 @@ class ExercisesController < ApplicationController
|
||||
return if performed?
|
||||
|
||||
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 }
|
||||
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 }
|
||||
|
||||
checked_exercise_tags.each do |et|
|
||||
et.factor = params[:tag_factors][et.tag_id.to_s][:factor]
|
||||
@ -624,9 +644,9 @@ class ExercisesController < ApplicationController
|
||||
authorize!
|
||||
@study_group_id = params[:study_group_id]
|
||||
@request_for_comments = RequestForComment
|
||||
.where(exercise: @exercise).includes(:submission)
|
||||
.where(submissions: {study_group_id: @study_group_id})
|
||||
.order(created_at: :desc)
|
||||
.where(exercise: @exercise).includes(:submission)
|
||||
.where(submissions: {study_group_id: @study_group_id})
|
||||
.order(created_at: :desc)
|
||||
|
||||
@graph_data = @exercise.get_working_times_for_study_group(@study_group_id)
|
||||
end
|
||||
|
@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ExternalUsersController < ApplicationController
|
||||
before_action :require_user!
|
||||
|
||||
@ -17,8 +19,8 @@ class ExternalUsersController < ApplicationController
|
||||
authorize!
|
||||
end
|
||||
|
||||
def working_time_query(tag=nil)
|
||||
"""
|
||||
def working_time_query(tag = nil)
|
||||
"
|
||||
SELECT user_id,
|
||||
bar.exercise_id,
|
||||
max(score) as maximum_score,
|
||||
@ -43,16 +45,16 @@ class ExternalUsersController < ApplicationController
|
||||
FROM submissions
|
||||
WHERE user_id = #{@user.id}
|
||||
AND user_type = 'ExternalUser'
|
||||
#{!current_user.admin? ? "AND study_group_id IN (#{current_user.study_groups.pluck(:id).join(', ')}) AND cause = 'submit'" : ''}
|
||||
#{current_user.admin? ? '' : "AND study_group_id IN (#{current_user.study_groups.pluck(:id).join(', ')}) AND cause = 'submit'"}
|
||||
GROUP BY exercise_id,
|
||||
user_id,
|
||||
id
|
||||
) AS foo
|
||||
) AS bar
|
||||
#{tag.nil? ? '' : ' JOIN exercise_tags et ON et.exercise_id = bar.exercise_id AND et.tag_id = ' + tag + ' '}
|
||||
#{tag.nil? ? '' : " JOIN exercise_tags et ON et.exercise_id = bar.exercise_id AND et.tag_id = #{tag} "}
|
||||
GROUP BY user_id,
|
||||
bar.exercise_id;
|
||||
"""
|
||||
"
|
||||
end
|
||||
|
||||
def statistics
|
||||
@ -62,11 +64,11 @@ class ExternalUsersController < ApplicationController
|
||||
statistics = {}
|
||||
|
||||
ApplicationRecord.connection.execute(working_time_query(params[:tag])).each do |tuple|
|
||||
statistics[tuple["exercise_id"].to_i] = tuple
|
||||
statistics[tuple['exercise_id'].to_i] = tuple
|
||||
end
|
||||
|
||||
render locals: {
|
||||
statistics: statistics
|
||||
statistics: statistics,
|
||||
}
|
||||
end
|
||||
|
||||
@ -75,15 +77,15 @@ class ExternalUsersController < ApplicationController
|
||||
authorize!
|
||||
|
||||
statistics = []
|
||||
tags = ProxyExercise.new().get_user_knowledge_and_max_knowledge(@user, @user.participations.uniq.compact)
|
||||
tags = ProxyExercise.new.get_user_knowledge_and_max_knowledge(@user, @user.participations.uniq.compact)
|
||||
tags[:user_topic_knowledge].each_pair do |tag, value|
|
||||
statistics.append({key: tag.name.to_s, value: (100.0 / tags[:max_topic_knowledge][tag] * value).round, id: tag.id})
|
||||
statistics.append({key: tag.name.to_s, value: (100.0 / tags[:max_topic_knowledge][tag] * value).round,
|
||||
id: tag.id})
|
||||
end
|
||||
statistics.sort_by! {|item| -item[:value]}
|
||||
statistics.sort_by! {|item| -item[:value] }
|
||||
|
||||
respond_to do |format|
|
||||
format.json { render(json: statistics) }
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -1,5 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class FileTemplatesController < ApplicationController
|
||||
before_action :set_file_template, only: [:show, :edit, :update, :destroy]
|
||||
before_action :set_file_template, only: %i[show edit update destroy]
|
||||
|
||||
def authorize!
|
||||
authorize(@file_template || @file_templates)
|
||||
@ -7,7 +9,7 @@ class FileTemplatesController < ApplicationController
|
||||
private :authorize!
|
||||
|
||||
def by_file_type
|
||||
@file_templates = FileTemplate.where(:file_type_id => params[:file_type_id])
|
||||
@file_templates = FileTemplate.where(file_type_id: params[:file_type_id])
|
||||
authorize!
|
||||
respond_to do |format|
|
||||
format.json { render :show, status: :ok, json: @file_templates.to_json }
|
||||
@ -82,13 +84,14 @@ class FileTemplatesController < ApplicationController
|
||||
end
|
||||
|
||||
private
|
||||
# Use callbacks to share common setup or constraints between actions.
|
||||
def set_file_template
|
||||
@file_template = FileTemplate.find(params[:id])
|
||||
end
|
||||
|
||||
# Never trust parameters from the scary internet, only allow the white list through.
|
||||
def file_template_params
|
||||
params[:file_template].permit(:name, :file_type_id, :content) if params[:file_template].present?
|
||||
end
|
||||
# Use callbacks to share common setup or constraints between actions.
|
||||
def set_file_template
|
||||
@file_template = FileTemplate.find(params[:id])
|
||||
end
|
||||
|
||||
# Never trust parameters from the scary internet, only allow the white list through.
|
||||
def file_template_params
|
||||
params[:file_template].permit(:name, :file_type_id, :content) if params[:file_template].present?
|
||||
end
|
||||
end
|
||||
|
@ -1,7 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class FileTypesController < ApplicationController
|
||||
include CommonBehavior
|
||||
|
||||
before_action :set_editor_modes, only: [:create, :edit, :new, :update]
|
||||
before_action :set_editor_modes, only: %i[create edit new update]
|
||||
before_action :set_file_type, only: MEMBER_ACTIONS
|
||||
|
||||
def authorize!
|
||||
@ -19,11 +21,14 @@ class FileTypesController < ApplicationController
|
||||
destroy_and_respond(object: @file_type)
|
||||
end
|
||||
|
||||
def edit
|
||||
end
|
||||
def edit; end
|
||||
|
||||
def file_type_params
|
||||
params[:file_type].permit(:binary, :editor_mode, :executable, :file_extension, :name, :indent_size, :renderable).merge(user_id: current_user.id, user_type: current_user.class.name) if params[:file_type].present?
|
||||
if params[:file_type].present?
|
||||
params[:file_type].permit(:binary, :editor_mode, :executable, :file_extension, :name, :indent_size, :renderable).merge(
|
||||
user_id: current_user.id, user_type: current_user.class.name
|
||||
)
|
||||
end
|
||||
end
|
||||
private :file_type_params
|
||||
|
||||
@ -39,7 +44,7 @@ class FileTypesController < ApplicationController
|
||||
|
||||
def set_editor_modes
|
||||
@editor_modes = Dir.glob('vendor/assets/javascripts/ace/mode-*.js').sort.map do |filename|
|
||||
name = filename.gsub(/\w+\/|mode-|.js$/, '')
|
||||
name = filename.gsub(%r{\w+/|mode-|.js$}, '')
|
||||
[name, "ace/mode/#{name}"]
|
||||
end
|
||||
end
|
||||
@ -51,8 +56,7 @@ class FileTypesController < ApplicationController
|
||||
end
|
||||
private :set_file_type
|
||||
|
||||
def show
|
||||
end
|
||||
def show; end
|
||||
|
||||
def update
|
||||
update_and_respond(object: @file_type, params: file_type_params)
|
||||
|
@ -1,11 +1,12 @@
|
||||
class FlowrController < ApplicationController
|
||||
# frozen_string_literal: true
|
||||
|
||||
class FlowrController < ApplicationController
|
||||
def insights
|
||||
require_user!
|
||||
# get the latest submission for this user that also has a test run (i.e. structured_errors if applicable)
|
||||
submission = Submission.joins(:testruns)
|
||||
.where(submissions: {user_id: current_user.id, user_type: current_user.class.name})
|
||||
.order('testruns.created_at DESC').first
|
||||
.where(submissions: {user_id: current_user.id, user_type: current_user.class.name})
|
||||
.order('testruns.created_at DESC').first
|
||||
|
||||
# Return if no submission was found
|
||||
if submission.blank? || @embed_options[:disable_hints] || @embed_options[:hide_test_results]
|
||||
@ -26,8 +27,8 @@ class FlowrController < ApplicationController
|
||||
end
|
||||
# once the programming language model becomes available, the language name can be added to the query to
|
||||
# produce more relevant results
|
||||
query = attributes.map{|att| att.value}.join(' ')
|
||||
{ submission: submission, error: error, attributes: attributes, query: query }
|
||||
query = attributes.map(&:value).join(' ')
|
||||
{submission: submission, error: error, attributes: attributes, query: query}
|
||||
end
|
||||
|
||||
# Always return JSON
|
||||
|
@ -93,7 +93,8 @@ class InternalUsersController < ApplicationController
|
||||
private :require_reset_password_token
|
||||
|
||||
def require_token(type)
|
||||
@user = InternalUser.send(:"load_from_#{type}_token", params[:token] || params[:internal_user].try(:[], :"#{type}_token"))
|
||||
@user = InternalUser.send(:"load_from_#{type}_token",
|
||||
params[:token] || params[:internal_user].try(:[], :"#{type}_token"))
|
||||
render_not_authorized unless @user
|
||||
end
|
||||
private :require_token
|
||||
|
@ -1,7 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ProxyExercisesController < ApplicationController
|
||||
include CommonBehavior
|
||||
|
||||
before_action :set_exercise_and_authorize, only: MEMBER_ACTIONS + [:clone, :reload]
|
||||
before_action :set_exercise_and_authorize, only: MEMBER_ACTIONS + %i[clone reload]
|
||||
|
||||
def authorize!
|
||||
authorize(@proxy_exercise || @proxy_exercises)
|
||||
@ -9,10 +11,11 @@ class ProxyExercisesController < ApplicationController
|
||||
private :authorize!
|
||||
|
||||
def clone
|
||||
proxy_exercise = @proxy_exercise.duplicate(public: false, token: nil, exercises: @proxy_exercise.exercises, user: current_user)
|
||||
proxy_exercise = @proxy_exercise.duplicate(public: false, token: nil, exercises: @proxy_exercise.exercises,
|
||||
user: current_user)
|
||||
proxy_exercise.send(:generate_token)
|
||||
if proxy_exercise.save
|
||||
redirect_to(proxy_exercise, notice: t('shared.object_cloned', model: ProxyExercise.model_name.human))
|
||||
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)
|
||||
@ -21,7 +24,7 @@ class ProxyExercisesController < ApplicationController
|
||||
|
||||
def create
|
||||
myparams = proxy_exercise_params
|
||||
myparams[:exercises] = Exercise.find(myparams[:exercise_ids].reject { |c| c.empty? })
|
||||
myparams[:exercises] = Exercise.find(myparams[:exercise_ids].reject(&:empty?))
|
||||
@proxy_exercise = ProxyExercise.new(myparams)
|
||||
authorize!
|
||||
|
||||
@ -39,7 +42,10 @@ class ProxyExercisesController < ApplicationController
|
||||
end
|
||||
|
||||
def proxy_exercise_params
|
||||
params[:proxy_exercise].permit(:description, :title, :public, :exercise_ids => []).merge(user_id: current_user.id, user_type: current_user.class.name) if params[:proxy_exercise].present?
|
||||
if params[:proxy_exercise].present?
|
||||
params[:proxy_exercise].permit(:description, :title, :public, exercise_ids: []).merge(user_id: current_user.id,
|
||||
user_type: current_user.class.name)
|
||||
end
|
||||
end
|
||||
private :proxy_exercise_params
|
||||
|
||||
@ -50,7 +56,7 @@ class ProxyExercisesController < ApplicationController
|
||||
end
|
||||
|
||||
def new
|
||||
@proxy_exercise = ProxyExercise.new
|
||||
@proxy_exercise = ProxyExercise.new
|
||||
@search = policy_scope(Exercise).ransack(params[:q])
|
||||
@exercises = @search.result.order(:title)
|
||||
authorize!
|
||||
@ -64,17 +70,15 @@ class ProxyExercisesController < ApplicationController
|
||||
|
||||
def show
|
||||
@search = @proxy_exercise.exercises.ransack
|
||||
@exercises = @proxy_exercise.exercises.ransack.result.order(:title) #@search.result.order(:title)
|
||||
@exercises = @proxy_exercise.exercises.ransack.result.order(:title) # @search.result.order(:title)
|
||||
end
|
||||
|
||||
#we might want to think about auth here
|
||||
def reload
|
||||
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? })
|
||||
myparams[:exercises] = Exercise.find(myparams[:exercise_ids].reject(&:blank?))
|
||||
update_and_respond(object: @proxy_exercise, params: myparams)
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -26,7 +26,7 @@ class RemoteEvaluationController < ApplicationController
|
||||
if @submission.present?
|
||||
score_achieved_percentage = @submission.normalized_score
|
||||
result = try_lti
|
||||
result.merge!({score: score_achieved_percentage * 100}) unless result[:score]
|
||||
result[:score] = score_achieved_percentage * 100 unless result[:score]
|
||||
status = result[:status]
|
||||
end
|
||||
|
||||
@ -38,7 +38,9 @@ class RemoteEvaluationController < ApplicationController
|
||||
lti_response = send_score(@submission)
|
||||
process_lti_response(lti_response)
|
||||
else
|
||||
{message: "Your submission was successfully scored with #{@submission.normalized_score}%. However, your score could not be sent to the e-Learning platform. Please reopen the exercise through the e-Learning platform and try again.", status: 410}
|
||||
{
|
||||
message: "Your submission was successfully scored with #{@submission.normalized_score}%. However, your score could not be sent to the e-Learning platform. Please reopen the exercise through the e-Learning platform and try again.", status: 410
|
||||
}
|
||||
end
|
||||
end
|
||||
private :try_lti
|
||||
@ -48,7 +50,8 @@ class RemoteEvaluationController < ApplicationController
|
||||
# Score has been reduced due to the passed deadline
|
||||
{message: I18n.t('exercises.submit.too_late'), status: 207, score: lti_response[:score_sent] * 100}
|
||||
elsif lti_response[:status] == 'success'
|
||||
{message: I18n.t('sessions.destroy_through_lti.success_with_outcome', consumer: @submission.user.consumer.name), status: 202}
|
||||
{message: I18n.t('sessions.destroy_through_lti.success_with_outcome', consumer: @submission.user.consumer.name),
|
||||
status: 202}
|
||||
else
|
||||
{message: I18n.t('exercises.submit.failure'), status: 424}
|
||||
end
|
||||
@ -77,7 +80,8 @@ class RemoteEvaluationController < ApplicationController
|
||||
submission_params[:study_group_id] = remote_evaluation_mapping.study_group_id
|
||||
submission_params[:cause] = cause
|
||||
submission_params[:user_type] = remote_evaluation_mapping.user_type
|
||||
submission_params[:files_attributes] = reject_illegal_file_attributes(remote_evaluation_mapping.exercise, files_attributes)
|
||||
submission_params[:files_attributes] =
|
||||
reject_illegal_file_attributes(remote_evaluation_mapping.exercise, files_attributes)
|
||||
submission_params
|
||||
end
|
||||
private :build_submission_params
|
||||
|
@ -5,7 +5,8 @@ class RequestForCommentsController < ApplicationController
|
||||
|
||||
before_action :require_user!
|
||||
before_action :set_request_for_comment, only: %i[show mark_as_solved set_thank_you_note]
|
||||
before_action :set_study_group_grouping, only: %i[index get_my_comment_requests get_rfcs_with_my_comments get_rfcs_for_exercise]
|
||||
before_action :set_study_group_grouping,
|
||||
only: %i[index get_my_comment_requests get_rfcs_with_my_comments get_rfcs_for_exercise]
|
||||
|
||||
def authorize!
|
||||
authorize(@request_for_comments || @request_for_comment)
|
||||
@ -16,15 +17,15 @@ class RequestForCommentsController < ApplicationController
|
||||
# GET /request_for_comments.json
|
||||
def index
|
||||
@search = RequestForComment
|
||||
.last_per_user(2)
|
||||
.with_last_activity
|
||||
.ransack(params[:q])
|
||||
.last_per_user(2)
|
||||
.with_last_activity
|
||||
.ransack(params[:q])
|
||||
@request_for_comments = @search.result
|
||||
.joins(:exercise)
|
||||
.where(exercises: {unpublished: false})
|
||||
.includes(submission: [:study_group])
|
||||
.order('created_at DESC')
|
||||
.paginate(page: params[:page], total_entries: @search.result.length)
|
||||
.joins(:exercise)
|
||||
.where(exercises: {unpublished: false})
|
||||
.includes(submission: [:study_group])
|
||||
.order('created_at DESC')
|
||||
.paginate(page: params[:page], total_entries: @search.result.length)
|
||||
|
||||
authorize!
|
||||
end
|
||||
@ -32,12 +33,12 @@ class RequestForCommentsController < ApplicationController
|
||||
# GET /my_request_for_comments
|
||||
def get_my_comment_requests
|
||||
@search = RequestForComment
|
||||
.with_last_activity
|
||||
.where(user: current_user)
|
||||
.ransack(params[:q])
|
||||
.with_last_activity
|
||||
.where(user: current_user)
|
||||
.ransack(params[:q])
|
||||
@request_for_comments = @search.result
|
||||
.order('created_at DESC')
|
||||
.paginate(page: params[:page])
|
||||
.order('created_at DESC')
|
||||
.paginate(page: params[:page])
|
||||
authorize!
|
||||
render 'index'
|
||||
end
|
||||
@ -45,13 +46,13 @@ class RequestForCommentsController < ApplicationController
|
||||
# GET /my_rfc_activity
|
||||
def get_rfcs_with_my_comments
|
||||
@search = RequestForComment
|
||||
.with_last_activity
|
||||
.joins(:comments) # we don't need to outer join here, because we know the user has commented on these
|
||||
.where(comments: {user_id: current_user.id})
|
||||
.ransack(params[:q])
|
||||
.with_last_activity
|
||||
.joins(:comments) # we don't need to outer join here, because we know the user has commented on these
|
||||
.where(comments: {user_id: current_user.id})
|
||||
.ransack(params[:q])
|
||||
@request_for_comments = @search.result
|
||||
.order('last_comment DESC')
|
||||
.paginate(page: params[:page])
|
||||
.order('last_comment DESC')
|
||||
.paginate(page: params[:page])
|
||||
authorize!
|
||||
render 'index'
|
||||
end
|
||||
@ -60,13 +61,13 @@ class RequestForCommentsController < ApplicationController
|
||||
def get_rfcs_for_exercise
|
||||
exercise = Exercise.find(params[:exercise_id])
|
||||
@search = RequestForComment
|
||||
.with_last_activity
|
||||
.where(exercise_id: exercise.id)
|
||||
.ransack(params[:q])
|
||||
.with_last_activity
|
||||
.where(exercise_id: exercise.id)
|
||||
.ransack(params[:q])
|
||||
@request_for_comments = @search.result
|
||||
.joins(:exercise)
|
||||
.order('last_comment DESC')
|
||||
.paginate(page: params[:page])
|
||||
.joins(:exercise)
|
||||
.order('last_comment DESC')
|
||||
.paginate(page: params[:page])
|
||||
# let the exercise decide, whether its rfcs should be visible
|
||||
authorize(exercise)
|
||||
render 'index'
|
||||
@ -91,7 +92,7 @@ class RequestForCommentsController < ApplicationController
|
||||
@request_for_comment.thank_you_note = params[:note]
|
||||
|
||||
commenters = @request_for_comment.commenters
|
||||
commenters.each { |commenter| UserMailer.send_thank_you_note(@request_for_comment, commenter).deliver_now }
|
||||
commenters.each {|commenter| UserMailer.send_thank_you_note(@request_for_comment, commenter).deliver_now }
|
||||
|
||||
respond_to do |format|
|
||||
if @request_for_comment.save
|
||||
@ -143,14 +144,16 @@ class RequestForCommentsController < ApplicationController
|
||||
# Never trust parameters from the scary internet, only allow the white list through.
|
||||
def request_for_comment_params
|
||||
# The study_group_id might not be present in the session (e.g. for internal users), resulting in session[:study_group_id] = nil which is intended.
|
||||
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)
|
||||
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
|
||||
|
||||
# The index page requires the grouping of the study groups
|
||||
# The study groups are grouped by the current study group and other study groups of the user
|
||||
def set_study_group_grouping
|
||||
current_study_group = StudyGroup.find_by(id: session[:study_group_id])
|
||||
my_study_groups = current_user.study_groups.reject { |group| group == current_study_group }
|
||||
my_study_groups = current_user.study_groups.reject {|group| group == current_study_group }
|
||||
@study_groups_grouping = [[t('request_for_comments.index.study_groups.current'), Array(current_study_group)],
|
||||
[t('request_for_comments.index.study_groups.my'), my_study_groups]]
|
||||
end
|
||||
|
@ -1,7 +1,10 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class SessionsController < ApplicationController
|
||||
include Lti
|
||||
|
||||
[:require_oauth_parameters, :require_valid_consumer_key, :require_valid_oauth_signature, :require_unique_oauth_nonce, :set_current_user, :require_valid_exercise_token, :set_study_group_membership, :set_embedding_options].each do |method_name|
|
||||
%i[require_oauth_parameters require_valid_consumer_key require_valid_oauth_signature require_unique_oauth_nonce
|
||||
set_current_user require_valid_exercise_token set_study_group_membership set_embedding_options].each do |method_name|
|
||||
before_action(method_name, only: :create_through_lti)
|
||||
end
|
||||
|
||||
@ -24,8 +27,8 @@ class SessionsController < ApplicationController
|
||||
redirect_to(params[:custom_redirect_target])
|
||||
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",
|
||||
consumer: @consumer))
|
||||
notice: t("sessions.create_through_lti.session_#{lti_outcome_service?(@exercise.id, @current_user.id) ? 'with' : 'without'}_outcome",
|
||||
consumer: @consumer))
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1,8 +1,10 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class StatisticsController < ApplicationController
|
||||
include StatisticsHelper
|
||||
|
||||
before_action :authorize!, only: [:show, :graphs, :user_activity, :user_activity_history, :rfc_activity,
|
||||
:rfc_activity_history]
|
||||
before_action :authorize!, only: %i[show graphs user_activity user_activity_history rfc_activity
|
||||
rfc_activity_history]
|
||||
|
||||
def policy_class
|
||||
StatisticsPolicy
|
||||
@ -15,8 +17,7 @@ class StatisticsController < ApplicationController
|
||||
end
|
||||
end
|
||||
|
||||
def graphs
|
||||
end
|
||||
def graphs; end
|
||||
|
||||
def user_activity
|
||||
respond_to do |format|
|
||||
@ -27,7 +28,7 @@ class StatisticsController < ApplicationController
|
||||
def user_activity_history
|
||||
respond_to do |format|
|
||||
format.html { render('activity_history', locals: {resource: :user}) }
|
||||
format.json { render_ranged_data :ranged_user_data}
|
||||
format.json { render_ranged_data :ranged_user_data }
|
||||
end
|
||||
end
|
||||
|
||||
@ -46,14 +47,21 @@ class StatisticsController < ApplicationController
|
||||
|
||||
def render_ranged_data(data_source)
|
||||
interval = params[:interval].to_s.empty? ? 'year' : params[:interval]
|
||||
from = DateTime.strptime(params[:from], '%Y-%m-%d') rescue DateTime.new(0)
|
||||
to = DateTime.strptime(params[:to], '%Y-%m-%d') rescue DateTime.now
|
||||
render(json: self.send(data_source, interval, from, to))
|
||||
from = begin
|
||||
DateTime.strptime(params[:from], '%Y-%m-%d')
|
||||
rescue StandardError
|
||||
DateTime.new(0)
|
||||
end
|
||||
to = begin
|
||||
DateTime.strptime(params[:to], '%Y-%m-%d')
|
||||
rescue StandardError
|
||||
DateTime.now
|
||||
end
|
||||
render(json: send(data_source, interval, from, to))
|
||||
end
|
||||
|
||||
def authorize!
|
||||
authorize self
|
||||
end
|
||||
private :authorize!
|
||||
|
||||
end
|
||||
|
@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class StudyGroupsController < ApplicationController
|
||||
include CommonBehavior
|
||||
|
||||
@ -20,7 +22,8 @@ class StudyGroupsController < ApplicationController
|
||||
|
||||
def update
|
||||
myparams = study_group_params
|
||||
myparams[:external_users] = StudyGroupMembership.find(myparams[:study_group_membership_ids].reject(&:empty?)).map(&:user)
|
||||
myparams[:external_users] =
|
||||
StudyGroupMembership.find(myparams[:study_group_membership_ids].reject(&:empty?)).map(&:user)
|
||||
myparams.delete(:study_group_membership_ids)
|
||||
update_and_respond(object: @study_group, params: myparams)
|
||||
end
|
||||
@ -30,7 +33,7 @@ class StudyGroupsController < ApplicationController
|
||||
end
|
||||
|
||||
def study_group_params
|
||||
params[:study_group].permit(:id, :name, :study_group_membership_ids => []) if params[:study_group].present?
|
||||
params[:study_group].permit(:id, :name, study_group_membership_ids: []) if params[:study_group].present?
|
||||
end
|
||||
private :study_group_params
|
||||
|
||||
@ -44,5 +47,4 @@ class StudyGroupsController < ApplicationController
|
||||
authorize(@study_groups || @study_group)
|
||||
end
|
||||
private :authorize!
|
||||
|
||||
end
|
||||
|
@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class SubmissionsController < ApplicationController
|
||||
include ActionController::Live
|
||||
include CommonBehavior
|
||||
@ -6,15 +8,16 @@ class SubmissionsController < ApplicationController
|
||||
include SubmissionScoring
|
||||
include Tubesock::Hijack
|
||||
|
||||
before_action :set_submission, only: [:download, :download_file, :render_file, :run, :score, :extract_errors, :show, :statistics, :stop, :test]
|
||||
before_action :set_docker_client, only: [:run, :test]
|
||||
before_action :set_files, only: [:download, :download_file, :render_file, :show, :run]
|
||||
before_action :set_file, only: [:download_file, :render_file, :run]
|
||||
before_action :set_mime_type, only: [:download_file, :render_file]
|
||||
skip_before_action :verify_authenticity_token, only: [:download_file, :render_file]
|
||||
before_action :set_submission,
|
||||
only: %i[download download_file render_file run score extract_errors show statistics stop test]
|
||||
before_action :set_docker_client, only: %i[run test]
|
||||
before_action :set_files, only: %i[download download_file render_file show run]
|
||||
before_action :set_file, only: %i[download_file render_file run]
|
||||
before_action :set_mime_type, only: %i[download_file render_file]
|
||||
skip_before_action :verify_authenticity_token, only: %i[download_file render_file]
|
||||
|
||||
def max_run_output_buffer_size
|
||||
if(@submission.cause == 'requestComments')
|
||||
if @submission.cause == 'requestComments'
|
||||
5000
|
||||
else
|
||||
500
|
||||
@ -34,30 +37,30 @@ class SubmissionsController < ApplicationController
|
||||
end
|
||||
|
||||
def command_substitutions(filename)
|
||||
{class_name: File.basename(filename, File.extname(filename)).camelize, filename: filename, module_name: File.basename(filename, File.extname(filename)).underscore}
|
||||
{class_name: File.basename(filename, File.extname(filename)).camelize, filename: filename,
|
||||
module_name: File.basename(filename, File.extname(filename)).underscore}
|
||||
end
|
||||
private :command_substitutions
|
||||
|
||||
def copy_comments
|
||||
# copy each annotation and set the target_file.id
|
||||
unless(params[:annotations_arr].nil?)
|
||||
params[:annotations_arr].each do | annotation |
|
||||
#comment = Comment.new(annotation[1].permit(:user_id, :file_id, :user_type, :row, :column, :text, :created_at, :updated_at))
|
||||
comment = Comment.new(:user_id => annotation[1][:user_id], :file_id => annotation[1][:file_id], :user_type => current_user.class.name, :row => annotation[1][:row], :column => annotation[1][:column], :text => annotation[1][:text])
|
||||
source_file = CodeOcean::File.find(annotation[1][:file_id])
|
||||
params[:annotations_arr]&.each do |annotation|
|
||||
# comment = Comment.new(annotation[1].permit(:user_id, :file_id, :user_type, :row, :column, :text, :created_at, :updated_at))
|
||||
comment = Comment.new(user_id: annotation[1][:user_id], file_id: annotation[1][:file_id],
|
||||
user_type: current_user.class.name, row: annotation[1][:row], column: annotation[1][:column], text: annotation[1][:text])
|
||||
source_file = CodeOcean::File.find(annotation[1][:file_id])
|
||||
|
||||
# retrieve target file
|
||||
target_file = @submission.files.detect do |file|
|
||||
# file_id has to be that of a the former iteration OR of the initial file (if this is the first run)
|
||||
file.file_id == source_file.file_id || file.file_id == source_file.id #seems to be needed here: (check this): || file.file_id == source_file.id ; yes this is needed, for comments on templates as well as comments on files added by users.
|
||||
end
|
||||
|
||||
#save to assign an id
|
||||
target_file.save!
|
||||
|
||||
comment.file_id = target_file.id
|
||||
comment.save!
|
||||
# retrieve target file
|
||||
target_file = @submission.files.detect do |file|
|
||||
# file_id has to be that of a the former iteration OR of the initial file (if this is the first run)
|
||||
file.file_id == source_file.file_id || file.file_id == source_file.id # seems to be needed here: (check this): || file.file_id == source_file.id ; yes this is needed, for comments on templates as well as comments on files added by users.
|
||||
end
|
||||
|
||||
# save to assign an id
|
||||
target_file.save!
|
||||
|
||||
comment.file_id = target_file.id
|
||||
comment.save!
|
||||
end
|
||||
end
|
||||
|
||||
@ -75,30 +78,35 @@ class SubmissionsController < ApplicationController
|
||||
require 'zip'
|
||||
stringio = Zip::OutputStream.write_buffer do |zio|
|
||||
@files.each do |file|
|
||||
zio.put_next_entry(file.path.to_s == '' ? file.name_with_extension : File.join(file.path, file.name_with_extension))
|
||||
zio.write(file.content.present? ? file.content : file.native_file.read)
|
||||
zio.put_next_entry(if file.path.to_s == ''
|
||||
file.name_with_extension
|
||||
else
|
||||
File.join(file.path,
|
||||
file.name_with_extension)
|
||||
end)
|
||||
zio.write(file.content.presence || file.native_file.read)
|
||||
end
|
||||
|
||||
# zip exercise description
|
||||
zio.put_next_entry(t('activerecord.models.exercise.one') + '.txt')
|
||||
zio.write(@submission.exercise.title + "\r\n======================\r\n")
|
||||
zio.put_next_entry("#{t('activerecord.models.exercise.one')}.txt")
|
||||
zio.write("#{@submission.exercise.title}\r\n======================\r\n")
|
||||
zio.write(@submission.exercise.description)
|
||||
|
||||
# zip .co file
|
||||
zio.put_next_entry(".co")
|
||||
zio.write(File.read id_file)
|
||||
zio.put_next_entry('.co')
|
||||
zio.write(File.read(id_file))
|
||||
File.delete(id_file) if File.exist?(id_file)
|
||||
|
||||
# zip client scripts
|
||||
scripts_path = 'app/assets/remote_scripts'
|
||||
Dir.foreach(scripts_path) do |file|
|
||||
next if file == '.' or file == '..'
|
||||
zio.put_next_entry(File.join('.scripts', File.basename(file)))
|
||||
zio.write(File.read File.join(scripts_path, file))
|
||||
end
|
||||
next if (file == '.') || (file == '..')
|
||||
|
||||
zio.put_next_entry(File.join('.scripts', File.basename(file)))
|
||||
zio.write(File.read(File.join(scripts_path, file)))
|
||||
end
|
||||
end
|
||||
send_data(stringio.string, filename: @submission.exercise.title.tr(" ", "_") + ".zip")
|
||||
send_data(stringio.string, filename: "#{@submission.exercise.title.tr(' ', '_')}.zip")
|
||||
end
|
||||
|
||||
def download_file
|
||||
@ -128,7 +136,7 @@ class SubmissionsController < ApplicationController
|
||||
end
|
||||
|
||||
def run
|
||||
# TODO reimplement SSEs with websocket commands
|
||||
# TODO: reimplement SSEs with websocket commands
|
||||
# with_server_sent_events do |server_sent_event|
|
||||
# output = @docker_client.execute_run_command(@submission, sanitize_filename)
|
||||
|
||||
@ -155,56 +163,54 @@ class SubmissionsController < ApplicationController
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# socket is the socket into the container, tubesock is the socket to the client
|
||||
|
||||
# give the docker_client the tubesock object, so that it can send messages (timeout)
|
||||
@docker_client.tubesock = tubesock
|
||||
|
||||
container_request_time = Time.now
|
||||
container_request_time = Time.zone.now
|
||||
result = @docker_client.execute_run_command(@submission, sanitize_filename)
|
||||
tubesock.send_data JSON.dump({'cmd' => 'status', 'status' => result[:status]})
|
||||
@waiting_for_container_time = Time.now - container_request_time
|
||||
@waiting_for_container_time = Time.zone.now - container_request_time
|
||||
|
||||
if result[:status] == :container_running
|
||||
socket = result[:socket]
|
||||
command = result[:command]
|
||||
|
||||
socket.on :message do |event|
|
||||
Rails.logger.info( Time.now.getutc.to_s + ": Docker sending: " + event.data)
|
||||
Rails.logger.info("#{Time.zone.now.getutc}: Docker sending: #{event.data}")
|
||||
handle_message(event.data, tubesock, result[:container])
|
||||
end
|
||||
|
||||
socket.on :close do |event|
|
||||
socket.on :close do |_event|
|
||||
kill_socket(tubesock)
|
||||
end
|
||||
|
||||
tubesock.onmessage do |data|
|
||||
Rails.logger.info(Time.now.getutc.to_s + ": Client sending: " + data)
|
||||
Rails.logger.info("#{Time.zone.now.getutc}: Client sending: #{data}")
|
||||
# Check whether the client send a JSON command and kill container
|
||||
# if the command is 'client_kill', send it to docker otherwise.
|
||||
begin
|
||||
|
||||
parsed = JSON.parse(data) unless data == "\n"
|
||||
if parsed.class == Hash && parsed['cmd'] == 'client_kill'
|
||||
Rails.logger.debug("Client exited container.")
|
||||
if parsed.instance_of?(Hash) && parsed['cmd'] == 'client_kill'
|
||||
Rails.logger.debug('Client exited container.')
|
||||
@docker_client.kill_container(result[:container])
|
||||
else
|
||||
socket.send data
|
||||
Rails.logger.debug('Sent the received client data to docker:' + data)
|
||||
Rails.logger.debug("Sent the received client data to docker:#{data}")
|
||||
end
|
||||
rescue JSON::ParserError => error
|
||||
rescue JSON::ParserError => e
|
||||
socket.send data
|
||||
Rails.logger.debug('Rescued parsing error, sent the received client data to docker:' + data)
|
||||
Rails.logger.debug("Rescued parsing error, sent the received client data to docker:#{data}")
|
||||
Sentry.set_extras(data: data)
|
||||
end
|
||||
end
|
||||
|
||||
# Send command after all listeners are attached.
|
||||
# Newline required to flush
|
||||
@execution_request_time = Time.now
|
||||
socket.send command + "\n"
|
||||
Rails.logger.info('Sent command: ' + command.to_s)
|
||||
@execution_request_time = Time.zone.now
|
||||
socket.send "#{command}\n"
|
||||
Rails.logger.info("Sent command: #{command}")
|
||||
else
|
||||
kill_socket(tubesock)
|
||||
end
|
||||
@ -212,7 +218,7 @@ class SubmissionsController < ApplicationController
|
||||
end
|
||||
|
||||
def kill_socket(tubesock)
|
||||
@container_execution_time = Time.now - @execution_request_time unless @execution_request_time.blank?
|
||||
@container_execution_time = Time.zone.now - @execution_request_time if @execution_request_time.present?
|
||||
# search for errors and save them as StructuredError (for scoring runs see submission_scoring.rb)
|
||||
errors = extract_errors
|
||||
send_hints(tubesock, errors)
|
||||
@ -225,7 +231,7 @@ class SubmissionsController < ApplicationController
|
||||
if @run_output.blank? || @run_output&.strip == '{"cmd":"exit"}' || @run_output&.strip == 'timeout:'
|
||||
@raw_output ||= ''
|
||||
@run_output ||= ''
|
||||
parse_message t('exercises.implement.no_output', timestamp: l(Time.now, format: :short)), 'stdout', tubesock
|
||||
parse_message t('exercises.implement.no_output', timestamp: l(Time.zone.now, format: :short)), 'stdout', tubesock
|
||||
end
|
||||
|
||||
# Hijacked connection needs to be notified correctly
|
||||
@ -237,25 +243,26 @@ class SubmissionsController < ApplicationController
|
||||
@raw_output ||= ''
|
||||
@run_output ||= ''
|
||||
# Handle special commands first
|
||||
if /^#exit|{"cmd": "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)
|
||||
@run_output = 'timeout: ' + @run_output # 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(sanitize_filename)
|
||||
test_command = @submission.execution_environment.test_command % command_substitutions(sanitize_filename)
|
||||
if test_command.blank?
|
||||
# If no test command is set, use the run_command for the RegEx below. Otherwise, no output will be displayed!
|
||||
test_command = run_command
|
||||
end
|
||||
unless /root@|:\/workspace|#{run_command}|#{test_command}|bash: cmd:canvasevent: command not found/.match(message)
|
||||
parse_message(message, 'stdout', tubesock, container)
|
||||
end
|
||||
case message
|
||||
when /^#exit|{"cmd": "exit"}/
|
||||
# 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)
|
||||
when /^#timeout/
|
||||
@run_output = "timeout: #{@run_output}" # 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(sanitize_filename)
|
||||
test_command = @submission.execution_environment.test_command % command_substitutions(sanitize_filename)
|
||||
if test_command.blank?
|
||||
# If no test command is set, use the run_command for the RegEx below. Otherwise, no output will be displayed!
|
||||
test_command = run_command
|
||||
end
|
||||
unless %r{root@|:/workspace|#{run_command}|#{test_command}|bash: cmd:canvasevent: command not found}.match?(message)
|
||||
parse_message(message, 'stdout', tubesock, container)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -263,58 +270,58 @@ class SubmissionsController < ApplicationController
|
||||
parsed = ''
|
||||
begin
|
||||
parsed = JSON.parse(message)
|
||||
if parsed.class == Hash and parsed.key?('cmd')
|
||||
if parsed.instance_of?(Hash) && parsed.key?('cmd')
|
||||
socket.send_data message
|
||||
Rails.logger.info('parse_message sent: ' + message)
|
||||
Rails.logger.info("parse_message sent: #{message}")
|
||||
@docker_client.exit_container(container) if container && parsed['cmd'] == 'exit'
|
||||
else
|
||||
parsed = {'cmd'=>'write','stream'=>output_stream,'data'=>message}
|
||||
parsed = {'cmd' => 'write', 'stream' => output_stream, 'data' => message}
|
||||
socket.send_data JSON.dump(parsed)
|
||||
Rails.logger.info('parse_message sent: ' + JSON.dump(parsed))
|
||||
Rails.logger.info("parse_message sent: #{JSON.dump(parsed)}")
|
||||
end
|
||||
rescue JSON::ParserError => e
|
||||
# Check wether the message contains multiple lines, if true try to parse each line
|
||||
if recursive and message.include? "\n"
|
||||
for part in message.split("\n")
|
||||
self.parse_message(part,output_stream,socket, container, false)
|
||||
if recursive && message.include?("\n")
|
||||
message.split("\n").each do |part|
|
||||
parse_message(part, output_stream, socket, container, false)
|
||||
end
|
||||
elsif message.include?('<img') || message.start_with?('{"cmd') || message.include?('"turtlebatch"')
|
||||
#Rails.logger.info('img foung')
|
||||
# Rails.logger.info('img foung')
|
||||
@buffering = true
|
||||
@buffer = ''
|
||||
@buffer += message
|
||||
#Rails.logger.info('Starting to buffer')
|
||||
elsif @buffering and message.include?('/>')
|
||||
# Rails.logger.info('Starting to buffer')
|
||||
elsif @buffering && message.include?('/>')
|
||||
@buffer += message
|
||||
parsed = {'cmd'=>'write','stream'=>output_stream,'data'=>@buffer}
|
||||
parsed = {'cmd' => 'write', 'stream' => output_stream, 'data' => @buffer}
|
||||
socket.send_data JSON.dump(parsed)
|
||||
#socket.send_data @buffer
|
||||
# socket.send_data @buffer
|
||||
@buffering = false
|
||||
#Rails.logger.info('Sent complete buffer')
|
||||
elsif @buffering and message.end_with?("}\r")
|
||||
# Rails.logger.info('Sent complete buffer')
|
||||
elsif @buffering && message.end_with?("}\r")
|
||||
@buffer += message
|
||||
socket.send_data @buffer
|
||||
@buffering = false
|
||||
#Rails.logger.info('Sent complete buffer')
|
||||
# Rails.logger.info('Sent complete buffer')
|
||||
elsif @buffering
|
||||
@buffer += message
|
||||
#Rails.logger.info('Appending to buffer')
|
||||
# Rails.logger.info('Appending to buffer')
|
||||
else
|
||||
#Rails.logger.info('else')
|
||||
parsed = {'cmd'=>'write','stream'=>output_stream,'data'=>message}
|
||||
# Rails.logger.info('else')
|
||||
parsed = {'cmd' => 'write', 'stream' => output_stream, 'data' => message}
|
||||
socket.send_data JSON.dump(parsed)
|
||||
Rails.logger.info('parse_message sent: ' + JSON.dump(parsed))
|
||||
Rails.logger.info("parse_message sent: #{JSON.dump(parsed)}")
|
||||
end
|
||||
ensure
|
||||
@raw_output += parsed['data'].to_s if parsed.class == Hash and parsed.key? 'data'
|
||||
@raw_output += parsed['data'].to_s if parsed.instance_of?(Hash) && parsed.key?('data')
|
||||
# save the data that was send to the run_output if there is enough space left. this will be persisted as a testrun with cause "run"
|
||||
@run_output += JSON.dump(parsed).to_s if @run_output.size <= max_run_output_buffer_size
|
||||
end
|
||||
end
|
||||
|
||||
def save_run_output
|
||||
unless @run_output.blank?
|
||||
@run_output = @run_output[(0..max_run_output_buffer_size-1)] # trim the string to max_message_buffer_size chars
|
||||
if @run_output.present?
|
||||
@run_output = @run_output[(0..max_run_output_buffer_size - 1)] # trim the string to max_message_buffer_size chars
|
||||
Testrun.create(
|
||||
file: @file,
|
||||
cause: 'run',
|
||||
@ -328,7 +335,7 @@ class SubmissionsController < ApplicationController
|
||||
|
||||
def extract_errors
|
||||
results = []
|
||||
unless @raw_output.blank?
|
||||
if @raw_output.present?
|
||||
@submission.exercise.execution_environment.error_templates.each do |template|
|
||||
pattern = Regexp.new(template.signature).freeze
|
||||
if pattern.match(@raw_output)
|
||||
@ -361,7 +368,7 @@ class SubmissionsController < ApplicationController
|
||||
tubesock.send_data JSON.dump(score_submission(@submission))
|
||||
|
||||
# To enable hints when scoring a submission, uncomment the next line:
|
||||
#send_hints(tubesock, StructuredError.where(submission: @submission))
|
||||
# send_hints(tubesock, StructuredError.where(submission: @submission))
|
||||
|
||||
tubesock.send_data JSON.dump({'cmd' => 'exit'})
|
||||
ensure
|
||||
@ -372,8 +379,9 @@ class SubmissionsController < ApplicationController
|
||||
|
||||
def send_hints(tubesock, errors)
|
||||
return if @embed_options[:disable_hints]
|
||||
errors = errors.to_a.uniq { |e| e.hint}
|
||||
errors.each do | error |
|
||||
|
||||
errors = errors.to_a.uniq(&:hint)
|
||||
errors.each do |error|
|
||||
tubesock.send_data JSON.dump({cmd: 'hint', hint: error.hint, description: error.error_template.description})
|
||||
end
|
||||
end
|
||||
@ -384,7 +392,7 @@ class SubmissionsController < ApplicationController
|
||||
private :set_docker_client
|
||||
|
||||
def set_file
|
||||
@file = @files.detect { |file| file.name_with_extension == sanitize_filename }
|
||||
@file = @files.detect {|file| file.name_with_extension == sanitize_filename }
|
||||
head :not_found unless @file
|
||||
end
|
||||
private :set_file
|
||||
@ -406,11 +414,9 @@ class SubmissionsController < ApplicationController
|
||||
end
|
||||
private :set_submission
|
||||
|
||||
def show
|
||||
end
|
||||
def show; end
|
||||
|
||||
def statistics
|
||||
end
|
||||
def statistics; end
|
||||
|
||||
def test
|
||||
hijack do |tubesock|
|
||||
@ -436,10 +442,10 @@ class SubmissionsController < ApplicationController
|
||||
server_sent_event.write(nil, event: 'start')
|
||||
yield(server_sent_event) if block_given?
|
||||
server_sent_event.write({code: 200}, event: 'close')
|
||||
rescue => exception
|
||||
Sentry.capture_exception(exception)
|
||||
logger.error(exception.message)
|
||||
logger.error(exception.backtrace.join("\n"))
|
||||
rescue StandardError => e
|
||||
Sentry.capture_exception(e)
|
||||
logger.error(e.message)
|
||||
logger.error(e.backtrace.join("\n"))
|
||||
server_sent_event.write({code: 500}, event: 'close')
|
||||
ensure
|
||||
server_sent_event.close
|
||||
@ -457,16 +463,16 @@ class SubmissionsController < ApplicationController
|
||||
)
|
||||
|
||||
# create .co file
|
||||
path = "tmp/" + user.id.to_s + ".co"
|
||||
path = "tmp/#{user.id}.co"
|
||||
# parse validation token
|
||||
content = "#{remote_evaluation_mapping.validation_token}\n"
|
||||
# parse remote request url
|
||||
content += "#{request.base_url}/evaluate\n"
|
||||
@submission.files.each do |file|
|
||||
file_path = file.path.to_s == '' ? file.name_with_extension : File.join(file.path, file.name_with_extension)
|
||||
content += "#{file_path}=#{file.file_id.to_s}\n"
|
||||
content += "#{file_path}=#{file.file_id}\n"
|
||||
end
|
||||
File.open(path, "w+") do |f|
|
||||
File.open(path, 'w+') do |f|
|
||||
f.write(content)
|
||||
end
|
||||
path
|
||||
|
@ -1,5 +1,6 @@
|
||||
class SubscriptionsController < ApplicationController
|
||||
# frozen_string_literal: true
|
||||
|
||||
class SubscriptionsController < ApplicationController
|
||||
def authorize!
|
||||
authorize(@subscription || @subscriptions)
|
||||
end
|
||||
@ -21,28 +22,26 @@ class SubscriptionsController < ApplicationController
|
||||
# DELETE /subscriptions/1
|
||||
# DELETE /subscriptions/1.json
|
||||
def destroy
|
||||
begin
|
||||
@subscription = Subscription.find(params[:id])
|
||||
rescue
|
||||
skip_authorization
|
||||
@subscription = Subscription.find(params[:id])
|
||||
rescue StandardError
|
||||
skip_authorization
|
||||
respond_to do |format|
|
||||
format.html { redirect_to request_for_comments_url, alert: t('subscriptions.subscription_not_existent') }
|
||||
format.json { render json: {message: t('subscriptions.subscription_not_existent')}, status: :not_found }
|
||||
end
|
||||
else
|
||||
authorize!
|
||||
rfc = @subscription.try(:request_for_comment)
|
||||
@subscription.deleted = true
|
||||
if @subscription.save
|
||||
respond_to do |format|
|
||||
format.html { redirect_to request_for_comments_url, alert: t('subscriptions.subscription_not_existent') }
|
||||
format.json { render json: {message: t('subscriptions.subscription_not_existent')}, status: :not_found }
|
||||
format.html { redirect_to request_for_comment_url(rfc), notice: t('subscriptions.successfully_unsubscribed') }
|
||||
format.json { render json: {message: t('subscriptions.successfully_unsubscribed')}, status: :ok }
|
||||
end
|
||||
else
|
||||
authorize!
|
||||
rfc = @subscription.try(:request_for_comment)
|
||||
@subscription.deleted = true
|
||||
if @subscription.save
|
||||
respond_to do |format|
|
||||
format.html { redirect_to request_for_comment_url(rfc), notice: t('subscriptions.successfully_unsubscribed') }
|
||||
format.json { render json: {message: t('subscriptions.successfully_unsubscribed')}, status: :ok}
|
||||
end
|
||||
else
|
||||
respond_to do |format|
|
||||
format.html { redirect_to request_for_comment_url(rfc), :flash => { :danger => t('shared.message_failure') } }
|
||||
format.json { render json: {message: t('shared.message_failure')}, status: :internal_server_error}
|
||||
end
|
||||
respond_to do |format|
|
||||
format.html { redirect_to request_for_comment_url(rfc), flash: {danger: t('shared.message_failure')} }
|
||||
format.json { render json: {message: t('shared.message_failure')}, status: :internal_server_error }
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -56,7 +55,10 @@ class SubscriptionsController < ApplicationController
|
||||
def subscription_params
|
||||
current_user_id = current_user.try(:id)
|
||||
current_user_class_name = current_user.try(:class).try(:name)
|
||||
params[:subscription].permit(:request_for_comment_id, :subscription_type).merge(user_id: current_user_id, user_type: current_user_class_name, deleted: false) if params[:subscription].present?
|
||||
if params[:subscription].present?
|
||||
params[:subscription].permit(:request_for_comment_id, :subscription_type).merge(user_id: current_user_id,
|
||||
user_type: current_user_class_name, deleted: false)
|
||||
end
|
||||
end
|
||||
private :subscription_params
|
||||
end
|
||||
|
@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class TagsController < ApplicationController
|
||||
include CommonBehavior
|
||||
|
||||
@ -18,8 +20,7 @@ class TagsController < ApplicationController
|
||||
destroy_and_respond(object: @tag)
|
||||
end
|
||||
|
||||
def edit
|
||||
end
|
||||
def edit; end
|
||||
|
||||
def tag_params
|
||||
params[:tag].permit(:name) if params[:tag].present?
|
||||
@ -42,8 +43,7 @@ class TagsController < ApplicationController
|
||||
end
|
||||
private :set_tag
|
||||
|
||||
def show
|
||||
end
|
||||
def show; end
|
||||
|
||||
def update
|
||||
update_and_respond(object: @tag, params: tag_params)
|
||||
|
@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class TipsController < ApplicationController
|
||||
include CommonBehavior
|
||||
|
||||
@ -19,15 +21,14 @@ class TipsController < ApplicationController
|
||||
destroy_and_respond(object: @tip)
|
||||
end
|
||||
|
||||
def edit
|
||||
end
|
||||
def edit; end
|
||||
|
||||
def tip_params
|
||||
return unless params[:tip].present?
|
||||
return if params[:tip].blank?
|
||||
|
||||
params[:tip]
|
||||
.permit(:title, :description, :example, :file_type_id)
|
||||
.each { |_key, value| value.strip! unless value.is_a?(Array) }
|
||||
.each {|_key, value| value.strip! unless value.is_a?(Array) }
|
||||
.merge(user_id: current_user.id, user_type: current_user.class.name)
|
||||
end
|
||||
private :tip_params
|
||||
@ -48,8 +49,7 @@ class TipsController < ApplicationController
|
||||
end
|
||||
private :set_tip
|
||||
|
||||
def show
|
||||
end
|
||||
def show; end
|
||||
|
||||
def update
|
||||
update_and_respond(object: @tip, params: tip_params)
|
||||
|
@ -28,10 +28,10 @@ class UserExerciseFeedbacksController < ApplicationController
|
||||
@exercise = Exercise.find(uef_params[:exercise_id])
|
||||
rfc = RequestForComment.unsolved.where(exercise_id: @exercise.id, user_id: current_user.id).first
|
||||
submission = begin
|
||||
current_user.submissions.where(exercise_id: @exercise.id).order('created_at DESC').first
|
||||
rescue StandardError
|
||||
nil
|
||||
end
|
||||
current_user.submissions.where(exercise_id: @exercise.id).order('created_at DESC').first
|
||||
rescue StandardError
|
||||
nil
|
||||
end
|
||||
|
||||
if @exercise
|
||||
@uef = UserExerciseFeedback.find_or_initialize_by(user: current_user, exercise: @exercise)
|
||||
@ -74,10 +74,10 @@ class UserExerciseFeedbacksController < ApplicationController
|
||||
|
||||
def update
|
||||
submission = begin
|
||||
current_user.submissions.where(exercise_id: @exercise.id).order('created_at DESC').first
|
||||
rescue StandardError
|
||||
nil
|
||||
end
|
||||
current_user.submissions.where(exercise_id: @exercise.id).order('created_at DESC').first
|
||||
rescue StandardError
|
||||
nil
|
||||
end
|
||||
rfc = RequestForComment.unsolved.where(exercise_id: @exercise.id, user_id: current_user.id).first
|
||||
authorize!
|
||||
if @exercise && validate_inputs(uef_params)
|
||||
@ -115,7 +115,7 @@ class UserExerciseFeedbacksController < ApplicationController
|
||||
end
|
||||
|
||||
def uef_params
|
||||
return unless params[:user_exercise_feedback].present?
|
||||
return if params[:user_exercise_feedback].blank?
|
||||
|
||||
exercise_id = if params[:user_exercise_feedback].nil?
|
||||
params[:exercise_id]
|
||||
@ -126,8 +126,8 @@ class UserExerciseFeedbacksController < ApplicationController
|
||||
user_id = current_user.id
|
||||
user_type = current_user.class.name
|
||||
latest_submission = Submission
|
||||
.where(user_id: user_id, user_type: user_type, exercise_id: exercise_id)
|
||||
.order(created_at: :desc).first
|
||||
.where(user_id: user_id, user_type: user_type, exercise_id: exercise_id)
|
||||
.order(created_at: :desc).first
|
||||
|
||||
params[:user_exercise_feedback]
|
||||
.permit(:feedback_text, :difficulty, :exercise_id, :user_estimated_worktime)
|
||||
@ -140,10 +140,8 @@ class UserExerciseFeedbacksController < ApplicationController
|
||||
def validate_inputs(uef_params)
|
||||
if uef_params[:difficulty].to_i.negative? || uef_params[:difficulty].to_i >= comment_presets.size
|
||||
false
|
||||
elsif uef_params[:user_estimated_worktime].to_i.negative? || uef_params[:user_estimated_worktime].to_i >= time_presets.size
|
||||
false
|
||||
else
|
||||
true
|
||||
!(uef_params[:user_estimated_worktime].to_i.negative? || uef_params[:user_estimated_worktime].to_i >= time_presets.size)
|
||||
end
|
||||
rescue StandardError
|
||||
false
|
||||
|
Reference in New Issue
Block a user