Merge branch 'MDL-21904' of git://github.com/jmvedrine/moodle

This commit is contained in:
Adrian Greeve 2019-03-20 14:12:48 +08:00
commit d08e28a3bb
6 changed files with 239 additions and 4 deletions

View File

@ -937,11 +937,15 @@ class qformat_default {
}
}
// export the question displaying message
$count++;
// Add the question to result.
if (!$checkcapabilities || question_has_capability_on($question, 'view')) {
$expout .= $this->writequestion($question, $contextid) . "\n";
$expquestion = $this->writequestion($question, $contextid);
// Don't add anything if witequestion returned nothing.
// This will permit qformat plugins to exclude some questions.
if ($expquestion !== null) {
$expout .= $expquestion . "\n";
$count++;
}
}
}

View File

@ -57,6 +57,10 @@ class qformat_aiken extends qformat_default {
return true;
}
public function provide_export() {
return true;
}
public function readquestions($lines) {
$questions = array();
$question = null;
@ -151,6 +155,49 @@ class qformat_aiken extends qformat_default {
// This is no longer needed but might still be called by default.php.
return;
}
public function exportpreprocess() {
// This format is not able to export categories.
$this->setCattofile(false);
return true;
}
public function writequestion($question) {
$endchar = "\n";
// Only export multichoice questions.
if ($question->qtype != 'multichoice') {
return null;
}
// Do not export multichoice multi questions.
if (!$question->options->single) {
return null;
}
// Aiken format is not able to handle question with more than 26 answers.
if (count($question->options->answers) > 26) {
return null;
}
// Export the question displaying message.
$expout = str_replace("\n", '', question_utils::to_plain_text($question->questiontext,
$question->questiontextformat, array('para' => false, 'newlines' => false))) . $endchar;
$num = 0;
foreach ($question->options->answers as $answer) {
$number = chr(ord('A') + $num);
$expout .= $number . ') ' . str_replace("\n", '', question_utils::to_plain_text($answer->answer,
$answer->answerformat, array('para' => false, 'newlines' => false))) . $endchar;
if ($answer->fraction > .99) {
$correctanswer = $number;
}
$num++;
}
// Add the correct answer.
$expout .= 'ANSWER: ' . $correctanswer;
return $expout;
}
}

View File

@ -0,0 +1,36 @@
@qformat @qformat_aiken
Feature: Test exporting questions using Aiken format.
In order to reuse questions
As an teacher
I need to be able to export them in Aiken format.
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
And the following "question categories" exist:
| contextlevel | reference | name |
| Course | C1 | Test questions |
And the following "questions" exist:
| questioncategory | qtype | name | template |
| Test questions | multichoice | Multi-choice-001 | two_of_four |
| Test questions | multichoice | Multi-choice-002 | one_of_four |
And I log in as "teacher1"
And I am on "Course 1" course homepage
Scenario: Aiken export
When I navigate to "Question bank > Export" in current page administration
And I set the field "id_format_aiken" to "1"
When I press "Export questions to file"
Then following "click here" should download between "68" and "70" bytes
# If the download step is the last in the scenario then we can sometimes run
# into the situation where the download page causes a http redirect but behat
# has already conducted its reset (generating an error). By putting a logout
# step we avoid behat doing the reset until we are off that page.
And I log out

View File

@ -0,0 +1,30 @@
@qformat @qformat_aiken
Feature: Test importing questions from Aiken format.
In order to reuse questions
As an teacher
I need to be able to import them in Aiken format.
Background:
Given the following "courses" exist:
| fullname | shortname | format |
| Course 1 | C1 | topics |
And the following "users" exist:
| username | firstname |
| teacher | Teacher |
And the following "course enrolments" exist:
| user | course | role |
| teacher | C1 | editingteacher |
And I log in as "teacher"
And I am on "Course 1" course homepage
@javascript @_file_upload
Scenario: import some Aiken questions
When I navigate to "Question bank > Import" in current page administration
And I set the field "id_format_aiken" to "1"
And I upload "question/format/aiken/tests/fixtures/questions.aiken.txt" file to "Import" filemanager
And I press "id_submitbutton"
Then I should see "Parsing questions from import file."
And I should see "Importing 2 questions from file"
And I should see "The Moodle project was started by:"
And I press "Continue"
And I should see "The Moodle project was started by:"

