Skip to content

Commit

Permalink
Merge pull request #2353 from oat-sa/hotfix/AUT-2882/categories-in-su…
Browse files Browse the repository at this point in the history
…bsections

Backport AUT-2882 categories in subsections to November 2022
  • Loading branch information
bziondik authored Jan 18, 2023
2 parents 13237fd + 103a54e commit e27ec6b
Show file tree
Hide file tree
Showing 13 changed files with 456 additions and 222 deletions.
28 changes: 17 additions & 11 deletions views/js/controller/creator/helpers/category.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,14 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright (c) 2017 (original work) Open Assessment Technologies SA ;
* Copyright (c) 2017-2023 (original work) Open Assessment Technologies SA ;
*/
/**
* Helper that provides a way to browse all categories attached to a test model at the item level.
*
* @author Jean-Sébastien Conan <[email protected]>
*/
define([
'lodash'
], function (_) {
define(['lodash'], function (_) {
'use strict';

/**
Expand All @@ -41,13 +39,21 @@ define([
* @param {Function} cb
*/
function eachCategories(testModel, cb) {
const getCategoriesRecursively = section => {
_.forEach(section.sectionParts, function (sectionPart) {
if (sectionPart['qti-type'] === 'assessmentItemRef') {
_.forEach(sectionPart.categories, function (category) {
cb(category, sectionPart);
});
}
if (sectionPart['qti-type'] === 'assessmentSection') {
getCategoriesRecursively(sectionPart);
}
});
};
_.forEach(testModel.testParts, function (testPart) {
_.forEach(testPart.assessmentSections, function (assessmentSection) {
_.forEach(assessmentSection.sectionParts, function (itemRef) {
_.forEach(itemRef.categories, function(category) {
cb(category, itemRef);
});
});
getCategoriesRecursively(assessmentSection);
});
});
}
Expand All @@ -70,7 +76,7 @@ define([
*/
listCategories: function listCategories(testModel) {
var categories = {};
eachCategories(testModel, function(category) {
eachCategories(testModel, function (category) {
if (!isCategoryOption(category)) {
categories[category] = true;
}
Expand All @@ -86,7 +92,7 @@ define([
*/
listOptions: function listOptions(testModel) {
var options = {};
eachCategories(testModel, function(category) {
eachCategories(testModel, function (category) {
if (isCategoryOption(category)) {
options[category] = true;
}
Expand Down
115 changes: 56 additions & 59 deletions views/js/controller/creator/helpers/categorySelector.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright (c) 2017 (original work) Open Assessment Technologies SA;
* Copyright (c) 2017-2023 (original work) Open Assessment Technologies SA;
*/
/**
* This helper manages the category selection UI:
Expand All @@ -32,53 +32,51 @@ define([
'taoQtiTest/controller/creator/templates/index',
'taoQtiTest/controller/creator/helpers/featureVisibility',
'select2'
], function($, _, __, eventifier, tooltip, templates, featureVisibility) {
], function ($, _, __, eventifier, tooltip, templates, featureVisibility) {
'use strict';

var allPresets = [],
let allPresets = [],
allQtiCategoriesPresets = [];


function categorySelectorFactory($container) {
var categorySelector,

$presetsContainer = $container.find('.category-presets'),
$presetsCheckboxes,
$customCategoriesSelect = $container.find('[name=category-custom]');
const $presetsContainer = $container.find('.category-presets');
const $customCategoriesSelect = $container.find('[name=category-custom]');

/**
* Read the form state from the DOM and trigger an event with the result, so the listeners can update the item/section model
* @fires categorySelector#category-change
*/
function updateCategories() {
var selectedCategories,
indeterminatedCategories,

presetSelected = $container
const presetSelected = $container
.find('.category-preset input:checked')
.toArray()
.map(function(categoryEl) {
.map(function (categoryEl) {
return categoryEl.value;
}),
presetIndeterminate = $container
.find('.category-preset input:indeterminate')
.toArray()
.map(function(categoryEl) {
.map(function (categoryEl) {
return categoryEl.value;
}),
customSelected = $customCategoriesSelect.siblings('.select2-container').find('.select2-search-choice').not('.partial')
customSelected = $customCategoriesSelect
.siblings('.select2-container')
.find('.select2-search-choice')
.not('.partial')
.toArray()
.map(function(categoryEl) {
.map(function (categoryEl) {
return categoryEl.textContent && categoryEl.textContent.trim();
}),
customIndeterminate = $customCategoriesSelect.siblings('.select2-container').find('.select2-search-choice.partial')
customIndeterminate = $customCategoriesSelect
.siblings('.select2-container')
.find('.select2-search-choice.partial')
.toArray()
.map(function(categoryEl) {
.map(function (categoryEl) {
return categoryEl.textContent && categoryEl.textContent.trim();
});

selectedCategories = presetSelected.concat(customSelected);
indeterminatedCategories = presetIndeterminate.concat(customIndeterminate);
const selectedCategories = presetSelected.concat(customSelected);
const indeterminatedCategories = presetIndeterminate.concat(customIndeterminate);

/**
* @event categorySelector#category-change
Expand All @@ -88,7 +86,7 @@ define([
this.trigger('category-change', selectedCategories, indeterminatedCategories);
}

categorySelector = {
const categorySelector = {
/**
* Create the category selection form
*
Expand All @@ -97,43 +95,41 @@ define([
* @param {string} [level] one of the values `testPart`, `section` or `itemRef`
*/
createForm: function createForm(currentCategories, level) {
var self = this,
const self = this,
presetsTpl = templates.properties.categorypresets,
customCategories = _.difference(currentCategories, allQtiCategoriesPresets);

const filteredPresets = featureVisibility.filterVisiblePresets(allPresets, level);
// add preset checkboxes
$presetsContainer.append(
presetsTpl({presetGroups: filteredPresets})
);

$presetsContainer.on('click', function(e) {
var $preset = $(e.target).closest('.category-preset'),
$checkbox;
$presetsContainer.append(presetsTpl({ presetGroups: filteredPresets }));

$presetsContainer.on('click', function (e) {
const $preset = $(e.target).closest('.category-preset');
if ($preset.length) {
$checkbox = $preset.find('input');
const $checkbox = $preset.find('input');
$checkbox.prop('indeterminate', false);

_.defer(function() {
_.defer(function () {
updateCategories.call(self);
});
}
});

// init custom categories field
$customCategoriesSelect.select2({
width: '100%',
tags : customCategories,
multiple : true,
tokenSeparators: [",", " ", ";"],
formatNoMatches : function(){
return __('Enter a custom category');
},
maximumInputLength : 32
}).on('change', function(){
updateCategories.call(self);
});
$customCategoriesSelect
.select2({
width: '100%',
tags: customCategories,
multiple: true,
tokenSeparators: [',', ' ', ';'],
formatNoMatches: function () {
return __('Enter a custom category');
},
maximumInputLength: 32
})
.on('change', function () {
updateCategories.call(self);
});

// enable help tooltips
tooltip.lookup($container);
Expand All @@ -145,17 +141,15 @@ define([
* @param {String[]} [indeterminate] - categories in an indeterminate state at a section level
*/
updateFormState: function updateFormState(selected, indeterminate) {
var customCategories;

indeterminate = indeterminate || [];

customCategories = _.difference(selected.concat(indeterminate), allQtiCategoriesPresets);
const customCategories = _.difference(selected.concat(indeterminate), allQtiCategoriesPresets);

// Preset categories

$presetsCheckboxes = $container.find('.category-preset input');
$presetsCheckboxes.each(function() {
var category = this.value;
const $presetsCheckboxes = $container.find('.category-preset input');
$presetsCheckboxes.each(function () {
const category = this.value;

this.indeterminate = false;
this.checked = false;
Expand All @@ -171,13 +165,16 @@ define([

$customCategoriesSelect.select2('val', customCategories);

$customCategoriesSelect.siblings('.select2-container').find('.select2-search-choice').each(function(){
var $li = $(this);
var content = $li.find('div').text();
if(indeterminate.indexOf(content) !== -1){
$li.addClass('partial');
}
});
$customCategoriesSelect
.siblings('.select2-container')
.find('.select2-search-choice')
.each(function () {
const $li = $(this);
const content = $li.find('div').text();
if (indeterminate.indexOf(content) !== -1) {
$li.addClass('partial');
}
});
}
};

Expand All @@ -198,7 +195,7 @@ define([
* label: 'Next Part Warning',
* qtiCategory : 'x-tao-option-nextPartWarning',
* description : 'Displays a warning before the user finishes a part'
* },
* },
* ...
* ]
* },
Expand All @@ -218,7 +215,7 @@ define([
*/
function extractCategoriesFromPresets() {
return allPresets.reduce(function (prev, current) {
var groupIds = _.pluck(current.presets, 'qtiCategory');
const groupIds = _.pluck(current.presets, 'qtiCategory');
return prev.concat(groupIds);
}, []);
}
Expand Down
Loading

0 comments on commit e27ec6b

Please sign in to comment.