# The LearningOnline Network with CAPA # # $Id: lonsubmissiontimeanalysis.pm,v 1.3 2003/10/30 16:20:18 matthew Exp $ # # Copyright Michigan State University Board of Trustees # # This file is part of the LearningOnline Network with CAPA (LON-CAPA). # # LON-CAPA 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 2 of the License, or # (at your option) any later version. # # LON-CAPA 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 LON-CAPA; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # /home/httpd/html/adm/gpl.txt # # http://www.lon-capa.org/ # package Apache::lonsubmissiontimeanalysis; use strict; use Apache::lonnet(); use Apache::loncommon(); use Apache::lonhtmlcommon(); use Apache::loncoursedata(); use Apache::lonstatistics; use Apache::lonlocal; use HTML::Entities(); use Time::Local(); use Spreadsheet::WriteExcel(); my $plotcolors = ['#33ff00', '#ff33cc', '#990000', '#aaaa66', '#663399', '#ff9933', '#66ccff', '#ff9999', '#cccc33', '#660000', '#33cc66', ]; my @SubmitButtons = ( { name => 'ProblemAnalyis', text => 'Analyze Problem Again' }, { name => 'SelectAnother', text => 'Choose a different resource' }, ); sub render_resource { my ($resource) = @_; ## ## Render the problem my $base; ($base,undef) = ($resource->{'src'} =~ m|(.*/)[^/]*$|); $base = "http://".$ENV{'SERVER_NAME'}.$base; my $rendered_problem = &Apache::lonnet::ssi_body($resource->{'src'}); $rendered_problem =~ s/<\s*form\s*/)|<\/nop>|g; return '
'. ''. $rendered_problem. '
'; } sub BuildSubmissionTimePage { my ($r,$c)=@_; $r->print('

'.&mt('Submission Time Plots').'

'); $r->print(&CreateInterface()); # my @Students = @Apache::lonstatistics::Students; # if (@Students < 1) { $r->print('

There are no students in the sections selected

'); } # &Apache::loncoursedata::clear_internal_caches(); if (exists($ENV{'form.ClearCache'}) || exists($ENV{'form.updatecaches'}) || (exists($ENV{'form.firstanalysis'}) && $ENV{'form.firstanalysis'} ne 'no')) { &Apache::lonstatistics::Gather_Full_Student_Data($r); } if (! exists($ENV{'form.firstanalysis'})) { $r->print(''); } else { $r->print(''); } $r->rflush(); # if (! exists($ENV{'form.problemchoice'}) || exists($ENV{'form.SelectAnother'})) { $r->print(''); $r->print(' 'x5); $r->print('

'.&mt('Please select a problem to analyze').'

'); $r->print(&ProblemSelector()); } else { foreach my $button (@SubmitButtons) { $r->print(''); $r->print(' 'x5); } $r->print(''); # $r->print('
'); # my ($symb,$part) = &get_problem_symb( &Apache::lonnet::unescape($ENV{'form.problemchoice'})); $r->rflush(); # my $resource = &get_resource_from_symb($symb); if (! defined($resource)) { $r->print('resource is undefined'); } else { $r->print('

'.$resource->{'title'}.'

'); $r->print('

'.$resource->{'src'}.'

'); $r->rflush(); $r->print(&render_resource($resource)); $r->rflush(); $r->print(&analyze_times($r,$resource,\@Students,$part)); } $r->print('
'); } } ######################################################### ######################################################### ## ## Time Analysis ## ######################################################### ######################################################### sub get_week_start { my ($randomtime) = @_; my ($sec,$min,$hour,$mday,$month,$year,$wday,$yday,$isdst) = localtime($randomtime); $randomtime -= $wday * 86400; ($sec,$min,$hour,$mday,$month,$year,$wday,$yday,$isdst) = localtime($randomtime); my $week_start = &Time::Local::timelocal(0,0,0,$mday,$month,$year); return $week_start; } sub analyze_times { my ($r,$resource,$students,$part) = @_; # # Convenience arrays my @FullWeekDay = (qw/Sunday Monday Tuesday Wednesday Thursday Friday Saturday/); my @WeekDay = (qw/SUN MON TUE WED THU FRI SAT SUN/); my @Month = (qw/Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec/); # my $html; # holds results of analysis # Get the data my $SubData = &Apache::loncoursedata::get_response_time_data ($students,$resource->{'symb'},$part); if (! defined($SubData) || ! ref($SubData)) { $html.= '

There is no submission data for this resource

'; return $html; } my $NumSub = scalar(@{$SubData}); if (! @{$SubData}) { $html.= '

There is no submission data for this resource

'; return $html; } # Process the data # my (undef,undef,undef,$mday,$month,$year,$wday,$yday,$isdst) = localtime(&get_time_from_row($SubData->[0])); my $day_start = &Time::Local::timelocal(0,0,0,$mday,$month,$year); # # Configure the bins used to store the data. my $binsize = 3600; # seconds my $bins_per_day = 86400/$binsize; my $bincount = 0; my $endtime = $day_start; # # Initialize loop variables my $max; my @Ydata=(0); my @AnsData=(0); my @Xlabel=($WeekDay[$wday]); my $cumulative_answers = 0; # foreach my $row (@$SubData) { my $subtime = &get_time_from_row($row); while ($subtime > $endtime && $endtime < time) { # Create a new bin $bincount++; $Ydata[$bincount]=0; $AnsData[$bincount]=$AnsData[$bincount-1]; $endtime += $binsize; if ($bincount % (86400/$binsize) == 0) { $wday++; $wday %= 7; $Xlabel[$bincount] = $WeekDay[$wday]; } else { $Xlabel[$bincount] = ''; } } $Ydata[$bincount]++; $max = $Ydata[$bincount] if ($max < $Ydata[$bincount]); $AnsData[$bincount] += &successful_submission($row); $cumulative_answers += &successful_submission($row); } foreach my $maximum (10,15,20,25,30,40,50,60,70,80,90,100, 120,150,200,250,300,350,400,450,500, 600,700,800,900,1000,1100,1200,1500,2000, 2500,3000,4000,5000) { if ($max < $maximum) { $max = $maximum; last; } } while ($bincount % $bins_per_day != 0) { $bincount++; $Ydata[$bincount]=0; $AnsData[$bincount]=$AnsData[$bincount-1]; $endtime += $binsize; if ($bincount % (86400/$binsize) == 0) { $wday ++; $wday %= 7; $Xlabel[$bincount] = $WeekDay[$wday]; } else { $Xlabel[$bincount] = ''; } } my $title = 'Number of Submissions and Number Correct'; my $xlabel; (undef,undef,undef,$mday,$month,$year,$wday) = localtime($day_start); $xlabel .= $FullWeekDay[$wday].' '. join(' ',($Month[$month],$mday,1900+$year)).' - '; (undef,undef,undef,$mday,$month,$year,$wday) = localtime($endtime); $xlabel .= $FullWeekDay[$wday].' '. join(' ',($Month[$month],$mday,1900+$year)); $html .= &Apache::loncommon::DrawXYYGraph($title, $xlabel, 'Number of Submissions per hour', $plotcolors, \@Xlabel, \@Ydata, 0,$max, \@AnsData, 0,scalar(@$students), (xskip => $bins_per_day, x_ticks => $bins_per_day, x_tick_offset => $bins_per_day, width => 50+2*$bincount) ); $html .= '
'; return $html; } sub successful_submission { my ($row) = @_; if (ref($row) eq 'ARRAY') { return $row->[&Apache::loncoursedata::RT_awarded()]; } return undef; return 0; } sub get_time_from_row { my ($row) = @_; if (ref($row) eq 'ARRAY') { return $row->[&Apache::loncoursedata::RT_timestamp()]; } return undef; } sub get_tries_from_row { my ($row) = @_; if (ref($row) eq 'ARRAY') { return $row->[&Apache::loncoursedata::RT_tries()]; } return undef; } sub Process_Row { my ($row) = @_; my %RowData; my ($student_id,$award,$tries,$time) = @$row; return %RowData; } ######################################################### ######################################################### ## ## Generic Interface Routines ## ######################################################### ######################################################### sub CreateInterface { ## ## Environment variable initialization if (! exists$ENV{'form.AnalyzeOver'}) { $ENV{'form.AnalyzeOver'} = 'Tries'; } ## ## Build the menu my $Str = ''; $Str .= ''."\n"; $Str .= ''; $Str .= ''; $Str .= ''; $Str .= ''; $Str .= ''."\n"; ## ## $Str .= ''; # $Str .= ''; # my $only_seq_with_assessments = sub { my $s=shift; if ($s->{'num_assess'} < 1) { return 0; } else { return 1; } }; &Apache::lonstatistics::MapSelect('Maps','multiple,all',5, $only_seq_with_assessments); ## ## $Str .= ''."\n"; $Str .= '
'.&mt('Sections').''.&mt('Enrollment Status').' 
'."\n"; $Str .= &Apache::lonstatistics::SectionSelect('Section','multiple',5); $Str .= ''; $Str .= &Apache::lonhtmlcommon::StatusOptions(undef,undef,5); $Str .= '
'."\n"; return $Str; } sub ProblemSelector { my $Str; $Str = "\n"; foreach my $seq (&Apache::lonstatistics::Sequences_with_Assess()) { next if ($seq->{'num_assess'}<1); my $seq_str = ''; foreach my $res (@{$seq->{'contents'}}) { next if ($res->{'type'} ne 'assessment'); if (@{$res->{'parts'}} == 1) { my $value = &Apache::lonnet::escape($res->{'symb'}.':'. $res->{'parts'}->[0]); my $checked; if ($ENV{'form.problemchoice'} eq $value) { $checked = 'checked '; } $seq_str .= '\n"; foreach my $part (@{$res->{'parts'}}) { my $value = &Apache::lonnet::escape ($res->{'symb'}.':'.$part); my $checked; if ($ENV{'form.problemchoice'} eq $value) { $checked = 'checked '; } $seq_str .= '\n"; } } } if ($seq_str ne '') { $Str .= ''. "\n".$seq_str; } } $Str .= "
'. ''. ''. ''.$res->{'title'}.' '; } else { $seq_str .= '
'. ' '.''. ''.$res->{'title'}.''. "
'. ''. ''.(' 'x5).'part '.$part."
 '.$seq->{'title'}.'
\n"; return $Str; } ######################################################### ######################################################### ## ## Misc functions (ought to be put in a module) ## ######################################################### ######################################################### sub get_problem_symb { my $problemstring = shift(); my ($symb,$partid) = ($problemstring=~ /^(.*):([^:]*)$/); return ($symb,$partid); } sub get_resource_from_symb { my ($symb) = @_; foreach my $seq (&Apache::lonstatistics::Sequences_with_Assess()) { foreach my $res (@{$seq->{'contents'}}) { if ($res->{'symb'} eq $symb) { return $res; } } } return undef; } 1; __END__