View File

@ -0,0 +1,114 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Unit tests for export/import description (info) for question category in the Moodle XML format.
*
* @package qformat_aiken
* @copyright 2018 Jean-Michel Vedrine
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->libdir . '/questionlib.php');
require_once($CFG->dirroot . '/question/format.php');
require_once($CFG->dirroot . '/question/format/aiken/format.php');
require_once($CFG->dirroot . '/question/engine/tests/helpers.php');
require_once($CFG->dirroot . '/question/editlib.php');
/**
* Unit tests for the Aiken question format export.
*
* @copyright 2018 Jean-Michel vedrine)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class qformat_aiken_export_test extends advanced_testcase {
/**
* Assert that 2 strings are the same, ignoring ends of line.
* We need to override this function because we don't want any output
* @param string $expectedtext The expected string.
* @param string $text The actual string.
*/
public function assert_same_aiken($expectedtext, $text) {
$this->assertEquals(str_replace("\r\n", "\n", $expectedtext),
str_replace("\r\n", "\n", $text));
}
public function test_export_questions() {
$this->resetAfterTest();
$this->setAdminUser();
// Create a new course category and and a new course in that.
$category = $this->getDataGenerator()->create_category();
$course = $this->getDataGenerator()->create_course(array('category' => $category->id));
$generator = $this->getDataGenerator()->get_plugin_generator('core_question');
$context = context_coursecat::instance($category->id);
$cat = $generator->create_question_category(array('contextid' => $context->id));
$question1 = $generator->create_question('shortanswer', null,
array('category' => $cat->id));
$question2 = $generator->create_question('essay', null,
array('category' => $cat->id));
$question3 = $generator->create_question('numerical', null,
array('category' => $cat->id));
$question4 = $generator->create_question('multichoice', 'one_of_four',
array('category' => $cat->id));
$question4 = $generator->create_question('multichoice', 'two_of_four',
array('category' => $cat->id));
$exporter = new qformat_aiken();
$exporter->category = $cat;
$exporter->setCourse($course);
$expectedoutput = <<<EOT
Which is the oddest number?
A) One
B) Two
C) Three
D) Four
ANSWER: A
EOT;
$this->assert_same_aiken($expectedoutput, $exporter->exportprocess());
}
public function test_export_multiline_question() {
$this->resetAfterTest();
$this->setAdminUser();
// Create a new course category and and a new course in that.
$category = $this->getDataGenerator()->create_category();
$course = $this->getDataGenerator()->create_course(array('category' => $category->id));
$generator = $this->getDataGenerator()->get_plugin_generator('core_question');
$context = context_coursecat::instance($category->id);
$cat = $generator->create_question_category(array('contextid' => $context->id));
$question = $generator->create_question('multichoice', 'one_of_four',
array('category' => $cat->id));
$question->questiontext = <<<EOT
<p>Which is the</p>
<p>oddest number?</p>
EOT;
$exporter = new qformat_aiken();
$exporter->category = $cat;
$exporter->setCourse($course);
$expectedoutput = <<<EOT
Which is the oddest number?
A) One
B) Two
C) Three
D) Four
ANSWER: A
EOT;
$this->assert_same_aiken($expectedoutput, $exporter->exportprocess());
}
}

View File

@ -7,6 +7,10 @@ qtype_numerical and qtype_multianswer plugins in the qtype_numerical_edit_form
and qtype_multianswer_edit_form classes has been moved to a static function
in the qtype_numerical class of the qtype_numerical plugin.
The exportprocess function of the qformat_default class doesn't output a blank line
if the result of the writequestion function is null. This permit to qformat plugins
to ignore some questions without the need to overwrite this function.
=== 3.5 ===
1) The question format exportprocess function now adds a