--- loncom/interface/statistics/lonstudentassessment.pm 2003/01/13 01:34:11 1.27 +++ loncom/interface/statistics/lonstudentassessment.pm 2003/03/07 15:18:39 1.38 @@ -1,7 +1,6 @@ # The LearningOnline Network with CAPA -# (Publication Handler # -# $Id: lonstudentassessment.pm,v 1.27 2003/01/13 01:34:11 minaeibi Exp $ +# $Id: lonstudentassessment.pm,v 1.38 2003/03/07 15:18:39 matthew Exp $ # # Copyright Michigan State University Board of Trustees # @@ -25,387 +24,1068 @@ # 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,12/24 Behrouz Minaei -# YEAR=2003 -# 1/11 Behrouz Minaei -### +# +####################################################### +####################################################### + +=pod + +=head1 NAME + +lonstudentassessment + +=head1 SYNOPSIS + +Presents assessment data about a student or a group of students. + +=head1 Subroutines + +=over 4 + +=cut + +####################################################### +####################################################### package Apache::lonstudentassessment; use strict; +use Apache::lonstatistics; use Apache::lonhtmlcommon; use Apache::loncoursedata; -use GDBM_File; +use Apache::lonnet; # for logging porpoises +use Spreadsheet::WriteExcel; + +####################################################### +####################################################### +=pod + +=item Package Variables + +=over 4 + +=item $Statistics Hash ref to store student data. Indexed by symb, + contains hashes with keys 'score' and 'max'. + +=cut + +####################################################### +####################################################### +my $Statistics; +####################################################### +####################################################### + +=pod + +=item $show_links 'yes' or 'no' for linking to student performance data + +=item $output_mode 'html', 'excel', or 'csv' for output mode + +=item $show 'all', 'totals', or 'scores' determines how much data is output + +=cut + +####################################################### +####################################################### +my $show_links; +my $output_mode; +my $show; + +####################################################### +####################################################### +# End of package variable declarations + +=pod + +=back + +=cut + +####################################################### +####################################################### + +=pod + +=item &BuildStudentAssessmentPage() + +Inputs: + +=over 4 + +=item $r Apache Request + +=item $c Apache Connection + +=back + +=cut + +####################################################### +####################################################### sub BuildStudentAssessmentPage { - my ($cacheDB,$students,$courseID,$formName,$headings,$spacing, - $studentInformation,$r,$c)=@_; - my %cache; - unless(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER(),0640)) { - $r->print('
Unable to tie database.'); + my ($r,$c)=@_; + undef($Statistics); + # + # Print out the HTML headers for the interface + # This also parses the output mode selector + # This step must always be done. + $r->print(&CreateInterface()); + $r->print(''); + $r->rflush(); + if (! exists($ENV{'form.notfirstrun'})) { + $r->print(<'."\n"); - foreach (@$students) { - if($c->aborted()) { return $Str; } - next if ($_ ne $selectedName && - $selectedName ne 'All Students'); - $selected = 1; - - my @who = ($_); - next if(&Apache::loncoursedata::DownloadStudentCourseData(\@who, 'true', - $cacheDB, 'true', - 'false', $courseID, - $r, $c) ne 'OK'); - next if($c->aborted()); - - if(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER(),0640)) { - my @before=(); - my @after=(); - my @updateColumn=(); - my $foundUpdate = 0; - foreach(@$infoKeys) { - if(/updateTime/) { - $foundUpdate=1; - push(@updateColumn, $_); - next; - } - if($foundUpdate) { - push(@after, $_); - } else { - push(@before, $_); - } - } - $Count++; - my $out = ''; - $out .= sprintf("%3d) ", $Count); - if($Count % 2) { - $out .= ''."\n"); - if($selected == 0) { - $Str .= ''; - } else { - $out .= ' '; - } - my $displayString = $out.'DISPLAYDATA'.$spacing; - $r->print(&Apache::lonhtmlcommon::FormatStudentInformation( - \%cache, $_, - \@before, - $displayString, - 'preformatted')); - - if($foundUpdate) { - $displayString = ''; - $displayString .= ''; - $displayString .= 'DISPLAYDATA'.$spacing; - $r->print(&Apache::lonhtmlcommon::FormatStudentInformation( - \%cache, $_, - \@updateColumn, - $displayString, - 'preformatted')); - } +sub get_student_fields_to_show { + my @to_show = @Apache::lonstatistics::SelectedStudentData; + foreach (@to_show) { + if ($_ eq 'all') { + @to_show = @Apache::lonstatistics::StudentDataOrder; + last; + } + } + return @to_show; +} - $displayString = 'DISPLAYDATA'.$spacing; - $r->print(&Apache::lonhtmlcommon::FormatStudentInformation( - \%cache, $_, - \@after, - $displayString, - 'preformatted')); - $r->print(&StudentReport(\%cache, $_, $spacing, $sequenceKeys)); - $r->print("\n"); - $r->rflush(); - untie(%cache); - } - } - $r->print('
'."\n"; - $Str .= '{'sectionList'}); - my @selectedSections = split(':',$cache->{'sectionsSelected'}); - $Str .= &Apache::lonhtmlcommon::MultipleSectionSelect(\@sections, - \@selectedSections, - 'Statistics'); + $Str .= &Apache::lonstatistics::SectionSelect('Section','multiple',5); $Str .= ' | '; - $Str .= &CreateColumnSelectionBox($doNotShow); + my $only_seq_with_assessments = sub { + my $s=shift; + if ($s->{'num_assess'} < 1) { + return 0; + } else { + return 1; + } + }; + $Str .= &Apache::lonstatistics::StudentDataSelect('StudentData','multiple', + 5,undef); $Str .= ' | '."\n";
- $Str .= ' 'HTML, with links',
+ value => 'html, with links',
+ description => 'Output HTML with each symbol linked to the problem '.
+ 'which generated it.',
+ mode => 'html',
+ show => 'all',
+ show_links => 'yes',
+ },
+ { name => 'HTML, without links',
+ value => 'html, without links',
+ description => 'Output HTML. By not including links, the size of the'.
+ ' web page is greatly reduced. If your browser crashes on the '.
+ 'full display, try this.',
+ mode => 'html',
+ show => 'all',
+ show_links => 'no',
+ },
+ { name => 'HTML, scores only',
+ value => 'html, scores only',
+ description => 'Output HTML, only showing the total number of correct'.
+ ' problems (or problem parts) and not the maximum possible for '.
+ 'each student',
+ mode => 'html',
+ show => 'scores',
+ show_links => 'no',
+ },
+ { name => 'HTML, totals',
+ value => 'html, totals',
+ description => 'Output HTML, but only the summary statistics for each'.
+ ' sequence selected.',
+ mode => 'html',
+ show => 'totals',
+ show_links => 'no',
+ },
+ { name => 'Excel, scores only',
+ value => 'excel, scores only',
+ description => 'Output an Excel file (compatable with Excel 95), '.
+ 'with a single column for each sequence showing the students '.
+ 'score.',
+ mode => 'excel',
+ show => 'scores',
+ show_links => 'no',
+ },
+ { name => 'Excel, totals',
+ value => 'excel, totals',
+ description => 'Output an Excel file (compatable with Excel 95), '.
+ 'with two columns for each sequence, the students score on the '.
+ 'sequence and the students maximum possible on the sequence',
+ mode => 'excel',
+ show => 'totals',
+ show_links => 'no',
+ },
+ { name => 'multi-sheet Excel',
+ value => 'multi-sheet excel',
+ description => 'Output an Excel file (compatable with Excel 95), '.
+ 'with a seperate worksheet for each sequence you have selected '.
+ 'the data for each problem part '.
+ '(number of tries, status, points awarded) will be listed.',
+ mode => 'multi-sheet excel',
+ show => 'totals',
+ show_links => 'no',
+ },
+ { name => 'multi-sheet Excel, by section',
+ value => 'multi-sheet excel, by section',
+ description => 'Output an Excel file (compatable with Excel 95), '.
+ 'with a seperate worksheet for each sequence you have selected '.
+ 'the data for each problem part '.
+ '(number of tries, status, points awarded) will be listed. '.
+ 'There will be one Excel workbook for each section selected.',
+ mode => 'multi-sheet excel',
+ show => 'by section',
+ show_links => 'no',
+ },
+ { name => 'CSV, everything',
+ value => 'csv, everything',
+ description => '',
+ mode => 'csv',
+ show => 'all',
+ show_links => 'no',
+ },
+ { name => 'CSV, scores only',
+ value => 'csv, scores only',
+ description => '',
+ mode => 'csv',
+ show => 'scores',
+ show_links => 'no',
+ },
+ { name => 'CSV, totals',
+ value => 'csv, totals',
+ description => '',
+ mode => 'csv',
+ show => 'totals',
+ show_links => 'no',
+ },
+ );
+
+sub OutputDescriptions {
+ my $Str = '';
+ $Str .= "Output Modes\n"; + $Str .= "
|
\n"; + # First, the @StudentData fields need to be listed + my @to_show = &get_student_fields_to_show(); + foreach my $field (@to_show) { + my $title=$Apache::lonstatistics::StudentData{$field}->{'title'}; + my $base =$Apache::lonstatistics::StudentData{$field}->{'base_width'}; + my $width=$Apache::lonstatistics::StudentData{$field}->{'width'}; + $Str .= $title.' 'x($width-$base).$padding; + } + # Now the selected sequences need to be listed + foreach my $sequence (&get_sequences_to_show) { + my $title = $sequence->{'title'}; + my $base = $sequence->{'base_width'}; + my $width = $sequence->{'width'}; + $Str .= $title.' 'x($width-$base).$padding; + } + $Str .= "total (of shown problems)\n"; + $Str .= "
"; + $r->print($Str); + $r->rflush(); + return; +} -# my $Str = '     '; +sub html_outputstudent { + my ($r,$student) = @_; my $Str = ''; - $Str .= '
DISPLAYDATAFORMATTING'; - $displayString .= $spacing.' | '."\n";
- $Str .= &Apache::lonhtmlcommon::CreateHeadings($cache,
- $infoKeys,
- $infoHeadings,
- $displayString,
- 'preformatted');
-
- $displayString = 'DISPLAYDATAFORMATTING'.$spacing; - $displayString .= ' | '."\n";
- $Str .= &Apache::lonhtmlcommon::CreateHeadings($cache,
- $sequenceKeys,
- $sequenceHeadings,
- $displayString,
- 'preformatted');
+ #
+ if($count++ % 5 == 0 && $count > 0) {
+ $r->print("Total Solved/Total Problems | ';
- $Str .= '
". " 1 correct by student in 1 try\n". @@ -516,210 +1177,18 @@ sub CreateLegend { return $Str; } -=pod - -=item &CreateColumnSelectionBox() +####################################################### +####################################################### -If there are columns not being displayed then this selection box is created -with a list of those columns. When selections are made and the page -refreshed, the columns will be removed from this box and the column is -put back in the chart. If there is no columns to select, no row is added -to the interface table. - -=over 4 -Input: $CacheData, $headings - - -$CacheData: A pointer to a hash tied to the cached data - -$headings: An array of the names of the columns for the student information. -They are used for displaying which columns are missing. - -Output: $notThere - -$notThere: The string contains one row of a table. The first column has the -name of the selection box. The second contains the selection box -which has a size of four. - -=back - -=cut - -sub CreateColumnSelectionBox { - my ($doNotShow)=@_; - - my $notThere = ''; - $notThere .= ''; - - return $notThere; -} - -=pod - -=item &CreateColumnSelectors() - -This function generates the checkboxes above the column headings. The -column will be removed if the checkbox is unchecked. - -=over 4 - -Input: $CacheData, $headings - -$CacheData: A pointer to a hash tied to the cached data - -$headings: An array of the names of the columns for the student -information. They are used to know what are the student information columns - -Output: $present - -$present: The string contains the first row of a table. Each column contains -a checkbox which is left justified. Currently left justification is used -for consistency of location over the column in which it presides. - -=back - -=cut - -sub CreateColumnSelectors { - my ($infoHeadings, $sequenceHeadings, $sequenceKeys)=@_; - - my $present = ''; - for(my $index=0; $index<(scalar @$infoHeadings); $index++) { - $present .= ''; - $present .= '[$index].'" />'; - $present .= ' '."\n"; - } - - for(my $index=0; $index<(scalar @$sequenceHeadings); $index++) { - $present .= ''; - $present .= '[$index].'" />'; - $present .= ' '."\n"; - } - - return $present; -} - -#---- END Student Assessment Web Page ---------------------------------------- - -#---- Student Assessment Worker Functions ------------------------------------ - -sub FindSelectedStudent { - my($cache, $selectedName, $students)=@_; - - if($selectedName eq 'All Students' || - $selectedName eq 'No Student Selected') { - return $selectedName; - } - - for(my $index=0; $index<(scalar @$students); $index++) { - my $fullname = $cache->{$students->[$index].':fullname'}; - if($fullname eq $selectedName) { - if($cache->{'StudentAssessmentMove'} eq 'next') { - if($index == ((scalar @$students) - 1)) { - $selectedName = $students->[0]; - return $selectedName; - } else { - $selectedName = $students->[$index+1]; - return $selectedName; - } - } elsif($cache->{'StudentAssessmentMove'} eq 'previous') { - if($index == 0) { - $selectedName = $students->[-1]; - return $selectedName; - } else { - $selectedName = $students->[$index-1]; - return $selectedName; - } - } else { - $selectedName = $students->[$index]; - return $selectedName; - } - last; - } - } - - return 'No Student Selected'; -} - -=pod - -=item &ShouldShowColumn() - -Determine if a specified column should be shown on the chart. - -=over 4 - -Input: $cache, $test - -$cache: A pointer to the hash tied to the cached data - -$test: The form name of the column (heading.$headingIndex) or -(sequence.$sequenceIndex) - -Output: 0 (false), 1 (true) +=pod =back =cut -sub ShouldShowColumns { - my ($cache,$headings,$cacheKey)=@_; - - my @infoKeys=(); - my @infoHeadings=(); - - my @sequenceKeys=(); - my @sequenceHeadings=(); - - my %doNotShow; - - my $index; - my $count = 0; - my $check = ''; - for($index=0; $index < scalar @$headings; $index++) { - $check = 'HeadingColumn'.$headings->[$index]; - if($cache->{'HeadingsFound'} =~ /$check/) { - push(@infoHeadings, $headings->[$index]); - push(@infoKeys, $cacheKey->[$index]); - } else { - $doNotShow{$count.':name'} = $headings->[$index]; - $doNotShow{$count.':id'} = 'HeadingColumn'.$headings->[$index]; - $count++; - } - } - - foreach my $sequence (split(/\:/,$cache->{'orderedSequences'})) { - $check = 'SequenceColumn'.$sequence; - if($cache->{'SequencesFound'} eq 'All Sequences' || - $cache->{'SequencesFound'} =~ /$check/) { - push(@sequenceHeadings, $cache->{$sequence.':title'}); - push(@sequenceKeys, $sequence); - } else { - $doNotShow{$count.':name'} = $cache->{$sequence.':title'}; - $doNotShow{$count.':id'} = 'SequenceColumn'.$sequence; - $count++; - } - } - - $doNotShow{'count'} = $count; - - return (\@infoHeadings, \@infoKeys, \@sequenceHeadings, - \@sequenceKeys, \%doNotShow); -} - -#---- END Student Assessment Worker Functions -------------------------------- +####################################################### +####################################################### 1; + __END__