# The LearningOnline Network with CAPA # # $Id: lonstudentsubmissions.pm,v 1.54 2009/04/21 13:28:23 bisitz 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(); use lib '/home/httpd/lib/perl/'; use LONCAPA; 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('
' .&mt('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); } # $r->print('

'. &Apache::lonstatistics::section_and_enrollment_description(). '

'); 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($resource)); $r->rflush(); } } if ($env{'form.output'} eq 'excel') { &prepare_excel_output($r,\@Problems,\@Students); } elsif ($env{'form.output'} eq 'csv') { &prepare_csv_output($r,\@Problems,\@Students); } else { &prepare_html_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 [_1]Prepare Report[_2] button to continue.','',''). '

'); $r->print(&Apache::lonstathelpers::MultipleProblemSelector (undef,'problemchoice','Statistics')); } } ## ## get_extra_response_headers ## sub get_extra_response_headers { my @extra_resp_headers; if ($env{'form.correctans'} eq 'true') { push(@extra_resp_headers,'Correct'); } if ($env{'form.prob_status'} eq 'true') { push(@extra_resp_headers,'Award Detail'); push(@extra_resp_headers,'Time'); push(@extra_resp_headers,'Attempt'); push(@extra_resp_headers,'Awarded'); } return @extra_resp_headers; } ## ## get_headers: ## return the proper headers for the given response sub get_headers { my ($prob,$partid,$respid,$resptype,$analysis,$output,$purpose, @basic_headers) = @_; my @headers; if ($resptype eq 'essay' && $purpose eq 'display' && ($output eq 'html')) {# || scalar(@{$prob->parts})!=1)) { @headers = (); } elsif ($resptype =~ /^(option|match|rank)$/) { my $prefix = '_'; if ($purpose eq 'display') { $prefix = ''; } my @foils = map { $prefix.$_; } sort(keys(%{$analysis->{$partid.'.'.$respid}->{'_Foils'}})); if (scalar(@basic_headers) && $basic_headers[0] eq 'Correct') { @foils = map { ($_ , $_.' Correct') } @foils; shift(@basic_headers); # Get rid of 'Correct' } @headers = (@foils,@basic_headers); } elsif (lc($resptype) eq 'task') { @headers = ('Grader','Status',@basic_headers,'Submission'); } else { @headers = ('Submission',@basic_headers); } return @headers; } ######################################################### ######################################################### ## ## HTML Output Routines ## ######################################################### ######################################################### sub prepare_html_output { my ($r,$problems,$students) = @_; my $c = $r->connection(); # # Set a flag for the case when there is just one problem my $single_response = 0; if (scalar(@$problems) == 1 && $problems->[0]->countResponses == 1) { $single_response = 1; } # # Compute the number of columns per response my @extra_resp_headers = &get_extra_response_headers(); # # Create the table header my @student_columns = ('username','domain','id','section'); # my %headers; my $student_column_count = scalar(@student_columns); $headers{'problem'} = qq{\ }; foreach (@student_columns) { $headers{'student'}.= ''.ucfirst($_).''; } # # we put the headers into the %headers hash my $total_col = scalar(@student_columns); my $nonempty_part_headers = 0; # my %problem_analysis; foreach my $prob (@$problems) { my %analysis = &Apache::lonstathelpers::get_problem_data($prob->src); $problem_analysis{$prob->src}=\%analysis; # my $prob_span = 0; my $single_part = 0; if (scalar(@{$prob->parts}) == 1) { $single_part = 1; } foreach my $partid (@{$prob->parts}) { my $part_span = 0; my $responses = [$prob->responseIds($partid)]; my $resptypes = [$prob->responseType($partid)]; for (my $i=0;$i[$i]; my @headers = &get_headers($prob,$partid,$respid, $resptypes->[$i], $problem_analysis{$prob->src}, 'html','display', @extra_resp_headers); if (scalar(@headers)>0) { $total_col += scalar(@headers); $part_span += scalar(@headers); $headers{'response'} .= ''. &mt('Response [_1]',$responses->[$i]).''; $headers{'student'}.= ''. join('', @headers). ''; } } if ($part_span == 0) { next; } if (! $single_part) { my $tmpname = $partid; if ($partid =~/^\d+$/) { $tmpname = $prob->part_display($partid); } if ($tmpname !~ /^part/) { $tmpname = 'Part '.$tmpname; } $headers{'part'} .= qq{$tmpname}; $nonempty_part_headers = 1; } else { $headers{'part'} .= qq{ }; } $prob_span += $part_span; } my $title = $prob->compTitle; if ($prob_span > 0) { $headers{'problem'}.= qq{$title}; } elsif ($single_response) { $prob_span = scalar(@student_columns); $headers{'problem'} = qq{$title}; } } if (exists($headers{'part'})) { $headers{'part'} = qq{\ }. $headers{'part'}; } if (exists($headers{'response'})) { $headers{'response'}= qq{\ }. $headers{'response'}; } my $full_header = $/.''.$/; $full_header .= ''.$headers{'problem'}.''.$/; if ($nonempty_part_headers) { $full_header .= ''.$headers{'part'}.''.$/; } $full_header .= ''.$headers{'response'}.''.$/; $full_header .= ''.$headers{'student'}.''.$/; # # Main loop my $count; $r->print($/.$full_header.$/); my $row_class = 'odd'; # css foreach my $student (@$students) { my $student_row_data; if ($count++ >= 30) { $r->print('
'.$/.$full_header.$/); $count = 0; } last if ($c->aborted()); foreach my $field (@student_columns) { $student_row_data .= ''.$student->{$field}.''; } # # Figure out what it is we need to output for this student my @essays; my %prob_data; my $maxrow; foreach my $prob (@$problems) { $prob_data{$prob->symb}={}; foreach my $partid (@{$prob->parts}) { my @responses = $prob->responseIds($partid); my @response_type = $prob->responseType($partid); for (my $i=0;$i<=$#responses;$i++) { my $respid = $responses[$i]; my $results = &Apache::loncoursedata::get_response_data_by_student ($student,$prob->symb(),$respid); my $resptype = $response_type[$i]; my @headers = &get_headers($prob,$partid,$respid, $resptype, $problem_analysis{$prob->src}, 'html','normal', @extra_resp_headers); my $width = scalar(@headers); next if ($width < 1); my $resp_data; $resp_data->{'fake'} = qq{ }; if (! defined($results)) { $results = []; } # if (scalar(@$results) > $maxrow && $resptype ne 'essay') { $maxrow = scalar(@$results); } for (my $j=scalar(@$results)-1;$j>=0;$j--) { if ($env{'form.all_sub'} ne 'true') { next if ($j ne scalar(@$results)-1); } my $response = &hashify_response($results->[$j], $prob, $student, $partid, $respid); if ($resptype eq 'essay') { push(@essays, &html_essay_results(\@headers, $prob,$partid,$respid, $response, $single_response). ''); } elsif (lc($resptype) eq 'task') { my $results = &html_task_results(\@headers, $prob,$partid,$respid, $response,$resptype); if ($results) { push(@{$resp_data->{'real'}},$results); } } else { push(@{$resp_data->{'real'}}, &html_non_essay_results(\@headers, $prob,$partid,$respid, $response,$resptype)); } } $prob_data{$prob->symb}->{$partid}->{$respid}=$resp_data; } # end of $i loop } # end of partid loop } # end of prob loop # # if there is no data, skip this student. next if (! $maxrow && ! scalar(@essays)); # # Go through the problem data and output a row. if ($row_class eq 'even') { $row_class = 'odd'; } else { $row_class = 'even'; } my $printed_something; for (my $rows_output = 0;$rows_output<$maxrow;$rows_output++) { my $html; my $no_data = 1; foreach my $prob (@$problems) { foreach my $partid (@{$prob->parts}) { my @responses = $prob->responseIds($partid); my @response_type = $prob->responseType($partid); for (my $i=0;$i<=$#responses;$i++) { my $respid = $responses[$i]; my $resp_data = $prob_data{$prob->symb}->{$partid}->{$respid}; next if ($response_type[$i] eq 'essay'); if (defined($resp_data->{'real'}->[$rows_output])) { $html .= $resp_data->{'real'}->[$rows_output]; $no_data = 0; } else { $html .= $resp_data->{'fake'}; } } } } if (! $no_data) { $r->print(qq{$student_row_data$html}.$/); $printed_something=1; } } if (@essays) { my $tr = qq{}; my $td = qq{}; if (! $printed_something) { $r->print($tr.$student_row_data.''.$/); } $r->print($tr.$td. join(''.$/.$tr.$td,@essays).''.$/); undef(@essays); } } # end of student loop return; } sub hashify_response { my ($response,$prob,$student,$partid,$respid) =@_; my $resp_hash = {}; if ($env{'form.correctans'} eq 'true') { $resp_hash->{'Correct'} = &Apache::lonstathelpers::get_student_answer ($prob,$student->{'username'},$student->{'domain'}, $partid,$respid); } $resp_hash->{'Submission'} = $response->[&Apache::loncoursedata::RDs_submission()]; $resp_hash->{'Time'} = $response->[&Apache::loncoursedata::RDs_timestamp()]; $resp_hash->{'Attempt'} = $response->[&Apache::loncoursedata::RDs_tries()]; $resp_hash->{'Awarded'} = $response->[&Apache::loncoursedata::RDs_awarded()]; if ($prob->is_task()) { $resp_hash->{'Grader'} = $response->[&Apache::loncoursedata::RDs_response_eval_2()]; if ($resp_hash->{'Attempt'} eq '0') { $resp_hash->{'Attempt'} = ''; } $resp_hash->{'Award Detail'} = $response->[&Apache::loncoursedata::RDs_part_award()]; $resp_hash->{'Status'} = $response->[&Apache::loncoursedata::RDs_response_eval()]; } else { $resp_hash->{'Award Detail'} = $response->[&Apache::loncoursedata::RDs_awarddetail()]; } return $resp_hash; } ##################################################### ## ## HTML helper routines ## ##################################################### sub html_essay_results { my ($headers,$prob,$partid,$respid,$response,$single_response)=@_; if (! ref($headers) || ref($headers) ne 'ARRAY') { return ''; } # Start of telling them what problem, part, and response my $Str; if (! $single_response) { my $id = $prob->compTitle; if (defined($partid) && $partid ne '0') { $id .= ' '.$prob->part_display($partid); } if (defined($respid)) { $id .= ' '.$respid; } $Str .= ''.$id.''.(' 'x4); } # shift(@$headers); # Get rid of the Submission header my $correct = ''; if ($headers->[0] eq 'Correct') { $correct = &html_format_essay_sub($response->{'Correct'}); shift(@$headers); } $Str .= ''. join('', map { (' 'x4).&mt($_.': [_1]',$response->{$_}); } @$headers).''; if (@$headers || ! $single_response) { $Str .= '
'; } $Str .= &html_format_essay_sub($response->{'Submission'}); # if (defined($correct) && $correct !~ /^\s*$/) { $Str .= '
'.&mt('Correct').''.$correct } return $Str; } sub html_format_essay_sub { my ($submission) = @_; return '' if (! defined($submission) || $submission eq ''); $submission = &HTML::Entities::decode($submission); $submission =~ s/\\\"/\"/g; $submission =~ s/\\\'/\'/g; $submission =~ s|\\r\\n|$/|g; $submission = &HTML::Entities::encode($submission,'<>&"'); $submission =~ s|$/\s*$/|$/

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

