--- loncom/interface/statistics/lonstudentsubmissions.pm 2004/02/23 16:13:31 1.4 +++ loncom/interface/statistics/lonstudentsubmissions.pm 2004/09/23 14:35:54 1.23 @@ -1,6 +1,6 @@ # The LearningOnline Network with CAPA # -# $Id: lonstudentsubmissions.pm,v 1.4 2004/02/23 16:13:31 matthew Exp $ +# $Id: lonstudentsubmissions.pm,v 1.23 2004/09/23 14:35:54 matthew Exp $ # # Copyright Michigan State University Board of Trustees # @@ -38,19 +38,10 @@ use HTML::Entities(); use Time::Local(); use Spreadsheet::WriteExcel(); -my @SubmitButtons = ({ name => 'PrevProblem', - text => 'Previous Problem' }, - { name => 'NextProblem', - text => 'Next Problem' }, - { name => 'break'}, - { name => 'ClearCache', - text => 'Clear Caches' }, - { name => 'updatecaches', - text => 'Update Student Data' }, - { name => 'SelectAnother', +my @SubmitButtons = ({ name => 'SelectAnother', text => 'Choose a different Problem' }, { name => 'Generate', - text => 'Generate Spreadsheet'}, + text => 'Generate Report'}, ); sub BuildStudentSubmissionsPage { @@ -75,18 +66,9 @@ sub BuildStudentSubmissionsPage { $r->print('

There are no students in the sections selected

'); } # - &Apache::loncoursedata::clear_internal_caches(); - if (exists($ENV{'form.ClearCache'}) || - exists($ENV{'form.updatecaches'}) || - (exists($ENV{'form.firstanalysis'}) && - $ENV{'form.firstanalysis'} ne 'no')) { - &Apache::lonstatistics::Gather_Full_Student_Data($r); - } - if (! exists($ENV{'form.firstanalysis'})) { - $r->print(''); - } else { - $r->print(''); - } + my @CacheButtonHTML = + &Apache::lonstathelpers::manage_caches($r,'Statistics','stats_status', + '

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

'); $r->rflush(); # if (exists($ENV{'form.problemchoice'}) && @@ -100,85 +82,449 @@ sub BuildStudentSubmissionsPage { $r->print(' 'x5); } } + foreach my $html (@CacheButtonHTML) { + $r->print($html.(' 'x5)); + } # - $r->print('
'); + $r->print('
'.$/); $r->rflush(); # - # Determine which problem we are to analyze - my $current_problem = &Apache::lonstathelpers::get_target_from_id - ($ENV{'form.problemchoice'}); - # - my ($prev,$curr,$next) = - &Apache::lonstathelpers::get_prev_curr_next($current_problem, - '.', - 'response', - ); - if (exists($ENV{'form.PrevProblem'}) && defined($prev)) { - $current_problem = $prev; - } elsif (exists($ENV{'form.NextProblem'}) && defined($next)) { - $current_problem = $next; - } else { - $current_problem = $curr; + # Determine which problems we are to analyze + my @Symbs = + &Apache::lonstathelpers::get_selected_symbs('problemchoice'); + foreach my $selected (@Symbs) { + $r->print(''.$/); } # - # Store the current problem choice and send it out in the form - $ENV{'form.problemchoice'} = - &Apache::lonstathelpers::make_target_id($current_problem); - $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 (! defined($current_problem->{'resource'})) { + if (! scalar(@Problems) || ! defined($Problems[0])) { $r->print('resource is undefined'); } else { - my $resource = $current_problem->{'resource'}; - $r->print('

'.$resource->{'title'}.'

'); - $r->print('

'.$resource->{'src'}.'

'); - $r->print(&Apache::lonstathelpers::render_resource($resource)); - $r->rflush(); - my %Data = &Apache::lonstathelpers::get_problem_data - ($resource->{'src'}); - my $ProblemData = $Data{$current_problem->{'part'}. - '.'. - $current_problem->{'respid'}}; - &prepare_excel_output($r,$current_problem, - $ProblemData,\@Students); + 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(''); + &mt('Prepare Report').'" />'); $r->print(' 'x5); - $r->print('

'.&mt('Please select a problem to analyze').'

'); - $r->print(&Apache::lonstathelpers::ProblemSelector('.')); + $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 = &mt('Part [_1]',$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::analyze_problem_as_student + ($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 .= ' '.$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::analyze_problem_as_student + ($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 ''); + 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 =~ s/=([^=])$//; + } elsif ($resptype =~ /^(option|match|rank)$/) { + $submission = + '