diff --git a/classes/local/api/package_api.php b/classes/local/api/package_api.php index ee37dbff..107a584e 100644 --- a/classes/local/api/package_api.php +++ b/classes/local/api/package_api.php @@ -106,17 +106,19 @@ public function create_question(?string $currentstate, object $formdata): questi * * @param string $questionstate * @param int $variant variant which should be started (`1` for questions with only one variant) + * @param array|null $attributes * @return attempt_started the attempt's state and metadata. Note that the attempt state never changes after the * attempt has been started. * @throws GuzzleException * @throws request_error * @throws moodle_exception */ - public function start_attempt(string $questionstate, int $variant): attempt_started { + public function start_attempt(string $questionstate, int $variant, ?array $attributes): attempt_started { $options['multipart'] = $this->transform_to_multipart( [ 'variant' => $variant, 'context' => $this->get_context_id(), + 'lms_provided_attributes' => $attributes, ], $questionstate, ); @@ -128,6 +130,7 @@ public function start_attempt(string $questionstate, int $variant): attempt_star * View a previously created attempt. * * @param string $questionstate + * @param array|null $attributes * @param string $attemptstate the attempt state previously returned from {@see start_attempt()} * @param string|null $scoringstate the last scoring state if this attempt has already been scored * @param object|null $response data currently entered by the student @@ -136,7 +139,7 @@ public function start_attempt(string $questionstate, int $variant): attempt_star * @throws request_error * @throws moodle_exception */ - public function view_attempt(string $questionstate, string $attemptstate, ?string $scoringstate = null, + public function view_attempt(string $questionstate, ?array $attributes, string $attemptstate, ?string $scoringstate = null, ?object $response = null): attempt { $options['multipart'] = $this->transform_to_multipart( [ @@ -144,6 +147,7 @@ public function view_attempt(string $questionstate, string $attemptstate, ?strin 'scoring_state' => $scoringstate, 'response' => $response, 'context' => $this->get_context_id(), + 'lms_provided_attributes' => $attributes, ], $questionstate, ); diff --git a/classes/local/api/question_data.php b/classes/local/api/question_data.php index 8a62d5ac..824a0817 100644 --- a/classes/local/api/question_data.php +++ b/classes/local/api/question_data.php @@ -98,6 +98,24 @@ public static function from_question_response(question_response $response): self ); } + /** + * Creates {@see question_data} from the question id. + * + * @param int $id + * @return ?self + * @throws moodle_exception + */ + public static function from_question_id(int $id): ?self { + global $DB; + + $field = $DB->get_field('qtype_questionpy', 'questiondata', ['questionid' => $id]); + if ($field === false) { + return null; + } + + return self::from_json($field); + } + /** * Creates {@see question_data} from JSON. * diff --git a/question.php b/question.php index e27fcf44..afa7f8a6 100644 --- a/question.php +++ b/question.php @@ -27,6 +27,7 @@ use qtype_questionpy\local\api\attempt; use qtype_questionpy\local\api\attempt_ui; use qtype_questionpy\local\api\package_dependency; +use qtype_questionpy\local\api\question_data; use qtype_questionpy\local\api\scoring_code; use qtype_questionpy\local\attempt_ui\question_ui_metadata_extractor; use qtype_questionpy\question_bridge_base; @@ -117,7 +118,10 @@ public function start_attempt(question_attempt_step $step, $variant): void { global $PAGE; try { - $attempt = $this->api->package($this->packagehash, $this->packagefile)->start_attempt($this->questionstate, $variant); + // Unfortunately, we cannot access attempt data, as the attempt is not stored in the database at this point of time. + $attributes = null; + $attempt = $this->api->package($this->packagehash, $this->packagefile) + ->start_attempt($this->questionstate, $variant, $attributes); $this->attemptstate = $attempt->attemptstate; $step->set_qt_var(constants::QT_VAR_ATTEMPT_STATE, $attempt->attemptstate); @@ -180,9 +184,18 @@ public function apply_attempt_state(question_attempt_step $step) { /* TODO: This method is also called from question_attempt->regrade and question_attempt->start_question_based_on, where we shouldn't need to get the UI. */ try { + if ($this->id !== null) { + $questiondata = question_data::from_question_id($this->id); + $requestedattributes = $questiondata->permissions?->attributes; + if ($requestedattributes) { + $attributes = $this->get_bridge()->get_attributes($requestedattributes); + } + } + $attempt = $this->api->package($this->packagehash, $this->packagefile) ->view_attempt( $this->questionstate, + $attributes ?? null, $this->attemptstate, $this->scoringstate, $lastresponse @@ -419,6 +432,7 @@ public function make_behaviour(question_attempt $qa, $preferredbehaviour): quest /** * Get the QuestionPy bridge used to retrieve additional information about an attempt. * + * @throws moodle_exception * @return question_bridge_base */ public function get_bridge(): question_bridge_base {