@pie-element/image-cloze-association 8.3.2-next.0 → 8.3.2-next.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -39,9 +39,10 @@ var model = function model(question, session, env) {
39
39
  return new Promise(function (resolve) {
40
40
  var shouldIncludeCorrectResponse = env.mode === 'evaluate';
41
41
 
42
- var _getCompleteResponseD = (0, _utils.getCompleteResponseDetails)(questionCamelized.validation),
42
+ var _getCompleteResponseD = (0, _utils.getCompleteResponseDetails)(questionCamelized.validation, questionCamelized.possibleResponses),
43
43
  responseAreasToBeFilled = _getCompleteResponseD.responseAreasToBeFilled,
44
- completeResponses = _getCompleteResponseD.possibleResponses;
44
+ completeResponses = _getCompleteResponseD.possibleResponses,
45
+ hasUnplacedChoices = _getCompleteResponseD.hasUnplacedChoices;
45
46
 
46
47
  var out = _objectSpread(_objectSpread({
47
48
  disabled: env.mode !== 'gather',
@@ -50,7 +51,8 @@ var model = function model(question, session, env) {
50
51
  responseCorrect: shouldIncludeCorrectResponse ? getScore(questionCamelized, session) === 1 : undefined,
51
52
  validation: shouldIncludeCorrectResponse ? questionCamelized.validation : undefined,
52
53
  responseAreasToBeFilled: responseAreasToBeFilled,
53
- completeResponses: completeResponses
54
+ completeResponses: completeResponses,
55
+ hasUnplacedChoices: hasUnplacedChoices
54
56
  });
55
57
 
56
58
  if (questionNormalized.shuffle) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.js"],"names":["log","normalize","question","defaults","model","session","env","questionNormalized","questionCamelized","Promise","resolve","shouldIncludeCorrectResponse","mode","validation","responseAreasToBeFilled","completeResponses","possibleResponses","out","disabled","responseCorrect","getScore","undefined","shuffle","possible_responses","role","teacherInstructions","teacherInstructionsEnabled","rationale","isResponseCorrect","correctResponses","responses","isCorrect","totalValidResponses","forEach","value","images","length","answers","answer","index","containerIndex","indexOf","splice","keepNonEmptyResponses","filtered","filter","response","isDefaultOrAltResponseCorrect","altResponses","validResponse","altResponse","getDeductionPerContainer","valid","totalStack","item","incorrectStack","maxValid","ignored","slice","getPartialScore","maxResponsePerZone","correctAnswers","incorrectAnswers","all","deductionList","id","nonEmptyResponses","denominator","str","toFixed","parseFloat","config","isPartialScoring","partialScoring","enabled","correct","outcome","score","empty","configCamelized","createCorrectResponseSession","valid_response","container","i","v","push","getInnerText","html","replaceAll","getContent","replace","validate","errors","field","required"],"mappings":"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","sourcesContent":["import debug from 'debug';\nimport { camelizeKeys } from 'humps';\nimport { partialScoring } from '@pie-lib/controller-utils';\nimport { cloneDeep, isEmpty, shuffle } from 'lodash';\n\nimport defaults from './defaults';\nimport { getAllUniqueCorrectness, getCompleteResponseDetails } from './utils';\n\nconst log = debug('pie-elements:image-cloze-association:controller');\n\nexport const normalize = (question) => ({ ...defaults, ...question });\n\nexport const model = (question, session, env) => {\n const questionNormalized = normalize(question);\n const questionCamelized = camelizeKeys(questionNormalized);\n\n return new Promise((resolve) => {\n const shouldIncludeCorrectResponse = env.mode === 'evaluate';\n\n const { responseAreasToBeFilled, possibleResponses: completeResponses } = getCompleteResponseDetails(\n questionCamelized.validation,\n );\n\n const out = {\n disabled: env.mode !== 'gather',\n mode: env.mode,\n ...questionCamelized,\n responseCorrect: shouldIncludeCorrectResponse ? getScore(questionCamelized, session) === 1 : undefined,\n validation: shouldIncludeCorrectResponse ? questionCamelized.validation : undefined,\n responseAreasToBeFilled,\n completeResponses,\n };\n\n if (questionNormalized.shuffle) {\n out.possibleResponses = shuffle(questionNormalized.possible_responses);\n }\n\n if (env.role === 'instructor' && (env.mode === 'view' || env.mode === 'evaluate')) {\n out.teacherInstructions = questionCamelized.teacherInstructionsEnabled\n ? questionCamelized.teacherInstructions\n : null;\n out.rationale = questionCamelized.rationale ? questionCamelized.rationale : null;\n } else {\n out.teacherInstructions = null;\n out.rationale = null;\n }\n\n resolve(out);\n });\n};\n\nexport const isResponseCorrect = (correctResponses, session) => {\n const responses = cloneDeep(correctResponses);\n let isCorrect = true;\n let totalValidResponses = 0;\n\n if (!session || isEmpty(session)) {\n return false;\n }\n\n responses.forEach((value) => (totalValidResponses += (value.images || []).length));\n\n if (session.answers && totalValidResponses === session.answers.length) {\n session.answers.forEach((answer) => {\n const index = (responses[answer.containerIndex]?.images || []).indexOf(answer.value);\n\n if (index >= 0) {\n // remove response from correct responses array to ensure that duplicates are evaluated correctly\n responses[answer.containerIndex].images.splice(index, 1);\n } else {\n isCorrect = false;\n }\n });\n } else {\n isCorrect = false;\n }\n\n return isCorrect;\n};\n\n// This applies for correct responses that have empty values\nconst keepNonEmptyResponses = (responses) => {\n const filtered = responses.filter((response) => response.images && response.images.length);\n return cloneDeep(filtered);\n};\n\n// This applies for items that don't support partial scoring.\nconst isDefaultOrAltResponseCorrect = (question, session) => {\n const {\n validation: { altResponses },\n } = question;\n let {\n validation: {\n validResponse: { value },\n },\n } = question;\n\n let isCorrect = isResponseCorrect(value, session);\n\n // Look for correct answers in alternate responses.\n if (!isCorrect && altResponses && altResponses.length) {\n altResponses.forEach((altResponse) => {\n if (isResponseCorrect(altResponse.value, session)) {\n isCorrect = true;\n }\n });\n }\n return isCorrect;\n};\n\n// Deduct only the items that exceeded the maximum valid response per container.\nconst getDeductionPerContainer = (containerIndex, answers, valid) => {\n const totalStack = answers.filter((item) => item.containerIndex === containerIndex);\n const incorrectStack = totalStack.filter((item) => !item.isCorrect);\n const maxValid = (valid.value[containerIndex].images || []).length;\n\n if (totalStack.length > maxValid) {\n const ignored = totalStack.length - maxValid;\n return incorrectStack.slice(-ignored);\n }\n return [];\n};\n\nexport const getPartialScore = (question, session) => {\n const {\n validation: { validResponse },\n maxResponsePerZone,\n } = question;\n let correctAnswers = 0;\n let incorrectAnswers = 0;\n let possibleResponses = 0;\n\n if (!session || isEmpty(session)) {\n return 0;\n }\n\n validResponse.value.forEach((value) => (possibleResponses += (value.images || []).length));\n\n if (session.answers && session.answers.length) {\n const all = getAllUniqueCorrectness(session.answers, validResponse.value);\n correctAnswers = all.filter((item) => item.isCorrect).length;\n incorrectAnswers = all.filter((item) => !item.isCorrect).length;\n\n // deduction rules: https://docs.google.com/document/d/1Oprm8Qs5fg_Dwoj2pNpsfu4D63QgCZgvcqTgeaVel7I/edit\n session.answers.forEach((answer) => {\n if (maxResponsePerZone > 1) {\n const deductionList = getDeductionPerContainer(answer.containerIndex, all, validResponse);\n\n if (deductionList.length) {\n deductionList.forEach((item) => {\n if (item.id === answer.id) {\n correctAnswers -= 1;\n }\n });\n }\n }\n });\n\n if (!maxResponsePerZone || maxResponsePerZone <= 1) {\n correctAnswers -= incorrectAnswers;\n }\n } else {\n correctAnswers = 0;\n }\n // negative values will implicitly make the score equal to zero\n correctAnswers = correctAnswers < 0 ? 0 : correctAnswers;\n\n // use length of validResponse since some containers can be left empty\n const nonEmptyResponses = keepNonEmptyResponses(validResponse.value);\n const denominator = maxResponsePerZone > 1 ? possibleResponses : (nonEmptyResponses || []).length;\n const str = (correctAnswers / denominator).toFixed(2);\n\n return parseFloat(str);\n};\n\nconst getScore = (config, session, env = {}) => {\n const isPartialScoring = partialScoring.enabled(config, env);\n const correct = isDefaultOrAltResponseCorrect(config, session);\n\n return isPartialScoring ? getPartialScore(config, session) : correct ? 1 : 0;\n};\n\nexport const outcome = (config, session, env = {}) => {\n return new Promise((resolve) => {\n log('outcome...');\n if (!session || isEmpty(session)) {\n resolve({ score: 0, empty: true });\n }\n\n const configCamelized = camelizeKeys(config);\n\n if (session.answers || []) {\n const score = getScore(configCamelized, session, env);\n resolve({ score });\n }\n });\n};\n\nexport const createCorrectResponseSession = (question, env) => {\n return new Promise((resolve) => {\n if (env.mode !== 'evaluate' && env.role === 'instructor') {\n const {\n validation: {\n valid_response: { value },\n },\n } = question;\n const answers = [];\n\n if (value) {\n value.forEach((container, i) => {\n (container.images || []).forEach((v) => {\n answers.push({\n value: v,\n containerIndex: i,\n });\n });\n });\n }\n\n resolve({\n answers,\n id: '1',\n });\n } else {\n resolve(null);\n }\n });\n};\n\n// remove all html tags\nconst getInnerText = (html) => (html || '').replaceAll(/<[^>]*>/g, '');\n\n// remove all html tags except img, iframe and source tag for audio\nconst getContent = (html) => (html || '').replace(/(<(?!img|iframe|source)([^>]+)>)/gi, '');\n\nexport const validate = (model = {}, config = {}) => {\n const errors = {};\n\n ['teacherInstructions'].forEach((field) => {\n if (config[field]?.required && !getContent(model[field])) {\n errors[field] = 'This field is required.';\n }\n });\n\n return errors;\n};\n"],"file":"index.js"}
1
+ {"version":3,"sources":["../src/index.js"],"names":["log","normalize","question","defaults","model","session","env","questionNormalized","questionCamelized","Promise","resolve","shouldIncludeCorrectResponse","mode","validation","possibleResponses","responseAreasToBeFilled","completeResponses","hasUnplacedChoices","out","disabled","responseCorrect","getScore","undefined","shuffle","possible_responses","role","teacherInstructions","teacherInstructionsEnabled","rationale","isResponseCorrect","correctResponses","responses","isCorrect","totalValidResponses","forEach","value","images","length","answers","answer","index","containerIndex","indexOf","splice","keepNonEmptyResponses","filtered","filter","response","isDefaultOrAltResponseCorrect","altResponses","validResponse","altResponse","getDeductionPerContainer","valid","totalStack","item","incorrectStack","maxValid","ignored","slice","getPartialScore","maxResponsePerZone","correctAnswers","incorrectAnswers","all","deductionList","id","nonEmptyResponses","denominator","str","toFixed","parseFloat","config","isPartialScoring","partialScoring","enabled","correct","outcome","score","empty","configCamelized","createCorrectResponseSession","valid_response","container","i","v","push","getInnerText","html","replaceAll","getContent","replace","validate","errors","field","required"],"mappings":"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","sourcesContent":["import debug from 'debug';\nimport { camelizeKeys } from 'humps';\nimport { partialScoring } from '@pie-lib/controller-utils';\nimport { cloneDeep, isEmpty, shuffle } from 'lodash';\n\nimport defaults from './defaults';\nimport { getAllUniqueCorrectness, getCompleteResponseDetails } from './utils';\n\nconst log = debug('pie-elements:image-cloze-association:controller');\n\nexport const normalize = (question) => ({ ...defaults, ...question });\n\nexport const model = (question, session, env) => {\n const questionNormalized = normalize(question);\n const questionCamelized = camelizeKeys(questionNormalized);\n\n return new Promise((resolve) => {\n const shouldIncludeCorrectResponse = env.mode === 'evaluate';\n\n const {\n responseAreasToBeFilled,\n possibleResponses: completeResponses,\n hasUnplacedChoices,\n } = getCompleteResponseDetails(questionCamelized.validation, questionCamelized.possibleResponses);\n\n const out = {\n disabled: env.mode !== 'gather',\n mode: env.mode,\n ...questionCamelized,\n responseCorrect: shouldIncludeCorrectResponse ? getScore(questionCamelized, session) === 1 : undefined,\n validation: shouldIncludeCorrectResponse ? questionCamelized.validation : undefined,\n responseAreasToBeFilled,\n completeResponses,\n hasUnplacedChoices,\n };\n\n if (questionNormalized.shuffle) {\n out.possibleResponses = shuffle(questionNormalized.possible_responses);\n }\n\n if (env.role === 'instructor' && (env.mode === 'view' || env.mode === 'evaluate')) {\n out.teacherInstructions = questionCamelized.teacherInstructionsEnabled\n ? questionCamelized.teacherInstructions\n : null;\n out.rationale = questionCamelized.rationale ? questionCamelized.rationale : null;\n } else {\n out.teacherInstructions = null;\n out.rationale = null;\n }\n\n resolve(out);\n });\n};\n\nexport const isResponseCorrect = (correctResponses, session) => {\n const responses = cloneDeep(correctResponses);\n let isCorrect = true;\n let totalValidResponses = 0;\n\n if (!session || isEmpty(session)) {\n return false;\n }\n\n responses.forEach((value) => (totalValidResponses += (value.images || []).length));\n\n if (session.answers && totalValidResponses === session.answers.length) {\n session.answers.forEach((answer) => {\n const index = (responses[answer.containerIndex]?.images || []).indexOf(answer.value);\n\n if (index >= 0) {\n // remove response from correct responses array to ensure that duplicates are evaluated correctly\n responses[answer.containerIndex].images.splice(index, 1);\n } else {\n isCorrect = false;\n }\n });\n } else {\n isCorrect = false;\n }\n\n return isCorrect;\n};\n\n// This applies for correct responses that have empty values\nconst keepNonEmptyResponses = (responses) => {\n const filtered = responses.filter((response) => response.images && response.images.length);\n return cloneDeep(filtered);\n};\n\n// This applies for items that don't support partial scoring.\nconst isDefaultOrAltResponseCorrect = (question, session) => {\n const {\n validation: { altResponses },\n } = question;\n let {\n validation: {\n validResponse: { value },\n },\n } = question;\n\n let isCorrect = isResponseCorrect(value, session);\n\n // Look for correct answers in alternate responses.\n if (!isCorrect && altResponses && altResponses.length) {\n altResponses.forEach((altResponse) => {\n if (isResponseCorrect(altResponse.value, session)) {\n isCorrect = true;\n }\n });\n }\n return isCorrect;\n};\n\n// Deduct only the items that exceeded the maximum valid response per container.\nconst getDeductionPerContainer = (containerIndex, answers, valid) => {\n const totalStack = answers.filter((item) => item.containerIndex === containerIndex);\n const incorrectStack = totalStack.filter((item) => !item.isCorrect);\n const maxValid = (valid.value[containerIndex].images || []).length;\n\n if (totalStack.length > maxValid) {\n const ignored = totalStack.length - maxValid;\n return incorrectStack.slice(-ignored);\n }\n return [];\n};\n\nexport const getPartialScore = (question, session) => {\n const {\n validation: { validResponse },\n maxResponsePerZone,\n } = question;\n let correctAnswers = 0;\n let incorrectAnswers = 0;\n let possibleResponses = 0;\n\n if (!session || isEmpty(session)) {\n return 0;\n }\n\n validResponse.value.forEach((value) => (possibleResponses += (value.images || []).length));\n\n if (session.answers && session.answers.length) {\n const all = getAllUniqueCorrectness(session.answers, validResponse.value);\n correctAnswers = all.filter((item) => item.isCorrect).length;\n incorrectAnswers = all.filter((item) => !item.isCorrect).length;\n\n // deduction rules: https://docs.google.com/document/d/1Oprm8Qs5fg_Dwoj2pNpsfu4D63QgCZgvcqTgeaVel7I/edit\n session.answers.forEach((answer) => {\n if (maxResponsePerZone > 1) {\n const deductionList = getDeductionPerContainer(answer.containerIndex, all, validResponse);\n\n if (deductionList.length) {\n deductionList.forEach((item) => {\n if (item.id === answer.id) {\n correctAnswers -= 1;\n }\n });\n }\n }\n });\n\n if (!maxResponsePerZone || maxResponsePerZone <= 1) {\n correctAnswers -= incorrectAnswers;\n }\n } else {\n correctAnswers = 0;\n }\n // negative values will implicitly make the score equal to zero\n correctAnswers = correctAnswers < 0 ? 0 : correctAnswers;\n\n // use length of validResponse since some containers can be left empty\n const nonEmptyResponses = keepNonEmptyResponses(validResponse.value);\n const denominator = maxResponsePerZone > 1 ? possibleResponses : (nonEmptyResponses || []).length;\n const str = (correctAnswers / denominator).toFixed(2);\n\n return parseFloat(str);\n};\n\nconst getScore = (config, session, env = {}) => {\n const isPartialScoring = partialScoring.enabled(config, env);\n const correct = isDefaultOrAltResponseCorrect(config, session);\n\n return isPartialScoring ? getPartialScore(config, session) : correct ? 1 : 0;\n};\n\nexport const outcome = (config, session, env = {}) => {\n return new Promise((resolve) => {\n log('outcome...');\n if (!session || isEmpty(session)) {\n resolve({ score: 0, empty: true });\n }\n\n const configCamelized = camelizeKeys(config);\n\n if (session.answers || []) {\n const score = getScore(configCamelized, session, env);\n resolve({ score });\n }\n });\n};\n\nexport const createCorrectResponseSession = (question, env) => {\n return new Promise((resolve) => {\n if (env.mode !== 'evaluate' && env.role === 'instructor') {\n const {\n validation: {\n valid_response: { value },\n },\n } = question;\n const answers = [];\n\n if (value) {\n value.forEach((container, i) => {\n (container.images || []).forEach((v) => {\n answers.push({\n value: v,\n containerIndex: i,\n });\n });\n });\n }\n\n resolve({\n answers,\n id: '1',\n });\n } else {\n resolve(null);\n }\n });\n};\n\n// remove all html tags\nconst getInnerText = (html) => (html || '').replaceAll(/<[^>]*>/g, '');\n\n// remove all html tags except img, iframe and source tag for audio\nconst getContent = (html) => (html || '').replace(/(<(?!img|iframe|source)([^>]+)>)/gi, '');\n\nexport const validate = (model = {}, config = {}) => {\n const errors = {};\n\n ['teacherInstructions'].forEach((field) => {\n if (config[field]?.required && !getContent(model[field])) {\n errors[field] = 'This field is required.';\n }\n });\n\n return errors;\n};\n"],"file":"index.js"}
@@ -60,7 +60,7 @@ var getAllUniqueCorrectness = function getAllUniqueCorrectness(answers, validRes
60
60
 
61
61
  exports.getAllUniqueCorrectness = getAllUniqueCorrectness;
62
62
 
63
- var getCompleteResponseDetails = function getCompleteResponseDetails(validation) {
63
+ var getCompleteResponseDetails = function getCompleteResponseDetails(validation, allChoices) {
64
64
  var extractImages = function extractImages(response) {
65
65
  return ((response === null || response === void 0 ? void 0 : response.value) || []).map(function (container) {
66
66
  return container.images;
@@ -88,10 +88,17 @@ var getCompleteResponseDetails = function getCompleteResponseDetails(validation)
88
88
  if (filledResponseAreas < responseAreasToBeFilled) {
89
89
  responseAreasToBeFilled = filledResponseAreas;
90
90
  }
91
+ }); // check if any correct answer have any unplaced answer choices
92
+
93
+ var hasUnplacedChoices = possibleResponses.some(function (response) {
94
+ return !allChoices.every(function (val) {
95
+ return response.includes(val);
96
+ });
91
97
  });
92
98
  return {
93
99
  responseAreasToBeFilled: responseAreasToBeFilled,
94
- possibleResponses: possibleResponses
100
+ possibleResponses: possibleResponses,
101
+ hasUnplacedChoices: hasUnplacedChoices
95
102
  };
96
103
  };
97
104
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils.js"],"names":["getAllCorrectness","answers","responses","map","answer","isCorrect","containerIndex","images","includes","value","getValidAnswer","response","filter","res","getAllUniqueCorrectness","validResponses","allCorrectness","forEach","answer1","valuesToParse","answer2","length","shift","index","finalAnswer","id","valid","getCompleteResponseDetails","validation","extractImages","container","countFilledResponseAreas","validResponse","altResponses","imagesPerContainer","possibleResponses","flat","responseAreasToBeFilled","altResponse","altImagesPerContainer","filledResponseAreas","push"],"mappings":"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","sourcesContent":["// functions also used in src/utils-correctness.js\n\nconst getAllCorrectness = (answers, responses) =>\n (answers || []).map((answer) => ({\n ...answer,\n isCorrect: ((responses[answer.containerIndex] && responses[answer.containerIndex].images) || []).includes(\n answer.value,\n ),\n }));\n\nconst getValidAnswer = (answer, response) =>\n ((response[answer.containerIndex] && response[answer.containerIndex].images) || []).filter(\n (res) => res === answer.value,\n );\n\nexport const getAllUniqueCorrectness = (answers, validResponses) => {\n let allCorrectness = getAllCorrectness(answers, validResponses);\n\n answers.forEach((answer1) => {\n const valuesToParse = answers.filter(\n (answer2) => answer2.value === answer1.value && answer2.containerIndex === answer1.containerIndex,\n );\n\n if (valuesToParse.length > 1) {\n // point only to duplicates but first\n valuesToParse.shift();\n // mark duplicates as incorrect\n valuesToParse.forEach((value, index) => {\n allCorrectness = (allCorrectness || []).map((finalAnswer) => {\n if (finalAnswer.id === value.id) {\n let valid = getValidAnswer(finalAnswer, validResponses);\n return {\n ...finalAnswer,\n isCorrect: valid.length > index + 1,\n };\n }\n return finalAnswer;\n });\n });\n }\n });\n return allCorrectness;\n};\n\n// calculate the minimum number of populated response areas (categories) in the correct answer or alternates\n// and create an array with the possible responses ids\nexport const getCompleteResponseDetails = (validation) => {\n const extractImages = (response) => (response?.value || []).map((container) => container.images);\n const countFilledResponseAreas = (container) => (container || []).filter((images) => images.length).length;\n\n const { validResponse, altResponses } = validation || {};\n const imagesPerContainer = extractImages(validResponse);\n const possibleResponses = [imagesPerContainer.flat()];\n let responseAreasToBeFilled = countFilledResponseAreas(imagesPerContainer);\n\n (altResponses || []).forEach((altResponse) => {\n const altImagesPerContainer = extractImages(altResponse);\n const filledResponseAreas = countFilledResponseAreas(altImagesPerContainer);\n possibleResponses.push(altImagesPerContainer.flat());\n\n if (filledResponseAreas < responseAreasToBeFilled) {\n responseAreasToBeFilled = filledResponseAreas;\n }\n });\n\n return { responseAreasToBeFilled, possibleResponses };\n};\n"],"file":"utils.js"}
1
+ {"version":3,"sources":["../src/utils.js"],"names":["getAllCorrectness","answers","responses","map","answer","isCorrect","containerIndex","images","includes","value","getValidAnswer","response","filter","res","getAllUniqueCorrectness","validResponses","allCorrectness","forEach","answer1","valuesToParse","answer2","length","shift","index","finalAnswer","id","valid","getCompleteResponseDetails","validation","allChoices","extractImages","container","countFilledResponseAreas","validResponse","altResponses","imagesPerContainer","possibleResponses","flat","responseAreasToBeFilled","altResponse","altImagesPerContainer","filledResponseAreas","push","hasUnplacedChoices","some","every","val"],"mappings":"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","sourcesContent":["// functions also used in src/utils-correctness.js\n\nconst getAllCorrectness = (answers, responses) =>\n (answers || []).map((answer) => ({\n ...answer,\n isCorrect: ((responses[answer.containerIndex] && responses[answer.containerIndex].images) || []).includes(\n answer.value,\n ),\n }));\n\nconst getValidAnswer = (answer, response) =>\n ((response[answer.containerIndex] && response[answer.containerIndex].images) || []).filter(\n (res) => res === answer.value,\n );\n\nexport const getAllUniqueCorrectness = (answers, validResponses) => {\n let allCorrectness = getAllCorrectness(answers, validResponses);\n\n answers.forEach((answer1) => {\n const valuesToParse = answers.filter(\n (answer2) => answer2.value === answer1.value && answer2.containerIndex === answer1.containerIndex,\n );\n\n if (valuesToParse.length > 1) {\n // point only to duplicates but first\n valuesToParse.shift();\n // mark duplicates as incorrect\n valuesToParse.forEach((value, index) => {\n allCorrectness = (allCorrectness || []).map((finalAnswer) => {\n if (finalAnswer.id === value.id) {\n let valid = getValidAnswer(finalAnswer, validResponses);\n return {\n ...finalAnswer,\n isCorrect: valid.length > index + 1,\n };\n }\n return finalAnswer;\n });\n });\n }\n });\n return allCorrectness;\n};\n\n// calculate the minimum number of populated response areas (categories) in the correct answer or alternates\n// and create an array with the possible responses ids\nexport const getCompleteResponseDetails = (validation, allChoices) => {\n const extractImages = (response) => (response?.value || []).map((container) => container.images);\n const countFilledResponseAreas = (container) => (container || []).filter((images) => images.length).length;\n\n const { validResponse, altResponses } = validation || {};\n const imagesPerContainer = extractImages(validResponse);\n const possibleResponses = [imagesPerContainer.flat()];\n let responseAreasToBeFilled = countFilledResponseAreas(imagesPerContainer);\n\n (altResponses || []).forEach((altResponse) => {\n const altImagesPerContainer = extractImages(altResponse);\n const filledResponseAreas = countFilledResponseAreas(altImagesPerContainer);\n possibleResponses.push(altImagesPerContainer.flat());\n\n if (filledResponseAreas < responseAreasToBeFilled) {\n responseAreasToBeFilled = filledResponseAreas;\n }\n });\n\n // check if any correct answer have any unplaced answer choices\n const hasUnplacedChoices = possibleResponses.some(\n (response) => !allChoices.every((val) => response.includes(val)),\n );\n\n return { responseAreasToBeFilled, possibleResponses, hasUnplacedChoices };\n};\n"],"file":"utils.js"}
@@ -17,9 +17,11 @@ export const model = (question, session, env) => {
17
17
  return new Promise((resolve) => {
18
18
  const shouldIncludeCorrectResponse = env.mode === 'evaluate';
19
19
 
20
- const { responseAreasToBeFilled, possibleResponses: completeResponses } = getCompleteResponseDetails(
21
- questionCamelized.validation,
22
- );
20
+ const {
21
+ responseAreasToBeFilled,
22
+ possibleResponses: completeResponses,
23
+ hasUnplacedChoices,
24
+ } = getCompleteResponseDetails(questionCamelized.validation, questionCamelized.possibleResponses);
23
25
 
24
26
  const out = {
25
27
  disabled: env.mode !== 'gather',
@@ -29,6 +31,7 @@ export const model = (question, session, env) => {
29
31
  validation: shouldIncludeCorrectResponse ? questionCamelized.validation : undefined,
30
32
  responseAreasToBeFilled,
31
33
  completeResponses,
34
+ hasUnplacedChoices,
32
35
  };
33
36
 
34
37
  if (questionNormalized.shuffle) {
@@ -44,7 +44,7 @@ export const getAllUniqueCorrectness = (answers, validResponses) => {
44
44
 
45
45
  // calculate the minimum number of populated response areas (categories) in the correct answer or alternates
46
46
  // and create an array with the possible responses ids
47
- export const getCompleteResponseDetails = (validation) => {
47
+ export const getCompleteResponseDetails = (validation, allChoices) => {
48
48
  const extractImages = (response) => (response?.value || []).map((container) => container.images);
49
49
  const countFilledResponseAreas = (container) => (container || []).filter((images) => images.length).length;
50
50
 
@@ -63,5 +63,10 @@ export const getCompleteResponseDetails = (validation) => {
63
63
  }
64
64
  });
65
65
 
66
- return { responseAreasToBeFilled, possibleResponses };
66
+ // check if any correct answer have any unplaced answer choices
67
+ const hasUnplacedChoices = possibleResponses.some(
68
+ (response) => !allChoices.every((val) => response.includes(val)),
69
+ );
70
+
71
+ return { responseAreasToBeFilled, possibleResponses, hasUnplacedChoices };
67
72
  };
package/lib/index.js CHANGED
@@ -63,6 +63,7 @@ var ImageClozeAssociation = /*#__PURE__*/function (_HTMLElement) {
63
63
  completeAudioEnabled = _ref.completeAudioEnabled,
64
64
  completeResponses = _ref.completeResponses,
65
65
  duplicateResponses = _ref.duplicateResponses,
66
+ hasUnplacedChoices = _ref.hasUnplacedChoices,
66
67
  maxResponsePerZone = _ref.maxResponsePerZone,
67
68
  responseAreasToBeFilled = _ref.responseAreasToBeFilled;
68
69
 
@@ -101,23 +102,22 @@ var ImageClozeAssociation = /*#__PURE__*/function (_HTMLElement) {
101
102
  if (duplicateResponses) {
102
103
  // an answer choice can be used multiple times
103
104
  return areResponseAreasFilled;
105
+ } // any correct answer have any unplaced answer choices (by the author)
106
+
107
+
108
+ if (hasUnplacedChoices) {
109
+ return areResponseAreasFilled;
104
110
  }
105
111
 
106
112
  var allAnswersValue = answers.map(function (answer) {
107
113
  return answer.value;
108
- }); // check if any correct answer have any unplaced answer choices
114
+ }); // check if any correct answer have any unplaced answer choices (by the student)
109
115
 
110
116
  var requiredAnswersPlaced = completeResponses.some(function (response) {
111
117
  return response.every(function (val) {
112
118
  return allAnswersValue.includes(val);
113
119
  });
114
- });
115
-
116
- if (!requiredAnswersPlaced) {
117
- // correct answer have unplaced answer choices
118
- return areResponseAreasFilled;
119
- } // all choices (required for a correct response) were placed into a response area
120
-
120
+ }); // true - all choices (required for a correct response) were placed into a response area
121
121
 
122
122
  return requiredAnswersPlaced;
123
123
  }
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.js"],"names":["ImageClozeAssociation","m","_model","dispatchEvent","ModelSetEvent","tagName","toLowerCase","isComplete","_render","autoplayAudioEnabled","completeAudioEnabled","completeResponses","duplicateResponses","maxResponsePerZone","responseAreasToBeFilled","elementContext","audioComplete","audio","querySelector","isInsidePrompt","closest","_session","answers","Array","isArray","filledResponseAreas","Map","map","item","containerIndex","values","length","areResponseAreasFilled","allAnswersValue","answer","value","requiredAnswersPlaced","some","response","every","val","includes","s","data","selector","SessionChangedEvent","info","document","createElement","id","Object","assign","style","position","top","width","height","display","justifyContent","alignItems","background","zIndex","cursor","img","src","EnableAudioAutoplayImage","alt","appendChild","observer","MutationObserver","mutationsList","forEach","mutation","type","_createAudioInfoToast","container","enableAudio","play","removeChild","removeEventListener","setTimeout","paused","addEventListener","handlePlaying","handleEnded","_audio","_handlePlaying","_handleEnded","_enableAudio","disconnect","observe","childList","subtree","el","React","ImageClozeAssociationComponent","model","session","updateAnswer","bind","ReactDOM","render","HTMLElement"],"mappings":"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","sourcesContent":["import React from 'react';\nimport ReactDOM from 'react-dom';\nimport { renderMath } from '@pie-lib/math-rendering';\nimport { EnableAudioAutoplayImage } from '@pie-lib/render-ui';\nimport { ModelSetEvent, SessionChangedEvent } from '@pie-framework/pie-player-events';\n\nimport ImageClozeAssociationComponent from './root';\n\nexport default class ImageClozeAssociation extends HTMLElement {\n set model(m) {\n this._model = m;\n\n this.dispatchEvent(new ModelSetEvent(this.tagName.toLowerCase(), this.isComplete(), !!this._model));\n this._render();\n }\n\n isComplete() {\n const {\n autoplayAudioEnabled,\n completeAudioEnabled,\n completeResponses,\n duplicateResponses,\n maxResponsePerZone,\n responseAreasToBeFilled,\n } = this._model || {};\n const elementContext = this;\n\n // check audio completion if audio settings are enabled and audio actually exists\n if (autoplayAudioEnabled && completeAudioEnabled && !this.audioComplete) {\n if (elementContext) {\n const audio = elementContext.querySelector('audio');\n const isInsidePrompt = audio && audio.closest('#preview-prompt');\n\n // only require audio completion if audio exists and is inside the prompt\n if (audio && isInsidePrompt) {\n return false;\n }\n }\n }\n\n if (!this._session || !this._session.answers) {\n return false;\n }\n\n const { answers } = this._session;\n\n if (!Array.isArray(answers)) {\n return false;\n }\n\n // filter answers by containerIndex and count the ones with content\n const filledResponseAreas = [...new Map(answers.map((item) => [item.containerIndex, item])).values()].length;\n // check if an answer choice was added to at least as many response areas\n // as the number of populated response areas in the correct answer\n const areResponseAreasFilled = filledResponseAreas >= responseAreasToBeFilled;\n\n if (maxResponsePerZone > 1) {\n if (duplicateResponses) {\n // an answer choice can be used multiple times\n return areResponseAreasFilled;\n }\n\n const allAnswersValue = answers.map((answer) => answer.value);\n\n // check if any correct answer have any unplaced answer choices\n const requiredAnswersPlaced = completeResponses.some((response) =>\n response.every((val) => allAnswersValue.includes(val)),\n );\n\n if (!requiredAnswersPlaced) {\n // correct answer have unplaced answer choices\n return areResponseAreasFilled;\n }\n\n // all choices (required for a correct response) were placed into a response area\n return requiredAnswersPlaced;\n }\n\n return areResponseAreasFilled;\n }\n\n set session(s) {\n if (s && !s.answers) {\n s.answers = [];\n }\n\n this._session = s;\n this._render();\n }\n\n get session() {\n return this._session;\n }\n\n updateAnswer(data) {\n this._session.answers = data;\n this._session.selector = 'Mouse';\n\n this.dispatchEvent(new SessionChangedEvent(this.tagName.toLowerCase(), this.isComplete()));\n\n this._render();\n }\n\n _createAudioInfoToast() {\n const info = document.createElement('div');\n info.id = 'play-audio-info';\n\n Object.assign(info.style, {\n position: 'absolute',\n top: 0,\n width: '100%',\n height: '100%',\n display: 'flex',\n justifyContent: 'center',\n alignItems: 'center',\n background: 'white',\n zIndex: '1000',\n cursor: 'pointer',\n });\n\n const img = document.createElement('img');\n img.src = EnableAudioAutoplayImage;\n img.alt = 'Click anywhere to enable audio autoplay';\n img.width = 500;\n img.height = 300;\n\n info.appendChild(img);\n return info;\n }\n\n connectedCallback() {\n this._render();\n\n // Observation: audio in Chrome will have the autoplay attribute,\n // while other browsers will not have the autoplay attribute and will need a user interaction to play the audio\n // This workaround fixes the issue of audio being cached and played on any user interaction in Safari and Firefox\n const observer = new MutationObserver((mutationsList, observer) => {\n mutationsList.forEach((mutation) => {\n if (mutation.type === 'childList') {\n const audio = this.querySelector('audio');\n const isInsidePrompt = audio && audio.closest('#preview-prompt');\n\n if (!this._model) return;\n if (!this._model.autoplayAudioEnabled) return;\n if (audio && !isInsidePrompt) return;\n if (!audio) return;\n\n const info = this._createAudioInfoToast();\n const container = this.querySelector('#main-container');\n const enableAudio = () => {\n if (this.querySelector('#play-audio-info')) {\n audio.play();\n container.removeChild(info);\n }\n\n document.removeEventListener('click', enableAudio);\n };\n\n // if the audio is paused, it means the user has not interacted with the page yet and the audio will not play\n // FIX FOR SAFARI: play with a slight delay to check if autoplay was blocked\n setTimeout(() => {\n if (audio.paused && !this.querySelector('#play-audio-info')) {\n // add info message as a toast to enable audio playback\n container.appendChild(info);\n document.addEventListener('click', enableAudio);\n } else {\n document.removeEventListener('click', enableAudio);\n }\n }, 500);\n\n // we need to listen for the playing event to remove the toast in case the audio plays because of re-rendering\n const handlePlaying = () => {\n const info = this.querySelector('#play-audio-info');\n\n if (info) {\n container.removeChild(info);\n }\n\n audio.removeEventListener('playing', handlePlaying);\n };\n\n audio.addEventListener('playing', handlePlaying);\n\n // we need to listen for the ended event to update the isComplete state\n const handleEnded = () => {\n this.audioComplete = true;\n this.dispatchEvent(new SessionChangedEvent(this.tagName.toLowerCase(), this.isComplete()));\n\n audio.removeEventListener('ended', handleEnded);\n };\n\n audio.addEventListener('ended', handleEnded);\n\n // store references to remove later\n this._audio = audio;\n this._handlePlaying = handlePlaying;\n this._handleEnded = handleEnded;\n this._enableAudio = enableAudio;\n\n observer.disconnect();\n }\n });\n });\n\n observer.observe(this, { childList: true, subtree: true });\n }\n\n disconnectedCallback() {\n document.removeEventListener('click', this._enableAudio);\n\n if (this._audio) {\n this._audio.removeEventListener('playing', this._handlePlaying);\n this._audio.removeEventListener('ended', this._handleEnded);\n this._audio = null;\n }\n }\n\n _render() {\n if (this._model && this._session) {\n const el = React.createElement(ImageClozeAssociationComponent, {\n model: this._model,\n session: this._session,\n updateAnswer: this.updateAnswer.bind(this),\n });\n\n ReactDOM.render(el, this, () => {\n renderMath(this);\n });\n }\n }\n}\n"],"file":"index.js"}
1
+ {"version":3,"sources":["../src/index.js"],"names":["ImageClozeAssociation","m","_model","dispatchEvent","ModelSetEvent","tagName","toLowerCase","isComplete","_render","autoplayAudioEnabled","completeAudioEnabled","completeResponses","duplicateResponses","hasUnplacedChoices","maxResponsePerZone","responseAreasToBeFilled","elementContext","audioComplete","audio","querySelector","isInsidePrompt","closest","_session","answers","Array","isArray","filledResponseAreas","Map","map","item","containerIndex","values","length","areResponseAreasFilled","allAnswersValue","answer","value","requiredAnswersPlaced","some","response","every","val","includes","s","data","selector","SessionChangedEvent","info","document","createElement","id","Object","assign","style","position","top","width","height","display","justifyContent","alignItems","background","zIndex","cursor","img","src","EnableAudioAutoplayImage","alt","appendChild","observer","MutationObserver","mutationsList","forEach","mutation","type","_createAudioInfoToast","container","enableAudio","play","removeChild","removeEventListener","setTimeout","paused","addEventListener","handlePlaying","handleEnded","_audio","_handlePlaying","_handleEnded","_enableAudio","disconnect","observe","childList","subtree","el","React","ImageClozeAssociationComponent","model","session","updateAnswer","bind","ReactDOM","render","HTMLElement"],"mappings":"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","sourcesContent":["import React from 'react';\nimport ReactDOM from 'react-dom';\nimport { renderMath } from '@pie-lib/math-rendering';\nimport { EnableAudioAutoplayImage } from '@pie-lib/render-ui';\nimport { ModelSetEvent, SessionChangedEvent } from '@pie-framework/pie-player-events';\n\nimport ImageClozeAssociationComponent from './root';\n\nexport default class ImageClozeAssociation extends HTMLElement {\n set model(m) {\n this._model = m;\n\n this.dispatchEvent(new ModelSetEvent(this.tagName.toLowerCase(), this.isComplete(), !!this._model));\n this._render();\n }\n\n isComplete() {\n const {\n autoplayAudioEnabled,\n completeAudioEnabled,\n completeResponses,\n duplicateResponses,\n hasUnplacedChoices,\n maxResponsePerZone,\n responseAreasToBeFilled,\n } = this._model || {};\n const elementContext = this;\n\n // check audio completion if audio settings are enabled and audio actually exists\n if (autoplayAudioEnabled && completeAudioEnabled && !this.audioComplete) {\n if (elementContext) {\n const audio = elementContext.querySelector('audio');\n const isInsidePrompt = audio && audio.closest('#preview-prompt');\n\n // only require audio completion if audio exists and is inside the prompt\n if (audio && isInsidePrompt) {\n return false;\n }\n }\n }\n\n if (!this._session || !this._session.answers) {\n return false;\n }\n\n const { answers } = this._session;\n\n if (!Array.isArray(answers)) {\n return false;\n }\n\n // filter answers by containerIndex and count the ones with content\n const filledResponseAreas = [...new Map(answers.map((item) => [item.containerIndex, item])).values()].length;\n // check if an answer choice was added to at least as many response areas\n // as the number of populated response areas in the correct answer\n const areResponseAreasFilled = filledResponseAreas >= responseAreasToBeFilled;\n\n if (maxResponsePerZone > 1) {\n if (duplicateResponses) {\n // an answer choice can be used multiple times\n return areResponseAreasFilled;\n }\n\n // any correct answer have any unplaced answer choices (by the author)\n if (hasUnplacedChoices) {\n return areResponseAreasFilled;\n }\n\n const allAnswersValue = answers.map((answer) => answer.value);\n\n // check if any correct answer have any unplaced answer choices (by the student)\n const requiredAnswersPlaced = completeResponses.some((response) =>\n response.every((val) => allAnswersValue.includes(val)),\n );\n\n // true - all choices (required for a correct response) were placed into a response area\n return requiredAnswersPlaced;\n }\n\n return areResponseAreasFilled;\n }\n\n set session(s) {\n if (s && !s.answers) {\n s.answers = [];\n }\n\n this._session = s;\n this._render();\n }\n\n get session() {\n return this._session;\n }\n\n updateAnswer(data) {\n this._session.answers = data;\n this._session.selector = 'Mouse';\n\n this.dispatchEvent(new SessionChangedEvent(this.tagName.toLowerCase(), this.isComplete()));\n\n this._render();\n }\n\n _createAudioInfoToast() {\n const info = document.createElement('div');\n info.id = 'play-audio-info';\n\n Object.assign(info.style, {\n position: 'absolute',\n top: 0,\n width: '100%',\n height: '100%',\n display: 'flex',\n justifyContent: 'center',\n alignItems: 'center',\n background: 'white',\n zIndex: '1000',\n cursor: 'pointer',\n });\n\n const img = document.createElement('img');\n img.src = EnableAudioAutoplayImage;\n img.alt = 'Click anywhere to enable audio autoplay';\n img.width = 500;\n img.height = 300;\n\n info.appendChild(img);\n return info;\n }\n\n connectedCallback() {\n this._render();\n\n // Observation: audio in Chrome will have the autoplay attribute,\n // while other browsers will not have the autoplay attribute and will need a user interaction to play the audio\n // This workaround fixes the issue of audio being cached and played on any user interaction in Safari and Firefox\n const observer = new MutationObserver((mutationsList, observer) => {\n mutationsList.forEach((mutation) => {\n if (mutation.type === 'childList') {\n const audio = this.querySelector('audio');\n const isInsidePrompt = audio && audio.closest('#preview-prompt');\n\n if (!this._model) return;\n if (!this._model.autoplayAudioEnabled) return;\n if (audio && !isInsidePrompt) return;\n if (!audio) return;\n\n const info = this._createAudioInfoToast();\n const container = this.querySelector('#main-container');\n const enableAudio = () => {\n if (this.querySelector('#play-audio-info')) {\n audio.play();\n container.removeChild(info);\n }\n\n document.removeEventListener('click', enableAudio);\n };\n\n // if the audio is paused, it means the user has not interacted with the page yet and the audio will not play\n // FIX FOR SAFARI: play with a slight delay to check if autoplay was blocked\n setTimeout(() => {\n if (audio.paused && !this.querySelector('#play-audio-info')) {\n // add info message as a toast to enable audio playback\n container.appendChild(info);\n document.addEventListener('click', enableAudio);\n } else {\n document.removeEventListener('click', enableAudio);\n }\n }, 500);\n\n // we need to listen for the playing event to remove the toast in case the audio plays because of re-rendering\n const handlePlaying = () => {\n const info = this.querySelector('#play-audio-info');\n\n if (info) {\n container.removeChild(info);\n }\n\n audio.removeEventListener('playing', handlePlaying);\n };\n\n audio.addEventListener('playing', handlePlaying);\n\n // we need to listen for the ended event to update the isComplete state\n const handleEnded = () => {\n this.audioComplete = true;\n this.dispatchEvent(new SessionChangedEvent(this.tagName.toLowerCase(), this.isComplete()));\n\n audio.removeEventListener('ended', handleEnded);\n };\n\n audio.addEventListener('ended', handleEnded);\n\n // store references to remove later\n this._audio = audio;\n this._handlePlaying = handlePlaying;\n this._handleEnded = handleEnded;\n this._enableAudio = enableAudio;\n\n observer.disconnect();\n }\n });\n });\n\n observer.observe(this, { childList: true, subtree: true });\n }\n\n disconnectedCallback() {\n document.removeEventListener('click', this._enableAudio);\n\n if (this._audio) {\n this._audio.removeEventListener('playing', this._handlePlaying);\n this._audio.removeEventListener('ended', this._handleEnded);\n this._audio = null;\n }\n }\n\n _render() {\n if (this._model && this._session) {\n const el = React.createElement(ImageClozeAssociationComponent, {\n model: this._model,\n session: this._session,\n updateAnswer: this.updateAnswer.bind(this),\n });\n\n ReactDOM.render(el, this, () => {\n renderMath(this);\n });\n }\n }\n}\n"],"file":"index.js"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pie-element/image-cloze-association",
3
- "version": "8.3.2-next.0+c5902668d",
3
+ "version": "8.3.2-next.2+4274a8ad1",
4
4
  "description": "",
5
5
  "repository": "pie-framework/pie-elements",
6
6
  "publishConfig": {
@@ -23,7 +23,7 @@
23
23
  },
24
24
  "author": "pie framework developers",
25
25
  "license": "ISC",
26
- "gitHead": "c5902668d5ebb7622f87dcf8131c94b88b428035",
26
+ "gitHead": "4274a8ad183add5a323b7005ab011e963a237407",
27
27
  "scripts": {
28
28
  "postpublish": "../../scripts/postpublish"
29
29
  },
package/src/index.js CHANGED
@@ -20,6 +20,7 @@ export default class ImageClozeAssociation extends HTMLElement {
20
20
  completeAudioEnabled,
21
21
  completeResponses,
22
22
  duplicateResponses,
23
+ hasUnplacedChoices,
23
24
  maxResponsePerZone,
24
25
  responseAreasToBeFilled,
25
26
  } = this._model || {};
@@ -60,19 +61,19 @@ export default class ImageClozeAssociation extends HTMLElement {
60
61
  return areResponseAreasFilled;
61
62
  }
62
63
 
64
+ // any correct answer have any unplaced answer choices (by the author)
65
+ if (hasUnplacedChoices) {
66
+ return areResponseAreasFilled;
67
+ }
68
+
63
69
  const allAnswersValue = answers.map((answer) => answer.value);
64
70
 
65
- // check if any correct answer have any unplaced answer choices
71
+ // check if any correct answer have any unplaced answer choices (by the student)
66
72
  const requiredAnswersPlaced = completeResponses.some((response) =>
67
73
  response.every((val) => allAnswersValue.includes(val)),
68
74
  );
69
75
 
70
- if (!requiredAnswersPlaced) {
71
- // correct answer have unplaced answer choices
72
- return areResponseAreasFilled;
73
- }
74
-
75
- // all choices (required for a correct response) were placed into a response area
76
+ // true - all choices (required for a correct response) were placed into a response area
76
77
  return requiredAnswersPlaced;
77
78
  }
78
79