PageRenderTime 50ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/mod/scorm/report/basic/report.php

https://bitbucket.org/moodle/moodle
PHP | 545 lines | 447 code | 33 blank | 65 comment | 112 complexity | 5c1a2d37639a92658e6e24d3f78a67a9 MD5 | raw file
Possible License(s): Apache-2.0, LGPL-2.1, BSD-3-Clause, MIT, GPL-3.0
  1. <?php
  2. // This file is part of Moodle - http://moodle.org/
  3. //
  4. // Moodle is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // Moodle is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
  16. /**
  17. * Core Report class of basic reporting plugin
  18. * @package scormreport
  19. * @subpackage basic
  20. * @author Dan Marsden and Ankit Kumar Agarwal
  21. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  22. */
  23. defined('MOODLE_INTERNAL') || die();
  24. require_once($CFG->libdir . '/csvlib.class.php');
  25. class scorm_basic_report extends scorm_default_report {
  26. /**
  27. * displays the full report
  28. * @param stdClass $scorm full SCORM object
  29. * @param stdClass $cm - full course_module object
  30. * @param stdClass $course - full course object
  31. * @param string $download - type of download being requested
  32. */
  33. function display($scorm, $cm, $course, $download) {
  34. global $CFG, $DB, $OUTPUT, $PAGE;
  35. $contextmodule= context_module::instance($cm->id);
  36. $action = optional_param('action', '', PARAM_ALPHA);
  37. $attemptids = optional_param_array('attemptid', array(), PARAM_RAW);
  38. $attemptsmode = optional_param('attemptsmode', SCORM_REPORT_ATTEMPTS_ALL_STUDENTS, PARAM_INT);
  39. $PAGE->set_url(new moodle_url($PAGE->url, array('attemptsmode' => $attemptsmode)));
  40. if ($action == 'delete' && has_capability('mod/scorm:deleteresponses', $contextmodule) && confirm_sesskey()) {
  41. if (scorm_delete_responses($attemptids, $scorm)) { //delete responses.
  42. add_to_log($course->id, 'scorm', 'delete attempts', 'report.php?id=' . $cm->id, implode(",", $attemptids), $cm->id);
  43. echo $OUTPUT->notification(get_string('scormresponsedeleted', 'scorm'), 'notifysuccess');
  44. }
  45. }
  46. // find out current groups mode
  47. $currentgroup = groups_get_activity_group($cm, true);
  48. // detailed report
  49. $mform = new mod_scorm_report_settings($PAGE->url, compact('currentgroup'));
  50. if ($fromform = $mform->get_data()) {
  51. $detailedrep = $fromform->detailedrep;
  52. $pagesize = $fromform->pagesize;
  53. set_user_preference('scorm_report_detailed', $detailedrep);
  54. set_user_preference('scorm_report_pagesize', $pagesize);
  55. } else {
  56. $detailedrep = get_user_preferences('scorm_report_detailed', false);
  57. $pagesize = get_user_preferences('scorm_report_pagesize', 0);
  58. }
  59. if ($pagesize < 1) {
  60. $pagesize = SCORM_REPORT_DEFAULT_PAGE_SIZE;
  61. }
  62. // select group menu
  63. $displayoptions = array();
  64. $displayoptions['attemptsmode'] = $attemptsmode;
  65. if ($groupmode = groups_get_activity_groupmode($cm)) { // Groups are being used
  66. if (!$download) {
  67. groups_print_activity_menu($cm, new moodle_url($PAGE->url, $displayoptions));
  68. }
  69. }
  70. // We only want to show the checkbox to delete attempts
  71. // if the user has permissions and if the report mode is showing attempts.
  72. $candelete = has_capability('mod/scorm:deleteresponses', $contextmodule)
  73. && ($attemptsmode!= SCORM_REPORT_ATTEMPTS_STUDENTS_WITH_NO);
  74. // select the students
  75. $nostudents = false;
  76. if (empty($currentgroup)) {
  77. // all users who can attempt scoes
  78. if (!$students = get_users_by_capability($contextmodule, 'mod/scorm:savetrack', 'u.id', '', '', '', '', '', false)) {
  79. echo $OUTPUT->notification(get_string('nostudentsyet'));
  80. $nostudents = true;
  81. $allowedlist = '';
  82. } else {
  83. $allowedlist = array_keys($students);
  84. }
  85. unset($students);
  86. } else {
  87. // all users who can attempt scoes and who are in the currently selected group
  88. if (!$groupstudents = get_users_by_capability($contextmodule, 'mod/scorm:savetrack', 'u.id', '', '', '', $currentgroup, '', false)) {
  89. echo $OUTPUT->notification(get_string('nostudentsingroup'));
  90. $nostudents = true;
  91. $groupstudents = array();
  92. }
  93. $allowedlist = array_keys($groupstudents);
  94. unset($groupstudents);
  95. }
  96. if ( !$nostudents ) {
  97. // Now check if asked download of data
  98. $coursecontext = context_course::instance($course->id);
  99. if ($download) {
  100. $shortname = format_string($course->shortname, true, array('context' => $coursecontext));
  101. $filename = clean_filename("$shortname ".format_string($scorm->name, true));
  102. }
  103. // Define table columns
  104. $columns = array();
  105. $headers = array();
  106. if (!$download && $candelete) {
  107. $columns[]= 'checkbox';
  108. $headers[]= null;
  109. }
  110. if (!$download && $CFG->grade_report_showuserimage) {
  111. $columns[]= 'picture';
  112. $headers[]= '';
  113. }
  114. $columns[]= 'fullname';
  115. $headers[]= get_string('name');
  116. $extrafields = get_extra_user_fields($coursecontext);
  117. foreach ($extrafields as $field) {
  118. $columns[] = $field;
  119. $headers[] = get_user_field_name($field);
  120. }
  121. $columns[]= 'attempt';
  122. $headers[]= get_string('attempt', 'scorm');
  123. $columns[]= 'start';
  124. $headers[]= get_string('started', 'scorm');
  125. $columns[]= 'finish';
  126. $headers[]= get_string('last', 'scorm');
  127. $columns[]= 'score';
  128. $headers[]= get_string('score', 'scorm');
  129. if ($detailedrep && $scoes = $DB->get_records('scorm_scoes', array("scorm"=>$scorm->id), 'sortorder, id')) {
  130. foreach ($scoes as $sco) {
  131. if ($sco->launch!='') {
  132. $columns[]= 'scograde'.$sco->id;
  133. $headers[]= format_string($sco->title);
  134. }
  135. }
  136. } else {
  137. $scoes = null;
  138. }
  139. if (!$download) {
  140. $table = new flexible_table('mod-scorm-report');
  141. $table->define_columns($columns);
  142. $table->define_headers($headers);
  143. $table->define_baseurl($PAGE->url);
  144. $table->sortable(true);
  145. $table->collapsible(true);
  146. // This is done to prevent redundant data, when a user has multiple attempts
  147. $table->column_suppress('picture');
  148. $table->column_suppress('fullname');
  149. foreach ($extrafields as $field) {
  150. $table->column_suppress($field);
  151. }
  152. $table->no_sorting('start');
  153. $table->no_sorting('finish');
  154. $table->no_sorting('score');
  155. if ( $scoes ) {
  156. foreach ($scoes as $sco) {
  157. if ($sco->launch!='') {
  158. $table->no_sorting('scograde'.$sco->id);
  159. }
  160. }
  161. }
  162. $table->column_class('picture', 'picture');
  163. $table->column_class('fullname', 'bold');
  164. $table->column_class('score', 'bold');
  165. $table->set_attribute('cellspacing', '0');
  166. $table->set_attribute('id', 'attempts');
  167. $table->set_attribute('class', 'generaltable generalbox');
  168. // Start working -- this is necessary as soon as the niceties are over
  169. $table->setup();
  170. } else if ($download =='ODS') {
  171. require_once("$CFG->libdir/odslib.class.php");
  172. $filename .= ".ods";
  173. // Creating a workbook
  174. $workbook = new MoodleODSWorkbook("-");
  175. // Sending HTTP headers
  176. $workbook->send($filename);
  177. // Creating the first worksheet
  178. $sheettitle = get_string('report', 'scorm');
  179. $myxls = $workbook->add_worksheet($sheettitle);
  180. // format types
  181. $format = $workbook->add_format();
  182. $format->set_bold(0);
  183. $formatbc = $workbook->add_format();
  184. $formatbc->set_bold(1);
  185. $formatbc->set_align('center');
  186. $formatb = $workbook->add_format();
  187. $formatb->set_bold(1);
  188. $formaty = $workbook->add_format();
  189. $formaty->set_bg_color('yellow');
  190. $formatc = $workbook->add_format();
  191. $formatc->set_align('center');
  192. $formatr = $workbook->add_format();
  193. $formatr->set_bold(1);
  194. $formatr->set_color('red');
  195. $formatr->set_align('center');
  196. $formatg = $workbook->add_format();
  197. $formatg->set_bold(1);
  198. $formatg->set_color('green');
  199. $formatg->set_align('center');
  200. // Here starts workshhet headers
  201. $colnum = 0;
  202. foreach ($headers as $item) {
  203. $myxls->write(0, $colnum, $item, $formatbc);
  204. $colnum++;
  205. }
  206. $rownum=1;
  207. } else if ($download =='Excel') {
  208. require_once("$CFG->libdir/excellib.class.php");
  209. $filename .= ".xls";
  210. // Creating a workbook
  211. $workbook = new MoodleExcelWorkbook("-");
  212. // Sending HTTP headers
  213. $workbook->send($filename);
  214. // Creating the first worksheet
  215. $sheettitle = get_string('report', 'scorm');
  216. $myxls = $workbook->add_worksheet($sheettitle);
  217. // format types
  218. $format = $workbook->add_format();
  219. $format->set_bold(0);
  220. $formatbc = $workbook->add_format();
  221. $formatbc->set_bold(1);
  222. $formatbc->set_align('center');
  223. $formatb = $workbook->add_format();
  224. $formatb->set_bold(1);
  225. $formaty = $workbook->add_format();
  226. $formaty->set_bg_color('yellow');
  227. $formatc = $workbook->add_format();
  228. $formatc->set_align('center');
  229. $formatr = $workbook->add_format();
  230. $formatr->set_bold(1);
  231. $formatr->set_color('red');
  232. $formatr->set_align('center');
  233. $formatg = $workbook->add_format();
  234. $formatg->set_bold(1);
  235. $formatg->set_color('green');
  236. $formatg->set_align('center');
  237. $colnum = 0;
  238. foreach ($headers as $item) {
  239. $myxls->write(0, $colnum, $item, $formatbc);
  240. $colnum++;
  241. }
  242. $rownum=1;
  243. } else if ($download=='CSV') {
  244. $csvexport = new csv_export_writer("tab");
  245. $csvexport->set_filename($filename, ".txt");
  246. $csvexport->add_data($headers);
  247. }
  248. $params = array();
  249. list($usql, $params) = $DB->get_in_or_equal($allowedlist, SQL_PARAMS_NAMED);
  250. // Construct the SQL
  251. $select = 'SELECT DISTINCT '.$DB->sql_concat('u.id', '\'#\'', 'COALESCE(st.attempt, 0)').' AS uniqueid, ';
  252. $select .= 'st.scormid AS scormid, st.attempt AS attempt, ' .
  253. user_picture::fields('u', array('idnumber'), 'userid') .
  254. get_extra_user_fields_sql($coursecontext, 'u', '', array('email', 'idnumber')) . ' ';
  255. // This part is the same for all cases - join users and scorm_scoes_track tables
  256. $from = 'FROM {user} u ';
  257. $from .= 'LEFT JOIN {scorm_scoes_track} st ON st.userid = u.id AND st.scormid = '.$scorm->id;
  258. switch ($attemptsmode) {
  259. case SCORM_REPORT_ATTEMPTS_STUDENTS_WITH:
  260. // Show only students with attempts
  261. $where = ' WHERE u.id ' .$usql. ' AND st.userid IS NOT NULL';
  262. break;
  263. case SCORM_REPORT_ATTEMPTS_STUDENTS_WITH_NO:
  264. // Show only students without attempts
  265. $where = ' WHERE u.id ' .$usql. ' AND st.userid IS NULL';
  266. break;
  267. case SCORM_REPORT_ATTEMPTS_ALL_STUDENTS:
  268. // Show all students with or without attempts
  269. $where = ' WHERE u.id ' .$usql. ' AND (st.userid IS NOT NULL OR st.userid IS NULL)';
  270. break;
  271. }
  272. $countsql = 'SELECT COUNT(DISTINCT('.$DB->sql_concat('u.id', '\'#\'', 'COALESCE(st.attempt, 0)').')) AS nbresults, ';
  273. $countsql .= 'COUNT(DISTINCT('.$DB->sql_concat('u.id', '\'#\'', 'st.attempt').')) AS nbattempts, ';
  274. $countsql .= 'COUNT(DISTINCT(u.id)) AS nbusers ';
  275. $countsql .= $from.$where;
  276. if (!$download) {
  277. $sort = $table->get_sql_sort();
  278. } else {
  279. $sort = '';
  280. }
  281. // Fix some wired sorting
  282. if (empty($sort)) {
  283. $sort = ' ORDER BY uniqueid';
  284. } else {
  285. $sort = ' ORDER BY '.$sort;
  286. }
  287. if (!$download) {
  288. // Add extra limits due to initials bar
  289. list($twhere, $tparams) = $table->get_sql_where();
  290. if ($twhere) {
  291. $where .= ' AND '.$twhere; //initial bar
  292. $params = array_merge($params, $tparams);
  293. }
  294. if (!empty($countsql)) {
  295. $count = $DB->get_record_sql($countsql, $params);
  296. $totalinitials = $count->nbresults;
  297. if ($twhere) {
  298. $countsql .= ' AND '.$twhere;
  299. }
  300. $count = $DB->get_record_sql($countsql, $params);
  301. $total = $count->nbresults;
  302. }
  303. $table->pagesize($pagesize, $total);
  304. echo '<div class="quizattemptcounts">';
  305. if ( $count->nbresults == $count->nbattempts ) {
  306. echo get_string('reportcountattempts', 'scorm', $count);
  307. } else if ( $count->nbattempts>0 ) {
  308. echo get_string('reportcountallattempts', 'scorm', $count);
  309. } else {
  310. echo $count->nbusers.' '.get_string('users');
  311. }
  312. echo '</div>';
  313. }
  314. // Fetch the attempts
  315. if (!$download) {
  316. $attempts = $DB->get_records_sql($select.$from.$where.$sort, $params,
  317. $table->get_page_start(), $table->get_page_size());
  318. echo '<div id="scormtablecontainer">';
  319. if ($candelete) {
  320. // Start form
  321. $strreallydel = addslashes_js(get_string('deleteattemptcheck', 'scorm'));
  322. echo '<form id="attemptsform" method="post" action="' . $PAGE->url->out(false) .
  323. '" onsubmit="return confirm(\''.$strreallydel.'\');">';
  324. echo '<input type="hidden" name="action" value="delete"/>';
  325. echo '<input type="hidden" name="sesskey" value="'.sesskey().'" />';
  326. echo '<div style="display: none;">';
  327. echo html_writer::input_hidden_params($PAGE->url);
  328. echo '</div>';
  329. echo '<div>';
  330. }
  331. $table->initialbars($totalinitials>20); // Build table rows
  332. } else {
  333. $attempts = $DB->get_records_sql($select.$from.$where.$sort, $params);
  334. }
  335. if ($attempts) {
  336. foreach ($attempts as $scouser) {
  337. $row = array();
  338. if (!empty($scouser->attempt)) {
  339. $timetracks = scorm_get_sco_runtime($scorm->id, false, $scouser->userid, $scouser->attempt);
  340. } else {
  341. $timetracks = '';
  342. }
  343. if (in_array('checkbox', $columns)) {
  344. if ($candelete && !empty($timetracks->start)) {
  345. $row[] = '<input type="checkbox" name="attemptid[]" value="'. $scouser->userid . ':' . $scouser->attempt . '" />';
  346. } else if ($candelete) {
  347. $row[] = '';
  348. }
  349. }
  350. if (in_array('picture', $columns)) {
  351. $user = (object)array(
  352. 'id'=>$scouser->userid,
  353. 'picture'=>$scouser->picture,
  354. 'imagealt'=>$scouser->imagealt,
  355. 'email'=>$scouser->email);
  356. foreach (get_all_user_name_fields() as $addname) {
  357. $user->$addname = $scouser->$addname;
  358. }
  359. $row[] = $OUTPUT->user_picture($user, array('courseid'=>$course->id));
  360. }
  361. if (!$download) {
  362. $row[] = '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$scouser->userid.'&amp;course='.$course->id.'">'.fullname($scouser).'</a>';
  363. } else {
  364. $row[] = fullname($scouser);
  365. }
  366. foreach ($extrafields as $field) {
  367. $row[] = s($scouser->{$field});
  368. }
  369. if (empty($timetracks->start)) {
  370. $row[] = '-';
  371. $row[] = '-';
  372. $row[] = '-';
  373. $row[] = '-';
  374. } else {
  375. if (!$download) {
  376. $row[] = '<a href="'.$CFG->wwwroot.'/mod/scorm/report/userreport.php?id='.$cm->id.'&amp;user='.$scouser->userid.'&amp;attempt='.$scouser->attempt.'">'.$scouser->attempt.'</a>';
  377. } else {
  378. $row[] = $scouser->attempt;
  379. }
  380. if ($download =='ODS' || $download =='Excel' ) {
  381. $row[] = userdate($timetracks->start, get_string("strftimedatetime", "langconfig"));
  382. } else {
  383. $row[] = userdate($timetracks->start);
  384. }
  385. if ($download =='ODS' || $download =='Excel' ) {
  386. $row[] = userdate($timetracks->finish, get_string('strftimedatetime', 'langconfig'));
  387. } else {
  388. $row[] = userdate($timetracks->finish);
  389. }
  390. $row[] = scorm_grade_user_attempt($scorm, $scouser->userid, $scouser->attempt);
  391. }
  392. // print out all scores of attempt
  393. if ($scoes) {
  394. foreach ($scoes as $sco) {
  395. if ($sco->launch!='') {
  396. if ($trackdata = scorm_get_tracks($sco->id, $scouser->userid, $scouser->attempt)) {
  397. if ($trackdata->status == '') {
  398. $trackdata->status = 'notattempted';
  399. }
  400. $strstatus = get_string($trackdata->status, 'scorm');
  401. // if raw score exists, print it
  402. if ($trackdata->score_raw != '') {
  403. $score = $trackdata->score_raw;
  404. // add max score if it exists
  405. if (scorm_version_check($scorm->version, SCORM_13)) {
  406. $maxkey = 'cmi.score.max';
  407. } else {
  408. $maxkey = 'cmi.core.score.max';
  409. }
  410. if (isset($trackdata->$maxkey)) {
  411. $score .= '/'.$trackdata->$maxkey;
  412. }
  413. // else print out status
  414. } else {
  415. $score = $strstatus;
  416. }
  417. if (!$download) {
  418. $row[] = '<img src="'.$OUTPUT->pix_url($trackdata->status, 'scorm').'" alt="'.$strstatus.'" title="'.$strstatus.'" /><br/>
  419. <a href="'.$CFG->wwwroot.'/mod/scorm/report/userreporttracks.php?id='.$cm->id.'&amp;scoid='.$sco->id.'&amp;user='.$scouser->userid.
  420. '&amp;attempt='.$scouser->attempt.'" title="'.get_string('details', 'scorm').'">'.$score.'</a>';
  421. } else {
  422. $row[] = $score;
  423. }
  424. } else {
  425. // if we don't have track data, we haven't attempted yet
  426. $strstatus = get_string('notattempted', 'scorm');
  427. if (!$download) {
  428. $row[] = '<img src="'.$OUTPUT->pix_url('notattempted', 'scorm').'" alt="'.$strstatus.'" title="'.$strstatus.'" /><br/>'.$strstatus;
  429. } else {
  430. $row[] = $strstatus;
  431. }
  432. }
  433. }
  434. }
  435. }
  436. if (!$download) {
  437. $table->add_data($row);
  438. } else if ($download == 'Excel' or $download == 'ODS') {
  439. $colnum = 0;
  440. foreach ($row as $item) {
  441. $myxls->write($rownum, $colnum, $item, $format);
  442. $colnum++;
  443. }
  444. $rownum++;
  445. } else if ($download == 'CSV') {
  446. $csvexport->add_data($row);
  447. }
  448. }
  449. if (!$download) {
  450. $table->finish_output();
  451. if ($candelete) {
  452. echo '<table id="commands">';
  453. echo '<tr><td>';
  454. echo '<a href="javascript:select_all_in(\'DIV\', null, \'scormtablecontainer\');">'.
  455. get_string('selectall', 'scorm').'</a> / ';
  456. echo '<a href="javascript:deselect_all_in(\'DIV\', null, \'scormtablecontainer\');">'.
  457. get_string('selectnone', 'scorm').'</a> ';
  458. echo '&nbsp;&nbsp;';
  459. echo '<input type="submit" value="'.get_string('deleteselected', 'quiz_overview').'"/>';
  460. echo '</td></tr></table>';
  461. // Close form
  462. echo '</div>';
  463. echo '</form>';
  464. }
  465. echo '</div>';
  466. if (!empty($attempts)) {
  467. echo '<table class="boxaligncenter"><tr>';
  468. echo '<td>';
  469. echo $OUTPUT->single_button(new moodle_url($PAGE->url,
  470. array('download'=>'ODS') + $displayoptions),
  471. get_string('downloadods'));
  472. echo "</td>\n";
  473. echo '<td>';
  474. echo $OUTPUT->single_button(new moodle_url($PAGE->url,
  475. array('download'=>'Excel') + $displayoptions),
  476. get_string('downloadexcel'));
  477. echo "</td>\n";
  478. echo '<td>';
  479. echo $OUTPUT->single_button(new moodle_url($PAGE->url,
  480. array('download'=>'CSV') + $displayoptions),
  481. get_string('downloadtext'));
  482. echo "</td>\n";
  483. echo "<td>";
  484. echo "</td>\n";
  485. echo '</tr></table>';
  486. }
  487. }
  488. } else {
  489. if ($candelete && !$download) {
  490. echo '</div>';
  491. echo '</form>';
  492. $table->finish_output();
  493. }
  494. echo '</div>';
  495. }
  496. // Show preferences form irrespective of attempts are there to report or not
  497. if (!$download) {
  498. $mform->set_data(compact('detailedrep', 'pagesize', 'attemptsmode'));
  499. $mform->display();
  500. }
  501. if ($download == 'Excel' or $download == 'ODS') {
  502. $workbook->close();
  503. exit;
  504. } else if ($download == 'CSV') {
  505. $csvexport->download_file();
  506. exit;
  507. }
  508. } else {
  509. echo $OUTPUT->notification(get_string('noactivity', 'scorm'));
  510. }
  511. }// function ends
  512. }