Annotation of loncom/interface/statistics/lonproblemanalysis.pm, revision 1.42

1.1       stredwic    1: # The LearningOnline Network with CAPA
                      2: #
1.42    ! matthew     3: # $Id: lonproblemanalysis.pm,v 1.41 2003/10/16 15:24:49 matthew Exp $
1.1       stredwic    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: 
1.11      minaeibi   28: package Apache::lonproblemanalysis;
1.1       stredwic   29: 
                     30: use strict;
                     31: use Apache::lonnet();
1.25      matthew    32: use Apache::loncommon();
1.7       stredwic   33: use Apache::lonhtmlcommon();
1.23      matthew    34: use Apache::loncoursedata();
                     35: use Apache::lonstatistics;
                     36: use Apache::lonlocal;
1.37      matthew    37: use HTML::Entities();
1.42    ! matthew    38: use Time::Local();
1.2       stredwic   39: 
1.40      matthew    40: my $plotcolors = ['#33ff00', 
                     41:                   '#0033cc', '#990000', '#aaaa66', '#663399', '#ff9933',
                     42:                   '#66ccff', '#ff9999', '#cccc33', '#660000', '#33cc66',
                     43:                   ]; 
1.39      matthew    44: 
1.1       stredwic   45: sub BuildProblemAnalysisPage {
1.23      matthew    46:     my ($r,$c)=@_;
1.24      matthew    47:     $r->print('<h2>'.&mt('Option Response Problem Analysis').'</h2>');
1.25      matthew    48:     $r->print(&CreateInterface());
1.28      matthew    49:     #
                     50:     my @Students = @Apache::lonstatistics::Students;
                     51:     #
1.41      matthew    52:     if (exists($ENV{'form.ClearCache'}) || 
                     53:         exists($ENV{'form.updatecaches'}) ||
1.33      matthew    54:         (exists($ENV{'form.firstanalysis'}) &&
                     55:          $ENV{'form.firstanalysis'} ne 'no')) {
                     56:         &Apache::lonstatistics::Gather_Full_Student_Data($r);
                     57:     }
                     58:     if (! exists($ENV{'form.firstanalysis'})) {
                     59:         $r->print('<input type="hidden" name="firstanalysis" value="yes" />');
                     60:     } else {
                     61:         $r->print('<input type="hidden" name="firstanalysis" value="no" />');
                     62:     }
1.39      matthew    63:     $r->rflush();
1.33      matthew    64:     if (exists($ENV{'form.problemchoice'}) && 
                     65:         ! exists($ENV{'form.SelectAnother'})) {
1.31      matthew    66:         $r->print('<input type="submit" name="ProblemAnalysis" value="'.
                     67:                   &mt('Analyze Problem Again').'" />');
                     68:         $r->print('&nbsp;'x5);
                     69:         $r->print('<input type="submit" name="ClearCache" value="'.
                     70:                   &mt('Clear Caches').'" />');
                     71:         $r->print('&nbsp;'x5);
1.33      matthew    72:         $r->print('<input type="submit" name="updatecaches" value="'.
                     73:                   &mt('Update Student Data').'" />');
                     74:         $r->print('&nbsp;'x5);
1.31      matthew    75:         $r->print('<input type="hidden" name="problemchoice" value="'.
                     76:                   $ENV{'form.problemchoice'}.'" />');
                     77:         $r->print('<input type="submit" name="SelectAnother" value="'.
                     78:                   &mt('Choose a different resource').'" />');
                     79:         $r->print('&nbsp;'x5);
                     80:         #
1.25      matthew    81:         $r->print('<hr />');
1.23      matthew    82:         #
1.25      matthew    83:         my ($symb,$part,$resid) = &get_problem_symb(
1.23      matthew    84:                      &Apache::lonnet::unescape($ENV{'form.problemchoice'})
                     85:                                            );
1.28      matthew    86:         #
1.23      matthew    87:         my $resource = &get_resource_from_symb($symb);
                     88:         if (defined($resource)) {
1.41      matthew    89:             $r->print('<h3>'.$resource->{'src'}.'</h3>');
1.25      matthew    90:             my %Data = &get_problem_data($resource->{'src'});
                     91:             my $ORdata = $Data{$part.'.'.$resid};
                     92:             ##
1.26      matthew    93:             ## Render the problem
1.25      matthew    94:             my $base;
                     95:             ($base,undef) = ($resource->{'src'} =~ m|(.*/)[^/]*$|);
                     96:             $base = "http://".$ENV{'SERVER_NAME'}.$base;
1.26      matthew    97:             my $rendered_problem = 
                     98:                 &Apache::lonnet::ssi_body($resource->{'src'});
1.30      matthew    99:             $rendered_problem =~ s/<\s*form\s*/<nop /g;
                    100:             $rendered_problem =~ s|(<\s*/form\s*>)|<\/nop>|g;
1.26      matthew   101:             $r->print('<table bgcolor="ffffff"><tr><td>'.
1.25      matthew   102:                       '<base href="'.$base.'" />'.
1.26      matthew   103:                       $rendered_problem.
                    104:                       '</td></tr></table>');
1.25      matthew   105:             ##
                    106:             ## Analyze the problem
1.26      matthew   107:             my $PerformanceData = 
                    108:                 &Apache::loncoursedata::get_optionresponse_data
1.28      matthew   109:                                            (\@Students,$symb,$resid);
1.26      matthew   110:             if (defined($PerformanceData) && 
                    111:                 ref($PerformanceData) eq 'ARRAY') {
1.36      matthew   112:                 if ($ENV{'form.AnalyzeOver'} eq 'Tries') {
1.33      matthew   113:                     my $analysis_html = &tries_analysis($PerformanceData,
                    114:                                                          $ORdata);
                    115:                     $r->print($analysis_html);
1.36      matthew   116:                 } elsif ($ENV{'form.AnalyzeOver'} eq 'Time') {
1.33      matthew   117:                     my $analysis_html = &time_analysis($PerformanceData,
1.28      matthew   118:                                                          $ORdata);
1.26      matthew   119:                 $r->print($analysis_html);
1.28      matthew   120:                 } else {
                    121:                     $r->print('<h2>'.
                    122:                               &mt('The analysis you have selected is '.
                    123:                                          'not supported at this time').
                    124:                               '</h2>');
                    125:                 }
1.26      matthew   126:             } else {
                    127:                 $r->print('<h2>'.
                    128:                           &mt('There is no student data for this problem.').
                    129:                           '</h2>');
                    130:             }
1.23      matthew   131:         } else {
                    132:             $r->print('resource is undefined');
1.7       stredwic  133:         }
1.23      matthew   134:         $r->print('<hr />');
1.25      matthew   135:     } else {
1.31      matthew   136:         $r->print('<input type="submit" name="ProblemAnalysis" value="'.
                    137:                   &mt('Analyze Problem').'" />');
                    138:         $r->print('&nbsp;'x5);
1.27      matthew   139:         $r->print('<h3>'.&mt('Please select a problem to analyze').'</h3>');
1.31      matthew   140:         $r->print(&OptionResponseProblemSelector());
1.1       stredwic  141:     }
                    142: }
                    143: 
