Skip to content

Commit 53e547a

Browse files
committed
feat: send requested lms attributes
1 parent e532c42 commit 53e547a

File tree

7 files changed

+64
-15
lines changed

7 files changed

+64
-15
lines changed

classes/local/api/package_api.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,17 +106,19 @@ public function create_question(?string $currentstate, object $formdata): questi
106106
*
107107
* @param string $questionstate
108108
* @param int $variant variant which should be started (`1` for questions with only one variant)
109+
* @param array|null $attributes
109110
* @return attempt_started the attempt's state and metadata. Note that the attempt state never changes after the
110111
* attempt has been started.
111112
* @throws GuzzleException
112113
* @throws request_error
113114
* @throws moodle_exception
114115
*/
115-
public function start_attempt(string $questionstate, int $variant): attempt_started {
116+
public function start_attempt(string $questionstate, int $variant, ?array $attributes): attempt_started {
116117
$options['multipart'] = $this->transform_to_multipart(
117118
[
118119
'variant' => $variant,
119120
'context' => $this->get_context_id(),
121+
'lms_provided_attributes' => $attributes,
120122
],
121123
$questionstate,
122124
);
@@ -128,6 +130,7 @@ public function start_attempt(string $questionstate, int $variant): attempt_star
128130
* View a previously created attempt.
129131
*
130132
* @param string $questionstate
133+
* @param array|null $attributes
131134
* @param string $attemptstate the attempt state previously returned from {@see start_attempt()}
132135
* @param string|null $scoringstate the last scoring state if this attempt has already been scored
133136
* @param object|null $response data currently entered by the student
@@ -136,14 +139,15 @@ public function start_attempt(string $questionstate, int $variant): attempt_star
136139
* @throws request_error
137140
* @throws moodle_exception
138141
*/
139-
public function view_attempt(string $questionstate, string $attemptstate, ?string $scoringstate = null,
142+
public function view_attempt(string $questionstate, ?array $attributes, string $attemptstate, ?string $scoringstate = null,
140143
?object $response = null): attempt {
141144
$options['multipart'] = $this->transform_to_multipart(
142145
[
143146
'attempt_state' => $attemptstate,
144147
'scoring_state' => $scoringstate,
145148
'response' => $response,
146149
'context' => $this->get_context_id(),
150+
'lms_provided_attributes' => $attributes,
147151
],
148152
$questionstate,
149153
);

classes/question_service.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,9 @@ public function get_question(int $questionid): object {
9090
}
9191
$result->qpy_id = $record->id;
9292
$result->qpy_package_hash = $record->pkgversionhash;
93-
$result->qpy_state = $record->state;
9493
$result->qpy_is_local = $record->islocal;
94+
$result->qpy_state = $record->state;
95+
$result->qpy_question_data = $record->questiondata;
9596
return $result;
9697
}
9798

question.php

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
use qtype_questionpy\local\api\attempt;
2828
use qtype_questionpy\local\api\attempt_ui;
2929
use qtype_questionpy\local\api\package_dependency;
30+
use qtype_questionpy\local\api\question_data;
3031
use qtype_questionpy\local\api\scoring_code;
3132
use qtype_questionpy\local\attempt_ui\question_ui_metadata_extractor;
3233
use qtype_questionpy\question_bridge_base;
@@ -46,6 +47,8 @@ class qtype_questionpy_question extends question_graded_automatically_with_count
4647
public string $packagehash;
4748
/** @var string */
4849
public string $questionstate;
50+
/** @var question_data */
51+
public question_data $questiondata;
4952
/** @var stored_file|null */
5053
private ?stored_file $packagefile;
5154

@@ -74,14 +77,18 @@ class qtype_questionpy_question extends question_graded_automatically_with_count
7477
*
7578
* @param string $packagehash
7679
* @param string $questionstate
80+
* @param question_data $questiondata
7781
* @param stored_file|null $packagefile
7882
* @param api $api
7983
*/
80-
public function __construct(string $packagehash, string $questionstate, ?stored_file $packagefile, api $api) {
84+
public function __construct(
85+
string $packagehash, string $questionstate, question_data $questiondata, ?stored_file $packagefile, api $api
86+
) {
8187
parent::__construct();
8288
$this->api = $api;
8389
$this->packagehash = $packagehash;
8490
$this->questionstate = $questionstate;
91+
$this->questiondata = $questiondata;
8592
$this->packagefile = $packagefile;
8693
}
8794

@@ -117,7 +124,9 @@ public function start_attempt(question_attempt_step $step, $variant): void {
117124
global $PAGE;
118125

119126
try {
120-
$attempt = $this->api->package($this->packagehash, $this->packagefile)->start_attempt($this->questionstate, $variant);
127+
$attributes = $this->get_requested_attributes();
128+
$attempt = $this->api->package($this->packagehash, $this->packagefile)
129+
->start_attempt($this->questionstate, $variant, $attributes);
121130

122131
$this->attemptstate = $attempt->attemptstate;
123132
$step->set_qt_var(constants::QT_VAR_ATTEMPT_STATE, $attempt->attemptstate);
@@ -180,9 +189,11 @@ public function apply_attempt_state(question_attempt_step $step) {
180189
/* TODO: This method is also called from question_attempt->regrade and
181190
question_attempt->start_question_based_on, where we shouldn't need to get the UI. */
182191
try {
192+
$attributes = $this->get_requested_attributes();
183193
$attempt = $this->api->package($this->packagehash, $this->packagefile)
184194
->view_attempt(
185195
$this->questionstate,
196+
$attributes,
186197
$this->attemptstate,
187198
$this->scoringstate,
188199
$lastresponse
@@ -419,11 +430,15 @@ public function make_behaviour(question_attempt $qa, $preferredbehaviour): quest
419430
/**
420431
* Get the QuestionPy bridge used to retrieve additional information about an attempt.
421432
*
422-
* @return question_bridge_base
433+
* @throws moodle_exception
434+
* @return question_bridge_base|null
423435
*/
424-
public function get_bridge(): question_bridge_base {
436+
public function get_bridge(): ?question_bridge_base {
425437
if ($this->bridge === null) {
426-
$this->bridge = question_bridge_base::create($this->get_behaviour()->get_qa());
438+
$attempt = $this->get_behaviour()->get_qa();
439+
if ($attempt->get_database_id() !== null && is_numeric($attempt->get_usage_id())) {
440+
$this->bridge = question_bridge_base::create($attempt);
441+
}
427442
}
428443
return $this->bridge;
429444
}
@@ -440,4 +455,15 @@ public function get_bridge(): question_bridge_base {
440455
public function set_bridge(question_bridge_base $bridge): void {
441456
$this->bridge = $bridge;
442457
}
458+
459+
/**
460+
* Retrieves the requested attributes if any.
461+
*
462+
* @return array|null
463+
* @throws moodle_exception
464+
*/
465+
private function get_requested_attributes(): ?array {
466+
$attributes = $this->questiondata->permissions?->attributes;
467+
return $attributes ? $this->get_bridge()?->get_attributes($attributes) : null;
468+
}
443469
}

questiontype.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
use core\di;
2626
use qtype_questionpy\local\api\api;
27+
use qtype_questionpy\local\api\question_data;
2728
use qtype_questionpy\package_file_service;
2829
use qtype_questionpy\question_service;
2930

@@ -186,8 +187,9 @@ protected function make_question_instance($questiondata) {
186187
return new qtype_questionpy_question(
187188
$questiondata->qpy_package_hash,
188189
$questiondata->qpy_state,
190+
question_data::from_json($questiondata->qpy_question_data),
189191
$packagefile,
190-
$this->api
192+
$this->api,
191193
);
192194
}
193195
}

tests/local/attempt_ui/question_ui_renderer_test.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
use PHPUnit\Framework\MockObject\Stub;
2727
use qtype_questionpy\constants;
2828
use qtype_questionpy\local\api\api;
29+
use qtype_questionpy\local\api\question_data;
2930
use qtype_questionpy_question;
3031
use question_attempt;
3132
use question_attempt_step;
@@ -508,7 +509,13 @@ private function create_question_attempt_stub(?string $packagehash = null, ?int
508509
array $lastresponse = []): question_attempt {
509510
$packagehash ??= hash('sha256', random_string(64));
510511
$id ??= mt_rand();
511-
$question = new qtype_questionpy_question($packagehash, '{}', null, $this->createStub(api::class));
512+
$question = new qtype_questionpy_question(
513+
$packagehash,
514+
'{}',
515+
$this->createStub(question_data::class),
516+
null,
517+
$this->createStub(api::class),
518+
);
512519

513520
$step = new question_attempt_step([constants::QT_VAR_RESPONSE => json_encode((object) $lastresponse)]);
514521

tests/question_service_test.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ protected function setUp(): void {
7474
public function test_get_question_should_load_package_and_state(): void {
7575
$pvi = package_versions_info_provider();
7676
$pvi->upsert();
77-
[$statestr, $qpyid] = $this->setup_question($pvi->versions[0]->hash);
77+
[$statestr, $qpyid, $questiondata] = $this->setup_question($pvi->versions[0]->hash);
7878

7979
$result = $this->questionservice->get_question(1);
8080

@@ -84,6 +84,7 @@ public function test_get_question_should_load_package_and_state(): void {
8484
'qpy_package_hash' => $pvi->versions[0]->hash,
8585
'qpy_state' => $statestr,
8686
'qpy_is_local' => '0',
87+
'qpy_question_data' => $questiondata,
8788
],
8889
$result
8990
);
@@ -438,7 +439,7 @@ private function setup_question(string $pkgversionhash): array {
438439
';
439440

440441
$response = new question_response('en', $statestr, scoring_method::automatically_scorable);
441-
$questiondata = question_data::from_question_response($response);
442+
$questiondata = question_data::from_question_response($response)->to_json();
442443

443444
global $DB;
444445
$qpyid = $DB->insert_record('qtype_questionpy', [
@@ -447,10 +448,10 @@ private function setup_question(string $pkgversionhash): array {
447448
'pkgversionhash' => $pkgversionhash,
448449
'islocal' => false,
449450
'state' => $statestr,
450-
'questiondata' => $questiondata->to_json(),
451+
'questiondata' => $questiondata,
451452
]);
452453

453-
return [$statestr, $qpyid];
454+
return [$statestr, $qpyid, $questiondata];
454455
}
455456

456457
/**

tests/question_test.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424
use qtype_questionpy\event\viewing_attempt_failed;
2525
use qtype_questionpy\local\api\api;
2626
use qtype_questionpy\local\api\package_api;
27+
use qtype_questionpy\local\api\question_data;
28+
use qtype_questionpy\local\api\question_response;
29+
use qtype_questionpy\local\api\scoring_method;
2730
use qtype_questionpy\local\attempt_ui\question_ui_metadata_extractor;
2831
use qtype_questionpy_question;
2932
use question_bank;
@@ -76,9 +79,14 @@ protected function setUp(): void {
7679
*/
7780
private function create_question(): qtype_questionpy_question {
7881
question_engine::load_behaviour_class('questionpy');
82+
$state = 'state';
83+
$response = new question_response('en', $state, scoring_method::automatically_scorable);
84+
$questiondata = question_data::from_question_response($response);
85+
7986
$question = new qtype_questionpy_question(
8087
hash('sha256', 'hash'),
81-
'state',
88+
$state,
89+
$questiondata,
8290
packagefile: null,
8391
api: $this->api
8492
);

0 commit comments

Comments
 (0)