Merge remote-tracking branch 'origin/master' into statistics
This commit is contained in:
@ -2,6 +2,8 @@ $(function() {
|
|||||||
var CHART_START = window.vis ? vis.moment().add(-1, 'minute') : undefined;
|
var CHART_START = window.vis ? vis.moment().add(-1, 'minute') : undefined;
|
||||||
var DEFAULT_REFRESH_INTERVAL = 5000;
|
var DEFAULT_REFRESH_INTERVAL = 5000;
|
||||||
|
|
||||||
|
var refreshInterval;
|
||||||
|
|
||||||
var dataset;
|
var dataset;
|
||||||
var graph;
|
var graph;
|
||||||
var groups;
|
var groups;
|
||||||
@ -46,6 +48,9 @@ $(function() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
var refreshData = function(callback) {
|
var refreshData = function(callback) {
|
||||||
|
if (! $.isController('dashboard')) {
|
||||||
|
clearInterval(refreshInterval);
|
||||||
|
} else {
|
||||||
var jqxhr = $.ajax({
|
var jqxhr = $.ajax({
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
method: 'GET'
|
method: 'GET'
|
||||||
@ -57,6 +62,7 @@ $(function() {
|
|||||||
updateTable(response);
|
updateTable(response);
|
||||||
requestAnimationFrame(refreshChart);
|
requestAnimationFrame(refreshChart);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var setGroupVisibility = function(response) {
|
var setGroupVisibility = function(response) {
|
||||||
@ -101,6 +107,7 @@ $(function() {
|
|||||||
initializeChart();
|
initializeChart();
|
||||||
refreshData();
|
refreshData();
|
||||||
var refresh_interval = location.search.match(/interval=(\d+)/) ? parseInt(RegExp.$1) : DEFAULT_REFRESH_INTERVAL;
|
var refresh_interval = location.search.match(/interval=(\d+)/) ? parseInt(RegExp.$1) : DEFAULT_REFRESH_INTERVAL;
|
||||||
setInterval(refreshData, refresh_interval);
|
refreshInterval = setInterval(refreshData, refresh_interval);
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -25,6 +25,11 @@ class SubmissionsController < ApplicationController
|
|||||||
create_and_respond(object: @submission)
|
create_and_respond(object: @submission)
|
||||||
end
|
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}
|
||||||
|
end
|
||||||
|
private :command_substitutions
|
||||||
|
|
||||||
def copy_comments
|
def copy_comments
|
||||||
# copy each annotation and set the target_file.id
|
# copy each annotation and set the target_file.id
|
||||||
unless(params[:annotations_arr].nil?)
|
unless(params[:annotations_arr].nil?)
|
||||||
@ -88,6 +93,11 @@ class SubmissionsController < ApplicationController
|
|||||||
# end
|
# end
|
||||||
|
|
||||||
hijack do |tubesock|
|
hijack do |tubesock|
|
||||||
|
# probably add:
|
||||||
|
# ensure
|
||||||
|
# #guarantee that the thread is releasing the DB connection after it is done
|
||||||
|
# ActiveRecord::Base.connectionpool.releaseconnection
|
||||||
|
# end
|
||||||
Thread.new { EventMachine.run } unless EventMachine.reactor_running? && EventMachine.reactor_thread.alive?
|
Thread.new { EventMachine.run } unless EventMachine.reactor_running? && EventMachine.reactor_thread.alive?
|
||||||
|
|
||||||
|
|
||||||
@ -103,7 +113,7 @@ class SubmissionsController < ApplicationController
|
|||||||
socket = result[:socket]
|
socket = result[:socket]
|
||||||
|
|
||||||
socket.on :message do |event|
|
socket.on :message do |event|
|
||||||
Rails.logger.info("Docker sending: " + event.data)
|
Rails.logger.info( Time.now.getutc.to_s + ": Docker sending: " + event.data)
|
||||||
handle_message(event.data, tubesock)
|
handle_message(event.data, tubesock)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -112,7 +122,7 @@ class SubmissionsController < ApplicationController
|
|||||||
end
|
end
|
||||||
|
|
||||||
tubesock.onmessage do |data|
|
tubesock.onmessage do |data|
|
||||||
Rails.logger.debug("Client sending: " + data)
|
Rails.logger.info(Time.now.getutc.to_s + ": Client sending: " + data)
|
||||||
# Check wether the client send a JSON command and kill container
|
# Check wether the client send a JSON command and kill container
|
||||||
# if the command is 'exit', send it to docker otherwise.
|
# if the command is 'exit', send it to docker otherwise.
|
||||||
begin
|
begin
|
||||||
@ -122,9 +132,11 @@ class SubmissionsController < ApplicationController
|
|||||||
@docker_client.exit_container(result[:container])
|
@docker_client.exit_container(result[:container])
|
||||||
else
|
else
|
||||||
socket.send data
|
socket.send data
|
||||||
|
Rails.logger.debug('Sent the received client data to docker:' + data)
|
||||||
end
|
end
|
||||||
rescue JSON::ParserError
|
rescue JSON::ParserError
|
||||||
socket.send data
|
socket.send data
|
||||||
|
Rails.logger.debug('Rescued parsing error, sent the received client data to docker:' + data)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
@ -145,8 +157,8 @@ class SubmissionsController < ApplicationController
|
|||||||
kill_socket(tubesock)
|
kill_socket(tubesock)
|
||||||
else
|
else
|
||||||
# Filter out information about run_command, test_command, user or working directory
|
# Filter out information about run_command, test_command, user or working directory
|
||||||
run_command = @submission.execution_environment.run_command
|
run_command = @submission.execution_environment.run_command % command_substitutions(params[:filename])
|
||||||
test_command = @submission.execution_environment.test_command
|
test_command = @submission.execution_environment.test_command % command_substitutions(params[:filename])
|
||||||
if !(/root|workspace|#{run_command}|#{test_command}/.match(message))
|
if !(/root|workspace|#{run_command}|#{test_command}/.match(message))
|
||||||
parse_message(message, 'stdout', tubesock)
|
parse_message(message, 'stdout', tubesock)
|
||||||
end
|
end
|
||||||
@ -157,6 +169,7 @@ class SubmissionsController < ApplicationController
|
|||||||
begin
|
begin
|
||||||
parsed = JSON.parse(message)
|
parsed = JSON.parse(message)
|
||||||
socket.send_data message
|
socket.send_data message
|
||||||
|
Rails.logger.info('parse_message sent: ' + message)
|
||||||
rescue JSON::ParserError => e
|
rescue JSON::ParserError => e
|
||||||
# Check wether the message contains multiple lines, if true try to parse each line
|
# Check wether the message contains multiple lines, if true try to parse each line
|
||||||
if ((recursive == true) && (message.include? "\n"))
|
if ((recursive == true) && (message.include? "\n"))
|
||||||
@ -166,6 +179,7 @@ class SubmissionsController < ApplicationController
|
|||||||
else
|
else
|
||||||
parsed = {'cmd'=>'write','stream'=>output_stream,'data'=>message}
|
parsed = {'cmd'=>'write','stream'=>output_stream,'data'=>message}
|
||||||
socket.send_data JSON.dump(parsed)
|
socket.send_data JSON.dump(parsed)
|
||||||
|
Rails.logger.info('parse_message sent: ' + JSON.dump(parsed))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -6,6 +6,7 @@ h1 = ExecutionEnvironment.model_name.human(count: 2)
|
|||||||
tr
|
tr
|
||||||
th = t('activerecord.attributes.execution_environment.name')
|
th = t('activerecord.attributes.execution_environment.name')
|
||||||
th = t('activerecord.attributes.execution_environment.user')
|
th = t('activerecord.attributes.execution_environment.user')
|
||||||
|
th = t('activerecord.attributes.execution_environment.pool_size')
|
||||||
th = t('activerecord.attributes.execution_environment.memory_limit')
|
th = t('activerecord.attributes.execution_environment.memory_limit')
|
||||||
th = t('activerecord.attributes.execution_environment.network_enabled')
|
th = t('activerecord.attributes.execution_environment.network_enabled')
|
||||||
th = t('activerecord.attributes.execution_environment.permitted_execution_time')
|
th = t('activerecord.attributes.execution_environment.permitted_execution_time')
|
||||||
@ -16,6 +17,7 @@ h1 = ExecutionEnvironment.model_name.human(count: 2)
|
|||||||
tr
|
tr
|
||||||
td = execution_environment.name
|
td = execution_environment.name
|
||||||
td = link_to(execution_environment.author, execution_environment.author)
|
td = link_to(execution_environment.author, execution_environment.author)
|
||||||
|
td = execution_environment.pool_size
|
||||||
td = execution_environment.memory_limit
|
td = execution_environment.memory_limit
|
||||||
td = symbol_for(execution_environment.network_enabled)
|
td = symbol_for(execution_environment.network_enabled)
|
||||||
td = execution_environment.permitted_execution_time
|
td = execution_environment.permitted_execution_time
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
span.badge.pull-right.score
|
span.badge.pull-right.score
|
||||||
|
|
||||||
p.lead = @exercise.description
|
p.lead = render_markdown(@exercise.description)
|
||||||
|
|
||||||
#alert.alert.alert-danger role='alert'
|
#alert.alert.alert-danger role='alert'
|
||||||
h4 = t('.alert.title')
|
h4 = t('.alert.title')
|
||||||
|
@ -4,6 +4,14 @@
|
|||||||
<%
|
<%
|
||||||
|
|
||||||
user = @request_for_comment.user
|
user = @request_for_comment.user
|
||||||
|
submission_id = self.class.connection.execute("select id from submissions
|
||||||
|
where exercise_id =
|
||||||
|
#{@request_for_comment.exercise_id} AND
|
||||||
|
user_id = #{@request_for_comment.user_id} AND
|
||||||
|
#{@request_for_comment.user_id} > created_at
|
||||||
|
order by created_at desc
|
||||||
|
limit 1").first['id'].to_i
|
||||||
|
submission = Submission.find(submission_id)
|
||||||
|
|
||||||
%>
|
%>
|
||||||
<%= user %> | <%= @request_for_comment.requested_at %>
|
<%= user %> | <%= @request_for_comment.requested_at %>
|
||||||
|
@ -300,7 +300,7 @@ de:
|
|||||||
failure: Fehlerhafte E-Mail oder Passwort.
|
failure: Fehlerhafte E-Mail oder Passwort.
|
||||||
success: Sie haben sich erfolgreich angemeldet.
|
success: Sie haben sich erfolgreich angemeldet.
|
||||||
create_through_lti:
|
create_through_lti:
|
||||||
session_with_outcome: 'Nachdem Sie die Aufgabe bearbeitet haben, wird Ihre Bewertung an %{consumer} übermittelt.'
|
session_with_outcome: 'Bitte beachten Sie, dass zur Gutschrift der Punkte Ihr Code nach der Bearbeitung durch Klicken auf den Button "Code zur Bewertung abgeben" eingetragen werden muss.'
|
||||||
session_without_outcome: 'Dies ist eine Übungs-Session. Ihre Bewertung wird nicht an %{consumer} übermittelt.'
|
session_without_outcome: 'Dies ist eine Übungs-Session. Ihre Bewertung wird nicht an %{consumer} übermittelt.'
|
||||||
destroy:
|
destroy:
|
||||||
link: Abmelden
|
link: Abmelden
|
||||||
|
@ -300,7 +300,7 @@ en:
|
|||||||
failure: Invalid email or password.
|
failure: Invalid email or password.
|
||||||
success: Successfully signed in.
|
success: Successfully signed in.
|
||||||
create_through_lti:
|
create_through_lti:
|
||||||
session_with_outcome: 'After you have finished the exercise, your grade will be transmitted to %{consumer}.'
|
session_with_outcome: 'Please click "Submit Code for Assessment" after scoring to send your score %{consumer}.'
|
||||||
session_without_outcome: 'This is a practice session. Your grade will not be transmitted to %{consumer}.'
|
session_without_outcome: 'This is a practice session. Your grade will not be transmitted to %{consumer}.'
|
||||||
destroy:
|
destroy:
|
||||||
link: Sign out
|
link: Sign out
|
||||||
|
@ -162,6 +162,7 @@ class DockerClient
|
|||||||
@socket ||= create_socket(@container)
|
@socket ||= create_socket(@container)
|
||||||
# Newline required to flush
|
# Newline required to flush
|
||||||
@socket.send command + "\n"
|
@socket.send command + "\n"
|
||||||
|
Rails.logger.info('Sent command: ' + command.to_s)
|
||||||
{status: :container_running, socket: @socket, container: @container}
|
{status: :container_running, socket: @socket, container: @container}
|
||||||
else
|
else
|
||||||
{status: :container_depleted}
|
{status: :container_depleted}
|
||||||
@ -173,6 +174,7 @@ class DockerClient
|
|||||||
We need to start a second thread to kill the websocket connection,
|
We need to start a second thread to kill the websocket connection,
|
||||||
as it is impossible to determine whether further input is requested.
|
as it is impossible to determine whether further input is requested.
|
||||||
"""
|
"""
|
||||||
|
#begin
|
||||||
@thread = Thread.new do
|
@thread = Thread.new do
|
||||||
timeout = @execution_environment.permitted_execution_time.to_i # seconds
|
timeout = @execution_environment.permitted_execution_time.to_i # seconds
|
||||||
sleep(timeout)
|
sleep(timeout)
|
||||||
@ -185,6 +187,10 @@ class DockerClient
|
|||||||
kill_container(container)
|
kill_container(container)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
#ensure
|
||||||
|
# guarantee that the thread is releasing the DB connection after it is done
|
||||||
|
# ActiveRecord::Base.connectionpool.releaseconnection
|
||||||
|
#end
|
||||||
end
|
end
|
||||||
|
|
||||||
def exit_container(container)
|
def exit_container(container)
|
||||||
@ -233,6 +239,7 @@ class DockerClient
|
|||||||
end
|
end
|
||||||
|
|
||||||
def self.find_image_by_tag(tag)
|
def self.find_image_by_tag(tag)
|
||||||
|
# todo: cache this.
|
||||||
Docker::Image.all.detect { |image| image.info['RepoTags'].flatten.include?(tag) }
|
Docker::Image.all.detect { |image| image.info['RepoTags'].flatten.include?(tag) }
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -246,8 +253,10 @@ class DockerClient
|
|||||||
|
|
||||||
def initialize(options = {})
|
def initialize(options = {})
|
||||||
@execution_environment = options[:execution_environment]
|
@execution_environment = options[:execution_environment]
|
||||||
@image = self.class.find_image_by_tag(@execution_environment.docker_image)
|
# todo: eventually re-enable this if it is cached. But in the end, we do not need this.
|
||||||
fail(Error, "Cannot find image #{@execution_environment.docker_image}!") unless @image
|
# docker daemon got much too much load. all not 100% necessary calls to the daemon were removed.
|
||||||
|
#@image = self.class.find_image_by_tag(@execution_environment.docker_image)
|
||||||
|
#fail(Error, "Cannot find image #{@execution_environment.docker_image}!") unless @image
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.initialize_environment
|
def self.initialize_environment
|
||||||
@ -255,7 +264,9 @@ class DockerClient
|
|||||||
fail(Error, 'Docker configuration missing!')
|
fail(Error, 'Docker configuration missing!')
|
||||||
end
|
end
|
||||||
Docker.url = config[:host] if config[:host]
|
Docker.url = config[:host] if config[:host]
|
||||||
check_availability!
|
# todo: availability check disabled for performance reasons. Reconsider if this is necessary.
|
||||||
|
# docker daemon got much too much load. all not 100% necessary calls to the daemon were removed.
|
||||||
|
# check_availability!
|
||||||
FileUtils.mkdir_p(LOCAL_WORKSPACE_ROOT)
|
FileUtils.mkdir_p(LOCAL_WORKSPACE_ROOT)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -298,7 +309,7 @@ class DockerClient
|
|||||||
output = container.exec(['bash', '-c', command])
|
output = container.exec(['bash', '-c', command])
|
||||||
Rails.logger.info "output from container.exec"
|
Rails.logger.info "output from container.exec"
|
||||||
Rails.logger.info output
|
Rails.logger.info output
|
||||||
result = {status: output[2] == 0 ? :ok : :failed, stdout: output[0].join, stderr: output[1].join}
|
result = {status: output[2] == 0 ? :ok : :failed, stdout: output[0].join.force_encoding('utf-8'), stderr: output[1].join.force_encoding('utf-8')}
|
||||||
end
|
end
|
||||||
# if we use pooling and recylce the containers, put it back. otherwise, destroy it.
|
# if we use pooling and recylce the containers, put it back. otherwise, destroy it.
|
||||||
(DockerContainerPool.config[:active] && RECYCLE_CONTAINERS) ? self.class.return_container(container, @execution_environment) : self.class.destroy_container(container)
|
(DockerContainerPool.config[:active] && RECYCLE_CONTAINERS) ? self.class.return_container(container, @execution_environment) : self.class.destroy_container(container)
|
||||||
|
Reference in New Issue
Block a user