-
-
Notifications
You must be signed in to change notification settings - Fork 4.5k
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
Adds diagnostic test engine service #16453
Adds diagnostic test engine service #16453
Conversation
…nto diagnostic_player
…tic_test_algorithm
…nto diagnostic_test_algorithm
…tic_test_algorithm
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.
A few minor comments. PTAL
export class DiagnosticTestPlayerStatusService { | ||
private _diagnosticTestPlayerCompletedEventEmitter = ( | ||
new EventEmitter<string[]>()); | ||
|
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.
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.
// and tested in the diagnostic test. | ||
_eligibleTopicIds: string[]; | ||
_pendingTopicIds: string[]; |
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.
_pendingTopicIds: string[]; | |
private _pendingTopicIds: string[]; |
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.
Done
this._diagnosticTestTopicTrackerModel = diagnosticTestTopicTrackerModel; | ||
this._initialCopyOfTopicTrackerModel = cloneDeep( | ||
diagnosticTestTopicTrackerModel); | ||
|
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.
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.
Removed
this.questionBackendApiService.fetchDiagnosticTestQuestionsAsync( | ||
this._currentTopicId, this._encounteredQuestionIds).then((response) => { | ||
this._diagnosticTestCurrentTopicStatusModel = ( | ||
new DiagnosticTestCurrentTopicStatusModel(response)); | ||
this._currentSkillId = ( | ||
this._diagnosticTestCurrentTopicStatusModel.getNextSkill()); | ||
this._currentQuestion = ( | ||
this._diagnosticTestCurrentTopicStatusModel.getNextQuestion( | ||
this._currentSkillId) |
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.
What will happen if fetch request fails? Should not we also catch the error response of the request?
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.
Added reject handler
this.questionBackendApiService.fetchDiagnosticTestQuestionsAsync( | ||
this._currentTopicId, this._encounteredQuestionIds).then( | ||
skillIdToQuestionsModel => { | ||
this._diagnosticTestCurrentTopicStatusModel = ( | ||
new DiagnosticTestCurrentTopicStatusModel( | ||
skillIdToQuestionsModel) | ||
); | ||
this._currentSkillId = ( | ||
this._diagnosticTestCurrentTopicStatusModel.getNextSkill()); | ||
|
||
this._currentQuestion = ( | ||
this._diagnosticTestCurrentTopicStatusModel.getNextQuestion( | ||
this._currentSkillId) | ||
); | ||
return successCallback(this._currentQuestion); | ||
} | ||
); |
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.
Ditto as above
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.
Done
Unassigning @Rijuta-s since the review is done. |
// and tested in the diagnostic test. | ||
_eligibleTopicIds: string[]; | ||
_pendingTopicIds: string[]; |
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.
Shouldn't it be pendingTopicIdsToTest (and similarly elsewhere)? I think just "pending" is too general.
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.
Updated
let rootTopicIds: string[] = this.getRootTopicIds(); | ||
let failedRootTopicIds: string[] = rootTopicIds.filter( | ||
topicId => failedTopicIds.indexOf(topicId) !== -1 | ||
); | ||
|
||
if (failedRootTopicIds.length >= 2) { | ||
// If the learner failed in two or more root topics, then any two root | ||
// topics were recommended, so that learner can start with any |
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.
... then we recommend any two root topics, so that the learner can start with any one of them.
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.
Done
recommendedTopicIds.push(failedRootTopicIds[0]); | ||
} else { | ||
let sortedTopicIds = this.getSortedTopicIds(); | ||
// If none of the failed topics is a root topic, then the failed topics | ||
// are topologically sorted and then the first topic among the sorted list |
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.
...then we sort the failed topics topologically and recommend the first topic from the sorted list.
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.
Done
@@ -260,6 +267,8 @@ export class DiagnosticTestPlayerEngineService { | |||
} | |||
|
|||
getRootTopicIds(): string[] { | |||
// The topics which do not contain any prerequisites are referred to as |
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 comment should go above a line that has "root topics" in it -- so, either above the function name, or above "let rootTopicIds".
You can also use JSDoc if you like.
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.
Added
// If none of the failed topics is a root topic, then the failed topics | ||
// are topologically sorted and then the first topic among the sorted list | ||
// will be recommended. | ||
let sortedTopicIds = this.getSortedInitialTopicIds(); |
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.
I don't know what "sorted initial topic IDs" means. Couldn't you just say getTopologicallySortedTopicIds()?
But also, I'm not sure which topic IDs you are referring to -- how does this method know which topics are failed? Shouldn't we pass in a list of failed topic IDs to it so that it can topologically sort those? The word "initial" doesn't convey anything to me, and if anything it sounds like the full list of topics which seems different from just the failed ones (as per the comment).
Lastly, I'm not even sure a full top-sort is needed -- couldn't we just compute a list of topic IDs which have no prerequisites within the set?
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.
Updated the name.
This method only sorts the topics. This method will be used internally by the getTopicRecommendations() function.
The recommendation logic:
- Check if the failed topics have any root topics.
- If 2 or more roots are present in the failed topics: Recommend any two roots.
- If 1 root topic is failed: Recommend the root topic.
- If no root topics then,
- Sort the topic IDs that were initially present i.e., topicIdToPrerequisiteTopicIds. Here I am sorting all the topics not only the failed ones because the failed topics may or may not have dependencies with each other.
- After sorting I am picking the first topic from the sorted list that is failed by the learner.
Answering the last point: Can you please confirm that in the last para, are you referring to the root topic IDs (i.e., compute a list of root topics, because they have no prerequisites)?
If yes: The failed topic IDs may or may not contains the root topics, if they contain any root topic, then the recommendation function will take care of that. Also, we only need to sort the topics if the failed topics list does not contain any of the root topics.
If not: Please help me to understand the last point.
Thank you
[Other comments are addressed locally, currently pushing the changes. I will reassign it for review once the changes will be pushed]
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.
Hi @Nik-09, I almost missed this! Please remember to reassign to whoever needs to take the next action, even though it might just be answering a question.
For the last point, I was talking about looking for topics within the failed topics that do not have prerequisites within the failed topics. I.e. consider the nodes that consist of the failed topics only. Within that graph, we just want to return the topics that have no other prerequisites, right?
E.g. if you have A --> B, B --> C, A --> D, D --> C, C --> E and only B, D, C, E are failed, then B and D are root topics in the "failed graph" and the ones that would be recommended. C has prerequisites B/D and E has prerequisite C, so they're not eligible. Although B and D have A as a prerequisite, A isn't a failed topic and isn't in the smaller "failed graph".
Does this clarify?
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.
Yes, I got the point, but originally we have a dict containing all the topicIdToPrerequisite topic IDs.
In the end, when we have to sort the failed topic ID, we just need a dependency graph for that we have to remove the non-failed topics from the original dependency graph, which may be extra.
Now in order to minimize the full topological sorting I think terminating the function if we encounter the first failed ID while traversing the full graph may improve the efficiency.
So based on that, the new method name can be: getTopologicallyFirstTopicId()
The method returns the first topic ID from the topologically sorted list of the given topic IDs.
Can you please provide feedback on this approach?
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.
I think something like that is totally fine. Not sure if you want to return 1 or 2 IDs but the basic idea makes sense to me!
@Nik-09 Reviewed and replied -- PTAL. |
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.
Thanks @Nik-09! I have the same comments as other reviewers in the codeowner files. Please address them. I'm approving to unblock.
Unassigning @kevintab95 since the review is done. |
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.
LGTM
|
||
|
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.
|
||
|
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.
Unassigning @heyimShivam since they have already approved the PR. |
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.
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.
Thanks @Nik-09, LGTM. On balance I think you can skip the "failed topic prereqs" optimization -- since the graph is only 15 nodes and this calculation is just run once, it probably won't make much difference anyway.
Feel free to merge when you're ready!
…tic_test_engine_service
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.
Thanks @Nik-09 ! LGTM!
Overview
The PR adds an engine to maintain the state and run the diagnostic test.
Essential Checklist
Proof that changes are correct
The PR does not contain any UI changes, hence UI proof is not required.
PR Pointers