From e8957d09d5d3c67a0daa09263b45ad0da5ea8731 Mon Sep 17 00:00:00 2001 From: Philipp Memmel Date: Tue, 7 Jan 2025 12:32:27 +0100 Subject: [PATCH] MBS-9809: Provide component and context id --- amd/build/editformhelper.min.js | 2 +- amd/build/editformhelper.min.js.map | 2 +- amd/src/editformhelper.js | 5 +++-- classes/external.php | 29 ++++++++++++++++++++++++++--- edit_aitext_form.php | 2 +- question.php | 3 +-- renderer.php | 2 -- 7 files changed, 33 insertions(+), 12 deletions(-) diff --git a/amd/build/editformhelper.min.js b/amd/build/editformhelper.min.js index b50e111..d6dc172 100644 --- a/amd/build/editformhelper.min.js +++ b/amd/build/editformhelper.min.js @@ -5,6 +5,6 @@ define("qtype_aitext/editformhelper",["exports","core/str","core/ajax","core/log * @module qtype_aitext/editformhelper * @copyright 2024 Justin Hunt * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_ajax=_interopRequireDefault(_ajax),_log=_interopRequireDefault(_log),_notification=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}(_notification);const Selectors_fields={sampleanswer:"#id_sampleanswer",sampleanswerbtn:"#id_sampleanswerbtn",sampleanswereval:"#id_sampleanswereval",aiprompt:"#id_aiprompt",markscheme:"#id_markscheme",defaultmark:"#id_defaultmark"};_exports.init=()=>{var strings={};(0,_str.get_strings)([{key:"prompttester",component:"qtype_aitext"},{key:"sampleanswerempty",component:"qtype_aitext"}]).done((function(s){var i=0;strings.prompttester=s[i++],strings.sampleanswerempty=s[i++]})),document.querySelector(Selectors_fields.sampleanswerbtn).addEventListener("click",(e=>{const form=e.target.closest("form"),sampleanswer=form.querySelector(Selectors_fields.sampleanswer),sampleanswereval=form.querySelector(Selectors_fields.sampleanswereval),aiprompt=form.querySelector(Selectors_fields.aiprompt),marksscheme=form.querySelector(Selectors_fields.markscheme),defaultmark=form.querySelector(Selectors_fields.defaultmark);""!==sampleanswer.value&&""!==aiprompt.value?(sampleanswereval.innerHTML='',_ajax.default.call([{methodname:"qtype_aitext_fetch_ai_grade",args:{response:sampleanswer.value,defaultmark:defaultmark.value,prompt:aiprompt.value,marksscheme:marksscheme.value},async:!1}])[0].then((function(airesponse){_log.default.debug(airesponse),airesponse.feedback&&(sampleanswereval.textContent=airesponse.feedback+" (GRADE: "+airesponse.marks+"/"+defaultmark.value+")")})).fail((error=>{(0,_notification.exception)(error),sampleanswereval.innerHTML=""}))):_notification.default.alert(strings.prompttester,strings.sampleanswerempty)}))}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_ajax=_interopRequireDefault(_ajax),_log=_interopRequireDefault(_log),_notification=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}(_notification);const Selectors_fields={sampleanswer:"#id_sampleanswer",sampleanswerbtn:"#id_sampleanswerbtn",sampleanswereval:"#id_sampleanswereval",aiprompt:"#id_aiprompt",markscheme:"#id_markscheme",defaultmark:"#id_defaultmark"};_exports.init=contextid=>{var strings={};(0,_str.get_strings)([{key:"prompttester",component:"qtype_aitext"},{key:"sampleanswerempty",component:"qtype_aitext"}]).done((function(s){var i=0;strings.prompttester=s[i++],strings.sampleanswerempty=s[i++]})),document.querySelector(Selectors_fields.sampleanswerbtn).addEventListener("click",(e=>{const form=e.target.closest("form"),sampleanswer=form.querySelector(Selectors_fields.sampleanswer),sampleanswereval=form.querySelector(Selectors_fields.sampleanswereval),aiprompt=form.querySelector(Selectors_fields.aiprompt),marksscheme=form.querySelector(Selectors_fields.markscheme),defaultmark=form.querySelector(Selectors_fields.defaultmark);""!==sampleanswer.value&&""!==aiprompt.value?(sampleanswereval.innerHTML='',_ajax.default.call([{methodname:"qtype_aitext_fetch_ai_grade",args:{response:sampleanswer.value,defaultmark:defaultmark.value,prompt:aiprompt.value,marksscheme:marksscheme.value,contextid:contextid},async:!1}])[0].then((function(airesponse){_log.default.debug(airesponse),airesponse.feedback&&(sampleanswereval.textContent=airesponse.feedback+" (GRADE: "+airesponse.marks+"/"+defaultmark.value+")")})).fail((error=>{(0,_notification.exception)(error),sampleanswereval.innerHTML=""}))):_notification.default.alert(strings.prompttester,strings.sampleanswerempty)}))}})); //# sourceMappingURL=editformhelper.min.js.map \ No newline at end of file diff --git a/amd/build/editformhelper.min.js.map b/amd/build/editformhelper.min.js.map index 8397b91..b36c471 100644 --- a/amd/build/editformhelper.min.js.map +++ b/amd/build/editformhelper.min.js.map @@ -1 +1 @@ -{"version":3,"file":"editformhelper.min.js","sources":["../src/editformhelper.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/ //\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 .\n\nimport {get_strings} from 'core/str';\nimport Ajax from 'core/ajax';\nimport Log from 'core/log';\nimport Notify from 'core/notification';\nimport {exception as displayException} from 'core/notification';\n\n/**\n * Question AI Text Edit Form Helper\n *\n * @module qtype_aitext/editformhelper\n * @copyright 2024 Justin Hunt \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nconst Selectors = {\n fields: {\n sampleanswer: '#id_sampleanswer',\n sampleanswerbtn: '#id_sampleanswerbtn',\n sampleanswereval: '#id_sampleanswereval',\n aiprompt: '#id_aiprompt',\n markscheme: '#id_markscheme',\n defaultmark: '#id_defaultmark',\n },\n};\n\n/**\n * Initialise the format chooser.\n */\nexport const init = () => {\n\n // Set up strings\n var strings={};\n get_strings([\n { \"key\": \"prompttester\", \"component\": 'qtype_aitext'},\n { \"key\": \"sampleanswerempty\", \"component\": 'qtype_aitext'},\n\n ]).done(function (s) {\n var i = 0;\n strings.prompttester = s[i++];\n strings.sampleanswerempty = s[i++];\n });\n\n document.querySelector(Selectors.fields.sampleanswerbtn).addEventListener('click', e => {\n\n const form = e.target.closest('form');\n const sampleanswer = form.querySelector(Selectors.fields.sampleanswer);\n const sampleanswereval = form.querySelector(Selectors.fields.sampleanswereval);\n const aiprompt = form.querySelector(Selectors.fields.aiprompt);\n const marksscheme = form.querySelector(Selectors.fields.markscheme);\n const defaultmark = form.querySelector(Selectors.fields.defaultmark);\n\n if(sampleanswer.value===\"\" || aiprompt.value===\"\"){\n Notify.alert(strings.prompttester, strings.sampleanswerempty);\n return;\n }\n\n //put spinner in place\n sampleanswereval.innerHTML='';\n\n Ajax.call([{\n methodname: 'qtype_aitext_fetch_ai_grade',\n args: {\n response: sampleanswer.value,\n defaultmark: defaultmark.value,\n prompt: aiprompt.value,\n marksscheme: marksscheme.value\n },\n async: false\n }])[0].then(function(airesponse) {\n Log.debug(airesponse);\n if (airesponse.feedback) {\n sampleanswereval.textContent = airesponse.feedback + ' (GRADE: ' + airesponse.marks + '/' + defaultmark.value + ')';\n }\n }).fail(error => {\n displayException(error);\n sampleanswereval.innerHTML = '';\n });\n });//end of click\n};\n"],"names":["Selectors","sampleanswer","sampleanswerbtn","sampleanswereval","aiprompt","markscheme","defaultmark","strings","done","s","i","prompttester","sampleanswerempty","document","querySelector","addEventListener","e","form","target","closest","marksscheme","value","innerHTML","call","methodname","args","response","prompt","async","then","airesponse","debug","feedback","textContent","marks","fail","error","alert"],"mappings":";;;;;;;w0BA4BMA,iBACM,CACJC,aAAc,mBACdC,gBAAiB,sBACjBC,iBAAkB,uBAClBC,SAAU,eACVC,WAAY,iBACZC,YAAa,iCAOD,SAGZC,QAAQ,wBACA,CACR,KAAS,yBAA6B,gBACtC,KAAS,8BAAkC,kBAE5CC,MAAK,SAAUC,OACVC,EAAI,EACRH,QAAQI,aAAeF,EAAEC,KACzBH,QAAQK,kBAAoBH,EAAEC,QAGlCG,SAASC,cAAcd,iBAAiBE,iBAAiBa,iBAAiB,SAASC,UAEzEC,KAAOD,EAAEE,OAAOC,QAAQ,QACxBlB,aAAegB,KAAKH,cAAcd,iBAAiBC,cACnDE,iBAAmBc,KAAKH,cAAcd,iBAAiBG,kBACvDC,SAAWa,KAAKH,cAAcd,iBAAiBI,UAC/CgB,YAAcH,KAAKH,cAAcd,iBAAiBK,YAClDC,YAAcW,KAAKH,cAAcd,iBAAiBM,aAEhC,KAArBL,aAAaoB,OAA+B,KAAjBjB,SAASiB,OAMvClB,iBAAiBmB,UAAU,iEAEtBC,KAAK,CAAC,CACPC,WAAY,8BACZC,KAAM,CACFC,SAAUzB,aAAaoB,MACvBf,YAAaA,YAAYe,MACzBM,OAAQvB,SAASiB,MACjBD,YAAaA,YAAYC,OAE7BO,OAAO,KACP,GAAGC,MAAK,SAASC,yBACbC,MAAMD,YACNA,WAAWE,WACX7B,iBAAiB8B,YAAcH,WAAWE,SAAW,YAAcF,WAAWI,MAAQ,IAAM5B,YAAYe,MAAQ,QAErHc,MAAKC,oCACaA,OACjBjC,iBAAiBmB,UAAY,6BAvBtBe,MAAM9B,QAAQI,aAAcJ,QAAQK"} \ No newline at end of file +{"version":3,"file":"editformhelper.min.js","sources":["../src/editformhelper.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/ //\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 .\n\nimport {get_strings} from 'core/str';\nimport Ajax from 'core/ajax';\nimport Log from 'core/log';\nimport Notify from 'core/notification';\nimport {exception as displayException} from 'core/notification';\n\n/**\n * Question AI Text Edit Form Helper\n *\n * @module qtype_aitext/editformhelper\n * @copyright 2024 Justin Hunt \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nconst Selectors = {\n fields: {\n sampleanswer: '#id_sampleanswer',\n sampleanswerbtn: '#id_sampleanswerbtn',\n sampleanswereval: '#id_sampleanswereval',\n aiprompt: '#id_aiprompt',\n markscheme: '#id_markscheme',\n defaultmark: '#id_defaultmark',\n },\n};\n\n/**\n * Initialise the format chooser.\n */\nexport const init = (contextid) => {\n\n // Set up strings\n var strings={};\n get_strings([\n { \"key\": \"prompttester\", \"component\": 'qtype_aitext'},\n { \"key\": \"sampleanswerempty\", \"component\": 'qtype_aitext'},\n\n ]).done(function (s) {\n var i = 0;\n strings.prompttester = s[i++];\n strings.sampleanswerempty = s[i++];\n });\n\n document.querySelector(Selectors.fields.sampleanswerbtn).addEventListener('click', e => {\n\n const form = e.target.closest('form');\n const sampleanswer = form.querySelector(Selectors.fields.sampleanswer);\n const sampleanswereval = form.querySelector(Selectors.fields.sampleanswereval);\n const aiprompt = form.querySelector(Selectors.fields.aiprompt);\n const marksscheme = form.querySelector(Selectors.fields.markscheme);\n const defaultmark = form.querySelector(Selectors.fields.defaultmark);\n\n if(sampleanswer.value===\"\" || aiprompt.value===\"\"){\n Notify.alert(strings.prompttester, strings.sampleanswerempty);\n return;\n }\n\n //put spinner in place\n sampleanswereval.innerHTML='';\n\n Ajax.call([{\n methodname: 'qtype_aitext_fetch_ai_grade',\n args: {\n response: sampleanswer.value,\n defaultmark: defaultmark.value,\n prompt: aiprompt.value,\n marksscheme: marksscheme.value,\n contextid: contextid\n },\n async: false\n }])[0].then(function(airesponse) {\n Log.debug(airesponse);\n if (airesponse.feedback) {\n sampleanswereval.textContent = airesponse.feedback + ' (GRADE: ' + airesponse.marks + '/' + defaultmark.value + ')';\n }\n }).fail(error => {\n displayException(error);\n sampleanswereval.innerHTML = '';\n });\n });//end of click\n};\n"],"names":["Selectors","sampleanswer","sampleanswerbtn","sampleanswereval","aiprompt","markscheme","defaultmark","contextid","strings","done","s","i","prompttester","sampleanswerempty","document","querySelector","addEventListener","e","form","target","closest","marksscheme","value","innerHTML","call","methodname","args","response","prompt","async","then","airesponse","debug","feedback","textContent","marks","fail","error","alert"],"mappings":";;;;;;;w0BA4BMA,iBACM,CACJC,aAAc,mBACdC,gBAAiB,sBACjBC,iBAAkB,uBAClBC,SAAU,eACVC,WAAY,iBACZC,YAAa,iCAOAC,gBAGbC,QAAQ,wBACA,CACR,KAAS,yBAA6B,gBACtC,KAAS,8BAAkC,kBAE5CC,MAAK,SAAUC,OACVC,EAAI,EACRH,QAAQI,aAAeF,EAAEC,KACzBH,QAAQK,kBAAoBH,EAAEC,QAGlCG,SAASC,cAAcf,iBAAiBE,iBAAiBc,iBAAiB,SAASC,UAEzEC,KAAOD,EAAEE,OAAOC,QAAQ,QACxBnB,aAAeiB,KAAKH,cAAcf,iBAAiBC,cACnDE,iBAAmBe,KAAKH,cAAcf,iBAAiBG,kBACvDC,SAAWc,KAAKH,cAAcf,iBAAiBI,UAC/CiB,YAAcH,KAAKH,cAAcf,iBAAiBK,YAClDC,YAAcY,KAAKH,cAAcf,iBAAiBM,aAEhC,KAArBL,aAAaqB,OAA+B,KAAjBlB,SAASkB,OAMvCnB,iBAAiBoB,UAAU,iEAEtBC,KAAK,CAAC,CACPC,WAAY,8BACZC,KAAM,CACFC,SAAU1B,aAAaqB,MACvBhB,YAAaA,YAAYgB,MACzBM,OAAQxB,SAASkB,MACjBD,YAAaA,YAAYC,MACzBf,UAAWA,WAEfsB,OAAO,KACP,GAAGC,MAAK,SAASC,yBACbC,MAAMD,YACNA,WAAWE,WACX9B,iBAAiB+B,YAAcH,WAAWE,SAAW,YAAcF,WAAWI,MAAQ,IAAM7B,YAAYgB,MAAQ,QAErHc,MAAKC,oCACaA,OACjBlC,iBAAiBoB,UAAY,6BAxBtBe,MAAM9B,QAAQI,aAAcJ,QAAQK"} \ No newline at end of file diff --git a/amd/src/editformhelper.js b/amd/src/editformhelper.js index 11d6b33..5c67abd 100644 --- a/amd/src/editformhelper.js +++ b/amd/src/editformhelper.js @@ -40,7 +40,7 @@ const Selectors = { /** * Initialise the format chooser. */ -export const init = () => { +export const init = (contextid) => { // Set up strings var strings={}; @@ -77,7 +77,8 @@ export const init = () => { response: sampleanswer.value, defaultmark: defaultmark.value, prompt: aiprompt.value, - marksscheme: marksscheme.value + marksscheme: marksscheme.value, + contextid: contextid }, async: false }])[0].then(function(airesponse) { diff --git a/classes/external.php b/classes/external.php index a405e14..ad4a7f6 100644 --- a/classes/external.php +++ b/classes/external.php @@ -47,6 +47,7 @@ public static function fetch_ai_grade_parameters(): external_function_parameters 'defaultmark' => new external_value(PARAM_INT, 'The total possible score'), 'prompt' => new external_value(PARAM_TEXT, 'The AI Prompt'), 'marksscheme' => new external_value(PARAM_TEXT, 'The marks scheme'), + 'contextid' => new external_value(PARAM_INT, 'The context id'), ] ); @@ -55,18 +56,40 @@ public static function fetch_ai_grade_parameters(): external_function_parameters /** * Similar to clicking the submit button. * - * @param array $response + * @param string $response * @param integer $defaultmark * @param string $prompt * @param string $marksscheme + * @param int $contextid the context id * @return array the response array */ - public static function fetch_ai_grade($response, $defaultmark, $prompt, $marksscheme): stdClass { + public static function fetch_ai_grade(string $response, int $defaultmark, string $prompt, string $marksscheme, int $contextid): stdClass { + [ + 'response' => $response, + 'defaultmark' => $defaultmark, + 'prompt' => $prompt, + 'marksscheme' => $marksscheme, + 'contextid' => $contextid, + ] = self::validate_parameters(self::fetch_ai_grade_parameters(), + [ + 'response' => $response, + 'defaultmark' => $defaultmark, + 'prompt' => $prompt, + 'marksscheme' => $marksscheme, + 'contextid' => $contextid, + ] + ); + $context = $contextid === 0 ? context_system::instance() : context::instance_by_id($contextid); + self::validate_context($context); + + // TODO Eventually move this to a own capability which by default is assigned to a teacher in a course. + require_capability('mod/quiz:grade', $context); + // Build an aitext question instance so we can call the same code that the question type uses when it grades. $type = 'aitext'; \question_bank::load_question_definition_classes($type); $aiquestion = new qtype_aitext_question(); - $aiquestion->contextid = 0; + $aiquestion->contextid = $contextid; $aiquestion->qtype = \question_bank::get_qtype('aitext'); // Make sure we have the right data for AI to work with. if (!empty($response) && !empty($prompt) && $defaultmark > 0) { diff --git a/edit_aitext_form.php b/edit_aitext_form.php index 470305f..1767306 100755 --- a/edit_aitext_form.php +++ b/edit_aitext_form.php @@ -135,7 +135,7 @@ protected function definition_inner($mform) { ['rows' => 10], $this->editoroptions); // Load any JS that we need to make things happen, specifically the prompt tester. - $PAGE->requires->js_call_amd('qtype_aitext/editformhelper', 'init', []); + $PAGE->requires->js_call_amd('qtype_aitext/editformhelper', 'init', [$this->context->id]); } /** diff --git a/question.php b/question.php index 01cc092..e3708e8 100755 --- a/question.php +++ b/question.php @@ -154,8 +154,7 @@ public function perform_request(string $prompt, string $purpose = 'feedback'): s $backend = get_config('qtype_aitext', 'backend'); if ($backend == 'local_ai_manager') { $manager = new local_ai_manager\manager($purpose); - $llmresponse = (object) $manager->perform_request($prompt, ['component' => 'qtype_aitext', - 'contextid' => $this->contextid]); + $llmresponse = (object) $manager->perform_request($prompt, 'qtype_aitext', $this->contextid); if ($llmresponse->get_code() !== 200) { throw new moodle_exception( 'err_retrievingfeedback', diff --git a/renderer.php b/renderer.php index 2902cc6..d438ab7 100755 --- a/renderer.php +++ b/renderer.php @@ -23,8 +23,6 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -use qtype_aitext\form\edit_spellcheck; - defined('MOODLE_INTERNAL') || die(); /** * Generates the output for aitext questions.