# The LearningOnline Network with CAPA # (Publication Handler # # $Id: lonproblemanalysis.pm,v 1.2 2002/07/30 21:31:48 stredwic Exp $ # # Copyright Michigan State University Board of Trustees # # This file is part of the LearningOnline Network with CAPA (LON-CAPA). # # LON-CAPA is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # LON-CAPA is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with LON-CAPA; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # /home/httpd/html/adm/gpl.txt # # http://www.lon-capa.org/ # # (Navigate problems for statistical reports # YEAR=2001 # 5/5,7/9,7/25/1,8/11,9/13,9/26,10/5,10/9,10/22,10/26 Behrouz Minaei # 11/1,11/4,11/16,12/14,12/16,12/18,12/20,12/31 Behrouz Minaei # YEAR=2002 # 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 # 5/12,5/14,5/15,5/19,5/26,7/16 Behrouz Minaei # ### package Apache::lonproblemanalysis; use strict; use Apache::lonnet(); use GDBM_File; my $jr; sub BuildProblemAnalysisPage { my ($cacheDB)=@_; my %cache; my $Str = ''; unless(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER,0640)) { $Str .= 'Unable to tie database.'; return $Str; } $Str .= &IntervalOptions($cache{'Interval'}); $Str .= &OptionResponseTable($cache{'OptionResponses'}, \%cache); untie(%cache); return $Str; } sub BuildAnalyzePage { my ($cacheDB, $students, $courseID,$r)=@_; $jr = $r; my $c = $r->connection; my $Str = ''; my %cache; foreach (@$students) { if($c->aborted) { return $Str; } my $courseData = &Apache::loncoursedata::DownloadCourseInformation($_, $courseID); if(tie(%cache,'GDBM_File',$cacheDB,&GDBM_WRCREAT,0640)) { &Apache::loncoursedata::ProcessStudentData(\%cache, $courseData, $_); untie(%cache); } else { last if($c->aborted()); next; } } unless(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER,0640)) { $Str .= 'Unable to tie database.'; return $Str; } my ($problemId, $part, $responseId)=split(':',$cache{'AnalyzeInfo'}); my $uri = $cache{$problemId.':source'}; my $problem = $cache{$problemId.':problem'}; my $title = $cache{$problemId.':title'}; my $interval = $cache{'Interval'}; my %ConceptData; $ConceptData{"Interval"} = $interval; #Initialize the option response true answers my ($analyzeData) = &InitAnalysis($uri, $part, $responseId, $problem, $students->[0], $courseID); if(defined($analyzeData->{'error'})) { $Str .= 'Incorrect part requested.
'; return $Str; } #compute the intervals &Interval($part, $problem, $interval, $analyzeData->{'concepts'}, \%ConceptData); $title =~ s/\ /"_"/eg; $Str .= '
'.$uri.''; #Java script Progress window # &Create_PrgWin(); # &Update_PrgWin("Starting-to-analyze-problem"); for(my $index=0; $index<(scalar @$students); $index++) { # &Update_PrgWin($index); # &OpStatus($problem, $students->[$index], $courseID, \%ConceptData, # $analyzeData->{'foil_to_concept'}, $analyzeData, \%cache); &OpStatus($problem, $students->[$index], \%ConceptData, $analyzeData->{'foil_to_concept'}, $analyzeData, \%cache); } # &Close_PrgWin(); $Str .= '
'; for (my $k=0; $k<$interval; $k++ ) { $Str .= &DrawGraph($k, $title, $analyzeData->{'concepts'}, \%ConceptData); } for (my $k=0; $k<$interval; $k++ ) { $Str .= &DrawTable($k, $analyzeData->{'concepts'}, \%ConceptData); } my $Answ=&Apache::lonnet::ssi($uri); $Str .= '
Here you can see the Problem:
'.$Answ; untie(%cache); return $Str.'
'; } #---- Problem Analysis Web Page ---------------------------------------------- sub IntervalOptions { my ($selectedInterval)=@_; my $interval = 1; for(my $n=1; $n<=7; $n++) { if($selectedInterval == $n) { $interval = $n; } } my $Ptr = '
Select number of intervals'."\n". ''."\n"; return $Ptr; } sub OptionResponseTable { my ($optionResponses,$cache)=@_; my $Str = ''; $Str .= '
Option Response Problems in this course:'."\n"; $Str .= '