1.33      matthew   144: #########################################################
                    145: #########################################################
                    146: ##
                    147: ##      Misc interface routines use by analysis code
                    148: ##
                    149: #########################################################
                    150: #########################################################
                    151: sub build_foil_index {
                    152:     my ($ORdata) = @_;
1.41      matthew   153:     return if (! exists($ORdata->{'Foils'}));
1.36      matthew   154:     my %Foildata = %{$ORdata->{'Foils'}};
                    155:     my @Foils = sort(keys(%Foildata));
                    156:     my %Concepts;
1.25      matthew   157:     foreach my $foilid (@Foils) {
1.36      matthew   158:         push(@{$Concepts{$Foildata{$foilid}->{'Concept'}}},
                    159:              $foilid);
                    160:     }
                    161:     undef(@Foils);
                    162:     # Having gathered the concept information in a hash, we now translate it
                    163:     # into an array because we need to be consistent about order.
                    164:     # Also put the foils in order, too.
                    165:     my $sortfunction = sub {
                    166:         my %Numbers = (one   => 1,
                    167:                        two   => 2,
                    168:                        three => 3,
                    169:                        four  => 4,
                    170:                        five  => 5,
                    171:                        six   => 6,
                    172:                        seven => 7,
                    173:                        eight => 8,
                    174:                        nine  => 9,
                    175:                        ten   => 10,);
                    176:         my $a1 = $a; 
                    177:         my $b1 = $b;
1.39      matthew   178:         if (exists($Numbers{lc($a)})) {
                    179:             $a1 = $Numbers{lc($a)};
1.36      matthew   180:         }
1.39      matthew   181:         if (exists($Numbers{lc($b)})) {
                    182:             $b1 = $Numbers{lc($b)};
1.36      matthew   183:         }
                    184:         $a1 cmp $b1;
                    185:     };
                    186:     my @Concepts;
                    187:     foreach my $concept (sort $sortfunction (keys(%Concepts))) {
1.39      matthew   188:         push(@Concepts,{ name => $concept,
1.36      matthew   189:                         foils => [@{$Concepts{$concept}}]});
                    190:         push(@Foils,(@{$Concepts{$concept}}));
1.25      matthew   191:     }
1.31      matthew   192:     #
                    193:     # Build up the table of row labels.
                    194:     my $table = '<table border="1" >'."\n";
1.39      matthew   195:     if (@Concepts > 1) {
                    196:         $table .= '<tr>'.
                    197:             '<th>'.&mt('Concept Number').'</th>'.
                    198:             '<th>'.&mt('Concept').'</th>'.
                    199:             '<th>'.&mt('Foil Number').'</th>'.
                    200:             '<th>'.&mt('Foil Name').'</th>'.
                    201:             '<th>'.&mt('Foil Text').'</th>'.
                    202:             '<th>'.&mt('Correct Value').'</th>'.
                    203:             "</tr>\n";
                    204:     } else {
                    205:         $table .= '<tr>'.
                    206:             '<th>'.&mt('Foil Number').'</th>'.
                    207:             '<th>'.&mt('Foil Name').'</th>'.
                    208:             '<th>'.&mt('Foil Text').'</th>'.
                    209:             '<th>'.&mt('Correct Value').'</th>'.
                    210:             "</tr>\n";
                    211:     }        
1.36      matthew   212:     my $conceptindex = 1;
                    213:     my $foilindex = 1;
                    214:     foreach my $concept (@Concepts) {
                    215:         my @FoilsInConcept = @{$concept->{'foils'}};
                    216:         my $firstfoil = shift(@FoilsInConcept);
1.39      matthew   217:         if (@Concepts > 1) {
                    218:             $table .= '<tr>'.
                    219:                 '<td>'.$conceptindex.'</td>'.
                    220:                 '<td>'.$concept->{'name'}.'</td>'.
                    221:                 '<td>'.$foilindex++.'</td>'.
                    222:                 '<td>'.$Foildata{$firstfoil}->{'name'}.'</td>'.
                    223:                 '<td>'.$Foildata{$firstfoil}->{'text'}.'</td>'.
                    224:                 '<td>'.$Foildata{$firstfoil}->{'value'}.'</td>'.
                    225:                 "</tr>\n";
                    226:         } else {
1.36      matthew   227:             $table .= '<tr>'.
1.39      matthew   228:                 '<td>'.$foilindex++.'</td>'.
                    229:                 '<td>'.$Foildata{$firstfoil}->{'name'}.'</td>'.
                    230:                 '<td>'.$Foildata{$firstfoil}->{'text'}.'</td>'.
                    231:                 '<td>'.$Foildata{$firstfoil}->{'value'}.'</td>'.
1.36      matthew   232:                 "</tr>\n";
1.39      matthew   233:         }
                    234:         foreach my $foilid (@FoilsInConcept) {
                    235:             if (@Concepts > 1) {
                    236:                 $table .= '<tr>'.
                    237:                     '<td></td>'.
                    238:                     '<td></td>'.
                    239:                     '<td>'.$foilindex.'</td>'.
                    240:                     '<td>'.$Foildata{$foilid}->{'name'}.'</td>'.
                    241:                     '<td>'.$Foildata{$foilid}->{'text'}.'</td>'.
                    242:                     '<td>'.$Foildata{$foilid}->{'value'}.'</td>'.
                    243:                     "</tr>\n";
                    244:             } else {
                    245:                 $table .= '<tr>'.
                    246:                     '<td>'.$foilindex.'</td>'.
                    247:                     '<td>'.$Foildata{$foilid}->{'name'}.'</td>'.
                    248:                     '<td>'.$Foildata{$foilid}->{'text'}.'</td>'.
                    249:                     '<td>'.$Foildata{$foilid}->{'value'}.'</td>'.
                    250:                     "</tr>\n";
                    251:             }                
1.36      matthew   252:         } continue {
                    253:             $foilindex++;
                    254:         }
1.31      matthew   255:     } continue {
1.36      matthew   256:         $conceptindex++;
1.25      matthew   257:     }
1.31      matthew   258:     $table .= "</table>\n";
1.39      matthew   259:     #
                    260:     # Build option index with color stuff
1.36      matthew   261:     return ($table,\@Foils,\@Concepts);
1.33      matthew   262: }
                    263: 
