I am committing the essay question type contributed by Mark Nielsen, see http://moodle.org/mod/forum/discuss.php?d=25845.
This commit is contained in:
@@ -839,6 +839,27 @@ function quiz_upgrade($oldversion) {
|
||||
$db->debug = $olddebug;
|
||||
}
|
||||
|
||||
if ($oldversion < 2005062600) {
|
||||
modify_database ('', "
|
||||
CREATE TABLE `prefix_quiz_essay` (
|
||||
`id` int(10) unsigned NOT NULL auto_increment,
|
||||
`question` int(10) unsigned NOT NULL default '0',
|
||||
`answer` varchar(255) NOT NULL default '',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `question` (`question`)
|
||||
) TYPE=MyISAM COMMENT='Options for essay questions'");
|
||||
|
||||
modify_database ('', "
|
||||
CREATE TABLE `prefix_quiz_essay_states` (
|
||||
`id` int(10) unsigned NOT NULL auto_increment,
|
||||
`stateid` int(10) unsigned NOT NULL default '0',
|
||||
`graded` tinyint(4) unsigned NOT NULL default '0',
|
||||
`fraction` varchar(10) NOT NULL default '0.0',
|
||||
`response` text NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) TYPE=MyISAM COMMENT='essay question type specific state information'");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -171,6 +171,35 @@ CREATE TABLE prefix_quiz_dataset_items (
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- Table structure for table `mdl_quiz_essay`
|
||||
--
|
||||
|
||||
CREATE TABLE `prefix_quiz_essay` (
|
||||
`id` int(10) unsigned NOT NULL auto_increment,
|
||||
`question` int(10) unsigned NOT NULL default '0',
|
||||
`answer` varchar(255) NOT NULL default '',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `question` (`question`)
|
||||
) TYPE=MyISAM COMMENT='Options for essay questions';
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- Table structure for table `mdl_quiz_essay_states`
|
||||
--
|
||||
|
||||
CREATE TABLE `prefix_quiz_essay_states` (
|
||||
`id` int(10) unsigned NOT NULL auto_increment,
|
||||
`stateid` int(10) unsigned NOT NULL default '0',
|
||||
`graded` tinyint(4) unsigned NOT NULL default '0',
|
||||
`fraction` varchar(10) NOT NULL default '0.0',
|
||||
`response` text NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) TYPE=MyISAM COMMENT='essay question type specific state information';
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- Table structure for table `prefix_quiz_grades`
|
||||
--
|
||||
|
||||
@@ -28,7 +28,8 @@ $QUIZ_QUESTION_TYPE = array ( MULTICHOICE => get_string("multichoice", "quiz")
|
||||
MATCH => get_string("match", "quiz"),
|
||||
DESCRIPTION => get_string("description", "quiz"),
|
||||
RANDOMSAMATCH => get_string("randomsamatch", "quiz"),
|
||||
MULTIANSWER => get_string("multianswer", "quiz")
|
||||
MULTIANSWER => get_string("multianswer", "quiz"),
|
||||
ESSAY => get_string("essay", "quiz")
|
||||
);
|
||||
// add remote question types
|
||||
if ($rqp_types = get_records('quiz_rqp_types')) {
|
||||
|
||||
@@ -0,0 +1,409 @@
|
||||
<?php
|
||||
|
||||
// This file allows a teacher to grade essay questions.
|
||||
// Could be later expanded to change user responses for all question types
|
||||
|
||||
# Flow of the file:
|
||||
# Get variables, run essencial queries, print header and print tabs
|
||||
# Check for post data submitted. If true, then process data (the data is the grades and comments for essay questions)
|
||||
# Check for userid, attemptid, or gradeall and for questionid. If found, print out the appropriate essay question attempts
|
||||
# Switch:
|
||||
# first case: print out all essay questions in quiz and the number of ungraded attempts
|
||||
# second case: print out all users and their attempts for a specific essay question
|
||||
|
||||
|
||||
require_once("../../config.php");
|
||||
require_once("lib.php");
|
||||
require_once("editlib.php");
|
||||
require_once($CFG->libdir.'/tablelib.php');
|
||||
|
||||
$quizid = required_param('quizid', PARAM_INT); // Course Module ID, or
|
||||
$action = optional_param('action', 'viewquestions', PARAM_ALPHA);
|
||||
$questionid = optional_param('questionid', 0, PARAM_INT);
|
||||
$attemptid = optional_param('attemptid', 0, PARAM_INT);
|
||||
$gradeall = optional_param('gradeall', 0, PARAM_INT);
|
||||
$userid = optional_param('userid', 0, PARAM_INT);
|
||||
|
||||
|
||||
if (!empty($questionid)) {
|
||||
if (! $question = get_record('quiz_questions', 'id', $questionid)) {
|
||||
error("Question with id $questionid not found");
|
||||
}
|
||||
///$number = optional_param('number', 0, PARAM_INT);
|
||||
}
|
||||
|
||||
if (! $quiz = get_record("quiz", "id", $quizid)) {
|
||||
error("Quiz with id $quizid not found");
|
||||
}
|
||||
if (! $course = get_record("course", "id", $quiz->course)) {
|
||||
error("Course is misconfigured");
|
||||
}
|
||||
if (! $cm = get_coursemodule_from_instance("quiz", $quiz->id, $course->id)) {
|
||||
error("Course Module ID was incorrect");
|
||||
}
|
||||
|
||||
require_login($course->id);
|
||||
|
||||
if (!$isteacher = isteacheredit($course->id)) {
|
||||
error("Only teachers authorized to edit the course '{$course->fullname}' can use this page!");
|
||||
}
|
||||
|
||||
add_to_log($course->id, "quiz", "manualgrading", "grading.php?quizid=$quiz->id", "$quiz->id", "$cm->id");
|
||||
|
||||
/// GROUP CODE FROM ATTEMPTS.PHP no sure how to use just yet... need to update later perhaps
|
||||
/// Check to see if groups are being used in this quiz
|
||||
# if ($groupmode = groupmode($course, $cm)) { // Groups are being used
|
||||
# $currentgroup = setup_and_print_groups($course, $groupmode, "attempts.php?id=$cm->id&mode=overview");
|
||||
# } else {
|
||||
# $currentgroup = false;
|
||||
# }
|
||||
|
||||
/// Get all students
|
||||
# if ($currentgroup) {
|
||||
# $users = get_group_students($currentgroup);
|
||||
# }
|
||||
# else {
|
||||
$users = get_course_students($course->id);
|
||||
# }
|
||||
|
||||
if(empty($users)) {
|
||||
print_heading(get_string("noattempts", "quiz"));
|
||||
return true;
|
||||
} else {
|
||||
// for sql queries
|
||||
$userids = implode(', ', array_keys($users));
|
||||
}
|
||||
|
||||
$strquizzes = get_string("modulenameplural", "quiz");
|
||||
$strmanualgrading = get_string("manualgrading", "quiz");
|
||||
|
||||
print_header_simple("$quiz->name", "",
|
||||
"<a href=\"index.php?id=$course->id\">$strquizzes</a>
|
||||
-> <a href=\"view.php?id=$cm->id\">$quiz->name</a> -> $strmanualgrading",
|
||||
"", "", true);
|
||||
|
||||
|
||||
$currenttab = 'manualgrading';
|
||||
include('tabs.php');
|
||||
echo '<div id="overDiv" style="position:absolute; visibility:hidden; z-index:1000;"></div>'; // for overlib
|
||||
|
||||
if ($data = data_submitted()) { // post data submitted, process it
|
||||
confirm_sesskey();
|
||||
|
||||
$question->maxgrade = get_field('quiz_question_instances', 'grade', 'quiz', $quiz->id, 'question', $question->id);
|
||||
$QUIZ_QTYPES[$question->qtype]->get_question_options($question);
|
||||
|
||||
// first, process all the data to extract the teacher's new responses for the question(s)
|
||||
foreach ($data as $key => $response) {
|
||||
$keyparts = explode('_', $key); // valid keys are in this format: attemptid_stateid_fieldname
|
||||
if (count($keyparts) == 3) { // must have 3 parts to the key
|
||||
// re-assign to nice variable names for readability
|
||||
$attemptid = $keyparts[0];
|
||||
$stateid = $keyparts[1];
|
||||
$fieldname = $keyparts[2];
|
||||
|
||||
$responses[$attemptid.'_'.$stateid][$fieldname] = $response;
|
||||
}
|
||||
}
|
||||
// now go through all of the responses to grade them and save them.
|
||||
// not totally sure if this process is correct or fully optimized. I need help here!
|
||||
foreach($responses as $ids => $response) {
|
||||
// get our necessary ids
|
||||
$ids = explode('_', $ids);
|
||||
$attemptid = $ids[0];
|
||||
$stateid = $ids[1];
|
||||
|
||||
// get our attempt
|
||||
if (! $attempt = get_record('quiz_attempts', 'id', $attemptid)) {
|
||||
error('No such attempt ID exists');
|
||||
}
|
||||
|
||||
// get the state
|
||||
$statefields = 'n.questionid as question, s.*, n.sumpenalty';
|
||||
$sql = "SELECT $statefields".
|
||||
" FROM {$CFG->prefix}quiz_states s,".
|
||||
" {$CFG->prefix}quiz_newest_states n".
|
||||
" WHERE s.id = n.newest".
|
||||
" AND n.attemptid = '$attempt->id'".
|
||||
" AND n.questionid = $question->id";
|
||||
$state = get_record_sql($sql);
|
||||
|
||||
// restore the state of the question
|
||||
quiz_restore_state($question, $state);
|
||||
|
||||
// this is the new response from the teacher
|
||||
$state->responses = $response;
|
||||
|
||||
// grade the question with the new state made by the teacher
|
||||
$QUIZ_QTYPES[$question->qtype]->grade_responses($question, $state, $quiz);
|
||||
|
||||
// finalize the grade
|
||||
$state->last_graded->grade = 0; // we dont want the next function to care about the last grade
|
||||
quiz_apply_penalty_and_timelimit($question, $state, $attempt, $quiz);
|
||||
|
||||
// want to update session. Also set changed to 1 to trick quiz_save_question_session to save our session
|
||||
$state->update = 1;
|
||||
$state->changed = 1;
|
||||
quiz_save_question_session($question, $state);
|
||||
|
||||
// method for changing sumgrades from report type regrade. Thanks!
|
||||
$sumgrades = 0;
|
||||
$questionids = explode(',', quiz_questions_in_quiz($attempt->layout));
|
||||
foreach($questionids as $questionid) {
|
||||
$lastgradedid = get_field('quiz_newest_states', 'newgraded', 'attemptid', $attempt->id, 'questionid', $questionid);
|
||||
$sumgrades += get_field('quiz_states', 'grade', 'id', $lastgradedid);
|
||||
}
|
||||
|
||||
if ($attempt->sumgrades != $sumgrades) {
|
||||
set_field('quiz_attempts', 'sumgrades', $sumgrades, 'id', $attempt->id);
|
||||
}
|
||||
|
||||
// update user's grade
|
||||
quiz_save_best_grade($quiz, $attempt->userid);
|
||||
}
|
||||
print_string('changessaved', 'quiz');
|
||||
} else if ( ( !empty($attemptid) or !empty($gradeall) or !empty($userid)) and !empty($questionid) ) { // need attemptid and questionid or gradeall and a questionid
|
||||
// this sql joins the attempts table and the user table
|
||||
$select = 'SELECT '.$db->Concat('u.id', '\'#\'', $db->IfNull('qa.attempt', '0')).' AS uniqueid,
|
||||
qa.id AS attemptid, qa.attempt, qa.timefinish, qa.preview,
|
||||
u.id AS userid, u.firstname, u.lastname, u.picture ';
|
||||
$from = 'FROM mdl_user u LEFT JOIN mdl_quiz_attempts qa ON (u.id = qa.userid AND qa.quiz = '.$quiz->id.') ';
|
||||
|
||||
if ($gradeall) { // get all user attempts
|
||||
$where = 'WHERE u.id IN ('.implode(',', array_keys($users)).') ';
|
||||
} else if ($userid) { // get all the attempts for a specific user
|
||||
$where = 'WHERE u.id='.$userid.' ';
|
||||
} else { // get a specific attempt
|
||||
$where = 'WHERE qa.id='.$attemptid.' ';
|
||||
}
|
||||
|
||||
$where .= 'AND '.$db->IfNull('qa.attempt', '0').' != 0 ';
|
||||
$where .= 'AND '.$db->IfNull('qa.timefinish', '0').' != 0 ';
|
||||
$sort = 'ORDER BY u.firstname, u.lastname, qa.attempt ASC';
|
||||
$attempts = get_records_sql($select.$from.$where.$sort);
|
||||
|
||||
echo '<form method="post" action="grading.php">'.
|
||||
'<input type="hidden" name="quizid" value="'.$quiz->id.'">'.
|
||||
'<input type="hidden" name="sesskey" value="'.$USER->sesskey.'">'.
|
||||
'<input type="hidden" name="action" value="viewquestion">'.
|
||||
'<input type="hidden" name="questionid" value="'.$questionid.'">';
|
||||
|
||||
foreach ($attempts as $attempt) {
|
||||
// retrieve the state
|
||||
if (!$neweststate = get_record('quiz_newest_states', 'attemptid', $attempt->attemptid, 'questionid', $questionid)) {
|
||||
error('Invalid attempt and question ids');
|
||||
}
|
||||
if (! $state = get_record('quiz_states', 'id', $neweststate->newest)) {
|
||||
error('Invalid state id');
|
||||
}
|
||||
|
||||
// get everything ready for the question to be printed
|
||||
$instance = get_record('quiz_question_instances', 'quiz', $quiz->id, 'question', $question->id);
|
||||
$question->instance = $instance->id;
|
||||
$question->maxgrade = $instance->grade;
|
||||
$question->name_prefix = $attempt->attemptid.'_'.$state->id.'_';
|
||||
$QUIZ_QTYPES[$question->qtype]->get_question_options($question);
|
||||
|
||||
quiz_restore_state($question, $state);
|
||||
$state->last_graded = $state;
|
||||
|
||||
$options = quiz_get_reviewoptions($quiz, $attempt, $isteacher);
|
||||
$options->validation = ($state->event == QUIZ_EVENTVALIDATE); // not sure what this is
|
||||
//$options->history = 'all'; // had this on, but seemed confusing for this
|
||||
|
||||
// IF this code is expanded to manually regrade any question type, then
|
||||
// readonly would be set to 0 and the essay question would have to be
|
||||
// updated. Also, regrade would most likly be tossed.
|
||||
$options->readonly = 1;
|
||||
$options->regrade = 1;
|
||||
|
||||
// print the user name, attempt count, the question, and some more hidden fields
|
||||
echo '<div align="center" width="80%" style="padding:15px;">'.
|
||||
'<p>'."$attempt->firstname $attempt->lastname: ".
|
||||
get_string('attempt', 'quiz')." $attempt->attempt".
|
||||
'</p>';
|
||||
|
||||
quiz_print_quiz_question($question, $state, '', $quiz, $options);
|
||||
echo '<input type="hidden" name="attemptids[]" value="'.$attempt->attemptid.'">'.
|
||||
'<input type="hidden" name="stateids[]" value="'.$state->id.'">';
|
||||
echo '</div>';
|
||||
}
|
||||
echo '<div align="center"><input type="submit" value="'.get_string('save', 'quiz').'"></div>'.
|
||||
'</form>';
|
||||
print_footer($course);
|
||||
exit();
|
||||
}
|
||||
|
||||
// our 2 different views
|
||||
// the first one displays all of the essay questions in the quiz
|
||||
// with the number of ungraded attempts for each essay question
|
||||
|
||||
// the second view displays the users who have answered the essay question
|
||||
// and all of their attempts at answering the question
|
||||
switch($action) {
|
||||
case 'viewquestions':
|
||||
notify(get_string('essayonly', 'quiz'));
|
||||
// just a basic table for this...
|
||||
$table = new stdClass;
|
||||
$table->head = array(get_string("essayquestions", "quiz"), get_string("ungraded", "quiz"));
|
||||
$table->align = array("left", "left");
|
||||
$table->wrap = array("wrap", "wrap");
|
||||
$table->width = "20%";
|
||||
$table->size = array("*", "*");
|
||||
$table->data = array();
|
||||
|
||||
// get the essay questions
|
||||
$questionlist = quiz_questions_in_quiz($quiz->questions);
|
||||
$sql = "SELECT q.*, i.grade AS maxgrade, i.id AS instance".
|
||||
" FROM {$CFG->prefix}quiz_questions q,".
|
||||
" {$CFG->prefix}quiz_question_instances i".
|
||||
" WHERE i.quiz = '$quiz->id' AND q.id = i.question".
|
||||
" AND q.id IN ($questionlist)".
|
||||
" AND q.qtype = '".ESSAY."'".
|
||||
" ORDER BY q.name";
|
||||
if (!$questions = get_records_sql($sql)) {
|
||||
print_heading(get_string('noessayquestionsfound', 'quiz'));
|
||||
print_footer($course);
|
||||
exit();
|
||||
}
|
||||
// get all the finished attempts by the users
|
||||
if ($attempts = get_records_select('quiz_attempts', "quiz = $quiz->id and timefinish > 0 and userid IN ($userids)", 'userid, attempt')) {
|
||||
foreach($questions as $question) {
|
||||
|
||||
$link = "<a href=\"grading.php?quizid=$quiz->id&action=viewquestion&questionid=$question->id\">".
|
||||
$question->name."</a>";
|
||||
// determine the number of ungraded attempts (essay question thing only)
|
||||
$ungraded = 0;
|
||||
foreach ($attempts as $attempt) {
|
||||
// grab the state then check if it is graded
|
||||
if (!$neweststate = get_record('quiz_newest_states', 'attemptid', $attempt->id, 'questionid', $question->id)) {
|
||||
error('Invalid attempt and question ids');
|
||||
}
|
||||
if (!$questionstate = get_record('quiz_essay_states', 'stateid', $neweststate->newest)) {
|
||||
error('Could not find question state');
|
||||
}
|
||||
if (!$questionstate->graded) {
|
||||
$ungraded++;
|
||||
}
|
||||
}
|
||||
|
||||
$table->data[] = array($link, $ungraded);
|
||||
}
|
||||
print_table($table);
|
||||
} else {
|
||||
print_heading(get_string('noattempts', 'quiz'));
|
||||
}
|
||||
break;
|
||||
case 'viewquestion':
|
||||
// gonna use flexible_table (first time!)
|
||||
$tablecolumns = array('picture', 'fullname', 'attempt');
|
||||
$tableheaders = array('', get_string('fullname'), get_string("attempts", "quiz"));
|
||||
|
||||
$table = new flexible_table('mod-quiz-report-grading');
|
||||
|
||||
$table->define_columns($tablecolumns);
|
||||
$table->define_headers($tableheaders);
|
||||
$table->define_baseurl($CFG->wwwroot.'/mod/quiz/grading.php?quizid='.$quiz->id.'&action=viewquestion&questionid='.$question->id);
|
||||
|
||||
$table->sortable(true);
|
||||
$table->initialbars(count($users)>20); // will show initialbars if there are more than 20 users
|
||||
$table->pageable(true);
|
||||
|
||||
$table->column_suppress('fullname');
|
||||
$table->column_suppress('picture');
|
||||
|
||||
$table->column_class('picture', 'picture');
|
||||
|
||||
// attributes in the table tag
|
||||
$table->set_attribute('cellspacing', '0');
|
||||
$table->set_attribute('id', 'grading');
|
||||
$table->set_attribute('class', 'generaltable generalbox');
|
||||
$table->set_attribute('align', 'center');
|
||||
$table->set_attribute('width', '50%');
|
||||
|
||||
// get it ready!
|
||||
$table->setup();
|
||||
|
||||
// this sql is a join of the attempts table and the user table. I do this so I can sort by user name and attempt number (not id)
|
||||
$select = 'SELECT '.$db->Concat('u.id', '\'#\'', $db->IfNull('qa.attempt', '0')).' AS uniqueid, qa.id AS attemptid, qa.attempt, u.id AS userid, u.firstname, u.lastname, u.picture ';
|
||||
$from = 'FROM mdl_user u LEFT JOIN mdl_quiz_attempts qa ON (u.id = qa.userid AND qa.quiz = '.$quiz->id.') ';
|
||||
$where = 'WHERE u.id IN ('.implode(',', array_keys($users)).') ';
|
||||
$where .= 'AND '.$db->IfNull('qa.attempt', '0').' != 0 ';
|
||||
$where .= 'AND '.$db->IfNull('qa.timefinish', '0').' != 0 ';
|
||||
|
||||
if($table->get_sql_where()) { // forgot what this does
|
||||
$where .= 'AND '.$table->get_sql_where();
|
||||
}
|
||||
|
||||
// sorting of the table
|
||||
if($sort = $table->get_sql_sort()) {
|
||||
$sort = 'ORDER BY '.$sort; // seems like I would need to have u. or qa. infront of the ORDER BY attribues... but seems to work..
|
||||
} else {
|
||||
// my default sort rule
|
||||
$sort = 'ORDER BY u.firstname, u.lastname, qa.attempt ASC';
|
||||
}
|
||||
|
||||
// set up the pagesize
|
||||
$total = count_records_sql('SELECT COUNT(DISTINCT('.$db->Concat('u.id', '\'#\'', $db->IfNull('qa.attempt', '0')).')) '.$from.$where);
|
||||
$table->pagesize(10, $total);
|
||||
|
||||
// this is for getting the correct records for a given page
|
||||
if($table->get_page_start() !== '' && $table->get_page_size() !== '') {
|
||||
$limit = ' '.sql_paging_limit($table->get_page_start(), $table->get_page_size());
|
||||
} else {
|
||||
$limit = '';
|
||||
}
|
||||
//$number = 1;
|
||||
// get the attempts and process them
|
||||
if ($attempts = get_records_sql($select.$from.$where.$sort.$limit)) {
|
||||
foreach($attempts as $attempt) {
|
||||
|
||||
$picture = print_user_picture($attempt->userid, $course->id, $attempt->picture, false, true);
|
||||
|
||||
// link here... grades all for this student
|
||||
$userlink = "<a href=\"grading.php?quizid=$quiz->id&questionid=$question->id&userid=$attempt->userid\">".
|
||||
$attempt->firstname.' '.$attempt->lastname.'</a>';
|
||||
|
||||
// nab the state of the attempt to see if it is graded or not
|
||||
if (!$neweststate = get_record('quiz_newest_states', 'attemptid', $attempt->attemptid, 'questionid', $question->id)) {
|
||||
error('Invalid attempt and question ids');
|
||||
}
|
||||
|
||||
if (!$questionstate = get_record('quiz_essay_states', 'stateid', $neweststate->newest)) {
|
||||
error('Could not find question state');
|
||||
}
|
||||
// change the color of the link based on being graded or not
|
||||
if (!$questionstate->graded) {
|
||||
$style = 'style="color:#FF0000"'; // red
|
||||
} else {
|
||||
$style = 'style="color:#008000"'; // green
|
||||
}
|
||||
|
||||
// link for the attempt
|
||||
$attemptlink = "<a $style href=\"grading.php?quizid=$quiz->id&questionid=$question->id&attemptid=$attempt->attemptid\">". // &number=$number
|
||||
$question->name." attempt $attempt->attempt</a>";
|
||||
|
||||
$table->add_data( array($picture, $userlink, $attemptlink) );
|
||||
}
|
||||
//$number += $question->length;
|
||||
}
|
||||
|
||||
// grade all and "back" links
|
||||
$links = "<center><a href=\"grading.php?quizid=$quiz->id&questionid=$questionid&gradeall=1\">".get_string('gradeall', 'quiz').'</a> | '.
|
||||
"<a href=\"grading.php?quizid=$quiz->id&action=viewquestions\">".get_string('backtoquestionlist', 'quiz').'</a></center>'.
|
||||
|
||||
// print everything here
|
||||
print_heading($question->name);
|
||||
echo $links;
|
||||
echo '<div id="tablecontainer">';
|
||||
$table->print_html();
|
||||
echo '</div>';
|
||||
echo $links;
|
||||
break;
|
||||
default:
|
||||
error("Invalid Action");
|
||||
}
|
||||
|
||||
print_footer($course);
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
$string['backtoquestionlist'] = 'Back to Question List';
|
||||
$string['changessaved'] = 'Grading Changes Saved';
|
||||
$string['comments'] = 'Comments';
|
||||
$string['editingessay'] = 'Editing Essay';
|
||||
$string['essay'] = 'Essay';
|
||||
$string['essayquestions'] = 'Essay Questions';
|
||||
$string['gradeall'] = 'Grade All';
|
||||
$string['gradeessays'] = 'Grade Essays';
|
||||
$string['manualgrading'] = 'Manual grading';
|
||||
$string['nocommentsyet'] = 'No comments yet.';
|
||||
$string['noessayquestionsfound'] = 'No Essay Questions Found';
|
||||
$string['essayonly'] = 'Manual grading is so far only implemented for essay questions';
|
||||
$string['numungraded'] = '($a ungraded)';
|
||||
$string['ungraded'] = 'Ungraded';
|
||||
?>
|
||||
@@ -62,6 +62,7 @@ define("NUMERICAL", "8");
|
||||
define("MULTIANSWER", "9");
|
||||
define("CALCULATED", "10");
|
||||
define("RQP", "11");
|
||||
define("ESSAY", "12");
|
||||
/**#@-*/
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php // $Id$
|
||||
|
||||
if (!empty($question->id)) {
|
||||
$options = get_record("quiz_essay", "question", "$question->id");
|
||||
}
|
||||
|
||||
if (!empty($options->answer)) {
|
||||
$essayfeedback = get_record("quiz_answers", "id", $options->answer);
|
||||
} else {
|
||||
$essayfeedback->feedback = "";
|
||||
}
|
||||
|
||||
print_heading_with_help(get_string("editingessay", "quiz"), "essay", "quiz");
|
||||
require("$CFG->dirroot/mod/quiz/questiontypes/essay/essay.html");
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,95 @@
|
||||
<form name="theform" method="post" action="question.php">
|
||||
<center>
|
||||
<table cellpadding="5">
|
||||
<tr valign="top">
|
||||
<td align="right">
|
||||
<b><?php print_string("category", "quiz") ?>:</b>
|
||||
</td>
|
||||
<td>
|
||||
<?php quiz_category_select_menu($course->id, true, true, $question->category); ?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr valign="top">
|
||||
<td align="right">
|
||||
<b><?php print_string("questionname", "quiz") ?>:</b>
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" name="name" size="50" value="<?php p($question->name) ?>" />
|
||||
<?php if (isset($err["name"])) formerr($err["name"]); ?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr valign="top">
|
||||
<td align="right">
|
||||
<b><?php print_string("question", "quiz") ?>:</b>
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<font size="1">
|
||||
<?php
|
||||
if ($usehtmleditor) {
|
||||
helpbutton("richtext", get_string("helprichtext"), "moodle", true, true);
|
||||
} else {
|
||||
helpbutton("text", get_string("helptext"), "moodle", true, true);
|
||||
}
|
||||
?>
|
||||
</font>
|
||||
</td>
|
||||
<td>
|
||||
<?php
|
||||
if (isset($err["questiontext"])) {
|
||||
formerr($err["questiontext"]);
|
||||
echo "<br />";
|
||||
}
|
||||
|
||||
print_textarea($usehtmleditor, 15, 60, 630, 300, "questiontext", $question->questiontext);
|
||||
|
||||
if ($usehtmleditor) {
|
||||
echo '<input type="hidden" name="questiontextformat" value="'.FORMAT_HTML.'" />';
|
||||
} else {
|
||||
echo "<div align=\"right\">";
|
||||
print_string("formattexttype");
|
||||
echo ": ";
|
||||
if (!isset($question->questiontextformat)) {
|
||||
$question->questiontextformat = FORMAT_MOODLE;
|
||||
}
|
||||
choose_from_menu(format_text_menu(), "questiontextformat", $question->questiontextformat, "");
|
||||
helpbutton("textformat", get_string("helpformatting"));
|
||||
echo "</div>";
|
||||
}
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr valign="top">
|
||||
<td align="right">
|
||||
<b><?php print_string("imagedisplay", "quiz") ?>:</b>
|
||||
</td>
|
||||
<td>
|
||||
<?php
|
||||
if (empty($images)) {
|
||||
print_string("noimagesyet");
|
||||
} else {
|
||||
choose_from_menu($images, "image", "$question->image", get_string("none"),"","");
|
||||
}
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr valign="top">
|
||||
<td align="right">
|
||||
<b><?php print_string("feedback", "quiz") ?>:</b>
|
||||
</td>
|
||||
<td>
|
||||
<textarea name="feedback" rows="2" cols="50" wrap="virtual"><?php p($essayfeedback->feedback) ?></textarea>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<input type="hidden" name="fraction" value="0" /> <?php // dont think there would ever be a different value other than 1... ?>
|
||||
|
||||
<?php
|
||||
$QUIZ_QTYPES[$question->qtype]->print_replacement_options($question, $course, $contextquiz);
|
||||
$QUIZ_QTYPES[$question->qtype]->print_question_form_end($question);
|
||||
?>
|
||||
|
||||
</center>
|
||||
|
||||
</form>
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 170 B |
@@ -0,0 +1,233 @@
|
||||
<?php // $Id$
|
||||
|
||||
//////////////////
|
||||
/// ESSAY ///
|
||||
/////////////////
|
||||
|
||||
/// QUESTION TYPE CLASS //////////////////
|
||||
class quiz_essay_qtype extends quiz_default_questiontype {
|
||||
|
||||
function name() {
|
||||
return 'essay';
|
||||
}
|
||||
|
||||
function get_question_options(&$question) {
|
||||
// Get additional information from database
|
||||
// and attach it to the question object
|
||||
if (!$question->options = get_record('quiz_essay', 'question', $question->id)) {
|
||||
notify('Error: Missing question options!');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$question->options->answers = get_records('quiz_answers', 'question',
|
||||
$question->id)) {
|
||||
notify('Error: Missing question answers!');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function save_question_options($question) {
|
||||
if ($answer = get_record("quiz_answers", "question", $question->id)) {
|
||||
// Existing answer, so reuse it
|
||||
$answer->answer = $question->feedback;
|
||||
$answer->feedback = $question->feedback;
|
||||
$answer->fraction = $question->fraction;
|
||||
if (!update_record("quiz_answers", $answer)) {
|
||||
$result->error = "Could not update quiz answer!";
|
||||
return $result;
|
||||
}
|
||||
} else {
|
||||
unset($answer);
|
||||
$answer->question = $question->id;
|
||||
$answer->answer = $question->feedback;
|
||||
$answer->feedback = $question->feedback;
|
||||
$answer->fraction = $question->fraction;
|
||||
if (!$answer->id = insert_record("quiz_answers", $answer)) {
|
||||
$result->error = "Could not insert quiz answer!";
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
if ($options = get_record("quiz_essay", "question", $question->id)) {
|
||||
// No need to do anything, since the answer IDs won't have changed
|
||||
// But we'll do it anyway, just for robustness
|
||||
$options->answer = $answer->id;
|
||||
if (!update_record("quiz_essay", $options)) {
|
||||
$result->error = "Could not update quiz essay options! (id=$options->id)";
|
||||
return $result;
|
||||
}
|
||||
} else {
|
||||
unset($options);
|
||||
$options->question = $question->id;
|
||||
$options->answer = $answer->id;
|
||||
if (!insert_record("quiz_essay", $options)) {
|
||||
$result->error = "Could not insert quiz essay options!";
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function print_question_formulation_and_controls(&$question, &$state, $quiz, $options) {
|
||||
|
||||
$answers = &$question->options->answers;
|
||||
//$correctanswers = $this->get_correct_responses($question, $state); // no correct answers ;)
|
||||
$readonly = empty($options->readonly) ? '' : 'disabled="disabled"';
|
||||
$nameprefix = $question->name_prefix;
|
||||
|
||||
/// Print question text and media
|
||||
echo format_text($question->questiontext,
|
||||
$question->questiontextformat,
|
||||
NULL, $quiz->course);
|
||||
|
||||
quiz_print_possible_question_image($quiz->id, $question);
|
||||
|
||||
/// Print input controls
|
||||
$stranswer = get_string("answer", "quiz");
|
||||
$strcomment = get_string("comments", "quiz");
|
||||
$usehtmleditor = can_use_html_editor();
|
||||
|
||||
// this prints out the student response box
|
||||
if (isset($state->responses[''])) {
|
||||
// security problem. responses[''] is never cleaned before it is sent to the db (I think)
|
||||
$value = clean_param($state->responses[''], PARAM_CLEANHTML);
|
||||
} else {
|
||||
$value = "";
|
||||
}
|
||||
|
||||
$inputname = $nameprefix;
|
||||
|
||||
echo "<p>$stranswer: ".
|
||||
'<div style="padding-left: 30px;">';
|
||||
if (empty($options->readonly)) {
|
||||
// the student needs to type in their answer so print out a text editor
|
||||
print_textarea($usehtmleditor, 18, 80, 630, 400, $inputname, $value);
|
||||
use_html_editor($inputname);
|
||||
} else {
|
||||
// it is read only, so just format the students answer and output it
|
||||
echo format_text($value);
|
||||
echo '<input type="hidden" name="'.$inputname.'" value="'.htmlSpecialChars($value).'">'; // need hidden one for grading
|
||||
}
|
||||
echo '</div></p>';
|
||||
|
||||
if (isset($state->responses['response'])) {
|
||||
$value = $state->responses['response'];
|
||||
} else {
|
||||
$value = "";
|
||||
}
|
||||
|
||||
$inputname = $nameprefix.'response';
|
||||
if(!empty($options->regrade)) { // special option set in regrade.php. This means we want to grade the essay question
|
||||
// print out a field for the teacher to add a comment
|
||||
echo "<p>$strcomment: ".
|
||||
'<div style="padding-left: 30px;">';
|
||||
print_textarea($usehtmleditor, 18, 80, 630, 400, $inputname, $value);
|
||||
use_html_editor($inputname);
|
||||
echo '</div></p><br />';
|
||||
|
||||
// dropdown for score... for the fraction
|
||||
$grades = array(1,0.9,0.8,0.75,0.70,0.66666,0.60,0.50,0.40,0.33333,0.30,0.25,0.20,0.16666,0.142857,0.10,0.05,0);
|
||||
foreach ($grades as $grade) {
|
||||
$percentage = 100 * $grade;
|
||||
$neggrade = -$grade;
|
||||
$gradeoptions["$grade"] = "$percentage %";
|
||||
$gradeoptionsfull["$grade"] = "$percentage %";
|
||||
$gradeoptionsfull["$neggrade"] = -$percentage." %";
|
||||
}
|
||||
$gradeoptionsfull["0"] = $gradeoptions["0"] = get_string("none");
|
||||
print_string("grade");
|
||||
echo ": ";
|
||||
choose_from_menu($gradeoptions, $nameprefix."fraction", $state->responses['fraction'],"");
|
||||
} else if (!empty($options->readonly)) {
|
||||
//read only so format the comment and print it out
|
||||
echo "<p>$strcomment: ".
|
||||
'<div style="padding-left: 30px;">';
|
||||
if (empty($value)) { // no comment yet
|
||||
echo format_text(get_string('nocommentsyet', 'quiz'));
|
||||
} else {
|
||||
echo format_text($value);
|
||||
}
|
||||
echo '</div></p>';
|
||||
}
|
||||
|
||||
// feedback
|
||||
if ($options->feedback) {
|
||||
foreach ($answers as $answer) {
|
||||
quiz_print_comment("<p align=\"right\">$answer->feedback</p>");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function grade_responses(&$question, &$state, $quiz) {
|
||||
$state->raw_grade = 0;
|
||||
// if a fraction is submitted, then we use it, otherwise the raw grade is 0
|
||||
if (isset($state->responses['fraction'])) {
|
||||
$state->raw_grade = $state->responses['fraction'];
|
||||
$state->options->graded = 1;
|
||||
}
|
||||
if (empty($state->raw_grade)) {
|
||||
$state->raw_grade = 0;
|
||||
}
|
||||
|
||||
// Make sure we don't assign negative or too high marks
|
||||
$state->raw_grade = min(max((float) $state->raw_grade,
|
||||
0.0), 1.0) * $question->maxgrade;
|
||||
$state->penalty = $question->penalty * $question->maxgrade; // should be no penalty for essays
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function create_session_and_responses(&$question, &$state, $quiz, $attempt) {
|
||||
$state->options->graded = 0; // our flag to indicated wheather an essay has been graded or not
|
||||
$state->responses['fraction'] = 0; // this fraction is used for grading. The teacher sets it while grading
|
||||
$state->responses['response'] = ''; // this is teacher response to the students essay
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function restore_session_and_responses(&$question, &$state) {
|
||||
if (!$options = get_record('quiz_essay_states', 'stateid', $state->id)) {
|
||||
return false;
|
||||
}
|
||||
$state->options->graded = $options->graded;
|
||||
$state->responses['fraction'] = $options->fraction;
|
||||
$state->responses['response'] = $options->response;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function save_session_and_responses(&$question, &$state) {
|
||||
$options->stateid = $state->id;
|
||||
$options->graded = $state->options->graded;
|
||||
|
||||
if (isset($state->responses['fraction'])) {
|
||||
$options->fraction = $state->responses['fraction'];
|
||||
}
|
||||
if (isset($state->responses['response'])) {
|
||||
$options->response = addslashes( clean_param($state->responses['response'], PARAM_CLEANHTML) );
|
||||
}
|
||||
if (isset($state->update)) {
|
||||
if (!$options->id = get_field('quiz_essay_states', 'id', 'stateid', $state->id)) {
|
||||
return false;
|
||||
}
|
||||
if (!update_record('quiz_essay_states', $options)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!insert_record('quiz_essay_states', $options)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
//// END OF CLASS ////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//// INITIATION - Without this line the question type is not in use... ///
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
$QUIZ_QTYPES[ESSAY] = new quiz_essay_qtype();
|
||||
|
||||
?>
|
||||
@@ -54,4 +54,54 @@ body#mod-quiz-report table#itemanalysis .qname {
|
||||
color: green !important;
|
||||
}
|
||||
|
||||
/* manual grading */
|
||||
body#mod-quiz-grading table#grading
|
||||
{
|
||||
width: 80%;
|
||||
margin: auto;
|
||||
}
|
||||
body#mod-quiz-grading table#grading
|
||||
{
|
||||
margin: 20px auto;
|
||||
}
|
||||
body#mod-quiz-grading table#grading .header,
|
||||
body#mod-quiz-grading table#grading .cell
|
||||
{
|
||||
padding: 4px;
|
||||
}
|
||||
body#mod-quiz-grading table#grading .header .commands
|
||||
{
|
||||
display: inline;
|
||||
}
|
||||
body#mod-quiz-grading table#grading .picture
|
||||
{
|
||||
width: 40px;
|
||||
}
|
||||
body#mod-quiz-grading table#grading td
|
||||
{
|
||||
border-left-width: 1px;
|
||||
border-right-width: 1px;
|
||||
border-left-style: solid;
|
||||
border-right-style: solid;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
|
||||
body#mod-quiz-grading table#grading .header {
|
||||
text-align: left;
|
||||
}
|
||||
body#mod-quiz-grading table#grading .picture {
|
||||
text-align: center !important;
|
||||
}
|
||||
body#mod-quiz-grading .controls {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
body#mod-quiz-grading table#grading td {
|
||||
border-color: #dddddd;
|
||||
}
|
||||
body#mod-quiz-grading table#grading .r1 {
|
||||
background-color: #eeeeee;
|
||||
}
|
||||
/* grading */
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
$row[] = new tabobject('preview', "attempt.php?q=$quiz->id", get_string('preview', 'quiz'));
|
||||
if (isteacheredit($course->id)) {
|
||||
$row[] = new tabobject('edit', "edit.php?quizid=$quiz->id", get_string('editquiz', 'quiz'));
|
||||
$row[] = new tabobject('manualgrading', "grading.php?quizid=$quiz->id", get_string("manualgrading", "quiz"));
|
||||
//$row[] = new tabobject('update', "$CFG->wwwroot/course/mod.php?update=$cm->id&sesskey=$USER->sesskey", get_string('updatethis', '', get_string('modulename', 'quiz')));
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
// This fragment is called by moodle_needs_upgrading() and /admin/index.php
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
$module->version = 2005060301; // The (date) version of this module
|
||||
$module->version = 2005062600; // The (date) version of this module
|
||||
$module->requires = 2005021600; // Requires this Moodle version
|
||||
$module->cron = 0; // How often should cron check this module (seconds)?
|
||||
|
||||
|
||||
Reference in New Issue
Block a user