File:  [LON-CAPA] / loncom / interface / statistics / lonproblemanalysis.pm
Revision 1.3: download - view: text, annotated - select for diffs
Thu Aug 1 20:49:06 2002 UTC (21 years, 11 months ago) by stredwic
Branches: MAIN
CVS tags: HEAD
First, added the parenthesis thing to the GDBM stuff.  Fixed the interface
problem statistics so that the buttons work correctly.  How the data
is interpretted is not finished.

    1: # The LearningOnline Network with CAPA
    2: # (Publication Handler
    3: #
    4: # $Id: lonproblemanalysis.pm,v 1.3 2002/08/01 20:49:06 stredwic Exp $
    5: #
    6: # Copyright Michigan State University Board of Trustees
    7: #
    8: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
    9: #
   10: # LON-CAPA is free software; you can redistribute it and/or modify
   11: # it under the terms of the GNU General Public License as published by
   12: # the Free Software Foundation; either version 2 of the License, or
   13: # (at your option) any later version.
   14: #
   15: # LON-CAPA is distributed in the hope that it will be useful,
   16: # but WITHOUT ANY WARRANTY; without even the implied warranty of
   17: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   18: # GNU General Public License for more details.
   19: #
   20: # You should have received a copy of the GNU General Public License
   21: # along with LON-CAPA; if not, write to the Free Software
   22: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   23: #
   24: # /home/httpd/html/adm/gpl.txt
   25: #
   26: # http://www.lon-capa.org/
   27: #
   28: # (Navigate problems for statistical reports
   29: # YEAR=2001
   30: # 5/5,7/9,7/25/1,8/11,9/13,9/26,10/5,10/9,10/22,10/26 Behrouz Minaei
   31: # 11/1,11/4,11/16,12/14,12/16,12/18,12/20,12/31 Behrouz Minaei
   32: # YEAR=2002
   33: # 1/22,2/1,2/6,2/25,3/2,3/6,3/17,3/21,3/22,3/26,4/7,5/6 Behrouz Minaei
   34: # 5/12,5/14,5/15,5/19,5/26,7/16  Behrouz Minaei
   35: #
   36: ###
   37: 
   38: package Apache::lonproblemanalysis; 
   39: 
   40: use strict;
   41: use Apache::lonnet();
   42: use GDBM_File;
   43: 
   44: my $jr;
   45: 
   46: sub BuildProblemAnalysisPage {
   47:     my ($cacheDB)=@_;
   48: 
   49:     my %cache;
   50:     my $Str = '';
   51:     unless(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER(),0640)) {
   52:         $Str .= '<html><body>Unable to tie database.</body></html>';
   53:         return $Str;
   54:     }
   55: 
   56:     $Str .= &IntervalOptions($cache{'Interval'});
   57:     $Str .= &OptionResponseTable($cache{'OptionResponses'}, \%cache);
   58: 
   59:     untie(%cache);
   60: 
   61:     return $Str;
   62: }
   63: 
   64: sub BuildAnalyzePage {
   65:     my ($cacheDB, $students, $courseID,$r)=@_;
   66: 
   67:     $jr = $r;
   68:     my $c = $r->connection;
   69: 
   70:     my $Str = '</form>';
   71:     my %cache;
   72:     foreach (@$students) {
   73:         if($c->aborted) {
   74:             return $Str;
   75:         }
   76:         my $downloadTime='';
   77:         if(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER(),0640)) {
   78:             $downloadTime = $cache{$_.':lastDownloadTime'};
   79:             untie(%cache);
   80:         }
   81:         if($downloadTime eq 'Not downloaded') {
   82:             my $courseData = 
   83:                 &Apache::loncoursedata::DownloadCourseInformation($_, 
   84:                                                                   $courseID);
   85:             if(tie(%cache,'GDBM_File',$cacheDB,&GDBM_WRCREAT(),0640)) {
   86:                 &Apache::loncoursedata::ProcessStudentData(\%cache, 
   87:                                                            $courseData, $_);
   88:                 untie(%cache);
   89:             } else {
   90:                 next;
   91:             }
   92:         }
   93:     }
   94: 
   95:     unless(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER(),0640)) {
   96:         $Str .= '<html><body>Unable to tie database.</body></html>';
   97:         return $Str;
   98:     }
   99: 
  100:     my ($problemId, $part, $responseId)=split(':',$cache{'AnalyzeInfo'});
  101:     my $uri      = $cache{$problemId.':source'};
  102:     my $problem  = $cache{$problemId.':problem'}; 
  103:     my $title    = $cache{$problemId.':title'};
  104:     my $interval = $cache{'Interval'};
  105: 
  106:     my %ConceptData;
  107:     $ConceptData{"Interval"} = $interval;
  108: 
  109:     #Initialize the option response true answers
  110:     my ($analyzeData) = &InitAnalysis($uri, $part, $responseId, $problem, 
  111:                                       $students->[0], $courseID);
  112:     if(defined($analyzeData->{'error'})) {
  113:         $Str .= 'Incorrect part requested.<br>';
  114:         return $Str;
  115:     }
  116: 
  117:     if($c->aborted()) {  untie(%cache); return $Str; }
  118: 
  119:     #compute the intervals
  120:     &Interval($part, $problem, $interval, $analyzeData->{'concepts'}, 
  121:               \%ConceptData);
  122: 
  123:     $title =~ s/\ /"_"/eg;
  124:     $Str .= '<br><b>'.$uri.'</b>';
  125: 
  126:     if($c->aborted()) {  untie(%cache); return $Str; }
  127:          
  128:     #Java script Progress window
  129: #    &Create_PrgWin();
  130: #    &Update_PrgWin("Starting-to-analyze-problem");
  131:     for(my $index=0; $index<(scalar @$students); $index++) {
  132:         if($c->aborted()) {  untie(%cache); return $Str; }
  133: #	&Update_PrgWin($index);
  134: #	&OpStatus($problem, $students->[$index], $courseID, \%ConceptData,
  135: #                  $analyzeData->{'foil_to_concept'}, $analyzeData, \%cache);
  136: 	&OpStatus($problem, $students->[$index], \%ConceptData, 
  137:                   $analyzeData->{'foil_to_concept'}, $analyzeData, \%cache);
  138:     }
  139: #    &Close_PrgWin();
  140: 
  141:     $Str .= '<br>';
  142:     for (my $k=0; $k<$interval; $k++ ) {
  143:         if($c->aborted()) {  untie(%cache); return $Str; }
  144: 	$Str .= &DrawGraph($k, $title, $analyzeData->{'concepts'}, 
  145:                            \%ConceptData);
  146:     }
  147:     for (my $k=0; $k<$interval; $k++ ) {
  148:         if($c->aborted()) {  untie(%cache); return $Str; }
  149: 	$Str .= &DrawTable($k, $analyzeData->{'concepts'}, \%ConceptData);
  150:     }
  151:     my $Answ=&Apache::lonnet::ssi($uri);
  152:     $Str .= '<br><b>Here you can see the Problem:</b><br>'.$Answ;
  153: 
  154:     untie(%cache);
  155: 
  156:     return $Str.'<form>';
  157: }
  158: 
  159: #---- Problem Analysis Web Page ----------------------------------------------
  160: 
  161: sub IntervalOptions {
  162:     my ($selectedInterval)=@_;
  163: 
  164:     my $interval = 1;
  165:     for(my $n=1; $n<=7; $n++) {
  166:         if($selectedInterval == $n) {
  167:             $interval = $n;
  168:         }
  169:     }
  170: 
  171:     my $Ptr = '<br><b>Select number of intervals</b>'."\n".
  172:        	      '<select name="Interval">'."\n";
  173:     for(my $n=1; $n<=7;$ n++) {
  174: 	$Ptr .= '<option';
  175:         if($interval == $n) {
  176:             $Ptr .= ' selected';
  177:         }
  178: 	$Ptr .= '>'.$n."</option>"."\n";
  179:     }
  180:     $Ptr .= '</select>'."\n";
  181: 
  182:     return $Ptr;
  183: }
  184: 
  185: sub OptionResponseTable {
  186:     my ($optionResponses,$cache)=@_;
  187:     my $Str = '';
  188:     $Str .= '<br><b> Option Response Problems in this course:</b>'."\n";
  189:     $Str .= '<br><br>'."\n";
  190:     $Str .= "<table border=2><tr><th> \# </th><th> Problem Title </th>";
  191:     $Str .= '<th> Resource </th><th> Analysis  </th></tr>'."\n";
  192: 
  193:     my $number=1;
  194:     my @optionResponses=split(':::', $optionResponses);
  195:     my %partCount;
  196:     foreach (@optionResponses) {
  197:         my ($problemId, $part, undef)=split(':',$_);
  198:         $partCount{$problemId.':'.$part}++;
  199:     }
  200: 
  201:     foreach (@optionResponses) {
  202:         my ($problemId, $part, $response)=split(':',$_);
  203:         my $uri = $cache->{$problemId.':source'};
  204:         my $title = $cache->{$problemId.':title'};
  205: 
  206:         my $Temp = '<a href="'.$uri.'" target="_blank">'.$title.'</a>';
  207:         $Str .= '<tr>';
  208:         $Str .= '<td> '.$number.' </td>';
  209:         $Str .= '<td bgcolor="#DDFFDD">'.$Temp.'</td>';
  210:         $Str .= '<td bgcolor="#EEFFCC">'.$uri.'</td>';
  211:         if($partCount{$problemId.':'.$part} < 2) {
  212:             $Str .= '<td><input type="submit" name="Analyze:::';
  213:             $Str .= $problemId.':'.$part.'" value="';
  214:             $Str .= 'Part '.$part;
  215:             $Str .= '" /></td></tr>'."\n";
  216:         } else {
  217:             $Str .= '<td><input type="submit" name="Analyze:::'.$_.'" value="';
  218:             $Str .= 'Part '.$part.' Response '.$response;
  219:             $Str .= '" /></td></tr>'."\n";
  220:         }
  221:         $number++;
  222:     }
  223:     $Str .= '</table>'."\n";
  224: 
  225:     return $Str;
  226: }
  227: 
  228: #---- END Problem Analysis Web Page ------------------------------------------
  229: 
  230: #---- Analyze Web Page -------------------------------------------------------
  231: 
  232: #restore the student submissions and finding the result
  233: sub OpStatus {
  234:     my ($problem, $student, $ConceptData, $foil_to_concept, 
  235:         $analyzeData, $cache)=@_;
  236: 
  237:     my $ids = $analyzeData->{'parts'};
  238:     my @True = ();
  239:     my @False = ();
  240:     my $flag=0;
  241:     my $latestVersion = $cache->{$student.':version:'.$problem};
  242:     if(!$latestVersion) {
  243:         return;
  244:     }
  245: 
  246:     my $tries=0;
  247:     for(my $version=1; $version<=$latestVersion; $version++) {
  248:         my $time=$cache->{$student.':'.$version.':'.$problem.':timestamp'};
  249: 
  250:         foreach my $id (@$ids) {
  251:             my ($currentPart, undef) = split(/\./, $id);
  252:             #check if this is a repeat submission, if so skip it
  253:             next if($cache->{$student.':'.$version.':'.$problem.
  254:                              ':resource.'.$currentPart.'.previous'});
  255:             #if no solved this wasn't a real submission, ignore it
  256:             if(!defined($cache->{"$student:$version:$problem".
  257:                                  ":resource.$currentPart.solved"})) {
  258:                 &Apache::lonxml::debug("skipping ");
  259:                 next;
  260:             }
  261:             my $Resp = $cache->{$student.':'.$version.':'.$problem.
  262:                                 ':resource.'.$id.'.submission'};
  263:             my %submission=&Apache::lonnet::str2hash($Resp);
  264:             foreach (keys(%submission)) {
  265:                 if($submission{$_}) {
  266:                     my $answer = $analyzeData->{$id.'.foil.value.'.$_};
  267:                     if($submission{$_} eq $answer) {
  268:                         &Decide("true", $foil_to_concept->{$_}, 
  269:                                 $time, $ConceptData);
  270:                     } else {
  271:                         &Decide("false", $foil_to_concept->{$_}, 
  272:                                 $time, $ConceptData);
  273:                     }
  274:                 }
  275:             }
  276:         }
  277:     }
  278: 
  279:     return;
  280: }
  281: 
  282: sub DrawGraph {
  283:     my ($k,$Src,$Concepts,$ConceptData)=@_;
  284:     my $Max=0;
  285:     my @data1;
  286:     my @data2;
  287: 
  288:     # Adjust Data and find the Max 
  289:     for (my $n=0; $n<(scalar @$Concepts); $n++ ) {
  290: 	my $tmp=$Concepts->[$n];
  291: 	$data1[$n]=$ConceptData->{$tmp.'.'.$k.'.true'};
  292: 	$data2[$n]=$ConceptData->{$tmp.'.'.$k.'.false'};
  293: 	my $Sum=$data1[$n]+$data2[$n];
  294: 	if($Max < $Sum) {
  295:             $Max=$Sum;
  296:         }
  297:     }
  298:     for (my $n=0; $n<(scalar @$Concepts); $n++ ) {
  299: 	if ($data1[$n]+$data2[$n]<$Max) {
  300: 	    $data2[$n]+=$Max-($data1[$n]+$data2[$n]);
  301: 	}
  302:     }
  303:     my $P_No = (scalar @data1);
  304: 
  305:     if($Max > 1) { 
  306: 	$Max += (10 - $Max % 10);
  307: 	$Max = int($Max);
  308:     } else {
  309:         $Max = 1;
  310:     }
  311: 
  312:     my $Titr=($ConceptData->{'Interval'}>1) ? $Src.'_interval_'.($k+1) : $Src;
  313: #    $GData=$Titr.'&Concepts'.'&'.'Answers'.'&'.$Max.'&'.$P_No.'&'.$data1.'&'.$data2;
  314:     my $GData = '';
  315:     $GData  = $Titr.'&Concepts&Answers&'.$Max.'&'.$P_No.'&';
  316:     $GData .= (join(',',@data1)).'&'.(join(',',@data2));
  317: 
  318:     return '<IMG src="/cgi-bin/graph.gif?'.$GData.'" border=1/>';
  319: }
  320: 
  321: sub DrawTable {
  322:     my ($k,$Concepts,$ConceptData)=@_;
  323:     my $Max=0;
  324:     my @data1;
  325:     my @data2;
  326:     my $Correct=0;
  327:     my $Wrong=0;
  328:     for(my $n=0; $n<(scalar @$Concepts); $n++ ) {
  329: 	my $tmp=$Concepts->[$n];
  330: 	$data1[$n]=$ConceptData->{$tmp.'.'.$k.'.true'};
  331: 	$Correct+=$data1[$n];
  332: 	$data2[$n]=$ConceptData->{$tmp.'.'.$k.'.false'};
  333: 	$Wrong+=$data2[$n];
  334: 	my $Sum=$data1[$n]+$data2[$n];
  335: 	if($Max < $Sum) {
  336:             $Max=$Sum;
  337:         }
  338:     }
  339:     for(my $n=0; $n<(scalar @$Concepts); $n++ ) {
  340: 	if ($data1[$n]+$data2[$n]<$Max) {
  341: 	    $data2[$n]+=$Max-($data1[$n]+$data2[$n]);
  342: 	}
  343:     }
  344:     my $P_No = (scalar @data1);
  345:     my $Str = '';
  346: #    $Str .= '<br><b>From: ['.localtime($ConceptData->{'Int.'.($k-1)});
  347: #    $Str .= '] To: ['.localtime($ConceptData->{"Int.$k"}).']</b>'; 
  348:     $Str .= "\n".'<table border=2>'.
  349:             "\n".'<tr>'.
  350:             "\n".'<th> # </th>'.
  351:             "\n".'<th> Concept </th>'.
  352:             "\n".'<th> Correct </th>'.
  353:             "\n".'<th> Wrong </th>'.
  354:             "\n".'</tr>';
  355: 
  356:     for(my $n=0; $n<(scalar @$Concepts); $n++ ) {
  357: 	$Str .= '<tr>'."\n";
  358:         $Str .= '<td>'.($n+1).'</td>'."\n";
  359:         my ($currentConcept) = split('::',$Concepts->[$n]);
  360:         $Str .= '<td bgcolor="EEFFCC">'.$currentConcept;
  361:         $Str .= '</td>'."\n";
  362:         $Str .= '<td bgcolor="DDFFDD">'.$data1[$n].'</td>'."\n";
  363:         $Str .= '<td bgcolor="FFDDDD">'.$data2[$n].'</td>'."\n";
  364:         $Str .= '</tr>'."\n";
  365:     }
  366:     $Str .= '<td></td><td><b>From:['.localtime($ConceptData->{'Int.'.$k});
  367:     $Str .= '] To: ['.localtime($ConceptData->{'Int.'.($k+1)}-1);
  368:     $Str .= ']</b></td><td>'.$Correct.'</td><td>'.$Wrong.'</td>';
  369:     $Str .= '</table>'."\n";
  370: 
  371:     return $Str;
  372: #$Apache::lonxml::debug=1;
  373: #&Apache::lonhomework::showhash(%ConceptData);
  374: #$Apache::lonxml::debug=0;
  375: }
  376: 
  377: #---- END Analyze Web Page ----------------------------------------------
  378: 
  379: sub Decide {
  380:     #deciding the true or false answer belongs to each interval
  381:     my ($type,$concept,$time,$ConceptData)=@_; 
  382:     my $k=0;
  383:     while($time > $ConceptData->{'Int.'.($k+1)} && 
  384:            $k < $ConceptData->{'Interval'}) {
  385:         $k++;
  386:     }
  387:     $ConceptData->{$concept.'.'.$k.'.'.$type}++;
  388: 
  389:     return;
  390: }
  391: 
  392: sub InitAnalysis {
  393:     my ($uri,$part,$responseId,$problem,$student,$courseID)=@_;
  394:     my ($name,$domain)=split(/\:/,$student);
  395: 
  396:     my %analyzeData;
  397:     # Render the student's view of the problem.  $Answ is the problem 
  398:     # Stringafied
  399:     my $Answ=&Apache::lonnet::ssi($uri,('grade_target'   => 'analyze',
  400:                                         'grade_username' => $name,
  401:                                         'grade_domain'   => $domain,
  402:                                         'grade_courseid' => $courseID,
  403:                                         'grade_symb'     => $problem));
  404: 
  405:     my %Answer=();
  406:     %Answer=&Apache::lonnet::str2hash($Answ);
  407: 
  408:     my $found = 0;
  409:     my @parts=();
  410:     if(defined($responseId)) {
  411:         foreach (@{$Answer{'parts'}}) {
  412:             if($_ eq $part.'.'.$responseId) {
  413:                 push(@parts, $_);
  414:                 $found = 1;
  415:                 last;
  416:             }
  417:         }
  418:     } else {
  419:         foreach (@{$Answer{'parts'}}) {
  420:             if($_ =~ /$part/) {
  421:                 push(@parts, $_);
  422:                 $found = 1;
  423:                 last;
  424:             }
  425:         }
  426:     }
  427: 
  428:     if($found == 0) {
  429:         $analyzeData{'error'} = 'No parts matching selected values';
  430:         return \%analyzeData;
  431:     }
  432: 
  433:     my @Concepts=();
  434:     my %foil_to_concept;
  435:     foreach my $currentPart (@parts) {
  436:         if(defined($Answer{$currentPart.'.concepts'})) {
  437:             foreach my $concept (@{$Answer{$currentPart.'.concepts'}}) {
  438:                 push(@Concepts, $concept);
  439:                 foreach my $foil (@{$Answer{$currentPart.'.concept.'.
  440:                                             $concept}}) {
  441:                     $analyzeData{$currentPart.'.foil.value.'.$foil} =
  442:                         $Answer{$currentPart.'.foil.value.'.$foil};
  443:                     $foil_to_concept{$foil} = $concept;
  444:                 }
  445:             }
  446:         } else {
  447:             foreach (keys(%Answer)) {
  448:                 if(/$currentPart.foil\.value\.(.*)$/) {
  449:                     push(@Concepts, $1);
  450:                     $foil_to_concept{$1} = $1;
  451:                     $analyzeData{$currentPart.'.foil.value.'.$1} =
  452:                         $Answer{$currentPart.'.foil.value.'.$1};
  453:                 }
  454:             }
  455:         }
  456:     }
  457: 
  458:     $analyzeData{'parts'} = \@parts;
  459:     $analyzeData{'concepts'} = \@Concepts;
  460:     $analyzeData{'foil_to_concept'} = \%foil_to_concept;
  461: 
  462:     return \%analyzeData;
  463: }
  464: 
  465: sub Interval {
  466:     my ($part,$symb,$interval,$Concepts,$ConceptData)=@_;
  467:     my $Int=$interval;
  468:     my $due = &Apache::lonnet::EXT('resource.'.$part.'.duedate',$symb);
  469:     my $opn = &Apache::lonnet::EXT('resource.'.$part.'.opendate',$symb);
  470:     my $add=int(($due-$opn)/$Int);
  471:     $ConceptData->{'Int.0'}=$opn;
  472:     for(my $i=1; $i<$Int; $i++) {
  473: 	$ConceptData->{'Int.'.$i}=$opn+$i*$add;
  474:     }
  475:     $ConceptData->{'Int.'.$Int}=$due;     
  476:     for(my $i=0; $i<$Int; $i++) {
  477: 	for(my $n=0; $n<(scalar @$Concepts); $n++ ) {
  478: 	    my $tmp=$Concepts->[$n];
  479: 	    $ConceptData->{$tmp.'.'.$i.'.true'}=0;
  480: 	    $ConceptData->{$tmp.'.'.$i.'.false'}=0;
  481: 	}
  482:     }
  483: }
  484: 1;
  485: __END__

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