File:  [LON-CAPA] / loncom / interface / statistics / lonsubmissiontimeanalysis.pm
Revision 1.27: download - view: text, annotated - select for diffs
Fri Aug 18 15:15:38 2006 UTC (17 years, 10 months ago) by raeburn
Branches: MAIN
CVS tags: version_2_2_X, version_2_2_99_0, version_2_2_2, version_2_2_1, version_2_2_0, HEAD
Bug 4954.  Filter title changed from "Enrollment Status" to "Access Status".  Access Status selections in lonhtmlcommon set to:
Currently Has Access
Will Have Future Access
Previously Had Access
Any Access Status
See comment appended to bug 4954 for more information.
Documentation updated to include ability to selectively display students with future access.

    1: # The LearningOnline Network with CAPA
    2: #
    3: # $Id: lonsubmissiontimeanalysis.pm,v 1.27 2006/08/18 15:15:38 raeburn Exp $
    4: #
    5: # Copyright Michigan State University Board of Trustees
    6: #
    7: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
    8: #
    9: # LON-CAPA is free software; you can redistribute it and/or modify
   10: # it under the terms of the GNU General Public License as published by
   11: # the Free Software Foundation; either version 2 of the License, or
   12: # (at your option) any later version.
   13: #
   14: # LON-CAPA is distributed in the hope that it will be useful,
   15: # but WITHOUT ANY WARRANTY; without even the implied warranty of
   16: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   17: # GNU General Public License for more details.
   18: #
   19: # You should have received a copy of the GNU General Public License
   20: # along with LON-CAPA; if not, write to the Free Software
   21: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   22: #
   23: # /home/httpd/html/adm/gpl.txt
   24: #
   25: # http://www.lon-capa.org/
   26: #
   27: 
   28: package Apache::lonsubmissiontimeanalysis;
   29: 
   30: use strict;
   31: use Apache::lonnet;
   32: use Apache::loncommon();
   33: use Apache::lonhtmlcommon();
   34: use Apache::loncoursedata();
   35: use Apache::lonstatistics;
   36: use Apache::lonstathelpers;
   37: use Apache::lonlocal;
   38: use HTML::Entities();
   39: use Time::Local();
   40: 
   41: my $plotcolors = ['#33ff00', 
   42:                   '#ff33cc', '#990000', '#aaaa66', '#663399', '#ff9933',
   43:                   '#66ccff', '#ff9999', '#cccc33', '#660000', '#33cc66',
   44:                   ]; 
   45: 
   46: my @SubmitButtons = (
   47:                      { name => 'PrevProblemAnalysis',
   48:                        text => 'Previous Problem' },
   49:                      { name => 'ProblemAnalysis',
   50:                        text => 'Analyze Problem Again' },
   51:                      { name => 'NextProblemAnalysis',
   52:                        text => 'Next Problem' },
   53:                      { name => 'SelectAnother',
   54:                        text => 'Choose a different Problem' },
   55:                      );
   56: 
   57: sub BuildSubmissionTimePage {
   58:     my ($r,$c)=@_;
   59:     #
   60:     my %Saveable_Parameters = ('Status' => 'scalar',
   61:                                'Section' => 'array');
   62:     &Apache::loncommon::store_course_settings('submissiontime_analysis',
   63:                                               \%Saveable_Parameters);
   64:     &Apache::loncommon::restore_course_settings('submissiontime_analysis',
   65:                                                 \%Saveable_Parameters);
   66:     #
   67:     &Apache::lonstatistics::PrepareClasslist();    
   68:     #
   69:     $r->print(&CreateInterface());
   70:     #
   71:     my @Students = @Apache::lonstatistics::Students;
   72:     #
   73:     if (@Students < 1) {
   74:         $r->print('<h2>There are no students in the sections selected</h2>');
   75:     }
   76:     #
   77:     my @CacheButtonHTML = 
   78:         &Apache::lonstathelpers::manage_caches($r,'Statistics','stats_status');
   79:     $r->rflush();
   80:     #
   81:     if (! exists($env{'form.problemchoice'}) ||
   82:         exists($env{'form.SelectAnother'})) {
   83:         my $submit_button = '<input type="submit" name="" value="'.
   84:             &mt('Graph Problem Submission Times').'" />';
   85:         $r->print($submit_button.'&nbsp;'x5);
   86:         $r->print('<h3>'.&mt('Please select a problem to analyze').'</h3>');
   87:         $r->print(&Apache::lonstathelpers::problem_selector('.',
   88:                                                             $submit_button));
   89:     } else {
   90:         foreach my $button (@SubmitButtons) {
   91:             $r->print('<input type="submit" name="'.$button->{'name'}.'" '.
   92:                       'value="'.&mt($button->{'text'}).'" />');
   93:             $r->print('&nbsp;'x5);
   94:         }
   95:         foreach my $html (@CacheButtonHTML) {
   96:             $r->print($html.('&nbsp;'x5));
   97:         }
   98:         $r->rflush();
   99:         #
  100:         # Determine which problem we are to analyze
  101:         my $current_problem = &Apache::lonstathelpers::get_target_from_id
  102:             ($env{'form.problemchoice'});
  103:         #
  104:         my ($navmap,$prev,$curr,$next) = 
  105:             &Apache::lonstathelpers::get_prev_curr_next($current_problem,
  106:                                                         '.',
  107:                                                         'part');
  108:         if (exists($env{'form.PrevProblemAnalysis'}) && defined($prev)) {
  109:             $current_problem = $prev;
  110:         } elsif (exists($env{'form.NextProblemAnalysis'}) && defined($next)) {
  111:             $current_problem = $next;
  112:         } else {
  113:             $current_problem = $curr;
  114:         }
  115:         #
  116:         # Store the current problem choice and send it out in the form
  117:         $env{'form.problemchoice'} = 
  118:             &Apache::lonstathelpers::make_target_id($current_problem);
  119:         $r->print('<input type="hidden" name="problemchoice" value="'.
  120:                   $env{'form.problemchoice'}.'" />');
  121:         #
  122:         $r->print('<hr />');
  123:         $r->rflush();
  124:         #
  125:         my $resource = $current_problem->{'resource'};
  126:         if (! defined($resource)) {
  127:             $r->print('resource is undefined');
  128:         } else {
  129:             $r->print('<h1>'.$resource->compTitle.'</h1>');
  130:             $r->print('<h3>'.$resource->src.'</h3>');
  131:             $r->print('<h4>'.
  132:                  &Apache::lonstatistics::section_and_enrollment_description().
  133:                       '</h4>');
  134:             $r->rflush();
  135:             $r->print(&Apache::lonstathelpers::render_resource($resource));
  136:             $r->print('<br />');
  137:             $r->rflush();
  138:             $r->print(&analyze_times($r,$resource->symb,\@Students,
  139:                                      $current_problem->{'part'}));
  140:         }
  141:         $r->print('<hr />');
  142:     }
  143: }
  144: 
  145: #########################################################
  146: #########################################################
  147: ##
  148: ##                  Time Analysis
  149: ##
  150: #########################################################
  151: #########################################################
  152: sub get_week_start {
  153:     my ($randomtime) = @_;
  154:     my ($sec,$min,$hour,$mday,$month,$year,$wday,$yday,$isdst) = 
  155:         localtime($randomtime);
  156:     $randomtime -= $wday * 86400;
  157:     ($sec,$min,$hour,$mday,$month,$year,$wday,$yday,$isdst) = 
  158:         localtime($randomtime);
  159:     my $week_start = &Time::Local::timelocal(0,0,0,$mday,$month,$year);
  160:     return $week_start;
  161: }
  162: 
  163: sub analyze_times {
  164:     my ($r,$symb,$students,$part) = @_;
  165:     my $htmltable;
  166:     #
  167:     # Convenience arrays
  168:     my @FullWeekDay = (qw/Sunday Monday Tuesday Wednesday Thursday Friday
  169:                        Saturday/);
  170:     my @WeekDay = (qw/SUN MON TUE WED THU FRI SAT SUN/);
  171:     my @Month = (qw/Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec/);
  172:     #
  173:     my $html; # holds results of analysis
  174:     # Get the data
  175:     my $SubData = &Apache::loncoursedata::get_response_time_data
  176:         ([&Apache::lonstatistics::get_selected_sections()],
  177:          [&Apache::lonstatistics::get_selected_groups()],
  178:          $Apache::lonstatistics::enrollment_status,
  179:          $symb,$part);
  180:     if (! defined($SubData) || ! ref($SubData)) {
  181:         $html.= '<h2>There is no submission data for this problem at all</h2>';
  182:         return $html;
  183:     }
  184:     my $NumSub = scalar(@{$SubData});
  185:     if (! @{$SubData}) {
  186:         $html.= '<h2>There is no submission data for this problem</h2>';
  187:         return $html;
  188:     }
  189:     # Process the data
  190:     #
  191:     my (undef,undef,undef,$mday,$month,$year,$wday,$yday,$isdst) = 
  192:         localtime(&get_time_from_row($SubData->[0]));
  193:     my $day_start = &Time::Local::timelocal(0,0,0,$mday,$month,$year);
  194:     #
  195:     # Configure the bins used to store the data.
  196:     my $binsize = 3600; # seconds
  197:     my $bins_per_day = 86400/$binsize;
  198:     my $bincount = 0;
  199:     my $endtime = $day_start;
  200:     #
  201:     # Initialize loop variables
  202:     my $max;            # The sum of @Ydata
  203:     my @Ydata=(0);      # number of submissions
  204:     my @AnsData=(0);    # number of correct submissions
  205:     my @Xlabel=($WeekDay[$wday]); # Labels of itmes
  206:     my @BinEnd;                   # The end time of each bin
  207:     my $cumulative_answers = 0;   # The sum of @AnsData
  208:     my %students;       # which students have attempted the problem?
  209:     #
  210:     foreach my $row (@$SubData) {
  211:         my $subtime = &get_time_from_row($row);
  212:         while ($subtime > $endtime && $endtime < time) {
  213:             # Create a new bin
  214:             $bincount++;
  215:             $Ydata[$bincount]   = 0;
  216:             $AnsData[$bincount] = 0;
  217:             $endtime += $binsize;
  218:             push(@BinEnd,$endtime);
  219:             if ($bincount % (86400/$binsize) == 0) {
  220:                 $wday++;
  221:                 $wday %= 7;
  222:                 $Xlabel[$bincount] = $WeekDay[$wday];
  223:             } else {
  224:                 $Xlabel[$bincount] = '';
  225:             }
  226:         }
  227:         $Ydata[$bincount]++;
  228:         $max = $Ydata[$bincount] if ($max < $Ydata[$bincount]);
  229:         $AnsData[$bincount] += &successful_submission($row);
  230:         $cumulative_answers += &successful_submission($row);
  231:         $students{$row->[&Apache::loncoursedata::RT_student_id()]}++;
  232:     }
  233:     #
  234:     # Pad the data to a full day
  235:     while ($bincount % $bins_per_day != 0) {
  236:         $bincount++;
  237:         $Ydata[$bincount]=0;
  238:         $AnsData[$bincount]=0;
  239:         $endtime += $binsize;
  240:         push(@BinEnd,$endtime);
  241:         if ($bincount % (86400/$binsize) == 0) {
  242:             $wday ++;
  243:             $wday %= 7;
  244:             $Xlabel[$bincount] = $WeekDay[$wday];
  245:         } else {
  246:             $Xlabel[$bincount] = '';
  247:         }
  248:     }
  249:     my $numstudents = scalar(keys(%students));
  250:     #
  251:     # Determine a nice maximum value to use
  252:     foreach my $maximum (10,15,20,25,30,40,50,60,70,80,90,100,
  253:                           120,150,200,250,300,350,400,450,500,
  254:                           600,700,800,900,1000,1100,1200,1500,2000,
  255:                           2500,3000,4000,5000) {
  256:         if ($max < $maximum) {
  257:             $max = $maximum;
  258:             last;
  259:         }
  260:     }
  261:     #
  262:     # Build the data table
  263:     $htmltable = '<p>'.
  264:         '<table rules="groups" frame="border" '.
  265:         'summary="Student submission data">'.
  266:         '<thead>'.
  267:         '<tr>'.
  268:         '<th valign="bottom">'.&mt('Begin').'</th>'.
  269:         '<th>'.('&nbsp;'x3).'</th>'.
  270:         '<th valign="bottom">'.&mt('End').'</th>'.
  271:         '<th valign="bottom">'.&mt('Submissions (plotted)').'</th>'.
  272:         '<th>'.('&nbsp;'x3).'</th>'.
  273:         '<th valign="bottom">'.&mt('Correct Submissions (not plotted)').'</th>'.
  274:         '<th>'.('&nbsp;'x3).'</th>'.
  275:         '<th valign="bottom">'.&mt('Cumulative Correct of those attempting the problem (not plotted)').'</th>'.
  276:         '<th>'.('&nbsp;'x3).'</th>'.
  277:         '<th valign="bottom">'.&mt('Cumulative Percent Correct of those attempting the problem (not plotted)').'</th>'.
  278:         '<th>'.('&nbsp;'x3).'</th>'.
  279:         '<th valign="bottom">'.&mt('Cumulative Percent Correct of selected students (plotted)').'</th>'.
  280:         '</tr>'.
  281:         '</thead>'.
  282:         '<tbody>';
  283:     my @CumulativeCorrect=(0);
  284:     my @corr_as_percent_of_selected;
  285:     my @corr_as_percent_of_answering;
  286:     for (my $i=0;$i<=$#Ydata;$i++) {
  287:         $CumulativeCorrect[$i]=$CumulativeCorrect[-1]+$AnsData[$i];
  288:         $corr_as_percent_of_answering[$i] = 
  289:             sprintf('%3.1f',100*$CumulativeCorrect[$i]/$numstudents);
  290:         $corr_as_percent_of_selected[$i] = 
  291:             sprintf('%3.1f',100*$CumulativeCorrect[$i]/scalar(@$students));
  292:         if ($Ydata[$i] != 0) {
  293:             next if (! defined($BinEnd[$i]) || $BinEnd[$i] == 0);
  294:             $htmltable .= 
  295:                 '<tr>'.
  296:                 '<td align="right"><nobr>'.
  297:                 &Apache::lonlocal::locallocaltime($BinEnd[$i]-$binsize).
  298:                 '</nobr></td>'.
  299:                 '<td>&nbsp;</td>'.
  300:                 '<td align="right"><nobr>'.
  301:                     &Apache::lonlocal::locallocaltime($BinEnd[$i]).'</td>'.
  302:                 '</nobr></td>'.
  303:                 '<td align="right">'.$Ydata[$i].('&nbsp;'x3).'</td>'.
  304:                 '<td>&nbsp;</td>'.
  305:                 '<td align="right">'.$AnsData[$i].('&nbsp;'x3).'</td>'.
  306:                 '<td>&nbsp;</td>'.
  307:                 '<td align="right">'.$CumulativeCorrect[$i].'</td>'.
  308:                 '<td>&nbsp;</td>'.
  309:                 '<td align="right">'.$corr_as_percent_of_answering[$i].'</td>'.
  310:                 '<td>&nbsp;</td>'.
  311:                 '<td align="right">'.$corr_as_percent_of_selected[$i].'</td>'.
  312:                 '</tr>'.$/;
  313:         }
  314:     }
  315:     $htmltable .= '</tbody></table></p>';
  316:     #
  317:     # Build the plot
  318:     my $title = '';#'Number of Submissions and Number Correct';
  319:     my $xlabel;
  320:     (undef,undef,undef,$mday,$month,$year,$wday) = localtime($day_start);
  321:     $xlabel .= $FullWeekDay[$wday].' '.
  322:         join(' ',($Month[$month],$mday,1900+$year)).' - ';
  323:     (undef,undef,undef,$mday,$month,$year,$wday) = localtime($endtime);
  324:     $xlabel .= $FullWeekDay[$wday].' '.
  325:         join(' ',($Month[$month],$mday,1900+$year));
  326:     my $width = 50+2*$bincount;
  327:     if ($width < 250) {
  328:         $width = 250;
  329:     }
  330:     #
  331:     $html .= &Apache::loncommon::DrawXYYGraph($title,
  332:                                               $xlabel,
  333:                                               'Submissions vs Time',
  334:                                               $plotcolors,
  335:                                               \@Xlabel,
  336:                                               \@Ydata,0,$max,
  337:                                               \@corr_as_percent_of_selected,0,100,
  338:                                               (xskip => $bins_per_day,
  339:                                                x_ticks => $bins_per_day,
  340:                                                x_tick_offset => $bins_per_day,
  341:                                                width => $width,
  342:                       y1_label=>'Number of Submissions per hour',
  343:                       y2_label=>'Percent of Students answering Correctly',
  344:                      'data.1.label'=>'Submissions per hour',
  345:                      'data.2.label'=>'Percent correct',
  346:                                                )
  347:                                               );
  348:     $html .= '<br />'.$htmltable;
  349:     return $html;
  350: }
  351: 
  352: sub successful_submission {
  353:     my ($row) = @_;
  354:     if (ref($row) eq 'ARRAY') {
  355:         return $row->[&Apache::loncoursedata::RT_awarded()];
  356:     }
  357:     return undef;
  358: }
  359: 
  360: sub get_time_from_row {
  361:     my ($row) = @_;
  362:     if (ref($row) eq 'ARRAY') {
  363:         return $row->[&Apache::loncoursedata::RT_timestamp()];
  364:     } 
  365:     return undef;
  366: }
  367: 
  368: sub get_tries_from_row {
  369:     my ($row) = @_;
  370:     if (ref($row) eq 'ARRAY') {
  371:         return $row->[&Apache::loncoursedata::RT_tries()];
  372:     }
  373:     return undef;
  374: }
  375: 
  376: sub Process_Row {
  377:     my ($row) = @_;
  378:     my %RowData;
  379:     my ($student_id,$award,$tries,$time) = @$row;
  380:     return %RowData;
  381: }
  382: 
  383: #########################################################
  384: #########################################################
  385: ##
  386: ##   Generic Interface Routines
  387: ##
  388: #########################################################
  389: #########################################################
  390: sub CreateInterface {
  391:     ##
  392:     ## Environment variable initialization
  393:     if (! exists$env{'form.AnalyzeOver'}) {
  394:         $env{'form.AnalyzeOver'} = 'Tries';
  395:     }
  396:     ##
  397:     ## Build the menu
  398:     my $Str = '';
  399:     $Str .= &Apache::lonhtmlcommon::breadcrumbs('Submission Time Plots');
  400:     $Str .= '<p>';
  401:     $Str .= '<table cellspacing="5">'."\n";
  402:     $Str .= '<tr>';
  403:     $Str .= '<th align="center">'.&mt('Sections').'</th>';
  404:     $Str .= '<th align="center">'.&mt('Groups').'</th>';
  405:     $Str .= '<th align="center">'.&mt('Access Status').'</th>';
  406:     $Str .= '</tr>'."\n";
  407:     ##
  408:     ## 
  409:     $Str .= '<tr><td align="center">'."\n";
  410:     $Str .= &Apache::lonstatistics::SectionSelect('Section','multiple',4);
  411:     $Str .= '</td>';
  412:     #
  413:     $Str .= '<td align="center">'."\n";
  414:     $Str .= &Apache::lonstatistics::GroupSelect('Group','multiple',4);
  415:     $Str .= '</td>';
  416:     #
  417:     $Str .= '<td align="center">';
  418:     $Str .= &Apache::lonhtmlcommon::StatusOptions(undef,undef,4);
  419:     $Str .= '</td>';
  420:     #
  421:     $Str .= '</tr>'."\n";
  422:     $Str .= '</table>'."\n";
  423:     #
  424:     $Str .= '<nobr>'.&mt('Status: [_1]',
  425:                          '<input type="text" '.
  426:                          'name="stats_status" size="60" value="" />').
  427:             '</nobr>'.'</p>';
  428:     ##
  429:     return $Str;
  430: }
  431: 
  432: 1;
  433: 
  434: __END__

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>