Skip to content

Commit

Permalink
Merge pull request #11783 from ozer550/handle-resource-selection
Browse files Browse the repository at this point in the history
Synchronise user selections with Quiz Creation State
  • Loading branch information
nucleogenesis authored Jan 30, 2024
2 parents cc75a0b + 9f64cc0 commit 171f6bb
Show file tree
Hide file tree
Showing 5 changed files with 292 additions and 42 deletions.
96 changes: 94 additions & 2 deletions kolibri/plugins/coach/assets/src/composables/useQuizCreation.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { v4 as uuidv4 } from 'uuid';
import { v4 } from 'uuid';
import isEqual from 'lodash/isEqual';
import uniqWith from 'lodash/uniqWith';
import range from 'lodash/range';
import shuffle from 'lodash/shuffle';
import { enhancedQuizManagementStrings } from 'kolibri-common/strings/enhancedQuizManagementStrings';
Expand All @@ -16,6 +17,10 @@ import { Quiz, QuizSection, QuizQuestion, QuizExercise } from './quizCreationSpe

const logger = logging.getLogger(__filename);

function uuidv4() {
return v4().replace(/-/g, '');
}

/** Validators **/
/* objectSpecs expects every property to be available -- but we don't want to have to make an
* object with every property just to validate it. So we use these functions to validate subsets
Expand All @@ -41,6 +46,10 @@ export default function useQuizCreation(DEBUG = false) {
// Local state
// -----------

/** @type {ComputedRef<QuizExercise[]>} Currently selected resource_pool
* from the side_panel*/
const _working_resource_pool = ref([]);

/** @type {ref<Quiz>}
* The "source of truth" quiz object from which all reactive properties should derive */
const _quiz = ref(objectWithDefaults({}, Quiz));
Expand Down Expand Up @@ -265,6 +274,12 @@ export default function useQuizCreation(DEBUG = false) {
_fetchChannels();
}

// // Method to initialize the working resource pool
function initializeWorkingResourcePool() {
// Set the value of _working_resource_pool to the resource_pool of the active section
set(_working_resource_pool, get(activeResourcePool));
}

/**
* @returns {Promise<Quiz>}
* @throws {Error} if quiz is not valid
Expand All @@ -282,7 +297,19 @@ export default function useQuizCreation(DEBUG = false) {
if (!validateQuiz(get(_quiz))) {
throw new Error(`Quiz is not valid: ${JSON.stringify(get(_quiz))}`);
}
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 => {
const resourcePoolAsIds = get(section).resource_pool.map(content => content.id);
section.resource_pool = resourcePoolAsIds;
return section;
});

const finalQuiz = get(_quiz);

finalQuiz.question_sources = sectionsWithResourcePoolAsIDs;

return ExamResource.saveModel({ data: finalQuiz });
}

/**
Expand Down Expand Up @@ -337,6 +364,11 @@ export default function useQuizCreation(DEBUG = false) {
}
}

function resetWorkingResourcePool() {
// Set the WorkingResource to empty array again!
set(_working_resource_pool, []);
}

/**
* @affects _channels - Fetches all channels with exercises and sets them to _channels */
function _fetchChannels() {
Expand Down Expand Up @@ -408,6 +440,9 @@ export default function useQuizCreation(DEBUG = false) {
/** @type {ComputedRef<Array>} A list of all channels available which have exercises */
const channels = computed(() => get(_channels));

// /** @type {ComputedRef<QuizExercise[]>} The current value of _working_resource_pool */
const workingResourcePool = computed(() => get(_working_resource_pool));

/** Handling the Select All Checkbox
* See: remove/toggleQuestionFromSelection() & selectAllQuestions() for more */

Expand Down Expand Up @@ -445,12 +480,45 @@ export default function useQuizCreation(DEBUG = false) {
}
});

/**
* @param {QuizExercise[]} resources
* @affects _working_resource_pool -- Updates it with the given resources and is ensured to have
* a list of unique resources to avoid unnecessary duplication
*/
function addToWorkingResourcePool(resources = []) {
set(_working_resource_pool, uniqWith([...get(_working_resource_pool), ...resources], isEqual));
}

/**
* @param {QuizExercise} content
* @affects _working_resource_pool - Remove given quiz exercise from _working_resource_pool
*/
function removeFromWorkingResourcePool(content) {
set(
_working_resource_pool,
_working_resource_pool.value.filter(obj => obj.id !== content.id)
);
}

/**
* @param {QuizExercise} content
* Check if the content is present in working_resource_pool
*/
function contentPresentInWorkingResourcePool(content) {
const workingResourceIds = get(workingResourcePool).map(wr => wr.id);
return workingResourceIds.includes(content.id);
}

/** @type {ComputedRef<Boolean>} Whether the select all checkbox should be indeterminate */
const selectAllIsIndeterminate = computed(() => {
return !get(allQuestionsSelected) && !get(noQuestionsSelected);
});

