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

Adds diagnostic test engine service #16453

Merged
merged 126 commits into from
Nov 13, 2022

Conversation

Nik-09
Copy link
Member

@Nik-09 Nik-09 commented Oct 31, 2022

Overview

The PR adds an engine to maintain the state and run the diagnostic test.

Essential Checklist

  • The PR title starts with "Fix #bugnum: ", followed by a short, clear summary of the changes. (If this PR fixes part of an issue, prefix the title with "Fix part of #bugnum: ...".)
  • The linter/Karma presubmit checks have passed locally on your machine.
  • "Allow edits from maintainers" is checked. (See here for instructions on how to enable it.)
    • This lets reviewers restart your CircleCI tests for you.
  • The PR is made from a branch that's not called "develop".

Proof that changes are correct

The PR does not contain any UI changes, hence UI proof is not required.

PR Pointers

  • Make sure to follow the instructions for making a code change.
  • If you need a review or an answer to a question, and don't have permissions to assign people, leave a comment like the following: "{{Question/comment}} @{{reviewer_username}} PTAL". Oppiabot will help assign that person for you.
  • For what code owners will expect, see the Code Owner's wiki page.
  • Make sure your PR follows conventions in the style guide, otherwise this will lead to review delays.
  • Never force push. If you do, your PR will be closed.
  • Some of the e2e tests are flaky, and can fail for reasons unrelated to your PR. We are working on fixing this, but in the meantime, if you need to restart the tests, please check the "If your build fails" wiki page.

Sorry, something went wrong.

Nik-09 added 30 commits October 10, 2022 22:32
@Nik-09 Nik-09 assigned seanlip, kevintab95 and heyimShivam and unassigned Nik-09 Nov 10, 2022
Copy link
Contributor

@Rijuta-s Rijuta-s left a 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[]>());

Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change

Copy link
Member Author

@Nik-09 Nik-09 Nov 12, 2022

Choose a reason for hiding this comment

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

Not removed, causing lint failures.

Screenshot from 2022-11-12 14-02-22

// and tested in the diagnostic test.
_eligibleTopicIds: string[];
_pendingTopicIds: string[];
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
_pendingTopicIds: string[];
private _pendingTopicIds: string[];

Copy link
Member Author

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);

Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change

Copy link
Member Author

Choose a reason for hiding this comment

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

Removed

Comment on lines +86 to +94
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)
Copy link
Contributor

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?

Copy link
Member Author

Choose a reason for hiding this comment

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

Added reject handler

Comment on lines 127 to 143
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);
}
);
Copy link
Contributor

Choose a reason for hiding this comment

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

Ditto as above

Copy link
Member Author

Choose a reason for hiding this comment

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

Done

@oppiabot oppiabot bot unassigned Rijuta-s Nov 10, 2022
@oppiabot
Copy link

oppiabot bot commented Nov 10, 2022

Unassigning @Rijuta-s since the review is done.

@oppiabot
Copy link

oppiabot bot commented Nov 10, 2022

Hi @Nik-09, it looks like some changes were requested on this pull request by @Rijuta-s. PTAL. Thanks!

// and tested in the diagnostic test.
_eligibleTopicIds: string[];
_pendingTopicIds: string[];
Copy link
Member

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.

Copy link
Member Author

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
Copy link
Member

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.

Copy link
Member Author

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
Copy link
Member

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.

Copy link
Member Author

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
Copy link
Member

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.

Copy link
Member Author

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();
Copy link
Member

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?

Copy link
Member Author

@Nik-09 Nik-09 Nov 12, 2022

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]

Copy link
Member

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?

Copy link
Member Author

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?

Copy link
Member

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!

@seanlip seanlip removed their assignment Nov 12, 2022
@seanlip
Copy link
Member

seanlip commented Nov 12, 2022

@Nik-09 Reviewed and replied -- PTAL.

Copy link
Member

@kevintab95 kevintab95 left a 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.

@oppiabot
Copy link

oppiabot bot commented Nov 12, 2022

Unassigning @kevintab95 since the review is done.

Copy link
Contributor

@heyimShivam heyimShivam left a comment

Choose a reason for hiding this comment

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

LGTM

Comment on lines 23 to 24


Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change

Comment on lines 41 to 42


Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change

@oppiabot
Copy link

oppiabot bot commented Nov 12, 2022

Unassigning @heyimShivam since they have already approved the PR.

@Nik-09 Nik-09 assigned seanlip and Rijuta-s and unassigned Nik-09 Nov 12, 2022
Copy link
Member Author

@Nik-09 Nik-09 left a comment

Choose a reason for hiding this comment

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

Hi, @seanlip @Rijuta-s I have addressed the open comments. Please take a look once you are free.
Thank you

Copy link
Member

@seanlip seanlip left a 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!

@seanlip seanlip removed their assignment Nov 12, 2022
Copy link
Contributor

@Rijuta-s Rijuta-s left a 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!

@Rijuta-s Rijuta-s merged commit f8a8d68 into oppia:develop Nov 13, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants