Merge branch 'MDL-76717_master' of https://github.com/marxjohnson/moodle
This commit is contained in:
@@ -31,6 +31,7 @@ require_once(__DIR__ . '/../../../../question/tests/behat/behat_question_base.ph
|
||||
use Behat\Gherkin\Node\TableNode as TableNode;
|
||||
|
||||
use Behat\Mink\Exception\ExpectationException as ExpectationException;
|
||||
use Behat\Mink\Exception\DriverException as DriverException;
|
||||
use mod_quiz\quiz_attempt;
|
||||
use mod_quiz\quiz_settings;
|
||||
|
||||
|
||||
@@ -63,7 +63,6 @@ class question_history_view extends view {
|
||||
protected function wanted_columns(): array {
|
||||
$this->requiredcolumns = [];
|
||||
$excludefeatures = [
|
||||
'question_usage_column',
|
||||
'history_action_column'
|
||||
];
|
||||
$questionbankcolumns = $this->get_question_bank_plugins();
|
||||
|
||||
+1
-1
@@ -6,6 +6,6 @@ define("qbank_usage/usage",["exports","core/fragment","core/modal_factory","core
|
||||
* @copyright 2021 Catalyst IT Australia Pty Ltd
|
||||
* @author Safat Shahin <safatshahin@catalyst-au.net>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_fragment=_interopRequireDefault(_fragment),_modal_factory=_interopRequireDefault(_modal_factory),_notification=_interopRequireDefault(_notification),Str=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(Str);_exports.init=(questionSelector,contextId)=>{let target=document.querySelector(questionSelector),questionId=target.getAttribute("data-questionid");target.addEventListener("click",(()=>{((questionId,contextId)=>{let args={questionid:questionId};_modal_factory.default.create({type:_modal_factory.default.types.CANCEL,title:Str.get_string("usageheader","qbank_usage"),body:_fragment.default.loadFragment("qbank_usage","question_usage",contextId,args),large:!0}).then((modal=>(modal.show(),modal.getRoot().on("click","a[href].page-link",(function(e){e.preventDefault();let attr=e.target.getAttribute("href");"#"!==attr&&(args.querystring=attr,modal.setBody(_fragment.default.loadFragment("qbank_usage","question_usage",contextId,args)))})),modal.getRoot().on("change","#question_usage_version_dropdown",(function(e){args.questionid=e.target.value,modal.setBody(_fragment.default.loadFragment("qbank_usage","question_usage",contextId,args))})),modal))).fail(_notification.default.exception)})(questionId,contextId)}))}}));
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_fragment=_interopRequireDefault(_fragment),_modal_factory=_interopRequireDefault(_modal_factory),_notification=_interopRequireDefault(_notification),Str=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(Str);let modal=null;const usageEvent=async(questionId,contextId,specificVersion)=>{let args={questionid:questionId,specificversion:specificVersion};if(null===modal){try{modal=await _modal_factory.default.create({type:_modal_factory.default.types.CANCEL,title:Str.get_string("usageheader","qbank_usage"),body:_fragment.default.loadFragment("qbank_usage","question_usage",contextId,args),large:!0})}catch(e){_notification.default.exception(e)}modal.show(),modal.getRoot().on("click","a[href].page-link",(function(e){e.preventDefault();let attr=e.target.getAttribute("href");"#"!==attr&&(args.querystring=attr,modal.setBody(_fragment.default.loadFragment("qbank_usage","question_usage",contextId,args)))})),modal.getRoot().on("change","#question_usage_version_dropdown",(function(e){args.questionid=e.target.value,modal.setBody(_fragment.default.loadFragment("qbank_usage","question_usage",contextId,args))}))}else modal.setBody(_fragment.default.loadFragment("qbank_usage","question_usage",contextId,args)),modal.show()};_exports.init=function(questionSelector,contextId){let specificVersion=arguments.length>2&&void 0!==arguments[2]&&arguments[2],target=document.querySelector(questionSelector),questionId=target.getAttribute("data-questionid");target.addEventListener("click",(()=>{usageEvent(questionId,contextId,specificVersion)}))}}));
|
||||
|
||||
//# sourceMappingURL=usage.min.js.map
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"usage.min.js","sources":["../src/usage.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Usage column selector js.\n *\n * @module qbank_usage/usage\n * @copyright 2021 Catalyst IT Australia Pty Ltd\n * @author Safat Shahin <[email protected]>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Fragment from 'core/fragment';\nimport ModalFactory from 'core/modal_factory';\nimport Notification from 'core/notification';\nimport * as Str from 'core/str';\n\n/**\n * Event listeners for the module.\n *\n * @method clickEvent\n * @param {int} questionId\n * @param {int} contextId\n */\nconst usageEvent = (questionId, contextId) => {\n let args = {\n questionid: questionId\n };\n ModalFactory.create({\n type: ModalFactory.types.CANCEL,\n title: Str.get_string('usageheader', 'qbank_usage'),\n body: Fragment.loadFragment('qbank_usage', 'question_usage', contextId, args),\n large: true,\n }).then((modal) => {\n modal.show();\n modal.getRoot().on('click', 'a[href].page-link', function(e) {\n e.preventDefault();\n let attr = e.target.getAttribute(\"href\");\n if (attr !== '#') {\n args.querystring = attr;\n modal.setBody(Fragment.loadFragment('qbank_usage', 'question_usage', contextId, args));\n }\n });\n // Version selection event.\n modal.getRoot().on('change', '#question_usage_version_dropdown', function(e) {\n args.questionid = e.target.value;\n modal.setBody(Fragment.loadFragment('qbank_usage', 'question_usage', contextId, args));\n });\n return modal;\n }).fail(Notification.exception);\n};\n\n/**\n * Entrypoint of the js.\n *\n * @method init\n * @param {string} questionSelector the question usage identifier.\n * @param {int} contextId the question context id.\n */\nexport const init = (questionSelector, contextId) => {\n let target = document.querySelector(questionSelector);\n let questionId = target.getAttribute('data-questionid');\n target.addEventListener('click', () => {\n // Call for the event listener to listed for clicks in any usage count row.\n usageEvent(questionId, contextId);\n });\n};\n"],"names":["questionSelector","contextId","target","document","querySelector","questionId","getAttribute","addEventListener","args","questionid","create","type","ModalFactory","types","CANCEL","title","Str","get_string","body","Fragment","loadFragment","large","then","modal","show","getRoot","on","e","preventDefault","attr","querystring","setBody","value","fail","Notification","exception","usageEvent"],"mappings":";;;;;;;;44BAuEoB,CAACA,iBAAkBC,iBAC/BC,OAASC,SAASC,cAAcJ,kBAChCK,WAAaH,OAAOI,aAAa,mBACrCJ,OAAOK,iBAAiB,SAAS,KAtClB,EAACF,WAAYJ,iBACxBO,KAAO,CACPC,WAAYJ,mCAEHK,OAAO,CAChBC,KAAMC,uBAAaC,MAAMC,OACzBC,MAAOC,IAAIC,WAAW,cAAe,eACrCC,KAAMC,kBAASC,aAAa,cAAe,iBAAkBnB,UAAWO,MACxEa,OAAO,IACRC,MAAMC,QACLA,MAAMC,OACND,MAAME,UAAUC,GAAG,QAAS,qBAAqB,SAASC,GACtDA,EAAEC,qBACEC,KAAOF,EAAEzB,OAAOI,aAAa,QACpB,MAATuB,OACArB,KAAKsB,YAAcD,KACnBN,MAAMQ,QAAQZ,kBAASC,aAAa,cAAe,iBAAkBnB,UAAWO,WAIxFe,MAAME,UAAUC,GAAG,SAAU,oCAAoC,SAASC,GACtEnB,KAAKC,WAAakB,EAAEzB,OAAO8B,MAC3BT,MAAMQ,QAAQZ,kBAASC,aAAa,cAAe,iBAAkBnB,UAAWO,UAE7Ee,SACRU,KAAKC,sBAAaC,YAejBC,CAAW/B,WAAYJ"}
|
||||
{"version":3,"file":"usage.min.js","sources":["../src/usage.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Usage column selector js.\n *\n * @module qbank_usage/usage\n * @copyright 2021 Catalyst IT Australia Pty Ltd\n * @author Safat Shahin <[email protected]>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Fragment from 'core/fragment';\nimport ModalFactory from 'core/modal_factory';\nimport Notification from 'core/notification';\nimport * as Str from 'core/str';\n\nlet modal = null;\n\n/**\n * Event listeners for the module.\n *\n * @method clickEvent\n * @param {int} questionId\n * @param {int} contextId\n * @param {boolean} specificVersion Is the view listing specific question versions?\n */\nconst usageEvent = async(questionId, contextId, specificVersion) => {\n let args = {\n questionid: questionId,\n specificversion: specificVersion,\n };\n if (modal === null) {\n try {\n modal = await ModalFactory.create({\n type: ModalFactory.types.CANCEL,\n title: Str.get_string('usageheader', 'qbank_usage'),\n body: Fragment.loadFragment('qbank_usage', 'question_usage', contextId, args),\n large: true,\n });\n } catch (e) {\n Notification.exception(e);\n }\n\n modal.show();\n modal.getRoot().on('click', 'a[href].page-link', function(e) {\n e.preventDefault();\n let attr = e.target.getAttribute(\"href\");\n if (attr !== '#') {\n args.querystring = attr;\n modal.setBody(Fragment.loadFragment('qbank_usage', 'question_usage', contextId, args));\n }\n });\n // Version selection event.\n modal.getRoot().on('change', '#question_usage_version_dropdown', function(e) {\n args.questionid = e.target.value;\n modal.setBody(Fragment.loadFragment('qbank_usage', 'question_usage', contextId, args));\n });\n } else {\n modal.setBody(Fragment.loadFragment('qbank_usage', 'question_usage', contextId, args));\n modal.show();\n }\n\n};\n\n/**\n * Entrypoint of the js.\n *\n * @method init\n * @param {string} questionSelector the question usage identifier.\n * @param {int} contextId the question context id.\n * @param {boolean} specificVersion Is the view listing specific question versions?\n */\nexport const init = (questionSelector, contextId, specificVersion = false) => {\n let target = document.querySelector(questionSelector);\n let questionId = target.getAttribute('data-questionid');\n target.addEventListener('click', () => {\n // Call for the event listener to listed for clicks in any usage count row.\n usageEvent(questionId, contextId, specificVersion);\n });\n};\n"],"names":["modal","usageEvent","async","questionId","contextId","specificVersion","args","questionid","specificversion","ModalFactory","create","type","types","CANCEL","title","Str","get_string","body","Fragment","loadFragment","large","e","exception","show","getRoot","on","preventDefault","attr","target","getAttribute","querystring","setBody","value","questionSelector","document","querySelector","addEventListener"],"mappings":";;;;;;;;k4BA6BIA,MAAQ,WAUNC,WAAaC,MAAMC,WAAYC,UAAWC,uBACxCC,KAAO,CACPC,WAAYJ,WACZK,gBAAiBH,oBAEP,OAAVL,MAAgB,KAEZA,YAAcS,uBAAaC,OAAO,CAC9BC,KAAMF,uBAAaG,MAAMC,OACzBC,MAAOC,IAAIC,WAAW,cAAe,eACrCC,KAAMC,kBAASC,aAAa,cAAe,iBAAkBf,UAAWE,MACxEc,OAAO,IAEb,MAAOC,yBACQC,UAAUD,GAG3BrB,MAAMuB,OACNvB,MAAMwB,UAAUC,GAAG,QAAS,qBAAqB,SAASJ,GACtDA,EAAEK,qBACEC,KAAON,EAAEO,OAAOC,aAAa,QACpB,MAATF,OACArB,KAAKwB,YAAcH,KACnB3B,MAAM+B,QAAQb,kBAASC,aAAa,cAAe,iBAAkBf,UAAWE,WAIxFN,MAAMwB,UAAUC,GAAG,SAAU,oCAAoC,SAASJ,GACtEf,KAAKC,WAAac,EAAEO,OAAOI,MAC3BhC,MAAM+B,QAAQb,kBAASC,aAAa,cAAe,iBAAkBf,UAAWE,eAGpFN,MAAM+B,QAAQb,kBAASC,aAAa,cAAe,iBAAkBf,UAAWE,OAChFN,MAAMuB,sBAaM,SAACU,iBAAkB7B,eAAWC,wEAC1CuB,OAASM,SAASC,cAAcF,kBAChC9B,WAAayB,OAAOC,aAAa,mBACrCD,OAAOQ,iBAAiB,SAAS,KAE7BnC,WAAWE,WAAYC,UAAWC"}
|
||||
@@ -27,23 +27,33 @@ import ModalFactory from 'core/modal_factory';
|
||||
import Notification from 'core/notification';
|
||||
import * as Str from 'core/str';
|
||||
|
||||
let modal = null;
|
||||
|
||||
/**
|
||||
* Event listeners for the module.
|
||||
*
|
||||
* @method clickEvent
|
||||
* @param {int} questionId
|
||||
* @param {int} contextId
|
||||
* @param {boolean} specificVersion Is the view listing specific question versions?
|
||||
*/
|
||||
const usageEvent = (questionId, contextId) => {
|
||||
const usageEvent = async(questionId, contextId, specificVersion) => {
|
||||
let args = {
|
||||
questionid: questionId
|
||||
questionid: questionId,
|
||||
specificversion: specificVersion,
|
||||
};
|
||||
ModalFactory.create({
|
||||
type: ModalFactory.types.CANCEL,
|
||||
title: Str.get_string('usageheader', 'qbank_usage'),
|
||||
body: Fragment.loadFragment('qbank_usage', 'question_usage', contextId, args),
|
||||
large: true,
|
||||
}).then((modal) => {
|
||||
if (modal === null) {
|
||||
try {
|
||||
modal = await ModalFactory.create({
|
||||
type: ModalFactory.types.CANCEL,
|
||||
title: Str.get_string('usageheader', 'qbank_usage'),
|
||||
body: Fragment.loadFragment('qbank_usage', 'question_usage', contextId, args),
|
||||
large: true,
|
||||
});
|
||||
} catch (e) {
|
||||
Notification.exception(e);
|
||||
}
|
||||
|
||||
modal.show();
|
||||
modal.getRoot().on('click', 'a[href].page-link', function(e) {
|
||||
e.preventDefault();
|
||||
@@ -58,8 +68,11 @@ const usageEvent = (questionId, contextId) => {
|
||||
args.questionid = e.target.value;
|
||||
modal.setBody(Fragment.loadFragment('qbank_usage', 'question_usage', contextId, args));
|
||||
});
|
||||
return modal;
|
||||
}).fail(Notification.exception);
|
||||
} else {
|
||||
modal.setBody(Fragment.loadFragment('qbank_usage', 'question_usage', contextId, args));
|
||||
modal.show();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -68,12 +81,13 @@ const usageEvent = (questionId, contextId) => {
|
||||
* @method init
|
||||
* @param {string} questionSelector the question usage identifier.
|
||||
* @param {int} contextId the question context id.
|
||||
* @param {boolean} specificVersion Is the view listing specific question versions?
|
||||
*/
|
||||
export const init = (questionSelector, contextId) => {
|
||||
export const init = (questionSelector, contextId, specificVersion = false) => {
|
||||
let target = document.querySelector(questionSelector);
|
||||
let questionId = target.getAttribute('data-questionid');
|
||||
target.addEventListener('click', () => {
|
||||
// Call for the event listener to listed for clicks in any usage count row.
|
||||
usageEvent(questionId, contextId);
|
||||
usageEvent(questionId, contextId, specificVersion);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -30,25 +30,32 @@ class helper {
|
||||
* Get the usage count for a question.
|
||||
*
|
||||
* @param \question_definition $question
|
||||
* @param bool $specificversion Count usages just for this version of the question?
|
||||
* @return int
|
||||
*/
|
||||
public static function get_question_entry_usage_count($question) {
|
||||
public static function get_question_entry_usage_count($question, bool $specificversion = false) {
|
||||
global $DB;
|
||||
|
||||
$sql = 'SELECT COUNT(*) FROM (' . self::question_usage_sql() . ') quizid';
|
||||
$sql = 'SELECT COUNT(*) FROM (' . self::question_usage_sql($specificversion) . ') quizid';
|
||||
|
||||
return $DB->count_records_sql($sql, [$question->id, $question->questionbankentryid, 'mod_quiz', 'slot']);
|
||||
$params = [$question->id, $question->questionbankentryid, 'mod_quiz', 'slot'];
|
||||
if ($specificversion) {
|
||||
$params[] = $question->id;
|
||||
}
|
||||
|
||||
return $DB->count_records_sql($sql, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the sql for usage data.
|
||||
*
|
||||
* @param bool $specificversion Count usages just for this version of the question?
|
||||
* @return string
|
||||
*/
|
||||
public static function question_usage_sql(): string {
|
||||
$sqlset = "(". self::get_question_attempt_usage_sql() .")".
|
||||
"UNION".
|
||||
"(". self::get_question_bank_usage_sql() .")";
|
||||
public static function question_usage_sql(bool $specificversion = false): string {
|
||||
$sqlset = "(". self::get_question_attempt_usage_sql($specificversion) .")".
|
||||
"UNION".
|
||||
"(". self::get_question_bank_usage_sql($specificversion) .")";
|
||||
return $sqlset;
|
||||
}
|
||||
|
||||
@@ -94,9 +101,10 @@ class helper {
|
||||
* parameters in the correct order which are the question id, then
|
||||
* the component and finally the question area.
|
||||
*
|
||||
* @param bool $specificversion Count usages just for this version of the question?
|
||||
* @return string
|
||||
*/
|
||||
public static function get_question_bank_usage_sql(): string {
|
||||
public static function get_question_bank_usage_sql(bool $specificversion = false): string {
|
||||
$sql = "SELECT qz.id as quizid,
|
||||
qz.name as modulename,
|
||||
qz.course as courseid
|
||||
@@ -108,6 +116,23 @@ class helper {
|
||||
WHERE qv.questionbankentryid = ?
|
||||
AND qr.component = ?
|
||||
AND qr.questionarea = ?";
|
||||
|
||||
if ($specificversion) {
|
||||
// Only get results where the reference matches the specific question ID that was requested,
|
||||
// or the question ID that's requested is the latest version, and the reference is set to null (always latest version).
|
||||
$sql .= " AND qv.questionid = ?
|
||||
AND (
|
||||
qv.version = qr.version
|
||||
OR (
|
||||
qr.version IS NULL
|
||||
AND qv.version = (
|
||||
SELECT MAX(qv1.version)
|
||||
FROM {question_versions} qv1
|
||||
WHERE qv1.questionbankentryid = qbe.id
|
||||
)
|
||||
)
|
||||
)";
|
||||
}
|
||||
return $sql;
|
||||
}
|
||||
|
||||
@@ -118,19 +143,29 @@ class helper {
|
||||
* called accompanying a $params array which includes the necessary
|
||||
* parameter, the question id.
|
||||
*
|
||||
* @param bool $specificversion Count usages just for this version of the question?
|
||||
* @return string
|
||||
*/
|
||||
public static function get_question_attempt_usage_sql(): string {
|
||||
public static function get_question_attempt_usage_sql(bool $specificversion = false): string {
|
||||
$sql = "SELECT qz.id as quizid,
|
||||
qz.name as modulename,
|
||||
qz.course as courseid
|
||||
FROM {quiz} qz
|
||||
JOIN {quiz_attempts} qa ON qa.quiz = qz.id
|
||||
JOIN {question_usages} qu ON qu.id = qa.uniqueid
|
||||
JOIN {question_attempts} qatt ON qatt.questionusageid = qu.id
|
||||
JOIN {question_attempts} qatt ON qatt.questionusageid = qu.id";
|
||||
if ($specificversion) {
|
||||
$sql .= "
|
||||
JOIN {question} q ON q.id = qatt.questionid
|
||||
WHERE qa.preview = 0
|
||||
AND q.id = ?";
|
||||
AND q.id = ?";
|
||||
} else {
|
||||
$sql .= "
|
||||
JOIN {question_versions} qv ON qv.questionid = qatt.questionid
|
||||
JOIN {question_versions} qv2 ON qv.questionbankentryid = qv2.questionbankentryid
|
||||
WHERE qa.preview = 0
|
||||
AND qv2.questionid = ?";
|
||||
}
|
||||
return $sql;
|
||||
}
|
||||
|
||||
|
||||
@@ -42,12 +42,13 @@ class question_usage_column extends column_base {
|
||||
|
||||
protected function display_content($question, $rowclasses): void {
|
||||
global $PAGE;
|
||||
$usagecount = helper::get_question_entry_usage_count($question);
|
||||
$usagecount = helper::get_question_entry_usage_count($question, $this->qbank->is_listing_specific_versions());
|
||||
$attributes = [];
|
||||
if (question_has_capability_on($question, 'view')) {
|
||||
$target = 'questionusagepreview_' . $question->id;
|
||||
$datatarget = '[data-target="' . $target . '"]';
|
||||
$PAGE->requires->js_call_amd('qbank_usage/usage', 'init', [$datatarget, $question->contextid]);
|
||||
$PAGE->requires->js_call_amd('qbank_usage/usage', 'init',
|
||||
[$datatarget, $question->contextid, $this->qbank->is_listing_specific_versions()]);
|
||||
$attributes = [
|
||||
'href' => '#',
|
||||
'data-target' => $target,
|
||||
|
||||
@@ -50,6 +50,11 @@ class question_usage_table extends table_sql {
|
||||
*/
|
||||
public $question;
|
||||
|
||||
/**
|
||||
* @var bool $specificversion Are we displaying the usage for a specific version, rather than all versions of the question?
|
||||
*/
|
||||
protected $specificversion;
|
||||
|
||||
/**
|
||||
* constructor.
|
||||
* Sets the SQL for the table and the pagination.
|
||||
@@ -57,7 +62,7 @@ class question_usage_table extends table_sql {
|
||||
* @param string $uniqueid
|
||||
* @param \question_definition $question
|
||||
*/
|
||||
public function __construct(string $uniqueid, \question_definition $question) {
|
||||
public function __construct(string $uniqueid, \question_definition $question, bool $specificversion = false) {
|
||||
global $PAGE;
|
||||
parent::__construct($uniqueid);
|
||||
$this->question = $question;
|
||||
@@ -74,17 +79,22 @@ class question_usage_table extends table_sql {
|
||||
$this->define_columns($columns);
|
||||
$this->define_headers($headers);
|
||||
$this->define_baseurl($PAGE->url);
|
||||
$this->specificversion = $specificversion;
|
||||
$this->set_attribute('id', 'question_usage_table');
|
||||
}
|
||||
|
||||
public function query_db($pagesize, $useinitialsbar = true) {
|
||||
global $DB;
|
||||
if (!$this->is_downloading()) {
|
||||
$total = helper::get_question_entry_usage_count($this->question);
|
||||
$total = helper::get_question_entry_usage_count($this->question, $this->specificversion);
|
||||
$this->pagesize($pagesize, $total);
|
||||
}
|
||||
|
||||
$sql = helper::question_usage_sql();
|
||||
$sql = helper::question_usage_sql($this->specificversion);
|
||||
$params = [$this->question->id, $this->question->questionbankentryid, 'mod_quiz', 'slot'];
|
||||
if ($this->specificversion) {
|
||||
$params[] = $this->question->id;
|
||||
}
|
||||
|
||||
if (!$this->is_downloading()) {
|
||||
$this->rawdata = $DB->get_records_sql($sql, $params, $this->get_page_start(), $this->get_page_size());
|
||||
|
||||
@@ -48,7 +48,8 @@ function qbank_usage_output_fragment_question_usage(array $args): string {
|
||||
$transaction->allow_commit();
|
||||
$displaydata['question'] = $quba->render_question($slot, $options, '1');
|
||||
|
||||
$questionusagetable = new \qbank_usage\tables\question_usage_table('question_usage_table', $question);
|
||||
$specificversion = clean_param($args['specificversion'] ?? false, PARAM_BOOL);
|
||||
$questionusagetable = new \qbank_usage\tables\question_usage_table('question_usage_table', $question, $specificversion);
|
||||
$questionusagetable->baseurl = new moodle_url('');
|
||||
if (isset($args['querystring'])) {
|
||||
$querystring = preg_replace('/^\?/', '', $args['querystring']);
|
||||
|
||||
@@ -38,3 +38,15 @@ Feature: Use the qbank plugin manager page for question usage
|
||||
Then I should see "Version 1"
|
||||
And I click on "Close" "button" in the ".modal-dialog" "css_element"
|
||||
And I should see "0" on the usage column
|
||||
|
||||
@javascript
|
||||
Scenario: Question usage modal should work with usage data
|
||||
Given quiz "Test quiz" contains the following questions:
|
||||
| question | page |
|
||||
| First question | 1 |
|
||||
And I am on the "Test quiz" "mod_quiz > question bank" page logged in as "admin"
|
||||
And I set the field "Select a category" to "Test questions"
|
||||
And I should see "Test questions"
|
||||
And I should see "1" on the usage column
|
||||
When I click "1" on the usage column
|
||||
Then "Test quiz" "table_row" should exist in the "question-usage_table" "region"
|
||||
|
||||
@@ -34,6 +34,16 @@ class helper_test extends \advanced_testcase {
|
||||
*/
|
||||
protected $quiz;
|
||||
|
||||
/**
|
||||
* @var \stdClass $user
|
||||
*/
|
||||
protected $user;
|
||||
|
||||
/**
|
||||
* @var \core_question_generator $questiongenerator
|
||||
*/
|
||||
protected $questiongenerator;
|
||||
|
||||
/**
|
||||
* @var array $questions
|
||||
*/
|
||||
@@ -46,20 +56,15 @@ class helper_test extends \advanced_testcase {
|
||||
$this->resetAfterTest();
|
||||
$layout = '1,2,0';
|
||||
// Make a user to do the quiz.
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$this->user = $this->getDataGenerator()->create_user();
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
// Make a quiz.
|
||||
$quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz');
|
||||
$this->quiz = $quizgenerator->create_instance(['course' => $course->id,
|
||||
'grade' => 100.0, 'sumgrades' => 2, 'layout' => $layout]);
|
||||
'grade' => 100.0, 'sumgrades' => 2, 'layout' => $layout]);
|
||||
|
||||
$quizobj = \mod_quiz\quiz_settings::create($this->quiz->id, $user->id);
|
||||
|
||||
$quba = \question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj->get_context());
|
||||
$quba->set_preferred_behaviour($quizobj->get_quiz()->preferredbehaviour);
|
||||
|
||||
$questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
|
||||
$cat = $questiongenerator->create_question_category();
|
||||
$this->questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
|
||||
$cat = $this->questiongenerator->create_question_category();
|
||||
|
||||
$page = 1;
|
||||
foreach (explode(',', $layout) as $slot) {
|
||||
@@ -68,13 +73,24 @@ class helper_test extends \advanced_testcase {
|
||||
continue;
|
||||
}
|
||||
|
||||
$question = $questiongenerator->create_question('shortanswer', null, ['category' => $cat->id]);
|
||||
$question = $this->questiongenerator->create_question('shortanswer', null, ['category' => $cat->id]);
|
||||
quiz_add_quiz_question($question->id, $this->quiz, $page);
|
||||
$this->questions [] = $question;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Record a quiz attempt.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function attempt_quiz(): void {
|
||||
$quizobj = \mod_quiz\quiz_settings::create($this->quiz->id, $this->user->id);
|
||||
|
||||
$quba = \question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj->get_context());
|
||||
$quba->set_preferred_behaviour($quizobj->get_quiz()->preferredbehaviour);
|
||||
$timenow = time();
|
||||
$attempt = quiz_create_attempt($quizobj, 1, false, $timenow, false, $user->id);
|
||||
$attempt = quiz_create_attempt($quizobj, 1, false, $timenow, false, $this->user->id);
|
||||
quiz_start_new_attempt($quizobj, $quba, $attempt, 1, $timenow);
|
||||
quiz_attempt_save_started($quizobj, $quba, $attempt);
|
||||
quiz_attempt::create($attempt->id);
|
||||
@@ -86,6 +102,7 @@ class helper_test extends \advanced_testcase {
|
||||
* @covers ::get_question_attempts_count_in_quiz
|
||||
*/
|
||||
public function test_get_question_attempts_count_in_quiz() {
|
||||
$this->attempt_quiz();
|
||||
foreach ($this->questions as $question) {
|
||||
$questionattemptcount = helper::get_question_attempts_count_in_quiz($question->id, $this->quiz->id);
|
||||
// Test the attempt count matches the usage count, each question should have one count.
|
||||
@@ -105,4 +122,132 @@ class helper_test extends \advanced_testcase {
|
||||
$this->assertEquals(1, $count);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If a question has been included via a random question attempt, this should be counted as a usage.
|
||||
*
|
||||
* @covers ::get_question_entry_usage_count
|
||||
* @return void
|
||||
*/
|
||||
public function test_get_random_question_attempts_usage_count(): void {
|
||||
$this->setAdminUser();
|
||||
$cat = $this->questiongenerator->create_question_category();
|
||||
$question = $this->questiongenerator->create_question('shortanswer', null, ['category' => $cat->id]);
|
||||
quiz_add_random_questions($this->quiz, 1, $cat->id, 1, false);
|
||||
|
||||
$qdef = \question_bank::load_question($question->id);
|
||||
$count = helper::get_question_entry_usage_count($qdef);
|
||||
$this->assertEquals(0, $count);
|
||||
|
||||
$this->attempt_quiz();
|
||||
|
||||
$count = helper::get_question_entry_usage_count($qdef);
|
||||
$this->assertEquals(1, $count);
|
||||
}
|
||||
|
||||
/**
|
||||
* When a question referenced directly is edited, the usage count of all versions remains the same.
|
||||
*
|
||||
* When checking usage of separate versions, the new version should show usages but the original version should not.
|
||||
*
|
||||
* @covers ::get_question_entry_usage_count
|
||||
* @return void
|
||||
*/
|
||||
public function test_edited_question_usage_counts(): void {
|
||||
foreach ($this->questions as $question) {
|
||||
$qdef = \question_bank::load_question($question->id);
|
||||
$count1 = helper::get_question_entry_usage_count($qdef);
|
||||
// Each question should have 1 usage.
|
||||
$this->assertEquals(1, $count1);
|
||||
|
||||
$newversion = $this->questiongenerator->update_question($question);
|
||||
$newqdef = \question_bank::load_question($newversion->id);
|
||||
|
||||
// Either version should return the same count if not checking a specific version.
|
||||
$count2 = helper::get_question_entry_usage_count($qdef);
|
||||
$this->assertEquals(1, $count2);
|
||||
$count3 = helper::get_question_entry_usage_count($newqdef);
|
||||
$this->assertEquals(1, $count3);
|
||||
// Checking the specific version count should return the counts for each version.
|
||||
// The original version is no longer included in the quiz, so has 0 usages.
|
||||
$count4 = helper::get_question_entry_usage_count($qdef, true);
|
||||
$this->assertEquals(0, $count4);
|
||||
// The new version is now included in the quiz, so has 1 usage.
|
||||
$count5 = helper::get_question_entry_usage_count($newqdef, true);
|
||||
$this->assertEquals(1, $count5);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When a question referenced directly with attempts is edited, the usage count of all versions remains the same.
|
||||
*
|
||||
* When checking usage of separate versions, both versions should show usage.
|
||||
*
|
||||
* @covers ::get_question_entry_usage_count
|
||||
* @return void
|
||||
*/
|
||||
public function test_edited_attempted_question_usage_counts(): void {
|
||||
$this->attempt_quiz();
|
||||
|
||||
foreach ($this->questions as $question) {
|
||||
$qdef = \question_bank::load_question($question->id);
|
||||
$count1 = helper::get_question_entry_usage_count($qdef);
|
||||
// Each question should have 1 usage.
|
||||
$this->assertEquals(1, $count1);
|
||||
|
||||
$newversion = $this->questiongenerator->update_question($question);
|
||||
$newqdef = \question_bank::load_question($newversion->id);
|
||||
|
||||
// Either version should return the same count if not checking a specific version.
|
||||
$count2 = helper::get_question_entry_usage_count($qdef);
|
||||
$this->assertEquals(1, $count2);
|
||||
$count3 = helper::get_question_entry_usage_count($newqdef);
|
||||
$this->assertEquals(1, $count3);
|
||||
// Checking the specific version count should return the counts for each version.
|
||||
// The original version is no longer included in the quiz. However, the is still an attempt using this question version,
|
||||
// so it has 1 usage.
|
||||
$count4 = helper::get_question_entry_usage_count($qdef, true);
|
||||
$this->assertEquals(1, $count4);
|
||||
// The new version is now included in the quiz, so has 1 usage.
|
||||
$count5 = helper::get_question_entry_usage_count($newqdef, true);
|
||||
$this->assertEquals(1, $count5);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When a random question with attempts is edited, it should still have the same usage count.
|
||||
*
|
||||
* When checking usage of separate versions, the original version should still show usage but the new version should not.
|
||||
*
|
||||
* @covers ::get_question_entry_usage_count
|
||||
* @return void
|
||||
*/
|
||||
public function test_edited_attempted_random_question_usage_count(): void {
|
||||
$this->setAdminUser();
|
||||
$cat = $this->questiongenerator->create_question_category();
|
||||
$question = $this->questiongenerator->create_question('shortanswer', null, ['category' => $cat->id]);
|
||||
quiz_add_random_questions($this->quiz, 1, $cat->id, 1, false);
|
||||
|
||||
$this->attempt_quiz();
|
||||
|
||||
$qdef = \question_bank::load_question($question->id);
|
||||
$count1 = helper::get_question_entry_usage_count($qdef);
|
||||
$this->assertEquals(1, $count1);
|
||||
|
||||
$newversion = $this->questiongenerator->update_question($question);
|
||||
$newqdef = \question_bank::load_question($newversion->id);
|
||||
|
||||
// Either version should return the same count if not checking a specific version.
|
||||
$count2 = helper::get_question_entry_usage_count($qdef);
|
||||
$this->assertEquals(1, $count2);
|
||||
$count3 = helper::get_question_entry_usage_count($newqdef);
|
||||
$this->assertEquals(1, $count3);
|
||||
// Checking the specific version count should return the counts for each version.
|
||||
// There is still an attempt of the original version has part of the random question attempt, so it has 1 usage.
|
||||
$count4 = helper::get_question_entry_usage_count($qdef, true);
|
||||
$this->assertEquals(1, $count4);
|
||||
// There is no attempt of the new version, so it has 0 usages.
|
||||
$count5 = helper::get_question_entry_usage_count($newqdef, true);
|
||||
$this->assertEquals(0, $count5);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user