# The LearningOnline Network with CAPA # # $Id: lonstudentsubmissions.pm,v 1.26 2004/11/08 15:14:52 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(); } } 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 Prepare Report button to continue.'). '

'); $r->print(&Apache::lonstathelpers::MultipleProblemSelector (undef,'problemchoice','Statistics')); } } ######################################################### ######################################################### ## ## 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 @response_headers = ('Submission'); if ($ENV{'form.correctans'} eq 'true') { push(@response_headers,'Correct'); } if ($ENV{'form.prob_status'} eq 'true') { push(@response_headers,'Award Detail'); push(@response_headers,'Time'); push(@response_headers,'Attempt'); push(@response_headers,'Awarded'); } my $response_multiplier = scalar(@response_headers); # # Create the table header my @student_columns = ('username','domain','id'); # 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; foreach my $prob (@$problems) { 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] eq 'essay') { # do something clever, like shut up. } else { $total_col += scalar(@response_headers); $headers{'response'} .= ''. &mt('Response [_1]',$responses->[$i]).''; $headers{'student'}.= ''.join('', @response_headers). ''; $part_span += scalar(@response_headers); } } if (! $single_part) { my $tmpname = $partid; if ($partid =~/^\d+$/) { $tmpname = $prob->part_display($partid); } $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 %problem_data; my $maxrow; foreach my $prob (@$problems) { my $symb = $prob->symb; $problem_data{$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 $resptype = $response_type[$i]; my $width = scalar(@response_headers); $problem_data{$symb}->{$partid}->{$respid}={}; my $resp_data = $problem_data{$symb}->{$partid}->{$respid}; $resp_data->{'fake'} = qq{ }; my $results = &Apache::loncoursedata::get_response_data_by_student ($student,$prob->symb(),$respid); if (! defined($results)) { $results = []; } # if (scalar(@$results) > $maxrow && $resptype ne 'essay') { $maxrow = scalar(@$results); } for (my $j=scalar(@$results)-1;$j>=0;$j--) { my $response = $results->[$j]; if ($ENV{'form.all_sub'} ne 'true') { next if ($j ne scalar(@$results)-1); } if ($resptype eq 'essay') { push(@essays, &html_essay_results($student, $prob,$partid,$respid, $response, $single_response). ''); } else { push(@{$resp_data->{'real'}}, &html_results($student, $prob,$partid,$respid, $response,$resptype)); } } } # 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) { my $symb = $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 $resp_data = $problem_data{$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; } ##################################################### ## ## HTML helper routines ## ##################################################### sub html_essay_results { my ($student,$prob,$partid,$respid,$response,$single_response)=@_; # my $submission =$response->[&Apache::loncoursedata::RDs_submission()]; $submission = &html_format_sub($submission,'essay'); # my $correct = ''; if ($ENV{'form.correctans'} eq 'true') { $correct = &Apache::lonstathelpers::get_student_answer ($prob,$student->{'username'},$student->{'domain'}, $partid,$respid); $correct = &html_format_sub($correct,'essay'); } 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); } if ($ENV{'form.prob_status'} eq 'true') { $Str .= ''; $Str .= $response->[&Apache::loncoursedata::RDs_awarddetail()]. (' 'x4); $Str .= &mt('Attempt [_1]', $response->[&Apache::loncoursedata::RDs_tries()]). (' 'x4); $Str .= &Apache::lonlocal::locallocaltime ($response->[&Apache::loncoursedata::RDs_timestamp()]). (' 'x4); $Str .= &mt('Awarded: [_1]', $response->[&Apache::loncoursedata::RDs_awarded()]). (' 'x4); $Str .= '
'; } $Str .= $submission; if (defined($correct) && $correct !~ /^\s*$/) { $Str .= '
'.&mt('Correct').''.$correct } return $Str; } sub html_results { my ($student,$prob,$partid,$respid,$response,$resptype) = @_; my $submission =$response->[&Apache::loncoursedata::RDs_submission()]; $submission = &html_format_sub($submission,$resptype); my $correct = ''; if ($ENV{'form.correctans'} eq 'true') { $correct = &Apache::lonstathelpers::get_student_answer ($prob,$student->{'username'},$student->{'domain'}, $partid,$respid); $correct = &html_format_sub($correct,$resptype); } my $Str; $Str .= ''.$submission.''; if ($ENV{'form.correctans'} eq 'true') { $Str .= ''.$correct.''; } if ($ENV{'form.prob_status'} eq 'true') { $Str .= ''. $response->[&Apache::loncoursedata::RDs_awarddetail()]. ''; $Str .= ''. &Apache::lonlocal::locallocaltime ($response->[&Apache::loncoursedata::RDs_timestamp()]). ''; $Str .= ''. $response->[&Apache::loncoursedata::RDs_tries()]. ''; $Str .= ''. $response->[&Apache::loncoursedata::RDs_awarded()]. ''; } return $Str; } sub html_format_sub { my ($submission,$resptype) = @_; return '' if (! defined($submission) || $submission eq ''); $submission = &HTML::Entities::decode($submission); $submission =~ s/\\\"/\"/g; $submission =~ s/\\\'/\'/g; if ($resptype eq 'essay') { $submission =~ s|\\r\\n|$/|g; $submission = &HTML::Entities::encode($submission,'<>&"'); $submission =~ s|$/\s*$/|$/

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

'.$submission.'

'; } elsif ($resptype eq 'radiobutton') { $submission = &HTML::Entities::encode($submission,'<>&"'); $submission =~ s/=([^=])$//; } elsif ($resptype =~ /^(option|match|rank)$/) { $submission = '