1.39      matthew   264: sub build_option_index {
                    265:     my ($ORdata)= @_;
                    266:     my $table = "<table>\n";
                    267:     my $optionindex = 0;
                    268:     my @Rows;
1.41      matthew   269:     foreach my $option (&mt('correct option chosen'),@{$ORdata->{'Options'}}) {
1.39      matthew   270:         push (@Rows,
                    271:               '<tr>'.
                    272:               '<td bgcolor="'.$plotcolors->[$optionindex++].'">'.
                    273:               ('&nbsp;'x4).'</td>'.
                    274:               '<td>'.$option.'</td>'.
                    275:               "</tr>\n");
                    276:     }
1.42    ! matthew   277:     shift(@Rows); # Throw away 'correct option chosen' color
1.39      matthew   278:     $table .= join('',reverse(@Rows));
                    279:     $table .= "</table>\n";
                    280: }
                    281: 
1.33      matthew   282: #########################################################
                    283: #########################################################
                    284: ##
                    285: ##         Tries Analysis
                    286: ##
                    287: #########################################################
                    288: #########################################################
                    289: sub tries_analysis {
                    290:     my ($PerformanceData,$ORdata) = @_;
                    291:     my $mintries = 1;
                    292:     my $maxtries = $ENV{'form.NumPlots'};
1.39      matthew   293:     my ($table,$Foils,$Concepts) = &build_foil_index($ORdata);
                    294:     if ((@$Concepts < 2) && ($ENV{'form.AnalyzeAs'} ne 'Foils')) {
                    295:         $table = '<h3>'.
                    296:             &mt('Not enough data for concept analysis.  '.
                    297:                 'Performing Foil Analysis').
                    298:             '</h3>'.$table;
                    299:         $ENV{'form.AnalyzeAs'} = 'Foils';
                    300:     }
1.33      matthew   301:     my %ResponseData = &analyze_option_data_by_tries($PerformanceData,
1.36      matthew   302:                                                      $mintries,$maxtries);
1.42    ! matthew   303:     my $analysis = '';
        !           304:     if ($ENV{'form.AnalyzeAs'} eq 'Foils') {
        !           305:         $analysis = &Tries_Foil_Analysis($mintries,$maxtries,$Foils,
        !           306:                                          \%ResponseData,$ORdata);
        !           307:     } else {
        !           308:         $analysis = &Tries_Concept_Analysis($mintries,$maxtries,
        !           309:                                             $Concepts,\%ResponseData,$ORdata);
        !           310:     }
        !           311:     $table .= $analysis;
        !           312:     return $table;
        !           313: }
        !           314: 
        !           315: sub Tries_Foil_Analysis {
        !           316:     my ($mintries,$maxtries,$Foils,$respdat,$ORdata) = @_;
        !           317:     my %ResponseData = %$respdat;
1.31      matthew   318:     #
                    319:     # Compute the data neccessary to make the plots
1.42    ! matthew   320:     my @PlotData; 
        !           321:     foreach my $foilid (@$Foils) {
        !           322:         for (my $i=$mintries;$i<=$maxtries;$i++) {
        !           323:             if ($ResponseData{$foilid}->[$i]->{'_total'} == 0) {
        !           324:                 push(@{$PlotData[$i]->{'_correct'}},0);
        !           325:             } else {
        !           326:                 push(@{$PlotData[$i]->{'_correct'}},
        !           327:                      100*$ResponseData{$foilid}->[$i]->{'_correct'}/
        !           328:                      $ResponseData{$foilid}->[$i]->{'_total'});
        !           329:             }
        !           330:             foreach my $option (@{$ORdata->{'Options'}}) {
        !           331:                 push(@{$PlotData[$i]->{'_total'}},
        !           332:                      $ResponseData{$foilid}->[$i]->{'_total'});
        !           333:                 if ($ResponseData{$foilid}->[$i]->{'_total'} == 0) {
        !           334:                     push (@{$PlotData[$i]->{$option}},0);
        !           335:                 } else {
        !           336:                     if ($ResponseData{$foilid}->[$i]->{'_total'} ==
        !           337:                         $ResponseData{$foilid}->[$i]->{'_correct'}) {
        !           338:                         push(@{$PlotData[$i]->{$option}},0);
1.39      matthew   339:                     } else {
                    340:                         push (@{$PlotData[$i]->{$option}},
                    341:                               100 * $ResponseData{$foilid}->[$i]->{$option} / 
1.42    ! matthew   342:                               ($ResponseData{$foilid}->[$i]->{'_total'} - 
        !           343:                                $ResponseData{$foilid}->[$i]->{'_correct'}));
1.39      matthew   344:                     }
1.36      matthew   345:                 }
                    346:             }
                    347:         }
1.42    ! matthew   348:     }
        !           349:     # 
        !           350:     # Build a table for the plots
        !           351:     my $analysis_html = "<table>\n";
        !           352:     my $foilkey = &build_option_index($ORdata);
        !           353:     for (my $i=$mintries;$i<=$maxtries;$i++) {
        !           354:         my $count = $ResponseData{'_total'}->[$i];
        !           355:         if ($count == 0) {
        !           356:             $count = 'no submissions';
        !           357:         } elsif ($count == 1) {
        !           358:             $count = '1 submission';
        !           359:         } else {
        !           360:             $count = $count.' submissions';
        !           361:         }
        !           362:         my $title = 'Attempt '.$i.', '.$count;
        !           363:         my @Datasets;
        !           364:         foreach my $option ('_correct',@{$ORdata->{'Options'}}) {
        !           365:             next if (! exists($PlotData[$i]->{$option}));
        !           366:             push(@Datasets,$PlotData[$i]->{$option});
        !           367:         }
        !           368:         my $correctgraph = &Apache::loncommon::DrawGraph
        !           369:             ($title,'Foil Number','Percent Correct',
        !           370:              100,$plotcolors,$Datasets[0]);
        !           371:         $analysis_html.= '<tr><td>'.$correctgraph.'</td>';
        !           372:         ##
        !           373:         ##
        !           374:         for (my $i=0; $i< scalar(@{$Datasets[0]});$i++) {
        !           375:             $Datasets[0]->[$i]=0;
        !           376:         }
        !           377:         $count = $ResponseData{'_total'}->[$i]-$ResponseData{'_correct'}->[$i];
        !           378:         if ($count == 0) {
        !           379:             $count = 'no submissions';
        !           380:         } elsif ($count == 1) {
        !           381:             $count = '1 submission';
        !           382:         } else {
        !           383:             $count = $count.' submissions';
        !           384:         }
        !           385:         $title = 'Attempt '.$i.', '.$count;
        !           386:         my $incorrectgraph = &Apache::loncommon::DrawGraph
        !           387:             ($title,'Foil Number','% Option Chosen Incorrectly',
        !           388:              100,$plotcolors,@Datasets);
        !           389:         $analysis_html.= '<td>'.$incorrectgraph.'</td>';
        !           390:         $analysis_html.= '<td>'.$foilkey."<td></tr>\n";
        !           391:     }
        !           392:     $analysis_html .= "</table>\n";
        !           393:     return $analysis_html;
        !           394: }
        !           395: 
        !           396: sub Tries_Concept_Analysis {
        !           397:     my ($mintries,$maxtries,$Concepts,$respdat,$ORdata) = @_;
        !           398:     my %ResponseData = %$respdat;
        !           399:     my $analysis_html = "<table>\n";
        !           400:     #
        !           401:     # Compute the data neccessary to make the plots
        !           402:     my @PlotData;
        !           403:     # Concept analysis
        !           404:     #
        !           405:     # Note: we do not bother with characterizing the students incorrect
        !           406:     # answers at the concept level because an incorrect answer for one foil
        !           407:     # may be a correct answer for another foil.
        !           408:     my %ConceptData;
        !           409:     foreach my $concept (@{$Concepts}) {
        !           410:         for (my $i=$mintries;$i<=$maxtries;$i++) {
        !           411:             #
        !           412:             # Gather the per-attempt data
        !           413:             my $cdata = $ConceptData{$concept}->[$i];
        !           414:             foreach my $foilid (@{$concept->{'foils'}}) {
        !           415:                 $cdata->{'_correct'} += 
        !           416:                     $ResponseData{$foilid}->[$i]->{'_correct'};
        !           417:                 $cdata->{'_total'}   += 
        !           418:                     $ResponseData{$foilid}->[$i]->{'_total'};
        !           419:             }
        !           420:             push (@{$PlotData[$i]->{'_total'}},$cdata->{'_total'});
        !           421:             if ($cdata->{'_total'} == 0) {
        !           422:                 push (@{$PlotData[$i]->{'_correct'}},0);
        !           423:             } else {
        !           424:                 push (@{$PlotData[$i]->{'_correct'}},
        !           425:                       100*$cdata->{'_correct'}/$cdata->{'_total'});
1.36      matthew   426:                 }
1.25      matthew   427:         }
1.42    ! matthew   428:     }
1.31      matthew   429:     # Build a table for the plots
1.25      matthew   430:     for (my $i=$mintries;$i<=$maxtries;$i++) {
1.39      matthew   431:         my $minstu = $PlotData[$i]->{'_total'}->[0];
                    432:         my $maxstu = $PlotData[$i]->{'_total'}->[0];
                    433:         foreach my $count (@{$PlotData[$i]->{'_total'}}) {
1.36      matthew   434:             if ($minstu > $count) {
                    435:                 $minstu = $count;
1.27      matthew   436:             }
1.36      matthew   437:             if ($maxstu < $count) {
                    438:                 $maxstu = $count;
1.27      matthew   439:             }
                    440:         }
1.39      matthew   441:         $maxstu = 0 if (! defined($maxstu));
                    442:         $minstu = 0 if (! defined($minstu));
1.35      matthew   443:         my $title;
1.42    ! matthew   444:         my $count = $ResponseData{'_total'}->[$i];
        !           445:         if ($count == 0) {
        !           446:             $count = 'no submissions';
        !           447:         } elsif ($count == 1) {
        !           448:             $count = '1 submission';
1.27      matthew   449:         } else {
1.42    ! matthew   450:             $count = $count.' submissions';
1.39      matthew   451:         }
1.42    ! matthew   452:         $title = 'Attempt '.$i.', '.$count;
        !           453:         my $graphlink = &Apache::loncommon::DrawGraph
        !           454:             ($title,'Concept Number','Percent Correct',
        !           455:              100,$plotcolors,$PlotData[$i]->{'_correct'});
        !           456:         $analysis_html.= '<tr><td>'.$graphlink."</td></tr>\n";
1.25      matthew   457:     }
1.42    ! matthew   458:     $analysis_html .= "</table>\n";
        !           459:     return $analysis_html;
1.25      matthew   460: }
                    461: 
                    462: sub analyze_option_data_by_tries {
1.26      matthew   463:     my ($PerformanceData,$mintries,$maxtries) = @_;
1.25      matthew   464:     my %Trydata;
                    465:     $mintries = 1         if (! defined($mintries) || $mintries < 1);
                    466:     $maxtries = $mintries if (! defined($maxtries) || $maxtries < $mintries);
1.26      matthew   467:     foreach my $row (@$PerformanceData) {
                    468:         next if (! defined($row));
1.42    ! matthew   469:         my $tries = &get_tries_from_row($row);
        !           470:         my %Row   = &Process_Row($row);
        !           471:         while (my ($foilid,$href) = each(%Row)) {
        !           472:             if (! ref($href)) { 
        !           473:                 $Trydata{$foilid}->[$tries] += $href;
        !           474:                 next;
        !           475:             }
        !           476:             while (my ($option,$value) = each(%$href)) {
        !           477:                 $Trydata{$foilid}->[$tries]->{$option}+=$value;
1.25      matthew   478:             }
                    479:         }
                    480:     }
                    481:     return %Trydata;
                    482: }
                    483: 
