# The LearningOnline Network with CAPA # # $Id: lonstudentsubmissions.pm,v 1.20 2004/09/20 19:24:02 matthew 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/ # package Apache::lonstudentsubmissions; use strict; use Apache::lonnet(); use Apache::loncommon(); use Apache::lonhtmlcommon(); use Apache::loncoursedata(); use Apache::lonstatistics; use Apache::lonlocal; use Apache::lonstathelpers; use HTML::Entities(); use Time::Local(); use Spreadsheet::WriteExcel(); my @SubmitButtons = ({ name => 'SelectAnother', text => 'Choose a different Problem' }, { name => 'Generate', text => 'Generate Report'}, ); sub BuildStudentSubmissionsPage { my ($r,$c)=@_; # my %Saveable_Parameters = ('Status' => 'scalar', 'Section' => 'array', 'NumPlots' => 'scalar', ); &Apache::loncommon::store_course_settings('student_submissions', \%Saveable_Parameters); &Apache::loncommon::restore_course_settings('student_submissions', \%Saveable_Parameters); # &Apache::lonstatistics::PrepareClasslist(); # $r->print(&CreateInterface()); # my @Students = @Apache::lonstatistics::Students; # if (@Students < 1) { $r->print('

There are no students in the sections selected

'); } # my @CacheButtonHTML = &Apache::lonstathelpers::manage_caches($r,'Statistics','stats_status', '

'.&mt('Loading student data').'

'); $r->rflush(); # if (exists($ENV{'form.problemchoice'}) && ! exists($ENV{'form.SelectAnother'})) { foreach my $button (@SubmitButtons) { if ($button->{'name'} eq 'break') { $r->print("
\n"); } else { $r->print(''); $r->print(' 'x5); } } foreach my $html (@CacheButtonHTML) { $r->print($html.(' 'x5)); } # $r->print('
'.$/); $r->rflush(); # # Determine which problems we are to analyze my @Symbs = &Apache::lonstathelpers::get_selected_symbs('problemchoice'); foreach my $selected (@Symbs) { $r->print(''.$/); } # # Get resource objects my $navmap = Apache::lonnavmaps::navmap->new(); if (!defined($navmap)) { $r->print('

'.&mt("Internal error").'

'); return; } my %already_seen; my @Problems; foreach my $symb (@Symbs) { my $resource = $navmap->getBySymb($symb); push(@Problems,$resource); } # if (! scalar(@Problems) || ! defined($Problems[0])) { $r->print('resource is undefined'); } else { if (scalar(@Problems) == 1) { my $resource = $Problems[0]; $r->print('

'.$resource->title.'

'); $r->print('

'.$resource->src.'

'); if ($ENV{'form.renderprob'} eq 'true') { $r->print( &Apache::lonstathelpers::render_resource({src => $resource->src}) ); $r->rflush(); } } &new_excel_output($r,\@Problems,\@Students); } $r->print('
'); } else { $r->print(''); $r->print(' 'x5); $r->print('

'. &mt('Computing correct answers greatly increasese the amount of time required to prepare a report.'). '

'); $r->print('

'. &mt('please select problems and use the Prepare Report button to continue.'). '

'); $r->print(&Apache::lonstathelpers::MultipleProblemSelector (undef,'problemchoice','Statistics')); } } ######################################################### ######################################################### sub new_excel_output { my ($r,$Problems,$Students) = @_; my $c = $r->connection(); # # # Determine the number of columns in the spreadsheet my $columncount = 3; # username, domain, id my $response_multiplier = 1; $response_multiplier ++ if ($ENV{'form.correctans'} eq 'true'); $response_multiplier += 4 if ($ENV{'form.prob_status'} eq 'true'); my $lastprob; foreach my $prob (@$Problems) { $columncount += ($response_multiplier * $prob->countResponses); last if ($columncount > 255); $lastprob = $prob; } if ($columncount > 255) { $r->print('

'.&mt('Unable to complete request').'

'.$/. '

