diff --git a/lang/en/vpl.php b/lang/en/vpl.php index 1cb1e084d1b60a0964ba3992f7735361bc561ca6..3fc71bb31153fab287cb9af845fa541cea19fb57 100644 --- a/lang/en/vpl.php +++ b/lang/en/vpl.php @@ -100,6 +100,8 @@ $string ['filenotrenamed'] = 'The \'{$a}\' file has NOT been renamed'; $string ['filerenamed'] = "The '{\$a->from}' file has been renamed to '{\$a->to}'"; $string ['filesChangedNotSaved'] = 'Files have changed but they have not been saved'; $string ['filesNotChanged'] = 'Files have not changed'; +$string ['filesdiffevolution'] = 'Files diff with required files evolution'; +$string ['filessizeevolution'] = 'Files size evolution'; $string ['filestoscan'] = 'Files to scan'; $string ['fileupdated'] = "The '{\$a}' file has been updated"; $string ['finalreduction'] = 'Final reduction'; diff --git a/locallib.php b/locallib.php index 9c92e2abf4e7d2bd1eb4d28acb9c1354c416a88c..33fb15fe1afbfd4ee3448432c515a0c968fdc938 100644 --- a/locallib.php +++ b/locallib.php @@ -849,7 +849,7 @@ function vpl_get_version() { static $version; if (! isset( $version )) { $plugin = new stdClass(); - require_once(dirname( __FILE__ ) . '/version.php'); + require(dirname( __FILE__ ) . '/version.php'); $version = $plugin->release; } return $version; @@ -936,3 +936,7 @@ function vpl_get_webservice_urlbase($vpl) { return $CFG->wwwroot . '/mod/vpl/webservice.php?moodlewsrestformat=json' . '&wstoken=' . $token . '&id=' . $vpl->get_course_module()->id . '&wsfunction='; } + +function vpl_timestamp_to_midnight($timestamp) { + return $timestamp - ($timestamp + date('Z')) % 86400; +} \ No newline at end of file diff --git a/similarity/diff.class.php b/similarity/diff.class.php index 0b4849c8115693b9db865b7a029405175e99a81e..709df0850f57dc9fc2469a6040fecfb854ea8015 100644 --- a/similarity/diff.class.php +++ b/similarity/diff.class.php @@ -185,8 +185,7 @@ class vpl_diff { } if ($detaileddiff) { $prize = self::diffline( $line, $lines2 [$j - 1] ); - } - else { + } else { $prize = $line === $lines2 [$j - 1] ? 1 : 0; } if ($matrix [$i - 1] [$j - 1] + $prize >= $max) { @@ -393,4 +392,111 @@ class vpl_diff { print_error( 'type error' ); } } + + /** + * Computes diff between two lines. + * @param string $line1 + * @param string $line2 + * @return int The diff in number of chars. + */ + static public function compute_linediff($line1, $line2) { + $n1 = strlen($line1); + $n2 = strlen($line2); + if ($n1 == 0 || $n2 == 0) { + return $n1 + $n2; + } + $queue = array_fill(0, $n1 + $n2 + 1, array()); + $done = array_fill(0, $n1 + 1, array_fill(0, $n2 + 1, false)); + $z = new stdClass(); + $z->i1 = 0; + $z->i2 = 0; + $z->d = 0; + $queue[0][] = $z; + $priority = 0; + + // A-star search of shortest diff. + while (true) { + while (count($queue[$priority]) == 0) { + $priority++; + } + $z = array_pop($queue[$priority]); + $i1 = $z->i1; + $i2 = $z->i2; + if ($done[$i1][$i2]) { + continue; + } else { + $done[$i1][$i2] = true; + } + + // Identical substring: 0 distance. + while ($i1 < $n1 && $i2 < $n2 && $line1{$i1} == $line2{$i2}) { + $i1++; + $i2++; + } + + if ($i1 == $n1 && $i2 == $n2) { + // Found shortest diff. + return $z->d; + } + + // Character addition in line1. + if ($i1 < $n1) { + $z1 = new stdClass(); + $z1->i1 = $i1 + 1; + $z1->i2 = $i2; + $z1->d = $z->d + 1; + $queue[$z1->d + abs(($n1 - $z1->i1) - ($n2 - $z1->i2))][] = $z1; + } + + // Character addition in line2. + if ($i2 < $n2) { + $z2 = new stdClass(); + $z2->i1 = $i1; + $z2->i2 = $i2 + 1; + $z2->d = $z->d + 1; + $queue[$z2->d + abs(($n1 - $z2->i1) - ($n2 - $z2->i2))][] = $z2; + } + + // Character change. + if ($i1 < $n1 && $i2 < $n2) { + $z3 = new stdClass(); + $z3->i1 = $i1 + 1; + $z3->i2 = $i2 + 1; + $z3->d = $z->d + 1; + $queue[$z3->d + abs(($n1 - $z3->i1) - ($n2 - $z3->i2))][] = $z3; + } + } + } + + /** + * Computes diff between two files. + * @param string $filedata1 + * @param string $filedata2 + * @return int The diff in number of chars. + */ + static public function compute_filediff($filedata1, $filedata2) { + if (strlen($filedata1) == 0 || strlen($filedata2) == 0) { + return strlen($filedata1) + strlen($filedata2); + } + $lines1 = explode("\n", $filedata1); + $lines2 = explode("\n", $filedata2); + $linesdiff = self::calculatediff($lines1, $lines2, false); + $totaldiff = 0; + foreach ($linesdiff as $diff) { + switch ($diff->type) { + case '=' : + break; + case '<' : + $totaldiff += strlen( $lines1[$diff->ln1 - 1] ) + 1; + break; + case '>' : + $totaldiff += strlen( $lines2[$diff->ln2 - 1] ) + 1; + break; + default : + $totaldiff += self::compute_linediff( $lines1[$diff->ln1 - 1], $lines2[$diff->ln2 - 1] ); + break; + } + } + return $totaldiff; + } } diff --git a/styles.css b/styles.css index ac3fbc14562018829072b6cd62cbe7b7edd874e9..d2fc4151a2580b506ef91f1226e4623851bb0d16 100644 --- a/styles.css +++ b/styles.css @@ -1,3 +1,18 @@ +/* ############################## */ +/* ##### Submissions charts ##### */ +/* ############################## */ + +.path-mod-vpl.pagelayout-incourse .chart-area { + width: 50%; + height: 50%; + display: inline-block; +} + +.path-mod-vpl .chart-area canvas { + width: 100%; + height: 100%; +} + /* ############################## */ /* # Submissions list highlight # */ /* ############################## */ diff --git a/views/activityworkinggraph.php b/views/activityworkinggraph.php index aaae10d403608cd225c681df72abfcfcc674e120..f69eb80a3d7fd4bfdd8f9251132f7f3b3dc58d23 100644 --- a/views/activityworkinggraph.php +++ b/views/activityworkinggraph.php @@ -26,7 +26,7 @@ require_once(dirname(__FILE__).'/../../../config.php'); require_once(dirname(__FILE__).'/../locallib.php'); require_once(dirname(__FILE__).'/../vpl.class.php'); -require_once(dirname(__FILE__).'/workinggraph.php'); +require_once(dirname(__FILE__).'/vpl_grapher.class.php'); require_login(); $id = required_param( 'id', PARAM_INT ); $vpl = new mod_vpl( $id ); @@ -34,11 +34,10 @@ $vpl->prepare_page( 'views/activityworkinggraph.php', array ( 'id' => $id ) ); -$course = $vpl->get_course(); -$instance = $vpl->get_instance(); $vpl->require_capability( VPL_GRADE_CAPABILITY ); -$vpl->print_header( get_string( 'timespent', VPL ) ); -$vpl->print_heading_with_help( 'timespent' ); -vpl_working_periods_graph($vpl, -1); +$vpl->print_header_simple(); +$grapher = new vpl_grapher($vpl); +$grapher->draw_working_periods_graph(); +$grapher->draw_daily_activity_graph(); $vpl->print_footer_simple(); diff --git a/views/previoussubmissionslist.php b/views/previoussubmissionslist.php index fef1aa5106a780796e17166e367c5a76ba441d70..c08d9d85b6695dff81f94d4ec6eb3df9773e9f17 100644 --- a/views/previoussubmissionslist.php +++ b/views/previoussubmissionslist.php @@ -27,29 +27,29 @@ require_once(dirname(__FILE__).'/../../../config.php'); require_once(dirname(__FILE__).'/../locallib.php'); require_once(dirname(__FILE__).'/../vpl.class.php'); require_once(dirname(__FILE__).'/../vpl_submission.class.php'); +require_once(dirname(__FILE__).'/vpl_grapher.class.php'); require_login(); $id = required_param( 'id', PARAM_INT ); $userid = required_param( 'userid', PARAM_INT ); -$detailed = abs( optional_param( 'detailed', 0, PARAM_INT ) ) % 2; +$detailed = optional_param( 'detailed', 0, PARAM_INT ); $vpl = new mod_vpl( $id ); $vpl->prepare_page( 'views/previoussubmissionslist.php', array ( 'id' => $id, 'userid' => $userid ) ); -$course = $vpl->get_course(); -if($USER->id != $userid /*|| !$vpl->get_instance()->allowshowprevious*/){ //Not owner +global $USER, $OUTPUT, $DB; + +if ($USER->id != $userid /*|| !$vpl->get_instance()->allowshowprevious*/) { $vpl->require_capability(VPL_GRADE_CAPABILITY); - } +} \mod_vpl\event\submission_previous_upload_viewed::log( array ( 'objectid' => $vpl->get_instance()->id, 'context' => context_module::instance( $id ), 'relateduserid' => $userid ) ); -$strdatesubmitted = get_string( 'datesubmitted', VPL ); -$strdescription = get_string( 'description', VPL ); if ($detailed) { require_once(dirname(__FILE__).'/../views/sh_factory.class.php'); vpl_sh_factory::include_js(); @@ -58,18 +58,27 @@ if ($detailed) { $vpl->print_header( get_string( 'previoussubmissionslist', VPL ) ); $vpl->print_view_tabs( basename( __FILE__ ) ); +$grapher = new vpl_grapher($vpl, $userid); +$grapher->draw_files_evolution_graph(); +$grapher->draw_working_periods_graph(); +$grapher->draw_files_evolution_graph(true); +$grapher->draw_daily_activity_graph(); + $table = new html_table(); $table->head = array ( '#', - $strdatesubmitted, - $strdescription + get_string( 'datesubmitted', VPL ), + get_string( 'grade' ), + get_string( 'files' ) ); $table->align = array ( 'right', 'left', + 'left', 'right' ); $table->nowrap = array ( + true, true, true, true @@ -79,58 +88,56 @@ $submissions = array (); $nsub = count( $submissionslist ); foreach ($submissionslist as $submission) { if ($detailed) { - $link = '#f' . $nsub; + $href = '#f' . $nsub; + } else { + $href = vpl_mod_href( 'forms/submissionview.php', 'id', $id, 'userid', $userid, 'submissionid', $submission->id ); + } + $text = userdate($submission->datesubmitted); + $link = '<a href="' . $href . '"'; + if ($submission->grader != 0) { + // Highlight submission as graded. + $gradingdetails = new stdClass(); + $gradingdetails->date = userdate($submission->dategraded, get_string('strftimedate', 'langconfig')); + $grader = $DB->get_record('user', array( 'id' => $submission->grader )); + $gradingdetails->gradername = $vpl->fullname($grader, false); + + $text .= ' (' . get_string('gradedonby', VPL, $gradingdetails) . ')'; + $link .= ' style="color: red;"'; + } + $link .= '>' . $text . '</a>'; + if ($submission->dategraded > 0) { + $grade = $vpl->format_grade($submission->grade); } else { - $link = vpl_mod_href( 'forms/submissionview.php', 'id', $id, 'userid', $userid, 'submissionid', $submission->id ); + $grade = get_string( 'nograde' ); } - //$date = '<a href="' . $link . '">' . userdate( $submission->datesubmitted ) . '</a>'; - if($submission->grader != 0) { - $date = '<a href="'.$link.'" style="color: rgb(255,0,0)">'.userdate($submission->datesubmitted).': Evaluated</a>'; - } - else { - $date = '<a href="'.$link.'">'.userdate($submission->datesubmitted).'</a>'; - } - $sub = new mod_vpl_submission( $vpl, $submission ); + $sub = new mod_vpl_submission( $vpl, $submission ); $submissions [] = $sub; $table->data [] = array ( $nsub --, - $date, + $link, + $grade, s( $sub->get_detail() ) ); } -echo '<div class="clearer"> </div>'; -echo '<div style="text-align: center">'; -echo '<img src="' . vpl_rel_url( 'submissionsgraph.php', 'id', $id, 'userid', $userid ) . '" alt="files size evolution" />'; -echo '</div>'; -echo '<div class="clearer"> </div>'; -echo '<div class="clearer"> </div>'; -echo '<div style="text-align: center">'; -echo '<img src="' . vpl_rel_url( 'workinggraph.php', 'id', $id, 'userid', $userid ) . '" alt="workingperiods" />'; -echo '</div>'; -echo '<div class="clearer"> </div>'; echo html_writer::table( $table ); -echo '<div style="text-align:center">'; -$urlbase = $CFG->wwwroot . '/mod/vpl/views/previoussubmissionslist.php?id=' . $id . '&userid=' . $userid . '&detailed='; + +$urlbase = vpl_mod_href('views/previoussubmissionslist.php', 'id', $id, 'userid', $userid); $urls = array ( - $urlbase . '0', - $urlbase . '1' + vpl_url_add_param($urlbase, 'detailed', 0), + vpl_url_add_param($urlbase, 'detailed', 1) ); echo $OUTPUT->url_select( array ( $urls [0] => get_string( 'detailedless' ), $urls [1] => get_string( 'detailedmore' ) -), $urls [$detailed] ); -echo '</div>'; +), $urls [$detailed], null ); if ($detailed) { $nsub = count( $submissionslist ); - foreach ($submissions as $index => $sub) { + foreach ($submissions as $sub) { echo '<hr><h2><a name="f' . $nsub . '"># ' . $nsub . '</a></h2>'; - $nsub --; $sub->print_submission(); } -} -$vpl->print_footer(); -if ($detailed) { vpl_sh_factory::syntaxhighlight(); } +$vpl->print_footer(); diff --git a/views/submissionsgraph.php b/views/submissionsgraph.php deleted file mode 100644 index 348e854743bbd2e257180badb1526819762340fc..0000000000000000000000000000000000000000 --- a/views/submissionsgraph.php +++ /dev/null @@ -1,102 +0,0 @@ -<?php -// This file is part of VPL for Moodle - http://vpl.dis.ulpgc.es/ -// -// VPL for Moodle is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// VPL for Moodle is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with VPL for Moodle. If not, see <http://www.gnu.org/licenses/>. - -/** - * Graph submissions statistics for a vpl instance and a user - * - * @package mod_vpl - * @copyright 2012 onwards Juan Carlos RodrÃguez-del-Pino - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - * @author Juan Carlos RodrÃguez-del-Pino <jcrodriguez@dis.ulpgc.es> - */ - -defined('MOODLE_INTERNAL') || die(); - -require_once(dirname(__FILE__).'/vpl_graph.class.php'); - -function vpl_submissions_graph($vpl, $userid) { - global $DB; - $course = $vpl->get_course(); - // No log. - $subsn = array (); - $series = array (); - $names = array (); - $submissionslist = $vpl->user_submissions( $userid ); - if (count( $submissionslist ) > 0) { - $submissionslist = array_reverse( $submissionslist ); - // Create submissions object. - $subs = array (); - foreach ($submissionslist as $submission) { - $subs [] = new mod_vpl_submission( $vpl, $submission ); - } - foreach ($subs as $sub) { - $filesarray = $sub->get_submitted_fgm()->getfilelist(); - foreach ($filesarray as $name) { - if (! in_array( $name, $names, true )) { - $names [] = $name; - $series [$name] = array (); - } - } - } - // Initial value. - $subshowl = ( int ) (count( $subs ) / 20); - if ($subshowl < 1) { - $subshow = 1; - } else { - $subshow = 5; - while ( true ) { - if ($subshow >= $subshowl) { - break; - } - $subshow *= 2; - if ($subshow >= $subshowl) { - break; - } - $subshow = ( int ) (2.5 * $subshow); - if ($subshow >= $subshowl) { - break; - } - $subshow *= 2; - if ($subshow >= $subshowl) { - break; - } - } - } - $nsub = 1; - foreach ($subs as $sub) { - $subsn [] = $nsub % $subshow == 0 ? $nsub : ''; - $filesarray = $sub->get_submitted_files(); - $files = array (); - foreach ($filesarray as $name => $data) { - $size = strlen( $data ); - $files [$name] = $size; - } - foreach ($names as $name) { - if (isset( $files [$name] )) { - $series [$name] [$nsub - 1] = $files [$name]; - } else { - $series [$name] [$nsub - 1] = null; - } - } - $nsub ++; - } - } - $user = $DB->get_record( 'user', array ( - 'id' => $userid - ) ); - vpl_graph::draw( $vpl->get_printable_name() . ' - ' . $vpl->fullname( $user, false ) - , get_string( 'submissions', VPL ) , get_string( "sizeb" ), $subsn, $series, $names ); -} \ No newline at end of file diff --git a/views/vpl_graph.class.php b/views/vpl_graph.class.php deleted file mode 100644 index c86a3ef56bb72ebd3171e762ad832d9519565cc0..0000000000000000000000000000000000000000 --- a/views/vpl_graph.class.php +++ /dev/null @@ -1,66 +0,0 @@ -<?php -// This file is part of VPL for Moodle - http://vpl.dis.ulpgc.es/ -// -// VPL for Moodle is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// VPL for Moodle is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with VPL for Moodle. If not, see <http://www.gnu.org/licenses/>. - -/** - * Graph submissions statistics for a vpl instance and a user - * - * @package mod_vpl - * @copyright 2012 onwards Juan Carlos RodrÃguez-del-Pino - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - * @author Juan Carlos RodrÃguez-del-Pino <jcrodriguez@dis.ulpgc.es> - */ - -defined('MOODLE_INTERNAL') || die(); - -global $CFG; -require_once($CFG->libdir . '/graphlib.php'); - -class vpl_graph { - /** - * Draw a graph image. Staked area - * - * @param $title string title of graph - * @param $xlabel string x label - * @param $ylabel string y label - * @param $legends array of strings, values are the names of every serie - * @param $xdata array x labels - * @param $ydata array of array of numbers first array is indexed by legend. - * @return void - */ - static public function draw($title, $xlabel, $ylabel, $xdata, $ydata, $legends = false, $typebar = false) { - global $OUTPUT; - $chart = new \core\chart_bar(); - $chart->set_stacked(true); - $chart->set_title($title); - $chart->set_labels($xdata); - $chart->get_xaxis(0, true)->set_label($xlabel); - $chart->get_yaxis(0, true)->set_label($ylabel); - $chart->get_xaxis(0, true)->set_labels($xdata); - if ( $legends == false) { - $serie = new \core\chart_series($ylabel, $ydata); - $chart->add_series($serie); - } else { - $chart->set_stacked(true); - foreach ($legends as $legen) { - $serie = new \core\chart_series($legen, $ydata[$legen]); - $serie->set_smooth(true); - $serie->set_type($serie::TYPE_LINE); - $chart->add_series($serie); - } - } - echo $OUTPUT->render($chart); - } -} diff --git a/views/vpl_grapher.class.php b/views/vpl_grapher.class.php new file mode 100644 index 0000000000000000000000000000000000000000..4769428c36c1ba329eb6afdf08a8634950252f07 --- /dev/null +++ b/views/vpl_grapher.class.php @@ -0,0 +1,339 @@ +<?php +// This file is part of VPL for Moodle - http://vpl.dis.ulpgc.es/ +// +// VPL for Moodle is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// VPL for Moodle is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with VPL for Moodle. If not, see <http://www.gnu.org/licenses/>. + +/** + * Graph submissions statistics for a vpl instance and a user + * + * @package mod_vpl + * @copyright 2012 onwards Juan Carlos RodrÃguez-del-Pino + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @author Juan Carlos RodrÃguez-del-Pino <jcrodriguez@dis.ulpgc.es> + */ + +defined('MOODLE_INTERNAL') || die(); + +require_once(dirname(__FILE__).'/../locallib.php'); +require_once(dirname(__FILE__).'/../vpl.class.php'); +require_once(dirname(__FILE__).'/../vpl_submission.class.php'); +require_once(dirname(__FILE__).'/../similarity/diff.class.php'); + +class vpl_grapher { + + protected $vpl; + protected $submissions; + protected $userid; + protected $username; + + public function __construct(&$vpl, $userid = null) { + $this->vpl = $vpl; + $this->userid = $userid; + if ($userid !== null) { + global $DB; + $user = $DB->get_record( 'user', array ( + 'id' => $userid + )); + $this->username = $vpl->fullname($user, false); + $usersubs = $vpl->user_submissions( $userid ); + $this->submissions = $usersubs; + $this->userssubmissions = array(); + $this->userssubmissions[] = $usersubs; + } else { + $this->submissions = array(); + $this->userssubmissions = array(); + $cm = $vpl->get_course_module(); + $currentgroup = groups_get_activity_group( $cm ); + if (! $currentgroup) { + $currentgroup = ''; + } + $list = $vpl->get_students( $currentgroup ); + foreach ($list as $userinfo) { + if ($vpl->is_group_activity() && $userinfo->id != $vpl->get_group_leaderid( $userinfo->id )) { + continue; + } + $usersubs = $vpl->user_submissions( $userinfo->id ); + $this->submissions = array_merge($this->submissions, $usersubs); + $this->userssubmissions[] = $usersubs; + } + } + } + + /** + * Draw a graph in page. + * + * @param $title string title of graph + * @param $xlabel string x label + * @param $ylabel string y label + * @param $xdata array x labels + * @param $ydata array of array of numbers first array is indexed by legend. + * @param $seriesnames array of strings, values are the names of every series. + * If provided, the chart will render as stacked line series. Otherwise, it will render as a bar chart. + */ + static protected function draw($title, $xlabel, $ylabel, $xdata, $ydata, $seriesnames = null) { + global $OUTPUT, $CFG; + require_once($CFG->libdir . '/graphlib.php'); + $chart = new \core\chart_bar(); + $chart->set_title($title); + $chart->set_labels($xdata); + $chart->get_xaxis(0, true)->set_label($xlabel); + $chart->get_yaxis(0, true)->set_label($ylabel); + $chart->get_xaxis(0, true)->set_labels($xdata); + if ( $seriesnames === null) { + $serie = new \core\chart_series($ylabel, $ydata); + $chart->add_series($serie); + } else { + $chart->set_stacked(false); + foreach ($seriesnames as $seriesname) { + $serie = new \core\chart_series($seriesname, $ydata[$seriesname]); + $serie->set_smooth(false); + $serie->set_type($serie::TYPE_LINE); + $chart->add_series($serie); + } + } + echo $OUTPUT->render($chart); + } + + /** + * Computes a value in {1,5,10,25,50,100,250,500,...} that will render fine as x-axis legend. + * @param $totalvalues int The number of values on x-axis + * @param $legendsnumber int The number of legends to display on x-axis + */ + static protected function compute_xaxis_legend_interval($totalvalues, $legendsnumber) { + $x = ( int ) ($totalvalues / $legendsnumber); // First estimation. + if ($x == 0) { + return 1; + } + $interval = 10 ** strlen($x); + if ($interval / 2 > $x) { + $interval /= 2; + if ($interval / 2 > $x) { + $interval /= 2; + } + } + return max( $interval, 5 ); + } + + /** + * Draw a graph showing submitted files evolution. + * @param boolean $diff Whether the graph should show the diff with required files, or the total size of the files. + */ + public function draw_files_evolution_graph($diff = false) { + if (count( $this->submissions ) == 0 || $this->userid === null) { + return; + } + $submissionslist = array_reverse( $this->submissions ); + + $interval = self::compute_xaxis_legend_interval(count( $submissionslist ), 20); + + $i = 1; + $subsn = array(); + $series = array(); + $names = array(); + $totalseries = array(); + if ($diff) { + $reqfiles = $this->vpl->get_fgm('required')->getAllFiles(); + } + foreach ($submissionslist as $subinstance) { + $submission = new mod_vpl_submission( $this->vpl, $subinstance ); + $files = $submission->get_submitted_files(); + $total = 0; + foreach ($files as $name => $data) { + if (!isset($series[$name])) { + $names[] = $name; + $series[$name] = array_fill(0, $i - 1, null); + } + if ($diff && isset($reqfiles[$name])) { + $value = vpl_diff::compute_filediff($reqfiles[$name], $data); + } else { + $value = strlen( $data ); + } + $series[$name][] = $value; + $total += $value; + } + foreach ($series as &$serie) { + if (count($serie) < $i) { + $serie[] = null; + } + } + $totalseries[] = $total; + $subsn[] = $i % $interval == 0 ? $i : ''; + $i++; + } + if ($diff && count($names) > 1) { + $names[] = get_string('total'); + $series[get_string('total')] = $totalseries; + } + $title = $this->username . ' - '; + if ($diff) { + $title .= get_string( 'filesdiffevolution', VPL ); + } else { + $title .= get_string( 'filessizeevolution', VPL ); + } + self::draw( $title, get_string( 'submissions', VPL ) , get_string( "sizeb" ), $subsn, $series, $names ); + } + + static protected function new_working_period($workduration, &$firstwork, $intervals) { + if ($intervals > 0) { // First work as average. + $firstwork = ( float ) $workduration / $intervals; + } + // Else use the last $firstwork. + return ($workduration + $firstwork); + } + + protected function get_working_periods() { + $workperiods = array (); + foreach ($this->userssubmissions as $usersubs) { + if (count( $usersubs ) == 0) { + continue; + } + $usersubs = array_reverse( $usersubs ); + $userworkperiods = array (); + $lastsavetime = 0; + $resttime = 20 * 60; // Rest period before next work 20 minutes. + $firstwork = 10 * 60; // Work before first save 10 minutes. + $intervals = - 1; + $workstart = 0; + foreach ($usersubs as $submission) { + // Start new work period. + if ($submission->datesubmitted - $lastsavetime >= $resttime) { + if ($workstart > 0) { // Is not the first submission. + $userworkperiods[] = self::new_working_period($lastsavetime - $workstart, $firstwork, $intervals); + } + $workstart = $submission->datesubmitted; + $intervals = 0; + } else { // Count interval. + $intervals ++; + } + $lastsavetime = $submission->datesubmitted; + } + $userworkperiods[] = self::new_working_period($lastsavetime - $workstart, $firstwork, $intervals); + $workperiods[] = $userworkperiods; + } + return $workperiods; + } + + public function draw_working_periods_graph() { + if (count( $this->submissions ) == 0) { + return; + } + if ($this->userid === null) { + $alldata = self::get_working_periods(); + // For every student, total time, number of period. + $totaltime = 0; + $maxstudenttime = 0; + $maxperiodtime = 0; + $totalperiods = 0; + $times = array (); + foreach ($alldata as $workingperiods) { + $totalperiods += count( $workingperiods ); + $time = 0; + foreach ($workingperiods as $period) { + $time += $period / 3600.0; + $maxperiodtime = max( $maxperiodtime, $period / 3600.0 ); + } + $totaltime += $time; + $maxstudenttime = max( $maxstudenttime, $time ); + $times [] = $time; + } + if ($maxstudenttime <= 3) { + $timeslice = 0.25; + $xformat = "%3.2f-%3.2f"; + } else if ($maxstudenttime <= 6) { + $timeslice = 0.50; + $xformat = "%3.1f-%3.1f"; + } else { + $timeslice = 1; + $xformat = "%3.0f-%3.0f"; + } + $ydata = array (); + $xdata = array (); + for ($slice = 0; $slice <= $maxstudenttime; $slice += $timeslice) { + $ydata [] = 0; + $xdata [] = sprintf( $xformat, $slice, ($slice + $timeslice) ); + } + foreach ($times as $time) { + $ydata [( int ) ($time / $timeslice)] ++; + } + $title = $this->vpl->get_printable_name(); + $n = count( $times ); + $straveragetime = get_string( 'averagetime', VPL, sprintf( '%3.1f', (( float ) $totaltime / $n) ) ); + $straverageperiods = get_string( 'averageperiods', VPL, sprintf( '%3.1f', (( float ) $totalperiods / $n) ) ); + $strvmaximumperiod = get_string( 'maximumperiod', VPL, sprintf( '%3.1f', (( float ) $maxperiodtime) ) ); + $xtitle = sprintf( '%s - %s - %s - %s', get_string( 'hours' ), + $straveragetime, $straverageperiods, $strvmaximumperiod ); + $ytitle = get_string( 'defaultcoursestudents' ); + self::draw( $title, $xtitle, $ytitle, $xdata, $ydata ); + } else { + $workingperiods = self::get_working_periods()[0]; + $xdata = array (); + $totaltime = 0.0; + $i = 1; + foreach ($workingperiods as &$period) { + $xdata[] = '#' . ($i++); + $totaltime += $period; + $period = round($period / 60.0, 2); + } + $title = $this->username . ' - ' . format_time( $totaltime ); + self::draw( $title, get_string( 'workingperiods', VPL ), get_string( 'minutes' ), $xdata, $workingperiods ); + } + } + + public function draw_daily_activity_graph() { + if (count( $this->submissions ) == 0) { + return; + } + + // Beginning of time span is either module start date, either first submission date. + if ($this->vpl->get_instance()->startdate > 0) { + $start = $this->vpl->get_instance()->startdate; + } else { + $start = time(); + foreach ($this->submissions as $subinstance) { + $start = min($start, $subinstance->datesubmitted); + } + } + $start = vpl_timestamp_to_midnight($start); + + // End of time span is either module due date, either now. + if ($this->vpl->get_instance()->duedate > 0) { + $end = $this->vpl->get_instance()->duedate; + } else { + $end = time(); + } + $end = vpl_timestamp_to_midnight($end); + + // Compute per-day activity (ie. number of submissions). + $days = array(); + $dailyactivity = array(); + for ($day = $start; $day <= $end; $day += 86400) { + $days[] = userdate($day, get_string('strftimedate', 'langconfig')); + $dailyactivity[] = 0; + } + + foreach ($this->submissions as $subinstance) { + $daysubmitted = vpl_timestamp_to_midnight($subinstance->datesubmitted); + $dailyactivity[($daysubmitted - $start) / 86400] ++; + } + + // Draw the graph. + if ($this->userid === null) { + $title = $this->vpl->get_printable_name(); + } else { + $title = $this->username; + } + $title .= ' - ' . get_string( 'nsubmissions', VPL, count($this->submissions) ); + self::draw( $title, get_string( 'day' ), get_string( 'submissions', VPL ), $days, $dailyactivity ); + } +} diff --git a/views/workinggraph.php b/views/workinggraph.php deleted file mode 100644 index 91e040bc079add8939dc6b28719028eac0b83a21..0000000000000000000000000000000000000000 --- a/views/workinggraph.php +++ /dev/null @@ -1,161 +0,0 @@ -<?php -// This file is part of VPL for Moodle - http://vpl.dis.ulpgc.es/ -// -// VPL for Moodle is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// VPL for Moodle is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with VPL for Moodle. If not, see <http://www.gnu.org/licenses/>. - -/** - * Graph working time for a vpl instance and/or a user - * - * @package mod_vpl - * @copyright 2012 Juan Carlos RodrÃguez-del-Pino - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - * @author Juan Carlos RodrÃguez-del-Pino <jcrodriguez@dis.ulpgc.es> - */ - -require_once(dirname( __FILE__ ) . '/../../../config.php'); -require_once(dirname( __FILE__ ) . '/vpl_graph.class.php'); -require_once(dirname( __FILE__ ) . '/../locallib.php'); -require_once(dirname( __FILE__ ) . '/../vpl.class.php'); -require_once(dirname( __FILE__ ) . '/../vpl_submission.class.php'); - -function vpl_get_working_periods($vpl, $userid) { - $submissionslist = $vpl->user_submissions( $userid ); - if (count( $submissionslist ) == 0) { - return array (); - } - $submissionslist = array_reverse( $submissionslist ); - $workperiods = array (); - if ($submissionslist) { - $lastsavetime = 0; - $resttime = 20 * 60; // Rest period before next work 20 minutes. - $firstwork = 10 * 59; // Work before first save 10 minutes. - $intervals = - 1; - $workstart = 0; - foreach ($submissionslist as $submission) { - /* Start new work period */ - if ($submission->datesubmitted - $lastsavetime >= $resttime) { - if ($workstart > 0) { // Is not the first submission. - if ($intervals > 0) { // First work as average. - $firstwork = ( float ) ($lastsavetime - $workstart) / $intervals; - } - // Else use the last $firstwork. - $workperiods [] = ($lastsavetime - $workstart + $firstwork) / (3600.0); - } - $workstart = $submission->datesubmitted; - $intervals = 0; - } else { // Count interval. - $intervals ++; - } - $lastsavetime = $submission->datesubmitted; - } - if ($intervals > 0) { // First work as average. - $firstwork = ( float ) ($lastsavetime - $workstart) / $intervals; - } // Else use the last $firstwork. - $workperiods [] = ($lastsavetime - $workstart + $firstwork) / (3600.0); - } - return $workperiods; -} - -require_login(); - -$id = required_param( 'id', PARAM_INT ); -$userid = optional_param( 'userid', - 1, PARAM_INT ); -$type = optional_param( 'type', 0, PARAM_INT ); -$vpl = new mod_vpl( $id ); -$course = $vpl->get_course(); -if($USER->id != $userid /*|| !$vpl->get_instance()->allowshowprevious*/){ //Not owner or not allow - $vpl->require_capability(VPL_GRADE_CAPABILITY); -} -// No log. -if ($userid < 0) { - $cm = $vpl->get_course_module(); - $currentgroup = groups_get_activity_group( $cm ); - if (! $currentgroup) { - $currentgroup = ''; - } - $list = $vpl->get_students( $currentgroup ); - $submissions = $vpl->all_last_user_submission(); - // Get all information. - $alldata = array (); - foreach ($list as $userinfo) { - if ($vpl->is_group_activity() && $userinfo->id != $vpl->get_group_leaderid( $userinfo->id )) { - continue; - } - $workingperiods = vpl_get_working_periods( $vpl, $userinfo->id ); - if (count( $workingperiods ) > 0) { - $alldata [] = $workingperiods; - } - } - session_write_close(); - // For every student, total time, number of period. - $totaltime = 0; - $maxstudenttime = 0; - $maxperiodtime = 0; - $totalperiods = 0; - $times = array (); - foreach ($alldata as $workingperiods) { - $totalperiods += count( $workingperiods ); - $time = 0; - foreach ($workingperiods as $period) { - $time += $period; - $maxperiodtime = max( $maxperiodtime, $period ); - } - $totaltime += $time; - $maxstudenttime = max( $maxstudenttime, $time ); - $times [] = $time; - } - if ($maxstudenttime <= 3) { - $timeslice = 0.25; - $xformat = "%3.2f-%3.2f"; - } else if ($maxstudenttime <= 6) { - $timeslice = 0.50; - $xformat = "%3.1f-%3.1f"; - } else { - $timeslice = 1; - $xformat = "%3.0f-%3.0f"; - } - $ydata = array (); - $xdata = array (); - for ($slice = 0; $slice <= $maxstudenttime; $slice += $timeslice) { - $ydata [] = 0; - $xdata [] = sprintf( $xformat, $slice, ($slice + $timeslice) ); - } - foreach ($times as $time) { - $ydata [( int ) ($time / $timeslice)] ++; - } - $title = $vpl->get_printable_name(); - $n = count( $times ); - $straveragetime = get_string( 'averagetime', VPL, sprintf( '%3.1f', (( float ) $totaltime / $n) ) ); - $straverageperiods = get_string( 'averageperiods', VPL, sprintf( '%3.1f', (( float ) $totalperiods / $n) ) ); - $strvmaximumperiod = get_string( 'maximumperiod', VPL, sprintf( '%3.1f', (( float ) $maxperiodtime) ) ); - $xtitle = sprintf( '%s - %s - %s - %s', get_string( 'hours' ), $straveragetime, $straverageperiods, $strvmaximumperiod ); - $ytitle = get_string( 'defaultcoursestudents' ); - vpl_graph::draw( $title, $xtitle, $ytitle, $xdata, $ydata, null, true ); -} else { - $ydata = vpl_get_working_periods( $vpl, $userid ); - session_write_close(); - $xdata = array (); - $hours = 0.0; - for ($i = 0; $i < count( $ydata ); $i ++) { - $xdata [] = $i + 1; - $hours += $ydata [$i]; - } - $user = $DB->get_record( 'user', array ( - 'id' => $userid - ) ); - $title = sprintf( "%s - %s", $vpl->fullname( $user, false ), get_string( 'numhours', '', sprintf( '%3.2f', $hours ) ) ); - $titlex = get_string( 'workingperiods', VPL ) . ' - ' . $vpl->get_printable_name(); - vpl_graph::draw( $title, $titlex, get_string( 'hours' ), $xdata, $ydata, null, true ); -} - diff --git a/vpl.class.php b/vpl.class.php index 17e8b3f0bceaccac2335d6e538f34831d8d55bbd..ad0bc342daa018e215047d266afe7086f5f83502 100644 --- a/vpl.class.php +++ b/vpl.class.php @@ -1306,6 +1306,15 @@ class mod_vpl { return $this->instance->grade; } + /** + * Format given grade as grade/maxgrade + * @param float $grade + * @return string Printable grade + */ + public function format_grade($grade) { + return format_float($grade, 2, true, false) . ' / ' . format_float($this->get_grade(), 2, true, false); + } + /** * print end of page */