1.33      matthew   484: #########################################################
                    485: #########################################################
                    486: ##
                    487: ##                 Time Analysis
                    488: ##
                    489: #########################################################
                    490: #########################################################
                    491: sub time_analysis {
                    492:     my ($PerformanceData,$ORdata) = @_;
1.42    ! matthew   493:     my ($table,$Foils,$Concepts) = &build_foil_index($ORdata);
        !           494:     if ((@$Concepts < 2) && ($ENV{'form.AnalyzeAs'} ne 'Foils')) {
        !           495:         $table = '<h3>'.
        !           496:             &mt('Not enough data for concept analysis.  '.
        !           497:                 'Performing Foil Analysis').
        !           498:             '</h3>'.$table;
        !           499:         $ENV{'form.AnalyzeAs'} = 'Foils';
        !           500:     }
1.33      matthew   501:     my $num_plots = $ENV{'form.NumPlots'};
                    502:     my $num_data = scalar(@$PerformanceData)-1;
                    503:     my $percent = sprintf('%2f',100/$num_plots);
1.42    ! matthew   504:     #
1.37      matthew   505:     $table .= "<table>\n";
1.33      matthew   506:     for (my $i=0;$i<$num_plots;$i++) {
1.42    ! matthew   507:         ##
1.34      matthew   508:         my $starttime = &Apache::lonhtmlcommon::get_date_from_form
                    509:             ('startdate_'.$i);
                    510:         my $endtime = &Apache::lonhtmlcommon::get_date_from_form
                    511:             ('enddate_'.$i);
                    512:         if (! defined($starttime) || ! defined($endtime)) {
1.42    ! matthew   513:             my $sec_in_day = 86400;
        !           514:             my $last_sub_time = &get_time_from_row($PerformanceData->[-1]);
        !           515:             my ($sday,$smon,$syear);
        !           516:             (undef,undef,undef,$sday,$smon,$syear) = 
        !           517:                 localtime($last_sub_time - $sec_in_day*$i);
        !           518:             $starttime = &Time::Local::timelocal(0,0,0,$sday,$smon,$syear);
        !           519:             $endtime = $starttime + $sec_in_day;
        !           520:             if ($i == ($num_plots -1 )) {
        !           521:                 $starttime = &get_time_from_row($PerformanceData->[0]);
        !           522:             }
        !           523:         }
1.34      matthew   524:         my $startdateform = &Apache::lonhtmlcommon::date_setter
                    525:             ('Statistics','startdate_'.$i,$starttime);
                    526:         my $enddateform = &Apache::lonhtmlcommon::date_setter
                    527:             ('Statistics','enddate_'.$i,$endtime);
1.42    ! matthew   528:         #
        !           529:         my $begin_index;
        !           530:         my $end_index;
        !           531:         my $j;
        !           532:         while (++$j < scalar(@$PerformanceData)) {
        !           533:             last if (&get_time_from_row($PerformanceData->[$j]) > $starttime);
        !           534:         }
        !           535:         $begin_index = $j;
        !           536:         while (++$j < scalar(@$PerformanceData)) {
        !           537:             last if (&get_time_from_row($PerformanceData->[$j]) > $endtime);
        !           538:         }
        !           539:         $end_index = $j;
        !           540:         ##
        !           541:         my $interval = {
        !           542:             begin_index => $begin_index,
        !           543:             end_index   => $end_index,
        !           544:             startdateform => $startdateform,
        !           545:             enddateform   => $enddateform,
        !           546:         };
        !           547:         if ($ENV{'form.AnalyzeAs'} eq 'Foils') {
        !           548:             $table .= &Foil_Time_Analysis($PerformanceData,$ORdata,$Foils,
        !           549:                                           $interval);
        !           550:         } else {
        !           551:             $table .= &Concept_Time_Analysis($PerformanceData,$ORdata,
        !           552:                                              $Concepts,$interval);
        !           553:         }
1.33      matthew   554:     }
1.42    ! matthew   555:     #
1.33      matthew   556:     return $table;
                    557: }
                    558: 
