Merge branch 'master' into disable_rfcs
# Conflicts: # app/assets/stylesheets/editor.css.scss
This commit is contained in:
20
.travis.yml
20
.travis.yml
@ -12,14 +12,11 @@ addons:
|
|||||||
before_install:
|
before_install:
|
||||||
- export DISPLAY=:99.0
|
- export DISPLAY=:99.0
|
||||||
- sh -e /etc/init.d/xvfb start
|
- sh -e /etc/init.d/xvfb start
|
||||||
# Config to run docker tests - doesn't work so far
|
- echo 'DOCKER_OPTS="-H tcp://127.0.0.1:2376 -H unix:///var/run/docker.sock --iptables=false"' | sudo tee /etc/default/docker > /dev/null
|
||||||
# - sudo apt-get update
|
- sudo service docker restart
|
||||||
# - sudo apt-get upgrade lxc-docker
|
- sleep 5
|
||||||
# - echo 'DOCKER_OPTS="-H tcp://127.0.0.1:4243 -H unix:///var/run/docker.sock --iptables=false"' | sudo tee /etc/default/docker > /dev/null
|
- docker pull openhpi/co_execenv_python
|
||||||
# - export DOCKER_HOST=tcp://192.168.23.75:2375
|
- docker pull openhpi/co_execenv_java
|
||||||
# - sudo service docker restart
|
|
||||||
# - sleep 5
|
|
||||||
# - docker pull openhpi/docker_ruby
|
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
- cp .rspec.travis .rspec
|
- cp .rspec.travis .rspec
|
||||||
@ -27,6 +24,7 @@ before_script:
|
|||||||
- cp config/code_ocean.yml.travis config/code_ocean.yml
|
- cp config/code_ocean.yml.travis config/code_ocean.yml
|
||||||
- cp config/database.yml.travis config/database.yml
|
- cp config/database.yml.travis config/database.yml
|
||||||
- cp config/secrets.yml.travis config/secrets.yml
|
- cp config/secrets.yml.travis config/secrets.yml
|
||||||
|
- cp config/docker.yml.erb.travis config/docker.yml.erb
|
||||||
- psql --command='CREATE DATABASE travis_ci_test;' --username=postgres
|
- psql --command='CREATE DATABASE travis_ci_test;' --username=postgres
|
||||||
- bundle exec rake db:schema:load RAILS_ENV=test
|
- bundle exec rake db:schema:load RAILS_ENV=test
|
||||||
|
|
||||||
@ -35,8 +33,4 @@ language: ruby
|
|||||||
rvm:
|
rvm:
|
||||||
- 2.3.6
|
- 2.3.6
|
||||||
|
|
||||||
script: bundle exec rspec --color --format documentation --require spec_helper --require rails_helper --tag ~docker && bundle exec codeclimate-test-reporter
|
script: bundle exec rspec --color --format documentation --require spec_helper --require rails_helper && bundle exec codeclimate-test-reporter
|
||||||
# one of the solutions I've found
|
|
||||||
# - sudo docker run --rm=true -v `pwd`:/ansible-apache:rw weldpua2008/docker-ansible:${OS_TYPE}${OS_VERSION}_v${ANSIBLE_VERSION} /bin/bash -c "/ansible-apache/tests/test-in-docker-image.sh ${OS_TYPE} ${OS_VERSION} ${ANSIBLE_VERSION}"
|
|
||||||
|
|
||||||
|
|
||||||
|
1
Gemfile
1
Gemfile
@ -7,6 +7,7 @@ gem 'carrierwave'
|
|||||||
gem 'coffee-rails'
|
gem 'coffee-rails'
|
||||||
gem 'concurrent-ruby'
|
gem 'concurrent-ruby'
|
||||||
gem 'concurrent-ruby-ext', platform: :ruby
|
gem 'concurrent-ruby-ext', platform: :ruby
|
||||||
|
gem 'activerecord-deprecated_finders', require: 'active_record/deprecated_finders'
|
||||||
gem 'docker-api', require: 'docker'
|
gem 'docker-api', require: 'docker'
|
||||||
gem 'factory_bot_rails'
|
gem 'factory_bot_rails'
|
||||||
gem 'forgery'
|
gem 'forgery'
|
||||||
|
@ -31,6 +31,7 @@ GEM
|
|||||||
activemodel (= 4.2.10)
|
activemodel (= 4.2.10)
|
||||||
activesupport (= 4.2.10)
|
activesupport (= 4.2.10)
|
||||||
arel (~> 6.0)
|
arel (~> 6.0)
|
||||||
|
activerecord-deprecated_finders (1.0.4)
|
||||||
activerecord-jdbc-adapter (50.0)
|
activerecord-jdbc-adapter (50.0)
|
||||||
activerecord (>= 2.2)
|
activerecord (>= 2.2)
|
||||||
activerecord-jdbcpostgresql-adapter (50.0)
|
activerecord-jdbcpostgresql-adapter (50.0)
|
||||||
@ -388,6 +389,7 @@ PLATFORMS
|
|||||||
ruby
|
ruby
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
|
activerecord-deprecated_finders
|
||||||
activerecord-jdbcpostgresql-adapter
|
activerecord-jdbcpostgresql-adapter
|
||||||
autotest-rails
|
autotest-rails
|
||||||
bcrypt
|
bcrypt
|
||||||
|
@ -42,17 +42,14 @@ button i.fa-spin {
|
|||||||
background-color: #008CBA;
|
background-color: #008CBA;
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
button {
|
button {
|
||||||
font-size: 80%;
|
font-size: 80%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button-two-only, .btn-group-two-only {
|
|
||||||
width: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
button, .btn-group {
|
button, .btn-group {
|
||||||
width: 50%;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-group {
|
.btn-group {
|
||||||
@ -195,4 +192,4 @@ button i.fa-spin {
|
|||||||
|
|
||||||
.enforce-bottom-margin {
|
.enforce-bottom-margin {
|
||||||
margin-bottom: 5px !important;
|
margin-bottom: 5px !important;
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.chosen-container {
|
.chosen-container {
|
||||||
width: 250px !important;
|
min-width: 250px !important;
|
||||||
|
width: 100% !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.code-field {
|
.code-field {
|
||||||
|
@ -14,46 +14,40 @@ class RequestForCommentsController < ApplicationController
|
|||||||
def index
|
def index
|
||||||
@search = RequestForComment
|
@search = RequestForComment
|
||||||
.last_per_user(2)
|
.last_per_user(2)
|
||||||
.joins('join "submissions" s on s.id = request_for_comments.submission_id
|
.with_last_activity
|
||||||
left outer join "files" f on f.context_id = s.id
|
|
||||||
left outer join "comments" on comments.file_id = f.id')
|
|
||||||
.group('request_for_comments.id, request_for_comments.user_id, request_for_comments.exercise_id,
|
|
||||||
request_for_comments.file_id, request_for_comments.question, request_for_comments.created_at,
|
|
||||||
request_for_comments.updated_at, request_for_comments.user_type, request_for_comments.solved,
|
|
||||||
request_for_comments.full_score_reached, request_for_comments.submission_id, request_for_comments.row_number') # ugly, but rails wants it this way
|
|
||||||
.select('request_for_comments.*, max(comments.updated_at) as last_comment')
|
|
||||||
.search(params[:q])
|
.search(params[:q])
|
||||||
@request_for_comments = @search.result.order('created_at DESC').paginate(page: params[:page], total_entries: @search.result.length)
|
@request_for_comments = @search.result
|
||||||
|
.order('created_at DESC')
|
||||||
|
.paginate(page: params[:page], total_entries: @search.result.length)
|
||||||
authorize!
|
authorize!
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# GET /my_request_for_comments
|
||||||
def get_my_comment_requests
|
def get_my_comment_requests
|
||||||
@search = RequestForComment
|
@search = RequestForComment
|
||||||
|
.with_last_activity
|
||||||
.where(user_id: current_user.id)
|
.where(user_id: current_user.id)
|
||||||
.joins('join "submissions" s on s.id = request_for_comments.submission_id
|
|
||||||
left outer join "files" f on f.context_id = s.id
|
|
||||||
left outer join "comments" on comments.file_id = f.id')
|
|
||||||
.group('request_for_comments.id')
|
|
||||||
.select('request_for_comments.*, max(comments.updated_at) as last_comment')
|
|
||||||
.search(params[:q])
|
.search(params[:q])
|
||||||
@request_for_comments = @search.result.order('created_at DESC').paginate(page: params[:page])
|
@request_for_comments = @search.result
|
||||||
|
.order('created_at DESC')
|
||||||
|
.paginate(page: params[:page])
|
||||||
render 'index'
|
render 'index'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# GET /my_rfc_activity
|
||||||
def get_rfcs_with_my_comments
|
def get_rfcs_with_my_comments
|
||||||
@search = RequestForComment
|
@search = RequestForComment
|
||||||
|
.with_last_activity
|
||||||
.joins(:comments) # we don't need to outer join here, because we know the user has commented on these
|
.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})
|
.where(comments: {user_id: current_user.id})
|
||||||
.joins('join "submissions" s on s.id = request_for_comments.submission_id
|
|
||||||
left outer join "files" f on f.context_id = s.id
|
|
||||||
left outer join "comments" as c on c.file_id = f.id')
|
|
||||||
.group('request_for_comments.id')
|
|
||||||
.select('request_for_comments.*, max(c.updated_at) as last_comment')
|
|
||||||
.search(params[:q])
|
.search(params[:q])
|
||||||
@request_for_comments = @search.result.order('last_comment DESC').paginate(page: params[:page])
|
@request_for_comments = @search.result
|
||||||
|
.order('last_comment DESC')
|
||||||
|
.paginate(page: params[:page])
|
||||||
render 'index'
|
render 'index'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# GET /request_for_comments/1/mark_as_solved
|
||||||
def mark_as_solved
|
def mark_as_solved
|
||||||
authorize!
|
authorize!
|
||||||
@request_for_comment.solved = true
|
@request_for_comment.solved = true
|
||||||
@ -66,6 +60,7 @@ class RequestForCommentsController < ApplicationController
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# POST /request_for_comments/1/set_thank_you_note
|
||||||
def set_thank_you_note
|
def set_thank_you_note
|
||||||
authorize!
|
authorize!
|
||||||
@request_for_comment.thank_you_note = params[:note]
|
@request_for_comment.thank_you_note = params[:note]
|
||||||
@ -82,10 +77,6 @@ class RequestForCommentsController < ApplicationController
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def submit
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
# GET /request_for_comments/1
|
# GET /request_for_comments/1
|
||||||
# GET /request_for_comments/1.json
|
# GET /request_for_comments/1.json
|
||||||
def show
|
def show
|
||||||
@ -146,10 +137,6 @@ class RequestForCommentsController < ApplicationController
|
|||||||
authorize!
|
authorize!
|
||||||
end
|
end
|
||||||
|
|
||||||
def comment_params
|
|
||||||
params.permit(:exercise_id, :feedback_text).merge(user_id: current_user.id, user_type: current_user.class.name)
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
private
|
||||||
# Use callbacks to share common setup or constraints between actions.
|
# Use callbacks to share common setup or constraints between actions.
|
||||||
def set_request_for_comment
|
def set_request_for_comment
|
||||||
@ -162,4 +149,8 @@ class RequestForCommentsController < ApplicationController
|
|||||||
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
|
end
|
||||||
|
|
||||||
|
def comment_params
|
||||||
|
params.permit(:exercise_id, :feedback_text).merge(user_id: current_user.id, user_type: current_user.class.name)
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -75,6 +75,11 @@ class SubmissionsController < ApplicationController
|
|||||||
zio.write(file.content)
|
zio.write(file.content)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# zip exercise description
|
||||||
|
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
|
# zip .co file
|
||||||
zio.put_next_entry(".co")
|
zio.put_next_entry(".co")
|
||||||
zio.write(File.read id_file)
|
zio.write(File.read id_file)
|
||||||
@ -167,7 +172,7 @@ class SubmissionsController < ApplicationController
|
|||||||
# if the command is 'client_kill', send it to docker otherwise.
|
# if the command is 'client_kill', send it to docker otherwise.
|
||||||
begin
|
begin
|
||||||
parsed = JSON.parse(data)
|
parsed = JSON.parse(data)
|
||||||
if parsed['cmd'] == 'client_kill'
|
if parsed.class == Hash && parsed['cmd'] == 'client_kill'
|
||||||
Rails.logger.debug("Client exited container.")
|
Rails.logger.debug("Client exited container.")
|
||||||
@docker_client.kill_container(result[:container])
|
@docker_client.kill_container(result[:container])
|
||||||
else
|
else
|
||||||
|
@ -47,7 +47,7 @@ class ExecutionEnvironment < ActiveRecord::Base
|
|||||||
private :validate_docker_image?
|
private :validate_docker_image?
|
||||||
|
|
||||||
def working_docker_image?
|
def working_docker_image?
|
||||||
DockerClient.pull(docker_image) unless DockerClient.image_tags.include?(docker_image)
|
DockerClient.pull(docker_image) unless DockerClient.find_image_by_tag(docker_image).blank?
|
||||||
output = DockerClient.new(execution_environment: self).execute_arbitrary_command(VALIDATION_COMMAND)
|
output = DockerClient.new(execution_environment: self).execute_arbitrary_command(VALIDATION_COMMAND)
|
||||||
errors.add(:docker_image, "error: #{output[:stderr]}") if output[:stderr].present?
|
errors.add(:docker_image, "error: #{output[:stderr]}") if output[:stderr].present?
|
||||||
rescue DockerClient::Error => error
|
rescue DockerClient::Error => error
|
||||||
|
@ -11,7 +11,13 @@ class RequestForComment < ActiveRecord::Base
|
|||||||
scope :not_stale, -> { where("user_id%10 <2 OR user_id%10 >= 4").where(exercise.exercise_collections.none{|ec| ec.id = 3} } ########### todo
|
scope :not_stale, -> { where("user_id%10 <2 OR user_id%10 >= 4").where(exercise.exercise_collections.none{|ec| ec.id = 3} } ########### todo
|
||||||
|
|
||||||
def self.last_per_user(n = 5)
|
def self.last_per_user(n = 5)
|
||||||
from("(#{row_number_user_sql}) as request_for_comments").where("row_number <= ?", n)
|
from("(#{row_number_user_sql}) as request_for_comments")
|
||||||
|
.where("row_number <= ?", n)
|
||||||
|
.group('request_for_comments.id, request_for_comments.user_id, request_for_comments.exercise_id,
|
||||||
|
request_for_comments.file_id, request_for_comments.question, request_for_comments.created_at,
|
||||||
|
request_for_comments.updated_at, request_for_comments.user_type, request_for_comments.solved,
|
||||||
|
request_for_comments.full_score_reached, request_for_comments.submission_id, request_for_comments.row_number')
|
||||||
|
# ugly, but necessary
|
||||||
end
|
end
|
||||||
|
|
||||||
# not used right now, finds the last submission for the respective user and exercise.
|
# not used right now, finds the last submission for the respective user and exercise.
|
||||||
@ -47,6 +53,14 @@ class RequestForComment < ActiveRecord::Base
|
|||||||
commenters.uniq {|user| user.id}
|
commenters.uniq {|user| user.id}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.with_last_activity
|
||||||
|
self.joins('join "submissions" s on s.id = request_for_comments.submission_id
|
||||||
|
left outer join "files" f on f.context_id = s.id
|
||||||
|
left outer join "comments" c on c.file_id = f.id')
|
||||||
|
.group('request_for_comments.id')
|
||||||
|
.select('request_for_comments.*, max(c.updated_at) as last_comment')
|
||||||
|
end
|
||||||
|
|
||||||
def to_s
|
def to_s
|
||||||
"RFC-" + self.id.to_s
|
"RFC-" + self.id.to_s
|
||||||
end
|
end
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#flash.fixed_error_messages data-message-failure=t('shared.message_failure')
|
#flash-container
|
||||||
- %w[alert danger info notice success warning].each do |severity|
|
#flash.container.fixed_error_messages data-message-failure=t('shared.message_failure')
|
||||||
div.alert.flash class="alert-#{{'alert' => 'warning', 'notice' => 'success'}.fetch(severity, severity)}"
|
- %w[alert danger info notice success warning].each do |severity|
|
||||||
p id="flash-#{severity}" = flash[severity]
|
div.alert.flash class="alert-#{{'alert' => 'warning', 'notice' => 'success'}.fetch(severity, severity)}"
|
||||||
span.fa.fa-times
|
p id="flash-#{severity}" = flash[severity]
|
||||||
|
span.fa.fa-times
|
||||||
|
@ -32,8 +32,8 @@ html lang='en'
|
|||||||
li = link_to(t('shared.help.link'), '#modal-help', data: {toggle: 'modal'})
|
li = link_to(t('shared.help.link'), '#modal-help', data: {toggle: 'modal'})
|
||||||
= render('session')
|
= render('session')
|
||||||
.container data-controller=controller_name
|
.container data-controller=controller_name
|
||||||
= render('breadcrumbs')
|
|
||||||
= render('flash')
|
= render('flash')
|
||||||
|
= render('breadcrumbs')
|
||||||
- if (controller_name == "exercises" && action_name == "implement")
|
- if (controller_name == "exercises" && action_name == "implement")
|
||||||
.container-fluid
|
.container-fluid
|
||||||
= yield
|
= yield
|
||||||
|
@ -10,7 +10,7 @@ h2 = t('shared.statistics')
|
|||||||
= row(label: '.score') do
|
= row(label: '.score') do
|
||||||
p == t('shared.out_of', maximum_value: @submission.exercise.maximum_score, value: @submission.score)
|
p == t('shared.out_of', maximum_value: @submission.exercise.maximum_score, value: @submission.score)
|
||||||
p = progress_bar(@submission.percentage)
|
p = progress_bar(@submission.percentage)
|
||||||
= row(label: '.final_submissions', value: @submission.exercise.submissions.final.distinct.count(:user_id, :user_type) - 1)
|
/= row(label: '.final_submissions', value: @submission.exercise.submissions.final.distinct.count(:user_id, :user_type) - 1)
|
||||||
/= row(label: '.average_score') do
|
/= row(label: '.average_score') do
|
||||||
/ p == t('shared.out_of', maximum_value: @submission.exercise.maximum_score, value: @submission.exercise.average_score.round(2))
|
/ p == t('shared.out_of', maximum_value: @submission.exercise.maximum_score, value: @submission.exercise.average_score.round(2))
|
||||||
/ p = progress_bar(@submission.exercise.average_percentage)
|
/ p = progress_bar(@submission.exercise.average_percentage)
|
||||||
|
@ -34,7 +34,21 @@ production:
|
|||||||
ws_host: ws://localhost:4243 #url to connect rails server to docker host
|
ws_host: ws://localhost:4243 #url to connect rails server to docker host
|
||||||
ws_client_protocol: wss:// #set the websocket protocol to be used by the client to connect to the rails server (ws on development, wss on production)
|
ws_client_protocol: wss:// #set the websocket protocol to be used by the client to connect to the rails server (ws on development, wss on production)
|
||||||
|
|
||||||
|
staging:
|
||||||
|
<<: *default
|
||||||
|
host: unix:///var/run/docker.sock
|
||||||
|
pool:
|
||||||
|
active: true
|
||||||
|
refill:
|
||||||
|
async: false
|
||||||
|
batch_size: 8
|
||||||
|
interval: 15
|
||||||
|
timeout: 60
|
||||||
|
workspace_root: <%= Rails.root.join('tmp', 'files', Rails.env) %>
|
||||||
|
ws_host: ws://localhost:4243 #url to connect rails server to docker host
|
||||||
|
ws_client_protocol: 'wss:' #set the websocket protocol to be used by the client to connect to the rails server (ws on development, wss on production)
|
||||||
|
|
||||||
test:
|
test:
|
||||||
<<: *default
|
<<: *default
|
||||||
host: tcp://192.168.59.104:2376
|
host: tcp://127.0.0.1:2376
|
||||||
workspace_root: <%= File.join('/', 'shared', Rails.env) %>
|
workspace_root: <%= File.join('/', 'shared', Rails.env) %>
|
||||||
|
@ -32,7 +32,7 @@ production:
|
|||||||
timeout: 60
|
timeout: 60
|
||||||
workspace_root: <%= Rails.root.join('tmp', 'files', Rails.env) %>
|
workspace_root: <%= Rails.root.join('tmp', 'files', Rails.env) %>
|
||||||
ws_host: ws://localhost:4243 #url to connect rails server to docker host
|
ws_host: ws://localhost:4243 #url to connect rails server to docker host
|
||||||
ws_client_protocol: 'wss:' #set the websocket protocol to be used by the client to connect to the rails server (ws on development, wss on production)
|
ws_client_protocol: wss:// #set the websocket protocol to be used by the client to connect to the rails server (ws on development, wss on production)
|
||||||
|
|
||||||
staging:
|
staging:
|
||||||
<<: *default
|
<<: *default
|
||||||
@ -50,5 +50,5 @@ staging:
|
|||||||
|
|
||||||
test:
|
test:
|
||||||
<<: *default
|
<<: *default
|
||||||
host: tcp://192.168.59.104:2376
|
host: tcp://127.0.0.1:2376
|
||||||
workspace_root: <%= File.join('/', 'shared', Rails.env) %>
|
workspace_root: <%= File.join('/', 'shared', Rails.env) %>
|
@ -3,3 +3,31 @@ class AddSubmissionToRequestForComments < ActiveRecord::Migration
|
|||||||
add_reference :request_for_comments, :submission
|
add_reference :request_for_comments, :submission
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
=begin
|
||||||
|
We issued the following on the database to add the submission_ids for existing entries
|
||||||
|
|
||||||
|
UPDATE request_for_comments
|
||||||
|
SET submission_id = sub.submission_id_external
|
||||||
|
FROM
|
||||||
|
(SELECT s.id AS submission_id_external,
|
||||||
|
rfc.id AS rfc_id,
|
||||||
|
s.created_at AS submission_created_at,
|
||||||
|
rfc.created_at AS rfc_created_at
|
||||||
|
FROM submissions s,
|
||||||
|
request_for_comments rfc
|
||||||
|
WHERE s.user_id = rfc.user_id
|
||||||
|
AND s.exercise_id = rfc.exercise_id
|
||||||
|
AND rfc.created_at + interval '2 hours' > s.created_at
|
||||||
|
AND s.created_at =
|
||||||
|
(SELECT MAX(created_at)
|
||||||
|
FROM submissions
|
||||||
|
WHERE exercise_id = s.exercise_id
|
||||||
|
AND user_id = s.user_id
|
||||||
|
AND rfc.created_at + interval '2 hours' > created_at
|
||||||
|
GROUP BY s.exercise_id,
|
||||||
|
s.user_id)) as sub
|
||||||
|
WHERE id = sub.rfc_id
|
||||||
|
AND submission_id IS NULL;
|
||||||
|
|
||||||
|
=end
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
class AddReachedFullScoreToRequestForComment < ActiveRecord::Migration
|
class AddReachedFullScoreToRequestForComment < ActiveRecord::Migration
|
||||||
def up
|
def up
|
||||||
add_column :request_for_comments, :full_score_reached, :boolean, default: false
|
add_column :request_for_comments, :full_score_reached, :boolean, default: false
|
||||||
RequestForComment.all.each { |rfc|
|
RequestForComment.find_each { |rfc|
|
||||||
if (rfc.submission.present? && rfc.submission.exercise.has_user_solved(rfc.user))
|
if rfc.submission.present? and rfc.submission.exercise.has_user_solved(rfc.user)
|
||||||
rfc.full_score_reached = true
|
rfc.full_score_reached = true
|
||||||
rfc.save
|
rfc.save
|
||||||
end
|
end
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
class FixTimestampsOnFeedback < ActiveRecord::Migration
|
class FixTimestampsOnFeedback < ActiveRecord::Migration
|
||||||
def change
|
def up
|
||||||
change_column_default(:user_exercise_feedbacks, :created_at, nil)
|
change_column_default(:user_exercise_feedbacks, :created_at, nil)
|
||||||
change_column_default(:user_exercise_feedbacks, :updated_at, nil)
|
change_column_default(:user_exercise_feedbacks, :updated_at, nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,3 +1,8 @@
|
|||||||
|
#flash-container {
|
||||||
|
position: relative;
|
||||||
|
top: -21px;
|
||||||
|
}
|
||||||
|
|
||||||
.flash {
|
.flash {
|
||||||
display: none;
|
display: none;
|
||||||
|
|
||||||
@ -10,14 +15,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.fixed_error_messages {
|
.fixed_error_messages {
|
||||||
position: fixed;
|
position: absolute;
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
top: 20px;
|
padding: inherit;
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding-left: 10%;
|
|
||||||
padding-right: 10%;
|
|
||||||
padding-top: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 140 KiB |
@ -98,7 +98,7 @@ describe Lti do
|
|||||||
let(:consumer) { FactoryBot.create(:consumer) }
|
let(:consumer) { FactoryBot.create(:consumer) }
|
||||||
let(:score) { 0.5 }
|
let(:score) { 0.5 }
|
||||||
let(:submission) { FactoryBot.create(:submission) }
|
let(:submission) { FactoryBot.create(:submission) }
|
||||||
let!(:lti_parameter) { FactoryBot.create(:lti_parameter)}
|
let!(:lti_parameter) { FactoryBot.create(:lti_parameter, consumers_id: consumer.id, external_users_id: submission.user_id, exercises_id: submission.exercise_id)}
|
||||||
|
|
||||||
context 'with an invalid score' do
|
context 'with an invalid score' do
|
||||||
it 'raises an exception' do
|
it 'raises an exception' do
|
||||||
@ -114,7 +114,6 @@ describe Lti do
|
|||||||
|
|
||||||
context 'when grading is not supported' do
|
context 'when grading is not supported' do
|
||||||
it 'returns a corresponding status' do
|
it 'returns a corresponding status' do
|
||||||
skip('ralf: this does not work, since send_score pulls data from the database, which then returns an empty array. On this is called .first, which returns nil and lets the test fail. Before Toms changes, this was taken from the session, which could be mocked')
|
|
||||||
expect_any_instance_of(IMS::LTI::ToolProvider).to receive(:outcome_service?).and_return(false)
|
expect_any_instance_of(IMS::LTI::ToolProvider).to receive(:outcome_service?).and_return(false)
|
||||||
expect(controller.send(:send_score, submission.exercise_id, score, submission.user_id)[:status]).to eq('unsupported')
|
expect(controller.send(:send_score, submission.exercise_id, score, submission.user_id)[:status]).to eq('unsupported')
|
||||||
end
|
end
|
||||||
@ -133,12 +132,10 @@ describe Lti do
|
|||||||
end
|
end
|
||||||
|
|
||||||
it 'sends the score' do
|
it 'sends the score' do
|
||||||
skip('ralf: this does not work, since send_score pulls data from the database, which then returns an empty array. On this is called .first, which returns nil and lets the test fail. Before Toms changes, this was taken from the session, which could be mocked')
|
|
||||||
controller.send(:send_score, submission.exercise_id, score, submission.user_id)
|
controller.send(:send_score, submission.exercise_id, score, submission.user_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns code, message, and status' do
|
it 'returns code, message, and status' do
|
||||||
skip('ralf: this does not work, since send_score pulls data from the database, which then returns an empty array. On this is called .first, which returns nil and lets the test fail. Before Toms changes, this was taken from the session, which could be mocked')
|
|
||||||
result = controller.send(:send_score, submission.exercise_id, score, submission.user_id)
|
result = controller.send(:send_score, submission.exercise_id, score, submission.user_id)
|
||||||
expect(result[:code]).to eq(response.response_code)
|
expect(result[:code]).to eq(response.response_code)
|
||||||
expect(result[:message]).to eq(response.body)
|
expect(result[:message]).to eq(response.body)
|
||||||
|
@ -2,6 +2,7 @@ FactoryBot.define do
|
|||||||
factory :user_exercise_feedback, class: UserExerciseFeedback do
|
factory :user_exercise_feedback, class: UserExerciseFeedback do
|
||||||
created_by_external_user
|
created_by_external_user
|
||||||
feedback_text 'Most suitable exercise ever'
|
feedback_text 'Most suitable exercise ever'
|
||||||
|
association :exercise, factory: :math
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -2,12 +2,22 @@ require 'rails_helper'
|
|||||||
require 'seeds_helper'
|
require 'seeds_helper'
|
||||||
|
|
||||||
describe DockerClient, docker: true do
|
describe DockerClient, docker: true do
|
||||||
|
WORKSPACE_PATH = '/tmp/code_ocean_test'
|
||||||
|
|
||||||
let(:command) { 'whoami' }
|
let(:command) { 'whoami' }
|
||||||
let(:docker_client) { described_class.new(execution_environment: FactoryBot.build(:java), user: FactoryBot.build(:admin)) }
|
let(:docker_client) { described_class.new(execution_environment: FactoryBot.build(:java), user: FactoryBot.build(:admin)) }
|
||||||
let(:execution_environment) { FactoryBot.build(:java) }
|
let(:execution_environment) { FactoryBot.build(:java) }
|
||||||
let(:image) { double }
|
let(:image) { double }
|
||||||
let(:submission) { FactoryBot.create(:submission) }
|
let(:submission) { FactoryBot.create(:submission) }
|
||||||
let(:workspace_path) { '/tmp' }
|
let(:workspace_path) { WORKSPACE_PATH }
|
||||||
|
|
||||||
|
before(:all) do
|
||||||
|
FileUtils.mkdir_p(WORKSPACE_PATH)
|
||||||
|
end
|
||||||
|
|
||||||
|
after(:all) do
|
||||||
|
FileUtils.rm_rf(WORKSPACE_PATH)
|
||||||
|
end
|
||||||
|
|
||||||
describe '.check_availability!' do
|
describe '.check_availability!' do
|
||||||
context 'when a socket error occurs' do
|
context 'when a socket error occurs' do
|
||||||
@ -129,7 +139,7 @@ describe DockerClient, docker: true do
|
|||||||
after(:each) { docker_client.send(:create_workspace_files, container, submission) }
|
after(:each) { docker_client.send(:create_workspace_files, container, submission) }
|
||||||
|
|
||||||
it 'creates submission-specific directories' do
|
it 'creates submission-specific directories' do
|
||||||
expect(Dir).to receive(:mkdir).at_least(:once)
|
expect(Dir).to receive(:mkdir).at_least(:once).and_call_original
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'copies binary files' do
|
it 'copies binary files' do
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
FactoryBot.define do
|
FactoryBot.define do
|
||||||
factory :error_template_attribute do
|
factory :error_template_attribute do
|
||||||
error_template nil
|
|
||||||
key "MyString"
|
key "MyString"
|
||||||
regex "MyString"
|
regex "MyString"
|
||||||
end
|
end
|
||||||
|
Reference in New Issue
Block a user