mirror of
https://github.com/moodle/moodle.git
synced 2025-01-18 22:08:20 +01:00
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:
parent
12efa52762
commit
a4f765eb1d
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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>
|
||||
|
@ -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' => '',
|
||||
|
@ -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);
|
||||
|
@ -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 = '';
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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';
|
||||
}
|
||||
|
@ -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.';
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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');
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -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])));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -26,7 +26,7 @@
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->component = 'qtype_essay';
|
||||
$plugin->version = 2013110500;
|
||||
$plugin->version = 2014011301;
|
||||
|
||||
$plugin->requires = 2013110500;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user