-
Notifications
You must be signed in to change notification settings - Fork 699
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
Questions randomly selected from resource pool #11823
Merged
nucleogenesis
merged 9 commits into
learningequality:develop
from
nucleogenesis:11734--questions-from-resource-pool
Feb 7, 2024
Merged
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
285dc32
remove duplicitous unused ref: activeExercisePool
nucleogenesis a761fb7
move selectQuestions.js into utils dir
nucleogenesis ef90585
wrap selectQuestions in the useQuizCreation context; update auto-fill…
nucleogenesis ba20587
add map of content, enter contentrenderer into accordion content slots
nucleogenesis 81bc2f4
style fixes; selected resources count reflects resource pool length
nucleogenesis 227380f
update object specs to reflect actual data; update test accordingly
nucleogenesis 95731e7
:interactive="false" on ContentRenderer in accordion
nucleogenesis 29a97ba
move working resource pool business into ResourceSelection
nucleogenesis 2751f26
fix rebase mistake
nucleogenesis File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,5 @@ | ||
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'; | ||
|
@@ -9,10 +8,10 @@ import { ContentNodeKinds } from 'kolibri.coreVue.vuex.constants'; | |
import { ChannelResource, ExamResource } from 'kolibri.resources'; | ||
import { validateObject, objectWithDefaults } from 'kolibri.utils.objectSpecs'; | ||
import { get, set } from '@vueuse/core'; | ||
import { computed, ref, provide, inject } from 'kolibri.lib.vueCompositionApi'; | ||
import { computed, ref, watch, provide, inject } from 'kolibri.lib.vueCompositionApi'; | ||
import logging from 'kolibri.lib.logging'; | ||
// TODO: Probably move this to this file's local dir | ||
import selectQuestions from '../modules/examCreation/selectQuestions.js'; | ||
import selectQuestions from '../utils/selectQuestions.js'; | ||
import { Quiz, QuizSection, QuizQuestion, QuizExercise } from './quizCreationSpecs.js'; | ||
|
||
const logger = logging.getLogger(__filename); | ||
|
@@ -30,14 +29,6 @@ function validateQuiz(quiz) { | |
return validateObject(quiz, Quiz); | ||
} | ||
|
||
/** | ||
* @param {QuizExercise} o - The resource to check | ||
* @returns {boolean} - True if the resource is a valid QuizExercise | ||
*/ | ||
function isExercise(o) { | ||
return o.kind === ContentNodeKinds.EXERCISE; | ||
} | ||
|
||
/** | ||
* Composable function presenting primary interface for Quiz Creation | ||
*/ | ||
|
@@ -46,10 +37,6 @@ 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)); | ||
|
@@ -178,13 +165,8 @@ export default function useQuizCreation(DEBUG = false) { | |
} else if (question_count > (targetSection.question_count || 0)) { | ||
// If the question_count is being increased, we need to add new questions to the end of the | ||
// questions array | ||
const newQuestions = selectQuestions( | ||
question_count - (targetSection.question_count || 0), | ||
targetSection.resource_pool.map(r => r.content_id), | ||
targetSection.resource_pool.map(r => r.title), | ||
targetSection.resource_pool.map(r => r.questions.map(q => q.question_id)), | ||
get(_quiz).seed | ||
); | ||
const numQuestionsToAdd = question_count - (targetSection.question_count || 0); | ||
const newQuestions = selectRandomQuestionsFromResources(numQuestionsToAdd); | ||
updates.questions = [...targetSection.questions, ...newQuestions]; | ||
} | ||
} | ||
|
@@ -201,6 +183,23 @@ export default function useQuizCreation(DEBUG = false) { | |
}); | ||
} | ||
|
||
/** | ||
* @description Selects random questions from the active section's `resource_pool` - no side | ||
* effects | ||
* @param numQuestions | ||
* @returns {QuizQuestion[]} | ||
*/ | ||
function selectRandomQuestionsFromResources(numQuestions) { | ||
const pool = get(activeResourcePool); | ||
return selectQuestions( | ||
numQuestions, | ||
pool.map(r => r.content_id), | ||
pool.map(r => r.title), | ||
pool.map(r => r.assessmentmetadata.assessment_item_ids), | ||
get(_quiz).seed | ||
); | ||
} | ||
|
||
/** | ||
* @param {QuizQuestion[]} newQuestions | ||
* @affects _quiz - Updates the active section's `questions` property | ||
|
@@ -272,12 +271,16 @@ export default function useQuizCreation(DEBUG = false) { | |
setActiveSection(newSection.section_id); | ||
} | ||
_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)); | ||
// Set watcher once we have a section in place | ||
watch(activeResourcePool, (resourcePool, old) => { | ||
if (!isEqual(resourcePool, old)) { | ||
updateSection({ | ||
section_id: get(_activeSectionId), | ||
questions: selectRandomQuestionsFromResources(get(activeSection).question_count), | ||
}); | ||
} | ||
}); | ||
} | ||
|
||
/** | ||
|
@@ -364,11 +367,6 @@ 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() { | ||
|
@@ -391,21 +389,6 @@ export default function useQuizCreation(DEBUG = false) { | |
} | ||
|
||
// Utilities | ||
/** | ||
* @params {string} section_id - The section_id whose resource_pool we'll use. | ||
* @returns {QuizQuestion[]} | ||
*/ | ||
/* | ||
function _getQuestionsFromSection(section_id) { | ||
const section = get(allSections).find(s => s.section_id === section_id); | ||
if (!section) { | ||
throw new Error(`Section with id ${section_id} not found.`); | ||
} | ||
return get(activeExercisePool).reduce((acc, exercise) => { | ||
return [...acc, ...exercise.questions]; | ||
}, []); | ||
} | ||
*/ | ||
|
||
// Computed properties | ||
/** @type {ComputedRef<Quiz>} The value of _quiz */ | ||
|
@@ -422,10 +405,13 @@ export default function useQuizCreation(DEBUG = false) { | |
); | ||
/** @type {ComputedRef<QuizExercise[]>} The active section's `resource_pool` */ | ||
const activeResourcePool = computed(() => get(activeSection).resource_pool); | ||
/** @type {ComputedRef<ExerciseResource[]>} The active section's `resource_pool` - that is, | ||
* Exercises from which we will enumerate all | ||
* available questions */ | ||
const activeExercisePool = computed(() => get(activeResourcePool).filter(isExercise)); | ||
/** @type {ComputedRef<QuizExercise[]>} The active section's `resource_pool` */ | ||
const activeResourceMap = computed(() => | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The alternative here would be to store an activeResourceMap as the basic representation, and have Looks like this is never exported, so maybe we should just set it as a map in the first place? |
||
get(activeResourcePool).reduce((acc, resource) => { | ||
acc[resource.content_id] = resource; | ||
return acc; | ||
}, {}) | ||
); | ||
/** @type {ComputedRef<QuizQuestion[]>} All questions in the active section's `resource_pool` | ||
* exercises */ | ||
const activeQuestionsPool = computed(() => []); | ||
|
@@ -440,9 +426,6 @@ 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 */ | ||
|
||
|
@@ -480,45 +463,12 @@ 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); | ||
|
@@ -528,15 +478,13 @@ 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('activeResourceMap', activeResourceMap); | ||
provide('activeQuestionsPool', activeQuestionsPool); | ||
provide('activeQuestions', activeQuestions); | ||
provide('selectedActiveQuestions', selectedActiveQuestions); | ||
|
@@ -548,13 +496,8 @@ export default function useQuizCreation(DEBUG = false) { | |
return { | ||
// Methods | ||
saveQuiz, | ||
initializeWorkingResourcePool, | ||
removeFromWorkingResourcePool, | ||
addToWorkingResourcePool, | ||
contentPresentInWorkingResourcePool, | ||
updateSection, | ||
replaceSelectedQuestions, | ||
resetWorkingResourcePool, | ||
addSection, | ||
removeSection, | ||
setActiveSection, | ||
|
@@ -569,9 +512,8 @@ export default function useQuizCreation(DEBUG = false) { | |
allSections, | ||
activeSection, | ||
inactiveSections, | ||
workingResourcePool, | ||
activeResourcePool, | ||
activeExercisePool, | ||
activeResourceMap, | ||
activeQuestionsPool, | ||
activeQuestions, | ||
selectedActiveQuestions, | ||
|
@@ -594,14 +536,9 @@ 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'); | ||
|
@@ -614,8 +551,7 @@ export function injectQuizCreation() { | |
const activeSection = inject('activeSection'); | ||
const inactiveSections = inject('inactiveSections'); | ||
const activeResourcePool = inject('activeResourcePool'); | ||
const workingResourcePool = inject('workingResourcePool'); | ||
const activeExercisePool = inject('activeExercisePool'); | ||
const activeResourceMap = inject('activeResourceMap'); | ||
const activeQuestionsPool = inject('activeQuestionsPool'); | ||
const activeQuestions = inject('activeQuestions'); | ||
const selectedActiveQuestions = inject('selectedActiveQuestions'); | ||
|
@@ -627,14 +563,9 @@ export function injectQuizCreation() { | |
return { | ||
// Methods | ||
saveQuiz, | ||
initializeWorkingResourcePool, | ||
addToWorkingResourcePool, | ||
contentPresentInWorkingResourcePool, | ||
removeFromWorkingResourcePool, | ||
deleteActiveSelectedQuestions, | ||
selectAllQuestions, | ||
updateSection, | ||
resetWorkingResourcePool, | ||
replaceSelectedQuestions, | ||
addSection, | ||
removeSection, | ||
|
@@ -651,9 +582,8 @@ export function injectQuizCreation() { | |
allSections, | ||
activeSection, | ||
inactiveSections, | ||
workingResourcePool, | ||
activeResourcePool, | ||
activeExercisePool, | ||
activeResourceMap, | ||
activeQuestionsPool, | ||
activeQuestions, | ||
selectedActiveQuestions, | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Presumably we initiate this whenever we do, so we might be able to decide more directly when to do this rather than a potentially expensive or unreliable isEqual call?
Not clear that this is actually a problem, so definitely not a blocker.