1.42    ! matthew   559: sub Foil_Time_Analysis {
        !           560:     my ($PerformanceData,$ORdata,$Foils,$interval) = @_;
        !           561:     my $analysis_html;
        !           562:     my $foilkey = &build_option_index($ORdata);
        !           563:     my ($begin_index,$end_index) = ($interval->{'begin_index'},
        !           564:                                     $interval->{'end_index'});
1.33      matthew   565:     my %TimeData;
                    566:     #
                    567:     # Compute the number getting the foils correct or incorrects
1.42    ! matthew   568:     for (my $j=$begin_index;$j<=$end_index;$j++) {
        !           569:         my $row = $PerformanceData->[$j];
1.33      matthew   570:         next if (! defined($row));
1.42    ! matthew   571:         my %Row = &Process_Row($row);
        !           572:         while (my ($foilid,$href) = each(%Row)) {
        !           573:             if (! ref($href)) {
        !           574:                 $TimeData{$foilid} += $href;
        !           575:                 next;
        !           576:             }
        !           577:             while (my ($option,$value) = each(%$href)) {
        !           578:                 $TimeData{$foilid}->{$option}+=$value;
1.33      matthew   579:             }
                    580:         }
                    581:     }
1.39      matthew   582:     my @Plotdata;
1.42    ! matthew   583:     foreach my $foil (@$Foils) {
        !           584:         my $total = $TimeData{$foil}->{'_total'};
        !           585:         if ($total == 0) {
        !           586:             push(@{$Plotdata[0]},0);
        !           587:         } else {
        !           588:             push(@{$Plotdata[0]},
        !           589:                  100 * $TimeData{$foil}->{'_correct'} / $total);
        !           590:         }
        !           591:         my $total_incorrect = $total - $TimeData{$foil}->{'_correct'};
        !           592:         my $optionidx = 1;
        !           593:         foreach my $option (@{$ORdata->{'Options'}}) {
        !           594:             if ($total_incorrect == 0) {
        !           595:                 push(@{$Plotdata[$optionidx]},0);
        !           596:             } else {
        !           597:                 push(@{$Plotdata[$optionidx]},
        !           598:                      100 * $TimeData{$foil}->{$option} / $total_incorrect);
1.37      matthew   599:             }
1.42    ! matthew   600:         } continue {
        !           601:             $optionidx++;
1.39      matthew   602:         }
1.42    ! matthew   603:     }
        !           604:     #
        !           605:     # Create the plot
        !           606:     my $count = $end_index-$begin_index;
        !           607:     my $title;
        !           608:     if ($count == 0) {
        !           609:         $title = 'no submissions';
        !           610:     } elsif ($count == 1) {
        !           611:         $title = 'one submission';
1.39      matthew   612:     } else {
1.42    ! matthew   613:         $title = $count.' submissions';
        !           614:     }
        !           615:     my $correctplot = &Apache::loncommon::DrawGraph($title,
        !           616:                                                     'Foil Number',
        !           617:                                                     'Percent Correct',
        !           618:                                                     100,
        !           619:                                                     $plotcolors,
        !           620:                                                     $Plotdata[0]);
        !           621:     for (my $j=0; $j< scalar(@{$Plotdata[0]});$j++) {
        !           622:         $Plotdata[0]->[$j]=0;
        !           623:     }
        !           624:     $count = $end_index-$begin_index-$TimeData{'_correct'};
        !           625:     if ($count == 0) {
        !           626:         $title = 'no submissions';
        !           627:     } elsif ($count == 1) {
        !           628:         $title = 'one submission';
        !           629:     } else {
        !           630:         $title = $count.' submissions';
        !           631:     }
        !           632:     my $incorrectplot = &Apache::loncommon::DrawGraph($title,
        !           633:                                                  'Foil Number',
        !           634:                                                  'Incorrect Option Choice',
        !           635:                                                  100,
        !           636:                                                  $plotcolors,
        !           637:                                                  @Plotdata);        
        !           638:     $analysis_html.='<tr>'.
        !           639:         '<td>'.$correctplot.'</td>'.
        !           640:         '<td>'.$incorrectplot.'</td>'.
        !           641:         '<td align="left" valign="top">'.$foilkey.'</td>'."</tr>\n";
        !           642:     $analysis_html.= '<tr>'.'<td colspan="3">'.
        !           643:         '<b>Start Time</b>:'.
        !           644:         ' &nbsp;'.$interval->{'startdateform'}.'<br />'.
        !           645:         '<b>End Time</b>&nbsp;&nbsp;: '.
        !           646:         '&nbsp;'.$interval->{'enddateform'}.'<br />'.
        !           647: #        '<b>Plot Title</b>&nbsp;&nbsp;:'.
        !           648: #        ("&nbsp;"x3).$interval->{'titleform'}.
        !           649:         '</td>'.
        !           650:         "</tr>\n";
        !           651:     return $analysis_html;
        !           652: }
        !           653: 
        !           654: sub Concept_Time_Analysis {
        !           655:     my ($PerformanceData,$ORdata,$Concepts,$interval) = @_;
        !           656:     my $analysis_html;
        !           657:     ##
        !           658:     ## Determine starttime, endtime, startindex, endindex
        !           659:     my ($begin_index,$end_index) = ($interval->{'begin_index'},
        !           660:                                     $interval->{'end_index'});
        !           661:     my %TimeData;
        !           662:     #
        !           663:     # Compute the number getting the foils correct or incorrects
        !           664:     for (my $j=$begin_index;$j<=$end_index;$j++) {
        !           665:         my $row = $PerformanceData->[$j];
        !           666:         next if (! defined($row));
        !           667:         my %Row = &Process_Row($row);
        !           668:         while (my ($foilid,$href) = each(%Row)) {
        !           669:             if (! ref($href)) {
        !           670:                 $TimeData{$foilid} += $href;
        !           671:                 next;
1.37      matthew   672:             }
1.42    ! matthew   673:             while (my ($option,$value) = each(%$href)) {
        !           674:                 $TimeData{$foilid}->{$option}+=$value;
1.37      matthew   675:             }
1.33      matthew   676:         }
                    677:     }
                    678:     #
1.42    ! matthew   679:     # Put the data in plottable form
        !           680:     my @Plotdata;
        !           681:     foreach my $concept (@$Concepts) {
        !           682:         my ($total,$correct);
        !           683:         foreach my $foil (@{$concept->{'foils'}}) {
        !           684:             $total += $TimeData{$foil}->{'_total'};
        !           685:             $correct += $TimeData{$foil}->{'_correct'};
        !           686:         }
        !           687:         if ($total == 0) {
        !           688:             push(@Plotdata,0);
        !           689:         } else {
        !           690:             push(@Plotdata,100 * $correct / $total);
        !           691:         }
        !           692:     }
        !           693:     #
1.33      matthew   694:     # Create the plot
1.42    ! matthew   695:     my $title = ($end_index - $begin_index).' submissions';
        !           696:     my $correctplot = &Apache::loncommon::DrawGraph($title,
        !           697:                                                     'Concept Number',
        !           698:                                                     'Percent Correct',
        !           699:                                                     100,
        !           700:                                                     $plotcolors,
        !           701:                                                     \@Plotdata);
        !           702:     $analysis_html.='<tr>'.
        !           703:         '<td>'.$correctplot.'</td>'.
        !           704:         '<td align="left" valign="top">'.
        !           705:         '<b>Start Time</b>: &nbsp;'.$interval->{'startdateform'}.'<br />'.
        !           706:         '<b>End Time</b>&nbsp;&nbsp;: '.
        !           707:            '&nbsp;'.$interval->{'enddateform'}.'<br />'.
        !           708: #        '<b>Plot Title</b>&nbsp;&nbsp;:'.("&nbsp;"x3).
        !           709: #            $interval->{'titleform'}.
        !           710:         '</td>'.
        !           711:         "</tr>\n";
        !           712:     return $analysis_html;
        !           713: }
        !           714: 
        !           715: #########################################################
        !           716: #########################################################
        !           717: ##
        !           718: ##             Excel output 
        !           719: ##
        !           720: #########################################################
        !           721: #########################################################
        !           722: sub prepare_excel_sheet {
        !           723:     my ($PerformanceData,$ORdata) = @_;
        !           724:     my (undef,$Foils,$Concepts) = &build_foil_index($ORdata);
        !           725: }
        !           726: 
        !           727: sub process_data_for_excel_sheet {
        !           728:     my ($PerformanceData,$Foils,$Concepts,$ORdata)=@_;
1.1       stredwic  729: }
                    730: 
