@pie-element/categorize 0.1.0

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.
Files changed (238) hide show
  1. package/dist/author/defaults.d.ts +224 -0
  2. package/dist/author/defaults.d.ts.map +1 -0
  3. package/dist/author/defaults.js +170 -0
  4. package/dist/author/design/builder.d.ts +11 -0
  5. package/dist/author/design/builder.d.ts.map +1 -0
  6. package/dist/author/design/builder.js +24 -0
  7. package/dist/author/design/buttons.d.ts +29 -0
  8. package/dist/author/design/buttons.d.ts.map +1 -0
  9. package/dist/author/design/buttons.js +36 -0
  10. package/dist/author/design/categories/RowLabel.d.ts +24 -0
  11. package/dist/author/design/categories/RowLabel.d.ts.map +1 -0
  12. package/dist/author/design/categories/RowLabel.js +46 -0
  13. package/dist/author/design/categories/alternateResponses.d.ts +32 -0
  14. package/dist/author/design/categories/alternateResponses.d.ts.map +1 -0
  15. package/dist/author/design/categories/alternateResponses.js +62 -0
  16. package/dist/author/design/categories/category.d.ts +45 -0
  17. package/dist/author/design/categories/category.d.ts.map +1 -0
  18. package/dist/author/design/categories/category.js +102 -0
  19. package/dist/author/design/categories/choice-preview.d.ts +26 -0
  20. package/dist/author/design/categories/choice-preview.d.ts.map +1 -0
  21. package/dist/author/design/categories/choice-preview.js +60 -0
  22. package/dist/author/design/categories/droppable-placeholder.d.ts +11 -0
  23. package/dist/author/design/categories/droppable-placeholder.d.ts.map +1 -0
  24. package/dist/author/design/categories/droppable-placeholder.js +70 -0
  25. package/dist/author/design/categories/index.d.ts +42 -0
  26. package/dist/author/design/categories/index.d.ts.map +1 -0
  27. package/dist/author/design/categories/index.js +146 -0
  28. package/dist/author/design/choices/choice.d.ts +11 -0
  29. package/dist/author/design/choices/choice.d.ts.map +1 -0
  30. package/dist/author/design/choices/choice.js +129 -0
  31. package/dist/author/design/choices/config.d.ts +22 -0
  32. package/dist/author/design/choices/config.d.ts.map +1 -0
  33. package/dist/author/design/choices/config.js +33 -0
  34. package/dist/author/design/choices/index.d.ts +42 -0
  35. package/dist/author/design/choices/index.d.ts.map +1 -0
  36. package/dist/author/design/choices/index.js +110 -0
  37. package/dist/author/design/header.d.ts +25 -0
  38. package/dist/author/design/header.d.ts.map +1 -0
  39. package/dist/author/design/header.js +49 -0
  40. package/dist/author/design/index.d.ts +53 -0
  41. package/dist/author/design/index.d.ts.map +1 -0
  42. package/dist/author/design/index.js +410 -0
  43. package/dist/author/design/input-header.d.ts +40 -0
  44. package/dist/author/design/input-header.d.ts.map +1 -0
  45. package/dist/author/design/input-header.js +69 -0
  46. package/dist/author/design/utils.d.ts +10 -0
  47. package/dist/author/design/utils.d.ts.map +1 -0
  48. package/dist/author/design/utils.js +7 -0
  49. package/dist/author/index.d.ts +52 -0
  50. package/dist/author/index.d.ts.map +1 -0
  51. package/dist/author/index.js +87 -0
  52. package/dist/author/main.d.ts +24 -0
  53. package/dist/author/main.d.ts.map +1 -0
  54. package/dist/author/main.js +30 -0
  55. package/dist/author/utils.d.ts +17 -0
  56. package/dist/author/utils.d.ts.map +1 -0
  57. package/dist/author/utils.js +17 -0
  58. package/dist/controller/defaults.d.ts +35 -0
  59. package/dist/controller/defaults.d.ts.map +1 -0
  60. package/dist/controller/defaults.js +29 -0
  61. package/dist/controller/index.d.ts +62 -0
  62. package/dist/controller/index.d.ts.map +1 -0
  63. package/dist/controller/index.js +155 -0
  64. package/dist/controller/utils.d.ts +21 -0
  65. package/dist/controller/utils.d.ts.map +1 -0
  66. package/dist/controller/utils.js +35 -0
  67. package/dist/delivery/categorize/categories.d.ts +35 -0
  68. package/dist/delivery/categorize/categories.d.ts.map +1 -0
  69. package/dist/delivery/categorize/categories.js +66 -0
  70. package/dist/delivery/categorize/category.d.ts +29 -0
  71. package/dist/delivery/categorize/category.d.ts.map +1 -0
  72. package/dist/delivery/categorize/category.js +48 -0
  73. package/dist/delivery/categorize/choice.d.ts +28 -0
  74. package/dist/delivery/categorize/choice.d.ts.map +1 -0
  75. package/dist/delivery/categorize/choice.js +94 -0
  76. package/dist/delivery/categorize/choices.d.ts +40 -0
  77. package/dist/delivery/categorize/choices.d.ts.map +1 -0
  78. package/dist/delivery/categorize/choices.js +51 -0
  79. package/dist/delivery/categorize/droppable-placeholder.d.ts +33 -0
  80. package/dist/delivery/categorize/droppable-placeholder.d.ts.map +1 -0
  81. package/dist/delivery/categorize/droppable-placeholder.js +64 -0
  82. package/dist/delivery/categorize/grid-content.d.ts +26 -0
  83. package/dist/delivery/categorize/grid-content.d.ts.map +1 -0
  84. package/dist/delivery/categorize/grid-content.js +39 -0
  85. package/dist/delivery/categorize/index.d.ts +58 -0
  86. package/dist/delivery/categorize/index.d.ts.map +1 -0
  87. package/dist/delivery/categorize/index.js +260 -0
  88. package/dist/delivery/index.d.ts +28 -0
  89. package/dist/delivery/index.d.ts.map +1 -0
  90. package/dist/delivery/index.js +152 -0
  91. package/dist/index.d.ts +2 -0
  92. package/dist/index.d.ts.map +1 -0
  93. package/dist/index.iife.d.ts +9 -0
  94. package/dist/index.iife.d.ts.map +1 -0
  95. package/dist/index.iife.js +152 -0
  96. package/dist/index.js +2 -0
  97. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_DataView.js +6 -0
  98. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_Hash.js +16 -0
  99. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_ListCache.js +16 -0
  100. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_Map.js +6 -0
  101. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_MapCache.js +16 -0
  102. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_Promise.js +6 -0
  103. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_Set.js +6 -0
  104. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_Stack.js +13 -0
  105. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_Symbol.js +5 -0
  106. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_Uint8Array.js +5 -0
  107. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_WeakMap.js +6 -0
  108. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_arrayEach.js +7 -0
  109. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_arrayFilter.js +10 -0
  110. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_arrayLikeKeys.js +15 -0
  111. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_arrayPush.js +7 -0
  112. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_assignValue.js +10 -0
  113. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_assocIndexOf.js +8 -0
  114. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_baseAssign.js +8 -0
  115. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_baseAssignIn.js +8 -0
  116. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_baseAssignValue.js +12 -0
  117. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_baseClone.js +57 -0
  118. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_baseCreate.js +14 -0
  119. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_baseGetAllKeys.js +9 -0
  120. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_baseGetTag.js +10 -0
  121. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_baseIsArguments.js +9 -0
  122. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_baseIsMap.js +9 -0
  123. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_baseIsNative.js +11 -0
  124. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_baseIsSet.js +9 -0
  125. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_baseIsTypedArray.js +11 -0
  126. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_baseKeys.js +12 -0
  127. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_baseKeysIn.js +13 -0
  128. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_baseTimes.js +7 -0
  129. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_baseUnary.js +8 -0
  130. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_cloneArrayBuffer.js +8 -0
  131. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_cloneBuffer.js +10 -0
  132. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_cloneDataView.js +8 -0
  133. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_cloneRegExp.js +8 -0
  134. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_cloneSymbol.js +8 -0
  135. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_cloneTypedArray.js +8 -0
  136. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_copyArray.js +8 -0
  137. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_copyObject.js +14 -0
  138. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_copySymbols.js +8 -0
  139. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_copySymbolsIn.js +8 -0
  140. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_coreJsData.js +5 -0
  141. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_defineProperty.js +10 -0
  142. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_freeGlobal.js +4 -0
  143. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_getAllKeys.js +9 -0
  144. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_getAllKeysIn.js +9 -0
  145. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_getMapData.js +8 -0
  146. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_getNative.js +9 -0
  147. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_getPrototype.js +5 -0
  148. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_getRawTag.js +14 -0
  149. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_getSymbols.js +10 -0
  150. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_getSymbolsIn.js +11 -0
  151. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_getTag.js +23 -0
  152. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_getValue.js +6 -0
  153. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_hashClear.js +7 -0
  154. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_hashDelete.js +7 -0
  155. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_hashGet.js +13 -0
  156. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_hashHas.js +9 -0
  157. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_hashSet.js +9 -0
  158. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_initCloneArray.js +8 -0
  159. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_initCloneByTag.js +33 -0
  160. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_initCloneObject.js +9 -0
  161. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_isIndex.js +8 -0
  162. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_isKeyable.js +7 -0
  163. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_isMasked.js +11 -0
  164. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_isPrototype.js +8 -0
  165. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_listCacheClear.js +6 -0
  166. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_listCacheDelete.js +9 -0
  167. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_listCacheGet.js +8 -0
  168. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_listCacheHas.js +7 -0
  169. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_listCacheSet.js +8 -0
  170. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_mapCacheClear.js +13 -0
  171. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_mapCacheDelete.js +8 -0
  172. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_mapCacheGet.js +7 -0
  173. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_mapCacheHas.js +7 -0
  174. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_mapCacheSet.js +8 -0
  175. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_nativeCreate.js +5 -0
  176. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_nativeKeys.js +5 -0
  177. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_nativeKeysIn.js +8 -0
  178. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_nodeUtil.js +9 -0
  179. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_objectToString.js +7 -0
  180. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_overArg.js +8 -0
  181. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_root.js +5 -0
  182. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_stackClear.js +7 -0
  183. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_stackDelete.js +7 -0
  184. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_stackGet.js +6 -0
  185. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_stackHas.js +6 -0
  186. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_stackSet.js +16 -0
  187. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/_toSource.js +15 -0
  188. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/cloneDeep.js +8 -0
  189. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/eq.js +6 -0
  190. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/isArguments.js +10 -0
  191. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/isArray.js +4 -0
  192. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/isArrayLike.js +8 -0
  193. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/isBuffer.js +6 -0
  194. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/isEmpty.js +21 -0
  195. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/isFunction.js +11 -0
  196. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/isLength.js +7 -0
  197. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/isMap.js +7 -0
  198. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/isObject.js +7 -0
  199. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/isObjectLike.js +6 -0
  200. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/isSet.js +7 -0
  201. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/isTypedArray.js +7 -0
  202. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/keys.js +9 -0
  203. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/keysIn.js +9 -0
  204. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/stubArray.js +6 -0
  205. package/dist/node_modules/.bun/lodash-es@4.17.23/node_modules/lodash-es/stubFalse.js +6 -0
  206. package/package.json +74 -0
  207. package/src/author/defaults.ts +182 -0
  208. package/src/author/design/builder.ts +63 -0
  209. package/src/author/design/buttons.tsx +60 -0
  210. package/src/author/design/categories/RowLabel.tsx +91 -0
  211. package/src/author/design/categories/alternateResponses.tsx +127 -0
  212. package/src/author/design/categories/category.tsx +161 -0
  213. package/src/author/design/categories/choice-preview.tsx +112 -0
  214. package/src/author/design/categories/droppable-placeholder.tsx +114 -0
  215. package/src/author/design/categories/index.tsx +254 -0
  216. package/src/author/design/choices/choice.tsx +200 -0
  217. package/src/author/design/choices/config.tsx +58 -0
  218. package/src/author/design/choices/index.tsx +185 -0
  219. package/src/author/design/header.tsx +74 -0
  220. package/src/author/design/index.tsx +764 -0
  221. package/src/author/design/input-header.tsx +109 -0
  222. package/src/author/design/utils.ts +14 -0
  223. package/src/author/index.ts +171 -0
  224. package/src/author/main.tsx +45 -0
  225. package/src/author/utils.ts +46 -0
  226. package/src/controller/defaults.ts +37 -0
  227. package/src/controller/index.ts +469 -0
  228. package/src/controller/utils.ts +79 -0
  229. package/src/delivery/categorize/categories.tsx +139 -0
  230. package/src/delivery/categorize/category.tsx +78 -0
  231. package/src/delivery/categorize/choice.tsx +117 -0
  232. package/src/delivery/categorize/choices.tsx +95 -0
  233. package/src/delivery/categorize/droppable-placeholder.tsx +95 -0
  234. package/src/delivery/categorize/grid-content.tsx +53 -0
  235. package/src/delivery/categorize/index.tsx +515 -0
  236. package/src/delivery/index.ts +390 -0
  237. package/src/index.iife.ts +16 -0
  238. package/src/index.ts +1 -0