'.$submission.'

'; return $submission; } sub html_task_results { my ($headers,$prob,$partid,$respid,$response,$resptype) = @_; if (! ref($headers) || ref($headers) ne 'ARRAY' || ! scalar(@$headers)) { return ''; } my @values; @values = map { $response->{$_}; } @$headers; my $td = ''; my $str = $td.join(''.$td,@values).''; return $str; } sub html_non_essay_results { my ($headers,$prob,$partid,$respid,$response,$resptype) = @_; if (! ref($headers) || ref($headers) ne 'ARRAY' || ! scalar(@$headers)) { return ''; } # my $submission = &HTML::Entities::decode(&unescape($response->{'Submission'})); return '' if (! defined($submission) || $submission eq ''); $submission =~ s/\\\"/\"/g; $submission =~ s/\\\'/\'/g; if ($resptype eq 'radiobutton') { $submission = &HTML::Entities::encode($submission,'<>&"'); $submission =~ s/=([^=])$//; $submission = ''.$submission.''; } $response->{'Submission'} = $submission; # my @values; if ($resptype =~ /^(option|match|rank)$/) { my %submission = map { my ($foil,$value) = split('=',&unescape($_)); ($foil,$value); } split('&',$response->{'Submission'}); my %correct; if (exists($response->{'Correct'})) { %correct = map { my ($foil,$value)=split('=',&unescape($_)); ($foil,$value); } split('&',$response->{'Correct'}); } # foreach my $original_header (@$headers) { if ($original_header =~ /^_/) { # '_' denotes a foil column my ($header) = ($original_header =~ m/^_(.*)$/); my $option = ''; if ( my ($foil) = ($header =~ /(.*) Correct$/)) { if (exists($correct{$foil})) { $option = $correct{$foil}; } } elsif (exists($submission{$header})) { $option = $submission{$header}; } push(@values,&HTML::Entities::encode($option)); } elsif ($original_header eq 'Time') { push(@values,&Apache::lonlocal::locallocaltime($response->{$original_header})); } else { # A normal column push(@values,$response->{$original_header}); } } } else { @values = map { $response->{$_}; } @$headers; } my $td = ''; my $str = $td.join(''.$td,@values).''; return $str; } ######################################################### ######################################################### ## ## Excel Output Routines ## ######################################################### ######################################################### sub prepare_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 @extra_resp_headers = &get_extra_response_headers(); my $lastprob; my %problem_analysis; foreach my $prob (@$Problems) { my %analysis = &Apache::lonstathelpers::get_problem_data($prob->src); $problem_analysis{$prob->src}=\%analysis; foreach my $partid (@{$prob->parts}) { my $responses = [$prob->responseIds($partid)]; my $resptypes = [$prob->responseType($partid)]; for (my $i=0;$i[$i], $resptypes->[$i], $problem_analysis{$prob->src}, 'excel','display', @extra_resp_headers); $columncount += scalar(@headers); } } 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].',$lastprob->compTitle).'

