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

Synchronise user selections with Quiz Creation State #11783

Merged

Conversation

ozer550
Copy link
Member

@ozer550 ozer550 commented Jan 25, 2024

Summary

  • Implementation of a _working_resource_pool ref in useQuizCreation to persist user-selected exercises across route navigations.
  • Initialization of the working resource pool on opening the side panel and clearing it upon closing, ensuring accurate tracking of changes.
  • Real-time updating of the working resource pool as users interact with checkboxes for exercise selection and deselection.
  • Integration of a confirmation modal when closing the side panel with unsaved changes.
  • Incorporation of select all and intermediate state of checkbox.

References

Solves the first part of #11734

Reviewer guidance

For side Panel:

- Go to Coach->Plan->Quiz.
- Create new Quiz.
- Add some exercises to the quiz and try to close the side panel.
- Confirmational now works as expected.
  (ConfirmCancellationModal.vue)

For working_resource_pool logic

- Add some excercises to Quiz.
- Go to section tab of quiz again.
- Click on edit Section.
- Change resources.
- When you visit already added excercises they would already be checked. 
(working_resource_pool utility in useQuizCreation and wiring in other components)


Testing checklist

  • Contributor has fully tested the PR manually
  • If there are any front-end changes, before/after screenshots are included
  • Critical user journeys are covered by Gherkin stories
  • Critical and brittle code paths are covered by unit tests

