1
.gitignore
vendored
1
.gitignore
vendored
@@ -12,6 +12,7 @@
|
|||||||
/rubocop.html
|
/rubocop.html
|
||||||
/tmp
|
/tmp
|
||||||
/vagrant/
|
/vagrant/
|
||||||
|
/.vagrant
|
||||||
*.sublime-*
|
*.sublime-*
|
||||||
/.idea
|
/.idea
|
||||||
/.vagrant
|
/.vagrant
|
||||||
|
@@ -388,6 +388,3 @@ DEPENDENCIES
|
|||||||
uglifier (>= 1.3.0)
|
uglifier (>= 1.3.0)
|
||||||
web-console (~> 2.0)
|
web-console (~> 2.0)
|
||||||
will_paginate (~> 3.0)
|
will_paginate (~> 3.0)
|
||||||
|
|
||||||
BUNDLED WITH
|
|
||||||
1.10.6
|
|
||||||
|
96
app/assets/javascripts/submission_statistics.js
Normal file
96
app/assets/javascripts/submission_statistics.js
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
$(function() {
|
||||||
|
|
||||||
|
var ACE_FILES_PATH = '/assets/ace/';
|
||||||
|
var THEME = 'ace/theme/textmate';
|
||||||
|
|
||||||
|
var currentSubmission = 0;
|
||||||
|
var active_file = undefined;
|
||||||
|
var fileTrees = []
|
||||||
|
var editor = undefined;
|
||||||
|
var fileTypeById = {}
|
||||||
|
|
||||||
|
var showActiveFile = function() {
|
||||||
|
var session = editor.getSession();
|
||||||
|
var fileType = fileTypeById[active_file.file_type_id]
|
||||||
|
session.setMode(fileType.editor_mode);
|
||||||
|
session.setTabSize(fileType.indent_size);
|
||||||
|
session.setValue(active_file.content);
|
||||||
|
session.setUseSoftTabs(true);
|
||||||
|
session.setUseWrapMode(true);
|
||||||
|
|
||||||
|
showFileTree(currentSubmission);
|
||||||
|
filetree = $(fileTrees[currentSubmission])
|
||||||
|
filetree.jstree("deselect_all");
|
||||||
|
filetree.jstree().select_node(active_file.file_id);
|
||||||
|
};
|
||||||
|
|
||||||
|
var initializeFileTree = function() {
|
||||||
|
$('.files').each(function(index, element) {
|
||||||
|
fileTree = $(element).jstree($(element).data('entries'));
|
||||||
|
fileTree.on('click', 'li.jstree-leaf', function() {
|
||||||
|
var id = parseInt($(this).attr('id'))
|
||||||
|
_.each(files[currentSubmission], function(file) {
|
||||||
|
if (file.file_id === id) {
|
||||||
|
active_file = file;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
showActiveFile();
|
||||||
|
});
|
||||||
|
fileTrees.push(fileTree);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var showFileTree = function(index) {
|
||||||
|
$('.files').hide();
|
||||||
|
$(fileTrees[index].context).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($.isController('exercises') && $('#timeline').isPresent()) {
|
||||||
|
|
||||||
|
_.each(['modePath', 'themePath', 'workerPath'], function(attribute) {
|
||||||
|
ace.config.set(attribute, ACE_FILES_PATH);
|
||||||
|
});
|
||||||
|
|
||||||
|
var slider = $('#submissions-slider>input');
|
||||||
|
var submissions = $('#data').data('submissions');
|
||||||
|
var files = $('#data').data('files');
|
||||||
|
var filetypes = $('#data').data('file-types');
|
||||||
|
|
||||||
|
editor = ace.edit('current-file');
|
||||||
|
editor.setShowPrintMargin(false);
|
||||||
|
editor.setTheme(THEME);
|
||||||
|
editor.$blockScrolling = Infinity;
|
||||||
|
editor.setReadOnly(true);
|
||||||
|
|
||||||
|
_.each(filetypes, function (filetype) {
|
||||||
|
filetype = JSON.parse(filetype);
|
||||||
|
fileTypeById[filetype.id] = filetype;
|
||||||
|
});
|
||||||
|
|
||||||
|
$('tr[data-id]>.clickable').each(function(index, element) {
|
||||||
|
element = $(element);
|
||||||
|
element.click(function() {
|
||||||
|
slider.val(index);
|
||||||
|
slider.change()
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
slider.on('change', function(event) {
|
||||||
|
currentSubmission = slider.val();
|
||||||
|
var currentFiles = files[currentSubmission];
|
||||||
|
var fileIndex = 0;
|
||||||
|
_.each(currentFiles, function(file, index) {
|
||||||
|
if (file.name === active_file.name) {
|
||||||
|
fileIndex = index;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
active_file = currentFiles[fileIndex];
|
||||||
|
showActiveFile();
|
||||||
|
});
|
||||||
|
|
||||||
|
active_file = files[0][0]
|
||||||
|
initializeFileTree();
|
||||||
|
showActiveFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
12
app/assets/stylesheets/statistics.css.scss
Normal file
12
app/assets/stylesheets/statistics.css.scss
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#submissions-slider {
|
||||||
|
margin-top: 25px;
|
||||||
|
margin-bottom: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#current-file.editor {
|
||||||
|
height: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clickable {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
@@ -2,7 +2,7 @@ class ExecutionEnvironmentsController < ApplicationController
|
|||||||
include CommonBehavior
|
include CommonBehavior
|
||||||
|
|
||||||
before_action :set_docker_images, only: [:create, :edit, :new, :update]
|
before_action :set_docker_images, only: [:create, :edit, :new, :update]
|
||||||
before_action :set_execution_environment, only: MEMBER_ACTIONS + [:execute_command, :shell]
|
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_testing_framework_adapters, only: [:create, :edit, :new, :update]
|
||||||
|
|
||||||
def authorize!
|
def authorize!
|
||||||
@@ -28,6 +28,9 @@ class ExecutionEnvironmentsController < ApplicationController
|
|||||||
render(json: @docker_client.execute_arbitrary_command(params[:command]))
|
render(json: @docker_client.execute_arbitrary_command(params[:command]))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def statistics
|
||||||
|
end
|
||||||
|
|
||||||
def execution_environment_params
|
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)
|
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
|
||||||
|
@@ -7,6 +7,7 @@ class ExercisesController < ApplicationController
|
|||||||
before_action :handle_file_uploads, only: [:create, :update]
|
before_action :handle_file_uploads, only: [:create, :update]
|
||||||
before_action :set_execution_environments, only: [:create, :edit, :new, :update]
|
before_action :set_execution_environments, only: [:create, :edit, :new, :update]
|
||||||
before_action :set_exercise, only: MEMBER_ACTIONS + [:clone, :implement, :run, :statistics, :submit, :reload]
|
before_action :set_exercise, only: MEMBER_ACTIONS + [:clone, :implement, :run, :statistics, :submit, :reload]
|
||||||
|
before_action :set_external_user, only: [:statistics]
|
||||||
before_action :set_file_types, only: [:create, :edit, :new, :update]
|
before_action :set_file_types, only: [:create, :edit, :new, :update]
|
||||||
before_action :set_teams, only: [:create, :edit, :new, :update]
|
before_action :set_teams, only: [:create, :edit, :new, :update]
|
||||||
|
|
||||||
@@ -125,6 +126,14 @@ class ExercisesController < ApplicationController
|
|||||||
end
|
end
|
||||||
private :set_exercise
|
private :set_exercise
|
||||||
|
|
||||||
|
def set_external_user
|
||||||
|
if params[:external_user_id]
|
||||||
|
@external_user = ExternalUser.find(params[:external_user_id])
|
||||||
|
authorize!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
private :set_exercise
|
||||||
|
|
||||||
def set_file_types
|
def set_file_types
|
||||||
@file_types = FileType.all.order(:name)
|
@file_types = FileType.all.order(:name)
|
||||||
end
|
end
|
||||||
@@ -143,6 +152,11 @@ class ExercisesController < ApplicationController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def statistics
|
def statistics
|
||||||
|
if(@external_user)
|
||||||
|
render 'exercises/external_users/statistics'
|
||||||
|
else
|
||||||
|
render 'exercises/statistics'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def submit
|
def submit
|
||||||
|
@@ -13,4 +13,10 @@ class ExternalUsersController < ApplicationController
|
|||||||
@user = ExternalUser.find(params[:id])
|
@user = ExternalUser.find(params[:id])
|
||||||
authorize!
|
authorize!
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def statistics
|
||||||
|
@user = ExternalUser.find(params[:id])
|
||||||
|
authorize!
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@@ -11,7 +11,10 @@ class Exercise < ActiveRecord::Base
|
|||||||
belongs_to :execution_environment
|
belongs_to :execution_environment
|
||||||
has_many :submissions
|
has_many :submissions
|
||||||
belongs_to :team
|
belongs_to :team
|
||||||
has_many :users, source_type: ExternalUser, through: :submissions
|
|
||||||
|
has_many :external_users, source: :user, source_type: ExternalUser, through: :submissions
|
||||||
|
has_many :internal_users, source: :user, source_type: InternalUser, through: :submissions
|
||||||
|
alias_method :users, :external_users
|
||||||
|
|
||||||
scope :with_submissions, -> { where('id IN (SELECT exercise_id FROM submissions)') }
|
scope :with_submissions, -> { where('id IN (SELECT exercise_id FROM submissions)') }
|
||||||
|
|
||||||
@@ -22,15 +25,54 @@ class Exercise < ActiveRecord::Base
|
|||||||
validates :title, presence: true
|
validates :title, presence: true
|
||||||
validates :token, presence: true, uniqueness: true
|
validates :token, presence: true, uniqueness: true
|
||||||
|
|
||||||
|
|
||||||
def average_percentage
|
def average_percentage
|
||||||
(average_score/ maximum_score * 100).round if average_score
|
(average_score / maximum_score * 100).round if average_score
|
||||||
end
|
end
|
||||||
|
|
||||||
def average_score
|
def average_score
|
||||||
if submissions.exists?(cause: 'submit')
|
if submissions.exists?(cause: 'submit')
|
||||||
maximum_scores_query = submissions.select('MAX(score) AS maximum_score').where(cause: 'submit').group(:user_id).to_sql.sub('$1', id.to_s)
|
maximum_scores_query = submissions.select('MAX(score) AS maximum_score').where(cause: 'submit').group(:user_id).to_sql.sub('$1', id.to_s)
|
||||||
self.class.connection.execute("SELECT AVG(maximum_score) AS average_score FROM (#{maximum_scores_query}) AS maximum_scores").first['average_score'].to_f
|
self.class.connection.execute("SELECT AVG(maximum_score) AS average_score FROM (#{maximum_scores_query}) AS maximum_scores").first['average_score'].to_f
|
||||||
end
|
else 0 end
|
||||||
|
end
|
||||||
|
|
||||||
|
def average_number_of_submissions
|
||||||
|
user_count = internal_users.distinct.count + external_users.distinct.count
|
||||||
|
return user_count == 0 ? 0 : submissions.count() / user_count.to_f()
|
||||||
|
end
|
||||||
|
|
||||||
|
def user_working_time_query
|
||||||
|
"""
|
||||||
|
SELECT user_id,
|
||||||
|
sum(working_time_new) AS working_time
|
||||||
|
FROM
|
||||||
|
(SELECT user_id,
|
||||||
|
CASE WHEN working_time >= '0:30:00' THEN '0' ELSE working_time END AS working_time_new
|
||||||
|
FROM
|
||||||
|
(SELECT user_id,
|
||||||
|
id,
|
||||||
|
(created_at - lag(created_at) over (PARTITION BY user_id
|
||||||
|
ORDER BY id)) AS working_time
|
||||||
|
FROM submissions
|
||||||
|
WHERE exercise_id=#{id}) AS foo) AS bar
|
||||||
|
GROUP BY user_id
|
||||||
|
"""
|
||||||
|
end
|
||||||
|
|
||||||
|
def average_working_time
|
||||||
|
self.class.connection.execute("""
|
||||||
|
SELECT avg(working_time) as average_time
|
||||||
|
FROM
|
||||||
|
(#{user_working_time_query}) AS baz;
|
||||||
|
""").first['average_time']
|
||||||
|
end
|
||||||
|
|
||||||
|
def average_working_time_for(user_id)
|
||||||
|
self.class.connection.execute("""
|
||||||
|
#{user_working_time_query}
|
||||||
|
HAVING user_id = #{user_id}
|
||||||
|
""").first['working_time']
|
||||||
end
|
end
|
||||||
|
|
||||||
def duplicate(attributes = {})
|
def duplicate(attributes = {})
|
||||||
|
@@ -4,7 +4,7 @@ class ExecutionEnvironmentPolicy < AdminOrAuthorPolicy
|
|||||||
end
|
end
|
||||||
private :author?
|
private :author?
|
||||||
|
|
||||||
[:execute_command?, :shell?].each do |action|
|
[:execute_command?, :shell?, :statistics?].each do |action|
|
||||||
define_method(action) { admin? || author? }
|
define_method(action) { admin? || author? }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@@ -1,2 +1,5 @@
|
|||||||
class ExternalUserPolicy < AdminOnlyPolicy
|
class ExternalUserPolicy < AdminOnlyPolicy
|
||||||
|
def statistics?
|
||||||
|
admin?
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@@ -10,7 +10,7 @@ h1 = ExecutionEnvironment.model_name.human(count: 2)
|
|||||||
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')
|
||||||
th colspan=4 = t('shared.actions')
|
th colspan=5 = t('shared.actions')
|
||||||
th colspan=2 = t('shared.resources')
|
th colspan=2 = t('shared.resources')
|
||||||
tbody
|
tbody
|
||||||
- @execution_environments.each do |execution_environment|
|
- @execution_environments.each do |execution_environment|
|
||||||
@@ -25,6 +25,7 @@ h1 = ExecutionEnvironment.model_name.human(count: 2)
|
|||||||
td = link_to(t('shared.edit'), edit_execution_environment_path(execution_environment))
|
td = link_to(t('shared.edit'), edit_execution_environment_path(execution_environment))
|
||||||
td = link_to(t('shared.destroy'), execution_environment, data: {confirm: t('shared.confirm_destroy')}, method: :delete)
|
td = link_to(t('shared.destroy'), execution_environment, data: {confirm: t('shared.confirm_destroy')}, method: :delete)
|
||||||
td = link_to(t('.shell'), shell_execution_environment_path(execution_environment))
|
td = link_to(t('.shell'), shell_execution_environment_path(execution_environment))
|
||||||
|
td = link_to(t('shared.statistics'), statistics_execution_environment_path(execution_environment))
|
||||||
td = link_to(t('activerecord.models.error.other'), execution_environment_errors_path(execution_environment.id))
|
td = link_to(t('activerecord.models.error.other'), execution_environment_errors_path(execution_environment.id))
|
||||||
td = link_to(t('activerecord.models.hint.other'), execution_environment_hints_path(execution_environment.id))
|
td = link_to(t('activerecord.models.hint.other'), execution_environment_hints_path(execution_environment.id))
|
||||||
|
|
||||||
|
15
app/views/execution_environments/statistics.html.slim
Normal file
15
app/views/execution_environments/statistics.html.slim
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
h1 = @execution_environment
|
||||||
|
|
||||||
|
.table-responsive
|
||||||
|
table.table
|
||||||
|
thead
|
||||||
|
tr
|
||||||
|
- ['.exercise', '.score', '.runs', '.worktime'].each do |title|
|
||||||
|
th.header = t(title)
|
||||||
|
tbody
|
||||||
|
- @execution_environment.exercises.each do |exercise|
|
||||||
|
tr
|
||||||
|
td = link_to exercise.title, controller: "exercises", action: "statistics", id: exercise.id
|
||||||
|
td = exercise.average_score
|
||||||
|
td = exercise.average_number_of_submissions
|
||||||
|
td = exercise.average_working_time
|
48
app/views/exercises/external_users/statistics.html.slim
Normal file
48
app/views/exercises/external_users/statistics.html.slim
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
h1 = "#{@exercise} (external user #{@external_user})"
|
||||||
|
- submissions = Submission.where("user_id = ? AND exercise_id = ?", @external_user.id, @exercise.id)
|
||||||
|
- current_submission = submissions.first
|
||||||
|
- if current_submission
|
||||||
|
- initial_files = current_submission.files.to_a
|
||||||
|
|
||||||
|
- all_files = []
|
||||||
|
- file_types = Set.new()
|
||||||
|
- submissions.each do |submission|
|
||||||
|
- submission.files.each do |file|
|
||||||
|
- file_types.add(ActiveSupport::JSON.encode(file.file_type))
|
||||||
|
- all_files.push(submission.files)
|
||||||
|
|
||||||
|
.hidden#data data-submissions=ActiveSupport::JSON.encode(submissions) data-files=ActiveSupport::JSON.encode(all_files) data-file-types=ActiveSupport::JSON.encode(file_types)
|
||||||
|
|
||||||
|
#stats-editor.row
|
||||||
|
- index = 0
|
||||||
|
- all_files.each do |files|
|
||||||
|
.files class=(@exercise.hide_file_tree ? 'hidden col-sm-3' : 'col-sm-3') data-index=index data-entries=FileTree.new(files).to_js_tree
|
||||||
|
- index += 1
|
||||||
|
div class=(@exercise.hide_file_tree ? 'col-sm-12' : 'col-sm-9')
|
||||||
|
#current-file.editor
|
||||||
|
|
||||||
|
#submissions-slider
|
||||||
|
input type='range' orient='horizontal' list='datapoints' min=0 max=submissions.length-1 value=0
|
||||||
|
datalist#datapoints
|
||||||
|
- index=0
|
||||||
|
- submissions.each do |submission|
|
||||||
|
option data-submission=submission
|
||||||
|
=index
|
||||||
|
- index += 1
|
||||||
|
|
||||||
|
#timeline
|
||||||
|
.table-responsive
|
||||||
|
table.table
|
||||||
|
thead
|
||||||
|
tr
|
||||||
|
- ['.time', '.cause', '.score'].each do |title|
|
||||||
|
th.header = t(title)
|
||||||
|
tbody
|
||||||
|
- submissions.each do |submission|
|
||||||
|
tr data-id=submission.id
|
||||||
|
td.clickable = submission.created_at.strftime("%F %T")
|
||||||
|
td = submission.cause
|
||||||
|
td = submission.score
|
||||||
|
|
||||||
|
- else
|
||||||
|
p = t('.no_data_available')
|
@@ -7,3 +7,23 @@ h1 = @exercise
|
|||||||
= row(label: '.average_score') do
|
= row(label: '.average_score') do
|
||||||
p == @exercise.average_score ? t('shared.out_of', maximum_value: @exercise.maximum_score, value: @exercise.average_score.round(2)) : empty
|
p == @exercise.average_score ? t('shared.out_of', maximum_value: @exercise.maximum_score, value: @exercise.average_score.round(2)) : empty
|
||||||
p = progress_bar(@exercise.average_percentage)
|
p = progress_bar(@exercise.average_percentage)
|
||||||
|
|
||||||
|
= row(label: '.average_worktime') do
|
||||||
|
p = @exercise.average_working_time
|
||||||
|
|
||||||
|
- Hash[:internal_users => t('.internal_users'), :external_users => t('.external_users')].each_pair do |symbol, label|
|
||||||
|
strong = label
|
||||||
|
.table-responsive
|
||||||
|
table.table
|
||||||
|
thead
|
||||||
|
tr
|
||||||
|
- ['.user', '.score', '.runs', '.worktime'].each do |title|
|
||||||
|
th.header = t(title)
|
||||||
|
tbody
|
||||||
|
- @exercise.send(symbol).distinct().each do |user|
|
||||||
|
tr
|
||||||
|
- submissions = @exercise.submissions.where('user_id=?', user.id)
|
||||||
|
td = link_to_if symbol==:external_users, "#{user.name} (#{user.email})", {controller: "exercises", action: "statistics", external_user_id: user.id, id: @exercise.id}
|
||||||
|
td = submissions.maximum('score') or 0
|
||||||
|
td = submissions.count('id')
|
||||||
|
td = @exercise.average_working_time_for(user.id) or 0
|
||||||
|
2
app/views/external_users/statistics.html.slim
Normal file
2
app/views/external_users/statistics.html.slim
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
h1 = @user
|
||||||
|
H2 = 'Hallo'
|
@@ -167,6 +167,11 @@ de:
|
|||||||
shell:
|
shell:
|
||||||
command: Befehl
|
command: Befehl
|
||||||
headline: Shell
|
headline: Shell
|
||||||
|
statistics:
|
||||||
|
exercise: Übung
|
||||||
|
score: Durchschnittliche Punktzahl
|
||||||
|
runs: Durchschnittliche Anzahl von Versuchen
|
||||||
|
worktime: Durchschnittliche Arbeitszeit
|
||||||
exercises:
|
exercises:
|
||||||
editor:
|
editor:
|
||||||
confirm_start_over: Wollen Sie wirklich von vorne anfangen?
|
confirm_start_over: Wollen Sie wirklich von vorne anfangen?
|
||||||
@@ -239,8 +244,21 @@ de:
|
|||||||
intermediate_submissions: Intermediäre Abgaben
|
intermediate_submissions: Intermediäre Abgaben
|
||||||
participants: Bearbeitende Nutzer
|
participants: Bearbeitende Nutzer
|
||||||
users: '%{count} verschiedene Nutzer'
|
users: '%{count} verschiedene Nutzer'
|
||||||
|
user: Nutzer
|
||||||
|
score: Punktzahl
|
||||||
|
runs: Versuche
|
||||||
|
worktime: Arbeitszeit
|
||||||
|
average_worktime: Durchschnittliche Arbeitszeit
|
||||||
|
internal_users: Interne Nutzer
|
||||||
|
external_user: Externe Nutzer
|
||||||
submit:
|
submit:
|
||||||
failure: Beim Übermitteln Ihrer Punktzahl ist ein Fehler aufgetreten. Bitte versuchen Sie es später erneut.
|
failure: Beim Übermitteln Ihrer Punktzahl ist ein Fehler aufgetreten. Bitte versuchen Sie es später erneut.
|
||||||
|
external_users:
|
||||||
|
statistics:
|
||||||
|
no_data_available: Keine Daten verfügbar.
|
||||||
|
time: Zeit
|
||||||
|
cause: Grund
|
||||||
|
score: Punktzahl
|
||||||
files:
|
files:
|
||||||
roles:
|
roles:
|
||||||
main_file: Hauptdatei
|
main_file: Hauptdatei
|
||||||
|
@@ -167,6 +167,11 @@ en:
|
|||||||
shell:
|
shell:
|
||||||
command: Command
|
command: Command
|
||||||
headline: Shell
|
headline: Shell
|
||||||
|
statistics:
|
||||||
|
exercise: Exercise
|
||||||
|
score: Average Score
|
||||||
|
runs: Average Number of Runs
|
||||||
|
worktime: Average Working Time
|
||||||
exercises:
|
exercises:
|
||||||
editor:
|
editor:
|
||||||
confirm_start_over: Do you really want to start over?
|
confirm_start_over: Do you really want to start over?
|
||||||
@@ -239,8 +244,21 @@ en:
|
|||||||
intermediate_submissions: Intermediate Submissions
|
intermediate_submissions: Intermediate Submissions
|
||||||
participants: Participating Users
|
participants: Participating Users
|
||||||
users: '%{count} distinct users'
|
users: '%{count} distinct users'
|
||||||
|
user: User
|
||||||
|
score: Score
|
||||||
|
runs: Runs
|
||||||
|
worktime: Working Time
|
||||||
|
average_worktime: Average Working Time
|
||||||
|
internal_users: Internal Users
|
||||||
|
external_users: External Users
|
||||||
submit:
|
submit:
|
||||||
failure: An error occured while transmitting your score. Please try again later.
|
failure: An error occured while transmitting your score. Please try again later.
|
||||||
|
external_users:
|
||||||
|
statistics:
|
||||||
|
no_data_available: No data available.
|
||||||
|
time: Time
|
||||||
|
cause: Cause
|
||||||
|
score: Score
|
||||||
files:
|
files:
|
||||||
roles:
|
roles:
|
||||||
main_file: Main File
|
main_file: Main File
|
||||||
|
@@ -20,12 +20,20 @@ Rails.application.routes.draw do
|
|||||||
|
|
||||||
get '/help', to: 'application#help'
|
get '/help', to: 'application#help'
|
||||||
|
|
||||||
|
concern :statistics do
|
||||||
|
member do
|
||||||
|
get :statistics
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
resources :consumers
|
resources :consumers
|
||||||
|
|
||||||
resources :execution_environments do
|
resources :execution_environments do
|
||||||
member do
|
member do
|
||||||
get :shell
|
get :shell
|
||||||
post 'shell', as: :execute_command, to: :execute_command
|
post 'shell', as: :execute_command, to: :execute_command
|
||||||
|
get :statistics
|
||||||
end
|
end
|
||||||
|
|
||||||
resources :errors, only: [:create, :index, :show]
|
resources :errors, only: [:create, :index, :show]
|
||||||
@@ -46,7 +54,9 @@ Rails.application.routes.draw do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
resources :external_users, only: [:index, :show]
|
resources :external_users, only: [:index, :show], concerns: :statistics do
|
||||||
|
resources :exercises, concerns: :statistics
|
||||||
|
end
|
||||||
|
|
||||||
namespace :code_ocean do
|
namespace :code_ocean do
|
||||||
resources :files, only: [:create, :destroy]
|
resources :files, only: [:create, :destroy]
|
||||||
|
@@ -130,6 +130,14 @@ describe ExecutionEnvironmentsController do
|
|||||||
expect_template(:shell)
|
expect_template(:shell)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'GET #statistics' do
|
||||||
|
before(:each) { get :statistics, id: execution_environment.id }
|
||||||
|
|
||||||
|
expect_assigns(execution_environment: :execution_environment)
|
||||||
|
expect_status(200)
|
||||||
|
expect_template(:statistics)
|
||||||
|
end
|
||||||
|
|
||||||
describe 'GET #show' do
|
describe 'GET #show' do
|
||||||
before(:each) { get :show, id: execution_environment.id }
|
before(:each) { get :show, id: execution_environment.id }
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user