Commit f59cb196 authored by Astor Bizard's avatar Astor Bizard
Browse files

Rework of charts: submissions graph, working periods graph, etc.

parent 272ffbc9
......@@ -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';
......
......@@ -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
......@@ -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;
}
}
/* ############################## */
/* ##### 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 # */
/* ############################## */
......
......@@ -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();
......@@ -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();
<?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
<?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);
}
}
<?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');