# 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 .= '<html><body>Unable to tie database.</body></html>';
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 = '</form>';
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 .= '<html><body>Unable to tie database.</body></html>';
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.<br>';
return $Str;
}
#compute the intervals
&Interval($part, $problem, $interval, $analyzeData->{'concepts'},
\%ConceptData);
$title =~ s/\ /"_"/eg;
$Str .= '<br><b>'.$uri.'</b>';
#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 .= '<br>';
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 .= '<br><b>Here you can see the Problem:</b><br>'.$Answ;
untie(%cache);
return $Str.'<form>';
}
#---- 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 = '<br><b>Select number of intervals</b>'."\n".
'<select name="Interval">'."\n";
for(my $n=1; $n<=7;$ n++) {
$Ptr .= '<option';
if($interval == $n) {
$Ptr .= ' selected';
}
$Ptr .= '>'.$n."</option>"."\n";
}
$Ptr .= '</select>'."\n";
return $Ptr;
}
sub OptionResponseTable {
my ($optionResponses,$cache)=@_;
my $Str = '';
$Str .= '<br><b> Option Response Problems in this course:</b>'."\n";
$Str .= '<br><br>'."\n";
$Str .= "<table border=2><tr><th> \# </th><th> Problem Title </th>";
$Str .= '<th> Resource </th><th> Analysis </th></tr>'."\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 = '<a href="'.$uri.'" target="_blank">'.$title.'</a>';
$Str .= '<tr>';
$Str .= '<td> '.$number.' </td>';
$Str .= '<td bgcolor="#DDFFDD">'.$Temp.'</td>';
$Str .= '<td bgcolor="#EEFFCC">'.$uri.'</td>';
if($partCount{$problemId.':'.$part} < 2) {
$Str .= '<td><input type="submit" name="Analyze:::';
$Str .= $problemId.':'.$part.'" value="';
$Str .= 'Part '.$part;
$Str .= '" /></td></tr>'."\n";
} else {
$Str .= '<td><input type="submit" name="Analyze:::'.$_.'" value="';
$Str .= 'Part '.$part.' Response '.$response;
$Str .= '" /></td></tr>'."\n";
}
$number++;
}
$Str .= '</table>'."\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 '<IMG src="/cgi-bin/graph.gif?'.$GData.'" border=1/>';
}
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 .= '<br><b>From: ['.localtime($ConceptData->{'Int.'.($k-1)});
# $Str .= '] To: ['.localtime($ConceptData->{"Int.$k"}).']</b>';
$Str .= "\n".'<table border=2>'.
"\n".'<tr>'.
"\n".'<th> # </th>'.
"\n".'<th> Concept </th>'.
"\n".'<th> Correct </th>'.
"\n".'<th> Wrong </th>'.
"\n".'</tr>';
for(my $n=0; $n<(scalar @$Concepts); $n++ ) {
$Str .= '<tr>'."\n";
$Str .= '<td>'.($n+1).'</td>'."\n";
my ($currentConcept) = split('::',$Concepts->[$n]);
$Str .= '<td bgcolor="EEFFCC">'.$currentConcept;
$Str .= '</td>'."\n";
$Str .= '<td bgcolor="DDFFDD">'.$data1[$n].'</td>'."\n";
$Str .= '<td bgcolor="FFDDDD">'.$data2[$n].'</td>'."\n";
$Str .= '</tr>'."\n";
}
$Str .= '<td></td><td><b>From:['.localtime($ConceptData->{'Int.'.$k});
$Str .= '] To: ['.localtime($ConceptData->{'Int.'.($k+1)}-1);
$Str .= ']</b></td><td>'.$Correct.'</td><td>'.$Wrong.'</td>';
$Str .= '</table>'."\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__
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>