--- loncom/interface/statistics/lonstudentsubmissions.pm 2004/09/01 21:13:04 1.15 +++ loncom/interface/statistics/lonstudentsubmissions.pm 2004/09/15 21:08:50 1.18 @@ -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.18 2004/09/15 21:08:50 matthew Exp $ # # Copyright Michigan State University Board of Trustees # @@ -38,15 +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 => 'SelectAnother', +my @SubmitButtons = ({ name => 'SelectAnother', text => 'Choose a different Problem' }, { name => 'Generate', - text => 'Generate Spreadsheet'}, + text => 'Generate Report'}, ); sub BuildStudentSubmissionsPage { @@ -90,62 +85,311 @@ sub BuildStudentSubmissionsPage { $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'}.'

'); - if ($ENV{'form.renderprob'} eq 'true') { - $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_html_output($r,$current_problem, - $ProblemData,\@Students); -# &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(); + } + } + 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)}; + } + } + &new_excel_output($r,\@Problems,\@Students,\%Data); } $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(&Apache::lonstathelpers::MultipleProblemSelector + (undef,'.','problemchoice','Statistics')); + } +} + +######################################################### +######################################################### + +sub new_excel_output { + my ($r,$Problems,$Students,$ProblemData) = @_; + my $c = $r->connection(); + # + 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++,$_,$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++; + } + } + } + # + # 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()); + $cols_output = 0; + foreach my $field (@StudentColumns) { + $worksheet->write($rows_output,$cols_output++, + $student->{$field}); + } + 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 $final_response = $results->[-1]; + my $submission = + $final_response->[ + &Apache::loncoursedata::RDs_submission() + ]; + $submission=&excel_format_response($submission,$resptype); + $worksheet->write($rows_output,$cols_output++, + $submission); + } + } + } + $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 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 eq 'option') { + $answer = join("\n", + map { + &Apache::lonnet::unescape($_) ; + } sort split('&',$answer) + ); } + if ($answer =~ m/^=/) { + $answer = ' '.$answer; + } + return $answer; } + +=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; +} + + ######################################################### ######################################################### ## @@ -154,88 +398,114 @@ sub BuildStudentSubmissionsPage { ######################################################### ######################################################### sub prepare_html_output { - my ($r,$problem,$ProblemData,$Students) = @_; + my ($r,$Problems,$Students) = @_; + my $problem; my $c = $r->connection(); my ($resource,$respid,$partid) = ($problem->{'resource'}, $problem->{'respid'}, $problem->{'part'}); - $r->print('

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

'); # 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; - 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; - } - } + 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 { - my $count = 0; - my $header; - if ($response_type eq 'essay') { - $header = &html_essay_header(); - } elsif ($response_type eq 'radiobutton') { - $header = &html_radiobutton_header(); - } elsif ($response_type eq 'option') { - $header = &html_option_header(); - } else { - $header = &html_generic_header(); + @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; } - $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->{'sname'} = $student->{'username'}.'@'. - $student->{'domain'}; - $data->{'time'} = &Apache::lonlocal::locallocaltime - ($response->[&Apache::loncoursedata::RDs_timestamp()]); - $data->{'attempt'} = - $response->[&Apache::loncoursedata::RDs_tries()]; - $data->{'submission'} = - $response->[&Apache::loncoursedata::RDs_submission()]; - $data->{'correct'} = $student->{'answer'}; - my $row; - if ($response_type eq 'essay') { - $row = &html_essay($data); - } elsif ($response_type eq 'radiobutton') { - $row = &html_radiobutton($data); - } elsif ($response_type eq 'option') { - $row = &html_option($data); - } else { - $row = &html_generic($data); - } - $r->print($row.$/); - $count++; + 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 .= ''; + } + 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('
'. + $data->{$col->{'name'}}.'
'.$/); } + $r->print(''.$/); return; } @@ -244,59 +514,31 @@ sub prepare_html_output { ## HTML helper routines ## ##################################################### -my @FullHeaders = ( - {name=>'sname', - display=>'Student'}, - {name => 'id', - display => 'Id'}, - {name => 'time', - display =>'Time'}, - {name => 'attempt', - display =>'Attempt'}, - {name => 'grading', - display =>'Grading'}, - ); - -sub html_essay_header { - my $header; - if ($ENV{'form.subdata'} eq 'true') { - foreach (@FullHeaders) { - $header .= ''.&mt($_->{'display'}).''; - } - } else { - $header = ''.&mt('Username and Submission').''; +sub html_headers { + my ($Columns) = @_; + my $Str; + foreach my $column (@$Columns) { + $Str .= ''.$column->{'display'}.''; } - return $header; + return $Str; } sub html_essay { - my ($data)=@_; + my ($submission,$correct,$tablewidth,$rowextra)=@_; # - $data->{'submission'} =~ s|\\r\\n|$/|g; - $data->{'submission'} = - &HTML::Entities::encode($data->{'submission'},'<>&"'); - $data->{'submission'} =~ s|$/\s*$/|$/

$/|g; - $data->{'submission'} =~ s|\\||g; - $data->{'submission'} = '

'.$data->{'submission'}.'

'; + $submission =~ s|\\r\\n|$/|g; + $submission = &HTML::Entities::encode($submission,'<>&"'); + $submission =~ s|$/\s*$/|$/

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

'.$submission.'

'; # - my $Str = ''; - if ($ENV{'form.subdata'} eq 'true') { - $Str .= - ''.$data->{'sname'}.''. - ''.$data->{'time'}.''. - ''.$data->{'attempt'}.''; - $Str .= ''; - $Str .= ''. - $data->{'submission'}.''; - } else { - $Str .= ''.$data->{'sname'}.''. - $data->{'submission'}.''; - } + my $Str = ''. + ''.$submission.''; if ($ENV{'form.correctans'} eq 'true') { $Str .= ''; - if (defined($data->{'correct'}) && $data->{'correct'} !~ /^\s*$/) { - $Str .= ''. - ''.&mt('Correct Answer:').''.$data->{'correct'}.''; + if (defined($correct) && $correct !~ /^\s*$/) { + $Str .= ''. + ''.&mt('Correct Answer:').''.$correct.''; } } $Str .= ''; @@ -304,273 +546,264 @@ sub html_essay { return $Str; } -sub html_radiobutton_header { - my $header; - if ($ENV{'form.subdata'} eq 'true') { - foreach (@FullHeaders) { - $header .= ''.&mt($_->{'display'}).''; - } - } else { - $header = - ''.&mt('Username').''; - } - $header .=''.&mt('Submission').''; +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_radiobutton { - my ($data)=@_; - # - $data->{'submission'} =~ s/=([^=])$//; - # - my $Str = ''; - if ($ENV{'form.subdata'} eq 'true') { - $Str .= - ''.''.$data->{'sname'}.''. - ''.$data->{'time'}.''. - ''.$data->{'attempt'}.''; - $Str .= ''.$data->{'submission'}.''; - if ($ENV{'form.correctans'} eq 'true') { - $Str .= ''.$data->{'correct'}.''; - } - } else { - $Str .= ''.$data->{'sname'}.''; - $Str .= ''.$data->{'submission'}.''; - if ($ENV{'form.correctans'} eq 'true') { - $Str .= ''.$data->{'correct'}.''; - } - } - $Str .= ''; - # - return $Str; +sub html_option_headers { + return &html_generic_headers(); } -sub html_generic_header { - my $header; - if ($ENV{'form.subdata'} eq 'true') { - foreach (@FullHeaders) { - $header .= ''.&mt($_->{'display'}).''; - } - } else { - $header = - ''.&mt('Username').''; - } - $header .= ''.&mt('Submission').''; - if ($ENV{'form.correctans'} eq 'true') { - $header .= ''.&mt('Correct').''; - } - return $header; +sub html_radiobutton_results { + my ($submission,$correct,$tablewidth,$rowclass)=@_; + $submission =~ s/=([^=])$//; + return &html_generic_results($submission,$correct,$tablewidth,$rowclass); } -sub html_generic { - my ($data)=@_; - $data->{'submission'} = &Apache::lonnet::unescape($data->{'submission'}); - my $Str = ''; - if ($ENV{'form.subdata'} eq 'true') { - $Str .= - ''.$data->{'sname'}.''. - ''.$data->{'time'}.''. - ''.$data->{'attempt'}.''; - } else { - $Str .= ''.$data->{'sname'}.''; - } - $Str .= ''.$data->{'submission'}.''; +sub html_generic_results { + my ($submission,$correct,$tablewidth,$rowclass)=@_; + my $Str .= ''.$submission.''; if ($ENV{'form.correctans'} eq 'true') { - $Str .= ''.$data->{'correct'}.''; + $Str .= ''.$correct.''; } $Str .= ''; return $Str; } -sub html_option_header { - my $header; - if ($ENV{'form.subdata'} eq 'true') { - foreach (@FullHeaders) { - $header .= ''.&mt($_->{'display'}).''; - } - } else { - $header = - ''.&mt('Username').''; - } - $header .= ''.&mt('Submission').''; - if ($ENV{'form.correctans'} eq 'true') { - $header .= ''.&mt('Correct').''; - } - return $header; -} - -sub html_option { - my ($data)=@_; - $data->{'submission'} = '
    '. +sub html_option_results { + my ($submission,$correct,$tablewidth,$rowclass)=@_; + $submission = + '
      '. '
    • '.join('
    • ', map { &Apache::lonnet::unescape($_) ; - } sort split('&',$data->{'submission'})). - '
      • '; - $data->{'correct'} = '
          '. - '
        • '.join('
        • ', - map { - &Apache::lonnet::unescape($_) ; - } sort split('&',$data->{'correct'})).'
        '; - my $Str = ''; - if ($ENV{'form.subdata'} eq 'true') { - $Str .= - ''.$data->{'sname'}.''. - ''.$data->{'time'}.''. - ''.$data->{'attempt'}.''; - } else { - $Str .= ''.$data->{'sname'}.''; + } sort split('&',$submission) + ). + '
          '; + if (defined($correct) && $correct !~ /^\s*$/) { + $correct = '
            '. + '
          • '.join('
          • ', + map { + &Apache::lonnet::unescape($_) ; + } sort split('&',$correct) + ). + '
          '; } - $Str .= ''.$data->{'submission'}.''; - if ($ENV{'form.correctans'} eq 'true') { - $Str .= ''.$data->{'correct'}.''; - } - $Str .= ''; - return $Str; + # + 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'); -=pod - - 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'); + $r->rflush(); # - # 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; + 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; } # - $workbook->set_tempdir('/home/httpd/perl/tmp'); # - my $format = &Apache::loncommon::define_excel_formats($workbook); - my $worksheet = $workbook->addworksheet('Student Submission Data'); + my @Columns; + if (exists($ENV{'form.concise'}) && $ENV{'form.concise'} eq 'true') { + foreach (@DefaultColumns) { + if ($_->{'name'} =~ /^(username|domain|id)$/){ + push(@Columns,$_); + } + } + } else { + @Columns = @DefaultColumns; + } # - # Make sure we get new weight data instead of data on a 10 minute delay - &Apache::lonnet::clear_EXT_cache_status(); + 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; + } # - # 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'}); + my $header = &csv_headers(\@Columns).','.&csv_generic_headers(); + print $outputfile $header.$/; # - # 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; + 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) { - if (! exists($row{$col})) { - $cols_output++; - next; - } - $worksheet->write($rows_output,$cols_output++,$row{$col}); + $row .= '"'. + &Apache::loncommon::csv_translate($data->{$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{'grading'} = $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}, - $row_format{$col}); - } - $rows_output++; + 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); } - } # End of else clause on if (! defined($results) .... + print $outputfile $row.$/; + } + &Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state, + 'last student'); } + close($outputfile); # - # Close the excel file - $workbook->close(); + # Close the progress window + &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state); # - # Write a link to allow them to download it - $r->print('

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

          '."\n"); - $r->print(''); + # Tell the user where to get their csv file + $r->print('
          '. + ''.&mt('Your csv file.').''."\n"); $r->rflush(); return; - } -=cut +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; +} ######################################################### ######################################################### @@ -580,42 +813,69 @@ sub html_option { ######################################################### ######################################################### sub prepare_excel_output { - my ($r,$problem,$ProblemData,$Students) = @_; + my ($r,$Problems,$Students) = @_; my $c = $r->connection(); - my ($resource,$respid,$partid) = ($problem->{'resource'}, - $problem->{'respid'}, - $problem->{'part'}); + # $r->print('

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

          '. - '

          '. - &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'); + $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->print(''); $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'); + + + + + + + + + + + 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/'. @@ -644,9 +904,15 @@ sub prepare_excel_output { $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'}); + $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 @@ -654,6 +920,7 @@ sub prepare_excel_output { 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'}, @@ -667,11 +934,12 @@ sub prepare_excel_output { ($rows_output,$weight_col); my $cols_output = 0; foreach my $col (@Columns) { - if (! exists($row{$col})) { + if (! exists($row{$col->{'name'}})) { $cols_output++; next; } - $worksheet->write($rows_output,$cols_output++,$row{$col}); + $worksheet->write($rows_output,$cols_output++, + $row{$col->{'name'}}); } $rows_output++; } else { @@ -702,7 +970,7 @@ sub prepare_excel_output { # This will be interpreted as a formula. That is bad! $row{'submission'} = " ".$row{'submission'}; } - $row{'grading'} = $response->[ + $row{'awarddetail'} = $response->[ &Apache::loncoursedata::RDs_awarddetail()]; $row{'awarded'} = $response->[ &Apache::loncoursedata::RDs_awarded()]; @@ -714,13 +982,16 @@ sub prepare_excel_output { ($rows_output,$weight_col); my $cols_output = 0; foreach my $col (@Columns) { - $worksheet->write($rows_output,$cols_output++,$row{$col}, - $row_format{$col}); + $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(); @@ -737,6 +1008,8 @@ sub prepare_excel_output { return; } +=cut + ######################################################### ######################################################### ## @@ -746,6 +1019,17 @@ sub prepare_excel_output { ######################################################### sub CreateInterface { ## + ## Output Selection + my $OutputSelector = $/.''.$/; + ## ## Environment variable initialization my $Str = ''; $Str .= &Apache::lonhtmlcommon::breadcrumbs @@ -753,9 +1037,9 @@ sub CreateInterface { $Str .= '

          '; $Str .= ''."\n"; $Str .= ''; - $Str .= ''; - $Str .= ''; - $Str .= ''; + $Str .= ''; + $Str .= ''; + $Str .= ''; $Str .= ''."\n"; # $Str .= ''; # $Str .= ''."\n";
          '.&mt('Sections').''.&mt('Enrollment Status').' '.&mt('Sections').''.&mt('Enrollment Status').''.&mt('Output Options').'
          '."\n"; @@ -788,22 +1072,23 @@ sub CreateInterface { } $last_sub_checkbox.= 'value="true" />'; # - # extra submission data checkbox - my $subdata_checkbox = ''. + $Str .= ''. +# ''.&mt('Output Format: [_1]',$OutputSelector).'
          '.$/. + '
          '. '
          '. - '
          '. - '
          '. +# '
          '. +# '
          '. '