Skip to content
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

Implement editing of questions #5614

Merged
merged 46 commits into from
Dec 10, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
d9f2824
Save.
tjiang11 Aug 30, 2018
f0cb957
Save.
tjiang11 Aug 30, 2018
9c8e702
Save. UrlInterpolationError topic null.
tjiang11 Aug 30, 2018
c93f186
Save.
tjiang11 Aug 30, 2018
f695dd6
Modify backend to return associated skills when fetching a question.
tjiang11 Aug 30, 2018
066faec
save.
tjiang11 Aug 30, 2018
07a11fb
Saving content works.
tjiang11 Aug 30, 2018
2386f69
Copy interaction.
tjiang11 Aug 30, 2018
4360648
Apply QuestionUpdateService for all changes.
tjiang11 Aug 30, 2018
a68adef
Circumvent race condition on initialization.
tjiang11 Aug 30, 2018
dd9d25f
Lint.
tjiang11 Aug 30, 2018
8a2c2bd
Clone UndoRedoService for questions.
tjiang11 Aug 30, 2018
4782099
Fetch question summaries after edit question.
tjiang11 Aug 30, 2018
a20b0ab
Create reusable function in QuestionEditorDirective.
tjiang11 Aug 30, 2018
4668f51
Lint.
tjiang11 Aug 30, 2018
fd91189
Add test.
tjiang11 Sep 1, 2018
2f4f1cc
Fix test.
tjiang11 Sep 1, 2018
3050886
Lint.
tjiang11 Sep 1, 2018
6d52d8c
Disable flag.
tjiang11 Sep 1, 2018
43807b1
Review changes.
tjiang11 Sep 3, 2018
7a4ff6f
Review changes.
tjiang11 Sep 9, 2018
41798c6
Review comments.
tjiang11 Sep 13, 2018
c496793
Lint.
tjiang11 Sep 13, 2018
fbdaa83
Review changes.
tjiang11 Sep 23, 2018
732be38
Deduplicate UndoRedoService.
tjiang11 Sep 23, 2018
7756f6d
Move updateFunction into QuestionUpdateService.
tjiang11 Sep 23, 2018
17ca89f
Fix test.
tjiang11 Oct 14, 2018
5785784
Lint.
tjiang11 Oct 14, 2018
13254ea
Update documentation.
tjiang11 Oct 15, 2018
fc4981d
Add tests for get_models_by_question_id
tjiang11 Oct 15, 2018
9ef2940
Update setQuestionStateData.
tjiang11 Oct 15, 2018
5861a5a
Fix test.
tjiang11 Oct 15, 2018
4ccea9c
Fix test.
tjiang11 Oct 15, 2018
0c589eb
Merge develop.
tjiang11 Oct 15, 2018
c0ad966
Lint.
tjiang11 Oct 15, 2018
9896d99
Add test.
tjiang11 Oct 25, 2018
4d3c329
Merge develop.
tjiang11 Oct 28, 2018
d37ab38
Lint.
tjiang11 Oct 28, 2018
f6f7e09
Edit questions in modal.
tjiang11 Oct 28, 2018
5722ca1
Add blank commit message.
tjiang11 Nov 4, 2018
3b5983f
Remove duplicate function; Fix issue with not being able to edit a qu…
tjiang11 Dec 1, 2018
c4c9b93
Merge develop.
tjiang11 Dec 1, 2018
98227d0
Lint.
tjiang11 Dec 1, 2018
5abc2c8
Lint.
tjiang11 Dec 1, 2018
387a9cb
Fix.
tjiang11 Dec 1, 2018
7a10a7b
Flip flag.
tjiang11 Dec 7, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Clone UndoRedoService for questions.
  • Loading branch information
tjiang11 committed Aug 30, 2018
commit 8a2c2bd030dc1883f2d39436e3c317b886e580b4
122 changes: 122 additions & 0 deletions core/templates/dev/head/domain/editor/undo_redo/UndoRedoService.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,3 +146,125 @@ oppia.factory('UndoRedoService', [
return UndoRedoService;
}
]);