'); $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 ($workbook,$filename,$format) = &Apache::loncommon::create_workbook($r); return if (! defined($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','section'); foreach (@StudentColumns) { $worksheet->write($header_row,$cols_output++,ucfirst($_), $format->{'bold'}); } # Problem headers my %start_col; foreach my $prob (@$Problems) { my $title = $prob->compTitle; $worksheet->write($title_row,$cols_output, $title,$format->{'h3'}); foreach my $partid (@{$prob->parts}) { $worksheet->write($partid_row,$cols_output, $prob->part_display($partid)); my $responses = [$prob->responseIds($partid)]; my $resptypes = [$prob->responseType($partid)]; for (my $i=0;$isymb}->{$partid}->{$responses->[$i]}= $cols_output; $worksheet->write($respid_row,$cols_output, $resptypes->[$i].', '.$responses->[$i]); my @headers = &get_headers($prob,$partid,$responses->[$i], $resptypes->[$i], $problem_analysis{$prob->src}, 'excel','display', @extra_resp_headers); foreach my $text (@headers) { if ($text eq 'Time') { $worksheet->set_column($cols_output,$cols_output,undef, $format->{'date'}); } $worksheet->write($header_row,$cols_output++,$text); } } } } # # 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; 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 $respid = $Response[$i]; my $resptype = $ResponseType[$i]; my $results = &Apache::loncoursedata::get_response_data_by_student ($student,$prob->symb(),$respid); my @headers = &get_headers($prob,$partid,$respid, $resptype, $problem_analysis{$prob->src}, 'excel','normal', @extra_resp_headers); if (! defined($results)) { $results = []; } # $rows_output = $student_row; # my $response_start_col = $start_col{$prob->symb}->{$partid}->{$respid}; for (my $j=scalar(@$results)-1;$j>=0;$j--) { $cols_output = $response_start_col; if ($env{'form.all_sub'} ne 'true') { next if ($j ne scalar(@$results)-1); } my $response = &hashify_response($results->[$j], $prob, $student, $partid, $respid); my @response_data = &compile_response_data(\@headers,$response, $prob,$partid,$respid, $resptype, \&excel_format_item); $worksheet->write_row($rows_output++,$cols_output, \@response_data); $cols_output+=scalar(@response_data); if ($rows_output > $max_row) { $max_row = $rows_output; } } } } } # Fill in the remaining rows with the students data for (my $row = $student_row+1;$row<$max_row;$row++) { my $cols = 0; foreach my $field (@StudentColumns) { $worksheet->write($row,$cols++, $student->{$field}); } } &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 compile_response_data { my ($headers,$response,$prob,$partid,$respid,$resptype,$format) = @_; if (! ref($headers) || ref($headers) ne 'ARRAY' || ! scalar(@$headers)) { return (); } if (ref($format) ne 'CODE') { $format = sub { return $_[0]; }; } # my $submission = &HTML::Entities::decode (&unescape($response->{'Submission'})); if (!$prob->is_task()) { return () if (! defined($submission) || $submission eq ''); } $submission =~ s/\\\"/\"/g; $submission =~ s/\\\'/\'/g; if ($resptype eq 'radiobutton') { $submission =~ s/=([^=])$//; } $response->{'Submission'} = $submission; # my @values; if ($resptype =~ /^(option|match|rank)$/) { my %submission = map { my ($foil,$value) = split('=',&unescape($_)); ($foil,$value); } split('&',$response->{'Submission'}); my %correct; if (exists($response->{'Correct'})) { %correct = map { my ($foil,$value)=split('=',&unescape($_)); ($foil,$value); } split('&',$response->{'Correct'}); } # foreach my $original_header (@$headers) { if ($original_header =~ /^_/) { # '_' denotes a foil column my ($header) = ($original_header =~ m/^_(.*)$/); my $option = ''; if ( my ($foil) = ($header =~ /(.*) Correct$/)) { if (exists($correct{$foil})) { $option = $correct{$foil}; } } elsif (exists($submission{$header})) { $option = $submission{$header}; } push(@values,&{$format}($option,$header)); } else { # A normal column push(@values,&{$format}($response->{$original_header}, $original_header)); } } } else { @values = map { &{$format}($response->{$_},$_); } @$headers; } return @values; } sub excel_format_item { my ($item,$type) = @_; if ($type eq 'Time') { $item = &Apache::lonstathelpers::calc_serial($item); } else { if ($item =~ m/^=/) { $item = ' '.$item; } $item =~ s/\\r//g; $item =~ s/\\n/\n/g; $item =~ s/(\s*$|^\s*)//g; $item =~ s/\\\'/\'/g; } return $item; } ######################################################### ######################################################### ## ## CSV output of student answers ## ######################################################### ######################################################### sub prepare_csv_output { my ($r,$problems,$students) = @_; my $c = $r->connection(); # $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('
' .&mt('Problems occurred in writing the CSV file. ' .'This error has been logged. ' .'Please alert your LON-CAPA administrator.') .'
'); $outputfile = undef; } # # Compute the number of columns per response my @extra_resp_headers = &get_extra_response_headers(); # # Create the table header my @student_columns = ('username','domain','id','section'); # my %headers; push(@{$headers{'student'}},@student_columns); # Pad for the student data foreach my $row ('problem','part','response') { $headers{$row}=[map {''} @student_columns]; } # # we put the headers into the %headers hash my %problem_analysis; my %start_col; my $max_column = scalar(@student_columns); foreach my $prob (@$problems) { my %analysis = &Apache::lonstathelpers::get_problem_data($prob->src); $problem_analysis{$prob->src}=\%analysis; $headers{'problem'}->[$max_column] = $prob->compTitle; foreach my $partid (@{$prob->parts}) { $headers{'part'}->[$max_column] = $prob->part_display($partid); my $responses = [$prob->responseIds($partid)]; my $resptypes = [$prob->responseType($partid)]; for (my $i=0;$i[$i], $resptypes->[$i], $problem_analysis{$prob->src}, 'csv','display', @extra_resp_headers); $start_col{$prob->symb}->{$partid}->{$responses->[$i]}= $max_column; $headers{'response'}->[$max_column]= &mt('Response [_1]',$responses->[$i]); for (my $j=0;$j<=$#headers;$j++) { $headers{'student'}->[$max_column+$j]=$headers[$j]; } $max_column += scalar(@headers); } } } foreach my $row ('problem','part','response','student') { print $outputfile '"'. join('","', map { &Apache::loncommon::csv_translate($_); } @{$headers{$row}}).'"'.$/; } # # Main loop foreach my $student (@$students) { last if ($c->aborted()); my @rows; foreach my $prob (@$problems) { foreach my $partid (@{$prob->parts}) { my @responses = $prob->responseIds($partid); my @response_type = $prob->responseType($partid); for (my $i=0;$i<=$#responses;$i++) { my $respid = $responses[$i]; my $resptype = $response_type[$i]; my @headers = &get_headers($prob,$partid,$respid,$resptype, $problem_analysis{$prob->src}, 'csv','normal', @extra_resp_headers); my $results = &Apache::loncoursedata::get_response_data_by_student ($student,$prob->symb(),$respid); if (! defined($results)) { $results = []; } for (my $j=0; $j[$idx], $prob,$student, $partid,$respid); my @data = &compile_response_data(\@headers,$response, $prob,$partid, $respid,$resptype, \&csv_format_item); my $resp_start_idx = $start_col{$prob->symb}->{$partid}->{$respid}; for (my $k=0;$k<=$#data;$k++) { $rows[$j]->[$resp_start_idx + $k] = $data[$k]; } } } } } foreach my $row (@rows) { print $outputfile '"'.join('","', map { $student->{$_}; } @student_columns).'"'; for (my $i=scalar(@student_columns);$i<$max_column;$i++) { my $value = &Apache::loncommon::csv_translate($row->[$i]); $value ||=''; print $outputfile ',"'.$value.'"'; } print $outputfile $/; } undef(@rows); &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_format_item { my ($item,$type) = @_; if ($type eq 'Time') { $item = localtime($item); } $item =&Apache::loncommon::csv_translate($item); return $item; } ######################################################### ######################################################### ## ## Generic Interface Routines ## ######################################################### ######################################################### sub CreateInterface { ## ## Output Selection my $output_selector = $/.''.$/; ## ## Environment variable initialization my $Str = ''; $Str .= &Apache::lonhtmlcommon::breadcrumbs('Student Submission Reports'); $Str .= '

