From 31e95855b85d551f249a2d7bed0d5479e159d3b3 Mon Sep 17 00:00:00 2001 From: gustav_delius Date: Mon, 27 Mar 2006 08:41:20 +0000 Subject: [PATCH] Improved quiz report plugin mechanism and converted the manual grading script into a report plugin --- lang/en_utf8/quiz.php | 11 + lang/en_utf8/quiz_analysis.php | 2 +- lang/en_utf8/quiz_grading.php | 5 + lang/en_utf8/quiz_overview.php | 5 + lang/en_utf8/quiz_regrade.php | 5 + lang/en_utf8/quiz_responses.php | 1 + mod/quiz/grading.php | 414 ---------------------------- mod/quiz/report/analysis/report.php | 5 +- mod/quiz/report/grading/report.php | 388 ++++++++++++++++++++++++++ mod/quiz/report/overview/report.php | 8 +- mod/quiz/tabs.php | 4 +- 11 files changed, 423 insertions(+), 425 deletions(-) create mode 100644 lang/en_utf8/quiz_grading.php create mode 100644 lang/en_utf8/quiz_overview.php create mode 100644 lang/en_utf8/quiz_regrade.php delete mode 100644 mod/quiz/grading.php create mode 100644 mod/quiz/report/grading/report.php diff --git a/lang/en_utf8/quiz.php b/lang/en_utf8/quiz.php index f18807c0ed..5c564a976c 100644 --- a/lang/en_utf8/quiz.php +++ b/lang/en_utf8/quiz.php @@ -493,5 +493,16 @@ $string['xmltypeunsupported'] = 'Question type $a is not supported by xml import $string['yourfinalgradeis'] = 'Your final grade for this quiz is $a'; $string['zerosignificantfiguresnotallowed'] = 'The correct answer cannot have zero significant figures!'; +$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['nocommentsyet'] = 'No comments yet.'; +$string['noessayquestionsfound'] = 'No Essay Questions Found'; +$string['ungraded'] = 'Ungraded'; ?> diff --git a/lang/en_utf8/quiz_analysis.php b/lang/en_utf8/quiz_analysis.php index 161db3aea7..48654547a8 100644 --- a/lang/en_utf8/quiz_analysis.php +++ b/lang/en_utf8/quiz_analysis.php @@ -1,6 +1,6 @@ diff --git a/lang/en_utf8/quiz_overview.php b/lang/en_utf8/quiz_overview.php new file mode 100644 index 0000000000..04373eea48 --- /dev/null +++ b/lang/en_utf8/quiz_overview.php @@ -0,0 +1,5 @@ + diff --git a/lang/en_utf8/quiz_regrade.php b/lang/en_utf8/quiz_regrade.php new file mode 100644 index 0000000000..6df6ab9f70 --- /dev/null +++ b/lang/en_utf8/quiz_regrade.php @@ -0,0 +1,5 @@ + diff --git a/lang/en_utf8/quiz_responses.php b/lang/en_utf8/quiz_responses.php index 79fd146781..bf732d11ee 100644 --- a/lang/en_utf8/quiz_responses.php +++ b/lang/en_utf8/quiz_responses.php @@ -4,4 +4,5 @@ $string['responsesoptions'] = 'Responses options'; $string['responsestitle'] = 'Detailed responses'; $string['pagesize'] = 'Attempts per page: '; $string['reportresponses'] = 'Detailed responses'; +$string['responses'] = 'Detailed responses'; ?> diff --git a/mod/quiz/grading.php b/mod/quiz/grading.php deleted file mode 100644 index d709b881ad..0000000000 --- a/mod/quiz/grading.php +++ /dev/null @@ -1,414 +0,0 @@ -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('question', '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!"); - } - - // if no questions have been set up yet redirect to edit.php - if (!$quiz->questions) { - redirect('edit.php?quizid='.$quiz->id); - } - - 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", "", - "id\">$strquizzes - -> id\">$quiz->name -> $strmanualgrading", - "", "", true); - - - $currenttab = 'manualgrading'; - include('tabs.php'); - echo ''; // 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); - $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}question_states s,". - " {$CFG->prefix}question_sessions n". - " WHERE s.id = n.newest". - " AND n.attemptid = '$attempt->uniqueid'". - " AND n.questionid = $question->id"; - $state = get_record_sql($sql); - - // restore the state of the question - restore_question_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 - $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 - question_apply_penalty_and_timelimit($question, $state, $attempt, $quiz); - - // want to update session. Also set changed to 1 to trick save_question_session to save our session - $state->update = 1; - $state->changed = 1; - 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('question_sessions', 'newgraded', 'attemptid', $attempt->uniqueid, 'questionid', $questionid); - $sumgrades += get_field('question_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); - } - notify(get_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 userattemptid, - qa.id AS attemptid, qa.uniqueid, qa.attempt, qa.timefinish, qa.preview, - u.id AS userid, u.firstname, u.lastname, u.picture '; - $from = 'FROM '.$CFG->prefix.'user u LEFT JOIN '.$CFG->prefix.'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 '
'. - ''. - ''. - ''. - ''; - - foreach ($attempts as $attempt) { - // retrieve the state - if (!$neweststate = get_record('question_sessions', 'attemptid', $attempt->uniqueid, 'questionid', $questionid)) { - error("Can not find newest states for attempt $attempt->uniqueid for question $questionid"); - } - if (! $state = get_record('question_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.'_'; - $QTYPES[$question->qtype]->get_question_options($question); - - restore_question_state($question, $state); - $state->last_graded = $state; - - $options = quiz_get_reviewoptions($quiz, $attempt, $isteacher); - $options->validation = ($state->event == QUESTION_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 '
'. - '

'."$attempt->firstname $attempt->lastname: ". - get_string('attempt', 'quiz')." $attempt->attempt". - '

'; - - print_question($question, $state, '', $quiz, $options); - echo ''. - ''; - echo '
'; - } - echo '
'. - '
'; - 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}question 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 (empty($questionlist) or !$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 = "id&action=viewquestion&questionid=$question->id\">". - $question->name.""; - // 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('question_sessions', 'attemptid', $attempt->uniqueid, 'questionid', $question->id)) { - error("Can not find newest states for attempt $attempt->uniqueid for question $question->id"); - } - if (!$questionstate = get_record('question_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 userattemptid, qa.id AS attemptid, qa.uniqueid, qa.attempt, u.id AS userid, u.firstname, u.lastname, u.picture '; - $from = 'FROM '.$CFG->prefix.'user u LEFT JOIN '.$CFG->prefix.'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 = "id&questionid=$question->id&userid=$attempt->userid\">". - $attempt->firstname.' '.$attempt->lastname.''; - - // nab the state of the attempt to see if it is graded or not - if (!$neweststate = get_record('question_sessions', 'attemptid', $attempt->uniqueid, 'questionid', $question->id)) { - error("Can not find newest states for attempt $attempt->uniqueid for question $questionid"); - } - - if (!$questionstate = get_record('question_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 = "id&questionid=$question->id&attemptid=$attempt->attemptid\">". // &number=$number - $question->name." attempt $attempt->attempt"; - - $table->add_data( array($picture, $userlink, $attemptlink) ); - } - //$number += $question->length; - } - - // grade all and "back" links - $links = "
id&questionid=$questionid&gradeall=1\">".get_string('gradeall', 'quiz').' | '. - "id&action=viewquestions\">".get_string('backtoquestionlist', 'quiz').'
'. - - // print everything here - print_heading($question->name); - echo $links; - echo '
'; - $table->print_html(); - echo '
'; - echo $links; - break; - default: - error("Invalid Action"); - } - - print_footer($course); - -?> diff --git a/mod/quiz/report/analysis/report.php b/mod/quiz/report/analysis/report.php index 295ded257f..a4a4cc8835 100644 --- a/mod/quiz/report/analysis/report.php +++ b/mod/quiz/report/analysis/report.php @@ -348,10 +348,7 @@ class quiz_report extends quiz_default_report { } } - echo '
'; - echo get_string("analysistitle", "quiz_analysis"); - helpbutton("itemanalysis", get_string("reportanalysis","quiz_analysis"), "quiz"); - echo '
'; + print_heading_with_help(get_string("analysistitle", "quiz_analysis"),"itemanalysis", "quiz"); echo '
'; $table->print_html(); diff --git a/mod/quiz/report/grading/report.php b/mod/quiz/report/grading/report.php new file mode 100644 index 0000000000..6f68767579 --- /dev/null +++ b/mod/quiz/report/grading/report.php @@ -0,0 +1,388 @@ +libdir.'/tablelib.php'); + +class quiz_report extends quiz_default_report { + + function display($quiz, $cm, $course) { /// This function just displays the report + global $CFG, $SESSION, $USER, $db, $QTYPES; + + $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); + + $this->print_header_and_tabs($cm, $course, $quiz, $reportmode="grading"); + + if (!empty($questionid)) { + if (! $question = get_record('question', 'id', $questionid)) { + error("Question with id $questionid not found"); + } + ///$number = optional_param('number', 0, PARAM_INT); + } + + add_to_log($course->id, "quiz", "manualgrading", "report.php?mode=grading&q=$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)); + } + + echo ''; // 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); + $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}question_states s,". + " {$CFG->prefix}question_sessions n". + " WHERE s.id = n.newest". + " AND n.attemptid = '$attempt->uniqueid'". + " AND n.questionid = $question->id"; + $state = get_record_sql($sql); + + // restore the state of the question + restore_question_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 + $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 + question_apply_penalty_and_timelimit($question, $state, $attempt, $quiz); + + // want to update session. Also set changed to 1 to trick save_question_session to save our session + $state->update = 1; + $state->changed = 1; + 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('question_sessions', 'newgraded', 'attemptid', $attempt->uniqueid, 'questionid', $questionid); + $sumgrades += get_field('question_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); + } + notify(get_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 userattemptid, + qa.id AS attemptid, qa.uniqueid, qa.attempt, qa.timefinish, qa.preview, + u.id AS userid, u.firstname, u.lastname, u.picture '; + $from = 'FROM '.$CFG->prefix.'user u LEFT JOIN '.$CFG->prefix.'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 '
'. + ''. + ''. + ''. + ''. + ''; + + foreach ($attempts as $attempt) { + // retrieve the state + if (!$neweststate = get_record('question_sessions', 'attemptid', $attempt->uniqueid, 'questionid', $questionid)) { + error("Can not find newest states for attempt $attempt->uniqueid for question $questionid"); + } + if (! $state = get_record('question_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.'_'; + $QTYPES[$question->qtype]->get_question_options($question); + + restore_question_state($question, $state); + $state->last_graded = $state; + + $options = quiz_get_reviewoptions($quiz, $attempt, isteacher()); + $options->validation = ($state->event == QUESTION_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 '
'. + '

'."$attempt->firstname $attempt->lastname: ". + get_string('attempt', 'quiz')." $attempt->attempt". + '

'; + + print_question($question, $state, '', $quiz, $options); + echo ''. + ''; + echo '
'; + } + echo '
'. + '
'; + 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_grading')); + // 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}question 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 (empty($questionlist) or !$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 = "id&action=viewquestion&questionid=$question->id\">". + $question->name.""; + // 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('question_sessions', 'attemptid', $attempt->uniqueid, 'questionid', $question->id)) { + error("Can not find newest states for attempt $attempt->uniqueid for question $question->id"); + } + if (!$questionstate = get_record('question_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/report.php?mode=grading&q='.$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 userattemptid, qa.id AS attemptid, qa.uniqueid, qa.attempt, u.id AS userid, u.firstname, u.lastname, u.picture '; + $from = 'FROM '.$CFG->prefix.'user u LEFT JOIN '.$CFG->prefix.'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 = "id&questionid=$question->id&userid=$attempt->userid\">". + $attempt->firstname.' '.$attempt->lastname.''; + + // nab the state of the attempt to see if it is graded or not + if (!$neweststate = get_record('question_sessions', 'attemptid', $attempt->uniqueid, 'questionid', $question->id)) { + error("Can not find newest states for attempt $attempt->uniqueid for question $questionid"); + } + + if (!$questionstate = get_record('question_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 = "id&questionid=$question->id&attemptid=$attempt->attemptid\">". // &number=$number + $question->name." attempt $attempt->attempt"; + + $table->add_data( array($picture, $userlink, $attemptlink) ); + } + //$number += $question->length; + } + + // grade all and "back" links + $links = "
id&questionid=$questionid&gradeall=1\">".get_string('gradeall', 'quiz').' | '. + "id&action=viewquestions\">".get_string('backtoquestionlist', 'quiz').'
'. + + // print everything here + print_heading($question->name); + echo $links; + echo '
'; + $table->print_html(); + echo '
'; + echo $links; + break; + default: + error("Invalid Action"); + } + return true; + } + +} + +?> diff --git a/mod/quiz/report/overview/report.php b/mod/quiz/report/overview/report.php index 5ba029faa5..9f03e554a9 100644 --- a/mod/quiz/report/overview/report.php +++ b/mod/quiz/report/overview/report.php @@ -245,13 +245,13 @@ class quiz_report extends quiz_default_report { // So join on groups_members and do an inner join on attempts. $from = 'FROM '.$CFG->prefix.'user u JOIN '.$CFG->prefix.'user_students us ON us.userid = u.id JOIN '.$CFG->prefix.'groups_members gm ON u.id = gm.userid '. 'JOIN '.$CFG->prefix.'quiz_attempts qa ON u.id = qa.userid AND qa.quiz = '.$quiz->id; - $where = ' WHERE us.course = '.$course->id.' AND gm.groupid = '.$currentgroup; + $where = ' WHERE us.course = '.$course->id.' AND gm.groupid = '.$currentgroup.' AND qa.preview = 0'; } else if (!empty($currentgroup) && !empty($noattempts)) { // We want a particular group and we want to do something funky with attempts // So join on groups_members and left join on attempts... $from = 'FROM '.$CFG->prefix.'user u JOIN '.$CFG->prefix.'user_students us ON us.userid = u.id JOIN '.$CFG->prefix.'groups_members gm ON u.id = gm.userid '. 'LEFT JOIN '.$CFG->prefix.'quiz_attempts qa ON u.id = qa.userid AND qa.quiz = '.$quiz->id; - $where = ' WHERE us.course = '.$course->id.' AND gm.groupid = '.$currentgroup; + $where = ' WHERE us.course = '.$course->id.' AND gm.groupid = '.$currentgroup.' AND qa.preview = 0'; if ($noattempts == 1) { // noattempts = 1 means only no attempts, so make the left join ask for only records where the right is null (no attempts) $where .= ' AND qa.userid IS NULL'; // show ONLY no attempts; @@ -260,7 +260,7 @@ class quiz_report extends quiz_default_report { // We don't care about group, and we only want to see students WITH attempts. // So just do a striaght inner join on attempts, don't worry about the groups_members table $from = 'FROM '.$CFG->prefix.'user u JOIN '.$CFG->prefix.'quiz_attempts qa ON u.id = qa.userid '; - $where = ' WHERE qa.quiz = '.$quiz->id; + $where = ' WHERE qa.quiz = '.$quiz->id.' AND qa.preview = 0'; } else if (empty($currentgroup) && !empty($noattempts)) { // We don't care about group, and we to do something funky with attempts // So do a left join on attempts @@ -275,7 +275,7 @@ class quiz_report extends quiz_default_report { } else { if (empty($noattempts)) { $from = 'FROM '.$CFG->prefix.'user u JOIN '.$CFG->prefix.'quiz_attempts qa ON u.id = qa.userid '; - $where = ' WHERE qa.quiz = '.$quiz->id; + $where = ' WHERE qa.quiz = '.$quiz->id.' AND qa.preview = 0'; $countsql = 'SELECT COUNT(DISTINCT('.$db->Concat('u.id', '\'#\'', $db->IfNull('qa.attempt', '0')).')) '.$from.$where; } } diff --git a/mod/quiz/tabs.php b/mod/quiz/tabs.php index e8768a960b..502a5c4727 100644 --- a/mod/quiz/tabs.php +++ b/mod/quiz/tabs.php @@ -41,7 +41,7 @@ if ($currenttab == 'reports' and isset($mode)) { $inactive[] = 'reports'; $allreports = get_list_of_plugins("mod/quiz/report"); - $reportlist = array ('overview', 'regrade', 'analysis'); // Standard reports we want to show first + $reportlist = array ('overview', 'regrade', 'grading', 'analysis'); // Standard reports we want to show first foreach ($allreports as $report) { if (!in_array($report, $reportlist)) { @@ -53,7 +53,7 @@ $currenttab = ''; foreach ($reportlist as $report) { $row[] = new tabobject($report, "$CFG->wwwroot/mod/quiz/report.php?q=$quiz->id&mode=$report", - get_string("report$report", "quiz")); + get_string($report, 'quiz_'.$report)); if ($report == $mode) { $currenttab = $report; } -- 2.39.5