oppia.factory('QuestionUndoRedoService', [
'$rootScope', 'EVENT_UNDO_REDO_SERVICE_CHANGE_APPLIED',
function($rootScope, EVENT_UNDO_REDO_SERVICE_CHANGE_APPLIED) {
var QuestionUndoRedoService = {};

var _appliedChanges = [];
var _undoneChanges = [];

var _dispatchMutation = function() {
$rootScope.$broadcast(EVENT_UNDO_REDO_SERVICE_CHANGE_APPLIED);
};
var _applyChange = function(changeObject, domainObject) {
changeObject.applyChange(domainObject);
_dispatchMutation();
};
var _reverseChange = function(changeObject, domainObject) {
changeObject.reverseChange(domainObject);
_dispatchMutation();
};

/**
* Pushes a change domain object onto the change stack and applies it to the
* provided domain object. When a new change is applied, all undone changes
* are lost and cannot be redone. This will fire an event as defined by the
* constant EVENT_UNDO_REDO_SERVICE_CHANGE_APPLIED.
*/
QuestionUndoRedoService.applyChange = function(changeObject, domainObject) {
_applyChange(changeObject, domainObject);
_appliedChanges.push(changeObject);
_undoneChanges = [];
};

/**
* Undoes the last change to the provided domain object. This function
* returns false if there are no changes to undo, and true otherwise. This
* will fire an event as defined by the constant
* EVENT_UNDO_REDO_SERVICE_CHANGE_APPLIED.
*/
QuestionUndoRedoService.undoChange = function(domainObject) {
if (_appliedChanges.length !== 0) {
var change = _appliedChanges.pop();
_undoneChanges.push(change);
_reverseChange(change, domainObject);
return true;
}
return false;
};

/**
* Reverses an undo for the given domain object. This function returns false
* if there are no changes to redo, and true if otherwise. This will fire an
* event as defined by the constant EVENT_UNDO_REDO_SERVICE_CHANGE_APPLIED.
*/
QuestionUndoRedoService.redoChange = function(domainObject) {
if (_undoneChanges.length !== 0) {
var change = _undoneChanges.pop();
_appliedChanges.push(change);
_applyChange(change, domainObject);
return true;
}
return false;
};

/**
* Returns the current list of changes applied to the provided domain
* object. This list will not contain undone actions. Changes to the
* returned list will not be reflected in this service.
*/
QuestionUndoRedoService.getChangeList = function() {
// TODO(bhenning): Consider integrating something like Immutable.js to
// avoid the slice here and ensure the returned object is truly an
// immutable copy.
return _appliedChanges.slice();
};

/**
* Returns a list of commit dict updates representing all chosen changes in
* this service. Changes to the returned list will not affect this service.
* Furthermore, the returned list is ready to be sent to the backend.
*/
QuestionUndoRedoService.getCommittableChangeList = function() {
var committableChangeList = [];
for (var i = 0; i < _appliedChanges.length; i++) {
committableChangeList[i] = _appliedChanges[i].getBackendChangeObject();
}
return committableChangeList;
};

QuestionUndoRedoService.setChangeList = function(changeList) {
_appliedChanges = angular.copy(changeList);
};

/**
* Returns the number of changes that have been applied to the domain
* object.
*/
QuestionUndoRedoService.getChangeCount = function() {
return _appliedChanges.length;
};

/**
* Returns whether this service has any applied changes.
*/
QuestionUndoRedoService.hasChanges = function() {
return _appliedChanges.length !== 0;
};

/**
* Clears the change history. This does not reverse any of the changes
* applied from applyChange() or redoChange(). This will fire an event as
* defined by the constant EVENT_UNDO_REDO_SERVICE_CHANGE_APPLIED.
*/
QuestionUndoRedoService.clearChanges = function() {
_appliedChanges = [];
_undoneChanges = [];
_dispatchMutation();
};

return QuestionUndoRedoService;
}
]);
14 changes: 11 additions & 3 deletions core/templates/dev/head/domain/question/QuestionUpdateService.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,24 @@ oppia.constant('CMD_UPDATE_QUESTION_PROPERTY', 'update_question_property');
oppia.factory('QuestionUpdateService', [
'QuestionObjectFactory',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think 2 or 3 dependencies can be declared in a line?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

'ChangeObjectFactory',
'UndoRedoService',
'QuestionUndoRedoService',
'QUESTION_PROPERTY_LANGUAGE_CODE',
'QUESTION_PROPERTY_QUESTION_STATE_DATA',
'CMD_UPDATE_QUESTION_PROPERTY',
function(
QuestionObjectFactory,
ChangeObjectFactory,
UndoRedoService,
QuestionUndoRedoService,
QUESTION_PROPERTY_LANGUAGE_CODE,
QUESTION_PROPERTY_QUESTION_STATE_DATA,
CMD_UPDATE_QUESTION_PROPERTY) {
var _changeCount = 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see this being incremented in this file, only reset seems to be there. Isn't this available from QuestionUndoRedoService?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah my bad, I forgot to remove those


var _applyChange = function(question, command, params, apply, reverse) {
var changeDict = angular.copy(params);
changeDict.cmd = command;
var changeObj = ChangeObjectFactory.create(changeDict, apply, reverse);
UndoRedoService.applyChange(changeObj, question);
QuestionUndoRedoService.applyChange(changeObj, question);
};

var _applyPropertyChange = function(
Expand Down Expand Up @@ -81,6 +83,12 @@ oppia.factory('QuestionUpdateService', [
}, function(changeDict, question) {
question.setStateData(oldStateData);
});
},
getChangeCount: function() {
return this._changeCount;
},
resetChangeCount: function() {
_changeCount = 0;
}
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,7 @@ oppia.directive('questionEditor', [
var stateData = $scope.question.getStateData();
stateData.interaction.defaultOutcome.setDestination(null);
if (stateData) {
angular.element(document).ready(function() {
$rootScope.$broadcast('stateEditorInitialized', stateData);
});
$rootScope.$broadcast('stateEditorInitialized', stateData);

if (stateData.content.getHtml() || stateData.interaction.id) {
$scope.interactionIsShown = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ oppia.directive('stateEditor', [
};

$scope.reinitializeEditor = function() {
console.log($scope.stateData);
$rootScope.$broadcast('stateEditorInitialized', $scope.stateData);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,56 @@ oppia.directive('stateInteractionEditor', [
interactionCustomizationArgs, false);
};

console.log("set listener stateEditorInitialized");
var _updateInteractionPreviewAndAnswerChoices = function() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you reuse the logic in StateEditorService.getAnswerChoices()? Better than duplicating.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

$scope.interactionId = StateInteractionIdService.savedMemento;

var currentCustomizationArgs =
StateCustomizationArgsService.savedMemento;
$scope.interactionPreviewHtml = _getInteractionPreviewTag(
currentCustomizationArgs);

// Special cases for multiple choice input and image click input.
if ($scope.interactionId === 'MultipleChoiceInput') {
$rootScope.$broadcast(
'updateAnswerChoices',
currentCustomizationArgs.choices.value.map(function(val, ind) {
return {
val: ind,
label: val
};
})
);
} else if ($scope.interactionId === 'ImageClickInput') {
var _answerChoices = [];
var imageWithRegions =
currentCustomizationArgs.imageAndRegions.value;
for (var j = 0; j < imageWithRegions.labeledRegions.length; j++) {
_answerChoices.push({
val: imageWithRegions.labeledRegions[j].label,
label: imageWithRegions.labeledRegions[j].label
});
}

$rootScope.$broadcast('updateAnswerChoices', _answerChoices);
} else if ($scope.interactionId === 'ItemSelectionInput' ||
$scope.interactionId === 'DragAndDropSortInput') {
$rootScope.$broadcast(
'updateAnswerChoices',
currentCustomizationArgs.choices.value.map(function(val) {
return {
val: val,
label: val
};
})
);
} else {
$rootScope.$broadcast('updateAnswerChoices', null);
}
};

$scope.$on('stateEditorInitialized', function(evt, stateData) {
console.log("receive stateEditorInitialized");
$scope.hasLoaded = false;
InteractionDetailsCacheService.reset();
console.log("broadcast initializeAnswerGroups");
$rootScope.$broadcast('initializeAnswerGroups', {
interactionId: stateData.interaction.id,
answerGroups: stateData.interaction.answerGroups,
Expand Down Expand Up @@ -415,53 +459,6 @@ oppia.directive('stateInteractionEditor', [
_updateInteractionPreviewAndAnswerChoices();
});
};

var _updateInteractionPreviewAndAnswerChoices = function() {
$scope.interactionId = StateInteractionIdService.savedMemento;

var currentCustomizationArgs =
StateCustomizationArgsService.savedMemento;
$scope.interactionPreviewHtml = _getInteractionPreviewTag(
currentCustomizationArgs);

// Special cases for multiple choice input and image click input.
if ($scope.interactionId === 'MultipleChoiceInput') {
$rootScope.$broadcast(
'updateAnswerChoices',
currentCustomizationArgs.choices.value.map(function(val, ind) {
return {
val: ind,
label: val
};
})
);
} else if ($scope.interactionId === 'ImageClickInput') {
var _answerChoices = [];
var imageWithRegions =
currentCustomizationArgs.imageAndRegions.value;
for (var j = 0; j < imageWithRegions.labeledRegions.length; j++) {
_answerChoices.push({
val: imageWithRegions.labeledRegions[j].label,
label: imageWithRegions.labeledRegions[j].label
});
}

$rootScope.$broadcast('updateAnswerChoices', _answerChoices);
} else if ($scope.interactionId === 'ItemSelectionInput' ||
$scope.interactionId === 'DragAndDropSortInput') {
$rootScope.$broadcast(
'updateAnswerChoices',
currentCustomizationArgs.choices.value.map(function(val) {
return {
val: val,
label: val
};
})
);
} else {
$rootScope.$broadcast('updateAnswerChoices', null);
}
};
}
]
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,9 +204,7 @@ oppia.directive('stateResponses', [
}
};

console.log("set listener initializeAnswerGroups");
$scope.$on('initializeAnswerGroups', function(evt, data) {
console.log('receive initializeAnswerGroups');
ResponsesService.init(data);
$scope.answerGroups = ResponsesService.getAnswerGroups();
$scope.defaultOutcome = ResponsesService.getDefaultOutcome();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,17 @@ oppia.directive('questionsTab', [
'MisconceptionObjectFactory', 'QuestionObjectFactory',
'QuestionSuggestionObjectFactory', 'SuggestionThreadObjectFactory',
'EVENT_QUESTION_SUMMARIES_INITIALIZED',
'UndoRedoService', 'UrlService', function(
'QuestionUndoRedoService', 'UrlService',
'UndoRedoService', function(
$scope, $http, $q, $uibModal, $window, AlertsService,
TopicEditorStateService, QuestionCreationService,
EditableQuestionBackendApiService, EditableSkillBackendApiService,
MisconceptionObjectFactory, QuestionObjectFactory,
QuestionSuggestionObjectFactory, SuggestionThreadObjectFactory,
EVENT_QUESTION_SUMMARIES_INITIALIZED,
UndoRedoService, UrlService) {
$scope.UndoRedoService = UndoRedoService;
QuestionUndoRedoService, UrlService,
UndoRedoService) {
$scope.QuestionUndoRedoService = QuestionUndoRedoService;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this used in the HTML file? If not, no need to have it in $scope.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is used in the html file now

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, but even then it only uses that single function. So, just surface that function to scope instead of the whole service.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

var _initTab = function() {
$scope.questionEditorIsShown = false;
$scope.question = null;
Expand Down Expand Up @@ -77,13 +79,13 @@ oppia.directive('questionsTab', [
$scope.questionIsBeingSaved = false;
});
} else {
if (UndoRedoService.hasChanges()) {
console.log(UndoRedoService.getCommittableChangeList());
if (QuestionUndoRedoService.hasChanges()) {
$scope.questionIsBeingSaved = true;
// TODO(tjiang11): Allow user to specify a commit message.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this tracked anywhere (in an issue perhaps)? Presumably need to ensure this is done prior to the feature being launched.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Filed as #5690 (planning to do this next while submitting my PR for adding the questions tab to the skill editor)

EditableQuestionBackendApiService.updateQuestion(
$scope.questionId, $scope.question.getVersion(), 'test',
UndoRedoService.getCommittableChangeList()).then(function() {
UndoRedoService.clearChanges();
$scope.questionId, $scope.question.getVersion(), 'blank',
QuestionUndoRedoService.getCommittableChangeList()).then(function() {
QuestionUndoRedoService.clearChanges();
$scope.questionIsBeingSaved = false;
}, function(error) {
AlertsService.addWarning(
Expand Down