1.33      matthew   731: #########################################################
                    732: #########################################################
                    733: ##
                    734: ##             Interface 
                    735: ##
                    736: #########################################################
                    737: #########################################################
1.23      matthew   738: sub CreateInterface {
1.28      matthew   739:     ##
                    740:     ## Environment variable initialization
1.36      matthew   741:     if (! exists$ENV{'form.AnalyzeOver'}) {
                    742:         $ENV{'form.AnalyzeOver'} = 'Tries';
1.28      matthew   743:     }
                    744:     ##
                    745:     ## Build the menu
1.7       stredwic  746:     my $Str = '';
1.23      matthew   747:     $Str .= '<table cellspacing="5">'."\n";
                    748:     $Str .= '<tr>';
                    749:     $Str .= '<td align="center"><b>'.&mt('Sections').'</b></td>';
                    750:     $Str .= '<td align="center"><b>'.&mt('Enrollment Status').'</b></td>';
1.31      matthew   751: #    $Str .= '<td align="center"><b>'.&mt('Sequences and Folders').'</b></td>';
                    752:     $Str .= '<td align="center">&nbsp;</td>';
1.23      matthew   753:     $Str .= '</tr>'."\n";
1.31      matthew   754:     ##
                    755:     ## 
1.23      matthew   756:     $Str .= '<tr><td align="center">'."\n";
                    757:     $Str .= &Apache::lonstatistics::SectionSelect('Section','multiple',5);
1.28      matthew   758:     $Str .= '</td>';
                    759:     #
                    760:     $Str .= '<td align="center">';
1.23      matthew   761:     $Str .= &Apache::lonhtmlcommon::StatusOptions(undef,undef,5);
1.28      matthew   762:     $Str .= '</td>';
                    763:     #
1.31      matthew   764: #    $Str .= '<td align="center">';
1.23      matthew   765:     my $only_seq_with_assessments = sub { 
                    766:         my $s=shift;
                    767:         if ($s->{'num_assess'} < 1) { 
                    768:             return 0;
                    769:         } else { 
                    770:             return 1;
                    771:         }
                    772:     };
1.31      matthew   773:     &Apache::lonstatistics::MapSelect('Maps','multiple,all',5,
1.23      matthew   774:                                               $only_seq_with_assessments);
1.36      matthew   775:     ##
                    776:     ##
1.28      matthew   777:     $Str .= '<td>';
1.36      matthew   778:     { # These braces are here to organize the code, not scope it.
                    779:         {
                    780:             $Str .= '<nobr>'.&mt('Analyze Over ');
                    781:             $Str .='<select name="AnalyzeOver" >';
                    782:             $Str .= '<option value="Tries" ';
                    783:             if (! exists($ENV{'form.AnalyzeOver'}) || 
                    784:                 $ENV{'form.AnalyzeOver'} eq 'Tries'){
                    785:                 # Default to Tries
                    786:                 $Str .= ' selected ';
                    787:             }
                    788:             $Str .= '>'.&mt('Tries').'</option>';
                    789:             $Str .= '<option value="Time" ';
                    790:             $Str .= ' selected ' if ($ENV{'form.AnalyzeOver'} eq 'Time');
                    791:             $Str .= '>'.&mt('Time').'</option>';
                    792:             $Str .= '</select></nobr><br />';
                    793:         }
                    794:         {
                    795:             $Str .= '<nobr>'.&mt('Analyze as ');
                    796:             $Str .='<select name="AnalyzeAs" >';
                    797:             $Str .= '<option value="Concepts" ';
                    798:             if (! exists($ENV{'form.AnalyzeAs'}) || 
                    799:                 $ENV{'form.AnalyzeAs'} eq 'Concepts'){
                    800:                 # Default to Concepts
                    801:                 $Str .= ' selected ';
                    802:             }
                    803:             $Str .= '>'.&mt('Concepts').'</option>';
                    804:             $Str .= '<option value="Foils" ';
                    805:             $Str .= ' selected ' if ($ENV{'form.AnalyzeAs'} eq 'Foils');
                    806:             $Str .= '>'.&mt('Foils').'</option>';
                    807:             $Str .= '</select></nobr><br />';
                    808:         }
                    809:         {
                    810:             $Str .= '<br /><nobr>'.&mt('Number of Plots:');
                    811:             $Str .= '<select name="NumPlots">';
                    812:             if (! exists($ENV{'form.NumPlots'}) 
                    813:                 || $ENV{'form.NumPlots'} < 1 
                    814:                 || $ENV{'form.NumPlots'} > 20) {
                    815:                 $ENV{'form.NumPlots'} = 5;
                    816:             }
                    817:             foreach my $i (1,2,3,4,5,6,7,8,10,15,20) {
                    818:                 $Str .= '<option value="'.$i.'" ';
                    819:                 if ($ENV{'form.NumPlots'} == $i) { $Str.=' selected '; }
                    820:                 $Str .= '>'.$i.'</option>';
                    821:             }
                    822:             $Str .= '</select></nobr>';
                    823:         }
1.28      matthew   824:     }
                    825:     $Str .= '</td>';
1.36      matthew   826:     ##
                    827:     ##
1.28      matthew   828:     $Str .= '</tr>'."\n";
1.23      matthew   829:     $Str .= '</table>'."\n";
1.42    ! matthew   830:     return $Str;
1.23      matthew   831: }
                    832: 
                    833: sub OptionResponseProblemSelector {
                    834:     my $Str;
                    835:     $Str = "\n<table>\n";
                    836:     foreach my $seq (&Apache::lonstatistics::Sequences_with_Assess()) {
                    837:         next if ($seq->{'num_assess'}<1);
                    838:         my $seq_str = '';
                    839:         foreach my $res (@{$seq->{'contents'}}) {
1.26      matthew   840:             next if ($res->{'type'} ne 'assessment');
1.23      matthew   841:             foreach my $part (@{$res->{'parts'}}) {
                    842:                 my $partdata = $res->{'partdata'}->{$part};
                    843:                 if (! exists($partdata->{'option'}) || 
                    844:                     $partdata->{'option'} == 0) {
                    845:                     next;
                    846:                 }
                    847:                 for (my $i=0;$i<scalar(@{$partdata->{'ResponseTypes'}});$i++){
                    848:                     my $respid = $partdata->{'ResponseIds'}->[$i];
                    849:                     my $resptype = $partdata->{'ResponseTypes'}->[$i];
                    850:                     if ($resptype eq 'option') {
1.25      matthew   851:                         my $value = &Apache::lonnet::escape($res->{'symb'}.':'.$part.':'.$respid);
1.23      matthew   852:                         my $checked = '';
                    853:                         if ($ENV{'form.problemchoice'} eq $value) {
                    854:                             $checked = 'checked ';
                    855:                         }
                    856:                         $seq_str .= '<tr><td>'.
                    857:   '<input type="radio" name="problemchoice" value="'.$value.'" '.$checked.'/>'.
                    858:   '</td><td>'.
                    859:   '<a href="'.$res->{'src'}.'">'.$res->{'title'}.'</a> ';
                    860:                         if ($partdata->{'option'} > 1) {
                    861:                             $seq_str .= &mt('response').' '.$respid;
                    862:                         }
                    863:                         $seq_str .= "</td></tr>\n";
1.11      minaeibi  864:                     }
                    865:                 }
                    866:             }
                    867:         }
1.23      matthew   868:         if ($seq_str ne '') {
                    869:             $Str .= '<tr><td>&nbsp</td><td><b>'.$seq->{'title'}.'</b></td>'.
                    870:                 "</tr>\n".$seq_str;
                    871:         }
1.11      minaeibi  872:     }
1.23      matthew   873:     $Str .= "</table>\n";
                    874:     return $Str;
1.33      matthew   875: }
                    876: 
                    877: #########################################################
                    878: #########################################################
                    879: ##
                    880: ##              Misc functions
                    881: ##
                    882: #########################################################
                    883: #########################################################
                    884: sub get_problem_symb {
                    885:     my $problemstring = shift();
                    886:     my ($symb,$partid,$resid) = ($problemstring=~ /^(.*):([^:]*):([^:]*)$/);
                    887:     return ($symb,$partid,$resid);
1.11      minaeibi  888: }
                    889: 