'.&mt('LON-CAPA is unable to produce your Excel spreadsheet because your selections will result in more than 255 columns. Excel allows only 255 columns in a spreadsheet.').'

'.$/. '

'.&mt('Consider selecting fewer problems to generate reports on, or reducing the number of items per problem. Or use HTML or CSV output.').'

'.$/. '

'.&mt('The last problem that will fit in the current spreadsheet is [_1].',&get_title($lastprob->title,$lastprob->src)).'

'); $r->rflush(); return; } # # Print out a message telling them what we are doing if (scalar(@$Problems) > 1) { $r->print('

'. &mt('Preparing Excel spreadsheet of student responses to [_1] problems', scalar(@$Problems)). '

'); } else { $r->print('

'. &mt('Preparing Excel spreadsheet of student responses'). '

'); } $r->rflush(); # # Create the excel spreadsheet my $filename = '/prtspool/'. $ENV{'user.name'}.'_'.$ENV{'user.domain'}.'_'. time.'_'.rand(1000000000).'.xls'; my $workbook = Spreadsheet::WriteExcel->new('/home/httpd'.$filename); if (! defined($workbook)) { $r->log_error("Error creating excel spreadsheet $filename: $!"); $r->print('

'.&mt("Unable to create new Excel file. ". "This error has been logged. ". "Please alert your LON-CAPA administrator"). '

'); return undef; } # $workbook->set_tempdir('/home/httpd/perl/tmp'); # my $format = &Apache::loncommon::define_excel_formats($workbook); my $worksheet = $workbook->addworksheet('Student Submission Data'); # # Add headers to the worksheet my $rows_output = 0; $worksheet->write($rows_output++,0, $ENV{'course.'.$ENV{'request.course.id'}.'.description'}, $format->{'h1'}); $rows_output++; my $cols_output = 0; my $title_row = $rows_output++; my $partid_row = $rows_output++; my $respid_row = $rows_output++; my $header_row = $rows_output++; $worksheet->write($title_row ,0,'Problem Title',$format->{'bold'}); $worksheet->write($partid_row,0,'Part ID',$format->{'bold'}); $worksheet->write($respid_row,0,'Response ID',$format->{'bold'}); # Student headers my @StudentColumns = ('username','domain','id'); foreach (@StudentColumns) { $worksheet->write($header_row,$cols_output++,ucfirst($_), $format->{'bold'}); } # Problem headers foreach my $prob (@$Problems) { my $title = &get_title($prob->title,$prob->src); $worksheet->write($title_row,$cols_output, $title,$format->{'h3'}); foreach my $partid (@{$prob->parts}) { $worksheet->write($partid_row,$cols_output,$partid); my $responses = [$prob->responseIds($partid)]; my $resptypes = [$prob->responseType($partid)]; for (my $i=0;$iwrite($respid_row,$cols_output, $resptypes->[$i].', '.$responses->[$i]); $worksheet->write($header_row,$cols_output,'Submission'); $cols_output++; if ($ENV{'form.correctans'} eq 'true') { $worksheet->write($header_row,$cols_output,'Correct'); $cols_output++; } if ($ENV{'form.prob_status'} eq 'true') { $worksheet->write($header_row,$cols_output++, 'Award Detail'); $worksheet->write($header_row,$cols_output++,'Attempt'); $worksheet->write($header_row,$cols_output++,'Time'); $worksheet->write($header_row,$cols_output++,'Awarded'); } } } } # # Populate the worksheet with the student data my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin ($r,'Excel File Compilation Status', 'Excel File Compilation Progress', scalar(@$Students),'inline',undef,'Statistics','stats_status'); my $max_row = $rows_output; foreach my $student (@$Students) { last if ($c->aborted()); $cols_output = 0; my $student_row = $max_row; foreach my $field (@StudentColumns) { $worksheet->write($student_row,$cols_output++, $student->{$field}); } my $last_student_col = $cols_output-1; my $response_count; foreach my $prob (@$Problems) { foreach my $partid (@{$prob->parts}) { my @Response = $prob->responseIds($partid); my @ResponseType = $prob->responseType($partid); for (my $i=0;$i<=$#Response;$i++) { my $response_start_col = $last_student_col + $response_count * $response_multiplier + 1; $response_count++; my $respid = $Response[$i]; my $resptype = $ResponseType[$i]; my $results = &Apache::loncoursedata::get_response_data_by_student ($student,$prob->symb(),$respid); if (! defined($results)) { $results = []; } # $rows_output = $student_row; # for (my $j=scalar(@$results)-1;$j>=0;$j--) { $cols_output = $response_start_col; my $response = $results->[$j]; if ($ENV{'form.all_sub'} ne 'true') { next if ($j ne scalar(@$results)-1); } my $cols_output = &write_excel_row ($worksheet, $rows_output++,$cols_output, $response,$student, $prob,$partid,$respid, $format,$resptype); if ($rows_output > $max_row) { $max_row = $rows_output; } } } } } $rows_output++; &Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state, 'last student'); } &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state); # # Close the excel file $workbook->close(); # # Write a link to allow them to download it $r->print('

