@ -13,9 +13,10 @@ $(function() {
|
|||||||
var THEME = 'ace/theme/textmate';
|
var THEME = 'ace/theme/textmate';
|
||||||
|
|
||||||
var editors = [];
|
var editors = [];
|
||||||
var active_file;
|
var active_file = undefined;
|
||||||
var active_frame;
|
var active_frame = undefined;
|
||||||
var running = false;
|
var running = false;
|
||||||
|
var qa_api = undefined;
|
||||||
|
|
||||||
var flowrResultHtml = '<div class="panel panel-default"><div id="{{headingId}}" role="tab" class="panel-heading"><h4 class="panel-title"><a data-toggle="collapse" data-parent="#flowrHint" href="#{{collapseId}}" aria-expanded="true" aria-controls="{{collapseId}}"></a></h4></div><div id="{{collapseId}}" role="tabpanel" aria-labelledby="{{headingId}}" class="panel-collapse collapse"><div class="panel-body"></div></div></div>'
|
var flowrResultHtml = '<div class="panel panel-default"><div id="{{headingId}}" role="tab" class="panel-heading"><h4 class="panel-title"><a data-toggle="collapse" data-parent="#flowrHint" href="#{{collapseId}}" aria-expanded="true" aria-controls="{{collapseId}}"></a></h4></div><div id="{{collapseId}}" role="tabpanel" aria-labelledby="{{headingId}}" class="panel-collapse collapse"><div class="panel-body"></div></div></div>'
|
||||||
|
|
||||||
@ -141,11 +142,20 @@ $(function() {
|
|||||||
event_source.addEventListener('close', handleStderrOutputForFlowr);
|
event_source.addEventListener('close', handleStderrOutputForFlowr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (qa_api) {
|
||||||
|
event_source.addEventListener('close', handleStreamedResponseForCodePilot);
|
||||||
|
}
|
||||||
|
|
||||||
event_source.addEventListener('status', function(event) {
|
event_source.addEventListener('status', function(event) {
|
||||||
showStatus(JSON.parse(event.data));
|
showStatus(JSON.parse(event.data));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var handleStreamedResponseForCodePilot = function(event) {
|
||||||
|
qa_api.executeCommand('syncOutput', [chunkBuffer]);
|
||||||
|
chunkBuffer = [{streamedResponse: true}];
|
||||||
|
}
|
||||||
|
|
||||||
var evaluateCodeWithoutStreamedResponse = function(url, callback) {
|
var evaluateCodeWithoutStreamedResponse = function(url, callback) {
|
||||||
var jqxhr = ajax({
|
var jqxhr = ajax({
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
@ -264,6 +274,9 @@ $(function() {
|
|||||||
var handleTestResponse = function(response) {
|
var handleTestResponse = function(response) {
|
||||||
clearOutput();
|
clearOutput();
|
||||||
printOutput(response[0], false, 0);
|
printOutput(response[0], false, 0);
|
||||||
|
if (qa_api) {
|
||||||
|
qa_api.executeCommand('syncOutput', [response]);
|
||||||
|
}
|
||||||
showStatus(response[0]);
|
showStatus(response[0]);
|
||||||
showTab(2);
|
showTab(2);
|
||||||
};
|
};
|
||||||
@ -276,6 +289,19 @@ $(function() {
|
|||||||
var initializeEditors = function() {
|
var initializeEditors = function() {
|
||||||
$('.editor').each(function(index, element) {
|
$('.editor').each(function(index, element) {
|
||||||
var editor = ace.edit(element);
|
var editor = ace.edit(element);
|
||||||
|
if (qa_api) {
|
||||||
|
editor.getSession().on("change", function (deltaObject) {
|
||||||
|
qa_api.executeCommand('syncEditor', [active_file, deltaObject]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var document = editor.getSession().getDocument();
|
||||||
|
// insert pre-existing code into editor. we have to use insertLines, otherwise the deltas are not properly added
|
||||||
|
var file_id = $(element).data('file-id');
|
||||||
|
var content = $('.editor-content[data-file-id=' + file_id + ']');
|
||||||
|
setActiveFile($(element).parent().data('filename'), file_id);
|
||||||
|
|
||||||
|
document.insertLines(0, content.text().split(/\n/));
|
||||||
editor.setReadOnly($(element).data('read-only') !== undefined);
|
editor.setReadOnly($(element).data('read-only') !== undefined);
|
||||||
editor.setShowPrintMargin(false);
|
editor.setShowPrintMargin(false);
|
||||||
editor.setTheme(THEME);
|
editor.setTheme(THEME);
|
||||||
@ -498,6 +524,13 @@ $(function() {
|
|||||||
return 'executable' in active_frame.data();
|
return 'executable' in active_frame.data();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var setActiveFile = function (filename, fileId) {
|
||||||
|
active_file = {
|
||||||
|
filename: filename,
|
||||||
|
id: fileId
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
var isActiveFileRenderable = function() {
|
var isActiveFileRenderable = function() {
|
||||||
return 'renderable' in active_frame.data();
|
return 'renderable' in active_frame.data();
|
||||||
};
|
};
|
||||||
@ -530,10 +563,17 @@ $(function() {
|
|||||||
panel.find('.row .col-sm-9').eq(3).find('a').attr('href', '#output-' + index);
|
panel.find('.row .col-sm-9').eq(3).find('a').attr('href', '#output-' + index);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var chunkBuffer = [{streamedResponse: true}];
|
||||||
|
|
||||||
var printChunk = function(event) {
|
var printChunk = function(event) {
|
||||||
var output = JSON.parse(event.data);
|
var output = JSON.parse(event.data);
|
||||||
if (output) {
|
if (output) {
|
||||||
printOutput(output, true, 0);
|
printOutput(output, true, 0);
|
||||||
|
// send test response to QA
|
||||||
|
// we are expecting an array of outputs:
|
||||||
|
if (qa_api) {
|
||||||
|
chunkBuffer.push(output);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
clearOutput();
|
clearOutput();
|
||||||
$('#hint').fadeOut();
|
$('#hint').fadeOut();
|
||||||
@ -580,6 +620,10 @@ $(function() {
|
|||||||
})) {
|
})) {
|
||||||
showTimeoutMessage();
|
showTimeoutMessage();
|
||||||
}
|
}
|
||||||
|
if (qa_api) {
|
||||||
|
// send test response to QA
|
||||||
|
qa_api.executeCommand('syncOutput', [response]);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var renderCode = function(event) {
|
var renderCode = function(event) {
|
||||||
@ -703,10 +747,7 @@ $(function() {
|
|||||||
var showFirstFile = function() {
|
var showFirstFile = function() {
|
||||||
var frame = $('.frame[data-role="main_file"]').isPresent() ? $('.frame[data-role="main_file"]') : $('.frame').first();
|
var frame = $('.frame[data-role="main_file"]').isPresent() ? $('.frame[data-role="main_file"]') : $('.frame').first();
|
||||||
var file_id = frame.find('.editor').data('file-id');
|
var file_id = frame.find('.editor').data('file-id');
|
||||||
active_file = {
|
setActiveFile(frame.data('filename'), file_id);
|
||||||
filename: frame.data('filename'),
|
|
||||||
id: file_id
|
|
||||||
};
|
|
||||||
$('#files').jstree().select_node(file_id);
|
$('#files').jstree().select_node(file_id);
|
||||||
showFrame(frame);
|
showFrame(frame);
|
||||||
toggleButtonStates();
|
toggleButtonStates();
|
||||||
@ -865,19 +906,32 @@ $(function() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($('#editor').isPresent()) {
|
var initializeCodePilot = function() {
|
||||||
if (isBrowserSupported()) {
|
if ($('#questions-column').isPresent() && QaApi.isBrowserSupported()) {
|
||||||
$('.score, #development-environment').show();
|
$('#editor-column').addClass('col-md-8').removeClass('col-md-10');
|
||||||
configureEditors();
|
$('#questions-column').addClass('col-md-3');
|
||||||
initializeEditors();
|
|
||||||
initializeEventHandlers();
|
var node = document.getElementById('questions-holder');
|
||||||
initializeFileTree();
|
var url = $('#questions-holder').data('url');
|
||||||
initializeTooltips();
|
|
||||||
renderScore();
|
qa_api = new QaApi(node, url);
|
||||||
showFirstFile();
|
}
|
||||||
showRequestedTab();
|
|
||||||
} else {
|
|
||||||
$('#alert').show();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($('#editor').isPresent()) {
|
||||||
|
if (isBrowserSupported()) {
|
||||||
|
initializeCodePilot();
|
||||||
|
$('.score, #development-environment').show();
|
||||||
|
configureEditors();
|
||||||
|
initializeEditors();
|
||||||
|
initializeEventHandlers();
|
||||||
|
initializeFileTree();
|
||||||
|
initializeTooltips();
|
||||||
|
renderScore();
|
||||||
|
showFirstFile();
|
||||||
|
showRequestedTab();
|
||||||
|
} else {
|
||||||
|
$('#alert').show();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -86,6 +86,12 @@ class ExercisesController < ApplicationController
|
|||||||
@submission = current_user.submissions.where(exercise_id: @exercise.id).order('created_at DESC').first
|
@submission = current_user.submissions.where(exercise_id: @exercise.id).order('created_at DESC').first
|
||||||
@files = (@submission ? @submission.collect_files : @exercise.files).select(&:visible).sort_by(&:name_with_extension)
|
@files = (@submission ? @submission.collect_files : @exercise.files).select(&:visible).sort_by(&:name_with_extension)
|
||||||
@paths = collect_paths(@files)
|
@paths = collect_paths(@files)
|
||||||
|
|
||||||
|
if current_user.respond_to? :external_id
|
||||||
|
@user_id = current_user.external_id
|
||||||
|
else
|
||||||
|
@user_id = '00000001-3100-4444-9999-000000000001'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def index
|
def index
|
||||||
|
@ -2,4 +2,17 @@ module ExerciseHelper
|
|||||||
def embedding_parameters(exercise)
|
def embedding_parameters(exercise)
|
||||||
"locale=#{I18n.locale}&token=#{exercise.token}"
|
"locale=#{I18n.locale}&token=#{exercise.token}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def qa_js_tag
|
||||||
|
javascript_include_tag qa_url + "/assets/qa_api.js"
|
||||||
|
end
|
||||||
|
|
||||||
|
def qa_url
|
||||||
|
config = CodeOcean::Config.new(:code_ocean)
|
||||||
|
enabled = config.read[:code_pilot][:enabled]
|
||||||
|
|
||||||
|
if enabled
|
||||||
|
config.read[:code_pilot][:url]
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -11,4 +11,5 @@
|
|||||||
- else
|
- else
|
||||||
= link_to(file.native_file.file.name_with_extension, file.native_file.url)
|
= link_to(file.native_file.file.name_with_extension, file.native_file.url)
|
||||||
- else
|
- else
|
||||||
.editor data-file-id=file.ancestor_id data-indent-size=file.file_type.indent_size data-mode=file.file_type.editor_mode data-read-only=file.read_only = file.content
|
.editor-content.hidden data-file-id=file.ancestor_id = file.content
|
||||||
|
.editor data-file-id=file.ancestor_id data-indent-size=file.file_type.indent_size data-mode=file.file_type.editor_mode data-read-only=file.read_only
|
||||||
|
@ -1,73 +1,79 @@
|
|||||||
h1 = @exercise
|
.row
|
||||||
|
#editor-column.col-md-10.col-md-offset-1
|
||||||
|
h1 = @exercise
|
||||||
|
|
||||||
span.badge.pull-right.score
|
span.badge.pull-right.score
|
||||||
|
|
||||||
p.lead = @exercise.description
|
p.lead = @exercise.description
|
||||||
|
|
||||||
#alert.alert.alert-danger role='alert'
|
#alert.alert.alert-danger role='alert'
|
||||||
h4 = t('.alert.title')
|
h4 = t('.alert.title')
|
||||||
p = t('.alert.text', application_name: application_name)
|
p = t('.alert.text', application_name: application_name)
|
||||||
|
|
||||||
#development-environment
|
#development-environment
|
||||||
ul.nav.nav-justified.nav-tabs role='tablist'
|
ul.nav.nav-justified.nav-tabs role='tablist'
|
||||||
li.active
|
li.active
|
||||||
a data-placement='top' data-toggle='tab' data-tooltip=true href='#instructions' role='tab' title=t('shared.tooltips.shortcut', shortcut: 'ALT + 1')
|
a data-placement='top' data-toggle='tab' data-tooltip=true href='#instructions' role='tab' title=t('shared.tooltips.shortcut', shortcut: 'ALT + 1')
|
||||||
i.fa.fa-question
|
i.fa.fa-question
|
||||||
= t('activerecord.attributes.exercise.instructions')
|
= t('activerecord.attributes.exercise.instructions')
|
||||||
li
|
li
|
||||||
a data-placement='top' data-toggle='tab' data-tooltip=true href='#workspace' role='tab' title=t('shared.tooltips.shortcut', shortcut: 'ALT + 2')
|
a data-placement='top' data-toggle='tab' data-tooltip=true href='#workspace' role='tab' title=t('shared.tooltips.shortcut', shortcut: 'ALT + 2')
|
||||||
i.fa.fa-code
|
i.fa.fa-code
|
||||||
= t('.workspace')
|
= t('.workspace')
|
||||||
li
|
li
|
||||||
a data-placement='top' data-toggle='tab' data-tooltip=true href='#outputInformation' role='tab' title=t('shared.tooltips.shortcut', shortcut: 'ALT + 3')
|
a data-placement='top' data-toggle='tab' data-tooltip=true href='#outputInformation' role='tab' title=t('shared.tooltips.shortcut', shortcut: 'ALT + 3')
|
||||||
i.fa.fa-terminal
|
i.fa.fa-terminal
|
||||||
= t('.output')
|
= t('.output')
|
||||||
li
|
li
|
||||||
a data-placement='top' data-toggle='tab' data-tooltip=true href='#progress' role='tab' title=t('shared.tooltips.shortcut', shortcut: 'ALT + 4')
|
a data-placement='top' data-toggle='tab' data-tooltip=true href='#progress' role='tab' title=t('shared.tooltips.shortcut', shortcut: 'ALT + 4')
|
||||||
i.fa.fa-line-chart
|
i.fa.fa-line-chart
|
||||||
= t('.progress')
|
= t('.progress')
|
||||||
|
|
||||||
hr
|
hr
|
||||||
|
|
||||||
.tab-content
|
.tab-content
|
||||||
#instructions.tab-pane.active
|
#instructions.tab-pane.active
|
||||||
p = render_markdown(@exercise.instructions)
|
p = render_markdown(@exercise.instructions)
|
||||||
br
|
br
|
||||||
p.text-center
|
p.text-center
|
||||||
a#start.btn.btn-lg.btn-success
|
a#start.btn.btn-lg.btn-success
|
||||||
i.fa.fa-code
|
i.fa.fa-code
|
||||||
= t('.start')
|
= t('.start')
|
||||||
#workspace.tab-pane = render('editor', exercise: @exercise, files: @files, submission: @submission)
|
#workspace.tab-pane = render('editor', exercise: @exercise, files: @files, submission: @submission)
|
||||||
#outputInformation.tab-pane data-message-no-output=t('.no_output')
|
#outputInformation.tab-pane data-message-no-output=t('.no_output')
|
||||||
#hint
|
#hint
|
||||||
.panel.panel-warning
|
.panel.panel-warning
|
||||||
.panel-heading = t('.hint')
|
.panel-heading = t('.hint')
|
||||||
.panel-body
|
.panel-body
|
||||||
#output
|
#output
|
||||||
pre = t('.no_output_yet')
|
pre = t('.no_output_yet')
|
||||||
- if CodeOcean::Config.new(:code_ocean).read[:flowr][:enabled]
|
- 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'
|
#flowrHint.panel.panel-info data-url=CodeOcean::Config.new(:code_ocean).read[:flowr][:url] role='tab'
|
||||||
.panel-heading = 'Gain more insights here'
|
.panel-heading = 'Gain more insights here'
|
||||||
.panel-body
|
.panel-body
|
||||||
#progress.tab-pane
|
#progress.tab-pane
|
||||||
#results
|
#results
|
||||||
h2 = t('.results')
|
h2 = t('.results')
|
||||||
p.test-count == t('.test_count', count: 0)
|
p.test-count == t('.test_count', count: 0)
|
||||||
ul.list-unstyled
|
ul.list-unstyled
|
||||||
ul#dummies.hidden.list-unstyled
|
ul#dummies.hidden.list-unstyled
|
||||||
li.panel.panel-default
|
li.panel.panel-default
|
||||||
.panel-heading
|
.panel-heading
|
||||||
h3.panel-title == t('.file', filename: '', number: 0)
|
h3.panel-title == t('.file', filename: '', number: 0)
|
||||||
.panel-body
|
.panel-body
|
||||||
= row(label: '.passed_tests', value: t('shared.out_of', maximum_value: 0, value: 0).html_safe)
|
= row(label: '.passed_tests', value: t('shared.out_of', maximum_value: 0, value: 0).html_safe)
|
||||||
= row(label: 'activerecord.attributes.submission.score', value: t('shared.out_of', maximum_value: 0, value: 0).html_safe)
|
= row(label: 'activerecord.attributes.submission.score', value: t('shared.out_of', maximum_value: 0, value: 0).html_safe)
|
||||||
= row(label: '.feedback')
|
= row(label: '.feedback')
|
||||||
= row(label: '.output', value: link_to(t('shared.show'), '#'))
|
= row(label: '.output', value: link_to(t('shared.show'), '#'))
|
||||||
#score data-maximum-score=@exercise.maximum_score data-score=@submission.try(:score)
|
#score data-maximum-score=@exercise.maximum_score data-score=@submission.try(:score)
|
||||||
h4
|
h4
|
||||||
span == "#{t('activerecord.attributes.submission.score')}: "
|
span == "#{t('activerecord.attributes.submission.score')}: "
|
||||||
span.score
|
span.score
|
||||||
.progress
|
.progress
|
||||||
.progress-bar role='progressbar'
|
.progress-bar role='progressbar'
|
||||||
br
|
br
|
||||||
p.text-center = render('editor_button', classes: 'btn-lg btn-success', data: {:'data-message-confirm' => t('exercises.editor.confirm_submit'), :'data-url' => submit_exercise_path(@exercise)}, icon: 'fa fa-send', id: 'submit', label: t('exercises.editor.submit'))
|
p.text-center = render('editor_button', classes: 'btn-lg btn-success', data: {:'data-message-confirm' => t('exercises.editor.confirm_submit'), :'data-url' => submit_exercise_path(@exercise)}, icon: 'fa fa-send', id: 'submit', label: t('exercises.editor.submit'))
|
||||||
|
- if qa_url
|
||||||
|
#questions-column
|
||||||
|
#questions-holder data-url="#{qa_url}/qa/index/#{@exercise.id}/#{@user_id}"
|
||||||
|
= qa_js_tag
|
@ -34,6 +34,12 @@ html lang='en'
|
|||||||
.container data-controller=controller_name
|
.container data-controller=controller_name
|
||||||
= render('breadcrumbs')
|
= render('breadcrumbs')
|
||||||
= render('flash')
|
= render('flash')
|
||||||
= yield
|
- if (controller_name == "exercises" && action_name == "implement")
|
||||||
|
.container-fluid
|
||||||
|
= yield
|
||||||
|
- else
|
||||||
|
.container
|
||||||
|
= yield
|
||||||
|
|
||||||
- template_variables = {execution_environment: @exercise.execution_environment} if action_name == 'implement'
|
- template_variables = {execution_environment: @exercise.execution_environment} if action_name == 'implement'
|
||||||
= render('shared/modal', classes: 'modal-lg', id: 'modal-help', template: 'application/help', template_variables: template_variables, title: t('shared.help.headline'))
|
= render('shared/modal', classes: 'modal-lg', id: 'modal-help', template: 'application/help', template_variables: template_variables, title: t('shared.help.headline'))
|
||||||
|
@ -1,11 +1,16 @@
|
|||||||
default: &default
|
default: &default
|
||||||
flowr:
|
flowr:
|
||||||
enabled: false
|
enabled: false
|
||||||
|
code_pilot:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
development:
|
development:
|
||||||
flowr:
|
flowr:
|
||||||
enabled: true
|
enabled: true
|
||||||
url: http://example.org:3000/api/exceptioninfo?id=&lang=auto
|
url: http://example.org:3000/api/exceptioninfo?id=&lang=auto
|
||||||
|
code_pilot:
|
||||||
|
enabled: true
|
||||||
|
url: //localhost:3000
|
||||||
|
|
||||||
production:
|
production:
|
||||||
<<: *default
|
<<: *default
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
test:
|
test:
|
||||||
flowr:
|
flowr:
|
||||||
enabled: false
|
enabled: false
|
||||||
|
code_pilot:
|
||||||
|
enabled: false
|
Reference in New Issue
Block a user