1.23      matthew   890: sub get_resource_from_symb {
                    891:     my ($symb) = @_;
                    892:     foreach my $seq (&Apache::lonstatistics::Sequences_with_Assess()) {
                    893:         foreach my $res (@{$seq->{'contents'}}) {
                    894:             if ($res->{'symb'} eq $symb) {
                    895:                 return $res;
1.2       stredwic  896:             }
1.1       stredwic  897:         }
                    898:     }
1.23      matthew   899:     return undef;
1.42    ! matthew   900: }
        !           901: 
        !           902: sub get_time_from_row {
        !           903:     my ($row) = @_;
        !           904:     if (ref($row)) {
        !           905:         return $row->[3];
        !           906:     } 
        !           907:     return undef;
        !           908: }
        !           909: 
        !           910: sub get_tries_from_row {
        !           911:     my ($row) = @_;
        !           912:     if (ref($row)) {
        !           913:         return $row->[4];
        !           914:     }
        !           915:     return undef;
        !           916: }
        !           917: 
        !           918: sub Process_Row {
        !           919:     my ($row) = @_;
        !           920:     my %RowData;
        !           921:     my ($award,$grading,$submission,$time,$tries) = @$row;
        !           922:     next if ($award eq 'MISSING_ANSWER');
        !           923:     if ($award =~ /(APPROX_ANS|EXACT_ANS)/) {
        !           924:         $RowData{'_correct'} = 1;
        !           925:     }
        !           926:     $RowData{'_total'} = 1;
        !           927:     my @Foilgrades = split('&',$grading);
        !           928:     my @Foilsubs   = split('&',$submission);
        !           929:     for (my $j=0;$j<=$#Foilgrades;$j++) {
        !           930:         my ($foilid,$correct)  = split('=',$Foilgrades[$j]);
        !           931:         my (undef,$submission) = split('=',$Foilsubs[$j]);
        !           932:         if ($correct) {
        !           933:             $RowData{$foilid}->{'_correct'}++;
        !           934:         } else {
        !           935:             $submission = &Apache::lonnet::unescape($submission);
        !           936:             $submission =~ s/\%20/ /g;
        !           937:             $RowData{$foilid}->{$submission}++;
        !           938:         }
        !           939:         $RowData{$foilid}->{'_total'}++;
        !           940:     }
        !           941:     return %RowData;
1.1       stredwic  942: }
                    943: 
