# The LearningOnline Network with CAPA
# (Publication Handler
#
# $Id: lonstatistics.pm,v 1.28 2002/07/19 18:17:34 minaeibi 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::lonstatistics;
use strict;
use Apache::Constants qw(:common :http);
use Apache::lonnet();
use Apache::lonhomework;
use Apache::loncommon;
use HTML::TokeParser;
use GDBM_File;
# -------------------------------------------------------------- Module Globals
my %hash;
my %CachData;
my %GraphDat;
my $r;
my $GData;
my %color;
my %foil_to_concept;
my @Concepts;
my %ConceptData;
my %Answer=();
my %mapsort;
my %Activity=();
my %Grade=();
my %DoDiff=();
my %Discuss=();
my $TotalDiscuss=0;
my $TotalDiscuss_=0;
sub LoadDiscussion {
# my $symb=shift;
# $r->print(' $cid ... '.$symb);
# my %contrib=&Apache::lonnet::dump('msu_2964385f9033c63msul1','msu','2964385f9033c63msul1');
my $cid=$ENV{'request.course.id'};
my %contrib=&Apache::lonnet::dump(
$ENV{'request.course.id'},
$ENV{'course.'.$ENV{'request.course.id'}.'.domain'},
$ENV{'course.'.$ENV{'request.course.id'}.'.num'});
foreach my $temp(keys %contrib) {
if ($temp=~/^version/) {
my $ver=$contrib{$temp};
my ($dummy,$prb)=split(':',$temp);
for (my $idx=1; $idx<=$ver; $idx++ ) {
my $name=$contrib{"$idx:$prb:sendername"};
$Discuss{"$name:$prb"}=$idx;
}
}
}
# $r->print(' cid='.$cid);
# my %contrib=&Apache::lonnet::restore($symb,$cid,
# $ENV{$cid.'.domain'},
# $ENV{'course.'.$cid.'.num'});
# $Apache::lonxml::debug=1;
# &Apache::lonhomework::showhash(%Discuss);
# $Apache::lonxml::debug=0;
}
sub LoadDoDiffFile {
my $file="/home/minaeibi/183d.txt";
open(FILEID, "<$file");
my $line=;
%DoDiff=();
my @Act=split('&',$line);
# $r->print(' '.$#Act);
for(my $n=0;$n<=$#Act;$n++){
my ($res,$Degree)=split('@',$Act[$n]);
$DoDiff{$res}=$Degree;
}
}
sub LoadClassFile {
my $file="/home/minaeibi/class.txt";
open(FILEID, "<$file");
my $line;
%Grade=();
while ($line=) {
my ($id,$ex1,$ex2,$ex3,$ex4,$hw,$final,$grade)=split(' ',$line);
$Grade{$id}=$grade;
}
}
#------- Classification
sub Classify {
my ($DiscFac, $students)=@_;
&LoadClassFile();
my $Count=0;
my @List=();
my @LS=();
my @LF=();
my @LM=();
my $cf=0;
my $cs=0;
my $cm=0;
foreach (keys(%$DiscFac)){
my @l=split(/\:/,$_);
if (!($students->{$l[1]})) {next;}
my $Grade=$Grade{$students->{$l[1]}};
if( $Grade > 3 ) {
$cs++;
push(@LS,("$l[6],$l[5],$l[4],$l[7],$l[8],$l[9],Successful"));
} elsif ( $Grade > 2 ) {
$cm++;
push(@LM,("$l[6],$l[5],$l[4],$l[7],$l[8],$l[9],Average"));
} else {
$cf++;
push(@LF,("$l[6],$l[5],$l[4],$l[7],$l[8],$l[9],Failed"));
}
}
for(my $n=0;$n<$cs;$n++){$r->print(' '.$LS[$n]);}
for(my $n=0;$n<$cm;$n++){$r->print(' '.$LM[$n]);}
for(my $n=0;$n<$cf;$n++){$r->print(' '.$LF[$n]);}
}
sub ProcAct {
# return;
my ($Act,$Submit)=@_;
my @Act=split(/\@/,$Act);
@Act = sort(@Act);
##$r->print(' '.$#Act);
##for(my $n=0;$n<=$#Act;$n++){
## $r->print(' n='.$n.')'.$Act[$n]);
## }
# my $Beg=$Act[0];
my $Dif=$Submit-$Act[0];
$Dif = ($Dif>0) ? ($Dif/3600) : 0;
# $r->print(' Access Number = '.$#Act.' Submit Time='.$Submit.' First Access='.$Act[0].' Last Access='.$Act[$#Act].' Submit - First = '.$Dif.'');
#time spent for solving the problem
# $r->print(' Def'.($Act[$#Act-1]-$Act[0]));
return $Dif;
}
sub LoadActivityLog {
# my $CacheDB = "/home/minaeibi/act183.log.cache";
my $CacheDB = "/home/httpd/perl/tmp/act183.log.cache";
if (-e "$CacheDB") {
if (tie(%Activity,'GDBM_File',"$CacheDB",&GDBM_READER,0640)) {
return;
}
else {
$r->print("Unable to tie log Cache hash to db file");
}
}
else {
if (tie(%Activity,'GDBM_File',$CacheDB,&GDBM_WRCREAT,0640)) {
foreach (keys %Activity) {delete $Activity{$_};}
&Build_log();
}
else {
$r->print("Unable to tie log Build hash to db file");
}
}
}
sub Build_log {
my $file="/home/minaeibi/act183.log";
open(FILEID, "<$file");
my $line;
my $count=0;
while ($line=) {
my ($time,$machine,$what)=split(':',$line);
$what=&Apache::lonnet::unescape($what);
my @accesses=split('&',$what);
foreach my $access (@accesses) {
$count++;
my ($date,$resource,$who,$domain,$post,@posts)=split(':',$access);
if (!$resource) { next; }
my $res=&Apache::lonnet::unescape($resource);
if (($res =~ /\.problem/)) {
$Activity{$who.':'.$res}.=$date.'@';
#$r->print(' '.$time.':'.$who.'---'.$res);
&Update_PrgInit($count);
}
}
}
# my $c=1;
# foreach (sort keys %Activity) {
# $r->print(' '.$c.')'.$_.' ... '.$Activity{$_});
# $c++;
# }
}
sub Activity {
# $rid=~/(\d+)\.(\d+)/;
# my $MapId=$1;
# my $PrbId=$2;
# my $MapOrg = $hash{'map_id_'.$MapId};
# my $Map = &Apache::lonnet::declutter($MapOrg);
# my $URI = $hash{'src_'.$rid};
# my $Symb = $Map.'___'.$PrbId.'___'.&Apache::lonnet::declutter($URI);
my $file="/home/minaeibi/activity.log";
my $userid='adamsde1';
$r->print(" Using $file");
$r->rflush();
open(FILEID, "<$file");
my $line;
my @allaccess;
my $Count=0;
while ($line=) {
my ($time,$machine,$what)=split(':',$line);
$what=&Apache::lonnet::unescape($what);
my @accesses=split('&',$what);
foreach my $access (@accesses) {
my ($date,$resource,$who,$domain,$post,@posts)=split(':',$access);
#if ($who ne $userid) { next; }
if (!$resource) { next; }
my $res=&Apache::lonnet::unescape($resource);
if (($res =~ /\.(sequence|problem|htm|html|page)/)) {
$Count++;
$r->print(" $Count) ".localtime($date).": $who --> $res");
# if ($post) {
# $Count++;
# $r->print(" $Count) Sent data ".join(':',
# &Apache::lonnet::unescape(@posts)).'');
# }
$r->rflush();
}
#push (@allaccess,unescape($access));
#print $machine;
}
}
# @allaccess=sort(@allaccess);
# $Count=0;
# foreach my $access (@allaccess) {
# my ($date,$resource,$who,$domain,$post,@posts)=split(':',$access);
# $Count++;
# $r->print(" $Count) $date: $who --> $resource");
# $r->rflush();
# if ($post) {
# $r->print(" Sent data ".join(':',unescape(@posts)).'');
# }
# }
}
sub InitAnalysis {
my ($uri,$part,$problem,$student,$courseID)=@_;
my ($uname,$udom)=split(/\:/,$student);
# Render the student's view of the problem. $Answ is the problem
# Stringafied
my $Answ=&Apache::lonnet::ssi($uri,('grade_target' => 'analyze',
'grade_username' => $uname,
'grade_domain' => $udom,
'grade_courseid' => $courseID,
'grade_symb' => $problem));
# my $Answ=&Apache::lonnet::ssi($URI,('grade_target' => 'analyze'));
# (my $garbage,$Answ)=split(/_HASH_REF__/,$Answ,2);
%Answer=();
%Answer=&Apache::lonnet::str2hash($Answ);
my $parts='';
foreach my $elm (@{$Answer{"parts"}}) {
$parts.="$elm,";
}
chop($parts);
my $conc='';
foreach my $elm (@{$Answer{"$parts.concepts"}}) {
$conc.="$elm@";
}
chop($conc);
@Concepts=split(/\@/,$conc);
foreach my $concept (@{$Answer{"$parts.concepts"}}) {
foreach my $foil (@{$Answer{"$parts.concept.$concept"}}) {
$foil_to_concept{$foil} = $concept;
#$ConceptData{$foil} = $Answer{"$parts.foil.value.$foil"};
}
}
return;
}
sub Interval {
my ($part,$symb)=@_;
my $Int=$ConceptData{"Interval"};
my $due = &Apache::lonnet::EXT('resource.$part.duedate',$symb)+1;
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<=$#Concepts; $n++ ) {
my $tmp=$Concepts[$n];
$ConceptData{"$tmp.$i.true"}=0;
$ConceptData{"$tmp.$i.false"}=0;
}
}
}
sub ShowOpGraph {
my ($cache, $students, $courseID)=@_;
my $uri = $cache->{'AnalyzeURI'};
my $part = $cache->{'AnalyzePart'};
my $problem = $cache->{'AnalyzeProblem'};
my $title = $cache->{'AnalyzeTitle'};
my $interval = $cache->{'Interval'};
$ConceptData{"Interval"} = $interval;
#Initialize the option response true answers
&InitAnalysis($uri, $part, $problem, $students->[0],$courseID);
#compute the intervals
&Interval($part,$problem);
$title =~ s/\ /"_"/eg;
$r->print(' '.$uri.'');
$r->rflush();
#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);
}
&Close_PrgWin();
$r->print(' ');
for (my $k=0; $k<$interval; $k++ ) {
&DrawGraph($k,$title);
}
for (my $k=0; $k<$interval; $k++ ) {
&DrawTable($k);
}
#$Apache::lonxml::debug=1;
#&Apache::lonhomework::showhash(%ConceptData);
#$Apache::lonxml::debug=0;
my $Answ=&Apache::lonnet::ssi($uri);
$r->print(" Here you can see the Problem: $Answ");
}
sub DrawTable {
my $k=shift;
my $Max=0;
my @data1;
my @data2;
my $Correct=0;
my $Wrong=0;
for (my $n=0; $n<=$#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<=$#Concepts; $n++ ) {
if ($data1[$n]+$data2[$n]<$Max) {
$data2[$n]+=$Max-($data1[$n]+$data2[$n]);
}
}
my $P_No = $#data1+1;
# $r->print(' From: ['.localtime($ConceptData{'Int.'.($k-1)}).
# '] To: ['.localtime($ConceptData{"Int.$k"}).']');
my $Str = "\n".'
From:['.localtime($ConceptData{'Int.'.$k}).
'] To: ['.localtime($ConceptData{'Int.'.($k+1)}-1).
"]
$Correct
$Wrong
";
$Str .= "\n".'
';
$r->print($Str);
#$Apache::lonxml::debug=1;
#&Apache::lonhomework::showhash(%ConceptData);
#$Apache::lonxml::debug=0;
}
sub DrawGraph {
my ($k,$Src)=@_;
my $Max=0;
my @data1;
my @data2;
# Adjust Data and find the Max
for (my $n=0; $n<=$#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<=$#Concepts; $n++ ) {
if ($data1[$n]+$data2[$n]<$Max) {
$data2[$n]+=$Max-($data1[$n]+$data2[$n]);
}
}
my $P_No = $#data1+1;
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;
$GData="$Titr&Concepts&Answers&$Max&$P_No&".
(join(',',@data1)).'&'.(join(',',@data2));
$r->print('');
}
sub Decide {
#deciding the true or false answer belongs to each interval
my ($type,$foil,$time)=@_;
my $k=0;
while ($time>$ConceptData{'Int.'.($k+1)} &&
$k<$ConceptData{'Interval'}) {$k++;}
$ConceptData{"$foil_to_concept{$foil}.$k.$type"}++;
}
#restore the student submissions and finding the result
sub OpStatus {
my ($problem, $student, $courseID)=@_;
my ($username,$userdomain)=split(/':'/,$student);
my $code='U';
my %reshash=&Apache::lonnet::restore($problem, $courseID, $userdomain,
$username);
my @True = ();
my @False = ();
my $flag=0;
if ($reshash{'version'}) {
my $tries=0;
&Apache::lonhomework::showhash(%Answer);
for (my $version=1;$version<=$reshash{'version'};$version++) {
my $time=$reshash{"$version:timestamp"};
foreach my $key (sort(split(/\:/,$reshash{$version.':keys'}))) {
if (($key=~/\.(\w+)\.(\w+)\.submission$/)) {
my $Id1 = $1; my $Id2 = $2;
#check if this is a repeat submission, if so skip it
if ($reshash{"$version:resource.$Id1.previous"}) { next; }
#if no solved this wasn't a real submission, ignore it
if (!defined($reshash{"$version:resource.$Id1.solved"})) {
&Apache::lonxml::debug("skipping ");
next;
}
my $Resp = $reshash{"$version:$key"};
my %submission=&Apache::lonnet::str2hash($Resp);
foreach (keys %submission) {
my $Ansr = $Answer{"$Id1.$Id2.foil.value.$_"};
if ($submission{$_}) {
if ($submission{$_} eq $Ansr) {
&Decide("true",$_,$time );
}
else {&Decide("false",$_,$time );}
}
}
}
}
}
}
}
#---- END Analyze Web Page ----------------------------------------------
#---- Problem Statistics Web Page ---------------------------------------
#------- Processing upperlist and lowerlist according to each problem
sub ProcessDiscriminant {
my ($List) = @_;
my @sortedList = sort (@$List);
my $Count = scalar @sortedList;
my $Problem;
my @Dis;
my $Slvd=0;
my $tmp;
my $Sum1=0;
my $Sum2=0;
my $nIndex=0;
my $nStudent=0;
my %Proc=undef;
while ($nIndex<$Count) {
($Problem,$tmp)=split(/\=/,$sortedList[$nIndex]);
@Dis=split(/\+/,$tmp);
my $Temp = $Problem;
do {
$nIndex++;
$nStudent++;
$Sum1 += $Dis[0];
$Sum2 += $Dis[1];
($Problem,$tmp)=split(/\=/,$sortedList[$nIndex]);
@Dis=split(/\+/,$tmp);
} while ( $Problem eq $Temp && $nIndex < $Count );
# $Proc{$Temp}=($Sum1/$nStudent).':'.$nStudent;
$Proc{$Temp}=($Sum1/$nStudent).':'.($Sum2/$nStudent);
# $r->print("$nIndex) $Temp --> ($nStudent) $Proc{$Temp} ");
$Sum1=0;
$Sum2=0;
$nStudent=0;
}
return %Proc;
}
#------- Creating Discimination factor
sub Discriminant {
my ($discriminantFactor)=@_;
my @discriminantKeys=keys(%$discriminantFactor);
my $Count = scalar @discriminantKeys;
my $UpCnt = int(0.27*$Count);
my $low=0;
my $up=$Count-$UpCnt;
my @UpList=();
my @LowList=();
$Count=0;
foreach my $key (sort(@discriminantKeys)) {
$Count++;
if($low < $UpCnt || $Count > $up) {
$low++;
my $str=$discriminantFactor->{$key};
foreach(split(/\:/,$str)){
if($_) {
if($low<$UpCnt) { push(@LowList,$_); }
else { push(@UpList,$_); }
}
}
}
}
my %DisUp = &ProcessDiscriminant(\@UpList);
my %DisLow = &ProcessDiscriminant(\@LowList);
return (\%DisUp, \%DisLow);
}
sub NumericSort {
$a <=> $b;
}
sub CreateProblemStatisticsTableHeading {
my ($displayFormat,$sequenceSource,$sequenceTitle,$headings)=@_;
if($displayFormat eq 'Display CSV Format') {
$r->print(' "'.$sequenceTitle.'","');
$r->print($sequenceSource.'"');
return;
}
$r->print(' '.$sequenceTitle.'');
my $Result = "\n".'
';
return $Ptr;
}
sub ProblemStatisticsLegend {
my $Ptr = '';
$Ptr = '
';
$Ptr .= '
';
$Ptr .= '#Stdnts:
';
$Ptr .= '
Total Number of Students opened the problem.';
$Ptr .= '
';
$Ptr .= 'Tries:
';
$Ptr .= '
Total Number of Tries for solving the problem.';
$Ptr .= '
';
$Ptr .= 'Mod:
';
$Ptr .= '
Maximunm Number of Tries for solving the problem.';
$Ptr .= '
';
$Ptr .= 'Mean:
';
$Ptr .= '
Average Number of the tries. [ Tries / #Stdnts ]';
$Ptr .= '
';
$Ptr .= '#YES:
';
$Ptr .= '
Number of students solved the problem correctly.';
$Ptr .= '
';
$Ptr .= '#yes:
';
$Ptr .= '
Number of students solved the problem by override.';
$Ptr .= '
';
$Ptr .= '%Wrng:
';
$Ptr .= '
Percentage of students tried to solve the problem ';
$Ptr .= 'but still incorrect. [ 100*((#Stdnts-(#YES+#yes))/#Stdnts) ]';
$Ptr .= '
';
# Kashy formula
# ' DoDiff : Degree of Difficulty of the problem. '.
# '[ Tries/(#YES+#yes+0.1) ] '.
#Gerd formula
$Ptr .= 'DoDiff:
';
$Ptr .= '
Degree of Difficulty of the problem. ';
$Ptr .= '[ 1 - ((#YES+#yes) / Tries) ]';
$Ptr .= '
';
$Ptr .= 'S.D.:
';
$Ptr .= '
Standard Deviation of the tries. ';
$Ptr .= '[ sqrt(sum((Xi - Mean)^2)) / (#Stdnts-1) ';
$Ptr .= 'where Xi denotes every student\'s tries ]';
$Ptr .= '
';
$Ptr .= 'Skew.:
';
$Ptr .= '
Skewness of the students tries.';
$Ptr .= '[(sqrt( sum((Xi - Mean)^3) / #Stdnts)) / (S.D.^3)]';
$Ptr .= '
';
$Ptr .= 'Dis.F.:
';
$Ptr .= '
Discrimination Factor: A Standard for evaluating the ';
$Ptr .= 'problem according to a Criterion ';
$Ptr .= '[Applied Criterion in %27 Upper Students - ';
$Ptr .= 'Applied the same Criterion in %27 Lower Students] ';
$Ptr .= '1st Criterion for Sorting the Students: ';
$Ptr .= 'Sum of Partial Credit Awarded / Total Number of Tries ';
$Ptr .= '2nd Criterion for Sorting the Students: ';
$Ptr .= 'Total number of Correct Answers / Total Number of Tries';
$Ptr .= '
';
$Ptr .= '
Disc.
';
$Ptr .= '
Number of Students had at least one discussion.';
$Ptr .= '
';
return $Ptr;
}
#---- END Problem Statistics Web Page ----------------------------------------
#---- Problem Statistics Graph Web Page --------------------------------------
# ------------------------------------------- Prepare data for Graphical chart
sub GetGraphData {
my $ylab = shift;
my $Col;
my $data='';
my $count = 0;
my $Max = 0;
my $cid=$ENV{'request.course.id'};
my $GraphDB = "/home/httpd/perl/tmp/$ENV{'user.name'}".
"_$ENV{'user.domain'}_$cid\_graph.db";
foreach (keys %GraphDat) {delete $GraphDat{$_};}
if (-e "$GraphDB") {
if (tie(%GraphDat,'GDBM_File',"$GraphDB",&GDBM_READER,0640)) {
if ( $ylab eq 'DoDiff Graph' ) {
$ylab = 'Degree-of-Difficulty';
$Col = 0;
}
else {
$ylab = 'Wrong-Percentage';
$Col = 1;
}
foreach (sort NumericSort keys %GraphDat) {
my @Temp=split(/\:/,$GraphDat{$_});
my $inf = $Temp[$Col];
if ( $Max < $inf ) {$Max = $inf;}
$data .= $inf.',';
$count++;
}
if ( $Max > 1 ) {
$Max += (10 - $Max % 10);
$Max = int($Max);
}
else { $Max = 1; }
untie(%GraphDat);
my $Course = $ENV{'course.'.$cid.'.description'};
$Course =~ s/\ /"_"/eg;
$GData=$Course.'&'.'Problems'.'&'.$ylab.'&'.$Max.'&'.$count.'&'.$data;
}
else {
$r->print("Unable to tie hash to db file");
}
}
}
#---- Problem Analysis Web Page ----------------------------------------------
sub IntervalOptions {
my ($cache)=@_;
my $interval = 1;
for(my $n=1; $n<=7; $n++) {
if($cache->{'Interval'} == $n) {
$interval = $n;
}
}
my $Ptr = ' Select number of intervals'."\n".
''."\n";
return $Ptr;
}
sub OptionResponseTable {
my ($cache)=@_;
my $Str = '';
$Str .= ' Option Response Problems in this course:'."\n";
$Str .= '
'."\n";
$Str .= "
\#
Problem Title
";
$Str .= '
Resource
Analysis
'."\n";
my $number=1;
foreach (split(':::',$cache->{'OptionResponses'})) {
my ($uri,$title,$part,$problem)=split('::',$_);
my $Temp = ''.$title.'';
$Str .= '
';
$Str .= '
'.$number.'
';
$Str .= '
'.$Temp.'
';
$Str .= '
'.$uri.'
';
$Str .= '
'."\n";
$number++;
}
$Str .= '
'."\n";
return $Str;
}
#---- END Problem Analysis Web Page ------------------------------------------
#---- Student Assessment Web Page --------------------------------------------
sub MapOptions {
my ($cache, $page)=@_;
my $Ptr = '