// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see .
/**
* Customized output for the attempt administration chart.
*
* The class overrides some definitions from core/chart_output_chartjs for custom output.
*
* @module mod_adaptivequiz/attempt_administration_chart_output
* @copyright 2024 Vitaly Potenko
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define([
'core/chart_output_chartjs',
'core/chartjs',
'mod_adaptivequiz/attempt_administration_chart_dataset_config'
], function(
Output,
Chartjs,
DatasetConfig
) {
/**
* Used to set the color as a hex code to avoid inconsistencies from Chartjs when displaying the color with opacity specified.
*/
const STANDARD_ERROR_LABEL_COLOR = '#ffcce0';
/**
* A filter callback for tooltip items.
*
* @param {Object} tooltipItem
* @return {Boolean}
*/
const tooltipItemsFilter = function (tooltipItem) {
return !(tooltipItem.datasetIndex === DatasetConfig.indices.STANDARD_ERROR_MAX
|| tooltipItem.datasetIndex === DatasetConfig.indices.STANDARD_ERROR_MIN);
};
/**
* A callback to add text after a tooltip item.
*
* @param {Object} tooltipItem
* @return {String}
*/
const afterTooltipItemLabel = function (tooltipItem) {
// Show extra text only after the ability measure and administered difficulty items.
if (!(tooltipItem.datasetIndex === DatasetConfig.indices.ABILITY_MEASURE
|| tooltipItem.datasetIndex === DatasetConfig.indices.ADMINISTERED_DIFFICULTY)) {
return '';
}
// If this is the ability measure item.
if (tooltipItem.datasetIndex === DatasetConfig.indices.ABILITY_MEASURE) {
// Reach out to the standard error data.
const stdErrorSeries = this._chart.getSeries()[DatasetConfig.indices.STANDARD_ERROR_PERCENT];
const stdErrorValue = stdErrorSeries.getValues()[tooltipItem.dataIndex];
return `${stdErrorSeries.getLabel()}: ${stdErrorValue}`;
}
// The rest case - administered difficulty item.
// Reach out to the right/wrong data.
const rightWrongSeries = this._chart.getSeries()[DatasetConfig.indices.CORRECT_WRONG_FLAG];
const rightWrongValue = rightWrongSeries.getValues()[tooltipItem.dataIndex];
return `${rightWrongSeries.getLabel()}: ${rightWrongValue}`;
};
/**
* Returns part of the config to set up the legend.
*
* @return {Object}
*/
const legendConfig = function () {
return {
labels: {
generateLabels(chart) {
let labels = Chartjs.defaults.plugins.legend.labels.generateLabels(chart);
// Store the standard error label for future use.
const stdErrorLabelText = labels.find((label) =>
label.datasetIndex === DatasetConfig.indices.STANDARD_ERROR_PERCENT).text;
// Remove everything hidden and one of the standard error labels.
labels = labels.filter((label) =>
!label.hidden && label.datasetIndex !== DatasetConfig.indices.STANDARD_ERROR_MIN);
// Order by dataset index.
labels.sort((label1, label2) => label1.datasetIndex - label2.datasetIndex);
// Convert one of the standard error labels to a proper one.
const stdErrorMaxIndex = labels.findIndex((label) =>
label.datasetIndex === DatasetConfig.indices.STANDARD_ERROR_MAX);
labels[stdErrorMaxIndex].text = stdErrorLabelText;
labels[stdErrorMaxIndex].strokeStyle = STANDARD_ERROR_LABEL_COLOR;
labels[stdErrorMaxIndex].fillStyle = STANDARD_ERROR_LABEL_COLOR;
return labels;
}
},
onClick: function () {
return false;
}
};
};
/**
* Output for the attempt administration chart.
*
* @class
* @extends {module:core/chart_output_chartjs}
*/
function AttemptAdministrationChartOutput() {
Output.apply(this, arguments);
}
AttemptAdministrationChartOutput.prototype = Object.create(Output.prototype);
/**
* Overrides config definition to add more custom features.
*
* @protected
* @override
* @return {Object}
*/
AttemptAdministrationChartOutput.prototype._makeConfig = function () {
let config = Output.prototype._makeConfig.apply(this, arguments);
// Define draw order.
config.data.datasets[DatasetConfig.indices.ABILITY_MEASURE].order = DatasetConfig.orderWeights.ABILITY_MEASURE;
config.data.datasets[DatasetConfig.indices.TARGET_DIFFICULTY].order = DatasetConfig.orderWeights.TARGET_DIFFICULTY;
config.data.datasets[DatasetConfig.indices.ADMINISTERED_DIFFICULTY].order =
DatasetConfig.orderWeights.ADMINISTERED_DIFFICULTY;
config.data.datasets[DatasetConfig.indices.STANDARD_ERROR_MAX].order = DatasetConfig.orderWeights.STANDARD_ERROR_BORDER;
config.data.datasets[DatasetConfig.indices.STANDARD_ERROR_MIN].order = DatasetConfig.orderWeights.STANDARD_ERROR_BORDER;
// Hide lines and points for standard error min/max datasets.
// In Chart.js version prior to 4.2.1 points visibility can be controlled with the 'pointRadius' property.
config.data.datasets[DatasetConfig.indices.STANDARD_ERROR_MAX].pointRadius = 0;
config.data.datasets[DatasetConfig.indices.STANDARD_ERROR_MIN].pointRadius = 0;
// For Chart.js since 4.2.1.
config.data.datasets[DatasetConfig.indices.STANDARD_ERROR_MAX].pointStyle = false;
config.data.datasets[DatasetConfig.indices.STANDARD_ERROR_MIN].pointStyle = false;
config.data.datasets[DatasetConfig.indices.STANDARD_ERROR_MAX].showLine = false;
config.data.datasets[DatasetConfig.indices.STANDARD_ERROR_MIN].showLine = false;
// Hide entire datasets with standard error percentages and right/wrong flags.
config.data.datasets[DatasetConfig.indices.STANDARD_ERROR_PERCENT].hidden = true;
config.data.datasets[DatasetConfig.indices.CORRECT_WRONG_FLAG].hidden = true;
// Tooltip.
config.options.plugins.tooltip.filter = tooltipItemsFilter;
config.options.plugins.tooltip.callbacks.afterLabel = afterTooltipItemLabel.bind(this);
config.options.plugins.tooltip.itemSort = function (tooltip1, tooltip2) {
return tooltip1.datasetIndex - tooltip2.datasetIndex;
};
// Legend.
config.options.plugins.legend = legendConfig();
return config;
};
return AttemptAdministrationChartOutput;
});