merged master into disable_rfcs

This commit is contained in:
Ralf Teusner
2018-05-16 17:44:28 +02:00
82 changed files with 2007 additions and 376 deletions

View File

@@ -7,9 +7,11 @@
ul.dropdown-menu role='menu'
- if current_user.admin?
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])

View File

@@ -0,0 +1,2 @@
- if policy(model).index?
li = link_to(model.model_name.human(count: 2), send(:"#{model.model_name.collection}_path"))

View 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)

View File

@@ -16,4 +16,5 @@
.form-group
= f.label(:hint)
= f.text_field(:hint, class: 'form-control')
.help-block == t('error_templates.hints.hint_templates')
.actions = render('shared/submit_button', f: f, object: @error_template)

View File

@@ -1,11 +1,18 @@
- exercises = Exercise.order(:title)
- users = InternalUser.order(:name)
= form_for(@exercise_collection, data: {exercises: exercises}, multipart: true) do |f|
= form_for(@exercise_collection, data: {exercises: exercises, users: users}, multipart: true) do |f|
= render('shared/form_errors', object: @exercise_collection)
.form-group
= f.label(:name)
= f.label(t('activerecord.attributes.exercise_collections.name'))
= f.text_field(:name, class: 'form-control', required: true)
.form-group
= f.label(:exercises)
= f.label(t('activerecord.attributes.exercise_collections.use_anomaly_detection'))
= f.check_box(:use_anomaly_detection, {class: 'form-control'})
.form-group
= f.label(t('activerecord.attributes.exercise_collections.user'))
= f.collection_select(:user_id, users, :id, :name, {}, {class: 'form-control'})
.form-group
= f.label(t('activerecord.attributes.exercise_collections.exercises'))
= f.collection_select(:exercise_ids, exercises, :id, :title, {}, {class: 'form-control', multiple: true})
.actions = render('shared/submit_button', f: f, object: @exercise_collection)

View File

@@ -8,7 +8,7 @@ h1 = ExerciseCollection.model_name.human(count: 2)
th = t('activerecord.attributes.exercise_collections.name')
th = t('activerecord.attributes.exercise_collections.updated_at')
th = t('activerecord.attributes.exercise_collections.exercises')
th colspan=3 = t('shared.actions')
th colspan=4 = t('shared.actions')
tbody
- @exercise_collections.each do |collection|
tr
@@ -18,6 +18,7 @@ h1 = ExerciseCollection.model_name.human(count: 2)
td = collection.exercises.size
td = link_to(t('shared.show'), collection)
td = link_to(t('shared.edit'), edit_exercise_collection_path(collection))
td = link_to(t('shared.statistics'), statistics_exercise_collection_path(collection))
td = link_to(t('shared.destroy'), collection, data: {confirm: t('shared.confirm_destroy')}, method: :delete)
= render('shared/pagination', collection: @exercise_collections)

View File

@@ -3,9 +3,25 @@ h1
= render('shared/edit_button', object: @exercise_collection)
= row(label: 'exercise_collections.name', value: @exercise_collection.name)
= row(label: 'exercise_collections.user', value: link_to(@exercise_collection.user.name, @exercise_collection.user)) unless @exercise_collection.user.nil?
= row(label: 'exercise_collections.use_anomaly_detection', value: @exercise_collection.use_anomaly_detection)
= row(label: 'exercise_collections.updated_at', value: @exercise_collection.updated_at)
h4 = t('activerecord.attributes.exercise_collections.exercises')
ul.list-unstyled
- @exercise_collection.exercises.sort_by{|c| c.title}.each do |exercise|
li = link_to(exercise, exercise)
.table-responsive
table.table
thead
tr
th = t('activerecord.attributes.exercise.title')
th = t('activerecord.attributes.exercise.execution_environment')
th = t('activerecord.attributes.exercise.user')
th = t('shared.actions')
tbody
- @exercises.sort_by{|c| c.title}.each do |exercise|
tr
td = link_to(exercise.title, exercise)
td = link_to_if(exercise.execution_environment && policy(exercise.execution_environment).show?, exercise.execution_environment, exercise.execution_environment)
td = exercise.user.name
td = link_to(t('shared.statistics'), statistics_exercise_path(exercise))
= render('shared/pagination', collection: @exercises)

View File

@@ -0,0 +1,17 @@
h1 = @exercise_collection
= row(label: 'exercise_collections.name', value: @exercise_collection.name)
= row(label: 'exercise_collections.updated_at', value: @exercise_collection.updated_at)
= row(label: 'exercise_collections.exercises', value: @exercise_collection.exercises.count)
= row(label: 'exercises.statistics.average_worktime', value: @exercise_collection.average_working_time.round(3).to_s + 's')
#graph
#data.hidden(data-working-times=ActiveSupport::JSON.encode(@exercise_collection.exercise_working_times) data-average-working-time=@exercise_collection.average_working_time)
#legend
- {time: t('exercises.statistics.average_worktime'),
min: 'min. anomaly threshold',
avg: 'average time',
max: 'max. anomaly threshold'}.each_pair do |klass, label|
.legend-entry
div(class="box #{klass}")
.box-label = label

View File

