--- loncom/interface/statistics/lonstudentassessment.pm 2003/03/03 19:28:29 1.31 +++ loncom/interface/statistics/lonstudentassessment.pm 2003/03/04 16:10:09 1.34 @@ -1,6 +1,6 @@ # The LearningOnline Network with CAPA # -# $Id: lonstudentassessment.pm,v 1.31 2003/03/03 19:28:29 matthew Exp $ +# $Id: lonstudentassessment.pm,v 1.34 2003/03/04 16:10:09 matthew Exp $ # # Copyright Michigan State University Board of Trustees # @@ -83,7 +83,7 @@ my $Statistics; =item $output_mode 'html', 'excel', or 'csv' for output mode -=item $show 'all' or 'totals' determines how much data is output +=item $show 'all', 'totals', or 'scores' determines how much data is output =cut @@ -143,6 +143,7 @@ the button marked "Update Disp

ENDMSG +# $r->print(&OutputDescriptions()); return; } # @@ -270,14 +271,70 @@ sub CreateInterface { ####################################################### ####################################################### +my @OutputOptions = + ({ name => 'HTML, with links', + value => 'html, with links', + description => 'Output HTML with each symbol linked to the problem '. + 'which generated it.'}, + { 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.'}, + { 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'}, + { name => 'HTML, totals', + value => 'html, totals', + description => 'Output HTML, but only the summary statistics for each'. + ' sequence selected.'}, + { 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.'}, +# { name => 'Excel, everything', +# value => 'excel, everything', +# 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.'}, + { 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'}, + { name => 'CSV, everything', + value => 'csv, everything', + description => ''}, + { name => 'CSV, scores only', + value => 'csv, scores only', + description => ''}, + { name => 'CSV, totals', + value => 'csv, totals', + description => ''}, + ); + +sub OutputDescriptions { + my $Str = ''; + $Str .= "

Output Modes

