merged qa into codeocean

This commit is contained in:
Nicholas Wittstruck
2015-03-27 21:27:40 +01:00
parent de154a6f66
commit bad98cca1d
5 changed files with 194 additions and 92 deletions

View File

@ -13,8 +13,8 @@ $(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 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>'
@ -128,9 +128,22 @@ $(function() {
var evaluateCodeWithStreamedResponse = function(url, callback) { var evaluateCodeWithStreamedResponse = function(url, callback) {
var event_source = new EventSource(url); var event_source = new EventSource(url);
event_source.addEventListener('close', function(event) {
event_source.close();
hideSpinner();
running = false;
toggleButtonStates();
if (JSON.parse(event.data).code !== 200) {
ajaxError();
showTab(1);
}
event_source.addEventListener('close', closeEventSource); if (qa_api) {
event_source.addEventListener('error', closeEventSource); qa_api.executeCommand('syncOutput', [chunkBuffer]);
chunkBuffer = [{streamedResponse: true}];
}
});
event_source.addEventListener('error', ajaxError);
event_source.addEventListener('hint', renderHint); event_source.addEventListener('hint', renderHint);
event_source.addEventListener('info', storeContainerInformation); event_source.addEventListener('info', storeContainerInformation);
event_source.addEventListener('output', callback); event_source.addEventListener('output', callback);
@ -264,6 +277,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 +292,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);
@ -500,10 +529,15 @@ $(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();
}; };
var isActiveFileRunnable = function() { var isActiveFileRunnable = function() {
return isActiveFileExecutable() && ['main_file', 'user_defined_file'].includes(active_frame.data('role')); return isActiveFileExecutable() && ['main_file', 'user_defined_file'].includes(active_frame.data('role'));
}; };
@ -532,10 +566,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();
@ -575,6 +616,10 @@ $(function() {
printOutput(result, false, index); printOutput(result, false, index);
printScoringResult(result, index); printScoringResult(result, index);
}); });
if (qa_api) {
// send test response to QA
qa_api.executeCommand('syncOutput', [response]);
}
}; };
var renderCode = function(event) { var renderCode = function(event) {
@ -698,10 +743,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();
@ -834,18 +876,52 @@ $(function() {
}; };
if ($('#editor').isPresent()) { if ($('#editor').isPresent()) {
if (isBrowserSupported()) { var qa_api;
$('.score, #development-environment').show(); if ($('#questions-column').isPresent() && QaApi.isBrowserSupported()) {
$('#editor-column').addClass('col-md-8').removeClass('col-md-10');
$('#questions-column').addClass('col-md-3');
var node = document.getElementById('questions-holder');
var url = $('#questions-holder').data('url');
var qa_api = new QaApi(node, url);
}
configureEditors(); configureEditors();
initializeEditors(); initializeEditors(qa_api);
initializeEventHandlers(); initializeEventHandlers();
initializeFileTree(); initializeFileTree();
initializeTooltips(); initializeTooltips();
renderScore(); renderScore();
showFirstFile(); showMainFile();
showRequestedTab(); showRequestedTab();
} else {
$('#alert').show();
} }
var stderrOutput = ''
var handleStderrOutputForFlowr = function(event) {
var json = JSON.parse(event.data);
if (json.stderr) {
stderrOutput += json.stderr;
} else if (json.code) {
var flowrHintBody = $('#flowrHint .panel-body')
jQuery.getJSON(flowrUrl + '&query=' + escape(stderrOutput), function(data) {
for (var question in data.queryResults) {
// replace everything, not only one occurence
var collapsibleTileHtml = flowrResultHtml.replace(/{{collapseId}}/g, 'collapse-' + question).replace(/{{headingId}}/g, 'heading-' + question)
var resultTile = $(collapsibleTileHtml)
resultTile.find('h4 > a').text(data.queryResults[question].title)
resultTile.find('.panel-body').append($(data.queryResults[question].body))
flowrHintBody.append(resultTile)
} }
$('#flowrHint').fadeIn()
})
stderrOutput = ''
}
};
}); });

View File

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

View File

@ -1,3 +1,5 @@
.row
#editor-column.col-md-10.col-md-offset-1
h1 = @exercise h1 = @exercise
span.badge.pull-right.score span.badge.pull-right.score
@ -71,3 +73,7 @@ p.lead = @exercise.description
.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

View File

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

View File

@ -1,3 +1,5 @@
test: test:
flowr: flowr:
enabled: false enabled: false
code_pilot:
enabled: false