'. &mt('Your Excel spreadsheet.'). '

'."\n"); $r->print(''); $r->rflush(); return; } sub write_excel_row { my ($worksheet,$row,$col,$response,$student,$prob,$partid,$respid, $format,$resptype) = @_; # my $submission =$response->[&Apache::loncoursedata::RDs_submission()]; $submission = &excel_format_response($submission,$resptype); $worksheet->write($row,$col++,$submission); if ($ENV{'form.correctans'} eq 'true') { my $correct = &Apache::lonstathelpers::analyze_problem_as_student ($prob,$student->{'username'},$student->{'domain'}, $partid,$respid); $correct =&excel_format_response($correct,$resptype); $worksheet->write($row,$col++,$correct); } if ($ENV{'form.prob_status'} eq 'true') { $worksheet->write ($row,$col++, $response->[&Apache::loncoursedata::RDs_awarddetail()]); $worksheet->write ($row,$col++,$response->[&Apache::loncoursedata::RDs_tries()]); $worksheet->write ($row,$col++, &Apache::lonstathelpers::calc_serial ($response->[&Apache::loncoursedata::RDs_timestamp()]), $format->{'date'}); $worksheet->write ($row,$col++,$response->[&Apache::loncoursedata::RDs_awarded()]); } return $col; } sub get_title { my ($title,$src) = @_; if ($title eq '') { ($title) = ($src =~ m|/([^/]+)$|); } else { $title =~ s/\:/:/g; } return $title; } sub excel_format_response { my ($answer,$responsetype) = @_; if ($responsetype eq 'radiobutton') { $answer =~ s/=([^=])$//; } elsif ($responsetype =~ /^(option|match)$/) { $answer = join("\n", map { &Apache::lonnet::unescape($_) ; } sort split('&',$answer) ); } if ($answer =~ m/^=/) { $answer = ' '.$answer; } return $answer; } ## ## Currently not used sub get_problem_data { my ($r,$Problems) = @_; # # Analyze my %Data; if (scalar(@$Problems) > 5) { # progress window my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin ($r,'Problem Analysis Status', 'Problem Analysis Progress', scalar(@$Problems), 'inline',undef,'Statistics','stats_status'); foreach my $problem (@$Problems) { $Data{$problem->symb} = {&Apache::lonstathelpers::get_problem_data ($problem->src)}; &Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state, 'last problem'); } &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state); } else { foreach my $problem (@$Problems) { $Data{$problem->symb} = {&Apache::lonstathelpers::get_problem_data ($problem->src)}; } } return \%Data; } =pod ######################################################### ######################################################### my @DefaultColumns = ( {name=>'username', display=>'Student'}, {name=>'domain', display=>'Domain'}, {name => 'id', display => 'Id'}, # FIXME: Probably need to add score ); my @PartColumns = ( {name => 'time', display =>'Time'}, {name => 'attempt', display =>'Attempt'}, {name => 'awarded', display =>'Award'}, {name => 'weight', display =>'Part Weight'}, {name => 'score', display =>'Score'}, ); my @ResponseColumns = ( {name => 'submission', display =>'Submission'}, {name => 'answer', display =>'Correct Answer'}, {name => 'awarddetail', display =>'Awarddetail'}, ); sub get_response_type { my ($resource,$partid,$respid) = @_; my $response_type = ''; for (my $i=0; $i{'partdata'}->{$partid}->{'ResponseIds'}}); $i++) { if($resource->{'partdata'}->{$partid}->{'ResponseIds'}->[$i] eq $respid){ $response_type = $resource->{'partdata'}->{$partid}->{'ResponseTypes'}->[$i]; last; } } return $response_type; } ######################################################### ######################################################### ## ## prepare_html_output ## ######################################################### ######################################################### sub prepare_html_output { my ($r,$Problems,$Students) = @_; my $problem; my $c = $r->connection(); my ($resource,$respid,$partid) = ($problem->{'resource'}, $problem->{'respid'}, $problem->{'part'}); # if ($ENV{'form.correctans'} eq 'true') { $r->print('

