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

Fix #110: Filter questions required for assessment in QuestionTrainingController #227

Merged
merged 25 commits into from
Oct 25, 2019

Conversation

vinitamurthi
Copy link
Contributor

@vinitamurthi vinitamurthi commented Oct 11, 2019

Explanation

This PR fetches a specific amount of questions, spread across each skill in the QuestionTrainingController and passes on the data provider to the QuestionAssessmentProgressController.
This also adds some fake question data with different interaction types - multiple choice, text input, and numeric input (these are the only ones that are currently supported even in the web question player) that can be used in a training session

Fixes #110

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 PR explanation includes the words "Fixes #bugnum: ..." (or "Fixes part of #bugnum" if the PR only partially fixes an issue).
  • The PR follows the style guide.
  • The PR does not contain any unnecessary auto-generated code from Android Studio.
  • The PR is made from a branch that's not called "develop".
  • The PR is assigned to an appropriate reviewer.

@vinitamurthi
Copy link
Contributor Author

@BenHenning PTAL, I have also added a json with 3 sample questions.

Copy link
Member

@BenHenning BenHenning left a comment

Choose a reason for hiding this comment

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

Thanks @vinitamurthi.

One top-level question: is this actually fixing #120? That issue appears to be closed. Is this fixing part of #110 or all of #110, instead?

Otherwise, my feedback is primarily around ensuring that we're generating this as expected. I was expecting more randomness, and it'd be good to make sure that we're testing multiple aspects of the controller.

@BenHenning
Copy link
Member

Also if there is missing functionality before we can fully finish #110, what is that functionality? Can we just implement it here to consider the training controller finished?

@vinitamurthi vinitamurthi changed the title Fix #120: Filter questions required for assessment in QuestionTrainingController Fix #110: Filter questions required for assessment in QuestionTrainingController Oct 15, 2019
@vinitamurthi
Copy link
Contributor Author

#110 is implemented completely by this PR, sorry I meant to write #110 in this PR but accidentally wrote that it fixes #120

Copy link
Member

@BenHenning BenHenning left a comment

Choose a reason for hiding this comment

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

Thanks @vinitamurthi--took another pass on this. Please let me know if you have any questions regarding my comments.

domain/src/main/assets/sample_questions.json Show resolved Hide resolved
val jsonContents = assetManager.open(assetName).bufferedReader().use { it.readText() }
return JSONObject(jsonContents)
}
}
Copy link
Member

Choose a reason for hiding this comment

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

Please add EOF newline here & elsewhere in this PR where one is missing.

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

Copy link
Member

Choose a reason for hiding this comment

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

This appears to still be missing. Please make sure all new files have EOF newlines.

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

@vinitamurthi
Copy link
Contributor Author

I have made all the changes requested, PTAL @BenHenning !

Copy link
Member

@BenHenning BenHenning left a comment

Choose a reason for hiding this comment

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

Thanks @vinitamurthi! Did you also address my question regarding the random number generation?

Overall this PR LGTM, but my main open question is around the randomness. Shouldn't we be randomly sorting the questions so that assessments are different each time? If so, we should also be thoroughly testing these flows in a deterministic way.

"solicit_answer_details": false
}
]
}
Copy link
Member

Choose a reason for hiding this comment

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

Nit: please add EOF newline.

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

class QuestionTrainingConstantsProvider @Inject constructor(
) {
fun getQuestionCountPerTrainingSession(): Int {
return QUESTION_COUNT_PER_TRAINING_SESSION
Copy link
Member

Choose a reason for hiding this comment

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

Suggest providing this via Dagger instead. That would look something like:

Annotation file:

@Qualifier annotation class QuestionCountPerTrainingSession

Module:

@Module
class QuestionModule {
  @Provides
  @QuestionCountPerTrainingSession
  fun provideQuestionCountPerTrainingSession(): Int = 10
}

When using it:

class Dependency @Inject(@QuestionCountPerTrainingSession private val int questionCountPerSession) {
  ...
}

This approach allows us to switch out the count in test modules to make it easier to perform testing.

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

import java.io.IOException
import javax.inject.Inject

/** Utility that helps retrieving JSON assets and converting them to a JSON object. */
Copy link
Member

Choose a reason for hiding this comment

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

Nit on verb tense: Utility that retrieves JSON assets and converts them to JSON objects.

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

val jsonContents = assetManager.open(assetName).bufferedReader().use { it.readText() }
return JSONObject(jsonContents)
}
}
Copy link
Member

Choose a reason for hiding this comment

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

This appears to still be missing. Please make sure all new files have EOF newlines.

class JsonAssetRetriever @Inject constructor(private val context: Context) {

/** Loads the JSON string from an asset and converts it to a JSONObject */
@Throws(IOException::class)
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 actually necessary? If not, prefer to remove since exceptions are always optional in Kotlin (this annotation should only be needed when calling this method from Java code, and we're not doing that).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed

@Provides
@Singleton
fun provideQuestionTrainingConstantsProvider(): QuestionTrainingConstantsProvider {
MockitoAnnotations.initMocks(this)
Copy link
Member

Choose a reason for hiding this comment

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

This is a bit hacky and ought to be done only for test classes. However we can avoid using Mockito here altogether if you move the constant directly to the dagger graph.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Got it, I have moved it to the dagger graph now

@@ -0,0 +1 @@
mock-maker-inline
Copy link
Member

Choose a reason for hiding this comment

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

Why do we need this? Prefer to remove.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I had kept this because mockito doesnt allow mocking final classes unless you do this and all kotlin classes are final by default. But since I dont need to use mockito anymore I removed it

Copy link
Member

@BenHenning BenHenning left a comment

Choose a reason for hiding this comment

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

Thanks!

import dagger.Provides
import javax.inject.Qualifier

/** Provider to return any constants required during the training session. */
Copy link
Member

Choose a reason for hiding this comment

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

Perhaps this should be on the Module instead of this annotation?

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

}
return trainingQuestions.take(questionCountPerSession)
}

Copy link
Member

Choose a reason for hiding this comment

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

Nit: please remove extra 2 newlines.

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

val questionsDataProvider = topicController.retrieveQuestionsForSkillIds(skillIdsList)
return dataProviders.transform(TRAINING_QUESTIONS_PROVIDER, questionsDataProvider) {
getFilteredQuestionsForTraining(
skillIdsList, it.shuffled(Random(questionTrainingSeed)),
Copy link
Member

Choose a reason for hiding this comment

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

Won't this cause the same order for subsequent assessments? I'm guessing we need to actually store the random object in a field to make sure it's shared across multiple shuffles.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes that's right my mistake. I am storing the random object as a member variable and reusing the same object

@vinitamurthi
Copy link
Contributor Author

I verified through a manual test that the random generator does send out a different assessment when called by the same object multiple times. I will submit this PR now and we can make any subsequent changes in a follow up!

@vinitamurthi vinitamurthi merged commit 904c9f4 into develop Oct 25, 2019
@vinitamurthi vinitamurthi deleted the question_data branch November 5, 2019 16:52
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.

Implement QuestionTrainingController [Blocked: #112, #120]
2 participants