-
-
Notifications
You must be signed in to change notification settings - Fork 26
Offline evaluation support #1434
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
043c2da
fa85250
87e07aa
153bbfa
8b18ce3
e009f18
14f45c9
be2efbb
6de3bcb
67dbaaa
088f213
5c6a170
7a7a96b
f40efbc
eaa5ace
364aba7
ff5b10c
6f58148
c35a745
01c4cef
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,7 +5,8 @@ ruby '~> 2.3' | |
|
||
gem 'puma' | ||
|
||
gem 'mumuki-domain', github: 'mumuki/mumuki-domain' | ||
gem 'mumuki-domain', github: 'mumuki/mumuki-domain', branch: 'feature-cumparsita' | ||
gem 'mulangjs', path: '../mulang/ghcjslib/gem' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not necessary in the first iteration There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Local evaluation |
||
|
||
gem 'execjs' | ||
gem 'therubyracer', platforms: :ruby | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -196,6 +196,8 @@ which are granted to be safe and stable. | |
* `setUpDeleteFiles` | ||
* `setUpDeleteFile` | ||
* `updateButtonsVisibility` | ||
* `mumuki.registerLocalTestRunner` | ||
* `mumuki.registerLocalExpectationsRunner` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK. Perhaps could be namespaced into This steps may not always be necessary for local evaluation. They should be documented There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Local evaluation |
||
* `mumuki.version` | ||
|
||
### Bridge Response Format | ||
|
@@ -204,9 +206,7 @@ which are granted to be safe and stable. | |
{ | ||
"status": "failed", | ||
"guide_finished_by_solution": false, | ||
"class_for_progress_list_item":"progress-list-item text-center danger active", | ||
"html":"...", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Although this is not strictly necessary for a first iteration, it has a lot of sense There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Pre rendering There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done here: #1435 |
||
"title_html":"...", | ||
"button_html":"...", | ||
"expectations_html":"...", | ||
"remaining_attempts_html":null, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,24 +14,15 @@ | |
//= require moment/es.js | ||
//= require moment/pt.js | ||
//= require webcomponents-lite | ||
//= require mulang | ||
//= require rails-ujs | ||
//= require turbolinks | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rollback |
||
//= require mumuki-styles | ||
//= require nprogress | ||
//= require nprogress-turbolinks | ||
//= require nprogress-ajax | ||
//= require jquery-console | ||
|
||
//= require codemirror.min | ||
//= require codemirror-simple-mode.js | ||
//= require codemirror-autorefresh | ||
//= require codemirror-modes | ||
//= require analytics | ||
//= require hotjar | ||
//= require muvment | ||
|
||
//= require_tree ./application | ||
|
||
NProgress.configure({ | ||
showSpinner: false | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,68 +1,69 @@ | ||
var mumuki = mumuki || {}; | ||
|
||
(function (mumuki) { | ||
var lastSubmission = {}; | ||
|
||
function Laboratory(exerciseId){ | ||
this.exerciseId = exerciseId; | ||
} | ||
|
||
function asString(json){ | ||
return JSON.stringify(json); | ||
} | ||
|
||
function sameAsLastSolution(newSolution){ | ||
return asString(lastSubmission.content) === asString(newSolution); | ||
} | ||
|
||
function lastSubmissionFinishedSuccessfully(){ | ||
return lastSubmission.result && lastSubmission.result.status !== 'aborted'; | ||
} | ||
|
||
function sendNewSolution(solution){ | ||
var token = new mumuki.CsrfToken(); | ||
var request = token.newRequest({ | ||
type: 'POST', | ||
url: window.location.origin + window.location.pathname + '/solutions' + window.location.search, | ||
data: solution | ||
}); | ||
|
||
return $.ajax(request).done(function (result) { | ||
lastSubmission = { content: solution, result: result }; | ||
}); | ||
} | ||
|
||
mumuki.load(function () { | ||
lastSubmission = {}; | ||
}); | ||
|
||
Laboratory.prototype = { | ||
|
||
(() => { | ||
class Laboratory { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done #1443 |
||
// ========== | ||
// Public API | ||
// ========== | ||
|
||
// Runs tests for the current exercise using the given submission | ||
// content. | ||
runTests: function(content) { | ||
return this._submitSolution({ solution: content }); | ||
}, | ||
runTests(content) { | ||
return this.runCurrentExerciseSolution({ solution: content }); | ||
} | ||
|
||
// Runs the current exercise solution, trying to get the response from cache first | ||
runCurrentExerciseSolution(solution) { | ||
const cachedSolution = mumuki.SubmissionsStore.getCachedResultFor(mumuki.currentExerciseId, solution); | ||
if(cachedSolution) { | ||
return $.Deferred().resolve(cachedSolution); | ||
} | ||
return this._runNewSolution(mumuki.currentExerciseId, solution); | ||
} | ||
|
||
// Actually sends the solution to server | ||
submitCurrentExerciseSolution(_exerciseId, solution) { // TODO use exerciseId instead of window.location | ||
const token = new mumuki.CsrfToken(); | ||
const request = token.newRequest({ | ||
type: 'POST', | ||
url: window.location.origin + window.location.pathname + '/solutions' + window.location.search, | ||
data: solution | ||
}); | ||
return $.ajax(request); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Local evaluation + local progress + local sync There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done in #1443 |
||
|
||
// =========== | ||
// Private API | ||
// =========== | ||
|
||
_submitSolution: function (solution) { | ||
if(lastSubmissionFinishedSuccessfully() && sameAsLastSolution(solution)){ | ||
return $.Deferred().resolve(lastSubmission.result); | ||
} else { | ||
return sendNewSolution(solution); | ||
_runNewSolution(exerciseId, solution){ | ||
const responsePromise = mumuki.Connection.runNewSolution(exerciseId, solution, this); | ||
return responsePromise.then((result) => { | ||
this._preRenderResult(exerciseId, result); | ||
const lastSubmission = { content: solution, result: result }; | ||
mumuki.SubmissionsStore.setLastSubmission(exerciseId, lastSubmission); | ||
return result; | ||
}); | ||
} | ||
|
||
// pre-renders some html parts of submission that are normally | ||
// generated by server, but will be empty in offline mode | ||
_preRenderResult(exerciseId, result) { | ||
// TODO defer rendering calculation. | ||
// Caching them may introduce bugs with the next-button | ||
try { | ||
const status = result.status; | ||
const exercise = mumuki.ExercisesStore.get(exerciseId); | ||
|
||
result.button_html = result.button_html || mumuki.renderButtonHtml(status); | ||
result.html = result.html || mumuki.renderCorollaryHtml(status, exercise); | ||
} catch (e) { | ||
console.log(`[Mumuki::Laboratory::Bridge] pre-rendering failed ${e}`); | ||
throw e; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Pre rendering There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done in #1435 |
||
} | ||
}, | ||
}; | ||
} | ||
|
||
} | ||
|
||
mumuki.bridge = { | ||
Laboratory: Laboratory | ||
}; | ||
|
||
}(mumuki)); | ||
})(); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -60,10 +60,17 @@ var mumuki = mumuki || {}; | |
}); | ||
} | ||
|
||
function setContent(content) { | ||
mumuki.page.editors.each(function (_, editor) { | ||
editor.getDoc().setValue(content); | ||
}); | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ??? |
||
mumuki.editor = mumuki.editor || {}; | ||
mumuki.editor.toggleFullscreen = toggleFullscreen; | ||
mumuki.editor.indentWithSpaces = indentWithSpaces; | ||
mumuki.editor.syncContent = syncContent; | ||
mumuki.editor.setContent = setContent; | ||
|
||
mumuki.page = mumuki.page || {}; | ||
mumuki.page.dynamicEditors = []; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,7 +7,7 @@ mumuki.load(function () { | |
url: $(this).data('confirmation-url'), | ||
xhrFields: {withCredentials: true}, | ||
success: function(data){ | ||
mumuki.updateProgressBarAndShowModal(data); | ||
mumuki.updateCurrentExerciseProgressBarAndShowModal(data); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rollback. Heavily backward incompatible |
||
} | ||
})); | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
(() => { | ||
|
||
const OfflineMode = new class { | ||
// Runs solution by evaluating it locally | ||
runNewSolution(exerciseId, solution, _bridge) { | ||
return mumuki.runSolutionLocally(exerciseId, solution); | ||
} | ||
|
||
// Renders progress from SubmissionsStore | ||
renderExercisesProgressBar() { | ||
$('.progress-list-item').each((_, it) => this._updateProgressListItemClass($(it))); | ||
} | ||
|
||
configureExerciseEditorValue() { | ||
const lastSubmission = mumuki.SubmissionsStore.getLastSubmission(mumuki.currentExerciseId); | ||
if (lastSubmission) { | ||
// TODO extract | ||
if (lastSubmission.content.solution) { | ||
$('#mu-custom-editor-value').val(lastSubmission.content.solution.content); | ||
} else { | ||
flbulgarelli marked this conversation as resolved.
Show resolved
Hide resolved
|
||
mumuki.editor.setContent(lastSubmission.content['solution[content]']); | ||
} | ||
} | ||
} | ||
|
||
_updateProgressListItemClass(a) { | ||
const exerciseId = a.data('mu-exercise-id'); | ||
const status = mumuki.SubmissionsStore.getLastSubmissionStatus(exerciseId); | ||
a.attr('class', mumuki.progressListItemClassForStatus(status, exerciseId == mumuki.currentExerciseId)); | ||
} | ||
} | ||
|
||
const OnlineMode = new class { | ||
// Runs solution by sending it to server | ||
runNewSolution(exerciseId, solution, bridge) { | ||
return bridge.submitCurrentExerciseSolution(exerciseId, solution); | ||
} | ||
|
||
// Does nothing. Progress is rendered by server | ||
renderExercisesProgressBar() { | ||
} | ||
|
||
// Does nothing. Editor value is configured by server | ||
configureExerciseEditorValue() { | ||
} | ||
|
||
} | ||
|
||
mumuki.goOnline = function () { | ||
mumuki.Connection = OnlineMode; | ||
}; | ||
|
||
mumuki.goOffline = function () { | ||
mumuki.Connection = OfflineMode; | ||
}; | ||
|
||
mumuki.goOnline(); | ||
})(); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
(() => { | ||
mumuki.load(() => { | ||
// Set global currentExerciseId | ||
const $muExerciseId = $('#mu-exercise-id')[0]; | ||
const $muExerciseResource = $('#mu-exercise-resource')[0]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Partially done in #1443 |
||
if ($muExerciseId) { | ||
mumuki.currentExerciseId = Number($muExerciseId.value); | ||
mumuki.ExercisesStore.saveJson(mumuki.currentExerciseId, $muExerciseResource.value); | ||
} else { | ||
mumuki.currentExerciseId = null; | ||
} | ||
}) | ||
})(); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
(() => { | ||
const ExercisesStore = new class { | ||
find(exerciseId) { | ||
const exercise = window.localStorage.getItem(this._keyFor(exerciseId)); | ||
if (!exercise) return null; | ||
return JSON.parse(exercise); | ||
} | ||
|
||
// Saves an exercise object | ||
save(exerciseId, exercise) { | ||
this.saveJson(exerciseId, JSON.stringify(exercise)); | ||
} | ||
|
||
// Saves an exercise json string | ||
saveJson(exerciseId, exerciseJson) { | ||
window.localStorage.setItem(this._keyFor(exerciseId), exerciseJson); | ||
} | ||
|
||
// Finds and existent exercise | ||
// Fails if exercise is missing | ||
get(exerciseId) { | ||
const exercise = this.find(exerciseId); | ||
if (!exercise) throw new Error(`Missing exercise ${exerciseId}`); | ||
return exercise; | ||
} | ||
|
||
_keyFor(exerciseId) { | ||
return `/exercise/${exerciseId}`; | ||
} | ||
}; | ||
mumuki.ExercisesStore = ExercisesStore; | ||
})(); |
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line only adds offline tests. We should merge them instead
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Local evaluation