'.&mt('Generating Correct Answers').'

'); &Apache::lonstathelpers::GetStudentAnswers($r,$problem,$Students, 'Statistics', 'stats_status'); } # $r->print('

'.&mt('Student Responses').'

'); # $r->rflush(); my $response_type = &get_response_type($resource,$partid,$respid); if (! defined($response_type)) { $r->print('

'.&mt('Unable to determine response type').'

'); return; } my $count = 0; my @Columns; if (exists($ENV{'form.concise'}) && $ENV{'form.concise'} eq 'true') { foreach (@DefaultColumns) { if ($_->{'name'} =~ /^(username|domain|id)$/){ push(@Columns,$_); } } } else { @Columns = @DefaultColumns; } my $header = ''.$/.&html_headers(\@Columns); if ($response_type eq 'essay') { $header .= &html_essay_headers(); } elsif ($response_type eq 'option') { $header .= &html_option_headers(); } else { $header .= &html_generic_headers(); } $header = ''.$header.''; # $r->print($/.$header.$/); foreach my $student (@$Students) { if ($count >= 50) { $r->print('
'.$/.$header.$/); $count = 0; } last if ($c->aborted()); my $results = &Apache::loncoursedata::get_response_data_by_student ($student,$resource->{'symb'},$respid); next if (! defined($results) || ref($results) ne 'ARRAY'); for (my $i=0;$i[$i]; if ($ENV{'form.last_sub_only'} eq 'true' && $i < (scalar(@$results)-1)) { next; } my $data; $data->{'username'} = $student->{'username'}; $data->{'domain'} = $student->{'domain'}; $data->{'id'} = $student->{'id'}; $data->{'fullname'} = $student->{'fullanem'}; $data->{'status'} = $student->{'status'}; $data->{'time'} = &Apache::lonlocal::locallocaltime ($response->[&Apache::loncoursedata::RDs_timestamp()]); $data->{'attempt'} = $response->[&Apache::loncoursedata::RDs_tries()]; $data->{'awarded'} = $response->[&Apache::loncoursedata::RDs_awarded()]; $data->{'awarddetail'} = $response->[&Apache::loncoursedata::RDs_awarddetail()]; $data->{'weight'} = &Apache::lonnet::EXT ('resource.'.$partid.'.weight',$resource->{'symb'}, undef,undef,undef); $data->{'score'} = $data->{'weight'} * $data->{'awarded'}; my $rowextra = 'bgcolor="#CCCCCC"'; if ($count % 2 == 1) { $rowextra = 'bgcolor="#EEEEEE"'; } my $row = ''; foreach my $col (@Columns) { $row .= ''. $data->{$col->{'name'}}.''; } if ($response_type eq 'essay') { $row .= &html_essay_results ($response->[&Apache::loncoursedata::RDs_submission()], $student->{'answer'}, scalar(@Columns),$rowextra); } elsif ($response_type eq 'option') { $row .= &html_option_results ($response->[&Apache::loncoursedata::RDs_submission()], $student->{'answer'}, scalar(@Columns),$rowextra); } else { $row .= &html_generic_results ($response->[&Apache::loncoursedata::RDs_submission()], $student->{'answer'}, scalar(@Columns),$rowextra); } $row .= ''; $r->print($row.$/); $count++; } } $r->print(''.$/); return; } ##################################################### ## ## HTML helper routines ## ##################################################### sub html_headers { my ($Columns) = @_; my $Str; foreach my $column (@$Columns) { $Str .= ''.$column->{'display'}.''; } return $Str; } sub html_essay { my ($submission,$correct,$tablewidth,$rowextra)=@_; # $submission =~ s|\\r\\n|$/|g; $submission = &HTML::Entities::encode($submission,'<>&"'); $submission =~ s|$/\s*$/|$/

