echo "</style>\n"; echo '<head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /></head>'; offlinequiz_load_useridentification(); $offlinequizconfig = get_config('offlinequiz'); $group = $groups[$result->offlinegroupid]; $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));
function offlinequiz_evaluation_cron($jobid = 0, $verbose = false) { global $CFG, $DB; raise_memory_limit(MEMORY_EXTRA); // Only count the jobs with status processing that have been started in the last 24 hours. $expiretime = time() - 86400; $runningsql = "SELECT COUNT(*)\n FROM {offlinequiz_queue}\n WHERE status = 'processing'\n AND timestart > :expiretime"; $runningjobs = $DB->count_records_sql($runningsql, array('expiretime' => $expiretime)); if ($runningjobs >= OFFLINEQUIZ_MAX_CRON_JOBS) { echo "Too many jobs running! Exiting!"; return; } // TODO do this properly. Just for testing. $sql = "SELECT * FROM {offlinequiz_queue} WHERE status = 'new'"; $params = array(); if ($jobid) { $sql .= ' AND id = :jobid '; $params['jobid'] = $jobid; } $sql .= " ORDER BY id ASC"; // If there are no new jobs, we simply exit. if (!($jobs = $DB->get_records_sql($sql, $params, 0, OFFLINEQUIZ_TOP_QUEUE_JOBS))) { if ($verbose) { echo get_string('nothingtodo', 'offlinequiz'); } return; } $numberofjobs = count($jobs); if ($verbose) { $pbar = new progress_bar('offlinequizcronbar', 500, true); $pbar->create(); $pbar->update(0, $numberofjobs, "Processing job - {0}/{$numberofjobs}."); } $numberdone = 0; foreach ($jobs as $job) { // Check whether the status is still 'new' (might have been changed by other cronjob). $transaction = $DB->start_delegated_transaction(); $status = $DB->get_field('offlinequiz_queue', 'status', array('id' => $job->id)); if ($status == 'new') { $DB->set_field('offlinequiz_queue', 'status', 'processing', array('id' => $job->id)); $job->timestart = time(); $DB->set_field('offlinequiz_queue', 'timestart', $job->timestart, array('id' => $job->id)); $alreadydone = false; } else { $alreadydone = true; } $transaction->allow_commit(); // If the job is still new, process it! if (!$alreadydone) { // Set up the context for this job. if (!($offlinequiz = $DB->get_record('offlinequiz', array('id' => $job->offlinequizid)))) { $DB->set_field('offlinequiz_queue', 'status', 'error', array('id' => $job->id)); $DB->set_field('offlinequiz_queue', 'info', 'offlinequiz not found', array('id' => $job->id)); continue; } if (!($course = $DB->get_record('course', array('id' => $offlinequiz->course)))) { $DB->set_field('offlinequiz_queue', 'status', 'error', array('id' => $job->id)); $DB->set_field('offlinequiz_queue', 'info', 'course not found', array('id' => $job->id)); continue; } if (!($cm = get_coursemodule_from_instance("offlinequiz", $offlinequiz->id, $course->id))) { $DB->set_field('offlinequiz_queue', 'status', 'error', array('id' => $job->id)); $DB->set_field('offlinequiz_queue', 'info', 'course module found', array('id' => $job->id)); continue; } if (!($context = context_module::instance($cm->id))) { $DB->set_field('offlinequiz_queue', 'status', 'error', array('id' => $job->id)); $DB->set_field('offlinequiz_queue', 'info', 'context not found', array('id' => $job->id)); continue; } if (!($groups = $DB->get_records('offlinequiz_groups', array('offlinequizid' => $offlinequiz->id), 'number', '*', 0, $offlinequiz->numgroups))) { $DB->set_field('offlinequiz_queue', 'status', 'error', array('id' => $job->id)); $DB->set_field('offlinequiz_queue', 'info', 'no offlinequiz groups found', array('id' => $job->id)); continue; } $coursecontext = context_course::instance($course->id); offlinequiz_load_useridentification(); // TODO. $jobdata = $DB->get_records_sql("\n SELECT *\n FROM {offlinequiz_queue_data}\n WHERE queueid = :queueid\n AND status = 'new'", array('queueid' => $job->id)); list($maxquestions, $maxanswers, $formtype, $questionsperpage) = offlinequiz_get_question_numbers($offlinequiz, $groups); $dirname = ''; $doubleentry = 0; foreach ($jobdata as $data) { $starttime = time(); $DB->set_field('offlinequiz_queue_data', 'status', 'processing', array('id' => $data->id)); // We remember the directory name to be able to remove it later. if (empty($dirname)) { $pathparts = pathinfo($data->filename); $dirname = $pathparts['dirname']; } set_time_limit(120); try { // Create a new scanner for every page. $scanner = new offlinequiz_page_scanner($offlinequiz, $context->id, $maxquestions, $maxanswers); // Try to load the image file. echo 'job ' . $job->id . ': evaluating ' . $data->filename . "\n"; $scannedpage = $scanner->load_image($data->filename); if ($scannedpage->status == 'ok') { echo 'job ' . $job->id . ': image loaded ' . $scannedpage->filename . "\n"; } else { if ($scannedpage->error == 'filenotfound') { echo 'job ' . $job->id . ': image file not found: ' . $scannedpage->filename . "\n"; } } // Unset the origfilename because we don't need it in the DB. unset($scannedpage->origfilename); $scannedpage->offlinequizid = $offlinequiz->id; // If we could load the image file, the status is 'ok', so we can check the page for errors. if ($scannedpage->status == 'ok') { // We autorotate so check_scanned_page will return a potentially new scanner and the scannedpage. list($scanner, $scannedpage) = offlinequiz_check_scanned_page($offlinequiz, $scanner, $scannedpage, $job->importuserid, $coursecontext, true); } else { 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); } } echo 'job ' . $job->id . ': scannedpage id ' . $scannedpage->id . "\n"; // If the status is still 'ok', we can process the answers. This potentially submits the page and // checks whether the result for a student is complete. if ($scannedpage->status == 'ok') { // We can process the answers and submit them if possible. $scannedpage = offlinequiz_process_scanned_page($offlinequiz, $scanner, $scannedpage, $job->importuserid, $questionsperpage, $coursecontext, true); echo 'job ' . $job->id . ': processed answers for ' . $scannedpage->id . "\n"; } else { if ($scannedpage->status == 'error' && $scannedpage->error == 'resultexists') { // Already process the answers but don't submit them. $scannedpage = offlinequiz_process_scanned_page($offlinequiz, $scanner, $scannedpage, $job->importuserid, $questionsperpage, $coursecontext, false); // Compare the old and the new result wrt. the choices. $scannedpage = offlinequiz_check_different_result($scannedpage); } } // If there is something to correct then store the hotspots for retrieval in correct.php. if ($scannedpage->status != 'ok' && $scannedpage->error != 'couldnotgrab' && $scannedpage->error != 'notadjusted' && $scannedpage->error != 'grouperror') { $scanner->store_hotspots($scannedpage->id); } if ($scannedpage->status == 'ok' || $scannedpage->status == 'submitted' || $scannedpage->status == 'suspended' || $scannedpage->error == 'missingpages') { // Mark the file as processed. $DB->set_field('offlinequiz_queue_data', 'status', 'processed', array('id' => $data->id)); } else { $DB->set_field('offlinequiz_queue_data', 'status', 'error', array('id' => $data->id)); $DB->set_field('offlinequiz_queue_data', 'error', $scannedpage->error, array('id' => $data->id)); } if ($scannedpage->error == 'doublepage') { $doubleentry++; } } catch (Exception $e) { echo 'job ' . $job->id . ': ' . $e->getMessage() . "\n"; $DB->set_field('offlinequiz_queue_data', 'status', 'error', array('id' => $data->id)); $DB->set_field('offlinequiz_queue_data', 'error', 'couldnotgrab', array('id' => $data->id)); $DB->set_field('offlinequiz_queue_data', 'info', $e->getMessage(), array('id' => $data->id)); $scannedpage->status = 'error'; $scannedpage->error = 'couldnotgrab'; if ($scannedpage->id) { $DB->update_record('offlinequiz_scanned_pages', $scannedpage); } else { $DB->insert_record('offlinequiz_scanned_pages', $scannedpage); } } } // End foreach jobdata. offlinequiz_update_grades($offlinequiz); $job->timefinish = time(); $DB->set_field('offlinequiz_queue', 'timefinish', $job->timefinish, array('id' => $job->id)); $job->status = 'finished'; $DB->set_field('offlinequiz_queue', 'status', 'finished', array('id' => $job->id)); echo date('Y-m-d-H:i') . ": Import queue with id {$job->id} imported.\n\n"; if ($user = $DB->get_record('user', array('id' => $job->importuserid))) { $mailtext = get_string('importisfinished', 'offlinequiz', format_text($offlinequiz->name, FORMAT_PLAIN)); // How many pages have been imported successfully. $countsql = "SELECT COUNT(id)\n FROM {offlinequiz_queue_data}\n WHERE queueid = :queueid\n AND status = 'processed'"; $params = array('queueid' => $job->id); $mailtext .= "\n\n" . get_string('importnumberpages', 'offlinequiz', $DB->count_records_sql($countsql, $params)); // How many pages have an error. $countsql = "SELECT COUNT(id)\n FROM {offlinequiz_queue_data}\n WHERE queueid = :queueid\n AND status = 'error'"; $mailtext .= "\n" . get_string('importnumberverify', 'offlinequiz', $DB->count_records_sql($countsql, $params)); $mailtext .= "\n" . get_string('importnumberexisting', 'offlinequiz', $doubleentry); $linkoverview = "{$CFG->wwwroot}/mod/offlinequiz/report.php?q={$job->offlinequizid}&mode=overview"; $mailtext .= "\n\n" . get_string('importlinkresults', 'offlinequiz', $linkoverview); $linkupload = "{$CFG->wwwroot}/mod/offlinequiz/report.php?q={$job->offlinequizid}&mode=rimport"; $mailtext .= "\n" . get_string('importlinkverify', 'offlinequiz', $linkupload); $mailtext .= "\n\n" . get_string('importtimestart', 'offlinequiz', userdate($job->timestart)); $mailtext .= "\n" . get_string('importtimefinish', 'offlinequiz', userdate($job->timefinish)); email_to_user($user, $CFG->noreplyaddress, get_string('importmailsubject', 'offlinequiz'), $mailtext); } } // End !alreadydone. $numberdone++; if ($verbose) { ob_flush(); $pbar->update($numberdone, $numberofjobs, "Processing job - {$numberdone}/{$numberofjobs}."); } } // End foreach. }
if ($instanceid = $enrol->add_default_instance($course)) { $instance = $DB->get_record('enrol', array('courseid' => $course->id, 'enrol' => 'manual'), '*', MUST_EXIST); } } if ($instance != false) { $enrol->enrol_user($instance, $userid, $offlinequizconfig->oneclickrole); } } // Now we look for other pages with that user and reset their status. $sql = "SELECT *\n FROM {offlinequiz_scanned_pages}\n WHERE offlinequizid = :offlinequizid\n AND status = 'error'\n AND error = 'usernotincourse'\n AND userkey = :currentuserkey\n AND id <> :currentpageid"; $params = array('offlinequizid' => $offlinequiz->id, 'currentuserkey' => $scannedpage->userkey, 'currentpageid' => $scannedpage->id); $otherpages = $DB->get_records_sql($sql, $params); foreach ($otherpages as $otherpage) { $otherpage->status = 'ok'; $otherpage->error = ''; $tempscanner = new offlinequiz_page_scanner($offlinequiz, $context->id, $maxquestions, $maxanswers); $tempcorners = array(); if ($dbcorners = $DB->get_records('offlinequiz_page_corners', array('scannedpageid' => $otherpage->id), 'position')) { foreach ($dbcorners as $corner) { $tempcorners[] = new oq_point($corner->x, $corner->y); } } else { $tempcorners[0] = new oq_point(55, 39); $tempcorners[1] = new oq_point(805, 49); $tempcorners[2] = new oq_point(44, 1160); $tempcorners[3] = new oq_point(805, 1160); } $tempscanner->load_stored_image($otherpage->filename, $tempcorners); $otherpage = offlinequiz_check_scanned_page($offlinequiz, $tempscanner, $otherpage, $USER->id, $coursecontext); if ($otherpage->status == 'ok') { $otherpage = offlinequiz_process_scanned_page($offlinequiz, $tempscanner, $otherpage, $USER->id, $questionsperpage, $coursecontext, true);
/** * Stores the choices made on a scanned page in the table offlinequiz_choices. If there are no insecure markings * the page is also submitted, i.e. the answers are processed by the question usage by activiy (quba). * * @param unknown_type $offlinequiz * @param unknown_type $scanner * @param unknown_type $scannedpage * @param unknown_type $teacherid * @param unknown_type $coursecontext */ function offlinequiz_process_scanned_page($offlinequiz, offlinequiz_page_scanner $scanner, $scannedpage, $teacherid, $questionsperpage, $coursecontext, $submit = false) { global $DB; $offlinequizconfig = get_config('offlinequiz'); if (property_exists($scannedpage, 'resultid') && $scannedpage->resultid) { $group = $DB->get_record('offlinequiz_groups', array('offlinequizid' => $offlinequiz->id, 'number' => $scannedpage->groupnumber)); $user = $DB->get_record('user', array($offlinequizconfig->ID_field => $scannedpage->userkey)); $result = $DB->get_record('offlinequiz_results', array('id' => $scannedpage->resultid)); $quba = offlinequiz_load_questions_usage_by_activity($result->usageid); // Retrieve the answers. This initialises the answer hotspots. $answers = $scanner->get_answers(); if (empty($answers)) { $scannedpage->status = 'error'; $scannedpage->error = 'notadjusted'; return $scannedpage; } $slots = $quba->get_slots(); // We start at the top of the page (e.g. 0, 96, etc). $startindex = ($scannedpage->pagenumber - 1) * $questionsperpage; // We end on the bottom of the page or when the questions are gone (e.g., 95, 105). $endindex = min($scannedpage->pagenumber * $questionsperpage, count($slots)); $answerindex = 0; $insecuremarkings = false; $choicesdata = array(); for ($slotindex = $startindex; $slotindex < $endindex; $slotindex++) { $slot = $slots[$slotindex]; $slotquestion = $quba->get_question($slot); $attempt = $quba->get_question_attempt($slot); $order = $slotquestion->get_order($attempt); // Order of the answers. // Note: The array length of a row is $maxanswers, so probably bigger than the number of answers in the slot. $row = $answers[$answerindex++]; $count = 0; $response = array(); if (!isset($choicesdata[$slot]) || !is_array($choicesdata[$slot])) { $choicesdata[$slot] = array(); } // Go through all answers of the slot question. foreach ($order as $key => $notused) { // Create the data structure for the offlinequiz_choices table. $choice = new stdClass(); $choice->scannedpageid = $scannedpage->id; $choice->slotnumber = $slot; $choice->choicenumber = $key; // Check what the scanner recognised. if ($row[$key] == 'marked') { $choice->value = 1; } else { if ($row[$key] == 'empty') { $choice->value = 0; } else { $choice->value = -1; $insecuremarkings = true; } } // We really want to save every single cross in the database. $choice->id = $DB->insert_record('offlinequiz_choices', $choice); $choicesdata[$slot][$key] = $choice; } } // End for (slot... if (!$insecuremarkings and $submit) { $scannedpage = offlinequiz_submit_scanned_page($offlinequiz, $scannedpage, $choicesdata, $startindex, $endindex); if ($scannedpage->status == 'submitted') { offlinequiz_check_result_completed($offlinequiz, $group, $result); } } // If insecure markings have been found, set the status appropriately. if (($scannedpage->status == 'ok' || $scannedpage->status == 'suspended') && $insecuremarkings) { $scannedpage->status = 'error'; $scannedpage->error = 'insecuremarkings'; $scannedpage->time = time(); $DB->update_record('offlinequiz_scanned_pages', $scannedpage); } } // End if status ok. return $scannedpage; }