$showlastaccess = true; $hiddenfields = explode(',', $CFG->hiddenuserfields); if (array_search('lastaccess', $hiddenfields) !== false and !has_capability('moodle/user:viewhiddendetails', $context)) { $showlastaccess = false; } $stractivityreport = get_string('pluginname', 'report_outline'); $stractivity = get_string('activity'); $strlast = get_string('lastaccess'); $strreports = get_string('reports'); $strviews = get_string('views'); $strrelatedblogentries = get_string('relatedblogentries', 'blog'); $PAGE->set_title($course->shortname . ': ' . $stractivityreport); $PAGE->set_heading($course->fullname); echo $OUTPUT->header(); echo $OUTPUT->heading(format_string($course->fullname)); list($uselegacyreader, $useinternalreader, $minloginternalreader, $logtable) = report_outline_get_common_log_variables(); // If no legacy and no internal log then don't proceed. if (!$uselegacyreader && !$useinternalreader) { echo $OUTPUT->box_start('generalbox', 'notice'); echo $OUTPUT->notification(get_string('nologreaderenabled', 'report_outline')); echo $OUTPUT->box_end(); echo $OUTPUT->footer(); die; } // We want to display the time we are beginning to get logs from in the heading. // If we are using the legacy reader check the minimum time in that log table. if ($uselegacyreader) { $minlog = $DB->get_field_sql('SELECT min(time) FROM {log}'); } // If we are using the internal reader check the minimum time in that table. if ($useinternalreader) {
/** * Display the most commonly used user complete information. * * @param int $userid the id of the user * @param int $cmid the course module id * @param string $module the name of the module (eg. 'book') * @param int $instanceid (eg. the 'id' in the 'book' table) * @return string */ function report_outline_user_complete($userid, $cmid, $module, $instanceid) { global $DB; list($uselegacyreader, $useinternalreader, $minloginternalreader, $logtable) = report_outline_get_common_log_variables(); // If using legacy log then get users from old table. if ($uselegacyreader) { // Create the params for the query. $params = array('userid' => $userid, 'module' => $module, 'action' => 'view', 'info' => $instanceid); // If we are going to use the internal (not legacy) log table, we should only get records // from the legacy table that exist before we started adding logs to the new table. $limittime = ''; if (!empty($minloginternalreader)) { $limittime = ' AND time < :timeto '; $params['timeto'] = $minloginternalreader; } $select = "SELECT COUNT(id) "; $from = "FROM {log} "; $where = "WHERE userid = :userid\n AND module = :module\n AND action = :action\n AND info = :info "; if ($legacylogcount = $DB->count_records_sql($select . $from . $where . $limittime, $params)) { $numviews = $legacylogcount; // Get the time for the last log. $select = "SELECT MAX(time) "; $lastlogtime = $DB->get_field_sql($select . $from . $where, $params); $strnumviews = get_string('numviews', '', $numviews); } } // Get record from sql_internal_table_reader and combine with the number of views from the legacy log table (if needed). if ($useinternalreader) { $params = array('userid' => $userid, 'contextlevel' => CONTEXT_MODULE, 'contextinstanceid' => $cmid, 'crud' => 'r', 'edulevel1' => core\event\base::LEVEL_PARTICIPATING, 'edulevel2' => core\event\base::LEVEL_TEACHING, 'edulevel3' => core\event\base::LEVEL_OTHER, 'anonymous' => 0); $select = "SELECT COUNT(*) as count "; $from = "FROM {" . $logtable . "} "; $where = "WHERE userid = :userid\n AND contextlevel = :contextlevel\n AND contextinstanceid = :contextinstanceid\n AND crud = :crud\n AND edulevel IN (:edulevel1, :edulevel2, :edulevel3)\n AND anonymous = :anonymous"; if ($internalreadercount = $DB->count_records_sql($select . $from . $where, $params)) { if (!empty($numviews)) { $numviews = $numviews + $internalreadercount; } else { $numviews = $internalreadercount; } // Get the time for the last log. $select = "SELECT MAX(timecreated) "; $lastlogtime = $DB->get_field_sql($select . $from . $where, $params); $strnumviews = get_string('numviews', '', $numviews); } } if (!empty($strnumviews) && !empty($lastlogtime)) { return $strnumviews . ' - ' . get_string('mostrecently') . ' ' . userdate($lastlogtime); } else { return get_string('neverseen', 'report_outline'); } }
/** * Automatic student evaluation * * This function automatically avaluates the student according to enrichment criteria * which are cross referenced with the data obtained through Learning Analytics data mining procedures. * * For the new logging system from Moodle 2.6 onwards, Learning Analytics will only be obtained from * Legacy log Table a.k.a. {log} or New Internal Log Table a.k.a {logstore_standard_log}. * * External logstores can't be used because their data structure and logging data are custom * and can not be predicted. Only a log store creator would be able to change the data queries bellow * according to his log store specifications. * * * @param array $criterion data about the rubric design * @param array $options enriched rubric options */ protected function evaluate_enrichment(&$criterion, $options) { global $DB; global $PAGE; global $CFG; $moduletypename = null; $benchmarkstudent = null; $benchmarkstudents = null; $benchmarkcriterion = null; $untiltime = null; $fromtime = null; $selectallstudents = null; $participatingstudents = null; $iterations = 0; $sql = null; // Get the current assignment data. $curmoduleid = (int) $PAGE->cm->module; $curmodulename = $PAGE->cm->modname; $curcmid = $PAGE->cm->id; $gradingmoduleid = $PAGE->cm->instance; $courseid = $PAGE->cm->course; //**** For Moodle 2.6 onwards new logging system ***// /* If the Legacy Log is used, we work with it. If not, we use the new Internal Log (Standard). The old log is more efficient for the queries used bellow. * If en External Logstore is used and both Legacy and new Internal logs are deactivated, do nothing, as we can't know in advance how the new * external logs are stored and how the store's log tables are structured. * */ // Set the necessary variables $uselegacyreader = true; $useinternalreader = null; $minloginternalreader = null; $logtable = null; $moodle_2_6_0_version = '2013111800'; // Get the necessary variables if needed if ($CFG->version >= $moodle_2_6_0_version) { require_once $CFG->dirroot . '/report/outline/locallib.php'; list($uselegacyreader, $useinternalreader, $minloginternalreader, $logtable) = report_outline_get_common_log_variables(); } //**** For moodle 2.2 versions assignment modules ****// $studentid = optional_param('userid', '', PARAM_INT); //**** For moodle 2.3 onwards assignment module ****// if (!$studentid) { require_once $CFG->dirroot . '/mod/assign/locallib.php'; $context = context_module::instance($PAGE->cm->id); $assignment = new assign($context, $PAGE->cm, $PAGE->cm->course); $useridlist = array_keys($assignment->list_participants(0, true)); sort($useridlist); // The users list containing the IDs of students evaluated. $rownum = $_REQUEST['rownum']; // Check if next or previous buttons are pressed in the avaluation form. if (array_key_exists('saveandshownext', $_REQUEST) || array_key_exists('nosaveandnext', $_REQUEST)) { $rownum++; } if (array_key_exists('nosaveandprevious', $_REQUEST)) { $rownum--; } $studentid = $useridlist[$rownum]; } // SQL for including all course enroled students. $selectindividual = "= {$studentid}"; $selectallstudents = "IN (SELECT u.id\n FROM {role_assignments} ra\n JOIN {context} c ON ra.contextid = c.id AND c.contextlevel = 50\n JOIN {user} u ON u.id = ra.userid\n JOIN {course} crse ON c.instanceid = crse.id\n WHERE ra.roleid = 5\n AND crse.id = {$courseid})"; // Timestamp enrichment calculations according to assignment module (old or new type assignments). // In case of old type assignment... if ($curmoduleid == $this->oldassignmoduleid) { // Get potential enrichment due date. if ($options['timestampenrichmentend']) { $sql = 'SELECT asmnt.timedue AS duedate FROM {assignment} asmnt WHERE asmnt.id = ' . $gradingmoduleid; $untiltime = $DB->get_field_sql($sql, null); } // Get potential availability start time. if ($options['timestampenrichmentstart']) { $sql = 'SELECT asmnt.timeavailable AS startdate FROM {assignment} asmnt WHERE asmnt.id = ' . $gradingmoduleid; $fromtime = $DB->get_field_sql($sql, null); } // In case of new type assignment... } else { if ($curmoduleid == $this->newassignmoduleid) { // Get potential enrichment due date. if ($options['timestampenrichmentend']) { $sql = 'SELECT asmnt.duedate AS duedate FROM {assign} asmnt WHERE asmnt.id = ' . $gradingmoduleid; $untiltime = $DB->get_field_sql($sql, null); } // Get potential availability start time. if ($options['timestampenrichmentstart']) { $sql = 'SELECT asmnt.allowsubmissionsfromdate AS startdate FROM {assign} asmnt WHERE asmnt.id = ' . $gradingmoduleid; $fromtime = $DB->get_field_sql($sql, null); } } } // Retrieve Learning Analytics data according to criterion type (collaboration - study - grade). switch ($criterion['criteriontype']) { // In case of checking student studying, perform data mining from study logs of selected course modules (resources). case gradingform_erubric_controller::INTERACTION_TYPE_STUDY: $moduletypename = 'resource'; foreach ($criterion['coursemodules'][$moduletypename][0] as $mdlinstance) { // Iterate through course modules. $tempinstance = explode('->', $mdlinstance); $moduleid = $tempinstance[0]; $instanceid = $tempinstance[1]; $timefield = 'time'; // Get log files according to curent log store if ($uselegacyreader) { // Old log $sql = "SELECT COUNT(lg.id) AS TOTALS\n FROM {log} lg\n INNER JOIN {course_modules} cm ON (lg.cmid = cm.id)\n WHERE lg.userid {$selectindividual}\n AND lg.action = 'view'\n AND cm.course = {$courseid}\n AND cm.module = {$moduleid}\n AND cm.instance = {$instanceid} "; } elseif ($useinternalreader) { // New log $sql = "SELECT COUNT(lg.id) AS TOTALS\n FROM {" . $logtable . "} lg\n INNER JOIN {modules} mdls ON (lg.objecttable = mdls.name)\n WHERE lg.userid {$selectindividual}\n AND lg.action = 'viewed'\n AND lg.courseid = {$courseid}\n AND mdls.id = {$moduleid}\n AND lg.objectid = {$instanceid} "; $timefield = 'timecreated'; } $this->get_value_from_learning_analytics($benchmarkstudent, $sql, 'lg', $timefield, $fromtime, $untiltime, 1); // If the criterion has a global reference according to all students participating. if ($criterion['referencetype'] == gradingform_erubric_controller::REFERENCE_STUDENTS) { $sql = "SELECT COUNT(crse.id) AS Totals\n FROM {role_assignments} ra\n JOIN {context} c ON ra.contextid = c.id AND c.contextlevel = 50\n JOIN {user} u ON u.id = ra.userid\n JOIN {course} crse ON c.instanceid = crse.id\n WHERE ra.roleid = 5\n AND crse.id = {$courseid} "; $count = $DB->get_field_sql($sql, null); if ($count && $count > 0) { // If there is at least one student. $iterations++; // Get log files according to curent log store if ($uselegacyreader) { // Old log $sql = "SELECT COUNT(lg.id) AS TOTALS\n FROM {log} lg\n INNER JOIN {course_modules} cm ON (lg.cmid = cm.id)\n WHERE lg.userid {$selectallstudents}\n AND lg.action = 'view'\n AND cm.course = {$courseid}\n AND cm.module = {$moduleid}\n AND cm.instance = {$instanceid} "; } elseif ($useinternalreader) { // New log $sql = "SELECT COUNT(lg.id) AS TOTALS\n FROM {" . $logtable . "} lg\n INNER JOIN {modules} mdls ON (lg.objecttable = mdls.name)\n WHERE lg.userid {$selectallstudents}\n AND lg.action = 'viewed'\n AND lg.courseid = {$courseid}\n AND mdls.id = {$moduleid}\n AND lg.objectid = {$instanceid} "; $timefield = 'timecreated'; } $this->get_value_from_learning_analytics($benchmarkstudents, $sql, 'lg', $timefield, $fromtime, $untiltime, $count); } } } break; // In case of checking student grades, perform data mining from grade logs of selected course modules (assignments). // In case of checking student grades, perform data mining from grade logs of selected course modules (assignments). case gradingform_erubric_controller::INTERACTION_TYPE_GRADE: $moduletypename = 'assignment'; foreach ($criterion['coursemodules'][$moduletypename][0] as $mdlinstance) { // Iterate through course modules. $tempinstance = explode('->', $mdlinstance); $moduleid = $tempinstance[0]; $instanceid = $tempinstance[1]; $tempstudentcurrgrade = null; $sql = "SELECT gr.finalgrade AS Grade\n FROM {grade_grades} gr\n INNER JOIN {grade_items} gi ON (gi.id = gr.itemid)\n INNER JOIN {modules} md ON (md.name = gi.itemmodule)\n WHERE gr.userid {$selectindividual}\n AND gi.courseid = {$courseid}\n AND md.id = {$moduleid}\n AND gi.iteminstance = {$instanceid} "; $this->get_value_from_learning_analytics($tempstudentcurrgrade, $sql, null, null, null, null, 1); // If the student has not been graded for this course assignment, // there is no point on calculating his or the others benchmark. if (!is_null($tempstudentcurrgrade)) { $iterations++; // Get maximum grade score for current assignment module in order to define student performance according to 100 scale. $sql = "SELECT assmnt.grade AS maxgrade "; if ($moduleid == $this->newassignmoduleid) { $sql .= "FROM {assign} assmnt "; // New type assignment modules (Moodle 2.3+) } else { $sql .= "FROM {assignment} assmnt "; // Old type assignment modules (Moodle 2.2) } $sql .= "WHERE assmnt.id = {$instanceid}"; $tempassignmax = (double) $DB->get_field_sql($sql, null); $benchmarkstudent += (double) round($tempstudentcurrgrade / $tempassignmax * 100, 2); // If the criterion has a global reference according to all students grades. if ($criterion['referencetype'] == gradingform_erubric_controller::REFERENCE_STUDENTS) { $sql = "SELECT AVG(gr.finalgrade) AS AvgGrades\n FROM {grade_grades} gr\n INNER JOIN {grade_items} gi ON (gi.id = gr.itemid)\n INNER JOIN {modules} md ON (md.name = gi.itemmodule)\n WHERE gr.userid {$selectallstudents}\n AND gi.courseid = {$courseid}\n AND md.id = {$moduleid}\n AND gi.iteminstance = {$instanceid} "; $tempstudentsgrade = $DB->get_field_sql($sql, null); if (!is_null($tempstudentsgrade)) { // If the value is valid. $benchmarkstudents += (double) round($tempstudentsgrade / $tempassignmax * 100, 2); } } } } break; // In case of checking student collaboration, perform data mining according to collaboration type: // - Entries, for simple log occurences in selected course modules, // - File Adds, for summing number of file adds (not number of files!), from course modules of type forum. // - Replies, for summing number of replied posts, from course modules of type forum. // - Interactions, for total number of students the evaluated student interacted during using the selected course modules. // In case of checking student collaboration, perform data mining according to collaboration type: // - Entries, for simple log occurences in selected course modules, // - File Adds, for summing number of file adds (not number of files!), from course modules of type forum. // - Replies, for summing number of replied posts, from course modules of type forum. // - Interactions, for total number of students the evaluated student interacted during using the selected course modules. case gradingform_erubric_controller::INTERACTION_TYPE_COLLABORATION: $moduletypename = 'activity'; // In case of students interactions, benchmarks will be calculated after all modules have been processed. if ($criterion['collaborationtype'] == gradingform_erubric_controller::COLLABORATION_TYPE_INTERACTIONS) { $distinctUsersFound = array(); } // In case of students file submissions, benchmarks will be initialised with zero values. if ($criterion['collaborationtype'] == gradingform_erubric_controller::COLLABORATION_TYPE_FILE_ADDS) { $benchmarkstudent = 0; if ($criterion['referencetype'] == gradingform_erubric_controller::REFERENCE_STUDENTS) { $benchmarkstudents = 0; } } foreach ($criterion['coursemodules'][$moduletypename][0] as $mdlinstance) { // Iterate through course modules. $tempinstance = explode('->', $mdlinstance); $moduleid = $tempinstance[0]; $instanceid = $tempinstance[1]; $logtablename = 'log'; $timefield = 'time'; switch ($criterion['collaborationtype']) { // In case of checking simple entries in forums and chats, // just check for 'add post' or 'talk' actions inside moodle log. case gradingform_erubric_controller::COLLABORATION_TYPE_ENTRIES: // Get log files according to curent log store if ($uselegacyreader) { // Old log $sql = "SELECT COUNT(lg.id) AS TOTALS\n FROM {log} lg\n INNER JOIN {course_modules} cm ON (lg.cmid = cm.id)\n WHERE lg.userid {$selectindividual}\n AND (lg.action = 'add post' OR lg.action = 'talk')\n AND cm.course = {$courseid}\n AND cm.module = {$moduleid}\n AND cm.instance = {$instanceid} "; } elseif ($useinternalreader) { // New log // There is no direct way to get both posts and talks from the new log // according to the module id (forum or chat) and instance id (forum id or chat id), // so we must seperate the original log query with two different queries that don't have to refer to the new log anymore. // Bye bye new log... Bummer :( // If this is a forum course module if ($moduleid == $this->forummoduleid) { $sql = "SELECT COUNT(fp.id) AS TOTALS\n FROM {forum_posts} fp\n INNER JOIN {forum_discussions} fdcs ON (fp.discussion = fdcs.id)\n WHERE fp.userid {$selectindividual}\n AND fdcs.course = {$courseid}\n AND fdcs.forum = {$instanceid} "; $logtablename = 'fp'; $timefield = 'created'; // Else only if this is a chat course module } elseif ($moduleid == $this->chatmoduleid) { $sql = "SELECT COUNT(chms.id) AS TOTALS\n FROM {chat_messages} chms\n WHERE chms.userid {$selectindividual}\n AND chms.system = 0\n AND chms.chatid = {$instanceid} "; $logtablename = 'chms'; $timefield = 'timestamp'; } } $this->get_value_from_learning_analytics($benchmarkstudent, $sql, $logtablename, $timefield, $fromtime, $untiltime, 1); // If the criterion has a global reference according to all students' collaborations, // we hold accountable only participating students. If a student is absent (no log entries), he is not accounted for. if ($criterion['referencetype'] == gradingform_erubric_controller::REFERENCE_STUDENTS) { $count = 0; // Get log files according to curent log store if ($uselegacyreader) { // Old log $sql = "SELECT DISTINCT (lg.userid) AS userids\n FROM {log} lg\n INNER JOIN {course_modules} cm ON (lg.cmid = cm.id)\n WHERE lg.userid {$selectallstudents}\n AND (lg.action = 'add post' OR lg.action = 'talk')\n AND cm.course = {$courseid}\n AND cm.module = {$moduleid}\n AND cm.instance = {$instanceid} "; // Count participating students and return the sql query to count their entries next $participatingstudents = $this->timestamp_and_count_active_studends_involved($count, $sql, 'lg', 'time', $fromtime, $untiltime); if ($count > 0) { // If there is at least one student. $iterations++; $sql = "SELECT COUNT(lg.id) AS TOTALS\n FROM {log} lg\n INNER JOIN {course_modules} cm ON (lg.cmid = cm.id)\n WHERE lg.userid IN ({$participatingstudents})\n AND (lg.action = 'add post' OR lg.action = 'talk')\n AND cm.course = {$courseid}\n AND cm.module = {$moduleid}\n AND cm.instance = {$instanceid} "; $this->get_value_from_learning_analytics($benchmarkstudents, $sql, 'lg', 'time', $fromtime, $untiltime, $count); } } elseif ($useinternalreader) { // New log // There is no direct way to get both posts and talks from the new log // according to the module id (forum or chat) and instance id (forum id or chat id), // so we must seperate the original log query with two different queries that don't have to refer to the new log anymore. // Bye bye new log... Bummer :( // If this is a forum course module if ($moduleid == $this->forummoduleid) { $logtablename = 'fp'; $timefield = 'created'; $sql = "SELECT DISTINCT (fp.userid) AS userids\n FROM {forum_posts} fp\n INNER JOIN {forum_discussions} fdcs ON (fp.discussion = fdcs.id)\n WHERE fp.userid {$selectallstudents}\n AND fdcs.course = {$courseid}\n AND fdcs.forum = {$instanceid} "; // Count participating students and return the sql query to count their entries next $participatingstudents = $this->timestamp_and_count_active_studends_involved($count, $sql, $logtablename, $timefield, $fromtime, $untiltime); if ($count > 0) { // If there is at least one student. $iterations++; $sql = "SELECT COUNT(fp.id) AS TOTALS\n FROM {forum_posts} fp\n INNER JOIN {forum_discussions} fdcs ON (fp.discussion = fdcs.id)\n WHERE fp.userid IN ({$participatingstudents})\n AND fdcs.course = {$courseid}\n AND fdcs.forum = {$instanceid} "; $this->get_value_from_learning_analytics($benchmarkstudents, $sql, $logtablename, $timefield, $fromtime, $untiltime, $count); } // Else only if this is a chat course module } else { if ($moduleid == $this->chatmoduleid) { $logtablename = 'chms'; $timefield = 'timestamp'; $sql = "SELECT DISTINCT (chms.userid) AS userids\n FROM {chat_messages} chms\n WHERE chms.userid {$selectallstudents}\n AND chms.system = 0\n AND chms.chatid = {$instanceid} "; // Count participating students and return the sql query to count their entries next $participatingstudents = $this->timestamp_and_count_active_studends_involved($count, $sql, $logtablename, $timefield, $fromtime, $untiltime); if ($count > 0) { // If there is at least one student. $iterations++; $sql = "SELECT COUNT(chms.id) AS TOTALS\n FROM {chat_messages} chms\n WHERE chms.userid IN ({$participatingstudents})\n AND chms.system = 0\n AND chms.chatid = {$instanceid} "; $this->get_value_from_learning_analytics($benchmarkstudents, $sql, $logtablename, $timefield, $fromtime, $untiltime, $count); } } } } } break; // In case of checking addition occurences of files in forums, // just count the attachement occurences in forum posts. // In case of checking addition occurences of files in forums, // just count the attachement occurences in forum posts. case gradingform_erubric_controller::COLLABORATION_TYPE_FILE_ADDS: $sql = "SELECT COUNT(fls.id) AS TOTALS\n FROM {forum_posts} fp\n INNER JOIN {forum_discussions} fd ON (fd.id = fp.discussion)\n INNER JOIN {files} fls ON (fls.itemid = fp.id)\n WHERE fp.userid {$selectindividual}\n AND fls.filesize > 0\n AND fls.component = 'mod_forum'\n AND fd.forum = {$instanceid} "; $this->get_value_from_learning_analytics($benchmarkstudent, $sql, 'fp', 'created', $fromtime, $untiltime, 1); // If the criterion has a global reference according to all students file submissions, // check all students participated in the forum, even if they haven't submited anything. // Leave out all those who didn't participated in the forum at all. if ($criterion['referencetype'] == gradingform_erubric_controller::REFERENCE_STUDENTS) { $count = 0; $sql = "SELECT DISTINCT (fp.userid) AS userids\n FROM {forum_posts} fp\n INNER JOIN {forum_discussions} fd ON (fd.id = fp.discussion)\n WHERE fp.userid {$selectallstudents}\n AND fd.forum = {$instanceid} "; $participatingstudents = $this->timestamp_and_count_active_studends_involved($count, $sql, 'fp', 'created', $fromtime, $untiltime); if ($count > 0) { // If there is at least one student. $iterations++; $sql = "SELECT COUNT(fls.id) AS TOTALS\n FROM {forum_posts} fp\n INNER JOIN {forum_discussions} fd ON (fd.id = fp.discussion)\n INNER JOIN {files} fls ON (fls.itemid = fp.id)\n WHERE fp.userid IN ({$participatingstudents})\n AND fls.filesize > 0\n AND fls.component = 'mod_forum'\n AND fd.forum = {$instanceid} "; $this->get_value_from_learning_analytics($benchmarkstudents, $sql, 'fp', 'created', $fromtime, $untiltime, $count); } } break; // In case of checking student replies in forums, // just count all student posts except self-replies and the ones referring to root post a.k.a. discussion. // In case of checking student replies in forums, // just count all student posts except self-replies and the ones referring to root post a.k.a. discussion. case gradingform_erubric_controller::COLLABORATION_TYPE_REPLIES: $sql = "SELECT COUNT(fp.id) AS TOTALS\n FROM {forum_posts} fp\n INNER JOIN {forum_discussions} fd ON (fd.id = fp.discussion)\n WHERE fp.userid {$selectindividual}\n AND fp.parent <> 0\n AND fp.parent NOT IN (SELECT fp2.id AS tempids\n FROM {forum_posts} fp2\n INNER JOIN {forum_discussions} fd2 ON (fd2.id = fp2.discussion)\n WHERE fp2.userid {$selectindividual}\n AND fp2.parent <> 0\n AND fd2.forum = {$instanceid})\n AND fd.forum = {$instanceid} "; $this->get_value_from_learning_analytics($benchmarkstudent, $sql, 'fp', 'created', $fromtime, $untiltime, 1); // If the criterion has a global reference according to all students replies. if ($criterion['referencetype'] == gradingform_erubric_controller::REFERENCE_STUDENTS) { $count = 0; $sql = "SELECT DISTINCT (fp.userid) AS userids\n FROM {forum_posts} fp\n INNER JOIN {forum_discussions} fd ON (fd.id = fp.discussion)\n WHERE fp.userid {$selectallstudents}\n AND fp.parent <> 0\n AND fp.parent NOT IN (SELECT fp2.id AS tempids\n FROM {forum_posts} fp2\n INNER JOIN {forum_discussions} fd2 ON (fd2.id = fp2.discussion)\n WHERE fp2.userid = fp.userid\n AND fp2.parent <> 0\n AND fd2.forum = {$instanceid})\n AND fd.forum = {$instanceid} "; $participatingstudents = $this->timestamp_and_count_active_studends_involved($count, $sql, 'fp', 'created', $fromtime, $untiltime); if ($count > 0) { // If there is at least one student. $iterations++; $sql = "SELECT COUNT(fp.id) AS TOTALS\n FROM {forum_posts} fp\n INNER JOIN {forum_discussions} fd ON (fd.id = fp.discussion)\n WHERE fp.userid IN ({$participatingstudents})\n AND fp.parent <> 0\n AND fp.parent NOT IN (SELECT fp2.id AS tempids\n FROM {forum_posts} fp2\n INNER JOIN {forum_discussions} fd2 ON (fd2.id = fp2.discussion)\n WHERE fp2.userid = fp.userid\n AND fp2.parent <> 0\n AND fd2.forum = {$instanceid})\n AND fd.forum = {$instanceid} "; $this->get_value_from_learning_analytics($benchmarkstudents, $sql, 'fp', 'created', $fromtime, $untiltime, $count); } } break; // In case of checking the number of distinct students, the evaluated student has interacted, in forums or chats... // 1. Retrieve all student ids (including the evaluated) for each course module. // 2. Check if current evaluated student's id exists in each module of interacted student's ids. // 3. Update the pile of current evaluated student's, interacted user's ids. // 4. Count the pile's size to retieve the number of all students' ids, // thus get the nubmer of all students the current one interacted. // In case of checking all students do the above for every student interacted in each course module... // (!!!Run time possible delay or script timeout for checking many users!!!) // In case of checking the number of distinct students, the evaluated student has interacted, in forums or chats... // 1. Retrieve all student ids (including the evaluated) for each course module. // 2. Check if current evaluated student's id exists in each module of interacted student's ids. // 3. Update the pile of current evaluated student's, interacted user's ids. // 4. Count the pile's size to retieve the number of all students' ids, // thus get the nubmer of all students the current one interacted. // In case of checking all students do the above for every student interacted in each course module... // (!!!Run time possible delay or script timeout for checking many users!!!) case gradingform_erubric_controller::COLLABORATION_TYPE_INTERACTIONS: if ($moduleid == $this->forummoduleid) { // Check forum modules. // Get each forum discussion and check interactions. $tempsql = "SELECT fd.id AS discussionid FROM {forum_discussions} fd WHERE fd.forum = {$instanceid}"; $discussions = $DB->get_records_sql($tempsql, null); foreach ($discussions as $dscn) { $discussionid = $dscn->discussionid; $sql = "SELECT DISTINCT(fp.userid) AS usersid\n FROM {forum_posts} fp\n INNER JOIN {forum_discussions} fd ON (fd.id = fp.discussion)\n WHERE fd.forum = {$instanceid}\n AND fd.id = {$discussionid}\n AND fp.userid {$selectallstudents} "; // This line added to ensure that only students are accounted. $tempusersarray = null; $this->get_value_from_learning_analytics($tempusersarray, $sql, 'fp', 'created', $fromtime, $untiltime, null); if ($criterion['referencetype'] == gradingform_erubric_controller::REFERENCE_STUDENT) { // Check only current student evaluated. if (!is_null($tempusersarray)) { $studentin = false; // Check all participating students to see if current student's id was in. foreach ($tempusersarray as $tempid) { if (isset($tempid->usersid) && $tempid->usersid == $studentid) { $studentin = true; break; } } if ($studentin) { // If student took part in that discussion. foreach ($tempusersarray as $tempid) { // Check all participating students. // If temp user id not the same with the student's and is unique, add to pile. if ($tempid->usersid != $studentid && (!isset($distinctUsersFound[$studentid]) || !in_array($tempid->usersid, $distinctUsersFound[$studentid]))) { $distinctUsersFound[$studentid][] = $tempid->usersid; } } } } // Check all students along with the current student evaluated, if there are any. } else { if (!is_null($tempusersarray)) { // Check all participating students in order to create and update // each one's unique pile of interactions. foreach ($tempusersarray as $tempcurrentid) { // loop A // Check all participating students again, to update the pile of the user selected by previous loop. foreach ($tempusersarray as $tempid) { // loop B // If temp user's id (loop B) is unique and not the same with user's id (loop A), // add to pile (of user A). if (isset($tempid->usersid) && isset($tempcurrentid->usersid) && $tempid->usersid != $tempcurrentid->usersid && (!isset($distinctUsersFound[$tempcurrentid->usersid]) || !in_array($tempid->usersid, $distinctUsersFound[$tempcurrentid->usersid]))) { $distinctUsersFound[$tempcurrentid->usersid][] = $tempid->usersid; } } } } } } } else { // Check chat modules. $tempusersarray = $this->get_interacted_users_according_to_chat_sessions($instanceid, $fromtime, $untiltime); if (empty($tempusersarray)) { break; } if ($criterion['referencetype'] == gradingform_erubric_controller::REFERENCE_STUDENT) { // Check only current student evaluated. // Go through all chat sessions to distinct interacted students. foreach ($tempusersarray as $tempsession) { foreach ($tempsession as $tempuserid) { // If temp user id not the same with the student's and is unique, add to pile. if ($tempuserid != $studentid && (!isset($distinctUsersFound[$studentid]) || !in_array($tempuserid, $distinctUsersFound[$studentid]))) { $distinctUsersFound[$studentid][] = $tempuserid; } } } } else { // Check all participating students in order to create and update // each one's unique pile of interactions. foreach ($tempusersarray as $tempsession) { // Iterate through all chat sessions. foreach ($tempsession as $tempuserid1) { // Get users list. foreach ($tempusersarray as $tempuserid2) { // Get users list again. // If temp2 user id not the same with temp1's and is unique, add to temp1's pile. if ($tempuserid1 != $tempuserid2 && (!isset($distinctUsersFound[$tempuserid1]) || !in_array($tempuserid2, $distinctUsersFound[$tempuserid1]))) { $distinctUsersFound[$tempuserid1][] = $tempuserid2; } } } } } } break; } } break; } // When user interactions are checked, benchmarks are calculated after all course modules are processed and only if there are interactions. if ($criterion['collaborationtype'] == gradingform_erubric_controller::COLLABORATION_TYPE_INTERACTIONS && !empty($distinctUsersFound)) { if (array_key_exists($studentid, $distinctUsersFound)) { // Check current student evaluated. $benchmarkstudent = count($distinctUsersFound[$studentid]); } else { $benchmarkstudent = 0; } if ($criterion['referencetype'] == gradingform_erubric_controller::REFERENCE_STUDENTS) { // Check all students participated. $benchmarkstudents = 0; $iterations = count($distinctUsersFound); foreach ($distinctUsersFound as $tempstudentid => $otherstudents) { $benchmarkstudents += count($distinctUsersFound[$tempstudentid]); } } } // If there is a benchmark for current student evaluated, calculate the criterion benchmark. Else do nothing, leave everything null. if (!is_null($benchmarkstudent)) { if ($criterion['collaborationtype'] != gradingform_erubric_controller::COLLABORATION_TYPE_INTERACTIONS && ($criterion['criteriontype'] == gradingform_erubric_controller::INTERACTION_TYPE_GRADE || $criterion['referencetype'] == gradingform_erubric_controller::REFERENCE_STUDENTS)) { $benchmarkstudent = (double) round($benchmarkstudent / $iterations, 2); } if ($criterion['referencetype'] == gradingform_erubric_controller::REFERENCE_STUDENTS) { // Check students benchmark. if ($benchmarkstudents) { // To avoid division by zero for small students' benchmark, make two point precision rounding. $benchmarkstudents = (double) round($benchmarkstudents / $iterations, 2); $benchmarkcriterion = (int) round($benchmarkstudent * 100 / $benchmarkstudents); } else { $benchmarkcriterion = null; } } else { $benchmarkcriterion = (int) $benchmarkstudent; // Just convert student benchmark to integer. } } // Run enrichment procedure to return the appropriate selected level (if exists), // and store criterion benchmarks for future reference. $criterion['checkedenrich'] = $this->get_enriched_level_from_benchmark($criterion['levels'], $benchmarkcriterion, $criterion['operator'], $options['sortlevelsasc']); $criterion['enrichedbenchmark'] = $benchmarkcriterion; $criterion['enrichedbenchmarkstudent'] = $benchmarkstudent; $criterion['enrichedbenchmarkstudents'] = $benchmarkstudents; }