// Initialize a page scanner. $scanner = new offlinequiz_page_scanner($offlinequiz, $context->id, $maxquestions, $maxanswers); // Load the stored image file. if (property_exists($scannedpage, 'id')) { // If we re-adjust, rotate, or changed the user we have to delete the stored hotspots. if ($action == 'readjust' || $action == 'rotate' || $action == 'checkuser') { $DB->delete_records('offlinequiz_hotspots', array('scannedpageid' => $scannedpage->id)); } // Load the stored image and the hotspots from the DB if they have not been deleted. $sheetloaded = $scanner->load_stored_image($scannedpage->filename, $corners, $scannedpage->id); } else { // Load the stored image and adjust the hotspots from scratch. $sheetloaded = $scanner->load_stored_image($scannedpage->filename, $corners); } // Make a first check. if (!$scanner->check_deleted()) { $scannedpage->status = 'error'; $scannedpage->error = 'notadjusted'; } // O=======================================. // O Step 1. Get the data from the stored scanned page. // O=======================================. if ($action == 'load') { $filename = $scannedpage->filename; $userkey = $scannedpage->userkey; $usernumber = substr($userkey, strlen($offlinequizconfig->ID_prefix), $offlinequizconfig->ID_digits); $groupnumber = intval($scannedpage->groupnumber); $pagenumber = intval($scannedpage->pagenumber); // Remember initial data for cancel action. $origfilename = $filename; $origuserkey = $userkey;
$offlinequiz->groupid = -$group->id; list($maxquestions, $maxanswers, $formtype, $questionsperpage) = offlinequiz_get_question_numbers($offlinequiz, array($group)); $offlinequizconfig->papergray = $offlinequiz->papergray; // Load corners from DB. $dbcorners = $DB->get_records('offlinequiz_page_corners', array('scannedpageid' => $scannedpage->id)); $corners = array(); foreach ($dbcorners as $corner) { $corners[] = new oq_point($corner->x, $corner->y); } // Initialize a page scanner. $scanner = new offlinequiz_page_scanner($offlinequiz, $context->id, $maxquestions, $maxanswers); // Load the stored picture file. $sheetloaded = $scanner->load_stored_image($scannedpage->filename, $corners); $pagenumber = $scannedpage->pagenumber; // Make a first check. $scanner->check_deleted(); $scanner->calibrate_and_get_group(); $scanner->get_usernumber(); $scanner->get_page(); // Necessary s.t. we can get the answer hotspots from the scanner. $scanner->set_page($pagenumber); $quba = question_engine::load_questions_usage_by_activity($result->usageid); $slots = $quba->get_slots(); // Determine the slice of slots we are interested in. // We start at the top of the page (e.g. 0, 96, etc). $startindex = min(($pagenumber - 1) * $questionsperpage, count($slots)); // We end on the bottom of the page or when the questions are gone (e.g., 95, 105). $endindex = min($pagenumber * $questionsperpage, count($slots)); // Load the choices made before from the database. There might not be any. $choices = $DB->get_records('offlinequiz_choices', array('scannedpageid' => $scannedpage->id), 'slotnumber, choicenumber'); // Choicesdata contains the choices data from the DB indexed by slotnumber and choicenumber.
/** * Checks groupnumber, userkey, and pagenumber of a scanned answer form * * @param unknown_type $offlinequiz * @param offlinequiz_page_scanner $scanner * @param unknown_type $scannedpage * @param unknown_type $teacherid * @param unknown_type $coursecontext */ function offlinequiz_check_scanned_page($offlinequiz, offlinequiz_page_scanner $scanner, $scannedpage, $teacherid, $coursecontext, $autorotate = false, $recheckresult = false, $ignoremaxanswers = false) { global $DB, $CFG; $offlinequizconfig = get_config('offlinequiz'); if (!$scanner->check_deleted()) { $scannedpage->status = 'error'; $scannedpage->error = 'notadjusted'; } if ($scannedpage->status == 'error' && $scanner->ontop && $autorotate) { echo 'rotating...' . "\n"; $oldfilename = $scannedpage->filename; if ($newfile = $scanner->rotate_180()) { $scannedpage->status = 'ok'; $scannedpage->error = ''; $scannedpage->userkey = null; $scannedpage->pagenumber = null; $scannedpage->groupnumber = null; $scannedpage->filename = $newfile->get_filename(); $corners = $scanner->get_corners(); $newcorners = array(); // Create a completely new scanner. $scanner = new offlinequiz_page_scanner($offlinequiz, $scanner->contextid, $scanner->maxquestions, $scanner->maxanswers); $sheetloaded = $scanner->load_stored_image($scannedpage->filename, $newcorners); if (!$sheetloaded) { $scannedpage->status = 'error'; $scannedpage->error = 'fatalerror'; } else { if (!$scanner->check_deleted()) { $scannedpage->status = 'error'; $scannedpage->error = 'notadjusted'; } else { $scannedpage->status = 'ok'; $scannedpage->error = ''; } } } } // Check the group number. $groupnumber = $scanner->calibrate_and_get_group(); // Call group first for callibration, such a crap! if (!property_exists($scannedpage, 'groupnumber') || $scannedpage->groupnumber == 0) { $scannedpage->groupnumber = $groupnumber; } $group = null; if ($scannedpage->status == 'ok' || $scannedpage->status == 'suspended') { if (!($group = $DB->get_record('offlinequiz_groups', array('offlinequizid' => $offlinequiz->id, 'number' => $scannedpage->groupnumber)))) { $scannedpage->status = 'error'; $scannedpage->error = 'grouperror'; } } // Adjust the maxanswers of the scanner according to the offlinequiz group deterimined above. if ($group && !$ignoremaxanswers) { $maxanswers = offlinequiz_get_maxanswers($offlinequiz, array($group)); if ($maxanswers != $scanner->maxanswers) { // Create a completely new scanner. $corners = $scanner->get_corners(); $scanner = new offlinequiz_page_scanner($offlinequiz, $scanner->contextid, $scanner->maxquestions, $maxanswers); $sheetloaded = $scanner->load_stored_image($scannedpage->filename, $corners); // Recursively call this method this time ignoring the maxanswers change. return offlinequiz_check_scanned_page($offlinequiz, $scanner, $scannedpage, $teacherid, $coursecontext, $autorotate, $recheckresult, true); } } // Check the user key (username, or userid, or other). $usernumber = $scanner->get_usernumber(); if (empty($scannedpage->userkey)) { $scannedpage->userkey = $offlinequizconfig->ID_prefix . $usernumber . $offlinequizconfig->ID_postfix; } if ($scannedpage->status == 'ok' || $scannedpage->status == 'suspended') { if (!($user = $DB->get_record('user', array($offlinequizconfig->ID_field => $scannedpage->userkey)))) { $scannedpage->status = 'error'; $scannedpage->error = 'nonexistinguser'; } else { $coursestudents = get_enrolled_users($coursecontext, 'mod/offlinequiz:attempt'); if (empty($coursestudents[$user->id])) { $scannedpage->status = 'error'; $scannedpage->error = 'usernotincourse'; } } } // Check the pagenumber. // Patch for old answer forms that did not have the page barcode. // With this patch the old forms can be uploaded in Moodle 2.x anyway. $pagenumber = $scanner->get_page(); if ($group && $group->numberofpages == 1) { $scannedpage->pagenumber = 1; $scanner->set_page(1); $page = 1; } else { if (!property_exists($scannedpage, 'pagenumber') || $scannedpage->pagenumber == 0 || $scannedpage->pagenumber == null) { $scannedpage->pagenumber = $pagenumber; } else { // This is neede because otherwise the scanner doesn't return answers (questionsonpage not set). $scanner->set_page($scannedpage->pagenumber); } $page = $scannedpage->pagenumber; if ($scannedpage->status == 'ok' || $scannedpage->status == 'suspended') { if ($page < 1 || $page > $group->numberofpages) { $scannedpage->status = 'error'; $scannedpage->error = 'invalidpagenumber'; } } } // If we have a valid userkey, a group and a page number then we can // check whether there is already a scanned page or even a completed result with the same group, userid, etc. if (($scannedpage->status == 'ok' || $scannedpage->status == 'suspended') && $user && $group && $page) { $resultexists = false; if (!property_exists($scannedpage, 'resultid') || !$scannedpage->resultid || $recheckresult) { $sql = "SELECT id\n FROM {offlinequiz_results}\n WHERE offlinequizid = :offlinequizid\n AND userid = :userid\n AND status = 'complete'"; $params = array('offlinequizid' => $offlinequiz->id, 'offlinegroupid' => $group->id, 'userid' => $user->id); if ($DB->get_record_sql($sql, $params)) { $resultexists = true; } } if ($resultexists) { $scannedpage->status = 'error'; $scannedpage->error = 'resultexists'; } else { if (!property_exists($scannedpage, 'id') || !$scannedpage->id) { $otherpages = $DB->get_records('offlinequiz_scanned_pages', array('offlinequizid' => $offlinequiz->id, 'userkey' => $user->{$offlinequizconfig->ID_field}, 'groupnumber' => $group->number, 'pagenumber' => $page)); } else { $sql = "SELECT id\n FROM {offlinequiz_scanned_pages}\n WHERE offlinequizid = :offlinequizid\n AND userkey = :userkey\n AND groupnumber = :groupnumber\n AND pagenumber = :pagenumber\n AND (status = 'ok' OR status = 'submitted')\n AND id <> :id"; $params = array('offlinequizid' => $offlinequiz->id, 'userkey' => $user->{$offlinequizconfig->ID_field}, 'groupnumber' => $group->number, 'pagenumber' => $page, 'id' => $scannedpage->id); $otherpages = $DB->get_records_sql($sql, $params); } if ($otherpages) { $scannedpage->status = 'error'; $scannedpage->error = 'doublepage'; } } } // Still everything OK, so we have a user and a group. Thus we can get/create the associated result // we also do that if another result exists, s.t. we have the answers later. if (empty($scannedpage->resultid)) { if ($scannedpage->status == 'ok' || $scannedpage->status == 'suspended' || $scannedpage->status == 'error' && $scannedpage->error == 'resultexists' || $scannedpage->status == 'error' && $scannedpage->error == 'usernotincourse') { // We have a group and a userid, so we can check if there is a matching partial result in the offlinequiz_results table. // The problem with this is that we could have several partial results with several pages. $sql = "SELECT *\n FROM {offlinequiz_results}\n WHERE offlinequizid = :offlinequizid\n AND offlinegroupid = :offlinegroupid\n AND userid = :userid\n AND status = 'partial'\n ORDER BY id ASC"; $params = array('offlinequizid' => $offlinequiz->id, 'offlinegroupid' => $group->id, 'userid' => $user->id); if (!($result = $DB->get_record_sql($sql, $params))) { // There is no result. First we have to clone the template question usage of the offline group. // We have to use our own loading function in order to get the right class. $templateusage = offlinequiz_load_questions_usage_by_activity($group->templateusageid); // Get the question instances for initial maxmarks. $sql = "SELECT questionid, maxmark\n FROM {offlinequiz_group_questions}\n WHERE offlinequizid = :offlinequizid\n AND offlinegroupid = :offlinegroupid"; $qinstances = $DB->get_records_sql($sql, array('offlinequizid' => $offlinequiz->id, 'offlinegroupid' => $group->id)); // Clone it... $quba = $templateusage->get_clone($qinstances); // And save it. The clone contains the same question in the same order and the same order of the answers. question_engine::save_questions_usage_by_activity($quba); $result = new stdClass(); $result->offlinequizid = $offlinequiz->id; $result->offlinegroupid = $group->id; $result->userid = $user->id; $result->teacherid = $teacherid; $result->usageid = $quba->get_id(); $result->attendant = 'scanonly'; $result->status = 'partial'; $result->timecreated = time(); $result->timemodified = time(); $newid = $DB->insert_record('offlinequiz_results', $result); if ($newid) { $result->id = $newid; $scannedpage->resultid = $result->id; } else { $scannedpage->status = 'error'; $scannedpage->error = 'noresult'; } } else { // There is a partial result, so we can just load the user's question usage. // From now on we can use the default question usage class. $scannedpage->resultid = $result->id; } } } // We insert the scanned page into the database in any case. $scannedpage->time = time(); if (property_exists($scannedpage, 'id') && !empty($scannedpage->id)) { $DB->update_record('offlinequiz_scanned_pages', $scannedpage); } else { $scannedpage->id = $DB->insert_record('offlinequiz_scanned_pages', $scannedpage); } // Now the scanned page definitely has an ID, so we can store the corners. if (!$DB->get_records('offlinequiz_page_corners', array('scannedpageid' => $scannedpage->id))) { $corners = $scanner->get_corners(); offlinequiz_save_page_corners($scannedpage, $corners); } if ($autorotate) { return array($scanner, $scannedpage); } else { return $scannedpage; } }