@@ -47,9 +47,12 @@ div id='output_sidebar_uncollapsed' class='hidden col-sm-12 enforce-bottom-margi
input#prompt-input.form-control type='text'
span.input-group-btn
button#prompt-submit.btn.btn-primary type="button" = t('exercises.editor.send')
#error-hints
.heading = t('exercises.implement.error_hints.heading')
ul.body
#output
pre = t('exercises.implement.no_output_yet')
- if CodeOcean::Config.new(:code_ocean).read[:flowr][:enabled]
#flowrHint.panel.panel-info data-url=CodeOcean::Config.new(:code_ocean).read[:flowr][:url] role='tab'
.panel-heading = 'Gain more insights here'
.panel-body
.panel-body

View File

@@ -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

View File

@@ -2,9 +2,15 @@ script src="http://labratrevenge.com/d3-tip/javascripts/d3.tip.v0.6.3.js"
h1 = @exercise
= row(label: '.participants', value: @exercise.users.distinct.count)
- [:intermediate, :final].each do |scope|
= row(label: ".#{scope}_submissions") do
= "#{@exercise.submissions.send(scope).count} (#{t('.users', count: @exercise.submissions.send(scope).distinct.count(:user_id))})"
= row(label: '.finishing_rate') do
p == @exercise.finishers.count ? "#{t('shared.out_of', maximum_value: @exercise.users.distinct.count, value: @exercise.finishers.count)} #{t('exercises.statistics.external_users')}" : empty
p = progress_bar((100.0 / @exercise.users.distinct.count * @exercise.finishers.count).round(2))
= 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 = progress_bar(@exercise.average_percentage)
@@ -40,4 +46,4 @@ h1 = @exercise
td = link_to_if symbol==:external_users, label, {controller: "exercises", action: "statistics", external_user_id: user.id, id: @exercise.id}
td = us['maximum_score'] or 0
td = us['runs']
td = @exercise.average_working_time_for(user.id) or 0
td = @exercise.average_working_time_for(user.id) or 0

View File

@@ -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 %>

View File

@@ -0,0 +1,22 @@
- content_for :head do
= javascript_include_tag(asset_path('vis.min.js', type: :javascript))
= stylesheet_link_tag(asset_path('vis.min.css', type: :stylesheet))
.group
.title
h1 = t("statistics.graphs.#{resource}_activity")
.spinner
.graph id="#{resource}-activity-history"
form
.form-group
label for="from-date" = t('.from')
input type="date" class="form-control" id="from-date" name="from" value=(params[:from] || DateTime.new(2016).to_date)
.form-group
label for="to-date" = t('.to')
input type="date" class="form-control" id="to-date" name="to" value=(params[:to] || DateTime.now.to_date)
.form-group
label for="interval" = t('.interval')
select class="form-control" id="interval" name="interval"
= [:year, :quarter, :month, :day, :hour, :minute, :second].each do | key |
option selected=(key.to_s == params[:interval]) = key
button type="submit" class="btn btn-primary" = t('.update')

View File

@@ -0,0 +1,17 @@
- content_for :head do
= javascript_include_tag(asset_path('vis.min.js', type: :javascript))
= stylesheet_link_tag(asset_path('vis.min.css', type: :stylesheet))
.group
.title
h1 = t('.user_activity')
a href=statistics_graphs_user_activity_history_path = t('.history')
.spinner
.graph#user-activity
.group
.title
h1 = t('.rfc_activity')
a href=statistics_graphs_rfc_activity_history_path = t('.history')
.spinner
.graph#rfc-activity

View File

@@ -0,0 +1,12 @@
#statistics-container
- statistics_data.each do | section |
h2 = section[:name]
.statistics-wrapper data-key=section[:key]
- section[:entries].each do | entry |
a href=entry[:url]
div data-key=entry[:key]
.title = entry[:name]
.data
span = entry[:data].to_s
span.unit = entry[:unit] if entry.key? :unit

View File

@@ -0,0 +1,38 @@
== t('mailers.user_mailer.exercise_anomaly_detected.body1',
receiver_displayname: @receiver_displayname,
collection_name: @collection.name)
table(border=1)
thead
tr
td = t('activerecord.attributes.exercise.title', locale: :de)
td = t('exercises.statistics.average_worktime', locale: :de)
td = t('shared.actions', locale: :de)
tbody
- @anomalies.keys.each do | id |
- exercise = Exercise.find(id)
tr
td = link_to(exercise.title, exercise_path(exercise))
td = @anomalies[id]
td = link_to(t('shared.statistics', locale: :de), statistics_exercise_path(exercise))
== t('mailers.user_mailer.exercise_anomaly_detected.body2',
receiver_displayname: @receiver_displayname,
collection_name: @collection.name)
table(border=1)
thead
tr
td = t('activerecord.attributes.exercise.title', locale: :en)
td = t('exercises.statistics.average_worktime', locale: :en)
td = t('shared.actions', locale: :en)
tbody
- @anomalies.keys.each do | id |
- exercise = Exercise.find(id)
tr
td = link_to(exercise.title, exercise_path(exercise))
td = @anomalies[id]
td = link_to(t('shared.statistics', locale: :en), statistics_exercise_path(exercise))
== t('mailers.user_mailer.exercise_anomaly_detected.body3')

View File

@@ -0,0 +1 @@
== t('mailers.user_mailer.exercise_anomaly_needs_feedback.body', receiver_displayname: @receiver_displayname, exercise: @exercise_title, link: link_to(@link, @link))