diff --git a/kolibri/plugins/coach/assets/src/composables/useQuizResources.js b/kolibri/plugins/coach/assets/src/composables/useQuizResources.js index d6efb4e5f87..52962abb7e8 100644 --- a/kolibri/plugins/coach/assets/src/composables/useQuizResources.js +++ b/kolibri/plugins/coach/assets/src/composables/useQuizResources.js @@ -47,31 +47,44 @@ export default function useQuizResources({ topicId } = {}) { * the_resources have been updated, filtering out all topics which do not have assessments */ async function annotateTopicsWithDescendantCounts(topicIds = []) { - return ContentNodeResource.fetchDescendantsAssessments(topicIds) - .then(({ data: topicsWithDescendantCounts }) => { + const promises = [ + ContentNodeResource.fetchDescendantsAssessments(topicIds), + ContentNodeResource.fetchDescendants(topicIds, { + descendant__kind: ContentNodeKinds.EXERCISE, + }), + ]; + return Promise.all(promises) + .then(([{ data: topicsWithAssessmentCounts }, { data: exerciseDescendants }]) => { const childrenWithAnnotatedTopics = get(_resources) .map(node => { // We'll map so that the topics are updated in place with the num_assessments, others // are left as-is - if (node.kind === ContentNodeKinds.TOPIC) { - const topic = topicsWithDescendantCounts.find(t => t.id === node.id); - if (topic) { - node.num_assessments = topic.num_assessments; + if ([ContentNodeKinds.TOPIC, ContentNodeKinds.CHANNEL].includes(node.kind)) { + const topicWithAssessments = topicsWithAssessmentCounts.find(t => t.id === node.id); + if (topicWithAssessments) { + node.num_assessments = topicWithAssessments.num_assessments; } + exerciseDescendants.forEach(exercise => { + if (exercise.ancestor_id === node.id) { + node.num_exercises ? (node.num_exercises += 1) : (node.num_exercises = 1); + } + }); + if (!validateObject(node, QuizExercise)) { - logger.warn('Topic node was not a valid QuizExercise after annotation:', node); + logger.error('Topic node was not a valid QuizExercise after annotation:', node); } } return node; }) .filter(node => { // Only keep topics which have assessments in them to begin with - if (node.kind !== ContentNodeKinds.TOPIC) { - return true; - } - return node.num_assessments > 0; + return ( + node.kind === ContentNodeKinds.EXERCISE || + ([ContentNodeKinds.TOPIC, ContentNodeKinds.CHANNEL].includes(node.kind) && + node.num_assessments > 0) + ); }); - set(_resources, childrenWithAnnotatedTopics); + setResources(childrenWithAnnotatedTopics); }) .catch(e => { // TODO Work out best UX for this situation -- it may depend on if we're fetching more diff --git a/kolibri/plugins/coach/assets/src/views/plan/CreateExamPage/ResourceSelection.vue b/kolibri/plugins/coach/assets/src/views/plan/CreateExamPage/ResourceSelection.vue index ea22d166a0f..46aa59b8c75 100644 --- a/kolibri/plugins/coach/assets/src/views/plan/CreateExamPage/ResourceSelection.vue +++ b/kolibri/plugins/coach/assets/src/views/plan/CreateExamPage/ResourceSelection.vue @@ -129,6 +129,7 @@ //selectFoldersOrExercises$, numberOfSelectedResources$, numberOfResources$, + selectedResourcesInformation$, } = enhancedQuizManagementStrings; // TODO let's not use text for this @@ -227,7 +228,7 @@ // to the value of the channels (treating those channels as the topics) -- we then // call this annotateTopicsWithDescendantCounts method to ensure that the channels are // annotated with their num_assessments and those without assessments are filtered out - annotateTopicsWithDescendantCounts(channels.value.map(c => c.id)).then(() => { + annotateTopicsWithDescendantCounts(resources.value.map(c => c.id)).then(() => { _loading.value = false; }); }); @@ -294,6 +295,7 @@ addToWorkingResourcePool, removeFromWorkingResourcePool, showBookmarks, + selectedResourcesInformation$, }; }, props: { @@ -320,29 +322,7 @@ this.contentList.some(content => workingResourceIds.includes(content.id)) ); }, - selectionMetadata(/*content*/) { - // TODO This should return a function that returns a string telling us how many of this - // topic's descendants are selected out of its total descendants -- basically answering - // "How many resources in the working resource_pool are from this topic?" - // Tracked in https://github.com/learningequality/kolibri/issues/11741 - return () => ''; - // let count = 0; - // let total = 0; - // if (this.ancestorCounts[content.id]) { - // count = this.ancestorCounts[content.id].count; - // total = this.ancestorCounts[content.id].total; - // } - // if (count) { - // return this.$tr('selectionInformation', { - // count, - // total, - // }); - // } - // return ''; - // return function() { - // console.log('Dynamic function called'); - // }; - }, + getBookmarksLink() { // Inject the showBookmarks parameter so that // the resourceSelection component now renderes only the @@ -391,21 +371,18 @@ }, toggleSelected({ content, checked }) { if (checked) { - this.addToSelectedResources(content); + this.addToWorkingResourcePool([content]); } else { this.removeFromWorkingResourcePool(content); } }, - addToSelectedResources(content) { - this.addToWorkingResourcePool([content]); - }, toggleTopicInWorkingResources(isChecked) { if (isChecked) { - this.isSelectAllChecked = true; this.addToWorkingResourcePool(this.contentList); } else { - this.isSelectAllChecked = false; - this.resetWorkingResourcePool(); + this.contentList.forEach(content => { + this.removeFromWorkingResourcePool(content); + }); } }, topicListingLink({ topicId }) { @@ -433,19 +410,23 @@ this.$router.replace(this.closePanelRoute); }, - // selectionMetadata(content) { - // if (content.kind === ContentNodeKinds.TOPIC) { - // const count = content.exercises.filter(exercise => - // Boolean(this.selectedExercises[exercise.id]) - // ).length; - // if (count === 0) { - // return ''; - // } - // const total = content.exercises.length; - // return this.$tr('total_number', { count, total }); - // } - // return ''; - // }, + selectionMetadata(content) { + if (content.kind === ContentNodeKinds.TOPIC) { + const total = content.num_exercises; + const numberOfresourcesSelected = this.workingResourcePool.reduce((acc, wr) => { + if (wr.ancestors.map(ancestor => ancestor.id).includes(content.id)) { + return acc + 1; + } + return acc; + }, 0); + + return this.selectedResourcesInformation$({ + count: numberOfresourcesSelected, + total: total, + }); + } + return ''; + }, }, }; diff --git a/packages/kolibri-common/strings/enhancedQuizManagementStrings.js b/packages/kolibri-common/strings/enhancedQuizManagementStrings.js index 7e14036b316..039a606f141 100644 --- a/packages/kolibri-common/strings/enhancedQuizManagementStrings.js +++ b/packages/kolibri-common/strings/enhancedQuizManagementStrings.js @@ -150,4 +150,8 @@ export const enhancedQuizManagementStrings = createTranslator('EnhancedQuizManag numberOfResources: { message: '{count, number} {count, plural, one {resource selected} other {resources selected}}', }, + selectedResourcesInformation: { + message: + '{count, number, integer} of {total, number, integer} {total, plural, one {resource selected} other {resources selected}}', + }, });