\n"; + $Str .= "
\n"; + foreach my $outputmode (@OutputOptions) { + $Str .="
".$outputmode->{'name'}."
\n"; + $Str .="
".$outputmode->{'description'}."
\n"; + } + $Str .= "
\n"; + return $Str; +} + sub CreateAndParseOutputSelector { my $Str = ''; my $elementname = 'outputmode'; # # Format for output options is 'mode, restrictions'; - my @Options = ('html, with links','html, without links', - 'html, totals only','excel, totals only', - 'csv, totals only','csv, everything'); my $selected = 'html, with links'; if (exists($ENV{'form.'.$elementname})) { if (ref($ENV{'form.'.$elementname} eq 'ARRAY')) { @@ -303,18 +360,20 @@ sub CreateAndParseOutputSelector { } else { $show_links = 'no'; } - if ($restriction eq 'totals only') { + if ($restriction eq 'totals') { $show = 'totals'; + } elsif ($restriction eq 'scores only') { + $show = 'scores'; } else { $show = 'everything'; } # # Build the form element $Str = qq/"; return $Str; @@ -412,6 +471,9 @@ sub html_outputstudent { if ($show eq 'totals') { $performance = ' 'x(length($seq_max)-length($score)).$ratio; $performance .= ' 'x($seq->{'width'}-length($performance)); + } elsif ($show eq 'scores') { + $performance = $score; + $performance .= ' 'x($seq->{'width'}-length($performance)); } else { # Pad with extra spaces $performance .= ' 'x($seq->{'width'}-$seq_max- @@ -475,42 +537,209 @@ sub html_finish { { my $excel_sheet; +my $excel_workbook; + +my $filename; +my $rows_output; +my $cols_output; + +my $num_students; +my $start_time; sub excel_initialize { my ($r) = @_; # - $r->print("

Not implemented yet

"); - return; - my $filename = '/prtspool/'. + $filename = '/prtspool/'. $ENV{'user.name'}.'_'.$ENV{'user.domain'}.'_'. time.'_'.rand(1000000000).'.xls'; - $excel_sheet = Spreadsheet::WriteExcel->new('/home/httpd'.$filename); - if (! defined($excel_sheet)) { + # + $excel_workbook = undef; + $excel_sheet = undef; + # + $rows_output = 0; + $cols_output = 0; + # + $num_students = 0; + $start_time = time; + # + # Create sheet + $excel_workbook = Spreadsheet::WriteExcel->new('/home/httpd'.$filename); + # + # Check for errors + if (! defined($excel_workbook)) { $r->log_error("Error creating excel spreadsheet $filename: $!"); $r->print("Problems creating new Excel file. ". "This error has been logged. ". "Please alert your LON-CAPA administrator"); - return 0; + return ; } # # The excel spreadsheet stores temporary data in files, then put them # together. If needed we should be able to disable this (memory only). # The temporary directory must be specified before calling 'addworksheet'. # File::Temp is used to determine the temporary directory. - $excel_sheet->set_tempdir($Apache::lonnet::tmpdir); + $excel_workbook->set_tempdir($Apache::lonnet::tmpdir); + # + # Add a worksheet + my $sheetname = $ENV{'course.'.$ENV{'request.course.id'}.'.description'}; + if (length($sheetname) > 31) { + $sheetname = substr($sheetname,0,31); + } + $excel_sheet = $excel_workbook->addworksheet($sheetname); + # + # Put the course description in the header + $excel_sheet->write($rows_output,$cols_output++, + $ENV{'course.'.$ENV{'request.course.id'}.'.description'}); + $cols_output += 3; + # + # Put a description of the sections listed + my $sectionstring = ''; + my @Sections = @Apache::lonstatistics::SelectedSections; + if (scalar(@Sections) > 1) { + if (scalar(@Sections) > 2) { + my $last = pop(@Sections); + $sectionstring = "Sections ".join(', ',@Sections).', and '.$last; + } else { + $sectionstring = "Sections ".join(' and ',@Sections); + } + } else { + if ($Sections[0] eq 'all') { + $sectionstring = "All sections"; + } else { + $sectionstring = "Section ".$Sections[0]; + } + } + $excel_sheet->write($rows_output,$cols_output++,$sectionstring); + $cols_output += scalar(@Sections); + # + # Put the date in there too + $excel_sheet->write($rows_output,$cols_output++, + 'Compiled on '.localtime(time)); + # + $rows_output++; + # + # Add the student headers + $cols_output = 0; + foreach my $field (&get_student_fields_to_show()) { + $excel_sheet->write($rows_output,$cols_output++,$field); + } + # + # Add the Sequence Headers + foreach my $seq (&get_sequences_to_show) { + $excel_sheet->write($rows_output,$cols_output,$seq->{'title'}); + if ($show eq 'totals') { + $excel_sheet->write($rows_output+1,$cols_output,'score'); + $excel_sheet->write($rows_output+1,$cols_output+1,'maximum'); + $cols_output += 2; + } else { + $cols_output++; + } + } + # + # Bookkeeping + if ($show eq 'totals') { + $rows_output += 2; + } else { + $rows_output += 1; + } + # + # Let the user know what we are doing + my $studentcount = scalar(@Apache::lonstatistics::Students); + $r->print("

Compiling Excel spreadsheet for ". + $studentcount.' student'); + $r->print('s') if ($studentcount > 1); + $r->print("

\n"); + $r->rflush(); # - # Determine the name to give the worksheet -# $excel_sheet->addworksheet(); - return; } sub excel_outputstudent { my ($r,$student) = @_; + return if (! defined($excel_sheet)); + $cols_output=0; + # + # Write out student data + my @to_show = &get_student_fields_to_show(); + foreach my $field (@to_show) { + $excel_sheet->write($rows_output,$cols_output++,$student->{$field}); + } + # + # Get student assessment data + my %StudentsData; + my @tmp = &Apache::loncoursedata::get_current_state($student->{'username'}, + $student->{'domain'}, + undef, + $ENV{'request.course.id'}); + if ((scalar @tmp > 0) && ($tmp[0] !~ /^error:/)) { + %StudentsData = @tmp; + } + # + # Write out sequence scores and totals data + foreach my $seq (&get_sequences_to_show) { + my ($performance,$score,$seq_max) = + &StudentPerformanceOnSequence($student,\%StudentsData, + $seq,'no'); + if ($show eq 'totals' || $show eq 'scores') { + $excel_sheet->write($rows_output,$cols_output++,$score); + } + if ($show eq 'totals') { + $excel_sheet->write($rows_output,$cols_output++,$seq_max); + } + } + # + # Bookkeeping + $rows_output++; + $cols_output=0; + # + # Time estimate + $num_students++; + if ($num_students % 10 == 0) { + my $time_est = (time - $start_time)/$num_students * + (scalar(@Apache::lonstatistics::Students)-$num_students); + $time_est = int($time_est); + if (int ($time_est/60) > 0) { + my $min = int($time_est/60); + my $sec = $time_est % 60; + $time_est = $min.' minutes'; + if ($sec > 1) { + $time_est.= ', '.$sec.' seconds'; + } elsif ($sec > 0) { + $time_est.= ', '.$sec.' second'; + } + } else { + $time_est .= ' seconds'; + } + $r->print($num_students.' out of '. + (scalar(@Apache::lonstatistics::Students)). + " students processed. ". + $time_est." remain. Elapsed: ".(time - $start_time). + "
\n"); + $r->rflush(); + } + return; } sub excel_finish { my ($r) = @_; + return if (! defined($excel_sheet)); + # + # Write the excel file + $excel_workbook->close(); + my $c = $r->connection(); + # + return if($c->aborted()); + # + # Tell the user where to get their excel file + $r->print('

'. + 'Your Excel spreadsheet.'."\n"); + my $total_time = time - $start_time; + if (int ($total_time / 60) > 0) { + $total_time = int($total_time/60).' minutes, '.($total_time % 60); + } + $r->print('
'.$total_time.' seconds total'); + $r->rflush(); + return; } } @@ -575,8 +804,7 @@ Inputs: ####################################################### ####################################################### sub StudentPerformanceOnSequence { - my ($student,$studentdata,$seq,$links,$totalonly) = @_; - $totalonly = 0 if (! defined($totalonly)); + my ($student,$studentdata,$seq,$links) = @_; $links = 'no' if (! defined($links)); my $Str = ''; my ($sum,$max) = (0,0);