provide('saveQuiz', saveQuiz);
provide('initializeWorkingResourcePool', initializeWorkingResourcePool);
provide('addToWorkingResourcePool', addToWorkingResourcePool);
provide('removeFromWorkingResourcePool', removeFromWorkingResourcePool);
provide('contentPresentInWorkingResourcePool', contentPresentInWorkingResourcePool);
provide('updateSection', updateSection);
provide('replaceSelectedQuestions', replaceSelectedQuestions);
provide('addSection', addSection);
Expand All @@ -460,11 +528,14 @@ export default function useQuizCreation(DEBUG = false) {
provide('updateQuiz', updateQuiz);
provide('addQuestionToSelection', addQuestionToSelection);
provide('removeQuestionFromSelection', removeQuestionFromSelection);
provide('resetWorkingResourcePool', resetWorkingResourcePool);
provide('channels', channels);
provide('quiz', quiz);
provide('allSections', allSections);
provide('activeSection', activeSection);
provide('inactiveSections', inactiveSections);
provide('activeResourcePool', activeResourcePool);
provide('workingResourcePool', workingResourcePool);
provide('activeExercisePool', activeExercisePool);
provide('activeQuestionsPool', activeQuestionsPool);
provide('activeQuestions', activeQuestions);
Expand All @@ -477,8 +548,13 @@ export default function useQuizCreation(DEBUG = false) {
return {
// Methods
saveQuiz,
initializeWorkingResourcePool,
removeFromWorkingResourcePool,
addToWorkingResourcePool,
contentPresentInWorkingResourcePool,
updateSection,
replaceSelectedQuestions,
resetWorkingResourcePool,
addSection,
removeSection,
setActiveSection,
Expand All @@ -493,6 +569,8 @@ export default function useQuizCreation(DEBUG = false) {
allSections,
activeSection,
inactiveSections,
workingResourcePool,
activeResourcePool,
activeExercisePool,
activeQuestionsPool,
activeQuestions,
Expand All @@ -516,9 +594,14 @@ export default function useQuizCreation(DEBUG = false) {

export function injectQuizCreation() {
const saveQuiz = inject('saveQuiz');
const initializeWorkingResourcePool = inject('initializeWorkingResourcePool');
const removeFromWorkingResourcePool = inject('removeFromWorkingResourcePool');
const contentPresentInWorkingResourcePool = inject('contentPresentInWorkingResourcePool');
const addToWorkingResourcePool = inject('addToWorkingResourcePool');
const updateSection = inject('updateSection');
const replaceSelectedQuestions = inject('replaceSelectedQuestions');
const addSection = inject('addSection');
const resetWorkingResourcePool = inject('resetWorkingResourcePool');
const removeSection = inject('removeSection');
const setActiveSection = inject('setActiveSection');
const initializeQuiz = inject('initializeQuiz');
Expand All @@ -530,6 +613,8 @@ export function injectQuizCreation() {
const allSections = inject('allSections');
const activeSection = inject('activeSection');
const inactiveSections = inject('inactiveSections');
const activeResourcePool = inject('activeResourcePool');
const workingResourcePool = inject('workingResourcePool');
const activeExercisePool = inject('activeExercisePool');
const activeQuestionsPool = inject('activeQuestionsPool');
const activeQuestions = inject('activeQuestions');
Expand All @@ -542,9 +627,14 @@ export function injectQuizCreation() {
return {
// Methods
saveQuiz,
initializeWorkingResourcePool,
addToWorkingResourcePool,
contentPresentInWorkingResourcePool,
removeFromWorkingResourcePool,
deleteActiveSelectedQuestions,
selectAllQuestions,
updateSection,
resetWorkingResourcePool,
replaceSelectedQuestions,
addSection,
removeSection,
Expand All @@ -561,6 +651,8 @@ export function injectQuizCreation() {
allSections,
activeSection,
inactiveSections,
workingResourcePool,
activeResourcePool,
activeExercisePool,
activeQuestionsPool,
activeQuestions,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<template>

<div>
<KModal
title="Are you sure you want to exit ?"
>
<p>All the changes made on selected resource will be lost</p>

<template #actions>
<KGrid>
<KGridItem
:layout12="{ span: 6 }"
:layout8="{ span: 4 }"
:layout4="{ span: 2 }"
>
<KButton
:text="coreString('cancelAction')"
class="kbuttons"
style="width:100%;"
@click="closeModal()"
/>
</KGridItem>
<KGridItem
:layout12="{ span: 6 }"
:layout8="{ span: 4 }"
:layout4="{ span: 2 }"
>
<KButton
:text="coreString('continueAction')"
appearance="raised-button"
style="width:100%;"
primary
@click="handleContinueAction"
/>
</KGridItem>
</KGrid>
</template>
</KModal>
</div>

</template>


<script>
import commonCoreStrings from 'kolibri.coreVue.mixins.commonCoreStrings';
export default {
name: 'ConfirmCancellationModal',
mixins: [commonCoreStrings],
props: {
closePanelRoute: {
type: Object,
required: true,
},
},
methods: {
handleContinueAction() {
this.$emit('continue');
this.$router.replace(this.closePanelRoute);
},
closeModal() {
this.$emit('cancel');
},
},
};
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,8 @@
</KTabsPanel>

<SectionSidePanel @closePanel="focusActiveSectionTab()" />


</div>

</template>
Expand Down Expand Up @@ -382,6 +384,7 @@
allSections,
activeSection,
inactiveSections,
workingResourcePool,
activeExercisePool,
activeQuestionsPool,
activeQuestions,
Expand Down Expand Up @@ -431,6 +434,7 @@
allSections,
activeSection,
inactiveSections,
workingResourcePool,
activeExercisePool,
activeQuestionsPool,
activeQuestions,
Expand Down Expand Up @@ -515,6 +519,7 @@
focusActiveSectionTab() {
const label = this.tabRefLabel(this.activeSection.section_id);
const tabRef = this.$refs[label];
// TODO Consider the "Delete section" button on the side panel; maybe we need to await
// nextTick if we're getting the error
if (tabRef) {
Expand Down
Loading

0 comments on commit 171f6bb

Please sign in to comment.