@@ -0,0 +1,469 @@
1
+ // @ts-nocheck
2
+ /**
3
+ * @synced-from pie-elements/packages/categorize/controller/src/index.js
4
+ * @auto-generated
5
+ *
6
+ * This file is automatically synced from pie-elements and converted to TypeScript.
7
+ * Manual edits will be overwritten on next sync.
8
+ * To make changes, edit the upstream JavaScript file and run sync again.
9
+ */
10
+
11
+ import { isEmpty } from 'lodash-es';
12
+ import { buildState, score } from '@pie-lib/categorize';
13
+ import { getFeedbackForCorrectness } from '@pie-element/shared-feedback';
14
+ import { lockChoices, getShuffledChoices, partialScoring } from '@pie-element/shared-controller-utils';
15
+ import Translator from '@pie-lib/translator';
16
+
17
+ const { translator } = Translator;
18
+ import defaults from './defaults.js';
19
+ import { getCompleteResponseDetails, isAlternateDuplicated, isCorrectResponseDuplicated } from './utils.js';
20
+
21
+ // eslint-disable-next-line no-console
22
+
23
+ export { score };
24
+
25
+ export const getPartialScore = (correctResponse, builtCategories) => {
26
+ // in the resulted best scenario we make a sum with all the correct responses
27
+ // and all the placements
28
+ const { placements, score } = builtCategories.reduce(
29
+ (acc, { choices = [] }) => ({
30
+ placements: acc.placements + choices.length,
31
+ score: acc.score + choices.filter((ch) => ch.correct).length,
32
+ }),
33
+ { placements: 0, score: 0 },
34
+ );
35
+
36
+ // in the correct response, we make a sum of the max possible score
37
+ const { maxScore } = correctResponse.reduce(
38
+ (acc, { choices }) => ({
39
+ maxScore: acc.maxScore + choices.length,
40
+ }),
41
+ { maxScore: 0 },
42
+ );
43
+
44
+ // if there are any extra placements, we subtract from the obtained score
45
+ const extraPlacements = placements > maxScore ? placements - maxScore : 0;
46
+ const totalScore = (score - extraPlacements) / maxScore;
47
+
48
+ return totalScore < 0 ? 0 : parseFloat(totalScore.toFixed(2));
49
+ };
50
+
51
+ const getAlternates = (correctResponse) =>
52
+ correctResponse.map((c) => c.alternateResponses).filter((alternate) => alternate);
53
+
54
+ export const getTotalScore = (question, session, env) => {
55
+ if (!session) {
56
+ return 0;
57
+ }
58
+
59
+ if (Object.keys(session).length === 0) {
60
+ return 0;
61
+ }
62
+ const { categories, choices } = question || {};
63
+ let { correctResponse } = question || {};
64
+ let { answers } = session || {};
65
+ answers = answers || [];
66
+ correctResponse = correctResponse || [];
67
+
68
+ // this function is used in pie-ui/categorize as well, in order to get the best scenario
69
+ // so we get the best scenario and calculate the score
70
+ const { categories: builtCategories, correct } = buildState(categories, choices, answers, correctResponse);
71
+
72
+ const alternates = getAlternates(correctResponse);
73
+ const enabled = partialScoring.enabled(question, env);
74
+
75
+ // if there are any alternates, there will be no partial scoring!
76
+ if (enabled && !alternates.length) {
77
+ // we apply partial scoring
78
+ return getPartialScore(correctResponse, builtCategories);
79
+ }
80
+
81
+ // else we apply dichotomous
82
+ return correct ? 1 : 0;
83
+ };
84
+
85
+ export const getCorrectness = (question, session, env) => {
86
+ return new Promise((resolve) => {
87
+ if (env.mode === 'evaluate') {
88
+ const score = getTotalScore(question, session, env);
89
+ if (score === 1) {
90
+ resolve('correct');
91
+ } else if (score === 0) {
92
+ resolve('incorrect');
93
+ } else {
94
+ resolve('partially-correct');
95
+ }
96
+ } else {
97
+ resolve(undefined);
98
+ }
99
+ });
100
+ };
101
+
102
+ export const createDefaultModel = (model = {}) =>
103
+ new Promise((resolve) => {
104
+ resolve({
105
+ ...defaults,
106
+ ...model,
107
+ });
108
+ });
109
+
110
+ export const normalize = (question) => ({ ...defaults, ...question });
111
+
112
+ /**
113
+ *
114
+ * @param {*} question
115
+ * @param {*} session
116
+ * @param {*} env
117
+ * @param {*} updateSession - optional - a function that will set the properties passed into it on the session.
118
+ */
119
+ export const model = async (question, session, env, updateSession) => {
120
+ const normalizedQuestion = normalize(question);
121
+ const answerCorrectness = await getCorrectness(normalizedQuestion, session, env);
122
+
123
+ const { mode, role } = env || {};
124
+
125
+ const {
126
+ categories,
127
+ categoriesPerRow,
128
+ choicesLabel,
129
+ choicesPosition,
130
+ correctResponse,
131
+ feedback,
132
+ feedbackEnabled,
133
+ promptEnabled,
134
+ prompt,
135
+ rowLabels,
136
+ rationaleEnabled,
137
+ rationale,
138
+ teacherInstructionsEnabled,
139
+ teacherInstructions,
140
+ language,
141
+ maxChoicesPerCategory,
142
+ extraCSSRules,
143
+ minRowHeight,
144
+ fontSizeFactor,
145
+ autoplayAudioEnabled,
146
+ completeAudioEnabled,
147
+ customAudioButton,
148
+ } = normalizedQuestion;
149
+ let { choices, note } = normalizedQuestion;
150
+ let fb;
151
+
152
+ const lockChoiceOrder = lockChoices(normalizedQuestion, session, env);
153
+
154
+ const filteredCorrectResponse = correctResponse.map((response) => {
155
+ const filteredChoices = (response.choices || []).filter((choice) => choice !== 'null');
156
+ return { ...response, choices: filteredChoices };
157
+ });
158
+
159
+ if (mode === 'evaluate' && feedbackEnabled) {
160
+ fb = await getFeedbackForCorrectness(answerCorrectness, feedback);
161
+ }
162
+
163
+ if (!lockChoiceOrder) {
164
+ choices = await getShuffledChoices(choices, session, updateSession, 'id');
165
+ }
166
+
167
+ if (!note) {
168
+ note = translator.t('common:commonCorrectAnswerWithAlternates', { lng: language });
169
+ }
170
+
171
+ const alternates = getAlternates(filteredCorrectResponse);
172
+ const { responseAreasToBeFilled, possibleResponses, hasUnplacedChoices } = getCompleteResponseDetails(
173
+ filteredCorrectResponse,
174
+ normalizedQuestion.allowAlternateEnabled ? alternates : [],
175
+ normalizedQuestion.choices,
176
+ );
177
+ const out = {
178
+ categories: categories || [],
179
+ categoriesPerRow: categoriesPerRow || 2,
180
+ maxChoicesPerCategory,
181
+ correctness: answerCorrectness,
182
+ choices: choices || [],
183
+ choicesLabel: choicesLabel || '',
184
+ choicesPosition,
185
+ disabled: mode !== 'gather',
186
+ feedback: fb,
187
+ lockChoiceOrder,
188
+ prompt: promptEnabled ? prompt : null,
189
+ rowLabels,
190
+ note,
191
+ env,
192
+ showNote: alternates && alternates.length > 0,
193
+ correctResponse: mode === 'evaluate' ? filteredCorrectResponse : undefined,
194
+ language,
195
+ extraCSSRules,
196
+ fontSizeFactor,
197
+ minRowHeight: minRowHeight,
198
+ autoplayAudioEnabled,
199
+ completeAudioEnabled,
200
+ customAudioButton,
201
+ possibleResponses,
202
+ responseAreasToBeFilled,
203
+ hasUnplacedChoices,
204
+ };
205
+
206
+ if (role === 'instructor' && (mode === 'view' || mode === 'evaluate')) {
207
+ out.rationale = rationaleEnabled ? rationale : null;
208
+ out.teacherInstructions = teacherInstructionsEnabled ? teacherInstructions : null;
209
+ } else {
210
+ out.rationale = null;
211
+ out.teacherInstructions = null;
212
+ }
213
+
214
+ return out;
215
+ };
216
+
217
+ /**
218
+ * Generates detailed trace log for scoring evaluation
219
+ * @param {Object} model - the question model
220
+ * @param {Object} session - the student session
221
+ * @param {Object} env - the environment
222
+ * @returns {Array} traceLog - array of trace messages
223
+ */
224
+ export const getLogTrace = (model, session, env) => {
225
+ const traceLog = [];
226
+ const { answers } = session || {};
227
+ const { categories, choices, correctResponse } = model || {};
228
+
229
+ const draggedChoices = answers.reduce(
230
+ (sum, a) => sum + (a.choices?.length || 0),
231
+ 0
232
+ );
233
+
234
+ const alternates = getAlternates(correctResponse);
235
+ const hasAlternates = alternates.length > 0;
236
+ const partialScoringEnabled = partialScoring.enabled(model, env);
237
+
238
+ const builtState =
239
+ draggedChoices > 0
240
+ ? buildState(categories, choices, answers, correctResponse)
241
+ : null;
242
+
243
+ const builtCategories = builtState?.categories || [];
244
+
245
+ if (draggedChoices > 0) {
246
+ traceLog.push(`Student placed ${draggedChoices} choice(s) into categories.`);
247
+
248
+ (categories || []).forEach((category, categoryIndex) => {
249
+ const categoryId = category.id;
250
+ const builtCategory = builtCategories.find(c => c.id === categoryId);
251
+ const studentChoices = builtCategory ? builtCategory.choices || [] : [];
252
+ const correctResponseForCategory = (correctResponse || []).find(cr => cr.category === categoryId);
253
+ const expectedChoices = correctResponseForCategory ? correctResponseForCategory.choices || [] : [];
254
+
255
+ if (expectedChoices.length > 0) {
256
+ if (studentChoices.length === 0) {
257
+ traceLog.push(`Category ${categoryId}: student left empty (should contain ${expectedChoices.length} choice(s)).`);
258
+ } else {
259
+ const correctCount = studentChoices.filter(choice => choice.correct).length;
260
+ const incorrectCount = studentChoices.length - correctCount;
261
+
262
+ if (correctCount > 0 && incorrectCount === 0) {
263
+ traceLog.push(`Category ${categoryId}: student placed ${correctCount} correct choice(s).`);
264
+ } else if (correctCount === 0 && incorrectCount > 0) {
265
+ traceLog.push(`Category ${categoryId}: student placed ${incorrectCount} incorrect choice(s).`);
266
+ } else {
267
+ traceLog.push(`Category ${categoryId}: student placed ${correctCount} correct and ${incorrectCount} incorrect choice(s).`);
268
+ }
269
+ }
270
+ }
271
+ });
272
+ } else {
273
+ traceLog.push('Student did not place any choices into categories.');
274
+ }
275
+
276
+ if (hasAlternates) {
277
+ traceLog.push(`Alternate response combinations are accepted for this question.`);
278
+ }
279
+
280
+ if (hasAlternates) {
281
+ traceLog.push(`Score calculated using all-or-nothing scoring (alternate responses disable partial scoring).`);
282
+ traceLog.push(`Student must get all categories completely correct to receive full credit.`);
283
+ } else if (partialScoringEnabled) {
284
+ traceLog.push(`Score calculated using partial scoring.`);
285
+ traceLog.push(`Student receives credit for each correct placement, with deductions for incorrect placements beyond required amount.`);
286
+
287
+ if (draggedChoices > 0) {
288
+ const totalCorrect = builtCategories.reduce((sum, cat) =>
289
+ sum + (cat.choices || []).filter(choice => choice.correct).length, 0);
290
+ const totalIncorrect = draggedChoices - totalCorrect;
291
+ const maxPossible = (correctResponse || []).reduce((sum, cat) =>
292
+ sum + (cat.choices || []).length, 0);
293
+
294
+ traceLog.push(`Partial scoring calculation: ${totalCorrect} correct placements, ${totalIncorrect} incorrect placements.`);
295
+
296
+ if (draggedChoices > maxPossible) {
297
+ const extraPlacements = draggedChoices - maxPossible;
298
+ traceLog.push(`${extraPlacements} extra placement(s) beyond required amount will be deducted from score.`);
299
+ }
300
+ }
301
+ } else {
302
+ traceLog.push(`Score calculated using all-or-nothing scoring.`);
303
+ traceLog.push(`Student must get all categories completely correct to receive full credit.`);
304
+ }
305
+
306
+ const score = getTotalScore(model, session, env);
307
+ traceLog.push(`Final score: ${score}.`);
308
+
309
+ return traceLog;
310
+ }
311
+
312
+ export const outcome = (question, session, env) => {
313
+ if (env.mode !== 'evaluate') {
314
+ return Promise.reject(new Error('Can not call outcome when mode is not evaluate'));
315
+ } else {
316
+ return new Promise((resolve) => {
317
+ if (!session || isEmpty(session)) {
318
+ resolve({
319
+ score: 0,
320
+ empty: true,
321
+ traceLog: ['Student did not place any choices into categories. Score is 0.'],
322
+ });
323
+ } else {
324
+ const traceLog = getLogTrace(question, session, env);
325
+ const score = getTotalScore(question, session, env);
326
+
327
+ resolve({
328
+ score,
329
+ empty: false,
330
+ traceLog,
331
+ });
332
+ }
333
+ });
334
+ }
335
+ };
336
+
337
+ export const createCorrectResponseSession = (question, env) => {
338
+ return new Promise((resolve) => {
339
+ const { mode, role } = env || {};
340
+
341
+ if (mode !== 'evaluate' && role === 'instructor') {
342
+ const { correctResponse } = question;
343
+
344
+ resolve({ answers: correctResponse, id: 1 });
345
+ } else {
346
+ return resolve(null);
347
+ }
348
+ });
349
+ };
350
+
351
+ // remove all html tags
352
+ const getInnerText = (html) => (html || '').replaceAll(/<[^>]*>/g, '');
353
+
354
+ // remove all html tags except img, iframe and source tag for audio
355
+ const getContent = (html) => (html || '').replace(/(<(?!img|iframe|source)([^>]+)>)/gi, '');
356
+
357
+ export const validate = (model = {}, config = {}) => {
358
+ const { categories, choices, correctResponse, maxAnswerChoices } = model;
359
+ const {
360
+ minChoices = 1,
361
+ minCategories = 1,
362
+ maxCategories = 12,
363
+ maxLengthPerChoice = 300,
364
+ maxLengthPerCategory = 150,
365
+ } = config;
366
+ const reversedChoices = [...(choices || [])].reverse();
367
+ const errors = {};
368
+ const choicesErrors = {};
369
+ const categoriesErrors = {};
370
+
371
+ ['teacherInstructions', 'prompt', 'rationale'].forEach((field) => {
372
+ if (config[field]?.required && !getContent(model[field])) {
373
+ errors[field] = 'This field is required.';
374
+ }
375
+ });
376
+
377
+ (categories || []).forEach((category) => {
378
+ const { id, label } = category;
379
+
380
+ if (getInnerText(label).length > maxLengthPerCategory) {
381
+ categoriesErrors[id] = `Category labels should be no more than ${maxLengthPerCategory} characters long.`;
382
+ }
383
+ });
384
+
385
+ (reversedChoices || []).forEach((choice, index) => {
386
+ const { id, content } = choice;
387
+
388
+ if (getInnerText(content).length > maxLengthPerChoice) {
389
+ choicesErrors[id] = `Tokens should be no more than ${maxLengthPerChoice} characters long.`;
390
+ }
391
+
392
+ if (!getContent(content)) {
393
+ choicesErrors[id] = 'Tokens should not be empty.';
394
+ } else {
395
+ const identicalAnswer = reversedChoices.slice(index + 1).some((c) => c.content === content);
396
+
397
+ if (identicalAnswer) {
398
+ choicesErrors[id] = 'Tokens content should be unique.';
399
+ }
400
+ }
401
+ });
402
+
403
+ const nbOfCategories = (categories || []).length;
404
+ const nbOfChoices = (choices || []).length;
405
+
406
+ if (nbOfCategories > maxCategories) {
407
+ errors.categoriesError = `No more than ${maxCategories} categories should be defined.`;
408
+ } else if (nbOfCategories < minCategories) {
409
+ errors.categoriesError = `There should be at least ${minCategories} category defined.`;
410
+ }
411
+
412
+ if (nbOfChoices < minChoices) {
413
+ errors.choicesError = `There should be at least ${minChoices} choices defined.`;
414
+ } else if (nbOfChoices > maxAnswerChoices) {
415
+ errors.choicesError = `No more than ${maxAnswerChoices} choices should be defined.`;
416
+ }
417
+
418
+ if (nbOfChoices && nbOfCategories) {
419
+ let hasAssociations = false;
420
+
421
+ (correctResponse || []).forEach((response) => {
422
+ const { choices = [], alternateResponses = [] } = response;
423
+
424
+ if (choices.length) {
425
+ hasAssociations = true;
426
+ } else {
427
+ alternateResponses.forEach((alternate) => {
428
+ if ((alternate || []).length) {
429
+ hasAssociations = true;
430
+ }
431
+ });
432
+ }
433
+ });
434
+
435
+ let duplicateAlternateIndex = -1;
436
+ let duplicateCategory = '';
437
+ (correctResponse || []).forEach((response) => {
438
+ const { choices = [], alternateResponses = [], category } = response;
439
+
440
+ if (duplicateAlternateIndex === -1) {
441
+ duplicateAlternateIndex = isCorrectResponseDuplicated(choices, alternateResponses);
442
+
443
+ if (duplicateAlternateIndex === -1) {
444
+ duplicateAlternateIndex = isAlternateDuplicated(alternateResponses);
445
+ }
446
+
447
+ duplicateCategory = category;
448
+ }
449
+ });
450
+
451
+ if (duplicateAlternateIndex > -1) {
452
+ errors.duplicateAlternate = { index: duplicateAlternateIndex, category: duplicateCategory };
453
+ }
454
+
455
+ if (!hasAssociations) {
456
+ errors.associationError = 'At least one token should be assigned to at least one category.';
457
+ }
458
+ }
459
+
460
+ if (!isEmpty(choicesErrors)) {
461
+ errors.choicesErrors = choicesErrors;
462
+ }
463
+
464
+ if (!isEmpty(categoriesErrors)) {
465
+ errors.categoriesErrors = categoriesErrors;
466
+ }
467
+
468
+ return errors;
469
+ };
@@ -0,0 +1,79 @@
1
+ // @ts-nocheck
2
+ /**
3
+ * @synced-from pie-elements/packages/categorize/controller/src/utils.js
4
+ * @auto-generated
5
+ *
6
+ * This file is automatically synced from pie-elements and converted to TypeScript.
7
+ * Manual edits will be overwritten on next sync.
8
+ * To make changes, edit the upstream JavaScript file and run sync again.
9
+ */
10
+
11
+ // used in configure too, for consistency modify it there too
12
+ export const multiplePlacements = { enabled: 'Yes', disabled: 'No', perChoice: 'Set Per Choice' };
13
+
14
+ // used to validate the config
15
+ export const isCorrectResponseDuplicated = (choices, alternate) => {
16
+ if (choices.length < 1 || alternate.length < 1) {
17
+ return -1;
18
+ }
19
+
20
+ const stringChoices = JSON.stringify(choices.sort());
21
+ const stringAlternate = alternate.reduce(
22
+ (total, current) => (current.length > 0 ? [...total, JSON.stringify(current.sort())] : total),
23
+ [],
24
+ );
25
+ const foundIndexDuplicate = stringAlternate.findIndex((alternate) => alternate.length && alternate === stringChoices);
26
+
27
+ return foundIndexDuplicate;
28
+ };
29
+
30
+ export const isAlternateDuplicated = (alternate) => {
31
+ if (alternate.length <= 1) {
32
+ return -1;
33
+ }
34
+
35
+ const elementSet = new Set();
36
+ const stringAlternate = alternate.reduce(
37
+ (total, current) => (current.length > 0 ? [...total, JSON.stringify(current.sort())] : total),
38
+ [],
39
+ );
40
+
41
+ for (let i = 0; i < stringAlternate.length; i++) {
42
+ if (elementSet.has(stringAlternate[i]) && stringAlternate[i]) {
43
+ return i;
44
+ }
45
+
46
+ elementSet.add(stringAlternate[i]);
47
+ }
48
+
49
+ return -1;
50
+ };
51
+
52
+ // calculate the minimum number of populated response areas (categories) in the correct answer or alternates
53
+ // and create an array with the possible responses ids
54
+ export const getCompleteResponseDetails = (correctResponse, alternates, allChoices) => {
55
+ const choicesPerCategory = correctResponse.map((category) => category.choices);
56
+ const possibleResponses = [choicesPerCategory.flat()];
57
+ let responseAreasToBeFilled = choicesPerCategory.filter((choices) => choices.length).length;
58
+
59
+ if (alternates.length) {
60
+ const alternatesPerChoice = alternates[0]?.length || 0; // number of alternates
61
+
62
+ [...Array(alternatesPerChoice).keys()].forEach((index) => {
63
+ const alternatesPerResponse = alternates.map((alternate) => alternate[index]);
64
+ const filledCategories = alternatesPerResponse.filter((category) => category?.length).length;
65
+ possibleResponses.push(alternatesPerResponse.flat());
66
+
67
+ if (filledCategories < responseAreasToBeFilled) {
68
+ responseAreasToBeFilled = filledCategories;
69
+ }
70
+ });
71
+ }
72
+
73
+ // check if any correct answer have any unplaced answer choices
74
+ const hasUnplacedChoices = possibleResponses.some(
75
+ (response) => !allChoices.every((val) => response.includes(val?.id)),
76
+ );
77
+
78
+ return { responseAreasToBeFilled, possibleResponses, hasUnplacedChoices };
79
+ };
@@ -0,0 +1,139 @@
1
+ // @ts-nocheck
2
+ /**
3
+ * @synced-from pie-elements/packages/categorize/src/categorize/categories.jsx
4
+ * @auto-generated
5
+ *
6
+ * This file is automatically synced from pie-elements and converted to TypeScript.
7
+ * Manual edits will be overwritten on next sync.
8
+ * To make changes, edit the upstream JavaScript file and run sync again.
9
+ */
10
+
11
+ import React from 'react';
12
+ import PropTypes from 'prop-types';
13
+ import { styled } from '@mui/material/styles';
14
+ import { color } from '@pie-lib/render-ui';
15
+
16
+ import GridContent from './grid-content.js';
17
+ import Category, { CategoryType } from './category.js';
18
+
19
+ export { CategoryType };
20
+
21
+ export class Categories extends React.Component {
22
+ static propTypes = {
23
+ categories: PropTypes.arrayOf(PropTypes.shape(CategoryType)),
24
+ model: PropTypes.shape({
25
+ categoriesPerRow: PropTypes.number,
26
+ }),
27
+ disabled: PropTypes.bool,
28
+ onDropChoice: PropTypes.func.isRequired,
29
+ onRemoveChoice: PropTypes.func.isRequired,
30
+ rowLabels: PropTypes.array,
31
+ };
32
+
33
+ static defaultProps = {
34
+ model: {
35
+ categoriesPerRow: 1,
36
+ },
37
+ };
38
+
39
+ render() {
40
+ const { categories, model, disabled, onDropChoice, onRemoveChoice, rowLabels } = this.props;
41
+ const { categoriesPerRow, minRowHeight } = model;
42
+
43
+ // split categories into an array of arrays (inner array),
44
+ // where each inner array represents how many categories should be displayed on one row
45
+ const chunkedCategories = [];
46
+ const cats = categories || [];
47
+ for (let i = 0; i < cats.length; i += categoriesPerRow) {
48
+ chunkedCategories.push(cats.slice(i, i + categoriesPerRow));
49
+ }
50
+
51
+ const hasNonEmptyString = (array) => {
52
+ let found = false;
53
+
54
+ (array || []).forEach((element) => {
55
+ if (typeof element === 'string' && element.trim() !== '' && element.trim() !== '<div></div>') {
56
+ found = true;
57
+ }
58
+ });
59
+
60
+ return found;
61
+ };
62
+
63
+ return (
64
+ <GridContent
65
+ columns={categoriesPerRow}
66
+ rows={Math.ceil(categories.length / categoriesPerRow) * 2}
67
+ extraStyle={{ flex: 1 }}
68
+ >
69
+ {chunkedCategories.map((cat, rowIndex) => {
70
+ let items = [];
71
+
72
+ // for each inner array of categories, create a row with category titles
73
+ // first cell of row has to be the row label
74
+ cat.forEach((c, columnIndex) => {
75
+ items.push(
76
+ <div style={{ display: 'flex' }}>
77
+ {columnIndex === 0 && hasNonEmptyString(rowLabels) ? (
78
+ <StyledRowLabel
79
+ key={rowIndex}
80
+ dangerouslySetInnerHTML={{
81
+ __html: rowLabels[rowIndex] || '',
82
+ }}
83
+ />
84
+ ) : null}
85
+ <StyledCategoryWrapper>
86
+ <StyledLabel
87
+ key={`category-label-${rowIndex}-${columnIndex}`}
88
+ dangerouslySetInnerHTML={{ __html: c.label }}
89
+ />
90
+
91
+ <Category
92
+ minRowHeight={minRowHeight}
93
+ onDropChoice={(h) => onDropChoice(c.id, h)}
94
+ onRemoveChoice={onRemoveChoice}
95
+ disabled={disabled}
96
+ key={`category-element-${rowIndex}-${columnIndex}`}
97
+ {...c}
98
+ />
99
+ </StyledCategoryWrapper>
100
+ </div>,
101
+ );
102
+ });
103
+
104
+ // if the last row has fewer categories than max on a row, fill the spaces with divs
105
+ items = items.concat(
106
+ Array(categoriesPerRow - cat.length)
107
+ .fill(<div />)
108
+ .map((value, index) => <div key={`fill-space-final-${index}`} />),
109
+ );
110
+
111
+ return items;
112
+ })}
113
+ </GridContent>
114
+ );
115
+ }
116
+ }
117
+
118
+ const StyledLabel: any = styled('div')(({ theme }) => ({
119
+ color: color.text(),
120
+ backgroundColor: color.background(),
121
+ textAlign: 'center',
122
+ paddingTop: theme.spacing(1),
123
+ }));
124
+
125
+ const StyledRowLabel: any = styled('div')({
126
+ alignItems: 'center',
127
+ display: 'flex',
128
+ justifyContent: 'center',
129
+ flex: 0.5,
130
+ marginRight: '12px',
131
+ });
132
+
133
+ const StyledCategoryWrapper: any = styled('div')({
134
+ display: 'flex',
135
+ flex: '2',
136
+ flexDirection: 'column',
137
+ });
138
+
139
+ export default Categories;