diff --git a/api/controller/GradingController.php b/api/controller/GradingController.php index 0088b90529c..70c2fe61f83 100644 --- a/api/controller/GradingController.php +++ b/api/controller/GradingController.php @@ -79,50 +79,51 @@ public function __invoke(Request $request, Response $response, array $args): Res $storeprefix = uniqid(); $gradingresponse = new StackGradingResponse(); $gradingresponse->isgradable = true; - + + if (!$question->is_gradable_response($data['answers'])) { + $gradingresponse->isgradable = false; + $response->getBody()->write(json_encode($gradingresponse)); + return $response->withHeader('Content-Type', 'application/json'); + } + $scores = []; foreach ($question->prts as $index => $prt) { $result = $question->get_prt_result($index, $data['answers'], true); + $scores[$index] = $result->get_score(); - // If not all inputs required for the prt have been filled out, - // or the prt evaluation caused an error, we abort the grading, - // and indicate that this input state is not gradable. - if ($result->get_errors() || !$question->has_necessary_prt_inputs($prt, $data['answers'], true)) { - $gradingresponse = new StackGradingResponse(); - $gradingresponse->isgradable = false; - - $response->getBody()->write(json_encode($gradingresponse)); - return $response->withHeader('Content-Type', 'application/json'); - } - - $feedbackstyle = $prt->get_feedbackstyle(); - - $feedback = $result->apply_placeholder_holder($result->get_feedback()); - $standardfeedback = $this->standard_prt_feedback($question, $result, $feedbackstyle); - - switch ($feedbackstyle) { - // Formative. - case 0: - $overallfeedback = $feedback; - break; - // Standard. - case 1: - case 2: - $overallfeedback = $standardfeedback . $feedback; - break; - // Compact. - // Symbolic. - case 3: - $overallfeedback = $standardfeedback; - break; - // Invalid. - default: - $overallfeedback = "Invalid Feedback style"; - break; + $errors = $result->get_errors(); + if ($errors) { + $overallfeedback = $errors; + } else if (!$question->has_necessary_prt_inputs($prt, $data['answers'], true)) { + continue; + } else { + $feedbackstyle = $prt->get_feedbackstyle(); + + $feedback = $result->apply_placeholder_holder($result->get_feedback()); + $standardfeedback = $this->standard_prt_feedback($question, $result, $feedbackstyle); + + switch ($feedbackstyle) { + // Formative. + case 0: + $overallfeedback = $feedback; + break; + // Standard. + case 1: + case 2: + $overallfeedback = $standardfeedback . $feedback; + break; + // Compact. + // Symbolic. + case 3: + $overallfeedback = $standardfeedback; + break; + // Invalid. + default: + $overallfeedback = "Invalid Feedback style"; + break; + } } - $scores[$index] = $result->get_score(); - $gradingresponse->prts[$index] = $translate->filter( \stack_maths::process_display_castext($overallfeedback), $language diff --git a/api/public/stackshared.js b/api/public/stackshared.js index c2035801fc3..c8ada40c6b2 100644 --- a/api/public/stackshared.js +++ b/api/public/stackshared.js @@ -269,8 +269,9 @@ function answer() { document.getElementById('errors').innerText = ''; } if (!json.isgradable) { + // Should we display this or nothing (like Moodle)? document.getElementById('stackapi_validity').innerText - = ' Please enter valid answers for all parts of the question.'; + = ' Please supply additional valid answers.'; return; } renameIframeHolders(); @@ -307,7 +308,7 @@ function answer() { const elements = document.getElementsByName(`${feedbackPrefix + name}`); if (elements.length > 0) { const element = elements[0]; - if (json.scores[name] !== undefined) { + if (json.scores[name] !== undefined && json.scoreweights[name]) { fb = fb + `
[[feedback:prt1]]
', $this->output->specificfeedback); $this->assertStringContainsString('correct', $this->output->prts->prt1); $this->assertEquals(0, count((array)$this->output->gradingassets)); - $this->assertEquals('Seed: 86; ans1: matrix([35,30],[28,24]) [valid]; prt1: !', $this->output->responsesummary); + $this->assertEquals( + 'Seed: 86; ans1: matrix([35,30],[28,24]) [score]; prt1: # = 1 | | 1-0-T', + $this->output->responsesummary + ); + $this->assertEquals(0, count($this->output->iframes)); + } + + public function test_grade_multipleprts(): void { + $this->requestdata['questionDefinition'] = stack_api_test_data::get_question_string('multipleprts'); + $this->requestdata['answers'] = (array) json_decode(stack_api_test_data::get_answer_string('multipleprts_correct')); + $gc = new GradingController(); + $gc->__invoke($this->request, $this->response, []); + $this->assertEquals(true, $this->output->isgradable); + $this->assertEquals(1, $this->output->score); + $this->assertEquals(1, $this->output->scores->even); + $this->assertEquals(1, $this->output->scores->odd); + $this->assertEquals(1, $this->output->scores->oddeven); + $this->assertEquals(1, $this->output->scores->poly); + $this->assertEquals(1, $this->output->scores->unique); + $this->assertEquals(1, $this->output->scores->total); + $this->assertEqualsWithDelta(0.2, $this->output->scoreweights->even, 0.0001); + $this->assertEqualsWithDelta(0.2, $this->output->scoreweights->odd, 0.0001); + $this->assertEqualsWithDelta(0.4, $this->output->scoreweights->oddeven, 0.0001); + $this->assertEquals(null, $this->output->scoreweights->poly ?? null); + $this->assertEqualsWithDelta(0.2, $this->output->scoreweights->unique, 0.0001); + $this->assertEquals(5, $this->output->scoreweights->total); + $this->assertEquals('', $this->output->specificfeedback); + $this->assertEquals( + 'Perhaps you could think of some non-polynomial examples as well?
', + $this->output->prts->poly + ); + $this->assertEquals(0, count((array)$this->output->gradingassets)); + $this->assertEquals( + 'Seed: -1; ans1: x^3 [score]; ans2: x^2 [score]; ans3: 0 [score]; ans4: true [score]; ' . + 'even: # = 1 | even-0-T; odd: # = 1 | odd-0-T; oddeven: # = 1 | ODD | EVEN; ' . + 'poly: # = 1 [formative] | ATLogic_True. | poly-1-T; unique: # = 1 | ATLogic_True. | unique-0-T', + $this->output->responsesummary + ); + $this->assertEquals(0, count($this->output->iframes)); + } + + public function test_grade_some_answers_multipleprt(): void { + $this->requestdata['questionDefinition'] = stack_api_test_data::get_question_string('multipleprts'); + // Including unsupplied and invalid answers. + $this->requestdata['answers'] = (array) json_decode(stack_api_test_data::get_answer_string('multipleprts_some')); + $gc = new GradingController(); + $gc->__invoke($this->request, $this->response, []); + $this->assertEquals(true, $this->output->isgradable); + $this->assertEqualsWithDelta(0.4, $this->output->score, 0.0001); + $this->assertEquals(null, $this->output->scores->even); + $this->assertEquals(1, $this->output->scores->odd); + $this->assertEquals(null, $this->output->scores->oddeven); + $this->assertEquals(null, $this->output->scores->poly); + $this->assertEquals(1, $this->output->scores->unique); + $this->assertEqualsWithDelta(0.4, $this->output->scores->total, 0.0001); + $this->assertEquals('', $this->output->specificfeedback); + $this->assertStringContainsString('Correct answer', $this->output->prts->odd); + $this->assertStringContainsString('Correct answer', $this->output->prts->unique); + $this->assertEquals(null, $this->output->prts->even ?? null); + $this->assertEquals(null, $this->output->prts->poly ?? null); + $this->assertEquals(null, $this->output->prts->oddeven ?? null); + $this->assertEquals(0, count((array)$this->output->gradingassets)); + $this->assertEquals( + 'Seed: -1; ans1: x^3 [score]; ans2: * [invalid]; ans4: true [score]; even: !; ' . + 'odd: # = 1 | odd-0-T; oddeven: !; poly: !; unique: # = 1 | ATLogic_True. | unique-0-T', + $this->output->responsesummary + ); $this->assertEquals(0, count($this->output->iframes)); } @@ -272,6 +338,10 @@ public function test_default(): void { $this->assertEquals(1, $this->output->scoreweights->total); $this->assertEquals('[[feedback:prt1]]
', $this->output->specificfeedback); $this->assertStringContainsString('correct', $this->output->prts->prt1); + $this->assertEquals( + 'Seed: -1; ans1: 1 [score]; prt1: # = 1 | prt1-1-T', + $this->output->responsesummary + ); } public function test_grade_scores(): void { @@ -293,6 +363,11 @@ public function test_grade_scores(): void { $this->assertEqualsWithDelta(0.1, $this->output->scoreweights->prt3, 0.0001); $this->assertEqualsWithDelta(0.1, $this->output->scoreweights->prt4, 0.0001); $this->assertEquals(10, $this->output->scoreweights->total); + $this->assertEquals( + 'Seed: -1; ans1: c [score]; ans2: 1 [score]; ans3: 0 [score]; ans4: 0 [score]; prt1: # = 1 | prt1-1-T; ' . + 'prt2: # = 1 | prt2-1-T; prt3: # = 0 | prt3-1-F; prt4: # = 1 | prt4-1-T', + $this->output->responsesummary + ); } public function test_download(): void { diff --git a/tests/fixtures/apifixtures.class.php b/tests/fixtures/apifixtures.class.php index fdedec264dd..df12c590333 100644 --- a/tests/fixtures/apifixtures.class.php +++ b/tests/fixtures/apifixtures.class.php @@ -31,6 +31,441 @@ class stack_api_test_data {2. Give an example of an even function. \(f_2(x)=\) [[input:ans2]]. [[validation:ans2]] [[feedback:even]]
+3. Give an example of a function which is odd and even. \(f_3(x)=\) [[input:ans3]]. [[validation:ans3]] [[feedback:oddeven]]
+[[feedback:poly]]
+4. Is the answer to 3. unique? [[input:ans4]] (Or are there many different possibilities.) [[validation:ans4]] [[feedback:unique]]
]]>A function \(f\) is even if \[ f(x)=f(-x) \forall x.\] + An example is \(f(x)=5x^4\). Indeed, polynomials with only even powers are fine.
+It is possible to have both \[ f(x)=f(-x)=-f(-x) \] in which case \(f(x)=0\) for all \(x\). This example is unique. +
]]>