".
- "Current map: \"$CurMap\" ".
- "Current Section: \"$CurSec\" ".
- "Number of valid students: $Count".
- " The Upper 27% has $UpCnt records.".
- " The Lower 27% has $UpCnt records ".
- "The Criterion of sorting the students: ".
- "( Sum of Partial Credits Awarded / ".
- "Total Number of Tries )".
- "
");
- $r->rflush();
- my $low=0;
- my $up=$Count-$UpCnt;
- my @UpList=();
- my @LowList=();
- $Count=0;
- foreach my $key (sort(keys(%DiscFac))){
- $Count++;
-# $r->print("$Count) $key ");
-
- if ($low < $UpCnt || $Count > $up) {
- $low++;
- my $str=$DiscFac{$key};
-# $r->print("$Count) $str ");
- foreach(split(/\:/,$str)){
- if ($_) {
- if ($low<$UpCnt){push(@LowList,$_);}
- else {push(@UpList,$_);}
- }
- }
- }
- }
- my %Up=&ProcessDisc(@UpList);
- my %Low=&ProcessDisc(@LowList);
- my @list = ();
- my $Useful;
- my $UnUseful;
- $p_count = 0;
-
- foreach my $key( keys %CachData) {
- my @Temp=split(/\:/,$CachData{$key});
- ($UnUseful,$Useful)=split(/\>/,$Temp[0]);
- $list[$p_count]=$Useful.'&'.$CachData{$key};
- $p_count++;
- }
-
- @list = sort MySort (@list);
-
- my $Result = "\n".'
';
- $Result .= "\n".'
P#
';
- $Result .= "\n".'
'.$Header{0}.'
';
- $Result .= "\n".'
'.'Discrimination Factor'.'
';
- $Result .= "\n".'
'.'%Upper Award'.'
';
- $Result .= "\n".'
'.'%Lower Award'.'
';
- $Result .= "\n".'
'.'Upper Records'.'
';
- $Result .= "\n".'
'.'Lower Records'.'
';
- $Result .= "\n".'
'.'%Degree of Difficulty'.'
';
- $Result .= "\n".'
';
- $r->print( $Result );
-
- for ( my $nIdx = 0; $nIdx < $p_count; $nIdx++ ) {
- my( $Pre, $Post ) = split(/\&/,$list[$nIdx]);
- my ($Temp,$MxTries,$StdNo,$TotalTries,$YES,$Override,
- $Wrng,$Avg,$SD,$Sk,$DoD,$res,$Prob)=split(/\:/,$Post);
- my ($UpDis,$UpNo)=split(/\:/,$Up{$Prob});
- my ($LwDis,$LwNo)=split(/\:/,$Low{$Prob});
- $UpNo = ($UpNo) ? $UpNo : 0;
- $LwNo = ($LwNo) ? $LwNo : 0;
- my $U_Dis = sprintf("%.4f", $UpDis)*100;
- my $L_Dis = sprintf("%.4f", $LwDis)*100;
- my $DisFac = $UpDis - $LwDis;
- my $_Dis = sprintf("%.4f", $DisFac)*100;
- $r->print( "\n".'
'.
- "\n".'
'.($nIdx+1).'
'.
- "\n".'
'.$Temp.'
'.
- "\n".'
'.$_Dis.'
'.
- "\n".'
'.$U_Dis.'
'.
- "\n".'
'.$L_Dis.'
'.
- "\n".'
'.$UpNo.'
'.
- "\n".'
'.$LwNo.'
'.
- "\n".'
'.($DoD).'
'.
- "\n".'
' );
- }
- $r->print("\n".'
');
- $r->rflush();
+sub current_student {
+ return $curr_student;
}
-sub CreateDiscFac {
- my $CacheDB = "/home/httpd/perl/tmp/$ENV{'user.name'}".
- "_$ENV{'user.domain'}_$cid\_statistics.db";
- my $CachDisFac = "/home/httpd/perl/tmp/$ENV{'user.name'}".
- "_$ENV{'user.domain'}_$cid\_DiscFactor.db";
-
- my $ptr='';
-# $ptr .= ' Discrimination Criterion: '."\n".
-# ' '."\n";
- $ptr .= ' ';
- $r->print($ptr);
-
- if ((-e "$CacheDB")&&
- ($ENV{'form.sort'} ne 'Recalculate Discrimintion Factor')) {
- if (tie(%CachData,'GDBM_File',"$CacheDB",&GDBM_READER,0640)) {
- tie(%DiscFac,'GDBM_File',$CachDisFac,&GDBM_READER,0640);
- #&DiscriminationTable();
- &Classify();
- }
- else {$r->print("Unable to tie hash to db file");}
- }
- else {
- if (tie(%CachData,'GDBM_File',$CacheDB,&GDBM_WRCREAT,0640)) {
- tie(%DiscFac,'GDBM_File',$CachDisFac,&GDBM_WRCREAT,0640);
- foreach (keys %CachData) {delete $CachData{$_};}
- foreach (keys %DiscFac) {delete $DiscFac{$_};}
- $DiscFlag=1;
- &Build_Statistics();
- $DiscFlag=0;
- &DiscriminationTable();
- }
- else {$r->print("Unable to tie hash to db file");}
+
+sub previous_student {
+ return $prev_student;
+}
+
+
+
+sub next_student {
+ return $next_student;
+}
+
+
+
+sub StudentDataSelect {
+ my ($elementname,$status,$numvisible)=@_;
+ if ($numvisible < 1) {
+ return;
}
- untie(%CachData);
- untie(%DiscFac);
-}
-
-
-# ------ Create different Student Report
-sub StudentReport {
- my ($sname,$sdom)=@_;
- if ( $sname eq 'All Students' ) {
- $r->print( '
WARNING:
- Please select a student
' );
- return;
- }
- my $shome=&Apache::lonnet::homeserver( $sname,$sdom );
- my $reply=&Apache::lonnet::reply('dump:'.$sdom.':'.$sname.':'.$cid,$shome );
- my %result = ();
- my $ResId;
- my $Code;
- my $Tries;
- my $TotalTries = 0;
- my $ParCr = 0;
- my $Wrongs;
- my %TempHash;
- my $Version;
- my $LatestVersion;
- my $PtrTry='';
- my $PtrCod='';
- my $SetNo=0;
- my $Str = "\n".'
'.
- ' #Stdnts: Total Number of Students opened the problem. '.
- ' Tries : Total Number of Tries for solving the problem. '.
- ' Mod : Maximunm Number of Tries for solving the problem. '.
- ' Mean : Average Number of the tries. [ Tries / #Stdns ] '.
- ' #YES : Number of students solved the problem correctly. '.
- ' #yes : Number of students solved the problem by override. '.
- ' %Wrng : Percentage of students tried to solve the problem but still incorrect. [ 100*((#Stdnts-(#YES+#yes))/#Stdnts) ] '.
- ' S.D. : Standard Deviation of the tries.[ sqrt(sum((Xi - Mean)^2)) / (#Stdnts-1) where Xi is every student\'s tries ] '.
- ' Skew. : Skewness of the students tries. [ (sqrt( sum((Xi - Mean)^3) / #Stdnts)) / (S.D.^3) ] '.
-# ' DoDiff : Degree of Difficulty of the problem. [ Tries/(#YES+#yes+0.1) ] '.
- ' DoDiff : Degree of Difficulty of the problem. [ 1 - ((#YES+#yes) / Tries) ] '.
-# ' Dis.F. : Discrimination Factor. [ Sum of Partial Credits Awarded / Total Number of Tries in %27 upper and lower students]'.
- '
';
+ }
+ $Str .= &Apache::loncommon::end_data_table_row();
}
-#--------------------- close Progress Line
- $r->print('');
- $r->rflush();
-# -------------------- sorting the Data
- @list = sort(@list);
- $OpSel2='';
- $OpSel1='selected';
-
- $p_count = 0;
- my $nIdx = 0;
- my $dummy;
- my $p_val;
- my $ResId;
- my $NoElements = $#list + 1;
-#-------------------------------- loop for data representation
- while ( $nIdx < $NoElements ) {
- my %storestats=();
- my ($Prob,$Tries,$Wrongs,$Code)=split(/\:/,$list[$nIdx]);
- my $Temp = $Prob;
- my $MxTries = 0;
- my $TotalTries = 0;
- my $YES = 0;
- my $Incorrect = 0;
- my $Override = 0;
- my $StdNo = 0;
- my @StdLst;
- do {
- $nIdx++;
- $StdNo++;
- $StdLst[ $StdNo ] = $Tries;
- $TotalTries += $Tries;
- if ( $MxTries < $Tries ) { $MxTries = $Tries; }
- if ( $Code eq 'C' ){ $YES++; }
- elsif( $Code eq 'I' ) { $Incorrect++; }
- elsif( $Code eq 'O' ) { $Override++; }
- elsif( $Code eq 'U' ) { $StdNo--; }
- ($Prob,$Tries,$Wrongs,$Code)=split(/\:/,$list[$nIdx]);
- } while ( $Prob eq $Temp && $nIdx < $NoElements );
+ $Str .= &Apache::loncommon::end_data_table();
+ #
+ $r->print($Str);
+ $r->rflush();
+ #
+ return;
+}
- $p_count++;
- ($Temp,$ResId)=split(/\*/,$Temp);
- $Temp = ''.$Temp.'';
+sub CreateMainMenu {
+ #
+ # Define menu data
+ my @reports = (
+ {categorytitle => 'Statistics and Analyses',
+ items => [
+ {url => '/adm/statistics?reportSelected=problem_statistics',
+ permission => 'F',
+ icon => 'document-open.png',
+ linktext => ('Overall Problem Statistics'),
+ linktitle => ('Student performance statistics on all problems.')},
+
+ {url => '/adm/statistics?reportSelected=problem_analysis',
+ permission => 'F',
+ icon => 'edit-find.png',
+ linktext => ('Detailed Problem Analysis'),
+ linktitle => ('Detailed statistics and graphs of student performance on problems.')},
+ ]},
+ {categorytitle => 'Plots',
+ items => [
+ {url => '/adm/statistics?reportSelected=submissiontime_analysis',
+ permission => 'F',
+ icon => 'subtimpl.png',
+ linktext => ('Submission Time Plots'),
+ linktitle => ('Display and analysis of submission times on assessments.')},
+
+ {url => '/adm/statistics?reportSelected=correct_problems_plot',
+ permission => 'F',
+ icon => 'coprplot.png',
+ linktext => ('Correct Problems Plot'),
+ linktitle => ('Display a histogram of student performance in the course.')},
+ ]});
+ if (&Apache::loncommon::needs_gci_custom()) {
+ push(@reports,
+ {categorytitle => 'Reports',
+ items => [
+ {url => '/adm/statistics?reportSelected=student_submission_reports',
+ permission => 'F',
+ icon => 'edit-copy.png',
+ linktext => ('Student Submission Reports'),
+ linktitle => ('Prepare reports of student submissions.')},
+ ]});
+ } else {
+ push(@reports,
+ {categorytitle => 'Reports',
+ items => [
+ {url => '/adm/statistics?reportSelected=student_submission_reports',
+ permission => 'F',
+ icon => 'edit-copy.png',
+ linktext => ('Student Submission Reports'),
+ linktitle => ('Prepare reports of student submissions.')},
+
+ {url => '/adm/statistics?reportSelected=survey_reports',
+ permission => 'F',
+ icon => 'docs.png',
+ linktext => ('Survey Reports'),
+ linktitle => ('Prepare reports on survey results.')},
+ ]});
+ }
- my $res = &Apache::lonnet::declutter($hash{'src_'.$ResId});
- my $urlres=$res;
-
- $ResId=~/(\d+)\.(\d+)/;
- my $Map = &Apache::lonnet::declutter( $hash{'map_id_'.$1} );
- $urlres=$Map;
+return &Apache::lonhtmlcommon::generate_menu(@reports);
+}
- $res = ''.$res.'';
-
- #$Map = ''.$res.'';
-
-#------------------------ Compute the Average of Tries about one problem
- my $Average = ($StdNo) ? $TotalTries/$StdNo : 0;
- $storestats{$ENV{'request.course.id'}.'___'.$urlres.'___timestamp'}=time;
- $storestats{$ENV{'request.course.id'}.'___'.$urlres.'___stdno'}=$StdNo;
- $storestats{$ENV{'request.course.id'}.'___'.$urlres.'___avetries'}=$Average;
-
-#-------------------------------- Compute percentage of Wrong tries
- my $Wrong = ( $StdNo ) ? 100 * ( $Incorrect / $StdNo ) : 0;
-
-#-------------------------------- Compute Standard Deviation
- my $StdDev = 0;
- if ( $StdNo > 1 ) {
- for ( my $n = 0; $n < $StdNo; $n++ ) {
- my $Dif = $StdLst[ $n ]-$Average;
- $StdDev += $Dif*$Dif;
- }
- $StdDev /= ( $StdNo - 1 );
- $StdDev = sqrt( $StdDev );
- }
-#-------------------------------- Compute Degree of Difficulty
- my $DoDiff = 0;
- if( $TotalTries > 0 ) {
- $DoDiff = 1 - ( ( $YES + $Override ) / $TotalTries );
-# $DoDiff = ($TotalTries)/($YES + $Override+ 0.1);
- }
-
- $storestats{$ENV{'request.course.id'}.'___'.$urlres.'___difficulty'}=$DoDiff;
+sub handler {
+ my $r=shift;
+ my $c = $r->connection();
+ #
+ # Check for overloading
+ my $loaderror=&Apache::lonnet::overloaderror($r);
+ if ($loaderror) { return $loaderror; }
+ $loaderror=
+ &Apache::lonnet::overloaderror($r,
+ $env{'course.'.$env{'request.course.id'}.'.home'});
+ if ($loaderror) { return $loaderror; }
+ #
+ # Check for access
+ if (! &Apache::lonnet::allowed('vgr',$env{'request.course.id'})) {
+ $env{'user.error.msg'}=
+ $r->uri.":vgr:0:0:Cannot view grades for complete course";
+ if (! &Apache::lonnet::allowed('vgr',
+ $env{'request.course.id'}.'/'.$env{'request.course.sec'})) {
+ $env{'user.error.msg'}=
+ $r->uri.":vgr:0:0:Cannot view grades with given role";
+ return HTTP_NOT_ACCEPTABLE;
+ }
+ }
+ #
+ # Send the header
+ &Apache::loncommon::no_cache($r);
+ &Apache::loncommon::content_type($r,'text/html');
+ $r->send_http_header;
+ if ($r->header_only) { return OK; }
+ my $gcicustom = &Apache::loncommon::needs_gci_custom();
+ if ($gcicustom) {
+ my $now = time;
+ my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
+ my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+ my $courseopt=&Apache::lonnet::get_courseresdata($cnum,$cdom);
+ my $duedate = $courseopt->{$env{'request.course.id'}.'.0.duedate'};
+ if ((!$duedate) || ($duedate > $now)) {
+ my $brcrum = [{href=> '/adm/statistics',
+ text=> 'Statistics',}];
+ my $args = {bread_crumbs => $brcrum,
+ bread_crumbs_component => 'Statistics Main Page'};
+ $r->print(&Apache::loncommon::start_page('Course Statistics',undef,$args));
+ $r->print('
'.&mt('Display of statistics').'
');
+ if ($duedate > $now) {
+ $r->print('
'.&mt('Aggregate test performance data will be available after the Concept Test end date: [_1].',
+ &Apache::lonlocal::locallocaltime($duedate)).'
');
+ } else {
+ $r->print('
'.
+ &mt('Aggregate test performance data unavailable without definition of an end date for the Concept Test.').'
');
+ }
+ $r->print(&Apache::loncommon::end_page());
+ return OK;
+ }
+ }
-#-------------------------------- Compute the Skewness
- my $Skewness = 0;
- my $Sum = 0;
- if ( $StdNo > 0 && $StdDev > 0 ) {
- for ( my $n = 0; $n < $StdNo; $n++ ) {
- my $Dif = $StdLst[ $n ]-$Average;
- $Skewness += $Dif*$Dif*$Dif;
- }
- $Skewness /= $StdNo;
- $Skewness /= $StdDev*$StdDev*$StdDev;
+ #
+ # Extract form elements from query string
+ &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},
+ ['sort','reportSelected',
+ 'SelectedStudent']);
+ #
+ # Give the LON-CAPA page header
+ my $style = <
+ ul.sub_studentans { list-style-type: none }
+ ul.sub_correctans { list-style-type: none }
+ tr.even { background-color: \#CCCCCC }
+ td.essay { border: 1px solid gray; }
+
+ENDSTYLE
+
+ $r->print(&Apache::loncommon::start_page('Course Statistics and Charts',
+ $style));
+ $r->rflush();
+ #
+ # Either print out a menu for them or send them to a report
+ &Apache::lonhtmlcommon::clear_breadcrumbs();
+ &Apache::lonhtmlcommon::add_breadcrumb({href=>'/adm/statistics',
+ title=>'Statistics',
+ text =>'Statistics',
+ faq=>139,
+ bug=>'Statistics and Charts'});
+ if (! exists($env{'form.reportSelected'}) ||
+ $env{'form.reportSelected'} eq '') {
+ $r->print(&Apache::lonhtmlcommon::breadcrumbs('Statistics Main Page').
+ &CreateMainMenu());
+ } else {
+ #
+ if (! &Apache::lonmysql::verify_sql_connection()) {
+ my $serveradmin = $r->dir_config('lonAdmEMail');
+ $r->print('
'.
+ &mt('Unable to connect to database!').
+ '
');
+ $r->print('
'
+ .&mt('Please notify the server administrator [_1]',
+ ,''.$serveradmin.'')
+ .'
');
+ $r->print('
'.
+ &mt('Course Statistics and Charts cannot be '.
+ 'retrieved until the database is restarted. '.
+ 'Your data is intact but cannot be displayed '.
+ 'at this time.').'
' );
- $GraphDat{$nIdx}=$DoD.':'.$Wrng;
- }
-}
+__END__
-# ------------------------------------------- Prepare data for Graphical chart
+=pod
-sub GetGraphData {
- my $Tag = 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 ( $Tag eq 'DoDiff Graph' ) {
- $Tag = 'Degree-of-Difficulty';
- $Col = 0;
- }
- else {
- $Tag = '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++;
- }
- untie(%GraphDat);
- my $Course = $ENV{'course.'.$cid.'.description'};
- $Course =~ s/\ /"_"/eg;
- $GData=$Course.'&'.$Tag.'&'.$Max.'&'.$count.'&'.$data;
+=head1 NAME
- }
- else {
- $r->print("Unable to tie hash to db file");
- }
- }
-}
+lonstatistics
+=head1 SYNOPSIS
-sub initial {
-# --------------------------------- Initialize the global varaibles
- undef @students;
- undef @cols;
- undef %maps;
- undef %section;
- undef %StuBox;
- undef @list;
- undef %CachData;
- undef %GraphDat;
- undef %DiscFac;
- undef $CurMap;
- undef $CurSec;
- undef $CurStu;
- undef $p_count;
- undef $Pos;
- undef $GData;
-}
+Main handler for statistics and chart.
+This is part of the LearningOnline Network with CAPA project
+described at http://www.lon-capa.org.
-sub ClassList {
- &GetStatus();
+=head1 PACKAGE VARIABLES
- $cid=$ENV{'request.course.id'};
- my $chome=$ENV{'course.'.$cid.'.home'};
- my ($cdom,$cnum)=split(/\_/,$cid);
-# ----------------------- Get first and last resource, see if there is anything
- $firstres=$hash{'map_start_/res/'.$ENV{'request.course.uri'}};
- $lastres=$hash{'map_finish_/res/'.$ENV{'request.course.uri'}};
- if (($firstres) && ($lastres)) {
-# ----------------------------------------------------------------- Render page
- my $classlst=&Apache::lonnet::reply
- ('dump:'.$cdom.':'.$cnum.':classlist',$chome);
- my $StudNo = 0;
- unless ($classlst=~/^error\:/) {
- foreach (sort split(/\&/,$classlst)) {
- my ($name,$value)=split(/\=/,$_);
- my ($end,$start)=split(/\:/,&Apache::lonnet::unescape($value));
- $name=&Apache::lonnet::unescape($name);
- my ($sname,$sdom)=split(/\:/,$name);
- my $ssec=&Apache::lonnet::usection($sdom,$sname,$cid);
- if ($ssec==-1) {next;}
- $ssec=($ssec) ? $ssec : '(none)';
- $ssec=(int($ssec)) ? int($ssec) : $ssec;
- #$r->print($sname.'...'.$ssec.' ');
- $section{$ssec}=$ssec;
- if ($CurSec eq 'All Sections' || $ssec eq $CurSec) {
- $students[$StudNo]=$name;
- $StuBox{$sname}=$ssec;
- }
- $StudNo++;
- }
- }
- else {
- $r->print('
Could not access course data
');
- }
- $r->print("Total number of students : ".($#students+1));
- $r->rflush();
-# --------------- Find all assessments and put them into some linear-like order
- &tracetable($firstres,'&'.$lastres.'&');
- }
+=over
-# ------------------------------------------------------------- End render page
- else {
- $r->print('
Undefined course sequence
');
- }
- &MapSecOptions();
-}
+=item @FullClasslist The full classlist
+=item @Students The students we are concerned with for this invocation
-sub Menu {
- my $InpStr = $ENV{'form.sort'};
- if ( $InpStr eq 'DoDiff Graph' || $InpStr eq '%Wrong Graph' ) {
- &GetGraphData($InpStr);
- $r->print('');
- }
- else {
- $r->print('LON-CAPA Statistics');
-
-
- $r->print(''.
- ''.
- '');
-# ---------------------------------------------------------------- Course title
- $r->print('