--- loncom/interface/statistics/lonstudentsubmissions.pm 2004/09/01 21:13:04 1.15 +++ loncom/interface/statistics/lonstudentsubmissions.pm 2011/01/17 00:19:41 1.62 @@ -1,6 +1,6 @@ # The LearningOnline Network with CAPA # -# $Id: lonstudentsubmissions.pm,v 1.15 2004/09/01 21:13:04 matthew Exp $ +# $Id: lonstudentsubmissions.pm,v 1.62 2011/01/17 00:19:41 www Exp $ # # Copyright Michigan State University Board of Trustees # @@ -27,9 +27,10 @@ package Apache::lonstudentsubmissions; use strict; -use Apache::lonnet(); +use Apache::lonnet; use Apache::loncommon(); use Apache::lonhtmlcommon(); +use Apache::lonquickgrades(); use Apache::loncoursedata(); use Apache::lonstatistics; use Apache::lonlocal; @@ -37,16 +38,14 @@ use Apache::lonstathelpers; use HTML::Entities(); use Time::Local(); use Spreadsheet::WriteExcel(); +use lib '/home/httpd/lib/perl/'; +use LONCAPA; + -my @SubmitButtons = ({ name => 'PrevProblem', - text => 'Previous Problem' }, - { name => 'NextProblem', - text => 'Next Problem' }, - { name => 'break'}, - { name => 'SelectAnother', +my @SubmitButtons = ({ name => 'SelectAnother', text => 'Choose a different Problem' }, { name => 'Generate', - text => 'Generate Spreadsheet'}, + text => 'Generate Report'}, ); sub BuildStudentSubmissionsPage { @@ -63,20 +62,29 @@ sub BuildStudentSubmissionsPage { # &Apache::lonstatistics::PrepareClasslist(); # + $r->print( &Apache::lonhtmlcommon::breadcrumbs('Student Submission Reports')); + &Apache::lonquickgrades::startGradeScreen($r,'statistics'); $r->print(&CreateInterface()); # my @Students = @Apache::lonstatistics::Students; # if (@Students < 1) { - $r->print('
'. + &mt('Computing correct answers greatly increases 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',\%anoncounter)); } } +sub array_shuffle { + my $array = shift; + return unless (ref($array) eq 'ARRAY'); + my $i = scalar(@$array); + my $j; + foreach my $item (@$array) { + --$i; + $j = int(rand($i+1)); + next if($i == $j); + @$array [$i,$j] = @$array[$j,$i]; + } + return @$array; +} + +## +## get_extra_response_headers +## +sub get_extra_response_headers { + my ($show_named) = @_; + my @extra_resp_headers; + if ($env{'form.correctans'} eq 'true') { + push(@extra_resp_headers,'Correct'); + } + if ($show_named) { + 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; +} + ######################################################### ######################################################### ## -## prepare_html_output +## HTML Output Routines ## ######################################################### ######################################################### sub prepare_html_output { - my ($r,$problem,$ProblemData,$Students) = @_; + my ($r,$problems,$students,$anoncounter,$show_named) = @_; my $c = $r->connection(); - my ($resource,$respid,$partid) = ($problem->{'resource'}, - $problem->{'respid'}, - $problem->{'part'}); - $r->print('}; + 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'}; + } + } } - $r->print($row.$/); - $count++; + } + if (! $no_data) { + $r->print(qq{ |
$/|g; - $data->{'submission'} =~ s|\\||g; - $data->{'submission'} = '
'.$data->{'submission'}.'
'; - # - my $Str = '$/|g; + $submission =~ s|\\||g; + $submission = '
'.$submission.'
'; + return $submission; } -sub html_generic_header { - my $header; - if ($ENV{'form.subdata'} eq 'true') { - foreach (@FullHeaders) { - $header .= ''.&mt("Unable to create new Excel file. ". - "This error has been logged. ". - "Please alert your LON-CAPA administrator"). - '
'); - return undef; + # + # Determine the number of columns in the spreadsheet + my $columncount = 3; # username, domain, id + my @extra_resp_headers = &get_extra_response_headers($show_named); + my $lastprob; + my %problem_analysis; + foreach my $prob (@$Problems) { + my $symb = $prob->symb(); + my %analysis = &Apache::lonstathelpers::get_problem_data($prob->src); + $problem_analysis{$prob->src}=\%analysis; + foreach my $partid (@{$prob->parts}) { + if (($prob->is_anonsurvey($partid)) || ($anoncounter->{$symb."\0".$partid})) { + next if ($show_named); + } else { + next unless ($show_named); + } + + my $responses = [$prob->responseIds($partid)]; + my $resptypes = [$prob->responseType($partid)]; + for (my $i=0;$i'.&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.').'
'.$/); + if (ref($lastprob)) { + $r->print(''.&mt('The last problem that will fit in the current spreadsheet is [_1].',$lastprob->compTitle).'
'); + } + $r->rflush(); + return; } # - $workbook->set_tempdir('/home/httpd/perl/tmp'); + # Print out a message telling them what we are doing + if (scalar(@$Problems) > 1) { + $r->print(''. - &mt('See the status bar above for student answer computation progress'). - '
'); - # - if ($ENV{'form.correctans'} eq 'true') { - &Apache::lonstathelpers::GetStudentAnswers($r,$problem,$Students, - 'Statistics', - 'stats_status'); - } + &mt('Generating CSV report of student responses').''); # - $r->print(''); + # 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(); - my @Columns; - push(@Columns,'username'); - push(@Columns,'domain'); - push(@Columns,'attempt'); - push(@Columns,'time'); - push(@Columns,'submission'); - if ($ENV{'form.correctans'} eq 'true') { push(@Columns,'correct'); } - push(@Columns,'grading'); - push(@Columns,'awarded'); - my $awarded_col = $#Columns; - push(@Columns,'weight'); - my $weight_col = $#Columns; - push(@Columns,'score'); # - # Create excel worksheet + # Open a file + my $outputfile; 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; + $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.') + .' '.&mt('This error has been logged.') + .' '.&mt('Please alert your LON-CAPA administrator.') + .'
' + ); + $outputfile = undef; + } + # + # Compute the number of columns per response + my @extra_resp_headers = &get_extra_response_headers($show_named); + # + # Create the table header + my @student_columns = ('username','domain','id','section'); + if ($show_named) { + @student_columns = qw(username domain id section); + } else { + @student_columns = qw(username); } + my $student_column_count = scalar(@student_columns); # - $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,\@Columns,$format->{'bold'}); - # - # Populate the worksheet with the student data - 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{'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})) { - $cols_output++; - next; - } - $worksheet->write($rows_output,$cols_output++,$row{$col}); + 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 $symb = $prob->symb(); + 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}) { + if (($prob->is_anonsurvey($partid)) || ($anoncounter->{$symb."\0".$partid})) { + next if ($show_named); + } else { + next unless ($show_named); } - $rows_output++; - } else { - for (my $i=0;$i'. - &mt('Your Excel spreadsheet.'). - '
'."\n"); - $r->print(''); + # Main loop + foreach my $student (@$students) { + last if ($c->aborted()); + my @rows; + foreach my $prob (@$problems) { + my $symb = $prob->symb; + foreach my $partid (@{$prob->parts}) { + if (($prob->is_anonsurvey($partid)) || ($anoncounter->{$symb."\0".$partid})) { + next if ($show_named); + } else { + next unless ($show_named); + } + 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'; - $Str .= '
'.&mt('Sections').' | '; - $Str .= ''.&mt('Enrollment Status').' | '; - $Str .= ''; - $Str .= ' | '.&mt('Sections').' | '; + $Str .= ''.&mt('Groups').' | '; + $Str .= ''.&mt('Student Data').&Apache::loncommon::help_open_topic("Chart_Student_Data").' | '; + $Str .= ''.&mt('Access Status').' | '; + $Str .= ''.&mt('Options').' | '; + $Str .= ''.&mt('Output Format').' | '; + $Str .= &Apache::loncommon::end_data_table_header_row(); # - $Str .= '
---|---|---|---|---|---|
'."\n"; + $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 .= ''."\n"; + $Str .= &Apache::lonstatistics::StudentDataSelect('StudentData','multiple', 5,undef); + $Str .= ' | '; + # $Str .= ''; $Str .= &Apache::lonhtmlcommon::StatusOptions(undef,undef,5); $Str .= ' | '; # # Render problem checkbox my $prob_checkbox = ''; - # - # extra submission data checkbox - my $subdata_checkbox = ''; - # - $Str .= ''.' '. - ' '. - ' '. - ' '. - ' | ';
- #
- $Str .= '
' + .&mt('Status: [_1]', + '') + .'
'; ## return $Str; }