Merge remote-tracking branch 'origin/master' into activity-graphs
# Conflicts: # config/locales/de.yml # config/locales/en.yml
This commit is contained in:
27
app/assets/javascripts/bootstrap-dropdown-submenu.js
vendored
Normal file
27
app/assets/javascripts/bootstrap-dropdown-submenu.js
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
$(document).ready(function () {
|
||||
|
||||
var subMenusSelector = 'ul.dropdown-menu [data-toggle=dropdown]';
|
||||
|
||||
function openSubMenu(event) {
|
||||
if (this.pathname === '/') {
|
||||
event.preventDefault();
|
||||
}
|
||||
event.stopPropagation();
|
||||
|
||||
$(subMenusSelector).parent().removeClass('open');
|
||||
$(this).parent().addClass('open');
|
||||
|
||||
var menu = $(this).parent().find("ul");
|
||||
var menupos = menu.offset();
|
||||
|
||||
var newPos;
|
||||
if ((menupos.left + menu.width()) + 30 > $(window).width()) {
|
||||
newPos = -menu.width();
|
||||
} else {
|
||||
newPos = $(this).parent().width();
|
||||
}
|
||||
menu.css({left: newPos});
|
||||
}
|
||||
|
||||
$(subMenusSelector).on('click', openSubMenu).on('mouseenter', openSubMenu);
|
||||
});
|
38
app/assets/stylesheets/bootstrap-dropdown-submenu.css.scss
vendored
Normal file
38
app/assets/stylesheets/bootstrap-dropdown-submenu.css.scss
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
.dropdown-submenu {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.dropdown-submenu > .dropdown-menu {
|
||||
top: 0;
|
||||
left: 100%;
|
||||
}
|
||||
|
||||
.dropdown-submenu > a:after {
|
||||
display: block;
|
||||
content: " ";
|
||||
float: right;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-color: transparent;
|
||||
border-style: solid;
|
||||
border-width: 5px 0 5px 5px;
|
||||
border-left-color: #cccccc;
|
||||
margin-top: 5px;
|
||||
margin-right: -10px;
|
||||
}
|
||||
|
||||
.dropdown-submenu:hover > a:after {
|
||||
border-left-color: #ffffff;
|
||||
}
|
||||
|
||||
.dropdown-submenu.pull-left {
|
||||
float: none;
|
||||
}
|
||||
|
||||
.dropdown-submenu.pull-left > .dropdown-menu {
|
||||
left: -100%;
|
||||
margin-left: 10px;
|
||||
-webkit-border-radius: 6px 0 6px 6px;
|
||||
-moz-border-radius: 6px 0 6px 6px;
|
||||
border-radius: 6px 0 6px 6px;
|
||||
}
|
@ -53,64 +53,72 @@
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
.testrun-assess-results {
|
||||
|
||||
.testrun-assess-results {
|
||||
.testrun-container {
|
||||
display: flex;
|
||||
margin-bottom: 10px;
|
||||
|
||||
display: flex;
|
||||
.testrun-output {
|
||||
overflow-x: auto;
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.result {
|
||||
margin-right: 10px;
|
||||
margin-top: 20px;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
.passed {
|
||||
border-radius: 50%;
|
||||
background-color: #8efa00;
|
||||
-webkit-box-shadow: 0 0 11px 1px rgba(44,222,0,1);
|
||||
-moz-box-shadow: 0 0 11px 1px rgba(44,222,0,1);
|
||||
box-shadow: 0 0 11px 1px rgba(44,222,0,1);
|
||||
}
|
||||
|
||||
.unknown {
|
||||
border-radius: 50%;
|
||||
background-color: #ffca00;
|
||||
-webkit-box-shadow: 0 0 11px 1px rgb(255, 202, 0);
|
||||
-moz-box-shadow: 0 0 11px 1px rgb(255, 202, 0);
|
||||
box-shadow: 0 0 11px 1px rgb(255, 202, 0);
|
||||
}
|
||||
|
||||
.failed {
|
||||
border-radius: 50%;
|
||||
background-color: #ff2600;
|
||||
-webkit-box-shadow: 0 0 11px 1px rgba(222,0,0,1);
|
||||
-moz-box-shadow: 0 0 11px 1px rgba(222,0,0,1);
|
||||
box-shadow: 0 0 11px 1px rgba(222,0,0,1);
|
||||
}
|
||||
|
||||
.result {
|
||||
margin-right: 10px;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
.passed {
|
||||
border-radius: 50%;
|
||||
background-color: #8efa00;
|
||||
-webkit-box-shadow: 0 0 11px 1px rgba(44,222,0,1);
|
||||
-moz-box-shadow: 0 0 11px 1px rgba(44,222,0,1);
|
||||
box-shadow: 0 0 11px 1px rgba(44,222,0,1);
|
||||
#mark-as-solved-button {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.unknown {
|
||||
border-radius: 50%;
|
||||
background-color: #ffca00;
|
||||
-webkit-box-shadow: 0 0 11px 1px rgb(255, 202, 0);
|
||||
-moz-box-shadow: 0 0 11px 1px rgb(255, 202, 0);
|
||||
box-shadow: 0 0 11px 1px rgb(255, 202, 0);
|
||||
#thank-you-container {
|
||||
display: none;
|
||||
margin-top: 20px;
|
||||
padding: 5px;
|
||||
border: solid lightgrey 1px;
|
||||
background-color: rgba(20, 180, 20, 0.2);
|
||||
border-radius: 4px;
|
||||
|
||||
button {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.failed {
|
||||
border-radius: 50%;
|
||||
background-color: #ff2600;
|
||||
-webkit-box-shadow: 0 0 11px 1px rgba(222,0,0,1);
|
||||
-moz-box-shadow: 0 0 11px 1px rgba(222,0,0,1);
|
||||
box-shadow: 0 0 11px 1px rgba(222,0,0,1);
|
||||
#thank-you-note {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#mark-as-solved-button {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
#thank-you-container {
|
||||
display: none;
|
||||
margin-top: 20px;
|
||||
padding: 5px;
|
||||
border: solid lightgrey 1px;
|
||||
background-color: rgba(20, 180, 20, 0.2);
|
||||
border-radius: 4px;
|
||||
|
||||
button {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
#thank-you-note {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
#commentitor {
|
||||
|
@ -58,6 +58,10 @@ div.negative-result {
|
||||
box-shadow: 0px 0px 11px 1px rgba(222,0,0,1);
|
||||
}
|
||||
|
||||
tr.highlight {
|
||||
border-top: 2px solid rgba(222,0,0,1);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// StatisticsController:
|
||||
|
||||
|
@ -326,6 +326,16 @@ class ExercisesController < ApplicationController
|
||||
|
||||
def statistics
|
||||
if(@external_user)
|
||||
@submissions = Submission.where("user_id = ? AND exercise_id = ?", @external_user.id, @exercise.id).order("created_at")
|
||||
@submissions_and_interventions = (@submissions + UserExerciseIntervention.where("user_id = ? AND exercise_id = ?", @external_user.id, @exercise.id)).sort_by { |a| a.created_at }
|
||||
deltas = @submissions.map.with_index do |item, index|
|
||||
delta = item.created_at - @submissions[index - 1].created_at if index > 0
|
||||
if delta == nil or delta > 10 * 60 then 0 else delta end
|
||||
end
|
||||
@working_times_until = []
|
||||
@submissions_and_interventions.each_with_index do |submission, index|
|
||||
@working_times_until.push((Time.at(deltas[1..index].inject(:+)).utc.strftime("%H:%M:%S") if index > 0))
|
||||
end
|
||||
render 'exercises/external_users/statistics'
|
||||
else
|
||||
user_statistics = {}
|
||||
|
@ -9,8 +9,9 @@
|
||||
li = link_to(t('breadcrumbs.dashboard.show'), admin_dashboard_path)
|
||||
li = link_to(t('breadcrumbs.statistics.show'), statistics_path)
|
||||
li.divider
|
||||
- models = [ExecutionEnvironment, Exercise, ExerciseCollection, ProxyExercise, Tag, Consumer, CodeHarborLink, UserExerciseFeedback,
|
||||
ErrorTemplate, ErrorTemplateAttribute, ExternalUser, FileType, FileTemplate, InternalUser].sort_by {|model| model.model_name.human(count: 2) }
|
||||
- models.each do |model|
|
||||
- if policy(model).index?
|
||||
li = link_to(model.model_name.human(count: 2), send(:"#{model.model_name.collection}_path"))
|
||||
= render('navigation_submenu', title: t('activerecord.models.exercise.other'), models: [Exercise, ExerciseCollection, ProxyExercise, Tag], link: exercises_path)
|
||||
= render('navigation_submenu', title: t('navigation.sections.users'), models: [InternalUser, ExternalUser])
|
||||
= render('navigation_collection_link', model: ExecutionEnvironment)
|
||||
= render('navigation_submenu', title: t('navigation.sections.errors'), models: [ErrorTemplate, ErrorTemplateAttribute])
|
||||
= render('navigation_submenu', title: t('navigation.sections.files'), models: [FileType, FileTemplate])
|
||||
= render('navigation_submenu', title: t('navigation.sections.integrations'), models: [Consumer, CodeHarborLink])
|
||||
|
@ -0,0 +1,2 @@
|
||||
- if policy(model).index?
|
||||
li = link_to(model.model_name.human(count: 2), send(:"#{model.model_name.collection}_path"))
|
6
app/views/application/_navigation_submenu.html.slim
Normal file
6
app/views/application/_navigation_submenu.html.slim
Normal file
@ -0,0 +1,6 @@
|
||||
li.dropdown.dropdown-submenu
|
||||
- link = link.nil? ? "#" : link
|
||||
a href=link class="dropdown-toggle" data-toggle="dropdown" = title
|
||||
ul class="dropdown-menu"
|
||||
- models.each do |model|
|
||||
= render('navigation_collection_link', model: model)
|
@ -1,19 +1,16 @@
|
||||
h1 = "#{@exercise} (external user #{@external_user})"
|
||||
- submissions = Submission.where("user_id = ? AND exercise_id = ?", @external_user.id, @exercise.id).order("created_at")
|
||||
- current_submission = submissions.first
|
||||
- submissions_and_interventions = (submissions + UserExerciseIntervention.where("user_id = ? AND exercise_id = ?", @external_user.id, @exercise.id)).sort_by { |a| a.created_at }
|
||||
|
||||
- current_submission = @submissions.first
|
||||
- if current_submission
|
||||
- initial_files = current_submission.files.to_a
|
||||
|
||||
- all_files = []
|
||||
- file_types = Set.new()
|
||||
- submissions.each do |submission|
|
||||
- @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)
|
||||
.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
|
||||
@ -27,14 +24,13 @@ h1 = "#{@exercise} (external user #{@external_user})"
|
||||
button.btn.btn-default id='play-button'
|
||||
span.fa.fa-play
|
||||
#submissions-slider.flex-item
|
||||
input type='range' orient='horizontal' list='datapoints' min=0 max=submissions.length-1 value=0
|
||||
input type='range' orient='horizontal' list='datapoints' min=0 max=@submissions.length-1 value=0
|
||||
datalist#datapoints
|
||||
- index=0
|
||||
- submissions.each do |submission|
|
||||
- @submissions.each do |submission|
|
||||
option data-submission=submission
|
||||
=index
|
||||
- index += 1
|
||||
- working_times_until = Array.new
|
||||
#timeline
|
||||
.table-responsive
|
||||
table.table
|
||||
@ -43,28 +39,27 @@ h1 = "#{@exercise} (external user #{@external_user})"
|
||||
- ['.time', '.cause', '.score', '.tests', '.time_difference'].each do |title|
|
||||
th.header = t(title)
|
||||
tbody
|
||||
- deltas = submissions.map.with_index {|item, index| delta = item.created_at - submissions[index - 1].created_at if index > 0; if delta == nil or delta > 10*60 then 0 else delta end}
|
||||
- submissions_and_interventions.each_with_index do |submission_or_intervention, index|
|
||||
tr data-id=submission_or_intervention.id
|
||||
td.clickable = submission_or_intervention.created_at.strftime("%F %T")
|
||||
- if submission_or_intervention.is_a?(Submission)
|
||||
td = submission_or_intervention.cause
|
||||
td = submission_or_intervention.score
|
||||
- @submissions_and_interventions.each_with_index do |this, index|
|
||||
- highlight = (index > 0 and @working_times_until[index] == @working_times_until[index - 1] and this.created_at > @submissions_and_interventions[index - 1].created_at)
|
||||
tr data-id=this.id class=('highlight' if highlight)
|
||||
td.clickable = this.created_at.strftime("%F %T")
|
||||
- if this.is_a?(Submission)
|
||||
td = this.cause
|
||||
td = this.score
|
||||
td
|
||||
-submission_or_intervention.testruns.each do |run|
|
||||
-this.testruns.each do |run|
|
||||
- if run.passed
|
||||
.unit-test-result.positive-result title=run.output
|
||||
- else
|
||||
.unit-test-result.unknown-result title=run.output
|
||||
td = Time.at(deltas[1..index].inject(:+)).utc.strftime("%H:%M:%S") if index > 0
|
||||
-working_times_until.push((Time.at(deltas[1..index].inject(:+)).utc.strftime("%H:%M:%S") if index > 0))
|
||||
- elsif submission_or_intervention.is_a? UserExerciseIntervention
|
||||
td = submission_or_intervention.intervention.name
|
||||
td = @working_times_until[index] if index > 0
|
||||
- elsif this.is_a? UserExerciseIntervention
|
||||
td = this.intervention.name
|
||||
td =
|
||||
td =
|
||||
td =
|
||||
p = t('.addendum')
|
||||
.hidden#wtimes data-working_times=ActiveSupport::JSON.encode(working_times_until);
|
||||
.hidden#wtimes data-working_times=ActiveSupport::JSON.encode(@working_times_until);
|
||||
div#progress_chart.col-lg-12
|
||||
.graph-functions-2
|
||||
|
||||
|
@ -43,10 +43,20 @@
|
||||
<% output_runs = testruns.select { |run| run.cause == 'run' } %>
|
||||
<% if output_runs.size > 0 %>
|
||||
<h5><%= t('request_for_comments.runtime_output') %></h5>
|
||||
<div class="testrun-output text">
|
||||
<span class="fa fa-chevron-up collapse-button"></span>
|
||||
<div class="collapsed testrun-output text">
|
||||
<span class="fa fa-chevron-down collapse-button"></span>
|
||||
<% output_runs.each do |testrun| %>
|
||||
<pre><%= testrun.try(:output) or t('request_for_comments.no_output') %></pre>
|
||||
<%
|
||||
output = testrun.try(:output)
|
||||
if output
|
||||
messages = output.scan(/{(?:(?:".+?":".+?")+?,?)+}/)
|
||||
messages.map! {|el| JSON.parse(el)}
|
||||
messages.keep_if {|message| message['cmd'] == 'write'}
|
||||
messages.map! {|message| message['data']}
|
||||
output = messages.join ''
|
||||
end
|
||||
%>
|
||||
<pre><%= output or t('request_for_comments.no_output') %></pre>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
@ -56,7 +66,13 @@
|
||||
<h5><%= t('request_for_comments.test_results') %></h5>
|
||||
<div class="testrun-assess-results">
|
||||
<% assess_runs.each do |testrun| %>
|
||||
<div class="result <%= testrun.passed ? 'passed' : 'failed' %>" title="<%= testrun.output %>"></div>
|
||||
<div class="testrun-container">
|
||||
<div class="result <%= testrun.passed ? 'passed' : 'failed' %>"></div>
|
||||
<div class="collapsed testrun-output text">
|
||||
<span class="fa fa-chevron-down collapse-button"></span>
|
||||
<pre><%= testrun.output or t('request_for_comments.no_output')%></pre>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
@ -366,7 +366,7 @@ de:
|
||||
score: Punktzahl
|
||||
tests: Unit Tests
|
||||
time_difference: 'Arbeitszeit bis hier*'
|
||||
addendum: '* Differenzen von mehr als 30 Minuten werden ignoriert.'
|
||||
addendum: '* Differenzen von mehr als 10 Minuten werden ignoriert.'
|
||||
proxy_exercises:
|
||||
index:
|
||||
clone: Duplizieren
|
||||
@ -770,3 +770,9 @@ de:
|
||||
to: "Bis"
|
||||
interval: "Intervall"
|
||||
update: "Aktualisieren"
|
||||
navigation:
|
||||
sections:
|
||||
errors: "Fehler"
|
||||
files: "Dateien"
|
||||
users: "Benutzer"
|
||||
integrations: "Integrationen"
|
||||
|
@ -366,7 +366,7 @@ en:
|
||||
score: Score
|
||||
tests: Unit Test Results
|
||||
time_difference: 'Working Time until here*'
|
||||
addendum: '* Deltas longer than 30 minutes are ignored.'
|
||||
addendum: '* Deltas longer than 10 minutes are ignored.'
|
||||
proxy_exercises:
|
||||
index:
|
||||
clone: Duplicate
|
||||
@ -770,3 +770,9 @@ en:
|
||||
to: "To"
|
||||
interval: "Interval"
|
||||
update: "Update"
|
||||
navigation:
|
||||
sections:
|
||||
errors: "Errors"
|
||||
files: "Files"
|
||||
users: "Users"
|
||||
integrations: "Integrations"
|
||||
|
Reference in New Issue
Block a user