'; $Str .= &Apache::loncommon::start_data_table(); $Str .= &Apache::loncommon::start_data_table_header_row(); $Str .= ''.&mt('Sections').''; $Str .= ''.&mt('Groups').''; $Str .= ''.&mt('Access Status').''; $Str .= ''.&mt('Options').''; $Str .= ''.&mt('Output Format').''; $Str .= &Apache::loncommon::end_data_table_header_row(); # $Str .= &Apache::loncommon::start_data_table_row(); $Str .= ''."\n"; $Str .= &Apache::lonstatistics::SectionSelect('Section','multiple',5); $Str .= ''; # $Str .= ''."\n"; $Str .= &Apache::lonstatistics::GroupSelect('Group','multiple',5); $Str .= ''; # $Str .= ''; $Str .= &Apache::lonhtmlcommon::StatusOptions(undef,undef,5); $Str .= ''; # # Render problem checkbox my $prob_checkbox = ''. &mt('Show problem').' '.$prob_checkbox.'
'. '
'. '
'. '
'. ''; # $Str .= ''.$output_selector.''; # $Str .= &Apache::loncommon::end_data_table_row(); $Str .= &Apache::loncommon::end_data_table(); # $Str .= '

' .&mt('Status: [_1]', '') .'

'; $Str .= '

'; ## return $Str; } 1; __END__ 500 Internal Server Error

Internal Server Error

The server encountered an internal error or misconfiguration and was unable to complete your request.

Please contact the server administrator at root@localhost to inform them of the time this error occurred, and the actions you performed just before this error.

More information about this error may be available in the server error log.