$/|g; $submission =~ s|\\||g; $submission = '

'.$submission.'

'; # my $Str = ''. ''.$submission.''; if ($ENV{'form.correctans'} eq 'true') { $Str .= ''; if (defined($correct) && $correct !~ /^\s*$/) { $Str .= ''. ''.&mt('Correct Answer:').''.$correct.''; } } $Str .= ''; # return $Str; } sub html_essay_headers { return ''; } sub html_generic_headers { my $header =''.&mt('Submission').''; if ($ENV{'form.correctans'} eq 'true') { $header .= ''.&mt('Correct').''; } return $header; } sub html_option_headers { return &html_generic_headers(); } sub html_radiobutton_results { my ($submission,$correct,$tablewidth,$rowclass)=@_; $submission =~ s/=([^=])$//; return &html_generic_results($submission,$correct,$tablewidth,$rowclass); } sub html_generic_results { my ($submission,$correct,$tablewidth,$rowclass)=@_; my $Str .= ''.$submission.''; if ($ENV{'form.correctans'} eq 'true') { $Str .= ''.$correct.''; } $Str .= ''; return $Str; } sub html_option_results { my ($submission,$correct,$tablewidth,$rowclass)=@_; $submission = '
    '. '
  • '.join('
  • ', map { &Apache::lonnet::unescape($_) ; } sort split('&',$submission) ). '
    • '; if (defined($correct) && $correct !~ /^\s*$/) { $correct = '
        '. '
      • '.join('
      • ', map { &Apache::lonnet::unescape($_) ; } sort split('&',$correct) ). '
      '; } # return &html_generic_results($submission,$correct,$tablewidth,$rowclass); } ######################################################### ######################################################### ## ## CSV output of student answers ## ######################################################### ######################################################### sub prepare_csv_output { my ($r,$Problems,$Students) = @_; my $problem; # my $c = $r->connection(); my ($resource,$respid,$partid) = ($problem->{'resource'}, $problem->{'respid'}, $problem->{'part'}); # if ($ENV{'form.correctans'} eq 'true') { $r->print('

      '.&mt('Generating Correct Answers').'

      '); &Apache::lonstathelpers::GetStudentAnswers($r,$problem,$Students, 'Statistics', 'stats_status'); } # $r->print('

      '. &mt('Generating CSV report of student responses').'

      '); # # Progress window my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin ($r,'CSV File Compilation Status', 'CSV File Compilation Progress', scalar(@$Students),'inline',undef,'Statistics','stats_status'); $r->rflush(); # # Open a file my $outputfile; my $filename = '/prtspool/'. $ENV{'user.name'}.'_'.$ENV{'user.domain'}.'_'. time.'_'.rand(1000000000).'.csv'; unless ($outputfile = Apache::File->new('>/home/httpd'.$filename)) { $r->log_error("Couldn't open $filename for output $!"); $r->print("Problems occured in writing the csv file. ". "This error has been logged. ". "Please alert your LON-CAPA administrator."); $outputfile = undef; } # # my @Columns; if (exists($ENV{'form.concise'}) && $ENV{'form.concise'} eq 'true') { foreach (@DefaultColumns) { if ($_->{'name'} =~ /^(username|domain|id)$/){ push(@Columns,$_); } } } else { @Columns = @DefaultColumns; } # my $response_type = &get_response_type($resource,$partid,$respid); if (! defined($response_type) || $response_type eq '') { $r->print('

      '.&mt('Unable to determine response type').'

      '); return; } # my $header = &csv_headers(\@Columns).','.&csv_generic_headers(); print $outputfile $header.$/; # foreach my $student (@$Students) { last if ($c->aborted()); my $results = &Apache::loncoursedata::get_response_data_by_student ($student,$resource->{'symb'},$respid); next if (! defined($results) || ref($results) ne 'ARRAY'); for (my $i=0;$i[$i]; if ($ENV{'form.last_sub_only'} eq 'true' && $i < (scalar(@$results)-1)) { next; } my $data; $data->{'username'} = $student->{'username'}; $data->{'domain'} = $student->{'domain'}; $data->{'id'} = $student->{'id'}; $data->{'fullname'} = $student->{'fullanem'}; $data->{'status'} = $student->{'status'}; $data->{'time'} = &Apache::lonlocal::locallocaltime ($response->[&Apache::loncoursedata::RDs_timestamp()]); $data->{'attempt'} = $response->[&Apache::loncoursedata::RDs_tries()]; $data->{'awarded'} = $response->[&Apache::loncoursedata::RDs_awarded()]; $data->{'awarddetail'} = $response->[&Apache::loncoursedata::RDs_awarddetail()]; $data->{'weight'} = &Apache::lonnet::EXT ('resource.'.$partid.'.weight',$resource->{'symb'}, undef,undef,undef); $data->{'score'} = $data->{'weight'} * $data->{'awarded'}; my $rowextra = ''; my $row; foreach my $col (@Columns) { $row .= '"'. &Apache::loncommon::csv_translate($data->{$col->{'name'}}).'",'; } if ($response_type eq 'option') { $row .= &csv_option_results ($response->[&Apache::loncoursedata::RDs_submission()], $student->{'answer'}, scalar(@Columns),$rowextra); } elsif ($response_type eq 'radiobutton') { $row .= &csv_radiobutton_results ($response->[&Apache::loncoursedata::RDs_submission()], $student->{'answer'}, scalar(@Columns),$rowextra); } else { $row .= &csv_generic_results ($response->[&Apache::loncoursedata::RDs_submission()], $student->{'answer'}, scalar(@Columns),$rowextra); } print $outputfile $row.$/; } &Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state, 'last student'); } close($outputfile); # # Close the progress window &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state); # # Tell the user where to get their csv file $r->print('
      '. ''.&mt('Your csv file.').''."\n"); $r->rflush(); return; } sub csv_headers { my ($Columns) = @_; my $Str; foreach my $column (@$Columns) { $Str .= '"'.&Apache::loncommon::csv_translate($column->{'display'}).'",'; } chop($Str); return $Str; } sub csv_generic_headers { my ($title) = @_; if (! defined($title)) { $title = &mt('Submission'); } my $header = '"'.&Apache::loncommon::csv_translate($title).'"'; if ($ENV{'form.correctans'} eq 'true') { $header .= ',"'.&Apache::loncommon::csv_translate(&mt('Correct')).'"'; } return $header; } #------------------------------------------ sub csv_essay_results { my ($submission,$correct,$tablewidth,$rowextra)=@_; # $submission =~ s|\\r|\\\\r|g; $submission =~ s|\\n|\\\\n|g; # return &csv_generic_results($submission,$correct,$tablewidth); } #------------------------------------------ sub csv_radiobutton_results { my ($submission,$correct,$tablewidth,$rowclass)=@_; $submission =~ s/=[^=]*$//; return &csv_generic_results($submission,$correct,$tablewidth,$rowclass); } #------------------------------------------ sub csv_option_results { my ($submission,$correct,$tablewidth,$rowclass)=@_; $submission = join(',', map { &Apache::lonnet::unescape($_) ; } sort split('&',$submission) ); if (defined($correct) && $correct !~ /^\s*$/) { $correct =join(',', map { &Apache::lonnet::unescape($_) ; } sort split('&',$submission)); } return &csv_generic_results($submission,$correct,$tablewidth,$rowclass); } #------------------------------------------ sub csv_generic_results { my ($submission,$correct,$tablewidth,$rowclass)=@_; my $Str .= '"'.&Apache::loncommon::csv_translate($submission).'"'; if ($ENV{'form.correctans'} eq 'true') { $Str .= ',"'.&Apache::loncommon::csv_translate($correct).'"'; } return $Str; } ######################################################### ######################################################### ## ## Excel output of student answers and correct answers ## ######################################################### ######################################################### sub prepare_excel_output { my ($r,$Problems,$Students) = @_; my $c = $r->connection(); # $r->print('

      '. &mt('Preparing Excel spreadsheet of student responses'). '

      '); if ($ENV{'form.correctans'} eq 'true') { $r->print('

      '. &mt('See the status bar above for student answer computation progress'). '

      '); foreach my $problem (@$Problems) { my ($resource,$respid,$partid) = ($problem->{'resource'}, $problem->{'respid'}, $problem->{'part'}); # &Apache::lonstathelpers::GetStudentAnswers($r,$problem,$Students, 'Statistics', 'stats_status'); } $r->print(''); } # $r->rflush(); my @ColumnLabels; my @Columns = @DefaultColumns; my %seen; foreach my $problem (@$Problems) { my ($resource,$respid,$partid) = ($problem->{'resource'}, $problem->{'respid'}, $problem->{'part'}); my $partkey = $resource->{'symb'}.':'.$partid; if (! $seen{$partkey}) { $ColumnLabels[$#Columns]=$resource->{'title'}; push(@ColumnLabels,$partid); push(@Columns,@PartColumns); } push(@Column if ($ENV{'form.correctans'} eq 'true') { push(@Columns,'Computers answer'); } $seen{$resource->{'symb'}.':'.$partid} .= ':'.$respid; } # my ($awarded_col,$weight_col); # for (my $i=0;$i<=$#Columns;$i++) { # if ($Columns[$i]->{'name'} eq 'weight' ) { $weight_col = $i; } # if ($Columns[$i]->{'name'} eq 'awarded') { $awarded_col = $i; } # } # # Create excel worksheet my $filename = '/prtspool/'. $ENV{'user.name'}.'_'.$ENV{'user.domain'}.'_'. time.'_'.rand(1000000000).'.xls'; my $workbook = Spreadsheet::WriteExcel->new('/home/httpd'.$filename); if (! defined($workbook)) { $r->log_error("Error creating excel spreadsheet $filename: $!"); $r->print('

      '.&mt("Unable to create new Excel file. ". "This error has been logged. ". "Please alert your LON-CAPA administrator"). '

      '); return undef; } # $workbook->set_tempdir('/home/httpd/perl/tmp'); # my $format = &Apache::loncommon::define_excel_formats($workbook); my $worksheet = $workbook->addworksheet('Student Submission Data'); # # Make sure we get new weight data instead of data on a 10 minute delay &Apache::lonnet::clear_EXT_cache_status(); # # Put on the standard headers and whatnot my $rows_output=0; $worksheet->write($rows_output++,0,$resource->{'title'},$format->{'h1'}); $worksheet->write($rows_output++,0,$resource->{'src'},$format->{'h3'}); $rows_output++; $worksheet->write_row($rows_output++,0, [map {$_->{'display'}} @Columns], $format->{'bold'}); # # Populate the worksheet with the student data my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin ($r,'Excel File Compilation Status', 'Excel File Compilation Progress', scalar(@$Students),'inline',undef,'Statistics','stats_status'); foreach my $student (@$Students) { last if ($c->aborted()); my $results = &Apache::loncoursedata::get_response_data_by_student ($student,$resource->{'symb'},$respid); my %row; $row{'username'} = $student->{'username'}; $row{'domain'} = $student->{'domain'}; $row{'id'} = $student->{'id'}; $row{'correct'} = $student->{'answer'}; $row{'weight'} = &Apache::lonnet::EXT ('resource.'.$partid.'.weight',$resource->{'symb'}, undef,undef,undef); if (! defined($results) || ref($results) ne 'ARRAY') { $row{'score'} = '='. &Spreadsheet::WriteExcel::Utility::xl_rowcol_to_cell ($rows_output,$awarded_col) .'*'. &Spreadsheet::WriteExcel::Utility::xl_rowcol_to_cell ($rows_output,$weight_col); my $cols_output = 0; foreach my $col (@Columns) { if (! exists($row{$col->{'name'}})) { $cols_output++; next; } $worksheet->write($rows_output,$cols_output++, $row{$col->{'name'}}); } $rows_output++; } else { for (my $i=0;$i[$i]; if ($ENV{'form.last_sub_only'} eq 'true' && $i < (scalar(@$results)-1)) { next; } delete($row{'time'}); delete($row{'attempt'}); delete($row{'submission'}); delete($row{'awarded'}); delete($row{'grading'}); delete($row{'score'}); my %row_format; # # Time is handled differently $row{'time'} = &Apache::lonstathelpers::calc_serial ($response->[&Apache::loncoursedata::RDs_timestamp()]); $row_format{'time'}=$format->{'date'}; # $row{'attempt'} = $response->[ &Apache::loncoursedata::RDs_tries()]; $row{'submission'} = $response->[ &Apache::loncoursedata::RDs_submission()]; if ($row{'submission'} =~ m/^=/) { # This will be interpreted as a formula. That is bad! $row{'submission'} = " ".$row{'submission'}; } $row{'awarddetail'} = $response->[ &Apache::loncoursedata::RDs_awarddetail()]; $row{'awarded'} = $response->[ &Apache::loncoursedata::RDs_awarded()]; $row{'score'} = '='. &Spreadsheet::WriteExcel::Utility::xl_rowcol_to_cell ($rows_output,$awarded_col) .'*'. &Spreadsheet::WriteExcel::Utility::xl_rowcol_to_cell ($rows_output,$weight_col); my $cols_output = 0; foreach my $col (@Columns) { $worksheet->write($rows_output,$cols_output++,$row{$col->{'name'}}, $row_format{$col->{'name'}}); } $rows_output++; } } # End of else clause on if (! defined($results) .... &Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state, 'last student'); } &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state); # # Close the excel file $workbook->close(); # # Write a link to allow them to download it $r->print('

      '. &mt('Your Excel spreadsheet.'). '

      '."\n"); $r->print(''); $r->rflush(); return; } =cut ######################################################### ######################################################### ## ## Generic Interface Routines ## ######################################################### ######################################################### sub CreateInterface { ## ## Output Selection my $output_selector = $/.''.$/; ## ## Environment variable initialization my $Str = ''; $Str .= &Apache::lonhtmlcommon::breadcrumbs (undef,'Student Submission Reports'); $Str .= '

      '; $Str .= ''."\n"; $Str .= ''; $Str .= ''; $Str .= ''; $Str .= ''; $Str .= ''."\n"; # $Str .= ''; # $Str .= ''; # # Render problem checkbox my $prob_checkbox = ''. &mt('Show problem [_1]',$prob_checkbox).'
      '. '
      '. '
      '. '
      '. ''; # $Str .= ''."\n"; $Str .= '
      '.&mt('Sections').''.&mt('Enrollment Status').''.&mt('Output Options').'
      '."\n"; $Str .= &Apache::lonstatistics::SectionSelect('Section','multiple',5); $Str .= ''; $Str .= &Apache::lonhtmlcommon::StatusOptions(undef,undef,5); $Str .= '
      '."\n"; # $Str .= ''.&mt('Status: [_1]', ''). ''.'

      '; ## return $Str; } 1; __END__