MDL-39756 add advanced attachment options to essay question type

Adds a set of options to the essay question type which implement
the following new features:

-Adds an input format which accepts only file uploads, and no
 inline text.
-Adds an option to make the inline text response optional when
 attachments are enabled, so students can choose to upload
 an essay file.
-Adds an option to make attachments required, so essays without
 attachments will be marked incomplete.
This commit is contained in:
Kyle Temkin 2013-06-19 23:31:01 -04:00 committed by Kyle J. Temkin
parent 12efa52762
commit a4f765eb1d
21 changed files with 392 additions and 11 deletions

View File

@ -404,8 +404,12 @@ class test_question_maker {
$essay->qtype = question_bank::get_qtype('essay');
$essay->responseformat = 'editor';
$essay->responserequired = 1;
$essay->responsefieldlines = 15;
$essay->attachments = 0;
$essay->attachmentsrequired = 0;
$essay->responsetemplate = '';
$essay->responsetemplateformat = FORMAT_MOODLE;
$essay->graderinfo = '';
$essay->graderinfoformat = FORMAT_MOODLE;

View File

@ -153,8 +153,10 @@ class qformat_blackboard_six_pool extends qformat_blackboard_six_base {
$question->responsetemplate = $this->text_field('');
$question->feedback = '';
$question->responseformat = 'editor';
$question->responserequired = 1;
$question->responsefieldlines = 15;
$question->attachments = 0;
$question->attachmentsrequired = 0;
$question->fraction = 0;
$questions[] = $question;

View File

@ -778,8 +778,10 @@ class qformat_blackboard_six_qti extends qformat_blackboard_six_base {
$question->fraction[] = 1;
$question->defaultmark = 1;
$question->responseformat = 'editor';
$question->responserequired = 1;
$question->responsefieldlines = 15;
$question->attachments = 0;
$question->attachmentsrequired = 0;
$question->responsetemplate = $this->text_field('');
$questions[]=$question;

View File

@ -293,10 +293,12 @@ class qformat_examview extends qformat_based_on_xml {
$feedback = trim($this->unxmlise($qrec['answer'][0]['#']));
$question->graderinfo = $this->text_field($feedback);
$question->responsetemplate = $this->text_field('');
$question->responserequired = 1;
$question->feedback = $feedback;
$question->responseformat = 'editor';
$question->responsefieldlines = 15;
$question->attachments = 0;
$question->attachmentsrequired = 0;
$question->fraction = 0;
return $question;
}

View File

@ -327,8 +327,10 @@ class qformat_gift extends qformat_default {
case 'essay':
$question->responseformat = 'editor';
$question->responserequired = 1;
$question->responsefieldlines = 15;
$question->attachments = 0;
$question->attachmentsrequired = 0;
$question->graderinfo = array(
'text' => '', 'format' => FORMAT_HTML, 'files' => array());
$question->responsetemplate = array(

View File

@ -634,8 +634,10 @@ class qformat_webct extends qformat_default {
$question = $this->defaultquestion();
$question->qtype = 'essay';
$question->responseformat = 'editor';
$question->responserequired = 1;
$question->responsefieldlines = 15;
$question->attachments = 0;
$question->attachmentsrequired = 0;
$question->graderinfo = array(
'text' => '',
'format' => FORMAT_HTML,

View File

@ -726,8 +726,12 @@ class qformat_xml extends qformat_default {
array('#', 'responseformat', 0, '#'), 'editor');
$qo->responsefieldlines = $this->getpath($question,
array('#', 'responsefieldlines', 0, '#'), 15);
$qo->responserequired = $this->getpath($question,
array('#', 'responserequired', 0, '#'), 1);
$qo->attachments = $this->getpath($question,
array('#', 'attachments', 0, '#'), 0);
$qo->attachmentsrequired = $this->getpath($question,
array('#', 'attachmentsrequired', 0, '#'), 0);
$qo->graderinfo = $this->import_text_with_files($question,
array('#', 'graderinfo', 0), '', $this->get_format($qo->questiontextformat));
$qo->responsetemplate['text'] = $this->getpath($question,
@ -1273,10 +1277,14 @@ class qformat_xml extends qformat_default {
case 'essay':
$expout .= " <responseformat>" . $question->options->responseformat .
"</responseformat>\n";
$expout .= " <responserequired>" . $question->options->responserequired .
"</responserequired>\n";
$expout .= " <responsefieldlines>" . $question->options->responsefieldlines .
"</responsefieldlines>\n";
$expout .= " <attachments>" . $question->options->attachments .
"</attachments>\n";
$expout .= " <attachmentsrequired>" . $question->options->attachmentsrequired .
"</attachmentsrequired>\n";
$expout .= " <graderinfo " .
$this->format($question->options->graderinfoformat) . ">\n";
$expout .= $this->writetext($question->options->graderinfo, 3);

View File

@ -355,8 +355,10 @@ END;
$expectedq->length = 1;
$expectedq->penalty = 0;
$expectedq->responseformat = 'editor';
$expectedq->responserequired = 1;
$expectedq->responsefieldlines = 15;
$expectedq->attachments = 0;
$expectedq->attachmentsrequired = 0;
$expectedq->graderinfo['text'] = '';
$expectedq->graderinfo['format'] = FORMAT_MOODLE;
$expectedq->responsetemplate['text'] = '';
@ -380,8 +382,10 @@ END;
<penalty>0</penalty>
<hidden>0</hidden>
<responseformat>monospaced</responseformat>
<responserequired>0</responserequired>
<responsefieldlines>42</responsefieldlines>
<attachments>-1</attachments>
<attachmentsrequired>1</attachmentsrequired>
<graderinfo format="html">
<text><![CDATA[<p>Grade <b>generously</b>!</p>]]></text>
</graderinfo>
@ -404,8 +408,10 @@ END;
$expectedq->length = 1;
$expectedq->penalty = 0;
$expectedq->responseformat = 'monospaced';
$expectedq->responserequired = 0;
$expectedq->responsefieldlines = 42;
$expectedq->attachments = -1;
$expectedq->attachmentsrequired = 1;
$expectedq->graderinfo['text'] = '<p>Grade <b>generously</b>!</p>';
$expectedq->graderinfo['format'] = FORMAT_HTML;
$expectedq->responsetemplate['text'] = '<p>Here is something <b>really</b> interesting.</p>';
@ -432,8 +438,10 @@ END;
$qdata->options->id = 456;
$qdata->options->questionid = 123;
$qdata->options->responseformat = 'monospaced';
$qdata->options->responserequired = 0;
$qdata->options->responsefieldlines = 42;
$qdata->options->attachments = -1;
$qdata->options->attachmentsrequired = 1;
$qdata->options->graderinfo = '<p>Grade <b>generously</b>!</p>';
$qdata->options->graderinfoformat = FORMAT_HTML;
$qdata->options->responsetemplate = '<p>Here is something <b>really</b> interesting.</p>';
@ -456,8 +464,10 @@ END;
<penalty>0</penalty>
<hidden>0</hidden>
<responseformat>monospaced</responseformat>
<responserequired>0</responserequired>
<responsefieldlines>42</responsefieldlines>
<attachments>-1</attachments>
<attachmentsrequired>1</attachmentsrequired>
<graderinfo format="html">
<text><![CDATA[<p>Grade <b>generously</b>!</p>]]></text>
</graderinfo>

View File

@ -43,8 +43,10 @@ class moodle1_qtype_essay_handler extends moodle1_qtype_handler {
$this->write_xml('essay', array(
'id' => $this->converter->get_nextid(),
'responseformat' => 'editor',
'responserequired' => 1,
'responsefieldlines' => 15,
'attachments' => 0,
'attachmentsrequired' => 0,
'graderinfo' => '',
'graderinfoformat' => FORMAT_HTML,
'responsetemplate' => '',

View File

@ -49,9 +49,9 @@ class backup_qtype_essay_plugin extends backup_qtype_plugin {
// Now create the qtype own structures.
$essay = new backup_nested_element('essay', array('id'), array(
'responseformat', 'responsefieldlines', 'attachments',
'graderinfo', 'graderinfoformat', 'responsetemplate',
'responsetemplateformat'));
'responseformat', 'responserequired', 'responsefieldlines',
'attachments', 'attachmentsrequired', 'graderinfo',
'graderinfoformat', 'responsetemplate', 'responsetemplateformat'));
// Now the own qtype tree.
$pluginwrapper->add_child($essay);

View File

@ -58,6 +58,12 @@ class restore_qtype_essay_plugin extends restore_qtype_plugin {
if (!isset($data->responsetemplateformat)) {
$data->responsetemplateformat = FORMAT_HTML;
}
if (!isset($data->responserequired)) {
$data->responserequired = 1;
}
if (!isset($data->attachmentsrequired)) {
$data->attachmentsrequired = 0;
}
// Detect if the question is created or mapped.
$questioncreated = $this->get_mappingid('question_created',
@ -103,8 +109,10 @@ class restore_qtype_essay_plugin extends restore_qtype_plugin {
$defaultoptions = new stdClass();
$defaultoptions->questionid = $q->id;
$defaultoptions->responseformat = 'editor';
$defaultoptions->responserequired = 1;
$defaultoptions->responsefieldlines = 15;
$defaultoptions->attachments = 0;
$defaultoptions->attachmentsrequired = 0;
$defaultoptions->graderinfo = '';
$defaultoptions->graderinfoformat = FORMAT_HTML;
$defaultoptions->responsetemplate = '';

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<XMLDB PATH="question/type/essay/db" VERSION="20120122" COMMENT="XMLDB file for Moodle question/type/essay"
<XMLDB PATH="question/type/essay/db" VERSION="20140113" COMMENT="XMLDB file for Moodle question/type/essay"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../../lib/xmldb/xmldb.xsd"
>
@ -9,8 +9,10 @@
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="questionid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="Foreign key linking to the question table."/>
<FIELD NAME="responseformat" TYPE="char" LENGTH="16" NOTNULL="true" DEFAULT="editor" SEQUENCE="false" COMMENT="The type of input area students should be given for their response."/>
<FIELD NAME="responserequired" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Nonzero if an inline text response is optional"/>
<FIELD NAME="responsefieldlines" TYPE="int" LENGTH="4" NOTNULL="true" DEFAULT="15" SEQUENCE="false" COMMENT="Approximate height, in lines, of the input box the students should be given for their response."/>
<FIELD NAME="attachments" TYPE="int" LENGTH="4" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Whether, and how many, attachments a student is allowed to include with their response. -1 means unlimited."/>
<FIELD NAME="attachmentsrequired" TYPE="int" LENGTH="4" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="The number of attachments that should be required"/>
<FIELD NAME="graderinfo" TYPE="text" NOTNULL="false" SEQUENCE="false" COMMENT="Information shown to people with permission to manually grade the question, when they are grading."/>
<FIELD NAME="graderinfoformat" TYPE="int" LENGTH="4" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="The text format for graderinfo."/>
<FIELD NAME="responsetemplate" TYPE="text" NOTNULL="false" SEQUENCE="false" COMMENT="The template to pre-populate student's response field during attempt."/>
@ -22,4 +24,4 @@
</KEYS>
</TABLE>
</TABLES>
</XMLDB>
</XMLDB>

View File

@ -140,10 +140,36 @@ function xmldb_qtype_essay_upgrade($oldversion) {
// Moodle v2.5.0 release upgrade line.
// Put any upgrade step following this.
// Moodle v2.6.0 release upgrade line.
// Put any upgrade step following this.
if ($oldversion < 2014011300) {
// Create new field responserequired (indicates whether inline response is required).
$table = new xmldb_table('qtype_essay_options');
$field = new xmldb_field('responserequired', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '1', 'responseformat');
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
// Essay savepoint reached.
upgrade_plugin_savepoint(true, 2014011300, 'qtype', 'essay');
}
if ($oldversion < 2014011301) {
// Create new field attachmentsrequired (indicates whether attachments should be required).
$table = new xmldb_table('qtype_essay_options');
$field = new xmldb_field('attachmentsrequired', XMLDB_TYPE_INTEGER, '4', null, XMLDB_NOTNULL, null, '0', 'attachments');
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
// Essay savepoint reached.
upgrade_plugin_savepoint(true, 2014011301, 'qtype', 'essay');
}
return true;
}

View File

@ -38,22 +38,40 @@ class qtype_essay_edit_form extends question_edit_form {
protected function definition_inner($mform) {
$qtype = question_bank::get_qtype('essay');
$mform->addElement('header', 'responseoptions', get_string('responseoptions', 'qtype_essay'));
$mform->setExpanded('responseoptions');
$mform->addElement('select', 'responseformat',
get_string('responseformat', 'qtype_essay'), $qtype->response_formats());
$mform->setDefault('responseformat', 'editor');
$mform->addElement('select', 'responserequired',
get_string('responserequired', 'qtype_essay'), $qtype->response_required_options());
$mform->setDefault('responserequired', 1);
$mform->disabledIf('responserequired', 'responseformat', 'eq', 'noinline');
$mform->addElement('select', 'responsefieldlines',
get_string('responsefieldlines', 'qtype_essay'), $qtype->response_sizes());
$mform->setDefault('responsefieldlines', 15);
$mform->disabledIf('responsefieldlines', 'responseformat', 'eq', 'noinline');
$mform->addElement('select', 'attachments',
get_string('allowattachments', 'qtype_essay'), $qtype->attachment_options());
$mform->setDefault('attachments', 0);
$mform->addElement('select', 'attachmentsrequired',
get_string('attachmentsrequired', 'qtype_essay'), $qtype->attachments_required_options());
$mform->setDefault('attachmentsrequired', 0);
$mform->addHelpButton('attachmentsrequired', 'attachmentsrequired', 'qtype_essay');
$mform->disabledIf('attachmentsrequired', 'attachments', 'eq', 0);
$mform->addElement('header', 'responsetemplateheader', get_string('responsetemplateheader', 'qtype_essay'));
$mform->addElement('editor', 'responsetemplate', get_string('responsetemplate', 'qtype_essay'),
array('rows' => 10), array_merge($this->editoroptions, array('maxfiles' => 0)));
$mform->addHelpButton('responsetemplate', 'responsetemplate', 'qtype_essay');
$mform->addElement('header', 'graderinfoheader', get_string('graderinfoheader', 'qtype_essay'));
$mform->setExpanded('graderinfoheader');
$mform->addElement('editor', 'graderinfo', get_string('graderinfo', 'qtype_essay'),
array('rows' => 10), $this->editoroptions);
}
@ -66,8 +84,10 @@ class qtype_essay_edit_form extends question_edit_form {
}
$question->responseformat = $question->options->responseformat;
$question->responserequired = $question->options->responserequired;
$question->responsefieldlines = $question->options->responsefieldlines;
$question->attachments = $question->options->attachments;
$question->attachmentsrequired = $question->options->attachmentsrequired;
$draftid = file_get_submitted_draft_itemid('graderinfo');
$question->graderinfo = array();
@ -91,6 +111,30 @@ class qtype_essay_edit_form extends question_edit_form {
return $question;
}
public function validation($fromform, $files) {
$errors = parent::validation($fromform, $files);
// Don't allow both 'no inline response' and 'no attachments' to be selected,
// as these options would result in there being no input requested from the user.
if ($fromform['responseformat'] == 'noinline' && !$fromform['attachments']) {
$errors['attachments'] = get_string('mustattach', 'qtype_essay');
}
// If 'no inline response' is set, force the teacher to require attachments;
// otherwise there will be nothing to grade.
if ($fromform['responseformat'] == 'noinline' && !$fromform['attachmentsrequired']) {
$errors['attachmentsrequired'] = get_string('mustrequire', 'qtype_essay');
}
// Don't allow the teacher to require more attachments than they allow; as this would
// create a condition that it's impossible for the student to meet.
if ($fromform['attachments'] != -1 && $fromform['attachments'] < $fromform['attachmentsrequired'] ) {
$errors['attachmentsrequired'] = get_string('mustrequirefewer', 'qtype_essay');
}
return $errors;
}
public function qtype() {
return 'essay';
}

View File

@ -24,11 +24,19 @@
*/
$string['allowattachments'] = 'Allow attachments';
$string['attachmentsoptional'] = 'Attachments are optional';
$string['attachmentsrequired'] = 'Require attachments';
$string['attachmentsrequired_help'] = 'This option specifies the minimum number of attachments required for a response to be considered gradable.';
$string['formateditor'] = 'HTML editor';
$string['formateditorfilepicker'] = 'HTML editor with file picker';
$string['formatmonospaced'] = 'Plain text, monospaced font';
$string['formatnoinline'] = 'No inline text';
$string['formatplain'] = 'Plain text';
$string['graderinfo'] = 'Information for graders';
$string['graderinfoheader'] = 'Grader Information';
$string['mustattach'] = 'When "no inline text" is selected, or responses are optional, you must allow at least one attachment.';
$string['mustrequire'] = 'When "no inline text" is selected, or responses are optional, you must require at least one attachment.';
$string['mustrequirefewer'] = 'You cannot require more attachments than you allow.';
$string['nlines'] = '{$a} lines';
$string['pluginname'] = 'Essay';
$string['pluginname_help'] = 'In response to a question (that may include an image) the respondent writes an answer of a paragraph or two. The essay question will not be assigned a grade until it has been reviewed by a teacher and manually graded.';
@ -38,5 +46,10 @@ $string['pluginnameediting'] = 'Editing an Essay question';
$string['pluginnamesummary'] = 'Allows a response of a few sentences or paragraphs. This must then be graded manually.';
$string['responsefieldlines'] = 'Input box size';
$string['responseformat'] = 'Response format';
$string['responseoptions'] = 'Response Options';
$string['responserequired'] = 'Require text';
$string['responsenotrequired'] = 'Text input is optional';
$string['responseisrequired'] = 'Require the student to enter text';
$string['responsetemplate'] = 'Response template';
$string['responsetemplateheader'] = 'Response Template';
$string['responsetemplate_help'] = 'Any text entered here will be displayed in the response input box when a new attempt at the question starts.';

View File

@ -34,9 +34,18 @@ defined('MOODLE_INTERNAL') || die();
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class qtype_essay_question extends question_with_responses {
public $responseformat;
/** @var int Indicates whether an inline response is required ('0') or optional ('1') */
public $responserequired;
public $responsefieldlines;
public $attachments;
/** @var int The number of attachments required for a response to be complete. */
public $attachmentsrequired;
public $graderinfo;
public $graderinfoformat;
public $responsetemplate;
@ -81,7 +90,27 @@ class qtype_essay_question extends question_with_responses {
}
public function is_complete_response(array $response) {
return array_key_exists('answer', $response) && ($response['answer'] !== '');
// Determine if the given response has inline text and attachments.
$hasinlinetext = array_key_exists('answer', $response) && ($response['answer'] !== '');
$hasattachments = array_key_exists('attachments', $response)
&& $response['attachments'] instanceof question_response_files;
// Determine the number of attachments present.
if ($hasattachments) {
$attachcount = count($response['attachments']->get_files());
} else {
$attachcount = 0;
}
// Determine if we have /some/ content to be graded.
$hascontent = $hasinlinetext || ($attachcount > 0);
// Determine if we meet the optional requirements.
$meetsinlinereq = $hasinlinetext || (!$this->responserequired) || ($this->responseformat == 'noinline');
$meetsattachmentreq = ($attachcount >= $this->attachmentsrequired);
// The response is complete iff all of our requirements are met.
return $hascontent && $meetsinlinereq && $meetsattachmentreq;
}
public function is_same_response(array $prevresponse, array $newresponse) {

View File

@ -63,8 +63,10 @@ class qtype_essay extends question_type {
}
$options->responseformat = $formdata->responseformat;
$options->responserequired = $formdata->responserequired;
$options->responsefieldlines = $formdata->responsefieldlines;
$options->attachments = $formdata->attachments;
$options->attachmentsrequired = $formdata->attachmentsrequired;
$options->graderinfo = $this->import_or_save_files($formdata->graderinfo,
$context, 'qtype_essay', 'graderinfo', $formdata->id);
$options->graderinfoformat = $formdata->graderinfo['format'];
@ -76,8 +78,10 @@ class qtype_essay extends question_type {
protected function initialise_question_instance(question_definition $question, $questiondata) {
parent::initialise_question_instance($question, $questiondata);
$question->responseformat = $questiondata->options->responseformat;
$question->responserequired = $questiondata->options->responserequired;
$question->responsefieldlines = $questiondata->options->responsefieldlines;
$question->attachments = $questiondata->options->attachments;
$question->attachmentsrequired = $questiondata->options->attachmentsrequired;
$question->graderinfo = $questiondata->options->graderinfo;
$question->graderinfoformat = $questiondata->options->graderinfoformat;
$question->responsetemplate = $questiondata->options->responsetemplate;
@ -101,6 +105,17 @@ class qtype_essay extends question_type {
'editorfilepicker' => get_string('formateditorfilepicker', 'qtype_essay'),
'plain' => get_string('formatplain', 'qtype_essay'),
'monospaced' => get_string('formatmonospaced', 'qtype_essay'),
'noinline' => get_string('formatnoinline', 'qtype_essay'),
);
}
/**
* @return array the choices that should be offerd when asking if a response is required
*/
public function response_required_options() {
return array(
1 => get_string('responseisrequired', 'qtype_essay'),
0 => get_string('responsenotrequired', 'qtype_essay'),
);
}
@ -128,6 +143,18 @@ class qtype_essay extends question_type {
);
}
/**
* @return array the choices that should be offered for the number of required attachments.
*/
public function attachments_required_options() {
return array(
0 => get_string('attachmentsoptional', 'qtype_essay'),
1 => '1',
2 => '2',
3 => '3'
);
}
public function move_files($questionid, $oldcontextid, $newcontextid) {
parent::move_files($questionid, $oldcontextid, $newcontextid);
$fs = get_file_storage();

View File

@ -178,6 +178,28 @@ abstract class qtype_essay_format_renderer_base extends plugin_renderer_base {
protected abstract function class_name();
}
/**
* An essay format renderer for essays where the student should not enter
* any inline response.
*
* @copyright 2013 Binghamton University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class qtype_essay_format_noinline_renderer extends plugin_renderer_base {
protected function class_name() {
return 'qtype_essay_noinline';
}
public function response_area_read_only($name, $qa, $step, $lines, $context) {
return '';
}
public function response_area_input($name, $qa, $step, $lines, $context) {
return '';
}
}
/**
* An essay format renderer for essays where the student should use the HTML

View File

@ -34,7 +34,7 @@ defined('MOODLE_INTERNAL') || die();
*/
class qtype_essay_test_helper extends question_test_helper {
public function get_test_questions() {
return array('editor', 'editorfilepicker', 'plain', 'monospaced', 'responsetemplate');
return array('editor', 'editorfilepicker', 'plain', 'monospaced', 'responsetemplate', 'noinline');
}
/**
@ -49,8 +49,10 @@ class qtype_essay_test_helper extends question_test_helper {
$q->questiontext = 'Please write a story about a frog.';
$q->generalfeedback = 'I hope your story had a beginning, a middle and an end.';
$q->responseformat = 'editor';
$q->responserequired = 1;
$q->responsefieldlines = 10;
$q->attachments = 0;
$q->attachmentsrequired = 0;
$q->graderinfo = '';
$q->graderinfoformat = FORMAT_HTML;
$q->qtype = question_bank::get_qtype('essay');
@ -93,8 +95,10 @@ class qtype_essay_test_helper extends question_test_helper {
$fromform->defaultmark = 1.0;
$fromform->generalfeedback = array('text' => 'I hope your story had a beginning, a middle and an end.', 'format' => FORMAT_HTML);
$fromform->responseformat = 'editorfilepicker';
$fromform->responserequired = 1;
$fromform->responsefieldlines = 10;
$fromform->attachments = 3;
$fromform->attachmentsrequired = 0;
$fromform->graderinfo = array('text' => '', 'format' => FORMAT_HTML);
$fromform->responsetemplate = array('text' => '', 'format' => FORMAT_HTML);
@ -126,8 +130,10 @@ class qtype_essay_test_helper extends question_test_helper {
$fromform->defaultmark = 1.0;
$fromform->generalfeedback = array('text' => 'I hope your story had a beginning, a middle and an end.', 'format' => FORMAT_HTML);
$fromform->responseformat = 'plain';
$fromform->responserequired = 1;
$fromform->responsefieldlines = 10;
$fromform->attachments = 0;
$fromform->attachmentsrequired = 0;
$fromform->graderinfo = array('text' => '', 'format' => FORMAT_HTML);
$fromform->responsetemplate = array('text' => '', 'format' => FORMAT_HTML);
@ -150,4 +156,87 @@ class qtype_essay_test_helper extends question_test_helper {
$q->responsetemplateformat = FORMAT_HTML;
return $q;
}
/**
* Makes an essay question without an inline text editor.
* @return qtype_essay_question
*/
public function make_essay_question_noinline() {
$q = $this->initialise_essay_question();
$q->responseformat = 'noinline';
$q->attachments = 3;
$q->attachmentsrequired = 1;
return $q;
}
/**
* Creates an empty draft area for attachments.
* @return int The draft area's itemid.
*/
protected function make_attachment_draft_area() {
$draftid = 0;
$contextid = 0;
$component = 'question';
$filearea = 'response_attachments';
// Create an empty file area.
file_prepare_draft_area($draftid, $contextid, $component, $filearea, null);
return $draftid;
}
/**
* Creates an attachment in the provided attachment draft area.
* @param int $draftid The itemid for the draft area in which the file should be created.
* @param string $name The filename for the file to be created.
* @param string $contents The contents of the file to be created.
*/
protected function make_attachment($draftid, $name, $contents) {
global $USER;
$fs = get_file_storage();
$usercontext = context_user::instance($USER->id);
// Create the file in the provided draft area.
$fileinfo = array(
'contextid' => $usercontext->id,
'component' => 'user',
'filearea' => 'draft',
'itemid' => $draftid,
'filepath' => '/',
'filename' => $name,
);
$fs->create_file_from_string($fileinfo, $contents);
}
/**
* Generates a draft file area that contains the provided number of attachments. You should ensure
* that a user is logged in with setUser before you run this function.
*
* @param int $attachments The number of attachments to generate.
* @return int The itemid of the generated draft file area.
*/
public function make_attachments($attachments) {
$draftid = $this->make_attachment_draft_area();
// Create the relevant amount of dummy attachments in the given draft area.
for ($i = 0; $i < $attachments; ++$i) {
$this->make_attachment($draftid, $i, $i);
}
return $draftid;
}
/**
* Generates a question_file_saver that contains the provided number of attachments. You should ensure
* that a user is logged in with setUser before you run this function.
*
* @param int $:attachments The number of attachments to generate.
* @return question_file_saver a question_file_saver that contains the given amount of dummy files, for use in testing.
*/
public function make_attachments_saver($attachments) {
return new question_file_saver($this->make_attachments($attachments), 'question', 'response_attachments');
}
}

View File

@ -36,7 +36,7 @@ require_once($CFG->dirroot . '/question/engine/tests/helpers.php');
* @copyright 2009 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class qtype_essay_question_testcase extends advanced_testcase {
class qtype_essay_question_test extends advanced_testcase {
public function test_get_question_summary() {
$essay = test_question_maker::make_an_essay_question();
$essay->questiontext = 'Hello <img src="http://example.com/globe.png" alt="world" />';
@ -139,11 +139,29 @@ class qtype_essay_question_testcase extends advanced_testcase {
}
public function test_is_complete_response() {
$this->resetAfterTest(true);
// Create a new logged-in user, so we can test responses with attachments.
$user = $this->getDataGenerator()->create_user();
$this->setUser($user);
// Create sample attachments to use in testing.
$helper = test_question_maker::get_test_helper('essay');
$attachments = array();
for ($i = 0; $i < 4; ++$i) {
$attachments[$i] = $helper->make_attachments_saver($i);
}
// Create the essay question under test.
$essay = test_question_maker::make_an_essay_question();
$essay->start_attempt(new question_attempt_step(), 1);
// The empty string should be considered an empty response, as should a lack of a response.
// Test the "traditional" case, where we must recieve a response from the user.
$essay->responserequired = 1;
$essay->attachmentsrequired = 0;
$essay->responseformat = 'editor';
// The empty string should be considered an incomplete response, as should a lack of a response.
$this->assertFalse($essay->is_complete_response(array('answer' => '')));
$this->assertFalse($essay->is_complete_response(array()));
@ -151,5 +169,74 @@ class qtype_essay_question_testcase extends advanced_testcase {
$this->assertTrue($essay->is_complete_response(array('answer' => 'A student response.')));
$this->assertTrue($essay->is_complete_response(array('answer' => '0 times.')));
$this->assertTrue($essay->is_complete_response(array('answer' => '0')));
// Test the case where two files are required.
$essay->attachmentsrequired = 2;
// Attaching less than two files should result in an incomplete response.
$this->assertFalse($essay->is_complete_response(array('answer' => 'A')));
$this->assertFalse($essay->is_complete_response(
array('answer' => 'A', 'attachments' => $attachments[0])));
$this->assertFalse($essay->is_complete_response(
array('answer' => 'A', 'attachments' => $attachments[1])));
// Anything without response text should result in an incomplete response.
$this->assertFalse($essay->is_complete_response(
array('answer' => '', 'attachments' => $attachments[2])));
// Attaching two or more files should result in a complete response.
$this->assertTrue($essay->is_complete_response(
array('answer' => 'A', 'attachments' => $attachments[2])));
$this->assertTrue($essay->is_complete_response(
array('answer' => 'A', 'attachments' => $attachments[3])));
// Test the case in which two files are required, but the inline
// response is optional.
$essay->responserequired = 0;
$this->assertFalse($essay->is_complete_response(
array('answer' => '', 'attachments' => $attachments[1])));
$this->assertTrue($essay->is_complete_response(
array('answer' => '', 'attachments' => $attachments[2])));
// Test the case in which both the response and inline text are optional.
$essay->attachmentsrequired = 0;
// Providing no answer and no attachment should result in an incomplete
// response.
$this->assertFalse($essay->is_complete_response(
array('answer' => '')));
$this->assertFalse($essay->is_complete_response(
array('answer' => '', 'attachments' => $attachments[0])));
// Providing an answer _or_ an attachment should result in a complete
// response.
$this->assertTrue($essay->is_complete_response(
array('answer' => '', 'attachments' => $attachments[1])));
$this->assertTrue($essay->is_complete_response(
array('answer' => 'Answer text.', 'attachments' => $attachments[0])));
// Test the case in which we're in "no inline response" mode,
// in which the response is not required (as it's not provided).
$essay->reponserequired = 0;
$essay->responseformat = 'noinline';
$essay->attachmensrequired = 1;
$this->assertFalse($essay->is_complete_response(
array()));
$this->assertFalse($essay->is_complete_response(
array('attachments' => $attachments[0])));
// Providing an attachment should result in a complete response.
$this->assertTrue($essay->is_complete_response(
array('attachments' => $attachments[1])));
// Ensure that responserequired is ignored when we're in inline response mode.
$essay->reponserequired = 1;
$this->assertTrue($essay->is_complete_response(
array('attachments' => $attachments[1])));
}
}

View File

@ -26,7 +26,7 @@
defined('MOODLE_INTERNAL') || die();
$plugin->component = 'qtype_essay';
$plugin->version = 2013110500;
$plugin->version = 2014011301;
$plugin->requires = 2013110500;