1.39      matthew   944: ##
                    945: ## get problem data and put it into a useful data structure.
                    946: ## note: we must force each foil and option to not begin or end with
                    947: ##       spaces as they are stored without such data.
                    948: ##
1.25      matthew   949: sub get_problem_data {
                    950:     my ($url) = @_;
                    951:     my $Answ=&Apache::lonnet::ssi($url,('grade_target' => 'analyze'));
1.23      matthew   952:     (my $garbage,$Answ)=split(/_HASH_REF__/,$Answ,2);
1.25      matthew   953:     my %Answer;
1.23      matthew   954:     %Answer=&Apache::lonnet::str2hash($Answ);
1.25      matthew   955:     my %Partdata;
                    956:     foreach my $part (@{$Answer{'parts'}}) {
                    957:         while (my($key,$value) = each(%Answer)) {
                    958:             next if ($key !~ /^$part/);
                    959:             $key =~ s/^$part\.//;
                    960:             if (ref($value) eq 'ARRAY') {
                    961:                 if ($key eq 'options') {
                    962:                     $Partdata{$part}->{'Options'}=$value;
                    963:                 } elsif ($key eq 'concepts') {
                    964:                     $Partdata{$part}->{'Concepts'}=$value;
                    965:                 } elsif ($key =~ /^concept\.(.*)$/) {
                    966:                     my $concept = $1;
                    967:                     foreach my $foil (@$value) {
1.36      matthew   968:                         $Partdata{$part}->{'Foils'}->{$foil}->{'Concept'}=
                    969:                                                                       $concept;
1.25      matthew   970:                     }
                    971:                 }
                    972:             } else {
                    973:                 if ($key=~ /^foil\.text\.(.*)$/) {
                    974:                     my $foil = $1;
1.36      matthew   975:                     $Partdata{$part}->{'Foils'}->{$foil}->{'name'}=$foil;
                    976:                     $Partdata{$part}->{'Foils'}->{$foil}->{'text'}=$value;
1.25      matthew   977:                 } elsif ($key =~ /^foil\.value\.(.*)$/) {
                    978:                     my $foil = $1;
1.36      matthew   979:                     $Partdata{$part}->{'Foils'}->{$foil}->{'value'}=$value;
1.25      matthew   980:                 }
                    981:             }
                    982:         }
1.23      matthew   983:     }
1.25      matthew   984:     return %Partdata;
1.1       stredwic  985: }
                    986: 
1.23      matthew   987: 1;
1.1       stredwic  988: 
                    989: __END__

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