PR process

  • PR has the correct target branch and milestone
  • PR has 'needs review' or 'work-in-progress' label
  • If PR is ready for review, a reviewer has been added. (Don't use 'Assignees')
  • If this is an important user-facing change, PR or related issue has a 'changelog' label
  • If this includes an internal dependency change, a link to the diff is provided

Reviewer checklist

  • Automated test coverage is satisfactory
  • PR is fully functional
  • PR has been tested for accessibility regressions
  • External dependency files were updated if necessary (yarn and pip)
  • Documentation is updated
  • Contributor is in AUTHORS.md

@github-actions github-actions bot added APP: Coach Re: Coach App (lessons, quizzes, groups, reports, etc.) DEV: frontend SIZE: medium labels Jan 25, 2024
Copy link
Member

@nucleogenesis nucleogenesis left a comment

Choose a reason for hiding this comment

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

In manual testing things work as expected!

Noting that the related issue's "select questions" business will be moved to a separate issue -- so validating that this works properly involved checking the Vue devtools and it all worked as expected, more or less.

Overall, feedback is largely formatting / clean-up releated and suggestions for JSDocs.

The one thing that I'm not sure for the cause of is that we cannot save the quiz, getting Must be a valid UUID errors for every item in the resource pool -- I think that this may just be a matter of using import { v4 } from 'uuid'; in useQuizCreation when we generate the section_id.

Note -- that with @ozer550 out I'll likely take on some of these fixes and force push to the branch so that we can try to get this ready to merge before next iteration.

</SidePanelModal>
<ConfirmCancellationModal
v-if="showConfirmationModal"
:closePanelRoute="closePanelRoute"
Copy link
Member

Choose a reason for hiding this comment

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

Need to handle the @closeModal event here (could just be an inline function like @closeModal=() => showConfirmationModal = false

@@ -65,6 +65,7 @@
<script>

import commonCoreStrings from 'kolibri.coreVue.mixins.commonCoreStrings';
import { injectQuizCreation } from '../../../composables/useQuizCreation';
Copy link
Member

Choose a reason for hiding this comment

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

Looks like all of the changes on this file can be removed as the injected methods aren't used in this component

* Adds resources to _working_resource_pool
*/
function addToWorkingResourcePool(resources = []) {
set(_working_resource_pool, [...get(_working_resource_pool), ...resources]);
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 we should wrap the array here with lodash's uniqWith and isEqual alike to:

uniqWith([...get(_working_resource_pool), ...resources], isEqual) -- this will use the lodash isEqual function to check equality of the objects in the list (see the conveniently relevant example in the lodash docs).

@@ -445,12 +465,40 @@ export default function useQuizCreation(DEBUG = false) {
}
});

/**
* Adds resources to _working_resource_pool
Copy link
Member

Choose a reason for hiding this comment

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

Let's get the JSDoc types up-to-date here w/ @param and @affects entries - the type for the resources param ought to be QuizExercise[]

}

/**
* Check if the content is present in working_resource_pool
Copy link
Member

Choose a reason for hiding this comment

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

JSDoc suggestion:
@param {QuizExercise} content - Check if the content is present...
@returns Boolean

computed: {
isTopicIdSet() {
return this.$route.params.topic_id;
},
isSelectAllChecked() {
//if all the reosurces in contentList are present in working_resource_pool
Copy link
Member

Choose a reason for hiding this comment

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

Formatting: reosurces -> resources

Comment on lines 308 to 310
// contentIsInLesson() {
// return ({ id }) => Boolean(this.channels);
// },
Copy link
Member

Choose a reason for hiding this comment

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

This can be removed

Comment on lines 311 to 317
// addableContent() {
// // Content in the topic that can be added if 'Select All' is clicked
// const list = this.contentList.value ? this.contentList.value : this.bookmarksList;
// return list.filter(
// content => !this.contentIsDirectoryKind(content) && !this.contentIsInLesson(content)
// );
// },
Copy link
Member

Choose a reason for hiding this comment

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

This can be removed - particularly with the uniqWith/isEqual suggestion elsewhere in this review in place

Comment on lines 362 to 364
// saveResources() {
// this.saveQuiz();
// },
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 commented out because it is no longer used?

Comment on lines 425 to 457
handleSearchTerm(searchTerm) {
const query = {
last_id: this.$route.query.last_id || this.$route.params.topicId,
};
const lastPage = this.$route.query.last;
if (lastPage) {
query.last = lastPage;
}
this.$router.push({
name: LessonsPageNames.SELECTION_SEARCH,
params: {
searchTerm,
},
query,
});
},
handleMoreResults() {
this.moreResultsState = 'waiting';
this.fetchAdditionalSearchResults({
searchTerm: this.searchTerm,
kind: this.filters.kind,
channelId: this.filters.channel,
currentResults: this.searchResults.results,
})
.then(() => {
this.moreResultsState = null;
this.moreResultsState;
})
.catch(() => {
this.moreResultsState = 'error';
});
},
*/
Copy link
Member

Choose a reason for hiding this comment

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

I thought that this had been removed in my last PR -- can we strip out the search business here as it'll be addressed in an upcoming issue.

@rtibbles rtibbles assigned rtibbles and unassigned nucleogenesis Jan 26, 2024
Copy link
Member

@rtibbles rtibbles left a comment

Choose a reason for hiding this comment

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

I am not seeing anything blocking here, per se - but I think it is worth reflecting on the granularity of each set of state.

One of the ongoing issues that we've had with vuex is that there is one granularity of state - global - and this has caused issues and edge cases where state has not been properly reset, updated etc.

I know that you've tried to preempt that by having a specific state reset function, but if we instead encapsulated the state at a finer level of granularity, then we would be able to pre-empty any of these issues, rather than having to add additional code to fix them.

@@ -558,6 +567,8 @@
set(this.dragActive, true);
},
openSelectResources(section_id) {
//initialize workingResourcePool
this.initializeWorkingResourcePool();
Copy link
Member

Choose a reason for hiding this comment

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

Is there a reason this couldn't happen within the ResourceSelection component instead?

@@ -382,6 +385,8 @@
allSections,
activeSection,
inactiveSections,
activeResourcePool,
Copy link
Member

Choose a reason for hiding this comment

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

Both of these are added, and neither are used.

setup() {
const {
//Computed
resetWorkingResourcePool,
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 it would be better to make this component emit an event rather than handle all state clearing and route changing.

@@ -477,8 +548,13 @@ export default function useQuizCreation(DEBUG = false) {
return {
// Methods
saveQuiz,
initializeWorkingResourcePool,
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 turning the useQuizCreation composable into a bit of a catch all again - I think all the state and methods here are at a smaller level of granularity than the useQuizCreation composable is handling.

I think with some judicious work, this could all be instantiated entirely within the ResourceSelection component, or in a separate composable that is only used inside the ResourceSelection component.

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 the main issue is handling that the component re-setups each navigation, hence it living in then composable. I think I initially suggested that it live in ResourceSelection but foresaw it being a problem until we fix the coach routing issues

return ExamResource.saveModel({ data: get(_quiz) });

// Here we update each section's `resource_pool` to only be the IDs of the resources
const sectionsWithResourcePoolAsIDs = get(allSections).map(section => {
Copy link
Member

Choose a reason for hiding this comment

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

Couldn't we just always maintain the frontend representation of this the same as the backend, and have a separate representation of the resources that map to these IDs that we populate when we start editing a specific section? (in the proposed separate state management within ResourceSelection that I propose below).

Copy link
Member

Choose a reason for hiding this comment

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

I specced it this way so that we have access to all of the Exercise metadata that we've already fetched at hand so we can do the ancestry calculations and get the assessmentids for question enumeration.

This is related to the issue I made and closed yesterday re: updating the serializer to take the object rather than the IDs - but since that's only a concern in the case that we edit a saved quiz (in which case we'd need to fetch the metadata).

I figured that this way reduces our needing to fetch the metadata wherever it is needed here while keeping things roughly in the shape of the backend data - except on the front end we keep the whole object whereas we just send the IDs.

To be honest I'm not entirely sure this needs to be saved at all except for in the case that we permit editing / duplication of quizzes as the 'questions' property has everything needed to render the quiz. The resource_pool is solely used for enumerating the total possible pool of questions the coach can choose from to add to the quiz.

Copy link
Member

Choose a reason for hiding this comment

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

Coming back to this after the weekend, I think that it makes sense to maintain a separate map of the metadata and to keep the list of IDs in the section's data withing the useQuizCreation module.

This would then be easy to extend to accommodate duplication/editing of a quiz or section as we can then populate the initial map with a single API call for the list of node IDs there.

Copy link
Member

@nucleogenesis nucleogenesis left a comment

Choose a reason for hiding this comment

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

@rtibbles

I've addressed the clean-up comments.

I'm going to add a note re: "refactor working resource pool to live in ResourceSelection.vue, rather than useQuizCreation, once rerouting between folders no longer results in remounting the component" to #11741 and we can hopefully aim for it in a planned patch release.

I'm approving & merging now to be sure issues we plan to assign tomorrow can be started.

@nucleogenesis nucleogenesis merged commit 171f6bb into learningequality:develop Jan 30, 2024
31 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
APP: Coach Re: Coach App (lessons, quizzes, groups, reports, etc.) DEV: frontend SIZE: medium
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants