# The LearningOnline Network with CAPA # # $Id: lonsubmissiontimeanalysis.pm,v 1.28 2006/12/21 19:52:28 albertel 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::lonstathelpers; use Apache::lonlocal; use HTML::Entities(); use Time::Local(); my $plotcolors = ['#33ff00', '#ff33cc', '#990000', '#aaaa66', '#663399', '#ff9933', '#66ccff', '#ff9999', '#cccc33', '#660000', '#33cc66', ]; my @SubmitButtons = ( { name => 'PrevProblemAnalysis', text => 'Previous Problem' }, { name => 'ProblemAnalysis', text => 'Analyze Problem Again' }, { name => 'NextProblemAnalysis', text => 'Next Problem' }, { name => 'SelectAnother', text => 'Choose a different Problem' }, ); sub BuildSubmissionTimePage { my ($r,$c)=@_; # my %Saveable_Parameters = ('Status' => 'scalar', 'Section' => 'array'); &Apache::loncommon::store_course_settings('submissiontime_analysis', \%Saveable_Parameters); &Apache::loncommon::restore_course_settings('submissiontime_analysis', \%Saveable_Parameters); # &Apache::lonstatistics::PrepareClasslist(); # $r->print(&CreateInterface()); # my @Students = @Apache::lonstatistics::Students; # if (@Students < 1) { $r->print('

There are no students in the sections selected

'); } # my @CacheButtonHTML = &Apache::lonstathelpers::manage_caches($r,'Statistics','stats_status'); $r->rflush(); # if (! exists($env{'form.problemchoice'}) || exists($env{'form.SelectAnother'})) { my $submit_button = ''; $r->print($submit_button.' 'x5); $r->print('

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

'); $r->print(&Apache::lonstathelpers::problem_selector('.', $submit_button)); } else { foreach my $button (@SubmitButtons) { $r->print(''); $r->print(' 'x5); } foreach my $html (@CacheButtonHTML) { $r->print($html.(' 'x5)); } $r->rflush(); # # Determine which problem we are to analyze my $current_problem = &Apache::lonstathelpers::get_target_from_id ($env{'form.problemchoice'}); # my ($navmap,$prev,$curr,$next) = &Apache::lonstathelpers::get_prev_curr_next($current_problem, '.', 'part'); if (exists($env{'form.PrevProblemAnalysis'}) && defined($prev)) { $current_problem = $prev; } elsif (exists($env{'form.NextProblemAnalysis'}) && defined($next)) { $current_problem = $next; } else { $current_problem = $curr; } # # Store the current problem choice and send it out in the form $env{'form.problemchoice'} = &Apache::lonstathelpers::make_target_id($current_problem); $r->print(''); # $r->print('
'); $r->rflush(); # my $resource = $current_problem->{'resource'}; if (! defined($resource)) { $r->print('resource is undefined'); } else { $r->print('

'.$resource->compTitle.'

'); $r->print('

'.$resource->src.'

'); $r->print('

'. &Apache::lonstatistics::section_and_enrollment_description(). '

'); $r->rflush(); $r->print(&Apache::lonstathelpers::render_resource($resource)); $r->print('
'); $r->rflush(); if (@Students) { $r->print(&analyze_times($r,$resource->symb,\@Students, $current_problem->{'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,$symb,$students,$part) = @_; my $htmltable; # # 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 ([&Apache::lonstatistics::get_selected_sections()], [&Apache::lonstatistics::get_selected_groups()], $Apache::lonstatistics::enrollment_status, $symb,$part); if (! defined($SubData) || ! ref($SubData)) { $html.= '

There is no submission data for this problem at all

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

There is no submission data for this problem

'; 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; # The sum of @Ydata my @Ydata=(0); # number of submissions my @AnsData=(0); # number of correct submissions my @Xlabel=($WeekDay[$wday]); # Labels of itmes my @BinEnd; # The end time of each bin my $cumulative_answers = 0; # The sum of @AnsData my %students; # which students have attempted the problem? # 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] = 0; $endtime += $binsize; push(@BinEnd,$endtime); 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); $students{$row->[&Apache::loncoursedata::RT_student_id()]}++; } # # Pad the data to a full day while ($bincount % $bins_per_day != 0) { $bincount++; $Ydata[$bincount]=0; $AnsData[$bincount]=0; $endtime += $binsize; push(@BinEnd,$endtime); if ($bincount % (86400/$binsize) == 0) { $wday ++; $wday %= 7; $Xlabel[$bincount] = $WeekDay[$wday]; } else { $Xlabel[$bincount] = ''; } } my $numstudents = scalar(keys(%students)); # # Determine a nice maximum value to use 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; } } # # Build the data table $htmltable = '

'. ''. ''. ''. ''. ''. ''. ''. ''. ''. ''. ''. ''. ''. ''. ''. ''. ''. ''; my @CumulativeCorrect=(0); my @corr_as_percent_of_selected; my @corr_as_percent_of_answering; for (my $i=0;$i<=$#Ydata;$i++) { $CumulativeCorrect[$i]=$CumulativeCorrect[-1]+$AnsData[$i]; $corr_as_percent_of_answering[$i] = sprintf('%3.1f',100*$CumulativeCorrect[$i]/$numstudents); $corr_as_percent_of_selected[$i] = sprintf('%3.1f',100*$CumulativeCorrect[$i]/scalar(@$students)); if ($Ydata[$i] != 0) { next if (! defined($BinEnd[$i]) || $BinEnd[$i] == 0); $htmltable .= ''. ''. ''. ''. ''. ''. ''. ''. ''. ''. ''. ''. ''. ''. ''.$/; } } $htmltable .= '
'.&mt('Begin').''.(' 'x3).''.&mt('End').''.&mt('Submissions (plotted)').''.(' 'x3).''.&mt('Correct Submissions (not plotted)').''.(' 'x3).''.&mt('Cumulative Correct of those attempting the problem (not plotted)').''.(' 'x3).''.&mt('Cumulative Percent Correct of those attempting the problem (not plotted)').''.(' 'x3).''.&mt('Cumulative Percent Correct of selected students (plotted)').'
'. &Apache::lonlocal::locallocaltime($BinEnd[$i]-$binsize). ' '. &Apache::lonlocal::locallocaltime($BinEnd[$i]).''.$Ydata[$i].(' 'x3).' '.$AnsData[$i].(' 'x3).' '.$CumulativeCorrect[$i].' '.$corr_as_percent_of_answering[$i].' '.$corr_as_percent_of_selected[$i].'

'; # # Build the plot 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)); my $width = 50+2*$bincount; if ($width < 250) { $width = 250; } # $html .= &Apache::loncommon::DrawXYYGraph($title, $xlabel, 'Submissions vs Time', $plotcolors, \@Xlabel, \@Ydata,0,$max, \@corr_as_percent_of_selected,0,100, (xskip => $bins_per_day, x_ticks => $bins_per_day, x_tick_offset => $bins_per_day, width => $width, y1_label=>'Number of Submissions per hour', y2_label=>'Percent of Students answering Correctly', 'data.1.label'=>'Submissions per hour', 'data.2.label'=>'Percent correct', ) ); $html .= '
'.$htmltable; return $html; } sub successful_submission { my ($row) = @_; if (ref($row) eq 'ARRAY') { return $row->[&Apache::loncoursedata::RT_awarded()]; } return undef; } 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 .= &Apache::lonhtmlcommon::breadcrumbs('Submission Time Plots'); $Str .= '

'; $Str .= ''."\n"; $Str .= ''; $Str .= ''; $Str .= ''; $Str .= ''; $Str .= ''."\n"; ## ## $Str .= ''; # $Str .= ''; # $Str .= ''; # $Str .= ''."\n"; $Str .= '
'.&mt('Sections').''.&mt('Groups').''.&mt('Access Status').'
'."\n"; $Str .= &Apache::lonstatistics::SectionSelect('Section','multiple',4); $Str .= ''."\n"; $Str .= &Apache::lonstatistics::GroupSelect('Group','multiple',4); $Str .= ''; $Str .= &Apache::lonhtmlcommon::StatusOptions(undef,undef,4); $Str .= '
'."\n"; # $Str .= ''.&mt('Status: [_1]', ''). ''.'

'; ## return $Str; } 1; __END__