'."\n"; $Str .= ""; $Str .= ''."\n"; my $number=1; my @optionResponses=split(':::', $optionResponses); my %partCount; foreach (@optionResponses) { my ($problemId, $part, undef)=split(':',$_); $partCount{$problemId.':'.$part}++; } foreach (@optionResponses) { my ($problemId, $part, $response)=split(':',$_); my $uri = $cache->{$problemId.':source'}; my $title = $cache->{$problemId.':title'}; my $Temp = ''.$title.''; $Str .= ''; $Str .= ''; $Str .= ''; $Str .= ''; if($partCount{$problemId.':'.$part} < 2) { $Str .= ''."\n"; } else { $Str .= ''."\n"; } $number++; } $Str .= '
\# Problem Title Resource Analysis
'.$number.' '.$Temp.''.$uri.'
'."\n"; return $Str; } #---- END Problem Analysis Web Page ------------------------------------------ #---- Analyze Web Page ------------------------------------------------------- #restore the student submissions and finding the result sub OpStatus { my ($problem, $student, $ConceptData, $foil_to_concept, $analyzeData, $cache)=@_; my $ids = $analyzeData->{'parts'}; my @True = (); my @False = (); my $flag=0; my $latestVersion = $cache->{$student.':version:'.$problem}; if(!$latestVersion) { return; } my $tries=0; for(my $version=1; $version<=$latestVersion; $version++) { my $time=$cache->{$student.':'.$version.':'.$problem.':timestamp'}; foreach my $id (@$ids) { my ($currentPart, undef) = split(/\./, $id); #check if this is a repeat submission, if so skip it next if($cache->{$student.':'.$version.':'.$problem. ':resource.'.$currentPart.'.previous'}); #if no solved this wasn't a real submission, ignore it if(!defined($cache->{"$student:$version:$problem". ":resource.$currentPart.solved"})) { &Apache::lonxml::debug("skipping "); next; } my $Resp = $cache->{$student.':'.$version.':'.$problem. ':resource.'.$id.'.submission'}; my %submission=&Apache::lonnet::str2hash($Resp); foreach (keys(%submission)) { if($submission{$_}) { my $answer = $analyzeData->{$id.'.foil.value.'.$_}; if($submission{$_} eq $answer) { &Decide("true", $foil_to_concept->{$_}, $time, $ConceptData); } else { &Decide("false", $foil_to_concept->{$_}, $time, $ConceptData); } } } } } return; } sub DrawGraph { my ($k,$Src,$Concepts,$ConceptData)=@_; my $Max=0; my @data1; my @data2; # Adjust Data and find the Max for (my $n=0; $n<(scalar @$Concepts); $n++ ) { my $tmp=$Concepts->[$n]; $data1[$n]=$ConceptData->{$tmp.'.'.$k.'.true'}; $data2[$n]=$ConceptData->{$tmp.'.'.$k.'.false'}; my $Sum=$data1[$n]+$data2[$n]; if($Max < $Sum) { $Max=$Sum; } } for (my $n=0; $n<(scalar @$Concepts); $n++ ) { if ($data1[$n]+$data2[$n]<$Max) { $data2[$n]+=$Max-($data1[$n]+$data2[$n]); } } my $P_No = (scalar @data1); if($Max > 1) { $Max += (10 - $Max % 10); $Max = int($Max); } else { $Max = 1; } my $Titr=($ConceptData->{'Interval'}>1) ? $Src.'_interval_'.($k+1) : $Src; # $GData=$Titr.'&Concepts'.'&'.'Answers'.'&'.$Max.'&'.$P_No.'&'.$data1.'&'.$data2; my $GData = ''; $GData = $Titr.'&Concepts&Answers&'.$Max.'&'.$P_No.'&'; $GData .= (join(',',@data1)).'&'.(join(',',@data2)); return ''; } sub DrawTable { my ($k,$Concepts,$ConceptData)=@_; my $Max=0; my @data1; my @data2; my $Correct=0; my $Wrong=0; for(my $n=0; $n<(scalar @$Concepts); $n++ ) { my $tmp=$Concepts->[$n]; $data1[$n]=$ConceptData->{$tmp.'.'.$k.'.true'}; $Correct+=$data1[$n]; $data2[$n]=$ConceptData->{$tmp.'.'.$k.'.false'}; $Wrong+=$data2[$n]; my $Sum=$data1[$n]+$data2[$n]; if($Max < $Sum) { $Max=$Sum; } } for(my $n=0; $n<(scalar @$Concepts); $n++ ) { if ($data1[$n]+$data2[$n]<$Max) { $data2[$n]+=$Max-($data1[$n]+$data2[$n]); } } my $P_No = (scalar @data1); my $Str = ''; # $Str .= '
From: ['.localtime($ConceptData->{'Int.'.($k-1)}); # $Str .= '] To: ['.localtime($ConceptData->{"Int.$k"}).']'; $Str .= "\n".''. "\n".''. "\n".''. "\n".''. "\n".''. "\n".''. "\n".''; for(my $n=0; $n<(scalar @$Concepts); $n++ ) { $Str .= ''."\n"; $Str .= ''."\n"; my ($currentConcept) = split('::',$Concepts->[$n]); $Str .= ''."\n"; $Str .= ''."\n"; $Str .= ''."\n"; $Str .= ''."\n"; } $Str .= ''; $Str .= '
# Concept Correct Wrong
'.($n+1).''.$currentConcept; $Str .= ''.$data1[$n].''.$data2[$n].'
From:['.localtime($ConceptData->{'Int.'.$k}); $Str .= '] To: ['.localtime($ConceptData->{'Int.'.($k+1)}-1); $Str .= ']'.$Correct.''.$Wrong.'
'."\n"; return $Str; #$Apache::lonxml::debug=1; #&Apache::lonhomework::showhash(%ConceptData); #$Apache::lonxml::debug=0; } #---- END Analyze Web Page ---------------------------------------------- sub Decide { #deciding the true or false answer belongs to each interval my ($type,$concept,$time,$ConceptData)=@_; my $k=0; while($time > $ConceptData->{'Int.'.($k+1)} && $k < $ConceptData->{'Interval'}) { $k++; } $ConceptData->{$concept.'.'.$k.'.'.$type}++; return; } sub InitAnalysis { my ($uri,$part,$responseId,$problem,$student,$courseID)=@_; my ($name,$domain)=split(/\:/,$student); my %analyzeData; # Render the student's view of the problem. $Answ is the problem # Stringafied my $Answ=&Apache::lonnet::ssi($uri,('grade_target' => 'analyze', 'grade_username' => $name, 'grade_domain' => $domain, 'grade_courseid' => $courseID, 'grade_symb' => $problem)); my %Answer=(); %Answer=&Apache::lonnet::str2hash($Answ); my $found = 0; my @parts=(); if(defined($responseId)) { foreach (@{$Answer{'parts'}}) { if($_ eq $part.'.'.$responseId) { push(@parts, $_); $found = 1; last; } } } else { foreach (@{$Answer{'parts'}}) { if($_ =~ /$part/) { push(@parts, $_); $found = 1; last; } } } if($found == 0) { $analyzeData{'error'} = 'No parts matching selected values'; return \%analyzeData; } my @Concepts=(); my %foil_to_concept; foreach my $currentPart (@parts) { if(defined($Answer{$currentPart.'.concepts'})) { foreach my $concept (@{$Answer{$currentPart.'.concepts'}}) { push(@Concepts, $concept); foreach my $foil (@{$Answer{$currentPart.'.concept.'. $concept}}) { $analyzeData{$currentPart.'.foil.value.'.$foil} = $Answer{$currentPart.'.foil.value.'.$foil}; $foil_to_concept{$foil} = $concept; } } } else { foreach (keys(%Answer)) { if(/$currentPart.foil\.value\.(.*)$/) { push(@Concepts, $1); $foil_to_concept{$1} = $1; $analyzeData{$currentPart.'.foil.value.'.$1} = $Answer{$currentPart.'.foil.value.'.$1}; } } } } $analyzeData{'parts'} = \@parts; $analyzeData{'concepts'} = \@Concepts; $analyzeData{'foil_to_concept'} = \%foil_to_concept; return \%analyzeData; } sub Interval { my ($part,$symb,$interval,$Concepts,$ConceptData)=@_; my $Int=$interval; my $due = &Apache::lonnet::EXT('resource.'.$part.'.duedate',$symb); my $opn = &Apache::lonnet::EXT('resource.'.$part.'.opendate',$symb); my $add=int(($due-$opn)/$Int); $ConceptData->{'Int.0'}=$opn; for(my $i=1; $i<$Int; $i++) { $ConceptData->{'Int.'.$i}=$opn+$i*$add; } $ConceptData->{'Int.'.$Int}=$due; for(my $i=0; $i<$Int; $i++) { for(my $n=0; $n<(scalar @$Concepts); $n++ ) { my $tmp=$Concepts->[$n]; $ConceptData->{$tmp.'.'.$i.'.true'}=0; $ConceptData->{$tmp.'.'.$i.'.false'}=0; } } } 1; __END__